简体   繁体   中英

How to call PowerShell Cmdlet from a binary (C#) module?

I am writing a custom binary (C#) Cmdlet, and from within this CmdLet I would like to call another PowerShell binary Cmdlet (ex. Get-ADUser) and bring the results back into my Cmdlet. What is the best way to accomplish this? Note: it seems that creating another instance of PowerShell (as described here ) inside my custom Cmdlet is not the most efficient way to accomplish this.

I looked at this question. However, it does not answer my question.

If you can reference the class that implements your cmdlet you can 'call' the cmdlet by creating an instance of the class, set any properties that represent parameters and call the Cmdlet.Invoke<T>() method. Here's an example:

using System.Linq;
using System.Management.Automation;

namespace MyModule.Commands
{

    [Cmdlet(VerbsLifecycle.Invoke, "AnotherCmdlet")]
    public class InvokeAnotherCmdlet : Cmdlet
    {
        [Parameter(Mandatory = true)]
        public string Username { get; set; }

        protected override void ProcessRecord()
        {
            GetADUserCommand anotherCmdlet = new GetADUserCommand() {
                // Pass CommandRuntime of calling cmdlet to the called cmdlet (note 1)
                CommandRuntime = this.CommandRuntime, 

                // Set parameters
                Username = this.Username
            };

            // Cmdlet code isn't ran until the resulting IEnumerable is enumerated (note 2)
            anotherCmdlet.Invoke<object>().ToArray();
        }
    }

    [Cmdlet(VerbsCommon.Get, "ADUser")]
    public class GetADUserCommand : Cmdlet
    {
        [Parameter(Mandatory = true)]
        public string Username { get; set; }

        protected override void ProcessRecord()
        {
            WriteVerbose($"Getting AD User '{Username}'");
        }
    }
}

A couple of things to note:

  1. You may wish to pass the value of the Cmdlet.CommandRuntime property of the calling Cmdlet object to the called Cmdlet object. This will ensure that, if your called cmdlet writes to object streams (eg by calling WriteObject ) those objects will make their way to the host. The alternative is for the calling cmdlet to enumerate the result of calling the Invoke<T>() method on the calling cmdlet.
  2. Calling the Invoke<T>() method on the calling cmdlet doesn't immediately invoke the cmdlet as the method name might suggest. It instead returns a IEnumberable<T> object. Enumerating the object will invoke the command.

You are right that the proper way is to create an instance of PowerShell . A strongly-typed approach would be

PowerShell ps = PowerShell.Create();
ps.AddCommand(new CmdletInfo("Get-Cmdlet", typeof(GetCmdletCommand)));

While there are merits to calling PowerShell from C#, if you are tempted to do so likely you should re-think your approach because you are likely following an anti-pattern. It would be much wiser to make it so that your cmdlets "fit" together by matching the output of one to the input of another and call them in a pipeline. A much better answer from another question describes the design choice of cmdlet "units" that get glued together.

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