Invoke a PSCmdlet from within a cmdlet

Question:

I have a PS-Module completely written in C#. It contains about 20 Cmdlets that are already in production. Some of these “share code”. Take this example:

I have a Cmdlet called InvokeCommitCommand that produces a “changeset”. This Cmdlet also publishes metadata of this changeset. I would now like to create a new Cmdlet called PublishCommitCommand that can be called independantly to execute the “publishing” of an already existing changeset. I would therefore like to refactor InvokeCommitCommand to make use of the new Cmdlet PublishCommitCommand and avoid code duplication.

More generally speaking … I am trying to invoke a cmdlet CommandB from cmdlet CommandA. They are defined as follows

I have a few options here. But none of them work.

1. Option

Invoke CommandB by creating an instance of it. That would’ve been my first guess. Like so:

Unfortunately that does not work. I get the exception:

Cmdlets derived from PSCmdlet cannot be invoked directly …

So … next option.

2. Option

Create an instance of PowerShell and run the command. Like so:

Unfortunately that doesn’t work either. This causes a new PowerShell instance to be created and therefore I loose all stream redirections I may have attached to the current PowerShell instance I am running in.

I know I can reuse the runspace. But using the same runspace does NOT save me from losing my redirections. If CommandB would call Write-Verbose "Huzzah!", I would not see that ‘Huzzah!’ anywhere.

In short: I need to run the CommandB in the same PS instance as CommandA

3. Option

Use a ScriptBlock. Like so:

That’s pretty nice. But the problem here is, that I have no means to pass any complex class arguments to the script block. If CommandB has a parameter of type … let’s say PSCredential, I have no easy way to pass that parameter to the script. If I had a PowerShell object, I could easily do

But I can not that with a ScriptBlock. True, I could use InvokeWithContext which allows me to pass variables to the scriptblock, but I would need to “wrap” each complex argument in a variable first… rather cumbersome.

Conclusion

Any ideas? The best thing would be if I somehow could – from inside CommandA get access to the current instance of PowerShell I am running in. I could then leverage option 2 without the issue of creating a new instance. But I do not know if that is even possible…

Answer:

The solution I came up with in the end is a helper class that implements the method suggested by PetSerAl. I use a ScriptBlock like in my 3rd option above, but with a few changes to make passing parameters less tedious.

So here is my helper class that does the job quite nicely:

This class basically provides two methods of invoking a cmdlet:

  1. You can use its static methods InvokeCommand and pass the name of the cmdlet plus any parameters as a Hashtable.
  2. Create an instance of the PsInvoker class and use AddArgument to add parameters and then use Invoke to run the cmdlet.

Thanks again to PetSerAl.

Source:

Invoke a PSCmdlet from within a cmdlet by licensed under CC BY-SA | With most appropriate answer!

Leave a Reply