Skip to main content

Creating a Pin to Top Email in Outlook using the EWS managed API and Powershell

In such an imperfect world in which we live coming up with different ways to look at seemingly ignorable problems can sometime be at the heart of innovation. It always strikes me a little odd that while the systems around the our inbox’s have grown and changed over’s the years there has always been a deficit in change around the way information is presented to us in that Inbox folder structure and the way in which a person can flexibly control the operation of how information is presented. In 2010 the new conversation view is a good step forward and some nice eye candy with its ability to group messages within a conversation. However for me this feels a little short of how one would intuitively look at a conversation. Eg a true conversation view would be able to explode the email conversation into conservatory elements rather than just grouping the emails together and asking me to do all the handwork. The problem with the later is it becomes another poweruser function useful to all but little used do to a lack of understanding in the user community. (But you can’t win look at the poor old clippy’s much maligned attempts to better educate users).

One area the pain of this verbiage originates from is the easy and frequency that many applications use email alerts and warning (it just so easy to put an alerting module in). As we fill our infrastructures and even clouds with monitoring and alerting systems are mailboxes in tern start to fill with output of these systems an increasing amount of malaise sets in. While the content of such alerts is useful in nature the size of frequency of this over a long period of time means the meaning of such alerts dissipates and rule redirections and other efforts people use to control the flow of these can create more problems with storage and invalidating the real meaning of these alert messages. So the idea behind this script and method was to look at ways of better using email based alerts within a current email environment.

So first for each alert I received I only wanted 1 email per day to represent this eg if your monitoring the event logs of large number of servers having one email that would float at the top of your mailbox and update itself anytime a important error that need investigation happened was what I really wanted. Essentially something that would pin itself on top of my other messages as some sort of reinforcement that this was important. But when you think about it this idea is applicable outside just the area of alerts you could use it for anything where you getting email based updates eg you could use it to create a pin to email that contained the movement of a particular stock price. So at the end of the week you would have 7 messages in your inbox with the movements of the stock price during that particular day and if it was important that you knew each time there was a big movement the current email would get updated and float to the top of you inbox and mark itself as unread. So look there is still some space for innovation and creative thinking in the realm boxed in software infrastructure.

So to deliver this I’ve created a EWS Managed API script that saves the changekey of the current message that it’s following as the postto message (if it exists else it will create it). Once a new update comes in it will update the body of the message with the text of the new event and change the received date and read properties which will cause the email to appear at the top of a folder. To allow the message to expire there is a search filter that only looks for message based on a creation time (not the update time).

To use the script you need to pass it the mailbox name to execute against and the text for the alert eg

./pinto.ps1 mailbox@domain.com “This is Alert Blah Blah Balh”

This is just a warning this is has no real testing and I’m not sure what it would do to archiving system (should be okay).

I’ve put a download of the code here the script itself looks like

$MailboxName = $args[0]
$MessageText = $args[1]
$MessageSubject = "Pin To Note Subject"

$expire = [DateTime]::Now.AddDays(-1)
if (Test-Path pifile.txt){
$pmailid = gc pifile.txt -totalcount 1
}
else{
$pmailid = "3B"
}
if ($pmailid -eq $null){$pmailid = "3B"}
$pmailid
function ConvertToByteArray($newString){

$byteArray = New-Object byte[] ($newString.length/2)
for($icnt=0;$icnt-lt$newString.length;$icnt=$icnt+2){
if ($icnt -eq 0){$byteArray[$icnt] = [Convert]::ToInt32($newString.SubString($icnt,2).tolower(), 16)}
else{$byteArray[($icnt/2)] = [Convert]::ToInt32($newString.SubString($icnt,2).tolower(), 16)}
}
return $byteArray

}

function CovertToHex($BinArray1){
$hexOut = ""
for($bc=0;$bc -lt $BinArray1.Length;$bc++){
$nhex = [Convert]::ToString($BinArray1[$bc], 16)
if ($nhex.length -eq 1){$nhex = "0" + $nhex}
$hexOut = $hexOut + $nhex
}
return $hexOut
}



$dllpath = "C:\Program Files\Microsoft\Exchange\Web Services\1.0\Microsoft.Exchange.WebServices.dll"
[void][Reflection.Assembly]::LoadFile($dllpath)
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2007_SP1)

$windowsIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$sidbind = "LDAP://<SID=" + $windowsIdentity.user.Value.ToString() + ">"
$aceuser = [ADSI]$sidbind

$service.AutodiscoverUrl($aceuser.mail.ToString())

$PrSearchKey = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(12299, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary);
$PR_Client_Submit_Time = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(57, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::SystemTime);
$PR_Delivery_Time = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(3590, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::SystemTime);
$PR_Flags = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(3591, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);

$Propset = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)
$Propset.Add($PR_Client_Submit_Time)
$Propset.Add($PR_Delivery_Time)
$Propset.Add($PrSearchKey)
$Propset.Add($PR_Flags)
$Propset.RequestedBodyType = [Microsoft.Exchange.WebServices.Data.BodyType]::Text

$Sfir = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo($PrSearchKey, [Convert]::ToBase64String((ConvertToByteArray($pmailId))))
$Sflt = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsGreaterThan([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived, $expire)

$sfCollection = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::And)

$sfCollection.add($Sfir)
$sfCollection.add($Sflt)


$view = new-object Microsoft.Exchange.WebServices.Data.ItemView(1)

$folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox,$MailboxName)
$InboxFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)

$frFolderResult = $InboxFolder.FindItems($sfCollection,$view)
if ($frFolderResult.Items.Count -ne 0){
"Item Found"
$frFolderResult.Items[0].load($Propset)
$frFolderResult.Items[0].Body.Text
$frFolderResult.Items[0].ExtendedProperties[0].Value = [DateTime]::Now
$frFolderResult.Items[0].ExtendedProperties[1].Value = [DateTime]::Now
$frFolderResult.Items[0].Body.Text = $frFolderResult.Items[0].Body.Text + $MessageText + "`r`n"
$frFolderResult.Items[0].isread = $false
$frFolderResult.Items[0].Update([Microsoft.Exchange.WebServices.Data.ConflictResolutionMode]::AlwaysOverwrite)
}
else{
"Item Not found"
$message = New-Object Microsoft.Exchange.WebServices.Data.EmailMessage($service)
$message.From = $MailboxName
$message.ToRecipients.Add($MailboxName)
$message.Subject = $MessageSubject
$message.Body = $MessageText + "`r`n"
$message.SetExtendedProperty($PR_Flags,"1")
$message.isread = $false
$message.save($InboxFolder.Id)
$message.load($Propset)
$message.ExtendedProperties[0].Value = [DateTime]::Now
$message.ExtendedProperties[1].Value = [DateTime]::Now
$message.Update([Microsoft.Exchange.WebServices.Data.ConflictResolutionMode]::AlwaysOverwrite)
CovertToHex($message.ExtendedProperties[2].Value) | out-file pifile.txt
}

Popular posts from this blog

The MailboxConcurrency limit and using Batching in the Microsoft Graph API

If your getting an error such as Application is over its MailboxConcurrency limit while using the Microsoft Graph API this post may help you understand why. Background   The Mailbox  concurrency limit when your using the Graph API is 4 as per https://docs.microsoft.com/en-us/graph/throttling#outlook-service-limits . This is evaluated for each app ID and mailbox combination so this means you can have different apps running under the same credentials and the poor behavior of one won't cause the other to be throttled. If you compared that to EWS you could have up to 27 concurrent connections but they are shared across all apps on a first come first served basis. Batching Batching in the Graph API is a way of combining multiple requests into a single HTTP request. Batching in the Exchange Mail API's EWS and MAPI has been around for a long time and its common, for email Apps to process large numbers of smaller items for a variety of reasons.  Batching in the Gr...

Sending a Message in Exchange Online via REST from an Arduino MKR1000

This is part 2 of my MKR1000 article, in this previous post  I looked at sending a Message via EWS using Basic Authentication.  In this Post I'll look at using the new Outlook REST API  which requires using OAuth authentication to get an Access Token. The prerequisites for this sketch are the same as in the other post with the addition of the ArduinoJson library  https://github.com/bblanchon/ArduinoJson  which is used to parse the Authentication Results to extract the Access Token. Also the SSL certificates for the login.windows.net  and outlook.office365.com need to be uploaded to the devices using the wifi101 Firmware updater. To use Token Authentication you need to register an Application in Azure https://msdn.microsoft.com/en-us/office/office365/howto/add-common-consent-manually  with the Mail.Send permission. The application should be a Native Client app that use the Out of Band Callback urn:ietf:wg:oauth:2.0:oob. You ...

How to test SMTP using Opportunistic TLS with Powershell and grab the public certificate a SMTP server is using

Most email services these day employ Opportunistic TLS when trying to send Messages which means that wherever possible the Messages will be encrypted rather then the plain text legacy of SMTP.  This method was defined in RFC 3207 "SMTP Service Extension for Secure SMTP over Transport Layer Security" and  there's a quite a good explanation of Opportunistic TLS on Wikipedia  https://en.wikipedia.org/wiki/Opportunistic_TLS .  This is used for both Server to Server (eg MTA to MTA) and Client to server (Eg a Message client like Outlook which acts as a MSA) the later being generally Authenticated. Basically it allows you to have a normal plain text SMTP conversation that is then upgraded to TLS using the STARTTLS verb. Not all servers will support this verb so if its not supported then a message is just sent as Plain text. TLS relies on PKI certificates and the administrative issue s that come around certificate management like expired certificates which is why ...
All sample scripts and source code is provided by for illustrative purposes only. All examples are untested in different environments and therefore, I cannot guarantee or imply reliability, serviceability, or function of these programs.

All code contained herein is provided to you "AS IS" without any warranties of any kind. The implied warranties of non-infringement, merchantability and fitness for a particular purpose are expressly disclaimed.