C#
Filtering Exchange PowerShell Locally
As an Exchange developer, it’s quite possible you’ve run across this error message:
Assignment statements are not allowed in restricted language mode or a Data section.
Or you’ve possibly seen this one:
Script block literals are not allowed in restricted language mode or a Data section.
While it’s quite easy to change the PSLanguageMode to FullLanguage instead of RestrictedLanguage it may be that you can’t do that for one reason or another. Here’s a small method I put together for filtering the results locally. Use your discretion about the size of your resultsets to bring back.
public IEnumerable<PSObject> ApplyPSFilter(IEnumerable psData, ScriptBlock scriptBlock) { using (var ps = System.Management.Automation.PowerShell.Create()) { ps.AddCommand("Where-Object"); ps.AddParameter("FilterScript", scriptBlock); return ps.Invoke(psData); } }
Using Credentials to Get Remote PowerShell Connection to Exchange Connection
Saw this come across the search logs so I figured I’d detail the steps needed to supply credentials to a remote PowerShell connection to Exchange. Since we’re connecting to another system to run the Exchange PS cmdlets, and we are not loading a PSSnapin, the remote system needs to have those cmdlets available. I’m also separating this out into individual methods for easy re-use.
To setup a remote connection, we need to create a new WSManConnectionInfo object. We’re specifically going to use the constructor that takes a Uri for the connection endpoint, a string value for the Uri of the shell and the credentials we want to use.
WSManConnectionInfo Constructor (Uri, String, PSCredential)
The Uri will be specific to your situation and will look something like this : http://myexchangeserver.domain.corp/powershellThe shell is pretty simple. In this case it is : http://scemas.microsoft.com/powershell/Microsoft.Exchange
And last but not least it the PSCredential needed for the connection. PSCredential takes a string for the Username and a SecureString for the password. Here’s an small method to pass in a string and get a SecureString back :
SecureString GetSecurePassword(string password) { var securePassword = new SecureString(); foreach (var c in password) { securePassword.AppendChar(c); } return securePassword; }
With that out of the way, let’s take a look at a snippet for getting the connection info :
WSManConnectionInfo GetConnectionInfo() { var securePassword = GetSecurePassword(password); var cred = new PSCredential(username, securePassword); var connectionInfo = new WSManConnectionInfo(new Uri("http://superserver.corp/powershell), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", cred); connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Credssp; //or Basic, Digest, Kerberos etc. return connectionInfo; }
Now let’s put it all together to make a remote connection :
using (var runspace = RunspaceFactory.CreateRunspace(connectionInfo)) { using (var ps = System.Management.Automation.PowerShell.Create()) { //psCommand was created above ps.Commands = psCommand; runspace.Open(); ps.Runspace = runspace; try { var results = ps.Invoke(); if (!ps.HadErrors) { runspace.Close(); //Yay! no errors. Do something else? } catch (RemoteException ex) { //error handling here } } }
So, a couple of things to mention here. First, psCommand was created like this. You could also create a Pipeline if you have more than one command to run or even use it to create a PS script to run. Also, if you need to do something with the results of the command, well, you’ll have to either make this return the results ((FavoriteCollectionType) or modify this to suit your needs. Second, we verify that the command had no errors (no exception but didn’t run successfully for whatever reason) but that’s it. Might want to expand here or in the calling method.
I hope this has been informative. 😀
Creating Exchange 2013 Mailbox in C#
Holy moly it’s been a long time since I posted. Here’s a quick code snippet on using C# to create a mailbox to hopefully kick a string of posts off. This creates an AD account for the user unlike my previous article on creating a mailbox in Exchange 2010. And since I listed a gotcha in that article, I’ll leave a tip in this one after the code. 😀
var psc = new PSCommand(); psc.AddCommand("New-Mailbox"); psc.AddParameter("Name", newUser.UserName); psc.AddParameter("UserPrincipalName", upn); psc.AddParameter("Alias", upn); psc.AddParameter("PrimarySmtpAddress", string.Format("{0}@{1}", newUser.UserName, domainName).ToLower()); psc.AddParameter("OrganizationalUnit", domainDn); psc.AddParameter("SamAccountName", samAccount); psc.AddParameter("Password", _ps.CreateSecurePassword("mysupersecretpassword")); psc.AddParameter("DomainController", domainController); PowerShell ps = PowerShell.Create(); ps.Commands = psc; try { var result = ps.Invoke(); if (!ps.HadErrors) { Console.WriteLine("Congrats! User created successfully"); } } catch (RemoteException ex) { //Error conditions here... }
Easy enough. Now for the tip. If you specify the PrimarySmtpAddress it will automatically set the EmailAddressPolicyEnabled value to false. So leave that parameter out if it’s important for your org.
Creating Exchange 2010 Mailboxes in C#
Wow, it’s been a while! I had full intentions of creating regular posts but that kind of slipped away. So I’ll make this one fairly short and just highlight the basics of creating an Exchange 2010 mailbox via remote code. Nothing fancy, but I will highlight one gotcha that I encountered when updating our user creation code after we migrated from Exchange 2003. Keep in mind that Exchange 2010 is built on Powershell and as such, all of connections and manipulation must be done through it.
First, we want to add our usings…
using System.Management.Automation; using System.Management.Automation.Remoting; using System.Management.Automation.Runspaces;
Next, we’re going to create the PSCredential, the WSManConnectionInfo, and the Runspace. We’ll then create a powershell command, invoke the powershell with the command and cross our fingers. 😀
PSCredential newCred = (PSCredential)null; WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri("http://exchangeserver01.my.domain/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("Enable-Mailbox"); command.AddParameter("Identity", user.Guid.ToString()); command.AddParameter("Alias", user.UserName); command.AddParameter("DomainController", ConnectingServer); powershell.Commands = command; try { runspace.Open(); powershell.Runspace = runspace; Collectionresults = powershell.Invoke(); } catch (Exception ex) { string er = ex.InnerException.ToString(); } finally { runspace.Dispose(); runspace = null; powershell.Dispose(); powershell = null; }
Now, a couple of things to note…
1) My gotcha…You don’t find a lot of information about creating a PSCredential using the credentials of the currently logged in user. Even the MSDN documentation only shows one constructor where you pass in a username and password. After searching around, some trial and error, I finally found this post detailing creating the PSCredential using Windows Authentication with no username or password supplied. Supplying credentials isn’t a problem, but as we log the account creations, and we didn’t have this issue when creating boxes in 2003, I wanted as seamless transition as possible. So this code just replaced the old code and my other Admins had no idea anything was different.
2) Accounts for my users need to be created at one of many different sites, and many times it’s not a site I’m currently at. So, the account is created on the ‘local to them’ server so they can immediately login and not need to wait for replication. Because of this, Exchange needs to know what server holds the account. If you don’t need this functionality, you can remove the DomainController command parameter above.
3) It is important to properly dispose of the runspace and powershell objects. Make sure this is done.
User and Group Information in AD
Continuing our Active Directory theme from the last post, I wanted to follow up on locating the groups that users belong to or groups you’d like to add to a user account. These examples will also continue with using the .Net 3.5 DirectoryServices.AccountManagement namespace since there is quite a bit less code than using the methods for .Net 2.0.
So, the first example deals with getting all of the groups of a user account. Some of the code you might have already seen.
UserPrincipal user = null; //Create a PrincipleContext that will search the full domain //ie not just the site's user OU //FQDC = Fully Qualified Domain Controller string userName = "MyUserToSearch"; using (var context = new PrincipalContext(ContextType.Domain, FQDC)) { try { if ((user = UserPrincipal.FindByIdentity(context, userName)) != null) { // Search for current groups PrincipalSearchResult<Principal> groups = user.GetGroups(); // Iterate group membership foreach (GroupPrincipal g in groups) { Console.WriteLine(g.Name); } } } }
Now we know how to grab the group information for a user, but what if we want to remove a group?
UserPrincipal user = null; //Create a PrincipleContext that will search the full domain //ie not just the site's user OU //FQDC = Fully Qualified Domain Controller string userName = "MyUserToSearch"; using (var context = new PrincipalContext(ContextType.Domain, FQDC)) { try { if ((user = UserPrincipal.FindByIdentity(context, userName)) != null) { // Search for current groups PrincipalSearchResult<Principal> groups = user.GetGroups(); // Iterate group membership and remove IT_Dept group foreach (GroupPrincipal g in groups) { if (g.Name == "IT_Dept") { g.Members.Remove(user); g.Save(); } } } } }
Finally, adding a new group is a little different.
UserPrincipal user = null; //Create a PrincipleContext that will search the full domain //ie not just the site's user OU //FQDC = Fully Qualified Domain Controller string userName = "MyUserToSearch"; using (var context = new PrincipalContext(ContextType.Domain, FQDC)) { try { if ((user = UserPrincipal.FindByIdentity(context, userName)) != null) { // Add user to new group string groupName = "GroupToAddUser"; using (var group = GroupPrincipal.FindByIdentity(context, IdentityType.Name, groupName)) { // Verify user is not currently a member and save if (!group.Members.Contains(user)) { group.Members.Add(user); group.Save(); } } } } }
Here we search for the group name explicitly, check that the user isn’t already in it and then add the user. Pretty simple stuff.
Active Directory UserPrincipals in .Net
Continuing our Active Directory theme from the last post, I wanted to step back for a second and discuss UserPrincipal objects a little more as I have a few posts relating more to accounts following this one.
The UserPrincipal class has two constructors. One takes a PrincipalContext and the other one takes the PrincipalContext, SAM account name, password, and enabled value. In general, I use the constructor with just the PrincipalContext when searching for an existing AD account and the ‘expanded’ constructor when I am creating a new AD user object. As most Active Directory environments need passwords, and I generally make it a point to not know user passwords (after I create them) I think this makes sense. So, now I’ll detail these two uses below.
First, let’s take a look at finding a user and mapping it to a UserPrincipal object. What I’m really focused on here is the UserPrincipal itself and I’ll cover searching more in other articles. Here’s some code:
using (var context = new PrincipalContext(ContextType.Domain, FQDC)) { UserPrincipal user = null; do { // We don't create accounts with '1' so we need to increment again. if (count == 1) { count++; } // Similar to the ADID we need to determine the identifier for the username string filter = count > 0 ? this.UserName + count.ToString() : this.UserName; user = UserPrincipal.FindByIdentity(context, filter); count++; } while (user != null); }
This is a somewhat expanded example of a routine I use to determine AD usernames but I didn’t want to just throw in three lines and call it a day. My primary AD domain has 18000+ user accounts so we’re bound to have duplicate names using our standard naming scheme. In case of a duplicate, I need to append a number on it so that’s what you see here, me determining the number. What’s truly important here is that by using the FindByIdentity method, we return a UserPrincipal object from AD that contains common AD attributes that we may be interested in and once we have this object we can manipulate these values quite easily. As a bonus, we can extend this object with custom code and map properties such as the EmployeeID field (which is not exposed by UserPrincipal). I’ll save that for a later post though.
Now, the second scenario involves us wanting to create a new user. Finding and connecting to a current user is easy and so is creating a new one.
using (PrincipalContext adPC = new PrincipalContext(ContextType.Domain, FQDC)) { using (UserPrincipal user = new UserPrincipal(adPC, this.CommonName, this.Pass, true)) { user.SamAccountName = UserName; user.UserName = UserName; user.UserPrincipalName = UserName + "@some.domain"; user.GivenName = FirstName; if (this.mName != "") { user.Initials = MiddleName; } user.Surname = LastName; user.DisplayName = this.CommonName; user.EmailAddress = UserName + "@mydomain.com"; user.SetPassword(this.Pass); user.Department = "Rocking IT Department"; user.Company = "My Fake Company"; user.PhysicalDeliveryOfficeName = officeLocation; user.ExpirePasswordNow(); user.Enabled = true; user.PasswordNotRequired = false; user.Save(); } }
So creating an account is pretty simple too. Using a PrincipalContext attached to the DC that you want to create the object on, you create a new UserPrincipal passing in the values for the context, the name as it’ll appear in AD (First Last or Last, First — however your procedures require it) the temporary password and true to say it’s enabled (although we do set that below). The rest are fairly standard properties that you may be setting in your schema. Once you’ve set all of your properties save the object and you have a new Active Directory user.
Querying AD from C# using DirectoryServices.AccountManagement
I ran across a need to query AD and make bulk changes to a subset of AD user accounts. When creating an account in Active Directory using a UserPrincipal object you must specify certain properties on the account. The ones I was interested in are as follows:
PasswordNotRequired
UserCannotChangePassword
PasswordNeverExpires
During my initial creation, I forgot to set PasswordNotRequired to false and needed to go back and set this on a group of accounts. My initial attempt had a problem…it only returns the first 1000 results:
using (var context = new PrincipalContext(ContextType.Domain, FQDC)) { UserPrincipal up = new UserPrincipal(context); up.EmployeeId = "*"; up.Enabled = true; PrincipalSearcher ps = new PrincipalSearcher(); ps.QueryFilter = up; PrincipalSearchResult<Principal> results = ps.FindAll(); if (results != null) ...
Here we setup a PrincipalContext and create a new UserPrincipal with it. We then set the search criteria (some value entered in the EmployeeID field and an Enabled account). After creating a PrincipalSearcher we set a QueryFilter and then return the results. Again, this works great but it only returns 1000 results. As I was expecting closer to 15000, this was a slight problem. 😉
So, how do we return all of the results? The solution is actually quite simple and I’ll highlight the one-line code change below.
... PrincipalSearcher ps = new PrincipalSearcher(up); ...
When creating the PrincipalSearcher, we need to pass in the UserPrincipal object to get the desired result. According to MSDN, the QueryFilter must be set (we set it in the example above) before passing it into the PS object. Also, you can go the long route and set the PageSize in the underlying DirectorySearchwe object, but I find this way to be much easier.
One other side note. Don’t forget how it easy it is to “parallelise” your operations.
if (results != null) { int count = 0; Parallel.ForEach (results, s => { code to do stuff.... }); }
.Net makes it very easy to make your operations parallel.