Search code examples
powershellinvoke-command

Invoke-Command in Workflow sequence


Please help to get Invoke-Command working. It says -ScriptBlock parameter is null. It seems RegHomePage() function is not available in InlineScript{}.

function RegHomePage (){
get-item -path Registry::"HKEY_USERS\*\Software\Microsoft\Internet Explorer\Main" | `
Get-ItemProperty | Where-Object {$_."Start Page" -ne $null} | Set-ItemProperty -Name "Start Page" -Value "about:blank"
}

$creds = Get-Credential -Credential value\wadmin
workflow foreachtest
{
    param([Parameter(mandatory=$true)][string[]]$computers)

    foreach -parallel -throttlelimit 20 ($computer in $computers)
    {
        sequence
        {
            $isPing = Test-Connection -count 1 $computer -quiet
            if($isPing){                
                $isWSMan  = [bool](Test-WSMan $computer -ErrorAction SilentlyContinue)
            }
            if($isWSMan){
                InlineScript{
                    Invoke-Command -ComputerName $USING:computer -ScriptBlock ${function:RegHomePage}
                    } -PSComputerName $computer
                echo "$computer OK"
            }
            Else{
                $Workflow:out += "$computer`r`n"
                echo "$computer FAILED"
            }       

        }

    }
    Out-File .\offline.txt -InputObject $out
}

foreachtest -computers (Get-Content .\comps.txt)

Solution

  • Seems to have a few issues with this inlineScript block.

    • Dont provide the PSComputerName parameter since you are already running a job on each computer. There is no need to reference other systems here.
    • I would suggest using Write-Output instead of echo (use powershell native commands)
    • Move the function within the inlinescript to bring it in scope of each iteration.
    workflow testing {
        foreach -parallel ($computer in $computers) {
            sequence {
                inlinescript {
                    function RegHomePage {
                        Get-Item -path Registry::"HKEY_USERS\*\Software\Microsoft\Internet Explorer\Main" | `
                        Get-ItemProperty | Where-Object {$_."Start Page" -ne $null} | Set-ItemProperty -Name "Start Page" -Value "about:blank"
                    }
                    Invoke-Command -ComputerName $using:computer -ScriptBlock ${Function:RegHomePage}
                }
            }
        }
    }
    

    Following is what I tested with.

    workflow testingWF {
        Param ([string[]] $computers)
    
        foreach -parallel ($computer in $computers) {
            sequence {
                InlineScript {
                    function testFunc {
                      Param($comp)
                      Write-Output "$($comp.split('.')[0]) == TestFunc"
                    }
    
                    Invoke-Command -ComputerName $Using:computer -ScriptBlock ${Function:testFunc} -ArgumentList $using:computer
    
                }
            }
        }
    }
    
    testingWF serverFQDN1,serverFQDN2
    
    #Prints
    server1 == TestFunc
    server2 == TestFunc
    

    Suggestion on how to re-write the above code

    Instead of using a workflow to run a parallel foreach loop, i would recommend replacing the functionality with -AsJob.

    foreach($computer in $computers) {
        Invoke-Command -ComputerName $computer -ScriptBlock ${Function:RegHomePage} -AsJob
    }
    
    # Remove Jobs when done
    Get-Job | Wait-Job | Remove-Job