Using the code below, a powershell process is spawned and the runspace opens properly. From here I can submit the powershell commands to create and import a remote powershell session. The issue is this new session loads and caches all of the imported modules into the host process and not the process spawned by the CreateOutOfProcessRunspace(...).Open() with no way of garbage collecting the cached modules.
PowerShellProcessInstance instance = new PowerShellProcessInstance(new Version(5, 1), null, null, true);
var runspace = RunspaceFactory.CreateOutOfProcessRunspace(new TypeTable(new string[0]), instance);
runspace.Open();
Using an initialization script for the powershell instance gets stuck on the runspace.Open(). As far as I can tell, it never even times out. I have let it run for a good 5 minutes (when it shouldn't take longer than 20 seconds to pull the remote modules) and the state of the runspace is still Opening.
string script = string.Join("\r\n", new string[] {
"$Username = \"mailbox\"",
"$SecurePass = ConvertTo-SecureString -AsPlainText password -Force",
"$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Username,$SecurePass",
"$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "url" -Credential $Credential -Authentication Basic -AllowRedirection",
"Import-PSSession $session"
});
PowerShellProcessInstance instance = new PowerShellProcessInstance(new Version(5, 1), null, ScriptBlock.Create(script), true);
var runspace = RunspaceFactory.CreateOutOfProcessRunspace(new TypeTable(new string[0]), instance);
runspace.Open();
Does anyone know of a way to isolate the remote powershell session into its own process and invoke it from a c# application?
For a work around, I am planning to isolate the powershell session into its own process by creating a separate executible that accepts Powershell commands and returns results. That way I can close that process and free memory when powershell is not in use.
Related
I have been working on this for days now and didn´t find a solution.
I´m trying to write a little programm to create some AD-User with attributes provided by a csv file.
Creating the AD-User itself works fine, but when it comes to the activation of the exchange mailbox, it fails.
The mailbox should be enabled with the samaaccountname. The database should be found and set by the manager mailadress. (The new mailbox schould be placed in the same database as the one from the managers).
When I run the script from the Powershell itself, it runs without any errors and does exactly what it is supposed to do. But wen called from my c# program, nothing happens.
My Powershellscript looks like this:
Param(
[string] $manager,
[string] $sam
)
$session = New-Pssession -ConfigurationName Microsoft.Exchange -ConnectionURI http://<FQDN>/Powershell/ -Authentication Kerberos
Import-Pssession $session -WarningAction:Ignore -disablenamechecking |out-null
$mb = get-mailbox $manager
Enable-Mailbox $sam -Database $mb.Database
and its called by this c# method:
public void mail_Box(string sam, string manager)
{
try
{
RunspaceConfiguration runconf = RunspaceConfiguration.Create();
Runspace run = RunspaceFactory.CreateRunspace(runconf);
run.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = run;
ps.AddScript("<pathToSkript>").AddParameter("manager", manager).AddParameter("sam", sam);
ps.Invoke();
}
catch (Exception e)
{
popup(e.Message);
}
}
And at this point I´m totaly stuck.
I tried to "build" the script within c# with preloaded exchange-powershell-module
PSSnapInException PSException = null;
PSSnapInInfo info = runconf.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.SnapIn", out PSException);
with the AddCommand and AddParameter methodes but this gave me snapin registration errors, or in case of using
AddScript(". $env:ExchangeInstallPath\\bin\\RemoteExchange.ps1")
i got errors like "Enable-Mailbox" is not a Cmdlet.
As you may see I´m not a developer, just a sysadmin trying to get things automated.
So I try to invoke a PS script from a ASPX/C# application. When I run the PS script from commandline (PS C:\scripts> ./do-barrelRoll.ps1 -s remoteComputer) it works as expected. But when I use C# it cannot connect to the remote computer (wrong user/password). Fun fact: The user and password to use are inside a config section in the PS script itself (see $SETUP variable)! What am I doing wrong?
Exact error message from ASPX (the server name is correct!):
[remoteComputer] Connecting to remote server remoteComputer failed with the following error message : The user name or password is incorrect
Relevant parts of the PS Script:
set-alias new New-Object
$passwd = $SETUP["password"] | ConvertTo-SecureString -asplaintext -force
$session = new -TypeName System.Management.Automation.PSCredential -argumentlist $SETUP["user"], $passwd
Invoke-Command –ComputerName $PARAM["s"] -Credential $session –ScriptBlock {
# Do a barrel roll
}
Relevant parts of ASPX/C# application:
using System.Management.Automation;
public ActionResult doAction(string server, string action)
{
var split = server.Split(';');
//...
var ps = PowerShell.Create();
ps.AddScript("Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned -f; C:\\scripts\\do-barrelRoll.ps1 -s " + split[1]);
Collection<PSObject> results = ps.Invoke();
// Here I retrive the results and the content of the error stream and display them
}
It works as expected at the command line most likely because your account has full script access and can run everything. The account that .NET runs under should be more constrained. You'll have to check that the account has rights to run unrestricted scripts. Check local policies and then go up from there. You most likely are not the same account that the .NET application is running as.
I am trying to run a power shell script from the c#.
When running the power shell script only, it runs successfully. But , while trying to run the same script from the c# . I get the error "The term 'New-CsOnlineSession' is not recognized as the name of a cmdlet"
Here is the code:
public static void GetLyncUsers(string userName, string password)
{
using (PowerShell powerShellInstance = PowerShell.Create())
{
var script = string.Format("$Username =\"{0}\"\n" +
"$Password =\"{1}\"\n" +
"$secpasswd = ConvertTo-SecureString $Password -AsPlainText -Force\n" +
"$cred = new-Object System.Management.Automation.PSCredential ($Username , $secpasswd)\n" +
"$CSSession = New-CsOnlineSession -Credential $cred\n" +
"Import-PSSession $CSSession -AllowClobber\n" +
"Get-CsOnlineUser", userName, password);
// use "AddScript" to add the contents of a script file to the end of the execution pipeline.
// use "AddCommand" to add individual commands/cmdlets to the end of the execution pipeline.
powerShellInstance.AddScript(script);
// use "AddParameter" to add a single parameter to the last command/script on the pipeline.
// invoke execution on the pipeline (collecting output)
Collection<PSObject> psOutput = powerShellInstance.Invoke();
// check the other output streams (for example, the error stream)
if (powerShellInstance.Streams.Error.Count > 0)
{
// I am getting this error
//The term 'New-CsOnlineSession' is not recognized as the name of a cmdlet
}
}
Is there anything i am missing? I am new to powershell in general.
Solution:
using (PowerShell powerShellInstance = PowerShell.Create())
{
// Import-Module lynconlineconnector
powershellInstance.Commands
.AddCommand("Import-Module")
.AddArgument("lynconlineconnector");
// rest of your code ....
Why?
When running an interactive session in powershell v3 and higher, the host traps CommandNotFound, and searches every module in all the known locations. If it finds the command, it automatically loads the module, and proceeds normally.
When running same script in C#, the CommandNotFound exception isn't trapped, and hence you get the error.
Related Question(s):
PowerShell - How to Import-Module in a Runspace
#PSTip Cmdlet Discovery and Module auto-loading
I ran into the same problem. You have to install the Lync/Skype For Business Online Connector as described on Technet
The setup program copies the Skype for Business Online Connector
module (and the New-CsOnlineSession cmdlet) to your local computer.
I'm attempting to execute a SQL Query from within Powershell, within C#. I have been successful in doing so with ActiveDirectory cmdlets and wanted to take it one step further.
My first issue is while the following format works with ActiveDirectory (and in the ISE) it fails in C#:
using (PowerShell pS = PowerShell.Create())
{
pS.AddCommand("import-module");
pS.AddArgument("sqlps");
pS.Invoke();
}
I've long since had the security set to Unrestricted, but the error I'm getting is:
CmdletInvocationException was unhandled
File C:\Program Files (x86)\Microsoft SQL Server\110\Tools\PowerShell\Modules\sqlps\Sqlps.ps1 cannot be loaded because running scripts is disabled on this system. For more information, see about_Execution_Policies at http://go.microsoft.com/fwlink/?LinkID=135170.
However, if I run like this I get no error, though a later "Get-Module -all" call shows no sign of the module:
using (PowerShell pS = PowerShell.Create())
{
pS.AddScript("Import-Module sqlps");
pS.Invoke();
}
If I then try importing the ActiveDirectory module and calling Get-Module, it shows nothing.
What's going on here?
I'm not that great with C sharp but when calling scripts from outside of powershell there is a flag when executing the program to bypass the execution policy, i.e.
powershell.exe -executionpolicy bypass -command "& '\\somepath\somescript.ps1' "
This allows remote scripts to be called, as even with unrestricted set I still found that it wanted to prompt for the execution of some scripts so for instance in the task scheduler it would simply fail to run.
Also when importing SQLPS I've also found it's useful to add the -DisableNameChecking flag, you may also want to push your location beforehand and pop it afterwards otherwise you will end up in the SQLPS PSdrive with no access to local locations if you need it.
Did you try something like this?
PowerShell ps = PowerShell.Create();
ps.AddScript("set-executionpolicy unrestricted -scope process");
ps.AddScript("import-module sqlps");
ps.AddScript("get-module sqlps");
var m = ps.Invoke();
foreach (var mm in m.Select(x => x.BaseObject as PSModuleInfo))
Console.WriteLine(new { mm.Name, mm.Version });
I had a similar issue with the sqlServer ps module. Looks like when executing from C# you need to load the modules manually into the runspace in order for this to work.
string scriptText = File.ReadAllText("yourScript.ps1");
//This is needed to use Invoke-sqlcommand in powershell. The module needs to be loaded into the runspace before executing the powershell.
InitialSessionState initial = InitialSessionState.CreateDefault();
initial.ImportPSModule(new string[] { #"SqlServer\SqlServer.psd1" });
Runspace runspace = RunspaceFactory.CreateRunspace(initial);
runspace.Open();
using (PowerShell psInstance = PowerShell.Create())
{
psInstance.Runspace = runspace;
psInstance.AddScript(scriptText);
var PSOutput = psInstance.Invoke();
}
Also add all the references located in the SqlServer.psd1. This file is usually found in "C:\Program Files\WindowsPowerShell\Modules\SqlServer". I added to folder to my solution to be able to execute on remote servers.
You need to add Microsoft.SqlServer.BatchParser.dll reference in order to execute invoke-sqlcommand from the Powershell.
You should be able to do the same for sqlps module. Rather use SqlServer as it is newer.
I am looking to replace a Virtual Box solution with MS Hyper-V since I have had many problems with non-Reproducible issues in my automated test-suite using Virtual Box. I have a Windows 8.1 computer I will be using to run the tests on.
The current Virtual Box flow:
Start a VM
Reset snapshot
Use C# to transfer files to Guest OS through the network
Use Virtual Box to trigger the transferred .exe file to start automated tests.
I see people using Powershell Scripts and WMI to start and stop their Hyper-V VMs, but I don't see any way to trigger the transferred files on the Guest OS.
Am I missing an API that I can use? Otherwise how could I trigger the EXE on the guest OS programmatically?
I ended up using System.Management.Automation.PowerShell. I will share the main code chunk I used to do each step so future users can get help.
The Main Code Chunk
var ps = PowerShell.Create();
//Restore Snapshots
ps.AddCommand("Restore-VMSnapshot");
ps.AddParameter("Name", snapshot);
ps.AddParameter("VMName", vmName);
ps.AddParameter("Confirm", false);
ps.Invoke();
ps.Commands.Clear();
//Start VM
ps.AddCommand("Start-VM");
ps.AddParameter("Name", vmName);
ps.Invoke();
ps.Commands.Clear();
//Get IP
string[] ipValues = null;
do
{
ps.AddCommand("Get-VMNetworkAdapter");
ps.AddParameter("VMName", vmName);
var ips = ps.Invoke();
ps.Commands.Clear();
if (ips.Count > 0)
{
ipValues = (string[])ips[0].Members["IPAddresses"].Value;
}
} while (ipValues.Length ==0);
string ip = ipValues[0];
//Move Exe to VM
File.Copy(#"...", "\\\\" + ip + "\\Users\\Public\\Documents\\...", true);
//Run Program
ps.AddScript("$Username = '...'; $Password = '...' ;$ComputerName = '"+ip+"' ;"+
"$Script = {Start-Process C:\\Users\\Public\\Documents\\....exe} ;$secpasswd = ConvertTo-SecureString $Password -AsPlainText -Force ;"+
"$mycreds = New-Object System.Management.Automation.PSCredential ($Username, $secpasswd) ;"+
" $Session = New-PSSession -ComputerName $ComputerName -credential $mycreds ; Invoke-Command -Session $Session -Scriptblock $Script");
var passwords = ps.Invoke();
ps.Commands.Clear();
Notes
The //GetIP section is a do{}while() cause the IP takes a while to be query-able.
There is alot of pre-work required with the host computer and VMs to make this system function, which I will not get into here as google explains those parts better than me.
The flow is designed to match another system which uses Virtual Box, so it may seems a bit inefficient.
This obviously needs to be modified to fit each situation, but should be a good starting point for Hyper-V Automation.
A very usefull PowerShell CmdLet to transfert files to VM is Copy-VMFile.
Syntax is explained here :
http://technet.microsoft.com/en-us/library/dn464282.aspx
Hope this helps !