Tag Archives: Office 365

Automating external takeover for domains in Microsoft 365

Microsoft 365 customers have the ability to add custom domains for their organization. In order to utilize a custom domain, a proof of ownership process is required. For each domain added to a Microsoft 365 tenant a verification txt and mx record is created. This txt or mx record is placed in the public DNS provider for the domain and when located the domain is validated too the organization.

Background

In the Microsoft 365 admin center when verifying a domain, the domain verification process will fail if we believe the domain is validated on another tenant.

We have confirmed that you own 365.net, but we can't add it to your account because the domain is already added to a different Microsoft 365 organization: Test.onmicrosoft.com.

Sign in to the admin center as pil*******min@Test.onmicrosoft.com, or another admin for that organization, and remove the domain 365.net. Try resetting the admin password if you can't sign in. You should be able to add the domain 365.net here after taking that step.

If you can't access Test.onmicrosoft.com, please contact our support team for help.

This error would indicate that the domain is registered within another tenant. It is possible that the domain is registered in what is known as a “viral” or unmanaged tenant.

“Viral” or Unmanaged Tenants

What is a “viral” or unmanaged tenant? In prior versions of Microsoft 365 if a user enrolled in a trial service, and the email address utilized for registration was not associated with a Microsoft 365 tenant, an unmanaged tenant was provisioned. The domain was then added to the unmanaged tenant. The domain is not actually verified and a global administrator for the tenant does not exist. Most commonly I observe unmanaged tenants associated with PowerBI trials or Azure Rights Management services. Unmanaged tenants are no longer provisioned in association with trial services.

You can judge if a domain is viral using Powershell and a web request.

$domainName = "365.net"

$functionURL = "https://login.microsoftonline.com/common/userrealm/"+$domainName+"?api-version=2.1"

(Invoke-WebRequest $functionURL).content

If a domain is not unmanaged the output generally looks something like this:

Managed Authentication:

{"NameSpaceType":"Managed","Login":"365.net","DomainName":"365.net","FederationBrandName":" Test","TenantBrandingInfo":null,"cloud_instance_name":"microsoftonline.com"}

Federated Authentication:

{"NameSpaceType":"Federated","federation_protocol":"WSTrust","Login":"domain.com","AuthURL":"https://fs.domain.com/adfs/ls/?username=domain.com&wa=wsignin1.0&wtrealm=urn%3afederation%3aMicrosoftOnline&wctx=","DomainName":"domain.com","FederationBrandName":"Test","TenantBrandingInfo":null,"cloud_instance_name":"microsoftonline.com"}

If the domain is unmanaged the output generally looks something like this with IsViral:TRUE present in the return:

{"NameSpaceType":"Managed","Login":"domain.com","DomainName":"domain.com","FederationBrandName":"domain.com","IsViral":true,"TenantBrandingInfo":null,"cloud_instance_name":"microsoftonline.com"}

The steps in this article generally only work when the domain is marked as viral or unmanaged. If the domain is managed in another tenant, the steps can be tried but are unlikely to be successful. Domains managed in other tenants require Microsoft support intervention to claim ownership and can be engaged by opening a support case.

Microsoft provides two options to assume ownership of a domain when it is associated with an unmanaged tenant. These options are outlined in https://learn.microsoft.com/en-us/entra/identity/users/domains-admin-takeover.

Internal Takeover

The internal admin takeover method assumes that you have access to receive email at the domain that you are trying to takeover. In this process you establish a trial with the account you have access to, and then you convert the tenant from unmanaged to managed. Once a global administrator has been established, the domain can be gracefully removed.

External Takeover

If you have landed on this article, then you are most likely doing the external takeover method. It is not uncommon to need to use the external takeover method to complete the domain verification. In many cases the domain is not available on another platform to receive email precluding the use of the internal method. In order to perform the external takeover, the following steps are utilized:

  • Add the domain to the tenant where it should reside.
  • Obtain the TXT or MX record necessary for verification.
  • Ensuring that TXT or MX record is in external / public DNS.
  • Validate the domain through Microsoft Graph specifying a force takeover method.

NOTE: Unmanaged or viral tenants are Microsoft 365 World Wide Commercial tenants. You cannot perform an external takeover from a commercial to GCC High or DOD tenant. You must first perform the external takeover to a commercial tenant then remove and add the domain to your GCC High or DOD tenant.

Background: Automating External Takeover

To assist in performing the external takeover I have released a script to the Powershell Gallery. ForceDomainTakeover.ps1 will perform the following functions:

  • Prompt the user for necessary information including graph environment, tenant ID, domain name, and graph authentication method.
    • Graph authentication supports interactive user authentication or app registration with certificate authentication or client secret.
    • Optional external DNS server may be specified for DNS queries.
  • Perform a web request to determine if the domain is viral.
  • Ensure the domain is added to the specified tenant.
  • Enumerate the DNS verification records and test DNS for the presence of a TXT or MX verification record.
  • Attempt the external admin takeover and log the results.
  • Logs all information to the log directory creating the necessary files for any Microsoft support engagement.

The automated process will test to ensure that the domain is present in the tenant specified. If the domain is not present the administrator will be prompted to add the domain. If the administrator declines to add the domain the process will end. The process can be re-run and the domain added or the domain added through other means.

[6/22/2025 2:16:30 PM] - Enter TestDomainName
Resource '365.net' does not exist or one of its queried reference-property objects are not present.

Status: 404 (NotFound)
ErrorCode: Request_ResourceNotFound
Date: 2025-06-22T18:16:32

Headers:
Transfer-Encoding             : chunked
Connection                    : keep-alive
Vary                          : Accept-Encoding
Strict-Transport-Security     : max-age=31536000
request-id                    : 2bb91cec-d88e-4394-86a4-bef490f00dd5
client-request-id             : dec3375c-402f-4e88-8ee9-fb6a4db82be7
x-ms-ags-diagnostic           : {"ServerInfo":{"DataCenter":"East US 2","Slice":"E","Ring":"5","ScaleUnit":"002","RoleInstance":"BN2PEPF0000365B"}}
x-ms-resource-unit            : 1
X-Cache                       : CONFIG_NOCACHE
Cache-Control                 : no-cache
Date                          : Sun, 22 Jun 2025 18:16:32 GMT


[6/22/2025 2:16:31 PM] - Specified Domain 365.net is not added to the specified tenant.
Add domain to tenant to proceed? Y/N:

The automated process will also verify that either the TXT or MX record for the domain is present in external DNS. If the records are not present an error is displayed providing the administrator with the correct records to create.

[6/22/2025 2:17:20 PM] - Testing to verify that public DNS is updated with verification records.
[6/22/2025 2:17:20 PM] - Enter TestDNSRecords
[6/22/2025 2:17:20 PM] - MS=ms55782524
[6/22/2025 2:17:20 PM] - ms55782524.msv1.invalid
[6/22/2025 2:17:20 PM] - M365 TXT Record: MS=ms55782524
[6/22/2025 2:17:20 PM] - M365 MX Record: ms55782524.msv1.invalid
[6/22/2025 2:17:20 PM] - Testing public DNS records.
[6/22/2025 2:17:20 PM] - TXT record not found in public dns.
[6/22/2025 2:17:20 PM] - TXT record not found in public dns.
[6/22/2025 2:17:20 PM] - TXT record not found in public dns.
[6/22/2025 2:17:20 PM] - MX record not found in public dns.
[6/22/2025 2:17:20 PM] -

 Either TXT Record [Most Common]: MS=ms55782524 or MX Record: ms55782524.msv1.invalid must be present in public dns.
 If the domain was recently added please add either of this records to proceed.




ClientId               : 14d82eec-204b-4c2f-b7e8-296a70dab67e
TenantId               : f7d9d2a4-dded-4f6f-90a9-5011281137b9
Scopes                 : {Application.Read.All, AuditLog.Read.All, Directory.Read.All, Directory.ReadWrite.All...}
AuthType               : Delegated
TokenCredentialType    : InteractiveBrowser
CertificateThumbprint  :
CertificateSubjectName :
SendCertificateChain   : False
Account                : Administrator
AppName                : Microsoft Graph Command Line Tools
ContextScope           : CurrentUser
Certificate            :
PSHostVersion          : 5.1.26100.4202
ManagedIdentityId      :
ClientSecret           :
Environment            : Global

out-logfile : [6/22/2025 2:17:20 PM] -

 Either TXT Record [Most Common]: MS=ms55782524 or MX Record: ms55782524.msv1.invalid must be present in public dns.
 If the domain was recently added please add either of this records to proceed.

At C:\Program Files\WindowsPowerShell\Scripts\ForceDomainTakeover.ps1:797 char:9
+         out-logfile -string ("`n `n Either TXT Record [Most Common]:  ...
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Out-LogFile

To start utilizing the script:

Install-Script ForceDomainTakeOver
Update-Module Microsoft.Graph.Authentication
Update-Module Microsoft.Graph.Identity.DirectoryManagement

If the script errors on attempting the force, make a secondary attempt and specify the beta endpoint – examples below.

Examples: ForceDomainTakeOver

#Run script and prompt for all necessary information / interactive authentication only

ForceDomainTakeover.ps1 -logFolderPath c:\temp
#Run script and prompt for interactive authentication only.

ForceDomainTakeover.ps1 -logFolderPath c:\temp -domainName "test.net" -msGraphEnvironmentName "Global" -msGraphTenantID "Azure-Tenant-ID" 
#Run the script and prompt for interactive credentials only - GCC High.

ForceDomainTakeover.ps1 -logFolderPath c:\temp -domainName "test.net" -msGraphEnvironmentName "USGov" -msGraphTenantID "Azure-Tenant-ID" 
#Run the script and utilize a graph app registration with certificate authentication.

ForceDomainTakeover.ps1 -logFolderPath c:\temp -domainName "test.net" -msGraphEnvironmentName "Global" -msGraphTenantID "Azure-Tenant-ID" -msGraphCertificateThumbprint "CertThumbprint" -msGraphApplicationID "ApplicationID"
#Run the script and utilize a graph app registration with client secret.

ForceDomainTakeover.ps1 -logFolderPath c:\temp -domainName "test.net" -msGraphEnvironmentName "Global" -msGraphTenantID "Azure-Tenant-ID" -msGraphClientSecret "ClientSecret" -msGraphApplicationID "ApplicationID"
#Run script and prompt for interactive credentials only / use graph beta endpoint

ForceDomainTakeover.ps1 -logFolderPath c:\temp -domainName "test.net" -msGraphEnvironmentName "Global" -msGraphTenantID "Azure-Tenant-ID" -msGraphUseBeta:$TRUE

A complete list of all debug information including the output from each step of the command is stored within the log file directory. The log file directory can be zipped and attached to a support case should support from Microsoft be required.

Office 365 – Distribution List Migration Version 2.0 – Part 40

Offering a new alternative for nested group migrations…

Distribution lists offer administrators the opportunity to nest within each other. As a part of distribution list migrations all child groups must be migrated prior to the parent group being migrated.

In the case of a single migration, if the parent is selected for migration and contains children, the migration will fail and the logs will indicate that a child group was found and must be migrated first.

In the case of multiple migrations, if the parent is attempted first before a child for migration, the parent will be scheduled for retry. After a migration pass has been attempted against each group, the parent distribution lists are retried if the child was included in the migration batch. 

I recently had a customer that presented an interesting scenario. They have a parent distribution list that contains multiple children. The children lists membership is managed by another product that only has on-premises integration. In this customer instance they desired to migrate the parent distribution list but retain the children on premises. The child distribution lists would continue to be managed through their third party system. Both the parent and child distribution lists were synchronized to Office 365 and available in Exchange Online.

They proposed a solution where they would remove the children distribution lists and then migrate the parent. Post migration, in Exchange Online, they would add the children distribution lists back as members. This is not blocked, synchronized distribution lists can be added as members of a cloud only distribution group. 

This obviously is a manual process and could lead to things being missed. Plus, what fun is a manual process when we could easily code a solution to support it 🙂

In version 2.9.8.24 a new switch has been introduced to SINGLE distribution list migrations. The switch is not available in multiple or multiple machine migrations. The switch is “-skipNestedGroupCheck”. When this switch is specified as a part of a migration if a mail enabled child group is found as a member of the distribution list to be migrated the migration is allowed to proceed. Instead of logging an error the mail enabled child distribution list is added as a valid recipient and as long as the recipient can be located in Office 365 the migration will proceed. At the end of the migration the synchronized child distribution lists are added as members of the migrated cloud distribution lists. 

At the conclusion of the migration the mail disabled group remains on premises. The membership of the mail disabled group remains the same. If at a later time a child distribution list is migrated the module already supports reviewing all cloud only distribution groups for membership. This group will be found as a member of the previously migrated group and the group membership should be retained.

Are there any mail flow considerations when doing this? The answer is yes! If you are not utilizing the -enableHybridMailFlow switch on premises Exchange Servers will continue to receive email at all child distribution lists. The parent distribution lists address will no longer be available on premises, therefore any email that relied on the expansion of the parent distribution list would fail.

If -enableHybridMailFlow is utilized the following mail flow route ensures that all group, including the non-migrated child distribution lists, receive the message.

  • The email is received by the on premises Exchange Server (parent@contoso.com).
  • Parent@contoso.com resolves to the dynamic distribution list created by -enableHybridMailFlow.
  • Dynamic distribution list expansion occurs and resolves to the single mail contact parent-migratedByScript@contoso.com
  • The mail contact parent-migratedByScript@contoso.com has a target address parent@contoso.mail.onmicrosoft.com. This address is resolved.
  • Mail flows through the hybrid connector and arrives in Office 365.
  • Parent@contoso.mail.onmicrosoft.com resolves to the migrated distribution list parent@contoso.com.
  • The migrated distribution list parent@contoso.com is expanded and includes child distribution lists.
  • The child distribution lists are now expanded.

I hope this new switch adds some additional flexibility and opens new scenarios for migration!