Search code examples
powershellpowershell-remoting

Working with Invoke-Command/Start-Job and PSRemoting


I'm working with a script that remotes into a series of servers and executes commands from a series of PoSh functions. I will need to retain the synchronous processing and would simply like to add the capability of performing an async operation on the function.

How can I transform this to be called as a job from a primary function using Start-Job or Invoke-Command -AsJob? These functions are part of a PowerShell module, if that makes a difference.

I've tried a couple of various examples I've seen here, but they do not seem to actually process the function.

For example, for the below function, I've tried:

foreach($s in $servers)
{
            if($lbFileLocation -eq $true)
            {
                #Rename-LoadBalancerFile -ServerName $server -Revert $false -filePath $filePath -cred $cred - #sync function works great
                Start-Job -Name 'RenLb'  -InitializationScript {Import-Module '.\Down.psm1'} $ScriptBlock -ArgumentList $server,$false,$lbFileLocation,$Cred | Out-Null
            }
 }

        Write-Host 'Waiting for LB rename.'
        While (Get-Job -Name 'RenLb' | where { $_.State -eq 'Running' } )
              {
                  Start-Sleep 1
              }

             Write-Host 'completed'

Original, synchronous function:

function Rename-LoadBalancerFile
{
    param
    (
        [string]
        [Parameter(Mandatory=$true)]
        $ServerName,
        [bool]
        [Parameter(Mandatory=$true)]
        $Revert,
        [string]
        [Parameter(Mandatory=$true)]
        $filePath,
        [PSCredential]
        [Parameter(Mandatory=$false)]
        $cred
    )

    $scriptBlock = {
        param
        (
            [bool]
            [Parameter(Mandatory=$true)]
            $Revert,
            [string]
            [Parameter(Mandatory=$true)]
            $filePath
        )

        if(Test-Path $filePath -eq $true)
        {
            Write-Host 'file tested true'
            if($Revert -eq $true)
            {
                $fileName = [IO.Path]::GetFileName($filePath)
                $directory = [IO.Path]::GetDirectoryName($filePath)

                Rename-Item -Path "$directory\file.txt" -NewName $fileName
            }
            else
            {
                Rename-Item -Path $filePath -NewName 'file.txt'
            }
        }
    }

    $session = New-PSSession -ComputerName $ServerName -Authentication Credssp -Credential $cred
    Invoke-Command -Session $session -ScriptBlock $scriptBlock -ArgumentList $Revert, $filePath
    Remove-PSSession $session
}

Solution

    1. Import-Module .\down.psm1 will probably fail because Start-Job doesn't have the same working directory as your script, so you need to either change the working directory or use absolute path to the module.
    2. What is $scriptblock in Start-Job? It's never created. Remember that $scriptblock needs to use $args[0] , $args[1] .. to access the values passed in using -ArgumentList or define the parameters (param($server,$filepath...)) in the beginning of the scriptblock.

    You could try something like this:

    #Get full path for module
    $module = Resolve-Path ".\Down.psm1"
    
    #Create scriptblock
    $scriptblock = { Rename-LoadBalancerFile -ServerName $args[0] -Revert $args[1] -filePath $args[2] -cred $args[3] }
    
    foreach($s in $servers)
    {
        Start-Job -Name "RenLb" -InitializationScript ([scriptblock]::Create("Import-Module $module")) -ScriptBlock $scriptblock -ArgumentList $s,$false,$lbFileLocation,$Cred | Out-Null
    }
    
    Write-Host 'Waiting for LB rename.'
    
    While (Get-Job -Name 'RenLb' | where { $_.State -eq 'Running' } )
    {
        Start-Sleep 1
    }
    
    Write-Host 'completed'
    

    I used ([scriptblock]::Create("Import-Module $module")) to generate the InitializationScript to make sure that the $module-variable was expanded to a filepath since $module isn't available in the new job-thread. You can replaced it with -InitializationScript { Import-Module c:\mymodule\down.psm1 } if you want to hardcode the modulepath.