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
}