<?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>SER1875BU Archives - LucD notes</title>
	<atom:link href="https://www.lucd.info/category/vmworld/2017/ser1875bu/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.lucd.info/category/vmworld/2017/ser1875bu/</link>
	<description>My PowerShell ramblings</description>
	<lastBuildDate>Tue, 13 Nov 2018 12:21:43 +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>SER1875BU Archives - LucD notes</title>
	<link>https://www.lucd.info/category/vmworld/2017/ser1875bu/</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>vSphere Permission cleanup</title>
		<link>https://www.lucd.info/2017/12/08/vsphere-permission-cleanup/</link>
					<comments>https://www.lucd.info/2017/12/08/vsphere-permission-cleanup/#respond</comments>
		
		<dc:creator><![CDATA[LucD]]></dc:creator>
		<pubDate>Fri, 08 Dec 2017 14:37:48 +0000</pubDate>
				<category><![CDATA[2017]]></category>
		<category><![CDATA[Permission]]></category>
		<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[Recursive]]></category>
		<category><![CDATA[SER1875BU]]></category>
		<category><![CDATA[PowerCLI]]></category>
		<category><![CDATA[recursive]]></category>
		<guid isPermaLink="false">http://www.lucd.info/?p=5695</guid>

					<description><![CDATA[Your vSphere environment is a living environment. Inventory objects are created and removed [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Your vSphere environment is a living environment. Inventory objects are created and removed all the time. Together with these inventory objects there are often security permissions that come along. Team X needs Power User access for all VMs in folder Project-X. But the life-cycle management of these permissions is often not as fluent as your VM life cycle management. There is no built in permission cleanup method.</p>
<p>As a result, old permissions might be left behind, and what is worse, redundant permissions might be present. This doesn&#8217;t make the task of investigating &#8220;Who can do what?&#8221; in your vSphere environment any easier.</p>
<p><a href="https://www.lucd.info/2017/12/08/vsphere-permission-cleanup/tree/" rel="attachment wp-att-5697"><img fetchpriority="high" decoding="async" class="alignnone wp-image-5697 size-large" src="https://www.lucd.info/wp-content/uploads/2017/12/tree-1024x619.jpg" alt="" width="770" height="465" srcset="https://www.lucd.info/wp-content/uploads/2017/12/tree-1024x619.jpg 1024w, https://www.lucd.info/wp-content/uploads/2017/12/tree-300x181.jpg 300w, https://www.lucd.info/wp-content/uploads/2017/12/tree-768x464.jpg 768w, https://www.lucd.info/wp-content/uploads/2017/12/tree-720x435.jpg 720w, https://www.lucd.info/wp-content/uploads/2017/12/tree.jpg 1128w" sizes="(max-width: 770px) 100vw, 770px" /></a></p>
<p>With the help of the function in this post you can now get rid of all these redundant permissions!</p>
<p><span id="more-5695"></span></p>
<h2>The Script</h2>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag">function Optimize-VIPermission{
&lt;#
.SYNOPSIS
  Find and remove redundant permissions on vSphere objects 
.DESCRIPTION
  The function will recursively scan the permissions on the
  inventory objects passed via the Entity parameter.
  Redundant permissions will be removed.
.NOTES
  Author:  Luc Dekens
.PARAMETER Entity
  One or more vSphere inventory objects from where the scan
  shall start
.EXAMPLE
  PS&gt; Optimize-Permission -Entity Folder1 -WhatIf
.EXAMPLE
  PS&gt; Optimize-Permission -Entity Folder?
.EXAMPLE
  PS&gt; Get-Folder -Name Folder* | Optimize-Permission
#&gt;

  [cmdletbinding(SupportsShouldProcess=$true)]
  param(
    [parameter(ValueFromPipeline)]
    [PSObject[]]$Entity
  )
 
  Begin{
    function Optimize-iVIPermission{
      [cmdletbinding(SupportsShouldProcess=$true)]
      param(
        [parameter(ValueFromPipeline)]
        [VMware.Vim.ManagedObjectReference]$Entity,
        [VMware.Vim.Permission[]]$Permission = $null
      )

      Process{
        $entityObj = Get-View -Id $Entity
        $removePermission = @()
        $newParentPermission = @()
        if($Permission){
          foreach($currentPermission in $entityObj.Permission){
            foreach($parentPermission in $Permission){
              if($parentPermission.Principal -eq $currentPermission.Principal -and
                 $parentPermission.RoleId -eq $currentPermission.RoleId){
                $removePermission += $currentPermission
                break
              }
              else{
                $newParentPermission += $currentPermission
              }
            }
          }
        }
        else{
          $newParentPermission += $entityObj.Permission
        }
        if($removePermission){
          if($pscmdlet.ShouldProcess("$($entityObj.Name)", "Cleaning up permissions")){
            $removePermission | %{
              $authMgr.RemoveEntityPermission($Entity,$_.Principal,$_.Group)
            }
          }
        }
        $Permission += $newParentPermission
       
        if($entityObj.ChildEntity){
            $entityObj.ChildEntity | Optimize-iVIPermission -Permission $Permission
        }
      }
    }
  }
 
  Process{
    foreach($entry in $Entity){
       if($entry -is [System.String]){
        $entry = Get-Inventory -Name $entry
       }
       Optimize-iVIPermission -Entity $entry.ExtensionData.MoRef
    }
  }
}</pre><p></p>
<h3>Annotations</h3>
<p><strong>Line 24-25</strong>: the function accepts one or more vSphere Inventory objects, as a parameter or through the pipeline</p>
<p><strong>Line 29-70</strong>: this is the internal function that does the actual work. The main function is just a wrapper to handle (and eventually convert) the parameter.</p>
<p><strong>Line 39-40</strong>: the function recursively descends the inventory tree, and it checks the permissions on each node against the recursive permissions that were assigned in nodes higher up in the tree. To check what is there and what can be removed, the arrays <strong>$removePermission</strong> and <strong>$newParentPermission</strong> are used.</p>
<p><strong>Line 44-45</strong>: in the current version of the function, the Principal and the Role need to be match, before the permission is considered to be removed</p>
<p><strong>Line 58-64</strong>: if redundant permissions were discovered on the current node in the vSphere inventory tree, then the redundant permission will be removed. Or if the WhatIf switch was used, a message will be displayed.</p>
<p><strong>Line 68</strong>: the internal function is called recursively for each child of the current node. The permissions that were already encountered on the parent nodes, are passed as a parameter to the internal function.</p>
<p><strong>Line 77-79</strong>: a &#8220;cheap&#8221; implementation of <a href="https://vdc-repo.vmware.com/vmwb-repository/dcr-public/85a74cac-7b7b-45b0-b850-00ca08d1f238/ae65ebd9-158b-4f31-aa9c-4bbdc724cc38/doc/about_obn.html" target="_blank" rel="noopener">OBN</a></p>
<h2>Sample Usage</h2>
<p>The use of the <strong>Optimize-VIPermission</strong> function is quite straightforward.</p>
<p>As a test environment, we applied some permissions on two different node in the tree. In this case for the Principal <strong>LOCAL\test</strong>.</p>
<p><img decoding="async" class="alignnone size-medium wp-image-5700" src="https://www.lucd.info/wp-content/uploads/2017/12/sample-300x163.jpg" alt="" width="300" height="163" srcset="https://www.lucd.info/wp-content/uploads/2017/12/sample-300x163.jpg 300w, https://www.lucd.info/wp-content/uploads/2017/12/sample.jpg 697w" sizes="(max-width: 300px) 100vw, 300px" /></p>
<p>In its simplest form, you just pass the name of the object where the optimization should start. Notice how we used the <strong>WhatIf</strong> switch on the function call. No actual permissions will be removed, but the function will show what it would do.</p><pre class="urvanov-syntax-highlighter-plain-tag">Optimize-VIPermission -Entity Test1 -WhatIf</pre><p>And this produces the following output.</p>
<p><img decoding="async" class="alignnone size-medium wp-image-5701" src="https://www.lucd.info/wp-content/uploads/2017/12/out-300x35.jpg" alt="" width="300" height="35" srcset="https://www.lucd.info/wp-content/uploads/2017/12/out-300x35.jpg 300w, https://www.lucd.info/wp-content/uploads/2017/12/out.jpg 670w" sizes="(max-width: 300px) 100vw, 300px" /></p>
<p>The function also accepts multiple starting points, you can pass multiple locations on the Entity parameter.</p><pre class="urvanov-syntax-highlighter-plain-tag">Optimize-VIPermission -Entity Test1,Test2 -WhatIf</pre><p>The function output now shows the following (because we added a second redundant permission in the tree under Test2).</p>
<p><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-5702" src="https://www.lucd.info/wp-content/uploads/2017/12/out2-300x39.jpg" alt="" width="300" height="39" srcset="https://www.lucd.info/wp-content/uploads/2017/12/out2-300x39.jpg 300w, https://www.lucd.info/wp-content/uploads/2017/12/out2.jpg 661w" sizes="auto, (max-width: 300px) 100vw, 300px" /></p>
<p>And you can use the function in a pipeline construct, like this.</p><pre class="urvanov-syntax-highlighter-plain-tag">Get-Folder -Name Test? | Optimize-VIPermission -WhatIf</pre><p>Which produces exactly the same output as the previous example.</p>
<p><span style="background-color: #ffff00;"><strong>Note</strong></span>: it is strongly advised to always run this first with the <strong>WhatIf</strong> switch. Only when the WhatIf switch is not used, or when it receives a $false value, will the redundant permissions actually be removed!</p>
<p>Enjoy!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.lucd.info/2017/12/08/vsphere-permission-cleanup/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Invoke-VMScriptPlus</title>
		<link>https://www.lucd.info/2017/09/14/invoke-vmscriptplus/</link>
					<comments>https://www.lucd.info/2017/09/14/invoke-vmscriptplus/#comments</comments>
		
		<dc:creator><![CDATA[LucD]]></dc:creator>
		<pubDate>Thu, 14 Sep 2017 10:55:32 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[OBN]]></category>
		<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[SER1875BU]]></category>
		<category><![CDATA[Bash]]></category>
		<category><![CDATA[Invoke-VMScript]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[PowerCLI]]></category>
		<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">http://www.lucd.info/?p=5649</guid>

					<description><![CDATA[The Invoke-VMScript cmdlet is definitely one of the PowerCLI cmdlets that is indispensable [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>The <a href="https://www.vmware.com/support/developer/PowerCLI/PowerCLI65R1/html/Invoke-VMScript.html" target="_blank" rel="noopener">Invoke-VMScript</a> cmdlet is definitely one of the <a href="https://communities.vmware.com/community/vmtn/automationtools/powercli" target="_blank" rel="noopener">PowerCLI</a> cmdlets that is indispensable when you need to do things inside the <strong>Guest OS</strong> of your VMs.</p>
<p>When you are interacting with a <strong>Windows</strong> based Guest OS you can run old-fashioned <strong>BAT</strong> files or use <strong>PowerShell</strong> scripts. When the Guest OS is <strong>Linux</strong> based, you currently only can run <strong>Bash</strong> scripts.</p>
<p>Most Linux flavours have a feature that is called <a href="https://en.wikipedia.org/wiki/Shebang_(Unix)" target="_blank" rel="noopener">SheBang</a>, and which allows you to specify in the first line of your bash script, which interpreter shall be used to run the following lines of the script. Unfortunately, the current <a href="https://www.vmware.com/support/developer/PowerCLI/PowerCLI65R1/html/Invoke-VMScript.html" target="_blank" rel="noopener">Invoke-VMScript</a> cmdlet doesn&#8217;t allow one to use that feature.</p>
<p><a href="https://www.lucd.info/2017/09/14/invoke-vmscriptplus/invokevmscriptplu/" rel="attachment wp-att-5653"><img loading="lazy" decoding="async" class="alignnone wp-image-5653 size-large" src="https://www.lucd.info/wp-content/uploads/2017/09/invokevmscriptplu-1024x525.jpg" alt="" width="770" height="395" srcset="https://www.lucd.info/wp-content/uploads/2017/09/invokevmscriptplu-1024x525.jpg 1024w, https://www.lucd.info/wp-content/uploads/2017/09/invokevmscriptplu-300x154.jpg 300w, https://www.lucd.info/wp-content/uploads/2017/09/invokevmscriptplu-768x394.jpg 768w, https://www.lucd.info/wp-content/uploads/2017/09/invokevmscriptplu-720x369.jpg 720w, https://www.lucd.info/wp-content/uploads/2017/09/invokevmscriptplu.jpg 1152w" sizes="auto, (max-width: 770px) 100vw, 770px" /></a></p>
<p>Time to tackle that issue, and expand the possibilities for all VMs that have a Linux-based Guest OS. So I decided to write my Invoke-VMScriptPlus function.</p>
<p><span style="background-color: #ffff00;"><strong>Update October 14th 2017</strong></span></p>
<ul>
<li>Added here-document bash sample</li>
</ul>
<p><span id="more-5649"></span></p>
<p>When the <a href="https://powercli.ideas.aha.io/" target="_blank" rel="noopener">PowerCLI Feature Request</a> website was <a href="https://twitter.com/kmruddy/status/903043133004431360" target="_blank" rel="noopener">announced</a> during VMworld session <strong>#SER2529BU</strong>, it was no surprise to me, to rather quickly see a request appearing to support other languages, besides bash.</p>
<p><a href="https://www.lucd.info/2017/09/14/invoke-vmscriptplus/phyton-support/" rel="attachment wp-att-5652"><img loading="lazy" decoding="async" class="alignnone wp-image-5652 size-medium" src="https://www.lucd.info/wp-content/uploads/2017/09/phyton-support-300x72.jpg" alt="" width="300" height="72" srcset="https://www.lucd.info/wp-content/uploads/2017/09/phyton-support-300x72.jpg 300w, https://www.lucd.info/wp-content/uploads/2017/09/phyton-support-768x183.jpg 768w, https://www.lucd.info/wp-content/uploads/2017/09/phyton-support-720x172.jpg 720w, https://www.lucd.info/wp-content/uploads/2017/09/phyton-support.jpg 980w" sizes="auto, (max-width: 300px) 100vw, 300px" /></a></p>
<p>First I did some tests, to check if I could get the Invoke-VMScript cmdlet to work with a SheBang line.</p><pre class="urvanov-syntax-highlighter-plain-tag">$vmName = 'ubuntu1'
$vm = Get-VM -Name $vmName

$codeBashPlain = @"
echo "Hello World!"
"@

$codeBashSheBang = @"
#!/usr/bin/env bash
echo "Hello World!"
"@

$sScript = @{
    VM = $vm
    GuestUser = 'lucd'
    GuestPassword = 'Just@Password1!'
    ScriptType = 'Bash'
    ScriptText = $codeBashPlain
}

Invoke-VMScript @sScript

$sScript['ScriptText'] = $codeBashSheBang
Invoke-VMScript @sScript</pre><p>Unfortunately it doesn&#8217;t.</p>
<p><a href="https://www.lucd.info/2017/09/14/invoke-vmscriptplus/script-out/" rel="attachment wp-att-5655"><img loading="lazy" decoding="async" class="alignnone wp-image-5655 size-medium" src="https://www.lucd.info/wp-content/uploads/2017/09/script-out-300x95.png" alt="" width="300" height="95" srcset="https://www.lucd.info/wp-content/uploads/2017/09/script-out-300x95.png 300w, https://www.lucd.info/wp-content/uploads/2017/09/script-out-768x244.png 768w, https://www.lucd.info/wp-content/uploads/2017/09/script-out-720x228.png 720w, https://www.lucd.info/wp-content/uploads/2017/09/script-out.png 939w" sizes="auto, (max-width: 300px) 100vw, 300px" /></a></p>
<p>When a cmdlet doesn&#8217;t do what you are trying to achieve, there is always the <a href="https://code.vmware.com/apis/196/vsphere#/doc/vim.vm.guest.ProcessManager.ProgramSpec.html" target="_blank" rel="noopener">vSphere API</a> that can help. The <a href="https://code.vmware.com/apis/196/vsphere#/doc/vim.vm.guest.GuestOperationsManager.html" target="_blank" rel="noopener">GuestOperationsManager</a> is the place to look. From there we can access methods to start and monitor a process in the guest  and to handle files inside the guest OS.<br />
The <a href="https://code.vmware.com/apis/196/vsphere#/doc/vim.vm.guest.ProcessManager.html#startProgram" target="_blank" rel="noopener">StartProgramInGuest</a> is the central method of the <strong>Invoke-VMScriptPlus</strong> function. The function uses the <strong>arguments</strong> property on the <a href="https://code.vmware.com/apis/196/vsphere#/doc/vim.vm.guest.ProcessManager.ProgramSpec.html" target="_blank" rel="noopener">GuestProgramSpec</a> object to <strong>redirect the stdio</strong> of the process.</p>
<p>The following flow-chart shows a high-level view of the logic that is used in the <strong>Invoke-VMScriptPlus</strong> function, and shows which method is used at which point.</p>
<p><a href="https://www.lucd.info/2017/09/14/invoke-vmscriptplus/invoke-vmscriptplus-flow/" rel="attachment wp-att-5656"><img loading="lazy" decoding="async" class="alignnone wp-image-5656 size-large" src="https://www.lucd.info/wp-content/uploads/2017/09/invoke-vmscriptplus-flow-1024x458.jpg" alt="" width="770" height="344" srcset="https://www.lucd.info/wp-content/uploads/2017/09/invoke-vmscriptplus-flow-1024x458.jpg 1024w, https://www.lucd.info/wp-content/uploads/2017/09/invoke-vmscriptplus-flow-300x134.jpg 300w, https://www.lucd.info/wp-content/uploads/2017/09/invoke-vmscriptplus-flow-768x344.jpg 768w, https://www.lucd.info/wp-content/uploads/2017/09/invoke-vmscriptplus-flow-720x322.jpg 720w, https://www.lucd.info/wp-content/uploads/2017/09/invoke-vmscriptplus-flow.jpg 1772w" sizes="auto, (max-width: 770px) 100vw, 770px" /></a></p>
<h2>The Code</h2>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag">#requires -Version 5.0
#requires -Modules VMware.VimAutomation.Core

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 | %{
                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
{
&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
.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 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')]
        [String]$GuestPassword,
        [Parameter(Mandatory=$true,ParameterSetName='PSCredential')]
        [PSCredential[]]$GuestCredential,
        [Parameter(Mandatory=$true)]
        [String]$ScriptText,
        [Parameter(Mandatory=$true)]
        [ValidateSet('bash','perl','python3','nodejs','php','lua')]
        [String]$ScriptType,
        [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'
        }
    }

    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

            # Test if code contains a SheBang, otherwise add it
            $targetCode = $shebangTab[$ScriptType]
            if($ScriptText -notmatch "^$($targetCode)"){
                $ScriptText = "$($targetCode)`n`r$($ScriptText)"
            }
    
            # Create Authentication Object (User + Password)
            
            if($PSCmdlet.ParameterSetName -eq 'PSCredential')
            {
                $GuestUser = $GuestCredential.GetNetworkCredential().username
                $GuestPassword = $GuestCredential.GetNetworkCredential().password
            }
    
            $auth = New-Object VMware.Vim.NamePasswordAuthentication
            $auth.InteractiveSession = $false
            $auth.Username = $GuestUser
            $auth.Password = $GuestPassword
            
            # Copy script to temp file in guest
            
            # Create temp file for script
            Try{
                $tempFile = $gFileMgr.CreateTemporaryFileInGuest($moref,$auth,"$($env:USERNAME)_","_$($PID)",'/tmp')
            }
            Catch{
                Throw "$error[0].Exception.Message"
            }
            
            # Create temp file for output
            Try{
                $tempOutput = $gFileMgr.CreateTemporaryFileInGuest($moref,$auth,"$($env:USERNAME)_","_$($PID)_output",'/tmp')
            }
            Catch{
                Throw "$error[0].Exception.Message"
            }
           
            # Copy script to temp file
            $lCode = $ScriptText.Split("`r") -join ''
            $attr = New-Object VMware.Vim.GuestFileAttributes
            $clobber = $true
            $filePath = $gFileMgr.InitiateFileTransferToGuest($moref,$auth,$tempFile,$attr,$lCode.Length,$clobber)
            $copyResult = Invoke-WebRequest -Uri $filePath -Method Put -Body $lCode
            
            if($copyResult.StatusCode -ne 200)
            {
                Throw "ScripText copy failed!`rStatus $($copyResult.StatusCode)`r$(($copyResult.Content | %{[char]$_}) -join '')"
            }
                
            # Make temp file executable
            $spec = New-Object VMware.Vim.GuestProgramSpec
            $spec.Arguments = "751 $($tempFile.Split('/')[-1])"
            $spec.ProgramPath = '/bin/chmod'
            $spec.WorkingDirectory = '/tmp'
            Try{
                $procId = $gProcMgr.StartProgramInGuest($moref,$auth,$spec)
            }
            Catch{
                Throw "$error[0].Exception.Message"
            }
            
            # Run temp file
            
            $spec = New-Object VMware.Vim.GuestProgramSpec
            $spec.Arguments = " &gt; $($tempOutput)"
            $spec.ProgramPath = "$($tempFile)"
            $spec.WorkingDirectory = '/tmp'
            Try{
                $procId = $gProcMgr.StartProgramInGuest($moref,$auth,$spec)
            }
            Catch{
                Throw "$error[0].Exception.Message"
            }
            
            # Wait for script to finish
            Try{
                $pInfo = $gProcMgr.ListProcessesInGuest($moref,$auth,@($procId))
                while($pInfo.EndTime -eq $null){
                    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 | %{[char]$_}) -join '')"
            }
            
            # Clean up

            # Remove output file
            $gFileMgr.DeleteFileInGuest($moref,$auth,$tempOutput)
            
            # Remove temp script file
            $gFileMgr.DeleteFileInGuest($moref,$auth,$tempFile)
    
            New-Object PSObject -Property @{
                VM = $vmInstance
                ScriptOutput = &amp;{
                    $out = ($fileContent.Content | %{[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
                ScriptText = $ScriptText
            }
        }
    }
}</pre><p></p>
<h3>Annotations</h3>
<p><strong>Line 1</strong>: The function requires PowerShell v5 or higher</p>
<p><strong>Line 2</strong>: The function requires the PowerCLI Core module</p>
<p><strong>Line 4-48</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 132</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 145-152</strong>: A hard-coded table with the supported interpreters, and their corresponding SheBang line.</p>
<p><strong>Line 159-168</strong>: If the VM is not powered on, or if the VMware Tools are not running, the function will return with a result.</p>
<p><strong>Line 173-176</strong>: Tests if there is a SheBang line in the ScriptText. If not, it will add a line based on the ScriptType value.</p>
<p><strong>Line 193-207</strong>: The function uses two temporary files to store the script and the script&#8217;s output.</p>
<p><strong>Line 210-219</strong>: The ScriptText is copied to the temporary file. This is done over HTTPS with the Invoke-WebRequest cmdlet.</p>
<p><strong>Line 222-231</strong>: The file containing the ScriptText needs to be made &#8220;executable&#8221;</p>
<p><strong>Line 235-256</strong>: Script execution is started, and the function waits till the process completes.</p>
<p><strong>Line 236</strong>: The function uses the Arguments property to redirect stdio to the second temporary file</p>
<p><strong>Line 260-265</strong>: The output is fetched, again with an Invoke-WebRequest.</p>
<p><strong>Line 270-273</strong>: Clean up the temporary files</p>
<p><strong>Line 275-295</strong>: Return an object containing the script output and further info about the script execution</p>
<h2>Sample Use</h2>
<p>The use of the function Invoke-VMScriptPlus is quite similar to the use of the original Invoke-VMScript.</p>
<p>Some examples.</p>
<h3>Bash</h3>
<p></p><pre class="urvanov-syntax-highlighter-plain-tag">$codeBash = @"
#!/usr/bin/env bash
echo "Hello World!"
"@

$sCode = @{
  VM = $vm
  GuestCredential = $cred
  ScriptTYpe = 'bash'
  ScriptText = $codeBash
}

Invoke-VMScriptPlus @sCode</pre><p>And the result</p>
<p><a href="https://www.lucd.info/2017/09/14/invoke-vmscriptplus/bash-out/" rel="attachment wp-att-5658"><img loading="lazy" decoding="async" class="alignnone wp-image-5658 size-medium" src="https://www.lucd.info/wp-content/uploads/2017/09/bash-out-300x218.png" alt="" width="300" height="218" srcset="https://www.lucd.info/wp-content/uploads/2017/09/bash-out-300x218.png 300w, https://www.lucd.info/wp-content/uploads/2017/09/bash-out.png 542w" sizes="auto, (max-width: 300px) 100vw, 300px" /></a></p>
<h4>Here document</h4>
<p>One feature that is often used in bash, is the so-called <a href="https://tldp.org/LDP/abs/html/here-docs.html" target="_blank" rel="noopener">here document</a>.</p>
<p>Unfortunately the <a href="https://vdc-repo.vmware.com/vmwb-repository/dcr-public/de211814-fded-41f0-bc26-c70cb7b8a9e9/076370ed-067b-4740-be9c-835da7485932/doc/Invoke-VMScript.html" target="_blank" rel="noopener">Invoke-VMScript</a> cmdlet doesn&#8217;t seem to support that feature for bash scripts. Code like this &#8230;</p><pre class="urvanov-syntax-highlighter-plain-tag">$code = @"
rm /tmp/test.txt
cat &gt; /tmp/test.txt &lt;&lt; EOF
Line 1
Line 2
EOF
ls -l /tmp/test.txt
echo File content
echo ------------
cat /tmp/test.txt
echo ------------
"@

$sINvoke = @{
    VM = Get-VM -Name $vmName
    ScriptType = 'bash'
    ScriptText = $code
    GuestUser = $user
    GuestPassword = $pswd
}
Invoke-VMScript @sINvoke | select -ExpandProperty ScriptOutput</pre><p>&#8230; produces an error like this.</p>
<p><a href="https://www.lucd.info/2017/09/14/invoke-vmscriptplus/error/" rel="attachment wp-att-5673"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5673" src="https://www.lucd.info/wp-content/uploads/2017/09/error.jpg" alt="" width="615" height="66" srcset="https://www.lucd.info/wp-content/uploads/2017/09/error.jpg 615w, https://www.lucd.info/wp-content/uploads/2017/09/error-300x32.jpg 300w" sizes="auto, (max-width: 615px) 100vw, 615px" /></a></p>
<p>But due to the way the <strong>Invoke-VMScriptPlus</strong> function transfers the script text to the guest, this feature is working without a glitch.</p>
<p>The same code as above, except that <strong>Invoke-VMScript</strong> is replaced by <strong>Invoke-VMScriptPlus</strong>.</p><pre class="urvanov-syntax-highlighter-plain-tag">$code = @"
rm /tmp/test.txt
cat &gt; /tmp/test.txt &lt;&lt; EOF
Line 1
Line 2
EOF
ls -l /tmp/test.txt
echo File content
echo ------------
cat /tmp/test.txt
echo ------------
"@

$sINvoke = @{
    VM = Get-VM -Name $vmName
    ScriptType = 'bash'
    ScriptText = $code
    GuestUser = $user
    GuestPassword = $pswd
}
Invoke-VMScriptPlus @sInvoke | select -ExpandProperty ScriptOutput</pre><p>&#8230; now produces the expected result.</p>
<p><a href="https://www.lucd.info/2017/09/14/invoke-vmscriptplus/ok/" rel="attachment wp-att-5674"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5674" src="https://www.lucd.info/wp-content/uploads/2017/09/ok.jpg" alt="" width="413" height="94" srcset="https://www.lucd.info/wp-content/uploads/2017/09/ok.jpg 413w, https://www.lucd.info/wp-content/uploads/2017/09/ok-300x68.jpg 300w" sizes="auto, (max-width: 413px) 100vw, 413px" /></a></p>
<h3>Python3</h3>
<p>In this example we are not adding the SheBang line in the ScriptText, but through the ScriptType value, the function will add this line.</p><pre class="urvanov-syntax-highlighter-plain-tag">$codePython = @"
print("Happy 10th Birthday PowerCLI!")
"@

$sCode = @{
 VM = $vm
 GuestCredential = $cred
 ScriptTYpe = 'python3'
 ScriptText = $codePython
}

Invoke-VMScriptPlus @sCode</pre><p>And the result.<br />
Notice how the Invoke-VMScriptPlus function added the SheBang line.</p>
<p><a href="https://www.lucd.info/2017/09/14/invoke-vmscriptplus/python-out/" rel="attachment wp-att-5659"><img loading="lazy" decoding="async" class="alignnone wp-image-5659 size-medium" src="https://www.lucd.info/wp-content/uploads/2017/09/python-out-300x134.png" alt="" width="300" height="134" srcset="https://www.lucd.info/wp-content/uploads/2017/09/python-out-300x134.png 300w, https://www.lucd.info/wp-content/uploads/2017/09/python-out-768x342.png 768w, https://www.lucd.info/wp-content/uploads/2017/09/python-out-720x321.png 720w, https://www.lucd.info/wp-content/uploads/2017/09/python-out.png 799w" sizes="auto, (max-width: 300px) 100vw, 300px" /></a></p>
<h3>Other</h3>
<p>During the VMworld Breakout session, where this function was first demonstrated, we showed additional examples in the following video.</p>
<p><iframe loading="lazy" width="640" height="361" src="https://player.vimeo.com/video/300485769" frameborder="0" webkitallowfullscreen="webkitallowfullscreen" mozallowfullscreen="mozallowfullscreen" allowfullscreen="allowfullscreen"></iframe></p>
<p>Since there are many Linux flavours out there, and since I obviously couldn&#8217;t test them all, I would appreciate it if you can send me feedback about which Linux flavours/versions work, and which don&#8217;t.</p>
<p>If there are requests for other languages, feel free to forward me your requests.</p>
<p>&nbsp;</p>
<p>Enjoy!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.lucd.info/2017/09/14/invoke-vmscriptplus/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
	</channel>
</rss>
