简体   繁体   中英

Is it possible to run the PowerShell shell from C# and keep it running, while (synchronously) executing multiple PowerShell scripts within it?

Questions:

  1. Is it possible to run the PowerShell shell from C# and keep it running, while (synchronously) executing multiple PowerShell scripts within this shell from the same C# application?
  2. Alternatively, is it possible to execute scripts using Windows PowerShell (version 5) rather than the default PowerShell Core (version 7) when using the PowerShell class (from the System.Management.Automation.Powershell namespace) inside a .NET Core application?

Background:

We are rebuilding an existing .NET Framework 4.8 application ( current application ) into a new ASP.NET Core application ( new application ). In the current application, using the PowerShell class (in the System.Management.Automation.Powershell namespace), we execute a PowerShell script (which then executes within Windows PowerShell 5) which calls Exchange to start creating mailboxes (using the enable-remotemailbox command; Exchange is installed on the same server that both the current application runs on and the PowerShell script is executed). Every time (generally several dozen times a day) a mailbox should be created, the PowerShell script is executed this way.

Our experience with the current application is that only the first time an Exchange command (in this case enable-remotemailbox) runs, this takes around 15 seconds . As long as the C# application is running (or, outside the C# application, but within PowerShell and as long as the PowerShell shell is open), subsequent Exchange commands run very fast, taking ~200 ms (apparently due to some setting up / caching / ...? only the first time the script is executed).

Our wish is to achieve the same functionality with the new application:

  1. Make PowerShell run the existing script to create mailboxes.
  2. Reach similar speeds as in the current application.
  3. Have a stable and reliable functionality for creating mailboxes.

I first tried using the PowerShell class, like we use in the current application, but as Windows PowerShell (version 5) is built on .NET Framework, and PowerShell Core (version 7) is built on .NET Core, and the new application is a .NET Core application, when using the PowerShell class, we (apparently) automatically use PowerShell Core to execute scripts. However, I believe I can conclude that it is not possible to run the enable-remotemailbox command in PowerShell Core (in Windows PowerShell, after adding a snap-in, enable-remotemaibox was available in the remainder of the script, but snap-ins do not exist in PowerShell Core anymore, and I cannot find a module which replaced the snap-in).

Next, I tried using the Process class (from the System.Diagnostics.Process namespace), by starting a Windows PowerShell (version 5) process and execute the script within it. Although this works, it takes around 15 seconds every time the enable-remotemailbox command runs, rather than just the first time.

There is one alternative that will very likely meet all demands (ie also the speed demand), and we will use it when the answer to both my 2 questions above is "No", and that is to use a .NET Framework Windows Service which we will be able to call from the .NET Core application. However, this seems to involve a bit much overhead when it is not necessary; therefore, I am first looking for an answer to the 2 questions above.

The initial 15 seconds you experience could be caused by a missing Powershell Module in your session. The first time the ps executes, it will be loaded. hence the additional 15 seconds.

Using powershell sdk 7 you are now running a complete ps host. In your previous project you did only "attach" to ps5 using System.Management.Automation.Powershell assembly. That package uses you installed ps on the host-machine and this ps may already has the 15second module pre-loaded. That is why you experience this difference.

You can configure your powershell execution with preloading the module you require. here is a code snippet:

    // a singleton in my project
    public class Runspace
    {
        public RunspacePool Pool { get; }

        public Runspace()
        {
            var session = InitialSessionState.CreateDefault();
            session.ImportPSModule("ActiveDirectory");
            Pool = RunspaceFactory.CreateRunspacePool(session);
        }
    }

    public class PsExecutor
    {
        private readonly Runspace _runspace;

        public PsExecutor(Runspace runspace)
        {
            _runspace = runspace;
        }

        public void Exec()
        {
            using (PowerShell ps = PowerShell.Create())
            {
                ps.RunspacePool = _runspace.Pool;
                ps.AddStatement().AddCommand("Get-ADUser").AddParameters();
                ps.Invoke();
            }
    }

You can log the time consumed by the Constructor of Runspace. And compare it to the 15 Seconds you experience now. The Plus of the solution: You can control the execution of the constructor.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM