Reputation: 459
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.
Upvotes: 7
Views: 1297
Reputation: 6830
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:
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.
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 IEnumerable<T>
object. Enumerating the object will invoke the command.
Upvotes: 2
Reputation: 638
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.
Upvotes: 0