Ash
Ash

Reputation: 3246

How can I get and set variables in the current runspace? (cmdlet based modules)

I am attempting to write a cmdlet based PowerShell module for an application we have. After the initial connection cmdlet, I want to store values somewhere while the user has their current PowerShell window open. One of the values is an auth key that I create from the user's credentials and then is required for future calls, without providing credentials for every action.

I thought it might be easy to get and set these in a custom object from C#, but I am struggling to get it to apply to the current runspace. I will admit, that I am pretty new to C# too.

Here is an example of my most successful attempt in a minimal form i.e. this will evaluate as true, however, it's not the same runspace. If I call $item in PowerShell, it obviously doesn't know what I am going on about.

using System.Management.Automation.Runspaces;

namespace Test
{
    public class VariableTest
    {
        public bool CreateVariable()
        {
            var runSpace = RunspaceFactory.CreateRunspace();
            runSpace.Open();

            runSpace.SessionStateProxy.SetVariable("item", "FooBar");
            var a = runSpace.SessionStateProxy.PSVariable.GetValue("item");

            if (a.ToString() == "FooBar")
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}

If I was writing this module in PowerShell, I'd probably just save them in a global variable. Can anyone help with getting and setting these values so I can call item in the PowerShell console? Or is this terribly wrong and something exists to already account for persistent module wide variables within C# that I have missed.

Upvotes: 3

Views: 2165

Answers (2)

Ash
Ash

Reputation: 3246

Many thanks to Mathias above for his guidance on this. Here is a minimal example for anyone else who comes looking for this.

Connect cmdlet

using System.Management.Automation;

namespace Test
{
    [Cmdlet(VerbsCommunications.Connect, "TestSystem")]
    public class VariableTest : PSCmdlet
    {
        private string _item;
        public string Item
        {
            get { return _item; }
            set { _item = value; }
        }

        protected override void BeginProcessing()
        {
            base.BeginProcessing();
        }

        protected override void ProcessRecord()
        {
            Item = "FooBar";
        }

        protected override void EndProcessing()
        {
            SessionState.PSVariable.Set(new PSVariable(nameof(Item), Item, ScopedItemOptions.Private));
        }
    }
}

Other cmdlet

using System.Management.Automation;

namespace Test
{
    [Cmdlet(VerbsCommunications.Read, "TestVariable")]
    public class ReadVariable : PSCmdlet
    {
        protected override void BeginProcessing()
        {
            base.BeginProcessing();
        }

        protected override void ProcessRecord()
        {
            var test = SessionState.PSVariable.Get("Item");
            WriteObject(test.Value.ToString());
        }
    }
}

Result

PS C:\> Connect-TestSystem
PS C:\> Read-TestVariable
FooBar

Upvotes: 1

Mathias R. Jessen
Mathias R. Jessen

Reputation: 174505

Assuming your Connect-* cmdlet inherits PSCmdlet, you can access variables through this.SessionState.PSVariable:

[Cmdlet("Connect", "SomeSystem")]
public class ConnectCmd : PSCmdlet
{
    protected override void EndProcessing()
    {
        SessionState.PSVariable.Set(new PSVariable("varName", valueGoesHere, ScopedItemOptions.Private));
    }
}

Now, any cmdlet running in the same runspace can retrieve and read the variable:

[Cmdlet("Get", "Stuff")]
public class GetCmd : PSCmdlet
{
    protected override void EndProcessing()
    {
        WriteObject(SessionState.PSVariable.Get("varName").Value);
    }
}

Please note that the Private option is not a visibility or scope modifier, but rather ensures that no one can overwrite the variable value.

Upvotes: 6

Related Questions