I'm struggling to pass my arguments to my custom made function.
This works fine:
Delete-OldFiles -Target $Target -OlderThanDays $OlderThanDays -LogName Auto_Clean.log
These don't work at all:
# (using -ScriptBlock ${Function:Delete-OldFiles} for all calls)
Invoke-Command -ArgumentList ( ".$Target", ".$OlderThanDays", ".$LogName Auto_Clean.log")
Invoke-Command -ArgumentList #("$Target", "$OlderThanDays", "Auto_Clean.log")
Invoke-Command -ArgumentList #({-OlderThanDays "10", -Target "E:\Share\Dir1", -LogName "Auto_Clean.log"})
Invoke-Command -ArgumentList (,#('$Target','$OlderThanDays','Auto_Clean.log'))
Can you help me on how to pass the following parameters correctly:
Param(
[Parameter(Mandatory=$False,Position=1)]
[String]$Server,
[Parameter(Mandatory=$True,Position=2)]
[ValidateScript({Test-Path $_})]
[String]$Target,
[Parameter(Mandatory=$True,Position=3)]
[Int]$OlderThanDays,
[Parameter(Mandatory=$True,Position=4)]
[String]$LogName,
[switch]$CleanFolders
)
When you use Invoke-Command -ArgumentList arguments are passed to script block as positional parameters. Looking at you param() block: it was not designed with using positional parameters in mind... Having optional parameters first, and mandatory after means you always need to provide optional parameter, or use named parameters for all mandatory ones.
In other words: having optional parameter in the first position will stop you from running this code in this fashion with Invoke-Command.
To test your code locally you should try it this way:
Delete-OldFiles $Target $OlderThanDays "Auto_Clean.log"
I would expect it to prompt you for LogName parameter.
Related
I have a script that I can run remotely via Invoke-Command
Invoke-Command -ComputerName (Get-Content C:\Scripts\Servers.txt) `
-FilePath C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1
As long as I use default parameters, it works fine. However, the script has 2 named [switch] parameters (-Debug and -Clear)
How can I pass the switched parameters via the Invoke-Command? I've tried the -ArgumentList but I'm getting errors so I must have the syntax wrong or something. Any help is greatly appreciated.
-ArgumentList is based on use with scriptblock commands, like:
Invoke-Command -Cn (gc Servers.txt) {param($Debug=$False, $Clear=$False) C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 } -ArgumentList $False,$True
When you call it with a -File it still passes the parameters like a dumb splatted array. I've submitted a feature request to have that added to the command (please vote that up).
So, you have two options:
If you have a script that looked like this, in a network location accessible from the remote machine (note that -Debug is implied because when I use the Parameter attribute, the script gets CmdletBinding implicitly, and thus, all of the common parameters):
param(
[Parameter(Position=0)]
$one
,
[Parameter(Position=1)]
$two
,
[Parameter()]
[Switch]$Clear
)
"The test is for '$one' and '$two' ... and we $(if($DebugPreference -ne 'SilentlyContinue'){"will"}else{"won't"}) run in debug mode, and we $(if($Clear){"will"}else{"won't"}) clear the logs after."
Without getting hung up on the meaning of $Clear ... if you wanted to invoke that you could use either of the following Invoke-Command syntaxes:
icm -cn (gc Servers.txt) {
param($one,$two,$Debug=$False,$Clear=$False)
C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 #PSBoundParameters
} -ArgumentList "uno", "dos", $false, $true
In that one, I'm duplicating ALL the parameters I care about in the scriptblock so I can pass values. If I can hard-code them (which is what I actually did), there's no need to do that and use PSBoundParameters, I can just pass the ones I need to. In the second example below I'm going to pass the $Clear one, just to demonstrate how to pass switch parameters:
icm -cn $Env:ComputerName {
param([bool]$Clear)
C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 "uno" "dos" -Debug -Clear:$Clear
} -ArgumentList $(Test-Path $Profile)
The other option
If the script is on your local machine, and you don't want to change the parameters to be positional, or you want to specify parameters that are common parameters (so you can't control them) you will want to get the content of that script and embed it in your scriptblock:
$script = [scriptblock]::create( #"
param(`$one,`$two,`$Debug=`$False,`$Clear=`$False)
&{ $(Get-Content C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 -delimiter ([char]0)) } #PSBoundParameters
"# )
Invoke-Command -Script $script -Args "uno", "dos", $false, $true
PostScript:
If you really need to pass in a variable for the script name, what you'd do will depend on whether the variable is defined locally or remotely. In general, if you have a variable $Script or an environment variable $Env:Script with the name of a script, you can execute it with the call operator (&): &$Script or &$Env:Script
If it's an environment variable that's already defined on the remote computer, that's all there is to it. If it's a local variable, then you'll have to pass it to the remote script block:
Invoke-Command -cn $Env:ComputerName {
param([String]$Script, [bool]$Clear)
& $ScriptPath "uno" "dos" -Debug -Clear:$Clear
} -ArgumentList $ScriptPath, (Test-Path $Profile)
My solution to this was to write the script block dynamically with [scriptblock]:Create:
# Or build a complex local script with MARKERS here, and do substitutions
# I was sending install scripts to the remote along with MSI packages
# ...for things like Backup and AV protection etc.
$p1 = "good stuff"; $p2 = "better stuff"; $p3 = "best stuff"; $etc = "!"
$script = [scriptblock]::Create("MyScriptOnRemoteServer.ps1 $p1 $p2 $etc")
#strings get interpolated/expanded while a direct scriptblock does not
# the $parms are now expanded in the script block itself
# ...so just call it:
$result = invoke-command $computer -script $script
Passing arguments was very frustrating, trying various methods, e.g.,
-arguments, $using:p1, etc. and this just worked as desired with no problems.
Since I control the contents and variable expansion of the string which creates the [scriptblock] (or script file) this way, there is no real issue with the "invoke-command" incantation.
(It shouldn't be that hard. :) )
I suspect its a new feature since this post was created - pass parameters to the script block using $Using:var. Then its a simple mater to pass parameters provided the script is already on the machine or in a known network location relative to the machine
Taking the main example it would be:
icm -cn $Env:ComputerName {
C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 -one "uno" -two "dos" -Debug -Clear $Using:Clear
}
I needed something to call scripts with named parameters. We have a policy of not using ordinal positioning of parameters and requiring the parameter name.
My approach is similar to the ones above but gets the content of the script file that you want to call and sends a parameter block containing the parameters and values.
One of the advantages of this is that you can optionally choose which parameters to send to the script file allowing for non-mandatory parameters with defaults.
Assuming there is a script called "MyScript.ps1" in the temporary path that has the following parameter block:
[CmdletBinding(PositionalBinding = $False)]
param
(
[Parameter(Mandatory = $True)] [String] $MyNamedParameter1,
[Parameter(Mandatory = $True)] [String] $MyNamedParameter2,
[Parameter(Mandatory = $False)] [String] $MyNamedParameter3 = "some default value"
)
This is how I would call this script from another script:
$params = #{
MyNamedParameter1 = $SomeValue
MyNamedParameter2 = $SomeOtherValue
}
If ($SomeCondition)
{
$params['MyNamedParameter3'] = $YetAnotherValue
}
$pathToScript = Join-Path -Path $env:Temp -ChildPath MyScript.ps1
$sb = [scriptblock]::create(".{$(Get-Content -Path $pathToScript -Raw)} $(&{
$args
} #params)")
Invoke-Command -ScriptBlock $sb
I have used this in lots of scenarios and it works really well.
One thing that you occasionally need to do is put quotes around the parameter value assignment block. This is always the case when there are spaces in the value.
e.g. This param block is used to call a script that copies various modules into the standard location used by PowerShell C:\Program Files\WindowsPowerShell\Modules which contains a space character.
$params = #{
SourcePath = "$WorkingDirectory\Modules"
DestinationPath = "'$(Join-Path -Path $([System.Environment]::GetFolderPath('ProgramFiles')) -ChildPath 'WindowsPowershell\Modules')'"
}
Hope this helps!
This is an unfortunate situation. Positional parameters work.
# test.ps1
param($myarg1, $myarg2, $myarg3)
"myarg1 $myarg1"
"myarg2 $myarg2"
"myarg3 $myarg3"
# elevated prompt
invoke-command localhost test.ps1 -args 1,$null,3
myarg1 1
myarg2
myarg3 3
Or you can hardcode a default.
# test2.ps1
param($myarg='foo2')
dir $myarg
invoke-command localhost test2.ps1
Cannot find path 'C:\Users\js\Documents\foo2' because it does not exist.
Or copy the script there:
$s = New-PSSession localhost
copy-item test2.ps1 $home\documents -ToSession $s
icm $s { .\test2.ps1 -myarg foo3 }
Cannot find path 'C:\Users\js\Documents\foo3' because it does not exist.
I have a script that I can run remotely via Invoke-Command
Invoke-Command -ComputerName (Get-Content C:\Scripts\Servers.txt) `
-FilePath C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1
As long as I use default parameters, it works fine. However, the script has 2 named [switch] parameters (-Debug and -Clear)
How can I pass the switched parameters via the Invoke-Command? I've tried the -ArgumentList but I'm getting errors so I must have the syntax wrong or something. Any help is greatly appreciated.
-ArgumentList is based on use with scriptblock commands, like:
Invoke-Command -Cn (gc Servers.txt) {param($Debug=$False, $Clear=$False) C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 } -ArgumentList $False,$True
When you call it with a -File it still passes the parameters like a dumb splatted array. I've submitted a feature request to have that added to the command (please vote that up).
So, you have two options:
If you have a script that looked like this, in a network location accessible from the remote machine (note that -Debug is implied because when I use the Parameter attribute, the script gets CmdletBinding implicitly, and thus, all of the common parameters):
param(
[Parameter(Position=0)]
$one
,
[Parameter(Position=1)]
$two
,
[Parameter()]
[Switch]$Clear
)
"The test is for '$one' and '$two' ... and we $(if($DebugPreference -ne 'SilentlyContinue'){"will"}else{"won't"}) run in debug mode, and we $(if($Clear){"will"}else{"won't"}) clear the logs after."
Without getting hung up on the meaning of $Clear ... if you wanted to invoke that you could use either of the following Invoke-Command syntaxes:
icm -cn (gc Servers.txt) {
param($one,$two,$Debug=$False,$Clear=$False)
C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 #PSBoundParameters
} -ArgumentList "uno", "dos", $false, $true
In that one, I'm duplicating ALL the parameters I care about in the scriptblock so I can pass values. If I can hard-code them (which is what I actually did), there's no need to do that and use PSBoundParameters, I can just pass the ones I need to. In the second example below I'm going to pass the $Clear one, just to demonstrate how to pass switch parameters:
icm -cn $Env:ComputerName {
param([bool]$Clear)
C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 "uno" "dos" -Debug -Clear:$Clear
} -ArgumentList $(Test-Path $Profile)
The other option
If the script is on your local machine, and you don't want to change the parameters to be positional, or you want to specify parameters that are common parameters (so you can't control them) you will want to get the content of that script and embed it in your scriptblock:
$script = [scriptblock]::create( #"
param(`$one,`$two,`$Debug=`$False,`$Clear=`$False)
&{ $(Get-Content C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 -delimiter ([char]0)) } #PSBoundParameters
"# )
Invoke-Command -Script $script -Args "uno", "dos", $false, $true
PostScript:
If you really need to pass in a variable for the script name, what you'd do will depend on whether the variable is defined locally or remotely. In general, if you have a variable $Script or an environment variable $Env:Script with the name of a script, you can execute it with the call operator (&): &$Script or &$Env:Script
If it's an environment variable that's already defined on the remote computer, that's all there is to it. If it's a local variable, then you'll have to pass it to the remote script block:
Invoke-Command -cn $Env:ComputerName {
param([String]$Script, [bool]$Clear)
& $ScriptPath "uno" "dos" -Debug -Clear:$Clear
} -ArgumentList $ScriptPath, (Test-Path $Profile)
My solution to this was to write the script block dynamically with [scriptblock]:Create:
# Or build a complex local script with MARKERS here, and do substitutions
# I was sending install scripts to the remote along with MSI packages
# ...for things like Backup and AV protection etc.
$p1 = "good stuff"; $p2 = "better stuff"; $p3 = "best stuff"; $etc = "!"
$script = [scriptblock]::Create("MyScriptOnRemoteServer.ps1 $p1 $p2 $etc")
#strings get interpolated/expanded while a direct scriptblock does not
# the $parms are now expanded in the script block itself
# ...so just call it:
$result = invoke-command $computer -script $script
Passing arguments was very frustrating, trying various methods, e.g.,
-arguments, $using:p1, etc. and this just worked as desired with no problems.
Since I control the contents and variable expansion of the string which creates the [scriptblock] (or script file) this way, there is no real issue with the "invoke-command" incantation.
(It shouldn't be that hard. :) )
I suspect its a new feature since this post was created - pass parameters to the script block using $Using:var. Then its a simple mater to pass parameters provided the script is already on the machine or in a known network location relative to the machine
Taking the main example it would be:
icm -cn $Env:ComputerName {
C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 -one "uno" -two "dos" -Debug -Clear $Using:Clear
}
I needed something to call scripts with named parameters. We have a policy of not using ordinal positioning of parameters and requiring the parameter name.
My approach is similar to the ones above but gets the content of the script file that you want to call and sends a parameter block containing the parameters and values.
One of the advantages of this is that you can optionally choose which parameters to send to the script file allowing for non-mandatory parameters with defaults.
Assuming there is a script called "MyScript.ps1" in the temporary path that has the following parameter block:
[CmdletBinding(PositionalBinding = $False)]
param
(
[Parameter(Mandatory = $True)] [String] $MyNamedParameter1,
[Parameter(Mandatory = $True)] [String] $MyNamedParameter2,
[Parameter(Mandatory = $False)] [String] $MyNamedParameter3 = "some default value"
)
This is how I would call this script from another script:
$params = #{
MyNamedParameter1 = $SomeValue
MyNamedParameter2 = $SomeOtherValue
}
If ($SomeCondition)
{
$params['MyNamedParameter3'] = $YetAnotherValue
}
$pathToScript = Join-Path -Path $env:Temp -ChildPath MyScript.ps1
$sb = [scriptblock]::create(".{$(Get-Content -Path $pathToScript -Raw)} $(&{
$args
} #params)")
Invoke-Command -ScriptBlock $sb
I have used this in lots of scenarios and it works really well.
One thing that you occasionally need to do is put quotes around the parameter value assignment block. This is always the case when there are spaces in the value.
e.g. This param block is used to call a script that copies various modules into the standard location used by PowerShell C:\Program Files\WindowsPowerShell\Modules which contains a space character.
$params = #{
SourcePath = "$WorkingDirectory\Modules"
DestinationPath = "'$(Join-Path -Path $([System.Environment]::GetFolderPath('ProgramFiles')) -ChildPath 'WindowsPowershell\Modules')'"
}
Hope this helps!
This is an unfortunate situation. Positional parameters work.
# test.ps1
param($myarg1, $myarg2, $myarg3)
"myarg1 $myarg1"
"myarg2 $myarg2"
"myarg3 $myarg3"
# elevated prompt
invoke-command localhost test.ps1 -args 1,$null,3
myarg1 1
myarg2
myarg3 3
Or you can hardcode a default.
# test2.ps1
param($myarg='foo2')
dir $myarg
invoke-command localhost test2.ps1
Cannot find path 'C:\Users\js\Documents\foo2' because it does not exist.
Or copy the script there:
$s = New-PSSession localhost
copy-item test2.ps1 $home\documents -ToSession $s
icm $s { .\test2.ps1 -myarg foo3 }
Cannot find path 'C:\Users\js\Documents\foo3' because it does not exist.
I tried to pass a hashtable to a scriptblock like this where $arg3 is my hashtable. However, it failed. How do I do it in the correct way?
It doesn't seem like it is passing anything to the script block.
$commandParameters.ComputerName = $ComputerName
$commandParameters.ScriptBlock = {
param(
[Parameter()]
[switch]$arg1 = $false,
[Parameter()]
[array]$arg2,
[Parameter()]
[hashtable]$arg3
)
enter code here
Doing something here
}
Invoke-Command #commandParameters -ArgumentList $arg1, #($arg2), $arg3
=================================================================
I found the answer myself and it works for me. This is how I build the associative array and then pass it to the script block.
I am not sure why, but I was using the dot notation ($hash.a.b) to reference the hash table in a function and it works, but it doesn't work for a script block. It looks like I need to use [ ] (e.g. $hash[a][b])in the script block.
$compADGroups = #{}
foreach ( $adGroup in $adGroups ) {
if ( $compADGroups.$computerNameGroup -eq $null ) {
$compADGroups[$computerName] = #{}
$compADGroups[$computerName]["Group"] = #{}
$compADGroups[$computerName]["Group"] = $hashString
}
}
$session = New-PSSession -ComputerName 'Computer1'
Invoke-Command -Session $session -ArgumentList $compADGroups -ScriptBlock { param($compADGroups) $compADGroups[$env:computername]["Group"]}
Get-PSSession | Remove-PSSession
Make sure you're using Invoke-Command correctly.
$ScriptBlock = {
param(
[Parameter(Mandatory=$True, Position=1)]
[hashtable]$myHashTable
)
# Code here
}
Invoke-Command -ScriptBlock $ScriptBlock -ArgumentList ([hashtable]$hashtable)
If you specify certain arguments for a scriptblock, make sure you also describe the position value, and more often than not whether it's mandatory. If you are trying to pass your hashtable in as the second argument in an implicitly defined array of arguments, write your scriptblock so that it takes the hashtable at that specific position.
For example,
$ScriptBlock= {
param(
[Parameter(Position=2)] # Take note of the position you set here
[hashtable]$myHashTable,
[Parameter(Position=1)]
[string]$myString,
[Parameter(Position=3)]
[int]$myInteger
)
# Do stuff
}
Invoke-Command -ScriptBlock $ScriptBlock -ArgumentList ($myString, $myHashTable, $myInteger);
# ^ variable is in second position
I need to write a function in PowerShell that receives a bunch of parameters, one of them being a ps1 file. I need to execute this file as part of my function code, but I don't know how to do that.
This is probably a very silly detail, but I failed horribly in trying to search for this.
This is my function at the moment. I tried using Invoke-Command there but it's not working:
Function Start-Dsc {
Param(
[Parameter(Mandatory = $true)]
[string] $configurationFile,
[Parameter(Mandatory = $true)]
[string] $configurationName,
[Parameter()]
[string] $configurationData,
[Parameter(Mandatory = $true)]
[string] $computerName
)
Begin {}
Process
{
Invoke-Command -Command "$configurationFile -ConfigurationData $configurationData";
Start-DscConfiguration -Path ".\$configurationName" -ComputerName $computerName -Verbose -Wait
}
End{}
}
UPDATE:
With Bacon Bits' help, I managed to make it work. The final script is a bit different than what I initially posted though. This is the final process block:
Process
{
Invoke-Command -FilePath $configurationFile -ComputerName 'localhost';
Invoke-Expression -Command "$configurationName -ConfigurationData $configurationData";
Start-DscConfiguration -Path ".\$configurationName" -ComputerName $computerName -Verbose -Wait
}
Arguments are a separate option in Invoke-Command. Try:
Invoke-Command -Command "$configurationFile" -ArgumentList "-ConfigurationData $configurationData";
You may also need to change -Command to -FilePath.
I have a script that I can run remotely via Invoke-Command
Invoke-Command -ComputerName (Get-Content C:\Scripts\Servers.txt) `
-FilePath C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1
As long as I use default parameters, it works fine. However, the script has 2 named [switch] parameters (-Debug and -Clear)
How can I pass the switched parameters via the Invoke-Command? I've tried the -ArgumentList but I'm getting errors so I must have the syntax wrong or something. Any help is greatly appreciated.
-ArgumentList is based on use with scriptblock commands, like:
Invoke-Command -Cn (gc Servers.txt) {param($Debug=$False, $Clear=$False) C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 } -ArgumentList $False,$True
When you call it with a -File it still passes the parameters like a dumb splatted array. I've submitted a feature request to have that added to the command (please vote that up).
So, you have two options:
If you have a script that looked like this, in a network location accessible from the remote machine (note that -Debug is implied because when I use the Parameter attribute, the script gets CmdletBinding implicitly, and thus, all of the common parameters):
param(
[Parameter(Position=0)]
$one
,
[Parameter(Position=1)]
$two
,
[Parameter()]
[Switch]$Clear
)
"The test is for '$one' and '$two' ... and we $(if($DebugPreference -ne 'SilentlyContinue'){"will"}else{"won't"}) run in debug mode, and we $(if($Clear){"will"}else{"won't"}) clear the logs after."
Without getting hung up on the meaning of $Clear ... if you wanted to invoke that you could use either of the following Invoke-Command syntaxes:
icm -cn (gc Servers.txt) {
param($one,$two,$Debug=$False,$Clear=$False)
C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 #PSBoundParameters
} -ArgumentList "uno", "dos", $false, $true
In that one, I'm duplicating ALL the parameters I care about in the scriptblock so I can pass values. If I can hard-code them (which is what I actually did), there's no need to do that and use PSBoundParameters, I can just pass the ones I need to. In the second example below I'm going to pass the $Clear one, just to demonstrate how to pass switch parameters:
icm -cn $Env:ComputerName {
param([bool]$Clear)
C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 "uno" "dos" -Debug -Clear:$Clear
} -ArgumentList $(Test-Path $Profile)
The other option
If the script is on your local machine, and you don't want to change the parameters to be positional, or you want to specify parameters that are common parameters (so you can't control them) you will want to get the content of that script and embed it in your scriptblock:
$script = [scriptblock]::create( #"
param(`$one,`$two,`$Debug=`$False,`$Clear=`$False)
&{ $(Get-Content C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 -delimiter ([char]0)) } #PSBoundParameters
"# )
Invoke-Command -Script $script -Args "uno", "dos", $false, $true
PostScript:
If you really need to pass in a variable for the script name, what you'd do will depend on whether the variable is defined locally or remotely. In general, if you have a variable $Script or an environment variable $Env:Script with the name of a script, you can execute it with the call operator (&): &$Script or &$Env:Script
If it's an environment variable that's already defined on the remote computer, that's all there is to it. If it's a local variable, then you'll have to pass it to the remote script block:
Invoke-Command -cn $Env:ComputerName {
param([String]$Script, [bool]$Clear)
& $ScriptPath "uno" "dos" -Debug -Clear:$Clear
} -ArgumentList $ScriptPath, (Test-Path $Profile)
My solution to this was to write the script block dynamically with [scriptblock]:Create:
# Or build a complex local script with MARKERS here, and do substitutions
# I was sending install scripts to the remote along with MSI packages
# ...for things like Backup and AV protection etc.
$p1 = "good stuff"; $p2 = "better stuff"; $p3 = "best stuff"; $etc = "!"
$script = [scriptblock]::Create("MyScriptOnRemoteServer.ps1 $p1 $p2 $etc")
#strings get interpolated/expanded while a direct scriptblock does not
# the $parms are now expanded in the script block itself
# ...so just call it:
$result = invoke-command $computer -script $script
Passing arguments was very frustrating, trying various methods, e.g.,
-arguments, $using:p1, etc. and this just worked as desired with no problems.
Since I control the contents and variable expansion of the string which creates the [scriptblock] (or script file) this way, there is no real issue with the "invoke-command" incantation.
(It shouldn't be that hard. :) )
I suspect its a new feature since this post was created - pass parameters to the script block using $Using:var. Then its a simple mater to pass parameters provided the script is already on the machine or in a known network location relative to the machine
Taking the main example it would be:
icm -cn $Env:ComputerName {
C:\Scripts\ArchiveEventLogs\ver5\ArchiveEventLogs.ps1 -one "uno" -two "dos" -Debug -Clear $Using:Clear
}
I needed something to call scripts with named parameters. We have a policy of not using ordinal positioning of parameters and requiring the parameter name.
My approach is similar to the ones above but gets the content of the script file that you want to call and sends a parameter block containing the parameters and values.
One of the advantages of this is that you can optionally choose which parameters to send to the script file allowing for non-mandatory parameters with defaults.
Assuming there is a script called "MyScript.ps1" in the temporary path that has the following parameter block:
[CmdletBinding(PositionalBinding = $False)]
param
(
[Parameter(Mandatory = $True)] [String] $MyNamedParameter1,
[Parameter(Mandatory = $True)] [String] $MyNamedParameter2,
[Parameter(Mandatory = $False)] [String] $MyNamedParameter3 = "some default value"
)
This is how I would call this script from another script:
$params = #{
MyNamedParameter1 = $SomeValue
MyNamedParameter2 = $SomeOtherValue
}
If ($SomeCondition)
{
$params['MyNamedParameter3'] = $YetAnotherValue
}
$pathToScript = Join-Path -Path $env:Temp -ChildPath MyScript.ps1
$sb = [scriptblock]::create(".{$(Get-Content -Path $pathToScript -Raw)} $(&{
$args
} #params)")
Invoke-Command -ScriptBlock $sb
I have used this in lots of scenarios and it works really well.
One thing that you occasionally need to do is put quotes around the parameter value assignment block. This is always the case when there are spaces in the value.
e.g. This param block is used to call a script that copies various modules into the standard location used by PowerShell C:\Program Files\WindowsPowerShell\Modules which contains a space character.
$params = #{
SourcePath = "$WorkingDirectory\Modules"
DestinationPath = "'$(Join-Path -Path $([System.Environment]::GetFolderPath('ProgramFiles')) -ChildPath 'WindowsPowershell\Modules')'"
}
Hope this helps!
This is an unfortunate situation. Positional parameters work.
# test.ps1
param($myarg1, $myarg2, $myarg3)
"myarg1 $myarg1"
"myarg2 $myarg2"
"myarg3 $myarg3"
# elevated prompt
invoke-command localhost test.ps1 -args 1,$null,3
myarg1 1
myarg2
myarg3 3
Or you can hardcode a default.
# test2.ps1
param($myarg='foo2')
dir $myarg
invoke-command localhost test2.ps1
Cannot find path 'C:\Users\js\Documents\foo2' because it does not exist.
Or copy the script there:
$s = New-PSSession localhost
copy-item test2.ps1 $home\documents -ToSession $s
icm $s { .\test2.ps1 -myarg foo3 }
Cannot find path 'C:\Users\js\Documents\foo3' because it does not exist.