Monday, July 21, 2014

Reporting on expired recurring meetings with EWS and Powershell

Someone asked an interesting question about recurring Appointments in Exchange recently that I thought I'd expand on a bit with blog post. Recurrence in Appointments,Meeting and Tasks are one of the more useful features of Exchange, but they are also one the features that are harder for developers and anybody writing code or scripts to get a handle on. These days there is some really good technical documentation on this so I won't explain too much but firstly http://msdn.microsoft.com/EN-US/library/office/dn727655(v=exchg.150).aspx but also the Exchange Protocol Document http://msdn.microsoft.com/en-us/library/ee201188(v=exchg.80).aspx are a must to read.

To refine this down a bit with Calendar Appointments if you have a recurring Appointment you don't have separate objects for each occurrence of the recurring Appointment, the recurrence is just a pattern stored in a property on what is called the master instance of that particular Appointment, Meeting or Task. Exception information is stored as part of the Recurrence pattern (while the actual data from the Exception if there is anything different from the Master instance eg say you add an attachment to an exception this data gets stored as AttachedItem on the Master instance).

What this all means is that when it comes time to actually query the Calendar Appointments these recurrence patterns need to be expanded. To do this in EWS you use the CalendarView class and you specify a Start and End Date for your query. Exchange will then do all the hard work for you and return the expanded Appointments (and exceptions) to you.

To get back to the actual question however of how you would find expired recurring Calendar Appointments you have to forget about making an expansion query and instead you want to make a query to return a list of these master instances of recurring calendar Appointments and then filter these at the client side to work out what has expired (or if you want to do other reporting like seeing who has made non expiring appointments you can do this).

So to do this you can use the FindItem operation with a restriction on the PidLidRecurring property http://msdn.microsoft.com/en-us/library/ee157994(v=EXCHG.80).aspx which looks like this

  1. $Recurring = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::Appointment, 0x8223,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Boolean);   
  2.   
  3. $sfItemSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo($Recurring,$true)   

This will then return a list of Master Appointment instances, to get the full details of the Recurrence of each appointment you need to do a GetItem on each of the master instance which in the Managed API you can use the LoadPropertiesForItems method for. This will make a batch GetItem request to the Exchange Server to return the detail on all Items your request it for.

Once you have the recurrence information you can then filter those Meetings that have an End Date (or Recurrence number) set by using the HasEnd property.  Then to check the Last Occurrence you can use the LastOccurrence property. If you want to report on other things like meetings that don't have an EndDate or meetings that are expiring in the next week or month this is where you can put your own customization.

The following script will produce a report of any expired Meetings in a Mailbox's calendar and save this to a CSV file. I've put a download of this script here the code itself looks like

  1. ## Get the Mailbox to Access from the 1st commandline argument  
  2.   
  3. $MailboxName = $args[0]  
  4.   
  5. ## Load Managed API dll    
  6. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.1\Microsoft.Exchange.WebServices.dll"    
  7.     
  8. ## Set Exchange Version    
  9. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2    
  10.     
  11. ## Create Exchange Service Object    
  12. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)    
  13.     
  14. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials    
  15.     
  16. #Credentials Option 1 using UPN for the windows Account    
  17. $psCred = Get-Credential    
  18. $creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())    
  19. $service.Credentials = $creds        
  20.     
  21. #Credentials Option 2    
  22. #service.UseDefaultCredentials = $true    
  23.     
  24. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates    
  25.     
  26. ## Code From http://poshcode.org/624  
  27. ## Create a compilation environment  
  28. $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider  
  29. $Compiler=$Provider.CreateCompiler()  
  30. $Params=New-Object System.CodeDom.Compiler.CompilerParameters  
  31. $Params.GenerateExecutable=$False  
  32. $Params.GenerateInMemory=$True  
  33. $Params.IncludeDebugInformation=$False  
  34. $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null  
  35.   
  36. $TASource=@' 
  37.   namespace Local.ToolkitExtensions.Net.CertificatePolicy{ 
  38.     public class TrustAll : System.Net.ICertificatePolicy { 
  39.       public TrustAll() {  
  40.       } 
  41.       public bool CheckValidationResult(System.Net.ServicePoint sp, 
  42.         System.Security.Cryptography.X509Certificates.X509Certificate cert,  
  43.         System.Net.WebRequest req, int problem) { 
  44.         return true; 
  45.       } 
  46.     } 
  47.   } 
  48. '@   
  49. $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)  
  50. $TAAssembly=$TAResults.CompiledAssembly  
  51.   
  52. ## We now create an instance of the TrustAll and attach it to the ServicePointManager  
  53. $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")  
  54. [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll  
  55.   
  56. ## end code from http://poshcode.org/624  
  57.     
  58. ## Set the URL of the CAS (Client Access Server) to use two options are availbe to use Autodiscover to find the CAS URL or Hardcode the CAS to use    
  59.     
  60. #CAS URL Option 1 Autodiscover    
  61. $service.AutodiscoverUrl($MailboxName,{$true})    
  62. "Using CAS Server : " + $Service.url     
  63.      
  64. #CAS URL Option 2 Hardcoded    
  65.     
  66. #$uri=[system.URI] "https://casservername/ews/exchange.asmx"    
  67. #$service.Url = $uri      
  68.     
  69. ## Optional section for Exchange Impersonation    
  70.     
  71. #$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)   
  72.   
  73. # Bind to the Calendar Folder  
  74. $folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Calendar,$MailboxName)     
  75. $Calendar = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)  
  76.   
  77. $Recurring = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::Appointment, 0x8223,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Boolean);   
  78.   
  79. $sfItemSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo($Recurring,$true)   
  80.   
  81.   
  82. #Define ItemView to retrive just 1000 Items      
  83. $ivItemView =  New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)     
  84. $rptCollection = @()  
  85. $fiItems = $null      
  86. do{   
  87.     $psPropset= new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)    
  88.     $fiItems = $service.FindItems($Calendar.Id,$sfItemSearchFilter,$ivItemView)     
  89.     if($fiItems.Items.Count -gt 0){  
  90.         [Void]$service.LoadPropertiesForItems($fiItems,$psPropset)    
  91.         foreach($Item in $fiItems.Items){   
  92.             if($Item.Recurrence.HasEnd){  
  93.                 if($Item.LastOccurrence.End -lt (Get-Date)){  
  94.                     $rptObj = "" | Select Mailbox,Organizer,AppointmentSubject,FirstOccuranceStart,FirstOccuranceEnd,LastOccuranceStart,LastOccuranceEnd  
  95.                     Write-Host "Appointment : "  $Item.Subject  
  96.                     Write-Host "Last Occurance : " $Item.LastOccurrence.Start  
  97.                     $rptObj.Mailbox = $MailboxName  
  98.                     $rptObj.Organizer = $Item.Organizer.Name  
  99.                     $rptObj.AppointmentSubject = $Item.Subject  
  100.                     $rptObj.FirstOccuranceStart = $Item.FirstOccurrence.Start  
  101.                     $rptObj.FirstOccuranceEnd = $Item.FirstOccurrence.End  
  102.                     $rptObj.LastOccuranceStart = $Item.LastOccurrence.Start  
  103.                     $rptObj.LastOccuranceEnd = $Item.LastOccurrence.End               
  104.                     $rptCollection += $rptObj  
  105.                 }  
  106.             }  
  107.         }  
  108.     }  
  109.     $ivItemView.Offset += $fiItems.Items.Count      
  110. }while($fiItems.MoreAvailable -eq $true)   
  111. $rptCollection | Export-Csv -NoTypeInformation -Path c:\Temp\$MailboxName-ExpiredRecurringMeetings.csv  
  112. Write-Host "Report written to " c:\Temp\$MailboxName-ExpiredRecurringMeetings.csv  



Wednesday, July 02, 2014

Creating a Mailbox Folder Growth Map with Powershell, EWS and eDiscovery

I've posted before on Exchange 2010 about how you can use AQS to produce a mailbox Item Age and Size reports. This same method can be used and enhanced on 2013 using eDiscovery to produce a growth report. Eg something like this that shows the mailbox growth for each folder (that has grown) based on the size of the Items in the folder for that month and an ASCII graph to show the growth vs the FolderSize


So to do this the follwing script uses eDiscovery on 2013 or Exchange Online to make a query of all the items in the primary mailbox over the period of 12 months using the following KQL query

received:2013-01-01..2014-01-01

Which queries for items that where received between these two dates, in the script I have variables to calculate this to give the exact 12 month period eg

$KQL = "received:" + $StartDate.ToString("yyyy-MM-dd") + ".." + $EndDate.ToString("yyyy-MM-dd");

The rest of the script is another variant of my eDiscovery template script with some other code to summaries the growth for each month and add the foldersizes.

To run this script you just feed in the name of the Mailbox you want to run it against eg

 .\growthMap12months.ps1 user@domain.com

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

  1. ## Get the Mailbox to Access from the 1st commandline argument  
  2.   
  3. $MailboxName = $args[0]  
  4. $SearchableMailboxString = $MailboxName;  
  5. $StartDate = (Get-Date).AddMonths(-11)  
  6. $EndDate = (Get-Date)  
  7.   
  8. $KQL = "received:" + $StartDate.ToString("yyyy-MM-dd") + ".." + $EndDate.ToString("yyyy-MM-dd");           
  9.  
  10.  
  11. ## Load Managed API dll    
  12. Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.1\Microsoft.Exchange.WebServices.dll"    
  13.    
  14. ## Set Exchange Version    
  15. $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013_SP1    
  16.    
  17. ## Create Exchange Service Object    
  18. $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)    
  19.    
  20. ## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials    
  21.    
  22. #Credentials Option 1 using UPN for the windows Account    
  23. $psCred = Get-Credential    
  24. $creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())    
  25. $service.Credentials = $creds        
  26.    
  27. #Credentials Option 2    
  28. #service.UseDefaultCredentials = $true    
  29.    
  30. ## Choose to ignore any SSL Warning issues caused by Self Signed Certificates    
  31.    
  32. ## Code From http://poshcode.org/624  
  33. ## Create a compilation environment  
  34. $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider  
  35. $Compiler=$Provider.CreateCompiler()  
  36. $Params=New-Object System.CodeDom.Compiler.CompilerParameters  
  37. $Params.GenerateExecutable=$False  
  38. $Params.GenerateInMemory=$True  
  39. $Params.IncludeDebugInformation=$False  
  40. $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null  
  41.   
  42. $TASource=@' 
  43.   namespace Local.ToolkitExtensions.Net.CertificatePolicy{ 
  44.     public class TrustAll : System.Net.ICertificatePolicy { 
  45.       public TrustAll() {  
  46.       } 
  47.       public bool CheckValidationResult(System.Net.ServicePoint sp, 
  48.         System.Security.Cryptography.X509Certificates.X509Certificate cert,  
  49.         System.Net.WebRequest req, int problem) { 
  50.         return true; 
  51.       } 
  52.     } 
  53.   } 
  54. '@   
  55. $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)  
  56. $TAAssembly=$TAResults.CompiledAssembly  
  57.  
  58. ## We now create an instance of the TrustAll and attach it to the ServicePointManager  
  59. $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")  
  60. [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll  
  61.  
  62. ## end code from http://poshcode.org/624  
  63.    
  64. ## Set the URL of the CAS (Client Access Server) to use two options are availbe to use Autodiscover to find the CAS URL or Hardcode the CAS to use    
  65.    
  66. #CAS URL Option 1 Autodiscover    
  67. $service.AutodiscoverUrl($MailboxName,{$true})    
  68. "Using CAS Server : " + $Service.url     
  69.     
  70. #CAS URL Option 2 Hardcoded    
  71.    
  72. #$uri=[system.URI] "https://casservername/ews/exchange.asmx"    
  73. #$service.Url = $uri      
  74.    
  75. ## Optional section for Exchange Impersonation    
  76.    
  77. #$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)   
  78. function ConvertToString($ipInputString){    
  79.     $Val1Text = ""    
  80.     for ($clInt=0;$clInt -lt $ipInputString.length;$clInt++){    
  81.             $Val1Text = $Val1Text + [Convert]::ToString([Convert]::ToChar([Convert]::ToInt32($ipInputString.Substring($clInt,2),16)))    
  82.             $clInt++    
  83.     }    
  84.     return $Val1Text    
  85. }   
  86.   
  87.   
  88. function GetFolderPaths{  
  89.     param (  
  90.             $rootFolderId = "$( throw 'rootFolderId is a mandatory Parameter' )",  
  91.             $Archive = "$( throw 'Archive is a mandatory Parameter' )"  
  92.           )  
  93.     process{  
  94.     #Define Extended properties    
  95.     $PR_FOLDER_TYPE = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(13825,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);    
  96.     $PR_MESSAGE_SIZE_EXTENDED = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(3592, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Long);  
  97.     $folderidcnt = $rootFolderId  
  98.     #Define the FolderView used for Export should not be any larger then 1000 folders due to throttling    
  99.     $fvFolderView =  New-Object Microsoft.Exchange.WebServices.Data.FolderView(1000)    
  100.     #Deep Transval will ensure all folders in the search path are returned    
  101.     $fvFolderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep;    
  102.     $psPropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)    
  103.     $PR_Folder_Path = new-object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(26293, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String);    
  104.     #Add Properties to the  Property Set    
  105.     $psPropertySet.Add($PR_Folder_Path);    
  106.     $psPropertySet.Add($PR_MESSAGE_SIZE_EXTENDED)  
  107.     $fvFolderView.PropertySet = $psPropertySet;    
  108.     #The Search filter will exclude any Search Folders    
  109.     $sfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo($PR_FOLDER_TYPE,"1")    
  110.     $fiResult = $null    
  111.     #The Do loop will handle any paging that is required if there are more the 1000 folders in a mailbox    
  112.     do {    
  113.         $fiResult = $Service.FindFolders($folderidcnt,$sfSearchFilter,$fvFolderView)    
  114.         foreach($ffFolder in $fiResult.Folders){  
  115.             $rptFld = "" | Select FolderPath,TotalItemCount,TotalFolderSize,PeriodGrowthSize  
  116.             $valDate = $StartDate  
  117.             do{  
  118.                 $MonthVal = $valDate.ToString("MMMM") + $valDate.ToString("yyyy")  
  119.                 Add-Member -InputObject $rptFld -MemberType NoteProperty -Name $MonthVal -Value 0   
  120.                 $valDate = $valDate.AddMonths(1)  
  121.             }while($valDate -le $EndDate)  
  122.             Add-Member -InputObject $rptFld -MemberType NoteProperty -Name "GrowthGraph" -Value ""  
  123.             $foldpathval = $null    
  124.             #Try to get the FolderPath Value and then covert it to a usable String     
  125.             if ($ffFolder.TryGetProperty($PR_Folder_Path,[ref] $foldpathval))    
  126.             {    
  127.                 $binarry = [Text.Encoding]::UTF8.GetBytes($foldpathval)    
  128.                 $hexArr = $binarry | ForEach-Object { $_.ToString("X2") }    
  129.                 $hexString = $hexArr -join ''    
  130.                 $hexString = $hexString.Replace("FEFF""5C00")    
  131.                 $fpath = ConvertToString($hexString)    
  132.             }   
  133.             $folderSize = $null  
  134.             if ($ffFolder.TryGetProperty($PR_MESSAGE_SIZE_EXTENDED,[ref] $folderSize))    
  135.             {     
  136.                 if ($folderSize -gt 0){  
  137.                     $rptFld.TotalFolderSize = $folderSize  
  138.                 }  
  139.                 else{  
  140.                     $rptFld.TotalFolderSize = $folderSize  
  141.                 }  
  142.             }  
  143.             "FolderPath : " + $fpath  
  144.             if($Archive){  
  145.                 $fpath = "\Archive-Mailbox\" + $fpath; 
  146.             } 
  147.             $rptFld.FolderPath = $fpath 
  148.             $rptFld.TotalItemCount = $ffFolder.TotalCount 
  149.             $Script:FolderCache.Add($ffFolder.Id.UniqueId,$rptFld); 
  150.         }  
  151.         $fvFolderView.Offset += $fiResult.Folders.Count 
  152.     }while($fiResult.MoreAvailable -eq $true)   
  153.     } 
  154. } 
  155.  
  156. $Script:FolderCache = New-Object system.collections.hashtable 
  157. GetFolderPaths -rootFolderId (new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$MailboxName)) -Archive $false   
  158. GetFolderPaths -rootFolderId (new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::RecoverableItemsRoot,$MailboxName)) -Archive $false   
  159.  
  160. $gsMBResponse = $service.GetSearchableMailboxes($SearchableMailboxString, $false); 
  161. $gsMBResponse 
  162. $msbScope = New-Object  Microsoft.Exchange.WebServices.Data.MailboxSearchScope[] $gsMBResponse.SearchableMailboxes.Length 
  163. $mbCount = 0; 
  164. foreach ($sbMailbox in $gsMBResponse.SearchableMailboxes) 
  165. { 
  166.     $msbScope[$mbCount] = New-Object Microsoft.Exchange.WebServices.Data.MailboxSearchScope($sbMailbox.ReferenceId, [Microsoft.Exchange.WebServices.Data.MailboxSearchLocation]::PrimaryOnly); 
  167.     $mbCount++; 
  168. } 
  169. $smSearchMailbox = New-Object Microsoft.Exchange.WebServices.Data.SearchMailboxesParameters 
  170. $mbq =  New-Object Microsoft.Exchange.WebServices.Data.MailboxQuery($KQL, $msbScope); 
  171. $mbqa = New-Object Microsoft.Exchange.WebServices.Data.MailboxQuery[] 1 
  172. $mbqa[0] = $mbq 
  173. $smSearchMailbox.SearchQueries = $mbqa; 
  174. $smSearchMailbox.PageSize = 100; 
  175. $smSearchMailbox.PageDirection = [Microsoft.Exchange.WebServices.Data.SearchPageDirection]::Next; 
  176. $smSearchMailbox.PerformDeduplication = $false;            
  177. $smSearchMailbox.ResultType = [Microsoft.Exchange.WebServices.Data.SearchResultType]::PreviewOnly; 
  178. $srCol = $service.SearchMailboxes($smSearchMailbox); 
  179. $rptCollection = @{} 
  180.  
  181. if ($srCol[0].Result -eq [Microsoft.Exchange.WebServices.Data.ServiceResult]::Success) 
  182. { 
  183.     Write-Host ("Items Found " + $srCol[0].SearchResult.ItemCount) 
  184.     if ($srCol[0].SearchResult.ItemCount -gt 0) 
  185.     {                   
  186.         do 
  187.         { 
  188.             $smSearchMailbox.PageItemReference = $srCol[0].SearchResult.PreviewItems[$srCol[0].SearchResult.PreviewItems.Length - 1].SortValue; 
  189.             foreach ($PvItem in $srCol[0].SearchResult.PreviewItems) { 
  190.                 $rptObj = "" | select FolderPath,DateTimeReceived,Subject,Size 
  191.                 if($Script:FolderCache.ContainsKey($PvItem.ParentId.UniqueId)){ 
  192.                     if($rptCollection.ContainsKey($PvItem.ParentId.UniqueId) -eq $false){ 
  193.                         $rptCollection.Add($PvItem.ParentId.UniqueId,$Script:FolderCache[$PvItem.ParentId.UniqueId]) 
  194.                     } 
  195.                     $MonthVal = $PvItem.ReceivedTime.ToString("MMMM") + $PvItem.ReceivedTime.ToString("yyyy") 
  196.                     $rptCollection[$PvItem.ParentId.UniqueId]."$MonthVal" += $PvItem.Size 
  197.                 } 
  198.                          
  199.             }                         
  200.             $srCol = $service.SearchMailboxes($smSearchMailbox); 
  201.             Write-Host("Items Remaining : " + $srCol[0].SearchResult.ItemCount); 
  202.         } while ($srCol[0].SearchResult.ItemCount-gt 0 ); 
  203.          
  204.     } 
  205.      
  206. } 
  207. $rptOutput = @() 
  208. $rptCollection.GetEnumerator() |  ForEach-Object{ 
  209.     $valDate = $StartDate 
  210.         do{ 
  211.             $MonthVal = $valDate.ToString("MMMM") + $valDate.ToString("yyyy") 
  212.             $_.Value.PeriodGrowthSize += $_.Value."$MonthVal" 
  213.             $_.Value."$MonthVal" = [math]::round($_.Value."$MonthVal"/1Mb,2) 
  214.             $valDate = $valDate.AddMonths(1) 
  215.         }while($valDate -le $EndDate) 
  216.         $GrowthPercent = [Math]::round((($_.Value.PeriodGrowthSize/$_.Value.TotalFolderSize) * 100)) 
  217.         $_.Value.PeriodGrowthSize = [math]::round($_.Value.PeriodGrowthSize/1Mb,2) 
  218.         $_.Value.TotalFolderSize = [math]::round($_.Value.TotalFolderSize /1Mb, 2) 
  219.         
  220.     $GrowthPercent 
  221.     $PercentGraph = ""   
  222.     for($intval=0;$intval -lt 100;$intval+=4){   
  223.         if($GrowthPercent -gt $intval){   
  224.             $PercentGraph += ""   
  225.         }   
  226.         else{          
  227.             $PercentGraph += "░"    
  228.         }    
  229.     }    
  230.     $_.Value.GrowthGraph = $PercentGraph      
  231.     $rptOutput += $_.Value  
  232. }  
  233. $rptOutput | Export-Csv -NoTypeInformation -Path c:\temp\$MailboxName-mbgrowMap.csv -Encoding UTF8  
  234. $rptOutput | Select FolderPath,GrowthGraph