How do I add multiple email addresses? - c#

Below is the code i'm using. I'm unable to add multiple addresses using the powershell Emailaddresses parameter. The code works fine if I just put in one email address, but once I add two addresses in the code below it returns exception stating invalid smtp address.
PSCredential credential = new PSCredential(username, password);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo((new Uri(liveIdconnectionUri)), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Default;
Runspace runspace = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(connectionInfo);
PowerShell powershell = PowerShell.Create();
runspace.Open();
powershell.Runspace = runspace;
var secure = new SecureString();
foreach (char c in textBox5.Text)
{
secure.AppendChar(c);
}
PSCommand command2 = new PSCommand();
command2.AddCommand("Set-Mailbox");
command2.AddParameter("Identity", "lferrigno");
command2.AddParameter("EmailAddressPolicyEnabled", 0);
command2.AddParameter("EmailAddresses", "SMTP:lferrigno#sscincorporated.com,lou.ferrigno#altegrahealth.com");
powershell.Commands = command2;
powershell.Invoke();

This is the code i ended up using since it was a collection.
string[] smtp = { "SMTP:" + textBox6.Text, 9 + "smtp:" + textBox4.Text + "#sscincorporated.com" };
command2.AddParameter("EmailAddresses", smtp);

The -EmailAddresses parameter takes an array argument (technically a collection of SmtpProxyAddress objects, but that's not important, you can provide it an array and the conversion will be handled autonomatically), but it looks like you're giving it a single string containing multiple addresses. You need to either provide an array argument where each element is an address, or try this:
command2.AddParameter("EmailAddresses","'SMTP:lferrigno#sscincorporated.com','SMTP:lou.ferrigno#altegrahealth.com'");
Even though that's still a string within your C# code, if it's passed to PowerShell as-is, PowerShell will interpret it as an array.
You should also be able to leave out the SMTP: prefixes, because that's the default and will be set automatically if you don't specify a prefix.

Related

Is it possible to import the AD module into an existing Exchange runspace using c#/powershell?

I am creating an exchange user (new-mailbox) and then setting some AD parameters on them after the user is created in the same runspace with commands that will not run in the Exchange runspace unless import-module 'activedirecty' is ran. Is there a way to import the module after the runspace is created as I can do with the Powershell prompt?
inside the same runspace session I want to run:
new-mailbox
set-mailbox
set-user
set-aduser
The last one is what requires me to import the AD module I can successfully run the command inside of Powershell directly, but can't seem to figure out how to add the module mid runspace session? I'd tried
powershell.AddParameter("import-module -name 'activedirectory'; set-aduser xxxx")
and
powershell.AddParameter("import-module -name 'activedirectory'")
powershell.AddParameter("set-aduser xxxx")
and
powershell.AddScript("import-module -name 'activedirectory'; set-aduser xxxx")
This works below
public void SetPasswordNeverExpiresProperty(bool PasswordNeverExpires, string alias)
{
string dn = "CN=xxx,OU=xxx,OU=xxx=xxx=xxx=xxx,DC=xx,DC=xx,DC=xxx,DC=xxx"
DirectoryEntry objRootDSE = new DirectoryEntry("LDAP://" + dn);
ArrayList props = new ArrayList();
int NON_EXPIRE_FLAG = 0x10000;
int EXPIRE_FLAG = 0x0200;
int valBefore = (int) objRootDSE.Properties["userAccountControl"].Value;
objRootDSE.Properties["userAccountControl"].Value = EXPIRE_FLAG;
objRootDSE.CommitChanges();
string valAfter = objRootDSE.Properties["userAccountControl"].Value.ToString();`
And I'm out of guesses, any help would be appreciated.
PSCredential ExchangeCredential = new PSCredential(PSDomain + #"\" + PSUsername, PSpwd);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri("xxxxxx/powershell"), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", ExchangeCredential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Kerberos;
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
{
psobjs = powershell.Invoke();
}
catch (Exception ex)
{
result = "Failed: " + ex.Message;
}
powershell.Commands.Clear();
}
I'll sum up my comments in an answer, since it seems I was unexpectedly helpful :)
I also had found that you can't use Import-Module when using remote PowerShell like that. It's kind of annoying, but such is life.
Years ago, I implemented an automatic account creation service in our environment for AD and Exchange 2010. I found I had to do the AD account manipulation with DirectoryEntry and then only the Exchange stuff with PowerShell.
The problem is making sure that both things happen on the same domain controller so you don't run into replication problems.
So you have two options: Use New-Mailbox to create the mailbox and AD account in one shot. As you pointed out, the OriginatingServer property of the result has the domain controller. But there is also a DistinguishedName property there too! (I just found this when you mentioned the server property) Then you can create a DirectoryEntry object against the same domain controller like this:
new DirectoryEntry($"LDAP://{domainController}/{distinguishedName}")
Or, what I did (I think because I didn't realize at the time that I could get the DC from the result of New-Mailbox), is create the AD object first with DirectoryEntry, pull the domain controller it got created on from .Options.GetCurrentServerName(), then pass that in the DomainController parameter to Enable-Mailbox.

Exchange 2010 replication problems while c# powershell invoking multiple commands

I have a problem with sequentially invoking multiple powershell commands on HA Exchange environment (on single development host everything work fine). As long as it was about only Exchange commands I used workaround by piping multiple commands (execution was on the same node) everything was fine. My code:
string casUri = "cas1.srv.net/Powershell?serializationLevel=Full";
string credentialUserName = "User";
string credentialUserPassword = "secret";
string newOU = "thenewou";
string baseOU = "OU=root,DC=srv,DC=net";
System.Security.SecureString passwd = new System.Security.SecureString();
foreach (char c in credentialUserPassword) {
passwd.AppendChar(c);
}
PSCredential credential = new PSCredential(credentialUserName, passwd);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(
new Uri(casUri),
"http://schemas.microsoft.com/powershell/Microsoft.Exchange",
credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Default;
runspacePool = RunspaceFactory.CreateRunspacePool(1, 5, connectionInfo);
runspacePool.Open();
powershell = PowerShell.Create();
Command command;
command = new PSCommand();
command.AddCommand("New-ADOrganizationalUnit");
command.AddParameter("Path", baseOU);
command.AddParameter("Name", newOu);
command.AddParameter("DisplayName", "My new OU");
powershell.Commands = command;
powershell.Invoke();
// ... another AD commands: Set-ADForest, New-AcceptedDomain (work fine)
// v--- trouble
command = new PSCommand();
command.AddCommand("New-GlobalAddressList");
command.AddParameter("Name", "GAL.Name");
command.AddParameter("RecipientContainer", newOu + "," baseOU);
powershell.Commands = command;
powershell.Invoke();
Problem doesn't exists in single host environment.
On HA like this, another commands run on different hosts:
[myApp]---[CAS load balancer]---[CAS-1..CAS-N]---[EX-1..EX-N]
This cause an exception:
Active Directory operation failed on DC2.srv.net. This error is not retriable.
Additional information: The name reference is invalid.
This may be caused by replication latency between Active Directory domain controllers.
Active directory response: 000020B5: AtrErr: DSID-03152804, #1:
0: 000020B5: DSID-03152804, problem 1005 (CONSTRAINT_ATT_TYPE), data 0, Att 9b7f0292 (msExchSearchBase)
I have no idea how to force persistent connection with single remote shell for few commands.
Have you got any solution ?
Have you try this?
command.AddParameter("DomainController", "Your Domain Contorller FQDN");

How pipe powershell commands in c#

The goal is get the smallest database of an Exchange 2010 site, so I'm trying to run the following powershell command from c#,
Get-MailboxDatabase -server Exchange2010 -Status | select-object Name,DatabaseSize
The problem I'm struggling is - how pipe the Select clause command.
This is my attemp,
WSManConnectionInfo wsConnectionInfo = new WSManConnectionInfo(new Uri("https://" + ExchangeSite + "/powershell?serializationLevel=Full"),
"http://schemas.microsoft.com/powershell/Microsoft.Exchange", getCredential());
wsConnectionInfo.AuthenticationMechanism = AuthenticationMechanism.Basic;
wsConnectionInfo.SkipCACheck = true;
wsConnectionInfo.SkipCNCheck = true;
rsRemoteRunspace = RunspaceFactory.CreateRunspace(wsConnectionInfo);
rsRemoteRunspace.Open();
Pipeline pipeLine = rsRemoteRunspace.CreatePipeline();
Collection<PSObject> DatabaSize = null;
Command myCommand = new Command("Get-MailboxDatabase");
myCommand.Parameters.Add("Server", "Exchange2010");
myCommand.Parameters.Add("Status", null);
Command myCommand2 = new Command("Select-Object");
myCommand.Parameters.Add("Name");
myCommand.Parameters.Add("DatabaseSize");
pipeLineMB.Commands.Add(myCommand);
pipeLineMB.Commands.Add(myCommand2);
DatabaSize = pipeLine.Invoke();
but I'm getting,
"A parameter cannot be found that matches parameter name 'Name'."
Please keep in mind I cannot use SnapIn because the code must run on a client machine that executes cmdlets on the Exchange server.
Any advice is welcome.
EDIT
I applied the fix suggested by yamen and the command was able to be invoked but when I try to get the value, I get: "Object reference not set to an instance of an object."
Notice that I can get the values of Servername and Name but it fails in the DatabaseSize so I guess the 'Status' flag is not being set properly because this flag is the one which enables this value.
Here did you mean this:
Command myCommand2 = new Command("Select-Object");
myCommand2.Parameters.Add("DatabaseSize");
Instead of this:
Command myCommand2 = new Command("Select-Object");
myCommand.Parameters.Add("DatabaseSize");
Notice myCommand2 on the second line?
Regardless, you might find that the parameter you're actually after is Property viz:
myCommand2.Parameters.Add("Property", "DatabaseSize");
And for more than one:
var props = new string[] { "DatabaseSize", "ServerName", "Name" };
myCommand2.Parameters.Add("Property", props);
I just tried this, to have a similar scenario
dir | select Name
It doesn't work. Gives me the same error saying 'Name' isn't a valid parameter. Then I tried the below, it works
dir | select -first 3
translates to
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
Command dir = new Command("dir");
pipeline.Commands.Add(dir);
Command select = new Command("select");
select.Parameters.Add("first", 3);
pipeline.Commands.Add(select);
You would need to find the name of the parameter for which DatabaseSize is the value, I guess.

How to add image to Active Directory user from C# using PowerShell remoting?

I'm writing a program in C# that adds a picture to an Active Directory user object (in the ThumbnailPhoto attribute). The program runs on another PC than the Exchange Server 2010. Using PowerShell remoting I'm able to, for example, enable a mailbox for the AD user.
When I execute the following commands in PowerShell (on another PC than the Exchange Server), it works perfectly:
enter-pssession exchange_server_fqdn
$cred = get-credential ( domain\administrator , **** )
$sess = new-pssession -configurationName Microsoft.Exchange -ConnectionUri http://exchange_server_fqdn/PowerShell/ -Authentication Kerberos -Credential $cred
import-pssession $sess
Import-RecipientDataProperty -Identity ADusername -Picture -FileData ([Byte[]]$(Get-Content -Path 'C:\\person.jpg' -Encoding Byte -ReadCount 0))
remove-pssession $sess
But I need to execute the commands from C#. The following code runs (no exception is thrown), but the image isn't added to the AD user object:
string username = "xxx";
string password = "yyy";
string exchangeServer = "zzz"; // FQDN of Exchange Server 2010
string liveIdconnectionUri = "http://" + exchangeServer + "/Powershell?serializationLevel=Full";
// credentials
SecureString passwordSecureString = new SecureString();
foreach (char c in password) passwordSecureString.AppendChar(c);
PSCredential credential = new PSCredential(username, passwordSecureString);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(
new Uri(liveIdconnectionUri),
"http://schemas.microsoft.com/powershell/Microsoft.Exchange",
credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Kerberos;
PowerShell powershell = PowerShell.Create();
Runspace runspace = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(connectionInfo);
runspace.Open();
powershell.Runspace = runspace;
PSCommand command = new PSCommand();
// Add ThumbnailPhoto attribute to Active Directory user
// This command works when executed on the Exchange Server 2010 in the Exchange Management Shell,
// But it doens't work when executed in C#.
command.AddScript("Import-RecipientDataProperty -Identity ADusername -Picture -FileData ([Byte[]]$(Get-Content -Path 'C:\\person.jpg' -Encoding Byte -ReadCount 0))");
powershell.Commands = command;
try {
Collection<PSObject> commandResults = powershell.Invoke<PSObject>();
foreach (PSObject result in commandResults) Console.WriteLine(result.ToString());
}
catch (Exception ex) {
Console.WriteLine(ex.Message);
}
The only problem in the code is the line with command.AddScript. If I replace that line with, for example, this:
command.AddCommand("Get-Mailbox");
command.AddParameter("Identity", "ADusername");
... then it does work. Enabling a mailbox also works.
How can I execute the command to add an image to an Active Directory user object from C# (using PowerShell remoting)?
Working code based on accepted answer:
DirectoryEntry container = new DirectoryEntry(LDAP_URI + USERS_DIR);
DirectoryEntry user = container.Children.Add("cn=" + username, "user");
// Set other property's of the user object:
//// user.Properties ["xxx"].Value = "yyy";
//// ...
byte [] buffer;
FileStream fileStream = new FileStream(#"c:\photo.jpg", FileMode.Open, FileAccess.Read);
try {
int length = (int) fileStream.Length; // get file length
buffer = new byte [length]; // create buffer
int count; // actual number of bytes read
int sum = 0; // total number of bytes read
// read until Read method returns 0 (end of the stream has been reached)
while ((count = fileStream.Read(buffer, sum, length - sum)) > 0)
sum += count; // sum is a buffer offset for next reading
}
finally {
fileStream.Close();
}
user.Properties ["thumbnailPhoto"].Value = buffer;
user.CommitChanges();
I would take a different approach:
byte[] pictureBytes = //Use a FileStream to read the data
command.AddCommand("Import-RecipientPropertyData");
command.AddParameter("Identity", "fooUser");
command.AddParameter("Picture", "$true");
command.AddParameter("Encoding", "Byte");
command.AddParameter("ReadCount", 0);
command.AddParameter(FileData, pictureBytes);
Alternatively, you could just read the picture in to your pictureBytes and use ADSI (DirectoryEntry) to write directly to the thumbnailPhoto attribute. Just make sure that pictureBytes is <= 10KB as that's the max on the attribute in AD.
This sounds like an authentication problem. Even though you're running the command on the Exchange server, it's actually being executed against Active Directory, and there's no guarantee which domain controller it will execute against. This is the double-hop scenario, which can be solved by using CredSSP in your WS-Man remoting session.

Error executing remote Power Shell Script (Exchange 2010)

I wrote a very small application, which access the Remote Power Shell of Exchanger Server 2010 SP1 and execute some scripts. Here is the sample code. Everything is in try and catch block.
string insecurePassword = "mypassword";
SecureString securePassword = new SecureString();
foreach (char passChar in insecurePassword.ToCharArray())
{
securePassword.AppendChar(passChar);
}
PSCredential credential = new PSCredential("mydomain\\administrator", securePassword);
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri("http://exchange2010.domain.com/powershell?serializationLevel=Full"), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", credential);
connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Kerberos;
Runspace runspace = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace(connectionInfo);
PowerShell powershell = PowerShell.Create();
PSCommand command = new PSCommand();
ICollection<System.Management.Automation.PSObject> results;
//The command I want to execute is Set-MailContact with some parameters.
command.AddCommand("Set-MailContact");
command.AddParameter("Identity", "SomeIdentityOfContact");
command.AddParameter("EmailAddressPolicyEnabled", false);
command.AddParameter("PrimarySmtpAddress", "myEmailAddress#domain.com");
command.AddParameter("Confirm", false);
command.AddParameter("Force", true);
powershell.Commands = command;
// open the remote runspace
runspace.Open();
// associate the runspace with powershell
powershell.Runspace = runspace;
// invoke the powershell to obtain the results
results = powershell.Invoke();
I am trying to set PrimarySmtpAddress of a MailContact, but for some reasons I am getting the following exception:
System.Management.Automation.RemoteException: Cannot process argument transformation on parameter 'PrimarySmtpAddress'. Cannot convert value "SMTP:myEmailAddress#domain.com" to type "Microsoft.Exchange.Data.SmtpAddress"
I think its must be due to serialization/de-serialization. Does someone have any idea on how to correctly pass the email address's value?
Any hint help will be highly appreciated!
I think you are confusing smtp server address with email address, try to pass something like smtp.yourcomapnydomain.com instead of such email address and test again.
Try leaving off the SMTP: qualifier. That's already implicit in the -primarySMTPAddress parameter.

Categories