Staff Off-Boarding with Office 365


Over time in any environment the day will come around when the inevitable happens and staff leave. This happens due to lots of different reasons but there is often a question as to what to do with user accounts when this happens. This is particularly true in smaller companies with no set process for IT departments off-boarding staff.

In my own environment, I decided to set up a standard script which would work with Active Directory and Office 365 to achieve the following;

  1. Connects to your local DC for active directory functions and Office 365 for mailbox functions
  2. Disable the employee’s user account (verifies the user details before proceeding)
  3. Stamp details on the disabled user account with the IT person who disabled the account for audit purposes
  4. Move the disabled user account to a separate OU
  5. Disable all forms of remote mail access (OWA, ActiveSync, POP, etc)
  6. Set an Out of Office stating that the employee has left the company
  7. Place the mailbox into Legal hold for data retention purposes
  8. Hides the account from the GAL
  9. Emails the former employee’s manager advising that the account has been disabled

The user account is then maintained for a period of 30 days before being deleted and thus returning any assigned Office 365 licenses to the pool etc.

This might help to give you some ideas about setting up your own scripting process for dealing with this issue.

Script Download Link –

Created with: SAPIEN Technologies, Inc., PowerShell Studio 2016 v5.2.128
Created on: 04/11/2016 21:00
Created by: Maurice Daly
Filename: DisableUserOffice365.ps1
This script provides a standard off-boarding method for staff leaving
the company.

The script does the following;
1. Disables the specified user account
2. Updates the user description with the user who disabled the account
and the time/date when the account was disabled
3. Moves the account to the disabled user account OU (needs to exist)
4. Sets an out of office reply stating that the employee has left the company
5. Disables activesync, pop3, imap etc
6. Places mail account into legal hold for 7 years (requires Office 365 E3)
7. Hides the mail account from the GAL
8. Emails the former employee's manager advising that the account has been disabled

Version 1.0
Initial release

Use : This script is provided as it and I accept no responsibility for
any issues arising from its use.

Twitter : @modaly_it
Blog :

Write-Host " **************** PLEASE ENTER ACTIVE DIRECTORY ADMIN CREDENTIALS **************** "
$Credential = Get-Credential -Credential "$env:USERDOMAIN\$env:USERNAME"
$DC = $env:LOGONSERVER.Substring(2)

#Initiate Remote PS Session to local DC
$ADPowerShell = New-PSSession -ComputerName $DC -Authentication Negotiate -Credential $Credential

# Import-Module ActiveDirectory
write-host "Importing Active Directory PowerShell Commandlets"
Invoke-Command -Session $ADPowerShell -scriptblock { import-module ActiveDirectory }
Import-PSSession -Session $ADPowerShell -Module ActiveDirectory -AllowClobber -ErrorAction Stop

# Retrieve AD Details
$ADDetails = Get-ADDomain
$Domain = $ADDetails.DNSRoot

write-host "Importing Office 365 PowerShell Commandlets"
Write-Host -ForegroundColor White -BackgroundColor DarkBlue " **************** PLEASE ENTER OFFICE 365 ADMIN CREDENTIALS **************** "
$Office365Credential = Get-Credential
$Office365PowerShell = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri -Credential $Office365Credential -Authentication Basic -AllowRedirection
Import-PSSession $Office365PowerShell

write-host " **************** Disable Active Directory User Account & Enable Out Of Office **************** "
write-host " "

# Get Variables
$DisabledDate = Get-Date
$LeaveDate = Get-Date -Format "dddd dd MMMM yyyy"
$DisabledBy = Get-ADUser "$env:username" -properties Mail
$DisabledByEmail = $DisabledBy.Mail

# Prompt for AD Username
$Employee = Read-Host "Employee Username"
$EmployeeDetails = Get-ADUser $Employee -properties *
If ($EmployeeDetails.Manager -ne $null)
$Manager = Get-ADUser $EmployeeDetails.Manager -Properties Mail


# Prompt for confirmation
write-host " ******************************** CONFIRM USER DISABLE REQUEST ******************************** "
write-host " "
write-host -ForegroundColor Yellow "Please review the Employee details below to ensure you are disabling the correct user account."
$EmployeeDetails | fl Name, Title, Company, @{ Expression = { $_.mail }; Label = "Email Address" }, @{Expression = { $_.Created }; Label = "Employment Started"}

$choice = " "
while ($choice -notmatch "[y|n]")
$choice = read-host "Do you want to continue? (Y/N)"

# Actions
if ($choice -eq "y")
write-host " ******************************** DISABLING USER ACCOUNT ******************************** "
write-host " "
write-host "Step1. Modifying user description for audit purposes" -ForegroundColor Yellow
Set-ADUser $Employee -Description "Disabled by $($ on $DisabledDate"
write-host "Step2. Disabling $Employee Active Directory Account." -ForegroundColor Yellow
Disable-ADAccount $Employee
write-host "Step3. Moving $Employee to the Disabled User Accounts OU." -ForegroundColor Yellow
write-host " "
Move-ADObject -Identity $EmployeeDetails.DistinguishedName -targetpath "OU=Disabled User Accounts,DC=YOURDOMAIN,DC=SUFFIX"
write-host "Waiting 5 seconds for AD & Exchange OU update to complete"
sleep -Seconds 5
write-host " "
write-host "Refreshing Employee Details for Exchange Modification."
write-host " "
Get-ADUser $Employee -Properties Description | Format-List Name, Enabled, Description
write-host "Step 4. Setting Exchange Out Of Office Auto-Responder." -ForegroundColor Yellow
Set-MailboxAutoReplyConfiguration -Identity $EmployeeDetails.Mail -AutoReplyState enabled -ExternalAudience all -InternalMessage "Please note that I no longer work for $($ as of $LeaveDate." -ExternalMessage "Please note that I no longer work for $($ as of $LeaveDate. Direct all correspondence to info@yourdomain.suffix."
Write-Host "Step 5. Disabling POP,IMAP, OWA and ActiveSync access for $User" -ForegroundColor Yellow
Set-CasMailbox -Identity $EmployeeDetails.mail -OWAEnabled $false -POPEnabled $false -ImapEnabled $false -ActiveSyncEnabled $false
Write-Host "Step 6. Placing mailbox $($ into Ligitgation hold for 7 years" -ForegroundColor Yellow
Set-Mailbox -Identity $EmployeeDetails.mail -LitigationHoldEnabled $true -LitigationHoldDuration 2555
Write-Host "Step 7. Hiding $($ from Global Address lists" -ForegroundColor Yellow
Set-ADUser -identity $Employee -add @{ msExchHideFromAddressLists = "True" }
Set-ADUser -instance $EmployeeDetails -whatif
If ($Manager.Mail -like "*@*")
Write-Host "Step 8. Sending Confirmation E-mail To Employee's Manager." -ForegroundColor Yellow
$msg = new-object Net.Mail.MailMessage
$smtp = new-object Net.Mail.SmtpClient("youroffice365mailserveraddress")
$msg.From = "Support@yourdomain.suffix"
$msg.subject = "IT Notification - Employee Leaver Confirmation"
$msg.body = "This email is confirm that $($EmployeeDetails.Name)'s account has been disabled. An out of office notification advising that $($EmployeeDetails.Name) has left the company has also been set. Note that the account will be deleted after 30 days."
write-host " "
write-host "Employee disable request cancelled" -ForegroundColor Yellow


Enabling Focused Inbox – Office 365

As you might be aware Microsoft is introducing a new feature in Office 365 called Focused mailbox. The new feature is effectively replacing the clutter inbox that we have become familiar with, but also adds new features such as the ability to highlight people in the body of an email with the use of the @ symbol in front of the person.

More details can be found @

Microsoft are currently in the process of rolling this out, if however you get itchy feet and want to enable the feature yourself simply run the PowerShell command below.
Note that I would not recommend this feature for Shared mailboxes, but if you want to go ahead just remove the filter.

Enable Focused Mail for Your Office 365 Tenant

$UserCredential = Get-Credential -Message "Office 365 Administrator Account Login"
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri -Credential $UserCredential -Authentication Basic -AllowRedirection
Import-PSSession $Session
Set-OrganizationConfig -FocusedInboxOn $True

Enable Focused Mail for All Users

$UserCredential = Get-Credential -Message "Office 365 Administrator Account Login"
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri -Credential $UserCredential -Authentication Basic -AllowRedirection
Import-PSSession $Session
Get-Mailbox | Where-Object {$_.RecipientTypeDetails -ne "SharedMailbox"} | Set-FocusedInbox -FocusedInboxOn $True

Update : 5/10/2016

Microsoft have delayed the roll-out of this feature, you can still prepare your mailboxes for the focused feature but your Office 365 tenant will need to be focused enabled by Microsoft.

Jetze Mellema has a blog post on this –

Update : 15/11/2016

I logged onto OWA this evening and finally focused mailbox has been enabled on our tenant 🙂




Office365 – Listing all members of both static and dynamic distrubiton groups

Here is a nice little script that connects to your Office365 environment, reads the contents of all distribution groups both static and dynamic and exports the filtered contents into a CSV file thus allowing you to apply filters etc in Excel.

     Created with:     SAPIEN Technologies, Inc., PowerShell Studio 2015 v4.2.85
     Created on:       12/06/2015 9:47 a.m.
     Created by:       Maurice Daly
     Filename:     GetDistributionGroupMembers.ps1
        List all members of all static and dynamic distribution groups from your
        Office 365 portal and export the contents into a CSV.

$UserCredential = Get-Credential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri -Credential $UserCredential -Authentication Basic -AllowRedirection
Import-PSSession $Session

$DistributionGroups = Get-DistributionGroup
$DynDistributionGroups = Get-DynamicDistributionGroup

$FilePath = "C:\DistributionGroupMembers.csv"

# Read Static Distribution Groups
foreach ($DistributionGroup in $DistributionGroups) {
    Get-DistributionGroupMember $DistributionGroup.PrimarySMTPAddress | Sort-Object name | Select-Object @{ Label = "Distribution Group"; Expression = { $ } }, Name | Export-Csv -Path $FilePath -Delimiter ";" -NoTypeInformation -Append -Force

# Read Dynamic Distribution Groups
foreach ($DynDistributionGroup in $DynDistributionGroups)
    Get-Recipient -RecipientPreviewFilter $DynDistributionGroup.RecipientFilter | Sort-Object name | Select-Object @{ Label = "Distribution Group"; Expression = { $ } }, Name | Export-Csv -Path $FilePath -Delimiter ";" -NoTypeInformation -Append -Force

# Close Remote PS Session
Get-PSSession | Remove-PSSession

Office 365 Online Archive Error – Failed to enable the new cloud archive

A couple of days ago I came across a strange issue whilst updating our archive directory contact details. A couple of our user updates did not reflect in the global address book in our Office 365 hybrid environment. I logged onto our domain controller and the details where correct, so I then logged into Azure Active Directory (as we are using Direction Sync) and found that the details pulled back from Azure were also correct, so what was the issue?.

When I opened the user properties in the Office 365 console I received the following error message:

“Exchange: Failed to enable the new cloud archive xxxxxxxx-xxxx-xxxxx-xxxx-xxxxxxxx of mailbox xxxxxxxx-xxxx-xxxxx-xxxx-xxxxxxxx because a different archive xxxxxxxx-xxxx-xxxxx-xxxx-xxxxxxxx exists. To enable the new archive, first disable the archive on-premises. After the next Dirsync sync cycle, enable the archive on-premises again.
Exchange: An unknown error has occurred. Refer to correlation ID: c0e37858-6d41-423a-b0bd-7d9bec686457″
So the issue was obviously related to a mailbox move that took place whereby the offline archive link was broken. So I used the Azure Active Directory PS (download from and connected again to my Azure environment to check the health properties of my users.
To do this run the following PS commands
Get-MSOLUser | Sort-Object DisplayName | ft DisplayName, ValidationStatus
This will return the full list of Office 365 enabled user accounts and their status. Narrowing this down with the following command will display a list of users with validation errors;
Get-MSOLUser | Where-Object {$_.ValidationStatus -eq "Error"} | Sort-Object DisplayName | ft DisplayName, ValidationStatus
Once you have the list of users with errors it is time to get their online archive information to compare against the details from Exchange. First lets retrieve the Archive GUID by running the following script in an Office365 PowerShell environment;
Get-Mailbox "user identity with error status" | ft Name, ArchiveGuid
Now switching over to our Exchange PS console I ran the following script to check what archive details matched;
Get-RemoteMailbox -Identity "user identity with error status" | fl DisplayName, ArchiveGuid
Comparing the two ArchiveGuid values presented the cause of the issue. During a routine mailbox move the ArchiveGuid value had not been updated to the current version.
I resolved this by running the following process;
1. Update the remote mailbox properties with the ArchiveGuid value obtained from Azure AD;
– Set-RemoteMailbox -Identity  “user identity with error status” –ArchiveGuid “Guid value from Azure AD”
2. Run a full directory sync in DirSync
3. Run Start-OnlineCoexistenceSync PS command:
On your DirSync server –
PS:\Import-Module DirSync
Now wait a few minutes, go back into your Office 365 portal and the issue should be resolved 🙂

Scripting Your Office 2010 Removal – Office 365 Click To Run Migration

Like many small and medium sized companies the temptation to move to the cloud for services such as Exchange, Lync & SharePoint aka Office 365, makes both financial and resourcing sense. Given that the company I work for had decided to make the move I was tasked with the upgrade process of Office 2010 to the Office 2013/365 click to run version.

My initial though was to use a PowerShell script calling WMI to find the MSIExec, such as;

Get-WmiObject -Class Win32_product | Where-Object { $ -like “Microsoft Office Professional Plus 2010” }

I would then use the returned Identifying number combined with MSIExec switches to uninstall the product, however Office 2010 creates multiple uninstall command lines for each of its individual products.

With that option ruled out I was now on to using the Office setup.exe installer combined with customised XML to remove the product, which in theory is the easiest thing to do as the product will in 99% of cases remove Office without fault. In this scenario you use the following command (assuming you are using Pro Plus) : setup.exe /uninstall  ProPlus /config “path-to-xml”

My XML consists of the following:

<Configuration Product=”ProPlus”>
<Display Level=”none” CompletionNotice=”no” SuppressModal=”yes” AcceptEula=”yes” />
<Setting Id=”SETUP_REBOOT” Value=”Never” />

The only issue I had with this method of uninstalling the product was the 1% rule where Office would simply refuse to uninstall. Microsoft provide a VBScript for such instances ( and again I thought this would be a simple process of calling the VBScript from within a PowerShell script deployed by SCCM.

I added lines to run CScript and call the OffScrub10.vbs script but this resulted in the following error in the output log – Insufficient registry access permissions – exiting. So after many attempts I found that the most simple way to allow the script to run without compromising the security of your estate by lowering UAC settings is to copy the CScript.exe, rename it to CScript-Admin.exe and set the exe to run with administrative rights via the registry:

REG ADD “HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers” /v $CSCriptLocation /t REG_SZ /d “RUNASADMIN” /f

After putting all of these steps together and also allowing for the removal of the Lync 2010 client, I created the following PowerShell script to remove your Office 2010 installation and also remove all email profiles from the local machine to avoid any PRF deployment issues when moving from your on-premise Exchange to Office 365. Simply put this script into your “Run Another Program First” section of your Office click to run installer in SCCM and automate the full removal and install of your new Office software.

Update – 20/2/2015 – Added 32/64 bit detection. Note you will need to copy the CScript.exe from both platforms and rename accordingly.

Script Download Link –

Update – Microsoft appear to have pulled the OffScrub10.vbs script from their site. I have uploaded a copy to the Microsoft Script Library page in the above link.

  Created with:  SAPIEN Technologies, Inc., PowerShell Studio 2014 v4.1.74
  Created on:    16/02/2015 12:30
  Created by:    Maurice Daly
  Office 2010 & Lync 2010 Clean Up Script. Use at your own risk.

# Office 2010 Uninstall Files

# Check if Lync 2010 is installed
$Lync2010Installed = Get-WmiObject -Class Win32_product | Where-Object { $ -like "Microsoft Lync 2010*" }

# Uninstall Lync 2010 if detected
If ($Lync2010Installed -ne $null)
 $LyncArgs = "/X" + $Lync2010Installed.IdentifyingNumber + " /passive"
 Start-Process msiexec.exe -ArgumentList $LyncArgs -Wait

# Copy Office 2010 tools to Windows Temp
Get-ChildItem -Path $Office2010Tools | Copy-Item -Destination "C:\Windows\Temp"

# CScript location variable
If ((Test-Path -Path "C:\Program Files (x86)") -eq $true)
 $CSCriptLocation = "C:\Windows\Temp\cscript-admin-64.exe"
 $OSVersion = "64Bit"
 $CSCriptLocation = "C:\Windows\Temp\cscript-admin-32.exe"
 $OSVersion = "32Bit"

# Elevate CScript-Admin executionable file for Office 2010 scrub
REG ADD "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers" /v $CSCriptLocation /t REG_SZ /d "RUNASADMIN" /f

# Uninstall Office 2010 using MS OffScrub10.vbs
$OfficeScrubArgs = " ProPlus /Bypass1 /NoCancel"
If ($OSVersion -eq "64Bit")
 $OfficeScrubCommand = "cmd /C C:\Windows\Temp\cscript-admin-64.exe \\YOURSERVER\YOURSHARE\YOURDIRECTORY\Office2010-Uninstall\OffScrub10.vbs $OfficeScrubArgs"
 $OfficeScrubCommand = "cmd /C C:\Windows\Temp\cscript-admin-32.exe \\YOURSERVER\YOURSHARE\YOURDIRECTORY\OffScrub10.vbs $OfficeScrubArgs"

Invoke-Expression $OfficeScrubCommand

# Outlook Registry Path
$OutlookReg = "\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles"

# Clean Outlook Profiles
New-PSDrive -PSProvider Registry -Name HKU -Root HKEY_USERS
$UserSIDS = Get-ChildItem -Path HKU:\ | Split-Path -Leaf
foreach ($SID in $UserSIDS)
 $Profile = "HKU:\" + $SID + $OutlookReg
 $ProfileExists = Test-Path -Path $Profile
 if ($ProfileExists -eq $true)
  Remove-Item -Path $Profile -Recurse -Force
Remove-PSDrive -Name HKU

<# Un-comment this section to also clear OST & PST locations
$UserDirectories = Get-ChildItem -Path "C:\Users" | Where-Object { $_.PSIsContainer } | Select-Object FullName
$OutlookLocalAppData = "\AppData\Local\Microsoft\Outlook"
$OutlookLocalAppData = "\AppData\Roaming\Microsoft\Outlook"
foreach ($User in $UserDirectories.FullName)
 if ((Test-Path -Path (Join-Path $User $OutlookLocalAppData)) -eq $true)
  Remove-Item -Path (Join-Path $User $OutlookLocalAppData) -Recurse -Force
 if ((Test-Path -Path (Join-Path $User $OutlookRemoteAppData)) -eq $true)
  Remove-Item -Path (Join-Path $User $OutlookRemoteAppData) -Recurse -Force