0

I am attempting to create a WPF application that will execute some powershell commands using a 3rd party module (ShareGate). After extensive research and banging my head on the keyboard, I have gotten the application to at least execute the cmdlets I have asked for. The cmdlet in question, if run in powershell, will prompt the user to log into a web service using edge I believe. When running the cmdlet from the application, it throws an error which is misleading "during the last update edge was not able to be installed...."

I think that this error is coming up because this implementation isn't allowing powershell to pop open the browser like it does within a powershell window.

My question is this: "How can I redirect the user prompt to come up within the wpf application? or can I?"

here is my method:

        public Task StartSGMigrations(IProgress<string> progress)
        {
            var sharegatePath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\Apps\\ShareGate\\Sharegate.Automation.dll";
            if (client != null)
            {
                try
                {
                    InitialSessionState iss = InitialSessionState.CreateDefault();
                    iss.ImportPSModule(new string[] { sharegatePath });
                    using (Runspace myRunSpace = RunspaceFactory.CreateRunspace(iss))
                    {
                        myRunSpace.Open();
                        using (PowerShell powershell = PowerShell.Create())
                        {
                            Dictionary<string, string> parameters = new Dictionary<string, string>();
                            powershell.AddScript("New-CopySettings -OnContentItemExists IncrementalUpdate");
                            powershell.AddScript("Connect-box -email " + _admin + " -admin");
                            powershell.AddScript("Connect-Site -Url \"https://xxxx-admin.sharepoint.com\" -Browser");

                            powershell.Runspace = myRunSpace;
                            var results = powershell.Invoke();
                            var errors = myRunSpace.SessionStateProxy.PSVariable.GetValue("Error");

                            foreach (var result in results)
                            {
                                progress.Report(result.ToString() + Environment.NewLine);
                            }
                        }
                        myRunSpace.Close();
                    }
                }
                catch (Exception ex)
                {
                    progress.Report(ex.Message);
                }
            }
            else
            {
                progress.Report("not connected to Box");
            }

            return Task.CompletedTask;
        }
    }

1 Answer 1

0

This will be fairly tricky to do if the commands you are calling do not support noninteractive execution.

What's going on:

Your application using the PowerShell api to call your scripts. This part's good. The problem is your scripts are using functionality of the PowerShell host (possibly prompting for credentials). Because the runspace is not associated with a host, any interactive capabilities will simply fail.

So you'd need to attach a host in order for it to work as expected (and that host would need to work the same as PowerShell.exe/pwsh.exe for whatever purposes your underlying cmdlets need).

If there were lots of implementations of a PowerShell host in the wild, I'd link you to them. Unfortunately, there are not. So unless you want to go down a deep rabbit hole, I'd suggest these alternatives:

  1. If the cmdlet supports providing credentials directly, try this
  2. If it does not, see if it "persists" credentials for a given user. (That is, open up a shell, login, close the shell, open another shell, and see if you can use the module without providing credentials).

If credentials do persist (and you can't do option 1), you should be able to call PowerShell.exe/pwsh.exe once to log in, and then load code normally.

If the credentials do not persist, you're stuck in a much more unfortunate situation, leaving you with paths 3,4, or 5:

  1. Call powershell.exe/pwsh.exe (hopefully in in a minimized window) and send the output back via JSON or CLIXML.

  2. Go down the rabbit hole and build yourself a host.

  3. Beg the cmdlet authors to better support noninteractive scenarios.

Between those options, I'd start with the last one.

Best of luck.

2
  • Connect-Box doesnt have a way to pass in the credentials or persist. Connect-Site does. there are 4 other calls I will have to make that I can pass in the credentials, unless MFA is enabled... option 5 seems the best, but I dont have time as im building this program to assist in an upcoming project. I assume that by building a host, im essentially recreating the console within my app? Commented Sep 8, 2022 at 2:47
  • No, building a host means implementing: PSHost (and all of the associated classes and methods). And even then, it's an expensive hopeful guess. (Connect-Box or Connect-Site might actually be checking that they're running in powershell.exe) Again, this will be tall order without the cmdlets supporting it directly (and not a guarantee of success) Commented Sep 8, 2022 at 3:50

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.