Friday, January 24, 2014

Office 365 - Remove OWA RecipientCache Entries with PowerShell

You may want to use this script to clear the AutoComplete Cache also!

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)

This script will loop through all of your users and clean up specific domain addresses from the new RecipientCache in Office 365 or Exchange 2013, it was created based off of a lot of help and code from Glen Scales blog, thanks for a quality site Glen!

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 RecipientCache 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 

###### Set the impersonationUser
$impersonationUser = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId
$impersonationUser.IdType = [Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress
Write-Host "Connected to EWS"
 
###### Get a list of mailboxes you want to clear the cache for
$mailboxes = Get-Mailbox  | Select-Object PrimarySMTPAddress

###### Loop through each mailbox
foreach($mailbox in $Mailboxes)
{
    try
    {
  ###### Process the RecipientCache for each user
  Write-Host $mailbox.PrimarySMTPAddress " recipient cache starting"
        #Connect to users account using impersonation
        $service.AutodiscoverUrl($mailbox.PrimarySMTPAddress,{$true})  
        $impersonationUser.Id = $mailbox.PrimarySMTPAddress
        $service.ImpersonatedUserId = $impersonationUser
        
  ###### isolate the RecipientCache for this mailbox
        $folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::RecipientCache,$mailbox.PrimarySMTPAddress) 
  $RecipientCache = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)
  
  $psPropset= new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)  
  
  ###### Define ItemView to retrieve just 1000 Items    
  $ivItemView =  New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)    
  $fiItems = $null    
  do{   
   $fiItems = $service.FindItems($RecipientCache.Id,$ivItemView)   
   [Void]$service.LoadPropertiesForItems($fiItems,$psPropset) 
   ###### This will look at each entry in their recipient cache
   foreach($Item in $fiItems.Items){    
    if($Item -is [Microsoft.Exchange.WebServices.Data.Contact]){
     if($Item.DisplayName.Contains($badDomain)){
      Write-Host $Item.DisplayName " deleting.."
      ######  Delete the "badDomain" address
      $Item.Delete([Microsoft.Exchange.WebServices.Data.DeleteMode]::SoftDelete)
     }
    }
   }   
   $ivItemView.Offset += $fiItems.Items.Count 
  }while($fiItems.MoreAvailable -eq $true)
  
  ###### show progress on the screen
  Write-Host $mailbox.PrimarySMTPAddress " mailbox recipient cache processed"
    }
    catch
    {
        ###### If the error is 'Error Exception calling "LoadPropertiesForItems"' It means there are no entries in the Recipient 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.

2 comments:

  1. Hello,
    This is very old, but I wanted to note that there is an issue. In this line:
    if($Item.DisplayName.Contains($badDomain)){

    There is no DisplayName attribute for a contact. I changed it to this and it worked:
    if(($Item.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1]).tostring().Contains($badDomain)){

    You would then want to update the next line as well:
    Write-Host $Item.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1] " deleting.."

    ReplyDelete
  2. Play casino online in South Africa - JtmHub
    For instance, you can play casino games 경산 출장안마 at the 사천 출장샵 online sports betting site. 구미 출장마사지 You can also use 군산 출장샵 a sportsbook, e-sportsbook or a poker 대전광역 출장마사지 room at your local casino.

    ReplyDelete