<# .Synopsis Get folders that have had recent content changes .Description Search a mailbox for folders that have changed content in the last number of specified minutes .Parameter EmailAddress Email address of the mailbox to search .Parameter MinutesBeforeNow Integer value of the number of minutes before now to set as a search restriction. Default is 30. .Example Get-FoldersWithContentChanges.ps1 johndoe@company.com .Example Get-FoldersWithContentChanges.ps1 -EmailAddress johndoe@company.com -MinutesBeforeNow 5 .Notes Version: 1.0 Date: 12/21/17 #> Param ( [Parameter(Position=0,Mandatory=$true,HelpMessage="Email address of mailbox")][string]$EmailAddress, [Parameter(Position=1,Mandatory=$false)][int]$MinutesBeforeNow = 30 ) function Convert-HexToASCII($hex) { $text = "" for ($i=0;$i -lt $hex.Length;$i++) { $text = $text + [Convert]::ToString([Convert]::ToChar([Convert]::ToInt32($hex.Substring($i,2),16))) $i++ } return $text } #Check for EWS API $apiPath = (($(Get-ItemProperty -ErrorAction SilentlyContinue -Path Registry::$(Get-ChildItem -ErrorAction SilentlyContinue -Path 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Exchange\Web Services' | Sort-Object Name -Descending | Select-Object -First 1 -ExpandProperty Name)).'Install Directory') + 'Microsoft.Exchange.WebServices.dll') if (Test-Path $apiPath) { Add-Type -Path $apiPath } else { Write-Error "The Exchange Web Services Managed API is required to use this script." -Category NotInstalled break } $exchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1 $exchangeService = New-Object -TypeName Microsoft.Exchange.WebServices.Data.ExchangeService($exchangeVersion) $exchangeservice.Url = 'https://outlook.office365.com/ews/exchange.asmx' if (-not($creds)) { $creds = Get-Credential } $exchangeService.Credentials = New-Object -TypeName Microsoft.Exchange.WebServices.Data.WebCredentials($creds) $changedTimeStart = [datetime]::Now.AddMinutes(-$MinutesBeforeNow) $output = @() #MAPI property types being referenced $propFolderType = New-Object -TypeName Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(13825,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer) $propFolderLastChanged = New-Object -TypeName Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x670A, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::SystemTime); $propFolderPath = New-Object -TypeName Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(26293, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String) $propDisplayName = [Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName #Properties to include in search result $ps = New-Object -TypeName Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::IdOnly) $ps.Add($propFolderLastChanged) $ps.Add($propFolderPath) $ps.Add($propDisplayName) #Folder view: Search subfolders and return properties in property set $fv = New-Object -TypeName Microsoft.Exchange.WebServices.Data.FolderView(1000) $fv.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep $fv.PropertySet = $ps #Search filter: Non-search folders with changed content since last N minutes $sfItem1 = New-Object -TypeName Microsoft.Exchange.WebServices.Data.SearchFilter+IsGreaterThan($propFolderLastChanged,$changedTimeStart) $sfItem2 = New-Object -TypeName Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo($propFolderType,1) $sfCollection = New-Object -TypeName Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::And) $sfCollection.Add($sfItem1) $sfCollection.Add($sfItem2) #Bind to folder to search from and check its own changed time because the search won't include itself $folderId = New-Object -TypeName Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root,$EmailAddress) $baseFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($exchangeService,$folderId,$ps) $lastChangeTime = $null [void]$baseFolder.TryGetProperty($propFolderLastChanged,[ref]$lastChangeTime) if ($lastChangeTime -gt $changedTimeStart) { $row = "" | Select-Object -Property FolderName,LastChanged,Path $row.FolderName = 'Root' $row.LastChanged = $lastChangeTime.ToLocalTime() $row.Path = '\' $output += $row } #Execute search and process results $findFolderResults = $baseFolder.FindFolders($sfCollection,$fv) foreach ($folder in $findFolderResults.Folders) { $row = "" | Select-Object -Property FolderName,LastChanged,Path $row.FolderName = $folder.DisplayName #Get the value of the last commit time $lastChangeTime = $null [void]$folder.TryGetProperty($propFolderLastChanged,[ref]$lastChangeTime) $row.LastChanged = $lastChangeTime.ToLocalTime() #Get the folder path and convert to readable string $folderPathPropValue = $null [void]$folder.TryGetProperty($propFolderPath,[ref]$folderPathPropValue) $byteArray = [Text.Encoding]::UTF8.GetBytes($folderPathPropValue) $hexString = ($byteArray | ForEach-Object {$_.ToString("X2")}) -join '' $hexString = $hexString.Replace("FEFF","5C00") $path = Convert-HexToASCII($hexString) $row.Path = $path $output += $row } if ($output.Length -gt 0) { $output | Sort-Object -Property LastChanged -Descending } else { Write-Output 'No folders have changed content in the specified window.' }