Error running Powershell script from c# - c#

I have a ps1 script than runs fine when it is executed from powershell. It creates a user in Office365:
Param(
[string]$adminUser,
[string]$password,
[string]$adminSite,
[string]$userDisplayName,
[string]$userFirstName,
[string]$userLastName,
[string]$userPrincipalName,
[string]$userLicense,
[string]$userOffice,
[string]$userDepartment
)
try {
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")
$executionPolicy = Get-ExecutionPolicy
Set-ExecutionPolicy RemoteSigned
$secpasswd = ConvertTo-SecureString $password -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($adminUser,$secpasswd)
Connect-MSolService -Credential $credential
#Write-Host "Conected to MSolService ..." -ForegroundColor Green
Connect-SPOService -Url $adminSite -Credential $credential # Here fail when running from .NET
#Write-Host "Conected to SP Online ..." -ForegroundColor Green
$user = New-MsolUser -FirstName $userFirstName -LastName $userLastName -UserPrincipalName $userPrincipalName -DisplayName $userDisplayName -LicenseAssignment $userLicenseAssignment -Office $userOffice -Department $userDepartment -UsageLocation ES
}catch [Exception] {
#Write-host "An Exception ocurred. The proccess is uncompleted" -ForegroundColor Red
#Write-Host $_.Exception.Message -ForegroundColor Red
Set-ExecutionPolicy $executionPolicy
return $false
}
Set-ExecutionPolicy $executionPolicy
return $user
It works.
However, I have a C# program that executes this script in this way:
private Collection<PSObject> RunPsScriptFromFile(string psScriptPath, Dictionary<string, Object> parameters) {
if (!File.Exists(psScriptPath)) {
throw new FileNotFoundException("File not found.", psScriptPath);
}
Collection<PSObject> returnObjects = null;
using (Runspace runSpace = RunspaceFactory.CreateRunspace()) {
runSpace.Open();
RunspaceInvoke runSpaceInvoker = new RunspaceInvoke(runSpace);
Pipeline pipeLine = runSpace.CreatePipeline();
Command cmd = new Command(psScriptPath, false);
if (parameters != null && parameters.Count > 0) {
foreach (KeyValuePair<string, Object> p in parameters) {
CommandParameter cp = new CommandParameter(p.Key, p.Value);
cmd.Parameters.Add(cp);
}
}
pipeLine.Commands.Add(cmd);
returnObjects = pipeLine.Invoke();
}
return returnObjects;
}
This program works fine with others scripts, but for this one, I get the following error (at the line I've marked in the script):
The 'Connect-SPOService' command was found in the module 'Microsoft.Online.SharePoint.PowerShell', but the module could not be loaded. For more information, run 'Import-Module Microsoft.Online.SharePoint.PowerShell'.
I found a question about this, but without answer:
Error running ps1 from c# code (Office 365)

I've modified my C# code:
pipeLine.Commands.Add(cmd);
returnObjects = pipeLine.Invoke();
var error = pipeLine.Error.ReadToEnd(); // New line
The "error" var contains the following:
The current processor architecture is X86. The 'C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.Online.SharePoint.PowerShell.psd1' module requires Amd64 architecture.
I've located this file and I've changed this line
# Processor architecture (None, X86, Amd64, IA64) required by this module
ProcessorArchitecture = 'Amd64'
for this one:
# Processor architecture (None, X86, Amd64, IA64) required by this module
ProcessorArchitecture = 'X86'
I don't know if it's a good solution, but it's works. I will keep looking.
Any suggestion is wellcome.

In C:\Users\<your user>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Windows PowerShell
There are two versions of powershell an x86 and a AMD64 helpfully with no suffix name.
Older versions of modules were written only in 32 bit, newer versions including the latest sharepoint online were written only in 64 bit.
If you start the environment in 32 bit it will obviously not run any 64 bit modules, and in a few edge cases you need to run the old 32 bit to run very old modules.
A better solution would be to invoke your powershell script
%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -command "script.ps1"
I would guess your finding the syswow 32 bit version first.

Related

Running Powershell in C# and Powershell is behaving differently

The image shows the same powershell command executed in a C# and Powershell ISE. My question is, Powershell shows complete list of process and process directory, but the result of the code executed in C# does not contain windows processes like svchost.exe.
Of course, both my C# Form and Powershell ISE are running as Administrator.
Also, I made sure to code my form to launch with administrative privileges.
To be double sure, I executed my C# by right click - Run as Administrator (what I also did with Powershell ISE)
Command/s: Powershell and C#
get-process | get-item -erroraction silentlycontinue | format-table name, directory
get-process | format-table name, directory
Although this particular command shows all the process:
get-process
Unfortunately, combining it with the syntax above will show different output from C#.
private string RunScript(string script)
{
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(script);
pipeline.Commands.Add("Out-String");
Collection<PSObject> results = pipeline.Invoke();
runspace.Close();
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject pSObject in results)
stringBuilder.AppendLine(pSObject.ToString());
return stringBuilder.ToString();
}
private void button1_Click(object sender, EventArgs e)
{
output.Clear();
output.Text = RunFilePs1(input.Text);
}
For some reason, Get-Process doesn't return the Path of some processes (probably the 64-bit ones) when running in 32-bit mode, so passing those to Get-Item fails to find anything.
You can see this by running your PS script from the x86 PS ISE.
So if you switch your C# Form to 64-bit, it will return all processes.

Powershell Invoke-Command ASPX/C# vs. Commandline

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.

The term 'New-CsOnlineSession' is not recognized as the name of a cmdlet

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.

Powershell SQLPS module not importing within C#

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.

How can I call the FailoverClusters PowerShell module when working from C#?

I’m trying to call a PowerShell script from C#, which is usually very straightforward, except that for some reason the commands in the FailoverClusters module can never be found when calling from C# (not in scope). Here’s what I’ve discovered:
The commands are always found when using an interactive PowerShell session:
PS C:\> Get-Cluster -Name DummyCluster
Name
----
DummyCluster
The commands are never found when using a local PowerShell session from C#:
var ps = PowerShell.Create();
ps.AddCommand("Get-Cluster");
ps.AddParameter("Name", "DummyCluster");
var r = ps.Invoke();
//Exception: command not found
I’ve tried about 6-7 different ways to import the module, and none of them worked. Here is one of them, taken from this tutorial on the subject:
var ps = PowerShell.Create();
var ss = InitialSessionState.CreateDefault();
var modules = new string[1]{"FailoverClusters"};
ss.ImportPSModule(modules);
var rs = RunspaceFactory.CreateRunspace(ss);
rs.Open();
var iv = new RunspaceInvoke(rs);
var r = iv.Invoke("Get-Cluster -Name DummyCluster");
//Exception: command not found
Interestingly, the commands are found when using a remote PowerShell session. This is a plausible work-around for certain use cases.
var ci = new WSManConnectionInfo(); //localhost remote connection
var rs = RunspaceFactory.CreateRunspace(ci);
rs.Open();
var iv = new RunspaceInvoke(rs);
var r = iv.Invoke("Get-Cluster -Name DummyCluster");
//Exception: access is denied
The FailoverClusters module is not visible from C#:
var ps = PowerShell.Create();
ps.AddCommand("Get-Module");
ps.AddParameter("ListAvailable");
ps.AddArgument("FailoverClusters");
var results = ps.Invoke();
Console.WriteLine(results.Count.ToString()); //prints 0
It is, however, visible from an interactive PowerShell session:
PS C:\> Get-Module -ListAvailable FailoverClusters
Directory: C:\windows\system32\WindowsPowerShell\v1.0\Modules
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Manifest 2.0.0.0 FailoverClusters {Add-ClusterCheckpoint, Add-ClusterDisk, Add-ClusterFileSe...
The FailoverClusters module is only available in 64-bit PowerShell sessions. Make sure that the C# DLL you're building is a 64-bit DLL.

Categories