Will Invoke-VMScript work ?
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.





@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
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.
@Ed, you are right. I’ll update the script to reflect what is stated in KB2010065
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.
@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.