Powershell invoke-command ArgumentList with optional argument
Asked Answered
O

2

1

I have a function that works with the invoke-command function either without any arguments or with all arguments. What I would like to do is run the function via invoke-command with only 1 argument. Here is my code

function remove-oldfiles {
param ([String]$days = 28,
[string]$path = "c:\temp\archive")

if ( -not (test-path $path) ) {throw "path not found"}

$cmd = ((Get-ChildItem -Path $path -Recurse |
        where {$_.lastwritetime -lt (get-date).adddays(-$days) -and -not $_.psicontainer}))

$cmd
write-Host("do you want to delete the files? ") -ForegroundColor Red
$ans = (read-host).ToUpper()

if ($ans -eq 'Y') { 
    write-host("deleting files...") 
    $cmd | remove-item -Force
    }
}

Both of these commands work:

Invoke-Command -ComputerName remote_machine -ScriptBlock ${function:remove-oldfiles} -ArgumentList 90, "c:\temp\archive"
Invoke-Command -ComputerName remote_machine -ScriptBlock ${function:remove-oldfiles} 

But if I try and run it with only 1 argument it doesn't work. Tried:

Invoke-Command -ComputerName remote_machine -ScriptBlock ${function:remove-oldfiles} -ArgumentList '-path "c:\temp\archive"'
Invoke-Command -ComputerName remote_machine -ScriptBlock ${function:remove-oldfiles} -ArgumentList "c:\temp\archive"

Is it possible to use Invoke-Command with -ArgumentList with just some arguments and not all?

Orenorenburg answered 25/2, 2022 at 21:49 Comment(2)
"Doesn't work" doesn't give us much context to work with. However, the issue is clear, and Invoke-Command is not the problem. Your function has 2 parameters, where, positionally the first one is the $days parameter, in your second example which "doesn't work" you're passing a path to that parameter.Estes
-ArgumentList: The parameters in the script block are passed by position from the array value supplied to ArgumentList. This is known as array splatting.Estes
I
2

No, unfortunately Invoke-Command does not support passing named arguments via -ArgumentList / -Args, only positional ones - see this answer for details - and the same goes for other cmdlets that support this parameter, notably Start-Job / Start-ThreadJob and Start-Process.

Therefore, with -ArgumentList, in order to bind the -path parameter - the 2nd positional one - you invariably must also pass an argument for the 1st positional one, -days.

Workaround:

Invoke-Command -ComputerName remote_machine -ScriptBlock {
  param($funcBody)
  # Here you can use named arguments, as usual.
  # To include values from the caller's scope, use the $using: scope, e.g.,
  # to use the value of local variable $archivePath:
  #  -path $using:archivePath
  & ([scriptblock]::Create($funcBody)) -path c:\temp\archive
} -ArgumentList ${function:remove-oldfiles}

Note:

  • Even though ${function:remove-oldfiles} contains the body of function remove-oldfiles as a script block in the caller's scope, it turns into a string when passed through the remoting de/serialization infrastructure, and must therefore be rebuilt as a script block with [scriptblock]::Create() in the remote session in order to be callable with &.

  • In principle, you should be able to include the function body directly in the remotely executing script block, via a $using: reference, namely
    ${using:function:remove-oldfiles} in your case - however, there are two separate bugs that prevent that as of PowerShell Core 7.3.0-preview.2:

    • A parsing bug breaks the call if the function name happens to contain -, such as in your case.
    • For other function names, such a reference seemingly deserializes to the empty string in the remote session.
    • Curiously, Start-Job, which also uses the remoting de/serialization infrastructure, does not exhibit these problems.
Indore answered 25/2, 2022 at 23:2 Comment(0)
E
0

The param() block defines what the -ArgumentList passes to the -ScriptBlock. You are passing 2 elements into the param block of your function. The variables have assignments as well, which I believe is why the command works without the switch.

In order for the the -ArgumentList to accept one argument, you could try to move $days=28 outside the param block, such that it is still in the scope of the function, such as:

  param ([string]$path = "c:\temp\archive")
  $days = 28
Exposed answered 25/2, 2022 at 22:15 Comment(1)
thanks for the reply; I'll move the $path outside the param block. The only param I may want to change is days.Orenorenburg

© 2022 - 2024 — McMap. All rights reserved.