Who was that message sent to?

Sometimes you need to know specifically who a message was sent to and there may be no obvious way to determine that (such as it may have been sent to a distribution group, or the sender may have used BCC, or in my case that prompts this post – an external user).  In these situations, the message tracking log in Exchange can be helpful but capturing the output in a format that you can send to your HR or Legal department may be a bit of a challenge, as the recipient field in the message tracking log is an array – and any type of collection object doesn’t export cleanly with the built-in cmdlets such as export-csv.

What you first need to to is establish what criteria will allow you to uniquely identify the message(s) you need to track for management.  The best criteria is the message ID, but you can use a combination of criteria such as sender and message subject.  These will become arguments to the Get-MessageTrackingLog Exchange Management Shell cmdlet.

In this case we are interested in who the message was actually delivered to, so you can further filter by the event ID of “deliver”.

Putting all that together, you end up with a Get-MessageTrackingLog command that will look something like this:

Get-MessageTrackingLog -MessageSubject "Pictures from the Office Party" -Sender "friskey.person@example.com"-start 1/2/2010 -eventid deliver

That’s simple enough, and it will return a stream of objects that match your filter criteria.  But you need to give someone a report of who received this message.  Unfortunately, it’s not as easy as piping the output to Export-CSV or Out-File.  First you have to extract all of the recipients.  The solution I used was a foreach-object loop on the results to create a CSV formatted result that I could then pipe to out-file.   This allowed me to capture the recipients as a simple string successfully.

The ForEach-Object loop uses the -Begin code block to create the header records for the file, and the -process code block to actually capture the tracking log fields that we were interested in.  The fields were placed in a sub-expression to allow for proper expansion of their values.

ForEach-Object -Begin {"Sender,TimeStamp,MessageSubject,Recipients"} -Process {"$($_.sender),$($_.timestamp),$($_.messagesubject),$($_.recipients)"}

Take the previous two examples, combine them with a list of transport servers to retrieve logs from and feed the output to out-file, and you can construct a one-liner that will provide you with a simple report of who received a given email that you can share with your management.

Get-TransportServer | Get-MessageTrackingLog -MessageSubject "Pictures from the Office Party" -Sender "friskey.person@example.com" -start 1/2/2010 -eventid deliver | % {"Sender,TimeStamp,MessageSubject,Recipients"}{"$($_.sender),$($_.timestamp),$($_.messagesubject),$($_.recipients)"} | out-file -Encoding ascii -FilePath $env:temp\OfficeParty.csv
Posted in Exchange 2007, PowerShell | Comments Off on Who was that message sent to?

Mailboxes Associated with Disabled Users

Periodically you may wish to identify mailboxes that are still active in Exchange 2007 but which are not associated with active AD users.  As you are aware, some mailboxes within Exchange require disabled accounts, so you generally want to exclude those mailboxes from such reports.  Below is a script that I put together to produce a report of disabled user’s mailboxes.

I prefer not to use external utilities when possible, as it makes my scripts more portable in nature, which is why you’ll see the use of ADSI instead of the Quest cmdlets.  This script does require the Exchange PSSnapIns, so you’ll want to execute this within the EMS.

  1. #This script is for getting a list of disabled users that still have mailboxes
  2. #Execute the script with the following command
  3. #.\DisabledUserMailboxes.ps1 -Output C:\DisabledUserMailboxes.csv
  5. param(
  6. [string]$Output = $(Throw "You need to specify the output file name using -output")
  7. )
  9. # Get a list of users with disabled accounts, that are mail enabled
  10. $domain = [ADSI]""
  11. $searcher = new-object System.DirectoryServices.DirectorySearcher($domain)
  12. $searcher.filter = "(&(userAccountControl:1.2.840.113556.1.4.803:=2)(homemdb=*))"
  13. [Void]$Searcher.PropertiesToLoad.Add("distinguishedname")
  14. $searcher.PageSize = 1000
  15. $searcher.SearchScope = "Subtree"
  16. $results = $searcher.findall()
  18. write-host "$($results.count) Disabled Users are mail enabled"
  20. # Create an empty array for storing the user data
  21. $users = @()
  23. #Enumerate through the disabled users and make sure they are user mailboxes, placing valid objects into the $users array
  24. foreach($result in $results) {
  25. $MailboxExists = $null
  27. #Verify the user has a mailbox that is a user mailbox, not a Room, Equipment, Shared, etc mailbox
  28. $MailboxExists = Get-Mailbox $($result.Properties.distinguishedname).tostring() | Where-Object {($_.recipienttypedetails -eq "UserMailbox")}
  30. # Make sure the result is neither an emtpy string nor a null value, if not add the current user to the list of disabled mailbox users
  31. if (($MailboxExists -ne "") -and ($MailboxExists -ne $null)){
  32. write-debug $($result.Properties.distinguishedname)
  33. $users += $($result.Properties.distinguishedname).tostring()
  34. }
  36. }
  38. # Export the list of disabled users that have Exchange mailboxes to a CSV file
  39. $users | select-object @{Name="DistinguishedName";Expression={$_}} | Export-Csv -Path $Output -Force -NoTypeInformation
Posted in Active Directory, Exchange 2007, PowerShell | Comments Off on Mailboxes Associated with Disabled Users

Automating Lotus Notes Credential in Transporter powershell cmdlets

One of the first challenges that we ran into was having to constantly type the password for the various transporter cmdlets that we placed into any of our automation for the conversion of mailboxes from Notes to Exchange. There are no examples that I could find for how to pass this information to a cmdlet such as Move-DominoMailbox. After some digging around I eventually stumbled upon the solution.

This allowed me to prompt for the credentials one time and store them in an encrypted file in a central location. The encrypted password information is user and workstation specific so it cannot be used by another user or even the same user on a different computer.

The following example will successfully allow the passing of a notes credential to the various Transporter Suite cmdlets without being prompted for the notes credentials:

To get and store the credential for the current user:

  1. $notespw = Read-Host "Enter the password for the Notes ID file" -AsSecureString
  2. $notespw | ConvertFrom-SecureString | Set-Content $pwfile -force

To retrieve the password and create the PSCredential object:
  1. $notespw = get-content $pwfile | ConvertTo-SecureString
  2. $notesid = new-object -typename system.management.automation.pscredential -argumentlist "-default-",$notespw

Example of use:

Get-DominoMailbox mary@contoso.com -SourceCredential $notesid

Posted in Lotus Notes Conversion, Microsoft Transporter Suite, PowerShell | Comments Off on Automating Lotus Notes Credential in Transporter powershell cmdlets

Report NDRs because a mailbox was full for the previous day

If you have service levels to maintain in your environment and a finite amount of disk space, there is a pretty good chance that you have mailbox quotas set to prevent users from over running your storage space.

Periodically you may be asked to report on the messages that were rejected for your environment because of full mailboxes.  When Exchange rejects a message because a user’s mailbox is full, it generates an NDR (Non-Deliverable Report) with a status code of 5.2.2.  You can search the Message Tracking Logs for this status code to determine who failed to receive messages due to their mailbox being full.

The following oneliner will produce a simple CSV report at %temp%\TooFullNDR.csv that includes the sender, recipient, message timestamp and recipient status (for further diagnosis if needed) for the most recent 24 hour period.

get-transportserver | Get-MessageTrackingLog -EventId fail -start (date).adddays(-1) | where {$_.recipientstatus -like "550 5.2.2*"} | select timestamp, sender, @{Name="Recipients"; Expression={[string]::join(";",$_.recipients)}}, messagesubject,@{Name="RecipientStatus";Expression={$_.recipientstatus}} | export-csv $env:temp\fullndr.csv –NoTypeInformation

This is a sample of what the above will output if you didn’t capture it as a CSV.

Timestamp       : 11/07/2008 3:32:18 PM
Sender          : david.sample@vendor.com
Recipients      : Michelle.baduser@company.com
MessageSubject  : Urgent: past due invoice
RecipientStatus : 550 5.2.2 STOREDRV.Deliver: mailbox full. The following infor
                  mation should help identify the cause: "MapiExceptionShutoffQ
                  uotaExceeded:16.18969:AA000000, 17.27161:00000000D40000000000
                  00000F00000000000000, 255.23226:9D000000, 255.27962:FE000000,
                   255.17082:DD040000, 0.26937:94000000, 4.21921:DD040000, 255.
                  27962:FA000000, 255.1494:34000000, 255.26426:FE000000, 4.7588
                  :0F010480, 4.6564:0F010480, 0.22086:0F010480, 4.4740:05000780
                  , 4.6276:05000780, 4.23921:EC030000, 6.21970:0F01048040000C68
                  0F010480, 4.23921:EC030000, 6.21970:0F01048000806F670F010480,
                   4.24305:0F010480, 4.5721:DD040000, 4.6489:DD040000, 4.2199:D
                  D040000, 4.17097:DD040000, 4.8620:DD040000, 255.1750:71040000
                  , 0.26849:0F010480, 255.21817:DD040000, 0.26297:0F010480, 4.1
                  6585:DD040000, 0.32441:0F010480, 4.1706:DD040000, 0.24761:000
                  00000, 4.20665:DD040000, 0.25785:00000000, 4.29881:DD040000".
Posted in Exchange 2007, Lotus Notes Conversion, PowerShell | Comments Off on Report NDRs because a mailbox was full for the previous day

Checking for a failure after executing statement

In PowerShell, you can check to see if there were any errors reported by the previously executed cmdlet in several ways. The most useful that I found for basic error handling was the built-in variable $?, which indicates the success or failure of the previous statement. If the value is equal to $false then the previous command failed to execute properly and you can branch based off of that. You can then check the $error collection if you want additional details about the error that occurred.

Its important to note that the $? variable indicates the last command, so you need to check it immediately after you have executed the statement you wish to check or you will get unexpected results.

An example that checks to see if the Exchange admin plug-ins are loaded in the current session, if not it tries to load them.

  1. # Check we're running with the Exchange snapin loaded
  2. Get-PSSnapin -Name "Microsoft.Exchange.Management.PowerShell.Admin" -ErrorAction "SilentlyContinue" | Out-Null
  3. if ($? -eq $FALSE) {
  4. # Try to load the snap-in
  5. Add-PSSnapin -Name "Microsoft.Exchange.Management.PowerShell.Admin" -ErrorAction "SilentlyContinue" | Out-Null
  6. if ($? -eq $FALSE) {
  7. Throw "Exchange Snap-in not loaded"
  8. }
  9. }

This same expression can be written as !$?, so the same example from above using the more terse syntax would be:

  1. # Check we're running with the Exchange snapin loaded
  2. Get-PSSnapin -Name "Microsoft.Exchange.Management.PowerShell.Admin" -ErrorAction "SilentlyContinue" | Out-Null
  3. if (!$?) {
  4. # Try to load the snap-in
  5. Add-PSSnapin -Name "Microsoft.Exchange.Management.PowerShell.Admin" -ErrorAction "SilentlyContinue" | Out-Null
  6. if (!$?) {
  7. Throw "Exchange Snap-in not loaded"
  8. }
  9. }

Posted in Exchange 2007, PowerShell | Comments Off on Checking for a failure after executing statement

Finding files modified after a given timestamp

If you want to work on files in a directory structure that were only modified since before or after a specific timestamp, you can use the below methodology to return a list of the matching items.  This is useful if you are processing data files in batches based on date/time cut offs but are not able to move or rename the processed files in a given folder.

First you need to create a variable for your date cut off.  This will be the date/time before or after which you wish to return matching files for.  For this example, we want a cut off watermark of 9/20/2008 at midnight.

$Watermark = get-date -Date 9/20/2008 -Hour 0 -Minute 0 -Second 0

Next you will need to use get-childitem to return a list of the files that were modified after this watermark for further action or reporting.  In this case, this command is intended to execute from a top level folder and return all modified files in any folder beneath that starting point.  The where clause here is exluding folders that were modified after a given date but not the files within them, as my script needed to process files and not folders.  Obviously you can work with the filter parameters to suit your specific requirements.

$NewFiles = get-childitem -recurse | where {$_.psiscontainer -eq $false -and $_.LastWriteTime -ge $watermark}
Posted in PowerShell | Comments Off on Finding files modified after a given timestamp

Valid Enum Values

If you need to know the possible valid values for a cmdlet argument and the parameter type is an enum value, the most useful way to get a list of the possible values


For Example

PS C:\> [System.Enum]::GetValues([System.DayOfWeek])
Posted in PowerShell | Comments Off on Valid Enum Values


So a recent major project at work has been converting us off of Lotus Notes to Exchange 2007. All I can say is thank god, it was painful going from Exchange 5.5 to Notes. I’m sure that’ll bring the out the Notes faithful to defend their religion, but anyone who’s sat on the client end of Notes more than likely shares my sentiments. In any event, it’s given me the opportunity to take on several new skillsets that I wouldn’t have been exposed to with my current employer, as I transferred into the team working on the conversion project from a much less interesting position.

Specifically, I’ve been working with Powershell a lot, as I’m sure any Exchange 2007 admins are aware it’s the heavy-lifting interface for Exchange administration. One of the things I have always disliked about administering most Microsoft products has been their lack of decent command line interfaces. Of course it appears most admins are perfectly OK with that, but I’ve always been a fan of automating as much of my job as possible. I’d rather spend 2 hours writing a script or batch file than spend 2 hours moving the mouse around. Especially if there’s any chance I’ll have to repeat that activity anytime in the future. I’m of the general school of thought that any senior level system administrator (regardless of the platform) should be able to handle basic shell scripts to automate repetitive aspects of their job.

Since I’d previously worked with VBScript, Visual Basic for Applications, and Perl it wasn’t substantially hard to pick up powershell, and having watched a handful of coworkers who had no previous background with Powershell pick it up has been encouraging.

But to say that there hasn’t been a learning curve would be misleading. In that light, over the course of the next few weeks I plan on posting some of the things I have discovered in the course of picking up powershell and automating the general tasks that we’ve had for migrating from Notes to Exchange. Hopefully these examples can save someone else a few frustrating hours of research and debugging.

Posted in Lotus Notes Conversion | Comments Off on Start-ExchangeConversion

Useful regular expression tutorials

These are some rather useful regular expression tutorials I stumbled across.

Basic Regular Expressions
Extreme regex foo

Posted in References, RegEx | Comments Off on Useful regular expression tutorials

Converting Virtual Server images to VMWare (Missing symmpi.sys)

I had an issue where I was trying to convert some Microsoft Virtual Server 2005 R2 images to VMWare images to run them on my mac.  The images were running Server 2003.  You don’t have to worry about it on WinXP images.

Using VMWare Converter, it would get to 97% and then fail.  After some digging through the logs, I figured out that it was because the VS images were missing the Symbios SCSI driver.  The fix was relatively simple.  You need a copy of the server install media.

From a command prompt:

cd %systemroot%\system32\drivers
expand d:\i386\symmpi.sy_ symmpi.sys Enter code here

Now the converter will successfully import the machine.

Posted in Virtualization | Comments Off on Converting Virtual Server images to VMWare (Missing symmpi.sys)