2

I'm trying to setup Powershell ISE to manage few VMs hosted under Hyper-V and using Powershell Direct.

What I'm trying to do is:

  1. collect in one script all relevant "settings" under variables whose name starts with an appropriate prefix, for example "vms";

  2. at the end of this script open a PowerShellTab for each VM to manage (if not already opened);

  3. copy in the runspace of this new Tab all the relevant settings variables (I think this is necessary for the next step);

  4. start a remote PSSession with the VM (if not already started) and copy or update the settings variables also in this remote PSSession;

  5. enter interactively this PSSession to be able to launch my commands (F8) after selecting portions of sample/template scripts.

My script is this one.

$vms = 'VMName1','VMName2'
$vmsCredentials = @{
    VMName1 = [pscredential]::new('User1', (ConvertTo-SecureString 'pw1' -AsPlainText -force))
    VMName2 = [pscredential]::new('User2', (ConvertTo-SecureString 'pw2' -AsPlainText -force))
}

$vmsInfo1 = 'blah,blah,blah'
$vmsInfo2 = @{}
$vmsInfo3 = @(1,2,3)

$tabPrevious = $psISE.CurrentPowerShellTab

$vms |% {

        Write-Information $_
        $tab = $psISE.PowerShellTabs |? DisplayName -eq $_

        if ($tab -eq $null)
        {
            # opens new PS ISE Tab
            $tab = $psISE.PowerShellTabs.Add()
            $psISE.PowerShellTabs.SetSelectedPowerShellTab($tabPrevious)
            $tab.DisplayName = $_
            $tab.ExpandedScript = $true
            $tab.Files.Clear()

            # open all files in new tab 
            $tabPrevious.Files |% FullPath |? { Test-Path $_ } |% {
                [void]$tab.Files.Add($_)
            }
        }
        else
        {
            # exit remote interactive session, if any 
            if ($tab.Prompt -match "^\[$_\]: ") {
                while (!$tab.CanInvoke) { sleep -Milliseconds 100 }
                $tab.Invoke('exit')
            }
        }

        # export/update variables to tab
        while (!$tab.CanInvoke) { sleep -Milliseconds 100 }
        $rs = ($tab.InvokeSynchronous({ $ExecutionContext.Host.Runspace }, $false))[0]
        if ($null -ne $rs) {
            Get-Variable "vms*" |% { $rs.SessionStateProxy.SetVariable($_.Name, $_.Value) }            
        }

        # start a new remote PSSession for the tab, if not already done
        while (!$tab.CanInvoke) { sleep -Milliseconds 100 }
        $tab.Invoke("if (`$null -eq `$pchPSSession) { `$pchPSSession = New-PSSession -VMName '$_' -Credential `$vmsCredentials['$_'] }")

        # export/update variables to the remote PSSession and enter interactively
        while (!$tab.CanInvoke) { sleep -Milliseconds 100 }
        $tab.Invoke("Invoke-Command `$pchPSSession { `$args |% { Set-Variable `$_.Name `$_.Value } } -ArgumentList (Get-Variable 'vms*'); Enter-PSSession -Session `$pchPSSession")

        # uncomment this line and no error occurs
        #[void]$psISE.PowerShellTabs.Remove($tab)
    }

Unfortunately this script works well:

  • the very first time I run the script in a fresh ISE,
  • or when only one new Tab need to be opened,
  • or if I close immediately the just added Tab (uncomment last line),
  • or under Debugger

otherwise the two last PowerShellTab.Invoke fails (Null Reference).

Any idea to solve this error? Any way to do it better?

unlikely
  • 398
  • 2
  • 8

0 Answers0