{"id":5903,"date":"2025-06-27T08:31:34","date_gmt":"2025-06-27T06:31:34","guid":{"rendered":"https:\/\/www.msb365.blog\/?p=5903"},"modified":"2025-06-27T08:31:34","modified_gmt":"2025-06-27T06:31:34","slug":"powershell-email-domain-migration-guide","status":"publish","type":"post","link":"https:\/\/www.msb365.blog\/?p=5903","title":{"rendered":"PowerShell Email Domain Migration Guide"},"content":{"rendered":"<p>\u00a0<\/p>\n<style>\n        body {<br \/>            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;<br \/>            line-height: 1.6;<br \/>            color: #333;<br \/>            max-width: 1200px;<br \/>            margin: 0 auto;<br \/>            padding: 20px;<br \/>            background-color: #f8f9fa;<br \/>        }<\/p>\n<p>        .container {<br \/>            background: white;<br \/>            padding: 40px;<br \/>            border-radius: 10px;<br \/>            box-shadow: 0 2px 10px rgba(0,0,0,0.1);<br \/>        }<\/p>\n<p>        h1 {<br \/>            color: #2c3e50;<br \/>            border-bottom: 3px solid #3498db;<br \/>            padding-bottom: 10px;<br \/>            margin-bottom: 30px;<br \/>        }<\/p>\n<p>        h2 {<br \/>            color: #34495e;<br \/>            margin-top: 40px;<br \/>            margin-bottom: 20px;<br \/>            padding-left: 10px;<br \/>            border-left: 4px solid #3498db;<br \/>        }<\/p>\n<p>        h3 {<br \/>            color: #2c3e50;<br \/>            margin-top: 30px;<br \/>            margin-bottom: 15px;<br \/>        }<\/p>\n<p>        .intro {<br \/>            background: #e8f4fd;<br \/>            padding: 20px;<br \/>            border-radius: 8px;<br \/>            border-left: 5px solid #3498db;<br \/>            margin-bottom: 30px;<br \/>        }<\/p>\n<p>        .warning {<br \/>            background: #fff3cd;<br \/>            border: 1px solid #ffeaa7;<br \/>            border-radius: 8px;<br \/>            padding: 15px;<br \/>            margin: 20px 0;<br \/>            border-left: 5px solid #f39c12;<br \/>        }<\/p>\n<p>        .warning strong {<br \/>            color: #d68910;<br \/>        }<\/p>\n<p>        pre {<br \/>            background: #2d3748;<br \/>            color: #e2e8f0;<br \/>            padding: 20px;<br \/>            border-radius: 8px;<br \/>            overflow-x: auto;<br \/>            margin: 20px 0;<br \/>            border-left: 4px solid #4299e1;<br \/>        }<\/p>\n<p>        code {<br \/>            font-family: 'Consolas', 'Monaco', 'Courier New', monospace;<br \/>            font-size: 14px;<br \/>            line-height: 1.4;<br \/>        }<\/p>\n<p>        .step {<br \/>            background: #f8f9fa;<br \/>            border: 1px solid #dee2e6;<br \/>            border-radius: 8px;<br \/>            padding: 20px;<br \/>            margin: 20px 0;<br \/>        }<\/p>\n<p>        .step-number {<br \/>            background: #3498db;<br \/>            color: white;<br \/>            width: 30px;<br \/>            height: 30px;<br \/>            border-radius: 50%;<br \/>            display: inline-flex;<br \/>            align-items: center;<br \/>            justify-content: center;<br \/>            font-weight: bold;<br \/>            margin-right: 10px;<br \/>        }<\/p>\n<p>        ul, ol {<br \/>            margin: 15px 0;<br \/>            padding-left: 30px;<br \/>        }<\/p>\n<p>        li {<br \/>            margin: 8px 0;<br \/>        }<\/p>\n<p>        .highlight {<br \/>            background: #fff3cd;<br \/>            padding: 2px 6px;<br \/>            border-radius: 4px;<br \/>            font-weight: bold;<br \/>        }<\/p>\n<p>        .footer {<br \/>            margin-top: 40px;<br \/>            padding-top: 20px;<br \/>            border-top: 1px solid #dee2e6;<br \/>            text-align: center;<br \/>            color: #6c757d;<br \/>        }<\/p>\n<p>        .toc {<br \/>            background: #f8f9fa;<br \/>            border: 1px solid #dee2e6;<br \/>            border-radius: 8px;<br \/>            padding: 20px;<br \/>            margin: 30px 0;<br \/>        }<\/p>\n<p>        .toc h3 {<br \/>            margin-top: 0;<br \/>            color: #495057;<br \/>        }<\/p>\n<p>        .toc ul {<br \/>            list-style-type: none;<br \/>            padding-left: 0;<br \/>        }<\/p>\n<p>        .toc li {<br \/>            margin: 10px 0;<br \/>        }<\/p>\n<p>        .toc a {<br \/>            color: #3498db;<br \/>            text-decoration: none;<br \/>            padding: 5px 10px;<br \/>            display: block;<br \/>            border-radius: 4px;<br \/>            transition: background-color 0.3s;<br \/>        }<\/p>\n<p>        .toc a:hover {<br \/>            background-color: #e9ecef;<br \/>        }<br \/>    <\/style>\n<p>\u00a0<\/p>\n<div class=\"container\">\n<h1><strong style=\"font-size: 16px;\">Overview:<\/strong><\/h1>\n<h4><span style=\"font-size: 16px;\">This comprehensive guide provides PowerShell commands for migrating email domains in hybrid Microsoft 365 and Active Directory environments. The scripts cover updating User Principal Names (UPNs), managing Exchange Online mailbox addresses, and configuring Active Directory proxy addresses.<\/span><\/h4>\n<p>\u00a0<\/p>\n<div class=\"toc\">\n<h3>Table of Contents<\/h3>\n<ul>\n<li><a href=\"#prerequisites\">Prerequisites<\/a><\/li>\n<li><a href=\"#step1\">Step 1: Update User Principal Names (Microsoft Graph)<\/a><\/li>\n<li><a href=\"#step2\">Step 2: Clean Up Exchange Online Mailbox Addresses<\/a><\/li>\n<li><a href=\"#step3\">Step 3: Add Proxy Addresses in Active Directory<\/a><\/li>\n<li><a href=\"#best-practices\">Best Practices and Considerations<\/a><\/li>\n<\/ul>\n<\/div>\n<div class=\"warning\"><strong>\u26a0\ufe0f Important Warning:<\/strong> These scripts make significant changes to user accounts and email addresses. Always test in a non-production environment first and ensure you have proper backups before running in production.<\/div>\n<h2 id=\"prerequisites\">Prerequisites<\/h2>\n<div class=\"step\">\n<p>Before running these scripts, ensure you have the following:<\/p>\n<ul>\n<li><strong>PowerShell Modules:<\/strong>\n<ul>\n<li>Microsoft.Graph (for Azure AD\/Entra ID operations)<\/li>\n<li>ExchangeOnlineManagement (for Exchange Online operations)<\/li>\n<li>ActiveDirectory (for on-premises AD operations)<\/li>\n<\/ul>\n<\/li>\n<li><strong>Permissions:<\/strong>\n<ul>\n<li>User.ReadWrite.All and Directory.ReadWrite.All in Microsoft Graph<\/li>\n<li>Exchange Administrator role for Exchange Online<\/li>\n<li>Domain Administrator or delegated permissions for Active Directory<\/li>\n<\/ul>\n<\/li>\n<li><strong>Network Access:<\/strong> Connectivity to Microsoft 365 and your on-premises Active Directory<\/li>\n<\/ul>\n<\/div>\n<h2 id=\"step1\">Step 1: Update User Principal Names (Microsoft Graph)<\/h2>\n<div class=\"step\">\n<div class=\"step-number\">1<\/div>\n<p><strong>Purpose:<\/strong> Update user UPNs from the old domain (@contoso.com) to the new domain (@fabrikam.net)<\/p>\n<\/div>\n<h3>Script Explanation<\/h3>\n<p>This script connects to Microsoft Graph and updates user principal names for all users in the specified domain. It also cleans up proxy addresses to remove references to the old domain.<\/p>\n<pre><code># Connect to Microsoft Graph with required permissions\r\nConnect-MgGraph -Scopes \"User.ReadWrite.All\",\"Directory.ReadWrite.All\"\r\n\r\n# Update UPNs\r\n$filteredUsers = Get-MgUser -All\r\n$users = $filteredUsers | Where-Object { $_.UserPrincipalName -like \"*@contoso.com\" }\r\n\r\nforeach ($user in $users) {\r\n    $oldUpn = $user.UserPrincipalName\r\n    $newUpn = $oldUpn -replace \"@contoso.com\",\"@fabrikam.net\"\r\n    Update-MgUser -UserId $user.Id -UserPrincipalName $newUpn -MailNickname ($newUpn.Split(\"@\")[0])\r\n    $updatedAddresses = $user.ProxyAddresses | Where-Object { $_ -notmatch $oldUpn }\r\n    Update-MgUser -UserId $user.Id -ProxyAddresses $updatedAddresses\r\n    Write-Host \"Updated UPN for user: $($user.DisplayName) from $oldUpn to $newUpn\"\r\n    Write-Host \"Updated ProxyAddresses for user: $($user.DisplayName) to $updatedAddresses\"\r\n}<\/code><\/pre>\n<h3>What This Script Does:<\/h3>\n<ul>\n<li>Connects to Microsoft Graph with necessary permissions<\/li>\n<li>Retrieves all users with UPNs ending in <span class=\"highlight\">@contoso.com<\/span><\/li>\n<li>Updates each user\u2019s UPN to use <span class=\"highlight\">@fabrikam.net<\/span><\/li>\n<li>Updates the mail nickname to match the new UPN<\/li>\n<li>Removes old proxy addresses containing the old domain<\/li>\n<li>Provides console output for tracking progress<\/li>\n<\/ul>\n<h2 id=\"step2\">Step 2: Clean Up Exchange Online Mailbox Addresses<\/h2>\n<div class=\"step\">\n<div class=\"step-number\">2<\/div>\n<p><strong>Purpose:<\/strong> Remove old email addresses from Exchange Online mailboxes<\/p>\n<\/div>\n<h3>Script Explanation<\/h3>\n<p>This script connects to Exchange Online and removes all email addresses containing the old domain from user mailboxes.<\/p>\n<pre><code># Connect to Exchange Online\r\nConnect-ExchangeOnline\r\n\r\n# Get all mailboxes\r\n$mailboxes = Get-Mailbox -ResultSize Unlimited\r\n\r\nforeach ($mailbox in $mailboxes) {\r\n    $originalAddresses = $mailbox.EmailAddresses\r\n    $filteredAddresses = @()\r\n    \r\n    $hasRemoved = $false\r\n    \r\n    foreach ($address in $originalAddresses) {\r\n        if ($address -like \"*@contoso.com\") {\r\n            Write-Host \"Removing address: $address from $($mailbox.PrimarySmtpAddress)\"\r\n            $hasRemoved = $true\r\n        } else {\r\n            $filteredAddresses += $address\r\n        }\r\n    }\r\n    \r\n    if ($hasRemoved) {\r\n        Set-Mailbox -Identity $mailbox.Identity -EmailAddresses $filteredAddresses\r\n    }\r\n}<\/code><\/pre>\n<h3>What This Script Does:<\/h3>\n<ul>\n<li>Connects to Exchange Online PowerShell<\/li>\n<li>Retrieves all mailboxes in the organization<\/li>\n<li>Examines each mailbox\u2019s email addresses<\/li>\n<li>Removes any addresses containing <span class=\"highlight\">@contoso.com<\/span><\/li>\n<li>Updates the mailbox with the filtered address list<\/li>\n<li>Only updates mailboxes where addresses were actually removed<\/li>\n<\/ul>\n<h2 id=\"step3\">Step 3: Add Proxy Addresses in Active Directory<\/h2>\n<div class=\"step\">\n<div class=\"step-number\">3<\/div>\n<p><strong>Purpose:<\/strong> Add new proxy addresses to users in Active Directory<\/p>\n<\/div>\n<h3>Script Explanation<\/h3>\n<p>This script adds new proxy addresses to users in a specified Active Directory Organizational Unit (OU).<\/p>\n<pre><code># Define the OU and the proxy address domain\r\n$OU = \"OU=Users,DC=example,DC=com\" # Replace with your target OU\r\n$ProxyDomain = \"newdomain.com\" # Replace with your desired proxy domain\r\n\r\n# Import Active Directory module\r\nImport-Module ActiveDirectory\r\n\r\n# Get all users in the specified OU\r\n$Users = Get-ADUser -Filter * -SearchBase $OU -Properties proxyAddresses, mail\r\n\r\nforeach ($User in $Users) {\r\n    # Construct the new proxy address\r\n    $PrimaryEmail = $User.mail\r\n    if ($PrimaryEmail) {\r\n        $Username = $PrimaryEmail.Split(\"@\")[0]\r\n        $NewProxy = \"smtp:$Username@$ProxyDomain\"\r\n        \r\n        # Check if the proxy address already exists\r\n        if (-not ($User.proxyAddresses -contains $NewProxy)) {\r\n            # Add the new proxy address\r\n            Set-ADUser -Identity $User.DistinguishedName -Add @{proxyAddresses=$NewProxy}\r\n            Write-Host \"Added proxy address $NewProxy to $($User.SamAccountName)\"\r\n        } else {\r\n            Write-Host \"Proxy address $NewProxy already exists for $($User.SamAccountName)\"\r\n        }\r\n    } else {\r\n        Write-Host \"User $($User.SamAccountName) does not have a primary email address.\"\r\n    }\r\n}<\/code><\/pre>\n<h3>Configuration Required:<\/h3>\n<ul>\n<li><strong>$OU:<\/strong> Replace with your target Organizational Unit distinguished name<\/li>\n<li><strong>$ProxyDomain:<\/strong> Replace with your new domain name<\/li>\n<\/ul>\n<h3>What This Script Does:<\/h3>\n<ul>\n<li>Imports the Active Directory PowerShell module<\/li>\n<li>Retrieves users from the specified OU with email properties<\/li>\n<li>Constructs new proxy addresses using the new domain<\/li>\n<li>Checks for existing proxy addresses to avoid duplicates<\/li>\n<li>Adds new proxy addresses to user accounts<\/li>\n<li>Provides detailed logging of all operations<\/li>\n<\/ul>\n<h2 id=\"best-practices\">Best Practices and Considerations<\/h2>\n<div class=\"warning\">\n<p><strong>Testing and Validation:<\/strong><\/p>\n<ul>\n<li>Always test scripts in a development environment first<\/li>\n<li>Run scripts on a small subset of users initially<\/li>\n<li>Verify changes before proceeding with the full migration<\/li>\n<\/ul>\n<\/div>\n<h3>Execution Order<\/h3>\n<p>Execute the scripts in the following order for optimal results:<\/p>\n<ol>\n<li><strong>Step 1:<\/strong> Update UPNs in Microsoft Graph<\/li>\n<li><strong>Step 2:<\/strong> Clean up Exchange Online addresses<\/li>\n<li><strong>Step 3:<\/strong> Add proxy addresses in Active Directory<\/li>\n<\/ol>\n<h3>Monitoring and Logging<\/h3>\n<ul>\n<li>Monitor the console output during script execution<\/li>\n<li>Consider adding additional logging to files for audit purposes<\/li>\n<li>Verify changes in both the Microsoft 365 admin center and Active Directory<\/li>\n<\/ul>\n<h3>Rollback Considerations<\/h3>\n<ul>\n<li>Document original UPNs and email addresses before making changes<\/li>\n<li>Consider creating backup scripts to reverse changes if needed<\/li>\n<li>Test rollback procedures in your development environment<\/li>\n<\/ul>\n<h3>Common Issues and Troubleshooting<\/h3>\n<ul>\n<li><strong>Permission Errors:<\/strong> Ensure you have the required administrative permissions<\/li>\n<li><strong>Module Not Found:<\/strong> Install required PowerShell modules before running scripts<\/li>\n<li><strong>Connection Timeouts:<\/strong> Consider adding retry logic for large environments<\/li>\n<li><strong>Duplicate Addresses:<\/strong> The scripts include checks to prevent duplicate proxy addresses<\/li>\n<\/ul>\n<div class=\"footer\">\n<p><em>This documentation provides PowerShell scripts for email domain migration in Microsoft 365 and Active Directory environments. Always follow your organization\u2019s change management procedures and test thoroughly before production deployment.<\/em><\/p>\n<\/div>\n<\/div>\n<p>\u00a0<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u00a0 \u00a0 Overview: This comprehensive guide provides PowerShell commands for migrating email domains in hybrid Microsoft 365 and Active Directory environments. The scripts cover updating User Principal Names (UPNs), managing Exchange Online mailbox addresses, and configuring Active Directory proxy addresses. \u00a0 Table of Contents Prerequisites Step 1: Update User Principal Names (Microsoft Graph) Step 2: [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":5974,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_crdt_document":"","om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[685,12,1923,2,3],"tags":[],"class_list":["post-5903","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-active-directory","category-azure","category-microsoft-365","category-exchange","category-powershell"],"post_mailing_queue_ids":[],"_links":{"self":[{"href":"https:\/\/www.msb365.blog\/index.php?rest_route=\/wp\/v2\/posts\/5903","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.msb365.blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.msb365.blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.msb365.blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.msb365.blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=5903"}],"version-history":[{"count":7,"href":"https:\/\/www.msb365.blog\/index.php?rest_route=\/wp\/v2\/posts\/5903\/revisions"}],"predecessor-version":[{"id":5975,"href":"https:\/\/www.msb365.blog\/index.php?rest_route=\/wp\/v2\/posts\/5903\/revisions\/5975"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.msb365.blog\/index.php?rest_route=\/wp\/v2\/media\/5974"}],"wp:attachment":[{"href":"https:\/\/www.msb365.blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5903"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.msb365.blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=5903"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.msb365.blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=5903"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}