ActiveSync Device and User Report for Office 365 D, MT, and Exchange 2010+

Today, I had a customer ask me for an ActiveSync device report.  Normally, this is a somewhat simple task (Get-Mailbox | Get-ActiveSyncDevice), but in a large environment with hundreds of domains representing different agencies or business units, that is kind of an unwieldy report to run (as well as including a lot of data for out-of-scope users).

So, here's a new script that hopefully solves that problem for someone else besides me.

The first problem, of course, was creating a dynamic filter that I could pass to a Get-Mailbox or Get-CASMailbox command.  In an earlier blog post, I talk about how I solved that problem.  So, using the [scriptblock]::Create() method to create a my filter gave me a big chunk of the solution.  Instead of having to manually execute a command, I could pass a parameter value to my script that would then be passed to Get-Mailbox -Filter and use server-side filtering to dramatically cut down the number of mailboxes that I would query.

Fun fact: the -Filter parameter in Get-Mailbox and Get-CASMailbox doesn't let you use the -match operator, so you're stuck with "like" and "eq" (and their counterparts).

 # begin snip
param (
    [Parameter(Mandatory=$false)]
        [string]$Domain
        )
If ($Domain)
    {
    If ($Domain.StartsWith("*")) { # Value already starts with an asterisk }
    Else { $Domain = "*" + $Domain}
    $Filter = [scriptblock]::Create("{EmailAddresses -like `"$Domain`" -and HasActiveSyncDevicePartnership -eq `$True}")
    }
If ($Domain)
    {
    $cmd = "Get-CASMailbox -ResultSize $ResultSize -Filter $Filter -WarningAction SilentlyContinue"
    $EASMailboxes = Invoke-Expression $cmd
    }
#end snip

With those two filtering parameters (EmailAddresses -like $Domain and HasActiveSyncDevicePartnerShip -eq $true), I was able to cut a query down--even for a domain that has 7,000 mailboxes in an Office 365 tenant with 120,000+ mailboxes to just a minute or two.

From there, building the report was a little more than looping through the $EASMailboxes param and then adding a Get-Mailbox to each object in the $EASMailboxes pipeline to retreive DisplayName and PrimarySmtpAddress (since Get-CASMailbox frustratingly doesn't surface those attributes).

 Foreach ($Mailbox in $EASMailboxes)
    {
    $EASDeviceStatistics = Get-ActiveSyncDeviceStatistics -Mailbox $Mailbox.Identity -WarningAction SilentlyContinue
    $MailboxStatistics = Get-Mailbox $Mailbox.Identity | Select DisplayName,PrimarySmtpAddress
    Write-Host -NoNewLine "Processing mailbox "; Write-Host -NoNewLine -ForegroundColor Green "[ $($i) / $($TotalEASMailboxes) ]"; Write-Host ", $($MailboxStatistics.DisplayName)"
    $j = 1
    $TotalEASDevices = $EASDeviceStatistics.Count
    If (!($TotalEASDevices)) { $TotalEASDevices = "1" }
    Foreach ($EASDevice in $EASDeviceStatistics)
        {
        Write-Host -NoNewLine "     Processing device [ $($j) / $($TotalEASDevices) ] ";Write-Host -NoNewLine -ForegroundColor Green "$($EASDevice.DeviceID)"; Write-Host 
        $line = New-Object PSObject
        Add-Member -InputObject $line -MemberType NoteProperty -Name "DisplayName" -Value $MailboxStatistics.DisplayName
        Add-Member -InputObject $line -MemberType NoteProperty -Name "PrimarySmtpAddress" -Value $MailboxStatistics.PrimarySmtpAddress
        Foreach ($Column in $EASColumns)
            {
            Add-Member -InputObject $line -MemberType NoteProperty -Name $Column -Value $EASDevice.$Column
            }
        Foreach ($Column in $CASColumns)
            {
            Add-Member -InputObject $line -MemberType NoteProperty -Name $Column -Value $Mailbox.$Column
            }
        Foreach ($Column in $CASArrayColumns)
            {
            $ColumnData = $Mailbox.$Column -join ";"
            Add-Member -InputObject $line -MemberType NoteProperty -Name $Column -Value $ColumnData
            }
            $Report += $line
        $j++
        }
    $i++
    }

One of the things that I discovered, though, was that I was retrieving data for mailboxes whose PrimarySmtpAddress didn't line up with the domain I had supplied in the -Domain parameter.  I was curious about this and decided to investigate--and discovered that those users had a proxyAddress that matched the domain in the filter, but had a different PrimarySmtpAddress (I'm guessing it was to cover reply-ability issues when people moved between agencies or departments).

As to why these objects got included, this would be due to the fun fact from earlier--you can't use the -match operator in the -Filter parameter, and since the PrimarySmtpAddress property of a mailbox isn't returned in Get-CASMailbox, the only one available to use is EmailAddresses.

The final step was to create a switch parameter to determine if the final output should exclude those mailboxes.  Fortunately, I can use RegEx here.  I just have to take off the pre-pended asterisk in the $Domain variable and run a -match against the PrimarySmtpAddress column of the report object.

 If ($ExcludeSecondaryDomains -and $Domain)
    {
    $PrimarySmtpDomain = $Domain.Substring(1)
    $TempReport = $Report | ? { $_.PrimarySmtpAddress -match $PrimarySmtpDomain }
    $TempReport | Export-Csv -NoTypeInformation $ReportName
    }
Else
    {
    $Report | Export-Csv -NoTypeInformation $ReportName
    }

You can grab the completed report script over on the TechNet Gallery.