Search code examples
powershellargument-passinginvoke-command

PowerShell Splatting the Argumentlist on Invoke-Command


How is it possible to use the parameters collected in a hash table for use with ArgumentList on Invoke-Command?

$CopyParams = @{
    Source      = 'E:\DEPARTMENTS\CBR\SHARE\Target'
    Destination = 'E:\DEPARTMENTS\CBR\SHARE\Target 2'
    Structure   = 'yyyy-MM-dd'
}
Invoke-Command -Credential $Cred -ComputerName 'SERVER' -ScriptBlock ${Function:Copy-FilesHC} -ArgumentList @CopyParams

Whatever I try, it's always complaining about the 'Source':

Cannot validate argument on parameter 'Source'. The "Test-Path $_" validation script for the argument with
 value "System.Collections.Hashtable" did not return true. Determine why the validation script failed

This blog talks about a similar problem, but I can't get it to work.

The same is true for a simple Copy-Item within Invoke-Command, example:

Invoke-Command -Credential $Cred -ComputerName 'SERVER' -ScriptBlock {Copy-Item} -ArgumentList @CopyParams

Invoke-Command : Missing an argument for parameter 'ArgumentList'. Specify a parameter of type 'System.Obj
ect[]' and try again.
At line:11 char:89
+ ... ck {Copy-Item} -ArgumentList @CopyParams

Thank you for your help.


Solution

  • 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.