Let’s assume that I have this PowerShell script block

$block={
    param($Message)
    Write-Host "$($env:COMPUTERNAME) says $Message"
}

The reason is that I’m developing this script that execute both on my computer but also remotely. I wish do this for a couple of reasons

  • Debug and develop the script blocks
  • Give the scripts to a another person and he executes them locally

If my script is driven by a $ComputerName parameter, then I need to replicate conditions such as

if ($ComputerName)
{
    Invoke-Command -ComputerName $ComputerName -ScriptBlock $block -ArgumentList "Hello"
}
else
{
    Invoke-Command -ScriptBlock $block -ArgumentList "Hello"
}

I don’t like this. It makes the scripts noisy and it forces me to replicate certain script patterns. Therefore I developed a small cmdlet that wraps the Invoke-Command.

<#
    .SYNOPSIS
        Wraps the Invoke-Command to seamlessy execute script blocks remote or local
    .DESCRIPTION
        Invoke-Command does not provide a transparent method to execute script blocks locally or remotely without conditions. This limited wrapper commandlet does this.
        Every blocked is wrapped with logging statements
    .PARAMETER  ScriptBlock
        The script block
    .PARAMETER  BlockName
        Name of the block. This is for logging purposes.
    .PARAMETER  ArgumentList
        Arguments for the script block.
    .PARAMETER  Computer
        Target computer
    .PARAMETER  Session
        Target session
    .EXAMPLE
        $block={
            param($Message)
            Write-Host "$($env:COMPUTERNAME) says $Message"
        }
        Invoke-CommandWrap -ComputerName @("EXAMPLE01","EXAMPLE02") -BlockName "Saying hello" -ScriptBlock $block -ArgumentList "Hello"

        VERBOSE: [Saying hello] Begin on EXAMPLE01 EXAMPLE02
        EXAMPLE01 says Hello
        EXAMPLE02 says Hello
        VERBOSE: [Saying hello] Finish on EXAMPLE01 EXAMPLE02

    .EXAMPLE
        $block={
            param($Message)
            Write-Host "$($env:COMPUTERNAME) says $Message"
        }
        $session=@("EXAMPLE01","EXAMPLE01")|New-PSSession
        Invoke-CommandWrap -Session $session -BlockName "Saying hello" -ScriptBlock $block -ArgumentList "Hello"

        VERBOSE: [Saying hello] Begin on EXAMPLE02 EXAMPLE01
        EXAMPLE02 says Hello
        EXAMPLE01 says Hello
        VERBOSE: [Saying hello] Finish on EXAMPLE02 EXAMPLE01

    .EXAMPLE
        $block={
            param($Message)
            Write-Host "$($env:COMPUTERNAME) says $Message"
        }
        Invoke-CommandWrap -BlockName "Saying hello" -ScriptBlock $block -ArgumentList "Hello"

        VERBOSE: [Saying hello] Begin local
        LOCALHOST says Hello
        VERBOSE: [Saying hello] Finish local
    .LINK
        Invoke-Command
#>
Function Invoke-CommandWrap {
    param (
        [Parameter(Mandatory=$true)]
        $ScriptBlock,
        [Parameter(Mandatory=$true)]
        $BlockName,
        [Parameter(Mandatory=$false)]
        [Parameter(ParameterSetName="Local")]
        [Parameter(ParameterSetName="Computer")]
        [Parameter(ParameterSetName="Session")]
        $ArgumentList=$null,
        [Parameter(Mandatory=$true,ParameterSetName="Computer")]
        $ComputerName=$null,
        [Parameter(Mandatory=$true,ParameterSetName="Session")]
        $Session=$null
    ) 

    if($Session)
    {
        Write-Debug "Targetting remote session $($session.ComputerName)"
        Write-Verbose "[$BlockName] Begin on $($session.ComputerName)"
        Invoke-Command -Session $Session -ScriptBlock $ScriptBlock -ArgumentList $ArgumentList
        Write-Verbose "[$BlockName] Finish on $($session.ComputerName)"
        return
    }
    if($ComputerName)
    {
        Write-Debug "Targetting remote computer $ComputerName"
        Write-Verbose "[$BlockName] Begin on $ComputerName"
        Invoke-Command -ComputerName $ComputerName -ScriptBlock $ScriptBlock -ArgumentList $ArgumentList
        Write-Verbose "[$BlockName] Finish on $ComputerName"
        return
    }
    Write-Debug "Targetting local"
    Write-Verbose "[$BlockName] Begin local"
    Invoke-Command -ScriptBlock $ScriptBlock -ArgumentList $ArgumentList
    Write-Verbose "[$BlockName] Finish local"
}

Code is also available in gist

By using this commadlet I’m allowed to do

Invoke-CommandWrap -ComputerName $ComputerName -ScriptBlock $block -ArgumentList "Hello" -BlockName "Saying hello"

The -BlockName parameter is to group logging together. It’s like it gives a name to the script block being executed.

For example the following script

$block={
    param($Message)
    Write-Host "$($env:COMPUTERNAME) says $Message"
}
#With multiple computers
Invoke-CommandWrap -ComputerName @("EXAMPLE01","EXAMPLE02") -BlockName "Saying hello" -ScriptBlock $block -ArgumentList "Hello"

#With multiple sessions
$session=@("EXAMPLE01","EXAMPLE01")|New-PSSession
Invoke-CommandWrap -Session $session -BlockName "Saying hello" -ScriptBlock $block -ArgumentList "Hello"

#Local
Invoke-CommandWrap -BlockName "Saying hello" -ScriptBlock $block -ArgumentList "Hello"

will output

VERBOSE: [Saying hello] Begin on EXAMPLE01 EXAMPLE02

EXAMPLE01 says Hello

EXAMPLE02 says Hello

VERBOSE: [Saying hello] Finish on EXAMPLE01 EXAMPLE02

VERBOSE: [Saying hello] Begin on EXAMPLE02 EXAMPLE01

EXAMPLE02 says Hello

EXAMPLE01 says Hello

VERBOSE: [Saying hello] Finish on EXAMPLE02 EXAMPLE01

VERBOSE: [Saying hello] Begin local

LOCALHOST says Hello

VERBOSE: [Saying hello] Finish local

Leave a Comment