1

Im trying to run a ps1 script out of the resource Folder in c#

Thanks to Yevhen Cherkes

.NET Get embedded Resource File

Load C# Embedded Resource Path into PowerShell Command Class

Got me closer.

Changing

private void button1_Click_1(object sender, EventArgs e)
        {
            PowerShell ps = PowerShell.Create();
            string skriptpfad = $@"C:\Users\username\Desktop\1Tool_Final\WinformsTool\bin\Debug\zuordnung.ps1";
            ps.AddCommand(skriptpfad);
            ps.AddParameter("Source", folderBrowserDialog1.SelectedPath);
            ps.AddParameter("Destination", folderBrowserDialog2.SelectedPath);
            ps.Invoke();
        }

to

private void button1_Click_1(object sender, EventArgs e)
    {
        var assembly = Assembly.GetExecutingAssembly();
        var resourceName = "zuordnung_test.Resources.zuordnung.ps1";

        using (Stream stream = assembly.GetManifestResourceStream(resourceName))
        using (StreamReader reader = new StreamReader(stream))
        {
            string skriptpfad = reader.ReadToEnd();
            PowerShell ps = PowerShell.Create();
            //string skriptpfad = $@"C:\zuordnung.ps1";
            ps.AddCommand(skriptpfad);
            ps.AddParameter("Source", folderBrowserDialog1.SelectedPath);
            ps.AddParameter("Destination", folderBrowserDialog2.SelectedPath);
            ps.Invoke();
        }
    }

This only throws an exception with the whole ps script - not known as cmdlet - though, it does read it but addcommand ran the script before. Is it simply AddScript now or am i still approaching it wrong ?

Thanks in Advance

  • 2
    I would suggest if at all possible, you convert your ps1 to C# to save a lot of hassle. If you are intending to deploy this you'll have hassles with setting powershell security beforehand – Nick.Mc Feb 02 '22 at 07:47
  • You must use ps.AddScript. https://stackoverflow.com/questions/11405384/how-to-pass-a-parameter-from-c-sharp-to-a-powershell-script-file/11405544 https://learn.microsoft.com/en-us/powershell/scripting/developer/hosting/adding-and-invoking-commands?view=powershell-7.2#addscript – Yevhen Cherkes Feb 02 '22 at 07:48
  • Thanks, I've got it to work with AddCommand though, pretty sure i mustnt – BlackArch.py Feb 02 '22 at 07:51
  • 2
    I think, using PowerShell files as embedded resources can solve your problem: https://stackoverflow.com/questions/44577716/net-get-embedded-resource-file – Yevhen Cherkes Feb 02 '22 at 07:54
  • 1
    One more link about embedded resources and PowerShell https://stackoverflow.com/questions/39359587/load-c-sharp-embedded-resource-path-into-powershell-command-class – Yevhen Cherkes Feb 02 '22 at 07:59
  • Thanks for those links, i've edited my question – BlackArch.py Feb 02 '22 at 08:52
  • 2
    @YevhenCherkes, just to be clear (the linked docs unfortunately don't make that clear): `.AddCommand()` is the right method for invoking `.ps1` files; using `.AddScript()` with a `.ps1` file's _content_ is a _limited workaround_ (which can be a shortcut if you don't want to manage execution policies). – mklement0 Feb 02 '22 at 16:53

2 Answers2

3

Let me add some background information to your own (effective) answer:

Note: Your problem came down to having to switch from .AddCommand() for script-file execution (.ps1) to .AddScript() for source-code string execution (from a resource). The following focuses mainly on when to use .AddCommand() vs. .AddScript() for script-file execution.

  • .AddCommand() is the right PowerShell SDK method for invoking script files (.ps1) and other command forms, namely cmdlets, functions, and external programs.

    • A potential problem is that the effective PowerShell execution policy may prevent execution of script files. On pristine machines running Windows desktop editions, script-file execution is by default disabled altogether (Restricted), whereas it is RemoteSigned on server machines.

    • Assuming the effective execution policy doesn't prevent script-file execution via GPOs (Group Policy Objects), you can solve this problem by setting the execution policy for the current PowerShell session (only), via the SDK; RemoteSigned (browser-downloaded files must have a valid signature) is usually the safe choice:

      // Create an initial default session state.
      var iss = InitialSessionState.CreateDefault2();
      // Set its script-file execution policy (for the current session only).
      iss.ExecutionPolicy = Microsoft.PowerShell.ExecutionPolicy.RemoteSigned;
      
      // Create a PowerShell instance with a runspace based on the 
      // initial session state.
      PowerShell ps = PowerShell.Create(iss);
      
      // ...
      // Now, .AddCommand() should work fine.
      ps.AddCommand(skriptpfad);
      // ...
      
      • If a GPO-controlled execution policy prevents you from setting the session's execution policy (or you simply want to avoid having to set the session execution policy explicitly), you can use .AddScript() as workaround, as discussed next - but do note that is has limitations and side effects.
  • The somewhat confusingly named .AddScript() method is for invoking PowerShell source code provided directly as a string, the content of a script file, so to speak, or, as in your case, a piece of PowerShell code retrieved from a resource embedded in an assembly.

    • Therefore, as long as a given script file's content (code) doesn't (directly or indirectly[1]) invoke other script files, you can use .AddScript() to bypass PowerShell's execution policies (which apply only to script files).

    • That is, as shown in your answer, you can read a .ps1 file's content into a string in memory up front, and pass that string to .AddScript(); the simplest way to dot that is to use System.IO.File.ReadAllText:

      ps.AddScript(File.ReadAllText(skriptpath));
      // Call ps.AddParameter() as needed.
      
    • Caveat: A side effect of this technique is that the script-file body invoked this way will be unaware of the script file's original file name and location.


[1] Note that some modules automatically execute .ps1 files during their import, and that the module import itself may happen automatically.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    Wow. Thanks for this explanation, really helped me understanding because you included some clear statements, which those docs were missing out on. – BlackArch.py Feb 03 '22 at 10:51
2
private void button1_Click_1(object sender, EventArgs e)
    {
        var assembly = Assembly.GetExecutingAssembly();
        var resourceName = "%projectname%.Resources.%script.ps1%";

        using (Stream stream = assembly.GetManifestResourceStream(resourceName))
        using (StreamReader reader = new StreamReader(stream))
        {
            string skriptpath = reader.ReadToEnd();
            PowerShell ps = PowerShell.Create();
            ps.AddScript(skriptpath);
            //ps.AddParameter...
            ps.Invoke();
        }
    }

is the solution for my question. I included the solutions of the mentioned above threads and changed AddCommand to AddScript ( idk why addcommand worked with reading it from the local file and doesnt in this case )