The ability to execute scripts inside the guest OS of your VMs, is definitely one of the more useful cmdlets available in VMware PowerCLI. A year ago I published the first version of my Invoke-VMScriptPlus function to solve some of the issues the Invoke-VMScript cmdlet has in my opinion.
That function allowed you to run multi-line scripts in a Linux guest OS on your VMs. It also allowed you to use she-bang lines, to indicate which interpreter your script had to run in (bash, perl, python, nodejs, php…). Another handy feature was that you could use Linux here-documents in your scripts.
With the introduction of PowerShell Core (aka PowerShell v6), the lack of support for any Guest OS of the Windows family became obvious. Prompted by a recent thread in the VMTN PowerCLI Community, I decided it was time to publish a new version of my Invoke-VMScriptPlus function.
Update August 21st 2018
- Added ScriptEnvironment
Intro
I defined some target features for the new version of the Invoke-VMScriptPlus function:
- Support the Windows family of Guest OS
- Support using PowerShell Core scripts (Windows and Linux)
- Fix the ScriptText length limitation
The Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 |
class MyOBN:System.Management.Automation.ArgumentTransformationAttribute { [ValidateSet( 'Cluster', 'Datacenter', 'Datastore', 'DatastoreCluster', 'Folder', 'VirtualMachine', 'VirtualSwitch', 'VMHost', 'VIServer' )] [String]$Type MyOBN([string]$Type) { $this.Type = $Type } [object] Transform([System.Management.Automation.EngineIntrinsics]$engineIntrinsics, [object]$inputData) { if ($inputData -is [string]) { if (-NOT [string]::IsNullOrWhiteSpace( $inputData )) { $cmdParam = "-$(if($this.Type -eq 'VIServer'){'Server'}else{'Name'}) $($inputData)" $sCmd = @{ Command = "Get-$($this.Type.Replace('VirtualMachine','VM')) $($cmdParam)" } return (Invoke-Expression @sCmd) } } elseif ($inputData.GetType().Name -match "$($this.Type)Impl") { return $inputData } elseif ($inputData.GetType().Name -eq 'Object[]') { return ($inputData | ForEach-Object { if ($_ -is [String]) { return (Invoke-Expression -Command "Get-$($this.Type.Replace('VirtualMachine','VM')) -Name `$_") } elseif ($_.GetType().Name -match "$($this.Type)Impl") { $_ } }) } throw [System.IO.FileNotFoundException]::New() } } function Invoke-VMScriptPlus { <# .SYNOPSIS Runs a script in a Linux guest OS. The script can use the SheBang to indicate which interpreter to use. .DESCRIPTION This function will launch a script in a Linux guest OS. The script supports the SheBang line for a limited set of interpreters. .NOTES Author: Luc Dekens Version: 1.0 14/09/17 Initial release 1.1 14/10/17 Support bash here-document 2.0 01/08/18 Support Windows guest OS, bat & powershell 2.1 03/08/18 PowerShell she-bang for Linux 2.2 17/08/18 Added ScriptEnvironment .PARAMETER VM Specifies the virtual machines on whose guest operating systems you want to run the script. .PARAMETER GuestUser Specifies the user name you want to use for authenticating with the virtual machine guest OS. .PARAMETER GuestPassword Specifies the password you want to use for authenticating with the virtual machine guest OS. .PARAMETER GuestCredential Specifies a PSCredential object containing the credentials you want to use for authenticating with the virtual machine guest OS. .PARAMETER ScriptText Provides the text of the script you want to run. You can also pass to this parameter a string variable containing the path to the script. Note that the function will add a SheBang line, based on the ScriptType, if none is provided in the script text. .PARAMETER ScriptType The supported Linux interpreters. Currently these are bash,perl,python3,nodejs,php,lua .PARAMETER ScriptEnvironment A string array with environment variables. These environment variables are available to the script from ScriptText .PARAMETER GuestOSType Indicates which type of guest OS the VM is using. The parameter accepts Windows or Linux. This parameter is a fallback for when the function cannot determine which OS Family the Guest OS belongs to .PARAMETER PSv6Version Indicates which PowerShell Core version to use. The default is 6.0.2 .PARAMETER CRLF Switch to indicate of the NL that is returned by Linux, shall be converted to a CRLF .PARAMETER Server Specifies the vCenter Server systems on which you want to run the cmdlet. If no value is passed to this parameter, the command runs on the default servers. For more information about default servers, see the description of Connect-VIServer. .EXAMPLE $pScript = @' #!/usr/bin/env perl use strict; use warnings; print "Hello world\n"; '@ $sCode = @{ VM = $VM GuestCredential = $cred ScriptType = 'perl' ScriptText = $pScript } Invoke-VMScriptPlus @sCode .EXAMPLE $pScript = @' print("Happy 10th Birthday PowerCLI!") '@ $sCode = @{ VM = $VM GuestCredential = $cred ScriptType = 'python3' ScriptText = $pScript } Invoke-VMScriptPlus @sCode #> [cmdletbinding()] param( [parameter(Mandatory = $true, ValueFromPipeline = $true)] [MyOBN('VirtualMachine')] [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine[]]$VM, [Parameter(Mandatory = $true, ParameterSetName = 'PlainText')] [String]$GuestUser, [Parameter(Mandatory = $true, ParameterSetName = 'PlainText')] [SecureString]$GuestPassword, [Parameter(Mandatory = $true, ParameterSetName = 'PSCredential')] [PSCredential[]]$GuestCredential, [Parameter(Mandatory = $true)] [String]$ScriptText, [Parameter(Mandatory = $true)] [ValidateSet('bash', 'perl', 'python3', 'nodejs', 'php', 'lua', 'powershell', 'powershellv6', 'bat')] [String]$ScriptType, [String[]]$ScriptEnvironment, [ValidateSet('Windows', 'Linux')] [String]$GuestOSType, [String]$PSv6Version = '6.0.2', [Switch]$CRLF, [MyOBN('VIServer')] [VMware.VimAutomation.ViCore.Types.V1.VIServer]$Server = $global:DefaultVIServer ) Begin { $si = Get-View ServiceInstance $guestMgr = Get-View -Id $si.Content.GuestOperationsManager $gFileMgr = Get-View -Id $guestMgr.FileManager $gProcMgr = Get-View -Id $guestMgr.ProcessManager $shebangTab = @{ 'bash' = '#!/usr/bin/env bash' 'perl' = '#!/usr/bin/env perl' 'python3' = '#!/usr/bin/env python3' 'nodejs' = '#!/usr/bin/env nodejs' 'php' = '#!/usr/bin/env php' 'lua' = '#!/usr/bin/env lua' 'powershellv6' = '#!/usr/bin/env pwsh' } } Process { foreach ($vmInstance in $VM) { # Preamble if ($vmInstance.PowerState -ne 'PoweredOn') { Write-Error "VM $($vmInstance.Name) is not powered on" continue } if ($vmInstance.ExtensionData.Guest.ToolsRunningStatus -ne 'guestToolsRunning') { Write-Error "VMware Tools are not running on VM $($vmInstance.Name)" continue } $moref = $vmInstance.ExtensionData.MoRef # Are we targetting a Windows or a Linux box? if (-not $GuestOSType) { switch -Regex ($vmInstance.Guest.OSFullName) { 'Windows' { $GuestOSType = 'Windows' if ('bat', 'powershell', 'powershellv6' -notcontains $ScriptType) { Write-Error "For a Windows guest OS the ScriptType can be Bat, PowerShell or PowerShellv6" continue } } 'Linux' { $GuestOSType = 'Linux' if ('bat', 'powershell' -contains $ScriptType) { Write-Error "For a Linux guest OS the ScriptType cannot be Bat or PowerShell" continue } } Default { Write-Error "Unable to determine the guest OS type on VM $($vmInstance.Name)" continue } } } if ($GuestOSType -eq 'Linux') { Write-Verbose "Seems to be a Linux guest OS" # Test if code contains a SheBang, otherwise add it $targetCode = $shebangTab[$ScriptType] if ($ScriptText -notmatch "^$($targetCode)") { Write-Verbose "Add SheBang $targetCode" $ScriptText = "$($targetCode)`n`r$($ScriptText)" } } # Create Authentication Object (User + Password) if ($PSCmdlet.ParameterSetName -eq 'PSCredential') { $GuestUser = $GuestCredential.GetNetworkCredential().username $plainGuestPassword = $GuestCredential.GetNetworkCredential().password } if ($PSCmdlet.ParameterSetName -eq 'PlainText') { $bStr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($GuestPassword) $plainGuestPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bStr) } $auth = New-Object VMware.Vim.NamePasswordAuthentication $auth.InteractiveSession = $false $auth.Username = $GuestUser $auth.Password = $plainGuestPassword # Copy script to temp file in guest # Create temp file for script $suffix = '' if ($ScriptType -eq 'bat') { $suffix = ".cmd" } if ('powershell', 'powershellv6' -contains $ScriptType) { $suffix = ".ps1" } Try { $tempFile = $gFileMgr.CreateTemporaryFileInGuest($moref, $auth, "$($env:USERNAME)_$($PID)", $suffix, $null) Write-Verbose "Created temp script file in guest OS $($tempFile.Name)" } Catch { Throw "$error[0].Exception.Message" } # Create temp file for output Try { $tempOutput = $gFileMgr.CreateTemporaryFileInGuest($moref, $auth, "$($env:USERNAME)_$($PID)_output", $null, $null) Write-Verbose "Created temp output file in guest OS $($tempOutput.Name)" } Catch { Throw "$error[0].Exception.Message" } # Copy script to temp file if ($GuestOSType -eq 'Linux') { $ScriptText = $ScriptText.Split("`r") -join '' } $attr = New-Object VMware.Vim.GuestFileAttributes $clobber = $true $filePath = $gFileMgr.InitiateFileTransferToGuest($moref, $auth, $tempFile, $attr, $ScriptText.Length, $clobber) $ip = $filePath.split('/')[2].Split(':')[0] $hostName = Resolve-DnsName -Name $ip | Select-Object -ExpandProperty NameHost $filePath = $filePath.replace($ip, $hostName) $copyResult = Invoke-WebRequest -Uri $filePath -Method Put -Body $ScriptText if ($copyResult.StatusCode -ne 200) { Throw "ScripText copy failed!`rStatus $($copyResult.StatusCode)`r$(($copyResult.Content | ForEach-Object{[char]$_}) -join '')" } Write-Verbose "Copied scipttext to temp script file" # Get current environment variables $SystemEnvironment = $gProcMgr.ReadEnvironmentVariableInGuest($moref, $auth, $null) # Run script switch ($GuestOSType) { 'Linux' { # Make temp file executable $spec = New-Object VMware.Vim.GuestProgramSpec $spec.Arguments = "751 $tempFile" $spec.ProgramPath = '/bin/chmod' Try { $procId = $gProcMgr.StartProgramInGuest($moref, $auth, $spec) Write-Verbose "Run script file" } Catch { Throw "$error[0].Exception.Message" } # Run temp file $spec = New-Object VMware.Vim.GuestProgramSpec if($ScriptEnvironment){ $spec.EnvVariables = $SystemEnvironment + $ScriptEnvironment } $spec.Arguments = " > $($tempOutput)" $spec.ProgramPath = "$($tempFile)" Try { $procId = $gProcMgr.StartProgramInGuest($moref, $auth, $spec) Write-Verbose "Run script with '$($tempFile) > $($tempOutput)'" } Catch { Throw "$error[0].Exception.Message" } } 'Windows' { # Run temp file $spec = New-Object VMware.Vim.GuestProgramSpec if($ScriptEnvironment){ $spec.EnvVariables = $SystemEnvironment + $ScriptEnvironment } switch ($ScriptType) { 'PowerShell' { $spec.Arguments = " /C powershell -NonInteractive -File $($tempFile) > $($tempOutput)" $spec.ProgramPath = "cmd.exe" } 'PowerShellv6' { $spec.Arguments = " /C ""C:\Program Files\PowerShell\$($PSv6Version)\pwsh.exe"" -NonInteractive -File $($tempFile) > $($tempOutput)" $spec.ProgramPath = "cmd.exe" } 'Bat' { $spec.Arguments = " /s /c cmd > $($tempOutput) 2>&1 /s /c $($tempFile)" $spec.ProgramPath = "cmd.exe" } } Try { $procId = $gProcMgr.StartProgramInGuest($moref, $auth, $spec) Write-Verbose "Run script with '$($spec.ProgramPath) $($spec.Arguments)'" } Catch { Throw "$error[0].Exception.Message" } } } # Wait for script to finish Try { $pInfo = $gProcMgr.ListProcessesInGuest($moref, $auth, @($procId)) Write-Verbose "Wait for process to end" while ($pInfo -and $null -eq $pInfo.EndTime) { Start-Sleep 1 $pInfo = $gProcMgr.ListProcessesInGuest($moref, $auth, @($procId)) } } Catch { Throw "$error[0].Exception.Message" } # Retrieve output from script $fileInfo = $gFileMgr.InitiateFileTransferFromGuest($moref, $auth, $tempOutput) $fileContent = Invoke-WebRequest -Uri $fileInfo.Url -Method Get if ($fileContent.StatusCode -ne 200) { Throw "Retrieve of script output failed!`rStatus $($fileContent.Status)`r$(($fileContent.Content | ForEach-Object{[char]$_}) -join '')" } Write-Verbose "Get output from $($fileInfo.Url)" # Clean up # Remove output file $gFileMgr.DeleteFileInGuest($moref, $auth, $tempOutput) Write-Verbose "Removed file $($tempOutput.Name)" # Remove temp script file $gFileMgr.DeleteFileInGuest($moref, $auth, $tempFile) Write-Verbose "Removed file $($tempFile.Name)" New-Object PSObject -Property @{ VM = $vmInstance ScriptOutput = & { $out = ($fileContent.Content | ForEach-Object {[char]$_}) -join '' if ($CRLF) { $out.Replace("`n", "`n`r") } else { $out } } Pid = $procId PidOwner = $pInfo.Owner Start = $pInfo.StartTime Finish = $pInfo.EndTime ExitCode = $pInfo.ExitCode ScriptType = $ScriptType ScriptSize = $ScriptText.Length ScriptText = $ScriptText GuestOS = $GuestOSType } } } } |
Annotations
Line 1-35: The latest version of my OBN (Object By Name) class. It allows one to pass, or the actual .Net object, or the name of the object, as an argument to a parameter. See also Home Made OBN
Line 136: When the Invoke-VMScriptPlus function cannot determine the Guest OS family, this parameter allows you to force a specific Guest OS family. Accepted values are Windows and Linux.
Line 133: The installation path of PowerShell Core on a Windows OS contains the version number. I could not find an easy way to determine the version number. This parameter allows to specify a specific PowerShell Core version. The default version is 6.0.2, and that is because that is the highest version VMware PowerCLI currently supports. Note that for the time being running VMware PowerCLI does not officially support running in PowerShell Core on a Windows Guest OS. Always consult the latest Compatibility Matrix to find out what is officially supported by VMware PowerCLI.
Line 134: This parameter allows you to pass a number of environment variables as an array of strings
Line 138: Most Linux OS return output with only a LF. This switch can be used to convert the LF to a CRLF, when the resulting output of the script is returned.
Line 147-155: A hard-coded table with the supported interpreters, and their corresponding SheBang line. This version of the Invoke-VMScriptPlus function adds PowerShell Core to the table.
Line 160-167: If the VM is not powered on, or if the VMware Tools are not running, or if the Guest OS family can not be determined, the function will return with an error message.
Line 170-191: The Invoke-VMScriptPlus function tests, based on the Guest OS family, if the requested ScriptType is valid.
Line 192-199: For a VM with a Guest OS in the Linux family, tests if there is a SheBang line in the ScriptText. If not, add a line based on the ScriptType value.
Line 202-213:Â Create the Authentication object for the GuestOperations methods. Note that in this release of the function, the GuestPassword requires a SecureString instead of a String.
Line 216-222: Depending on the ScriptType, the temporary file, see the following annotations, will have a filetype.
Line 223-237: The Invoke-VMScriptPlus function uses two temporary files to store the script and the script’s output.
Line 239-241: If the target Guest OS family is Linux, the line endings in the ScriptText are converted from CRLF to LF
Line 242-252: The ScriptText is copied to the temporary file. This is done over HTTPS with the Invoke-WebRequest cmdlet. Note that the temporary filename returned earlier contains the IP address of the ESXi node on which the VM is running. This potentially causes problems with the Invoke-WebRequest and invalid certificates. In the URI for the file, the function replaces the IP address with FQDN.
Line 256: Fetch the current system environment variables
Line 263-272: On a Guest OS in the Linux family, the file containing the ScriptText needs to be made “executable”
Line 276: The function merges the system environment variables with the provided environment variables. The resulting array is passed to the StartProgramInGuest call.
Line 278-286: On a Guest OS in the Windows family, the path to the PowerShell Core EXE, contains the version number. This is where the $PSv6Version parameter comes into play. The default value is currently set to 6.0.2.
Line 280-286,308-314: Script execution is started
Line 318-328: Wait for the script execution to end
Line 330-335: The output is fetched, again with an Invoke-WebRequest.
Line 338-341: Clean up the temporary files
Line 343-364: Return an object containing the script output and further info about the script execution
Sample Use
The following snippet lists the content of the os-release file, a common method in Linux distros to obtain the name and version of the OS. We use the following simple bash script.
Note that in this new version of the Invoke-VMScriptPlus function you have to use a SecureString (instead of a String in the previous version) for the GuestPassword parameter. This is a breaking change, but was done to comply with the ScriptAnalyzer rules!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
$user = 'luc' $pswd = 'SuperSecret1!' $sPswd = ConvertTo-SecureString -String $pswd -AsPlainText -Force $code =@' if [ -f /etc/os2-release ] then cat /etc/os-release else echo 'File os-release not found' fi '@ $sInvP = @{ VM = 'opensuse1' ScriptType = 'Bash' ScriptText = $code GuestOSType = 'Linux' GuestUser = $user GuestPassword = $sPswd } Invoke-VMScriptPlus @sInvP |
For an openSuse guest OS the result comes back as
For an Ubuntu guest OS
And for a CentOS guest OS as
One of the target goals of the Invoke-VMScriptPlus v2 was to be able to use the function to runs scripts in a Windows guest OS. The following sample uses the wmic command.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$user = 'local\administrator' $pswd = 'SuperSecret1!' $sPswd = ConvertTo-SecureString -String $pswd -AsPlainText -Force $code = @' wmic os get version '@ $sInvP = @{ VM = 'Server1' ScriptType = 'bat' ScriptText = $code GuestUser = $user GuestPassword = $sPswd } Invoke-VMScriptPlus @sInvP |
The result comes back as
And of course PowerShell scripts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$user = 'local\administrator' $pswd = 'SuperSecret1!' $sPswd = ConvertTo-SecureString -String $pswd -AsPlainText -Force $cred = New-Object System.Management.Automation.PSCredential -ArgumentList $user,$sPswd $code = @' (Get-CimInstance Win32_OperatingSystem).version '@ $sInvP = @{ VM = 'vEng' ScriptType = 'PowerShell' ScriptText = $code GuestCredential = $cred } Invoke-VMScriptPlus @sInvP |
Which returns
Note how this last invocation uses the GuestCredential parameter instead of GuestUser and GuestPassword. The Invoke-VMScriptPlus function supports both methods of passing the guest OS credentials.
This last sample is run under the “normal” PowerShell installed in the Windows guest OS, as we can easily see with
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$user = 'local\administrator' $pswd = 'SuperSecret1!' $sPswd = ConvertTo-SecureString -String $pswd -AsPlainText -Force $cred = New-Object System.Management.Automation.PSCredential -ArgumentList $user,$sPswd $code = @' $PSVersionTable '@ $sInvP = @{ VM = 'vEng' ScriptType = 'PowerShell' ScriptText = $code GuestCredential = $cred } Invoke-VMScriptPlus @sInvP |
The output of that script
Another new feature of the Invoke-VMScriptPlus function is the ability to call PowerShell Core.
On Windows
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$user = 'local\administrator' $pswd = 'SuperSecret1!' $sPswd = ConvertTo-SecureString -String $pswd -AsPlainText -Force $cred = New-Object System.Management.Automation.PSCredential -ArgumentList $user,$sPswd $code = @' $PSVersionTable '@ $sInvP = @{ VM = 'vEng' ScriptType = 'PowerShellv6' ScriptText = $code GuestCredential = $cred } Invoke-VMScriptPlus @sInvP |
which gives.
As you might have noted, we have both PowerShell version installed side-by-side on that station.
And also on Linux
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$user = 'luc' $pswd = 'SuperSecret1!' $sPswd = ConvertTo-SecureString -String $pswd -AsPlainText -Force $code =@' $PSVersionTable '@ $sInvP = @{ VM = 'opensuse1' ScriptType = 'PowerShellv6' ScriptText = $code GuestOSType = 'Linux' GuestUser = $user GuestPassword = $sPswd } Invoke-VMScriptPlus @sInvP |
Which results in
The final new feature I introduced in Invoke-VMScriptPlus v2 is the length limitation that exists in the regular Invoke-VMScript cmdlet. That limitation blocks you when you want to transfer scripts that surpass a specific length. Depending on the circumstances that limitation pops up in scripts that have a length somewhere in the neighbourhood of 2600 characters.
A new feature that was introduced is the ScriptEnvironment parameter. This allows the caller to pass environment variables. A simple example:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$vm = Get-VM ubuntu1 $sInvoke = @{ VM = $vm ScriptType = 'Bash' ScriptText = 'printenv' GuestUser = 'luc' GuestPassword = ConvertTo-SecureString -String 'SuperSecret1!' -AsPlainText -Force GuestOSType = 'Linux' ScriptEnvironment = 'Msg1=Hello','Msg2=World' } Invoke-VMScriptPlus @sInvoke |
And we see our environment variables added to the execution environment.
If you encounter “issues” or have suggestions for additions and improvements, please let me know.
Enjoy!
Todd Ouimet
Hi LucD,
Thank you for such a great Function!!
I had an issue when running it. I would get:
Invoke-WebRequest : Unable to connect to the remote server
After digging into the code I saw you were replacing the $ip for the $hostname on line 247 which worked great for those of us that are using custom SSL certs on the host that does not include a SAN IP address.
It turned out the failure was coming from line 330 & 331:
$fileInfo = $gFileMgr.InitiateFileTransferFromGuest($moref, $auth, $tempOutput)
$fileContent = Invoke-WebRequest -Uri $fileInfo.Url -Method Get
$fileInfo.Url contained the IP address not the hostname. I added the following one line of code and modified line 331 and all works great now!!
$Url = ($fileInfo.Url).replace($ip, $hostName)
$fileContent = Invoke-WebRequest -Uri $Url -Method Get -Verbose
Running on Win2016, PowerShell version 5.1.14393.3471, VMware PowerCLI 11.5.0 build 14912921
Regards,
Todd
LucD
Hi Todd,
Thanks, much appreciated.
That issue was resolved in the latest version, see Invoke-VMScriptPlus V3
The update adds the new Switch -NoIPinCert, and some other new features.
Give it a try.
Luc
Todd Ouimet
Oops sorry about that. I completely missed the latest version. Once again, amazing work from LucD! Thank you very much!
dion
The prvious error was using v7 of powershell Core, in the vurrent version I get this error:
The term ‘Resolve-DnsName’ is not recognized as the name of a cmdlet, function, script file, or operable program
as this relies on .Net and has not yet been ported. So it seems more likely that it might work with v7 but there is that other string problem
LucD
I’ll have a look, let me get back to you.
LucD
Hi again,
First, note that I started from the Invoke-VMScriptPlus v3 function.
The Resolve-DnsName cmdlet missing issue can be fixed by replacing that with
$hostName = [System.Net.Dns]::GetHostEntry($ip) | Select-Object -expandProperty HostName
Note that the cmdlet occurs twice in the function.
But in PS v7rc1, we then get a new error.
This is currently still an open issue it seems (see #10146).
Dion
I have users on linux who want to use powershell core but I get the following error when trying it from in Fedore 29:
Exception calling “CreateTemporaryFileInGuest” with “5” argument(s): “Failed to authenticate with the guest operating system using the supplied credentials.”
Can VIX and or your script work on from Linux powershell ?
Tanveer Ahmed
Hey Luc,
I am trying to reboot a Ubuntu box using invoke-VMscriptplus , all other commands are working fine where I am fetching the information however, the reboot command is not rebooting the box.
any help will be appreciated.
LucD
I normally do this by starting a background process, and by waiting a couple of seconds to start the reboot.
This allows the Invoke-VMScriptPlus function, which requires the VMware Tools to be running, to come back correctly.
Something like this
$sInvoke = @{
VM = $vm
ScriptType = 'bash'
ScriptText = 'sudo bash -c "sleep 10; reboot now" &'
GuestUser = $user
GuestPassword = $pswd
}
Invoke-VMScript @sInvoke
sylvain
Hi Luc,
Thank you for that piece of good job.
I have a slight comment however,
it looks like your script does not support the defaultVIServerMode set to multiple.
to bypass the error :
Exception calling “CreateTemporaryFileInGuest” with “5” argument(s): “The object ‘vim.VirtualMachine:vm-123456’ has already been deleted or has not been completely created”
I specified at the first line the viserver like :
$si = Get-View ServiceInstance -server $server
and everything works smoothly.
Can you confirm that ?
thank you for your reply.
Enjoy !
LucD
Thanks.
I’m not convinced that the error you mention is related to the ‘multiple’ mode.
That looks more like a VM that has just been created/removed, and where the VirtualMachine object that is passed to the function (the value on the VM parameter) has not been refreshed since.
DDR
Did you checked if script is working on Linux based machines with installed PowerShell? Some command are related to Windows and using script to execute command on VMs from Linux box will not be possible. Or I am wrong?
LucD
Afaik, the function is not using any cmdlets that will not work on a Linux box.
Did you encounter any issues?
Arundev
Thanks for the improvised function LucD.
The one thing lacking is that, even after all these years we still dont have a workaround for running invokevmscript in elevated privileges.
Seen from many of the articles, all uses a user which has admin rights, but not the administrator user itself.
Expecting a workaround from the master for running elevated (Runas) commands via invokevmscript
LucD
I have been looking at that for quite some time, believe me.
The issue is that due to security, and rightfully so, you can’t just elevate a user or a task.
But I’ll give it another try.
Don’t hold your breath though 🙂
DDR
Issue
Resolve-DnsName : The term \'Resolve-DnsName\' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. Invoke-VMScriptPlus_v2.ps1:246 char:13
+ $hostName = Resolve-DnsName -Name $ip | Select-Object -ExpandProperty ...
Seems, that Resolve-DNSName not exists in PowerShell in Linux, just Windows.
LucD
No, it’s not I’m afraid
Aaron R.
Hey Luc,
I haven’t had a chance to really dig through the meat of the code within the function yet, but I was wondering have you thought of incorporating (if you haven’t already) your Elevate process code into the Invoke-VMScriptPlus? Maybe as a Switch or an additional Parameter? Similar to how Start-Process allows a param -Verb RunAs to elevate the process action. I only ask because one of the biggest issues I run into is UAC being snarky and I know your elevate-Process to run as admin without the annoying access denied response.
I have many situations, in my efforts for automation, where I am deploying scripted building of environments through vCenter and then automation in configuring resource platforms. One of my biggest hangups is trying to run things that require elevation through Invoke-VMScript that are key to get the systems I am automating deployment of, one of those things being enabling winrm through PS, that and Register-ScheduledTask both of which scream back with access denied. Just thought maybe your two scripts combined could help Admins/Engineers such as myself work through these various small hurdles.
YVS Kumar
hi luc,
Getting below error while running invoke-vmscript. Guest OS is 2016 server.
Invoke-VMScript The operation is not allowed in the current state
Can you help ?
LucD
Are the VMware Tools installed and running?
Do you see any further info in the vmware.log file?
Markus Kraus
Hi Luc,
thanks for this great Function.
I ran into an issue where the Resolve-DnsName Cmdlet returns an Answer and the Authority. In my case this was fixed with this modification:
$hostName = (Resolve-DnsName -Name $ip).where({$_.Section -eq “Answer”}) | Select-Object -ExpandProperty NameHost
Greetings,
Markus
LucD
Thanks Markus
Prabhu G
Hi LucD,
Im trying to run a command on remote VM’s powershell using Invoke-VmScript cmdlet.
$dns_name = “Test-DNSVM”
function dnsFeatureInstall {
$scriptText = @”
Install-WindowsFeature DNS -IncludeManagementTools
“@
$user = ‘administrator’
$password = ‘password’
write-host “installing DNS feature on $dns_name”
Invoke-VMScript -Scripttype Powershell -ScriptText $scriptText -VM $dns_name -GuestUser “$user” -GuestPassword $password
}
dnsFeatureInstall
But it is not working. I know by default this cmdlet uses cmd.exe. so I changed the scripttype to powershell. Any helpl will be appreciated.