Jaylen
Jaylen

Reputation: 40381

How to pass parameter to a PowerShell script using C#?

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

Answers (2)

mklement0
mklement0

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().

  • Also note that if the script path happened to contain spaces or embedded variable references, you'd have to use embedded quoting and call the result via &, 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

Mathias R. Jessen
Mathias R. Jessen

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

Related Questions