none
PowerShell + EWS + Office365 + Folder.FindItems() = what appears to be cached results for subsequent queries RRS feed

  • Question

  • Hi,

    I have a script that is able to query my mailbox for AADSync related email notifications about UPN related errors.  The script can get the email notifications and parse the table in the message and dump the data to CSV no problem.  The script will do a search collection based search using only 2 criteria - a search string, and is message unread.  After processing the message, the script then marks the message as read and moves on.  All of this works fine - the first time I execute the script.  When I run the script more than once using the same search string, I get the exact same results even though the messages have already been marked as read.  If I happen to run the script the next day when there are no messages in an unread state with the specified search string, then I get zero results.  However, if I change the messages to an unread state and rerun the script, I continue to get zero results.  It's seems as though there is some server side caching?  Now, here's the kicker.... if I simply remove one character from the search string and run the script again, I get all of the messages back that I've just marked as unread and the data is dumped to CSV. 

    Is there a way for me to add code that tells the server "hey, you've already used this search string, but search again with the same string and give me fresh data".  :)

    Here's the script code:

    [CmdletBinding()]

    param

    (

           [Parameter(Mandatory = $false)]

           [String]$OutlookFolderName = "AADSyncErrors",

        [Parameter(Mandatory = $false)]

        [string]$SearchString = "[FederatedUser.UserPrincipalName]",

        #[Parameter(Mandatory = $false)]

        #[System.Uri]$Uri =   "https://<domain>/EWS/Exchange.asmx",

        [Parameter(Mandatory = $false)]

        [switch]$Delete,

           [Parameter(Mandatory = $true)]

           [System.Management.Automation.PsCredential]$Credential

    )

    function _GetScriptName

    {

        Return $MyInvocation.ScriptName

    }

    #   -----------------------------------------------------------------------------

    #

    # Main Script Execution

    #

    #   -----------------------------------------------------------------------------

    $Error.Clear()

    $dtmScriptStartTimeUTC = [datetime]::UtcNow

    $dtmFormatString = "yyyy-MM-dd HH:mm:ss"

    $webServicesDllPath = "C:\Program   Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll"

    $HtmlAgilityPackDllPath = "C:\Users\<user>\Downloads\HTML   Agility Pack\HtmlAgilityPack.1.4.6\Net45\HtmlAgilityPack.dll"

    Add-Type -Path $webServicesDllPath

    Add-Type -Path $HtmlAgilityPackDllPath

    $scriptName = _GetScriptName

    Write-Verbose ("[{0}] [SCRIPT] Beginning execution of   script." -f   $dtmScriptStartTimeUTC.ToLocalTime().ToString($dtmFormatString))

    Write-Verbose ("[{0}] [SCRIPT] Script Name: {1}" -f [datetime]::Now.ToString($dtmFormatString),   $scriptName)

    $exchangeService = New-Object   Microsoft.Exchange.WebServices.Data.exchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013_SP1)

    $exchangeService.Credentials = New-Object System.Net.NetworkCredential([string]$Credential.UserName,[string]$Credential.GetNetworkCredential().Password)

    #$exchangeService.Url =   $Uri.AbsoluteUri

    Write-Verbose ("[{0}] [SCRIPT] Discovering Service   URL..." -f   [datetime]::Now.ToString($dtmFormatString))

    $exchangeService.AutodiscoverUrl($Credential.UserName.ToString())

    Write-Verbose ("[{0}] [SCRIPT] Service URL: {1}" -f [datetime]::Now.ToString($dtmFormatString),   $exchangeService.Url)

    $folder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($exchangeService, [Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox)

    #$folder =   [Microsoft.Exchange.WebServices.Data.Folder]::Bind($exchangeService,   [Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot)

    $folderView = New-Object Microsoft.Exchange.WebServices.Data.FolderView(1)

    $searchFilter = New-Object   Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,$OutlookFolderName)

    $findFolderResults =   $exchangeService.FindFolders($folder.Id,$searchFilter,$folderView)

    Write-Verbose ("[{0}] [SCRIPT] `"{1}`" contains   `"{2}`" unread messages" -f [datetime]::Now.ToString($dtmFormatString),   $findFolderResults.DisplayName, $findFolderResults.UnreadCount)

    # https://msdn.microsoft.com/EN-US/library/office/dn579422(v=exchg.150).aspx

    $filterBodyContainsSubString = New-Object   Microsoft.Exchange.WebServices.Data.SearchFilter+ContainsSubstring([Microsoft.Exchange.WebServices.Data.ItemSchema]::Body, $SearchString, [Microsoft.Exchange.WebServices.Data.ContainmentMode]::Substring, [Microsoft.Exchange.WebServices.Data.ComparisonMode]::IgnoreCase)

    $filterUnread = New-Object   Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::IsRead, $false)

    $colSearchFilter = New-Object System.Collections.ArrayList

    [void]$colSearchFilter.Add($filterBodyContainsSubString)

    [void]$colSearchFilter.Add($filterUnread)

    $searchFilterCollection = New-Object   Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::And,   $colSearchFilter.ToArray())

    $dtHeaders = "Identity", "ErrorDescription", "SourceAnchor"

    $dt = New-Object System.Data.DataTable

    foreach ($header in $dtHeaders)

    {

           [void]$dt.Columns.Add([System.Data.DataColumn]$header.ToString())

    }

    $dt.PrimaryKey = $dt.Columns[2]

    $itemView =  New-Object Microsoft.Exchange.WebServices.Data.ItemView(1)

    $propertySet = New-Object   Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)

    $itemView.PropertySet =   $propertySet

    $itemView.OrderBy.Add([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived, [Microsoft.Exchange.WebServices.Data.SortDirection]::Descending)

    do

    {

        Write-Verbose ("[{0}] [SCRIPT] Searching for items matching criteria..." -f [datetime]::Now.ToString($dtmFormatString))

        $findItemsResults =   $exchangeService.FindItems($findFolderResults.Id, $searchFilterCollection,   $itemView)

        #Write-Host ("Result item   count: `"{0}`"" -f $findItemsResults.Items.Count)

        if ($findItemsResults.Items.Count -gt 0)

        {

            Write-Verbose ("[{0}] [SCRIPT] Loading extended message properties..." -f [datetime]::Now.ToString($dtmFormatString))

              [void]$exchangeService.LoadPropertiesForItems($findItemsResults,   $propertySet)

            foreach ($item in $findItemsResults.Items)

            {

                $htmlDoc = New-Object HtmlAgilityPack.HtmlDocument

                  $htmlDoc.LoadHtml($item.Body.Text)

                $table =   $htmlDoc.DocumentNode.SelectNodes("//table/tbody/tr/td")

                if ($table -eq $null) {continue}

                Write-Verbose ("[{0}] [SCRIPT] Exporting table data from message..." -f [datetime]::Now.ToString($dtmFormatString))

                # data elements are every groups of 3 rows.  get the data that aligns to the correct   header, but skip the first 3 elements since those only contain header names

                for ($outerCount = 3; $outerCount -lt $table.Count; $outerCount += 3)

                {

                    # if sourceAnchor value is   already in the data table, just skip to the next one (dupes are not expected   in the same email message - just taking precaution across multiple messages)

                    #if ($dt.Rows.Contains($table[$outerCount   + 2].innerText)){continue}

                    $newRow = $dt.NewRow()

                    for ($innerCount = $outerCount;   $innerCount -lt ($outerCount + 3); $innerCount++)

                    {

                        switch ($innerCount % 3)

                        {

                            0 {$newRow[0] =   $table[$innerCount].innerText}

                            1 {$newRow[1] =   $table[$innerCount].innerText}

                            2 {$newRow[2] =   $table[$innerCount].innerText}

                        }

                    }

                    if ($newRow[1].ToString().Contains($SearchString) -eq   $true) {$dt.Rows.Add($newRow)}

                }

                if ($Delete)

                {

                    Write-Verbose ("[{0}] [SCRIPT] Deleting message item..." -f [datetime]::Now.ToString($dtmFormatString))

                    $item.Delete([Microsoft.Exchange.WebServices.Data.DeleteMode]::SoftDelete)

                    #$item.Delete([Microsoft.Exchange.WebServices.Data.DeleteMode]::MoveToDeletedItems)

                }

                else

                {

                    Write-Verbose ("[{0}] [SCRIPT] Marking message item as   read..." -f   [datetime]::Now.ToString($dtmFormatString))

                    $item.IsRead = $true

                    $item.Update([Microsoft.Exchange.WebServices.Data.ConflictResolutionMode]::AlwaysOverwrite)

                }

            }

        }

        $itemView.offset +=   $findItemsResults.Items.Count

    }

    while($findItemsResults.MoreAvailable)

    # saving results

    Write-Verbose ("[{0}] [SCRIPT] Number of results found:   `"{1}`"" -f   [datetime]::Now.ToString($dtmFormatString),   $dt.Rows.Count)

    $OutputFileName = "{0}.ImmutableIDs.csv" -f   $dtmScriptStartTimeUTC.ToLocalTime().ToString("yyyy-MM-dd_HH.mm.ss")

    if ($dt.Rows.Count -gt 0)

    {

           Write-Verbose ("[{0}] [SCRIPT] Saving results to: {1}" -f [datetime]::Now.ToString($dtmFormatString),   $OutputFileName)

           $dt   | Export-Csv $OutputFileName   -NoTypeInformation

    }

    $dtmScriptStopTimeUTC = [datetime]::UtcNow

    $elapsedTime = New-TimeSpan -Start $dtmScriptStartTimeUTC   -End $dtmScriptStopTimeUTC

    Write-Verbose ("[{0}] [SCRIPT] Script Complete" -f   $dtmScriptStopTimeUTC.ToLocalTime().ToString($dtmFormatString))

    Write-Verbose ("[{0}] [SCRIPT] Elapsed Time:  {1:N0}.{2:N0}:{3:N0}:{4:N1}  (Days.Hours:Minutes:Seconds)" -f [datetime]::UtcNow.ToString($dtmFormatString),   $elapsedTime.Days, $elapsedTime.Hours, $elapsedTime.Minutes,   $elapsedTime.Seconds)

    #   -----------------------------------------------------------------------------

    #

    # End of Script.

    #

    #   -----------------------------------------------------------------------------



    Friday, September 18, 2015 9:40 PM

All replies