Today I was asked to produce the email addresses of a few thousand employees based on the First Name and Last Names provided by HR. Easy, right? Well it would be except of course for the many edge cases where the name HR has for an employee is completely different than what is actually recorded in Active Directory. Realizing I was going to have a problem with many failed addresses, I had to come up with an easy way of producing a list of the failed addresses along with those that I was able to match. In doing so, I figured out how to solve a long standing frustration with PowerShell and wanted to share. Consider this scenario where you want to see if a mailbox exists. You run get-mailbox “Mike Smith” | select name
The mailbox exists so you get an object back and everything looks great. Now however imagine if you search for a mailbox that doesn’t exist. What happens?
You get the wall of red text that is synonymous with PowerShell errors. In this case however, we only need to know the user name that failed. We don’t need any of the other text. How can we accomplish that?
It turns out that most cmdlets support a parameter called –ErrorVariable. The purpose of this is to simply store any errors that are generated by the cmdlet. This should be used in conjunction with the –ErrorAction parameter where it would be set to “SilentlyContinue”. In other words, if an error happens, don’t bug me about it but store the results in a variable.
Now so far this isn’t terribly exciting. If I try to display the contents of the error variable afterwards, I get the same wall of text. That doesn’t help much. However, by complete accident, I discovered an interesting trick you can play with this variable. If you typecast it as a string before displaying it, you only get the error message text you care about! Consider the example below:
As you can see, I was now able to display only the error message I cared about!
(Side note: I had to declare the Err variable here as global due to an apparent bug with the Exchange Remoting functionality. If I don’t declare it as global, it is never populated)
Now that we have this error message, we can start to do more fancy things. Like in my case for example, I was only interested in the actual employee Name (ie the thing between the quotes). I can grab that by using the split function:
Handy!
In the interests of completeness, below is the actual script I wrote to extract the email addresses of all of the users from a text file and get their email addresses. It uses a custom object and stores the failed users in there as well with a blank email address. That way you can easily sort it afterwards to identify which ones worked and which ones did not.
# This script takes a list of first name and last names and generates a list of matching email addresses for that user # Exchange; Email; Usernames; Error Handling; # Import a list of first name and last names taken from another source (IE HR records) $Users = Get-Content c:\temp\usernames.txt # Generate a custom object that will be used to store the records found # Need to use a custom object as we'll be storing both the data from Exchange as well as the errors generated from users that don't have Exchange mailboxes $myObj = @(); # Function to add new usernames and email addresses to a custom object for later export Function AddToObject { $Script:myObj += New-Object PSObject -Property @{UserName = $args[0]; Email = $args[1]}} ForEach($User in $Users) { # Loop through each user and export their name and email address # If there is an error, save it to the Variable $Err. Also if there is an error, do NOT display it to the screen $Results = Get-Recipient $User -ErrorVariable Err -ErrorAction SilentlyContinue | select name, PrimarySMTPAddress # If a valid record was returned (ie the user has an email address), write that record to the custom object if($Results -ne $Null) { AddToObject $Results.name $Results.PrimarySMTPAddress} # If an error was found (no email address), extract the username from the error message (Text in quotes) and write that to the custom object if($Err) { AddToObject ([string]$err|%{$_.split('''')[2]}) } } # Output the results (both valid and invalid addresses) and export it to the clipboard $myObj | ConvertTo-Csv | clip