Monday, July 28, 2008

Show Exchange Whitespace / Retained Items and Deleted Mailbox Space in Powershell with flashy visuals

Each night most Exchange servers will run a scheduled maintenance operation which among other things does an online de-fragmentation of the Exchange Store, and removes deleted items and mailboxes that are past the stores configured retention periods. The result of these operations are logged to the event log with some interesting information about how much whitespace the online defrag operation recovered, the size of the retained items and retained deleted mailboxes remaining in the store. I've used these before in VBS here to do this in powershell is pretty easy. Lets jump into some code samples this is how you can get the whitepace in a Exchange store by querying the event logs via WMI for 1221 Event Log ids and then parsing the Size out of the event log message.

$WmidtQueryDT = [System.Management.ManagementDateTimeConverter]::ToDmtfDateTime([DateTime]::UtcNow.AddDays(-3))
Get-WmiObject -computer servername -query ("Select * from Win32_NTLogEvent Where Logfile='Application' and Eventcode = '1221' and TimeWritten >='" + $WmidtQueryDT + "'") |
foreach-object{
$mbnamearray = $_.message.split("`"")
$esEndString = $mbnamearray[2].indexof("megabytes ")-6
[System.Management.ManagementDateTimeConverter]::ToDateTime($_.TimeGenerated).ToString() + " " + $mbnamearray[1] + " " + $mbnamearray[2].Substring(5,$esEndString) + " MB"

}

For the Retained items you can do the following

$WmidtQueryDT = [System.Management.ManagementDateTimeConverter]::ToDmtfDateTime([DateTime]::UtcNow.AddDays(-3))
Get-WmiObject -computer servername -query ("Select * from Win32_NTLogEvent Where Logfile='Application' and Eventcode = '1207' and TimeWritten >='" + $WmidtQueryDT + "'") |
foreach-object{
$mbnamearray = $_.message.split("`"")
$enditem = $mbnamearray[2].Substring($mbnamearray[2].indexof("End:"))
$esize = $enditem.SubString($enditem.indexof("items")+7,$enditem.indexof(" Kbytes")-($enditem.indexof("items")+7))
[System.Management.ManagementDateTimeConverter]::ToDateTime($_.TimeGenerated).ToString() + " " + $mbnamearray[1] + " " + [math]::round(($esize/1024),2) + " MB"

}

For deleted Mailboxes you can do the following

$WmidtQueryDT = [System.Management.ManagementDateTimeConverter]::ToDmtfDateTime([DateTime]::UtcNow.AddDays(-3))
Get-WmiObject -computer servername -query ("Select * from Win32_NTLogEvent Where Logfile='Application' and Eventcode = '9535' and TimeWritten >='" + $WmidtQueryDT + "'") |
foreach-object{
$mbnamearray = $_.message.split("`"")
$retMbs = $mbnamearray[2].Substring($mbnamearray[2].indexof("have been removed."))
$retMbsSize = $retMbs.Substring(($retMbs.indexof("(")+1),$retMbs.indexof(")")-($retMbs.indexof("(")+1)).Replace(" KB","")
[System.Management.ManagementDateTimeConverter]::ToDateTime($_.TimeGenerated).ToString() + " " + $mbnamearray[1] + " " + [math]::round(($retMbsSize/1024),2) + " MB"

}

Okay so to put these little snippiets into something a whole lot more usefull by producing a Report that will first go out and grab the mailbox/public folder store sizes then grab the Whitespace, RetainedItems sizes and retained deleted Mailbox sizes and put them into a Datagrid and display them in a Winform. Also following on from last week we can then graph this data the first graph is a pie graph that show the percentage of space used across all stores on the server. Then the second graph is a stacked bar chart that compares all the parameters that where measured across all stores on a server. The below Pic shows what I mean


The graphs are generated using Google Charts and the picture Control Image location trick i talked about last week. This script uses straight ADSI and WMI so will work on either Exchange 2003 and 2007. I've put a download of this script here the code itself looks like

[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")

$snServerName = "servername"
$StoreFileSizes = @{ }
$StoreWhitespace = @{ }
$StoreRetainItemSize = @{ }
$StoreDeletedMailboxSize = @{ }
$hcHourCount = @{ }
$root = [ADSI]'LDAP://RootDSE'
$cfConfigRootpath = "LDAP://" + $root.ConfigurationNamingContext.tostring()
$configRoot = [ADSI]$cfConfigRootpath
$searcher = new-object System.DirectoryServices.DirectorySearcher($configRoot)
$searcher.Filter = '(&(objectCategory=msExchExchangeServer)(cn=' + $snServerName + '))'
$searchres = $searcher.FindOne()
$snServerEntry = New-Object System.DirectoryServices.directoryentry
$snServerEntry = $searchres.GetDirectoryEntry()




$adsiServer = [ADSI]('LDAP://' + $snServerEntry.DistinguishedName)
$dfsearcher = new-object System.DirectoryServices.DirectorySearcher($adsiServer)
$dfsearcher.Filter = "(objectCategory=msExchPrivateMDB)"
$srSearchResults = $dfsearcher.FindAll()
foreach ($srSearchResult in $srSearchResults){
$msMailStore = $srSearchResult.GetDirectoryEntry()
$sgStorageGroup = $msMailStore.psbase.Parent
if ($sgStorageGroup.msExchESEParamBaseName -ne "R00"){
$edbfile = [WMI]("\\" + $snServerName + "\root\cimv2:CIM_DataFile.Name='" + $msMailStore.msExchEDBFile + "'")
$StoreFileSize = [math]::round($edbfile.filesize/1048576,0)
$exStoreName = ($sgStorageGroup.Name.ToString() + "\" + $msMailStore.Name.ToString())
$StoreFileSizes.Add($exStoreName,$StoreFileSize)
}
}
"Finshed Mailstores"
$dfsearcher.Filter = "(objectCategory=msExchPublicMDB)"
$srSearchResults = $dfsearcher.FindAll()
foreach ($srSearchResult in $srSearchResults){
$msMailStore = $srSearchResult.GetDirectoryEntry()
$sgStorageGroup = $msMailStore.psbase.Parent
if ($sgStorageGroup.msExchESEParamBaseName -ne "R00"){
$edbfile = [WMI]("\\" + $snServerName + "\root\cimv2:CIM_DataFile.Name='" + $msMailStore.msExchEDBFile + "'")
$StoreFileSize = [math]::round($edbfile.filesize/1048576,0)
$exStoreName = ($sgStorageGroup.Name.ToString() + "\" + $msMailStore.Name.ToString())
$StoreFileSizes.Add($exStoreName,$StoreFileSize)
}
}
"Finshed Public Folder Stores"
$WmidtQueryDT = [System.Management.ManagementDateTimeConverter]::ToDmtfDateTime([DateTime]::UtcNow.AddDays(-3))
Get-WmiObject -computer $snServerName -query ("Select * from Win32_NTLogEvent Where Logfile='Application' and Eventcode = '1221' and TimeWritten >='" + $WmidtQueryDT + "'") |
foreach-object{
$mbnamearray = $_.message.split("`"")
$esEndString = $mbnamearray[2].indexof("megabytes ")-6
if ($StoreWhitespace.Containskey($mbnamearray[1]) -eq $false){
$StoreWhitespace.Add($mbnamearray[1],$mbnamearray[2].Substring(5,$esEndString))
}
}
"Finished WhiteSpace"
$WmidtQueryDT = [System.Management.ManagementDateTimeConverter]::ToDmtfDateTime([DateTime]::UtcNow.AddDays(-3))
Get-WmiObject -computer $snServerName -query ("Select * from Win32_NTLogEvent Where Logfile='Application' and Eventcode = '1207' and TimeWritten >='" + $WmidtQueryDT + "'") |
foreach-object{
$mbnamearray = $_.message.split("`"")
$enditem = $mbnamearray[2].Substring($mbnamearray[2].indexof("End:"))
$esize = $enditem.SubString($enditem.indexof("items")+7,$enditem.indexof(" Kbytes")-($enditem.indexof("items")+7))
if ($StoreRetainItemSize.Containskey($mbnamearray[1]) -eq $false){
$StoreRetainItemSize.Add($mbnamearray[1],[math]::round(($esize/1024),0))
}

}
"Finshed Retained Items"
$WmidtQueryDT = [System.Management.ManagementDateTimeConverter]::ToDmtfDateTime([DateTime]::UtcNow.AddDays(-3))
Get-WmiObject -computer $snServerName -query ("Select * from Win32_NTLogEvent Where Logfile='Application' and Eventcode = '9535' and TimeWritten >='" + $WmidtQueryDT + "'") |
foreach-object{
$mbnamearray = $_.message.split("`"")
$retMbs = $mbnamearray[2].Substring($mbnamearray[2].indexof("have been removed."))
$retMbsSize = $retMbs.Substring(($retMbs.indexof("(")+1),$retMbs.indexof(")")-($retMbs.indexof("(")+1)).Replace(" KB","")
if ($StoreDeletedMailboxSize.Containskey($mbnamearray[1]) -eq $false){
$StoreDeletedMailboxSize.Add($mbnamearray[1],[math]::round(($retMbsSize/1024),0))
}

}
"Finished Deleted Items"
$Dataset = New-Object System.Data.DataSet
$ssTable = New-Object System.Data.DataTable
$ssTable.TableName = "TrackingLogs"
$ssTable.Columns.Add("Storage Group")
$ssTable.Columns.Add("Store Name")
$ssTable.Columns.Add("EDB (MB)",[int])
$ssTable.Columns.Add("WhiteSpace (MB)",[int])
$ssTable.Columns.Add("Retained (MB)",[int])
$ssTable.Columns.Add("Deleted (MB)",[int])
$Dataset.tables.add($ssTable)

$i = 0
$TitleBlock = "Mailboxes" + "|" + "Public Folders" + "|" + "WhiteSpace" + "|" + "Retained Items" + "|" + "Deleted Mailboxes"
$lval = 0
$mbsize = 0
$pfsize = 0
$wsSize = 0
$risize = 0
$dmsize = 0
$lval = 0
$valueBlockbc = ""
$valueBlockbc1 = ""
$valueBlockbc2 = ""
$valueBlockbc3 = ""
$valueBlockbc4 = ""
$TitleBlockbc1 = ""
$dscale = ""
$StoreFileSizes.GetEnumerator() | sort name -descending | foreach-object {
$snames = $_.key.split("\")
if ($StoreDeletedMailboxSize.containskey($_.key)){
$cmdsize = ([INT]$StoreWhitespace[$_.key] + [INT]$StoreRetainItemSize[$_.key] + [INT]$StoreDeletedMailboxSize[$_.key])
$mbsize = + ($_.Value - $cmdsize)
$dmsize = $dmsize + $StoreDeletedMailboxSize[$_.key]
$ssTable.Rows.Add($snames[0],$snames[1],[math]::round(($_.Value),2),$StoreWhitespace[$_.key],$StoreRetainItemSize[$_.key],$StoreDeletedMailboxSize[$_.key])
if ($valueBlockbc -eq ""){$valueBlockbc = $mbsize.ToString()
$valueBlockbc1 = $StoreWhitespace[$_.key].ToString()
$valueBlockbc2 = $StoreRetainItemSize[$_.key].ToString()
$valueBlockbc3 = $StoreDeletedMailboxSize[$_.key].ToString() }
else {$valueBlockbc = $valueBlockbc + "," + $mbsize.ToString()
$valueBlockbc1 = $valueBlockbc1 + "," + $StoreWhitespace[$_.key].ToString()
$valueBlockbc2 = $valueBlockbc2 + "," + $StoreRetainItemSize[$_.key].ToString()
$valueBlockbc3 = $valueBlockbc3 + "," + $StoreDeletedMailboxSize[$_.key].ToString()
}
}
else{
$pfsize = $pfsize + ($_.Value - ([INT]$StoreWhitespace[$_.key] + [INT]$StoreRetainItemSize[$_.key]))
$ssTable.Rows.Add($snames[0],$snames[1],[math]::round(($_.Value),2),$StoreWhitespace[$_.key],$StoreRetainItemSize[$_.key],0)
if ($valueBlockbc -eq ""){$valueBlockbc = $pfsize.ToString()
$valueBlockbc1 = $StoreWhitespace[$_.key].ToString()
$valueBlockbc2 = $StoreRetainItemSize[$_.key].ToString()
$valueBlockbc3 = "0" }
else {$valueBlockbc = $valueBlockbc + "," + $pfsize.ToString()
$valueBlockbc1 = $valueBlockbc1 + "," + $StoreWhitespace[$_.key].ToString()
$valueBlockbc2 = $valueBlockbc2 + "," + $StoreRetainItemSize[$_.key].ToString()
$valueBlockbc3 = $valueBlockbc3 + ",0"}
}
if ($TitleBlockbc1 -eq ""){$TitleBlockbc1 = $snames[1]}
else {$TitleBlockbc1 = $snames[1] + "|" + $TitleBlockbc1}
$wsSize = $wsSize + $StoreWhitespace[$_.key]
$risize = $risize + $StoreRetainItemSize[$_.key]
if ($dscale -eq "") {$dscale = "0," + $_.Value}
else {$dscale = $dscale + "|0," + $_.Value}
if ($lval -lt $_.Value) {$lval = $_.Value}



}
$valueBlock = $mbsize.ToString() + "," + $pfsize.ToString() + "," + $wssize.ToString() + "," + $risize.ToString() + "," + $dmsize.ToString()
$csString = "http://chart.apis.google.com/chart?cht=p3&chs=370x120&chds=0," + $lval + "&chd=t:" + $valueBlock + "&chl=" + $TitleBlock + "&chco=0000ff,00ff00,ff0000,FFFFFF,000000"
$csString
$csString1 = "http://chart.apis.google.com/chart?cht=bhs&chs=850x350&chd=t:" + $valueBlockbc + "|" + $valueBlockbc1 + "|" + $valueBlockbc2 + "|" + $valueBlockbc3 +"&chds=0," + $lval + "&chxt=x,y&chxr=" + "&chxr=0,0," + ($lval+20) + "&chxl=1:|" + $TitleBlockbc1 + "&chdl=MailStorage|WhiteSpace|RetainedItems|DeletedMailboxes&chco=4d89f9,ff0000,c6d9fd,000000"
$csString1
$form = new-object System.Windows.Forms.form
$form.Text = "Server Store Size Report"

#add Picture box

$pbox = new-object System.Windows.Forms.PictureBox
$pbox.Location = new-object System.Drawing.Size(615,10)
$pbox.Size = new-object System.Drawing.Size(380,150)
$pbox.ImageLocation = $csString
$form.Controls.Add($pbox)

$pbox1 = new-object System.Windows.Forms.PictureBox
$pbox1.Location = new-object System.Drawing.Size(10,320)
$pbox1.Size = new-object System.Drawing.Size(1000,350)
$pbox1.ImageLocation = $csString1
$form.Controls.Add($pbox1)

# Add DataGrid View

$dgDataGrid = new-object System.windows.forms.DataGridView
$dgDataGrid.Location = new-object System.Drawing.Size(10,10)
$dgDataGrid.size = new-object System.Drawing.Size(600,300)
$dgDataGrid.ColumnHeadersHeightSizeMode = [System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode]::AutoSize
$dgDataGrid.AutoSizeColumnsMode = [System.Windows.Forms.DataGridViewAutoSizeColumnsMode]::Fill
$form.Controls.Add($dgDataGrid)
$dgDataGrid.DataSource = $ssTable

$form.Size = new-object System.Drawing.Size(1000,600)

$form.topmost = $true
$form.Add_Shown({$form.Activate()})
$form.ShowDialog()

Monday, July 21, 2008

Using Google Charts in Exchange Powershell Scripts

I've been looking at rewriting some message tracking scripts and one the things that's helpfull when you are doing message tracking is having some nice flashy visuals to help you out. Now there are some nice 3rd party packages out there like powergadgets who will separate you from your money and also the WPF framework stuff that give you some charting functionality. But if you want something that's free and will work from any workstation/server that has powershell and a Internet connection then its hard to go past Google Charts. Once you work you way around the different URL options its pretty much just a matter of creating some code to build the URL.

So to put this into something that is actually useful here's a couple of samples that first generate Rate information from the message tracking log files on Exchange 2003 using the wonder of WMI and on Exchange 2007 using the Exchange Management Shell get-messagetrackinglog cmdlet. The script itself is pretty simple it uses a hashtable to build this rate information then loops back though the hashtable to build a Google chart Url then it creates a Winform and adds a picture control setting the location property of the picture control to the URL that was built for Google charts. The cool thing is that's all you have to do because the picture control will handle all the http request stuff and download the png image and then display it to you how cool is that ! . Well I was pretty impressed anyway and you may see this technique popping up here a little more in the future.

I've put a sample of two scripts one for 2003 one for 2007 that shows a graph of the sent and received email for the last 6 hours. To use this script you need to set the hardcoded servername variable

$servername = "servername"

I've put a download of the code here the 2007 script looks like.

[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")

$servername = "servername"


$hcHourCount = @{ }
$dtQueryDT = [DateTime]::UtcNow.AddHours(-6)
$dtQueryDTf = [DateTime]::UtcNow
Get-MessageTrackingLog -Server $servername -ResultSize Unlimited -Start $dtQueryDT -End $dtQueryDTf -EventId "Send" | ForEach-Object{
$mdate = $_.TimeStamp
if ($hcHourCount.ContainsKey($mdate.hour)){
$hcHourCount[$mdate.hour] = [int]$hcHourCount[$mdate.hour] + 1
}
else{
$hcHourCount.Add($mdate.hour,1)
}
}
Get-MessageTrackingLog -Server $servername -ResultSize Unlimited -Start $dtQueryDT -End $dtQueryDTf -EventId "Receive" | ForEach-Object{
$mdate = $_.TimeStamp
if ($hcHourCount.ContainsKey($mdate.hour)){
$hcHourCount[$mdate.hour] = [int]$hcHourCount[$mdate.hour] + 1
}
else{
$hcHourCount.Add($mdate.hour,1)
}
}


$valueBlock = ""
$TitleBlock = ""
$lval = 0

$hcHourCount.GetEnumerator() | sort name -descending | foreach-object {
if ($lval -lt $_.value){$lval = $_.value}
if ($valueBlock -eq "") {$valueBlock = $_.value.ToString()}
else {$valueBlock = $valueBlock + "," + $_.value.ToString()}
if ($TitleBlock -eq ""){$TitleBlock = $_.key.ToString() + ":00"}
else {$TitleBlock = $_.key.ToString() + ":00" + "|" + $TitleBlock}

}

$hcHourCount
$csString = "http://chart.apis.google.com/chart?cht=bhg&chs=400x250&chd=t:" + $valueBlock + "&chds=0," + ($lval+20) + "&chxt=x,y&chxr=" + "&chxr=0,0," + ($lval+20) + "&chxl=1:|" + $TitleBlock + "&chco=00ff00&chtt=Message+Volume++Last+6+Hours"
$form = new-object System.Windows.Forms.form
$form.Text = "Last 6 Hours Graph"

#add Picture box

$pbox = new-object System.Windows.Forms.PictureBox
$pbox.Location = new-object System.Drawing.Size(10,10)
$pbox.Size = new-object System.Drawing.Size(400,250)
$pbox.ImageLocation = $csString
$form.Controls.Add($pbox)
$form.Size = new-object System.Drawing.Size(500,350)

$form.topmost = $true
$form.Add_Shown({$form.Activate()})
$form.ShowDialog()

Thursday, July 10, 2008

Mail enabling a Public Folder using Exchange Web Services in Exchange 2007

A quick one from the mail bag if you are creating public folders using Exchange Web Services and you want these folders to be mail enalbed without needing to use the Exchange Management Shell or the Public Folder Management console in SP1. Then you can still do this using the same method that worked in Exchange 2000/3 by setting the following two Mapi properties

PR_PUBLISH_IN_ADDRESS_BOOK = 0x3FE6000B
PR_PF_PROXY_REQUIRED = 0x671F000B

So in a CreateFolder Request you need something like

CreateFolderType cfCreateFolder = new CreateFolderType();
PathToExtendedFieldType PR_PUBLISH_IN_ADDRESS_BOOK = new PathToExtendedFieldType();
PR_PUBLISH_IN_ADDRESS_BOOK.PropertyTag = "0x3FE6";
PR_PUBLISH_IN_ADDRESS_BOOK.PropertyType = MapiPropertyTypeType.Boolean;
PathToExtendedFieldType PR_PF_PROXY_REQUIRED = new PathToExtendedFieldType();
PR_PF_PROXY_REQUIRED.PropertyTag = "0x671F";
PR_PF_PROXY_REQUIRED.PropertyType = MapiPropertyTypeType.Boolean;
ExtendedPropertyType pbPubAddressbook = new ExtendedPropertyType();
ExtendedPropertyType prProxyRequired = new ExtendedPropertyType();
pbPubAddressbook.ExtendedFieldURI = PR_PUBLISH_IN_ADDRESS_BOOK;
prProxyRequired.ExtendedFieldURI = PR_PF_PROXY_REQUIRED;
pbPubAddressbook.Item = "1";
prProxyRequired.Item = "1";
FolderType nfNewFolder = new FolderType();
nfNewFolder.ExtendedProperty = new ExtendedPropertyType[2];
nfNewFolder.ExtendedProperty[0] = prProxyRequired;
nfNewFolder.ExtendedProperty[1] = pbPubAddressbook;
nfNewFolder.DisplayName = nfNewFolderName;
TargetFolderIdType tfTargetFolder = new TargetFolderIdType();
cfCreateFolder.ParentFolderId = new TargetFolderIdType();
cfCreateFolder.ParentFolderId.Item = pfParentFolder;
cfCreateFolder.Folders = new FolderType[] { nfNewFolder };
CreateFolderResponseType cfResponse = esb.CreateFolder(cfCreateFolder);

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 .