Tuesday, July 12, 2022

Is my domain name attached to a tenant?

A common issue is verifying whether a domain name is already attached to a Microsoft 365/Azure AD tenant. If you don't check ahead of time, you often find that you can't add a new domain name to a tenant at a critical time during a project.

To check whether a domain name is already in Microsoft 365:

If there is a tenant ID, then it's already registered.

This often gets us to the point where no one knows for sure what tenant and who might be able to log in. It might have been created for volume licensing or a Power BI trial. There's really no way to know.

Instructions on how to get the domain name back:


Monday, May 2, 2022

Mailbox moves between Exchange Online tenants

For the last several years I've been involved mostly in tenant to tenant migration projects. These projects typically include migration of email, teams, and SharePoint. My focus is on email migration.

For migration, there are a number of third party tools such as MigrationWiz (Bit Titan) and On Demand Migration for Email (Quest ODME) that can be used. I don't know all of the details for all tools, but at least in the case of ODME, the migration is copies mailbox data, but doesn't synchronize it. So, if you migrate data from source to target and the user later deleted data from the source, it is not deleted in the target. Occasionally, this is annoying and causes confusion for users.

Microsoft has tenant-to-tenant mailbox moves in Preview. This type of move is a migration batch like moving mailboxes between on-premises Exchange and Exchange Online. In this case, you get true syncing which is a better quality move. It looks like a bit of a pain to setup and manage, but I expect the experience will improve over time.

The documentation claims that mailbox permissions like Full Access are maintained as long as users are part of the same migration batch. It does not mention inbox rules.

Another interesting item that's coming up is Microsoft 365 cross-tenant SMTP domain sharing. This  allows an email domain to exist in two Microsoft 365 tenants simultaneously and will make it easier to manage incremental migrations between tenants when you want to maintain the same email address. The feature is currently in private preview, but is scheduled to be moved into public preview in June 2022.

You can monitor the development status at:

Saturday, February 12, 2022

Query recently created users in Azure AD

Recently had a project where we wanted to identify users created an automated process in the last day. This script gets the job done.

#Gather a list of recently created users

#specify time that will be compared against -24 is the most recent 24 hours
#you can use the option funtion addDays for a longer time period
#Note that time from AzureAD is UTC

$time = (get-date).ToUniversalTime().AddHours(-24)

$users = Get-AzureADUser -All

$newusers = New-Object Collections.Generic.List

foreach ($u in $users) {
    # Write-Host $u.ExtensionProperty.createdDateTime
    If ([datetime]$u.ExtensionProperty.createdDateTime -gt $time) {

Write-host "There are " $newusers.count " new users"

Friday, February 4, 2022

Dell XPS 13 9380 Pulsating Fan

Short version: BIOS 1.17.0 released Jan 2022 appears to fix pulsating fan issue for Dell XPS 13 9380 in Windows 11

Update: I'm still seeing the pulsating fan occasionally, but less than before the BIOS update.

I recently upgraded my Dell XPS 13 9380 to Windows 11. The entire process was smooth and fast. I was pleasantly surprised by how well it went.

About an hour after the upgrade, as the computer was going to sleep, the fan started to turn on and off at about 1 second intervals. This pulsating stopped if you woke up the computer or (oddly) if you unplugged it from power.

When in doubt, update all drivers and firmware. So, I used Dell Support Assist to download and update all the drivers and firmware. There were about 10 updates. And after applying updates and a restart all seemed good for about 24 hours.

The next afternoon, the fan started to pulsate again. Some Windows Updates had applied since the Dell updates were installed and I was concerned that might be causing the issue. In desperation, I ran Support Assist again, and it showed two updates to apply. One of which was BIOS firmware version 1.17.0.

The BIOS update showed in the history as having been installed yesterday, but it must not have applied properly. This time, the BIOS update applied properly and the problem appears to be resolved.


Monday, November 22, 2021

User Profile Ramifications when Renaming Users on Azure AD-Joined Computers

I'm starting to work more with devices that are Azure AD-joined rather than domain-joined. One of my key questions was what happens to user profiles when an Azure AD user sign-in name (UPN) is changed. I was pleasantly surprised by how well it worked.

For my testing, I created an Azure AD user and signed in to create a profile. During sign-in, I created a PIN for authentication. While signed in, I also configured an Outlook profile and OneDrive. Then I tried changing the domain portion of the username and the userid portion of the username. The results were the same:

  • I could still sign in with the PIN.
  • I could sign in as the same user (username displayed on sign-in screen) with the password.
  • I could sign in with the new username (typed in) and password.

After signing in:

  • The workplace account was updated to the new username.
  • Outlook was still able to sign-in without user intervention and updated the account.
  • OneDrive continued to function without user intervention and updated the account.
  • The same Windows 10 user profile was retained.


Wednesday, November 17, 2021

Managing Microsoft 365 Licenses by using Microsoft Graph

Microsoft has announced that after June 2022, the MSOL and AzureAD cmdlets for managing user licenses in Microsoft 365 will cease working. These cmdlets rely on management functionality that is being retired. To manage licenses programmatically, you need to start using Microsoft Graph.

Here is the announcement:

Microsoft Graph is a web-API that you can use to manage Microsoft 365 users, groups, and services. If you're a programmer, then perhaps the idea of building a web request to perform administrative tasks sounds like a good idea. However, for an admin guy like me that typically uses PowerShell cmdlets for management tasks, building web requests is a bit painful. Fortunately, the Microsoft Graph PowerShell SDK has been released that provides PowerShell cmdlets to access Microsoft Graph features.

To get more information about the Microsoft Graph PowerShell SDK:

 Connecting with Microsoft Graph

Just like you use Connect-AzureAD or Connect-MsolService, for Microsoft Graph, you use Connect-MgGraph. When you connect, you need to specify a scope that defines your permissions. So, unlike previous versions, the connection does not automatically gain the full permissions based on your roles like Global Admin. I haven't experimented with exactly which scopes are required to manage user licenses. However, I can confirm that the following example does work.

Connect-MgGraph -Scopes "User.ReadWrite.All","Directory.ReadWrite.All"

To get more information about Microsoft Graph scopes:

License Structure and Naming

If you've been managing licenses through the web interface in Microsoft 365 or the MSOL cmdlets, you're used to seeing license names such as Office 365 E3. When you manage licenses by using Microsoft Graph, you need to know the SkuId property of the licenses available in your tenant. You can obtain the SkuId for a license by using Get-MgSubscribedSku as shown in the following figure. The SkuPartNumber property is a more user friendly name that you can recognize.

Within each licenses type, there are also service plans. These correlate with apps provided by a license such as Exchange Online (Plan 2). To enable or disable the service plans, you need to use the ServicePlanId for a  service plan. If you place your subscribed SKUs in a variable, you can view the service plans included in the SKU as shown in the following figure.

To get a list of generally available SKUs and their service plans:

Viewing Assigned Licenses

You can view the licenses assigned to a user by using the Get-MgUserLicenseDetail cmdlet as shown in the following example:

Get-MgUserLicenseDetail -UserId user@domain.com

The results of this command return the users license assigned to the user. An array of licenses is returned if multiple licenses have been assigned. Within each license returned, you can view the ServicePlans property to see if any service plans have been disabled for a user.

You can also query assigned license information by using Get-MgUser. The licensing information isn't returned by default and you need to specify that the AssignedLicenses property will be retrieved as shown in the figure below. Notice that these results list the service plans that are disabled for a license.

Querying Users with Assigned Licenses

If you want to query all of the users with a specific license, you can do this by using Get-MgUser with a filter for a specific SkuId. The example below shows the syntax.

Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq 78e66a63-337a-4a9a-8959-41c6654dfb56)" -Property AssignedLicenses,UserPrincipalName,Id

When you are filtering based on AssignedLicenses there are some limitations on the results returned. By default, only 100 results are returned. If you use the PageSize parameter, you can specify up to 999 results are returned as shown below.

Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq 78e66a63-337a-4a9a-8959-41c6654dfb56)" -PageSize 999

If you have a larger tenant, and you try to use the All parameter to return results larger than these limits, you will be the following error Get-MgUser : The specified page token value has expired and can no longer be included in your request.To avoid this error, you need to query a list of all users and then filter by using Where-Object as shown below.

$allusers = Get-MgUser -All -Property AssignedLicenses,UserPrincipalName,Id,DisplayName
$A1plusUsers = $allusers | Where-Object {$_.AssignedLicenses.SkuId -contains "78e66a63-337a-4a9a-8959-41c6654dfb56"}

Modifying Assigned Licenses

To add or remove licenses for a user, you use the Set-MgUserLicense cmdlet. When you run the cmdlet, you need to provide the following parameters:

  • UserId. The user being modified. You can specify the user by the object Id or UserPrincipalName.
  • AddLicenses. A hash table that specifies the SkuId of a license being added and the ServicePlanId of any service plans that are being disabled.
  • RemoveLicenses. A string that identifies the SkuId of a license being removed.

The following code shows how to build a hash table for the AddLicenses parameter. This specifies a license SkuId and a service plan that's disabled in the license.

$A1FacultySku = @{
    SkuID = "94763226-9b3c-4e75-a931-5c89701abe66"
    DisabledPlans = "9aaf7827-d63c-4b61-89c3-182f06f82e5c"

The following code lists a SkuID that will be disabled.

$A1PlusFacultySku = "78e66a63-337a-4a9a-8959-41c6654dfb56"

The command that modifies the user license is below. Note that the UserId parameter will accept a UPN also.

Set-MgUserLicense -UserId $User.Id -AddLicenses $A1FacultySku -RemoveLicenses $A1PlusFacultySku

The RemoveLicenses and AddLicenses parameters are mandatory. If you don't provide an empty array, you'll get an error such as Set-MgUserLicense : One or more parameters of the function import 'assignLicense' are missing from the request payload. The missing parameters are: removeLicenses. If you don't want to remove any licenses, you need to provide an empty array for RemoveLicenses as shown below. If you are only removing licenses, you need to provide an empty array for the AddLicenses parameter.

Set-MgUserLicense -UserId $User.id -AddLicenses $A1FacultySku -RemoveLicenses @()

If you want to modify the disabled plans for a licenses, you build a new hash table with the license and all of the plans you want disabled. Then you apply the new hash table with the AddLicenses parameter. The new license assignment overwrites the existing license assignment.

If you want to add multiple licenses, you can provide a comma separated list of hash tables. I have not explicitly tested, but I think providing an array with the hash tables would also work.

If you want to remove multiple licenses, create an array with the SkuIDs that you want to remove.

Thursday, October 28, 2021

AADSTS90072 User Does not Exist in Tenant

During a recent migration project from one tenant to another, a test user was unable to sign in. The sign-in page in Office 365 redirected to AD FS on-premises for authentication. The user credentials worked in AD FS and the web browser was redirected back to Office 365. Then this error was displayed: 

Sign in

Sorry, but we’re having trouble signing you in.

AADSTS90072: User account 'Bob.Smith@domain.com' from identity
provider'urn:sso.domain.com:domain.com' does not exist in
tenant 'Byron Co' and cannot access the application '4765445b-
32c6-49b0-83e6-1d93765276ca'(OfficeHome) in that tenant. The
account needs to be added as an external user in the tenant
first. Sign out and sign in again with a different Azure
Active Directory user account.

This error indicates that the user authenticated by AD FS does not exist in Azure AD. Based on the UPN, Bob.Smith@domain.com existed both in on-premises AD and Azure AD. So, there is some other property being used to match the two objects after AD FS authentication.

To understand where this broke down, you need to understand how objects in AD are linked with objects in Azure AD. There is an ImmutableID property on Azure AD users that links Azure AD users to on-premises AD users. Early implementations of Azure AD Connect (or Dirsync) copied the object GUID from on-premises AD and used that value for the ImmutableID. This worked well until you migrated user objects to new AD domain or AD forest where they'd have a different GUID.

New implementations of Azure AD Connect use ms-ds-ConsistencyGUID in the on-premises user object instead of GUID. This value can be copied between domains to preserve synchronization during object migrations. By default ms-ds-ConsistencyGUID is populated with the same value as the object GUID the first time the object is synced.

AD FS authentication uses the object GUID or ms-ds-ConsistencyGUID during authentication. This value must match the ImmutableID in AzureAD to allow authentication to complete. The older instructions for configuring AD FS authentication manually had you configure a rule for object GUID. If you use Azure AD Connect to configure AD FS then it creates rules that use ms-ds-ConsistencyGUID if populated or object GUID. This article talks about the rule configuration: https://docs.microsoft.com/en-us/azure/active-directory/hybrid/how-to-connect-fed-management#modclaims.

If you update your deployment of Azure AD Connect to use ms-ds-ConsistencyGUID as the source anchor and forget to update AD FS to allow ms-ds-ConsistencyGUID in the authentication process, AD FS authentication will continue to work because object GUID and ms-ds-ConsistencyGIUD are the same value by default. However when you start migrating objects and retain the ms-ds-ConsistencyGUID (which will now be different from the object GUID) authentication starts to fail because the token passed back to Office 365 for authentication contains the object GUID which doesn't match the immutable ID/ms-ds-ConsistencyGUID. Thus the error message above.

In our case, the AD migration tool we were using copied the ms-ds-ConsistencyGUID from a source AD domain to target AD domain which caused our authentication issue. Because users were getting new mailboxes in this migration, we didn't need to maintain ms-ds-ConsistencyGUID. Our short term fix was to copy the object GUID value and place that in ms-ds-ConsistencyGUID and immutableID. However, the correct long term solution is to update AD FS to correctly use ms-ds-ConsistencyGUID during authentication.

This article has some examples you can use to convert object GUID to ms-ds-ConsistencyGUID and ImmutableID: http://byronwright.blogspot.com/2020/10/convert-immutableid-to-hex.html.