<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Guest OS Archives - LucD notes</title>
	<atom:link href="https://www.lucd.info/tag/guest-os/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.lucd.info/tag/guest-os/</link>
	<description>My PowerShell ramblings</description>
	<lastBuildDate>Tue, 21 Aug 2018 17:39:47 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9</generator>

<image>
	<url>https://www.lucd.info/wp-content/uploads/2018/12/cropped-120px-Tibetan_Dharmacakra-32x32.png</url>
	<title>Guest OS Archives - LucD notes</title>
	<link>https://www.lucd.info/tag/guest-os/</link>
	<width>32</width>
	<height>32</height>
</image> 
<atom:link rel="hub" href="https://pubsubhubbub.appspot.com"/><atom:link rel="hub" href="https://pubsubhubbub.superfeedr.com"/><atom:link rel="hub" href="https://websubhub.com/hub"/>	<item>
		<title>Invoke-VMScriptPlus v2</title>
		<link>https://www.lucd.info/2018/08/05/invoke-vmscriptplus-v2/</link>
					<comments>https://www.lucd.info/2018/08/05/invoke-vmscriptplus-v2/#comments</comments>
		
		<dc:creator><![CDATA[LucD]]></dc:creator>
		<pubDate>Sun, 05 Aug 2018 11:16:26 +0000</pubDate>
				<category><![CDATA[Guest Operations]]></category>
		<category><![CDATA[Guest OS]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[PowerCLI]]></category>
		<category><![CDATA[VMware Tools]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[Invoke-VMScript]]></category>
		<guid isPermaLink="false">http://www.lucd.info/?p=5804</guid>

					<description><![CDATA[Invoke-VMScriptPlus function V2. Run scripts in a VM's Guest OS. Can target Windows and Linux Guest OS families. And adds support for PowerShell Core (aka PowerShell v6).]]></description>
										<content:encoded><![CDATA[
<p>The ability to execute scripts inside the guest OS of your VMs, is definitely one of the more useful cmdlets available in <a href="https://code.vmware.com/web/dp/tool/vmware-powercli/10.1.1">VMware PowerCLI</a>. A year ago I published the first version of my <a href="https://www.lucd.info/2017/09/14/invoke-vmscriptplus/">Invoke-VMScriptPlus</a> function to solve some of the issues the <a href="https://vdc-repo.vmware.com/vmwb-repository/dcr-public/73d6de02-05fd-47cb-8f73-99d1b33aea17/850c6b63-eb82-4d9c-bfcf-79279b5e5637/doc/Invoke-VMScript.html">Invoke-VMScript</a> cmdlet has in my opinion.<br />That function allowed you to run <strong>multi-line scripts</strong> in a Linux guest OS on your VMs. It also allowed you to use <strong>she-bang</strong> lines, to indicate which interpreter your script had to run in (bash, perl, python, nodejs, php&#8230;). Another handy feature was that you could use Linux <strong>here-documents</strong> in your scripts.</p>
<p><img fetchpriority="high" decoding="async" class="alignnone wp-image-5807 size-large" src="https://www.lucd.info/wp-content/uploads/2018/08/invokevmscriptplus_v2-1024x921.jpg" alt="" width="770" height="693" srcset="https://www.lucd.info/wp-content/uploads/2018/08/invokevmscriptplus_v2-1024x921.jpg 1024w, https://www.lucd.info/wp-content/uploads/2018/08/invokevmscriptplus_v2-300x270.jpg 300w, https://www.lucd.info/wp-content/uploads/2018/08/invokevmscriptplus_v2-768x691.jpg 768w, https://www.lucd.info/wp-content/uploads/2018/08/invokevmscriptplus_v2-720x648.jpg 720w, https://www.lucd.info/wp-content/uploads/2018/08/invokevmscriptplus_v2.jpg 1043w" sizes="(max-width: 770px) 100vw, 770px" /></p>





<p>With the introduction of <a href="https://github.com/PowerShell/PowerShell">PowerShell Core</a> (aka PowerShell v6), the lack of support for any Guest OS of the Windows family became obvious. Prompted by a recent thread in the <a href="https://communities.vmware.com/community/vmtn/automationtools/powercli">VMTN PowerCLI Community</a>, I decided it was time to publish a new version of my <strong>Invoke-VMScriptPlus</strong> function.</p>
<p>&nbsp;</p>

<p><span style="background-color: #ffff00;"><strong>Update August 21st 2018</strong></span></p>
<ul>
<li>Added ScriptEnvironment</li>
</ul>

<p><span id="more-5804"></span></p>



<h2>Intro</h2>
<p>I defined some target features  for the new version of the <strong>Invoke-VMScriptPlus</strong> function:</p>



<ul class="wp-block-list">
<li>Support the Windows family of Guest OS</li>
<li>Support using PowerShell Core scripts (Windows and Linux)</li>
<li>Fix the ScriptText length limitation</li>
</ul>
<h2>The Code</h2>
<pre class="lang:ps decode:true ">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 <code>$_")
          }
          elseif ($_.GetType().Name -match "$($this.Type)Impl") {
            $_
          }
        })
    }
    throw [System.IO.FileNotFoundException]::New()
  }
}
function Invoke-VMScriptPlus {
  &lt;#
.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 &amp; 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
  #&gt;
  [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)</code>n<code>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("</code>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!<code>rStatus $($copyResult.StatusCode)</code>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 = " &gt; $($tempOutput)"
          $spec.ProgramPath = "$($tempFile)"
          Try {
            $procId = $gProcMgr.StartProgramInGuest($moref, $auth, $spec)
            Write-Verbose "Run script with '$($tempFile) &gt; $($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) &gt; $($tempOutput)"
              $spec.ProgramPath = "cmd.exe"
            }
            'PowerShellv6' {
              $spec.Arguments = " /C ""C:\Program Files\PowerShell\$($PSv6Version)\pwsh.exe"" -NonInteractive -File $($tempFile) &gt; $($tempOutput)"
              $spec.ProgramPath = "cmd.exe"
            }
            'Bat' {
              $spec.Arguments = " /s /c cmd &gt; $($tempOutput) 2&gt;&amp;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!<code>rStatus $($fileContent.Status)</code>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 = &amp; {
          $out = ($fileContent.Content | ForEach-Object {[char]$_}) -join ''
          if ($CRLF) {
            $out.Replace("<code>n", "</code>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
      }
    }
  }
}</pre>
<p>&nbsp;</p>
<h2>Annotations</h2>
<p><strong>Line 1-35</strong>: 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 <a href="https://www.lucd.info/2017/05/30/home-made-obn/" target="_blank" rel="noopener">Home Made OBN</a></p>
<p><strong>Line 136</strong>: 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 <strong>Windows</strong> and <strong>Linux</strong>.</p>
<p><strong>Line 133</strong>: The installation path of <strong>PowerShell Core</strong> on a Windows OS contains the <strong>version number</strong>. 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. <strong>Note</strong> 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 <a href="https://vdc-download.vmware.com/vmwb-repository/dcr-public/5258f6a7-9431-4960-a68e-bc5ef87a4ebf/49a4989b-e7a4-46e6-adae-1f39a68dd347/powercli1000-compat-matrix.html" target="_blank" rel="noopener">Compatibility Matrix</a> to find out what is officially supported by <a href="https://code.vmware.com/web/dp/tool/vmware-powercli/10.1.1" target="_blank" rel="noopener">VMware PowerCLI</a>.</p>
<p><strong>Line 134</strong>: This parameter allows you to pass a number of environment variables as an array of strings</p>
<p><strong>Line 138</strong>: 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.</p>
<p><strong>Line 147-155</strong>: 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.</p>
<p><strong>Line 160-167</strong>: 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.</p>
<p><strong>Line 170-191</strong>: The Invoke-VMScriptPlus  function tests, based on the Guest OS family, if the requested ScriptType is valid.</p>
<p><strong>Line 192-199</strong>: 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.</p>
<p><strong>Line 202-213:</strong> Create the Authentication object for the GuestOperations methods. <strong>Note</strong> that in this release of the function, the GuestPassword requires a <strong>SecureString</strong> instead of a String.</p>
<p><strong>Line 216-222</strong>: Depending on the ScriptType, the temporary file, see the following annotations, will have a filetype.</p>
<p><strong>Line 223-237</strong>: The Invoke-VMScriptPlus  function uses two temporary files to store the script and the script’s output.</p>
<p><strong>Line 239-241</strong>: If the target Guest OS family is Linux, the line endings in the ScriptText are converted from CRLF to LF</p>
<p><strong>Line 242-252</strong>: 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.</p>
<p><strong>Line 256</strong>: Fetch the current system environment variables</p>
<p><strong>Line 263-272</strong>: On a Guest OS in the Linux family, the file containing the ScriptText needs to be made “executable”</p>
<p><strong>Line 276</strong>: The function merges the system environment variables with the provided environment variables. The resulting array is passed to the StartProgramInGuest call.</p>
<p><strong>Line 278-286</strong>: 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.</p>
<p><strong>Line 280-286,308-314</strong>: Script execution is started</p>
<p><strong>Line 318-328</strong>: Wait for the script execution to end</p>
<p><strong>Line 330-335</strong>: The output is fetched, again with an Invoke-WebRequest.</p>
<p><strong>Line 338-341</strong>: Clean up the temporary files</p>
<p><strong>Line 343-364</strong>: Return an object containing the script output and further info about the script execution</p>
<h2>Sample Use</h2>
<p>The following snippet lists the content of the <strong>os-release</strong> file, a common method in Linux distros to obtain the name and version of the OS. We use the following simple bash script.</p>
<p><strong>Note</strong> that in this new version of the Invoke-VMScriptPlus function you have to use a <strong>SecureString</strong> (instead of a String in the previous version) for the GuestPassword parameter. This is a breaking change, but was done to comply with the <a href="https://www.powershellgallery.com/packages/PSScriptAnalyzer/1.17.1" target="_blank" rel="noopener">ScriptAnalyzer</a> rules!</p>
<pre class="lang:ps decode:true">$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</pre>
<p>For an <strong>openSuse</strong> guest OS the result comes back as</p>
<p><img decoding="async" class="alignnone size-medium wp-image-5816" src="https://www.lucd.info/wp-content/uploads/2018/08/snip1-opensuse-1-278x300.jpg" alt="" width="278" height="300" srcset="https://www.lucd.info/wp-content/uploads/2018/08/snip1-opensuse-1-278x300.jpg 278w, https://www.lucd.info/wp-content/uploads/2018/08/snip1-opensuse-1.jpg 441w" sizes="(max-width: 278px) 100vw, 278px" /></p>
<p>For an <strong>Ubuntu</strong> guest OS</p>
<p><img decoding="async" class="alignnone size-medium wp-image-5817" src="https://www.lucd.info/wp-content/uploads/2018/08/snip1-ubuntu-296x300.jpg" alt="" width="296" height="300" srcset="https://www.lucd.info/wp-content/uploads/2018/08/snip1-ubuntu-296x300.jpg 296w, https://www.lucd.info/wp-content/uploads/2018/08/snip1-ubuntu.jpg 497w" sizes="(max-width: 296px) 100vw, 296px" /></p>
<p>And for a <strong>CentOS</strong> guest OS as</p>
<p><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-5818" src="https://www.lucd.info/wp-content/uploads/2018/08/snip1-centos-223x300.jpg" alt="" width="223" height="300" srcset="https://www.lucd.info/wp-content/uploads/2018/08/snip1-centos-223x300.jpg 223w, https://www.lucd.info/wp-content/uploads/2018/08/snip1-centos.jpg 428w" sizes="auto, (max-width: 223px) 100vw, 223px" /></p>
<p>One of the target goals of the <strong>Invoke-VMScriptPlus v2</strong> was to be able to use the function to runs scripts in a <strong>Windows guest OS</strong>. The following sample uses the <strong>wmic</strong> command.</p>
<pre class="lang:ps decode:true">$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

</pre>
<p>The result comes back as</p>
<p><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-5819" src="https://www.lucd.info/wp-content/uploads/2018/08/snip2-win-300x194.jpg" alt="" width="300" height="194" srcset="https://www.lucd.info/wp-content/uploads/2018/08/snip2-win-300x194.jpg 300w, https://www.lucd.info/wp-content/uploads/2018/08/snip2-win.jpg 415w" sizes="auto, (max-width: 300px) 100vw, 300px" /></p>
<p>And of course PowerShell scripts</p>
<pre class="lang:ps decode:true">$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</pre>
<p>Which returns</p>
<p><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-5820" src="https://www.lucd.info/wp-content/uploads/2018/08/snip3-win-300x151.jpg" alt="" width="300" height="151" srcset="https://www.lucd.info/wp-content/uploads/2018/08/snip3-win-300x151.jpg 300w, https://www.lucd.info/wp-content/uploads/2018/08/snip3-win.jpg 461w" sizes="auto, (max-width: 300px) 100vw, 300px" /></p>
<p><strong>Note</strong> how this last invocation uses the <strong>GuestCredential</strong> parameter instead of <strong>GuestUser</strong> and <strong>GuestPassword</strong>. The <strong>Invoke-VMScriptPlus</strong> function supports both methods of passing the guest OS credentials.</p>
<p>This last sample is run under the &#8220;normal&#8221; PowerShell installed in the Windows guest OS, as we can easily see with</p>
<pre class="lang:ps decode:true">$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</pre>
<p>The output of that script</p>
<p><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-5822" src="https://www.lucd.info/wp-content/uploads/2018/08/snip4-win-300x249.jpg" alt="" width="300" height="249" srcset="https://www.lucd.info/wp-content/uploads/2018/08/snip4-win-300x249.jpg 300w, https://www.lucd.info/wp-content/uploads/2018/08/snip4-win.jpg 512w" sizes="auto, (max-width: 300px) 100vw, 300px" /></p>
<p>Another new feature of the <strong>Invoke-VMScriptPlus</strong> function is the ability to call <strong>PowerShell Core</strong>.</p>
<p>On Windows</p>
<pre class="lang:ps decode:true ">$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</pre>
<p>which gives.<br />As you might have noted, we have both PowerShell version installed <strong>side-by-side</strong> on that station.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-5823" src="https://www.lucd.info/wp-content/uploads/2018/08/snip5-win-300x242.jpg" alt="" width="300" height="242" srcset="https://www.lucd.info/wp-content/uploads/2018/08/snip5-win-300x242.jpg 300w, https://www.lucd.info/wp-content/uploads/2018/08/snip5-win.jpg 551w" sizes="auto, (max-width: 300px) 100vw, 300px" /></p>
<p>And also on Linux</p>
<pre class="lang:ps decode:true">$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</pre>
<p>Which results in</p>
<p><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-5824" src="https://www.lucd.info/wp-content/uploads/2018/08/snip2-ubuntu-300x200.jpg" alt="" width="300" height="200" srcset="https://www.lucd.info/wp-content/uploads/2018/08/snip2-ubuntu-300x200.jpg 300w, https://www.lucd.info/wp-content/uploads/2018/08/snip2-ubuntu.jpg 684w" sizes="auto, (max-width: 300px) 100vw, 300px" /></p>
<p>The final new feature I introduced in <strong>Invoke-VMScriptPlus v2</strong> is the <strong>length limitation</strong> that exists in the regular <a href="https://vdc-repo.vmware.com/vmwb-repository/dcr-public/d402b7ed-b345-4fda-880d-a48e8885e910/b6ff10a7-3769-4346-8a83-d92d99d6caf3/doc/Invoke-VMScript.html" target="_blank" rel="noopener">Invoke-VMScript</a> 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.</p>
<p>A new feature that was introduced is the <strong>ScriptEnvironment</strong> parameter. This allows the caller to pass environment variables. A simple example:</p>
<pre class="lang:ps decode:true">$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</pre>
<p>And we see our environment variables added to the execution environment.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-5931" src="https://www.lucd.info/wp-content/uploads/2018/08/invoke-environment-300x268.jpg" alt="" width="300" height="268" srcset="https://www.lucd.info/wp-content/uploads/2018/08/invoke-environment-300x268.jpg 300w, https://www.lucd.info/wp-content/uploads/2018/08/invoke-environment.jpg 619w" sizes="auto, (max-width: 300px) 100vw, 300px" /></p>
<p>If you encounter &#8220;issues&#8221; or have suggestions for additions and improvements, please let me know.</p>
<p>Enjoy!</p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.lucd.info/2018/08/05/invoke-vmscriptplus-v2/feed/</wfw:commentRss>
			<slash:comments>23</slash:comments>
		
		
			</item>
	</channel>
</rss>
