Skip to content

Updating the flag status of an Exchange email through Powershell EWS

I was recently involved in a Exchange Migration that also involved an Enterprise Vault migration. The company in question currently had about 2000 archived that needed to be moved into the Exchange on-prem mailboxes before migrating them to Exchange Online.

For that migration, we used Archive Shuttle from Quadrotech which does an amazing job migrating the emails back into the Exchange mailbox. But we ran into one issue that was unacceptable for the customer:

If a mail was archived when it was flagged (a to do flag was added to the email) and the mail was unflagged while it was in the Enterprise Vault archive, it would come back as flagged. This organization heavily relied on flagged items and some test users would end up with 300 unnecessary flagged items. The amount of extra flagged items was problematic and we had to find a solution for this.

ArchiveShuttle has no workaround this by default, so I had to resort to Exchange Web Services Powershell to do the job.

The workflow is pretty simple:

  1. Export all the current flagged items
  2. Start the archive migration
  3. Wait for the migration to finish
  4. Remove all unnecessary flagged items

Getting all flagged items

Getting all flagged items from a mailbox is relatively simple. First I tried getting all the folders in an Exchange mailbox like this:

$RootFolder= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root,$ImpersonatedMailboxName)
$fvFolderView = New-Object Microsoft.Exchange.WebServices.Data.FolderView(31000)
$fvFolderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep;
$folders = $service.FindFolders($RootFolder,$fvFolderView)

But if you then loop over all the folders and filter for flagged items, you will get all the flagged items twice. That’s because there is a folder in every mailbox called ‘To-Do Search’. So we can simply loop over every folder until we find the To-Do Search folder and then get all the flagged items from that one folder. We do this with the following code:

if($folder.DisplayName -eq "To-Do Search" -or $folder.DisplayName -eq "Zoeken naar taken"){
	Write-Log "[INFO] - Found To Do folder"

    try{
        $view = New-Object Microsoft.Exchange.WebServices.Data.ItemView 300
        $filter = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+Exists(new-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x1090, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer))
        $items = $folder.FindItems($filter, $view)
        Write-Log "[INFO] - Got all items"
    }
    catch{
        Write-Log "[ERROR] - Getting all items from folder $($folder.DisplayName)"
        Write-Log "$($_.Exception.Message)"
    }

    #Check all items to see which item is flagged
    Foreach($item in $items){
        if($item.flag.FlagStatus -eq "Flagged"){
            #Found a flagged item, add it to our collection
            $flagged += $item
        }
    }
    Break
}

Now that we have all the flagged items we can easily export them to a CSV. Check out the full script on Github. This also does some other (smaller) tasks:

  • Get some basic info of the users
  • Export the flags when the user has an EV archive

Cleaning up the flags

After the migration we will have to compare the current flagged items vs the flagged items we previously exported. If an item is flagged after the migration, but wasn’t flagged before the migration (and wasn’t created after we exported the flags), it means this flag isn’t current and we will have to unflag it.

The code below does exactly that

Write-Log "[INFO] - Starting Function Invoke-FlagComparision"

$CSVCreated = Get-Date $CSVCreated -Hour 0 -Minute 0 -Second 0

Write-Log "[INFO] - CSV Datetime is $CSVCreated"

Write-Log "[INFO] - Starting foreach to enumerate over all current flagged items"

foreach($flag in $currentFlags){
    Write-Log "[INFO] - Checking current flagged item $($flag.Subject) received on $($flag.DateTimeReceived) with InternetMessageID $($flag.InternetMessageId)"
    if($flag.Flag.StartDate -ge (Get-Date $CSVCreated -Format "yyyy/MM/dd HH:mm:ss")){
        Write-Log "[INFO] - Flag was created - $($flag.Flag.StartDate) - after CSV file was generated - $CSVCreated - so it is current"
        $flagCurrent = $true
    }
    else{
        Write-Log "[INFO] - Flag wasn't been created - $($flag.Flag.StartDate) - after CSV file was generated - $CSVCreated"
        $flagCurrent = $false
        foreach($previousFlagged in $previousFlags){
            Write-Log "[INFO] - Comparing item with previous flagged item $($previousFlagged.Subject) received on $($previousFlagged.DateTimeReceived) with InternetMessageID $($previousFlagged.InternetMessageId)"
            if($flag.InternetMessageId -eq $previousFlagged.InternetMessageId){
                Write-Log "[INFO] - Match found, flag is current"
                $flagCurrent = $true
                Break
            }
        }
    }
	if($flagCurrent -eq $false){
        Write-Log "[INFO] - No match found, flag is not current. Removing flag"
        Set-ItemCompleted -MailItem $flag
    }
    Write-Log "[INFO] ------- Foreach looped ------------"
}

The first for-each loop will loop over every flag that is currently in the mailbox. If that flag was created after we exported the flags, we don’t want to remove it. If it wasn’t, we will loop over every flag we previously exported and compare it with the current flag by it’s InternetMessageID. If there is a match, we won’t remove the flag. It there isn’t a match, the flag isn’t current and should be removed.

I choose to not completely remove the flags, but to complete the flags. If something goes wrong, we have a backup. Completing the flag is pretty easy:

$MailItem.flag.FlagStatus = "Complete"
        $MailItem.Update([Microsoft.Exchange.WebServices.Data.ConflictResolutionMode]::AutoResolve);

All actions are logged in a text file off course if anything should go wrong.

The full ‘cleanup’ script is also available through GitHub.

Feel free to use this script in product or contact me if you want to customize it.
Happy scripting!

9 thoughts on “Updating the flag status of an Exchange email through Powershell EWS Leave a comment

  1. Thanks for using Archive Shuttle. I am the product owner and your blog was provided to me by the engineer working with you from Quadrotech. The engineer logged an enhancement request related to this, so thank you for the documentation on how you accomplished it as I am sure it will lower our level of effort should we implement it. I noticed your script ran per mailbox. Was this an issue with isolated users, or the entire user base? Did you (or did you consider) implementing this script as a stage 2 workflow step to automate execution of it? Do you have any other feedback related to Archive Shuttle you could provide me? Thanks again for the posting and your patronage and hopefully we find a chance to continue working together.

    Like

    • Hi J

      I am very happy with Archive Shuttle, great product and did the job in for this client.
      I try this script per mailbox because in the first step, I exported all users who had flags. The second script only checks the users who had flags in the first step.

      I didn’t consider adding this into the workflow as these script output .csv files to store data.
      The first script (that exports the flags) needs to be run before the migration. That script stores all the current flags of the users in a CSV file per user.
      Those files are then used after the migration. If it’s possible to store data in Archive Shuttle, that might be an option.

      Like

  2. Hi,
    Just saw the post while searching for a requirement in one of the pain areas where we’re desperately needing a PS script to pull out reports Daily, Weekly & / or Monthly on emails that reaches a shared mailbox, email actioned by <> which I plan to achieve by flag marking it and mark it “complete” so that the date & timestamp of the completion could be obtained.(Most welcome if any other easier ideas). It would really help save a lot of time in manually pulling these. Thanks!

    Like

    • Not sure what you intention is? You want to flag an email in order to keep track of your progress by extracted emails? Why not just write a timestamp to an external source?

      Like

  3. Hello,

    i want to “unflag” all flagged emails in a public Folder with hundreds of subfolders on a 2016 Exchange Server.
    Not more not less.
    Could you, or someone else, help me how to do this?

    Like

      • i had to archive a public folder. In this public folder and the subfolders are many flagged emails.
        The archive Programm (Mailstore) doesn’t allow to delete flagged emails after archiving.
        So i have to remove the flag. The archiving log file says that there are more than 4000 flagged mails in the folders.
        To delete by hand much too much)
        so the script should only remove all flags regardless of anything

        Like

Leave a comment