Invoke-VMScriptPlus v2

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

 

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!

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.

The result comes back as

And of course PowerShell scripts

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

The output of that script

Another new feature of the Invoke-VMScriptPlus function is the ability to call PowerShell Core.

On Windows

which gives.
As you might have noted, we have both PowerShell version installed side-by-side on that station.

And also on Linux

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:

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!

 

23 Comments

    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.

      Peer certificate cannot be authenticated with given CA certificates

      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.

Leave a Reply

Your email address will not be published. Required fields are marked *

*
*

This site uses Akismet to reduce spam. Learn how your comment data is processed.