PowerShell Splatting the Argumentlist on Invoke-Command

和自甴很熟 提交于 2019-12-01 18:14:53
Ben

One-liner, to convert a remote script to accept named parameters from a hash.

Given a scriptblock which you wish to call like this:

$Options = @{
    Parameter1 = "foo"
    Parameter2 = "bar"
}

Invoke-Command -ComputerName REMOTESERVER -ArgumentList $Options -ScriptBlock {
    param(
        $Parameter1,
        $Parameter2
    )
    #Script goes here, this is just a sample
    "ComputerName: $ENV:COMPUTERNAME"
    "Parameter1: $Parameter1"
    "Parameter2: $Parameter2"
} 

You can convert it like so

Invoke-Command -Computername REMOTESERVER -ArgumentList $Options -ScriptBlock {param($Options)&{
    param(
        $Parameter1,
        $Parameter2
    )
    #Script goes here, this is just a sample
    "ComputerName: $ENV:COMPUTERNAME"
    "Parameter1: $Parameter1"
    "Parameter2: $Parameter2"
} @Options}

What's going on? Essentially we've wrapped the original script block like so:

{param($Options)& <# Original script block (including {} braces)#> @options }

This makes the original script block an anonymous function, and creates the outer script block which has a parameter $Options, which does nothing but call the inner script block, passing @options to splat the hash.

Here's one way to approach passing named parameters:

function Copy-FilesHC 
{
  param ($Source,$Destination,$Structure)
  "Source is $Source"
  "Desintation is $Destination"
  "Structure is $Structure"
  }


$CopyParams = @{
    Source      = 'E:\DEPARTMENTS\CBR\SHARE\Target'
    Destination = "'E:\DEPARTMENTS\CBR\SHARE\Target 2'" #Nested quotes required due to embedded space in value.
    Structure   = 'yyyy-MM-dd'
}

$SB = [scriptblock]::Create(".{${Function:Copy-FilesHC}} $(&{$args}@CopyParams)")

Invoke-Command -Credential $Cred -ComputerName 'SERVER' -ScriptBlock $SB

Basically, you create a new script block from your invoked script, with the parameters splatted to that from the hash table. Everything is already in the script block with the values expanded, so there's no argument list to pass.

I found a workaround, but you have to make sure that your Advanced function which is located in your module file is loaded up front in the local session. So it can be used in the remote session. I wrote a small helper function for this.

Function Add-FunctionHC {
    [CmdletBinding(SupportsShouldProcess=$True)]
    Param(
        [String]$Name
    )
    Process {
        Try {
            $Module = (Get-Command $Name -EA Stop).ModuleName
        }
        Catch {
            Write-Error "Add-FunctionHC: Function '$Name' doesn't exist in any module"
            $Global:Error.RemoveAt('1')
            Break
        }
        if (-not (Get-Module -Name $Module)) {
            Import-Module -Name $Module
        }
    }
}

# Load funtion for remoting
Add-FunctionHC -Name 'Copy-FilesHC'

$CopyParams = @{
    Source      = 'E:\DEPARTMENTS\CBR\SHARE\Target\De file.txt'
    Destination = 'E:\DEPARTMENTS\CBR\SHARE\Target 2'
}

$RemoteFunctions = "function Copy-FilesHC {${function:Copy-FilesHC}}" #';' seperated to add more

Invoke-Command -ArgumentList $RemoteFunctions -ComputerName 'SERVER' -Credential $Cred -ScriptBlock {
    Param (
        $RemoteFunctions
    )
    . ([ScriptBlock]::Create($RemoteFunctions))
    $CopyParams = $using:CopyParams
    Copy-FilesHC @CopyParams
}

The big advantage is that you don't need to copy your complete function in the script and it can stay in the module. So when you change something in the module to the function it will also be available in the remote session, without the need to update your script.

I recently experienced a similar problem and solved it by building the hash (or rebuilding the hash) inside the invoke by leveraging the $using variable scope (more on that here)

it looks something like this:

$Source      = 'E:\DEPARTMENTS\CBR\SHARE\Target'
$Destination = 'E:\DEPARTMENTS\CBR\SHARE\Target 2'
$Structure   = 'yyyy-MM-dd'

Invoke-Command -Credential $Cred -ComputerName 'SERVER' -ScriptBlock {
    $CopyParms= @{ 
        'Source'=$Using:Source
        'Destination'=$Using:Destination
        'Structure'=$Using:Structure
    }
    Function:Copy-FilesHC @CopyParms
}

This is what works for me:

$hash = @{
    PARAM1="meaning of life"
    PARAM2=42
    PARAM3=$true
}
$params = foreach($x in $hash.GetEnumerator()) {"$($x.Name)=""$($x.Value)"""}

I know this is late, but I ran into the same problem and found a solution that worked for me. Assigning it to a variable within the scriptblock and then using that variable to splat didn't show any problems.

Here's an example:

$param=@{"parameter","value"}
invoke-command -asjob -session $session -ScriptBlock {$a=$args[0];cmdlet @a } -ArgumentList $param
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!