Modifying guest network interface with the Invoke-VMScript cmdlet


images

Set-VMGuestNetworkInterface

With the news that the ‘Set-VMGuestNetworkInterface’ cmdlet is to be deprecated in the next release of PowerCLI (http://blogs.vmware.com/PowerCLI/2014/11/announcement-future-cmdlet-deprecation.html), the question is how do I now modify the guest network interface?

The ability to perform this action can now be moved to the Invoke-VMScript cmdlet, which is a pretty cool way to invoke a script in the guest operating system of the virtual machine when VMware Tools is installed.

As per the description of the cmdlet you will need to satisfy the below:

To run Invoke-VMScript, the user must have read access to the folder containing the virtual machine and a VirtualMachine.Interaction.Console Interaction privilege. The virtual machines must be powered on and have VMware Tools installed. Network connectivity to the ESX system hosting the virtual machine on port 902 must be present to authenticate with the host or the guest OS, one of the HostUser/HostPassword (GuestUser/GuestPassword) pair and HostCredential (GuestCredential) parameters must be provided. The guest account you use to authenticate with the guest operating system must have administrator’s privileges.

To run this cmdlet against vCenter Server/ESX/ESXi versions earlier than 5.0, you need to meet the following requirements:

*You must run the cmdlet on the 32-bit version of Windows PowerShell.
*You must have access to the ESX that hosts the virtual machine over TCP port 902.
*For vCenter Server/ESX/ESXi versions earlier than 4.1, you need VirtualMachine.Interact.ConsoleInteract privilege.  For vCenterServer/ESX/ESXi 4.1 and later, you need VirtualMachine.Interact.GuestControl privilege.

As mentioned VMware Tools is required to be installed and the virtual machine to be powered on, in my use case modifying the guest network interface is a one of a number of script blocks in deployment script to customise a virtual machine and therefore in order to confirm that VMware Tools is running following a virtual machine power on event I run a statement in a script block until the Guest.ToolsStatus is retrieved as ‘toolsOK’ from the Get-View cmdlet, where the MoRef value for the virtual machine is retrieved from an associated array.

Do     
    {
    Start-Sleep -Seconds 10
    $GuestToolsStatus = (Get-View $VM.Id -Property Guest).Guest.ToolsStatus
    } 
Until ($GuestToolsStatus -eq toolsOk)

Now we want to modify the guest network interface, with the following requirements.

  • Configure IPv4 address
  • Configure subnet mask
  • Configure default gateway
  • Configure DNS servers

So, in order to modify the guest network interface I will be replacing the soon to be deprecated ‘Set-VMGuestNetworkInterface’ cmdlet with the ‘New-NetIPAddress’ and ‘Set-DnsClientServerAddress’ cmdlets in Windows Powershell.

In order to run the script inside the guest operating system and in this example a Microsoft Windows Server 2012 operating, I will create a ScriptText variable containing the syntax of the two cmdlets I wish to invoke.  Please not the ‘;’ separator to invoke multiple lines of commands in the text of the script.

$ScriptText = "New-NetIPAddress –InterfaceAlias Ethernet0 –IPAddress 10.0.0.5 -AddressFamily IPv4 –PrefixLength 24 -DefaultGateway 10.0.0.254;
Set-DnsClientServerAddress -InterfaceAlias Ethernet0 -ServerAddresses 10.0.0.1,10.0.0.2"

In the above example, we are modifying the network interface ‘Ethernet0’ to use the IPv4 address 10.0.0.5/24 with a default gateway 10.0.0.254 and the DNS servers 10.0.0.1 and 10.0.0.2.

Now, let’s actually run the command in the guest operating operating system, using the Invoke-VMScript cmdlet, where guest credentials are retrieved from paramaters supplied for my script.

Invoke-VMScript -VM $VM -ScriptText $ScriptText -GuestUser $GuestUser -GuestPassword $GuestPassword

As you can see this is a powerful way to invoke scripts inside the guest operating system. In the default state the script type invoked on a Microsoft Windows operating system is Powershell. However, you may specify the type to be Bat or for a Linux operating system Bash.

The script above, can be downloaded from https://github.com/dean1609/scripts/commit/1bd96807a899a3f8861ca676354eb129545fa1c9.

Advertisements

Update: Patch Management for Guest VMS with Windows Update Server and WUInstall on vSphere

I previously described steps to automate approved updates from a WSUS server with WUInstall for virtual machines managed by VMware vCenter Server (https://deangrant.wordpress.com/2014/01/24/patch-management-for-guest-vms-with-windows-update-server-and-wuinstall-on-vsphere/).

I have recently updated this process to filter virtual machines objects by tagging objects in the new functionality in the vSphere Web Client. Also, the previous process had no concept of  task parallelism, which has now also been addressed.

The script requires two mandatory parameters to be specified, we need to specify the vCenter server to which we require to establish a connection and the the category tag that has been assigned to the virtual machine to specify the infrastructure environment.

Param ([Parameter(Mandatory=$true)][string] $vCenter, [Parameter(Mandatory=$true)][string] $Environment)

Now we will register the ‘VMware.VimAutomation.Core’ snap-in and establish the connection to the vCenter server.

If (-not (Get-PSSnapin VMware.VimAutomation.Core -ErrorAction SilentlyContinue)) 
    {
    Add-PSSnapin VMware.VimAutomation.Core | Out-Null 
    }
Connect-VIServer $vCenter

Once connected, we will be required to store the current session details of the established connection to pass as a variable later in the script block to create a snapshot for the virtual machine prior to invoking the WUInstall executable.

$Session = ($global:DefaultVIServer).SessionId 

In order to filter virtual machines, we will firstly build a collection of virtual machine objects by invoking the Get-View cmdlet to retrieve only the name property values.

$VMs = Get-View -ViewType VirtualMachine -Property Name

For the collection of virtual machine objects we will now perform an operation on each object to filter the virtual machine collection to retreive only virtual machines to which the Tag Assignment ‘Environment’ is equal to the mandatory parameter specified and ‘Updates Enabled’ is equal to ‘Yes’.

ForEach ($VM in $VMS) 
    { 
    If (((Get-TagAssignment -Entity $VM.Name -Category Environment).Tag).Name -eq $Environment -and ((Get-TagAssignment -Entity $VM.Name -Category "Updates Enabled").Tag).Name -eq "Yes")
        { 

Before we invoke the automated process on each virtual machine the Get-Date cmdlet will retrieve the DateTime object to the format ‘yyyy-MM-dd’, to append to the log file generated by the WUInstall executable.

$Date = (get-date).toString('dd-MM-yyyy-HHmm')

The WUInstall executable will be invoked remotely by leveraging the PSExec utility and therefore we will specify the arguments to the executable in a variable to passed in the script block. The WUInstall will be located on the virtual machine in the location C:\Program Files\WUInstall and will create a log file in the directory location.

$WUInstall = "\\$VM -s 'C:\Program Files\WUInstall\WUInstall.exe' /install /autoaccepteula /reboot_if_needed /logfile 'C:\Program Files\WUInstall\wuinstall_$Date.log'"

In order to leverage task parallelism the script block create will be invoked to run each job as a background job on the local computer using the Start-Job cmdlet. For each background job we will require to establish a connection to the vCenter server using the session ID rather than reconnecting.

One of the requirements it to create a virtual machine snapshot prior to the installation of the approved updates, where the name will be ‘Windows Update on yyyy-MM-dd’ and then we will invoke the WUInstall on the virtual machine using the PSExec utility.

    $ScriptBlock = [scriptblock]::create("C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe -Command {
                Add-PSSnapin VMware.VimAutomation.Core | Out-Null 
                Connect-VIServer $vCenter -Session $Session 
                New-Snapshot -VM $VM -Name 'Windows Update on $Date'
                & 'C:\Program Files (x86)\SysinternalsSuite\PsExec.exe ' $WUInstall
                }")

Now we will invoke the background task to initiate the process.

Start-Job -ScriptBlock $ScriptBlock   
        }
    }

Depending on the number of virtual machines to which you are invoking the command you may wish to limit the number of background jobs that are invoked in parallel. The below will limit the number of backgorund jobs run to be 10 and once all background jobs have the state of completed deletes the backgound jobs invoked.

While((Get-Job -State 'Running').Count -ge "10")
    {
        Start-Sleep -Milliseconds 30
    }
    
While (Get-Job -State "Running") 
{ 
    Start-Sleep -Milliseconds 30
}

Remove-Job * -Confirm:$False

Adding NFS storage using the command line interface (CLI) with esxcfg and PowerCLI

In this example I will look at creating an NFS datastore using both esxcfg command line interface and PowerCLI to which I will use the following parameters.

  • Datastore Name: openfiler-nfs2
  • NFS Host: 10.0.0.6
  • NFS mount point: /mnt/nfs/nfs2/vmfs/

By connecting to an ESXi hosts console session we can invoke the esxcfg command line interface to create the new NFS datastore by invoking the esxcfg-nas switch.

esxcfg-nas -a openfiler-nfs2 -o 10.0.0.6 -s /mnt/nfs/nfs2/vmfs/

Once created, you should receive confirmation the NAS datastore has been created and connected as below:

Connecting to NAS volume: openfiler-nfs2
openfiler-nfs2 created and connected
openfiler-nfs2 is /mnt/nfs/nfs2/vmfs/ from 10.0.0.6 mounted available

We can remove the NFS datastore by invoking the ‘esxcfg-nas -d’  and specifying the datastore name.

esxcfg-nas -d openfiler-nfs2

To which on deletion you will receive confirmation as below.

NAS volume openfiler-nfs2 deleted.

Alternatively using PowerCLI we  can invoke the New-Datastore cmdlet to create a new NFS datastore  on the ESXi Host ‘deanesxi1.dean.local’.

New-Datastore -VMHost deanesxi1.dean.local -Nfs -Name openfiler-nfs2 -NfsHost 10.0.0.6 -Path /mnt/nfs/nfs2/vmfs/

To confirm the NFS datastore has been create we can invoke the Get-Datastore cmdlet to retrieve a collection of datastore where the type is ‘NFS’.

Get-Datastore | where {$_.type -eq "NFS"}

As below, we can confirm the new NFS datastore has been created.

Name            FreeSpaceGB CapacityGB
----            ----------- ----------
openfiler-nfs2  21.271     21.302

In order to remove a NFS datastore we can invoke the Remove-Datastore cmdlet to specify the datastore to remove on the ESXi host, where I am invoking the command without requiring user confirmation.

Remove-Datastore -Datastore openfiler-nfs2 -VMHost deanesxi1.dean.local -Confirm:$False

PowerCLI 5.8r1 Reference Poster Now Available!

For those who spend most of the day with your head buried in PowerCLI, the  5.8r1 reference poster is now available for download at http://bit.ly/PowerCLIPosterv2

For more information, see http://blogs.vmware.com/PowerCLI/2014/10/powercli-5-8r1-reference-poster-now-available.html

 

 

PowerCLI: Adding multiple disks to a virtual machine using the New-HardDisk cmdlet

I was recently adding a number of  hard disks to a virtual machine on an number of SCSI controller devices, so rather than using the vSphere Web Client I looked at using the New-HardDisk cmdlet in order to repeat this task of adding a new virtual machine hard disk .

I wanted to create the virtual machine hard disks on a single datastore with the below characteristics:

  • Capacity: 30GB
  • Storage Format: EagerZeroedThick
  • SCSI Controller: SCSI Controller 1

I could achieve this for a single hard disk by invoking the New-HardDisk cmdlet as below:

New-HardDisk -VM VM1 -CapacityGB 30 -Datastore "Datastore1" -StorageFormat EagerZeroedThick -Controller "SCSI Controller 1"

However, as I was required to this multiple times, I leveraged the use of pure math with the ForEach loop to achieve this task to create the six virtual hard disks.

ForEach ($HardDisk in (1..6))
    { 
    New-HardDisk -VM VM1 -CapacityGB 30 -Datastore Datastore1 -StorageFormat EagerZeroedThick -Controller "SCSI Controller 1"
    } 

Furthermore, I was adding additional hard disks on a number of SCSI controllers, I could expand the above further to invoke multiple ForEach loops for each SCSI controller, specifying a different datastore and size for each.

ForEach ($HardDisk in (1..6))
    { 
    New-HardDisk -VM VM1 -CapacityGB 30 -Datastore Datastore1 -StorageFormat EagerZeroedThick -Controller "SCSI Controller 1"
    } 
ForEach ($HardDisk in (1..2))
    { 
    New-HardDisk -VM VM1 -CapacityGB 10 -Datastore Datastore2 -StorageFormat EagerZeroedThick -Controller "SCSI Controller 2"
    } 
ForEach ($HardDisk in (1..3))
    { 
    New-HardDisk -VM VM1 -CapacityGB 20 -Datastore Datastore3 -StorageFormat EagerZeroedThick -Controller "SCSI Controller 3"
    } 

 

PowerCLI: Starting and Stopping the SSH service on multiple ESXi hosts

I was recently looking at enabling the SSH service on multiple ESXi hosts in a cluster to invoke a change using remote access, in order to do so I was able to start and stop the SSH service on each ESXi host using PowerCLI.

Firstly, I established a connection to the vCenter Server and created a collection of ESXi hosts from a specific cluster.

if (-not (Get-PSSnapin VMware.VimAutomation.Core -ErrorAction SilentlyContinue)) 
    {
    Add-PSSnapin VMware.VimAutomation.Core | Out-Null 
    }

Connect-VIServer server1.domain.local

$VMHosts = Get-Cluster "Cluster1" | Get-VMHost

For each ESXi host returned in the collection we will retrieve the service we require to restart using the ‘Get-VMHostService’ cmdlet and filter to retrieve the key name which matches the SSH service and pass this to the ‘Start-VMHostService’ cmdlet to start the service on each ESXi Host.

ForEach ($VMHost in $VMHosts) 

    { 
    Start-VMHostService -HostService (Get-VMHostService -VMHost $VMHost.Name | Where { $_.Key -eq "TSM-SSH"} )
    } 

You can also stop the SSH service as above, but to remove the requirement for user interaction you will be required to specify the Command switch to invoke the ‘Stop-VMHostService’ cmdlet without confirmation.

ForEach ($VMHost in $VMHosts) 

    { 
    Stop-VMHostService -HostService (Get-VMHostService -VMHost $VMHost.Name | Where { $_.Key -eq "TSM-SSH"} ) -Confirm:$false 
    } 

VMware Tools Upgrade fails with vix error code 21009

On remediating virtual machines using the Update Manager, a number of VMware Tools upgrades would fail reporting the following:

Error Upgrading VMware Tools
vix error code = 21009

On investigation of vix error codes at https://www.vmware.com/support/developer/vix-api/vix16_reference/errors/errors.html there was error code reference for the above. So now it was time to look at the vmware.log file for one of the impacted virtual machines, to which I found the following log entry

TOOLS INSTALL Error copying upgrader binary into guest. success = 0, HgfsStatus = 8

A quick search of the VMware KB, returned the following http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1023459 where the cause of the issue is that the file ‘VMwareToolsUpgrader.exe’ located at ‘C:\Windows\Temp’ only has read-only permissions.

In order to resolve the issue, I removed the file ‘VMwareToolsUpgrader.exe’ from one of the virtual machines returning the error, and attempted to remediate from the attached baseline to which this completed with success.

Therefore, I was required to remove this file from each virtual machine where the file existed, this is where PowerCLI and in particular the Invoke-VMScript cmdlet come in very useful.

Firstly, we will establish a connection to the vCenter system.

If (-not (Get-PSSnapin VMware.VimAutomation.Core -ErrorAction SilentlyContinue)) 
    {
    Add-PSSnapin VMware.VimAutomation.Core | Out-Null 
    }

Connect-VIServer server1.domain.local | Out-Null

Now, we build a collection of virtual machines, in this instance we only require those with a Microsoft Windows guest operating system.

$VMs = Get-VM | Where-Object {$_.Guest.OSFullName -like "*Windows*"}

For each virtual machine retrieved in the collection we will perform an operation to create the script text required to remove the file and then pass the variable to the Invoke-VMscript cmdlet. In this instance the script type will be configured to be ‘Bat’ as a number of virtual machines in the collection may not have Windows Powershell installed, the default script type for the Invoke-VMScript cmdlet.

The script text as below will use conditional logic to determine if the ‘VMwareToolsUpgrader.exe’ file exists and delete the file.

IF EXIST C:\WINDOWS\Temp\VMwareToolsUpgrader.exe DEL C:\WINDOWS\Temp\VMwareToolsUpgrader.exe

I also want to produce output of each virtual machine the operation has been performed on and report the exit code, while also ignoring any Warnings generated where the VMware Tools service is no up to date.

ForEach ($VM in $VMs)

    { 

        $ScriptText = "IF EXIST C:\WINDOWS\Temp\VMwareToolsUpgrader.exe DEL C:\WINDOWS\Temp\VMwareToolsUpgrader.exe"
        
        $Script = Invoke-VMScript -ScriptText $ScriptText -VM $VM.Name -ScriptType Bat -WarningAction SilentlyContinue
        
        "" + $VM.Name + " completed with exit code " + $Script.ExitCode 
    }