0

I am building some PowerShell cmdlets using the C#/.NET APIs and have a cmdlet that needs access to all the currently imported modules. If I run Get-Module in the terminal I can see 10+ modules loaded, but when my cmdlet runs:

using(var ps = PowerShell.Create(RunspaceMode.Current))
{
    var modules = ps.AddCommand("Get-Module").Invoke<PSModuleInfo[]>();
    /// ...
}

modules here is empty. Its almost like a scoping problem, but I thought RunspaceMode.Current would allow you to access that underlying runspace that was opened with you first open the pwsh shell.

I must be missing something or misunderstanding exactly how runspaces work.

3
  • 2
    "a cmdlet that needs access to all the currently imported modules" sounds like a code smell... what is it that you're trying to accomplish? Commented Nov 19, 2020 at 20:59
  • Well tbh this isn't exactly the code I am dealing with, just a basic example. However, my code does some dynamic PS module proxy generation and after my cmdlet is "done" I need it to clean up after itself. So I am trying to call "Remove-Module" on these dynamic modules and nothing seems to happen when that is ran from inside the cmdlet. It appears to be the same issue that "Get-Module" has though and its a simpler example which is why I used that in my question. Commented Nov 19, 2020 at 21:04
  • If I wrote my Cmdlet in PS directly this would all work, so I guess I don't understand why this doesn't work as soon as you try it in C#. Commented Nov 19, 2020 at 21:06

1 Answer 1

1

You're not supposed to mess around with the invoking runspace directly from inside a cmdlet - it's already busy executing your cmdlet. You also cannot invoke a PSCmdlet directly from another, for the same reason.

Instead, you'll want to use PSCmdlet.InvokeCommand.InvokeScript() to have the runtime execute a script for you (just like if you had a Get-Module statement in a PowerShell function):

[Cmdlet(VerbsCommon.Get, "ModuleProxy")]
public class GetModuleProxyCommand : PSCmdlet
{
    // ...

    protected override void EndProcessing()
    {
        foreach(var module in InvokeCommand.InvokeScript("Get-Module"))
        {
            WriteObject(module);
        }
    }
}

That being said, if what you're doing affects the environment in a way that requires you to execute Remove-Module or clean stuff up in the callers context, you're probably much better off doing your work in a separate runspace anyway :)

Sign up to request clarification or add additional context in comments.

3 Comments

Ok thanks that at least clears up "why" it doesn't seem to a let you do this. I am doing some very non-traditional things here with PS so its not as straightforward as one might think. These "extra" proxies are transient and meant to only act as a facade to provide caching of connection/auth. It's a long story, haha. But I will try to do this another way similar to what you stated. Thanks
@MattHintzke That doesn't sound especially non-traditional or weird at all - in fact it sounds a lot like JEA :)
Haha, ok well that is good. Since you seem like a PowerShell guru you might be interested in seeing what we are building. skykick.com/manage. Aims to help reduce the cognitive load of IT management as SaaS, IaaS, and PaaS becomes more complex.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.