Friday, January 24, 2014

Office 365 - Remove OWA Autocomplete Entries with PowerShell

You may want to look at this article also to clear the Recipient Cache in O365!

Recently, I worked on an email migration from Lotus Notes to Office 365.  There were some email addresses brought over that were non-routable and the client requested we help them clear these "bad" addresses.

I wanted to document this since there isn't much information out there (that I could find, at least)

I've used the script from Troy's Tech blog here as a starting point.  I couldn't get his script to work, line for line, so it's been updated to connect to Exchange 2013, which is what O365 is currently using.  This script will loop through all of your users and clean up Autofill Cache for any address ending in whateverdomainyouwant.com, just add that domain (or search string) in the "$badDomain" variable.

PreReqs:
This script requires EWS API 2.0 and using an Impersonated user.  I have also only tested it using PowerShell 4 on Windows 8.1.
Link to EWS 2.0 API
For the impersonated user, here is the code I used to set it. (I used the admin user for this)
New-ManagementRoleAssignment –Name:impersonationAssignmentName  Role:ApplicationImpersonation –User:(user email address)

Here's the script (make sure to change the $user and $badDomain variables)

###### ===============================================================================
# Author:  Mike Sweany
# Date:    1/23/2014
# Version: 1.0
# Purpose: This script will delete any AutoFill addresses for Office365 Web App 
# if it matches the domain set for "$badDomain"
# PreReqs: Office 365, Powershell 4
# PreReqs: EWS 2.0 Managed API 
########## http://www.microsoft.com/en-us/download/details.aspx?id=35371
# PreReqs: Administrative user configured as Impersonated user
########## New-ManagementRoleAssignment –Name:impersonationAssignmentName  Role:ApplicationImpersonation –User:(user email address)
###### ===============================================================================

###### Specify your administrative user credentials on the line below
$User = "user@domain.onmicrosoft.com"
$badDomain = "@baddomain.com"

###### This will pop-up a dialog and request your password
$psCred = Get-Credential $User
###### Import the Local Microsoft Online PowerShell Module Cmdlets and Connect to O365 Online
Connect-MsolService -Credential $psCred

###### Establish an Remote PowerShell Session to Exchange Online
$msoExchangeURL = "https://ps.outlook.com/powershell/"
$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $msoExchangeURL -Credential $psCred -Authentication Basic -AllowRedirection
Import-PSSession $session
write-host "Connected to O365"

###### Load EWS API dll    
Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"   
 
###### Set Exchange Version    
$ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013    
   
###### Create Exchange Service Object    
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)   

###### Credentials for EWS
$creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())    
$service.Credentials = $creds 
$impersonationUser = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId
$impersonationUser.IdType = [Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress
write-host "Connected to EWS"

###### Create an xmlDoc object to store the auto complete cache.
$xmlDoc = New-Object System.Xml.XmlDocument
 
###### Get a list of mailboxes you want to clear the cache for
$mailboxes = Get-Mailbox  | Select-Object PrimarySMTPAddress
 
foreach($mailbox in $Mailboxes)
{
    try
    {
  write-host $mailbox.PrimarySMTPAddress " processing"
        ###### Connect to users account using impersonation
        $service.AutodiscoverUrl($mailbox.PrimarySMTPAddress,{$true})  
        $impersonationUser.Id = $mailbox.PrimarySMTPAddress
        $service.ImpersonatedUserId = $impersonationUser
         
        ###### Get root folder
        $folder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,[Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root)
         
        ###### Get OWA Autocomplete cache
        $config = [Microsoft.Exchange.WebServices.Data.UserConfiguration]::Bind($service, "OWA.RecipientCache", $folder.ParentFolderId, [Microsoft.Exchange.WebServices.Data.UserConfigurationProperties]::All)
 
        #At this point you could just use the folowing 2 lines to clear all entries
        #$config.Delete()
        #$config.Update()
         
        ###### Convert the byte array and store as xmldoc. There seems to be a special character at the beginning so I don't include it in the xml
        $stringData = [System.Text.Encoding]::UTF8.GetString($config.XmlData).Substring(1)
        $xmlDoc.LoadXml($stringData)
        $nodes = $xmlDoc.SelectNodes("/AutoCompleteCache/entry")
         
        #loop through each entry and remove if necessary
        foreach($node in $nodes)
        {
            if($node.smtpAddr -ne $null -and $node.smtpAddr.Contains($badDomain))
            {
                $node.ParentNode.RemoveChild($node) | Out-Null
                write-host "`tRemoving: $($node.smtpAddr) from $($mailbox.PrimarySMTPAddress)"
            }
        }
         
        ###### Convert the xml back into a byte array
        $data = [System.Text.Encoding]::UTF8.GetBytes([System.Text.Encoding]::UTF8.GetString($config.XmlData).Substring(0,1) + $xmlDoc.OuterXml)
        $config.XmlData = $data
 
        ###### Save the config back to the users mailbox
        $config.Update()
  
  ###### Show progress on the screen
  write-host $mailbox.PrimarySMTPAddress " processed"
    }
    catch
    {
        ###### If the error is 'The specified object was not found in the store.' It means there are no entries in the Autocomplete cache. This error can be ignored
        if(!$error[0].ToString().contains("The specified object was not found in the store."))
        {
            write-host "`tfailed: $($mailbox.PrimarySMTPAddress) Error $($error[0])" -ForegroundColor Red
        }
    }
}

That's it, I hope it helps you.

1 comment:


  1. Excellent Blog! I would like to thanks for the efforts you have made in writing this post.
    I am hoping the same best work from you in the future as well. This is very beneficial to me.
    I’m happy I came across this website. I still learn something new from your posts! It will give
    you a great idea aboutupdate here

    ReplyDelete