0

I have a simple function in C# to call a PowerShell command and return the output and it works fine. However I want to have to be able to return values for custom functions that I add to PowerShell. Here is my C# code:

Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
ps.Commands.AddScript("MyCustomCommand");
ICollection<PSObject> results = ps.Invoke();

When I run PowerShell locally I have the profiles set up in the location C:\Windows\System32\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1 so when I open PowerShell and input MyCustomCommand it returns a string:

Function MyCustomCommand {
[CmdletBinding()]
param()
    return "someoutput"
}

However when I make that same MyCustomCommand call from C# it's not found. How can I add this profile permanently in a way that C# will also return that same output when calling the command in the same way that happens when I open an instance of PowerShell on my desktop?

Update I used the modules solution recommended by @start-automating but I was not able to run due to the execution policy so I modified it:

ps.Runspace = runspace;
ps.AddCommand("Set-ExecutionPolicy").AddArgument("Unrestricted");
ps.AddCommand("Import-Module").AddArgument("MyNewModuleName");
ps.Commands.AddScript("MyCustomCommand");
2
  • Do you want your C# application to load the profile associated with powershell.exe at runtime, or do you want to embed your current profile script in the application? Commented Sep 28, 2022 at 19:33
  • I believe the first option. I basically have a built in ps command that I'm unable to test because it doesn't return anything but I want to be able to change the ps profile on a test machine so that it will override the default ps command and return some sample data so I can confirm the program is working correctly when data is returned Commented Sep 28, 2022 at 20:09

1 Answer 1

1

What's Going Wrong?

It can't run your command in C# because your command is not yet loaded.

Ok, So How Do You Load it?

You're already using a [Runspace]. This is good. The [Runspace] is what you'll need to load the function into. There are two ways to do this:

  1. Use [InitialSessionState]

This is the recommended route. [InitialSessionState] is used to populate the initial state of a PowerShell [Runspace]. Here you can load modules, declare commands, add variables, etc. The easiest way to handle things would be to have them be in a module in the first place.


InitialSessionState iss = InitialSessionState.CreateDefault2();
// Note: .CreateDefault2 is the recommended default of a session state
// Note: .CreateDefault reflects the default session state in v2-v4 timeframes
iss.ImportPSModule(new[] { "NameOfModule" });
iss.ImportPSModulesFromPath("PathToModule");
Runspace runspace = RunspaceFactory.CreateRunspace(iss);

You can also declare individual functions by hand, using a SessionStateFunctionEntry

InitialSessionState iss = InitialSessionState.CreateDefault2();
iss.Commands.Add(new SessionStateFunctionEntry("MyFunction","MyDefinition"));

Using either technique, this should populate your runspace with the commands you need.

  1. .AddCommand/.AddScript with useLocalScope set to $false

The other way you can approach this is by running a command that will change the [Runspace]. By default, when you .AddCommand or .AddScript to a [PowerShell] object, it will use a "local" scope. This means any changes you make (commands you add, modules you import, variables you set, etc) will be "locally scoped", and will not be available the next time you run in the [Runspace].

To make this work the way you'd want, to use .AddScript / .AddCommand with useLocalScope (the second argument), set to false.

Here's a quick PowerShell proof of concept:

[PowerShell]::Create().AddScript('$x = 1',$false).AddStatement().AddScript('$x').Invoke()

Hope this Helps

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

3 Comments

Hey thanks I think I'm close I created the module and attempted to import it but at the moment I'm getting an error: File C:\\Users\\User\\Documents\\WindowsPowerShell\\Modules\\Get-MpThreatDetection\\Get-MpThreatDetection.psm1 cannot be loaded because running scripts is disabled on this system. For more information, see about_Execution_Policies
Update I was unable to set execution policy as I had seen recommended elsewhere with iss.ExecutionPolicy because my iss object didn't have that property. I believe it's because I'm using the .Net Framework version (Microsoft.PowerShell.5.ReferenceAssemblies), I will update my answer with how I got around that
ExecutionPolicy is one of those gotchas that might make me use option #2 (running some commands). You can generally use Set-ExecutionPolicy -Scope Process to change the execution policy for the current process, without being admin.

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.