Home > PowerCLI, PowerShell, Virtual Machine > Will Invoke-VMScript work ?

Will Invoke-VMScript work ?

January 1st, 2012 Leave a comment Go to comments

The Invoke-VMScript cmdlet can be a very useful cmdlet, but sometimes it will fail against one or more of your VMs. And it is not always immediately clear why the Invoke-VMScript cmdlet will not work against that specific VM.
The cmdlet help contains a number of prerequisites, but how do you verify if all the prerequisites are fulfilled?
I decided to create a function that would verify the prerequisites, and that would, if requested, which of the prerequisites was missing.

The prerequisites

Official

The official prerequisites are all documented in the help for the Invoke-VMScript cmdlet. The following table is a summary.

PowerCLI 4.1 4.1U1 5
only 32-bit engine x x x
VMware Tools installed x x x
read access to VM folder x x x
Virtual Machine.Interaction.Console privilege x x x
VM powered on x x x
port 902 to ESX(i) hosting the VM x x x
VIX 1.6.2 x x
VIX 1.10 x
Windows XP, Windows 7, Windows 2003 Server, Windows 2008 Server,Redhat Enterprise 5 x x x

Unofficial

From my own experience and from several posts in the PowerCLI Community, I have 2 additional prerequisites.

  • The cmdlet seems to work most of the time when the guest OS is Windows 2008 R2. Note that there have been reports about errors with this OS. In the current PowerCLI build Windows 2008 R2 is not in the list of supported guest OS.
  • When you use the hostname in the Connect-VIServer cmdlet, the Invoke-VMScript doesn’t seem to work. When you do the Connect-VIServer with the FQDN or the IP address, the cmdlet works. The error message looks like this

The list of my 2 non-official prerequisites

PowerCLI 4.1 4.1U1 5
Windows 2008 R2 Server x
Connected with FQDN or IP x x x

The Script

function Test-InvokeVMScript{
<#
.SYNOPSIS  Test if Invoke-VMScript will work
.DESCRIPTION The function verifies if all prerequisites
  are present to use the Invoke-VMScript cmdlet.
.NOTES  Author:  Luc Dekens
.PARAMETER VM
  The virtual machine for which you want to test the
  prerequisites. You can pass the name of the virtual
  machine or the VM object
.PARAMETER Official
  This switch specifies if only the official prerequistes
  should be verified or not. The default value is $True,
  so only the official prerequisites
.PARAMETER Detail
  Switch that specifies if all the prerequisite details
  should be returned or not. The default is $False
.EXAMPLE
  PS> Test-InvokeVMScript -VM $vm -Detail
.EXAMPLE
  PS> Get-VM VM* | Test-InvokeVMScript -Official:$false
#>

  param(
  [CmdletBinding()]
  [parameter(Mandatory = $true, ValueFromPipeline = $true)]
  [PSObject]$VM,
  [switch]$Official = $True,
  [switch]$Detail
  )

  begin{
    $pCLIMajor,$pCLIMinor = Get-PowerCLIVersion | %{$_.Major,$_.Minor}
    $apiMajor,$apiMinor = (Get-View ServiceInstance).Content.About.ApiVersion.Split('.')
  }

  process{
    if($VM.GetType().Name -eq "string"){
      $VM = Get-VM -Name $VM
    }

    $condPoweredOn = $condCpu = $condTools = $condPort = $condRead = $condRole = $condOS = $False

    # Common prerequisites

    # Powered on
    if($vm.PowerState -eq "PoweredOn"){
      $condPoweredOn = $True
    }

    # 32-bit engine
    if ($env:Processor_Architecture -eq "x86"){
      $condCpu = $true
    }

    # Tools installed
    if($VM.Guest.State -eq "Running" -and $VM.ExtensionData.Summary.Guest.ToolsVersionStatus -eq "guestToolsCurrent"){
      $condTools = $True
    }

    # Port 902 open
    $originalEA = $ErrorActionPreference
    $ErrorActionPreference = “SilentlyContinue”
    $socket = New-Object Net.Sockets.TcpClient
    $socket.Connect($VM.Host.Name,902)
    if($socket.Connected){
      $condPort = $True
      $socket.Close()
    }
    Remove-Variable -Name socket -Confirm:$false
    $ErrorActionPreference = $originalEA

    # Folder read access
    $datastore,$file = $VM.ExtensionData.Config.Files.VmPathName.Split(']')
    $datastoreName = $datastore.Trim('[')
    $fileName = $file.Split('/')[1]
    $file = $file.TrimStart(' ')
    $ds = Get-Datastore -Name $datastoreName
    New-PSDrive -Location $ds -Name DS -PSProvider VimDatastore -Root '\' | Out-Null
    $currentProgPref = $ProgressPreference
    $ProgressPreference = 'SilentlyContinue'
    Copy-DatastoreItem -Item ('DS:\' + $file) -Destination $env:Temp -ErrorAction SilentlyContinue
    $ProgressPreference = $currentProgPref
    Remove-PSDrive -Name DS
    $path = $env:Temp + '\' + $fileName
    $condRead = Test-Path -Path $path
    if($condRead){
      Remove-Item -Path $path -Confirm:$false
    }

    # Privilege
    $authMgr = Get-View AuthorizationManager
    $role = $authMgr.RoleList | where {$vm.ExtensionData.EffectiveRole -eq $_.RoleId}
    $condRole = $role.Privilege -contains "VirtualMachine.Interact.ConsoleInteract"

    # Supported OS
    $supportedOS = "winLonghornGuest",  # Windows 2008
    "winLonghorn64Guest",               # Windows 2008 (64 bit)
    "windows7Guest",                     # Windows 7
    "windows7_64Guest",                 # Windows 7 (64 bit)
    "windows7Server64Guest",            # Windows Server 2008 R2 (64 bit)
    "winXPProGuest",                     # Windows XP Professional
    "winXPPro64Guest",                   # Windows XP Professional Edition (64 bit)
    "winXPHomeGuest",                   # Windows XP Home Edition
    "rhel5Guest",                       # Red Hat Enterprise Linux 5
    "rhel5_64Guest",                     # Red Hat Enterprise Linux 5 (64 bit)
    "rhel6Guest",                       # Red Hat Enterprise Linux 6
    "rhel6_64Guest"                     # Red Hat Enterprise Linux 6 (64 bit)
    $guestId = $VM.ExtensionData.Summary.Guest.GuestId
    if($guestId -like "winNet*" -or $supportedOS -contains $guestId){
      $condOS = $true
    }

    # Version dependent prerequisites
    $propertiesVix =[System.Diagnostics.FileVersionInfo]::GetVersionInfo($env:programfiles + '\VMware\VMware VIX\VixCOM.dll')
    $majorVix = $propertiesVix.FileMajorPart
    $minorVix = $propertiesVix.FileMinorPart
    $buildVix = $propertiesVix.FileBuildPart
    $versionVix = ([string]$majorVix + '.' + [string]$minorVix + '.' + [string]$buildVix)

    if(($pCLIMajor -eq 5 -and $versionVix -eq '1.10.0') -or
    ($pCLIMajor -eq 4 -and $versionVix -eq '1.6.2')){
      $condVix = $true
    }

    # Unofficial conditions
    if(!$Official){
      $condFQDN_U = $condOS_U = $False

      # OS that seems to work (most of the time)
      if($guestId -like "windows7Server64Guest"){  # Windows Server 2008 R2 (64 bit)
        $condOS_U = $True
      }

      # Short name
      $condFQDN_U = $global:DefaultVIserver.Name.Contains('.')
    }

    # Result
    if($Official){
      $result = $condPoweredOn -and $condCpu -and $condTools -and
        $condPort -and $condRead -and $condRole -and $condOS
    }
    else{
      $result = $condPoweredOn -and $condCpu -and $condTools -and
        $condPort -and $condRead -and $condRole -and
        ($condOS -or $condOS_U) -and $condFQDN_U
    }

    $resultObj = New-Object PSObject -Property @{
      VM = $VM.Name
      OK = $result
    }
    if($Detail){
      $resultObj = $resultObj |
      Add-Member -Name PoweredOn -Value $condPoweredOn -MemberType NoteProperty -PassThru |
      Add-Member -Name X86Engine -Value $condCpu -MemberType NoteProperty -PassThru |
      Add-Member -Name ToolsInstalled -Value $condTools -MemberType NoteProperty -PassThru |
      Add-Member -Name Port902Open -Value $condPort -MemberType NoteProperty -PassThru |
      Add-Member -Name FolderReadAccess -Value $condRead -MemberType NoteProperty -PassThru |
      Add-Member -Name PrivilegeConsoleInteraction -Value $condRole -MemberType NoteProperty -PassThru |
      Add-Member -Name SupportedOS -Value $condOs -MemberType NoteProperty -PassThru
      if(!$Official){
        $resultObj = $resultObj |
        Add-Member -Name FQDNorIPConnection -Value $condFQDN_U -MemberType NoteProperty -PassThru
      }
    }
    $resultObj
  }
}

Annotations

Line 32-35: Retrieve some properties the script will use later.

Line 38-40: My poor man’s Object By Name (OBN) implementation.

Line 42: Set all prerequisites to $false

Line 73-89: To test if the caller has read access to the VM folder, the script will copy the VMX file to local storage.

Line 82: This is the major bottleneck in the function. The Copy-DatastoreItem cmdlet is quite slow compared to the other PowerCLI cmdlets.

Line 110: All Windows Server 2003 variations have a guestId that starts with “winNet”. See the VirtualMachineGuestOsIdentifier enumeration.

Line 131-133: Chekc if the guest OS is Windows 2009 R2. If you don’t want this test, just comment out these lines.

Line 136: By testing if there any dots in the name, we can verify if it is a Fully Qualified Domain Name or an IP address.

Sample use

A very straightforward example

$vm = Get-VM -Name MyVM
Test-InvokeVMScript -VM $vm

This will return a simple object, where the OK property will say if the prerequisites are met or not.

If we want to know which of the prerequisites failed, we can do

$vm = Get-VM -Name MyVM
Test-InvokeVMScript -VM $vm -Detail

From the output we can immediately see why the Invoke-VMScript cmdlet would fail.

Looks like we are not running this in a 32-bit engine.

To also see my non-official prerequisites, we can do

Get-VM MyVM2 | Test-InvokeVMScript -Official:$false -Detail

This produces the following

All the official prerequisites are fulfilled, but it looks as if our Connect-VIServer might have been done with a short hostname.

Let me know if you know of any other prerequisites, and I will add them to the function.

Enjoy the function.

  1. JJ_Cain
    January 12th, 2012 at 07:37 | #1

    @LucD- First time post, long time fan. Thanks for this script – great time saver. I am doing a mass migration of Windows guests (~50 a week) to upgrade to pvscsi and vmxnet3. I have a large number of 2k8R2 VMs coming up and during my testing, 1 line from my .ps1 is not passed. I use Invoke-VMscript several times throughout the .ps1 but this 1 liner to remove the e1000 is not playing nice. It does not like the “@” even when in the “double quotes”. Any ideas to get this passed? single quotes? Here-string?
    #$remove1 = C:\Devcon64 -r remove “@PCI\VEN_8086&DEV_100F&SUBSYS_075015AD&REV_01\4&B70F118&0&0888″
    #Invoke-VMScript -vm $vm -ScriptType bat -ScriptText $remove1 -GuestCredential $vmcredentials

  2. January 6th, 2012 at 15:59 | #2

    I’ve just discovered that the PowerCLI cmdlets for guest OS integration (including Invoke-VMScript) aren’t backwards compatible. Presumably the VIX change is the reason for this? I just put up a quick blogpost – http://www.vexperienced.co.uk/2012/01/06/powercli-v5-gotcha-if-you-use-guest-os-cmdlets/.

    I can see the script checks that the VIX version is correct, but does it check the version of the host running the VM? Otherwise it’ll give the all clear to a PC with PowerCLI 5 installed but if the VM runs on an ESX4 host the scripts will still fail.

    • January 6th, 2012 at 16:10 | #3

      @Ed, you are right. I’ll update the script to reflect what is stated in KB2010065

  3. January 2nd, 2012 at 06:48 | #4

    Thanks again for another great script. You helped me out with a script a year ago when I was doing a mass migration of Windows guests with an upgrade to pvscsi and vmxnet. As a different part of that project I did use invoke-vmscript against about 500 Windows VM with about 80% Windows 2003 SP2. I didn’t have any problem getting the invoke-vmscript to work on the 2003 VMs. I did ensure that PowerShell 2.0 and all the WinRM management components were installed prior to that project so that may be why it worked so well. The only weird issue I remember is that I would have to connect with host credentials instead of vCenter credentials.

    • January 2nd, 2012 at 08:19 | #5

      @Benj, thanks.
      Afaik WINRM is not needed for the Invoke-VMScript cmdlet, it uses the VMware Tools to execute commands inside the guest OS. When you do PowerShell Remoting, which is another possibility to execute commands inside a guest OS, you do need WINRM.
      In PowerCLI 4.0 you did have to pass the hosts credentials, when your Connect-VIServer was executed against a vCenter. That requirement was dropped in recent PowerCLI builds.

  1. No trackbacks yet.