Reputation: 40381
I am trying to execute a PowerShell script using C#
My script requires parameters to run otherwise, it'll throw an error.
Here is a stripped down version of my script
param ($url)
if ($url -eq $null) {
throw "No Url was provided"
}
write-host "$url was the provided value"
Now, using C#
I am trying to execute the script as follow
try {
var defaultSessionState = InitialSessionState.CreateDefault();
defaultSessionState.ExecutionPolicy = Microsoft.PowerShell.ExecutionPolicy.Unrestricted;
using PowerShell ps = PowerShell.Create(defaultSessionState);
ps.AddScript(@"d:\test.ps1");
ps.AddParameter("url", "http://example.com/test/");
ps.Runspace.SessionStateProxy.SetVariable("ErrorActionPreference", "stop");
}
catch (Exception e) {
}
But I keep getting No Url was provided
error. How can I correctly pass the parameter to my script, and how to access it?
Upvotes: 2
Views: 3663
Reputation: 440037
Mathias R. Jessen's helpful answer provides an effective solution, but let me expand on the explanation:
The PowerShell SDK's .AddScript()
method is somewhat poorly named in that it doesn't execute a script file, but an arbitrary piece of PowerShell code. That is, it expects a string containing entire PowerShell statements, in other words: the contents of a script file - not the script file's path.
Given that in PowerShell a piece of reusable code is represented as a script block, something like .AddScriptBlock()
would be a less confusing name (though note that you must not enclose the string containing the statements to execute in { ... }
when you call .AddScript()
).[1]
Mathias's answer shows how .AddCommand()
is the right method to use, because a command in PowerShell is anything that represents something that can be directly executed: aliases, functions, cmdlets, external executables - and also script files.
As for what you tried:
String d:\test.ps1
is interpreted as a piece of PowerShell code (the same way it would be if you submitted this string on the command line). That code happens to call a script file, but does so without arguments.
That is, your script was called, but without the parameter you added via .AddParameter()
.
&
, the call operator; e.g.,.AddScript(@"& 'd:\test 1.ps1'");
- see this answer to a closely related question for details.However, note that your .AddParameter()
call technically still worked, but it was the script block as a whole that received the parameter, not the (one) statement inside it - the call to d:\test.ps1
.[2]
Technically, it would have been possible to relay the arguments to the script-file invocation, by way of splatting the automatic $args
variable (in which all arguments are collected, in the absence of explicit parameter declarations via a param(...)
block):
ps.AddScript(@"d:\test.ps1 @args");
That said, if all your code does is to invoke a single script file (with arguments), the .AddCommand()
solution is both simpler and more efficient.
As an aside: .AddScript()
, unlike .AddCommand()
, is not subject to the executing machine's PowerShell execution policy, assuming that the statements passed don't try to invoke script files; that is, statements composed only of expressions and commands other than script files (*.ps1
) execute irrespective of what execution policy is in effect, though note that some modules automatically try to execute script files on import.
[1] .AddScript("...")
, expressed in PowerShell code, effectively does the following:
. { ... }
That is, it parses the string as a script block, and executes it via .
, the dot-sourcing operator (when .Invoke()
is later called).
[2] As stated, what you pass to .AddScript()
is parsed into a script block, and script blocks - like functions and script files - can accept arguments, both positionally via the automatic $args
variable and - with explicitly declared parameters via a param(...)
block - named. To use a simple example: .AddScript('Write-Output $args[0]').AddArgument('foo')
is the equivalent of . { Write-Output $args[0] } 'foo'
Upvotes: 2
Reputation: 174855
AddScript()
is for executing raw code - D:\test.ps1
happens to be valid code, but it is more meant for dropping in a full self-contained script as a string.
You'll want to add the script file reference as a command, at which point we can apply parameters - for this, use AddCommand()
instead:
using (PowerShell ps = PowerShell.Create(defaultSessionState))
{
ps.AddCommand("d:\test.ps1");
ps.AddParameter("url", "http://example.com/test/");
ps.AddParameter("ErrorAction", "Stop");
// or
ps.Runspace.SessionStateProxy.SetVariable("ErrorActionPreference", "stop");
}
Upvotes: 2