I'm tying to use C# to connect to exchange and return the mailbox listing for a particular user. I am able to run this locally in PowerShell, however in the context of the script it's not showing what I expect.
I'm seeing outputs like tmp_1qf14mc3.wiv or tmp_ra1gbenl.ols instead of the mailbox listing.
Additional notes:
I have set up the reference to and imported the namespace of System.Management.Automation;
I'm running this through Visual Studio 2017, using the IIS Express build
This uses the .NET Core API project
.
using (PowerShell PowerShellInstance = PowerShell.Create())
{
// Attempt to get the mailbox listing for the user
PowerShellInstance.AddScript(
"$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList " + user + ",(ConvertTo-SecureString -String " + pass + " -AsPlainText -Force); " +
"$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $credential -Authentication Basic -AllowRedirection; " +
"Import-PSSession $Session -DisableNameChecking; " +
"Get-Mailbox; " +
"Remove-PSSession $Session");
// Invoke execution to collect the output
Collection<PSObject> PSOutput = PowerShellInstance.Invoke();
// Go through each output returned
foreach (PSObject outputItem in PSOutput)
{
// Dump the output to see what's coming out.
if (outputItem != null)
{
return new string[] { outputItem.ToString() };
}
}
}
return new string[] { "Did not get a powershell outputItem" };
Related
I need help to solve problem to auto-mate deploy .Net Core web app Api to a server.
As you know is impossible to overwrite .dll if you not stop AppPool before , and there is no solution for that in IIS .
Actually by using PowerShell I can perform a script to do what I need the script is showed below:
PowerShell script
Actually I need a console application to performe same work , I found that I can use Microsoft.PowerShell.SDK to implement a solution.
public static void RunC()
{
string us = "xxxxxxxxxxxxx"; //User
string pw = "xxxxxxxxxxxxxxxx";//Passwprd
string sv = "xxx.x.xx.xxx";//Server
string apppoolname = "xxxxxxxxxxxxx";
StringBuilder script = new StringBuilder();
//Creazione script PS
script.Append("$password = ConvertTo-SecureString \"" + pw + "\" -AsPlainText -Force" + Environment.NewLine);
script.Append("$user = \"" + us + "\"" + Environment.NewLine);
script.Append("$cred = New-Object System.Management.Automation.PSCredential ($user,$password)" + Environment.NewLine);
script.Append("Enter-PSSession -ComputerName \"" + sv + "\" -Credential $cred" + Environment.NewLine);
script.Append("Import-Module webadministration");
script.Append("Stop-WebAppPool \"" + apppoolname + "\"");
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(script.ToString());
//pipeline.Commands.Add("Out-String");
Collection<PSObject> results = pipeline.Invoke();
StringBuilder sb = new StringBuilder();
foreach (PSObject pSObject in results)
{
sb.AppendLine(pSObject.ToString());
}
Console.WriteLine(sb.ToString());
}
But I got error show in the image , seams Module is not lodaded or something similar ..
some one can help me in some way?
Thank you :-)
I dont see the error in the image, but if it is a issue caused by the WebAdministration not being imported this should fix it:
#Requires -Modules WebAdministration
Place that at the begging of the script and it will try to import the module if it's not available already in the session. Microsoft documentation:
Specifies PowerShell modules that the script requires. Enter the module name and an optional version number.
If the required modules aren't in the current session, PowerShell imports them. If the modules can't be imported, PowerShell throws a terminating error.
source
Hope it helps, good luck.
I am running Powershell commands within C# to get mailbox exchange folder delegates.
I am using the following 2 commands:
string scriptText = "Get-MailboxFolderPermission -Identity \"" + email + ":\\calendar\" | ? { ($_.user.tostring() -notlike \"Anonymous\") -and ($_.user.tostring() -notlike \"Default\") } | select User, AccessRights"
using (var powerShell = PowerShell.Create()) {
powerShell.AddScript(scriptText);
powerShell.Runspace = Runspace;
var iAsyncResult = powerShell.BeginInvoke();
psData = powerShell.EndInvoke(iAsyncResult);
}
string scriptText = "Get-MailboxPermission -Identity " + email + " | ? {($_.user.tostring() -ne “NT AUTHORITY\\SELF”) –and ($_.user.tostring() -ne “DOMAINDiscovery Management”) -and $_.IsInherited -eq $false -and ($_.AccessRights -match \"" + accessRights + "\")} | select User, AccessRights";
using (var powerShell = PowerShell.Create()) {
powerShell.AddScript(scriptText);
powerShell.Runspace = Runspace;
var iAsyncResult = powerShell.BeginInvoke();
psData = powerShell.EndInvoke(iAsyncResult);
}
At random intervals I get the following exception:
hostexception: a command that prompts the user failed because the host program or the command type does not support user interaction. the host was attempting to request confirmation with the following message: enter your credentials
I have no user prompts or anything in my commands. Does anyone know why my script is failing?
It turns out that it runs fine when the shell is run as admin.
I'm trying to get some data from Azure Active Directory using C# code with reference to System.Management.Automation. I've got no errors with code execution, just null results and no output to textfile. Does anyone have this problem before or maybe I missed something? Thank you!
public void RunScriptTest()
{
string username = "Username";
string password = "Password";
List<String> listResults = new List<String>();
PowerShell powershell = PowerShell.Create();
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
powershell.Runspace = runspace;
powershell.AddScript("Install-Module -Name AzureAD -Force; \n");
powershell.AddScript("Import-Module -Name AzureAD -Verbose \n");
powershell.AddScript("$username = \"" + username + "\"; \n" +
"$password = convertTo-securestring '" + password + "' -AsPlainText -Force; \n" +
"$cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $password; \n" +
"Connect-AzureAD - Credential $cred; \n");
powershell.AddScript("Get-AzureADUser | Out-File -FilePath " + #"C:\TestResults\1.txt");
Collection<PSObject> results = powershell.Invoke();
runspace.Close();
StringBuilder stringBuilder = new StringBuilder();
foreach(PSObject obj in results)
{
listresults.Add(obj.ToString());
}
}
Sequencing .AddScript() calls without intervening .AddStatement() calls makes only the last .AddScript() call effective - all previous calls are ignored.
In order to examine errors that may have occurred during execution via .Invoke(), you must access the powershell.Streams.Error stream.
Therefore, the immediate fix is to replace your powershell.AddScript(...) calls with $powershell.AddStatement().AddScript(...)
Note that your PowerShellCode appears designed not to produce any output, so there's no point in trying to populate listresults.
I have an application, that allows the user to configure basic WMI settings on a Win 10 IoT machine.
I am currently struggling with reading all WEKF_PredefinedKey settings, that are enabled.
I am simply running a skript, that I added as string to the project settings named ReadEnabledKeys:
$CommonParams = #{"namespace"="root\standardcimv2\embedded"}
$CommonParams += $PSBoundParameters
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned;
$keys = Get-WMIObject -class WEKF_PredefinedKey #CommonParams
foreach($k in $keys)
{
if($k.Enabled -eq $false)
{
"$k";
}
}
My call in C# code looks like this (Note: using System.Management.Automation):
using (PowerShell PowerShellInstance = PowerShell.Create())
{
PowerShellInstance.AddScript(Properties.Settings.Default.ReadEnabledKeys);
var result = PowerShellInstance.Invoke();
}
My variable result will always stay empty.
If I run the skript in Powershell directly, the output is just fine (all shortcuts, that are currently not disabled).
I have something similar programmed with the unified write filter, where I enable and disable it:
$COMPUTER = "localhost"
$NAMESPACE = "root\standardcimv2\embedded"
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned;
$objUWFInstance = Get-WMIObject -namespace $NAMESPACE -class UWF_Filter;
$retval = $objUWFInstance.Enable();
if ($retval.ReturnValue -eq 0) {"Unified Write Filter will be enabled after the next system restart."}
else {"Unknown Error: " + "{0:x0}" -f $retval.ReturnValue}
And the C# call:
using (PowerShell PowerShellInstance = PowerShell.Create())
{
PowerShellInstance.AddScript(Properties.Settings.Default.EnableUWF);
// [0] = result or error
var result = PowerShellInstance.Invoke();
if (result[0].ToString().ToLower().Contains("enabled"))
MessageBox.Show(result[0].ToString(), "", MessageBoxButton.OK, MessageBoxImage.Information);
else
MessageBox.Show("Error when enabling the filter! " + Environment.NewLine + result[0].ToString(), "",
MessageBoxButton.OK, MessageBoxImage.Error);
}
Here my result variable will be filled with the expected strings.
I have tried Write-Host $k, as I suspected something wrong with the stream, but this was without any success.
The output in Powershell looks like this:
PS C:\Users\Administrator> C:\Users\Administrator\Desktop\Newfolder\Untitled1.ps1
\\DESKTOP-RMGOBMG\root\standardcimv2\embedded:WEKF_PredefinedKey.Id="Alt"\\DESKTOP-RMGOBMG\root\standardcimv2\embedded:WEKF_PredefinedKey.Id="Application"
\\DESKTOP-RMGOBMG\root\standardcimv2\embedded:WEKF_PredefinedKey.Id="Ctrl+Esc"
\\DESKTOP-RMGOBMG\root\standardcimv2\embedded:WEKF_PredefinedKey.Id="Ctrl+F4"
\\DESKTOP-RMGOBMG\root\standardcimv2\embedded:WEKF_PredefinedKey.Id="Ctrl+Tab"
.
.
.
Can anyone tell me, what the problem is?
The problem appears to be with your script. Setting the ExecutionPolicy midstream doesn't do anything and you aren't writing a function so adding $PSBoundParameters also doesn't do anything. Here's an example that should work (I'd specify PS version in the future. I know you're on v5.1/win10 due to keyboard filtering)
$collection = [System.Collections.Generic.List[string]]::new()
foreach ($key in (Get-CimInstance -Namespace 'root\standardcimv2\embedded' -ClassName WEKF_PredefinedKey)) {
if (-not $key.Enabled) {
$collection.Add($key.ToString())
}
}
return $collection
(simplified)
#(Get-CimInstance -Namespace root\standardcimv2\embedded -ClassName WEKF_PredefinedKey).
Where{-not $_.Enabled}.
ForEach('ToString')
Example:
using (PowerShell ps = PowerShell.Create())
{
string script = #"Import-Module -Name C:\Windows\system32\WindowsPowerShell\v1.0\Modules\Microsoft.PowerShell.Management\Microsoft.PowerShell.Management.psd1 -ErrorAction Stop; #(Get-WmiObject -Namespace root\standardcimv2\embedded -Class WEKF_PredefinedKey -ErrorAction Stop).Where{-not $_.Enabled}.ForEach('ToString')";
ps.AddScript(script);
var result = ps.Invoke();
}
In Exchange 2007 this line of code is used to load the Exchange Poweshell commands snapin:
PSSnapInInfo info = rsConfig.AddPSSnapIn(
"Microsoft.Exchange.Management.PowerShell.Admin",
out snapInException);
However, this does not exist in Exchange 2010 and I am pulling my hair out trying to find out how to access the Exchange Powershell commands from C# code. Microsoft.Exchange.Management.PowerShell.Admin does not exist anywhere on the Exchange Server and I can find nothing on Google that talks about an equivalent line of code.
How do I access Exchange Management Tools from C# code in Exchange 2010?
Below is my complete code for reference, it all works until I add the line of code:
//Creating and Opening a Runspace
RunspaceConfiguration rsConfig = RunspaceConfiguration.Create();
PSSnapInException snapInException = null;
PSSnapInInfo info = rsConfig.AddPSSnapIn(
"Microsoft.Exchange.Management.PowerShell.Admin",
out snapInException);
Runspace myRunSpace = RunspaceFactory.CreateRunspace(rsConfig);
myRunSpace.Open();
//How Do I Run a Cmdlet?
//create a new instance of the Pipeline class
Pipeline pipeLine = myRunSpace.CreatePipeline();
//create an instance of the Command class
// by using the name of the cmdlet that you want to run
Command myCommand = new Command(txtCommand.Text);
//add the command to the Commands collection of the pipeline
pipeLine.Commands.Add(myCommand);
Collection<PSObject> commandResults = pipeLine.Invoke();
// iterate through the commandResults collection
// and get the name of each cmdlet
txtResult.Text = "start ....";
foreach (PSObject cmdlet in commandResults)
{
string cmdletName = cmdlet.Properties["Name"].Value.ToString();
System.Diagnostics.Debug.Print(cmdletName);
txtResult.Text += "cmdletName: " + cmdletName;
}
txtResult.Text += ".... end";
I don't know for sure, but Exchange 2010 powershell might be implemented as a powershell 2.0 module, which is loaded in a different manner. To find out, go to a system with the exchange management shell on it, and fire it up. Next, run:
ps> get-module
This will list the loaded v2 modules. I would expect the exchange one to appear if you have started the dedicated exchange management shell. If you loaded the regular powershell console, try:
ps> get-module -list
This will list all modules available to load. If you spot the right one, then you'll need to build your code against the v2 system.management.automation dll. For reasons beyond the scope of this reply, v2 powershell's assembly has the same strong name as v1's, so you cannot easily have both versions of powershell on the same machine. Build this from a machine with v2 powershell installed:
InitialSessionState initial = InitialSessionState.CreateDefault();
initialSession.ImportPSModule(new[] { *modulePathOrModuleName* });
Runspace runspace = RunspaceFactory.CreateRunspace(initial);
runspace.Open();
RunspaceInvoke invoker = new RunspaceInvoke(runspace);
Collection<PSObject> results = invoker.Invoke(*myScript*);
Hope this helps,
-Oisin
After a lot of trial and error, I finally figured this out. The problem with the code above is it works great when run against Exchange 2007 but things have changed in Exchange 2010. Instead of the snapin called "Microsoft.Exchange.Management.PowerShell.Admin", use this snapin, "Microsoft.Exchange.Management.PowerShell.E2010".
The complete code to run a Powershell command from C# looks like this. Hope this helps someone else trying to do this.
You will need references to System.Management.Automation.Runspaces, System.Collections.ObjectModel and System.Management.Automation also.
I found that the reference to System.Management.Automation had to be manually added to the csproj file itself in the ItemGroup section using notepad like this:
<Reference Include="System.Management.Automation" />
code below:
private class z_test
{
//set up
private RunspaceConfiguration rsConfig = RunspaceConfiguration.Create();
private PSSnapInException snapInException = null;
private Runspace runSpace;
private void RunPowerShell()
{
//create the runspace
runSpace = RunspaceFactory.CreateRunspace(rsConfig);
runSpace.Open();
rsConfig.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.E2010", out snapInException);
//set up the pipeline to run the powershell command
Pipeline pipeLine = runSpace.CreatePipeline();
//create the script to run
String sScript = "get-mailbox -identity 'rj'";
//invoke the command
pipeLine.Commands.AddScript(sScript);
Collection<PSObject> commandResults = pipeLine.Invoke();
//loop through the results of the command and load the SamAccountName into the list
foreach (PSObject results in commandResults)
{
Console.WriteLine(results.Properties["SamAccountName"].Value.ToString());
}
pipeLine.Dispose();
runSpace.Close();
}
}
This is what I am doing:
$sessionOptionsTimeout=180000
$sessionOptionsTimeout=180000
$so = New-PSSessionOption -OperationTimeout $sessionOptionsTimeout -IdleTimeout $sessionOptionsTimeout -OpenTimeout $sessionOptionsTimeout
$connectionUri="http://$fqdn/powershell?serializationLevel=Full;ExchClientVer=14.3.91.1"
$s = New-PSSession -ConnectionURI "$connectionUri" -ConfigurationName Microsoft.Exchange -SessionOption $so
$s | Enter-PSSession
PS>get-mailboxserver
EncryptionRequired AutoDatabaseMountDial DatabaseCopyAutoActivationPo
licy
------------------ --------------------- ----------------------------
e GoodAvailability Unrestricted
e GoodAvailability Unrestricted
Now, converting above to .net (c#) should be easy...
Essentially an exerpt from: "C:\Program Files\Microsoft\Exchange Server\V14\Bin\ConnectFunctions.ps1"
Please refer to the following function:
function _NewExchangeRunspace([String]$fqdn,
[System.Management.Automation.PSCredential]
$credential=$null,
[bool]$UseWIA=$true,
[bool]$SuppressError=$false,
$ClientApplication=$null,
$AllowRedirection=$false)
{
$hostFQDN = _GetHostFqdn
if (($fqdn -ne $null) -and ($hostFQDN -ne $null) -and ($hostFQDN.ToLower() -eq $fqdn.ToLower()))
{
$ServicesRunning = _CheckServicesStarted
if ($ServicesRunning -eq $false)
{
return
}
}
Write-Verbose ($ConnectFunctions_LocalizedStrings.res_0005 -f $fqdn)
$so = New-PSSessionOption -OperationTimeout $sessionOptionsTimeout -IdleTimeout $sessionOptionsTimeout -OpenTimeout $sessionOptionsTimeout;
$setupRegistryEntry = get-itemproperty HKLM:\SOFTWARE\Microsoft\ExchangeServer\v14\Setup -erroraction:silentlycontinue
if ( $setupRegistryEntry -ne $null)
{
$clientVersion = "{0}.{1}.{2}.{3}" -f $setupRegistryEntry.MsiProductMajor, $setupRegistryEntry.MsiProductMinor, $setupRegistryEntry.MsiBuildMajor, $setupRegistryEntry.MsiBuildMinor
$connectionUri = "http://$fqdn/powershell?serializationLevel=Full;ExchClientVer=$clientVersion"
}
else
{
$connectionUri = "http://$fqdn/powershell?serializationLevel=Full"
}
if ($ClientApplication -ne $null)
{
$connectionUri = $connectionUri + ";clientApplication=$ClientApplication"
}
write-host -fore Yellow ("connectionUri: " + $connectionUri)
$contents = 'New-PSSession -ConnectionURI "$connectionUri" -ConfigurationName Microsoft.Exchange -SessionOption $so'
if (-not $UseWIA)
{
$contents = $contents + ' -Authentication Kerberos -Credential $credential'
}
if ($SuppressError)
{
$contents = $contents + ' -erroraction silentlycontinue'
}
if ($AllowRedirection)
{
$contents = $contents + ' -AllowRedirection'
}
write-host -fore Yellow ("contents: " + $contents)
write-host -fore Yellow ("join n contents: " + [string]::join("`n", $contents))
[ScriptBlock] $command = $executioncontext.InvokeCommand.NewScriptBlock([string]::join("`n", $contents))
$session=invoke-command -Scriptblock $command
if (!$?)
{
# ERROR_ACCESS_DENIED = 5
# ERROR_LOGON_FAILURE = 1326
if (!(5 -eq $error[0].exception.errorcode) -and
!(1326 -eq $error[0].exception.errorcode))
{
#Write-Verbose ($ConnectFunctions_LocalizedStrings.res_0006 -f $fqdn)
return
}
else
{
# no retries if we get 5 (access denied) or 1326 (logon failure)
#$REVIEW$ connectedFqdn is not set. Is it okay?
break connectScope
}
}
$session
}