Tag Archives: azure

Auditing Consumer Accounts

Microsoft provides a variety of consumer services that are accessed through a “Microsoft Account”. It is possible for a Microsoft 365 customer to have utilized their email address to sign up for Microsoft consumer services.

When a user has both a Microsoft 365 Identity and a Microsoft consumer identity, they are prompted to select a work or school account or personal account when signing into Microsoft services.

Creating a consumer or personal account using a domain that is registered in Microsoft 365 is now blocked and has been for several years. In many cases if a Microsoft 365 customer has both a personal and work or school account, they set it up prior to when this block was put in place. Here is a sample error that someone receives today when trying to utilize a domain registered in Microsoft 365 to establish a consumer account:

Microsoft does not provide the ability for administrators to compel a user to change the identity of a consumer account. We do provide guidance for users that want to change their personal account identity to either stop being prompted to select an account or to simply disconnect the relationship between the two identities. Change the email address or phone number for your Microsoft account – Microsoft Support

In general, users that have both a personal and Microsoft 365 account do not have issues utilizing both services with the same address. I have seen some issues though that cause concern, such as users receiving unusual sign in activity notices in their corporate mailboxes when the activity is actually related to their consumer identity. Microsoft account unusual sign-in activity | TIMMCMIC

I have worked on several escalations recently where customers have asked to quantify the number of consumer accounts that are related to Microsoft 365 identities. The goal of the project is to proactively reach out to these users and provide them guidance on how to follow the account rename process.

To provide administrators with an option to audit their consumer accounts I have published AuditConsumerAccounts to the PowerShell Gallery. Audit consumer accounts is a PowerShell module that allows administrators to gather all identities and addresses from EntraID and perform an audit against Microsoft consumer account information. At the conclusion of the audit a report is generated that lists any consumer accounts discovered as well as any accounts that an error was encountered.

AuditConsumerAccounts supports a variety of graph authentication methods. If the account list to be audited is greater than 50 members it is recommended to utilize a graph connection method that relies on an app registration within the tenant. This allows the module to batch users into smaller groups for testing rather than iterate through users sequentially.

Administrators also have the flexibility to define the scope of addresses they would like to test for consumer identities. By default, the objects user principal name is always included for testing. The module also supports the ability to add just primary SMTP addresses for testing or add all user proxy addresses for testing. In most environments the user principal name and primary smtp address are the same resulting in only a single address being verified. The module also supports testing only for a specific domain across the address set based on a list of domains provided at runtime. Note: The domains must be registered as domains within the tenant to utilize this feature.

AuditConsumerAccounts tests the entire organization by default. The command also supports providing a graph filter to scope a query of objects against EntraID or allows administrators to specify a list of user identities to specifically test. This is helpful if you are only interested in auditing a subset of your users.

AuditConsumerAccounts provides additional security flexibility by allowing the caller to specify the permissions required to both read domains and read user information. For information on the permissions set required for domains see Get domain – Microsoft Graph v1.0 | Microsoft Learn and for permissions sets required for users see Get user – Microsoft Graph v1.0 | Microsoft Learn

To start using AuditConsumerAccounts run PowerShell 7.6.1 as administrator and run the following command:

Install-Module AuditConsumerAccounts

When the module has been successfully installed the start-AuditConsumerAccounts command is utilized to begin the audit process. Here are some examples:

Start an audit of all accounts and all proxy addresses using graph interactive authentication.

$logFolderPath = "c:\temp"
$tenantID = "EntraTenantID" #This is your Entra Tenant ID
$environment = "Global" #Acceptable values Global,USGov,USDOD,China
$userPermission = "Directory.ReadWrite.All" #Acceptable values User.Read.All,User.ReadWrite.All,Directory.Read.All,Directory.ReadWrite.All
$domainPermission = "Domain.ReadWrite.All" #Acceptable values Domain.Read.All,Domain.ReadWrite.All
start-auditConsumerAccounts -logFolderPath $logFolderPath -msGraphTenantID $tenantID -msGraphEnvironment $environment -msGraphDomainPermissions $domainPermission -msGraphUserPermissions $userPermission

Start an audit of all accounts and all proxy addresses using graph client secret authentication.

$logFolderPath = "c:\temp"
$tenantID = "EntraTenantID" #This is your Entra Tenant ID
$appID = "App registration ID"
$clientSecret = "Client secret assigned to app registration"
$environment = "Global" #Acceptable values Global,USGov,USDOD,China
$userPermission = "Directory.ReadWrite.All" #Acceptable values User.Read.All,User.ReadWrite.All,Directory.Read.All,Directory.ReadWrite.All
$domainPermission = "Domain.ReadWrite.All" #Acceptable values Domain.Read.All,Domain.ReadWrite.All
start-auditConsumerAccounts -logFolderPath $logFolderPath -msGraphTenantID $tenantID -msGraphEnvironment $environment -msGraphDomainPermissions $domainPermission -msGraphUserPermissions $userPermission -msGraphClientSecret $clientSecret -msGraphApplicationID $appID

Start an audit of all accounts and all proxy addresses using graph certificate authentication.

$logFolderPath = "c:\temp"
$tenantID = "EntraTenantID" #This is your Entra Tenant ID
$appID = "App registration ID"
$certificateThumpprint = "Cert thumbprint assigned to app registration"
$environment = "Global" #Acceptable values Global,USGov,USDOD,China
$userPermission = "Directory.ReadWrite.All" #Acceptable values User.Read.All,User.ReadWrite.All,Directory.Read.All,Directory.ReadWrite.All
$domainPermission = "Domain.ReadWrite.All" #Acceptable values Domain.Read.All,Domain.ReadWrite.All
start-auditConsumerAccounts -logFolderPath $logFolderPath -msGraphTenantID $tenantID -msGraphEnvironment $environment -msGraphDomainPermissions $domainPermission -msGraphUserPermissions $userPermission -msGraphCertificateThumbprint $certificateThumbprint -msGraphApplicationID $appID

Start an audit of all accounts and only primary proxy addresses using graph client secret authentication. This is just one example, -testPrimarySMTPOnly:$TRUE can be added to any command using any authentication type.

$logFolderPath = "c:\temp"
$tenantID = "EntraTenantID" #This is your Entra Tenant ID
$appID = "App registration ID"
$clientSecret = "Client secret assigned to app registration"
$environment = "Global" #Acceptable values Global,USGov,USDOD,China
$userPermission = "Directory.ReadWrite.All" #Acceptable values User.Read.All,User.ReadWrite.All,Directory.Read.All,Directory.ReadWrite.All
$domainPermission = "Domain.ReadWrite.All" #Acceptable values Domain.Read.All,Domain.ReadWrite.All
start-auditConsumerAccounts -logFolderPath $logFolderPath -msGraphTenantID $tenantID -msGraphEnvironment $environment -msGraphDomainPermissions $domainPermission -msGraphUserPermissions $userPermission -msGraphClientSecret $clientSecret -msGraphApplicationID $appID -testPrimarySMTPOnly:$TRUE

Start an audit of select accounts using a graph filter and using graph client secret authentication. This is just one example, -msGraphRecipientFilter can be added to any command using any authentication type.

$logFolderPath = "c:\temp"
$tenantID = "EntraTenantID" #This is your Entra Tenant ID
$appID = "App registration ID"
$clientSecret = "Client secret assigned to app registration"
$environment = "Global" #Acceptable values Global,USGov,USDOD,China
$userPermission = "Directory.ReadWrite.All" #Acceptable values User.Read.All,User.ReadWrite.All,Directory.Read.All,Directory.ReadWrite.All
$domainPermission = "Domain.ReadWrite.All" #Acceptable values Domain.Read.All,Domain.ReadWrite.All
$graphFilter = "startsWith(DisplayName, 'a')"
start-auditConsumerAccounts -logFolderPath $logFolderPath -msGraphTenantID $tenantID -msGraphEnvironment $environment -msGraphDomainPermissions $domainPermission -msGraphUserPermissions $userPermission -msGraphClientSecret $clientSecret -msGraphApplicationID $appID -msGraphRecipientFilter $graphFilter

Start an audit of select accounts by providing an array of user identities in the format of Azure Object IDs or User Principal Names. -bringYourOwnAddresses can be utilized with any graph authentication type and will test all addresses on the user by default unless -testPrimarySMTPOnly:$TRUE is also specified. This example uses graph client secret authentication.

#Define an array of addresses
$addressesToTest=@()
#Add users to the array to test either manually or through some other export / import.
$addressesToTest += "user1@domain.com"
$addressesToTest += "user2@domain.com"
$addressesToTest += "user3@domain.com"
$logFolderPath = "c:\temp"
$tenantID = "EntraTenantID" #This is your Entra Tenant ID
$appID = "App registration ID"
$clientSecret = "Client secret assigned to app registration"
$environment = "Global" #Acceptable values Global,USGov,USDOD,China
$userPermission = "Directory.ReadWrite.All" #Acceptable values User.Read.All,User.ReadWrite.All,Directory.Read.All,Directory.ReadWrite.All
$domainPermission = "Domain.ReadWrite.All" #Acceptable values Domain.Read.All,Domain.ReadWrite.All
start-auditConsumerAccounts -logFolderPath $logFolderPath -msGraphTenantID $tenantID -msGraphEnvironment $environment -msGraphDomainPermissions $domainPermission -msGraphUserPermissions $userPermission -msGraphClientSecret $clientSecret -msGraphApplicationID $appID -bringYourOwnAddresses $addressesToTest

Start an audit of select domains by providing an array of domains. -bringYourOwnDomains can be utilized with any graph authentication type and will test all addresses that match the domain by default unlesss -testPrimarySMTPOnly:$TRUE is also specified.

#Define an array of domains.
$domainsToTest = @()
#Add domains to the array to test.
#These domains must be registered in the tenant that you are authenticating to.
$domainsToTest += "domain.com"
$domainsToTest += "domain2.com"
$logFolderPath = "c:\temp"
$tenantID = "EntraTenantID" #This is your Entra Tenant ID
$appID = "App registration ID"
$clientSecret = "Client secret assigned to app registration"
$environment = "Global" #Acceptable values Global,USGov,USDOD,China
$userPermission = "Directory.ReadWrite.All" #Acceptable values User.Read.All,User.ReadWrite.All,Directory.Read.All,Directory.ReadWrite.All
$domainPermission = "Domain.ReadWrite.All" #Acceptable values Domain.Read.All,Domain.ReadWrite.All
start-auditConsumerAccounts -logFolderPath $logFolderPath -msGraphTenantID $tenantID -msGraphEnvironment $environment -msGraphDomainPermissions $domainPermission -msGraphUserPermissions $userPermission -msGraphClientSecret $clientSecret -msGraphApplicationID $appID -bringYourOwnDomains $domainsToTest

When the command is executed a graph connection using the specified authentication type is automatically performed. The scopes specified in the execution are verified as present after the connection has successfully been made. If the connection is unsuccessful or the scopes are not present the command will fail.

Once the graph connection is established, a call is made to extract all domain information from the tenant. If a custom domains list is provided, the list is validated to ensure the domains are registered in the tenant.

When the domains have been validated, the next step is to pull the users from EntraID. By default, all users are pulled unless a filter is specified or a list of addresses is specified. Guest accounts are automatically excluded from testing.

When the list of users has been gathered the command starts creating objects representing address combinations to test. As indicated user principal names are always included for testing as well as all proxy addresses on the user unless -testPrimarySMTPOnly:$TRUE is specified. The script automatically removes any addresses on a user that are not at domains registered in Microsoft 365. At the conclusion of list generation, the list is filtered to remove duplicates from being tested.

At this time further testing depends on the graph authentication type and number of objects to test. If the authentication type is not interactive and the number of users to test is greater than 50, the list of addresses is broken up into groups of 50. The script will then recursively create jobs to test users in batches of 50, with 5 concurrent jobs running at any one time. Each job sleeps for 1 second after each user validation with the entire job sleeping randomly between 5 – 10 minutes at the conclusion of testing 50 users. This is required to avoid service side throttling when attempting bulk user evaluation. A new job is created as jobs finish until all jobs representing all of the users have completed. If interactive authentication is utilized or the user count is less than 50 the list of users is iterated through sequentially until all testing has completed.

When the command finishes auditing all details of the audit are exported to an XML file located in the log file directory. An HTML report is also created and displayed to the administrator. The report contains three sections. The first section is the list of consumer accounts identified, the second section is a list of account test errors, and the last section is the timeline of testing.

It is expected to have a list of accounts that fail to audit either due to HTTP call timeouts or an audit backoff from the service. The command exports all failed accounts to a separate file that allows administrators to retry the reporting operations on these accounts. Here is an example using graph client secret authentication:

#Import the list of failed accounts from the first run.
$failedAccounts = import-cliXML -path "c:\temp\AuditConsumerAccounts\AuditConsumerAccounts-ConsumerAccountsErrors.xml"
$logFolderPath = "c:\temp2" #USE A DIFFERENT LOG DIRECTORY TO NOT OVERWRITE ALREADY GATHERED DATA
$tenantID = "EntraTenantID" #This is your Entra Tenant ID
$appID = "App registration ID"
$clientSecret = "Client secret assigned to app registration"
$environment = "Global" #Acceptable values Global,USGov,USDOD,China
$userPermission = "Directory.ReadWrite.All" #Acceptable values User.Read.All,User.ReadWrite.All,Directory.Read.All,Directory.ReadWrite.All
$domainPermission = "Domain.ReadWrite.All" #Acceptable values Domain.Read.All,Domain.ReadWrite.All
start-auditConsumerAccounts -logFolderPath $logFolderPath -msGraphTenantID $tenantID -msGraphEnvironment $environment -msGraphDomainPermissions $domainPermission -msGraphUserPermissions $userPermission -msGraphClientSecret $clientSecret -msGraphApplicationID $appID -bringYourOwnAddresses $failedAccounts

The auditing process can take a long time to complete, and this is expected. In testing to complete 140,000 address tests took 3 days 6 hours to complete. The list of throttled or failed HTTP calls averaged 500 in these repeated tests.

With the auditing process complete administrators may now understand the scope of commercial accounts present and if they desire to do so develop a targeted communications campaign to assist these users in updating their identities to no longer have a relationship to registered addresses in Microsoft 365.

Graph and Client Secret Authentication

Microsoft Graph supports multiple methods to authenticate to EntraID to perform graph functions. One method of connecting is to utilize client secret authentication. This process is a little different than interactive authentication or certificate authentication.

The client secret authentication process begins with creating an app registration in Entra ID. To create the app registration:

To create the app registration:

  • Select New registration
  • In the name field I recommend something intuitive – for example MicrosoftGraphClientSecretAuth
  • Do not change any other fields.
  • Select the register button to complete creating the app registration.

When the application registration has been completed successfully you are automatically taken to the app registration. At this time copy the Application (Client) ID as this will be required for further operations.

To enable the application registration to perform work API permissions for Microsoft Graph must be added. To add api permissions:

  • Under Manage -> API Permissions
  • Select the add permission button.
  • Select Microsoft Graph
  • Select Application Permissions for the application permission type
  • In the search box type the permission that you are looking for.
    • For example, if modifying a domain type Domain.ReadWrite.All
    • In the permissions list this searches for all relevant permissions.
    • In this example you would expand domains and select Domain.ReadWrite.All
  • Select the add permission button when all of the permissions have been added that are required for your graph work.

When permissions have been added to an app registration, they do not become active until someone with permissions to grant consent provides consent. On the API permissions page is an option “Grant Consent for TenantName”. It is important that after assigning API permissions this option is selected to grant consent and active the permissions.

The final step in the process is to create the client secret. To create the client secret:

  • Select Manage -> Certificates & Secrets
  • Select New client secret
  • In the description I recommend something intuitive – for example – “MicrosoftGraphClientSecret”
  • Adjust the expiration to an acceptable time limit – I recommend the default of 180 days.
  • Select the Add button to complete the client secret.

The client secret is now displayed. There is a copy button to the right of the VALUE field. Select copy and save this value in another location. This will be your only opportunity to see the value of the secret created.

***WARNING*** Treat this value as you would any privileged account password. This value combined with the application ID will allow any administrator access to this application and all permissions assigned.

To connect to Microsoft Graph utilizing client secret authentication:

#Ensure that all graph modules are up to date.
#Note that this process can take an extended period of time depending on the number of graph modules installed
Get-InstalledModule Microosft.Graph.* | update-Module -force -confirm:$FALSE

#The variables referenced below will require you to fill in the blanks with information obtained in previous steps.
#Define the environment you are connecting to.
#Global / World Wide = Global
#GCC High = USGov
#DOD = USGovDOD
#China = China
$environmentName = Global
#Obtain the azure tenant ID where you will be authentication and use it here.
$tenantID = "AzureTenantIDGUID"
#Set the application ID of the app registration that was copied earlier.
$appID = "YourAppRegistrationID"
#Set the client secret
$clientSecret = "YourAppClientSecret"
#Establish the client secret password.
$securePassword = ConvertTo-SecureString -string $clientSecret -asPlainText -force
#Establih the secret credential.
$clientSecretCredential = New-Object -typeName System.Management.Automation.PSCredential -argumentList $appID,$securePassword
#Create the connection to Microsoft Graph
connect-MGGraph -environmentName $environmentName -tenantID $tenantID -clientSecretCredential $clientSecretCredential

If the connection is successful, you are now using Microsoft Graph with client secret authentication and with the permissions granted to the app registration utilized.

Domain Authentication, User Settings, and Entra PasswordPolicies

PasswordPolicies in Microsoft EntraID allow administrators to control the application of password expiration or strong password requirements across their M365 tenant. In this article I want to explore PasswordPolicies configurations and how settings interact with authentication types, domains, and individual users.

Background – PasswordPolices Management and Domains

Microsoft Entra ID password expiration is a per domain setting. Each registered and verified root domain can have unique password expiration policies applied to them, while sub-domains will inherit the password policy from its parent. The settings that control password expiration are PasswordNotificationWindowInDays and PasswordValidityPeriodInDays.

Tenant Defaults

In Microsoft 365 the default settings for all domains are that the PasswordNotificationWindowInDays and PasswordValidityPeriodInDays is NULL meaning password expiration is not enabled for tenants created after 2021.

https://learn.microsoft.com/en-us/microsoft-365/admin/manage/set-password-expiration-policy?view=o365-worldwide

As the admin, you can make user passwords expire after a certain number of days, or set passwords to never expire. By default, passwords are set to never expire for your organization.

Using Get-MGDomain the password expiration settings can be verified:

PS C:\> get-mgDomain -DomainId domain.net | select-object PasswordNotificationWindowInDays,PasswordValidityPeriodInDays | fl


PasswordNotificationWindowInDays :
PasswordValidityPeriodInDays     :

This also corresponds to the password expiration settings in the Microsoft 365 Admin Center showing NULL values.

If your tenant was created prior to 2021 your password expiration period by default is 90 days. Tenants created after 2021 have passwords set to never expire by default regardless of the checkbox in the M365 Admin Center password expiration policy user interface.

https://learn.microsoft.com/en-us/entra/identity/authentication/concept-sspr-policy#password-policies-that-only-apply-to-cloud-user-accounts

Default value: No expiration. If the tenant was created before 2021, it has a 90 day expiration value by default. You can check current policy with Get-MgDomain.
The value is configurable by using the Update-MgDomain cmdlet from the Microsoft Graph module for PowerShell.

Enabling password expiration in the M365 Admin Center

The M365 admin center has a single setting to manage the password expiration policy. This setting does not distinguish between domains and modification of these settings triggers changes across all domains registered in Microsoft 365.

Setting the property “days before passwords expire” adjusts the PasswordValidityPeriodInDays across all eligable domains.

Using Get-MGDomain the password expiration settings can be verified.

Id                               : domain.com
AuthenticationType               : Federated
IsDefault                        : False
PasswordNotificationWindowInDays :
PasswordValidityPeriodInDays     :

Id                               : domain.onmicrosoft.com
AuthenticationType               : Managed
IsDefault                        : True
PasswordNotificationWindowInDays :
PasswordValidityPeriodInDays     :

Id                               : domain.net
AuthenticationType               : Managed
IsDefault                        : False
PasswordNotificationWindowInDays : 30
PasswordValidityPeriodInDays     : 90

Id                               : subdomain.domain.net
AuthenticationType               : Managed
IsDefault                        : False
PasswordNotificationWindowInDays :
PasswordValidityPeriodInDays     :

Id                               : domain2.onmicrosoft.com
AuthenticationType               : Managed
IsDefault                        : False
PasswordNotificationWindowInDays : 30
PasswordValidityPeriodInDays     : 90

Id                               : domain.mail.onmicrosoft.com
AuthenticationType               : Managed
IsDefault                        : False
PasswordNotificationWindowInDays : 30
PasswordValidityPeriodInDays     : 90

Enabling password expiration in the M365 Admin Center – Domain Exceptions

When setting a password expiration window in the M365 Admin Center certain domains are excluded from having their settings automatically set. These include

  • Domains that are Federated for authentication
  • Any domains with the default flag set
  • Any sub domains where the root domain property is set
  • New domains added after the password expiration policy was enabled
  • Domains converted from federated to managed authentication

Federated Domain

Domains that are federated for authentication do not support password expiration in Microsoft 365. All determinants for password expiration are made through the federated authentication process. For example, users federated through Active Directory Federation Services and utilizing Active Directory for authentication would have their passwords expired by on-premises Active Directory and rely on ADFS to handle the password expiration.

Get-MGDomain -domainID domain.com | select-object ID,AuthenticationType,IsDefault,PasswordNotificationWindowInDays,PasswordValidityPeriodInDays

Id                               : domain.com
AuthenticationType               : Federated
IsDefault                        : False
PasswordNotificationWindowInDays :
PasswordValidityPeriodInDays     :

Default Domain

The default domain serves as the domain name utilized on Microsoft 365 objects when no domain is specified or no custom domain is registered.

Get-MGDomain -domainID domain.onmicrosoft.com | select-object ID,AuthenticationType,IsDefault,PasswordNotificationWindowInDays,PasswordValidityPeriodInDays

Id                               : domain.onmicrosoft.com
AuthenticationType               : Managed
IsDefault                        : True
PasswordNotificationWindowInDays :
PasswordValidityPeriodInDays     :

Root Domain

When a subdomain is added to Microsoft 365 where the root domain is already present and registered, a property on the sub-domain is automatically set called root domain. This automatically inherits authentication and password expiration settings that are set on the root domain. If custom authentication or password expiration settings are required for child domains, the root domain property must be cleared.

Get-MGDomain -domainID subdomain.domain.net | select-object ID,AuthenticationType,IsDefault,PasswordNotificationWindowInDays,PasswordValidityPeriodInDays

Id                               : subdomain.domain.net
AuthenticationType               : Managed
IsDefault                        : False
PasswordNotificationWindowInDays :
PasswordValidityPeriodInDays     :

Get-MgDomainRootDomain -DomainId subdomain.domain.net | select-object ID,AuthenticationType,IsDefault,PasswordNotificationWindowInDays,PasswordValidityPeriodInDays | fl

Id                               : domain.net
AuthenticationType               : Managed
IsDefault                        : False
PasswordNotificationWindowInDays : 30
PasswordValidityPeriodInDays     : 90

Domains Added after adjusting settings in the M365 Admin Center

If a domain is added after the expiration period is established in the M365 Admin Center, these domains do not get the PasswordNotificationWindowInDays automatically set.

new-mgDomain -Id newdomain.net

Get-MgDomain -DomainId newdomain.net | select-object ID,AuthenticationType,IsDefault,PasswordNotificationWindowInDays,PasswordValidityPeriodInDays | fl


Id                               : newdomain.net
AuthenticationType               : Managed
IsDefault                        : False
PasswordNotificationWindowInDays :
PasswordValidityPeriodInDays     :

Domains Converted from Federated Authentication to Managed Authentication

In M365 we allow administrators to change the authentication type of a domain from Federated Authentication to Managed Authentication. When setting the password expiration time in the M365 Admin Center any domains that are federated are skipped and the settings are not applied. Additionally, when a domain authentication type is converted from Federated back to Managed, the password expiration settings are not automatically applied.

Get-MGDomain -domainID domain.com | select-object ID,AuthenticationType,IsDefault,PasswordNotificationWindowInDays,PasswordValidityPeriodInDays

Id                               : domain.com
AuthenticationType               : Federated
IsDefault                        : False
PasswordNotificationWindowInDays :
PasswordValidityPeriodInDays     :

Update-MgDomain -DomainId domain.com -AuthenticationType Managed

Get-MgDomain -DomainId domain.com | select-object ID,AuthenticationType,IsDefault,PasswordNotificationWindowInDays,PasswordValidityPeriodInDays | fl


Id                               : domain.com
AuthenticationType               : Managed
IsDefault                        : False
PasswordNotificationWindowInDays :
PasswordValidityPeriodInDays     :

Setting passwords to never expire in the M365 Admin Center

It is Microsoft’s recommendation that customers disable password expiration in Microsoft 365.

https://learn.microsoft.com/en-us/microsoft-365/admin/manage/set-password-expiration-policy?view=o365-worldwide

Current research strongly indicates that mandated password changes do more harm than good. They drive users to choose weaker passwords, reuse passwords, or update old passwords in ways that are easily guessed by hackers. We recommend enabling multi-factor authentication. To learn more about password policy, check out Password policy recommendations.

If a tenant is in the default state, the PasswordValidityPeriodInDays is NULL demonstrating that password expiration is disabled. When selecting the option “Set password to never expire (recommended)” the value 2147483647 is stamped on each domain where password expiration may be enabled. The value 2147483647 is 5883516.8410959 years, effectively disabling any password expiration prompts. (This also happens to be the max value that PasswordValidityPeriodInDays can be set to.)

Get-MgDomain -DomainId domain.net | select-object ID,AuthenticationType,IsDefault,PasswordNotificationWindowInDays,PasswordValidityPeriodInDays | fl


Id                               : domain.net
AuthenticationType               : Managed
IsDefault                        : False
PasswordNotificationWindowInDays : 30
PasswordValidityPeriodInDays     : 2147483647

Setting password expiration using Microsoft Graph

Microsoft Graph provides administrators with the ability to manage password expiration at a more granular level. The command Update-MGDomain allows specifying a custom expiration period on an individual domain.

#Note:  Password notification window in days currently does not do anything but must be specified when running the command.  Users are no longer notified of pending password expiration in Microsoft 365 interfaces.

Update-MgDomain -DomainId domain.net -PasswordValidityPeriodInDays "45" -PasswordNotificationWindowInDays "15"

Microsoft Graph does not allow you to update the settings on domain that is enabled for federated authentication following the same rules as the M365 Admin Center.

Update-MgDomain -DomainId domain.com -PasswordValidityPeriodInDays "45" -PasswordNotificationWindowInDays "15"
Update-MgDomain : Domain operation is not allowed.

Microsoft graph will not allow you to update the password expiration days on a subdomain where root domain is populated.

Update-MgDomain -DomainId subdomain.e-domain.net -PasswordValidityPeriodInDays "45" -PasswordNotificationWindowInDays "15"
Update-MgDomain : Domain operation is not allowed.

Microsoft graph will allow you to set a password expiration days on the default domain even though the M365 Admin Center skips this domain.

Background – Password Policies Management and Users

In the previous section we introduced the settings on domains for password expiration. Every user that is provisioned in Microsoft 365 has a user principal name which incorporates one of the verified domains. The user’s domain settings determine the password policy that is applied to the user in conjunctions with the user’s individual password policy setting.

It may become necessary to manage the password policies of individual users. For example, you may have a service account tied to a domain where password expiration is enabled but you do not want the password to expire on that account. Microsoft Graph provides administrators with the ability to manage a user’s password policy settings through the Update-MGUser command. The ability to run this command on an individual user depends on the domain’s authentication type. Let’s explore how a domain’s authentication type influences the ability to manage a user’s password policies.

CloudPasswordPolicyForPasswordSyncedUsersEnabled

In some installations administrators wish to have the Microsoft 365 password expire at or near the same time as the on-premises Active Directory password expires. By default, the password expiration time is not a value that synchronizes between Active Directory and Microsoft 365.

Directory synchronization provides an optional feature that administrators may enabled named CloudPasswordPolicyForPasswordSyncedUsersEnabled.

https://learn.microsoft.com/en-us/entra/identity/hybrid/connect/how-to-connect-password-hash-synchronization

When the CloudPasswordPolicyForPasswordSyncedUsersEnabled is set to false, any directory synchronized user automatically has their password policies set to DisablePasswordExpiration. We will explore later how this can be modified for individual users based on the domain’s authentication type.

When this feature is enabled, the following changes occur:

New Users

  • All new users synchronized after the feature is enabled will have the PasswordPolicies attribute not set. (NULL = NONE).

Previously Synchronized Users

  • The user changes their password on premises due to password expiration or another password change requirement.
  • Password hash synchronization writes the password hash to Entra ID.
  • The users default value of DisablePasswordExpiration is now re-written to None
    • Note: Enabling this feature does not convert all users to expire passwords, the conversion only occurs as a result of on-premises password change.
  • Microsoft 365 will not evaluate the last password change timestamp and the expiration period and prompt the user for password changes when the passwords have expired.

It is extremely important to note that the password expiration period in Microsoft 365 is always evaluated against the last password change timestamp. There are several operations that could occur at any time that may reset user’s password in EntraID without any password change operation occuring on premises. For example, if a new Entra Connect server is introduced and password hash synchronization is enabled, all password hashes are synchronized effectively changing the expiration date between Active Directory and EntraID.

Default State: Password Policies Attribute

The password polices attribute may have a different default state depending on whether the user is a cloud only user or if the user is directory synchronized. Additionally, if the user is directory synchronized the default password policies state may change depending on the dir sync features enabled.

The password policies attribute has a value of NULL, None, or DisablePasswordExpiration. NULL or NONE are equivalent and will result in the cloud password expiring for managed domains based on the domains settings. The value DisablePasswordExpiration will prevent the users account from expiring the password based on the managed domains settings. If a password policy is set to DisablePasswordExpiration and later the password should expire, setting the password policies to NONE will expire the user’s password.

It may be possible that a user’s password policies attribute differs from the states outlined below. If the domain was managed, password policies were adjusted on users, and then the domain was converted to federated the password policies do not change. If the user was utilizing a federated domain UPN suffix and was then changed to a managed domain suffix the password policies attribute could be edited. This value will not change states as the user’s UPN suffix changes between federated and managed domains or managed domains to federated domains.

Cloud Only User / Managed Domain

PS C:\> get-mgUser -UserId CloudOnlyManaged@domain.net -Property PasswordPolicies | select -ExpandProperty PasswordPolicies
PS C:\>

Directory Synchronized User / CloudPasswordPolicyForPasswordSyncedUsersEnabled FALSE / Federated Domain

get-mgUser -UserId FederatedUser1@domain.com -Property PasswordPolicies | select -ExpandProperty PasswordPolicies
DisablePasswordExpiration

Directory Synchronized User / CloudPasswordPolicyForPasswordSyncedUsersEnabled FALSE / Managed Domain

get-mgUser -UserId ManagedUser1@e-mcmichael.net -Property PasswordPolicies | select -ExpandProperty PasswordPolicies
DisablePasswordExpiration

Directory Synchronized User / CloudPasswordPolicyForPasswordSyncedUsersEnabled TRUE / Federated Domain (Post Password Change)

get-mgUser -UserId FederatedUser1@domain.com -Property PasswordPolicies | select -ExpandProperty PasswordPolicies
None

Directory Synchronized User / CloudPasswordPolicyForPasswordSynchronizedUsersEnabled TRUE / Managed Domain (Post Password Change)

get-mgUser -UserId ManagedUser1@domain.net -Property PasswordPolicies | select -ExpandProperty PasswordPolicies
None

Directory Synchronized NEW User / CloudPasswordPolicyForPasswordSynchronizedUsersEnabled TRUE / Managed Domain

get-mgUser -UserId ManagedUser3@e-mcmichael.net -Property PasswordPolicies | select -ExpandProperty PasswordPolicies
PS C:\>

Directory Synchronized NEW User / CloudPasswordPolicyForPasswordSynchronizedUsersEnabled TRUE / Federated Domain

get-mgUser -UserId FederatedUser3@e-mcmichael.com -Property PasswordPolicies | select -ExpandProperty PasswordPolicies
PS C:\>

Setting PasswordPolicies and Federated Domain Authentication (CloudPasswordPolicyForPasswordSyncedUsersEnabled FALSE)

When a user is created with a domain that utilizes federated authentication the ability to manage the PasswordPolicies attribute is limited.

When using Update-MGUser to set the PasswordPolicies attribute to NONE an error is encountered:

Update-MgUser -UserId FederatedUser1@domain.com -PasswordPolicies None
Update-MgUser : Unable to update the specified properties for on-premises mastered Directory Sync objects or objects
currently undergoing migration.

We note that update-MGUser is not supported on federated domains in the following article.

https://learn.microsoft.com/en-us/entra/identity/hybrid/connect/how-to-connect-password-hash-synchronization

 Note

The Update-MgUser PowerShell command doesn't work on federated domains.

The error returned though is misleading. Based on the error provided one would believe that the passwordPolicies attributes is locked because the object is enabled for directory synchronization. This is not correct the actual error is because the user is set with a domain that is enabled for federated authentication. At the time of publishing our internal engineering groups are reviewing this error and working to make it more accurately reflect the underlying reason.

When using Update-MGUser to set the PasswordPolicies attribute to DisablePasswordExpiration the command completes successfully although in this example it is setting a value that already exists therefore performing no actions on the user object. (See Why did certain scenarios for the federated users work if the commands if update-MGUser does not work on federated domains? below for further explanation.)

Update-MgUser -UserId FederatedUser1@domain.com -PasswordPolicies DisablePasswordExpiration

Setting Password Policies, Federated Domain Authentication, with Managed Authentication Staged Rollout (CloudPasswordPolicyForPasswordSyncedUsersEnabled FALSE)

EntraID allows administrators to test managed authentication without fully converting a federated domain. This feature is known as Managed Authentication Staged Rollout. For more information on managed authentication staged rollout please review: https://learn.microsoft.com/en-us/entra/identity/hybrid/connect/how-to-connect-staged-rollout

When a user is enabled for staged rollout certain password expiration policies apply to the user. Attempting to use Update-MGUser to set the password policies to NONE fails for the same reason as a Federated user.

Update-MgUser -UserId FederatedUser2@domain.com -PasswordPolicies None
Update-MgUser : Unable to update the specified properties for on-premises mastered Directory Sync objects or objects
currently undergoing migration.

Setting Password Policies and Managed Domain Authentication (CloudPasswordPolicyForPasswordSyncedUsersEnabled FALSE)

When a user is created with a domain that utilizes managed authentication the password policies attribute can be fully managed. Here are some examples:

Cloud Only User / Managed Authentication

get-mgUser -UserId CloudOnlyManaged@domain.net -Property PasswordPolicies | select -ExpandProperty PasswordPolicies

#Null Return#

update-mgUser -UserId CloudOnlyManaged@domain.net -PasswordPolicies "DisablePasswordExpiration"

get-mgUser -UserId CloudOnlyManaged@domain.net -Property PasswordPolicies | select -ExpandProperty PasswordPolicies
DisablePasswordExpiration

Directory Synchronized User / CloudPasswordPolicyForPasswordSyncedUsersEnabled FALSE/ Managed Authentication

get-mgUser -UserId ManagedUser1@domain.net -Property PasswordPolicies | select -ExpandProperty PasswordPolicies
DisablePasswordExpiration

update-mgUser -UserId ManagedUser1@domain.net -PasswordPolicies None

get-mgUser -UserId ManagedUser1@domain.net -Property PasswordPolicies | select -ExpandProperty PasswordPolicies
None

Setting PasswordPolicies and Federated Domain Authentication (CloudPasswordPolicyForPasswordSyncedUsersEnabled TRUE)

The results are the same as CloudPasswordPolicyForPasswordSyncedUsersEnabled FALSE.

If attempting to set the value to NONE the command succeeds as the value was already set to NONE as a result of the feature enablement and subsequent password reset.

Update-MgUser -UserId FederatedUser1@domain.com -PasswordPolicies None

If attempting to set the value to DisablePasswordExpiration the command fails.

Update-MgUser -UserId FederatedUser1@domain.com -PasswordPolicies DisablePasswordExpiration
Update-MgUser : Unable to update the specified properties for on-premises mastered Directory Sync objects or objects
currently undergoing migration.

Setting Password Policies, Federated Domain Authentication, with Managed Authentication Staged Rollout (CloudPasswordPolicyForPasswordSyncedUsersEnabled TRUE)

The results are the same as “Setting PasswordPolicies and Federated Domain Authentication (CloudPasswordPolicyForPasswordSyncedUsersEnabled TRUE)” outlined in the previous section.

Setting Password Policies and Managed Domain Authentication (CloudPasswordPolicyForPasswordSyncedUsersEnabled TRUE)

The results are the same as “Setting Password Policies and Managed Domain Authentication (CloudPasswordPolicyForPasswordSyncedUsersEnabled FALSE)” outlined in a previous section.

get-mgUser -UserId ManagedUser3@domain.net -Property PasswordPolicies | select -ExpandProperty PasswordPolicies

update-MGUser -UserId ManagedUser3@domain.net -PasswordPolicies "DisablePasswordExpiration"

get-mgUser -UserId ManagedUser3@domain.net -Property PasswordPolicies | select -ExpandProperty PasswordPolicies
DisablePasswordExpiration

update-MGUser -UserId ManagedUser3@domain.net -PasswordPolicies "None"

get-mgUser -UserId ManagedUser3@domain.net -Property PasswordPolicies | select -ExpandProperty PasswordPolicies
None

Why did certain scenarios for the federated users work if the commands if update-MGUser does not work on federated domains?

In the federated scenario the only time that the command did not error was when it was setting a value that already existed on the object. For example, DisablePasswordExpiration was already set to running Update-MGUser -passwordPolicies DisablePasswordExpiration succeeded. When a graph call is made to set an attribute that already exists on the object with the same value the underlying API is not invoked. The call is then “successful” even though no attempt to change the value occurred.

Quick Reference Table

  • Cloud Only User
    • Managed Authentication
      • Default Value: NULL
      • Update-MGUser Accepted Values: DisablePasswordExpiration or NONE
  • Directory Synchronized User
    • CloudPasswordPolicyForPasswordSyncedUsersEnabled FALSE
      • Managed Authentication
        • Default Value: DisablePasswordExpiration
          • Update-MGUser Accepted Values: DisablePasswordExpiration or NONE
      • Federated Authentication
        • Default Value: DisablePasswordExpiration
        • Update-MGUser Accepted Values: No accepted values
    • CloudPasswordPolicyForPasswordSyncedUsersEnabled TRUE
      • Default Values All Authentication Types
        • Prior to password hash synchronization: DisablePasswordExpiration or NONE
        • Post password hash synchronization: None
        • New account creation post enablement: NULL
      • Managed Authentication
        • Update-MGUser Accepted Values: DisablePasswordExpiration or None
      • Federated Authentication
        • Update-MGUser Accepted Values: No accepted values

References

https://docs.azure.cn/en-us/entra/identity/authentication/concept-password-ban-bad-combined-policy

https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.users/update-mguser?view=graph-powershell-1.0

https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.identity.directorymanagement/get-mgdomain?view=graph-powershell-1.0

https://learn.microsoft.com/en-us/microsoft-365/admin/manage/set-password-expiration-policy?view=o365-worldwide

https://learn.microsoft.com/en-us/entra/identity/hybrid/connect/how-to-connect-password-hash-synchronization

https://learn.microsoft.com/en-us/microsoft-365/admin/add-users/set-password-to-never-expire?view=o365-worldwide

https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.users/get-mguser?view=graph-powershell-1.0

https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.identity.directorymanagement/update-mgdomain?view=graph-powershell-1.0

Thoughts on Microsoft 365 and Domain Federation

This week I was working with a customer that converted a domain from federated authentication to managed authentication for the purposes of testing the transition. This is not an uncommon occurrence, as many customers that utilized federated authentication often combine it with Password Hash Synchronization to provide a measure of redundancy for authentication in the event that their federated authentication provider is no longer available.

At the time that I was engaged the customer was raising an escalation on how to convert the domain back to federated authentication. To convert the domain to managed authentication the customer utilized Update-MGDomain -domainID “Domain” -authenticationType “Managed”. This is an appropriate method to convert the domain from federated to managed authentication. The customer was attempting to use Update-MGDomain -domainID “Domain” -authenticationType “Federated” and received the following error

update-MGDomain -DomainId "domain.net" -AuthenticationType "Federated"

update-MGDomain : Changing authenticationType from Managed to Federated is currently not supported.
Status: 400 (BadRequest)

The documentation for Update-MGDomain does list “Federated” as an authentication type.

https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.identity.directorymanagement/update-mgdomain?view=graph-powershell-1.0

Technically this is correct, the command does evaluate and return an authentication type of Federated but cannot be utilized on its own to convert a domain from managed to federated authentication. Federating a domain requires information and attributes beyond what Update-MGDomain accepts.

This leads to a conversation regarding the “supported” method to federate a domain with Microsoft 365. Our response was to utilize Entra Connect and the integration with ADFS. There is not though a specific document that says this is the “supported” method. If there’s no documentation how do we come to conclusion this is the “supported” method.

Prior to the integration of ADFS with Entra Connect customers had access to a command convert-MSOLDomainToFederated. This command was designed to be run on ADFS and not only converted the domain to federation in EntraID by stamping the appropriate federation attributes but also created a generic federation trust within ADFS. In many cases the generic federation trust that was created did not reflect the same settings that customers utilized for synchronization, such as sourceAnchor, UPN, device registration etc. In the most simplest scenarios, it worked fine but more advanced scenarios it did not support.

The Convert-MSOLDomainToFederated has since been deprecated and has no equivalent graph replacement. This leaves Entra Connect an attractive alternative to federating a domain. Using Entra Connect has several benefits over other options. When using Entra Connect the ADFS farm is registered with the installation. As changes to configurations occur Entra Connect will automatically update the properties of the federation trust. It automates the addition of ADFS and WAP servers to existing farms and supports full certificate management capabilities. One of the drawbacks is that converting a domain to federated from managed authentication is a per domain process. It works well for single domains but not necessarily for 100s of domains.

Microsoft Graph supports the ability to convert a domain to federated authentication through the new-MGDomainFederationConfiguration command.

https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.identity.directorymanagement/new-mgdomainfederationconfiguration?view=graph-powershell-1.0

As long as the administrator is aware of the appropriate values this command can be utilized to convert the domain. This also assumes that the federation trust that exists in ADFS has been created in a manner that fully supports Microsoft 365.

What is the recommendation if we have to convert to managed authentication in an emergency?

If the ADFS environment, Active Directory, or network infrastructure becomes compromised many customers rely on the conversion to managed authentication to restore access to their cloud services. When password hash synchronization has been enabled this is an easy and quick transition. When the recovery actions have been completed, I recommend utilizing the Entra Connect installation to re-federate a minimum of two domains. This ensures that the federation settings within Microsoft 365 have accurate values and the federation trust in ADFS is created in a manner supporting Microsoft 365. For additional domains Microsoft graph can be utilized to pull the federation settings from the converted domains and other domains federated with New-MGDomainFederationConfiguration.

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.

Insufficient privileges when setting a user license

Set-MGUserLicense allows administrators to utilize Microsoft Graph to set a users licenses. https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.users.actions/set-mguserlicense?view=graph-powershell-1.0 Set-MGUserLicense leverages the user:AssignLicense graph interface to manage the users licenses. https://learn.microsoft.com/en-us/graph/api/user-assignlicense?view=graph-rest-1.0&tabs=http

I recently worked a customer escalation where when executing set-MGUserLicense the following error was noted:

Authorization_RequestDenied,Microsoft.Graph.PowerShell.Cmdlets.SetMgUserLicense_AssignExpanded
Set-MgUserLicense : Insufficient privileges to complete the operation.

Status: 403 (Forbidden)
ErrorCode: Authorization_RequestDenied

When Microsoft Graph returns an insufficient privileges error message this generally means that the permissions scopes required either do not exist on the user or graph application registration running the command. According to the user:AssignLicense interface documentation the minimum required permission is LicenseAssignment.ReadWrite.All and the maximum permissions are User.ReadWrite.All and Directory.ReadWrite.All.

Graph provides a method to review the context of the authentication as well as the scopes authorized using Get-MGContext. https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.authentication/get-mgcontext?view=graph-powershell-1.0. When reviewing the scopes post authentication the following was displayed:

Get-MgContext | Select-Object -ExpandProperty Scopes

DeviceManagementManagedDevices.Read.All
UserAuthenticationMethod.Read.All
User.ReadWrite.All
Policy.ReadWrite.ApplicationConfiguration
Application.ReadWrite.All
Group.Read.All
Directory.ReadWrite.All
Directory.Read.All
User.Read.All
GroupMember.Read.All
DeviceManagementRBAC.Read.All
DeviceManagementManagedDevices.ReadWrite.All
Mail.Send
Organization.Read.All
AuditLog.Read.All
Policy.Read.All
DeviceManagementManagedDevices.PrivilegedOperations.All

The maximum scopes required are present for the graph connection, yet the insufficient privileges error continues. The following command was being utilized to set the users licenses:

Set-MGUserLicense -userID 'ObjectID' -AddLicenses @{SkuId = '38b434d2-a15e-4cde-9a98-e737c75623e1'} -RemoveLicenses @()

When reviewing the SKU ID this SKU ID is associated with a Visio Plan 2 sku. (https://learn.microsoft.com/en-us/entra/identity/users/licensing-service-plan-reference). If you pay close attention to the table, you will see that the product name is Visio Plan 2, but the String ID is Visio_Plan2_Dept. The insufficient privileges is actually a red herring in terms of how we would normally interpret this error. In this case the insufficient privileges are not derived from lacking graph scopes but rather from the fact that this sku cannot be directly assigned to a user. A department sku can only be assigned by the Microsoft License Manager which is a self-service license acquisition process by the user.

To correct the insufficient privileges error the correct Visio Plan 2 sku was specified.

External domain takeover and Microsoft Graph

When attempting to prove ownership of a domain administrators sometimes encounter a scenario where the domain is associated with another tenant. It is possible that the domain is associated with an unmanaged (viral) tenant within Microsoft 365.

We provide documentation to administrators on how to convert the unmanaged tenant to managed and then remove the domain. https://learn.microsoft.com/en-us/entra/identity/users/domains-admin-takeover

The external takeover method relies on Microsoft Graph to complete the process. Unfortunately, the instructions currently contained in our published guidance no longer work. In this post I want to provide updated guidance on how to utilize graph to perform the external takeover method.

In order to preform an external takeover the account or graph application that you are using must have the appropriate rights for domain management. Information on domain verify can the permissions supported may be found here: https://learn.microsoft.com/en-us/graph/api/domain-verify?view=graph-rest-1.0&tabs=http. In this instance Domain.ReadWrite.All are the only permissions supported. Please note that consent may be required in order to add these permissions.

Prior to performing the external admin takeover, the domain will need to be added to the tenant and the appropriate DNS records for validation in place.

To perform the process of the external takeover we will:

  • Connect to Microsoft Graph and specify the minimum permissions required.
  • Create a URL to call domain verify.
  • Create body parameters to include with the domain verify post that force takeover is specified.
  • Invoke the graph method and capture the results.

The code is as follows:

Connect-MGGraph -scopes "Domain.ReadWrite.All"

$domainID = "domainToTakeOver.com"

$uri = "https://graph.microsoft.com/v1.0/domains/$domainID/verify"

$body = @ { forceTakeover = $true } | ConverTo-JSON

$response = Invoke-MGGraphRequest -Method POST -uri $uri -Body $body

$response | ConvertTo-JSON

$response

The response should contain a success or failure message.

Microsoft 365 GCC High Tenants and Missing Domains

Microsoft 365 provides a Government Community Cloud High offering for the U.S Government, contractors, and other organizations that qualify. As with our commercial or worldwide tenants many customers elect to add a vanity or custom domain to their M365 GCC-H tenant.

In a typical commercial tenant the vanity domains are managed through the domains tab of the Microsoft 365 Administration Center. Here is a sample instructions for commercial customers – https://learn.microsoft.com/en-us/microsoft-365/admin/setup/add-domain?view=o365-worldwide.

In the M365 Admin Center for GCC-H tenants the domains tab is missing. This is currently by design. When adding, verifying, or removing domains in a GCC-H tenant Microsoft Graph must be utilized. In this post I want to outline the manual steps for adding and verifying a domain in a GCC-H tenant.

Graph commands often span multiple PowerShell modules in order to achieve their work. Prior to running any graph commands, I recommend that all associated graph PowerShell modules be updated. This ensures that you do not have a mismatch, for example, between the authentication module and the identity module. Note: This process can take a long time depending on the number of graph modules you have installed.

Get-InstalledModule Microsoft.Graph.* | Update-InstalledModule -force -confirm:$FALSE

If you are new to graph or to ensure that you have the module necessary to perform domain work, the Microsoft.Graph.Identity.DirectoryManagement module must be installed.

Install-Module Microsoft.Graph.Identity.DirectoryManagement

When adding and verifying the domain there are three graph commands that will be utilized.

In order to run the following commands, the graph permission Domain.ReadWrite.All must either be consented to on the individual account or for the entire organization. Note: If you do not have global administrator privileges you will not be able to provide consent for graph scopes. It may be necessary for another administrator to perform the consent on your behalf.

The process to add a domain via graph:

$tenantID = "Entra Tenant ID for the GCC H" organization" #User Supplied
$scopes = "Domain.ReadWrite.All"
$environment = "USGov"

Connect-MgGraph -Environment $environment -TenantId $tenantID -Scopes $scopes
$domainID = "contoso.com"

$params = @{
	id = $domainID
}

new-MGDomain -bodyParameter $params

If the new-MGDomain command is successful the following return is expected.

Id          AuthenticationType AvailabilityStatus IsAdminManaged IsDefault IsInitial IsRoot IsVerified Manufacturer Mod
                                                                                                                    el
--          ------------------ ------------------ -------------- --------- --------- ------ ---------- ------------ ---
contoso.com Managed                               True           False     False     False  False

Once the domain has been added with new-MGDomain the DNS verification records are obtained. In the below example for the record type TXT you would use the value MS=ms41165256 or for the record type MX you would use ms41165256.msv1.invalid with preference 32767.

$dnsRecords = get-MGDomainVerificationDNSRecord -domainID $domainID
$dnsRecords | fl

Id                   : aceff52c-06a5-447f-ac5f-256ad243cc5c
IsOptional           : False
Label                : contoso.com
RecordType           : Txt
SupportedService     : Email
Ttl                  : 3600
AdditionalProperties : {[@odata.type, #microsoft.graph.domainDnsTxtRecord], [text, MS=ms41165256]}

Id                   : 5fbde38c-0865-497f-82b1-126f596bcee9
IsOptional           : False
Label                : contoso.com
RecordType           : Mx
SupportedService     : Email
Ttl                  : 3600
AdditionalProperties : {[@odata.type, #microsoft.graph.domainDnsMxRecord], [mailExchange, ms41165256.msv1.invalid],
                       [preference, 32767]}

These records are inserted into your commercial DNS provider for the domain you are adding. Please note that it can take several minutes for your new public DNS records to be accessible. You may consider using a third party tool or nslookup to validate that the DNS records are available prior to proceeding with the next step.

confirm-MGDomain -domainID $domainID

If the confirm is successful, the domain will be validated and available in the M365 GCC-H tenant.

To complete the integration of custom domains with Microsoft 365 services you have to publish additional DNS records. For GCC-H tenants these DNS records must be calculated manually. You can find resources for calculating these DNS records at https://learn.microsoft.com/en-us/microsoft-365/enterprise/dns-records-for-office-365-gcc-high?view=o365-worldwide.

Converting Direct Assigned Licenses to Group Assigned Licenses

In Microsoft 365 users may be assigned the same license both direct and through group-based licensing.  For organizations that are converting to group-based licensing this is not an uncommon scenario to ensure that users do not lose access to the service during the transition to group-based assignments.  When a license is assigned to user both direct and through group-based licensing only a single license is consumed. 

Managing direct and group based license assignment in the M365 Admin Center…

In the M365 Admin Center if a group-based license is assigned to the user the option to manage that individual license is greyed out.  A note is displayed “this is inherited by group-based licensing and can’t be changed here.  Manage group-based licenses from the Groups pivot in license details.”  If the user also had a direct assigned license this prevents the removal of the direct assigned license from the users’ properties.

The M365 Admin Center also allows expansion of individual licenses and displaying the users that have the license assigned.  Administrators also have the ability to assign and unassign licenses from this view. 

If you select the user that has both a direct and group-based license assignment and select unassign license an error is displayed.

When a user has both a direct and group-based license assignment the directly assigned license cannot be managed in the M365 Admin Center.

Using Graph to Manage Direct License Assignment

Get-MGUser provides the properties AssignedLicenses and LicenseAssignmentStates.  The license assignment states provides information regarding the licenses assigned to the user and how those licenses are assigned.

PS C:\> Get-MgUser -UserId "!LicenseTestUser2@domain" -Property AssignedLicenses, LicenseAssignmentStates, DisplayName | Select-Object DisplayName, AssignedLicenses -ExpandProperty LicenseAssignmentStates  | fl


DisplayName          : !LicenseTestUser2
AssignedLicenses     : {314c4481-f395-4525-be8b-2ec4bb1e9d91}
AssignedByGroup      : 519fe352-6f2d-4022-973e-ad72c5bcf63d
DisabledPlans        : {882e1d05-acd1-4ccb-8708-6ee03664b117}
Error                : None
LastUpdatedDateTime  : 2/3/2025 1:08:24 PM
SkuId                : 314c4481-f395-4525-be8b-2ec4bb1e9d91
State                : Active
AdditionalProperties : {}

DisplayName          : !LicenseTestUser2
AssignedLicenses     : {314c4481-f395-4525-be8b-2ec4bb1e9d91}
AssignedByGroup      :
DisabledPlans        : {}
Error                : None
LastUpdatedDateTime  : 2/3/2025 1:05:23 PM
SkuId                : 314c4481-f395-4525-be8b-2ec4bb1e9d91
State                : Active
AdditionalProperties : {}

In this case the user has two license assignments for the license 314c4481-f395-4525-be8b-2ec4bb1e9d91.  The first state has AssignedByGroup 519fe352-6f2d-4022-973e-ad72c5bcf63d and the second state shows no AssignedByGroup.  This demonstrates that the user has both a group and direct license assignment. 

With the inability to manage the direct assigned license in the M365 Admin Center if the desire is to remove the direct assigned license graph must be utilized.  Here is an example of removing the direct assigned license.  (See Using graph to modify group based licenses… | TIMMCMIC for how to build the body parameters section.)

#Establish the body parameters hash table.
$params = @{}
#Build the add licenses array
$addLicenses = @()

#Build the remove licenses array
$removeLicenses = @()
$disabledPlans = @()
$removeLicenses += "314c4481-f395-4525-be8b-2ec4bb1e9d91"
$params = @{"AddLicenses" = $addLicenses ; "RemoveLicenses" = $removeLicenses}
Set-MgUserLicense -UserId "!LicenseTestUser2@domain" -BodyParameter $params

When the command completes successfully repeating the get displays the following results.

PS C:\> Get-MgUser -UserId "!LicenseTestUser2@domain" -Property AssignedLicenses, LicenseAssignmentStates, DisplayName | Select-Object DisplayName, AssignedLicenses -ExpandProperty LicenseAssignmentStates  | fl


DisplayName          : !LicenseTestUser2
AssignedLicenses     : {314c4481-f395-4525-be8b-2ec4bb1e9d91}
AssignedByGroup      : 519fe352-6f2d-4022-973e-ad72c5bcf63d
DisabledPlans        : {882e1d05-acd1-4ccb-8708-6ee03664b117}
Error                : None
LastUpdatedDateTime  : 2/3/2025 1:08:24 PM
SkuId                : 314c4481-f395-4525-be8b-2ec4bb1e9d91
State                : Active
AdditionalProperties : {}

This output confirms that the user has a single license assignment state and that the license is assigned by a group.

Summary

When a direct and group-based license exists on the user the direct license assignment cannot be managed in the M365 Admin Center.  To migrate to group-based licensing the direct assigned license can be removed using Microsoft Graph. 

Group Based Licensing – an all or none proposition…

Group based licensing provides a great deal of flexibility for administrators to automate the license assignment process.  The move from utilizing the Entra portal to the Microsoft 365 Admin Center for group based licensing has brought about several changes not only in the way license are assigned but also the backend technologies utilized to apply the licenses.

Assigning licenses to groups…

The Microsoft 365 Admin Center enables the application of groups to licenses.  Groups can be assigned to multiple licenses at the same time.  I would like to refer to this as a license template and think about it these are the licenses that this group needs to apply in order for the application to be successful. 

In this example I have a group !LicenseTest that applies two licenses, the Office 365 A1 for Students and Office 365 A1 for Faculty.  (Note:  In practical terms it would make no sense to use a group to assign both of these licenses but it served the purpose of this blog.)  The license template was created in the M365 Admin Center by adding the group to the licenses.

The same information is available through Microsoft Graph. 

PS C:\> Get-MgGroup -GroupId cb974e33-9030-4dcb-b3e0-8f5ef57f3462 -Property AssignedLicenses | fl AssignedLicenses


AssignedLicenses : {94763226-9b3c-4e75-a931-5c89701abe66, 314c4481-f395-4525-be8b-2ec4bb1e9d91}

Success or Failure…

 As users are added to the group an asynchronous process within Microsoft 365 is responsible for processing all of the group based licensing.  Each license in the M365 Admin center shows the groups assigning the license and the group processing state.  In the example of our test group reviewing the license status shows the following in the M365 Admin Center.

The group processing status shows license assignment failures for both licenses.  To review the errors the group is selected under the license.  For the Office 365 A1 for Students, when selecting the group and reviewing errors, the following is displayed:

The status of the license assignment is “Other” with no further details.  For the Office 365 A1 for Faculty, when selecting the group and reviewing the errors, the following is displayed:

The status of the license assignment is “Not Enough Licenses”.  This error is generally self-explanatory.  At the time the license assignment was attempted there were not enough licenses to apply this license to the user.

When reviewing license assignment failures, the Entra audit logs is also extremely helpful.   Here is a sample audit log entry:

Activity
Date 1/29/2025, 11:59 AM
Activity Type Change user license
Correlation ID c71dad7e-a6e0-46b2-897c-065022231dbb
Category UserManagement
Status failure
Status reason 
Microsoft.Online.Provisioning.SubscriptionManagement.SubscriptionFullException
User Agent
Initiated by (actor)
Type Application
Display Name Microsoft Azure AD Group-Based Licensing
App ID
Service principal ID
Service principal name
Additional Details
Licensing Error Message
Not enough licenses are available to complete this operation. Purchase more licenses or remove unneeded licenses from users and groups for SKU [94763226-9b3c-4e75-a931-5c89701abe66]. Licenses being added: [94763226-9b3c-4e75-a931-5c89701abe66]

Remember earlier when I referred to putting multiple licenses on the group as a “template” this template did not apply successfully.  Under the new architecture if all the licenses that the group covers cannot be applied then no licenses are applied to the users. 

All or none…why?

There are several architectural reasons that this makes sense as well as some practical reasons.  From a practical standpoint customers very often apply licenses together so that users have a consistent functionality and experience.  For example, I recently worked with a customer that applies an F3 license to their users but also adds Exchange Plan 2 standalone for additional mailbox capacity.  If the F3 was provisioned without the associated Exchange Plan 2 the user would not receive the desired mailbox size. 

From an architectural perspective there are now more licenses that have cross dependencies on each other.  Take the example of having a Teams Audio Conferencing Standalone license that requires a teams plan from a Microsoft E3 license.  If the Microsoft E3 failed to apply this would subsequently generate a failure of the Teams Audio Conferencing license. 

The main architectural change though that is driving this is the migration of license assignment functions to the graph interface.  When graph attempts to assign the licenses, it does so not individually but rather overall.  This can be reproduced by attempting to assign the licenses with Microsoft Graph.  Here is an example:

#Establish the body parameters hash table.
$params = @{}

#Build the add licenses array
$addLicenses = @()

#Build the remove licenses array
$removeLicenses = @()

#************************************************************************

#Build the disabled plans for the first license to be added.

$disabledPlans = @()

#Set the skuID for the first license.

$skuID = "94763226-9b3c-4e75-a931-5c89701abe66"

#Build the hash value for the added licenses.

$skuHash = @{"DisabledPlans" = $disabledPlans ; "SkuID" = $skuID}

#Add the skuHash to the array of licenses to add.

$addLicenses += $skuHash

#************************************************************************

#Build the second entry to the add licenses.

#Build the disabled plans for the second license to be added.

$disabledPlans = @()

#Set the skuID for the first license.

$skuID = "314c4481-f395-4525-be8b-2ec4bb1e9d91"

#Build the hash value for the added licenses.

$skuHash = @{"DisabledPlans" = $disabledPlans ; "SkuID" = $skuID}

#Add the skuHash to the array of licenses to add.

$addLicenses += $skuHash

#************************************************************************

#***********************
#Complete the params has table.
#***********************

$params = @{"AddLicenses" = $addLicenses ; "RemoveLicenses" = $removeLicenses}

#***********************
#Set the license on the user.
#***********************

set-mgUserLicense -userID 95b87c6b-680d-4942-8ca9-7dd25cd0c3ed -bodyParameter $params

set-mgUserLicense : Subscription with SKU 94763226-9b3c-4e75-a931-5c89701abe66 does not have any available licenses.
Status: 400 (BadRequest)
ErrorCode: Request_BadRequest
Date: 2025-01-29T17:19:56
Headers:
Transfer-Encoding             : chunked
Vary                          : Accept-Encoding
Strict-Transport-Security     : max-age=31536000
request-id                    : 9f334bdd-c0b6-44e0-a67d-de157e2b69b6
client-request-id             : 6e2f76c8-8b7a-4998-91c9-6986cad1e09d
x-ms-ags-diagnostic           : {"ServerInfo":{"DataCenter":"East
US","Slice":"E","Ring":"5","ScaleUnit":"002","RoleInstance":"BL02EPF0001DE6C"}}
x-ms-resource-unit            : 1
Cache-Control                 : no-cache
Date                          : Wed, 29 Jan 2025 17:19:56 GMT
At line:1 char:1
+ set-mgUserLicense -userID 95b87c6b-680d-4942-8ca9-7dd25cd0c3ed -bodyP ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: ({ UserId = 95b8...ionJsonSchema }:<>f__AnonymousType3`3) [Set-MgUserL
   icense_Assign], Exception
    + FullyQualifiedErrorId : Request_BadRequest,Microsoft.Graph.PowerShell.Cmdlets.SetMgUserLicense_Assign

The attempt to directly assign both licenses at the same time in a single operation failed resulting in the user having no licenses assigned.  If the license assignment attempts are performed individually one will succeed and the other will fail resulting in the user having one of the two desired licenses.

The “other” status…

I often receive calls from customers regarding group based license failures and the “other” status.  The other status generally means that the group is assigning more than one license and another license has failed to apply.  This is sometimes hard to track down in the Microsoft 365 Admin Center as you must select each license that you have and then review the groups assigning those licenses.  There is no interface within the Microsoft 365 Admin Center that shows you all of the licenses assigned to a single group.  The GraphLicenseManager may assist in determining the source of failures.

Using Graph License Manager to review license assignment failures…

The GraphLicenseManager does have the ability to assist in tracking this down without looking at each license.  For more information on installing and using the GraphLicenseManager see the following references:

Using GraphLicenseManager to view group members and reprocess users… | TIMMCMIC

When the graph license manager is installed and configured selecting the “Group License Manager” allows you to visualize the licenses that are assigned to a group. 

The “View Group Info” button displays information about the members of the group as well as any licensing errors associated with the users. 

The errors column summarizes all the errors that are preventing the license assignment to the user and converts the GUIDs associated with Microsoft 365 SKUs to user readable formats. 

•	CountViolation---Office 365 A1 for Faculty---Error
•	Other---Office 365 A1 For Students---Error

Collecting all the errors preventing license assignment makes it efficient for administrators to identify the root cause of the license assignment failure and being a reconciliation.

How to prevent a single license assignment failure from stopping other license assignment…

With the shift in architecture, it is no longer possible to have portions of the group based license apply to a user.  If the group processing the user has four licenses applied to it, all four must be successful.  This was a departure from Entra and legacy processing where a group could assign some of the licenses specified and error on others.  Is there any way to resolve this? 

Resolving this is a matter of rethinking how license assignments are done.  One solution I have utilized uses a single group to start the license assignment but where the licenses are individually applied by other groups.  To keep the single group concept, I encourage the use of Entra dynamic groups utilizing the memberOf filter. 

Configure dynamic membership groups with the memberOf attribute in the Azure portal – Microsoft Entra ID | Microsoft Learn

Using the example that I have used previously a single license group is created !LicenseTest.  This group is where the members will be placed to receive a license.  In the EntraID Portal two dynamic groups are created, one to apply the Office 365 A1 for Faculty and the other to apply the Office 365 A1 for Students. 

Once the dynamic groups have processed their membership, they can be added to the licenses in the M365 Admin Center.  With each of the dynamic groups processing licenses individually any errors that are encountered are limited to just those groups. 

The properties of the individual user that was previously in error confirm the license successfully assigned.

Summary

Group based licensing offers a great deal of flexibility to ensure that users receive the licenses that they should.  When a group assigns multiple licenses all the licenses attached to that group must be assigned successfully in order for any licenses to be assigned.  If it becomes necessary to allow partial license assignment multiple dynamic groups can be utilized to assign individual licenses while using a single group to trigger license assignment.