Recently, I had a use case to start a workflow based on an email sent to an inbox. After a brief search I determined that there's not really a way to do this with PowerShell. Sometimes our reach exceeds PowerShell's grasp and for those times it is fortunate that PowerShell can play so well with .NET.
To accomplish this task, it is necessary to install the Exchange Web Service API. You can find it here:
Download Microsoft Exchange Web Services Managed API 2.2
You can find the full documentation here:
Start using web services in Exchange
The first step is to download and install the EWS API. Once that is done, you'll need to load it to gain its powers.
# Load the EWS Managed API
$dllpath = "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll"
[void][Reflection.Assembly]::LoadFile($dllpath)
Once you've got it loaded, we can create an object in PowerShell and start manipulating it. If you are working with .NET objects, then you need to head to the MSDN and do some research on the class you are trying to manipulate. In this case, we are going to use the
Exchange Service class. We are looking at the
ExchangeService constructor and we are going to
specify the version of EWS we need when calling the class (2013 in this case).
Reading the documentation on the ExchangeService class, we can see that we have options for authentication. In my case, I needed to pass credentials through to this, rather than use the credentials of the user running the script. For example purposes, I'm just going to provide a PSCredential object by calling the Get-Credential cmdlet and inputting credentials with access to the account I'm using:
Note that looking at the documentation for the
ExchangeServiceBase.Credentials property we need to convert the PSCredential object to a
Network Credential.
Once we've got authentication, we need an endpoint to auth to. The EWS autodiscover endpoint can help us and there's a handy
method on the ExchangeService class that we can use to connect to it with the mailboxname.
$exchangeService = new-object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013_SP1)
# Set Credentials in Exchange WebService Object
$ExchangeAdminCreds = Get-Credential
$exchangeService.Credentials = $ExchangeAdminCreds.GetNetworkCredential()
# Autodiscover to determine EWS URL
$mailboxname = "YourMailbox@yourexchange.com"
$exchangeService.AutodiscoverUrl($mailboxName)
For my needs, I wanted to monitor the inbox of this folder. I discovered that to connect to the inbox, we needed to go up a level to the Microsoft.Exchange.WebServices.Data namespace and find the
WellKnownFolderName enumeration, and then use this with the mailbox name to get the ID with
Microsoft.Exchange.WebServices.Data.FolderId constructor. Once you've got that, you can
bind to the folder. Sometimes, you'll need to work backwards to determine how these work together (start at the action you need to perform and read what it requires).
# Load type for WellKnownFolderName
$inboxFolderName = [Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox
# Identify FolderID from target mailbox
$InboxFolderID = New-Object -TypeName Microsoft.Exchange.WebServices.Data.FolderId -argumentlist $inboxFolderName, $mailboxName
# Bind to the Inbox folder of the target mailbox
$inboxFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($exchangeService,$inboxFolderID)
You're thinking, great - now I've got my binding to the folder and I'm ready to start getting emails. Nope, we've still got some more work to do. It might be a good idea, depending on your needs to not get a billion hits. You can limit this with an
itemview. Additionally, we'll need to set up a searchfilter to find the results we need. For this example, I need to limit by multiple criteria. This requires a
SearchFilterCollection. To create that, we look at
its constructor and work backwards for what we need (
datetimerecieved and
from for example).
# Optional: reduce the query overhead by viewing the inbox 10 items at a time
$itemView = New-Object -TypeName Microsoft.Exchange.WebServices.Data.ItemView -ArgumentList 10
# Load Types for creating search filter.
$dateTimeItem = [Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived
$from = [Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::From
$searchbefore = (Get-Date).AddMinutes(-1)
$searchafter = (Get-Date).AddDays(-2)
$emailfrom = "someaddress@somewhere.com"
$searchFilter = New-Object -TypeName Microsoft.Exchange.WebServices.Data.SearchFilter+IsGreaterThanOrEqualTo -ArgumentList $dateTimeItem,$searchafter
$searchFilter1 = New-Object -TypeName Microsoft.Exchange.WebServices.Data.SearchFilter+IsLessThanOrEqualTo -ArgumentList $dateTimeItem,$searchbefore
$searchFilter2 = New-Object -TypeName Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo -ArgumentList $from,$emailfrom
$SearchFilterCollection = New-Object -TypeName Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection -ArgumentList "And",$searchFilter
$SearchFilterCollection.Add($searchFilter1)
$SearchFilterCollection.Add($searchFilter2)
Now, we can finally put this all together using the
FindItems method of the ExchangeService class. As I mentioned earlier, the way to navigate this is to find what you need to do and work backwards.
The
$FoundItems variable contains an array of emails which should match your search criteria. I'll leave it to you to manipulate those and explore MSDN.
# Use FindItems method with arguments FolderID,SearchFilter,ItemView
$FoundItems = $exchangeService.FindItems($inboxFolder.Id,$searchFilterCollection,$itemView)