Tag Archives: VMware

VMware vRealize LifeCycle Manager 8 – Migration Process Screenshots

VMware vRealize LifeCycle Manager 8 released earlier this week, 17th October 2019.

Note the official name and abbreviation, its a long one!

  • vRSLCM (vRealize Suite LifeCycle Manager)

You can find the supporting official documentation here;

What's New Blog Link:
What's New Blog Post

Download Link:
Product Download

Release Notes:
Release Notes

Documentation Link:
Resources
Migration Process

The best news about this release is the “easy installer“, which also allows you to migrate from older versions. In this post, I’ve documented the screenshots in steps for you, as I know many of you out there like to see the end to end process before you undergo an update yourself, so you know what to expect.

During this migration process the following will happen;

  1. New LCM virtual appliance deployed
  2. New IDM appliance deployed (unless you select to link to an existing environment)
  3. Existing LCM settings and content will be migrated
Migration Process Screenshots

1. Load up the Easy Installer UI and select the Migrate option

Procedure
1. After you download the file, mount the vra-lcm-installer.iso file.
2. Browse to the folder vrlcm-ui-installer inside the CD-ROM.
3. The folder contains three sub-folder for three operating systems. Based on your operating system, browse to the corresponding operating system folder inside vrlcm-ui-installer folder.
4. Run the executable as per the correct steps for your OS.

2. You’ll get the below introduction page explaining the Migrate option, and some pre-req info.

3. As with every software, there is a EULA to accept.

4. Select the target vCenter environment where you want the LifeCycle Manager and Identity Manager (if needed) appliances to be deployed.

You will be asked to confirm the connection SSL Thumbprint for the vCenter provided.

5. Select the Datacenter or VM folder within your target vCenter that you want to deploy the Virtual Appliances.

6. Select the compute resource within your target vCenter to deploy the Virtual Appliances

7.  Select the storage location for your virtual appliances, at this stage I am unaware if you are able to select different datastores for each Virtual Appliance.

7. Provide details for the Network configuration for the new virtual appliances. For the easy installer it is assumed you will be deploying both to the same network subnet range.

8. Provide the default passwords for both virtual appliances, this password will be used for the following accounts;

  • vRealize LifeCycle Manager
    • Root Password
    • Admin Password
  • VMware Identity Manager
    • Root Password
    • Admin Password
    • sshuser password
    • Default Configuration User Password (You will configure the name of this account later)

9. Configuration of the LifeCycle Appliance deployment

  • Name of VM in vCenter
  • IP address
  • Hostname  (FDQN as in DNS)

10. Next you will provide the details of the existing LifeCycle Manager you are migrating from. The wizard does not seem to do any prechecks of the information provided, like it does when you connect to vCenter at the start.

11.Configuration of the VMware Identity Manager appliance

  • Install a New Identity Manager
    • Name of VM in vCenter
    • IP address
    • Hostname  (FDQN as in DNS)
    • Default Configuration Admin (Provide a name that is not root or admin)
  • Import Existing VMware Identity Manager
    • No configuration necessary, it will be pulled across from the existing LCM environment.

12. The usual Summary page of all the options you have selected/configured.

13. Finally as the process runs, you will get a progress bar with the various stages. And then once complete, a link to the new vRSLCM (vRealize Suite LifeCycle Manager) UI and the request that’s created to migrate the data from the old vRSLCM environment.

So this concludes the post!

Regards

Dean

VMware Ports and Protocols – All in one place!

Getting hold of all the VMware products various ports and protocols has sometimes come across like pulling blood from a stone.

However as announced yesterday at VMworld 2019, we have a new dedicated website just for this subject!

https://ports.vmware.com/

At a high-level, this system offers the following features:

  • A centralized system that is easy for our product teams to publish and modify and update the ports and protocols for each product.
  • Provides capability to customers with several Search filters on products, releases, port number, protocol, purpose and so on.
  • Download feature filtered data for offline view.

And for all you dark theme lovers out there, yes there is also a dark theme option!

Regards

Dean

PowerCLI with a GUI – Clone a machine, add DHCP Reservations, alter CPUID

In this blog post, I am going to break down a PowerShell code I have created (with help from some colleagues). The functions of this PowerShell code are;

  • Present a GUI form to the end user
    • Connect to a vCenter
    • Select the virtual machine to be cloned
    • Select the datastore the new VM is to be stored on (display DS free space)
    • Select the host for this VM to be created against (display free memory on the host)
    • Set the new VM name
    • Create an IP reservation in both the Production and DR DHCP Scopes

Below are some functional screenshots of the code’s GUI and also a rough flowchart of what I needed to achieve.

You can skip to the end to find the full code.

A little more background on the script

So my customer had a dedicated environment for hosting their custom application, however these applications were built and running inside an old unsupported OS which expected to be running on a particular era of CPU’s to run correctly, for example todays Intel Skylake would cause the OS to panic and not run. As you can also imagine with this type of older OS, there are no VM Tools support either.

Here is the architecture diagram;

Providing DR around this environment was interesting, we could protect the VM using SRM and storage array LUN replication. But this also presented some issues, when the VM boots in DR. “what happens with networking?” hence we setup a DHCP reservation on both Production and DR. Meaning we know the VMs IP regardless of where its booted.

To get around our little deployment issues, I built this script so that my customer could easily clone a base VM and configure it to boot grabbing a known controlled IP from DHCP and have the right masked CPU. Removing the need to have an intimate knowledge of the steps to setup a new application tier and the intricacies involved.

Other information around the environment. Cloudflare offers DNS with a low TTL on records, also if you happy to hit the wrong web server, there is a configuration to bounce you to the correct one anyway. In a DR event, we change the DR web server configuration to not bounce your connection to the production side. The DB servers are setup with application level replication in real-time.

All of this environment is sat in a completely separate network zone, and some special configuration is in place on the firewall and DHCP server to allow for remote PowerShell to work from the administration network to the DHCP Servers only. There is no access to any other part of the above architecture due to zoning/firewalling/networking.

Building the GUI

This is one of the areas that I’ll happily admit to having help on, which was building the GUI. You can easily achieve something very similar by using https://poshgui.com/

We have split the code into function, making the code more portable for future use; A function is basically a named block of code. When you call the function name, the script block within that function runs. (WindowsITPro).

The first function builds the GUI form, and the first boxes that appear allowing us to connect to the vCenter

function PickVcenter()
{
#Text label for vCenter selection
$dropDownLabel = New-Object System.Windows.Forms.Label
$dropDownLabel.Location = New-Object System.Drawing.Size(10,12)
$dropDownLabel.Size = New-Object System.Drawing.Size(120, 20)
$dropDownLabel.Text = "Select vCenter Server"
$form.Controls.Add($dropDownLabel)

#Dropdown list for vCenter names
$dropDownList = New-Object System.Windows.Forms.ComboBox
$dropDownList.Location = New-Object System.Drawing.Size(150,10)
$dropDownList.Size = New-Object System.Drawing.Size(150,30)
$dropDownList.Items.Add("vCenter1")
$dropDownList.Items.Add("vCenter2")
$form.Controls.Add($dropDownList)

#Button to click off connect to selected vCenter
$button = New-Object System.Windows.Forms.Button
$button.Location = New-Object System.Drawing.Size(310, 10)
$button.Size = New-Object System.Drawing.Size(60, 20)
$button.Text = "Connect"
$button.Add_Click({ConnectVIServer})
$form.Controls.Add($button)

#Dialog title name of GUI Form, and size of form
$form.Text = "Clone Me Baby!"
$form.Size = New-Object System.Drawing.Size(1000,500)
$form.StartPosition = "CenterScreen"

$form.ShowDialog()

}

Next is the second function whereby we take the text selected in the drop down box to a string, and use this as a variable to connect to the vCenter in question.

function ConnectVIServer() {
#takes the selection in the previous function to a string, to be used as a variable
$choice = $dropDownList.SelectedItem.ToString()
try {
$viServer = Connect-VIServer $choice
if ($viServer -eq $null) { return }
#Loads the ShowVMs function should a connection be made
ShowVMs
}
catch { Write-Host -ForegroundColor Red "Exception: $_" }
}

Once authentication to your selected vCenter is running, we create the rest of the form, which will build the rest of the information as selectable variables which eventually will pipe into our final clone command when we get to press the big button!

  • List the available VM’s to clone
  • List the Datastores to select as a target
  • Which Host to clone the VM to (show memory utilization of the host)
  • The VM Name
  • The DHCP settings
function ShowVMs() {

#Text Label for listbox of VMs returned
$listBoxVMsLabel = New-Object System.Windows.Forms.Label
$listBoxVMsLabel.Location = New-Object System.Drawing.Size(10,60)
$listBoxVMsLabel.Text = "Select VM"
$form.Controls.Add($listBoxVMsLabel)
#Listbox populated with VMs
$listBoxVMs.Location = New-Object System.Drawing.Size(10,85)
$listBoxVMs.Size = New-Object System.Drawing.Size(200,20)
$listBoxVMs.Height = 200
#Command used to pull VMs from vCenter filtered on vCenter tag, this can be any mixture of the Get-VM command

Get-VM -Tag "Clone-VM" | % {
$listBoxVMs.Items.Add($_.Name)
}
$form.Controls.Add($listBoxVMs)

#Datastore list box label
$listBoxDSLabel = New-Object System.Windows.Forms.Label
$listBoxDSLabel.Location = New-Object System.Drawing.Size(250,60)
$listBoxDSLabel.Text = "Select Datastore"
$form.Controls.Add($listBoxDSLabel)
#Datastore list box
$listBoxDS.Location = New-Object System.Drawing.Size(250,85)
$listBoxDS.Size = New-Object System.Drawing.Size(200,20)
$listBoxDS.Height = 200
#Powershell command used to retrieve the datastore details
Get-DataStore | % {

$freeGB = [string]::Format("{0:#,##0}", $_.FreeSpaceGB)
$listBoxDS.Items.Add("$($_.Name) | $freeGB GB Free")
}
$form.Controls.Add($listBoxDS)

#Host list box label
$listBoxHostLabel = New-Object System.Windows.Forms.Label
$listBoxHostLabel.Location = New-Object System.Drawing.Size(475,60)
$listBoxHostLabel.Text = "Select Target Host"
$form.Controls.Add($listBoxHostLabel)
#Host list box
$listBoxHost.Location = New-Object System.Drawing.Size(475,85)
$listBoxHost.Size = New-Object System.Drawing.Size(200,20)
$listBoxHost.Height = 200
#Powershell command to find hosts, and also parse the information so we can display the free memory on the host
Get-VMHost | % {

$memoryTotalMB = $_.MemoryTotalMB
$memoryUsageMB = $_.MemoryUsageMB
$memoryFreeMB = $_.MemoryTotalMB - $_.MemoryUsageMB
$memoryFreePerc = 0.0
try { $memoryFreePerc = $memoryFreeMB / $memoryTotalMB}
catch { }
$freeMB = [string]::Format("{0:#,##0.0%}", $memoryFreePerc)
$listBoxHost.Items.Add("$($_.Name) | $freeMB Free")

}
$form.Controls.Add($listBoxHost)

#New Virtual Machine name label
$newNameLabel = New-Object System.Windows.Forms.Label
$newNameLabel.Location = New-Object System.Drawing.Size(700, 60)
$newNameLabel.Text = "New VM Name"
$form.Controls.Add($newNameLabel)
#New virtual machine name text box
$newNameTextBox.Location = New-Object System.Drawing.Size(700, 85)
$newNameTextBox.Size = New-Object System.Drawing.Size(200, 20)
$form.Controls.Add($newNameTextBox)

#Clone button on the GUI form
$cloneButton = New-Object System.Windows.Forms.Button
$cloneButton.Location = New-Object System.Drawing.Size(700, 120)
$cloneButton.Size = New-Object System.Drawing.Size(60, 20)
$cloneButton.Text = "Clone"
#Confirming actions to be taken when button is clicked
$cloneButton.Add_Click({CloneVM $listBoxVMs $listBoxDS $listBoxHost $newNameTextBox $VMIPAddrTextBox $DHCPScopeBox})
$form.Controls.Add($cloneButton)

#Virtual Machine IP address on Production
$VMIPaddr = New-Object system.windows.Forms.Label
$VMIPaddr.Text = "Production IP address"
$VMIPaddr.AutoSize = $true
$VMIPaddr.Width = 25
$VMIPaddr.Height = 10
$VMIPaddr.location = new-object system.drawing.point(700,170)
$Form.controls.Add($VMIPaddr)
#Virtual Machine IP address text box
$VMIPAddrTextBox.Width = 110
$VMIPAddrTextBox.Height = 20
$VMIPAddrTextBox.location = new-object system.drawing.point(700,190)
$Form.controls.Add($VMIPAddrTextBox)

#Production DHCP Server label
$DHCPscopes = New-Object system.windows.Forms.Label
$DHCPscopes.Text = "Production DHCP Scopes"
$DHCPscopes.AutoSize = $true
$DHCPscopes.Width = 25
$DHCPscopes.Height = 10
$DHCPscopes.location = new-object system.drawing.point(700,215)
$Form.controls.Add($DHCPscopes)

#DHCP list box for production
$DHCPScopeBox.Text = "listBox"
$DHCPScopeBox.Width = 115
$DHCPScopeBox.Height = 75
$DHCPScopeBox.location = new-object system.drawing.point(700,240)
#Specifying a varible where the credentials to be used are stored securely
$MyCredentials=IMPORT-CLIXML C:\Scripts\SecureCredentials.xml
#Powershell command to remotely connect to a server in the DMZ
Invoke-command -computername 10.10.1.1 -Scriptblock {get-dhcpserverv4scope } -credential $MyCredentials | % {
$DHCPScopeBox.Items.Add("$($_.ScopeId)")}
$Form.controls.Add($DHCPScopeBox)

#Label for text box, for IP address to be removed from the DHCP scope
$DBVMIPaddr = New-Object system.windows.Forms.Label
$DBVMIPaddr.Text = "Prod DB IP address"
$DBVMIPaddr.AutoSize = $true
$DBVMIPaddr.Width = 25
$DBVMIPaddr.Height = 10
$DBVMIPaddr.location = new-object system.drawing.point(700,330)
$Form.controls.Add($DBVMIPaddr)

#Text box, for IP address that is to be removed from DHCP scope
$DBVMIPAddrTextBox.Width = 110
$DBVMIPAddrTextBox.Height = 20
$DBVMIPAddrTextBox.location = new-object system.drawing.point(700,350)
$Form.controls.Add($DBVMIPAddrTextBox)

#Label for text box, virtual Machine IP address on DR environment
$DRVMIPaddr = New-Object system.windows.Forms.Label
$DRVMIPaddr.Text = "DR IP address"
$DRVMIPaddr.AutoSize = $true
$DRVMIPaddr.Width = 25
$DRVMIPaddr.Height = 10
$DRVMIPaddr.location = new-object system.drawing.point(820,170)
$Form.controls.Add($DRVMIPaddr)

#Text box, for IP address that is to be reserved in DHCP
$DRVMIPAddrTextBox.Width = 110
$DRVMIPAddrTextBox.Height = 20
$DRVMIPAddrTextBox.location = new-object system.drawing.point(820,190)
$Form.controls.Add($DRVMIPAddrTextBox)

#DR DHCP Server label
$DRDHCPscopes = New-Object system.windows.Forms.Label
$DRDHCPscopes.Text = "DR DHCP Scopes"
$DRDHCPscopes.AutoSize = $true
$DRDHCPscopes.Width = 25
$DRDHCPscopes.Height = 10
$DRDHCPscopes.location = new-object system.drawing.point(820,215)
$Form.controls.Add($DRDHCPscopes)

#DR DHCP server scopes
$DRDHCPScopeBox.Text = "listBox"
$DRDHCPScopeBox.Width = 115
$DRDHCPScopeBox.Height = 75
$DRDHCPScopeBox.location = new-object system.drawing.point(820,240)
#Secure creds location to be used as variable for connection to server
$DRMyCredentials=IMPORT-CLIXML C:\Scripts\DRSecureCredentials.xml
#Command used to connect to DR DHCP server
Invoke-command -computername 10.50.1.1 -Scriptblock {get-dhcpserverv4scope } -credential $DRMyCredentials | % {
$DRDHCPScopeBox.Items.Add("$($_.ScopeId)")}
$Form.controls.Add($DRDHCPScopeBox)

#Label for text box, for IP address to be removed from the DR DHCP scope
$DRDBVMIPaddr = New-Object system.windows.Forms.Label
$DRDBVMIPaddr.Text = "DR DB IP address"
$DRDBVMIPaddr.AutoSize = $true
$DRDBVMIPaddr.Width = 25
$DRDBVMIPaddr.Height = 10
$DRDBVMIPaddr.location = new-object system.drawing.point(820,330)
$Form.controls.Add($DBVMIPaddr)

#Text box, for IP address that is to be removed from DR DHCP scope
$DRDBVMIPAddrTextBox.Width = 110
$DRDBVMIPAddrTextBox.Height = 20
$DRDBVMIPAddrTextBox.location = new-object system.drawing.point(820,350)
$Form.controls.Add($DRDBVMIPAddrTextBox)

}

We now build a function which will take the returned values presented from the earlier parts of the form and convert them to usable strings for use with the PowerCLI Clone VM command.

function CloneVM()
{  
$vmName = $listBoxVMs.Items[$listBoxVMs.SelectedIndex].ToString()   
$dsName = $listBoxDS.Items[$listBoxDS.SelectedIndex].ToString().Split("|")[0].ToString().Trim()
$hostName = $listBoxHost.Items[$listBoxHost.SelectedIndex].ToString().Split("|")[0].ToString().Trim()
$DHCPscope = $DHCPScopeBox.Items[$DHCPScopeBox.SelectedIndex].ToString()
$IPaddvm = $VMIPAddrTextBox.Text
$DRIPaddvm = $DRVMIPAddrTextBox.Text
$DBIPaddvm = $DBVMIPAddrTextBox.Text
DRDHCPscope = $DRDHCPScopeBox.Items[$DRDHCPScopeBox.SelectedIndex].ToString()
	$DRDBIPaddvm = $DRDBVMIPAddrTextBox.Text
$newName = $newNameTextBox.Text

#Basic error checking, unable to continue if the below items are empty
if ($vmName.Length -eq 0) { return }
if ($dsName.Length -eq 0) { return }
if ($hostName.Length -eq 0) { return }
if ($newName.Length -eq 0) { return }
if ($DHCPScope.Length -eq 0) { return }
if ($IPaddvm.Length -eq 0) { return }
if ($DRIPaddvm.Length -eq 0) {return}

#Capture current date for logging purposes
$Date = get-date

#Output message which reads back the selected variables and confirms the action taken upon hitting the "clone" button
$message = [string]::Format("Cloning {0} on DS {1}, host {2}, named {3}", $vmName, $dsName, $hostName, $newName)
$OutputTextBox = New-Object System.Windows.Forms.TextBox
$OutputTextBox.Location = New-Object System.Drawing.Size(10, 300)
$OutputTextBox.Size = New-Object System.Drawing.Size(600, 200)
$OutputTextBox.Text = $message
$form.Controls.Add($OutputTextBox)

### Code to clone VM from selected objects ###
$VM = New-VM -VM $vmName -Name $newName -VMHost $hostName -DiskStorageFormat Thick -Datastore $dsName -Notes "Clone created $(whoami) $Date"

### Configure CPUID in VMX File ###
#Setup a connection to the datastore where the VM is located
New-PSDrive -Location $ds -Name DS -PSProvider VimDatastore -Root "\"
#copies VMX file from vmware datastore location to temporary location on c: drive
copy-datastoreitem -item "DS:$vmname\$vmname.vmx" -destination "c:\scripts\$vmname.vmx" -Force
#Regex to place CPUID settings into vmx file, thank you Ian Morris for the continued assistance with this!
get-childitem "c:\scripts\$vmname.vmx" | % {
$content = get-content $_
[System.Text.StringBuilder] $newContent = New-Object -TypeName "System.Text.StringBuilder"
[int] $i = 0
$stream = [System.IO.StreamWriter] $_.FullName
foreach ($s in $content)
{
if ([Regex]::IsMatch($s, "cpuid\.") -eq $false)
{
$stream.WriteLine($s)
[void]$newContent.Append($s)
}
}
#These are the CPUID lines mask the VM into thinking it has a Intel Xeon 2.7 GHz chip from around 2007
$stream.WriteLine("cpuid.2.eax=`"00000101101100001011000100000001`"")
$stream.WriteLine("cpuid.2.ebx=`"00000000010101100101011111110000`"")
$stream.WriteLine("cpuid.2.ecx=`"00000000000000000000000000000000`"")
$stream.WriteLine("cpuid.2.edx=`"00101100101101000011000001001000`"")
$stream.Close()
} 
#Copy the modified VMX back to the datastore, by default any duplicate named will will be replaced
copy-datastoreitem -item "c:\scripts\$vmname.vmx" -destination "DS:$vmname\$vmname.vmx"
#close the PS Drive connection to the datastore
Remove-PSDrive -Name DS
#Delete the modified VMX file from the windows machine running the script
Remove-item "c:\scripts\$vmname.vmx"

### Setup DHCP Scopes ###
#Need to recall the secure stored credentials to connect to the DHCP Servers
$MyCredentials=IMPORT-CLIXML C:\Scripts\SecureCredentials.xml
$DRMyCredentials=IMPORT-CLIXML C:\Scripts\DRSecureCredentials.xml
#Grab the new VMs MAC address and convert the string to a format usable with the DHCP Powershell Module
$vmmac = get-vm $vmName | get-networkadapter
$vmmacalt = $vmmac.MacAddress -replace ":"

#Using invoke-command to connect to DHCP servers and setup the DHCP scope reservations and remove the IP address from the available range as a precaution
Invoke-command -computername 10.10.1.1 -Scriptblock {Add-DhcpServerv4Reservation -ScopeId $Using:DHCPScope -IPAddress $Using:IPAddvm -Name $Using:vmName -ClientId $Using:vmmacalt} -credential $MyCredentials
Invoke-command -computername 10.50.1.1 -Scriptblock {Add-DhcpServerv4Reservation -ScopeId $Using:DRDHCPScope -IPAddress $Using:DRIPAddvm -Name $Using:vmName -ClientId $Using:vmmacalt} -credential $DRMyCredentials
Invoke-command -computername 10.10.1.1 -Scriptblock {Add-DhcpServerv4ExclusionRange -ScopeID $Using:DHCPScope -StartRange $Using:DBIPAddvm -EndRange $Using:DBIPAddvm } -credential $MyCredentials
Invoke-command -computername 10.50.1.1 -Scriptblock {Add-DhcpServerv4ExclusionRange -ScopeID $Using:DRDHCPScope -StartRange $Using:DRDBIPAddvm -EndRange $Using:DRDBIPAddvm } -credential $DRMyCredentials
}

Final part of the script is essentially the “Go” commands when we build run the script, this calls the necessary modules and builds the form itself from the earlier configuration.

There is also a code snippet from this blog post, which hides the console once the form is built.

### Start form
#Add necessary modules and .Net to build GUI form
Import-Module VMware.PowerCLI
Add-Type -AssemblyName System.Windows.Forms

#Hide PowerShell Console > code snippet from http://blog.dbsnet.fr/hide-powershell-console-from-a-gui
Add-Type -Name Window -Namespace Console -MemberDefinition '
[DllImport("Kernel32.dll")]
public static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
'
$consolePtr = [Console.Window]::GetConsoleWindow()
[Console.Window]::ShowWindow($consolePtr, 0)

#Build form based on earlier configuration
$form = New-Object System.Windows.Forms.Form
$listBoxVMs = New-Object System.Windows.Forms.ListBox
$listBoxDS = New-Object System.Windows.Forms.ListBox
$listBoxHost = New-Object System.Windows.Forms.ListBox
$newNameTextBox = New-Object System.Windows.Forms.TextBox
$VMIPAddrTextBox = New-Object System.Windows.Forms.TextBox
$DHCPScopeBox  = New-Object System.Windows.Forms.ListBox
$DRVMIPAddrTextBox = New-Object System.Windows.Forms.TextBox
$DRDHCPScopeBox  = New-Object System.Windows.Forms.ListBox
$DBVMIPAddrTextBox = New-Object System.Windows.Forms.TextBox
$DRDBVMIPAddrTextBox = New-Object System.Windows.Forms.TextBox

PickVcenter

This code went through a good number of revisions, and could still be improved further and expanded even more, however it does achieve its main goal and was a great learning experience for me.

In terms of troubleshooting, I occasionally had some issues with connectivity to the DHCP servers, so I’d just run up PowerShell ISE, run the section of code with “write-host $variableselected” to see what was happening, below is a screenshot of my butchered code at a time I was doing this (I know it’s in visual code this time not ISE).

I’d like to thank my ex-colleague Ian Morris whom worked with me over many weeks on this script at the time of the project, without his help it wouldn’t have been possible.

Below is the full code
function PickVcenter()
{
#Text label for vCenter selection
$dropDownLabel = New-Object System.Windows.Forms.Label
$dropDownLabel.Location = New-Object System.Drawing.Size(10,12)
$dropDownLabel.Size = New-Object System.Drawing.Size(120, 20)
$dropDownLabel.Text = "Select vCenter Server"
$form.Controls.Add($dropDownLabel)

#Dropdown list for vCenter names
$dropDownList = New-Object System.Windows.Forms.ComboBox
$dropDownList.Location = New-Object System.Drawing.Size(150,10)
$dropDownList.Size = New-Object System.Drawing.Size(150,30)
$dropDownList.Items.Add("vCenter01.vEducate.co.uk")
$dropDownList.Items.Add("vCenter02.vEducate.co.uk")
$form.Controls.Add($dropDownList)

#Button to click off connect to selected vCenter
$button = New-Object System.Windows.Forms.Button
$button.Location = New-Object System.Drawing.Size(310, 10)
$button.Size = New-Object System.Drawing.Size(60, 20)
$button.Text = "Connect"
$button.Add_Click({ConnectVIServer})
$form.Controls.Add($button)

#Dialog title name of GUI Form, and size of form
$form.Text = "Clone Me Baby!"
$form.Size = New-Object System.Drawing.Size(1000,500)
$form.StartPosition = "CenterScreen"

$form.ShowDialog()

}

function ConnectVIServer() {
#takes the selection in the previous function to a string, to be used as a variable
$choice = $dropDownList.SelectedItem.ToString()
try {
$viServer = Connect-VIServer $choice
if ($viServer -eq $null) { return }
#Loads the ShowVMs function should a connection be made
ShowVMs
}
catch { Write-Host -ForegroundColor Red "Exception: $_" }
}

function ShowVMs() {

#Text Label for listbox of VMs returned
$listBoxVMsLabel = New-Object System.Windows.Forms.Label
$listBoxVMsLabel.Location = New-Object System.Drawing.Size(10,60)
$listBoxVMsLabel.Text = "Select VM"
$form.Controls.Add($listBoxVMsLabel)
#Listbox populated with VMs
$listBoxVMs.Location = New-Object System.Drawing.Size(10,85)
$listBoxVMs.Size = New-Object System.Drawing.Size(200,20)
$listBoxVMs.Height = 200
#Command used to pull VMs from vCenter filtered on vCenter tag, this can be any mixture of the Get-VM command

Get-VM -Tag "Clone-VM" | % {
$listBoxVMs.Items.Add($_.Name)
}
$form.Controls.Add($listBoxVMs)

#Datastore list box label
$listBoxDSLabel = New-Object System.Windows.Forms.Label
$listBoxDSLabel.Location = New-Object System.Drawing.Size(250,60)
$listBoxDSLabel.Text = "Select Datastore"
$form.Controls.Add($listBoxDSLabel)
#Datastore list box
$listBoxDS.Location = New-Object System.Drawing.Size(250,85)
$listBoxDS.Size = New-Object System.Drawing.Size(200,20)
$listBoxDS.Height = 200
#Powershell command used to retrieve the datastore details
Get-DataStore | % {

$freeGB = [string]::Format("{0:#,##0}", $_.FreeSpaceGB)
$listBoxDS.Items.Add("$($_.Name) | $freeGB GB Free")
}
$form.Controls.Add($listBoxDS)

#Host list box label
$listBoxHostLabel = New-Object System.Windows.Forms.Label
$listBoxHostLabel.Location = New-Object System.Drawing.Size(475,60)
$listBoxHostLabel.Text = "Select Target Host"
$form.Controls.Add($listBoxHostLabel)
#Host list box
$listBoxHost.Location = New-Object System.Drawing.Size(475,85)
$listBoxHost.Size = New-Object System.Drawing.Size(200,20)
$listBoxHost.Height = 200
#Powershell command to find hosts, and also parse the information so we can display the free memory on the host
Get-VMHost | % {

$memoryTotalMB = $_.MemoryTotalMB
$memoryUsageMB = $_.MemoryUsageMB
$memoryFreeMB = $_.MemoryTotalMB - $_.MemoryUsageMB
$memoryFreePerc = 0.0
try { $memoryFreePerc = $memoryFreeMB / $memoryTotalMB}
catch { }
$freeMB = [string]::Format("{0:#,##0.0%}", $memoryFreePerc)
$listBoxHost.Items.Add("$($_.Name) | $freeMB Free")

}
$form.Controls.Add($listBoxHost)

#New Virtual Machine name label
$newNameLabel = New-Object System.Windows.Forms.Label
$newNameLabel.Location = New-Object System.Drawing.Size(700, 60)
$newNameLabel.Text = "New VM Name"
$form.Controls.Add($newNameLabel)
#New virtual machine name text box
$newNameTextBox.Location = New-Object System.Drawing.Size(700, 85)
$newNameTextBox.Size = New-Object System.Drawing.Size(200, 20)
$form.Controls.Add($newNameTextBox)

#Clone button on the GUI form
$cloneButton = New-Object System.Windows.Forms.Button
$cloneButton.Location = New-Object System.Drawing.Size(700, 120)
$cloneButton.Size = New-Object System.Drawing.Size(60, 20)
$cloneButton.Text = "Clone"
#Confirming actions to be taken when button is clicked
$cloneButton.Add_Click({CloneVM $listBoxVMs $listBoxDS $listBoxHost $newNameTextBox $VMIPAddrTextBox $DHCPScopeBox})
$form.Controls.Add($cloneButton)

#Virtual Machine IP address on Production
$VMIPaddr = New-Object system.windows.Forms.Label
$VMIPaddr.Text = "Production IP address"
$VMIPaddr.AutoSize = $true
$VMIPaddr.Width = 25
$VMIPaddr.Height = 10
$VMIPaddr.location = new-object system.drawing.point(700,170)
$Form.controls.Add($VMIPaddr)
#Virtual Machine IP address text box
$VMIPAddrTextBox.Width = 110
$VMIPAddrTextBox.Height = 20
$VMIPAddrTextBox.location = new-object system.drawing.point(700,190)
$Form.controls.Add($VMIPAddrTextBox)

#Production DHCP Server label
$DHCPscopes = New-Object system.windows.Forms.Label
$DHCPscopes.Text = "Production DHCP Scopes"
$DHCPscopes.AutoSize = $true
$DHCPscopes.Width = 25
$DHCPscopes.Height = 10
$DHCPscopes.location = new-object system.drawing.point(700,215)
$Form.controls.Add($DHCPscopes)

#DHCP list box for production
$DHCPScopeBox.Text = "listBox"
$DHCPScopeBox.Width = 115
$DHCPScopeBox.Height = 75
$DHCPScopeBox.location = new-object system.drawing.point(700,240)
#Specifying a varible where the credentials to be used are stored securely
$MyCredentials=IMPORT-CLIXML C:\Scripts\SecureCredentials.xml
#Powershell command to remotely connect to a server in the DMZ
Invoke-command -computername 10.10.1.1 -Scriptblock {get-dhcpserverv4scope } -credential $MyCredentials | % {
$DHCPScopeBox.Items.Add("$($_.ScopeId)")}
$Form.controls.Add($DHCPScopeBox)

#Label for text box, for IP address to be removed from the DHCP scope
$DBVMIPaddr = New-Object system.windows.Forms.Label
$DBVMIPaddr.Text = "Prod DB IP address"
$DBVMIPaddr.AutoSize = $true
$DBVMIPaddr.Width = 25
$DBVMIPaddr.Height = 10
$DBVMIPaddr.location = new-object system.drawing.point(700,330)
$Form.controls.Add($DBVMIPaddr)

#Text box, for IP address that is to be removed from DHCP scope
$DBVMIPAddrTextBox.Width = 110
$DBVMIPAddrTextBox.Height = 20
$DBVMIPAddrTextBox.location = new-object system.drawing.point(700,350)
$Form.controls.Add($DBVMIPAddrTextBox)

#Label for text box, virtual Machine IP address on DR environment
$DRVMIPaddr = New-Object system.windows.Forms.Label
$DRVMIPaddr.Text = "DR IP address"
$DRVMIPaddr.AutoSize = $true
$DRVMIPaddr.Width = 25
$DRVMIPaddr.Height = 10
$DRVMIPaddr.location = new-object system.drawing.point(820,170)
$Form.controls.Add($DRVMIPaddr)

#Text box, for IP address that is to be reserved in DHCP
$DRVMIPAddrTextBox.Width = 110
$DRVMIPAddrTextBox.Height = 20
$DRVMIPAddrTextBox.location = new-object system.drawing.point(820,190)
$Form.controls.Add($DRVMIPAddrTextBox)

#DR DHCP Server label
$DRDHCPscopes = New-Object system.windows.Forms.Label
$DRDHCPscopes.Text = "DR DHCP Scopes"
$DRDHCPscopes.AutoSize = $true
$DRDHCPscopes.Width = 25
$DRDHCPscopes.Height = 10
$DRDHCPscopes.location = new-object system.drawing.point(820,215)
$Form.controls.Add($DRDHCPscopes)

#DR DHCP server scopes
$DRDHCPScopeBox.Text = "listBox"
$DRDHCPScopeBox.Width = 115
$DRDHCPScopeBox.Height = 75
$DRDHCPScopeBox.location = new-object system.drawing.point(820,240)
#Secure creds location to be used as variable for connection to server
$DRMyCredentials=IMPORT-CLIXML C:\Scripts\DRSecureCredentials.xml
#Command used to connect to DR DHCP server
Invoke-command -computername 10.50.1.1 -Scriptblock {get-dhcpserverv4scope } -credential $DRMyCredentials | % {
$DRDHCPScopeBox.Items.Add("$($_.ScopeId)")}
$Form.controls.Add($DRDHCPScopeBox)

#Label for text box, for IP address to be removed from the DR DHCP scope
$DRDBVMIPaddr = New-Object system.windows.Forms.Label
$DRDBVMIPaddr.Text = "DR DB IP address"
$DRDBVMIPaddr.AutoSize = $true
$DRDBVMIPaddr.Width = 25
$DRDBVMIPaddr.Height = 10
$DRDBVMIPaddr.location = new-object system.drawing.point(820,330)
$Form.controls.Add($DBVMIPaddr)

#Text box, for IP address that is to be removed from DR DHCP scope
$DRDBVMIPAddrTextBox.Width = 110
$DRDBVMIPAddrTextBox.Height = 20
$DRDBVMIPAddrTextBox.location = new-object system.drawing.point(820,350)
$Form.controls.Add($DRDBVMIPAddrTextBox)

}


function CloneVM()
{  
$vmName = $listBoxVMs.Items[$listBoxVMs.SelectedIndex].ToString()   
$dsName = $listBoxDS.Items[$listBoxDS.SelectedIndex].ToString().Split("|")[0].ToString().Trim()
$hostName = $listBoxHost.Items[$listBoxHost.SelectedIndex].ToString().Split("|")[0].ToString().Trim()
$DHCPscope = $DHCPScopeBox.Items[$DHCPScopeBox.SelectedIndex].ToString()
$IPaddvm = $VMIPAddrTextBox.Text
$DRIPaddvm = $DRVMIPAddrTextBox.Text
$DBIPaddvm = $DBVMIPAddrTextBox.Text
DRDHCPscope = $DRDHCPScopeBox.Items[$DRDHCPScopeBox.SelectedIndex].ToString()
	$DRDBIPaddvm = $DRDBVMIPAddrTextBox.Text
$newName = $newNameTextBox.Text

#Basic error checking, unable to continue if the below items are empty
if ($vmName.Length -eq 0) { return }
if ($dsName.Length -eq 0) { return }
if ($hostName.Length -eq 0) { return }
if ($newName.Length -eq 0) { return }
if ($DHCPScope.Length -eq 0) { return }
if ($IPaddvm.Length -eq 0) { return }
if ($DRIPaddvm.Length -eq 0) {return}

#Capture current date for logging purposes
$Date = get-date

#Output message which reads back the selected variables and confirms the action taken upon hitting the "clone" button
$message = [string]::Format("Cloning {0} on DS {1}, host {2}, named {3}", $vmName, $dsName, $hostName, $newName)
$OutputTextBox = New-Object System.Windows.Forms.TextBox
$OutputTextBox.Location = New-Object System.Drawing.Size(10, 300)
$OutputTextBox.Size = New-Object System.Drawing.Size(600, 200)
$OutputTextBox.Text = $message
$form.Controls.Add($OutputTextBox)

### Code to clone VM from selected objects ###
$VM = New-VM -VM $vmName -Name $newName -VMHost $hostName -DiskStorageFormat Thick -Datastore $dsName -Notes "Clone created $(whoami) $Date"

### Configure CPUID in VMX File ###
#Setup a connection to the datastore where the VM is located
New-PSDrive -Location $ds -Name DS -PSProvider VimDatastore -Root "\"
#copies VMX file from vmware datastore location to temporary location on c: drive
copy-datastoreitem -item "DS:$vmname\$vmname.vmx" -destination "c:\scripts\$vmname.vmx" -Force
#Regex to place CPUID settings into vmx file, thank you Ian Morris for the continued assistance with this!
get-childitem "c:\scripts\$vmname.vmx" | % {
$content = get-content $_
[System.Text.StringBuilder] $newContent = New-Object -TypeName "System.Text.StringBuilder"
[int] $i = 0
$stream = [System.IO.StreamWriter] $_.FullName
foreach ($s in $content)
{
if ([Regex]::IsMatch($s, "cpuid\.") -eq $false)
{
$stream.WriteLine($s)
[void]$newContent.Append($s)
}
}
#These are the CPUID lines mask the VM into thinking it has a Intel Xeon 2.7 GHz chip from around 2007
$stream.WriteLine("cpuid.2.eax=`"00000101101100001011000100000001`"")
$stream.WriteLine("cpuid.2.ebx=`"00000000010101100101011111110000`"")
$stream.WriteLine("cpuid.2.ecx=`"00000000000000000000000000000000`"")
$stream.WriteLine("cpuid.2.edx=`"00101100101101000011000001001000`"")
$stream.Close()
} 
#Copy the modified VMX back to the datastore, by default any duplicate named will will be replaced
copy-datastoreitem -item "c:\scripts\$vmname.vmx" -destination "DS:$vmname\$vmname.vmx"
#close the PS Drive connection to the datastore
Remove-PSDrive -Name DS
#Delete the modified VMX file from the windows machine running the script
Remove-item "c:\scripts\$vmname.vmx"

### Setup DHCP Scopes ###
#Need to recall the secure stored credentials to connect to the DHCP Servers
$MyCredentials=IMPORT-CLIXML C:\Scripts\SecureCredentials.xml
$DRMyCredentials=IMPORT-CLIXML C:\Scripts\DRSecureCredentials.xml
#Grab the new VMs MAC address and convert the string to a format usable with the DHCP Powershell Module
$vmmac = get-vm $vmName | get-networkadapter
$vmmacalt = $vmmac.MacAddress -replace ":"

#Using invoke-command to connect to DHCP servers and setup the DHCP scope reservations and remove the IP address from the available range as a precaution
Invoke-command -computername 10.10.1.1 -Scriptblock {Add-DhcpServerv4Reservation -ScopeId $Using:DHCPScope -IPAddress $Using:IPAddvm -Name $Using:vmName -ClientId $Using:vmmacalt} -credential $MyCredentials
Invoke-command -computername 10.50.1.1 -Scriptblock {Add-DhcpServerv4Reservation -ScopeId $Using:DRDHCPScope -IPAddress $Using:DRIPAddvm -Name $Using:vmName -ClientId $Using:vmmacalt} -credential $DRMyCredentials
Invoke-command -computername 10.10.1.1 -Scriptblock {Add-DhcpServerv4ExclusionRange -ScopeID $Using:DHCPScope -StartRange $Using:DBIPAddvm -EndRange $Using:DBIPAddvm } -credential $MyCredentials
Invoke-command -computername 10.50.1.1 -Scriptblock {Add-DhcpServerv4ExclusionRange -ScopeID $Using:DRDHCPScope -StartRange $Using:DRDBIPAddvm -EndRange $Using:DRDBIPAddvm } -credential $DRMyCredentials
}

### Start form
#Add necessary modules and .Net to build GUI form
Import-Module VMware.PowerCLI
Add-Type -AssemblyName System.Windows.Forms

#Hide PowerShell Console > code snippet from http://blog.dbsnet.fr/hide-powershell-console-from-a-gui
Add-Type -Name Window -Namespace Console -MemberDefinition '
[DllImport("Kernel32.dll")]
public static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
'
$consolePtr = [Console.Window]::GetConsoleWindow()
[Console.Window]::ShowWindow($consolePtr, 0)

#Build form based on earlier configuration

PickVcenter

 

Using vROPs to track adoption – Such as VMware Tools upgrades

I had an interesting question from a customer recently;

Can we track the adoption/upgrades of VMware tools to the latest version in a vROPs dashboard

At first, I thought sure this is easy. But then looking at the various different default options, nothing would give me a graph which shows the increasing uptake or upgrades of VMware Tools to the last version.

I consulted internally, and between myself and my customer we came up with the following solution. Which was quite simple once we thought it out.

Solution

  1. Create a custom group based on the information you want to capture

In the below example, I’ve done this to target VMs with VMware Tools 10.3.10, you could leave it as “10.3” to target any 10.3.X release.

The property we are interested in, for this example is “Summary > Guest Operating System >  Tools Version”

2. Now we need to create a dashboard, and all we are simply going to do, is count the members of the customer group over time using the population metric.

  • Create your dashboard, and select “Metric Chart” as your visual for the dashboard
  • Click to Edit this visual (you will not see any graph yet)

  • Under Input Data, select “metrics” (should be the default selection)
  • Click the green + to add our objects

  • Filter for your custom group and select
  • Then filter your metric options for “population”
  • Save this selection

  • You should now have the below configuration
  • I’ve also highlighted where you can change the name of the visual as its displayed in the dashboard.
  • Save this configuration

And now you will have a basic dashboard as below.

This essentially shows us the adoption count of VMs using VMware tools 10.3.10.

Very simple, and we could use this for other metrics as well, lets say I want to track VMs moving into a new datastore, I could just pick the custom group filter as “Properties > Summary > Configuration > Datastore” then state contains “datastore name”

As VMs move into this datastore, the count will increase.

Finally I’ll sign off by showing you my customers dashboard that they happily shared a screenshot of.

You can download a copy of the dashboard created for this blog post at the VMware {code} website.

 

Regards

Dean

Interview – Ian Sanderson talks community and career growth

The last interview I wrote up was back in 2017, although I’ve made efforts to kick off a continuation of this series, I stalled. I recently went over some of the past interviews and its amazing how in 18 month or so, people’s careers and focuses have changed, never mind the IT industry.

So kicking off the first interview of 2019, I reached out to my friend Ian Sanderson. Ian has 15 years of IT experience under his belt, taking the usual route into the IT industry, “I cut my teeth in the virtualisation world with Hyper-V in 2008, but my focus has been VMware since 2010”, he tells me as we kick off discussing “Ian in his own words.”

Ian and myself became friends and comrades with similar interests due to our activity in the IT community, interactions on twitter soon turned into bumping into one another at events, and catching up over coffee, and late night drinks at vendor community programs.

I ask Ian to define what the IT community means to him, “Community to me is like having an extended family of people who you can bounce ideas off, or call upon for help with other things” he says, “It is not a one way street though. I try my best to give back to people in any way I can help out. It’s really about comradery & helping each other achieve their end goals.”

“The wealth of collective knowledge in the vCommunity honestly amazes me. There is always someone, somewhere who has the solution to a problem you may have.”

So where did it all start for Ian? “My first real interactions with the community kicked off when I became a Veeam Vanguard in 2016. Prior to that I had the odd interaction on twitter and an outdated blog but nothing really significant. Being virtually air dropped into a group of like-minded people who love Veeam really sparked my passion for getting more involved in community events.”

I’m not shocked to find this answer pretty much echos similar answers to others in the IT community. Small steps into twitter; invites to slack groups; and a sense of needing to give back to the community we have all taken so much from. (We’ve all googled for an answer, and ended up at someones personal blog post, finding they have fixed the same issue).

Career progression

It’s no secret that a lot of ambitious IT folk have gone on to do very well in their perspective areas boosted by their work/activity in the IT community. There’s no secret group or handshakes, just purely hard work, a love of technology, and mostly a friendly atmosphere, as Ian equates earlier “Its really about comradery and helping each other achieve their end goals.” Continue reading Interview – Ian Sanderson talks community and career growth