I have the function below with multiple threads, which was fine with explicit credentials when I was the only one using it, but I've been asked to put it into a powershell script where it will be plain text to leverage the multithread capability.
Is it possible to use the implied credentials of the account running the script with wsmanconnectioninfo? If not, is there a different way to create the Exchange shell connection without explicit credentials?
private Collection<PSObject> runPowerShellScript(object server)
{
Collection<PSObject> psobjs = new Collection<PSObject>();
string result = "";
string serverName = server.ToString();
string loginPassword = "xxx";
System.Security.SecureString secpassword = new SecureString();
foreach (char c in loginPassword)
{
secpassword.AppendChar(c);
}
PSCredential credential = new PSCredential(#"domain/samaccount", secpassword);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri("https://xxxxx/powershell"), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Basic;
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
PowerShell powershell = PowerShell.Create();
if (runspace.RunspaceStateInfo.State == RunspaceState.Opened)
{
// do nothing
}
else
{
runspace.Open();
powershell.Runspace = runspace;
}
try
{
PSCommand command = new PSCommand();
command.AddScript($#"get-mailboxdatabase -Server " + server + " -Status");
powershell.Commands = command;
psobjs = powershell.Invoke();
if (powershell.HadErrors == true)
{
result = "Failed - " + powershell.Streams.Error[0].ToString();
result = result.Replace("\"", "*");
}
}
catch (Exception ex)
{
string fail = ex.Message;
}
}
object serverNameO = server;
PSObject serverNameObj = new PSObject(serverNameO);
psobjs.Insert(0, serverNameObj);
return psobjs;
}
This is working
WSManConnectionInfo wmc = new WSManConnectionInfo(new Uri(`"http://xxx/powershell`"));
wmc.AuthenticationMechanism = AuthenticationMechanism.Kerberos;
wmc.ShellUri = `"http://schemas.microsoft.com/powershell/Microsoft.Exchange`";
using (Runspace runspace = RunspaceFactory.CreateRunspace(wmc))
{
}
Related
All is working well on developer's computer from VS or from bin/release files of wpf app. But, when I run the same project from VS or bin/release files on another computer I'm receiving an error:"The term 'Enable-Mailbox' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again."
Here is the code where the error is happening:
public static class ManageMailBox
{
public static bool CreateMailBox(string strUserID, string admin_user, string pword) //, string str_upn)
{
try
{
string strExchangeServer = Constants.ExchangeServer;
Uri uri = new Uri(#"http://" + strExchangeServer + #"/powershell?serializationLevel=Full"); // orig works
/// must pass secure string
char[] passwordChars = pword.ToCharArray();
SecureString password = new SecureString();
foreach (char c in passwordChars)
{
password.AppendChar(c);
}
PSCredential credential = new PSCredential("DOMAIN\\" + admin_user, password);
Runspace runspace = RunspaceFactory.CreateRunspace();
PowerShell powershell = PowerShell.Create();
PSCommand command = new PSCommand();
command.AddCommand("New-PSSession");
command.AddParameter("ConfigurationName", "Microsoft.Exchange");
command.AddParameter("ConnectionUri", uri);
command.AddParameter("Credential", credential);
command.AddParameter("Authentication", "Default");
PSSessionOption sessionOption = new PSSessionOption();
sessionOption.SkipCACheck = true;
sessionOption.SkipCNCheck = true;
sessionOption.SkipRevocationCheck = true;
command.AddParameter("SessionOption", sessionOption);
powershell.Commands = command;
try
{
// open the remote runspace
runspace.Open();
// associate the runspace with powershell
powershell.Runspace = runspace;
// invoke the powershell to obtain the results
Collection<PSSession> result = powershell.Invoke<PSSession>();
foreach (ErrorRecord current in powershell.Streams.Error)
{
throw new Exception("Exception: " + current.Exception.ToString());
throw new Exception("Inner Exception: " + current.Exception.InnerException);
}
if (result.Count != 1)
throw new Exception("Unexpected number of Remote Runspace connections returned.");
// Set the runspace as a local variable on the runspace
powershell = PowerShell.Create();
command = new PSCommand();
command.AddCommand("Set-Variable");
command.AddParameter("Name", "ra");
command.AddParameter("Value", result[0]);
powershell.Commands = command;
powershell.Runspace = runspace;
powershell.Invoke();
// First import the cmdlets in the current runspace (using Import-PSSession)
powershell = PowerShell.Create();
command = new PSCommand();
//command.AddScript("Set-ExecutionPolicy -Unrestricted");
command.AddScript("Import-PSSession -Session $ra");
powershell.Commands = command;
powershell.Runspace = runspace;
powershell.Invoke();
// Now run get-ExchangeServer
powershell = PowerShell.Create();
command = new PSCommand();
command.AddCommand("Enable-Mailbox");
command.AddParameter("Identity", strUserID);
command.AddParameter("Alias", strUserID);
command.AddParameter("Database", "IAP Mailbox Database 0948752629");
powershell.Commands = command;
powershell.Runspace = runspace;
powershell.Invoke(); // ERROR !!! The term 'Enable-Mailbox' is not recognized as the name of a cmdlet, function
return true;
}
catch(Exception ex) {
throw new Exception(ex.Message.ToString()); }
finally
{
// dispose the runspace and enable garbage collection
runspace.Dispose();
runspace = null;
// Finally dispose the powershell and set all variables to null to free
// up any resources.
powershell.Dispose();
powershell = null;
}
}
catch (Exception argex)
{
throw new ArgumentException(argex.Message.ToString());
}
}
}
I am trying to write code in C# that get mailbox details from user via Power Shell command.
The power shell command script is:
Import-PSSession -session (New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://myServer.myDomain.com/Powershell)
Get-Mailbox -Identity helpdesk
The script runs OK from PowerShell.
Now my goal is to run it using C#.
This is my function:
private void button1_Click(object sender, EventArgs e)
{
m_RunSpace = RunspaceFactory.CreateRunspace();
m_RunSpace.Open();
Pipeline pipeLine = m_RunSpace.CreatePipeline();
Command newSession = new Command("New-PSSession");
newSession.Parameters.Add("-ConfigurationName", "Microsoft.Exchange");
newSession.Parameters.Add("-ConnectionUri", "http://myServer.myDomain.com/Powershell");
Command createSessionForExch = new Command("Import-PSSession");
createSessionForExch.Parameters.Add("-Session", newSession);
Command getMailbox = new Command("Get-Mailbox");
getMailbox.Parameters.Add("-Identity", "helpdesk");
pipeLine.Commands.Add(createSessionForExch);
pipeLine.Commands.Add(getMailbox);
Collection<PSObject> commandResults = pipeLine.Invoke();
foreach (PSObject cmdlet in commandResults)
{
}
}
But I receive an error that the "Get-Mailbox" command is not recognize:
Probably because the Import-PSSessions wasn't invok correctly.
I need help how to run the command Import-PSSession correctly from C#
Hope, you have found the solution. If not, please give this code a try:
String url = "http://" + ExchangeServerName + "/powershell?serializationLevel=Full";
System.Uri uri = new Uri(url);
Console.WriteLine(url);
System.Security.SecureString securePassword = String2SecureString(password);
System.Management.Automation.PSCredential creds = new System.Management.Automation.PSCredential(userName, securePassword);
Runspace runspace = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace();
PowerShell powershell = PowerShell.Create();
PSCommand command = new PSCommand();
command.AddCommand("New-PSSession");
command.AddParameter("ConfigurationName", "Microsoft.Exchange");
command.AddParameter("ConnectionUri", uri);
command.AddParameter("Credential", creds);
command.AddParameter("Authentication", Authentication);
//PSSessionOption sessionOption = new PSSessionOption();
//sessionOption.SkipCACheck = true;
//sessionOption.SkipCNCheck = true;
//sessionOption.SkipRevocationCheck = true;
//command.AddParameter("SessionOption", sessionOption);
powershell.Commands = command;
try
{
runspace.Open();
powershell.Runspace = runspace;
Collection<PSSession> result = powershell.Invoke<PSSession>();
foreach (ErrorRecord current in powershell.Streams.Error)
Console.WriteLine("The following Error happen when opening the remote Runspace: " + current.Exception.ToString() +
" | InnerException: " + current.Exception.InnerException);
if (result.Count != 1)
throw new System.Exception("Unexpected number of Remote Runspace connections returned.");
// Set the runspace as a local variable on the runspace
powershell = PowerShell.Create();
command = new PSCommand();
command.AddCommand("Set-Variable");
command.AddParameter("Name", "ra");
command.AddParameter("Value", result[0]);
powershell.Commands = command;
powershell.Runspace = runspace;
powershell.Invoke();
// First import the cmdlets in the current runspace (using Import-PSSession)
powershell = PowerShell.Create();
command = new PSCommand();
//command.AddScript("Import-PSSession $ra");
command.AddScript("Invoke-Command -ScriptBlock { Get-Mailbox -Identity:" + MailBoxName + " } -Session $ra");
powershell.Commands = command;
powershell.Runspace = runspace;
//powershell.Commands.AddCommand("Import-Module").AddArgument("activedirectory");
powershell.Invoke();
//command = new PSCommand();
//command.AddCommand("Get-Mailbox");
////Change the name of the database
//command.AddParameter("Identity", "");
//powershell.Commands = command;
//powershell.Runspace = runspace;
Collection<PSObject> results = new Collection<PSObject>();
results = powershell.Invoke();
Full an interesting tutorial about : Access Exchange Online by PowerShell in C#
Get-Mailbox example in step 3.
Use WSManConnectionInfo class to supply the uri,schema to open the remote runspace.Please find the code :
private static string CreateConnection()
{
Runspace remoteRunspace = null;
openRunspace(
"https://pod51057psh.outlook.com/powershell-liveid?PSVersion=3.0",
"http://schemas.microsoft.com/powershell/Microsoft.Exchange",
#"xxx#xxx.com",
"xxxxx",
ref remoteRunspace
);
// Command getLicenseCommand = new Command("Get-MsolAccountSku");
//Command activityCommand = new Command("Get-GroupActivityReport");
//activityCommand.Parameters.Add(new CommandParameter("ReportType", "Daily"));
//activityCommand.Parameters.Add(new CommandParameter("StartDate", "03/4/2014"));
//activityCommand.Parameters.Add(new CommandParameter("EndDate", "03/27/2014"));
Command activityCommand = new Command("Get-Mailbox");
activityCommand.Parameters.Add("-Identity", "xxx");
StringBuilder stringBuilder = new StringBuilder();
using (PowerShell powershell = PowerShell.Create())
{
powershell.Runspace = remoteRunspace;
powershell.Commands.AddCommand(activityCommand);
powershell.Invoke();
var results = powershell.Invoke();
remoteRunspace.Close();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
}
remoteRunspace.Close();
return stringBuilder.ToString();
}
public static void openRunspace(string uri, string schema, string username, string livePass, ref Runspace remoteRunspace)
{
System.Security.SecureString password = new System.Security.SecureString();
foreach (char c in livePass.ToCharArray())
{
password.AppendChar(c);
}
PSCredential psc = new PSCredential(username, password);
WSManConnectionInfo rri = new WSManConnectionInfo(new Uri(uri), schema, psc);
rri.AuthenticationMechanism = AuthenticationMechanism.Basic;
remoteRunspace = RunspaceFactory.CreateRunspace(rri);
remoteRunspace.Open();
}
I'm trying to add collection of aliases to Exchange server. This can be done only via Powershell cmdlets.
As Microsoft have wrapper under powershell and distributed call can be done only in runspace I use System.Management.Automation utilities for this.
Command that adds aliases looks like this one:
Set-Mailbox -Identity john#contoso.com -EmailAddresses #{add=”john#northamerica.contoso.com”}
Where Set-Mailbox is a command, all other fields are parameters and #add shows that we add new element to existing collection.
As Exchange runspace is running in PSLanguageMode.NoLanguage mode than only Command can be executed but not Scripts. With this approach exception is risen:
Command addAliasCommand = new Command("Set-Mailbox -Identity john#contoso.com -EmailAddresses #{add=”john#northamerica.contoso.com”}", true);
Only clear Command with parameters can be executed:
Command addAliasCommand = new Command("Set-Mailbox", true);
addAliasCommand.Parameters.Add("identity", "test#test.onmicrosoft.com");
addAliasCommand.Parameters.Add("EmailAddresses", "testing.alias10#test.onmicrosoft.com, testing.alias11#test.onmicrosoft.com");
But problem with this approach that it’s completely rewrites collection of aliases, when I want to add/remove new ones.
The question is how to add pointer #Add that will show that these values are added to the existing collection of ProxyAddressCollection?
Full code:
System.Security.SecureString secureString = new System.Security.SecureString();
foreach (char c in Password)
secureString.AppendChar(c);
PSCredential credential = new PSCredential(AdminLogin, secureString);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri("https://ps.outlook.com/PowerShell"), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Basic;
connectionInfo.SkipCACheck = true;
connectionInfo.SkipCNCheck = true;
connectionInfo.MaximumConnectionRedirectionCount = 4;
IList<string> gmResults = null;
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
runspace.Open();
using (Pipeline plPileLine = runspace.CreatePipeline())
{
try
{
Command addAliasCommand = new Command("Set-Mailbox", true);
addAliasCommand.Parameters.Add("identity", "test#test.onmicrosoft.com");
addAliasCommand.Parameters.Add("EmailAddresses", "testing.alias10#test.onmicrosoft.com, testing.alias11#test.onmicrosoft.com");
var rsResultsresults = plPileLine.Invoke();
if (!string.IsNullOrEmpty(resultObjectName))
{
gmResults =
rsResultsresults.Select(obj => obj.Members[resultObjectName].Value.ToString()).ToList();
}
plPileLine.Stop();
}
catch (Exception e)
{
return null;
}
finally
{
runspace.Close();
runspace.Dispose();
}
}
runspace.Close();
}
The #{ add = "john#northamerica.contoso.com" } is actually a Hashtable which is a #{ key = value } structure, so you could do:
Command addAliasCommand = new Command("Set-Mailbox", true);
addAliasCommand.Parameters.Add("identity", "john#contoso.com");
var addresses = new Hashtable();
addresses.Add("add", "john#northamerica.contoso.com");
addAliasCommand.Parameters.Add("EmailAddresses", addresses);
I add the same problem. I ended up using this:
var pipeline = runspace.CreatePipeline();
string cmdAlias = "Set-Mailbox " + username + "#" + domainName + " -EmailAddresses #{Add='" + username + "#" + domainNameAlias + "'}";
pipeline.Commands.AddScript(cmdAlias);
pipeline.Invoke();
I have a question to C# and Powershell in a ASP.NET Application.
I want to create a User in our Active Directory and I want/must use a powershell for this. I had built a Webapllication that could add a mailcontact in the Exchange Server one year ago. For this I used the System.Management.Automation Namespace with the Powershell Classes. But I don't know how I can do it for the Active Directory.
the ps command that I must use:
New-ADUser %prefix-%name
-SamAccountName "%name"
-UserPrincipalName "%name#test-company.com"
-GivenName "%text1"
-SurName "%text2"
-displayname "%name"
-enabled $true
-Path '%OU'
-AllowReversiblePasswordEncryption $true
-PasswordNeverExpires $true
-AccountPassword (ConvertTo-Securestring "%name" -asplaintext -Force)
and here my cs code:
public void CreateRemoteConnectionToActiveDirectory(string Username, string password//,...comming)
{
SecureString securePassword = new SecureString();
str_password = password;
str_username = Username;
foreach (char x in str_password)
{
securePassword.AppendChar(x);
}
PSCredential cred = new PSCredential(str_username, securePassword);
// connection?
}
My Old Code for the exchange Server:
public string CreateRemoteConnectionToExchange(string UserName, string Password, string Mailbox)
{
SecureString SecurePassword = new SecureString();
string str_password = Password;
string str_username = UserName;
foreach (char x in str_password)
{
SecurePassword.AppendChar(x);
}
PSCredential cred = new PSCredential(str_username, SecurePassword);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri(ExchangeServer), Schema, cred);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Default;
Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo);
PowerShell powershell = PowerShell.Create();
PSCommand command = new PSCommand();
command.AddCommand("New-MailContact");
command.AddParameter("ExternalEmailAddress", "SMTP:" + Mailbox + MailExtension);
command.AddParameter("Name", Mailbox);
command.AddParameter("Alias", Mailbox);
command.AddParameter("FirstName", Mailbox);
command.AddParameter("Initials", "");
command.AddParameter("LastName", "");
command.AddParameter("OrganizationalUnit", OrganizationalUnit);
command.AddParameter("DomainController", configDC);
powershell.Commands = command;
try
{
runspace.Open();
powershell.Runspace = runspace;
powershell.Invoke();
return "Der Kontakt wurde Erfolgreich erstellt";
}
catch (Exception ex)
{
///...
}
finally
{
runspace.Dispose();
runspace = null;
powershell.Dispose();
powershell = null;
}
}
How I can do this. A Example,tutorial or a tipp would me help.
Looks like your connection just needs to connect to either your AD server or another server that can actually execute the command.
I am trying to create an Exchange mailbox in C#. The following code doesn't produce an error, but it also doesn't appear to create a mailbox as I would expect:
private void buttonCreateUser_Click(object sender, EventArgs e)
{
Boolean Success = CreateUser(textBoxFirstName.Text, textBoxLastName.Text,
textBoxAlias.Text, textBoxPassword.Text,
comboBoxDomain.SelectedItem.ToString(),
comboBoxOrganizationalUnit.SelectedItem.ToString());
if (Success)
{
labelStatus.Text = "User Created";
}
else
{
labelStatus.Text = "There Is Some Error";
}
}
public Boolean CreateUser(string FirstName, string LastName, string Alias,
string PassWord, string DomainName, string OrganizationalUnit)
{
string Name = FirstName + " " + LastName;
string PrincipalName = FirstName + "." + LastName + "#" + DomainName;
Boolean success = false;
RunspaceConfiguration rsConfig = RunspaceConfiguration.Create();
SecureString spassword = new SecureString();
spassword.Clear();
foreach (char c in PassWord)
{
spassword.AppendChar(c);
}
PSSnapInException snapInException = null;
PSSnapInInfo info = rsConfig.AddPSSnapIn(
"Microsoft.Exchange.Management.PowerShell.E2010", out snapInException);
Runspace myRunSpace = RunspaceFactory.CreateRunspace(rsConfig);
myRunSpace.Open();
Pipeline pipeLine = myRunSpace.CreatePipeline();
Command myCommand = new Command("New-MailBox");
myCommand.Parameters.Add("Name", Name);
myCommand.Parameters.Add("Alias", Alias);
myCommand.Parameters.Add("UserPrincipalName", PrincipalName);
myCommand.Parameters.Add("Confirm", true);
myCommand.Parameters.Add("SamAccountName", Alias);
myCommand.Parameters.Add("FirstName", FirstName);
myCommand.Parameters.Add("LastName", LastName);
myCommand.Parameters.Add("Password", spassword);
myCommand.Parameters.Add("ResetPasswordOnNextLogon", false);
myCommand.Parameters.Add("OrganizationalUnit", OrganizationalUnit);
pipeLine.Commands.Add(myCommand);
pipeLine.Invoke();
myRunSpace.Dispose();
success = true;
return success;
}
I don't get an error, so I don't know what I am doing wrong.
Update
I am using Web Service For this.If I run same code with windows application it works,But not with WebService? should I make any change in Exchange Server? Though I can Get information of MailBox with Get-MailBox but New-MailBox not Creating User.
I've been struggling with this very issue for a few days (I knew nothing about C# as a language before this week). Anyway, for anyone like me, looking to implement this, but struggling, here is an example that works for me:
We use Exchange2010, and I can run this from a machine that doesn't have the exchange tools installed.
using System.Management.Automation;
using System.Management.Automation.Remoting;
using System.Management.Automation.Host;
using System.Collections.ObjectModel;
using Microsoft.PowerShell.Commands;
static string createmailbox(string name, string alias, string email, string database, string UPN)
{
SecureString spassword = new SecureString();
string PassWord = "<set default password here or read in input from form>";
spassword.Clear();
foreach (char c in PassWord)
{
spassword.AppendChar(c);
}
string orgunit = "<define the OU here if you always use the same one, alternatively, add as parameter in function call>";
string dc = "<similarly, add DC here if needed, or call from function>";
PSCredential newCred = (PSCredential)null;
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri("<put in CAS server FQDN here>/powershell?serializationLevel=Full"),
"http://schemas.microsoft.com/powershell/Microsoft.Exchange", newCred);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Kerberos;
Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo);
PowerShell powershell = PowerShell.Create();
PSCommand command = new PSCommand();
command.AddCommand("New-Mailbox");
command.AddParameter("-Name", name);
command.AddParameter("-Alias", alias);
command.AddParameter("-UserPrincipalName", email);
command.AddParameter("-PrimarySMTPAddress", email);
command.AddParameter("-Password",spassword);
// (ConvertTo-SecureString -AsPlainText "P4ssw0rd" -Force)
command.AddParameter("-Database", database);
command.AddParameter("-OrganizationalUnit", orgunit);
// command.AddParameter("-Email", email);
command.AddParameter("-DomainController", dc);
powershell.Commands = command;
try
{
runspace.Open();
powershell.Runspace = runspace;
Collection<PSObject> results = powershell.Invoke();
return results.ToString();
}
catch (Exception ex)
{
string er = ex.InnerException.ToString();
}
finally
{
runspace.Dispose();
runspace = null;
powershell.Dispose();
powershell = null;
}
}
My use case is via a WPF form, so the parameters are populated from text boxes on the form. I've set things like the password (we have a default password for shared mailboxes and the user account is disabled), the OU and the domain controller as static text, but they could be called via variables just as easily.
I get the solution for this .I change permission leval of inproxy.dll and whooooo its working great ...