591 lines
23 KiB
PowerShell
Raw Normal View History

<#
=============================================================================================
Name: Automate Microsoft 365 User Offboarding with PowerShell
Description: This script can perform 14 Microsoft 365 offboarding activities.
website: blog.Admindroid.com
Script by: AdminDroid Team
For detailed Script execution: https://blog.admindroid.com/automate-microsoft-365-user-offboarding-with-powershell
==============================================================================================
param(
[string]$TenantId,
[string]$ClientId,
[string]$CertificateThumbprint,
[string]$CSVFilePath,
[String]$UPNs
)
Function ConnectModules
{
$MsGraphBetaModule = Get-Module Microsoft.Graph.Beta -ListAvailable
if($MsGraphBetaModule -eq $null)
{
Write-host "Important: Microsoft Graph Beta module is unavailable. It is mandatory to have this module installed in the system to run the script successfully."
$confirm = Read-Host Are you sure you want to install Microsoft Graph Beta module? [Y] Yes [N] No
if($confirm -match "[yY]")
{
Write-host "Installing Microsoft Graph Beta module..."
Install-Module Microsoft.Graph.Beta -Scope CurrentUser -AllowClobber
Write-host "Microsoft Graph Beta module is installed in the machine successfully" -ForegroundColor Magenta
}
else
{
Write-host "Exiting. `nNote: Microsoft Graph Beta module must be available in your system to run the script" -ForegroundColor Red
Exit
}
}
$ExchangeOnlineModule = Get-Module ExchangeOnlineManagement -ListAvailable
if($ExchangeOnlineModule -eq $null)
{
Write-host "Important: Exchange Online module is unavailable. It is mandatory to have this module installed in the system to run the script successfully."
$confirm = Read-Host Are you sure you want to install Exchange Online module? [Y] Yes [N] No
if($confirm -match "[yY]")
{
Write-host "Installing Exchange Online module..."
Install-Module -Name ExchangeOnlineManagement -Scope CurrentUser
Write-host "Exchange Online Module is installed in the machine successfully" -ForegroundColor Magenta
}
else
{
Write-host "Exiting. `nNote: Exchange Online module must be available in your system to run the script"
Exit
}
}
Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null
Disconnect-ExchangeOnline -Confirm:$false
Write-Host "Connecting modules(Microsoft Graph and Exchange Online module)...`n"
try{
if($TenantId -ne "" -and $ClientId -ne "" -and $CertificateThumbprint -ne "")
{
Connect-MgGraph -TenantId $TenantId -ClientId $ClientId -CertificateThumbprint $CertificateThumbprint -ErrorAction SilentlyContinue -ErrorVariable ConnectionError|Out-Null
if($ConnectionError -ne $null)
{
Write-Host $ConnectionError -Foregroundcolor Red
Exit
}
$Scopes = (Get-MgContext).Scopes
$ApplicationPermissions=@("Directory.ReadWrite.All","AppRoleAssignment.ReadWrite.All","User.EnableDisableAccount.All","RoleManagement.ReadWrite.Directory")
foreach($Permission in $ApplicationPermissions)
{
if($Scopes -notcontains $Permission)
{
Write-Host "Note: Your application required the following graph application permissions: Directory.ReadWrite.All,AppRoleAssignment.ReadWrite.All,User.EnableDisableAccount.All,RoleManagement.ReadWrite.Directory" -ForegroundColor Yellow
Exit
}
}
Connect-ExchangeOnline -AppId $ClientId -CertificateThumbprint $CertificateThumbprint -Organization (Get-MgBetaDomain | Where-Object {$_.isInitial}).Id -ShowBanner:$false
}
else
{
Connect-MgGraph -ErrorAction SilentlyContinue -Errorvariable ConnectionError |Out-Null
if($ConnectionError -ne $null)
{
Write-Host $ConnectionError -Foregroundcolor Red
Exit
}
Connect-ExchangeOnline -UserPrincipalName (Get-MgContext).Account -ShowBanner:$false
}
}
catch
{
Write-Host $_.Exception.message -ForegroundColor Red
Exit
}
Write-Host "Microsoft Graph Beta PowerShell module is connected successfully" -ForegroundColor Cyan
Write-Host "Exchange Online module is connected successfully" -ForegroundColor Cyan
}
Function DisableUser
{
try{
Update-MgBetaUser -UserId $UPN -AccountEnabled:$false
$Script:DisableUserAction = "Success"
}
catch
{
$Script:DisableUserAction = "Failed"
$ErrorLog = "$($UPN) - Disable User Action - "+$Error[0].Exception.Message
$ErrorLog>>$ErrorsLogFile
}
}
Function ResetPasswordToRandom
{
$Password = -join ((48..57) + (65..90) + (97..122) | ForEach-Object { [char]$_ } | Get-Random -Count 8)
$log = "$UPN - $Password"
$Pwd = ConvertTo-SecureString $Password -AsPlainText Force
try{
$Passwordprofile = @{
forceChangePasswordNextSignIn = $true
password = $Pwd
}
Update-MgBetaUser -UserId $UPN -PasswordProfile $Passwordprofile
$log>>$PasswordLogFile
$Script:ResetPasswordToRandomAction = "Success"
}
catch
{
$Script:ResetPasswordToRandomAction ="Failed"
$ErrorLog = "$($UPN) - Reset Password To Random Action - "+$Error[0].Exception.Message
$ErrorLog>>$ErrorsLogFile
}
}
Function ResetOfficeName
{
try{
Update-MgBetaUser -UserId $UPN -OfficeLocation "EXD"
$Script:ResetOfficeNameAction = "Success"
}
catch
{
$Script:ResetOfficeNameAction = "Failed"
$ErrorLog = "$($UPN) - Reset Office Name Action - "+$Error[0].Exception.Message
$ErrorLog>>$ErrorsLogFile
}
}
Function RemoveMobileNumber
{
try{
Update-MgBetaUser -UserId $UPN -MobilePhone ' '
$Script:RemoveMobileNumberAction = "Success"
}
catch
{
$Script:RemoveMobileNumberAction = "Failed"
$ErrorLog = "$($UPN) - Remove Mobile Number Action - "+$Error[0].Exception.Message
$ErrorLog>>$ErrorsLogFile
}
}
Function RemoveGroupMemberships
{
#Remove memberships from group
$groupMemberships = $Memberships|?{$_.AdditionalProperties.'@odata.type' -eq '#microsoft.graph.group'}
foreach($Membership in $groupMemberships)
{
try{
Remove-MgBetaGroupMemberByRef -GroupId $Membership.Id -DirectoryObjectId $UserId -ErrorAction SilentlyContinue -ErrorVariable MemberRemovalErr
if($MemberRemovalErr)
{
Remove-DistributionGroupMember -Identity $Membership.Id -Member $UserId -BypassSecurityGroupManagerCheck -Confirm:$false
}
}
catch
{
$ErrorLog = "$($UPN) - GroupId($($Membership.Id)) - Remove Group Memberships Action - "+$Error[0].Exception.Message
$ErrorLog>>$ErrorsLogFile
}
}
#Remove ownerships from group
$UserOwnerships = Get-MgBetaUserOwnedObject -UserId $UPN|?{$_.AdditionalProperties.'@odata.type' -eq '#microsoft.graph.group'}
foreach($UserOwnership in $UserOwnerships)
{
try{
Remove-MgBetaGroupOwnerByRef -GroupId $UserOwnership.Id -DirectoryObjectId $UserId -ErrorAction SilentlyContinue -ErrorVariable OwnerRemovalErr
if($OwnerRemovalErr)
{
$ErrorLog = "$($UPN) - GroupId($($UserOwnership.Id)) - Remove Group Memberships Action - "+$OwnerRemovalErr.Exception.Message
$ErrorLog>>$ErrorsLogFile
}
}
catch
{
$ErrorLog = "$($UPN) - GroupId($($UserOwnership.Id)) - Remove Group Memberships Action - "+$Error[0].Exception.Message
$ErrorLog>>$ErrorsLogFile
}
}
$DistributionGroupOwnerships = Get-DistributionGroup | where {$_.ManagedBy -contains "$UserId"}
foreach($DistributionGroupOwnership in $DistributionGroupOwnerships)
{
Set-DistributionGroup -Identity $DistributionGroupOwnership.Identity -BypassSecurityGroupManagerCheck -ManagedBy @{Remove=$UPN} -ErrorAction SilentlyContinue -ErrorVariable OwnerRemovalErr
if($OwnerRemovalErr)
{
$ErrorLog = "$($UPN) - GroupId($($DistributionGroupOwnership.ExternalDirectoryObjectId)) - Remove Group Memberships Action - "+$OwnerRemovalErr.Exception.Message
$ErrorLog>>$ErrorsLogFile
}
}
if($ErrorLog -eq $null)
{
$Script:RemoveGroupMembershipsAction = "Success"
}
elseif($groupMemberships -eq $null -and $UserOwnerships -eq $null -and $DistributionGroupOwnerships -eq $null)
{
$Script:RemoveGroupMembershipsAction = "No group memberships"
}
else
{
$Script:RemoveGroupMembershipsAction = "Failed"
}
}
Function RemoveAdminRoles
{
$AdminRoles = $Memberships|?{$_.AdditionalProperties.'@odata.type' -eq '#microsoft.graph.directoryRole'}
if($AdminRoles -eq $null)
{
$Script:RemoveAdminRolesAction = "No admin roles"
}
else
{
foreach($AdminRole in $AdminRoles)
{
try{
Remove-MgBetaDirectoryRoleMemberByRef -DirectoryObjectId $UserId -DirectoryRoleId $AdminRole.Id
}
catch
{
$ErrorLog = "$($UPN) - Role Id($($Role.DisplayName)) Remove Admin Roles Action - "+$Error[0].Exception.Message
$ErrorLog>>$ErrorsLogFile
}
}
if($ErrorLog -eq $null)
{
$Script:RemoveAdminRolesAction = "Success"
}
else
{
$Script:RemoveAdminRolesAction = "Failed"
}
}
}
Function RemoveAppRoleAssignments
{
$AppRoleAssignments = Get-MgBetaUserAppRoleAssignment -UserId $UPN
if($AppRoleAssignments -ne $null)
{
$AppRoleAssignments | ForEach-Object {
try{
Remove-MgBetaUserAppRoleAssignment -AppRoleAssignmentID $_.Id -UserId $UPN
}
catch
{
$ErrorLog = "$($UPN) - Remove App Role Assignments Action - "+$Error[0].Exception.Message
$ErrorLog>>$ErrorsLogFile
}
}
if($ErrorLog -eq $null)
{
$Script:RemoveAppRoleAssignmentsAction = "Success"
}
else
{
$Script:RemoveAppRoleAssignmentsAction = "Failed"
}
}
else
{
$Script:RemoveAppRoleAssignmentsAction = "No app role assignments"
}
}
Function HideFromAddressList
{
if($MailBoxAvailability -eq 'No')
{
$Script:HideFromAddressListAction = "No Exchange license assigned to user"
return
}
try{
Set-Mailbox -Identity $UPN -HiddenFromAddressListsEnabled $true
$Script:HideFromAddressListAction = "Success"
}
catch
{
$Script:HideFromAddressListAction = "Failed"
$ErrorLog = "$($UPN) - Hide From Address List Action - "+$Error[0].Exception.Message
$ErrorLog>>$ErrorsLogFile
}
}
Function RemoveEmailAlias
{
if($MailBoxAvailability -eq 'No')
{
$Script:RemoveEmailAliasAction = "No Exchange license assigned to user"
return
}
try{
$EmailAliases=Get-Mailbox $UPN| select -ExpandProperty emailaddresses| ?{$_.StartsWith("smtp")}
if($EmailAliases -eq $null)
{
$Script:RemoveEmailAliasAction = "No alias"
}
else
{
Set-Mailbox $UPN -EmailAddresses @{Remove=$EmailAliases} -WarningAction SilentlyContinue
$Script:RemoveEmailAliasAction = "Success"
}
}
catch
{
$Script:RemoveEmailAliasAction = "Failed"
$ErrorLog = "$($UPN) - Remove Email Alias Action - "+$Error[0].Exception.Message
$ErrorLog>>$ErrorsLogFile
}
}
Function WipingMobileDevice
{
if($MailBoxAvailability -eq 'No')
{
$MobileDeviceAction = "No Exchange license assigned to user"
return
}
try{
$MobileDevice = Get-MobileDevice -Mailbox $UPN
$MobileDevice| Clear-MobileDevice
$Script:MobileDeviceAction = "Success"
}
catch
{
$Script:MobileDeviceAction = "Failed"
$ErrorLog = "$($UPN) - Wiping Mobile Device Action - "+$Error[0].Exception.Message
$ErrorLog>>$ErrorsLogFile
}
}
Function DeleteInboxRule
{
if($MailBoxAvailability -eq 'No')
{
$Script:DeleteInboxRuleAction = "No Exchange license assigned to user"
return
}
try{
$MailboxRule = Get-InboxRule -Mailbox $UPN
$MailboxRule| Remove-InboxRule -Confirm:$False
$Script:DeleteInboxRuleAction = "Success"
}
catch
{
$Script:DeleteInboxRuleAction = "No inbox rule"
}
}
Function ConvertToSharedMailbox
{
if($MailBoxAvailability -eq 'No')
{
$Script:ConvertToSharedMailboxAction = "No Exchange license assigned to user"
return
}
try{
Set-Mailbox -Identity $UPN -Type Shared -WarningAction SilentlyContinue
$Script:ConvertToSharedMailboxAction = "Success"
}
catch
{
$Script:ConvertToSharedMailboxAction = "Failed"
$ErrorLog = "$($UPN) - Convert To Shared Mailbox Action - "+$Error[0].Exception.Message
$ErrorLog>>$ErrorsLogFile
}
}
Function RemoveLicense
{
$Licenses = Get-MgBetaUserLicenseDetail -UserId $UPN
if($Licenses -ne $null)
{
Set-MgBetaUserLicense -UserId $UPN -RemoveLicenses @($Licenses.SkuId) -AddLicenses @() -ErrorAction SilentlyContinue -ErrorVariable LicenseError | Out-Null
if($LicenseError)
{
$Script:RemoveLicenseAction = "Failed"
$ErrorLog = "$($UPN) - Remove License Action - "+$LicenseError.Exception.Message
$ErrorLog>>$ErrorsLogFile
}
else
{
$Script:RemoveLicenseAction = "Removed licenses - $($Licenses.SkuPartNumber -join ',')"
}
}
else
{
$Script:RemoveLicenseAction = "No license"
}
}
Function SignOutFromAllSessions
{
Revoke-MgBetaUserSignInSession -UserId $UPN | Out-Null
$Script:SignOutFromAllSessionsAction = "Success"
}
Function Disconnect_Modules
{
Disconnect-MgGraph -ErrorAction SilentlyContinue| Out-Null
Disconnect-ExchangeOnline -Confirm:$false
Exit
}
Function main
{
ConnectModules
#Importing CSV file
if($CSVFilePath -ne "")
{
$CSVFilePath = $CSVFilePath.Trim()
try{
$UPNCSVFile = Import-Csv -Path $CSVFilePath -Header UserPrincipalName
[array]$UPNs = $UPNCSVFile.UserPrincipalName
}
catch
{
Write-Host $_.Exception.Message -ForegroundColor Red
Exit
}
}
elseif($UPNs -ne "")
{
[array]$UPNs = $UPNs.Split(',')
}
else
{
$UPNs = Read-Host `nEnter the UserPrincipalName of the user you want to offboard
if($UPNs -ne "")
{
[array]$UPNs = $UPNs -split ','
}
else
{
Write-Host You must provide UPN of the user to offboard. -ForegroundColor Red
Disconnect_Modules
}
}
$Location = Get-Location
$ExportCSV = "$Location\M365UserOffBoarding_StatusFile_$((Get-Date -format yyyy-MMM-dd-ddd` hh-mm-ss` tt).ToString()).csv"
$PasswordLogFile = "$Location\PasswordLogFile_$((Get-Date -format yyyy-MMM-dd-ddd` hh-mm-ss` tt).ToString()).txt"
$InvalidUserLogFile = "$Location\InvalidUsersLogFile$((Get-Date -format yyyy-MMM-dd-ddd` hh-mm-ss` tt).ToString()).txt"
$ErrorsLogFile = "$Location\ErrorsLogFile$((Get-Date -format yyyy-MMM-dd-ddd` hh-mm-ss` tt).ToString()).txt"
$AvailabiltyOfInvalidUser = $false
Write-Host "`nWe can perform below operations.`n" -ForegroundColor Cyan
Write-Host " 1. Disable user" -ForegroundColor Yellow
Write-Host " 2. Reset password to random" -ForegroundColor Yellow
Write-Host " 3. Reset Office name" -ForegroundColor Yellow
Write-Host " 4. Remove Mobile number" -ForegroundColor Yellow
Write-Host " 5. Remove group memberships" -ForegroundColor Yellow
Write-Host " 6. Remove admin roles" -ForegroundColor Yellow
Write-Host " 7. Remove app role assignments" -ForegroundColor Yellow
Write-Host " 8. Hide from address list" -ForegroundColor Yellow
Write-Host " 9. Remove email alias" -ForegroundColor Yellow
Write-Host " 10. Wiping mobile device" -ForegroundColor Yellow
Write-Host " 11. Delete inbox rule" -ForegroundColor Yellow
Write-Host " 12. Convert to shared mailbox" -ForegroundColor Yellow
Write-Host " 13. Remove license" -ForegroundColor Yellow
Write-Host " 14. Sign-out from all sessions" -ForegroundColor Yellow
Write-Host " 15. All the above operations" -ForegroundColor Yellow
$Actions=Read-Host "`nPlease choose the action to continue"
if($Actions -eq "")
{
Write-Host "`nPlease choose the action from the above." -ForegroundColor Red
Exit
}
$Actions = $Actions.Trim()
$Actions = $Actions.Split(',')
$CheckActions = Compare-Object -Referenceobject $Actions -DifferenceObject @(1..15)
if($CheckActions|?{$_.SideIndicator -eq "<="})
{
Write-Host "`nPlease choose the correct action number from the above actions." -ForegroundColor Red
Disconnect_Modules
}
Foreach($UPN in $UPNs)
{
$UPN = $UPN.Trim()
Write-Progress "Processing $UPN"
$Script:Status = "$UPN - "
$User = Get-MgBetaUser -UserId $UPN -ErrorAction SilentlyContinue
$UserId = $User.Id
if($User -eq $null)
{
$InvalidUser= "$UPN"
$InvalidUser>>$InvalidUserLogFile
Continue
}
$MailBox = Get-Mailbox -Identity $UPN -RecipientTypeDetails UserMailbox -ErrorAction SilentlyContinue
if($MailBox -ne $null)
{
$MailBoxAvailability = "Yes"
}
else
{
$MailBoxAvailability = "No"
}
if($Actions -contains 15)
{
$Actions = 1..14
}
if($Actions -contains 5 -or $Actions -contains 6) # To get memberships of the user (group and roles)
{
$Memberships = Get-MgBetaUserMemberOf -UserId $UPN
}
foreach($Action in $Actions)
{
switch($Action){
1 { DisableUser ; break }
2 { ResetPasswordToRandom ; break }
3 { ResetOfficeName ; break }
4 { RemoveMobileNumber ; break }
5 { RemoveGroupMemberships ; break }
6 { RemoveAdminRoles ; break }
7 { RemoveAppRoleAssignments ; break }
8 { HideFromAddressList ; break }
9 { RemoveEmailAlias ; break }
10 { WipingMobileDevice ; break }
11 { DeleteInboxRule ; break }
12 { ConvertToSharedMailbox ; break }
13 { RemoveLicense ; break }
14 { SignOutFromAllSessions ; break }
Default {
Write-Host "No action found. Please provide valid input" -ForegroundColor Red
Disconnect_Modules
}
}
}
#This is for to set mailbox availablity value only if actions contains mailbox related actions. Otherwise its value set to be null.
$MailboxRelatedActions = Compare-Object $Actions @(8,9,10,11,12,13) -IncludeEqual -ExcludeDifferent
if($MailboxRelatedActions.count -eq 0)
{
$MailBoxAvailability = ""
}
if($MailBoxAvailability -eq "No")
{
$MailBoxAvailability = "No Exchange license assigned to user"
}
$Result = [PSCustomObject]@{
'UPN'=$UPN;'Disable User'=$DisableUserAction;'Reset Password To Random'=$ResetPasswordToRandomAction;'Reset OfficeName'=$ResetOfficeNameAction;'Remove Mobile Number'=$RemoveMobileNumberAction;
'Remove Group Memberships'=$RemoveGroupMembershipsAction;'Remove Admin Roles'=$RemoveAdminRolesAction;'Remove AppRole Assignments'=$RemoveAppRoleAssignmentsAction;'Exchange User'=$MailBoxAvailability;
'Hide From Address List'= $HideFromAddressListAction;'Remove Email Alias'=$RemoveEmailAliasAction;'Wiping Mobile Device'=$MobileDeviceAction;
'Delete Inbox Rule'=$DeleteInboxRuleAction;'ConvertToSharedMailbox'=$ConvertToSharedMailboxAction;'Remove License' =$RemoveLicenseAction;'SignOut From All Sessions'=$SignOutFromAllSessionsAction;}
$Result | Export-csv -Path $ExportCSV -Append -NoTypeInformation
$Variables = @("DisableUserAction","ResetPasswordToRandomAction","ResetOfficeNameAction","RemoveMobileNumberAction","RemoveGroupMembershipsAction","RemoveAdminRolesAction","RemoveAppRoleAssignmentsAction","MailBoxAvailability","HideFromAddressListAction","RemoveEmailAliasAction","MobileDeviceAction","DeleteInboxRuleAction","ConvertToSharedMailboxAction","RemoveLicenseAction","SignOutFromAllSessionsAction")
$Variables | ForEach-Object {Clear-Variable -Name $_ -ErrorAction SilentlyContinue}
}
Write-Host `nScript executed successfully -ForegroundColor Green
if(Test-Path -Path $ExportCSV)
{
Write-Host `nStatus file available in -NoNewline -Foregroundcolor Yellow; Write-Host " $ExportCSV"
$Prompt = New-Object -ComObject wscript.shell
$UserInput = $Prompt.popup("Do you want to open output file?",` 0,"Open Output File",4)
if ($UserInput -eq 6)
{
Invoke-Item "$ExportCSV"
}
}
if(Test-Path -Path $InvalidUserLogFile)
{
Write-Host `nInvalid users log file available in -NoNewline -Foregroundcolor Yellow; Write-Host " $InvalidUserLogFile"
}
if(Test-Path -Path $ErrorsLogFile)
{
Write-Host `nErrors log file available in -NoNewline -Foregroundcolor Yellow; Write-Host " $InvalidUserLogFile .You can see the reason for failed actions."
}
if($Actions -contains 15 -or $Actions -contains 2)
{
Write-Host `nPassword log file available in -NoNewline -Foregroundcolor Yellow; Write-Host " $PasswordLogFile"
}
Write-Host `n~~ Script prepared by AdminDroid Community ~~`n -ForegroundColor Green
Write-Host "~~ Check out " -NoNewline -ForegroundColor Green; Write-Host "admindroid.com" -ForegroundColor Yellow -NoNewline; Write-Host " to get access to 1800+ Microsoft 365 reports. ~~" -ForegroundColor Green `n`n
Disconnect_Modules
}
. main