Entra / Microsoft 365 · Users & guests
Convert mail contacts to guest accounts
Converts Exchange Online mail contacts into Entra ID guest user accounts.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
Connect-ExchangeOnlineConnect-MgGraph -Scopes Directory.ReadWrite.All
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
Connect-ExchangeOnlineConnect-MgGraph -Scopes Directory.ReadWrite.All[array]$Contacts = Get-ExoRecipient -RecipientTypeDetails MailContact -ResultSize Unlimited -Filter {CustomAttribute2 -ne "Migrated"} -PropertySets AllIf (!($Contacts)) { Write-Host "No mail contacts found... " ; break }Write-Host ("Found {0} mail contacts - now processing..." -f $Contacts.count)Add-Type -AssemblyName 'System.Web'# Get email addresses for current guest accounts[array]$GuestEmail = Get-MgUser -All -Filter "userType eq 'Guest'" | Sort-Object Mail | Select-Object -ExpandProperty Mail[int]$i = 0$DLUpdates = [System.Collections.Generic.List[Object]]::new()ForEach ($Contact in $Contacts) {$i++Write-Host ("Processing mail contact {0} ({1}/{2})" -f $Contact.PrimarySmtpaddress, $i, $Contacts.count)If ($Contact.PrimarySmtpAddress -in $GuestEmail) {Write-Host ("Contact {0} with email {1} is already registered as guest account - hiding mail contact" -f $Contact.DisplayName, $Contact.PrimarySmtpAddress)Set-MailContact -Identity $Contact.Alias -HiddenFromAddressListsEnabled $True -CustomAttribute2 "Migrated"} Else {# Create a password for the new account$NewPassword = [System.Web.Security.Membership]::GeneratePassword(10, 3)$NewPasswordProfile = @{}$NewPasswordProfile["Password"]= $NewPassword$NewPasswordProfile["ForceChangePasswordNextSignIn"] = $True# Determine usage location$UsageLocation = "US"Switch ($Contact.CountryOrRegion) {"Bulgaria" { $UsageLocation = "BG" }"Canada" { $UsageLocation = "CA" }"France" { $UsageLocation = "FR" }"Germany" { $UsageLocation = "DE" }"Ireland" { $UsageLocation = "IE" }"Italy" { $UsageLocation = "IT" }"Switzerland" { $UsageLocation = "CH" }"United States" { $UsageLocation = "US" }"United Kingdom" { $UsageLocation = "UK" }} #End Switch# New-MgUser gets upset if null strings are passed in parameters[string]$City = " "; [string]$Office = " "; [string]$JobTitle = " "; [string]$Department = " "[String]$Country = " "; [string]$PostalCode = " "; [string]$Company = " "; [string]$FirstName = "Unknown"[string]$LastName = "Unknown"; [string]$DisplayName = " "If ($Contact.City) { $City = $Contact.City }If ($Contact.Office) { $Office = $Contact.Office }If ($Contact.Title) { $JobTitle = $Contact.Title }If ($Contact.CountryOrRegion) { $Country = $Contact.CountryOrRegion }If ($Contact.PostalCode) { $PostalCode = $Contact.PostalCode }If ($Contact.Company) { $Company = $Contact.Company }If ($Contact.Department) { $Department = $Contact.Department }If ($Contact.FirstName) { $FirstName = $Contact.FirstName }If ($Contact.LastName) { $LastName = $Contact.LastName }If ($Contact.DisplayName) { $DisplayName = $Contact.DisplayName }# Calculate values for mail nickname and user principal name for the guest account# Use your own domain rather than office365itpros.com....$Alias = $Contact.alias -replace '[?]',''$NickName = $Alias + ".Contact"$UPN = $NickName + "#EXT#@Office365itpros.com"# Give mail contact a different SMTP address so it doesn't clash$NewPrimarySmtpAddress = $NickName + ".temp@Office365itpros.com"Set-MailContact -Identity $Contact.Alias -EmailAddresses $NewPrimarySmtpAddress# Populate hash table with properties for the new account$NewUserProperties = @{UserType = "Guest"GivenName = $FirstNameSurname = $LastNameDisplayName = $DisplayNameJobTitle = $JobTitleDepartment = $DepartmentMailNickname = $NickNameMail = $Contact.PrimarySmtpAddressUserPrincipalName = $UPNCountry = $CountryCity = $CityPostalCode = $PostalCodeOfficeLocation = $OfficeCompany = $CompanyUsageLocation = $UsageLocationPasswordProfile = $NewPasswordProfileAccountEnabled = $true }# Try to create new guest accountTry {$NewGuestAccount = New-MgUser @NewUserProperties }Catch {Write-Host ("Couldn't create new Guest account using these properties")Write-Host $NewUserPropertiesBreak }# Let the new guest appear in Exchange address listsUpdate-MgUser -UserId $NewGuestAccount.Id -ShowInAddressList:$True# Hide the mail contact and keep a record of the old email address (that the guest account now hasSet-MailContact -Identity $Contact.Alias -HiddenFromAddressListsEnabled $True -CustomAttribute1 $Contact.PrimarySmtpAddress -CustomAttribute2 "Migrated"# Update distribution groups...# Because Exchange Online doesn't create the new Mail User object immediately, we need to wait before we can# swap DL membership and replace the old mail contact records with the new guest accounts. So we write out the# information into a list and process the updates later$DN = $Contact.DistinguishedName[array]$DLs = Get-ExoRecipient -ResultSize Unlimited -Filter "Members -eq '$DN'" -RecipientTypeDetails MailUniversalDistributionGroup -ErrorAction SilentlyContinueIf ($DLs) {Write-Host ("User is a member of {0} groups" -f $DLs.count)ForEach ($DL in $DLs) {$DataLine = [PSCustomObject] @{DLName = $DL.DisplayNameDLAlias = $DL.AliasDLId = $DL.ExternalDirectoryObjectIdDLOldMember = $DNDLNewMember = $NewGuestAccount.Id }$DLUpdates.Add($Dataline) }} #End If $DLs} #End if Not found in existing guest accounts} #End ForEach Contact# Export all the DL Updates to process$DLUpdates | Export-CSV -NoTypeInformation c:\temp\DLUpdateToProcess.csv# --- End of script# This is the code needed to process the distribution list updates in the CSV file created by the code above.[array]$DLUpdatesToProcess = Import-CSV c:\temp\DLUpdateToProcess.csvForEach ($Update in $DLUpdatesToProcess) {Remove-DistributionGroupMember -Identity $Update.DLAlias -Member $Update.DLOldMember -Confirm:$FalseAdd-DistributionGroupMember -Identity $Update.DLAlias -Member $Update.DLNewMember}
Attribution
Author
Office365itpros