Using plink to modify ESXi host configuration files via SSH from a PowerCLI script

I am a big advocate of automation and saving time with a good script. Whenever I can find a task that is fairly lengthy, and is likely to be repeated in future, I always consider scripting it. There are many way to configure an ESXi host when it comes to writing build or automation scripts. In fact, I often feel we are quite spoilt for choice. Here are just some of the tools we have available to use:

  • PowerCLI
  • esxcli
  • vMA
  • vCLI

I was working on a build configuration script the other day using PowerCLI and found the need to edit some configuration files on the hosts. I wanted to edit the configuration file /etc/vmware/config during the execution of a single PowerCLI script without needing to stop the script or have an additional step to do myself. The following is what I came up with to achieve this:

  • Configure host as normal using PowerCLI
  • Use PowerCLI to start SSH service on host
  • execute plink script to connect to host, run command via SSH, then disconnect
  • Use PowerCLI to stop SSH service on host
  • Continue with rest of PowerCLI script

 

Plink is a command line connection tool – essentially a command line version of PuTTy. You can call it from dos prompt and issue it with a single, (or list) of commands to run once connected to a specified host. You can download Plink over here.

 

So without further ado, let’s take a look at the script as I described above.

# At start of our script we ask for the host's IP or name (this could be automated if you like)
$hostIP = Read-Host "Enter ESX host IP/dns name: "
$vmhost = Get-VMHost $hostIP

# Start the SSH service
$sshService = Get-VmHostService -VMHost $vmhost | Where { $_.Key -eq “TSM-SSH”}
Start-VMHostService -HostService $sshService -Confirm:$false

# Use SSH / plink to configure host with some additional script
cmd /c "plink.exe -ssh -pw HOSTROOTPASSWORD -noagent -m commands.txt root@$hostIP"

# Stop SSH service
Stop-VMHostService -HostService $sshService -Confirm:$false

 

As you can see, we start off by asking for the host IP or name, this is the only bit of manual input, but even this could be automated. The script then finds the SSH service on the host, and starts it. After this, the script calls the plink.exe file via cmd /c and connects using the root user@ the host’s IP as we entered at the beginning of the script over SSH. Plink is pointed to a commands.txt file (previously placed in the script execution folder), which contains the actual lines of bash script to be executed on the ESXi host via SSH.

Here is the content of the commands.txt file that I refer plink.exe to use (as an example, this bit of script enables copy/paste operations on all VMs running on this host in the guest OS’ console, as per VMware KB 1026437), but could contain any other commands you wish to execute on the ESXi host over SSH.

echo 'isolation.tools.copy.disable="FALSE"' >> /etc/vmware/config
echo 'isolation.tools.paste.disable="FALSE"' >> /etc/vmware/config

 

* Note two very useful techniques show by Alan in the comments section below, showing how to automatically download plink.exe if it is not available when the script is run, and also how to accept the SSH fingerprint key request by piping Y to plink.exe via the script – check out Alan’s blog post here for more detail.

Issue using the Count method to count objects with PowerShell 2.0

 

I came across a small issue with a little helper script I wrote to count vSphere objects using PowerCLI this morning. It’s been a couple of a weeks since I last did a blog post – things have been very busy, so I have not been able to commit much time over the last few weeks to blogging. As such, I thought I would do a quick post around this small issue I came across earlier. It has more than likely been covered off elsewhere, but will be a good reference point to come back to if I ever forget!

 

So to the issue I saw. Essentially, if an object count is 1 or less, then the object is returned as the object type itself. For example, where only one Distributed Virtual Switch exists in a vSphere environment, and we use the cmdlet, Get-VirtualSwitch -Distributed, a single object is returned of BaseType “VMware.VimAutomation.ViCore.Impl.V1.VIObjectImpl“.

 

However, if we had more than one dvSwitch, then we would get a BaseType of “System.Array” returned.

 

We are able to use the Count() method on an array with PowerShell version 2.0, but are not able to use the Count() method on a single object. The work around I found here (when using PowerShell 2.0) is to cast the object type specifically as an array.

So in the case of our dvSwitch example above, originally we would have done:

@dvSwitchCount = (Get-VirtualSwitch -Distributed).Count

 

To cast this as an array, and therefore having an accurate count of the objects, whether there are no members, one member, or more, we would use:

@dvSwitchCount = @(Get-VirtualSwitch -Distributed).Count

 

Note the addition of the “@” sign – used to cast the variable as an array.

Jonathan Medd also kindly pointed out that this is fixed with PowerShell 3.0 – have a read of the new features to see the addition that allows .Count to be used on any type of object over here.

PowerCLI – script to deploy multiple random VMs

There are probably tons of scripts out there to deploy VMs in a vSphere environment, but I was in the mood for scripting this evening and decided to create my own PowerCLI script to automatically deploy multiple random VMs to my home lab. The point was to create random “content” for something exciting I have been working on 🙂

 

 

[download id=”19″]

 

$i = 1
[int]$NumberToDeploy = 10 # Number of VMs to deploy
[string]$NamingConvention = "homelab-VM-" # Prefix for VM names
[string]$folderLoc = "Discovered virtual machine" # Name of VM folder to deploy into

while ($i -le $NumberToDeploy) {
	$NumCPUs = (Get-Random -Minimum 1 -Maximum 3) #NUM CPU of either 1 or 2.
	$MemoryMB = ("16","32","64") | Get-Random # Get a random VM Memory size from this list (MB)
	$DiskSize = ("256","512","768" ) | Get-Random # Get a random disk size from this list (MB)
	$targetDatastore = Get-Datastore | Where {($_.ExtensionData.Summary.MultipleHostAccess -eq "True") -and ($_.FreeSpaceMB -gt "10240")} | Get-Random
	$targetVMhost = Get-VMHost | Where { $_.ConnectionState -eq "Connected" } | Get-Random # Selects a random host which is in "connected State"
	$targetNetwork = ("VM Network","VM Distributed Portgroup") | Get-Random
	$GuestType = (	"darwinGuest","dosGuest","freebsd64Guest","freebsdGuest","mandrake64Guest",
					"mandrakeGuest","other24xLinux64Guest","other24xLinuxGuest","other26xLinux64Guest",
					"other26xLinuxGuest","otherGuest","otherGuest64","otherLinux64Guest","otherLinuxGuest",
					"rhel5Guest","suse64Guest","suseGuest","ubuntu64Guest","ubuntuGuest",
					"win2000AdvServGuest","win2000ProGuest","win2000ServGuest","win31Guest","win95Guest",
					"win98Guest","winLonghorn64Guest","winLonghornGuest","winNetBusinessGuest",
					"winNetDatacenter64Guest","winNetDatacenterGuest","winNetEnterprise64Guest",
					"winNetEnterpriseGuest","winNetStandard64Guest","winNetStandardGuest","winNetWebGuest",
					"winNTGuest","winVista64Guest","winVistaGuest","winXPPro64Guest",
					"winXPProGuest") | Get-Random
	$VMName = $NamingConvention + $i

	if ((Get-VM $VMName -ErrorAction SilentlyContinue).Name -eq $VMName) {
		Write-Host "$VMName already exists, skipping creation of this VM!" -ForegroundColor Yellow
		}
	else {	
		Write-Host "Deploying $VMName to $folderLoc ..." -ForegroundColor Green
		#Create our VM
		New-VM -Name $VMName -ResourcePool $targetVMhost -Datastore $targetDatastore -NumCPU $NumCPUs -MemoryMB $MemoryMB -DiskMB $DiskSize `
		-NetworkName $targetNetwork -Floppy -CD -DiskStorageFormat Thin -GuestID $GuestType -Location $folderLoc | Out-Null
		}
	$i++
}

 

This script will automatically deploy a variable number of Virtual Machines, based on a certain naming convention you specify. It’ll also randomly choose VM settings. Here is what it does:

 

  • Deploy any number of VMs by changing the total number of VMs to deploy (specify in script as $NumberToDeploy)
  • Deploys each VM to a random ESXi host which is in a connected state
  • Deploys each VM to a random shared datastore (i.e. a datastore with multiple hosts connected)
  • Sets a random vCPU count to each VM (specify options in the script as $NumCPUs)
  • Sets a random Memory size for each VM (specify options in the script as $MemoryMB)
  • Create a virtual disk as thin provisioned on each VM with a random disk size (specify disk size options in the script as $DiskSize)
  • Adds each VM to a random VM network (specify VM network options in the script as $targetNetwork)
  • Specifies a random GuestOS type for each VM deployed
  • Creates each VM in a specified VM & Templates folder (specify in script as $folderLoc)

 

All options are already set to defaults in the script as is stands, but don’t forget to change important options unique to your environment like the $folderLoc (folder to deploy VMs into), and the list of VM networks ($targetNetwork). Other items are automatically determined (datastore and host to deploy to for example).

Also note that some of the built-in guestOS types might not be supported on some ESXi hosts. In these cases, the script just skips creating that VM and moves onto the next. You may see a red error message for the failed VM in these cases. For a full list of GuestID types, check out this page.

PowerCLI – creating port groups and specifications to modify port groups

I wanted to quickly create some standard VM port groups across a particular vSwitch for all hosts in my lab / testing environment at work. Since I was using Standard vSwitches and not a dvSwitch, I didn’t feel like using the GUI to create these on every individual ESXi host. In addition to creating the port group on each vSwitch, I also wanted to change the security policy on each for Promiscuous mode to “Accept”. The reason for this being that this port group is going to be used to run virtual nested ESXi hosts, and this is required to allow nested VMs to communicate on the network.

 

So the obvious solution here for me was to create a quick PowerCLI script to create these port groups on all hosts and set the security option for each too. Here is the script:

 

$vSwitch = "vSwitch0"
$portgrpname = "vInception Portgroup"
$AllConnectedHosts = Get-VMHost | Where {$_.ConnectionState -eq "Connected"}

foreach ($esxihost in $AllConnectedHosts) {
	$currentvSwitch = $esxihost | Get-VirtualSwitch | Where {$_.Name -eq $vSwitch}
	New-VirtualPortGroup -Name $portgrpname -VirtualSwitch $currentvSwitch -Confirm:$false
	$currentesxihost = Get-VMHost $esxihost | Get-View
	$netsys = Get-View $currentesxihost.configmanager.networksystem
	$portgroupspec = New-Object VMWare.Vim.HostPortGroupSpec
	$portgroupspec.vswitchname = $vSwitch
	$portgroupspec.Name = $portgrpname
	$portgroupspec.policy = New-object vmware.vim.HostNetworkPolicy
	$portgroupspec.policy.Security = New-object vmware.vim.HostNetworkSecurityPolicy
	$portgroupspec.policy.Security.AllowPromiscuous = $true
	$netsys.UpdatePortGroup($portgrpname,$PortGroupSpec)
	
}

 
Keep in mind that this script will create the port group on “vSwitch0” – change this if your vSwitch that is hosting this port group on each host is named differently. It will obviously rely on this vSwitch existing to work. You can also modify the $portgrpname to your own choice of course.

Lastly, you can easily modify this script to change other Security options for the new port group, as the port group specification has already been created in this script. Just use the $portgroupspec.policy.Security object to add other specifications.

Enjoy!
 

PowerCLI 5.1 – new cmdlets and changes between the beta and final releases

I was wondering what new cmdlets had been added in PowerCLI 5.1 as opposed to PowerCLI version 5.0.1. I also wanted to see if there were any changes between the beta release of vSphere 5.1 and the final release which was made public yesterday. The answer is yes, there are indeed changes between all three versions! Here are the cmdlet counts for each version:

 

[table tablesorter=”1″ file=”http://www.shogan.co.uk/wp-content/uploads/powercli-version-cmdlet-counts.csv “][/table]

 

To see what the differences were, I ran the following on each version of PowerCLI (5.0.1, 5.1 beta, and 5.1 final).

 

First of all to get the number of cmdlets and see if there were any changes at a quick glance, I ran a simple count against the Get-VICommand cmdlet:

(Get-VICommand).Count

Seeing differences between each version, I then decided to get a full list of cmdlets for each version, and then run a diff against these.

Get-VICommand | Export-CSV C:\cmdletsforversionX.csv

 

I then opened each CSV file, grabbed the full list of cmdlets from the “Name” column, and ran these against each other using on online difference checking site. Here are the results:

 

vSphere PowerCLI 5.1 beta had an additional 4 cmdlets over PowerCLI 5.0.1, with 1 having been removed.

 

PowerCLI 5.1 beta changes

Removed New
Get-EsxSoftwareChannel Get-DeployOption
Get-EsxSoftwareDepot
Remove-EsxImageProfile
Set-DeployOption

 

vSphere PowerCLI 5.1 (final/public release) had an additional 47 cmdlets over PowerCLI 5.1 beta, with none having been removed. These mostly seem to be related to the vCloud Suite as far as I can tell.

 PowerCLI 5.1 beta to 5.1 public release changes

[table tablesorter=”1″ file=”http://www.shogan.co.uk/wp-content/uploads/powercli-5-1-public-cmdlet-additions.csv”][/table]

 

It is worth noting that in each case I had a full installation of PowerCLI – i.e. had selected to install PowerCLI normal and Cloud cmdlets during installation.

So it looks like I’ll need to spend some time getting acquainted with the new cmdlets. If you are curious as to what each does, don’t forget the built in help – using “Get-Help Cmdletname” and the use of the -examples switch.