Monday, July 07, 2008

Creating an RSS Feed from a calendar or inbox folder using Powershell with an EWS helper class

A number of years ago i posted some scripts to create RSS feeds from public folders and calendar folders using Exchange event sinks and some pretty simple VBS. These scripts where pretty simple and worked pretty well and I've still got some running today. On Exchange 2007 development technologies have moved on and so its time to move these scripts along as well. The big advantage is that with Powershell, .NET 2.0 and Exchange Web Services the flexibility or what you can do and therefore what you can then leverage with the newer advances in the .NET framework offer you a pretty nice sandpit to play in.

The reality of this increase in flexibility is unfortunately an increase also in the complexity meaning that this reasonably simple VBS script becomes a little more complicated and while its possible to put together a script using SOAP strings it can be a pain to make this reusable and reliable in wide number of scenarios. To make this easier i've included the EWS routine's in a .NET class library along with a parser to write the XML RSS feed. This helper library has become a little dysfunctional and needs a clean up but it does handle the important tasks when creating a RSS feed from an Exchange Mailbox such as.

  • Creating a EWS Connection and using Autodisover if needed to find the CAS URL and handle Self Signed SSL certificates if they are used.
  • Implement the ability to use EWS impersonation or delegation to access another users Mailbox folder
  • Use Covertid to covert the EWS ID into OWA id to be used in the RSS links
  • Use FindItem , Calendar Paging and Restrictions to produce an array of items for the Feed based on Calendar or Inbox folders and a Time , Read/Unread restrictions.
  • Use GetItem to get the Body of Message to include in the Description Property of a RSS feed
These are some of the challenges to producing a usable RSS feed with Exchange 2007 and a good argument to actually use Visual Studio instead of Powershell. But this is why building your own class library can give you the best of both worlds and simplify the Powershell code you need to write. Also a library can help put this power in the hands of those sysadmin that don't want to dive into .NET coding.

To use the library to create a RSS feed from powershell you basically need to first load the dll

[void][Reflection.Assembly]::LoadFile("C:\temp\EWSUtil.dll")

Then you can use the objects that are defined within this class the first thing you need to do is create a EWS connection (this is a custom class i've created within the Class library that contains a Exchange Service Binding for EWS). The parameters you pass into the object creation affect what authentication is used (eg impersonation or delegation) and also whether autodisover is used or not. I haven't included any overloads this time as i didn't really have the time so all the parameters are mandatory you just need to pass in $null if you don't want to use some. So the parameters for the EWSConnection object.

$casURL = "https://" + $servername + "/EWS/Exchange.asmx"
$ewc = new-object EWSUtil.EWSConnection("user@domamain.com",$false, "username", "password", "domain", $casURL )

  1. This is the email address of the mailbox you want to create the feed from.
  2. This is a boolean that indicates whether you want to use EWS impersonation to access the mailbox you specified in 1. If this is set to $false then delegate access is used.
  3. This is the username to use if you want to specify implicant credentials if you want to use the currently logged on user set this to $null
  4. This is the password to use if you want to specify implicant credentials if you want to use the currently logged on user set this to $null
  5. This is the domain to use if you want to specify implicant credentials if you want to use the currently logged on user set this to $null
  6. This is the URL for the CAS server to use if you set this to $null the library will try to use autodiscover to find a CAS server URL. (this isn't site aware)
If your creating a calendar feed you need to set a duration to show the appointments within.The following will create a duration object to show appointments for the next 30 days. If your creating a feed from a mail folder like the inbox you need to do the reverse of this to get the items from the last 30 day received in the inbox etc.

$drDuration = new-object EWSUtil.EWS.Duration
$drDuration.StartTime = [DateTime]::UtcNow
$drDuration.EndTime = [DateTime]::UtcNow.AddDays(30)

The next thing you need to define is the folder you want to report on the writefeed method at the moment can only deal with Mail and calendar folders/objects well. You could use this on a public folder as well i wanted to include some easy search functions in the library to help find public folders easy (but again haven't had time). So at the moment if you want to do a pubic folder you would need to pass in the EWSID of the folder.

[EWSUtil.EWS.DistinguishedFolderIdType] $dType = new-object EWSUtil.EWS.DistinguishedFolderIdType
$dType.Id = [EWSUtil.EWS.DistinguishedFolderIdNameType]::calendar

The Queryfolder class is a class that uses a finditem operation to query Folder items based on some restrictions that are determined by the parameter passed into it. This has one overload in that it allows you to either specify a normal FolderID or a DistinguishedFolderId. So the parameter for the QueryFolder class are

$qfolder = new-object EWSUtil.QueryFolder($ewc,$dType, $drDuration,$false,$true)

  1. The Exchange WebServices Connection (custom class from library)
  2. The FolderIDtype or DistinguishedFolderIdType of the folder you want to produce the feed from
  3. Duration for query this can be either the time period for the calendar view you want to create or the time period of inbox emails you want to display.
  4. Unread or Read this is only relative to Mail items if you want the feed to only show unread items in the inbox for example set this to true
  5. IsCalendar you need to set this to $true if you want to create a feed from a Calendar folder correctly
This object will return a Generic list of mailbox items which you can then choose to either use yourself in any manner you wish or if you use it with the WriteFeed object it will generate an RSS feed file from this Generic list. Again this object has a number of parameters you need to pass to it.

$wfeed = new-object EWSUtil.WriteFeed($ewc.esb,$ewc.emEmailAddress, "c:\calendarfeed1.xml", "Calendar-Feed", $servername, $qfolder.fiFolderItems)

  1. The Exchange WebServices Connection (custom class from library)
  2. The filename and path for the feed you want created
  3. The Title for the feed.
  4. The ServerName used for the Links
  5. The folderItems generic list

Thats pretty much it I've included two sample powershell scripts in the download as well as a compiled version of the code and the source itself if you want to do your own thing. I've put a download of the code here .

11 comments:

Anonymous said...

u are THE man!!! the new dll works great, i love the powershell way with .net

you should work for microsoft and build this stuff into exchange.

John said...

Working great for me mate.

Can I use the old style event sink or do you have something available to work with an EWS notification?

Would be great to add this onto the blog thread if you can?

Also, a precompiled version that excludes the body of the calendar item would be ideal for my scenario.

Thanks Glen

Glen said...

Hi John,

No I dont have any notification code for this at the moment i just use a scheduled task but an event sink would work okay if you need real time updates.

Cheers
Glen

John said...

Hi Glen, I had a look around for a transport agent for calendar events but didnt find anything.

Have you made anything before you would care to share? :)

Glen said...

You generally wouldn't use a transport Agent for calendar event because in the case of normal appointments no actual email is generated so no transport events would fire. If you want to look at a event type approach you need to look at using EWS notifications or look at using Event sink which have been deemphasised in 2007

Cheers
Glen

Anonymous said...

Where are the settings to determine if the body of the email is just a link or is completely listed in the rss feed?

Glen said...

sorry there is no option for this it will allways try and include the body if availible.

Cheers
Glen

KarlMitschke said...

How can you find the EWSID of a public folder?

Glen said...

You should be able to use something like

[void][Reflection.Assembly]::LoadFile("C:\temp\EWSUtil.dll")

$pfPublicFolderPath = "/Level 1/Level2/Level3"

$mbMailboxEmail = "administrator@domain.com.au"
$ewc = new-object EWSUtil.EWSConnection($mbMailboxEmail,$false, "", "", "","")

$parentFolder = new-object EWSUtil.EWS.DistinguishedFolderIdType
$parentFolder.Id = [EWSUtil.EWS.DistinguishedFolderIdNameType]::publicfoldersroot
$fldarry = new-object EWSUtil.EWS.BaseFolderIdType[] 1
$fldarry[0] = $parentFolder
$rfRootFolder = $ewc.getFolder($fldarry)
$rfRootFolder[0].DisplayName

$pfArray = $pfPublicFolderPath.Split("/")
$pfFolder = $rfRootFolder[0]
for ($lint = 1; $lint -lt $pfArray.Length; $lint++) {
$pfFolder = $ewc.FindSubFolder($pfFolder, $pfArray[$lint]);
}
"Folder Name : " + $pfFolder.DisplayName.ToString()
"Folder Id : " + $pfFolder.FolderID.ID.ToString()

Cheers
Glen

Anonymous said...

Hi Glen, FindSubFolder does not exist as a method of EWSConnection. Did you mean something else, or was this method lost over time?

Anonymous said...

Hi I found this updated version of EWSUtil at another location...

http://msgdev.mvps.org/exdevblog/ewsutil.zip