Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c#

powershell

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.

like image 425
immutableT Avatar asked Sep 08 '15 20:09

immutableT


1 Answers

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 (e.g. 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.
like image 72
Dan Stevens Avatar answered Oct 02 '22 02:10

Dan Stevens