InfiniTec - Henning Krauses Blog

Don't adjust your mind - it's reality that is malfunctioning

How to get the number of unread mails from multiple mailboxes using the EWS Managed API and PowerShell

Interesting question in the Exchange development forum today: How do I get the number of unread messages in the inbox folder from a number of mailboxes? The questioner wants to send a text message to each owner of a mailbox notifying him of the number of unread mails in his inbox. The requirement was to do this with PowerShell. So, here is a quick solution for this problem:

param ([string] $inputFile, [System.Management.Automation.PSCredential] $credential)

$ErrorActionPreference = "Stop"

if (($inputFile -eq [String]::Empty) -or (Test-Path $inputFile) -eq $false) 
{
    throw "Invalid file specified ({0})." -f $inputFile
}

[Reflection.Assembly]::LoadFrom("C:\Program Files\Microsoft\Exchange\Web Services\1.1\Microsoft.Exchange.WebServices.dll")

$service =  New-Object Microsoft.Exchange.WebServices.Data.ExchangeService -ArgumentList([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1)

if ($credential -ne $null) 
{
    $service.Credentials = $credential.GetNetworkCredential()
}
else 
{
    $service.UseDefaultCredentials = $true;
}


function GetUnreadInboxMails([string] $emailAddress) 
{
    $service.AutodiscoverUrl($emailAddress, {$true});
    
    $maibox = New-Object Microsoft.Exchange.WebServices.Data.Mailbox($emailAddress)
    $folderId = New-Object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox, $mailbox)
    $folder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service, $folderId);
    $result = New-Object PSObject -Property @{ 
        EmailAddress = $emailAddress
        UnreadMailCount=  $folder.UnreadCount;
    }
    return $result;
}

function ProcessResult($entry) 
{
    "Mailbox: {0}" -f $entry.EmailAddress
    "Unread Mails: {0}" -f $entry.UnreadMailCount
    "==============================================================================="
}

$addresses = Import-Csv $inputFile

$addresses | % {GetUnreadInboxMails($_.MailAddress)}  | % {ProcessResult ($_)} 

Just paste this script into an editor and save it as ps1 file.

The script mainly consists of two function: GetUnreadInboxMails and ProcessResult. The former method retrieves the number of unread mails in the inbox of the mailbox of the specified user. The number of unread mails can be retrieved from a Folder instance: The UnreadCount property. The method returns the number of unread mails along with the mail address of the current mailbox. The other important function is the ProcessResult method. In this script it merely dumps the result to the console. The questioner in the post linked above can use this function to call his HTTP service to send a text message.

The script has two parameters:

  1. The name of a CSV file containing the list of mailboxes to process. The first line should contain the column name “MailAddress” somewhere. The following lines should contain the email address of the mailbox in this column.
  2. A PSCredential object containing valid credentials to use for the Exchange access.

The credential used for the script needs read access to the inbox folder of each mailbox specified in the file.


Posted by Henning Krause on Monday, July 25, 2011 7:56 PM, last modified on Monday, July 25, 2011 7:56 PM
Permalink | Post RSSRSS comment feed

Exchange Managed API autodiscover with Powershell

Powershell is a great tool to automate all sorts of things – including fiddling around with your Exchange mailbox. And the Autodiscover makes it really easy to connect to it – especially if you’re on Office 365 and don’t even know your CAS server.

So first, we need to load the EWS Managed API dll into the current runspace:

[Reflection.Assembly]::LoadFrom("C:\Program Files\Microsoft\Exchange\Web Services\1.1\Microsoft.Exchange.WebServices.dll")

Then, create an ExchangeService instance and set its credentials:

$service =  New-Object Microsoft.Exchange.WebServices.Data.ExchangeService -ArgumentList([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1)
$service.Credentials = New-Object System.Net.NetworkCredential("someone@infinitec.de", "password", "domain");

Now we are ready to use AutoDiscover. But depending on your infrastructure, AutoDiscover might need to follow some redirections before it has discovered your CAS Server. Like in this case:

$service.AutodiscoverUrl("someone@infinitec.de");

Exception calling "AutodiscoverUrl" with "1" argument(s): "Autodiscover blocked a potentially insecure redirection to https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml. To allow Autodiscover to follow the redirection, use the AutodiscoverUrl(string, AutodiscoverRedirectionUrlValidationCallback) overload."
At line:1 char:25
+ $service.AutodiscoverUrl <<<< ("hkrause@infinitec.de");
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

This happens because the AutoDiscover process looks at autodiscover.infinitec.de and instead of an A record pointing to the AutoDiscover service, it finds a CNAME redirecting it to autodiscover.outlook.com. Because this might pose a security risk, the AutoDiscoverUrl method aborts  the discovery process and throws the Exception displayed above. The solution is also outlined: Instead of calling the method AutoDiscoverUrl(mailAddress) call the overload which expects a delegate as a second paramter. This delegate has a string as input and returns the $true if the discovery process should follow the redirection; false otherwise.

How can this overload be used with PowerShell? The answer is a ScriptBlock. If you simply want to allow the discovery process to follow all redirects, simply call it this way:

$service.AutodiscoverUrl("someone@infinitec.de", {$true})

But if you want to verify the discovery process is redirected to the correct url, use this version:

$TestUrlCallback = {
 param ([string] $url)
 if ($url -eq "https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml") {$true} else {$false}
}

$service.AutodiscoverUrl("someone@infinitec.de", $TestUrlCallback)

You can embed whatever checks you need to verify the given url in the third line of the $TestUrlCallback method.


Posted by Henning Krause on Friday, July 22, 2011 5:30 PM, last modified on Sunday, July 24, 2011 1:22 AM
Permalink | Post RSSRSS comment feed