PowerShell Pass Named parameters to ArgumentList
Solution 1
One option:
$params = @{
P1 = 1
P2 = 2
P3 = 3
}
$ScriptPath = 'D:\Test.ps1'
$sb = [scriptblock]::create(".{$(get-content $ScriptPath -Raw)} $(&{$args} @params)")
Invoke-Command -ComputerName server -ScriptBlock $sb
Solution 2
The code by mjolinor works great, but it took me several minutes to understand it.
The code makes a simple thing - generates a content of script block with built-in parameters:
&{
Param (
[string]$P3,
[string]$P2,
[string]$P1
)
Write-Output "P1 Value:" $P1
Write-Output "P2 Value:" $P2
Write-Output "P3 Value:" $P3
} -P1 1 -P2 2 -P3 3
Then this script block is passed to Invoke-Command.
To simplify the code:
".{$(get-content $ScriptPath -Raw)} $(&{$args} @params)"
$scriptContent = Get-Content $ScriptPath -Raw
$formattedParams = &{ $args } @params
# The `.{}` statement could be replaced with `&{}` here, because we don't need to persist variables after script call.
$scriptBlockContent = ".{ $scriptContent } $formattedParams"
$sb = [scriptblock]::create($scriptBlockContent)
Let's make a basic C# implementation:
void Run()
{
var parameters = new Dictionary<string, string>
{
["P1"] = "1",
["P2"] = "2",
["P3"] = "3"
};
var scriptResult = InvokeScript("Test.ps1", "server", parameters)
Console.WriteLine(scriptResult);
}
string InvokeScript(string filePath, string computerName, Dictionary<string, string> parameters)
{
var innerScriptContent = File.ReadAllText(filePath);
var formattedParams = string.Join(" ", parameters.Select(p => $"-{p.Key} {p.Value}"));
var scriptContent = "$sb = { &{ " + innerScriptContent + " } " + formattedParams + " }\n" +
$"Invoke-Command -ComputerName {computerName} -ScriptBlock $sb";
var tempFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".ps1");
File.WriteAllText(tempFile, scriptContent);
var psi = new ProcessStartInfo
{
FileName = "powershell",
Arguments = $@"-ExecutionPolicy Bypass -File ""{tempFile}""",
RedirectStandardOutput = true,
UseShellExecute = false
};
var process = Process.Start(psi);
var responseText = process.StandardOutput.ReadToEnd();
File.Delete(tempFile);
return responseText;
}
The code generates a temporary script and executes it.
Example script:
$sb = {
&{
Param (
[string]$P3,
[string]$P2,
[string]$P1
)
Write-Output "P1 Value:" $P1
Write-Output "P2 Value:" $P2
Write-Output "P3 Value:" $P3
} -P1 1 -P2 2 -P3 3
}
Invoke-Command -ComputerName server -ScriptBlock $sb
Solution 3
Here's a simple solution:
[PowerShell]::Create().AddCommand('D:\test.ps1').AddParameters(@{ P1 = 1; P2 = 2; P3 = 3 }).Invoke()
Here's output:
PS C:\Windows\system32> [PowerShell]::Create().AddCommand('D:\test.ps1').AddParameters(@{ P1 = 1; P2 = 2; P3 = 3 }).Invoke()
P1 Value :
1
P2 Value:
2
P3 Value :
3
Solution 4
If you are trying to use the -FilePath with named parameters (-P1 1 -P2 2), then I found this will work. Use a script block to run the file, instead of the using -FilePath.
Invoke-Command -ComputerName server -ScriptBlock {& "D:\test.ps1" -P1 1 -P2 2 -P3 3}
Parveen Kumar
17+ years of IT experience. Worked extensively in designing, implementation and deployment of web applications/portals/SPA etc. primarily in Microsoft technologies. Sound experience in ASP.NET (C#)/.NET CORE, SQL Server/Entity Framework/MVC/Web API/ReactJS/Azure for multiple development projects. Experience in Client-interaction, handling challenging clients, and leading teams.
Updated on August 01, 2020Comments
-
Parveen Kumar over 3 years
I have a PowerShell script that accepts 3 named parameters. Please let me know how to pass the same from command line. I tried below code but same is not working. It assigns the entire value to P3 only. My requirement is that P1 should contain 1, P2 should 2 and P3 should be assigned 3.
Invoke-Command -ComputerName server -FilePath "D:\test.ps1" -ArgumentList {-P1 1 -P2 2 -P3 3}
Ihe below is script file code.
Param ( [string]$P3, [string]$P2, [string]$P1 ) Write-Output "P1 Value :" $P1 Write-Output "P2 Value:" $P2 Write-Output "P3 Value :" $P3
-
Parveen Kumar over 9 yearscan you please provide more details on this.
-
Parveen Kumar over 9 yearsYes I am able to achieve the result using the above code.
-
Parveen Kumar over 9 yearsCan you please let me know how to achieve the same using C# code as my requirement is to run this PowerShell script from C#.
-
mjolinor over 9 yearsSorry, don't know C# well enough to convert that for you.
-
CarlR about 7 yearsThis code works really well until you have an array as a parameter. When that happens you just get "System.String[]" as the value since the param block effectively gets a .ToString() applied to it when it gets expanded. I have yet to find a reliable way of avoiding this, if anyone has a solution i would love to see it!
-
mjolinor about 7 years@CarlR - Try P3 = "@('a','b','c')"
-
sephirot about 7 yearsIt works well. To pass string with spaces you have to wrap value with additional apostrophe. E.g.
P1 = "'My string parameter'"
-
Eric Eskildsen over 5 yearsThis doesn't work—the entire hashtable is passed as the first argument. The docs say the arguments need to be a comma-separated list (non-associative array) (source).
-
batbrat almost 5 yearsCould you please explain what
$(&{args})
does? I'm trying to understand how that combines with@params
to give you a string like "-P1 1 -P2 2 -P3 3". -
mjolinor almost 5 yearsWorking from the inside out, {$args} is a script block that simply returns whatever arguments it's been given. &{args} executes the script block. Wrapping it in $() - $(&{$args}) makes it a subexpression, so that it expands within the string. Because the argument given was a hash table, the output is a series of key/value pairs - the result of the splatting operation as it was passed to the script block.
-
Garric about 3 yearsOnly this code returned me the result from another external function
-
Garric about 3 yearsInvoke-Command was unable to return the win form to me as a result of execution
-
Garric about 3 yearsMy form was on over thread, but your code worked fine.
-
Garric about 3 yearsYes, more. Your solution doesn't break whitespace, unlike $sb = [scriptblock]::create(".{$(get-content $ScriptPath -Raw)} $(&{$args} @params)")
-
tommylux about 2 yearsI love this solution and your explanation is great. However, What if your variable is a string with spaces in, how can I encapsulate the args like so: "-P2 'This is P2' -P1 'This Is P1' -P3 'This is P3'"
-
tommylux about 2 yearsCan you better this? IF ($StrArgs -is [Hashtable]) { $formattedParams = Foreach ($i in $StrArgs.keys) { "-${i}:
"$($StrArgs.$i)
"" } } Else { $formattedParams = $StrArgs }