3

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?

2
  • 2
    Change ps.AddScript("d:\test.ps1") to ps.AddCommand("d:\test.ps1") Commented May 18, 2020 at 22:56
  • 1
    [Docs](learn.microsoft.com/en-us/dotnet/api/… addScript takes in a string and executes that... considers the script to be executable in powershell (not a file) Commented May 19, 2020 at 3:09

2 Answers 2

3

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'

Sign up to request clarification or add additional context in comments.

2 Comments

Great answer. Passing parameters to a script, is it a feature that we can rely on? Or does it just happen to work? Can you point to the documentation for this feature? I really want to use it, but I am afraid it might break in the future.
Thanks, @MonticolaExplorator. Yes, you can rely on this feature. Please see the second footnote I've just added to the answer.
2

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");
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.