Entra ID – Office 365 for IT Pros https://office365itpros.com The Ultimate Guide to Mastering Microsoft 365 Fri, 24 Oct 2025 09:24:04 +0000 en-US hourly 1 https://i0.wp.com/office365itpros.com/wp-content/uploads/2025/06/cropped-cropped-O365Cover-Twelfth-Edition-final.jpg?fit=32%2C32&ssl=1 Entra ID – Office 365 for IT Pros https://office365itpros.com 32 32 150103932 Allowing Users to Add Enterprise Apps to Entra ID is a Bad Idea https://office365itpros.com/2025/10/24/enterprise-apps-my-mistake/?utm_source=rss&utm_medium=rss&utm_campaign=enterprise-apps-my-mistake https://office365itpros.com/2025/10/24/enterprise-apps-my-mistake/#comments Fri, 24 Oct 2025 07:00:00 +0000 https://office365itpros.com/?p=71242

Reviewing Enterprise Apps is a Good Idea

Over the years, I have advised Microsoft 365 tenants to check and clean up enterprise apps regularly. Initially, the Graph APIs available to report information about enterprise apps weren’t too approachable and lacked some data. However, the situation has improved and it’s now easier to get a solid handle on the enterprise apps present in a tenant, the usage of those apps, and the permissions used by apps to access data.

Given that the original clean-up script dates back to April 2020, I’ve been writing a new script based on the Microsoft Graph PowerShell SDK to demonstrate how to generate review data. (Microsoft released V2.32 of the SDK on October 20, 2025, so far, the new version appears to be solid). In any case, once I’ve finished tweaking the code, I’ll write up details about what the script does and release it via the Office 365 for IT Pros GitHub repository.

The Case of the Newly-Added Enterprise Application

One of the checks performed by the script highlights recently added service principals. After writing the code, I was interested to discover the presence of an enterprise app called GuideAnts, added on 15 October 2025 by my account. I couldn’t remember anything about adding such an app. Advancing age has a nasty habit of eroding immediate recall.

In any case, running an audit log search confirmed that my account had added the service principal (Use the Search-UnifiedAuditLog cmdlet to search the audit log for events with operations = “Add Service Principal.”). Here’s an extract from the audit log:

Actor                         : {@{ID=Tony.Redmond@office365itpros; Type=5}, @{ID=1003BFFD805C87B0; Type=3}, @{ID=Azure ESTS Service; Type=1}, @{ID=00000001-0000-0000-c000-000000000000; Type=2}…}
InterSystemsId                : e5fce0de-688c-4e1e-bf64-22d9246ba0e6
IntraSystemId                 : 00000000-0000-0000-0000-000000000000
SupportTicketId               :
Target                        : {@{ID=ServicePrincipal_d448e5cc-80cc-4c95-8aca-356068dc2972; Type=2},@{ID=d448e5cc-80cc-4c95-8aca-356068dc2972; Type=2}, @{ID=ServicePrincipal; Type=2},@{ID=guideants; Type=1}…}

Having still no memory of doing such a thing, I exported my browser history and loaded the CSV file into PowerShell to check it:

$History = Import-CSV browserhistory.csv
$History | Where-Object {$_.pagetitle -like "*GuideAnts*"} | Format-table DateTime, PageTitle, NavigatedToURL

DateTime                 PageTitle                 NavigatedToUrl
--------                 ---------                 --------------
2025-10-15T20:26:54.855Z GuideAnts Notebooks       https://go.guideants.ai/access
2025-10-15T20:26:30.514Z GuideAnts Notebooks       https://go.guideants.ai/login
2025-10-15T20:26:29.801Z GuideAnts Notebooks       https://go.guideants.ai/

This is the kind of interaction captured when someone goes through the consent process to add an enterprise app (Figure 1) and consents on behalf of the organization. There was no doubt. I was the culprit.

Consent requested for the GuideAnts enterprise application.
Figure 1: Consent requested for the GuideAnts enterprise application

This is an example of bad practice in action. I might have been tired, and I might have wanted to check out the app because I was writing about ISV AI-powered add-ins for Microsoft 365 at the time, but these are not acceptable excuses.

Consent Approval Workflow for Enterprise Apps

I violated my personal standards in three ways. First, I added an enterprise app without much consideration, perhaps because the permissions sought for the app were pretty benign. Second, I added an unverified app. Enterprise apps published by ISVs should go through the Microsoft verification process to give tenants some additional trust that the app comes from a reputable publisher.

Third, I used my administrator account. Had I used my normal account, I wouldn’t have been able to add an enterprise app because the tenant settings would block immediate app creation by users. Instead, a request to add the app would have gone through a consent approval workflow for approval by an administrator (Figure 2). Even if that administrator was me, being forced to go through the approval process might have caused me to think why an enterprise app was needed, or to review the reply URLs used by the app and ask myself why these URLs are required.

Seeking approval for the GuideAnts enterprise app.
Figure 2: Seeking approval for the GuideAnts enterprise app

We live and learn from our mistakes. I hope that I won’t make the same mistake again!

GuideAnts AI Notebooks

Apart from noting the unverified nature of the enterprise app, none of the above is criticism of the GuideAnts app (an AI-powered notebook). The app’s author is Doug Ware, an ex-MVP, who publishes some interesting AI-related content on Elumenotion.com. The app is currently in preview. You can read more about GuideAnts here and decide if you want its enterprise app to exist in your tenant. Use invite code 22VG6Y if you want to join the preview.


Learn how to exploit the data available to Microsoft 365 tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work.

]]>
https://office365itpros.com/2025/10/24/enterprise-apps-my-mistake/feed/ 3 71242
Updating the Entra ID Password Protection Policy with the Microsoft Graph PowerShell SDK https://office365itpros.com/2025/10/23/password-protection-policy-ps/?utm_source=rss&utm_medium=rss&utm_campaign=password-protection-policy-ps https://office365itpros.com/2025/10/23/password-protection-policy-ps/#respond Thu, 23 Oct 2025 07:00:00 +0000 https://office365itpros.com/?p=70919

Use SDK Cmdlets to Create or Update Password Protection Policy Settings

A reader asks if the script written for the article about updating the Entra ID banned password list can be used to update other settings in the Entra ID password protection policy. The answer is “of course.” The code is PowerShell, and it can be adapted to update any of the password protection settings found in the Entra admin center (Figure 1).

 Entra ID password protection policy settings.
Figure 1: Entra ID password protection policy settings

A few considerations must be remembered when updating the Entra ID password protection policy:

Creating a Password Protection Policy

The underlying concepts for creating a custom password policy are similar to the management of other Entra ID policies (like the Microsoft 365 groups policy):

Check if a custom policy exists, or rather, a directory setting object created using the directory setting template for password rules. The template always has the identifier 5cf42378-d67d-4f36-ba46-e8b86229381d, so we can check if a custom password protection policy exists follows:

$Policy = (Get-MgBetaDirectorySetting | Where-Object {$_.TemplateId -eq "5cf42378-d67d-4f36-ba46-e8b86229381d"})

A client-side filter is used because the Graph API does not support server-side filtering against template identifiers.

If a password policy object is not available, you can create a new password policy object. The values for the policy settings are in a hash table containing an array of values. Each value (a setting) is a hash table consisting of the setting name and its value. For example, this code creates the hash table to hold the setting for lockout duration:

$Value5 = @{}
$Value5.Add("Name", "LockoutDurationInSeconds")
$Value5.Add("Value", $LockoutDuration -as [int32])

After populating values for all settings (or just the ones that are different from the default), run the New-MgBetaDirectorySetting cmdlet to create the new custom password policy:

$NewBannedListParameters = @{}
$NewBannedListParameters.Add("templateId", "5cf42378-d67d-4f36-ba46-e8b86229381d")
$NewBannedListParameters.Add("values", ($Value1, $Value2, $Value3, $Value4, $Value5, $Value6))
$Policy = New-MgBetaDirectorySetting -BodyParameter $NewBannedListParameters -ErrorAction Stop

Updating the Password Protection Policy

If a custom policy already exists, fetch the policy settings, update the value for the settings that you want to change, and use the Update-MgBetaDirectorySetting cmdlet to update the policy. This example changes the lock out duration time to 120 seconds (the default is 60 seconds):

[array]$PolicyValues = Get-MgBetaDirectorySetting -DirectorySettingId $Policy.Id | Select-Object -ExpandProperty Values
($PolicyValues | Where-Object {$_.Name -eq "LockOutDurationInSeconds"}).Value = 120
Update-MgBetaDirectorySetting -DirectorySettingId $Policy.id -Values $PolicyValues -ErrorAction Stop

The code for these operations is the same as used in the script to update the banned passwords list. Grab what you need from that script and repurpose it to do whatever you need to. For instance, some organizations like to validate that the password policy settings in the tenants that they manage are consistent and up to date. This is easily done on a periodic basis by creating a PowerShell runbook in Azure Automation. I imagine that checking the password policy would only be one of the Entra ID configuration checks that such a runbook would process. At least, that’s how I would do it.

Next Step – Testing Configurations

The Maester utility includes some checks against the password policy and it would be easy to expand test coverage to whatever aspect of the password policy you consider needs to be checked. Once you’ve mastered programmatic manipulation of the Entra ID password protection policy settings, anything is possible.


Support the work of the Office 365 for IT Pros team by subscribing to the Office 365 for IT Pros eBook. Your support pays for the time we need to track, analyze, and document the changing world of Microsoft 365 and Office 365. Only humans contribute to our work!

]]>
https://office365itpros.com/2025/10/23/password-protection-policy-ps/feed/ 0 70919
Important Change Coming for Entra ID Passkeys in November 2025 https://office365itpros.com/2025/10/22/passkey-setting-policy/?utm_source=rss&utm_medium=rss&utm_campaign=passkey-setting-policy https://office365itpros.com/2025/10/22/passkey-setting-policy/#respond Wed, 22 Oct 2025 07:00:00 +0000 https://office365itpros.com/?p=71218

Passkey Settings Behavior Change After Introduction of New Passkey Profiles

If your focus is on Entra ID or security, you probably agree with the statement that passkeys are the future for authentication. Or at least, the immediate next step. Who knows what might happen after passkeys are fully deployed? After all, it wasn’t so long ago that people congratulated themselves for using SMS messages for multifactor authentication.

In any case, message center notification MC1097225 (first published 17 June 2025, updated 20 October 2025) marks an important point in the evolution of passkey support within Entra ID. Where today Entra ID supports tenant-wide controls for passkeys as an authentication method, from November 2025 (December 2025 for government clouds), the preview Entra ID feature will support up to ten passkey profiles per tenant. The intention behind the change is to allow tenants to exert more granular control over which users can use what passkeys for authentication.

Granular control is usually goodness, and there’s goodness in this change. You’ll be able to create a passkey profile for departments or other groups and dictate what kind of passkeys the users within the scope of the profile can use.

Passkey Authenticator Attestation

A potential downside exists that should be understood before rushing to embrace the change. When a tenant opts in to use the new approach, Entra ID switches to a new schema to describe what passkey policies are. Logically enough, the existing passkey settings become the default passkey policy, and if the setting to enforce attestation is disabled, Entra ID will become less strict about the kind of passkeys it accepts as an authentication method.

Passkeys have an Authenticator Attestation GUID (AAGUID), a 128-bit identifier to identify the make and model. In enterprise environments, it is common practice to decide on a set of passkeys or FIDO2 keys that the tenant wishes to support. This decision is enforced by specifying the AAGUIDs in the passkey settings.

But as part of the change to the new passkey schema, Microsoft says that “if Enforce attestation is disabled (in a policy), we (Entra ID) will start accepting security key or passkey providers using the following attestation statements:

  • “none” 
  • “tpm” 
  • “packed” (AttCA type only) 
  • Custom attestation formats ≤ 32 characters 

This will allow a wider range of security keys and passkey providers to be accepted for registration and authentication in Microsoft Entra ID.”

That doesn’t sound too serious, but it does mean that if your current passkey settings do not enforce attestation (Figure 1), anyone covered by the default policy created when the switchover happens will be able to choose whatever passkey type they like.

Passkey settings (prior to the change) with enforce attestation not enforced.
Figure 1: Passkey settings (prior to the change) with enforce attestation not enforced

A Passkey Setting Worth Checking

Some tenants might not care very much about the non-enforcement of attestation. Others will care deeply because of the work they’ve done previously to figure out what kind of passkeys should be used within the tenant. In either case, it’s worthwhile considering the topic and deciding if attestation should be enforced.

Microsoft says that there’s no administrator action necessary for the change. It will be deployed automatically to tenants, and you might not realize that anything has happened if you don’t have the need to review authentication methods.

APIs Not Ready for Change

MC1097225 contains an important note: “If you continue using Graph API or third-party tools to modify the policy, the schema will not change until General Availability.” Remember, what comes in November is a preview and it takes time for APIs to catch up with change. Customers who have built tools to manage authentication methods can continue to use those methods until general availability happens, which will probably be in early to mid-2026 (my guess). When that happens, I guess I’ll revisit my password and authentication methods report script.


Support the work of the Office 365 for IT Pros team by subscribing to the Office 365 for IT Pros eBook. Your support pays for the time we need to track, analyze, and document the changing world of Microsoft 365 and Office 365. Only humans contribute to our work!

]]>
https://office365itpros.com/2025/10/22/passkey-setting-policy/feed/ 0 71218
The My Sign-Ins Portal, Applications, and Conditional Access https://office365itpros.com/2025/10/15/my-sign-ins-portal/?utm_source=rss&utm_medium=rss&utm_campaign=my-sign-ins-portal https://office365itpros.com/2025/10/15/my-sign-ins-portal/#respond Wed, 15 Oct 2025 07:00:00 +0000 https://office365itpros.com/?p=71121

Making Conditional Access and the My Sign-Ins Portal Work Better

A couple of weeks ago, I attended a keynote at the TEC 2025 conference where Alex Simons, Microsoft Corporate VP for Entra, discussed the investments Entra is making to develop agents to help tenant administrators to work smarter. There’s a cost to these agents in the form of Entra premium licenses and the security compute units required to run the agents. Microsoft’s bet is that they can deliver sufficient value to customers through agents to take the cost question off the table. Time will tell.

The Conditional Access optimization agent is one of the agents Microsoft has available in preview. I think both agents can do more and have said so both in print and in person. At this point, the conditional access agent seems more practical and likely to have an impact simply because it’s so easy to screw up conditional access policies.

Which brings me to a LinkedIn post by David Nündel reporting that Microsoft has exposed several additional first-party applications in the Entra admin center. There’s nothing really surprising here because Microsoft 365 and Entra ID are constructed from many multitenant applications. Instances of these applications exist in customer tenants (or rather, service principals for the applications) that can then be used in different aspects of tenant management.

Applications and the My Sign-Ins Portal

What is surprising and useful is that the newly-exposed applications relate to the My Sign-ins portal where users can perform actions such as changing their password, removing themselves as guest accounts from other Microsoft 365 tenants, and viewing recent sign-in activity (Figure 1).

Viewing recent sign-in activity through the My Sign-Ins portal.
Figure 1: Viewing recent sign-in activity through the My Sign-Ins portal

The point is that the My Sign-ins portal relies on access to several applications to display the information revealed by the various menu options. If access to the applications is blocked by something like a conditional access policy, then the portal cannot function. And as it so happens, the newly revealed applications are those that are needed by the My Sign-Ins portal. Six applications are in the set with the following display names and application identifiers:

  • My Signins: 19db86c3-b2b9-44cc-b339-36da233a3be2
  • My Profile: 8c59ead7-d703-4a27-9e55-c96a0054c8d2
  • Microsoft App Access Panel: 0000000c-0000-0000-c000-000000000000
  • AADReporting: 1b912ec3-a9dd-4c4d-a53e-76aa7adb28d7
  • Windows Azure Active Directory: 00000002-0000-0000-c000-000000000000
  • Azure Credential Configuration Endpoint Service: ea890292-c8c8-4433-b5ea-b09d0668e1a6

Checking Service Principals for the My Sign-Ins Portal Applications

Service principals for most or maybe all of these applications are likely already present in your tenant. When I checked using the Microsoft Graph PowerShell SDK command shown below, only the My SignIns application was missing:

Get-MgServicePrincipal -filter "displayName eq 'Azure Credential Configuration Endpoint Service' or displayName eq 'Windows Azure Active Directory' or displayName eq 'AADReporting' or displayName eq 'Microsoft App Access Panel' or displayName eq 'My Profile' or displayName eq 'My SignIns'" | Format-Table DisplayName, Id, AppId

DisplayName                                     Id                                   AppId
-----------                                     --                                   -----
My Profile                                      1f1f813e-0778-4b5b-a379-a924c97e023f 8c59ead7-d703-4a27-9e55-c96a0054c8d2
AADReporting                                    31bd9b44-bc6b-42df-9be6-3030109b84a5 1b912ec3-a9dd-4c4d-a53e-76aa7adb28d7
Microsoft App Access Panel                      10334c63-ac46-4b2a-a80a-dc9c62e34dd8 0000000c-0000-0000-c000-000000000000
Windows Azure Active Directory                  2be71509-6ab9-44d7-bfd8-eff4e50bfc7c 00000002-0000-0000-c000-000000000000
Azure Credential Configuration Endpoint Service 6d1fdc7c-f64b-4aeb-9133-5246b467035c ea890292-c8c8-4433-b5ea-b09d0668e1a6

The problem was easily fixed by running the New-MgServicePrincipal cmdlet:

New-MgServicePrincipal -AppId 19db86c3-b2b9-44cc-b339-36da233a3be2

DisplayName Id                                   AppId                                SignInAudience      ServicePrincipalType
----------- --                                   -----                                --------------      --------------------
My Signins  a7cda215-2932-4042-8e3e-631ecf7ae23b 19db86c3-b2b9-44cc-b339-36da233a3be2 AzureADMultipleOrgs Application

The command to create a service principal from an application identifier works because the My SignIns application is a multitenant application owned by Microsoft. We can prove this by using the tenant relationship API to check the value of the identifier for the owning tenant. Using the Find-MgTenantRelationshipTenantInformationByTenantId cmdlet requires the Graph CrossTenantInformation.ReadBasic.All permission:

$AppTenantOwner = (Get-MgServicePrincipal -ServicePrincipalId a7cda215-2932-4042-8e3e-631ecf7ae23b).AppOwnerOrganizationId
Find-MgTenantRelationshipTenantInformationByTenantId -TenantId $AppTenantOwner
Write-Host ("The tenant name is {0} and its default domain is {1}" -f $TenantInfo.displayName, $TenantInfo.DefaultDomainName)

The tenant name is Microsoft Services and its default domain is sharepoint.com

No Point in Repeating What’s Already Available

With all the applications in place, you can use them in conditional access policies. I don’t like repeating information that’s already online, and I hate seeing many different descriptions of a new feature published by people who haven’t bothered to add any personal insight or knowledge to help others understand the technology better.

With that point in mind, you can read about how these applications could be used in a description of configuring conditional access for guest users by MVP Kenneth Van Surksum. Kenneth adds a few more applications to the “must exclude from blocking” list, so it’s important that you read the article. Excluding applications in conditional access policies simply allows users to access applications that they need to do their jobs, or to make functionality work, like the exclusion required by Outlook to handle sensitivity labels.

Now all I want to know is whether the Entra conditional access optimization agent is ready to optimize for this condition. I suspect not, because it’s clear that first generation agents solve immediate issues (like stopping people from locking themselves out) rather than delivering great insight into more subtle policy details.


]]>
https://office365itpros.com/2025/10/15/my-sign-ins-portal/feed/ 0 71121
Microsoft Introduces Restore Capability for Conditional Access Policies https://office365itpros.com/2025/10/03/restore-a-conditional-access-policy/?utm_source=rss&utm_medium=rss&utm_campaign=restore-a-conditional-access-policy https://office365itpros.com/2025/10/03/restore-a-conditional-access-policy/#respond Fri, 03 Oct 2025 07:00:00 +0000 https://office365itpros.com/?p=71000

Restore a Conditional Access Policy Only Possible via Graph Requests For Now

Documented but not announced, the beta endpoint for the Microsoft Graph conditionalAccessPolicy resource now supports an API to restore soft-deleted conditional access policies. The restore API is used alongside the API to list soft-deleted objects used with conditional access policies. The APIs are relatively new and have not yet shown up in the Entra admin center, which currently issues a solemn warning if an administrator deletes a conditional access policy (Figure 1).

The Entra admin center warns about the consequences of deleting a conditional access policy.

Restore a conditional access policy.
Figure 1: The Entra admin center warns about the consequences of deleting a conditional access policy

Finding Soft-deleted Conditional Access Policies

The code to discover if a tenant has any soft-deleted conditional access policies is straightforward. Because the API is relatively new, a cmdlet is not yet available in the Microsoft Graph PowerShell SDK (V2.31). No doubt a cmdlet will appear in a future version.

To discover if any soft-deleted conditional access policies exist, we execute the Invoke-MgGraphRequest cmdlet to run a HTTP request against the API and examine the details of what the API returns. The Policy.Read.ConditionalAccess permission must be available to the interactive session and the signed-in user must hold a suitable Entra role such as Conditional Access administrator or Security administrator.

$Uri = 'https://graph.microsoft.com/beta/identity/conditionalAccess/deletedItems/policies'
$Data = Invoke-MgGraphRequest -Uri $Uri -Method Get -OutputType PSObject | Select-Object -ExpandProperty Value

If ($Data) {
  Write-Host ""
  Write-Host ("{0} soft-deleted conditional access policies found" -f $Data.count)
  Write-Host ""
  $Data | Format-Table Id, displayName, createdDateTime, deletedDateTime
} Else {
  Write-Host "No soft-deleted conditional access policies found to restore"
}

1 soft-deleted conditional access policies found

id                                   displayName                   createdDateTime     deletedDateTime
--                                   -----------                   ---------------     ---------------
14786eef-facd-41ac-83e6-19b317d3e054 Strong MFA for Hard Deletions 05/02/2025 14:05:07 02/10/2025 17:01:05

The output shows that the API found a soft-deleted conditional access policy. Like other Entra ID soft-deleted objects, conditional access policies remain in the soft-deleted state after deletion. When the retention period expires, Entra removes the policy object permanently and it is no longer recoverable.

Restore a Soft-Deleted Conditional Access Policy

Restoring a soft-deleted conditional access policy requires the Policy.ReadWrite.ConditionalAccess permission. The signed-in user must also hold a suitable RBAC role as described above. This example selects the first item in an array of soft-deleted policies returned using the first example, creates the URL to restore the policy, and executes the request to restore the soft-deleted conditional access policy. A successful restore populates the variable used to accept the output of the Invoke-MgGraphRequest cmdlet, so the code checks the variable to make sure that the restore worked:

$PolicyId = $Data[0].Id
$RestoredPolicy = $null
$Uri = ("https://graph.microsoft.com/beta/identity/conditionalAccess/deletedItems/policies/{0}/restore" -f $PolicyId)
Try {
  $RestoredPolicy = Invoke-MgGraphRequest -Uri $Uri -Method Post -ErrorAction Stop
} Catch {
  Write-Host ("Error restoring conditional access policy {0}" -f $PolicyId)
}
If ($RestoredPolicy) {
  Write-Host ("Successfully restored soft-deleted {0} conditional access policy" -f $RestoredPolicy.displayName)
}

Another way to check that the restore worked is to run the Get-MgIdentityConditionalAccessPolicy cmdlet:

$Check = Get-MgIdentityConditionalAccessPolicy -ConditionalAccessPolicyId $RestoredPolicy.id
If ($Check) {Write-Host "Restore worked!"}

The newly restored policy will be visible in the Entra admin center the next time the Conditional Access Policies page refreshes.

Permanently Remove a Soft-Deleted Conditional Access Policy

If necessary, soft-deleted policies can be removed before the 30-day retention period expires with the delete policyDeleteItem API. Once again, the example uses the first item in an array of soft-deleted policies.

$PolicyId = $Data[0].Id
$Uri = ("https://graph.microsoft.com/beta/identity/conditionalAccess/deletedItems/policies/{0}" -f $PolicyId)
Try {
  Invoke-MgGraphRequest -Uri $Uri -Method Delete -ErrorAction Stop
} Catch {
  Write-Host ("Failed to remove soft-deleted policy {0}" -f $PolicyId)
}

Recovery Options are Always Good

It’s always good to have a get out of jail card that allows the recovery of items deleted in error and the new restore capabilities are a good addition to the PowerShell cmdlets for managing conditional access policies.

I’m not sure how many administrators delete conditional access policies instead of first disabling unwanted policies for a period of a week or so before proceeding to deletion. That’s still the best way of removing conditional access policies from a tenant because everything can be done through the Entra admin center. However, Microsoft has some AI-powered Entra administrative agents in preview. The current set of agents includes the conditional access optimization agent, which is designed to analyze and optimize the conditional access policies found in a tenant, including:

The agent also evaluates all existing enabled policies to propose potential consolidation of similar policies. When the agent identifies a suggestion, you can have the agent update the associated policy with one click-remediation.

If the conditional access optimization agent recommends consolidation into a smaller set of policies, it probably will result in the removal of some policies that are no longer required. Administrators click to action the agent’s recommendations. It’s good to know that if the agent proposes the removal of some policies that should be kept, at least administrators can recover the deleted policies if they go ahead with “one-click remediation.”


Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2025/10/03/restore-a-conditional-access-policy/feed/ 0 71000
Updating the User Password and Authentication Report https://office365itpros.com/2025/09/23/password-and-authentication-report/?utm_source=rss&utm_medium=rss&utm_campaign=password-and-authentication-report https://office365itpros.com/2025/09/23/password-and-authentication-report/#respond Tue, 23 Sep 2025 07:00:00 +0000 https://office365itpros.com/?p=70810

Change to Beta Graph API Requires Update to User Password and Authentication Report Script

Last year, I wrote about how to exploit the newly-added Graph API to report the per-user MFA state for Entra ID accounts. As with all additions to the Graph, the new API used the beta endpoint, which is where APIs function when Microsoft is happy to expose the API in public while still knowing that some work is needed before the API can advance to the production (V1.0) endpoint.

Recently, a reader posted a comment about the article and said that when they ran the script in their tenant, no data showed up in the “Default MFA method” column in the report. I write scripts to illustrate principals and explore what’s possible rather than attempting to create production-ready code. In addition, it’s impossible for me to see what’s happening in another tenant, unless the tenant administrator gives me the necessary account with permissions, and that shouldn’t happen. My default response is therefore to ask people who report issues to do some basic PowerShell debugging by selecting the code where the problem seems to lie and running the commands to see what happens. Besides, I was on vacation, so I didn’t want to spend any time looking into what the issue might be.

Searching for the Problem

To cut a long story short, the person who reported the problem responded with sufficient data to indicate that an issue was present. After returning from vacation, it was time to open Visual Studio Code (and GitHub Copilot) and try to figure out what was happening.

The code looked good, and nothing seemed out of the ordinary. The script used Graph requests rather than cmdlets from the Microsoft Graph PowerShell SDK, but that’s a normal state with newly-released beta APIs because the AutoRest tool that generates SDK cmdlets from the API metadata hasn’t yet included the cmdlets in an SDK release. To make the code easier to read, I replaced a couple of API requests with SDK cmdlets and then noticed that the output from the Get-MgBetaUserAuthenticationRequirement cmdlet no longer contained details of a user’s default MFA method. This was the cause of the problem.

The Downside of Beta Graph APIs

Using beta APIs (or beta cmdlets) is a double-edged sword. The positive is access to functionality sooner than waiting for Microsoft to upgrade an API to a production release. Some APIs stay in the beta endpoint for years, and you can’t predict when Microsoft might consider an API to be reliable, robust, and performant enough to justify promotion from beta to production.

The downside is that beta features are prone to change based on developer and customer feedback, the need to accommodate changes elsewhere in the underlying workload, or as the side effects of bug fixing or tuning for performance. I have no idea why Microsoft removed the default MFA method output from the data reported by the cmdlet. Maybe it was because the UserPreferredMethodForSecondaryAuthentication property contains similar information.

A New User Password and Authentication Report Script

In any case, my script can only report what’s available, so I updated the code to remove the obsolete column and renamed the UserPreferredMethodForSecondaryAuthentication column to be “Preferred MFA method” (Figure 1). The change seemed to make sense at the time. If you disagree, feel free to create a GitHub push request to update the script. I’m happy to review any and all suggestions.

The adjusted user password and authentication report.
Figure 1: The adjusted user password and authentication report

Don’t Depend on Beta Graph APIs

All of this goes to show that developers who rely on beta Graph APIs need to keep a wary eye out for changes that might impact their code. The change can happen at any time and probably won’t be flagged by Microsoft because it is, after all, a change to a beta API that’s expressly not intended for production use. I should learn my lesson (some day). In the meantime, enjoy the updated user password and authentication report script and all the other examples in the Office365itpros GitHub repository. I’m sure there’s a few more bugs lurking there that I need to attend to.


Need some assistance to write and manage PowerShell scripts for Microsoft 365, including Azure Automation runbooks? Get a copy of the Automating Microsoft 365 with PowerShell eBook, available standalone or as part of the Office 365 for IT Pros eBook bundle.

]]>
https://office365itpros.com/2025/09/23/password-and-authentication-report/feed/ 0 70810
What’s the Best Way to Manage Guest Accounts? https://office365itpros.com/2025/09/18/guest-account-management/?utm_source=rss&utm_medium=rss&utm_campaign=guest-account-management https://office365itpros.com/2025/09/18/guest-account-management/#respond Thu, 18 Sep 2025 07:00:00 +0000 https://office365itpros.com/?p=70767

Home-Brewed PowerShell or Microsoft Solutions for Guest Account Management

A recent podcast from the genial Merill Fernando featured Microsoft’s Jeremy Conley to talk about “how to really govern guest access.” The tagline “many tenants have 2-4x more guests than employees” captures the focus of the episode (a good listen) and while many organizations might not believe that guest accounts are quite so numerous in their Microsoft 365 tenants, the simple fact is that it’s all too easy to accumulate a vast collection of guests.

Microsoft 365 is responsible for this sad state of affairs. I started talking about the problems of guest accounts “going bad” (aging) soon after the introduction of Azure AD guest accounts for Office 365 groups in 2016 and the situation hasn’t improved much since. Things really took off with the introduction of Teams in 2017 and later, the adoption of guest accounts by SharePoint Online as the basis for sharing. My basic recommendation has always been to review guest accounts annually with the aim of removing unused guests.

Use Entra ID Governance or PowerShell for Guest Account Management

Microsoft has solutions to help, but only if organizations invest in Entra P2 licenses (naturally) to liberate ID governance features like lifecycle management and access reviews. If you can afford the licenses, you should certainly investigate using lifecycle management and access reviews to control guest accounts. But you don’t need to spend any money on additional licenses because controlling guest accounts is a reasonably straightforward task using PowerShell. Let’s discuss some of the tactics that tenants could adopt for guest management.

First, Microsoft doesn’t implement an expiration date for guest accounts, but this is easily done by assigning an expiration date to guest accounts and using that date as the basis for checking if guest accounts are still needed.

For any type of guest account management, it’s a good idea to review guest sign-in activity. If a guest account doesn’t sign into a tenant within a certain period (say, 90 days), it’s probably obsolete and can be removed.

Entra ID supports the concepts of account sponsorship. In other words, one or more sponsor accounts can be associated with member or guest accounts. Sponsors are not assigned by default, but setting a default sponsor is easily done for guest accounts. The problem with default sponsors is that the selected account might not have any insight into how a guest account is used, but a default sponsor is better than none, and the lack of activity should always be the primary reason for considering an account to be inactive and a candidate for removal.

Sponsors for an Entra ID guest account.

Guest account management.
Figure 1: Sponsors for an Entra ID guest account

Sponsors are supposed to know why an account exists, so if a guest account is deemed obsolete due to lack of sign-in activity, you can report this fact and use the report data to contact the sponsors to ask if accounts should be removed or kept.

The Need to Nag Sponsors

One thing I haven’t done yet is to send nagging email to account sponsors to say that their sponsored guest accounts will be automatically removed in a week or so if they don’t reply with a justification for keeping the accounts. This is a good example of where a scheduled Azure Automation runbook is a good choice to run the code to check for obsolete guest accounts and email the account sponsors. I must write that script!

No one wants to remove guest accounts that are required for business purposes. Teams is probably the best example of where important guest accounts that appear underused might exist. I’ve documented five practical actions to manage guest accounts used with Teams topic in this article. Enforcing multifactor authentication for guest accounts through a conditional access policy is a critical step.

Act to Make Sure Your Tenant Implements Guest Account Management

Whether you decide to manage guest accounts using your own code or with Microsoft’s solutions really doesn’t matter. The important thing is to manage guest accounts, especially in terms of a regular clean-out of obsolete accounts. Insisting on multifactor authentication removes most of any security risk associated with having some underused guest accounts in Entra ID, but who doesn’t like a clean directory?


Need some assistance to write and manage PowerShell scripts for Microsoft 365, including Azure Automation runbooks? Get a copy of the Automating Microsoft 365 with PowerShell eBook, available standalone or as part of the Office 365 for IT Pros eBook bundle.

]]>
https://office365itpros.com/2025/09/18/guest-account-management/feed/ 0 70767
Entra ID’s Keep Me Signed In Feature – Good or Bad? https://office365itpros.com/2025/09/17/kmsi-good-or-bad/?utm_source=rss&utm_medium=rss&utm_campaign=kmsi-good-or-bad https://office365itpros.com/2025/09/17/kmsi-good-or-bad/#comments Wed, 17 Sep 2025 07:00:00 +0000 https://office365itpros.com/?p=70756

Should Microsoft 365 Tenants Disable Keep Me Signed In?

When I wrote about the Entra ID Keep Me Signed In (KMSI) feature in February 2022, I concluded that growing threats might have made the feature less valuable than it once was. Like anything to do with Microsoft 365, the passing of time requires re-evaluation of attitudes and opinions, and this is true for KMSI too. Here’s my best attempt at summarizing the current state of the art.

Recapping How the Keep Me Signed In Feature Works

As a recap, KMSI is the option presented to users after they authenticate to “stay signed in” to reduce the number of times Entra ID forces the user to sign in. If the user chooses to stay signed in by choosing the Yes option (Figure 1), Entra ID creates a persistent authentication cookie that can last for up to 90 days (as opposed to 24 hours, which is the lifetime of a non-persistent cookie). With a persistent autentication cookie available, the user can connect to applications without signing in for the lifetime of the cookie. Because the cookie is persistent, it doesn’t matter if the browser session is restarted.

Opting for KMSI.
Figure 1: Opting for KMSI

The Don’t show this again checkbox has nothing to do with the creation of the persistent authentication cookie. The checkbox controls whether Entra displays the prompt on the device for future sign-ins.

Obviously, a persistent authentication cookie is a bad idea if workstations are shared, but when workstations are personal and only used by a single person, keep me signed in is a nice way to reduce the friction of signing in. In fact, the Entra ID sign-in flow contains some logic to detect if a sign-in originates from a shared device and won’t show the stay signed in screen in this case. The same is true if Entra ID considers a sign-on to be high risk.

Clearing browser cookies on a workstation will remove the persistent authentication cookie.

Conditional Policies and Sign-in Frequency

Conditional access policies can interfere with the operation of persistent authentication cookies. If a conditional access policy insists that users reauthenticate based on a certain frequency, the full authentication process is invoked, and users must provide credentials. Some tenants impose unreasonable demands on users (or just guest accounts) and insist on very frequent authentication, so it’s a matter of achieving balance between annoying users and maintaining the desired level of security.

Considering the Question of Enabling Keep Me Signed In

All of which brings me back to the question of whether Microsoft 365 tenants should enable or disable KMSI. Generally speaking, I don’t see anything wrong with KMSI when the following conditions are true:

  • People use personal rather than shared workstations. Authentication processing for people who use shared workstations can be controlled by specific conditional access policies.
  • Strong multi-factor authentication is in place to ensure that the initial authentication is secure and is unlikely to be compromised by external attackers. In other words, use the Microsoft authenticator app or passkeys.
  • Conditional access policies are in place to impose a reasonable sign-in frequency. Monthly seems about right. After using a weekly frequency for the last few years (for one tenant that I access frequently as a guest), I think this interval creates too much friction.

As always, the first order of business is to prevent user accounts being compromised. If an account is not compromised, KMSI is unlikely to cause a problem. The widespread adoption of continuous access evaluation by Microsoft 365 workloads makes closing off compromised account access easier, but that’s no excuse to avoid deploying strong multifactor authentication everywhere to protect every Microsoft 365 account.

Configuring Keep Me Signed In

To configure KMSI for everyone in a tenant, use the checkbox in User settings in the Entra admin center (Figure 2). KMSI is either enabled or disabled. It can’t be enabled for a specific group of users and disabled for everyone else.

Entra admin center option to control KMSI.
Figure 2: The Entra admin center option to control KMSI

KMSI is Fine in the Right Conditions

Microsoft 365 users have enough on their plate to cope with the ongoing and constant change in the apps they use daily. Reducing friction from sign-ins through features like KMSI seems like a good idea, providing it can be done securely and doesn’t compromise the tenant. Deploying strong multifactor authentication and effective conditional access policies go a long way to establishing the right conditions for KMSI. But if your tenant is open to compromise because it still uses single factor authentication (passwords) or lets people use weak multifactor authentication methods, don’t blame KMSI when you are compromised. At that point, persistent authentication cookies are the least of your worries.


So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across the Microsoft 365 ecosystem. Subscribe to the Office 365 for IT Pros eBook to receive insights updated monthly into what happens within Microsoft 365, why it happens, and what new features and capabilities mean for your tenant.

]]>
https://office365itpros.com/2025/09/17/kmsi-good-or-bad/feed/ 1 70756
People Settings Appear in the Microsoft 365 Admin Center https://office365itpros.com/2025/09/04/microsoft-365-profile-card-2/?utm_source=rss&utm_medium=rss&utm_campaign=microsoft-365-profile-card-2 https://office365itpros.com/2025/09/04/microsoft-365-profile-card-2/#respond Thu, 04 Sep 2025 07:00:00 +0000 https://office365itpros.com/?p=70629

Customize the Microsoft 365 Profile Card Through the Admin Center

A July 3 LinkedIn post by Microsoft’s Wictor Wilén revealed that the Settings section of the Microsoft 365 admin center now has a People Settings section where tenant administrators and people administrators can customize the properties that appear on the Microsoft 365 people card. Figure 1 shows the properties available for inclusion in the profile card.

Selecting properties for display on the Microsoft 365 profile card.
Figure 1: Selecting properties for display on the Microsoft 365 profile card

No Custom Properties

The thing about the set of properties shown by the Microsoft 365 admin center is that they don’t include any custom properties (the Exchange custom properties referred to by Entra ID as the on-premises extension properties) added to the profile card through the Microsoft Graph. To keep focus, properties ingested from another source, like Copilot connectors for people data, are out of scope for this discussion.

For example, the profile card for my tenant features three custom properties for Employee Id, Employee Type, and Cost Center (see this article about how to customize the Microsoft 365 user profile card with PowerShell). Similar properties are in the list shown in Figure 1, but these are Entra ID properties rather than the custom properties I used.

Two of the properties (EmployeeId and EmployeeType) have been available in Entra ID for several years, but never featured in the set surfaced in the profile card (which is why I used custom attributes)

The CostCenter property is part of the CompanyDetail Graph resource type that’s currently in beta. Looking at the other optional properties listed in Figure 1, we find that the Division property is also part of the CompanyDetail resource, while the Role property is part of the PositionDetail resource, another beta resource.

Because these properties are in beta, you cannot update them for user accounts through the Entra admin center or Microsoft 365 admin center. Instead, you’ll need to use cmdlets like New-MgBetaUserProfilePosition (which updates the Role property), or the Update-MgUser cmdlet to update the CostCenter and Division properties. As shown below, after updating the properties for a user account, the Get-MgUser cmdlet can retrieve the updated values:

$Parameters = @{
  employeeOrgData = @{
   "costCenter" = "881-22993"
   "division" = "CEO Office"
  }
}

Update-MgUser -UserId Tony.Redmond@office365itpros.com -BodyParameter $Parameters

Get-MgUser -Userid Tony.Redmond@office365itpros.com -Property id, displayName, userPrincipalName, employeeOrgData | Select-Object -ExpandProperty employeeOrgData

CostCenter Division
---------- --------
881-22993  CEO Office

Microsoft 365 Profile Card In a State of Transition

The Microsoft 365 profile card is in a state of transition. Some properties come from Entra ID, and some come from SharePoint Online, which can end up in a messy profile card with apparent duplications. Add in some custom properties derived from Exchange custom attributes, and it’s a recipe for confusion.

The good news is that Microsoft is attempting to solve the problem by defining the properties required by Microsoft 365 tenants in Graph resources. If Graph resources include rich descriptions of about people and their roles, then there’ll be no need to use custom properties.

Tenants use custom properties today because it has been the only way to present customized information about people on the profile card. A migration to move values from custom to standard properties will have to occur in the future. For example, my tenant needs to extract values from the custom properties used by the profile card today and transfer the values to the default Graph properties. The transition will be completed by updating the set of properties shown on the user profile card to use the default rather than custom properties. You can download a script from the Office365itpros GitHub repository that demonstrates the general principle.

More Settings for the Microsoft 365 Profile Card to Come

The big expansion of the People Settings section in the Microsoft 365 admin center needs to be filled with more than the single option to customize the people card. I expect that Microsoft will use the page to present all the settings that affect the profile card, including the display of personal pronouns and name pronunciation recordings. It just takes time to roll these changes out, but I wouldn’t be surprised to see the other settings appear in the Microsoft 365 admin center before the end of the year.


Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2025/09/04/microsoft-365-profile-card-2/feed/ 0 70629
Reporting Authentication Method Usage Data via the Graph https://office365itpros.com/2025/08/21/authentication-methods-graph/?utm_source=rss&utm_medium=rss&utm_campaign=authentication-methods-graph https://office365itpros.com/2025/08/21/authentication-methods-graph/#respond Thu, 21 Aug 2025 07:00:00 +0000 https://office365itpros.com/?p=70383

New Summary Methods for Entra ID Authentication Methods

Three new Entra ID Graph resources are available in the authentication methods usage reports API to help track adoption of authentication methods, especially the progress towards usage of strong authentication methods for multifactor authentication. It’s easy enough to create a report detailing the authentication methods in use with PowerShell. These resources deliver summary information in a very accessible manner, providing that you have the necessary Entra P1 or P2 licenses needed to access report data via the Graph.

Microsoft doesn’t add to the Graph without good reason. In this case, it’s likely that these resources and methods will be used for reporting in the Entra admin center. Microsoft Graph PowerShell SDK cmdlets aren’t available for the APIs yet, so the examples below use the Invoke-MgGraphRequest cmdlet to run requests. Like all Entra ID sign-in data, information is available for the last 30 days. To run the requests, the Graph SDK session needs the AuditLog.Read.All permission and the signed-in user must hold one of the roles specified in the documentation like Reports Reader or Security Administrator.

User Events Summary

The userEventsSummary resource provides a list of events associated with user activities to register an authentication method or reset using SSPR. The method supports several different filters to allow events to be found based on user principal name, display name, success or failure, and authentication method. Here’s an example which looks for every matching event that’s available. Only one event is available in my tenant, where a user set up SMS as a secondary authentication method. This isn’t a strong authentication method and the user deserves to be nagged to use a stronger method, like the Microsoft authenticator app.

$Uri = 'https://graph.microsoft.com/beta/reports/authenticationMethods/userEventsSummary'
[array]$Data = Invoke-MgGraphRequest -Uri $Uri -Method GET -OutputType PSObject | Select-Object -ExpandProperty Value

$Data

id                : d93bdb0a-8cb7-4303-a8ef-48c488997c38
feature           : registration
userPrincipalName : John.Jameson@office365itpros.com
userDisplayName   : John Jameson
isSuccess         : True
authMethod        : sms
failureReason     :
eventDateTime     : 01/08/2025 12:35:30

User Registration Method Summary

The userRegistrationMethodSummary resource is a summary of the number of users registered for each authentication method. To access the summary, run the request as follows:

$Uri = "https://graph.microsoft.com/beta/reports/authenticationMethods/usersRegisteredByMethod(includedUserTypes='all',includedUserRoles='all')"
[array]$data = Invoke-MgGraphRequest -Uri $Uri -Method Get -OutputType PSObject | Select-Object -ExpandProperty userRegistrationMethodCounts

$Data

authenticationMethod               userCount
--------------------               ---------
password                                   0
email                                     11
mobilePhone                              126
alternateMobilePhone                       0
officePhone                                0
microsoftAuthenticatorPush               115
softwareOneTimePasscode                   15
hardwareOneTimePasscode                    0
microsoftAuthenticatorPasswordless         2
windowsHelloForBusiness                    1
fido2SecurityKey                           0
temporaryAccessPass                        0
securityQuestion                           0
macOsSecureEnclaveKey                      0
passKeyDeviceBound                         1
passKeyDeviceBoundAuthenticator            1
passKeyDeviceBoundWindowsHello             0
externalAuthMethod                         0

User MFA Sign-In Summary

The userMfaSignInSummary list method is the one that interested me the most. Essentially, the report gives a daily sign-in report for 30 days detailing the total sign-ins and the number for MFA and single-factor sign-ins. A record looks like this:

id                  : f7c6b675-e664-44dc-9523-947501b0fac6
createdDateTime     : 15/07/2025 00:00:00
totalSignIns        : 59
singleFactorSignIns : 17
multiFactorSignIns  : 42

I used the report to interrogate the sign-in audit log for days when single-factor sign-ins occurred to discover which accounts and apps are involved. The command used to find single-factor sign-ins for a day looks like this (the beta cmdlet is used because the V1.0 cmdlet doesn’t currently return the authentication requirement):

[array]$SignInRecords = Get-MgBetaAuditLogSignIn -Filter "createdDateTime gt $StartDate and createdDateTime lt $EndDate and AuthenticationRequirement eq 'singleFactorAuthentication' and status/errorCode eq 0" -Sort "createdDateTime DESC"

Figure 1 shows the report output. The idea is that administrators can use this information to figure out why single-factor authentications still happen. Most of the accounts highlighted by the report are guest accounts that don’t use MFA because I disabled the conditional access policy to enforce MFA for guest accounts.

Report summarizing non-MFA sign-ins by users and apps.

Authentication methods.
Figure 1: Report summarizing non-MFA sign-ins by users and apps

You can download the full script from the Office 365 for IT Pros GitHub repository.

Looking for Usage

Any API is only useful if it has a purpose. Hopefully, these notes spark some ideas for how to use the report data in campaigns to improve multifactor adoption within Microsoft 365 tenants. At least, that’s the general idea…


Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2025/08/21/authentication-methods-graph/feed/ 0 70383
Entra ID Governance Levies Charges for Guest Accounts https://office365itpros.com/2025/07/29/entra-id-governance-levies-charges/?utm_source=rss&utm_medium=rss&utm_campaign=entra-id-governance-levies-charges https://office365itpros.com/2025/07/29/entra-id-governance-levies-charges/#respond Tue, 29 Jul 2025 07:00:00 +0000 https://office365itpros.com/?p=70165

Monthly Fee for Guest Accounts That Use ID Governance Features

I recently revisited Entra ID access reviews to find a banner warning about charges for guest accounts that consume Entra ID Governance features (Figure 1). Apparently, the new charges started in June 2025 and are paid for on a metered basis through an Azure subscription associated with the Entra tenant.

Entra ID warns of new changes for ID Governance features.
Figure 1: Entra ID warns of new changes for ID Governance features

The relevant documentation reveals the set of chargeable features for guest accounts. Access reviews for inactive guest accounts are on the list, and that’s why the banner showed up.

Charging is on a monthly active user basis (MAU). This is not the same as the MAU for general guest access to Microsoft 365 groups, teams, and sites, which covers normal activities for up to 50,000 guest accounts monthly. In this case, a monthly active user is any guest account that takes advantage of one of the listed Entra ID governance feature during a month. Every ID Governance MAU incurs a charge of $0.75 (six times the price charged for guest accounts that surpass the 50,000 MAU threshold for normal activity).

Going back to access reviews, if my calculation is correct, a tenant using access reviews to detect and remove inactive guests (as recommended by Microsoft) with access reviews scheduled on a quarterly basis will incur a $3 cost per guest account (4 x 0.75 x number of guests). That might seem like small beans, but costs have a corrosive habit of accruing over time.

DIY Inactive Guest Reviews

It’s not as if Microsoft performs any great magic to detect inactive guests. It’s perfectly feasible to write your own inactive guest removal process and schedule the process using Azure Automation. Your version might not be as pretty as Microsoft’s is, but you can build more intelligence into the review by including searches against audit log data to detect activities other than sign-ins. And a DIY process won’t require Entra P2 licenses either.

Coding a Report of Likely Costs

The Microsoft documentation includes the helpful advice that “You can identify actions that will be billed to the Microsoft Entra ID Governance for guests add-on by looking at your audit logs.” Even a small tenant will have large quantities of data in the Entra ID audit log, so some automation is needed. The data from the Entra ID audit log is eventually ingested into the unified audit log, but in this case, we’ll work with the Entra ID data.

The steps required to find audit log entries that mark chargeable transactions are:

Run the Connect-MgGraph cmdlet to open an interactive Microsoft Graph session. The session needs consent for the AuditLog.Read.All permission, and the signed in user must be an administrator with a role that allows access to the audit logs, like Reports Reader or Security administrator. Finally, the account must have an Entra P1 license, which is needed for API access to audit logs.

Now run the Get-MgAuditLogDirectoryAudit cmdlet to retrieve the audit log entries to analyze. Because Microsoft bills monthly, it seems logical to fetch the logs for the current month:

$FirstDayOfMonth = (Get-Date -Day 1).ToString('yyyy-MM-ddT00:00:00Z')
[array]$AuditRecords = Get-MgAuditLogDirectoryAudit -All -Filter "activityDateTime ge $FirstDayOfMonth and result eq 'success'"

I can’t find a good server-side filter to find the audit records for chargeable events, so a client-side filter does the trick:

[array]$GovernanceRecords = $AuditRecords | Where-Object { $_.additionalDetails.key -eq "GovernanceLicenseFeatureUsed"}

The next part scans the governance records to find if guest users are involved. If so, data is extracted and reported:

If  ('"Guest"' -in $Record.TargetResources.ModifiedProperties.NewValue) {
     $UserDisplayName = ($Record.TargetResources.ModifiedProperties | Where-Object {$_.DisplayName -eq "DisplayName"}).NewValue
     $UserEmail = ($Record.TargetResources.ModifiedProperties | Where-Object {$_.DisplayName -eq "Email"}).NewValue 
     $UserUPN = ($Record.TargetResources.ModifiedProperties | Where-Object {$_.DisplayName -eq "PrincipalName"}).NewValue
     $UserId = ($Record.TargetResources.ModifiedProperties | Where-Object {$_.DisplayName -eq "TargetId"}).NewValue
}

After all records are processed, a report file containing every chargeable event for guest records is available. To reduce the set to individual users, the script sorts the data to extract unique values:

[array]$GovernanceUsers = $GovernanceReport | Sort-Object UserId -Unique | Select-Object UserId, UserDisplayName, UserEmail, UserUPN

Finally, the script reports the results (Figure 2).

Reporting guest accounts that used Entra ID Governance features.
Figuire 2: Reporting guest accounts that used Entra ID Governance features

You can download the script I wrote from the Office 365 IT Pros GitHub repository.

No Great Insight from Azure Billing

The billing reports for the Azure subscription that is charged has a line item for “P2 monthly active users.” I haven’t seen a detailed list of the guest accounts covered by the charge. Perhaps Microsoft will include this information in the future. If not, I guess it should be easy to correlate the charges levied against a subscription with the list of guest accounts extracted from the audit logs.


Learn more about how the Microsoft 365 applications really work on an ongoing basis by subscribing to the Office 365 for IT Pros eBook. Our monthly updates keep subscribers informed about what’s important across the Office 365 ecosystem.

]]>
https://office365itpros.com/2025/07/29/entra-id-governance-levies-charges/feed/ 0 70165
Entra ID Introduces Linkable Token Identifiers for Audit Events https://office365itpros.com/2025/07/24/linkable-token-identifier/?utm_source=rss&utm_medium=rss&utm_campaign=linkable-token-identifier https://office365itpros.com/2025/07/24/linkable-token-identifier/#comments Thu, 24 Jul 2025 07:00:00 +0000 https://office365itpros.com/?p=70140

Identifier Makes it Easier to Track User Activities in a Single Session

An interesting July 21 Technical Community announcement describes the introduction of linkable token identifiers for audit events. Essentially, when a user authenticates a session with Entra ID, the session is stamped with a unique GUID (the linkable token identifier). The linkable token identifier is persistent for the session and is inherited by workloads that support the token and inserted into the audit events generated by those workloads accessed during the session.

The idea is that by tracing the linkable token identifier, you can find out what the user did during a session. Linking audit events through the new identifier makes it easier for investigators to query audit data to discover the set of actions taken during a session and their sequencing. This might be necessary to check if an attacker has compromised an account and is exfiltrating data or taking other actions that you don’t want to happen. Of course, account compromise is less likely to happen when user accounts are protected with strong multifactor authentication like the Microsoft Authenticator app, but that’s another story.

The linkable token identifiers are now available in Entra sign-in logs, Exchange Online audit events, SharePoint Online audit events, Teams audit events, and Graph activity logs. Figure 1 shows the identifiers listed in the Entra sign-in log.

Linkable token identifiers listed in the Entra sign-in log.
Figure 1: Linkable token identifiers listed in the Entra sign-in log

Using Audit Searches to Track Activities

Audit events end up in the unified audit log and the article includes a screen shot showing the results of a search using a linked token identifier. Unhappily, the article doesn’t explain that you must use a keyword search to find events linked to a certain identifier (Figure 2).

Setting up an audit log search using a linkable token identifier.
Figure 2: Setting up an audit log search using a linkable token identifier

The reason why a keyword (free text) search is necessary is that workload developers are inconsistent in how they include linkable token identifiers in the AuditData payload of their events. As you’d expect, Entra ID includes a simple SessionId property in the payload, but other workloads like Exchange Online and SharePoint Online refer to the token as AADSessionId.

Finding and Reporting Activities Based on Identifiers

Which brings me to subject of how to search the audit log with PowerShell for all events with a linkable token identifier. The process is reasonably simple. For example, using the Search-UnifiedAuditLog cmdlet, the code is something like this:

[array]$Records = Search-UnifiedAuditLog -Formatted -StartDate $StartDate -EndDate (Get-Date) -UserIds $UserId -FreeText $Session -SessionCommand ReturnLargeSet -ResultSize 5000
  If ($Records.Count -eq 0) {
    Write-Host "No audit records found for session $Session"
    Continue
  } Else {
    $Records = $Records | Sort-Object Identity -Unique
    $Records = $Records | Sort-Object {$_.CreationDate -as [datetime]} 
    Write-Host ("Found {0} audit records for session {1}" -f $Records.Count, $Session)
}

Reporting Audit Events for All Sessions

The code uses a free text search to find all audit events that include the specified linkable token identifier between two dates, removes any duplicates events from the returned set, and sorts the set by the created date.

But how about extending this to generate a report for all events for all sessions within the 30-day period that Entra ID retains sign-in logs. After all, multiple sessions might be created around the same time, and only one of the sessions might be suspicious. To do this, we need to find the full set of sign-in events captured for a user and find the linkable token identifiers in that set. Here’s how to find that information using the Get-MgBetaAuditLogSignIn cmdlet from the Microsoft Graph PowerShell SDK (the beta cmdlet is needed to return the identifiers):

[array]$Logs = Get-MgBetaAuditLogSignIn -Filter "userPrincipalName eq 'lotte.vetler@office365itpros.com'" -All
[array]$Sessions = $Logs | Group-Object SessionId -NoElement | Select-Object -ExpandProperty Name

The $Sessions array now contains the linkable token identifiers, and to generate the report, it’s a matter of looping through the set of identifiers to use each with Search-UnifiedAuditLog to generate the set of audit events for the session. Figure 3 shows the kind of report that can be created from the generated data.

HTML report of a user’s audit events divided into sessions.
Figure 3: HTML report of a user’s audit events divided into sessions

The code I used to create the report is available for download from the Office 365 for IT Pros GitHub repository. In addition to the HTML report, the script also generates a CSV or XLSX file (an Excel worksheet is created if the ImportExcel module is available).

Good for Investigators

I’m sure that investigators will appreciate being able to easily connect the dots to discover what happened during a session. Adding linkable token identifiers to audit events is an example of a low-touch, high-value enhancement for Microsoft 365 tenants. It would be nice if all updates had the same impact.


Need some assistance to write and manage PowerShell scripts for Microsoft 365? Get a copy of the Automating Microsoft 365 with PowerShell eBook, available standalone or as part of the Office 365 for IT Pros eBook bundle.

]]>
https://office365itpros.com/2025/07/24/linkable-token-identifier/feed/ 3 70140
Token Protection Extends to Microsoft Graph PowerShell SDK Sessions https://office365itpros.com/2025/06/26/token-protection-graph-sdk/?utm_source=rss&utm_medium=rss&utm_campaign=token-protection-graph-sdk https://office365itpros.com/2025/06/26/token-protection-graph-sdk/#respond Thu, 26 Jun 2025 07:00:00 +0000 https://office365itpros.com/?p=69782

Token Protection, PRTs, Device Binding, and Session Keys

Last year, I discussed how to use a conditional access policy to apply a new session control called token protection. The idea is to protect against token theft by requiring connections to have a token (the Primary Refresh Token, or PRT) that has a “cryptographically secure tie” with the device that the connection originates from. The PRT is “bound” to a device key that’s securely stored in the device’s Trusted Platform Module (TPM). PRTs are supported on Windows 10 or later devices.

The PRT is an “opaque blob” that’s specific to a user account and device. The Entra ID authentication service issues a PRT following a successful connection by a user when the device is registered, joined, or hybrid joined. Entra ID also issues a session key, an encrypted symmetric key to serve as proof of possession when a PRT attempts to obtain tokens for applications. If an attacker attempts to hijack a connection with an access token they’ve stolen, they’ll fail because they don’t have access to the device key.

Why Does This Matter?

As noted in my article last year, it’s possible to create a conditional access policy with a session control requiring token protection. In other words, when a connection attempts to satisfy the conditions of the policy, it must be able to prove that its PRT is bound to the device where the connection originates and the user making the request. This process is managed by a component called Web Account Manager (WAM).

But conditional access policies can only work if everything involved in the connection understand what’s going on. At the time I wrote the last article, limited support existed for token protection. The reason for this article is that interactive Microsoft Graph PowerShell SDK sessions now support token protection (see details about support for token protection by other applications here). This opens the possibility of extending additional protection for administrators and developers who might work on sensitive data through the Graph SDK.

The reason why you might want to do this is revealed in a recent Entra ID change that shows the resources a user can access when they satisfy a conditional access policy to connect. In this case, the connection is to an interactive Graph PowerShell SDK session, and the resources available in that session depends on the delegated permissions held by the Microsoft Graph Command Line Tools service principal. The set of permissions tends to swell over time as administrators grant consent to permissions needed to work with different cmdlets, but as Figure 1 shows, a Graph PowerShell SDK session can have access to many different resources.

Conditional access policy signin reveals the Resources accessible to the Microsoft Graph PowerShell SDK.
Figure 1: Resources accessible to the Microsoft Graph PowerShell SDK

Enabling Token Protection for Graph Interactive Sessions

Normally, interactive Graph PowerShell SDK sessions don’t use WAM. To enable WAM for Graph sessions, run the Set-MgGraphOption cmdlet before running Connect-MgGraph. As the documentation says, the cmdlet sets global configuration options, so the configuration setting stays in force for all Microsoft Graph interactive sessions on the workstation until it is reversed.

Set-MgGraphOption –EnableLoginByWAM $true
Connect-MgGraph

If the device isn’t registered or joined, the conditional access policy condition for token protection isn’t satisfied and the sign-in attempt is rejected with a 530084 error code. The cause is obvious if you examine the policy details captured in the sign-in event (Figure 2).

The token protection session  control for a conditional access policy rejects a connection attempt.
Figure 2: The token protection session control rejects a connection attempt

WAM doesn’t affect app-only authentication for the Graph SDK, including Azure Automation runbooks that use modules and cmdlets from the Graph PowerShell SDK.

Token Protection and Elevated PowerShell Sessions

The Web Account Manager option doesn’t work in elevated PowerShell sessions (run as administrator). Attempts to connect fail with the error “InteractiveBrowserCredential authentication failed: User canceled authentication.

The solution is two-fold. First, revert to normal authentication on the workstation by running the Set-MgGraphOption cmdlet to set EnableLoginByWAM to $false. If you don’t, authentication fails because a protected token isn’t available (Figure 3). The second step is to remove users who need to run Graph cmdlets in elevated PowerShell sessions from the scope of the conditional access policy. This avoids the user running into problems on other workstations.

Failure to connect because a conditional access policy condition requires a protected token that isn’t available.
Figure 3: Failure to connect because a conditional access policy condition requires a protected token that isn’t available

Token Protection and Microsoft Graph PowerShell SDK Versions

The WAM option also doesn’t work with the latest versions of the Microsoft Graph PowerShell SDK. This is likely due to Microsoft’s decision to remove support for .NET6 from V2.25 on. In V2.28 of the SDK, the error when running Connect-MgGraph is:

InteractiveBrowserCredential authentication failed: Could not load type 'Microsoft.Identity.Client.AuthScheme.TokenType' from assembly 'Microsoft.Identity.Client, Version=4.67.2.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae'.

While Microsoft gets their act together and decides how to fix the issue, the only option is to remain using V2.25. PCs that have upgraded to the current V2.28 release must downgrade to V2.25.

Token Protection is Just Another Tool

Token protection is not for everyone. Its linkup with conditional access policies is another tool for administrators to consider when figuring out how to secure their tenant. My recommendation is that you test the feature and make a measured decision whether it has any value for your organization. Remember that this is an evolving space and other applications are likely to support token protection over time. Maybe one of those applications will be exactly the one you want to secure.


Support the work of the Office 365 for IT Pros team by subscribing to the Office 365 for IT Pros eBook. Your support pays for the time we need to track, analyze, and document the changing world of Microsoft 365 and Office 365.

]]>
https://office365itpros.com/2025/06/26/token-protection-graph-sdk/feed/ 0 69782
Microsoft to Block Users Granting Third-Party App Access to User Sites and Files https://office365itpros.com/2025/06/19/app-consent-policy-user-app-consent/?utm_source=rss&utm_medium=rss&utm_campaign=app-consent-policy-user-app-consent https://office365itpros.com/2025/06/19/app-consent-policy-user-app-consent/#comments Thu, 19 Jun 2025 13:30:35 +0000 https://office365itpros.com/?p=69714

New Microsoft-Managed App Consent Policy to Control User Consent for Apps

Message center notification MC1097272 (17 June 2025) announces Microsoft’s intention to restrict access to some legacy protocols and introduce a new managed app consent policy to the ability of users to grant consent to third-party apps that want access to files and sites.

Microsoft says that they are updating default settings to help Microsoft 365 tenants “meet the minimum security benchmark and harden your tenant’s security posture.” As far as I can tell, this appears to be a reference to section IM-2 of the Microsoft cloud security benchmark. For good measure, Microsoft throws in the Secure Future Initiative and Secure by Default principle to provide further justification for the change.

No Problem with Blocking Obsolete and Insecure Protocols

I don’t think anyone will complain about blocking browser access to SharePoint and OneDrive via the Relying Party Suite (RPS – another relatively unknown component for most Microsoft 365 tenants). Legacy protocols are blocked in the SharePoint tenant configuration, and this change reinforces the block.

Get-SPOTenant | Select-Object LegacyBrowserAuthProtocolsEnabled

LegacyBrowserAuthProtocolsEnabled
---------------------------------
                             True

Likewise, I don’t think anyone will complain about blocking the FrontPage Remote Procedure Call (FPRPC) protocol for Office file opens. It’s an outdated protocol that attackers have leveraged (here’s an example).

App Consent Policy to Prevent Third-Party Access to Files and Sites

My interest was drawn to the third block, which will introduce a Microsoft-managed app consent policy to require administrator consent for third-party apps that access files and sites. There are a bunch of app consent policies already present in tenants that you can see by running the Get-MgPolicyPermissionGrantPolicy cmdlet from the Microsoft Graph PowerShell SDK (any policy prefixed by “microsoft” is a Microsoft-managed app consent policy):

Get-MgPolicyPermissionGrantPolicy | Format-Table Id, DisplayName, Description -AutoSize

Like many other Microsoft 365 policies, the policy is a container, and the real settings (“condition sets”) are found by running the Get-MgPolicyPermissionGrantPolicyInclude cmdlet. For example, this app consent policy allows administrators to manage all aspects of all apps in a tenant:

Get-MgPolicyPermissionGrantPolicyInclude -PermissionGrantPolicyId "microsoft-application-admin" | Format-List

ClientApplicationIds                        : {all}
ClientApplicationPublisherIds               : {all}
ClientApplicationTenantIds                  : {all}
ClientApplicationsFromVerifiedPublisherOnly : False
Id                                          : 811d2da7-443c-43da-96e7-28d285b234e9
PermissionClassification                    : all
PermissionType                              : application
Permissions                                 : {all}
ResourceApplication                         : any
AdditionalProperties                        : {}

ClientApplicationIds                        : {all}
ClientApplicationPublisherIds               : {all}
ClientApplicationTenantIds                  : {all}
ClientApplicationsFromVerifiedPublisherOnly : False
Id                                          : 60461179-740e-4d8b-9e00-1456a338c44b
PermissionClassification                    : all
PermissionType                              : delegated
Permissions                                 : {all}
ResourceApplication                         : any
AdditionalProperties                        : {}

For more details, see the Graph documentation for permission grant policies. There’s no UX in the Entra admin center to manage app consent policies. This article throws more light onto how to build your own app consent policies.

The Change Doesn’t Affect All Tenants

The change only affects tenants that use the default user consent settings. In my case, I changed the settings (Figure 1) to allow users to consent to a set of low-impact permissions such as User.Read. The Sites.Read.All or Files.Read permissions are definitely not in the low-impact category, which is why Microsoft is blocking them.

User Consent Settings - App Consent Policy
Figure 1: Blocking the ability for users to register applications

If you allow users to consent to permissions, you should monitor the consents to make sure that apps are not asking for unexplainable permissions. You can check permission consents through audit records. You’ll also need to make sure that the admin consent request workflow is operational.

See this article for a script to use the Microsoft Graph PowerShell SDK to generate a report of all delegated permission grants that exist in a tenant. The script reports permissions that apply to all users (consent type of “AllPrincipals”) and specific users (“Principal”). It ignores common permissions such as those required to read the user’s own profile and focuses on permissions that might disclose information if used badly. If you’re concerned about the permissions users have already granted to their data, filter on the grants of the Principal consent type and review those.

The ChatGPT Conundrum

What’s interesting about Microsoft’s move is that it neatly blocks the ability of users to grant consent for the permissions needed by the ChatGPT app to upload files from SharePoint Online and OneDrive for Business for processing by one of the ChatGPT models. A cynic might say that Microsoft is taking this step to make sure that Microsoft 365 Copilot has sole access to files stored in SharePoint Online and OneDrive for Business. A more benign reading is that Microsoft is simply making sure that users can’t inadvertently grant access to third-party apps to access and read their Microsoft 365 files.

In any case, I don’t think people should upload files to ChatGPT because this activity creates all sorts of security concerns. Fortunately, it’s easy to find and block the ChatGPT app if it’s already in a tenant. In addition, ChatGPT cannot process encrypted files protected by sensitivity labels because it doesn’t have the access right needed to open protected files.

Don’t Drop Your Guard

No can argue that we do need to do better to secure tenants, so the changes proposed by Microsoft are welcome. The changes will begin rolling out in mid-July and are due to be in all tenants sometime in August 2025.

There are still too many tenants that don’t protect user accounts with multifactor authentication, which is why bad actors keep using password spray attackers in an attempt to compromise accounts. A recent report describes a password spray attack by a group called SneakyStrike against Entra ID accounts. The report is a little overhyped, but it’s a good reminder that attackers still patiently look for weak tenants to penetrate.


Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2025/06/19/app-consent-policy-user-app-consent/feed/ 4 69714
Updating the Entra ID Custom Banned Password List with PowerShell https://office365itpros.com/2025/06/19/custom-banned-password-list/?utm_source=rss&utm_medium=rss&utm_campaign=custom-banned-password-list https://office365itpros.com/2025/06/19/custom-banned-password-list/#respond Thu, 19 Jun 2025 07:00:00 +0000 https://office365itpros.com/?p=69687

Use Microsoft Graph PowerShell SDK Cmdlets to Maintain the Entra ID Custom Banned Password List

Vasil Michev is busy these days. Apart from his day job, he’s doing the technical reviews for the Office 365 for IT Pros (2026 edition) and Automating Microsoft 365 with PowerShell (2nd edition) eBooks, both due for release on July 1, 2025. Technical editing is an important part of our publication process because it’s an annual end-to-end review of all content to help authors refine their chapters, eliminate old and unnecessary text, and consider what they should be covering.

And still Vasil finds time for his own writing, such as a recent article about using the Microsoft Graph PowerShell SDK to update the banned password list for Entra ID accounts. Given that the Graph PowerShell SDK is a major topic for Automating Microsoft with PowerShell, my attention was immediately drawn to the article to understand what it described and consider it for inclusion in the book. It is now, along with 350+ pages of other PowerShell content about automating different aspects of Microsoft 365 activities.

Global Banned Password List

The Entra ID password protection feature maintains a global list of banned passwords. Microsoft maintains the list and updates it on an ongoing basis from telemetry for Entra ID authentication. All attempts to change account passwords are checked against the global banned list to make sure that the new password is reasonably strong. In other words, it’s not something like “Mypassword” or “Cats.” Tenant administrators cannot affect how Entra ID uses the global list of banned passwords, nor can they add or remove values from the list. It’s just part of how Entra ID works, and this part of password protection is included in the version of Entra ID included with all Microsoft 365 tenants.

Custom Banned Password List

If a tenant has Entra P1 or P2 licenses, they can implement a custom banned password list. The custom list supplements the global banned password list. The custom list is limited to 1,000 entries, but those entries are “key base terms” of between 4 and 16 characters. In other words, Entra ID blocks variations and combinations of the terms in the custom banned password list.

When a custom banned password list is available, Entra ID combines its entries with the global banned password list. The idea is that tenants might want to stop people using organization-specific terms like the names of locations or buildings in passwords because these terms might be easy for attackers to guess in a spray attack. Of course, you shouldn’t be depending on passwords and should deploy multifactor authentication to protect accounts, but it’s worthwhile protecting passwords as much as possible.

Blocking Passwords

Figure 1 shows some of the entries in the custom banned password list as viewed through the Entra admin center. You can see that the last entry is for “VictorMeldrew.” This is a key base term for password checking.

The custom banned password list in the Entra admin center.
Figure 1: The custom banned password list in the Entra admin center

In Figure 2, an administrator has attempted to change an account password through the Microsoft 365 admin center. The password looks strong, but Entra ID rejects it because it includes a key base term. Telling the administrator that the password is easily guessable is just the way Microsoft chose to say: “can’t use that password.”

The custom banned password list stops a password based on a key base term.
Figure 2: : The custom banned password list stops a password based on a key base term

Updating the Custom Banned Password List with a Script

Vasil’s article covers the basics of creating a directory settings object to hold password protection settings, including the custom banned password list. I used that information to create a script that’s more like something you might use as production code, which you can download from GitHub.

The code:

  • Checks if the correct permission (Directory.ReadWrite.All) is available to read, create, and update directory settings. This is a very high-level permission that should be restricted as tightly as possible. You should also monitor the apps that hold this permission to make sure that they are used correctly.
  • Import a list of key base terms from a CSV file and checks that each term is at least 4 and no more than 16 characters long.
  • Uses the Get-MgBetaDirectorySetting cmdlet to check if a directory setting object for password protection is defined in the tenant. If not, the script runs the New-MgBetaDirectorySetting cmdlet to create and populate a new directory setting object with the list of key base terms (and other default values). The directory setting object is derived from the directory settings template for password rules. The template always has an identifier of 5cf42378-d67d-4f36-ba46-e8b86229381d.
  • If a directory setting object for password protection is available, fetch the list of current key base terms and combine it with the new list to generate a combined list. The Update-MgBetaDirectorySetting cmdlet then updates the directory setting object with the combined list.
  • Export the newly-updated list to a CSV file.

If you prefer to use the input CSV file as the definitive set of key base terms and not combine the input set with the current set, it’s easy to comment out the two lines that create a combined list.

The only semi-weird thing about the list of key base terms is that it uses tabs for delimitation (which is why the code splits the list using [char]9).

Hopefully the script is of some use. If not, I won’t be offended. Check out the 320-plus scripts in the Office365Itpros GitHub repository. You might find something more useful there!


Need some assistance to write and manage PowerShell scripts for Microsoft 365? Get a copy of the Automating Microsoft 365 with PowerShell eBook, available standalone or as part of the Office 365 for IT Pros eBook bundle.

]]>
https://office365itpros.com/2025/06/19/custom-banned-password-list/feed/ 0 69687
SharePoint Online Dumps OTP Authentication for Sharing Links https://office365itpros.com/2025/06/10/entra-id-b2b-collaboration-spo/?utm_source=rss&utm_medium=rss&utm_campaign=entra-id-b2b-collaboration-spo https://office365itpros.com/2025/06/10/entra-id-b2b-collaboration-spo/#comments Tue, 10 Jun 2025 07:00:00 +0000 https://office365itpros.com/?p=69570

Change Applies on July 1 to Tenants that Integrated SharePoint with Entra ID B2B Collaboration

The announcement in message center notification MC1089315 (6 June 2025) that Microsoft is dumping the old one-time passcode (OTP) authentication mechanism for SharePoint Online and OneDrive for Business sharing is unexpected, but only because it took Microsoft so long to make the change.

Inputting a one-time passcode to access a shared file.

Entra ID B2B Collaboration
Figure 1: Inputting a one-time passcode to access a shared file

After July 1, 2025, external users who have received a sharing link from a user in a tenant that uses OTP authentication will discover that they have lost access to the shared content (files, folders, or sites). Microsoft says that they’re making the change to “enhance security.” I think this is correct, and the change delivers an additional benefit to Microsoft because it gets rid of an old feature.

A History of One-time Passcodes in SharePoint Online

OTP-based sharing links (aka, the “Secure external sharing recipient experience”) predates the support of Entra ID B2B Collaboration (guest accounts) within SharePoint Online. That support arrived as a result of guest access to Office 365 groups (now Microsoft 365 groups) in September 2016. Guest accounts took a while to catch on, and Office 365 groups only became really popular after the advent of Teams in early 2017. Indeed, Teams didn’t surpass 20 million active users in November 2019 before massive growth occurred in Teams usage during the Covid-19 pandemic.

Although Teams growth propelled similar growth in groups and SharePoint usage, there was no great push to move tenants off OTP authentication to SharePoint and OneDrive integration with Azure AD (now Entra ID). External sharing worked, so why bother?

Microsoft began the process to get off OTP by integrating OTP with Entra ID B2B Collaboration in October 2021. Essentially, the change ensured that external users who received OTP sharing links had guest accounts created for them in the tenant directory. The next step made sure that new tenants created after March 31, 2023, could only use B2B collaboration.

The plan now revealed “only impacts organizations that have already enabled or plan to enable SharePoint and OneDrive integration with Microsoft Entra B2B.” In other words, nothing changes for tenants that did not link SharePoint Online and OneDrive for Business to Entra ID B2B Collaboration. I wonder what proportion of the SharePoint community still use one-time passcodes exclusively for sharing.

The Result of the Change

MC1089315 rates this change to be “highly relevant.” In other words, it will affect how users work because:

  • After July 1, all new sharing links generated for external people will use Entra ID B2B Collaboration and the sharees will receive email containing the sharing link generated by the Entra ID Invitation Manager service. This shouldn’t cause too much upheaval because the process is reasonably painless. I use it all the time to share documents with several other Microsoft 365 tenants and haven’t had any issues with sharing links that I can remember.
  • After July 1, all previously issued sharing links based on one-time passcodes generated by SharePoint Online and OneDrive for Business will stop working. Obviously, this aspect of the change could cause confusion when a link sent to users doesn’t work. July 1 is a Tuesday, and it’s entirely possible that many sharing links with one-time passcodes arrive in user mailboxes on Monday, June 30. If the recipients action the links immediately, they can access the shared content. If they delay, the links stop working. It’s as simple as that.

Microsoft says that users will be told “Sorry, something went wrong. This organization has updated its guest access settings. To access this item, please contact the person who shared it with you and ask them to reshare it with you.” What’s gone wrong is that Microsoft decommissioned one-time passcodes. However, the statement is accurate that the only way to resume access to the shared content is to receive a new sharing link generated based on B2B collaboration. The potential for impact on users and the knock-on effect on help desks is clear.

MC1089315 notes that users will be required to complete multi-factor authentication (MFA) registration as part of the Entra ID B2B onboarding process. That’s strictly only true if the tenant that hosts the content requires MFA, most likely with a conditional access policy to block access unless an MFA challenge is satisfied. Even if your tenant doesn’t use MFA today (which it should), it is the hosting tenant that gets to choose whether MFA is required.

A Good Change

I bet this change will cause confusion and some upheaval in the weeks after July 1. After that, everything should calm down as the old OTP-based sharing links work their way out of the system. It’s good to have consistency and security and having one method to secure sharing links seems like a good change to make. At least, it is in my book.


So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across the Microsoft 365 ecosystem. Subscribe to the Office 365 for IT Pros eBook to receive monthly insights into what happens, why it happens, and what new features and capabilities mean for your tenant.

]]>
https://office365itpros.com/2025/06/10/entra-id-b2b-collaboration-spo/feed/ 23 69570
Microsoft Launches Agent Management in the Entra Admin Center https://office365itpros.com/2025/05/27/entra-agent-id/?utm_source=rss&utm_medium=rss&utm_campaign=entra-agent-id https://office365itpros.com/2025/05/27/entra-agent-id/#respond Tue, 27 May 2025 07:00:00 +0000 https://office365itpros.com/?p=69365

Entra Agent ID Debuts as the Basis for Agent Management

Last February, I reviewed the burgeoning presence of agents within Microsoft 365 and asked if the danger existed of agents running amok. In other words, agents could be deployed and run without any kind of tenant control. Three months later, the May 19, 2025, announcement that the Entra admin center will support the management of custom agents is a good step forward in the development of a management framework for agents to match Microsoft’s vision of an agentic future.

Listing Agents in the Entra Admin Center

Microsoft’s announcement says that “it’s critical to track agent identities, manage their lifecycle and permissions, and carefully secure their access to your organization’s resources.” Their answer is Entra Agent ID, which is now available in preview in the Entra admin center. Essentially, the Entra admin center recognizes agents as a type of object that it can manage and surfaces the agents in the Enterprise applications section if an administrator applies a filter to display agents (Figure 1), just like using a filter to display the service principals used by managed identities.

Filtering agents in the Entra admin centre
Figure 1: Filtering agents in the Entra admin centre

“Agents” covers anything from the Copilot agents in SharePoint Online sites to the more sophisticated (and complicated) custom agents created with Copilot Studio or the Azure AI Foundry. Copilot agents linked to SharePoint sites don’t pose a management issue because they are managed (and shared) like any other site object. Entra Agent ID takes on the task of managing custom agents by automatically assigning these agents an identity within Entra ID. Microsoft says that giving agents a manageable identity “is the first step in a broader initiative to manage and protect non-human identities as organizations continue to build AI agents.”

Limited Functionality for Now

The functionality available to manage agents through the Entra admin center is very limited. Essentially, you can list agents and amend some properties. However, you cannot update the display name of an agent to replace the obscure naming used by Copilot Studio. When I tried, the Entra admin center failed with a data validation error (Figure 2). I was able to update the Notes and Logo properties.

An error when updating Entra Agent ID properties.
Figure 2: An error when updating Entra Agent ID properties

I assume the reason why the display name can’t be updated is that the value is used in some way to link Entra ID to Copilot Studio. I’m surprised by the failure because updating agent display names would make it much easier to find agents. A display name is just that – a name by which people can recognize an object. It shouldn’t be a form of immutable link between systems (that’s what GUIDs are for).

The only reason I recognize the agent is that it’s the only one I have published from Copilot Studio. Looking through a bunch of computer-generated names that mean nothing to humans to find the right agent to manage isn’t a real issue now, but if Microsoft lives up to its promise to deliver “more access management, security, and identity governance capabilities for Microsoft Entra Agent ID, plus support for agents from Security Copilot, Microsoft 365 Copilot and third-party solutions” over the next six months, the ability to quickly locate an agent in the Entra admin center will become more important.

It All Takes Time

Rome wasn’t built in a day and Entra ID won’t truly accommodate agents quickly. It will take time before the Entra admin center can do more than simply listing agents. Giving agents an Entra identity lays the foundation for other administrative features. I hope that Microsoft also delivers Graph APIs for agent management, just in case Entra doesn’t deliver everything an organization needs.


So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across the Microsoft 365 ecosystem. Subscribe to the Office 365 for IT Pros eBook to receive monthly insights into what happens, why it happens, and what new features and capabilities mean for your tenant.

]]>
https://office365itpros.com/2025/05/27/entra-agent-id/feed/ 0 69365
Use an OWA Mailbox Policy to Block Attachment Download for the New Outlook for Windows https://office365itpros.com/2025/05/13/owa-mailbox-policy-new-outlook/?utm_source=rss&utm_medium=rss&utm_campaign=owa-mailbox-policy-new-outlook https://office365itpros.com/2025/05/13/owa-mailbox-policy-new-outlook/#comments Tue, 13 May 2025 07:00:00 +0000 https://office365itpros.com/?p=69037

Make Sure that Users Can’t Download Copies of Attachments to Unmanaged Devices

A recent encounter with David Los in Microsoft’s HQ in Redmond reminded me of a relatively unknown feature of OWA mailbox policies that might be of interest as the new Outlook for Windows progresses. In October 2018, David wrote about how to combine a setting in a OWA mailbox policy with an Entra ID conditional access policy to block the download of attachments on untrusted (unmanaged) devices. It’s a similar idea to the SharePoint Online’s block download access policy.

Fast forward seven years and OWA mailbox policies control many aspects of how the new Outlook for Windows work, so let’s see if the setting works as well for it as it does for OWA.

Updating the Conditional Access Setting for an OWA Mailbox Policy

The magic starts with the ConditionalAccessPolicy setting in a OWA mailbox policy. The values of the setting can be:

  • Off (default): Exchange Online doesn’t attempt to apply a CA policy.
  • ReadOnly: Users can’t download attachments to make local copies (which means that they cannot use the Office apps to edit files). They can view attachments in the browser.
  • ReadOnlyPlusAttachmentsBlocked: User cannot view attachments at all.

To set the block in the OWA mailbox policy, sign into the Exchange Online management PowerShell module with an account holding the Exchange administrator role and run the Set-OWAMailboxPolicy cmdlet to update an OWA mailbox policy. I don’t recommend that you update the default policy unless you want the block to apply to all users. Choose a different policy (or create a new policy by running the New-OWAMailboxPolicy cmdlet instead).

After updating the policy, run the Get-OWAMailboxPolicy cmdlet to check that the setting is in place for the chosen OWA mailbox policy. Note that the ConditionalAccessFeatures property for the policy reports the set of restrictions for OWA to enforce.

Set-OWAMailboxPolicy -Identity NoOfflineAccess -ConditionalAccessPolicy ReadOnly
Get-OWAMailboxPolicy -Identity NoOfflineAccess | Format-List ConditionalAccess*

ConditionalAccessPolicy   : ReadOnly
ConditionalAccessFeatures : {Offline, AttachmentDirectFileAccessOnPrivateComputersEnabled, AttachmentDirectFileAccessOnPublicComputersEnabled, AttachmentPrintWithoutDownload}

When the ConditionalAccessPolicy setting is ReadOnlyPlusAttachmentsBlocked, the AttachmentWacViewingOnPrivateComputersEnabled and AttachmentWacViewingOnPublicComputersEnabled are added to the set of restrictions.

Use the Set-CASMailbox cmdlet to apply the OWA mailbox policy to a mailbox. It normally takes about 15 minutes for an updated policy to be effective. In the meantime, run Get-CASMailbox to check which mailboxes come within the scope of the policy, just in case some other mailboxes are affected.

Set-CasMailbox -Identity "Marty.King" -OwaMailboxPolicy 'NoOfflineAccess'
Get-CasMailbox -RecipientTypeDetails UserMailbox | Where-Object {$_.OWAMailboxPolicy -eq 'NoOfflineAccess'} | Format-Table DisplayName, OWAMailboxPolicy

Create a Conditional Access Policy to Block OWA Downloads

Figure 1 illustrates the details of the conditional access policy to enforce the blocks specified in the OWA mailbox policy. The session control for the CA policy says: “use app enforced restrictions,” which is the set of restrictions defined in the OWA mailbox policy. The only role conditional access has here is to notify the selected app(s) that they should apply restrictions because the device used for the connection is unmanaged.

The app is Office 365 Exchange Online, the enterprise app used by Exchange Online for many purposes, including OWA (its role in managing hybrid rich coexistence is being replaced by a dedicated tenant app soon).

The Conditional Access policy to block attachments for OWA and the new Outlook for Windows.
Figure 1: The Conditional Access policy to block attachments for OWA and the new Outlook for Windows

Testing the Block Download Policy with OWA

To test the policies, I ran OWA on an iPad (an unmanaged device). A banner on messages with attachments informed me that the block on download and printing existed (Figure 2). Microsoft refers to this as the “limited access experience.”

The effect of the OWA Mailbox policy to block attachments when advised by conditional access.
Figure 2: The effect of the OWA Mailbox policy to block attachments when advised by conditional access

A side-effect of imposing the CA policy is that the light version of OWA is blocked, probably because the light version is so simple that it doesn’t include the necessary smarts to handle the CA policy.

Testing with the New Outlook for Windows

Experience so far of managing the new Outlook is that settings from OWA mailbox policies apply to the Monarch client. Testing confirms that this is also true for conditional access restrictions. Installing and running the new Outlook for Windows on a Windows PC shows that the client picks up the same restriction as applied to OWA (Figure 3).

The new Outlook for Windows respects the block imposed by the OWA mailbox policy.
Figure 3: The new Outlook for Windows respects the block imposed by the OWA mailbox policy

It’s nice that the restrictions imposed by the OWA mailbox policy work, but it would be nicer if the documentation reflected the fact. I’m sure Microsoft will get around to updating its web pages. In the meantime, to learn more about blocking access to downloads, here’s a Practical365.com article to read.


Learn about managing Exchange Online and the rest of Microsoft 365 by subscribing to the Office 365 for IT Pros eBook. Use our experience to understand what’s important and how best to protect your tenant.

]]>
https://office365itpros.com/2025/05/13/owa-mailbox-policy-new-outlook/feed/ 1 69037
Entra ID to Disable Service Principal-Less Authentication https://office365itpros.com/2025/04/15/service-principal-less-auth/?utm_source=rss&utm_medium=rss&utm_campaign=service-principal-less-auth https://office365itpros.com/2025/04/15/service-principal-less-auth/#comments Tue, 15 Apr 2025 07:00:00 +0000 https://office365itpros.com/?p=68839

Block for Service Principal-Less Authentication in March 2026

One of the latest announcements from Microsoft engineering groups to improve the overall security of their cloud infrastructure is Entra’s decision to halt service principal-less authentication from March 2026. It’s an example of eradicating old practices that are unacceptable in today’s threat environment.

Service principal-less authentication happens when an enterprise app (aka a multitenant app) is used without a service principal in the host tenant. Enterprise apps are created by Microsoft and other software vendors. The Microsoft Graph Command Line Tools app (used to connect to the Microsoft Graph with PowerShell) is an example of a multitenant app. In this case, the Microsoft Graph Command Line Tools app has a service principal in the host tenant, which the app uses to hold the set of delegated Graph permissions available in interactive PowerShell sessions.

The Lack of a Service Principal

When an enterprise app doesn’t have a service principal, it probably means that the app only needs a basic level of authentication to allow the app to run. Post authentication, the app takes care of whatever processing it needs to do without using tenant-assigned permissions. Microsoft wants to block this kind of authentication saying that “Service principal-less authentication can be abused if the resource applications (i.e. APIs) perform incomplete validations.” Microsoft says that they have verified that validations aren’t vulnerable to service principal-less authentication, but they want to block the route now to avoid the possibility of a gap appearing in the future.

Reading between the lines, Microsoft doesn’t want apps to simply appear in a tenant, have a fairly loose connection to Entra ID, and be able to process data without administrative oversight.

After the block is active from March 2026, Entra ID will not allow enterprise apps to authenticate if they don’t have a matching service principal. It’s therefore wise to check tenants to identify any apps in this category to allow the problem to be mitigated.

Finding Problem Enterprise Apps

In their documentation, Microsoft explains how to use sign-in logs to identify problem enterprise apps. The advice focuses on looking for information through the Service principal sign-ins tab. I didn’t find anything there, but I found some interesting results by scanning sign-in logs for interactive connections. This code looks for sign-ins that have a service principal of ‘00000000-0000-0000-0000-000000000000’ and reports what it finds (this article describes a more comprehensive script to report service principal sign-ins):

$Uri = "https://graph.microsoft.com/beta/auditLogs/signIns?`$filter=servicePrincipalId eq '00000000-0000-0000-0000-000000000000'"
[array]$Data = Invoke-MgGraphRequest -Uri $Uri -Method Get
$Data = $Data.Value
$Data | Group-Object ResourceDisplayName -NoElement | Sort-Object Count | Format-Table Name, Count
 
Name                               Count
----                               -----
Exchange Admin Center                  1
Microsoft 365 App Catalog Services     1
Microsoft Activity Feed Service        1
Microsoft Office 365 Portal            1
OfficeHome                             1
Password Breach Authenticator          1
Microsoft Edge Auth                    3
Account Linking                        5
IrisSelectionFrontDoor                 5
Office365 Shell WCSS-Server            5
                                       6
Augmentation Loop                      6
Microsoft News Feed                   10
Office 365 Exchange Microservices     10
Edge Sync                             14

A bunch of Microsoft apps are in the collection, many of which appear in the Entra documentation explaining how to identify first-party apps. In addition, there’s an odd entry for a nameless app that comes from the Microsoft Services tenant (home tenant identifier f8cdef31-a31e-4b4a-93e4-5f571e91255a with an application identifier of 29d9ed98-a469-4536-ade2-f981bc1d605e). The default domain name for the app is sharepoint.com, so it’s reasonable to conclude that it’s something to do with SharePoint Online.

Figure 1 shows the details of a sign-in for the Office365 Shell WCSS-Server app, generated when I accessed the SharePoint Online admin center.

Details of a service principal-less authentication sign-in record.
Figure 1: Details of a service principal-less authentication sign-in record

The Office365 Shell WCSS-Server app is browser code that runs whenever a user navigates to most Microsoft 365 browser apps. The shell, also known as the suite header, is shared code that loads as part of almost all workloads, including SharePoint, OneDrive, Outlook, and Viva Engage. The code is probably performing some internal processing that doesn’t need the tenant to grant permissions, so it doesn’t have a service principal.

Mitigating the Lack of a Service Principal

All of this is very interesting, but Microsoft’s mitigation for service principal-less authentication is for tenants to create a service principal for each of the affected apps. Given that so many Microsoft apps seem to be affected, does this mean that tenant administrators will have to create service principals for these apps?

The answer is no. The Entra ID team confirmed to me that they’ll take care of Microsoft apps and will update the documentation to this effect. The mitigation requirement only arises for third-party enterprise apps. The call to action remains to check if any of these apps exist in your tenant, and if you find some examples, it’s time to contact the app owners to ask them how they plan to function when service principal-less authentication no longer works.


So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across the Microsoft 365 ecosystem, including Entra ID. Subscribe to the Office 365 for IT Pros eBook to receive monthly insights into what happens, why it happens, and what new features and capabilities mean for your tenant.

]]>
https://office365itpros.com/2025/04/15/service-principal-less-auth/feed/ 7 68839
Bringing Artificial Intelligence to Entra ID Conditional Access https://office365itpros.com/2025/04/04/conditional-access-optimization/?utm_source=rss&utm_medium=rss&utm_campaign=conditional-access-optimization https://office365itpros.com/2025/04/04/conditional-access-optimization/#respond Fri, 04 Apr 2025 07:00:00 +0000 https://office365itpros.com/?p=68746

Conditional Access Optimization Agent Keeps a Wary Eye on Connections

All around Microsoft, program managers and executives are seeking opportunities to deploy artificial intelligence in products, preferably if that usage justifies the requirement for an additional license. Some of the resulting ideas are good, like the Facilitator agent for Teams group chats. Others need more time to appreciate the use case, if one exists. The point is that you can expect more AI-powered features (whether Copilot in apps or a variety of agents) to appear in Microsoft 365 applications as time passes.

Entra ID Brings AI to the Table

All of which brings me to New innovations in Microsoft Entra to strengthen AI security and identity protection, published on March 24, 2025, where Alex Simons sets out the case for using AI to increase security and explains how Microsoft is applying AI in Entra ID.

Before I go further, let me know how disappointed I am that many technical conferences focusing on Microsoft 365 ignore or give lip service to Entra ID. The foundation of any successful and secure Microsoft 365 tenant is a well-managed Entra ID instance. It’s regrettable that Entra ID doesn’t receive the attention that it should on the schedules for even some major conferences. For instance, the current session lineup for the “Microsoft 365 Community conference” mentions Entra once and Copilot 46 times. That tells a story, mostly in terms of where Microsoft marketing money is going.

Smarter Policy Management Through the Conditional Access Optimization Agent

In any case, my attention was drawn to the Conditional Access Optimization Agent (now in private preview), which offers “smarter policy management.” Apparently, the agent monitors how an Entra ID tenant processes inbound connections to understand where the connections originate, the resources they access, and the authentication paths used. It picks up details like new user accounts and applications. The agent then puts the information together to figure out if the conditional access policies used by the tenant can be optimized.

Conditional Access Optimization Agent (source: Microsoft).
Figure 1: : Conditional Access Optimization Agent (source: Microsoft)

I like this idea. It’s a good example of applying artificial intelligence to a bounded set of data with a clear intention (that the tenant can alter with custom instructions). Unlike human administrators, some of whom are well capable of assessing the state of health of conditional access within a tenant, agents work all the time with a relentless focus on their instructions and the data they’ve been given.

The claims advanced in the article seem a little misleading (the agent hardly “protected” 700K sign-ins for the example tenant just by watching and processing connection data, and creating a new group containing 16 users to add to an existing conditional access policy isn’t rocket science either), but it’s possible to see the value that such an agent can bring by relieving administrators of the mundane task of reviewing conditional policy settings and sign-in logs on an ongoing basis to look for potential gaps and anomalies worth investigating.

Security Copilot Brings the AI Smarts

The Conditional Access Optimization Agent is one of six Security Copilot agents unveiled on March 24. Getting Security Copilot (the “proactive problem solver”) on board is where the cost arises. It’s hard to know just how much putting manners on your policies will cost because Security Copilot charges on the basis of Security Compute Units (SCU). Provisioned SCUs cost $4/hour in the U.S., but there’s no information available about how many SCUs the Conditional Access Optimization Agent will consume over a month or however long it takes for the agent to come up with its suggestions.

Organizations that use Security Copilot already probably have a good grasp on costs and can estimate (better than I can) the costs to add extra tasks. One way to look at it is that an experienced consultant who knows conditional access inside out might charge a day or two to review a tenant’s policies. For the purpose of easy maths, let’s say that the bill is $2,000, or 500 SCUs. Looking at the situation like that seems to make using Security Copilot a no-brainer. However, it’s a very black and white example and IT is full of grey. It will be interesting to learn about the real-life experience of operational tenants in terms of both agent output and cost.


So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across the Microsoft 365 ecosystem. Subscribe to the Office 365 for IT Pros eBook to receive monthly insights into what happens, why it happens, and what new features and capabilities mean for your tenant.

]]>
https://office365itpros.com/2025/04/04/conditional-access-optimization/feed/ 0 68746
Duplicate Mail User Objects Created for Guest Accounts https://office365itpros.com/2025/03/28/ex1015484-problem/?utm_source=rss&utm_medium=rss&utm_campaign=ex1015484-problem https://office365itpros.com/2025/03/28/ex1015484-problem/#comments Fri, 28 Mar 2025 07:00:00 +0000 https://office365itpros.com/?p=68633

EX1015484 Issue Causes Duplicate Exchange Online Mail User Objects Linked to Entra ID Guest Accounts

I am indebted to MVP Joe Stocker for sharing information about incident EX1015484 (duration from February 20 7:38AM PST to February 27 5AM PST). The problem as reported by Microsoft (Figure 1) is that when administrators create new Entra ID guest accounts, duplicate objects appear in Exchange Online that prevent email delivery to the guest accounts.

 Problem provisioning duplicate guest users in Exchange Online (EX1015484).
Figure 1: Problem provisioning duplicate guest users in Exchange Online (EX1015484)

Creating Mail User Objects

Entra ID and Exchange Online use a dual-write mechanism to update objects. Guest accounts originate when external users are added to Teams or Microsoft 365 groups, or when an administrator invites an external user to join the tenant from the Entra admin center.

When Entra ID creates a new guest user account, Exchange Online creates a mail user object. The existence of the mail user object allows guest users to be included in distribution lists. The mail user object has an email address, so email can be sent to the object, and the transport system is able to route messages to the guest account. Exchange Online removes a mail user object automatically following the removal of the guest user account from Entra ID. If the deleted Entra ID account is restored, the mail user object reappears.

Duplicated SMTP Addresses

In the case of EX1015484, it seems like Microsoft shipped a feature update with a bug that created mail user objects with duplicate SMTP email addresses. The Exchange transport system insists that email-enabled objects have unique email addresses because that’s the basis for routing messages to their final destinations.

Apparently, tenants need to contact Microsoft support to remove the duplicate objects. You can’t just remove duplicate mail user objects because of the dual-write mechanism. Entra ID is the directory of record, so any attempts to run Remove-MailUser to delete an object linked to a guest account will fail:

Remove-MailUser -Identity a9f35d52-572e-4438-a129-08d8c00ae88b

Confirm
Are you sure you want to perform this action?
Removing the mail enabled user Identity:"a9f35d52-572e-4438-a129-08d8c00ae88b" will delete the mail enabled user and
the associated Windows Live ID "elifon_contoso.com#EXT#office365itpros.onmicrosoft.com".
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): y
Remove-MailUser: ||An Azure Active Directory call was made to keep object in sync between Azure Active Directory and Exchange Online.
However, it failed. Detailed error message:  Resource 'a9f35d52-572e-4438-a129-08d8c00ae88b' does not exist or one of its queried reference-property objects are not present. DualWrite (Graph) RequestId:
61220f4c-90ea-4fa5-bf1f-c07b5d10c26d The issue may be transient and please retry a couple of minutes later. If issue persists, please see exception members for more information.

Removing the guest accounts from Entra ID and restoring them after a few minutes might well work. I can’t say because the problem didn’t affect any tenant that I have access to.

In any case, Joe posted some PowerShell to find mail-enabled objects with duplicate SMTP addresses:

Connect-ExchangeOnline; Get-Recipient -ResultSize unlimited | Select-Object -ExpandProperty EmailAddresses | Where-Object {$_ -like "smtp:*"} | Group-Object -Property {$_.ToString().ToLower()} | Where-Object {$_.Count -gt 1} | Select-Object @{Name="SMTPAddress";Expression={$_.Name.Substring(5)}}, Count | Sort-Object -Property Count -Descending

The code is faster when a filter is applied to select mail user objects. Here’s my version (updated for Exchange Online Management PowerShell module V3.7.2; if you run V3.7.1, filter against MailUser objects)

Connect-ExchangeOnline; 
Get-ExoRecipient -RecipientTypeDetails GuestMailUser -ResultSize unlimited | Select-Object -ExpandProperty EmailAddresses | Where-Object {$_ -like "smtp:*"} | Group-Object -Property {$_.ToString().ToLower()} | Where-Object {$_.Count -gt 1} | Select-Object @{Name="SMTPAddress";Expression={$_.Name.Substring(5)}}, Count | Sort-Object -Property Count -Descending

I tested the amended code by removing the check for addresses with a count greater than 1, so I am pretty sure that it works. Feel free to improve the code!

Problems Happen

It’s regrettable that EX1015484 happens, but that’s the nature of software. The issue has been resolved, and you will no longer encounter mail user objects with duplicate SMTP addresses in your tenant. It’s worth running the code shown above just in case that the problem hit your tenant and left some bad objects behind.  


So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across the Microsoft 365 ecosystem. Subscribe to the Office 365 for IT Pros eBook to receive monthly insights into what happens, why it happens, and what new features and capabilities mean for your tenant.

]]>
https://office365itpros.com/2025/03/28/ex1015484-problem/feed/ 9 68633
How to Use Bulk User Operations in Entra Admin Center https://office365itpros.com/2025/02/12/update-multiple-entra-id-accounts/?utm_source=rss&utm_medium=rss&utm_campaign=update-multiple-entra-id-accounts https://office365itpros.com/2025/02/12/update-multiple-entra-id-accounts/#respond Wed, 12 Feb 2025 07:00:00 +0000 https://office365itpros.com/?p=68040

Update Multiple Entra ID Accounts in a Single Action

It’s perhaps a natural assumption that administrative consoles like the Entra admin center perform actions against singular objects. However, that’s not the case because the Entra admin center now boasts an upgraded edit menu which supports operations against multiple user accounts (Figure 1). As indicated by the admin center, the update is currently in preview.

Bulk User account operations in the Entra admin center.

Update Multiple Entra ID accounts
Figure 1: Bulk User account operations in the Entra admin center

The older bulk operations menu has options for bulk create, bulk invite, and bulk delete.

No Notification from Entra

The disappointing thing is that Microsoft 365 administrators might struggle to discover interesting news like this because the Entra development group don’t post notifications to the Microsoft 365 message center. Hearing about changes might depend on fortuitously seeing a message in a social media feed or reading an article like this. It’s odd that Entra doesn’t take advantage of posting notifications in the Microsoft 365 message center because Microsoft 365 is a significant workload for Entra ID that generates large amounts of revenue through premium licenses.

The only documentation for bulk Entra ID updates that I can find refer to the bulk operations menu and says “Bulk operations in the Microsoft Entra admin portal could time out and fail on large tenants. This limitation is a known issue due to scaling limitations.”

The recommended workaround is to use the Microsoft Graph PowerShell SDK. That’s certainly a good idea if you want to process large numbers of accounts. It takes a little while to master user account management with the Graph SDK, but once you understand the basic mechanism, everything clicks into place and scripting account management isn’t a challenge.

What You Can do to Update Multiple Entra ID Accounts

Using the options in the edit menu is easy. Select some accounts (which can be a mixture of member and guest accounts), and choose one of the supported actions to update multiple Entra ID accounts:

  • Edit properties (Figure 2). Only certain properties can be edited.
  • Add manager. Every account should have a manager
  • Add sponsors. Account sponsorship is really intended for guest accounts. A flaw in the implementation means that the UI doesn’t reveal if the chosen accounts already have sponsors. There also doesn’t seem to be a way to cancel sponsor assignment if you decide not to select a sponsor. The perils of preview software…
  • Add as members of a group.
  • Add to administrative unit.
  • Edit account status. This option changes the accountEnabled property for the selected accounts from Enabled to Disabled or vice versa.
  • Revoke sessions with a forced sign-out. Affected user accounts must reauthenticate to reconnect.
Editing account properties to update multiple Entra ID accounts,
Figure 2: Editing account properties to update multiple Entra ID accounts

As you might expect, any change made to a user account is captured in an individual audit record and is discoverable by searching the Entra ID audit log (Figure 3) or the unified audit log (after ingestion).

Audit record for a bulk change made to an individual Entra ID user account.
Figure 3: Audit record for a bulk change made to an individual Entra ID user account

Update Multiple Entra ID Accounts is Goodness

The new edit menu option is an example of a change that’s surprising because it hasn’t appeared before now. Making changes to multiple accounts at one time is a great way to speed up administration. It avoids the need to use PowerShell to process one-off changes for small groups of users. However, I’d always use PowerShell for anything more complex because of the extra control it affords.

After all, the nice thing about PowerShell is that you get to choose how to implement functionality without waiting for Microsoft to add options to an admin center. Then again, good things come to those who wait…


Need some assistance to write and manage PowerShell scripts for Microsoft 365? Get a copy of the Automating Microsoft 365 with PowerShell eBook, available standalone or as part of the Office 365 for IT Pros eBook bundle.

]]>
https://office365itpros.com/2025/02/12/update-multiple-entra-id-accounts/feed/ 0 68040
How to Create Dynamic Administrative Units with PowerShell https://office365itpros.com/2023/09/25/dynamic-administrative-units-ps/?utm_source=rss&utm_medium=rss&utm_campaign=dynamic-administrative-units-ps https://office365itpros.com/2023/09/25/dynamic-administrative-units-ps/#comments Mon, 25 Sep 2023 01:00:00 +0000 https://office365itpros.com/?p=61642

Creating a Dynamic Administrative Unit Per Department

I wrote about using dynamic Entra ID administrative units earlier this year. Not much has changed since then as the feature remains in preview, but an interesting question asked about creating dynamic administrative units with PowerShell. I could have referred the questioner to Microsoft’s documentation, but its examples feature cmdlets from the soon-to-be-deprecated Azure AD module. An example using the Microsoft Graph PowerShell SDK seems like a better idea, so that’s what I cover here.

The question asked about using a CSV file containing department names with the idea of creating a separate dynamic administrative unit for each department. Using CSV files is an effective way of driving scripts, but if the tenant directory is accurate and maintained, it’s easy to extract a list of departments from user accounts.

Scripting the Creation of Dynamic Administrative Units

The steps in a script to create a dynamic administrative unit per department are as follows:

  • Run the Get-MgUser cmdlet to fetch the set of licensed Entra ID member accounts in the tenant. It’s important to fetch licensed accounts to exclude accounts used with shared mailboxes, room mailboxes, and member accounts created through synchronization for multi-tenant organizations.
  • Create an array of departments from user accounts.
  • Create an array of existing administrative units that we can check against to avoid creating duplicate administrative units.
  • For each department, run the New-MgDirectoryAdministrativeUnit cmdlet to create a new administrative unit.
  • Calculate the membership rule to find accounts belonging to the department.
  • Run the Update-MgDirectoryAdministrativeUnit to transform the administrative unit to use dynamic membership.

Here’s the code used to create a new administrative unit:

$Description = ("Dynamic administrative unit created for the {0} department created {1}" -f $Department, (Get-Date))
    $DisplayName = ("{0} dynamic administrative unit" -f $Department)

    If ($DisplayName -in $CurrentAUs.DisplayName) {
        Write-Host ("Administrative unit already exists for {0}" -f $DisplayName)
    } Else {
    # Create the new AU
    $NewAUParameters = @{
        displayName = $DisplayName
        description = $Description
        isMemberManagementRestricted = $false
       }
       $NewAdminUnit = (New-MgDirectoryAdministrativeUnit -BodyParameter $NewAUParameters)
    }

And here’s the code to transform it into a dynamic administrative unit:

$MembershipRule = '(user.department -eq "' + $Department + '" -and user.usertype -eq "member")'
# Create hash table with the parameters
$UpdateAUParameters = @{
    membershipType = "Dynamic"
    membershipRuleProcessingState = "On"
    membershipRule = $MembershipRule
}
    
Try {
      Update-MgDirectoryAdministrativeUnit -AdministrativeUnitId $NewAdminUnit.Id -BodyParameter $UpdateAUParameters -ErrorAction Stop
      Write-Host ("Created dynamic administrative unit for the {0} department called {1}" -f $Department, $NewAdminUnit.DisplayName)
} Catch {
      Write-Host ("Error updating {0} with dynamie properties" -f $NewAdminUnit.DisplayName )
}


        

Figure 1 shows the properties of a dynamic administrative unit created by the script, which you can download from GitHub.

Properties of a dynamic administrative unit
Figure 1: Properties of a dynamic administrative unit

Membership Rules Glitches

The membership rule determines the membership of a dynamic administrative unit. Although you can construct filters to use with the Get-MgUser cmdlet to find licensed user accounts belonging to a department, the same flexibility doesn’t exist for the rules used to interrogate Entra ID to find members for a dynamic administrative unit (or dynamic Microsoft 365 group).

The problem is that membership rules don’t allow you to mix properties of different types. For instance, the rule can find user accounts belonging to a department (a string property), but it can’t combine that clause with a check against the assignedLicenses property to make sure that the account is licensed. That’s because assignedLicenses is a multi-value property and the rule can’t mix checks against strings with checks against multi-value properties. If you try, Entra ID signals a “mixed use of properties from different types of object” error. In effect, because we want to create dynamic administrative units based on department, the membership rule is limited to string properties.

However, you can check for the existance of a service plan in the set of assigned licenses held by accounts. Not being able to check licenses while being able to check service plans is a tad strange. For example, this rule finds all member accounts in the Sales department with an enabled Exchange Online Plan 2 service plan in their assigned licenses:

(user.assignedPlans -any (assignedPlan.servicePlanId -eq "efb87545-963c-4e0d-99df-69c6916d9eb0" -and assignedPlan.capabilityStatus -eq "Enabled")) -and (user.department -eq "Sales") -and (user.usertype -eq "member")'

Finding the Right Cmdlet to Do the Job

I bet some folks reading this article ask the question “how do I find out what cmdlets to use to interact with Entra ID objects?” It’s a fair question. The SDK modules contain hundreds of cmdlets, some of which have extraordinarily long and complex names. My answer is to use the Graph X-ray add-on to gain insight into what the Entra ID admin center does to manipulate objects. If a method is good enough for the Entra ID admin center, it’s probably good enough for you.


Learn about using Entra ID, the Microsoft Graph PowerShell SDK, and the rest of Microsoft 365 by subscribing to the Office 365 for IT Pros eBook. Use our experience to understand what’s important and how best to protect your tenant.

]]>
https://office365itpros.com/2023/09/25/dynamic-administrative-units-ps/feed/ 4 61642
Managing the Entra ID Registration Campaign for Stronger Authentication https://office365itpros.com/2023/09/18/registration-campaign-starts/?utm_source=rss&utm_medium=rss&utm_campaign=registration-campaign-starts https://office365itpros.com/2023/09/18/registration-campaign-starts/#comments Mon, 18 Sep 2023 01:00:00 +0000 https://office365itpros.com/?p=61620

Registration Campaigns Push for Stronger Authentication Methods

A year ago, Microsoft VP for Identity Security Alex Weinert spoke at the TEC 2022 conference and was critical about the slow adoption of multi-factor authentication (MFA) within Microsoft 365 tenants. At the time, only 26.64% of all Entra ID accounts used MFA (34.15% for accounts holding an administrative role). During his presentation, Alex talked about some of the initiatives Microsoft planned to drive MFA adoption and more secure authentication, including changes to the authenticator app, the introduction of authenticator-lite in Outlook mobile, and differentiation of authentication strengths for conditional access policies.

The changes discussed at TEC 2022 are now in production, but there’s still room for improvement. On July 17, Alex Weinert published a Microsoft Technical Community post titled Advancing Modern Strong Authentication focused on Microsoft’s push to get users off SMS responses to use a stronger method such as the Authenticator app. Alex noted that Microsoft telemetry records SMS and voice phone calls still being used for 44% of responses, He also said that Microsoft research concludes that SMS is 40% less effective at repelling compromise by bad actors compared to the Authenticator app, possibly due to an increase in man-in-the-middle attacks.

Entra ID includes a feature called registration campaigns to help organizations move users to the Authenticator app. Essentially, administrators create a campaign and users start to see prompts to “improve your sign ins.” Users can snooze the prompt for a predefined period of up to 14 days but eventually they’ll need to select the authentication method defined for the campaign (the Authenticator app). Nagging until you do something…

The Mail Announcing the Registration Campaign Arrives

Bringing things back to TEC, as I prepared to travel to Atlanta for the 2023 conference this week, I received a note from Microsoft saying that users in my tenant that use SMS and voice methods for MFA responses would be prompted to switch to the Authenticator app (Figure 1).

Microsoft email announcing the start of a registration campaign
Figure 1: Microsoft email announcing the start of a registration campaign

Good as it is for users to upgrade their authentication method, I didn’t want users to receive an unexpected “Improve Your Sign-in” prompt while I was out of the office. Atlanta isn’t too far away, but the five hours dislocation from my normal working hours would definitely interfere with communications.

Pausing the Campaign

The reason why I received the notification was that the tenant settings for Entra ID had an enabled campaign. I’m not quite sure how the campaign was initiated, but it’s probably due to something I did in the past when checking out new Entra ID features. Microsoft complied and launched the campaign by warning me that it was about to begin.

The short-term solution is simple. I edited the campaign settings to put it into a disabled state (Figure 2).

Settings for an Entra ID authentication registration campaign
Figure 1: Settings for an Entra ID authentication registration campaign

The available settings for a registration campaign are Disabled, Enabled, and Microsoft controlled. The latter allows Microsoft to control a registration campaign, which is fine if everyone’s prepared for the change. For instance, it’s a good idea to help users install the Authenticator app on their mobile devices in advance.

It’s also wise to brief people about why using the Authenticator app is easy. The first time a user sees number matching and the additional context (location) displayed by the app when responding to an MFA challenge, they might conclude that it’s a more complex process than typing in a simple code received by SMS. But when they understand that number matching makes it harder for attackers to compromise MFA and the additional context helps them recognize suspicious activity (like an unexpected app provoking a challenge), it usually leads to a good result.

The Campaign Relaunches Soon

It’s a good idea to get rid of SMS and voice responses to MFA challenges, so I’ll relaunch the registration campaign when I return from TEC 2023. Life should be calmer then. At least, that’s the plan until the next emergency arises.


So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across Entra ID and the rest of the Microsoft 365 ecosystem. Subscribe to the Office 365 for IT Pros eBook to receive monthly insights into what happens, why it happens, and what new features and capabilities mean for your tenant.

]]>
https://office365itpros.com/2023/09/18/registration-campaign-starts/feed/ 4 61620
EntraExporter Tool Exports Details of an Entra ID Tenant https://office365itpros.com/2023/08/24/entraexporter-tool/?utm_source=rss&utm_medium=rss&utm_campaign=entraexporter-tool https://office365itpros.com/2023/08/24/entraexporter-tool/#comments Thu, 24 Aug 2023 01:00:00 +0000 https://office365itpros.com/?p=61313

I’m always on the lookout for tools that might help tenant administrators understand more about the technology they manage. The EntraExporter tool is an example of the kind of utility that I consider to be both valuable and interesting.

EntraExporter is a community-developed PowerShell module designed to export information about the objects and policies in an Entra ID instance for a tenant to JSON files. It’s a way of capturing information about objects like user accounts, groups, administrative units, organization branding, subscriptions, and policies to record of current settings. This is not a backup product, but it is an excellent way of noting the exact configuration of an Entra ID tenant at a point in time.

Installing EntraExporter

To install EntraExporter, run the Install-Module command (this assumes that the PowerShell gallery is a trusted repository). I used this command rather than the example in the documentation:

Install-Module EntraExporter -Scope Allusers

I always install PowerShell modules with Scope AllUsers to force PowerShell to put the module files in $env:ProgramFiles\PowerShell\Modules. From PowerShell 6 onward, Install-Module installs modules in $HOME\Documents\PowerShell\Modules if no scope is defined. This is fine unless you redirect Windows known folders to OneDrive, in which case you end up with module files in OneDrive. The script I wrote to update PowerShell modules used by Office 365/Microsoft 365 installs and updates modules in $env:ProgramFiles\PowerShell\Modules.

The EntraExporter team recommends that you use PowerShell 7 to run the tool.

Running EntraExporter

EntraExporter uses the Microsoft Graph PowerShell SDK to extract information from Entra ID. As the tool runs interactively, it uses delegate permissions, which is fine because the tool only exports information. However, EntraExporter needs a bunch of permissions to access the different objects and policies it processes, so the connect command is:

Connect-MgGraph -Scopes 'Directory.Read.All', 'Policy.Read.All', 'IdentityProvider.Read.All', 'Organization.Read.All', 'User.Read.All', 'EntitlementManagement.Read.All', 'UserAuthenticationMethod.Read.All', 'IdentityUserFlow.Read.All', 'APIConnectors.Read.All', 'AccessReview.Read.All', 'Agreement.Read.All', 'Policy.Read.PermissionGrant', 'PrivilegedAccess.Read.AzureResources', 'PrivilegedAccess.Read.AzureAD', 'Application.Read.All'

The SDK seeks consent for the permissions when you run the command to connect:

Connect-EntraExporter

The signed in user running EntraExporter must grant consent to the requested permissions to access the data (Figure 1). Again, consenting to the requested set of permissions is fine, if you remember that the service principal for the SDK retains consent to use those permissions in future. I’ve written about the way that the SDK accrues Graph permissions over time and possible solutions.

 Requesting consent for the Graph permissions used by EntraExporter
Figure 1: Requesting consent for the Graph permissions used by EntraExporter

One thing I do not like about the Microsoft Graph PowerShell SDK is the way that its enterprise app proclaims itself to be “unverified.” Any Microsoft app in widespread use should be verified to give tenant administrators more confidence about the app’s provenance.

EntraExporter can run in an Azure Automation runbook. To make this possible, make sure that:

Exporting Entra ID Information

With all the necessary permissions in place, I ran the Export-Entra script with the All parameter to export as much directory information as possible. The documentation notes that “B2C, B2B, Static Groups and group memberships, Applications, Service Principals, Users, Privileged Identity Management (built in roles, default roles settings, non-permanent role assignments)” are not exported by default.

Export-Entra -Path 'C:\EntraID\' -All

Various filters are available to select the exact directory information to export, but I wanted to see everything!

How EntraExporter Works

All the EntraExporter code is available in GitHub for your perusal. A quick review identified that the driving force behind the export is the schema defined in Get-EEDefaultSchema.ps1, which tells the exporter the types of objects to export and how to export them. For instance, here’s the definition for user accounts:

# Users
        @{
            GraphUri = 'users'
            Path = 'Users'
            Filter = $null
            QueryParameters = @{ '$count' = 'true'; expand = "extensions" }
            ApiVersion = 'beta'
            Tag = @('All', 'Users')
            DelegatedPermission = 'Directory.Read.All'
            ApplicationPermission = 'Directory.Read.All'
        }

Apart from the slight glitch obvious in Figure 1 (reproducible in the Graph Explorer), everything went smoothly when running an export. The time taken to process an export depends on how many objects are in a tenant directory, particularly groups and users (because they tend to be most numerous). Running a full export can take time because of the need to enumerate group memberships and details of service principals. For a small to medium tenant, expect that everything will be done in 10-15 minutes.

EntraExporter hits an error exporting details of administration units
Figure 2: EntraExporter hits an error exporting details of administration units

The export results in a set of folders in the target location. In Figure 3, you can set the set of folders (one for each type defined in the schema). The content of each folder are the JSON files generated by EntraExporter. If there are many objects, the JSON output for individual objects are in their own folder. This is what you see in Figure 3, where each user object has a folder named after the user account object identifier.

Files generated by EntraExporter
Figure 3: Files generated by EntraExporter

Opening a JSON file reveals the properties of an object. Figure 4 shows the JSON file for a user object viewed through Visual Studio Code.

JSON properties of a user account generated by EntraExporter
Figure 4: JSON properties of a user account generated by EntraExporter

Not Perfect But Entra Exporter’s a Nice Tool to Have

No doubt some will consider Entra Exporter a simple tool of little use because it doesn’t come with features like the ability to reconstruct an object from the exported data. But that’s missing the point. Many organizations have written their own versions of Entra Exporter to capture configurations because they need this data for different reasons (auditing, change control, etc.). The advantage of Entra Exporter is that a tool is available for free that is written in PowerShell and therefore very customizable if it doesn’t meet your exact needs.


Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2023/08/24/entraexporter-tool/feed/ 5 61313
Entra ID Guest Accounts Can Now Have Sponsors https://office365itpros.com/2023/08/17/guest-account-sponsors/?utm_source=rss&utm_medium=rss&utm_campaign=guest-account-sponsors https://office365itpros.com/2023/08/17/guest-account-sponsors/#comments Thu, 17 Aug 2023 01:00:00 +0000 https://office365itpros.com/?p=61219

Defining Guest Account Sponsors with GUI and PowerShell

In July 2023, Microsoft added a new preview feature to allow organizations to assign ‘sponsors’ for Entra ID guest accounts. The idea is that an organization should be able to assign people or groups to be the sponsor of guest accounts. The sponsor should be “a responsible individual,” meaning someone who understand why a guest account is present in the directory, how that guest account is used, and what access they have to data. A sponsor can be an individual account or a group, and a guest account can have up to five sponsors (a mixture of accounts and groups).

When the time comes to review guest accounts and decide to keep or remove the account, sponsors can justify the retention of the guest account or ask for its removal. For instance, if a group owner uses a tool like Entra ID Access Review to conduct a periodic review of the membership of a group (team) and doesn’t recognize a guest account, they can contact the sponsor for more information. Whether or not the group owner gets anything useful from the sponsor is another matter.

Defining Entra ID Guest Account Sponsors

According to Microsoft’s documentation, “If you don’t specify a sponsor, the inviter will be added as a sponsor.” They then go on to explain how to invite an external user and add a sponsor to the new Entra ID guest account (Figure 1).

Adding sponsor information for a new guest account
Figure 1: Adding sponsor information for a new guest account

However, if you don’t add a sponsor to the new external account, the sponsor information is not filled in with the identifier of the account used to create and send the invitation. Maybe my tenant is missing some bits, which is entirely possible.

Sponsor information isn’t filled in either if you add a guest account by adding an external user to a team or sharing a document with them. This isn’t surprising because the sponsors feature is in preview and it takes time for applications like Teams, Outlook, SharePoint Online, and OneDrive for Business to catch up and populate new guest account properties.

In summary, if you want to update the sponsor for a guest account using a GUI, the only way is to edit the account properties in the Entra ID admin center.

Programmatic Updates for Guest Account Sponsors

A beta Graph API is available to list, update, and remove guest account sponsors. As usual, the Graph Explorer is an invaluable tool to help understand how a Graph API works (Figure 2).

Getting sponsor information for a guest account with the Graph Explorer
Figure 2: Getting sponsor information for a guest account with the Graph Explorer

The Get-MgBetaUser cmdlet from the beta module of the Microsoft Graph PowerShell SDK (now at V2.3) can fetch information about sponsors. For example, this code fetches information about a guest account including the sponsors. It then uses the Get-MgUser cmdlet to resolve the set of user identifiers into display names.

$User = Get-MgBetaUser -UserId 7bfd3f83-be63-4a5a-bbf8-c821e2836920 -Property Id, displayName, Sponsors -ExpandProperty Sponsors
ForEach ($Id in $User.Sponsors.Id) { Get-MgUser -UserId $Id | Select-Object DisplayName }

Of course, the code doesn’t handle the situation where a sponsor is a group, but that’s easily added if needed.

If you wanted to scan all guest accounts that don’t have sponsors defined and add a default sponsor, you could do something like this. The code:

  • Defines an account to be the default sponsor.
  • Builds a payload to use when updating the guest accounts.
  • Finds guest accounts in the tenant.
  • Checks each guest account for sponsors. If none are found, the script applies the default sponsor.

Connect-MgGraph -Scopes User.ReadWrite.All

$DefaultSponsorId = (Get-MgUser -UserId James.Ryan@office365itpros.com).Id
$Body = '{"@odata.id": "https://graph.microsoft.com/beta/users/' + $DefaultSponsorId + '"}'

[array]$Guests = Get-MgBetaUser -Filter "userType eq 'Guest'" -All -Property Id, displayName, Sponsors -ExpandProperty Sponsors | Sort-Object displayName
If ($Guests) {
    Write-Host "Scanning for sponsors"
    ForEach ($Guest in $Guests) {
      If ($Null -eq $Guest.Sponsors.Id) {
         Write-Host ("Guest {0} has no sponsors - updating with default sponsor" -f $Guest.displayName) 
         $Uri = ("https://graph.microsoft.com/beta/users/{0}/sponsors/`$ref" -f $Guest.Id)
         Invoke-MgGraphRequest -Uri $Uri -Method Post -Body $Body
      }
    }
}

Auditing Updates to Guest Account Sponsors

Last week I wrote about the way that Entra ID auditing does not capture details of changes to the usage location property for user accounts. As it turns out, updating a guest account with sponsor information creates an audit record without details of the change. Again, this could be a matter of timing and an update is coming to make sure that audit log events for account updates capture sponsor information correctly.

Tracking Guest Additions

Since Azure B2B Collaboration introduced guest accounts in summer 2016, administrators have been tracking the creation of guest accounts in different ways (for instance, here’s how to track the addition of guest accounts to teams). In many cases, the reason for doing so was to know who was responsible for the creation of a guest account. With sponsors, that need might go away, or at least it might be easier to retrieve the “who created that account information” by using the sponsor information stored for accounts. That is, once the apps record sponsors.


Learn about using Entra ID, PowerShell, the Microsoft Graph, and the rest of Office 365 by subscribing to the Office 365 for IT Pros eBook. Use our experience to understand what’s important and how best to protect your tenant.

]]>
https://office365itpros.com/2023/08/17/guest-account-sponsors/feed/ 2 61219
Updating Entra ID Risky Users with PowerShell https://office365itpros.com/2023/08/16/entra-id-risky-users/?utm_source=rss&utm_medium=rss&utm_campaign=entra-id-risky-users https://office365itpros.com/2023/08/16/entra-id-risky-users/#respond Wed, 16 Aug 2023 01:00:00 +0000 https://office365itpros.com/?p=61184

Entra ID Identity Protection Monitors Sign-Ins to Find Problem Accounts

Entra ID Identity Protection is a solution that uses machine learning to monitor and detect problematic sign-in activity for a tenant. The idea is that machine learning is better at examining audit logs to recognize signs of potential problems, especially when a tenant supports a large number of accounts. Entra ID Identity Protection requires Azure AD Premium P2 licenses.

Graph API for Entra ID Risky Users

The riskyUser resource type is a Graph API used by Entra ID Identity Protection to. programmatically represent risky user accounts. Usually, it is Entra ID Identity Protection that marks an account as being at risk based on the pattern of activity for the account (for instance, a large number of attempted sign-ins that fail). Alternatively, the risk state of a user can be set by an administrator if they know that an account is compromised.

The actions supported by the API are:

  • List risky users.
  • Get details of a risky user.
  • List the history of a risky user.
  • Confirm that the state of a user account is risky due to known compromise.
  • Dismiss the risky status for a user (use the Graph API as shown below).

Examples of Microsoft Graph PowerShell SDK cmdlets based on the Risky User API include:

# Use scope IdentityRiskyUser.ReadWrite.All to read risky user information and IdentityRiskyUser.ReadWrite.All to update.
Connect-MgGraph -Scopes IdentityRiskyUser.ReadWrite.All
Get-MgRiskyUser | Sort-Object RiskLastUpdatedDateTime -Descending
Get-MgRiskyUser -RiskyUserId 96bfb216-e88c-4f1f-86d7-04747e5fc686 | Format-List)
Get-MgRiskyUserHistory -RiskyUserId 96bfb216-e88c-4f1f-86d7-04747e5fc686 | Format-List)
Confirm-MgRiskyUserCompromised -UserIds (Get-MgUser -UserId Ben.James@Office365itpros.com).Id

Accounts that need to be checked show up under risky activities in the Protection section of the Entra ID admin center (risky activities can be attributed to service principals if Identity Protection finds an app doing something unusual). Figure 1 shows the details logged for an account after an administrator ran the Confirm-MgRiskyUserCompromised cmdlet. This action forces Identity Protection to regard the account as being highly at risk, so it sends global administrators email to inform them about the problem.

Details of a risky user in Entra ID Identity Protection

Entra ID risky users
Figure 1: Details of a risky user in Entra ID Identity Protection

Once an account is flagged as risky, it remains in this state until an administrator remediates the risk. On my tenant, I had some old risky users that hadn’t been processed. To clean things up, I used the PowerShell code below to find risky users awaiting administrator remediation and checked the last updated date to see if the risky user state was more than 183 days old. If true, I used the Graph API to dismiss the risky user state.

Write-Host "Finding risky users"
[array]$RiskyUsers = Get-MgRiskyUser -Filter "(riskState ne 'remediated') and (riskState ne 'dismissed')" | Sort-Object RiskLastUpdatedDateTime -Descending

$Uri = "https://graph.microsoft.com/beta/riskyUsers/dismiss"
[datetime]$CheckDate = (Get-Date).AddDays(-183)

ForEach ($User in $RiskyUsers) {  
   If ($User.RiskLastUpdatedDateTime -le $CheckDate) {
      Write-Host ("User {0} ({1}) risk state last updated on {2} - removing..." -f $User.UserDisplayName, $User.UserPrincipalName, $User.RiskLastUpdatedDateTime)
      $DismissedUserInfo = '{"UserIds": [ "' + $User.Id + '" ]}'
      Invoke-MgGraphRequest -Uri $Uri -Body $DismissedUserInfo -Method Post
   }
}


User Jim.Smith (Jim.Smith@office365itpros.com) risk state last updated on 18/08/2022 15:54:04 - removing...
User Chris Bishop (Chris.Bishop@office365itpros.com) risk state last updated on 11/05/2021 06:05:39 - removing...

This code dismisses the risk state even if the state is deemed High. A more complete implementation might exclude these risky users and process only those with risk state of medium or low.

It takes a few minutes before the dismissed state becomes effective for accounts.

Linking Entra ID Risky Users to Conditional Access

When I first read about the Confirm-MgRiskyUserCompromised cmdlet, I wondered if I would ever use such a command. If an administrator knows about an account compromise, surely their first step is to prevent access to whoever has compromised the account by disabling access and changing its password? After all, no need exists to mark an account as risky when an administrator knows that it is compromised/

But then I wondered if the advantage lies in the fact that high-risk accounts can be picked up by a conditional access policy. Identity Protection started off with its own risk policies, and Microsoft is now encouraging customers to migrate to conditional access policies instead, citing advantages such as Graph API support and greater flexibility. Given the number of features Microsoft has added to conditional access policies recently, like protected actions and authentication strength, the argument is certainly true.

Creating an authentication policy to block access to accounts marked at risk is straightforward.

  • Select the users within scope of the policy.
  • Add All Cloud Apps as the target resource.
  • Set User Risk to High (or High and Medium).
  • Set session sign-in frequency to every time.
  • Under access control, select grant access with a password change (and for good measure, require multi-factor authentication).

When the conditional access policy is active, continuous access evaluation will detect the risk and block connections from any user (Figure 2) marked as High risk (like those marked by the Confirm-MgRiskyUserCompromised cmdlet). An administrator will have to reset the account password or, if the account is enabled for Self-Service Password Reset (SSPR), the user will have to choose a new password before they can sign in again.

Entra ID blocks a risky user from making a connection
Figure 2: Entra ID blocks a risky user from making a connection

The thing about conditional access policies is that it’s easy to get into a mess with conflicting and competing policies. I always test with a single user and I use the IdPowerTools app to document the set of policies defined in my tenant.

Entra ID Risky Users Just Another Thing to Consider in a Security Plan

Entra ID Identity Protection isn’t a silver bullet to prevent user account compromise. Enabling multi-factor authentication for all user accounts is a much more fundamental solution to the compromise problem. Identity Protection brings some extra intelligence to the task of managing sign-ins and helps administrators to identify problem connections more quickly and more accurately. Having a Graph API (and SDK cmdlets) available to automate operations for risky user accounts is a bonus.

Deciding how to use these tools takes time and consideration. They must fit in your overall security posture and not conflict with any of the other protection techniques that are in use. Identity Protection is just another factor for administrator to consider.


Learn how to exploit the data available to Microsoft 365 tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work.

]]>
https://office365itpros.com/2023/08/16/entra-id-risky-users/feed/ 0 61184
Filtering Against the Entra ID Employee Hire Date Property https://office365itpros.com/2023/08/10/entra-id-employee-hire-date/?utm_source=rss&utm_medium=rss&utm_campaign=entra-id-employee-hire-date https://office365itpros.com/2023/08/10/entra-id-employee-hire-date/#comments Thu, 10 Aug 2023 01:00:00 +0000 https://office365itpros.com/?p=61146

Two Filters Available for the Entra ID Employee Hire Date Property

In an article published earlier this year about different ways to find Entra ID (Azure AD) user accounts with PowerShell, I commented that the Get-MgUser cmdlet could not apply a server-side filter against the Entra ID employee hire date property. For instance, to find accounts with an employee hire date, you must use Get-MgUser to fetch accounts and then apply a client-side filter to find the target objects. For instance, this code finds accounts with an employee hire date later than 1 January 2023:

[array]$Employees = Get-MgUser -filter "userType eq 'Member' and EmployeeId ge ' '" -Property Id, displayname, userprincipalname, employeeid, employeehiredate, employeetype
$CheckDate = Get-Date “8-Jul-2023”
$Employees | Where-Object {$CheckDate -as [datetime] -lt $_.EmployeeHireDate} | Sort-Object {$_.EmployeeHireDate -as [datetime]} -Descending | Format-Table DisplayName, userPrincipalName, employeeHireDate -AutoSize

DisplayName   UserPrincipalName                 EmployeeHireDate
-----------   -----------------                 ----------------
Michael King  Michael.King@office365itpros.com  01/08/2023 23:00:00
Terry Hegarty Terry.Hegarty@office365itpros.com 01/08/2023 23:00:00
Hans Geering  Hans.Geering@office365itpros.com  31/07/2023 23:00:00
Chris Bishop  Chris.Bishop@office365itpros.com  31/07/2023 23:00:00

The problem persists in the latest version of the Microsoft Graph PowerShell SDK using both the Get-MgUser and Get-MgBetaUser cmdlets.

Dynamic Groups Support for Employee Hire Date

All of which brings me to news that membership rules for Entra ID dynamic groups support the PowerShell le and ge operators against the employee hire date property. This capability is a preview for now.

In a nutshell, the new feature supports the creation of dynamic groups (which require Entra ID Premium P1 licenses) based on a filter against the EmployeeHireDate property. Two kinds of date filters are available. The first performs a simple comparison to test if the employee hire date is greater than or equal to or less than or equal to a specified date. For example, this command creates a dynamic Microsoft 365 group with a membership rule that finds all member accounts with an employee hire date greater or equal to 1 January 2023:

$Group = New-MgGroup -DisplayName "New Employees (Dynamic)" -Description "Dynamic group containing new employees (2023)" -MailEnabled:$True -SecurityEnabled:$False -MailNickname New.Employees.2023 -GroupTypes "DynamicMembership", "Unified" -MembershipRule "(user.employeehiredate -ge ""2023-01-01T00:00:00Z"" -and (user.usertype eq ""Member"")" -MembershipRuleProcessingState "On"

Dates must be passed in the sortable format rather than a more human-friendly type. For PowerShell, use Get-Date to set the date and format the output as follows:

$DateForFilter = (((Get-Date).AddDays(-365)) | Get-Date -format 'yyyy-MM-ddThh:mm:ssZ')

The second filter tests the employee hire date against a calculated date based on the current date. This example creates a dynamic Microsoft 365 group with a membership rule that looks for employees with hire dates within the last 31 days (system.now is the current date):

$Group = New-MgGroup -DisplayName "New Employees (Last Month)" -Description "Dynamic group containing employees hired in the last month" -MailEnabled:$True -SecurityEnabled:$False -MailNickname New.Employees.LastMonth -GroupTypes "DynamicMembership", "Unified" -MembershipRule "(user.employeehiredate -ge system.now -minus p31d ) -and (user.usertype eq ""Member"")" -MembershipRuleProcessingState "On"

It looks like only day intervals are supported. Entra ID rule validation rejects values like p4w (4 weeks) or p1m (1 month).

Validating the Filter Against the Entra ID Employee Hire Date Property

It’s easy to check the effectiveness of the membership rule. Let Entra ID calculate the membership for the dynamic group and note who’s present (Figure 1):

Viewing members of a dynamic group using a membership rule using the Entra ID employee hire date property

Azure AD employee hire date property
Figure 1: Viewing members of a dynamic group using a membership rule using the Entra ID employee hire date property

Then run the Get-MgUser example shown at the top of the article with an appropriate value inserted into the $CheckDate variable (use this code to set the variable to 31 days from the current date).

$CheckDate = (Get-Date).AddDays(-31)

Check the results generated by PowerShell against the set shown in the Entra ID admin center. The two should match. If they don’t, wait for 30 minutes or so to be sure that Entra ID has had time to process any recent updates and try again.

Time Updates All Cmdlets

It takes time for the Graph SDK cmdlets to catch up with new developments and preview features. Now that the Entra ID developers have enabled date filtering against the employee hire date property, it can’t be long before server-side filters work with Get-MgUser too. And if they don’t, there is a workaround – fetch the membership of the dynamic group with Get-MgGroupMember and use that information instead of running Get-MgUser. That’s the kind of lateral thinking we’re looking for in the great PowerShell script-off competition at TEC 2023 next month!


Support the work of the Office 365 for IT Pros team by subscribing to the Office 365 for IT Pros eBook. Your support pays for the time we need to track, analyze, and document the changing world of Microsoft 365 and Office 365.

]]>
https://office365itpros.com/2023/08/10/entra-id-employee-hire-date/feed/ 1 61146
Entra ID Audit Captures Some But Not All Updates of User Account Properties https://office365itpros.com/2023/08/08/audit-user-account-changes/?utm_source=rss&utm_medium=rss&utm_campaign=audit-user-account-changes https://office365itpros.com/2023/08/08/audit-user-account-changes/#comments Tue, 08 Aug 2023 01:00:00 +0000 https://office365itpros.com/?p=61117

Audit User Account Changes – But Not For All Properties

In a Twitter (X) discussion about Microsoft Entra ID logging, one of the participants commented that “On the small end, just being able to see who changed a user account property like UsageLocation” would be a good thing. The point here is that changing the usage location of a user account can have licensing implications.

The complaint is that the Entra ID audit log doesn’t disclose who updated a user account and changes the usage location. And as it turns out, the statement is true, as a quick test reveals. Update a user account and change several properties, including the usage location. Wait for a few minutes and check what the Entra ID audit log reports. It should show something like Figure 1. The changes made to all other properties are there, but there’s no trace of the change made to move the account’s usage location (in this case, from Ireland to France).

Details of updated properties for a user account in the Entra ID audit log

Audit user account changes
Figure 1: Details of updated properties for a user account in the Entra ID audit log

Later on in the conversation, the original complainant stated that they had “opened a case last year on the UsageLocation issue. I was told that the missing data is ‘by design’ and it was closed.” That response seems strange. Why would Entra ID consider that not logging changes made to account user locations is a design feature? After all, their documentation emphasizes that “you must specify the Usage location for all members.”

How the Audit Log Stores Details of User Account Updates

Entries from the Entra ID audit log flow through to the unified audit log (now holding 180 days of audit data for Office 365 E3 accounts). Perhaps the data ingested by the unified audit log would hold the missing usage location information. It’s always worth checking.

Many workloads pump information to the unified audit log, and it’s a great source of who-did-what knowledge covering situations like permission consent grants, usage of sensitivity labels, and keeping an eye on membership changes in Microsoft 365 groups. Knowing how to extract information from the audit log is a skill that every Microsoft 365 enterprise tenant administrator should have.

Unfortunately, while some fields are standard, the bulk of the interesting audit information is in the AuditData property. Workloads can stick whatever they like into AuditData, and some workloads take free liberty to do their own thing. The result is that interpreting the content of audit events is always “interesting” and takes more time than it should.

In this case, the action we want to check is “update user.” (with or without the full stop). Not all of the events logged for this action are interesting because some originate from background processes. Checking a bunch of events revealed that the ones which hold relevant data have an entry in the ModifiedProperties property called “Included Updated Properties.” For instance, the Entra ID audit record shown in Figure 1 includes this data when ingested into the unified audit log:

$Auditdata.ModifiedProperties | fl

Name     : City
NewValue : [
             "Flayosc"
           ]
OldValue : [
             "Dublin"
           ]

Name     : Country
NewValue : [
             "France"
           ]
OldValue : [
             "Ireland"
           ]

Name     : TelephoneNumber
NewValue : [
             "+33 4 9242554"
           ]
OldValue : [
             "01-2070807"
           ]

Name     : StreetAddress
NewValue : [
             "24 Chemin de Floriege"
           ]
OldValue : [
             "15 Fairways"
           ]

Name     : State
NewValue : [
             "Var"
           ]
OldValue : [
             "Dublin"
           ]

Name     : PostalCode
NewValue : [
             "83780"
           ]
OldValue : [
             "D18A6R4"
           ]

Name     : Included Updated Properties
NewValue : City, Country, TelephoneNumber, StreetAddress, State, PostalCode
OldValue :

Six properties are described in the Included Updated Properties property, but there’s no trace of UsageLocation. So no joy there…

Writing a Script to Report Changed Properties

After discovering how audit records hold details about updated properties, it didn’t take too long to create a script to report changed properties for user accounts. The biggest problem is extracting data in a reportable format from the AuditData property. With some trial and error and some persistence, it’s possible to generate a reasonable report. Figure 2 shows the script output.

Reporting changes to user account properties
Figure 2: Reporting changes to user account properties

You can download the script from GitHub. It is very much an example to illustrate the principal of extracting this information and can be improved. For instance, detailing the exact changes made to licenses assigned to an account.

The Hassle of Dealing with Non-Standard Audit Events

The unified audit log is a huge benefit for enterprise tenants. It’s just a pity that the Microsoft development groups make it so difficult for administrators to extract information from the audit log because of some quixotic approaches to how they format data written in log events. It would be nice if all groups used a standard method to format audit entries, but I guess that’s not going to happen. But it would be nice if Entra ID logged changes to the usage location property.


Learn how to exploit the data available to Microsoft 365 tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work.

]]>
https://office365itpros.com/2023/08/08/audit-user-account-changes/feed/ 1 61117
Managing Assigned Licenses for Deleted User Accounts https://office365itpros.com/2023/08/07/deleted-user-account-licenses/?utm_source=rss&utm_medium=rss&utm_campaign=deleted-user-account-licenses https://office365itpros.com/2023/08/07/deleted-user-account-licenses/#comments Mon, 07 Aug 2023 01:00:00 +0000 https://office365itpros.com/?p=61072

Why Some Deleted User Accounts Store License Assignment Information And Some Do Not

A reader asks why the Microsoft 365 admin center displays a license for a deleted user account (Figure 1). The follow-up question is how they can remove the license and reassign it to another user.

Deleted user account with license assignment information
Figure 1: Deleted user account with license assignment information

The answer is that they don’t need to do anything. When an administrator removes a user account, Entra ID moves the account into its deleted items container (aka the wastebasket). The deleted account remains there for 30 days, during which time an administrator can restore the account (see the big blue button in Figure 1). The ideal situation is for a restored account to come back with all its settings intact, including assigned licenses. Entra ID tracks the licenses that the deleted account once had so that it can reassign the licenses to the newly-restored account.

Any licenses assigned to a deleted user account become available following the account’s deletion. This includes accounts used for shared mailboxes where assigned licenses exist to enable features like archiving. No one wants to keep expensive licenses on ice pending account restores, so often the licenses end up being assigned to other accounts.

It Depends on How User Accounts Are Deleted

The interesting thing is that the presence of assigned licenses for deleted accounts depends on the method used to delete the account. When an administrator deletes an account through the Microsoft 365 admin center, the process removes license assignments before removing the account, which means that if you examine the properties of the deleted account afterward, no licenses are present (Figure 2).

Deleted user account with no license assignment information
Figure 2: Deleted user account with no license assignment information

However, if you use PowerShell or the Microsoft Entra admin center to remove an account, the deleted account object retains license information. The licenses are not assigned, but the license information is present in the properties of the deleted user object. This is why Figure 1 shows that a deleted account has a license.

The reason why the Microsoft 365 admin center removes licenses and other administrative interfaces do not is due to the multi-phase process the Microsoft 365 admin center uses for account removal. The process includes steps such giving another user access to the user’s OneDrive for Business account (Figure 3) to allow for the recovery of any important information before the permanent removal of the user account.

Steps in the Microsoft 365 admin center account deletion process
Figure 3: Steps in the Microsoft 365 admin center account deletion process

PowerShell and the Microsoft Entra admin center only concern themselves with the removal of the user account object, and that’s why some deleted user accounts have license assignment information and others do not.

Care Needed When Restoring Deleted Accounts

The Microsoft 365 admin center user restore process warns administrators to:

  • Assign licenses after restoring the account.
  • Change the account password.

A user account has no access to Microsoft 365 services after it is restored until these steps are complete.

By comparison, if you restore a deleted account through the Microsoft Entra admin center or PowerShell, the license assignments noted in the account properties become active again. This can lead to an over-assignment condition where too many user accounts have licenses for specific products, like Office 365 E3. In this situation, administrators must buy additional licenses or remove licenses from other accounts (or delete other accounts).

To check if the properties of any deleted accounts include license assignments, you can run these Microsoft Graph PowerShell SDK commands to fetch details of deleted accounts and report if any license data exists:

Connect-MgGraph -Scope Directory.Read.All
[array]$DeletedUsers = Get-MgDirectoryDeletedItemAsUser -Property DeletedDateTime, Id, displayName, userPrincipalName, assignedlicenses | Sort-Object DeletedDateTime -Descending
ForEach ($User in $DeletedUsers) {
  If ($User.assignedLicenses) {
     $Licenses = $User | Select-Object -ExpandProperty assignedLicenses
     [string]$Skus = $Licenses.SkuID -Join ", "
     Write-Host ("Deleted user {0} has license information noted in their account properties {1}" -f $User.displayName, $Skus ) }
}

If you use PowerShell to script the recovery of user accounts, you should check for license assignments and validate that available licenses are available before recovering the account. This article explains how to fetch subscription information using the Get-MgSubscribedSku cmdlet and the subscriptions API, including the count of assigned and available licenses. It’s easy to check if a license for a SKU is available before assigning it to a recovered account.

Alternatively, go ahead and recover the account and fix the licensing problem later through the Microsoft 365 admin center.

Processing Differences Exist

This discussion reveals a difference in behavior between the raw processing performed by Graph APIs and the wrapper around the APIs implemented in the Microsoft 365 admin center. Sometimes the differences bubble up to the surface and the reasons for the differences aren’t immediately clear until you poke around to discover why things happen the way that they do. Isn’t that often the case in IT?


Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2023/08/07/deleted-user-account-licenses/feed/ 5 61072
Microsoft Entra ID Access Reviews Get Machine Learning Recommendations https://office365itpros.com/2023/07/21/entra-id-access-review-ai/?utm_source=rss&utm_medium=rss&utm_campaign=entra-id-access-review-ai https://office365itpros.com/2023/07/21/entra-id-access-review-ai/#respond Fri, 21 Jul 2023 01:00:00 +0000 https://office365itpros.com/?p=60922

Entra ID Access Reviews Based on User Affiliation Won’t Work Without an Accurate Directory

I like the idea behind Azure AD (Entra ID) Access Reviews and have previously described their use to identify inactive guest members of Microsoft 365 groups. I’ve also covered how to use the Graph API to retrieve information about Access Reviews.

Access reviews are a premium identity governance feature, which means that the people involved in reviews need Azure AD Premium P2 licenses (more on this later). Given that automation saves time, paying for a few P2 licenses is not a big deal.

Which brings us to the July 17 announcement covering two new features for Access reviews:

  • Machine learning powered access review recommendations (user to group affiliation).
  • User inactivity access review scoping.

Artificial Intelligence Everywhere

As everyone knows, Microsoft’s current big bet is the application of machine learning and artificial intelligence wherever possible within their products. Microsoft 365 Copilot is the poster child for this initiative, but I have my doubts that many organizations will seize the opportunity to buy $30 monthly licenses to help people create better documents or process email more quickly. Yes, it’s only a dollar a day, but a dollar a day across an entire organization soon becomes big money, especially if you need to buy Microsoft 365 E3 or E5 licenses to become eligible to use Copilot.

Directory Accuracy Key for User-to-Group Affiliation in Entra ID Access Reviews

According to Microsoft’s documentation, “Machine Learning based recommendation opens the journey to automate access reviews, thereby enabling intelligent automation and reducing access rights attestation fatigue.” That’s quite a promise. In reality, recommendations based on user-to-group affiliation means that access reviews use a machine learning-based score to figure out if group members are close to or far from other group members in terms of the organization’s reporting structure.

In other words, if you are in a team with another person and share the same manager, you have high affiliation. Someone else who works in a completely different part of the organization and has a manager who has no relationship in the reporting structure to another in your reporting chain has low affiliation with you and your co-workers.

This is yet another example of a Microsoft feature that depends on a high level of accuracy for manager-employee links in the directory. Unhappily, the directory of some tenants is sadly neglected, with just enough attention being paid to ensure that users can sign into their accounts. Maintaining organizational information so that Teams, the Microsoft 365 user profile, and Outlook’s Org Explorer can display accurate organization charts is not as high on the agenda as Microsoft obviously thinks it should be.

Finding Inactive Group Members

Machine learning to analyze user affiliation won’t work for guest members of Microsoft 365 groups because these accounts usually aren’t part of the organization’s reporting structure. What Microsoft calls “User inactivity access review scoping” means checking sign-in logs to establish if an account has signed in within a set period (like 30 days). If they haven’t signed in, the account is deemed to be inactive and becomes a candidate for removal from the group.

Although it’s got a spiffing new name, this feature was in public preview for a long time and is a blunt instrument for detecting inactivity. For instance, many guests participate in group discussions via email. They don’t need to sign into the host tenant to receive copies of group discussions.

Testing Entra ID Access Reviews with Affiliation

In any case, I decided to try out the new features. After creating a new access review and requesting that the review should include user-to-group affiliation, I saw that Entra ID politely rejected my request (Figure 1).

No AI available for this Entra ID Access Review
Figure 1: No AI available for this Entra ID Access Review

The answer lies in the fact that access reviews including user to group affiliation or inactive users require Microsoft Entra ID Governance licenses. I have Azure AD Premium P1 and P2 licenses, but no governance licenses.

According to Microsoft’s June 7 announcement for Microsoft Entra ID Governance:

ID Governance can be added to Azure AD Premium P1 or P2 licenses as a cost-effective way to bring comprehensive identity governance to all employees and business guests, for $7 per user per month for Azure AD Premium Plan 1 (P1) customers.”

Microsoft offers a 1-month free trial of 25 licenses for Microsoft Entra ID Governance. In this instance, I’ll pass. Perhaps the delights of Entra ID Governance will attract me in the future. It’s just disappointing to find that features are blocked because of new licensing demands.

Reviewing Guests is a Good Idea, and You Don’t Need Entra ID Access Reviews to Check

It’s sensible to review guest accounts regularly and remove those that are no longer used for B2B collaboration (guest accounts in Teams and groups) or SharePoint sharing. You don’t need Access Reviews to check guest accounts as some basic PowerShell will do the trick. And if you only want to find old guest accounts over a certain age, you can do that with this script. Even better, neither script won’t need any additional licenses.


So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across Office 365. Subscribe to the Office 365 for IT Pros eBook to receive monthly insights into what happens, why it happens, and what new features and capabilities mean for your tenant.

]]>
https://office365itpros.com/2023/07/21/entra-id-access-review-ai/feed/ 0 60922
Microsoft Launches Restricted Administrative Units in Preview https://office365itpros.com/2023/07/14/restricted-administrative-units/?utm_source=rss&utm_medium=rss&utm_campaign=restricted-administrative-units https://office365itpros.com/2023/07/14/restricted-administrative-units/#comments Fri, 14 Jul 2023 01:00:00 +0000 https://office365itpros.com/?p=60850

Restricted Administrative Units Protect Sensitive User Accounts and Security Groups

Following up on its announcement of the wonders promised by the renaming of Azure AD to Microsoft Entra ID, Microsoft released the preview of Entra ID Restricted Administrative Units, a type of administrative unit designed to protect sensitive user accounts, devices, and security groups from unfettered access by tenant administrators. Microsoft describes three scenarios when they think this capability is useful:

  • Protect user accounts for people such as senior executives so that accounts holding regular administrative roles cannot perform tasks such as resetting passwords for those accounts.
  • Enable country-level administration for specific user accounts and security groups.
  • Restrict the ability to update the membership of security groups that protect sensitive data.

It’s worth noting that restrictions apply within Entra ID. Administrators can continue to process updates to mailbox properties such as adjusting the primary SMTP address of mailboxes owned by accounts within restricted administrative units.

Creating a Restricted Administrative Units

Creating a restricted administrative group is simple. Go to the Microsoft Entra admin center, access the administrative units blade, and add a new unit. Make sure that the Restricted management administrative unit option is set to Yes (Figure 1).

Creating a new restricted administrative unit
Figure 1: Creating a new restricted administrative unit

You can’t switch a normal administrative unit to restricted after creation, nor can you do the reverse and remove the restricted scope to make a restricted administrative unit “normal” once it’s created.

Management Roles for Restricted Administrative Units

Next, just like a regular administrative unit, you assign management roles. The difference is that Entra ID scopes these roles to the administrative unit, so you should assign appropriate roles that you consider necessary to manage the accounts and security groups (Microsoft 365 groups and distribution lists are unsupported) that are members of the administrative unit. For instance, if you want country-level management for user accounts, you’d assign administrators from that country to the User administrator role.

Figure 2 shows the final point in the creation wizard, and you can see that two roles assignments exist for the restricted administrative unit. Administrators of restricted administrative units require A Microsoft Entra ID P1 licenses.

Final stage of creating a new restricted administrative unit
Figure 2: Final stage of creating a new restricted administrative unit

Microsoft’s documentation includes more detail, including some limits and restrictions.

Restricted Administrative Units in Action

The nice thing about restricted administrative unit is that accounts assigned global (full directory) roles cannot override the scoping that restricts management access to the administrative unit. Take the situation where a global administrator attempts to update the job title of an account that’s a member of a restricted administrative unit. The Microsoft Entra admin center blocks access to editing account properties (Figure 3).

Restricted administrative unit scoping prevents account property updates
Figure 3: Restricted administrative unit scoping prevents account property updates

And if the administrator tries to circumvent the block with PowerShell by running the Update-MgUser cmdlet, the operation fails with an insufficient privileges error:

Update-MgUser -UserId Rene.Artois@office365itpros.com -JobTitle "Cafe Owner and Resistence Hero"

update-mguser : Insufficient privileges to complete the operation. Target object is a member of a restricted management administrative unit and can only be modified by administrators scoped to that administrative unit. Check that you are assigned a role that has permission to perform the operation for this restricted management administrative unit. Learn more: https://go.microsoft.com/fwlink/?linkid=2197831
Status: 403 (Forbidden)
ErrorCode: Authorization_RequestDenied

Of course, global administrators can solve their problem by removing the account from the restricted administrative unit, updating the account properties, and putting them back into the unit. However, these actions create audit records that might be difficult for the administrator to explain.

Remember that individual user accounts can be members of multiple administrative units. For example, my account could be a member of four administrative units, two of which are restricted. In this situation, holders of roles assigned to either of the restricted administrative units can manage my account.

New and Useful Scoping Mechanism

Restricted administrative units offer another way to scope responsibilities for account, device, and security group management. I suspect the lack of support for Microsoft 365 groups is because of the number of associated workloads that can connect to these groups. Not supporting distribution groups is also unsurprising given their affiliation with Exchange Online. The likelihood is that large enterprises will be most interested in the functionality, but it’s open to all tenants with the necessary licenses.


Learn how to exploit the data available to Microsoft 365 tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work.

]]>
https://office365itpros.com/2023/07/14/restricted-administrative-units/feed/ 2 60850
Microsoft Rebrands Azure AD as Microsoft Entra ID https://office365itpros.com/2023/07/13/microsoft-entra-id-azure-ad/?utm_source=rss&utm_medium=rss&utm_campaign=microsoft-entra-id-azure-ad https://office365itpros.com/2023/07/13/microsoft-entra-id-azure-ad/#comments Thu, 13 Jul 2023 01:00:00 +0000 https://office365itpros.com/?p=60822

Microsoft Entra is the Latest Microsoft Rebranding Triumph

On July 11, Microsoft announced that the Microsoft Entra brand is absorbing Azure Active Directory (Azure AD), which now becomes Microsoft Entra ID. The announcement came along with news of two new Entra products as Microsoft ventures into the Security Service Edge (SSE) arena to take on competitors such as Zscaler, Palo Alto Networks, and Netskope, all leaders in Gartner’s 2023 Magic Quadrant for the SSE space.

According to the announcement, Microsoft 365 scenarios in Microsoft Entra Internet Access are in preview today. The Microsoft Technical Community blog says that Entra Internet Access includes “unique capabilities for Microsoft 365, including Universal Tenant Restrictions, to prevent data exfiltration to other tenants or personal accounts including anonymous access, near-real time threat detection, higher precision of the risk assessment on user, location, and device, and more seamless access to Microsoft 365 apps.” If you’re interested, head to the preview sign-up page for Entra Internet Access.

The rebranding of Azure AD to become Microsoft Entra ID
Figure 1: The rebranding of Azure AD to become Microsoft Entra ID

A message center notification (MC637368) followed up to make sure that Microsoft 365 tenant administrators heard the news about Azure AD’s new name, even though this is just a rebranding exercise that delivers precisely zero new functionality to any tenant. It’s like previous rebranding triumphs where Microsoft made:

  • Microsoft 365 the catch-all brand for Office.
  • Microsoft Defender the catch-all brand for Security.
  • Microsoft Purview the catch-all brand for Compliance.
  • Microsoft Viva the catch-all brand for anything that the marketeers could see.

Web pages might change, documentation might use different terminology, but Azure AD remains the same. Microsoft characterizes the name change as representing “the evolution and unification of the Microsoft Entra product family, and a commitment to simplify secure access experiences for everyone.” This is corporate speak for “we’re stuffing Azure AD into the Microsoft Entra brand to create Entra ID. It makes us look good even if it doesn’t do anything for the end user.” More information about the rebranding is available here.

Microsoft Entra ID Isn’t for On-Premises Software

In my opinion, Azure AD did a good job to deliver secure access experiences with many enhancements delivered over the last two years in the drive to make modern authentication and MFA more pervasive, like adding Authenticator Lite to Outlook mobile, and improving how conditional access policies work by including new capabilities like measuring the authentication strength for connections. The rebrand adds nothing.

A case can be argued that Microsoft is throwing away the reputation accrued over nearly 25 years by Active Directory and Azure Active Directory to give a existing product a new name. Equally, you could argue that renaming Azure AD will remove the confusion that sometimes exists between the cloud and on-premises directories. What’s for sure is that Windows Active Directory is not changing its name because the Entra brand does not extend to on-premises software. The same applies to Active Directory Federation Services (AD FS) and Active Directory Domain Services (AD DS).

On the plus side, Microsoft isn’t changing licensing or pricing. They also say that they’re not changing capabilities, but that’s just corporate fluff too because code doesn’t work differently when it gets a new name. URLs, APIs, and authentication libraries remain the same.

Microsoft Entra Renaming Schedule

Following the 30-day notification period for tenants, Microsoft will roll out the name change over the rest of 2023, with service plan names (the kind you see when assigning licenses to user accounts) due to change on October 1, 2023. You can download a useful CSV file of license and service plan names from Microsoft. I use this data in the Microsoft 365 licensing report script (updated to work with the Microsoft Graph PowerShell SDK V2).

By early 2024, we should all have transferred our allegiance to Microsoft Entra ID and consigned the Azure AD name to the wastebasket of computer brands.

More Work for the Book Team

From the perspective of the Office 365 for IT Pros eBook, we have some work to do to update our chapters to replace Azure AD with Entra ID where appropriate. I guess we don’t have to do this immediately, but it’s certainly something that must happen over time. It’s just another item for our to-do list!


Learn how to exploit the data available to Microsoft 365 tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work.

]]>
https://office365itpros.com/2023/07/13/microsoft-entra-id-azure-ad/feed/ 1 60822
Retrieving Entra ID Privileged Identity Management Role Assignments https://office365itpros.com/2023/07/12/privileged-identity-management-ps/?utm_source=rss&utm_medium=rss&utm_campaign=privileged-identity-management-ps https://office365itpros.com/2023/07/12/privileged-identity-management-ps/#comments Wed, 12 Jul 2023 01:00:00 +0000 https://office365itpros.com/?p=60809

Taking Account of PIM When Blocking User Access to Exchange Online PowerShell

Updated 13 November 2024 – See this article for more information about PIM role assignments and this article for an updated reporting script.

In May, I wrote a Practical365.com article about disabling PowerShell access to Exchange Online for all but administrative accounts. Given the happiness of attackers to use PowerShell to attack Exchange (mostly against Exchange Server, but certainly also Exchange Online), it makes sense to remove the ability of “normal” users to run Exchange cmdlets.

In any case, the example script I use in the article demonstrates how to use the Get-MgDirectoryRoleMember cmdlet to find holders of the Exchange administrator and Global administrator roles. These are the people who need to run PowerShell against Exchange Online, so the script leaves their accounts intact. For anyone else, the script calls the Set-User cmdlet to disable PowerShell access. I suggest that the script is a good candidate for Azure Automation to make sure that new accounts can’t use PowerShell.

Privileged Identity Management

Everything works for most tenants. The problem is that some tenants use Azure AD Privileged Identity Management (PIM), an optional service that requires Azure AD Premium P2 licenses. PIM is most commonly used by large enterprises to control access to resources. Unlike normal open-ended permanent assignments to privileged roles like Exchange administrator, PIM allows the assignments to be time-limited on an on-demand basis.

To do this, PIM differentiates between eligible and active role assignments. An eligible role assignment is not currently effective. If needed, an administrator can activate the assignment to allow its holder to use the permissions available to active role holders. Assignments can be time-limited and expire after a certain period. A comment for the original article pointed out that it didn’t handle PIM assignments and the script is therefore unusable in tenants that use PIM.

If you look at role assignments through the Privileged Identity Management section of the Microsoft Entra admin center, you can see those with eligible, active, and expired assignments for the different roles used in the tenant. Figure 1 shows the active assignments for the Exchange administrator and Global administrator roles. You can see that some service principals are in the set of Exchange administrators. Azure Automation uses these service principals to allow managed identities to sign into Exchange Online and run cmdlets as an administrator.

 PIM assignments for the Exchange administrator and Global administrator roles
Figure 1: PIM assignments for the Exchange administrator and Global administrator roles

The problem is that the Get-MgDirectoryRoleMember cmdlet only reports active role assignments. The assignments eligible for activation are ignored. For the purposes of this exercise, tenants using PIM must include accounts with eligible assignments when determining what accounts can access PowerShell.

Privileged Identity Management APIs

After some searching, I found a script written by Paul Contreras that explains how to get PIM role assignments for Azure AD. The script uses the Get-AzureADMSPrivilegedRoleAssignment cmdlet from the AzureADPreview module to retrieve assignments.

Given that the AzureADPreview module is due for deprecation in March 2024, I looked for an equivalent Microsoft Graph PowerShell SDK cmdlet. Microsoft’s cmdlet map to help developers move from the Azure AD and MSOL modules to the SDK didn’t help. I had great hope for the Get-MgBetaRoleManagementDirectoryRoleAssignment cmdlet but the cmdlet appears to only return “normal” role assignments.

One complication is that the current (beta) Graph API for governance role assignments is due for deprecation. Its documentation points to “Privileged Identity Management iteration 2 APIs.” Obviously, the underlying APIs are in a state of change, so the lack of SDK support isn’t surprising.

Amending the Role Assignment Script for PIM (Updated)

I amended the original script to use the Get-AzureADMSPrivilegedRoleAssignment cmdlet to fetch the assignments known for the Global administrator and Exchange administrator roles. This was fine until the retirement of the AzureAD module. V2.0 of the script replaces the AzureAD cmdlet with the Get-MgBetaRoleManagementDirectoryRoleAssignmentSchedule cmdlet from the Microsoft Graph PowerShell SDK (V2.22).

Write-Output "Retrieving assignment information from Privileged Identity Management..."                    
# Get PIM assignments for accounts holding Exchange administrator or Global administrator roles
[array]$ActiveAssignments = Get-MgBetaRoleManagementDirectoryRoleAssignmentSchedule -Filter "(RoleDefinitionId eq '$($ExoAdminRoleId)') or (RoleDefinitionId eq '$($GlobalAdminRoleId)')" -ExpandProperty RoleDefinition, Principal, DirectoryScope -All

# Filter out the Exchange administrators
[array]$ExoRoleMembers = $ActiveAssignments | Where-Object {$_.RoleDefinitionId -eq $ExoAdminRoleId} | Select-Object RoleDefinitionId, Principal, MemberType   
If (!($ExoRoleMembers)) { Write-Output "Can't find any Exchange administrators! Exiting..." ; break }                                                                                                

# Do the same for global administrators
[array]$GARoleMembers = $ActiveAssignments | Where-Object {$_.RoleDefinitionId -eq $GlobalAdminRoleId} | Select-Object RoleDefinitionId, Principal, MemberType
If (!($GARoleMembers)) { Write-Output "Can't find any global administrators! Exiting..." ; break }

The script then loops through the arrays of assignments to fetch details of user account (with Get-MgUser) and members of groups used for PIM (with Get-MgGroupMember). The script stores information about the assignments that we can report (Figure 2).

Reporting PIM role assignments

Privileged Identity Management
Figure 2: Reporting PIM role assignments

The next step is to create an array of administrator user principal names to check against Exchange mailboxes. Basically, if a mailbox belongs to an administrator, we allow PowerShell access. If it doesn’t, we block PowerShell access.

[array]$ExoMailboxes = Get-ExoMailbox -Filter {CustomAttribute5 -eq $Null} -ResultSize Unlimited -RecipientTypeDetails UserMailbox -Properties CustomAttribute5
ForEach ($Mbx in $ExoMailboxes) {
   # If not an admin holder, go ahead and block PowerShell
   If ($Mbx.userPrincipalName -notin $AdminAccounts) {
     Write-Output ("Blocking PowerShell access for mailbox {0}..." -f $Mbx.displayName)
     Try {
         Set-User -Identity $Mbx.userPrincipalName -RemotePowerShellEnabled $False -Confirm:$False
         $MessageText = "PowerShell disabled on " + (Get-Date -format s)
         Set-Mailbox -Identity $Mbx.userPrincipalName -CustomAttribute5 $MessageText
     }
     Catch {
         Write-Output ("Error disabling PowerShell for mailbox {0}" -f $Mbx.userPrincipalNane )
     }
   }
} # End ForEach

An improvement to the original script is that the final step is to check that administrator accounts have PowerShell access. This is to pick up new administrators that receive individual PIM assignments or join a group with a PIM assignment.

Write-Output "Checking administrator mailboxes to make sure that they have PowerShell access..."
ForEach ($Mbx in $AdminAccounts) {
   [string]$mbx = $mbx
   $PSEnabled = (Get-User -Identity $Mbx  -ErrorAction SilentlyContinue).RemotePowerShellEnabled
   If (!($PsEnabled)) {
        Write-Output ("Resetting PowerShell access for admin account {0}" -f $Mbx)
        Set-User -Identity $Mbx -RemotePowerShellEnabled $True -Confirm:$False 
   }
}

The full script is available from GitHub.

Always Learning

The nice thing about working with Microsoft 365 is that there’s always something to learn. Authors learn from the comments posted for our articles. The comments force us to research before we can answer questions posed by readers. That’s a good thing.


Support the work of the Office 365 for IT Pros team by subscribing to the Office 365 for IT Pros eBook. Your support pays for the time we need to track, analyze, and document the changing world of Microsoft 365 and Office 365.

]]>
https://office365itpros.com/2023/07/12/privileged-identity-management-ps/feed/ 4 60809
How to Report Renewal Dates for Microsoft 365 Subscriptions https://office365itpros.com/2023/07/06/microsoft-365-subscriptions-ps/?utm_source=rss&utm_medium=rss&utm_campaign=microsoft-365-subscriptions-ps https://office365itpros.com/2023/07/06/microsoft-365-subscriptions-ps/#comments Thu, 06 Jul 2023 01:00:00 +0000 https://office365itpros.com/?p=60708

New Method to Retrieve Renewal Dates for Microsoft 365 Subscriptions

As part of my campaign to help people move off the old MSOL and AzureAD PowerShell modules to use the Microsoft Graph PowerShell SDK before Microsoft deprecates the modules, I wrote a script to demonstrate how to use the Graph SDK to create a licensing report for a tenant. One of the replies to the article observed that the output of the Get-MgSubscribedSku cmdlet didn’t provide the same information as the old Get-MsolSubscription cmdlet. Specifically, the SDK cmdlet doesn’t tell you the renewal date for a product (SKU).

Relief is now available, but not yet in an SDK cmdlet. Instead, you can fetch the renewal information using a new beta Graph subscriptions endpoint described in Vasil’s blog. This is different to the SubscribedSku API, which is what I think is the base for the Get-MgSubscribedSku cmdlet. The LicenseAssignment.Read.All or a higher permission (like Directory.Read.All) is needed to use the API.

Practical Example of Displaying Renewal Dates for Microsoft 365 Subscriptions

As an example of how you might use the information, I took the output generated by the Get-MgSubscribedSku cmdlet and reformatted it so that it looks like the output from the Get-MsolSubscription cmdlet. The cmdlet lists the SKU part number, active units (available units), warning units (licenses that have expired or have another problem), and consumed units (licenses assigned to user accounts). I wanted to add the renewal date and number of days until the renewal date.

To fetch the renewal dates, I then use the Invoke-MgGraphRequest cmdlet to query the https://graph.microsoft.com/V1.0/directory/subscriptions endpoint. If a SKU has a renewal date, it is in the nextLifecycleDateTime property. Some SKUs that don’t expire (like Power BI standard) don’t have renewal dates. Here’s an example of the information for a Viva Topics subscription that has a renewal date.

Name                           Value
----                           -----
skuId                          4016f256-b063-4864-816e-d818aad600c9
skuPartNumber                  TOPIC_EXPERIENCES
createdDateTime                05/02/2021 18:09:21
totalLicenses                  25
id                             de6eac24-b4b7-4f7e-abeb-9e4f10b36883
serviceStatus                  {System.Collections.Hashtable, System.Collections.Hashtable, System.Collections.Hasht...
ocpSubscriptionId              eeda0292-642e-4901-9825-aa7dfc9b0efc
isTrial                        True
status                         Warning
nextLifecycleDateTime          30/07/2023 14:53:22

To make it easy to lookup the renewal data for a SKU, I created a hash table to store SKU identifiers and renewal dates. The final step is to loop through the SKU information and add the renewal date. Here’s the code:

Connect-MgGraph -Scopes Directory.Read.All -NoWelcome
# Get the basic information about tenant subscriptions
[array]$Skus = Get-MgSubscribedSku
$SkuReport = [System.Collections.Generic.List[Object]]::new()
ForEach ($Sku in $Skus) {
 $DataLine = [PSCustomObject][Ordered]@{
   SkuPartNumber = $Sku.SkuPartNumber
   SkuId         = $Sku.SkuId
   ActiveUnits   = $Sku.PrepaidUnits.Enabled
   WarningUnits  = $Sku.PrepaidUnits.Warning
   ConsumedUnits = $Sku.ConsumedUnits }
 $SkuReport.Add($Dataline)
}

# Get the renewal data
$Uri = "https://graph.microsoft.com/V1.0/directory/subscriptions"
[array]$SkuData = Invoke-MgGraphRequest -Uri $Uri -Method Get
# Put the renewal information into a hash table
$SkuHash = @{}
ForEach ($Sku in $SkuData.Value) { $SkuHash.Add($Sku.SkuId,$Sku.nextLifecycleDateTime) }

# Update the report with the renewal information
ForEach ($R in $SkuReport) {
  $DaysToRenew = $Null
  $SkuRenewalDate = $SkuHash[$R.SkuId]
  $R | Add-Member -NotePropertyName "Renewal date" -NotePropertyValue $SkuRenewalDate -Force 
  If ($SkuRenewalDate) {
   $DaysToRenew = -(New-TimeSpan $SkuRenewalDate).Days
   $R | Add-Member -NotePropertyName "Days to renewal" -NotePropertyValue $DaysToRenew -Force 
 }
}

$SkuReport | Format-Table SkuPartNumber, ActiveUnits, WarningUnits, ConsumedUnits, "Renewal date", "Days to renewal" -AutoSize

Figure 1 shows the output.

Reporting Microsoft 365 subscriptions with renewal dates.
Figure 1: Reporting Microsoft 365 subscriptions with renewal dates

Future SDK Cmdlet Will Probably Come

Obviously, it would be much better if an SDK cmdlet exposed renewal dates for Microsoft 365 subscriptions. Given that the subscriptions endpoint is new, it’s likely that a new SDK will appear after Microsoft’s AutoRest process runs to process the metadata for the endpoint. I’d expect this to happen sometime in the next few weeks.

In the interim, if access to subscription renewal dates is holding up the migration of some old MSOL or AzureAD scripts, a solution is available.


Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2023/07/06/microsoft-365-subscriptions-ps/feed/ 5 60708
Reporting User-Preferred MFA Methods for Entra ID User Accounts https://office365itpros.com/2023/06/21/report-user-authentication-methods/?utm_source=rss&utm_medium=rss&utm_campaign=report-user-authentication-methods https://office365itpros.com/2023/06/21/report-user-authentication-methods/#comments Wed, 21 Jun 2023 01:00:00 +0000 https://office365itpros.com/?p=60513

New Graph API Reveals MFA Preferred Authentication Method for User Accounts

Graph authentication methods

In his copious spare time when he’s not reviewing chapters of the Office 365 for IT Pros eBook in his technical editor role, Vasil Michev writes for his blog. A recent post covers the Graph API to configure multi-factor authentication methods for Azure AD user accounts. This API is helpful because it fills in a gap in Graph coverage.

We’ve been able to report authentication methods set on accounts for quite a while, but setting methods has been problematic, especially with the upcoming deprecation of the Microsoft Services Online module (MSOL). Until now, the MSOL cmdlets to deal with “strong authentication methods” are what people have had to use in automation scenarios. Go to Vasil’s blog to learn about how to fetch and set the preferred MFA authentication method for Azure AD accounts (the signInPreferences object for accounts), or read up on the documentation.

Vasil makes the point that the new APIs have not yet appeared in the form of cmdlets in the Microsoft Graph PowerShell SDK. This is because a process needs to run (called AutoRest) to generate the SDK cmdlets from Graph APIs. Microsoft runs the process regularly, but some delay is always expected.

Invoke Graph Requests

The workaround is to use the Invoke-MgGraphRequest cmdlet. Here’s an example of using the cmdlet to fetch details of all Entra ID user accounts that have at least one assigned license (to filter out accounts used for room mailboxes, etc.) The filter used with the Get-MgUser cmdlet is a good example of using a lambda operator with what Microsoft calls a complex Entra ID query (the check assigned licenses). Because it’s a complex query, we need to use the ConsistencyLevel parameter and pass eventual as its value. If you haven’t seen this kind of filter used to find accounts before, store it away because it’ll be one that you use time and time again in your scripts.

After fetching the set of users, it’s a matter of running the query to return the authentication sign in preferences for each account and storing the details in a PowerShell list object. Here’s the code:

Connect-MgGraph -Scopes UserAuthenticationMethod.ReadWrite.All, User.ReadBasic.All -NoWelcome
[array]$Users = Get-MgUser -Filter "assignedLicenses/`$count ne 0 and userType eq 'Member'" -ConsistencyLevel eventual -CountVariable Records -All

$Report = [System.Collections.Generic.List[Object]]::new() 
ForEach ($User in $Users) {
 $Uri = ("https://graph.microsoft.com/beta/users/{0}/authentication/signInPreferences" -f $User.Id)
 $AuthData = Invoke-MgGraphRequest -Uri $Uri -Method Get

 $ReportLine = [PSCustomObject]@{
    User   = $User.displayName
    UPN    = $User.userPrincipalName
    'System preferred MFA enabled' = $AuthData.isSystemPreferredAuthenticationMethodEnabled
    'System preferred MFA method'  = $AuthData.systemPreferredAuthenticationMethod
    'Secondary auth method'        = $AuthData.userPreferredMethodForSecondaryAuthentication }
  $Report.Add($ReportLine)

}

System Preferred Authentication Policy

An important factor to take into account is the existence of the Entra ID system-preferred authentication policy, which is now generally available. When this policy is active (as it soon will be for all tenants), Entra ID uses the strongest authentication method available to an account. A note in the documentation for updating authentication methods says that “this value is ignored except for a few scenarios where a user is authenticating via NPS extension or ADFS adapter.” That’s something to consider when updating user accounts.

Progress, Not Perfect

I don’t think anyone would say that things are perfect in terms of the transition from the old MSOL and Entra ID PowerShell modules to the Graph (APIs or SDK cmdlets). Migrations are never perfect, and we’ll be coping with the effects of this changeover for many months to come. That being said, it’s nice to see progress, albeit in small steps.


Learn how to exploit the data available to Microsoft 365 tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work.

]]>
https://office365itpros.com/2023/06/21/report-user-authentication-methods/feed/ 2 60513
Azure AD Access Token Lifetimes and Long-running PowerShell Scripts https://office365itpros.com/2023/05/29/azure-ad-access-token-lifetime/?utm_source=rss&utm_medium=rss&utm_campaign=azure-ad-access-token-lifetime https://office365itpros.com/2023/05/29/azure-ad-access-token-lifetime/#respond Mon, 29 May 2023 01:00:00 +0000 https://office365itpros.com/?p=60194

Sometimes Scripts Need Extended Azure AD Access Token Lifetimes

A recent issue where Microsoft limited the page size for the Graph List Users API when retrieving sign-in activity sparked a request from a reader who had problems with a script. They reported that roughly an hour into the script, it failed with a 401 Unauthorized error. The reason is that the access token granted to the app to allow it to run Graph requests to fetch data expired, meaning that the next time the app tried to request data, the Graph refused.

The default Azure AD access token lifetime varies between 60 and 90 minutes (75 minutes on average). The variation exists on purpose to avoid cyclical spikes in demand. Exceptions to the rule do exist. For example, applications like SharePoint Online and OWA that support continuous access evaluation (CAE) can use tokens that last up to 28 hours. These apps support a feature known as claim challenge that is unlikely to be found in apps that execute Graph requests through PowerShell.

Apps can retrieve access tokens from Azure AD using different OAuth 2.0 authentication flows, including password, device code, and authorization code. Azure AD registered apps usually use the client credentials authentication flow. The app authenticates using its own credentials instead of trying to impersonate a user. Valid app credentials include a secret known to the app, a certificate, or a certificate thumbprint.

The client credentials authentication flow does not include the issuance of a refresh token. The lack of a refresh token, which allows apps to silently renew access tokens, means that if you want to keep a script running, you must either:

  • Configure the tenant with a longer access token lifetime.
  • Include code in the script to fetch a new access token before the current one expires.

Configurable Azure AD Access Token Lifetimes

Azure AD supports configurable token lifetimes. This is a preview feature that can set a longer lifetime for an access token. However, the current implementation supports setting token lifetimes for all apps in an organization or for multi-tenant applications. For instance, this code creates a new token lifetime policy that sets a default two-hour token lifetime. Note the organization default setting is True, so this policy applies to all apps in the organization.

$PolicySettings = @{
    "definition"= @("{'TokenLifetimePolicy':{'Version': 1, 'AccessTokenLifetime': '2:00:00'}}")
    "displayName"= "Org-wide 2 Hr AccessTokenPolicy"
    "IsOrganizationDefault" = $True
} 
 
New-MgPolicyTokenLifetimePolicy -BodyParameter $PolicySettings

To test the policy, use an app to request an access token. Here is some PowerShell code to get an access token using the client credentials authentication flow. In this case, the credential is a client secret stored in the app.

$AppId = “de0d7a5d-982a-49e2-8c52-f4596f32b437”
$TenantId = “a662313f-14fc-43a2-9a7a-d2e27f4f3478”
$AppSecret = “3il8Q~Yx4_DOJZxHAxvp7akxW5TQxXdSzhsGpdme”
$Uri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
$Body = @{
    client_id     = $AppId
    scope         = "https://graph.microsoft.com/.default"
    client_secret = $AppSecret
    grant_type    = "client_credentials"
}
# Get OAuth 2.0 Token
$TokenRequest = Invoke-WebRequest -Method Post -Uri $Uri -ContentType "application/x-www-form-urlencoded" -Body $body -UseBasicParsing
# Unpack Access Token
$Token = ($tokenRequest.Content | ConvertFrom-Json).access_token

Write-Host ("Retrieved new access token at {0}" -f (Get-Date)) -foregroundcolor red

Take the access token stored in the $Token variable and examine its contents. For example, Figure 1 shows how jwt.io displays the settings in an access token. To verify that the token lifetime works is expected, compare the time of issuance with the expiration time. In this instance, the timespan should be two hours.

Checking the expiration time for an Azure AD access token

Azure AD access token lifetime
Figure 1: Checking the expiration time for an Azure AD access token

Although creating a token lifetime policy with a new default lifetime for the organization works, increasing token lifetime in this manner is not something to do on a whim. It would be better to be able to assign a token lifetime policy only to the apps that need to use extended token lifetimes.

An organization can support multiple token lifetime policies. It would be nice to be able to apply suitable policies to apps as needed but this doesn’t seem to be possible currently. The Microsoft PowerShell Graph SDK includes the New-MgApplicationTokenLifetimePolicyByRef cmdlet, and you can use the cmdlet assign a token lifetime policy to an application. Alas, this has no effect on the access tokens issued by Azure AD to the app. I’ve been discussing this point with Microsoft and investigations continue.

Tracking Azure AD Access Token Lifetime in Scripts

The alternative is to incorporate code into scripts to track the lifetime of an access token so that the script can retrieve a new token before the old one expires. The script for the Microsoft 365 Groups and Teams Activity Report uses this technique. A function checks if the current time is greater than the calculated token expiration time. If it is, the script requests a new token:

Function Check-AccessToken {
# Function to check if the access token needs to be refreshed. If it does, request a new token
# This often needs to happen when the script processes more than a few thousands groups
$TimeNow = (Get-Date)
if($TimeNow -ge $TokenExpiredDate) {
  $Global:Token = GetAccessToken
  $Global:TokenExpiredDate = (Get-Date).AddMinutes($TimeToRefreshToken) 
#  Write-Host "Requested new access token - expiration at" $TokenExpiredDate 
}
Return $Token
}

The function can then be called whenever necessary within the script.

$Global:Token = Check-AccessToken

This is a relatively unsophisticated mechanism, but it allows the script to process tens of thousands of groups. Variations on the theme can handle other situations.

Only for Special Scripts

The default lifetime for an access token is sufficient for most scripts. Even scripts that run dozens of Graph requests can usually complete processing in a few minutes. It is scripts that must retrieve tens of thousands of items (or even hundreds of thousands of items) that usually deal with inadequate Azure AD access token lifetimes. In those cases, you’ll be glad that methods exist to avoid the dreaded 401 Unauthorized error.


Support the work of the Office 365 for IT Pros team by subscribing to the Office 365 for IT Pros eBook. Your support pays for the time we need to track, analyze, and document the changing world of Microsoft 365 and Office 365.

]]>
https://office365itpros.com/2023/05/29/azure-ad-access-token-lifetime/feed/ 0 60194
Microsoft Graph Early Adopter Badges and Other Stuff https://office365itpros.com/2023/05/26/microsoft-graph-early-adopter/?utm_source=rss&utm_medium=rss&utm_campaign=microsoft-graph-early-adopter https://office365itpros.com/2023/05/26/microsoft-graph-early-adopter/#comments Fri, 26 May 2023 01:00:00 +0000 https://office365itpros.com/?p=60213

Giving Microsoft Feedback, Azure AD Cmdlet Throttling, and Microsoft Graph Early Adopter Badges

I’m the proud possessor of a badge awarded through the Microsoft Graph Early Adopter Recognition program, something that I never knew about nor realized that badges were on offer. Notification about the badge arrived in a surprise email. Apparently, the badge (Figure 1) recognizes people who provide Microsoft with valuable (meaningful) feedback about Microsoft Graph tools and SDKs. According to the program blurb, you should submit feedback by creating an issue in the GitHub repository of a Microsoft Graph product to allow the program managers to know about the issue and recognize the feedback.

The Microsoft Graph Early Adopter badge
Figure 1: The Microsoft Graph Early Adopter badge

Feedback About the Microsoft Graph PowerShell SDK

As far as I know, the only time I left feedback like this was to note some concerns about the direction Microsoft was heading for V2.0 of the Microsoft Graph PowerShell SDK. Microsoft has still not addressed some of the concerns, especially around the proposal to have sets of differently-named cmdlets for the V1.0 and beta endpoints.

If Microsoft’s proposal proceeds, anyone who’s upgrading PowerShell code to replace cmdlets from the soon-to-retire Azure AD and Microsoft Online Services modules with Microsoft Graph PowerShell SDK will have to revisit their code anywhere they use the beta endpoint. For example, the Get-MgUser cmdlet doesn’t return license details for an account via the V1.0 endpoint but it does via the beta endpoint. In any case, spirited discussions continue about that point.

Azure AD and MSOL Cmdlet Throttling

Speaking of the PowerShell retirements, I’ve noticed many examples where people report problems with throttling of their scripts. Figure 2 shows an example reported in the Facebook Office 365 Technical Discussions group where the New-MsolUser cmdlet halted after 20 transactions because it “exceeded the maximum number of allowable transactions.” The advice to “try again later” is because this is a temporary throttle imposed by Microsoft to advise people that they need to move off the deprecated modules.

Figure 2: The woes of throttling hit the New-MsolUser cmdlet
Figure 2: The woes of throttling hit the New-MsolUser cmdlet

It would be nice if Microsoft issued a more explicit and understandable error message. Something along the lines of “this cmdlet will stop working on 30 June 2023. Time to update your code! For now, we’re just throttling, but we will get serious soon…” It’s obvious that the message about the module retirements has not landed in some places, even though Microsoft has been banging the drum for about two years.

Note that cmdlet throttling only happens for cmdlets that interact with license management. The other Azure AD and MSOL cmdlets will continue working after the retirement date, but assigning and updating licenses to Azure AD accounts through PowerShell should now use Graph API requests or Graph SDK cmdlets.

Getting Back to Badges

Going back to the original topic, I don’t quite know how to feel about badges awarded for giving feedback. People like recognition, which is why Teams has the Praise app and Viva Insights includes a version of that app. The joy went out of those apps when Microsoft removed support for the creation and use of custom badges.

I never put myself in the category of those motivated by badges or awards, so I am ambivalent about the Microsoft Graph Early Adopter badge. I understand why the Microsoft team has gone down the path of creating a digital badge to recognize external contributions, but it doesn’t move my needle. But if it floats your boat, enjoy the opportunity to share the recognition with your nearest and dearest.

On the point of feedback, maybe the best way to let Microsoft know exactly what you think about their products, at least in the Microsoft 365 space, is to use the feedback portal. Spend a little time thinking about the message you want to send. Write it down in Word or another text editor. Leave it for an hour or so and then check the text again. And finally, copy your feedback into the appropriate space. You know it makes sense.


Support the work of the Office 365 for IT Pros team by subscribing to the Office 365 for IT Pros eBook. Your support pays for the time we need to track, analyze, and document the changing world of Microsoft 365 and Office 365

]]>
https://office365itpros.com/2023/05/26/microsoft-graph-early-adopter/feed/ 1 60213
Protected Actions for Azure AD Conditional Access Policies https://office365itpros.com/2023/05/11/protected-actions-ca/?utm_source=rss&utm_medium=rss&utm_campaign=protected-actions-ca https://office365itpros.com/2023/05/11/protected-actions-ca/#comments Thu, 11 May 2023 01:00:00 +0000 https://office365itpros.com/?p=60059

Protected Actions are a New Method to Highlight Specific Administrative Actions

Over the last year or so, Microsoft has pumped out a set of enhancements to make Azure AD conditional access policies more flexible and powerful. Changes such as token protection (to help address the threat of token theft) and authentication strength (to insist on a specific form of multi-factor authentication for a connection) are good examples of what’s going on.

The latest preview defines a set of “Protected actions” for use with conditional access. The preview associates an authentication context (previously used to mark sensitive SharePoint Online sites) with administrator actions in a conditional access policy. When active, the policy insists that administrators who wish to perform actions specified in the policy must meet specific requirements. For example, instead of satisfying a multi-factor authentication challenge with the Microsoft authenticator app, the policy might force administrator to use a FIDO2 key before Azure AD allows them to perform an action.

Limited Set of Protected Actions for Preview

For now, the preview supports seven protected actions. Three are related to named locations; four cover management of conditional access policies. The set is enough to let people understand the concept of what Microsoft is trying to do and I expect Microsoft to add more protected actions over time.

Using Protected Actions

To start, go to the Conditional Access section of the Microsoft Entra admin center and define an authentication context. The easiest way to think about an authentication context is to regard it as a tag to mark something to protect with a conditional access policy. In this case, the tag links some protected actions with a policy. When Azure AD assesses connections, it knows that anytime accounts within the scope of the policy try to perform a protected action, their connection must meet the conditions set in the policy. A tenant can define up to 25 authentication contexts to use as they wish. To test protected actions, I created an authentication context called CAPolicy.

Next, create a conditional access policy to use the new authentication context. Figure 1 shows what I used. The policy covers some selected users and specifies the newly-created authentication context. The access control requires passwordless MFA.

Conditional access policy to use protected actions
Figure 1: Conditional access policy to use protected actions

The next step is to add protected actions to the authentication context. Open the Roles & Admins section of the Entra admin center and select Protected actions. Select the authentication context and then add protected actions (referred to as permissions in the GUI). You only need to add a single action to make the conditional access policy effective. I chose the four actions related to conditional access policies (Figure 2).

Selecting protected actions to link to an authentication context
Figure 2: Selecting protected actions to link to an authentication context

Testing Protected Actions

Now sign in as one of the accounts within the scope of the conditional access policy without using passwordless authentication and try to amend the settings of a conditional access policy (one of the four protected actions selected above). You can amend settings like adding a new authentication context or changing the accounts and groups within the scope of the policy, but you can’t save updates to a conditional access policy through the GUI (Figure 3) or with PowerShell (using the Microsoft Graph PowerShell SDK).

Blocking protected actions
Figure 3: Blocking protected actions

If the account is enabled for multi-factor authentication and can satisfy the challenge requirements set by the policy, Azure AD displays a “click here to reauthenticate” banner to allow the user to go through “step-up authentication” and meet the requirements. In the example shown in Figure 3, the account isn’t MFA-enabled and therefore cannot authenticate in the manner set by the policy, which is why Azure AD simply disables updates.

For more information, consult the online documentation.

Solid if Limited Concept (for Now)

Protected actions is a preview, with limited capabilities due toa small set of selectable actions. However, there’s enough there to see how valuable this concept might be if Microsoft expands the set of protectable actions to cover more features available through the Microsoft Entra admin center and perhaps even the Azure admin center.


So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across Microsoft 365. Subscribe to the Office 365 for IT Pros eBook to receive monthly insights into what happens, why it happens, and what new features and capabilities mean for your tenant.

]]>
https://office365itpros.com/2023/05/11/protected-actions-ca/feed/ 1 60059
Find Out Who’s Using Teams Shared Channels in Another Tenant https://office365itpros.com/2023/04/24/teams-shared-channels-profile/?utm_source=rss&utm_medium=rss&utm_campaign=teams-shared-channels-profile https://office365itpros.com/2023/04/24/teams-shared-channels-profile/#respond Mon, 24 Apr 2023 01:00:00 +0000 https://office365itpros.com/?p=59879

Use Inbound and Outbound Shared User Profiles to Reveal the Ins and Outs of Membership in Teams Shared Channels

In August 2022, I discussed how to use the Get-AssociatedTeam cmdlet to report the membership of channels in teams for users within a tenant. It’s a useful cmdlet that includes the ability to report membership of shared channels in other tenants. In most cases, the reports that can be generated from the data returned by the Get-AssociatedTeam cmdlet meet the needs of administrators to know what channels users access.

Microsoft 365 often offers multiple ways to report data. In this instance, Azure AD supports shared user profile resources created for use with Azure AD B2B Direct Connect, the underlying cross-tenant access mechanism for shared channels.

  • An inbound shared user profile represents an Azure AD user from an external Azure AD tenant whose profile data is shared with your tenant. The profile data is used by applications like Teams to display information about the inbound user in shared channels.
  • Conversely, an outbound shared user profile represents Azure AD users from your tenant who share their profile information when they access resources in other Azure AD tenants.

Essentially, when a shared channel owner invites an external user to become a member of the channel and that user confirms their acceptance, Azure AD creates an inbound shared user profile to note this fact. Azure AD creates an outbound shared user profile when a user from your tenant becomes a member of a shared channel hosted by another tenant. For example, Figure 1 shows the membership of a Teams shared channel. The users marked with (External) have inbound shared user profiles.

Membership information for Teams shared channels
Figure 1: Membership information for Teams shared channels

Using PowerShell to Report Shared User Profiles

The Microsoft Graph PowerShell SDK contains cmdlets to fetch information about unbound and outbound user profiles. With consent for the CrossTenantUserProfileSharing.Read.All permission, you can connect to the Graph and run these commands:

Connect-MgGraph -Scopes CrossTenantUserProfileSharing.Read.All
Select-MgProfile beta
Get-MgDirectoryOutboundSharedUserProfile

UserId
------
08dda855-5dc3-4fdc-8458-cbc494a5a774
5b52fba5-349e-4624-88cd-d790883fe4c4
a221d10f-e0cf-4a1d-b6a2-4e844670f118
cad05ccf-a359-4ac7-89e0-1e33bf37579e
eff4cd58-1bb8-4899-94de-795f656b4a18

The output is a list of identifiers for Azure AD user accounts, so it’s not very exciting. IApart from not listing account names, the output doesn’t tell us what outbound tenants are accessed. To get that information, we must run the Get-MgDirectoryOutboundSharedUserProfileTenant cmdlet for each account. The output of that cmdlet is a list of tenant identifiers, which we can resolve to discover the tenant name. Here’s the code:

[array]$Users =  Get-MgDirectoryOutboundSharedUserProfile | Select-Object -ExpandProperty UserId
ForEach ($User in $Users) {
   $UserData = Get-MgUser -UserId $User
   [array]$TenantNames = $Null; $TenantDisplayNames = $Null
   [array]$TenantIds = Get-MgDirectoryOutboundSharedUserProfileTenant -OutboundSharedUserProfileUserId $User | Select-Object -ExpandProperty TenantId
   If ($TenantIds) {
       ForEach ($TenantId in $TenantIds) {
         $Uri = ("https://graph.microsoft.com/beta/tenantRelationships/findTenantInformationByTenantId(tenantId='{0}')" -f $TenantId.ToString())
         $ExternalTenantData = Invoke-MgGraphRequest -Uri $Uri -Method Get  
         $TenantNames += $ExternalTenantData.DisplayName
       }
       $TenantDisplayNames = $TenantNames -join ", "    
   }
   Write-Host ("User {0} has outbound shared profiles in these tenants {1}" -f $UserData.DisplayName, $TenantDisplayNames)
}

User Sean Landy has outbound shared profiles in these tenants o365maestro
User Ken Bowers has outbound shared profiles in these tenants o365maestro
User Tony Redmond has outbound shared profiles in these tenants o365maestro, Microsoft Community & Event Tenant

Getting Inbound Shared User Profiles

The Get-MgDirectoryinboundSharedUserProfile cmdlet lists information stored about inbound shared user profiles. We can’t read Azure AD to find information about these users because they come from other tenants. This is what the cmdlet returns:

Get-MgDirectoryinboundSharedUserProfile | Format-List

DisplayName          : Alex Wilber
HomeTenantId         : 22e90715-3da6-4a78-9ec6-b3282389492b
UserId               : a6453657-2058-4c15-a38a-b0a94f0ed737
UserPrincipalName    : AlexW@o365maestro.onmicrosoft.com
AdditionalProperties : {}

Once again, we can resolve the tenant identifier to make the information more understandable:

[array]$Guests = Get-MgDirectoryinboundSharedUserProfile
ForEach ($Guest in $Guests) {
    $Uri = ("https://graph.microsoft.com/beta/tenantRelationships/findTenantInformationByTenantId(tenantId='{0}')" -f $Guest.HomeTenantId.ToString())
    $ExternalTenantData = Invoke-MgGraphRequest -Uri $Uri -Method Get  
    Write-Host ("User {0} comes from tenant {1}" -f $Guest.DisplayName,  $ExternalTenantData.DisplayName)
}

User Christina Smith comes from tenant CM Portal Solutions
User Nicolas Blood comes from tenant NBConsult
User Alex Wilber comes from tenant o365maestro
User Tom Jones comes from tenant o365maestro
User Vlad Bitton comes from tenant vNext Solutions

The interesting thing here is that I didn’t recognize some of the user names and tenants that Azure AD stored inbound shared user profiles for. However, given that the names were all MVPs and my tenant supported many beta versions of Teams shared channels in the past, it’s entirely possible that the profiles originated in a test. Azure AD doesn’t register a date to tell you when it created a profile, so there’s no clue from that source.

Tracking External Access to Teams Shared Channels

My previous article describes how to create a report about the users accessing shared channels in your tenant. The added piece of information covered here is finding the set of Azure AD accounts from your tenant who use Azure AD B2B Connect to access resources in other tenants. That’s a valuable nugget if you want to track who’s interacting with Teams shared channels externally.

]]>
https://office365itpros.com/2023/04/24/teams-shared-channels-profile/feed/ 0 59879
Reducing the Likelihood of Token Theft with Conditional Access Policies https://office365itpros.com/2023/04/21/token-protection-azure-ad-ca/?utm_source=rss&utm_medium=rss&utm_campaign=token-protection-azure-ad-ca https://office365itpros.com/2023/04/21/token-protection-azure-ad-ca/#comments Fri, 21 Apr 2023 01:00:00 +0000 https://office365itpros.com/?p=59891

New Token Protection Conditional Access Policy Session Control

Now that the removal of basic authentication from Exchange Online has made password spray attacks far less likely to compromise user credentials for an Entra ID account, those who want to sneak into a tenant need another avenue to explore. Microsoft’s Detection and Response Team (DART) reports an increase in adversary-in-the-middle phishing attacks where attempts are made to capture user credentials and the tokens used by applications to access protected resources like user mailboxes or SharePoint Online sites.

If you need further evidence of the techniques used to compromise and exploit tokens, this article by Edwin David is a good read. It’s a reminder that although all user accounts should be protected by multi-factor authentication, MFA is not a silver bullet and attackers will continue to develop methods to work around barriers erected by tenants.

Token Binding to Devices

Which brings me to a new session control for Entra ID conditional access policies designed to protect sign-in tokens (refresh tokens) using token protection. The control, which has just appeared in preview, creates a “cryptographically secure tie” between the token and the device Entra ID issues the token to (aka token binding). Without the client secret (the device), the token is useless to an attacker. The device needs to run Windows 10 or above and be Entra ID joined, hybrid Entra ID joined, or registered in Entra ID. When this is the case, a user’s identity is bound to the device.

Microsoft notes that “Token theft is thought to be a relatively rare event, but the damage from it can be significant.” One interpretation of this statement is that Microsoft knows the bad guys are working on using more token thefts, so they’re investing to get ahead of the curve.

Clients

It’s a preview, so some limitations are inevitable. For instance, conditional access policies with token protection can only process connections from tenant accounts and can’t handle inbound connections from guest accounts. Token protection supports Microsoft 365 apps for enterprise subscription versions of desktop clients accessing Exchange Online and SharePoint Online. Perpetual versions of the Office apps aren’t supported. The apps include the OneDrive sync client (22.217 or later) and the Teams desktop client (1.6.00.1331 or later). These are relatively old versions already, so meeting the software requirements should not be a big issue.

PowerShell clients accessing Exchange Online, SharePoint Online, or these endpoints via the Microsoft Graph APIs are unsupported by conditional access policies with token protection, meaning that users are blocked from accessing Exchange and SharePoint. The same is true for some other apps like Visual Studio and the Power BI desktop app. It also applies to connections generated using OWA and Outlook mobile.

In effect, the users selected to test a token protection condition access policy (Figure 1) should be those who don’t need to use any of the unsupported clients and are happy to limit their access to Outlook desktop and Teams.

Parts of a conditional access policy with token protection
Figure 1: Parts of a conditional access policy with token protection

Users who don’t meet the policy requirements (like attempting to sign in with OWA, the browser version of Teams, or the SharePoint Online or OneDrive for Business browser clients) will fail to connect (Figure 2).

Token protection stops an app connecting
Figure 2: Token protection stops an app connecting

In fact, any Office browser app that connects to Exchange or SharePoint resources will be inaccessible. For instance, Viva Engage (Yammer) will start up before immediately exiting when the client attempts to access SharePoint Online.

A Pointer to the Future

Given the relative lack of support by Microsoft 365 apps for token protection, this preview feature is unlikely to get the same range of testing as other recent extensions to conditional access policies (like authentication strength). That being said, if token theft becomes as biga  problem as some security commentators think it might, it will be good to have methods like token protection ready to repel the threat.


Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2023/04/21/token-protection-azure-ad-ca/feed/ 1 59891
The Right Way to Revoke Access from Entra ID User Accounts with PowerShell https://office365itpros.com/2023/04/18/revoke-access-for-entra-id-accounts/?utm_source=rss&utm_medium=rss&utm_campaign=revoke-access-for-entra-id-accounts https://office365itpros.com/2023/04/18/revoke-access-for-entra-id-accounts/#comments Tue, 18 Apr 2023 01:00:00 +0000 https://office365itpros.com/?p=59843

Use the Revoke-MgUserSignInSession cmdlet to Revoke Access for Entra ID Accounts

Updated 24 January 2024

Microsoft’s documentation for how to revoke access to an Entra ID user account describes the use of the Revoke-AzureADUserAllRefreshToken cmdlet from the Azure AD PowerShell module. That’s unfortunate because of the upcoming deprecation of that module. If we consult Microsoft’s cmdlet map to find the appropriate replacement cmdlet from the Microsoft Graph PowerShell SDK, it turns out to be Invoke-MgInvalidateUserRefreshToken, which “Invalidates all of the user’s refresh tokens issued to applications (as well as session cookies in a user’s browser), by resetting the refreshTokensValidFromDateTime user property to the current date-time.”

The guidance could not be clearer. Any script using the Revoke-AzureADUserAllRefreshToken should replace it with the Invoke-MgInvalidateUserRefreshToken cmdlet. Except when you discover that the SDK also includes the Revoke-MgUserSignInSession cmdlet. This cmdlet is in beta and its documentation is less than perfect (or totally inadequate), but the salient fact is that it performs the same task. These two commands have the same effect:

$RevokeStatus = Revoke-MgUserSignInSession -UserId $UserId
$InvalidateStatus = Invoke-MgInvalidateUserRefreshToken -UserId $UserId

Up to now, the Office 365 for IT Pros eBook (chapter 5) documented how to use the Invoke-MgInvalidateUserRefreshToken cmdlet to block an Entra ID user account. Finding the alternative cmdlet used in a Microsoft example provoked a query to ask why two cmdlets did the same thing.

Microsoft’s response is that they built Invoke-MgInvalidateUserRefreshToken for a specific purpose. The cmdlet still works and has the significant benefit of being part of the production (V1.0) module. However, Microsoft’s recommendation is to use Revoke-MgUserSignInSession in the future.

Revoking Access for an Entra ID Account is the Start

Of course, revoking access for an Entra ID user account might just be the first step in the process of securing the account. Revoking access will force the user to reauthenticate, but if you want to stop further access to the account, you must:

Disabling the account and changing the password are both critical events that force Entra ID to signal applications that support continuous access evaluation (CAE) to terminate sessions. Many of the important Microsoft 365 apps like Outlook and SharePoint Online support CAE (see current list).

This PowerShell code does the necessary. The account signed into the Microsoft Graph PowerShell SDK must have the user administrator role to update account details and the Cloud Device Administrator role to update the device. Alternatively, the Global administrator role will do the job:

Connect-MgGraph -Scopes Directory.AccessAsUser.All
$Account = Read-Host "Enter the User Principal Name of the account to block"
$User = (Get-MgUser -UserId $Account -ErrorAction SilentlyContinue)
If (!($User)) { Write-Host ("Can't find an Entra ID user account for {0}" -f $Account); break }
Write-Host ("Revoking access and changing password for account {0}" -f $User.DisplayName)  
# Disable the account
Update-MgUser -UserId $User.Id -AccountEnabled:$False
# Create a password profile with details of a new password
$NewPassword = @{}
$NewPassword["Password"]= "!NewYorkCity2022?"
$NewPassword["ForceChangePasswordNextSignIn"] = $True
Update-MgUser -UserId $User.Id -PasswordProfile $NewPassword
# Revoke signed in sessions and refresh tokens
$RevokeStatus = Revoke-MgUserSignInSession -UserId $User.Id
If ($RevokeStatus.Value -eq $true) {
   Write-Host ("Access revoked for user {0}" -f $User.DisplayName)
}
# Disable registered devices
[array]$UserDevices = Get-MgUserRegisteredDevice -UserId $User.Id
If ($UserDevices) {
ForEach ($Device in $UserDevices) {
    Update-MgDevice -DeviceId $Device.Id -AccountEnabled:$False}
}

Figure 1 shows that after running the script, the user account is disabled and the SignInSessionsValidFromDateTime property (referred to as refreshTokensValidFromDateTime above) is set to the time when the Revoke-MgUserSignInSession cmdlet ran.

Running PowerShell to revoke access for an Entra ID account.
Figure 1: Running PowerShell to revoke access for an Entra ID user account

Consequences of Disabling an Entra ID User Account

In a scenario like a departing employee, losing access to some teams might not be important. If it is, or in situations where it’s necessary to preserve the account in full working order, an alternative to disabling an account is to change its password and revoke access. The account remains active but is inaccessible unless those attempting to sign-in know the new password.

Example of Knowledge Gap

In July 2022, I wrote about the opening of a knowledge gap as tenants transitioned from the depreciated Azure AD and Microsoft Online Services (MSOL) modules. Having two cmdlets that revoke user access to pick from is one too many. It doesn’t help people migrate scripts to use the Microsoft Graph PowerShell SDK. But at least the recommendation is clear: use Revoke-MgUserSignInSession.


Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2023/04/18/revoke-access-for-entra-id-accounts/feed/ 9 59843
Generate a HTML Report of Managers and Direct Reports with the Graph SDK https://office365itpros.com/2023/04/06/entra-id-manager-direct-reports/?utm_source=rss&utm_medium=rss&utm_campaign=entra-id-manager-direct-reports https://office365itpros.com/2023/04/06/entra-id-manager-direct-reports/#comments Thu, 06 Apr 2023 01:00:00 +0000 https://office365itpros.com/?p=59707

Creating a Report From Entra ID Manager and Direct Reports Data with PowerShell

It’s always good to be able to build on the knowledge contributed by someone else. This brings me to a post by Vasil Michev, the esteemed technical editor for the Office 365 for IT Pros eBook. The post covers how to Create an All Managers group in Microsoft 365 and covers how to do this in different ways for different types of group. It brought back some memories of Microsoft’s initiative in April 2017 to auto-generate a Microsoft 365 group for every manager with its membership populated with the manager’s direct report.

Retrieving Entra ID Managers and Direct Reports

In any case, Vasil discussed how the Get-Recipient (but not Get­ExoRecipient) and Get-User cmdlets have a filter to find accounts that have direct reports using the backlink from users to their managers. By definition, these accounts are managers, so you can use the commands as the basis to control the membership of distribution lists, dynamic distribution lists, or Microsoft 365 groups.

Get-Recipient -Filter {DirectReports -ne $Null}
Get-User -Filter {DirectReports -ne $Null}

The only problem is that the output of the two cmdlets is imperfect. The cmdlets find accounts with direct reports, but their results include some accounts that don’t have any direct reports. In my tenant, I found that the cmdlets found three accounts with no direct reports. I believe that these accounts had direct reports at some point in the past, but they don’t now. For instance, when I queried the accounts to see the set of direct reports reported by Get-User, I see a blank:

Get-User -Identity Ben.Owens | Select-Object Name, Manager, DirectReports

Name      Manager      DirectReports
----      -------      -------------
Ben Owens tony.redmond {}

The same is true when viewing details of the account through Exchange address lists, the organization chart in Teams, or the Outlook Org Explorer (Figure 1).

Outlook Org Explorer lists no direct reports for a manager
Figure 1: Outlook Org Explorer lists no direct reports for a manager

According to message center notification MC492902 (updated 7 February 2023), the Outlook Org Explorer is only available to users with the “Microsoft Viva Suite” or “Microsoft Viva Suite with Glint” licenses, which is why you might not be seeing it. Originally, Microsoft said that the Org Explorer would be available to accounts with Microsoft 365 E3/E5 or Microsoft 365 Business licenses, but they decided to align this feature with the Viva initiative. The Org Explorer is not available for OWA.

My conclusion is that synchronization between Entra ID and Exchange Online leaves some vestige behind in the DirectReports property following the removal of the last direct report for a manager. It’s enough to stop the filter working accurately.

Reporting Entra ID Managers and Direct Reports

Which brings me back to considering how to report the links between managers and employees using the information stored in Entra ID. I covered this ground in an article two years ago, but I didn’t realize the flaw in Get-User at the time, so the script I wrote (available from GitHub) can produce incorrect results. A different approach is needed.

Given that Entra ID is the source of the information, it makes sense to use Graph APIs to retrieve data. I chose to use the Microsoft Graph PowerShell SDK to avoid the necessity to create a registered app.

The new script (also available from GitHub) does the following:

  • Finds user accounts with at least one assigned license. This step filters out accounts created for purposes like room and shared mailboxes.
  • Use the Get-MgUserManager cmdlet to check each account to see if it has a manager. If not, note this fact.
  • Use the Get-MgUserDirectReport cmdlet to see if the account has direct reports. If it does, record the details of the manager’s reports.
  • Create an HTML report detailing each manager and their reports.
  • At the end of the report, add a section detailing accounts without managers.
  • Output the HTML file and a CSV file containing details of managers and reports.

Figure 2 shows some example output. Because the code is PowerShell, it’s easy to tweak it to include other information about each employee.

Reporting managers and their direct reports

Azure AD Managers
Figure 2: Reporting managers and their direct reports

Go to the Source to Find Managers and Direct Reports

It’s never nice to discover that a technique you thought worked well is no longer fit for purpose and it’s necessary to rework a script. The Get-User and Get-Recipient cmdlets return accurate information about managers and direct reports, but only if managers always have at least one report. I guess that’s possible, but it’s better to make sure by using Graph APIs to retrieve data about managers and their direct reports. At least then you’ll know that your reports show the same reporting relationships that surface elsewhere in Microsoft 365.


Support the work of the Office 365 for IT Pros team by subscribing to the Office 365 for IT Pros eBook. Your support pays for the time we need to track, analyze, and document the changing world of Microsoft 365 and Office 365.

]]>
https://office365itpros.com/2023/04/06/entra-id-manager-direct-reports/feed/ 6 59707
Microsoft Limits Graph API Requests for User Account Data https://office365itpros.com/2023/04/05/signinactivity-limit-graph-api/?utm_source=rss&utm_medium=rss&utm_campaign=signinactivity-limit-graph-api https://office365itpros.com/2023/04/05/signinactivity-limit-graph-api/#respond Wed, 05 Apr 2023 01:00:00 +0000 https://office365itpros.com/?p=59723

Old Limit with SignInActivity was 999 – New Limit for Azure AD Accounts is 120

Because it retrieves details of Azure AD accounts, the List Users API is one of the most heavily used of the Microsoft Graph APIs. It also underpins the Get-MgUser cmdlet from the Microsoft Graph PowerShell SDK. Microsoft generates the cmdlet from the API using a process called AutoRest, which means that changes made to the API show up soon afterward in the cmdlet.

I’ve documented some of the issues that developers must deal with when coding with the cmdlets from the Microsoft Graph PowerShell SDK. The cmdlets have been stable recently, which is a relief because tenants are migrating scripts from the Azure AD and MSOL modules. However, last week an issue erupted in a GitHub discussion that caused a lot of disruption.

In a nutshell, if you use List Users to fetch Azure AD accounts and include the SignInActivity property, the API limits the page size for results to 120 items. Calls made without specifying SignInActivity can set the page size to be anything up to 999 items.

An Unannounced Change

To help manage demand on the service, all Graph API requests limit the number of items that they return. To retrieve all matching items for a request, developers must fetch pages of results until nothing remains. When a developer knows that large numbers of items must be fetched, they often increase the page size to reduce the number of requests.

Microsoft didn’t say anything about the new restriction on requests that fetch Azure AD account data with sign-in activity. Developers only discovered the problem when programs and scripts failed. I first learned of the issue when some of the users of the Office 365 for IT Pros GitHub repository reported that a Graph request which included a $top query parameter to increase the page size to 999 items failed. For example:

$uri = "https://graph.microsoft.com/beta/users?`$select=displayName,userPrincipalName,mail,id,CreatedDateTime,signInActivity,UserType&`$top=999"
[array]$Data = Invoke-RestMethod -Method GET -Uri $Uri -ContentType "application/json" -Headers $Headers
Invoke-RestMethod : The remote server returned an error: (400) Bad Request.
At line:1 char:16
+ ... ray]$Data = Invoke-RestMethod -Method GET -Uri $Uri -ContentType "app ...
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest)
   [Invoke-RestMethod], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.I

As shown in Figure 2, testing with the Get-MgUser cmdlet revealed some more information in the error (“Cannot query data for more than 120 users at a time”). This was the first time I learned about a query limit:

Get-MgUser reports more useful error information

Cannot query data for more than 120 users at a time (SignInActivity)
Figure 2: Get-MgUser reports more useful error information

According to a response reported in the GitHub discussion, Microsoft support reported

The PG have confirmed that this endpoint will be transitioning from beta to General Availability (GA).

As part of this transition, changes to its behavior has been made, this includes not requesting more than 120 results per call. They recommend requesting less than 120 results per call, which can be done by setting the top parameter to, say 100.”

It’s likely that Microsoft made the change because retrieving sign-in activity data for Azure AD accounts is an expensive operation. Reducing the page size to 120 possibly makes it easier to process a request than if it asked for 999 items.

Beta Version of List Users Moving to Production

When the product group (PG) says that the endpoint is transitioning from beta to GA, it means that instead of needing to use https://graph.microsoft.com/beta/users to access sign-in activity, the data will be available through https://graph.microsoft.com/V1.0/users. If you use the Microsoft Graph PowerShell SDK, you won’t have to run the Select-MgProfile cmdlet to choose the beta endpoint. Moving the beta version of the API to the production endpoint is a good thing because there are many other account properties now only available through the beta endpoint (like license assignments).

If you use the Microsoft Graph PowerShell SDK, the Get-MgUser cmdlet is unaffected by the change if you specify the All parameter. This is because the cmdlet handles pagination internally and fetches all pages automatically without the need to specify a page size. For instance, this works:

$AccountProperties = @( ‘Id’, ‘DisplayName’, ‘SignInActivity’)
[array]$Users = Get-MgUser -All -Property $AccountProperties | Select-Object $AccountProperties

Moving to Production

Although it’s good that Microsoft is (slowly) moving the beta versions of the List Users API towards production, it’s a pity that they introduced a change that broke so many scripts and programs without any warning. At worse, this so exhibits a certain contempt for the developer community. At best, it’s a bad sign when communication with the developer community is not a priority. That’s just sad.


Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2023/04/05/signinactivity-limit-graph-api/feed/ 0 59723
Time Running Out for AzureAD and MSOL PowerShell Modules https://office365itpros.com/2023/04/04/azuread-powershell-retirement/?utm_source=rss&utm_medium=rss&utm_campaign=azuread-powershell-retirement https://office365itpros.com/2023/04/04/azuread-powershell-retirement/#comments Tue, 04 Apr 2023 01:00:00 +0000 https://office365itpros.com/?p=59646

Last Gasp for AzureAD PowerShell Retirement as Deadline Approaches

Updated 2 April 2024

Microsoft’s original announcement about the deprecation of the AzureAD and Microsoft Online Services (MSOL) PowerShell modules goes back to 26 August, 2021. At that time, Microsoft wanted to have the retirement done by June 30, 2022. Customer pushback duly ensued and Microsoft decided to push the dates out another year to allow customers more time to upgrade their scripts. They subsequently deferred the deprecation for a further nine months to March 30, 2024. The time for the AzureAD PowerShell retirement is now rapidly approaching.

This was the only sensible course of action. The Graph APIs for dealing with many Entra ID user account interactions, especially license assignments, were sadly undocumented. The suggestion of using cmdlets from the Microsoft Graph PowerShell SDK ran into difficulties because some of the cmdlets didn’t work as expected. Allied to that, the documentation for the SDK cmdlets remains poor and inscrutable at times. These issues have largely been addressed.

Microsoft Graph PowerShell SDK Improved Over Time

Time is a great healer and allows for improvements to be made. The Graph Explorer works better and the Graph X-Ray tool reveals details about how Microsoft uses Graph calls in places like the Microsoft Entra admin center.

In addition, Microsoft developed documentation to help people migrate scripts, including a cmdlet map to translate old cmdlets to new. The important thing to realize here is that automatic translation from one set of cmdlets to the other is difficult. People code in PowerShell in different ways and it’s not always clear how to translate code to a new cmdlet. Some community-based projects do exist (here’s a new one that is spinning up), but any attempt to covert to SDK cmdlets must take the SDK foibles into consideration, like its fundamental disregard for the PowerShell pipeline.

But mostly time allowed people to share their knowledge about how to use SDK cmdlets to automate administrative tasks like user and group management. For instance, here’s a writeup I did about license management for Entra ID user accounts using the SDK, and here’s another covering how to create a license report for Entra ID user accounts.

What Will Happen Between Now and the Final AzureAD PowerShell Retirement

But time eventually runs out and we are now at the point where Microsoft will soon retire the AzureAD and MSOL modules. Here’s my understanding of the situation:

  • The licensing cmdlets from the AzureAD and MSOL modules do not work for tenants created after November 1, 2022. These tenants must use Graph APIs or SDK cmdlets to manage license assignments for Entra ID user accounts.
  • For all tenants, March 30, 2024 is the official deprecation date for the licensing cmdlets in the AzureAD and MSOL modules.
  • Apart from the cmdlets that assign or work with licenses, deprecation doesn’t mean “stop working.” Microsoft blocks the cmdlets that to Entra ID user accounts. This is in line with the warning posted on July 29, 2022, that “Customers may notice performance delays as we approach the retirement deadline,” The affected cmdlets are:
    • Set-MsolUserLicenseSet-AzureADUserLicense
    • New-MsolUser (where the creation of an account includes a license assignment)
The Set-AzureADUserLicense cmdlet stopped  working on June 30, 2023

AzureAD PowerShell retirement
Figure 1: The Set-AzureADUserLicense cmdlet stopped working on June 30, 2023
  • After March 30, 2024, the AzureAD and MSOL modules are deprecated and unsupported apart from security fixes. With the notable exception of the licensing cmdlets, Microsoft says that the modules will continue to function until March 30, 2025. At that point, Microsoft will retire the AzureAD and MSOL modules. Cmdlets from the two modules might still run, but no guarantees exist that they will be successful. In other words, scripts might fail without warning.

The Bottom Line About the AzureAD PowerShell Retirement

The AzureAD and MSOL modules are now on borrowed time. If you haven’t already started to upgrade scripts to use the Graph APIs or the Microsoft Graph PowerShell SDK, scripts that use these modules could encounter an unpleasant failure very soon. It’s time to get busy to make sure that all scripts are migrated to run using the SDK cmdlets before March 30, 2024.


The Office 365 for IT Pros eBook includes hundreds of examples of working with Microsoft 365 through PowerShell. We explain how to run Microsoft Graph API queries through PowerShell and how to use the cmdlets from the Microsoft Graph PowerShell SDK in a very practical and approachable manner.

]]>
https://office365itpros.com/2023/04/04/azuread-powershell-retirement/feed/ 18 59646
Azure AD Admin Center Moves to Microsoft Entra Admin Center https://office365itpros.com/2023/03/27/changes-in-microsoft-365/?utm_source=rss&utm_medium=rss&utm_campaign=changes-in-microsoft-365 https://office365itpros.com/2023/03/27/changes-in-microsoft-365/#comments Mon, 27 Mar 2023 01:00:00 +0000 https://office365itpros.com/?p=59545

Example of Ongoing Changes in Microsoft 365

I guess we all knew it was coming (after all, Microsoft published message center notification MC477013 in December 2022), but the news that the Microsoft Entra admin center (Figure 1) will replace the Azure AD admin center from April 1, 2023 is yet another example of the ongoing and constant changes in Microsoft 365. Those changes range from a massive introduction of fundamental new functionality, like Microsoft 365 Copilot, to a small update to how something appears.

The Microsoft Entra admin center

Changes in Microsoft 365
Figure 1: The Microsoft Entra admin center – one of the many changes in Microsoft 365

In this instance, Microsoft portraits the replacement of the Azure AD admin center as a unification of its identity management platform (Azure AD) with its identity and access solutions. Another way of looking at the move is that it allows Microsoft to bring those identity and access solutions to the attention of some organizations who wouldn’t otherwise consider them. Every time you open the Entra admin center, identity governance and other solutions will be there to discover. To be fair to Microsoft, if you access Azure AD from the Microsoft 365 admin center, the link goes direct to the Azure AD section of the Entra admin center.

Microsoft says that the old Azure AD admin center will continue to function until May 2023. Azure customers who don’t use Microsoft 365 can manage Azure AD through the Azure portal.

Many Rebranding Campaigns

Microsoft is well known for its love of rebranding campaigns. Microsoft 365 has steadily embraced a huge ecosystem, including the subscription version of the Office apps, and we’ll probably have to rename the next version of the Office 365 for IT Pros eBook to use Microsoft 365 instead. Microsoft Purview is another example, albeit one that at least collected together a bunch of different compliance solutions under a common banner. Defender did the same for security solutions, and so on.

Sometimes, Microsoft makes changes for what appears to be no good reason. Take the announcement in MC532194 (March 23) that Teams now uses an “EA” indicator instead of “P” when users run the preview version of the software. I’m still wondering why “Early Access” is any better than “Preview.” The change appears to deliver zero added value except that it aligns with the nomenclature Microsoft uses in places like the Office Insider program. From my perspective, the change meant that we needed to update Chapter 15 in the Office 365 for IT Pros eBook and our article about Teams preview.

Naming Changes Affect the Wider Technical Community

Microsoft makes naming changes for its own reasons. I doubt that they take the wider community into consideration when they decide on these updates but the effect of a naming change or rebrand ripple through documentation and training. For instance, video training companies that have a program telling people how to use the Azure AD admin center must now update their collateral and perhaps even reshoot some or all of their video. That’s a big cost for the production company.

The same is true for books that cover Azure AD or any of the other topics affected by naming or branding changes. Switching references from the Azure AD admin center to the Entra admin center isn’t quite as simple as doing a search and replace. Microsoft often takes the opportunity to rename options in administrative consoles when they change things. Data lifecycle management is now the place in the Purview compliance portal that was once known as the location for the management of retention labels and policies. The justification is that the section of the portal now spans additional options such as adaptive scopes, policy lookup, and legacy Exchange mailbox retention policies and tags (both of which are still very useful).

Changes in Microsoft 365 Will Keep on Happening

I don’t expect Microsoft to poll the technical community before they change the name of anything inside Microsoft 365. It won’t happen and would be unreasonable. Microsoft will continue to make changes how and when they like, even if the outcome displeases some. Their decision to stop accepting inbound email from old and vulnerable on-premises Exchange servers to protect Exchange Online is a good example of a change that inflamed many opinions. However, we don’t get to vote.

Content producers like Office 365 for IT Pros simply need to be proactive and respond to Microsoft changes the best way we can. In that respect, being able to publish a complete new book every month is a major advantage, even if it takes a lot of hard work. Now back to the task of looking for all those references to the Azure AD admin center – a change that we’ll probably make in the May 2023 update.

]]>
https://office365itpros.com/2023/03/27/changes-in-microsoft-365/feed/ 7 59545
Microsoft Expands Multi-Factor Authentication Methods to Companion Apps https://office365itpros.com/2023/03/22/authenticator-lite-outlook/?utm_source=rss&utm_medium=rss&utm_campaign=authenticator-lite-outlook https://office365itpros.com/2023/03/22/authenticator-lite-outlook/#comments Wed, 22 Mar 2023 01:00:00 +0000 https://office365itpros.com/?p=59524

Introducing Authenticator Lite

Without too much fuss, Microsoft introduced the preview of a new “surface” (way) for users to complete multi-factor authentication (MFA) challenges. The new method is a companion app for the Microsoft Authenticator app and is covered by Microsoft 365 roadmap item 122289 and is slated for roll-out in May 2023.

Azure AD already covers a variety of methods to satisfy MFA challenges. The methods are categorized from weak to strong in terms of their ability to resist attacks and conditional access policies can insist that a connection uses a certain strength of MFA response before it is accepted. “Authenticator lite” is rated as strong as the Authenticator app because it’s basically code taken from Authenticator and built into other Microsoft apps. In addition, Authenticator lite only supports push notifications with number matching and one-time codes, which are less likely to provoke MFA fatigue than the traditional “click here to approve” response.

Outlook Mobile Leads the Way

Outlook mobile (iOS 4.2309.0, Android 4.2308.0, or higher versions) is the first Microsoft 365 app to pick up the Authenticator Lite code. Some might ask why Microsoft choose Outlook as the test case. I think it’s because Outlook is likely the most heavily used mobile client. The last time Microsoft gave a number for Outlook mobile (April 2019), they reported that Outlook for iOS and Android had more than 100 million users. At that time, Office 365 reached 180 million monthly active users. Now Office 365 is up around 400 million monthly active users. Assuming Outlook mobile has kept pace, it has around 220 million monthly active users.

Building MFA responses into the most popular mobile client is a great way of making MFA easier for organizations to deploy. Microsoft wants customers to deploy MFA. They also want customers to use strong MFA responses and move away from methods like SMS text-based responses. The recent introduction of the Azure AD system-preferred authentication policy to force Azure AD to select the strongest available authentication method for a user when it issues a challenge is a pointer to the future. Who needs to resort to an SMS response when you can respond to a number challenge within Outlook? It makes absolute sense.

Update the Azure AD Authentication Methods Policy

If you’re interested in trying Authenticator Lite with Outlook mobile, the steps to make everything happen are covered in a Microsoft article. In summary:

First, use a Graph API PATCH request to update the Azure AD Authentication Methods Policy to update the companionAppAllowedState setting from disabled (the default) to enabled. The easiest way to do this is with the Graph Explorer (make sure to sign in with an administrator account because you’ll need to consent to the Policy.ReadWrite.AuthenticationMethod permission to update the policy. The relevant lines for the policy in my tenant look like those shown in Figure 1. The state is enabled and the policy is targeted at a group of users with an identifier of “all_users.” This is a special identifier that instructs Azure AD to apply the policy setting to all tenant users. If you want to limit the policy to a specific set of users, create a security group with those users as members and update the authentication methods policy with the group identifier.

Checking the settings of the Azure AD Authentication Methods policy

Authenticator Lite
Figure 1: Checking the settings of the Azure AD Authentication Methods policy

The updated policy might take a little time to become effective and people can respond to MFA challenges from Outlook. Only accounts enabled to use the Authenticator app (with the mode set to Push or Any) to respond to MFA challenges can use Authenticator Lite within Outlook, and responses are limited to number matching or one-time codes. It’s important to realize that if the Microsoft Authenticator app is present on a device, Outlook won’t attempt to use Authenticator Lite and instead refers all authentication challenges to the full Authenticator app.

It’s also important to realize that the code incorporated into Outlook supports fewer options than the full Authenticator app. For instance, it doesn’t support Self-Service Password Reset (SSPR). The Authenticator app is a more appropriate option for users who need functionality like handling MFA responses for other cloud services like Twitter and GitHub.

MFA Responses for the Masses

I like any action that reduces the friction of MFA deployment and operation for both organizations and users. Authenticator Lite falls into this category. Although I won’t use the new capability because I need the power of the full Authenticator app, I think that Authenticator Lite will meet the needs of most Microsoft 365 users when it comes to responding to MFA challenges.


Support the work of the Office 365 for IT Pros team by subscribing to the Office 365 for IT Pros eBook. Your support pays for the time we need to track, analyze, and document the changing world of Microsoft 365 and Office 365.

]]>
https://office365itpros.com/2023/03/22/authenticator-lite-outlook/feed/ 1 59524
SharePoint Online Gets Closer to Azure AD https://office365itpros.com/2023/03/20/azure-ad-b2b-collaboration-spo/?utm_source=rss&utm_medium=rss&utm_campaign=azure-ad-b2b-collaboration-spo https://office365itpros.com/2023/03/20/azure-ad-b2b-collaboration-spo/#comments Mon, 20 Mar 2023 01:00:00 +0000 https://office365itpros.com/?p=59428

Azure AD B2B Collaboration and Guest Accounts for SharePoint Sharing

Two recent message center notifications highlight closer integration between SharePoint Online and Azure AD. MC526130 (11 March) says that new tenants created after March 31, 2023 will automatically enable the SharePoint Online integration with Azure B2B integration. Existing tenants aren’t impacted by this change. The associated update, also scheduled for roll-out in late March, is MC525663 (10 March). The news here is that SharePoint Online site sharing will use the Azure B2B Invitation manager instead of the legacy SharePoint Invitation Manager (Microsoft 365 roadmap item 117557).

Rationalization Around Azure AD

The two updates rationalize existing sharing methods with external users and focus on Azure AD as the driving force for managing invitations. The journey toward Azure AD B2B Collaboration started in 2021, so it’s been a while coming. The project makes a lot of sense for both customers and Microsoft (their gain is through reduced engineering expenses).

Ten years ago, it was reasonable for SharePoint to manage site sharing invitations. Today, when the site collection-based architecture is replaced by single-sites and most sharing occurs through Microsoft 365 groups and Teams, it’s illogical for SharePoint Online to have its own mechanism. 280 million monthly active Teams users create a lot of work for SharePoint.

Another factor is that site sharing with external users is a relatively uncommon action today. Most external users join groups or teams and gain access to the group-connected site. Although non-group connected sites do exist, they’re in the minority and some of those sites (like hub and communication sites) aren’t candidates for sharing with external people. And of course, even site owners might be blocked from sharing sites by a sensitivity label.

Time to Review Applicable Policies

Overall, I don’t think the change will disrupt many organizations. As Microsoft notes “You may want to review your Azure B2B Invitation Manager policies.” Two policies are worthy of note. The first is the Azure B2B Collaboration policy, which includes an allow or deny list (but not both) of domains.

The policy is now found under Collaboration restrictions in the External Identities section of the Azure AD admin center (Figure 1). It is commonly used to block sharing with consumer domains (deny list) or to restrict collaboration to a set of known domains belonging to partner organizations (allow list). If the organization already supports guest accounts, it’s likely that the collaboration policy already exists. Even so, changes like this are useful reminders of the need for regular review of any policy that affects how external people access tenant resources.

Azure AD B2B Collaboration policy settings
Figure 1: Azure AD B2B Collaboration policy settings

Azure AD cross-tenant access policies are a more powerful and flexible mechanism to control external access through both Azure B2B collaboration and Azure AD direct connect (used for Teams shared channels). Cross-tenant access policies are still relatively new and don’t need to be implemented unless required for a specific reason, so your tenant might not use them yet.

Although the Azure AD B2B Collaboration policy is likely to dominate for the immediate future, over time, I expect a slow transition to take advantage of the granular control available in cross-tenant access policies. When an organization changes over, SharePoint Online will take advantage. Leveraging advances made in Azure AD is an excellent reason for SharePoint Online to embrace Azure AD more fully.

Review Guest Accounts Too

Azure AD B2B collaboration works but that doesn’t mean that you don’t need to manage guest accounts. As more sharing happens, more guest accounts end up in your Azure AD. Some guest accounts are used once to share a document. Others are in ongoing use as guest members of groups and teams access shared documents. It’s a good idea to keep an eye on guest accounts and remove them as they become obsolete.


Support the work of the Office 365 for IT Pros team by subscribing to the Office 365 for IT Pros eBook. Your support pays for the time we need to track, analyze, and document the changing world of Microsoft 365 and Office 365.

]]>
https://office365itpros.com/2023/03/20/azure-ad-b2b-collaboration-spo/feed/ 1 59428
Document Entra ID Conditional Access Policies with the IdPowerToys App https://office365itpros.com/2023/03/16/idpowertoys-ca-documentation/?utm_source=rss&utm_medium=rss&utm_campaign=idpowertoys-ca-documentation https://office365itpros.com/2023/03/16/idpowertoys-ca-documentation/#comments Thu, 16 Mar 2023 01:00:00 +0000 https://office365itpros.com/?p=59457

Manual and Automatic Documentation of Conditional Access Policy Settings as PowerPoint Presentation

Windows has its Power Toys and now Microsoft’s identity management team is getting into the act with Identity Power Toys (idPowerToys), an app to help Azure Active Directory power users get work done. The initial release of the app is limited to a Conditional Access Documentator, a useful tool to read the configuration of conditional access policies fromEntra ID and generate documentation in the form of a PowerPoint presentation (using components from Syncfusion). The IdPowerToys GitHub repository is available for all to browse and contribute to.

Conditional access policies set conditions and criteria for Entra ID to examine inbound connections to decide if a connection should be accepted or rejected. A typical conditional access policy is one that requires accounts to use multi-factor authentication (MFA). The policy could even define that the authentication method used for the MFA response should be a certain strength. For instance, an SMS response is unacceptable but a response from the Microsoft Authenticator app is OK.

Only its creators love the GUI used to manage conditional access policies in the Microsoft Entra (Azure AD) admin center. It’s easy to make mistakes and people have been known to lock themselves out by implementing conditions that they can’t meet. It’s also easy to create conditions that make the daily interaction between people and apps miserable, such as cranking up the sign-in frequency for connections. Many different policies might exist in large enterprise tenants, and it can be hard to understand the flow that a connection traverses as Entra ID applies conditions from the set of policies. Examination of records in the sign-in log throws some light onto the situation but can be a drag.

The Conditional Access Documentator

Enter the Conditional Access Documentator, the first IdPowerToys app. The app is available online and supports two modes:

  • Automatic generation: IdPowerToys retrieves of conditional access policies using an enterprise app created in the tenant’s Entra ID and generates a PowerPoint presentation. You can opt to mask different elements of the output. For instance, if you choose to mask policy names, IdPowerToys generates its own version of the policy name based on what it does. If you choose to mask user names, IdPowerToys outputs their account identifier instead of their display name.
  • Manual generation: A tenant administrator runs a PowerShell command or uses the Graph Explorer to retrieve the JSON-formatted information about conditional access policies and pastes the results into a text box. IdPowerToys uses the information to create the PowerPoint file. Masking isn’t supported for manual generation.

An enterprise app is a registered Entra ID app owned by another tenant that creates an instance of the app in other tenants. Alongside the app instance, Entra ID creates a service principal to hold the permissions needed by the app. An administrator must grant consent before the app can use the permissions to access Entra ID to fetch the information about conditional access policies.

Some will be uneasy about granting an app permissions like Directory.Read.All (read information about accounts, groups, and other objects) and Policy.Read.All (read all policy information for the organization). However, as shown in Figure 1, the permissions are delegated, not application, which means that an account holding an administrator role must sign-into the app to use the permissions.

Permissions assigned to the IdPowerToys app.
Figure 1: Permissions assigned to the IdPowerToys app

If you’re uneasy about creating an enterprise app with permissions, use the manual generation method and run the Invoke-GraphRequest cmdlet to fetch the data and output it to the clipboard. This command only works when run by an administrator:

Invoke-GraphRequest -Uri 'https://graph.microsoft.com/beta/policies/conditionalAccessPolicies' -OutputType Json | Set-Clipboard

Figure 2 shows the results retrieved from the Graph pasted into the IdPowerToys app.

Pasting conditional access policy settings into IdPowerToys to generate documentation manually.
Figure 2: Pasting conditional access policy settings into IdPowerToys to generate documentation manually

In either case, the PowerPoint presentation generated to document conditional access policies is the same. For my tenant, which has 12 conditional access policies (not all in use), the app generated a 609 KB file with 13 slides (one title slide and one for each policy), divided into sets of enabled and disabled policies. Within a set, policies are sorted by last modified date, so the policy with the most recent modification appears first.

Figure 3 shows a presentation generated by IdPowerToys with details of a conditional access policy in the slide. This is a common policy to require MFA for guest access, with tweaks to require a certain authentication strength and to set the sign-in frequency to 90 days. You can see that the policy is enabled.

PowerPoint depiction of a conditional access policy.
Figure 3: PowerPoint depiction of a conditional access policy

Visualize Conditional Access Policies Differently

Conceptually, generating documentation for conditional access policies isn’t difficult. Graph API requests exist to fetch the information and after that it’s a matter of parsing the conditions, actions, access controls, and session controls to output in your desired format. Some might prefer their documentation in Word. I think PowerPoint is just fine. IdPowerToys delivers documentation that just might help organizations visualize, clarify, and rationalize their conditional access policies, and that’s a good thing.


Support the work of the Office 365 for IT Pros team by subscribing to the Office 365 for IT Pros eBook. Your support pays for the time we need to track, analyze, and document the changing world of Microsoft 365 and Office 365.

]]>
https://office365itpros.com/2023/03/16/idpowertoys-ca-documentation/feed/ 19 59457
Pragmatic and Practical Security is Better than Hard-line Security https://office365itpros.com/2023/03/14/entra-id-sign-in-frequency-guests/?utm_source=rss&utm_medium=rss&utm_campaign=entra-id-sign-in-frequency-guests https://office365itpros.com/2023/03/14/entra-id-sign-in-frequency-guests/#comments Tue, 14 Mar 2023 01:00:00 +0000 https://office365itpros.com/?p=59388

An Unreasonable Entra ID Sign-in Frequency Creates a Barrier to Productivity

I had an unpleasant surprise this week when the security team for one of the companies where I have a guest account decided to improve tenant security. I strongly support any effort to improve tenant security, especially when the effort means better use of multi-factor authentication. It’s a topic I’ll cover during the TEC Europe 2023 tour in London, Paris, and Frankfurt in April. Registration for those events is now open.

It’s always important to take a pragmatic and practical view of security and not to implement anything that has a significant impact on user productivity. All change can impact users, but most of the time people learn to live with change and it’s not disruptive. Unfortunately, deciding to increase the user sign-in frequency for Entra ID accounts can be extraordinarily disruptive if you go too far.

Sign-in frequency is the period before a user must sign in again when attempting to access a resource, like opening a SharePoint Online document, creating a message with OWA, or accessing a Teams channel. By default, Entra ID uses a rolling 90-day window for its sign-in frequency. In other words, once you successfully sign-into a tenant, Entra ID won’t ask you to sign-in again for another 90 days. If people use strong authentication methods with multifactor authentication, the rolling window works well.

Revoking User Account Access

Ninety days sounds like a long time, and it is. But this period needs to be viewed through the prism of how Entra ID and Microsoft 365 applications work. For example, in early 2022, Microsoft enabled Continuous Access Evaluation (CAE) for all tenants. CAE is a mechanism that allows Entra ID to notify applications of a critical change in the directory, such as an updated password. Applications that understand CAE, like SharePoint Online, revoke existing access for the account to require the user to reauthenticate.

The Microsoft 365 admin center also includes an option to sign users out of all current sessions (Figure 1) to force them to reauthenticate.

Forcing a user to sign out and reauthenticate
Figure 1: Forcing a user to sign out and reauthenticate

Of course, you might want to do more than sign a user out. In some cases, like employee departures, you might want to block future sign-ins. This is an operation that’s easily scripted with PowerShell. For example, this code:

  • Retrieves the identifier for an Entra ID user account.
  • Disables the account.
  • Sets a new password.
  • Revokes all refresh tokens.

$UserId = (Get-MgUser -UserId Lotte.Vettler@Office365itpros.com).Id
# Disable the account
Update-MgUser-UserId $UserId -AccountEnabled:$False
# Set a new password
$NewPassword = @{}
$NewPassword["Password"]= "!DoneAndDusted?"
$NewPassword["ForceChangePasswordNextSignIn"] = $True
Update-MgUser -UserId $UserId -PasswordProfile $NewPassword -AccountEnabled:$True
# Revoke refresh tokens
$RevokeStatus = Revoke-MgUserSignInSession -UserId $User.Id

It might take a little time for the full block to be effective because tokens must expire, and clients recognize the need for reauthentication, but it will happen.

How Conditional Access Can Make Guest Accounts Miserable

The reason I had a problem was that the security team updated the conditional access policies for guest users to enforce a 60-minute sign-in frequency (Figure 2). This change had a horrible effect. Guests switching to the tenant with Teams inevitably resulted in an MFA challenge. Opening a document stored in SharePoint Online or OneDrive for Business in that tenant brought an MFA challenge. My day was filled with MFA challenges, except when sending email to people in the tenant to complain about the new policy. Email isn’t affected by conditional access policies.

Setting the sign-in frequency in an Entra ID conditional access policy

Entra ID sign-in frequency for guest accounts set in a conditional access policy
Figure 2: Setting the sign-in frequency in an Entra ID conditional access policy

As Microsoft notes in their documentation, “Based on customer feedback, sign-in frequency will apply for MFA as well.” They understate the matter. Sign-in frequency does apply for MFA too.

I understand the motivation on the part of the security team. Forcing people to reauthenticate before they can access resources is a good thing. Using MFA is a good thing. Forcing MFA challenges every hour must be a brilliant change to make.

Only it isn’t. As an external person working with another company, the change made my productivity much worse, and I doubt that it added one iota to the overall security effectiveness of the tenant. The tenant did not use number matching and additional context for MFA challenges, so the constant MFA challenges were a great example of how user fatigue creeps in as I clicked and clicked again to say “yes, it’s me.” System-preferred authentication wasn’t used either, so while I used the Authenticator app, other guests might use relatively insecure SMS challenge/response.

Overall, the change made it unpleasant to work with the tenant and that’s bad. A one-hour sign-in frequency is just too rigid and strict. I don’t know of any other tenant (where I am a guest) that uses such a short frequency. Most tenants I know of use the 90-day default. Some use 7 days. The most security-conscious (before now) uses a 1-day frequency.

No Best Answer for All Tenants

In truth, I don’t know the best user sign-in frequency to use for either tenant or guest accounts. It all depends on the security posture that an organization wants to assume. But I can say that most tenants would be better off making sure that all accounts use MFA and eliminating the use of the less secure authentication methods before reducing the sign-in frequency. If you’re concerned about guest hygiene (in this case, how secure a guest account is), have a different and more restrictive conditional access policy for guest access while remembering the need to get work done through Azure B2B collaboration. And review guest accounts annually to remove unwanted and obsolete crud.

To me, bringing users along on the journey to better security is a better tactic than ramming heightened security down their throats. It’s always been that way.


So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across Office 365. Subscribe to the Office 365 for IT Pros eBook to receive monthly insights into what happens, why it happens, and what new features and capabilities mean for your tenant.

]]>
https://office365itpros.com/2023/03/14/entra-id-sign-in-frequency-guests/feed/ 2 59388
Azure AD Moves to Block OAuth App Hijacking https://office365itpros.com/2023/03/03/azure-ad-app-property-lock/?utm_source=rss&utm_medium=rss&utm_campaign=azure-ad-app-property-lock https://office365itpros.com/2023/03/03/azure-ad-app-property-lock/#comments Fri, 03 Mar 2023 01:00:00 +0000 https://office365itpros.com/?p=59307

Azure AD App Property Lock Feature Blocks Updates to App Credentials

In a relatively unpublicized move, the Azure AD development group has closed a hole exploited by attackers who add their own credentials to registered apps. The new app instance property lock feature (preview) allows developers to lock sensitive properties of apps. It’s intended for use by enterprise apps, which are the way that developers like Microsoft and Adobe install apps in other Azure AD organizations. The enterprise app stores app properties while the service principal created by Azure AD in the host organization holds the permissions assigned to the app in that organization. After provisioning the app into a new tenant, the developer can lock the app against change.

Why Attackers Go After OAuth Apps

In the past, attackers have been able to hijack an enterprise app by adding a credential like a X.509 certificate to the app. Unless the organization monitors the audit events created for application updates, the new credential will exist undetected and the attacker can use it to request Azure AD to issue an access token containing the permissions assigned to the app. Apart from its permissions, attackers don’t need any further access to the app. Instead, the attackers use the access token to access whatever data the permissions allow. In some cases, the attackers might access items in mailboxes; in others they might go after sensitive documents stored in SharePoint Online sites. Once they’ve compromised the target repository, the attackers can exfiltrate or wipe the data (potentially a Microsoft 365 “wiperware” attack).

Hijacking OAuth permissions assigned to apps is not a theoretical attack vector. It’s what was used in the Solarwinds campaign in 2021. The attackers generated an X.509 certificate and added it to Azure AD apps and used highly-permissioned apps to access data. Another example of OAuth app abuse is the September 2022 instance when attackers used an OAuth app to create an inbound connector to send spam.

Applying an Azure AD App Property Lock

The property lock feature allows developers to block any changes to some or all the sensitive properties for an app (the properties used in authentication flows). It’s important to emphasize that the property lock is not mandatory. Developers must apply it to their apps before the apps are used in other tenants.

You can lock properties for a registered app but cannot update enterprise apps created in your tenant by another organization (because an external organization owns the app). For instance, you cannot change the iOS accounts enterprise app used by Apple for some iOS device management, like the change needed to force the iOS mail app to use modern authentication.

To start, go to app registrations, select the app to lock and then access the authentication tab. The App instance property lock option is toward the bottom of the screen (Figure 1).

Accessing the app instance property lock feature for an app
Figure 1: Accessing the app instance property lock feature for an app

Click Configure and select the properties to lock (Figure 2).

electing the app properties to lock
Figure 2: Selecting the app properties to lock

Save the changes and the property lock is in force. Any subsequent attempt to update credentials will fail anywhere outside the home tenant.

Checking for App Credential Updates

Azure AD feeds audit information to the unified audit log, including events logged for app credential updates. Unfortunately, the information in the audit records follows an esoteric format that makes the data harder to interpret than it needs to be. Here’s a code snippet showing how to run the PowerShell Search-UnifiedAuditLog cmdlet to retrieve and report audit records for app credential changes.

$StartDate = (Get-Date).AddDays(-90)
$EndDate = Get-Date

[array]$Records = Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -Formatted -ResultSize 5000 -Operations "Update application – Certificates and secrets management "
$Report = [System.Collections.Generic.List[Object]]::new() 
ForEach ($Record in $Records) {
 $AuditData = $Record.AuditData | ConvertFrom-Json
  $Mods = $AuditData.modifiedproperties.NewValue
  $ReportLine  = [PSCustomObject] @{
     Timestamp        = $Record.CreationDate
     User             = $AuditData.UserId
     AppName          = $AuditData.Target[3].Id
     Modified         = $AuditData.modifiedproperties.NewValue }
 $Report.Add($ReportLine)
}

The same information is available in the Azure AD audit log (Figure 3).

App credential update details in the Azure AD audit log
Figure 3: App credential update details in the Azure AD audit log

Attacks Don’t Stop When a Hole Closes

Although regrettable that the holes existed in the first place, it’s good that Microsoft is closing off one of the vulnerabilities exploited by attackers with the Azure AD App property lock. It’s an example of the chess game played out between the attackers and defenders around the protection of cloud services. Now that this hole is closing, attackers will consider their next move. Stay vigilant and keep checking the audit log to detect suspicious events!


Learn about protecting your Microsoft 365 tenant by subscribing to the Office 365 for IT Pros eBook. Use our experience to understand features like the Azure AD app property lock and the most efficient ways to protect your data.

]]>
https://office365itpros.com/2023/03/03/azure-ad-app-property-lock/feed/ 2 59307
Comparing Entra ID Guest Accounts and Exchange Online Mail Contacts https://office365itpros.com/2023/03/02/mail-contacts-vs-guest-accounts/?utm_source=rss&utm_medium=rss&utm_campaign=mail-contacts-vs-guest-accounts https://office365itpros.com/2023/03/02/mail-contacts-vs-guest-accounts/#comments Thu, 02 Mar 2023 01:00:00 +0000 https://office365itpros.com/?p=59163

Are Guest Accounts Better Than Mail Contacts?

During an online discussion following publication of my article about how to purge guest accounts with unredeemed invitations from Entra ID, Microsoft’s Jef Kazimer said that he sees many Microsoft 365 organizations using guest accounts instead of mail contacts because guest accounts have better lifecycle management, even if the guests never sign in.

That idea got me thinking. Exchange Online is the largest Microsoft 365 workload and some organizations create many thousands of mail contacts for different reasons. For instance, they might have contacts for people in partner organizations so that users can easily find those contacts in the Global Address List (GAL). Mail contacts also exist in Exchange Server and many of the contacts now in Exchange Online originated. Hybrid organizations can synchronize on-premises contacts to Entra ID, but the management of those objects must be done on-premises.

Understanding Mail Contacts

Before comparing mail contacts with guest accounts, we need to understand what a mail contact is. Mail contact objects exist in both the Exchange directory (EXODS) and Entra ID. For example, to create a mail contact, you run the New-MailContact cmdlet:

New-MailContact -Name Jef.Kazimer -DisplayName "Jef Kazimer" -ExternalEmailAddress "Jef.Kazimer@contoso.com" -FirstName "Jef" -LastName "Kazimer"

This action creates a contact object in both Exchange Online and Entra ID. The Exchange object is what people think of when they think about a mail contact. The Entra ID object exists to hold properties unrelated to email processing. Because it uses mail contacts as addressable email recipients, all Exchange Online really cares about is the email address. Once an object has an email address, Exchange can route messages to it and allow the object to participate in distribution lists. The Get-MailContact cmdlet confirms the details of the new contact object:

Get-MailContact -Identity Jef.Kazimer | Format-Table DisplayName, ExternalEmailAddress

DisplayName ExternalEmailAddress
----------- --------------------
Jef Kazimer SMTP:Jef.Kazimer@contoso.com

The external directory object identifier stored in the mail contact points to the Entra ID object, which we can retrieve using the Get-MgContact cmdlet from the Microsoft Graph PowerShell SDK:

Get-MgContact -OrgContactId (Get-MailContact -Identity Jef.Kazimer).ExternalDirectoryObjectId | Format-Table displayName, proxyAddresses

DisplayName ProxyAddresses
----------- --------------
Jef Kazimer {SMTP:Jef.Kazimer@contoso.com}

The mail contact is a sparse object so far. To populate the other properties that you might want users to see in the GAL (Figure 1), you must run the Set-Contact cmdlet to update the Entra ID object:

Set-Contact -Identity Jef.Kazimer -StreetAddress "14, Preston Villas" -City "Bellevue" -StateorProvince "Washington" -PostalCode "98004" -Phone "+1 425-214-765" -MobilePhone "+1 425-214-705" -Pager $Null -HomePhone "+1 425-270-765" -Company "Contoso" -Title "Azure AD Guru" -Department "Information Technology" -Fax "+1 425-214-761" -Initials "JK" -Notes "Distinguished Person" -Office "Liberty Square" -CountryOrRegion "United States"
A fully-populated mail contact as seen by Outlook for Windows
Figure 1: A fully-populated mail contact as seen by Outlook for Windows

The Get-MgContact cmdlet reports the newly-populated properties as does the Get-ExoRecipient cmdlet. There are some exceptions and caveats:

  • Remember to include the PropertySet All parameter to force Get-ExoRecipient to retrieve the full set of properties.
  • Get-ExoRecipient doesn’t retrieve the street address because it’s not included in the GAL.
  • Get-MgContact uses compound properties to hold some information. For instance, to see the elements of a contact’s address, you must expand the properties stored in the Addresses property:
Get-MgContact -OrgContactId (Get-MailContact -Identity Jef.Kazimer).ExternalDirectoryObjectId | Select-Object -ExpandProperty Addresses


City     CountryOrRegion OfficeLocation PostalCode State      Street
----     --------------- -------------- ---------- -----      ------
Bellevue United States   Liberty Square 98004      Washington 14, Preston Villas

Managing Mail Contacts

A Set-MailContact cmdlet is available to update properties of the Exchange objects, including the set of custom attributes available for all mail-enabled objects. The Set-Contact cmdlet updates the information held in Entra ID contact objects such as the address data shown above.

When administrators manage mail contacts through the Microsoft 365 admin center or Exchange admin center, they can work with both Exchange Online and Entra ID object properties. The GUI hides the fact that the settings presented to the administrator come from two directories, much like it disguises the interaction between Entra ID and Exchange when managing mailbox-enabled user accounts.

Guest Accounts and Guest Mail Users

Now that we understand mail contacts, let’s discuss the relationship between Exchange Online and guest accounts. Following the creation of a guest account, a background process creates a special type of mail user object with a RecipientTypeDetails setting of GuestMailUser based on the properties of the guest account. The mail user object allows:

  • Guest members of Outlook groups to participate in group conversations via email.
  • Mail routing to guest accounts.
  • Guest accounts to appear in the GAL and other Exchange address lists.

Guest mail user objects exist in the Exchange directory until the removal of their linked guest accounts from Entra ID. Although you can view guest mail user objects through the Exchange admin center, the GUI won’t allow you to update their properties.Changes must be made to the guest account using the Entra admin center or with a Graph API (including the Microsoft Graph PowerShell SDK cmdlets). You can update the Exchange-specific properties with the Set-MailUser cmdlet.

To see the set of guest mail user objects, run the Get-ExoRecipient cmdlet:

Get-ExoRecipient -RecipientTypeDetails GuestMailUser | Format-Table DisplayName, PrimarySmtpAddress, HiddenFromAddressListsEnabled

The last property is True (the default) if the guest account isn’t visible to Exchange address lists. Run the Set-MailUser cmdlet to update HiddenFromAddressListsEnabled to True to expose the object. Here’s an example:

Set-MailUser -Identity warren.gatland@o365maestro.onmicrosoft.com -HiddenFromAddressListsEnabled $False

Note that it takes at least a day before newly exposed objects show up in the offline address look (OAB).

Adding Guest Mail Users to Distribution Lists

Because the guest mail users are routable objects, they can be added to distribution lists. This example spells things out, but it’s possible to add a guest mail user to a distribution list by passing its display name or email address without going to the bother of fetching the object with Get-MailUser.

$GuestMailUser = Get-MailUser Get-MailUser -Filter {DisplayName -eq "Warren Gatland" -and RecipientTypeDetails -eq "GuestMailUser"}
Add-DistributionGroupMember -Identity "o365maestro Contacts" -Member $GuestMailUser.Name

Move to Guest Accounts or Stay with Mail Contacts

Getting back to the original point, Jef says that guest accounts have better lifecycle management. In other words, if an organization invests in creating guest accounts instead of mail contacts, they’ll benefit from the work Microsoft does to improve how Entra ID manages external identities.

There’s some truth here. A guest account supports more properties, including custom security attributes and support dynamic Entra ID Groups and dynamic Entra ID administrative units. They’re a Microsoft 365 entity rather than being restricted to just Exchange Online. Entra ID development for external identities, including guest accounts, is active whereas I suspect the development effort for Exchange mail contacts entered an “only fix bugs” maintenance stage years ago. On the other hand, mail contacts are simple and effective and work across hybrid Exchange organizations.

If you’re a cloud-only organization, the choice exists to use either. If you decide to use guest accounts, the existence of guest mail user objects smoothen the transition and make sure that address lists, distribution lists, an email routing continue working. guest accounts are a better long-term bet, but that doesn’t mean that anyone should switch anytime soon.


Learn more about how the Microsoft 365 applications like Exchange Online and Entra ID really work on an ongoing basis by subscribing to the Office 365 for IT Pros eBook. Our monthly updates keep subscribers informed about what’s important across the Office 365 ecosystem.

]]>
https://office365itpros.com/2023/03/02/mail-contacts-vs-guest-accounts/feed/ 10 59163
Cleaning up Teams Premium Trial Licenses https://office365itpros.com/2023/02/09/remove-teams-premium-license/?utm_source=rss&utm_medium=rss&utm_campaign=remove-teams-premium-license https://office365itpros.com/2023/02/09/remove-teams-premium-license/#comments Thu, 09 Feb 2023 01:00:00 +0000 https://office365itpros.com/?p=59022

Remove Teams Premium Licenses from Azure AD User Accounts After 30-Day Trial Finishes

Microsoft makes a Teams Premium trial license to allow customers test whether the functionality available in Teams Premium is worth the $10/user/month cost. Some of the features, like meeting templates, might be less obviously worth the money. Others, like the advanced webinar functionality (like having a waitlist for webinar participants) might just be what you need. The trial allows you to try before you buy by testing all the features with up to 25 users for 30 days.

Once the 30-day period finishes, Microsoft automatically terminates the license validity and users lose access to the premium features. Even if you decide to go ahead with Teams Premium, it’s a good idea to clean up by removing the licenses from the user accounts that participated in the trial. This is easily done in the Microsoft 365 admin center by selecting the license, selecting all accounts holding the license and choosing Unassign licenses (Figure 1).

Removing the Teams Premium license from user accounts in the Microsoft 365 admin center

Remove Teams Premium licenses from Azure AD accounts
Figure 1: Removing the Teams Premium license from user accounts in the Microsoft 365 admin center

Remove Teams Premium Licenses with PowerShell

Given that we’re all learning how to manage licenses with the Microsoft Graph because of the imminent retirement of the Azure AD and MSOL modules, it’s good to know how to remove licenses. Let’s examine what’s needed to remove the Teams Premium trial licenses.

First, we must know the SKU identifier for the license. To do this, run the Get-MgSubscribedSku cmdlet and look through the set of licenses known to the tenant to find Teams Premium:

Get-MgSubscribedSku | Format-List SkuId, SkuPartNumber, ServicePlans

SkuId         : 36a0f3b3-adb5-49ea-bf66-762134cf063a

SkuPartNumber : Microsoft_Teams_Premium

ServicePlans  : {MCO_VIRTUAL_APPT, MICROSOFT_ECDN, TEAMSPRO_VIRTUALAPPT, TEAMSPRO_CUST...}

According to the Azure AD list of licenses and identifiers, the SKU identifier for Teams Premium is 989a1621-93bc-4be0-835c-fe30171d6463 rather than the 36a0f3b3-adb5-49ea-bf66-762134cf063a shown here. This is because the first value is for the paid license. The second is for the trial license. Both SKUs have the same part number and display name (which is why the license shown in Figure 1 is called Microsoft Teams Premium). It would be nice if Microsoft added a trial suffix for its trial licenses.

In any case, both SKUs include seven separate service plans. A service plan is a license for a piece of functionality that cannot be bought. Instead, it’s bundled into a product (SKU) like Teams Premium. Service plans allow administrators to selectively disable functionality enabled by a license. For instance, you could disable advanced virtual appointments without affecting the other elements in Teams Premium. Table 1 lists the service plans covered by Teams Premium.

Service plan identifierService plan nameDisplay name
85704d55-2e73-47ee-93b4-4b8ea14db92bMICROSOFT_ECDNMicrosoft Content Delivery Network
0504111f-feb8-4a3c-992a-70280f9a2869TEAMSPRO_MGMTMicrosoft Teams Premium Management
cc8c0802-a325-43df-8cba-995d0c6cb373TEAMSPRO_CUSTMicrosoft Teams Premium Branded Meetings
f8b44f54-18bb-46a3-9658-44ab58712968TEAMSPRO_PROTECTIONMicrosoft Teams Premium Advanced Meeting Protection
9104f592-f2a7-4f77-904c-ca5a5715883fTEAMSPRO_VIRTUALAPPTMicrosoft Teams Premium Virtual Appointment
711413d0-b36e-4cd4-93db-0a50a4ab7ea3MCO_VIRTUAL_APPTMicrosoft Teams Premium Virtual Appointments
78b58230-ec7e-4309-913c-93a45cc4735bTEAMSPRO_WEBINARMicrosoft Teams Premium Webinar
Table 1: Teams Premium service plans

PowerShell Code to Remove Teams Premium Licenses from Azure AD Accounts

Now that we know the SKU identifier, we can run some PowerShell to:

  • Find user accounts with the Teams Premium license. This is done using a lambda filter against the assignedLicenses property of each account.
  • Remove the license from those accounts.

Connect-MgGraph -Scope User.ReadWrite.All
Select-MgProfile Beta
# Populate identifier for target product (SKU)
$TeamsPremiumSku = "36a0f3b3-adb5-49ea-bf66-762134cf063a"
[array]$Users = Get-MgUser -filter "assignedLicenses/any(s:s/skuId eq $TeamsPremiumSku)" -All
If (!($Users)) { Write-Host "No Teams Premium Trial licenses found - exiting" ; break }
Write-Host ("Removing {0} Teams trial licenses from {1}..." -f $Users.count, ($Users.displayName -join ", "))

ForEach($User in $Users) {
  Try {
    $Status = Set-MgUserLicense -UserId $User.Id -RemoveLicenses $TeamsPremiumSku -AddLicenses @{}  }
  Catch {
    Write-Host "Error removing Teams Premium Trial license from {0}" -f $User.displayName }
}

Updated with an appropriate SKU identifier, the code will remove licenses for other Microsoft 365 products.

Remove Teams Premium Licenses to Avoid Confusion

It doesn’t matter if you leave expired licenses in place. They won’t affect how people use Microsoft 365. However, given that the paid-for and trial versions of the Teams Premium licenses have the same display name, it’s best to remove trial licenses to avoid potential future confusion.


Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2023/02/09/remove-teams-premium-license/feed/ 1 59022
How to Purge Guest Accounts with Unredeemed Invitations from Entra ID https://office365itpros.com/2023/02/07/entra-id-guest-accounts-unredeemed/?utm_source=rss&utm_medium=rss&utm_campaign=entra-id-guest-accounts-unredeemed https://office365itpros.com/2023/02/07/entra-id-guest-accounts-unredeemed/#comments Tue, 07 Feb 2023 01:00:00 +0000 https://office365itpros.com/?p=58988

Use PowerShell to Find and Remove Entra ID Guest Accounts Who Don’t Want to Join Your Party

Updated 5 September 2023

A January 30 post by Microsoft’s Jef Kazimer about using Azure Automation with Managed Identities to remove unredeemed guests from Entra ID (Azure AD) promised to be a good read. Jef is a Principal Program Manager in the Microsoft Entra organization. Apart from using Azure Automation (something that every tenant administrator should master), highlighting the Microsoft Graph PowerShell SDK V2.0 (currently in early preview) gave me another reason to read the article.

I have expressed some concerns about Microsoft’s plans for the V2.0 of the Microsoft Graph PowerShell SDK. Leaving those concerns aside, it’s always good to learn how others approach a problem, especially as I’ve recently covered similar ground in terms of how to decide to remove guest accounts using the SDK. The differences between the two methods of reviewing guest accounts is that Jef looks for instances where guest accounts never went through the invitation redemption process to fully validate their accounts. On the other hand, my script looks at how long it’s been since a guest signed into the tenant and the number of groups the account is a member of to determine “staleness.” Let’s consider how to review guest accounts based on unredeemed invitations.

Outlining the Process

On paper, the steps involved to find and remove guest accounts with unredeemed invitations are straightforward:

  • Find guest accounts that have not redeemed the invitations received to join the tenant.
  • Remove the accounts from Entra ID.

Jef’s article suggests that this should be a regular process executed by an Azure Automation job using a managed identity to sign into the Graph and run the necessary PowerShell commands. I agree and think this is a good way to make sure to clear out unwanted guest accounts periodically.

Where I disagree is the detail of how to find the guests. Let’s discuss.

The Need for Administrative Units

Jef uses a dynamic administrative unit (currently a preview feature) to manage guest accounts. While it’s certainly convenient to create a dynamic administrative unit and assign the user management role for the administrative unit to the managed identity, this approach is optional and creates a potential requirement for Entra ID Premium P1 licenses. If your organization has those licenses, using a dynamic administrative unit offers the advantage of reducing the scope for the managed identity to process Entra ID accounts.

In some organizations, using administrative units (both the standard and dynamic variants) could be overkill because user management is a task performed by one or two administrators. In larger organizations, granularity in user management can be a desirable aspect, which is why administrative units exist.

Finding Entra ID Guest Accounts with Unredeemed Invitations

The first step is to find the target set of guest accounts. The simplest way is to run the Get-MgUser cmdlet and filter accounts to look for guests:

Connect-MgGraph -Scope Directory.ReadWrite.All
Select-MgProfile Beta
[array]$Guests = Get-MgUser -Filter "userType eq 'Guest'" -All

The guest accounts we want are those that have the ExternalUserState property set to “PendingAcceptance.” In other words, Entra ID issued an invitation to the guest’s email address, but the guest never followed up to redeem their invitation. This amended call to Get-MgUser fetches the set of guest accounts with unredeemed invitations:

[array]$Guests = Get-MgUser -Filter "userType eq 'Guest' and ExternalUserState eq 'PendingAcceptance'" -All

Jef’s version uses the Get-MsIDUnredeemedInviteUser cmdlet from the MSIdentityTools module to find guest accounts with unredeemed invitations. It’s certainly worth considering using the MSIdentityTools module to manage Entra ID, but it’s also worth understanding how to do a job with the basic tools, which is what I do here.

Determining the Age of an Unredeemed Invitation

It would be unwise to remove any Entra ID guest accounts without giving their owners a little time to respond. Taking vacation periods into account, 45 days seem sufficient time for anyone to make their minds up. The loop to remove unredeemed guest accounts needs to check how long it’s been since Entra ID issued the invitation and only process the accounts that exceed the age threshold.

Our script can check when Entra ID created an invitation by checking the ExternalUserStateChangeDateTime property, which holds a timestamp for the last time the state of the account changed. The only state change for the accounts we’re interested in occurred when Entra ID created the invitations to join the tenant, so we can use the property to measure how long it’s been since a guest received their invitation.

This code shows how to loop through the set of guests with unredeemed invitations, check if their invitation is more than 45 days old, and remove the account that satisfy the test. To keep a record of what it does, the script logs the deletions.

[datetime]$Deadline = (Get-Date).AddDays(-45)
$Report = [System.Collections.Generic.List[Object]]::new()
ForEach ($Guest in $Guests) {
  # Check Date
  [datetime]$InvitationSent = $Guest.ExternalUserStateChangeDateTime
  If ($InvitationSent -le $Deadline) {
     $DateInvitation = Get-Date($InvitationSent) -format g
     $DaysOld = (New-TimeSpan ($InvitationSent)).Days
     Try { 
        Remove-MgUser -UserId $Guest.Id
        $ReportLine = [PSCustomObject][Ordered]@{  
          Date        = Get-Date
          User        = $Guest.displayName
          UPN         = $Guest.UserPrincipalName
          Invited     = $DateInvitation
          "Days old"  = $DaysOld }
        $Report.Add($ReportLine)
      }
      Catch {
        Write-Error $_
      }
   } #End if
} #End Foreach Guest
Write-Host "Guest Accounts removed for" ($Report.User -Join ", ")

Figure 1 shows some data from the report generated for the deletions. In an Azure Automation scenario, you could create a report in SharePoint Online, send email to administrators, or post a message to a Teams channel to advise people about the removed accounts.

Old Entra ID guest accounts with unredeemed invitations
Figure 1: Old guest accounts with unredeemed invitations

Caveats Before Removing Entra ID Guest Accounts

The code works and stale guest account disappear to the Entra ID recycle bin. However, the danger exists that some of the accounts might be in active use. Take guest accounts created to represent the email addresses of Teams channels. These email addresses represent a connector to import messages into Teams channels. No one can sign into these non-existent mailboxes so no one  will ever redeem the guest invitations. However, the mail user objects created by Exchange Online for these guest accounts allow them to be included in distribution lists, added to address lists, and so on.

Another example is when a guest joins an Outlook group (a Microsoft 365 group whose membership communicates via email). Guest members of these groups do not need to redeem their invitation unless they intend to sign into the tenant to access Teams or SharePoint Online or another application that supports Azure B2B Collaboration. If you remove these guest accounts based on their invitation redemption status, some important email-based communication might fail, and that would be a bad thing.

One way around the issue is to mark Entra ID guest accounts used for these purposes by writing a value into an appropriate property. For instance, set the department to EMAIL. Here’s how to mark the set of guest accounts used to route email to Teams channels:

[array]$MailGuests = $Guests | Where-Object {$_.Mail -Like "*teams.ms*"}  
ForEach ($MG in $MailGuests) { Update-MgUser -UserId $MG.Id -Department "EMAIL" }

And here’s how to mark the guest members for an Outlook group using cmdlets from the Exchange Online management module:

[array]$Members = Get-UnifiedGroupLinks -Identity 'Exchange Grumpy Alumni' -LinkType Member
ForEach ($Member in $Members) { 
  If ($Member.RecipientType -eq "MailUser")  { Set-User -Identity $Member.Name -Department "EMAIL" -Confirm:$False }
}

After marking some guest accounts as exceptions, we can find the set of guest accounts to process with:

[array]$Guests = Get-MgUser -Filter "userType eq 'Guest'" -All | Where-Object {$_.ExternalUserState -eq "PendingAcceptance" -and $_.Department -ne "EMAIL"}

All of this goes to prove that setting out to automate what appears to be a straightforward administrative task might lead to unforeseen consequences if you don’t think through the different ways applications use the objects.

Using SDK V2.0

Coming back to using V2.0 of the Microsoft Graph PowerShell SDK, nothing done so far needs V2.0. The only mention of a V2.0-specific feature is the support for a managed identity when connecting to the Graph. The code used to connect is:

Connect-MgGraph -Identity

A one-liner is certainly convenient, but it’s possible to connect to a managed identity with the Graph SDK with code that is just a little more complicated. Here’s what I do:

Connect-AzAccount -Identity
$AccessToken = Get-AzAccessToken -ResourceUrl "https://graph.microsoft.com"
Connect-MgGraph -AccessToken $AccessToken.Token

Going from three lines to one is probably not a huge benefit!


So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across Office 365. Subscribe to the Office 365 for IT Pros eBook to receive monthly insights into what happens, why it happens, and what new features and capabilities mean for your tenant.

]]>
https://office365itpros.com/2023/02/07/entra-id-guest-accounts-unredeemed/feed/ 3 58988
Azure AD Introduces IPv6 Support https://office365itpros.com/2023/01/27/azure-ad-ipv6-support/?utm_source=rss&utm_medium=rss&utm_campaign=azure-ad-ipv6-support https://office365itpros.com/2023/01/27/azure-ad-ipv6-support/#respond Fri, 27 Jan 2023 01:00:00 +0000 https://office365itpros.com/?p=58856

Azure AD IPv6 Connections Supported from March 31, 2023

The Azure AD development group certainly believes in keeping tenant administrators on their toes. Not content with releasing a steady stream of new functionality, Azure AD is refreshing its infrastructure by introducing support for IPv6 starting March 31, 2023. Given the size of the Microsoft 365 infrastructure, it’s impossible to put an exact date when support reaches a specific tenant.

As Microsoft says, this will allow customers to “reach the Azure AD services over IPv4, IPv6 or dual stack endpoints.” In other words, requests to Azure AD for authentication and other services from clients can travel over an IPv6 connection in addition to the current IPv4 connections. Microsoft stresses that they are not deemphasizing support for IPv4 in any way.

The change should be transparent from a user perspective. Most users don’t pay much attention to networking configuration and accept the default settings necessary to connect via Wi-Fi, cell network, or LAN. If problems happen, they’re more likely to surface for network and tenant administrators.

Conditional Access, Named Locations, and Azure AD IPv6

In its support page for the introduction of IPv6 to Azure AD, Microsoft specifically calls out the need to update any conditional access policies that apply restrictions using named locations. A named location allows a condition access policy to identify incoming traffic from a specific place based on a country (using GPS location or IP address) or a specific IPv4/IPv6 address range. The policy can then allow or block the connection. The Microsoft Entra admin center currently only supports IPv4 addresses for country identification.

After Azure AD supports IPv6, it creates the possibility that clients will present IPv6 addresses when they attempt to connect. If the conditional access policy doesn’t recognize the address as a permitted connection source, the client cannot connect.

Microsoft says that organizations should check conditional access policies to find those that use named locations and then update the named locations used by those policies to include the range of IPv6 addresses that clients will use. Figure 1 shows how to assign an IPv6 range to a named location in the Microsoft Entra admin center (or Azure AD admin center).

Adding an IPv6 address range for an Azure AD named location

Azure AD IPv6
Figure 1: Adding an IPv6 address range for an Azure AD named location

Obviously, it might take some effort to determine the full set of IPv6 addresses that clients might use, so it’s best to start this work as soon as possible.

Other Places Where IP Addresses Lurk

A change in originating IP address has consequences for other parts of an infrastructure. For instance, connection data captured by Azure AD will now contain IPv6 addresses. For instance, the unified audit log ingests information about user sign-ins from Azure AD. The audit records contain the IP address used by the client. Here’s what an audit record found by the Search-UnifiedAuditLog cmdlet holds:

RecordType   : AzureActiveDirectoryStsLogon
CreationDate : 24/01/2023 18:33:13
UserIds      : Jane.Smithoffice365itpros.com
Operations   : UserLoggedIn
AuditData    : {
                 "CreationTime": "2023-01-24T18:33:13",
                 "Id": "78bb9d6f-afc2-4ea7-ab7f-16fdb7423e00",
                 "Operation": "UserLoggedIn",
                 "OrganizationId": "a662313f-14fc-43a2-9a7a-d2e27f4f3478",
                 "RecordType": "AzureActiveDirectoryStsLogon",
                 "ResultStatus": "Success",
                 "UserKey": "eff4cd58-1bb8-4899-94de-795f656b4a18",
                 "UserType": "Regular",
                 "Version": 1,
                 "Workload": "AzureActiveDirectory",
                 "ClientIP": "78.17.97.20",
                 "ObjectId": "797f4846-ba00-4fd7-ba43-dac1f8f63013",

Audit records generated by Azure AD can go elsewhere. For instance, a connector is available to import the data into Microsoft Sentinel. The flow of data from Azure AD to other applications highlights the need to check that reports and analysis of this data is capable of processing IPv6 addresses.

All Change in Azure AD

March 2023 is going to be a big month for tenant administrators. Already, work had to be done to upgrade PowerShell scripts to remove old Azure AD and MSOL cmdlets that perform license management operations. These cmdlets will stop working when Microsoft 365 introduces a new license management platform on March 31. Now work must be done to check what IPv6 addresses might show up once Microsoft enables IPv6 support in the tenant. It’s all go inside Microsoft 365…


So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across Office 365. Subscribe to the Office 365 for IT Pros eBook to receive monthly insights into what happens, why it happens, and what new features and capabilities mean for your tenant.

]]>
https://office365itpros.com/2023/01/27/azure-ad-ipv6-support/feed/ 0 58856
Reporting Group Membership for Entra ID Guest Accounts with the Microsoft Graph PowerShell SDK https://office365itpros.com/2023/01/18/old-entra-id-guest-accounts/?utm_source=rss&utm_medium=rss&utm_campaign=old-entra-id-guest-accounts https://office365itpros.com/2023/01/18/old-entra-id-guest-accounts/#comments Wed, 18 Jan 2023 01:00:00 +0000 https://office365itpros.com/?p=58742

Finding Guest Accounts in Microsoft 365 Groups

Updated: 18-Sep-2024

The article explaining how to report old Entra ID guest accounts and their membership of Microsoft 365 Groups (and teams) in a tenant is very popular and many people use its accompanying script. Taking the idea a little further, this article explores how to find guest accounts above a certain age (365 days – configurable in the script) and report the groups these guests are members of. Any old guest accounts that aren’t in any groups are candidates for removal.

The script uses an old technique featuring the distinguished name of guest accounts to scan for group memberships using the Get-Recipient cmdlet. The approach works, but the variation of values that can exist in distinguished names due to the inclusion of characters like apostrophes and vertical lines means that some special processing is needed to make sure that lookups work. Achieving consistency in distinguished names might be one of the reasons for Microsoft’s plan to make Exchange Online mailbox identification more effective.

In any case, time moves on and code degrades. I wanted to investigate how to use the Microsoft Graph PowerShell SDK to replace Get-Recipient. The script already uses the SDK to find Entra ID guest accounts with the Get-MgUser cmdlet.

The Graph Foundation

Graph APIs provide the foundation for all SDK cmdlets. The first thing to find is an appropriate API to find group membership. I started off with getMemberGroups. The PowerShell example for the API suggests that the Get-MgDirectoryObjectMemberGroup cmdlet is the one to use. For example:

$UserId = (Get-MgUser -UserId Terry.Hegarty@Office365itpros.com).id 
[array]$Groups = Get-MgDirectoryObjectMemberGroup -DirectoryObjectId $UserId -SecurityEnabledOnly:$False

The cmdlet works and returns a list of group identifiers that can be used to retrieve information about the groups that the user belongs to. For example:

Get-MgGroup -GroupId $Groups[0] | Format-Table DisplayName, Id, GroupTypes

DisplayName                     Id                                   GroupTypes
-----------                     --                                   ----------
All Tenant Member User Accounts 05ecf033-b39a-422c-8d30-0605965e29da {DynamicMembership, Unified}

However, because Get-MgDirectoryObjectMemberGroup returns a simple list of group identifiers, the developer must do extra work to call Get-MgGroup for each group to retrieve group properties. Not only is this extra work, calling Get-MgGroup repeatedly becomes very inefficient as the number of guests and their membership in groups increase.

Looking Behind the Scenes with Graph X-Ray

The Entra admin center both list the groups that user accounts (tenant and guests) belong to. Performance is snappy and it seemed unlikely that the code used was making multiple calls to retrieve the properties for each group. Many of the sections in these admin centers use Graph API requests to fetch information, and the Graph X-Ray tool reveals those requests. Looking at the output, it’s interesting to see that the admin center uses the beta Graph endpoint with the groups memberOf API (Figure 1).

Using the Graph X-Ray tool to find the Graph API for group membership

Azure AD Guest Accounts
Figure 1: Using the Graph X-Ray tool to find the Graph API for group membership

We can reuse the call used by the Entra admin center to create the query (containing the object identifier for the user account) and run the query using the SDK Invoke-MgGraphRequest cmdlet. One change made to the command is to include a filter to select only Microsoft 365 groups. If you omit the filter, the Graph returns all the groups a user belongs to, including security groups and distribution lists. The group information is in an array that’s in the Value property returned by the Graph request. For convenience, we put the data into a separate array.

$Uri = ("https://graph.microsoft.com/V1.0/users/{0}/memberOf/microsoft.graph.group?`$filter=groupTypes/any(a:a eq 'unified')&`$top=200&$`orderby=displayName&`$count=true" -f $Guest.Id)
[array]$Data = Invoke-MgGraphRequest -Uri $Uri
[array]$GuestGroups = $Data.Value

Using a Graph request works and you can continue using it if you like. The latest version of the script uses the Get-MgUserMemberOf SDK cmdlet instead. The cmdlet returns groups and administrative units, so a filter is used to extract the groups from the set retrieved from the Graph. Here’s the current code:

   [array]$GuestGroups = Get-MgUserMemberOf -UserId $Guest.id

   If ($GuestGroups) { 
      # Exclude administrative units
      $RealGroups = $GuestGroups | Where-Object {$_.additionalproperties.'@odata.type' -eq '#microsoft.graph.group'}
      $GroupNames = $RealGroups.additionalProperties.displayName -join ", " 
   

A Get-MgUserTransitiveMemberOf cmdlet is also available to return the membership of nested groups. We don’t need to do this because we’re only interested in Microsoft 365 groups, which don’t support nesting. The cmdlet works in much the same way:

[array]$TransitiveData = Get-MgUserTransitiveMemberOf -UserId Kim.Akers@office365itpros.com -All

The Script Based on the SDK

When revising the script, I made some other improvements, including adding a basic assessment of whether a guest account is stale or very stale. The assessment is intended to highlight if I should consider removing these accounts because they’re obviously not being used. Figure 2 shows the output of the report.

Report highlighting potentially obsolete Entra ID guest accounts
Figure 2: Report highlighting potentially obsolete Entra ID uest accounts

You can download a copy of the script from GitHub.

Cleaning up Obsolete Entra ID Guest Accounts

Reporting obsolete guest accounts is nice. Cleaning up old junk from Entra ID is even better. The script generates a PowerShell list with details of all guests over a certain age and the groups they belong to. To generate a list of the very stale guest accounts, filter the list:

[array]$DeleteAccounts = $Report | Where-Object {$_.StaleNess -eq "Very Stale"}

To complete the job and remove the obsolete guest accounts, a simple loop to call Remove-MgUser to process each account:

ForEach ($Account in $DeleteAccounts) {
   Write-Host ("Removing guest account for {0} with UPN {1}" -f $Account.Name, $Account.UPN) 
   Remove-MgUser -UserId $Account.Id }

Obsolete or stale guest accounts are not harmful, but their presence slows down processing like PowerShell scripts. For that reason, it’s a good idea to clean out unwanted guests periodically.


Learn about mastering the Microsoft Graph PowerShell SDK and the Microsoft 365 PowerShell modules by subscribing to the Office 365 for IT Pros eBook. Use our experience to understand what’s important and how best to protect your tenant.

]]>
https://office365itpros.com/2023/01/18/old-entra-id-guest-accounts/feed/ 4 58742
Achieving Consistency in Country Settings Across Azure AD and Exchange Online https://office365itpros.com/2023/01/09/azure-ad-user-country-settings/?utm_source=rss&utm_medium=rss&utm_campaign=azure-ad-user-country-settings https://office365itpros.com/2023/01/09/azure-ad-user-country-settings/#comments Mon, 09 Jan 2023 01:00:00 +0000 https://office365itpros.com/?p=58612

Managing Azure AD User Country and Regional Settings

A question arose about why Exchange Online doesn’t synchronize country settings from Azure AD user accounts, leading to a situation where an Azure AD user account and its mailbox might have inconsistent values. Here’s an example where the Get-MgUser and Get-Recipient cmdlets report different country values for a user account:

Get-MgUser -UserId Sean.Landy@Office365itpros.com | Format-Table country, usagelocation

Country UsageLocation
------- -------------
Austria FR

Get-Recipient -Identity Sean.Landy@Office365itpros.com | Select-Object country*

CountryOrRegion
---------------
France

The technical reason for the apparent inconsistency is simple: Get-MgUser reads data for a user account from Azure AD while Get-Recipient reads information about a mailbox from EXODS, the Exchange Online directory. We’re dealing with two different objects stored in two different directories.

EXODS exists to manage mail-specific properties for mail-enabled objects, like mailboxes. EXODS also manages Exchange objects that aren’t in Azure AD such as public folders and dynamic distribution lists.

Dual Write Between Azure AD and EXODS

To ensure consistency across the two directories, Azure AD and EXODS use a dual-write process. In other words, when an application attempts to update an object, the write operation must succeed in both directories before Azure AD and EXODS commit the change.

However, this doesn’t happen for every property for every object in the two directories. Although the mailbox CountryOrRegion property receives the same value as the user account’s Country property when Exchange Online creates a new mailbox, synchronization doesn’t follow for further updates. Azure AD and EXODS synchronize updates to other elements of address information like the street address, city, and province made in either directory, but ignore changes to the Country property in Azure AD or the CountryOrRegion property in EXODS. Perhaps the reason is that the two properties have different names and purposes: One is specific to a country while the other can store a country or region name. In fact, EXODS doesn’t store a Country property for mailboxes.

All of which means that it is possible to update an Azure AD account with a new value for the country property without any effect on EXODS. For example, this command updates Azure AD without doing anything to EXODS:

Update-MgUser -UserId Sean.Landy@Office365itpros.com -Country "Ireland"

Likewise, the same is true of an update to EXODS with the Set-User cmdlet. Azure AD ignores this update:

Set-User -Identity Sean.Landy@Office365itpros.com -CountryOrRegion "United States"

In practical terms, the inconsistency might be irritating but it isn’t important. Azure AD is the directory of record for Microsoft 365 and applications should go to it for information about user accounts. The information stored in EXODS about mailbox owners is for informational purposes only. If you want everything to match, then you must create a mechanism (a PowerShell script most likely) to synchronize the properties you want to be consistent.

Azure AD Account Usage Location

Another potential inconsistency is the usage location assigned to an Azure AD account. In the example above, the usage location is FR (France) but the Country property says Austria. The usage location is where Microsoft delivers the service to the account and it’s important that it’s correct because Microsoft cannot deliver some elements of Microsoft 365 (mostly to do with encryption) in certain countries.

Life being what it is, the usage location set when creating an account can change. For instance, a user might relocate to work in an office in another country for a period. There’s no requirement to update the usage location for the account because this should reflect the user’s normal location. In addition, an account’s usage location isn’t associated with the tenant home location. The location (or datacenter region) for a tenant establishes where Microsoft delivers services to the tenant from and where tenant data resides. This can be a country-level datacenter (like France, Switzerland, or South Africa), or a regional datacenter (like the U.S. or Western Europe). Tenant accounts located in countries outside a datacenter location can access services delivered to the tenant. Multi-geo tenants are available should local data residency be necessary.

Mailbox Regional Settings

When you create a new Microsoft 365 account and license the account for Exchange Online, the mailbox does not inherit regional properties from the country or service location defined for the Azure AD account. This is deliberate because regional properties are personal to the user and define the language used to interact with the mailbox, its time zone, and the preferred date format. Different groups of people in the same country often use different regional settings. Examples include Welsh speakers in the United Kingdom and Flemish speakers in Belgium.

OWA applies default regional properties based on the tenant location the first time the mailbox owner signs in and creates a set of default folders. For example, mailboxes that use the English language have an Inbox folder, while mailboxes configured for French use Boîte de réception. Users can update regional settings for OWA through Outlook settings. (Figure 1). If they change the selected language, they have the option to rename the default folders.

Selecting regional settings for OWA

Azure AD user country settings
Figure 1: Selecting regional settings for OWA

Administrators can run the Set-MailboxRegionalConfiguration cmdlet to change the regional settings for a mailbox. In this example, the mailbox language, time zone, and date and time formats match the settings for a Dutch user working in the Netherlands. Notice the use of the LocalizeDefaultFolderName parameter, set to $True to force Exchange Online to create default folder names in Dutch for the mailbox:

Set-MailboxRegionalConfiguration –Identity 'Rob Young' –Language nl-NL 
–TimeZone 'W. Europe Standard Time' –DateFormat ‘d-M-yyyy’–TimeFormat 'HH:mm' 
–LocalizeDefaultFolderName:$True

Apart from the language, the time zone is the most important setting because it’s used by Microsoft 365 applications. For example, Teams displays the local time zone for other users when showing their details in profile cards. If your organization scripts the creation of new accounts, it’s a good idea to make sure that the code includes the configuration of an appropriate time zone setting for the mailbox.

Reporting Azure AD User Country and Regional Settings

It’s easy to audit the language settings of Azure AD accounts and mailboxes. Here’s some code to show how:

$Report = [System.Collections.Generic.List[Object]]::new()
[array]$Users = Get-MgUser -Filter "assignedLicenses/`$count ne 0 and userType eq 'Member'" -ConsistencyLevel eventual -CountVariable Records -All
ForEach ($User in $Users) {
  Write-Host ("Processing account {0}" -f $User.DisplayName)
  $RegionalSettings = $Null
  $RegionalSettings = Get-MailboxRegionalConfiguration -Identity $User.UserPrincipalName -ErrorAction SilentlyContinue
  $CountryOrRegion = (Get-User -Identity $User.UserPrincipalName -ErrorAction SilentlyContinue) | Select-Object -ExpandProperty CountryOrRegion
  If ($RegionalSettings) {
  $ReportLine = [PSCustomObject]@{ 
    User                   = $User.UserPrincipalName
    DisplayName            = $User.DisplayName
    Country                = $User.Country
    "Preferred Language"   = $User.PreferredLanguage
    "Usage Location"       = $User.UsageLocation
    "Country or region"    = $CountryOrRegion
    Language               = $RegionalSettings.Language.DisplayName
    DateFormat             = $RegionalSettings.DateFormat
    TimeFormat             = $RegionalSettings.TimeFormat
    TimeZone               = $RegionalSettings.TimeZone }
 $Report.Add($ReportLine) }
}

Figure 2 shows the output. This data is from a test tenant, but it illustrates how easy it is for inconsistencies to occur across the range of country settings available for accounts and mailboxes.

Azure AD user account and mailbox country and regional settings
Figure 2: Azure AD user account and mailbox country and regional settings

The most important element to get correct is the time zone because it affects the user experience. It would be easy to make sure that Country (Azure AD) and CountryOrRegion (EXODS) contain the same value, but aside from configuring values during account creation, you should leave regional settings alone as they’re a matter of personal choice.


Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2023/01/09/azure-ad-user-country-settings/feed/ 6 58612
Adding QR Codes to Microsoft Authenticator for Entra ID Guest Accounts https://office365itpros.com/2023/01/04/microsoft-authenticator-app-qr/?utm_source=rss&utm_medium=rss&utm_campaign=microsoft-authenticator-app-qr https://office365itpros.com/2023/01/04/microsoft-authenticator-app-qr/#comments Wed, 04 Jan 2023 01:00:00 +0000 https://office365itpros.com/?p=58395

Moving to a New Mobile Phone Means New Codes for the Microsoft Authenticator App

Moving to a new mobile device always involves a certain amount of hassle. The advent of mobile authenticator apps makes the move a little harder, especially when guest accounts on other tenants are involved.

In my case, I moved from an oldish iPhone 11 to a new iPhone 14. I was very happy with the 11 and used it since 2019. However, its battery showed signs of age and I fancied a change, which is all the reason I needed to get the 14.

Moving apps from an old iPhone to a new device is very easy. Minor hassles like making Outlook the default mail app for iOS and adding Teams to the pinned app list are easily overcome. It’s all the messing around with app passwords and authentication that causes the hassle.

Which brings me to the Microsoft Authenticator app. I am a strong proponent of multi-factor authentication and use the authenticator app to protect my Microsoft 365 and other accounts, including services like GitHub and Twitter. The app has a backup and recovery capability that I used to restore details of the accounts I use with authenticator. Unhappily (as noted in the support article), “Only your personal and non-Microsoft account credentials are stored, which includes your username and the account verification code that’s required to prove your identity.”

MFA Responses by Microsoft Authenticator App Need Device-Specific Credentials

For Microsoft school or work (Entra ID) accounts, the article explains that accounts that use push notifications (like MFA challenges) need additional verification to recover information. Push notifications require using a credential tied to a specific device. To restore accounts protected by MFA using the authenticator app on the new phone, this means that “you must scan a QR code given to you by your account provider.

The key to getting a new QR code for your Entra ID account is the Security info section of the My account page. After signing into your account, this section displays the sign-in methods used to access your account (Figure 1). This is the same kind of information that’s available when examining authentication methods for user accounts with the Microsoft Graph PowerShell SDK.

Listing sign-in methods for an Entra ID account
Figure 1: Listing sign-in methods for an Entra ID account

Note: If a user can’t access the My account page because they don’t have access to their old phone and therefore cannot respond to an MFA challenge, an administrator can temporarily downgrade the MFA requirement to SMS to allow the user to sign in and access the page.

Adding a QR Code for a New Device

Remember that the credential used by the Microsoft Authenticator app to respond to MFA challenges is device-specific. To generate a new QR code, click Add sign-in method and select Authenticator app from the list of options. You’ll then be told that you need to install the app, which is fine because it’s already on the device. Click Next to start the setup process and click Next again to see a new QR code for the app (Figure 2).

enerating a new QR code for the Microsoft Authenticator app
Figure 2: Generating a new QR code for the Microsoft Authenticator app

You can scan the code using Authenticator and once this happens, the connection between account, app, and credential works. The process includes a verification step to prove that the Authenticator app can use the credential.

After setting up Authenticator for a new device, you’ll have multiple Microsoft Authenticator entries in your sign-in methods list (one per device). It’s perfectly safe to remove the entries for devices that you no longer use.

Adding a QR Code for a Guest Account

Everything works very nicely for a full tenant account. Generating a QR code to allow Authenticator to satisfy MFA challenges for a guest account is a little more complicated. I have guest accounts in multiple Microsoft 365 organizations, mostly because I am a guest member of Teams in those organizations. Let’s assume that you see that a guest account shows up in Authenticator flagged with “Action required” (Figure 3). This means that Authenticator can’t satisfy challenges for this account because it doesn’t have the necessary credentials.

The Microsoft Authenticator app flags that action is needed to fix an account
Figure 3: The Microsoft Authenticator app flags that action is needed to fix an account

To secure the credentials for the account, the trick is to use the option to switch organizations via the icon in the top right-hand corner of the My Account page. This reveals the set of organizations that your account belongs to, starting with your account in the home tenant and then listing the organizations (aka host tenants) where you have a guest account (Figure 4).

Selecting an organization where an account is a guest
Figure 4: Selecting an organization where an account is a guest

Switching to another organization uses your account (the guest account in this case) to sign-into that organization. You can then use the Security Info page to go through the same steps to generate a new QR code and add it to the entry for the guest account in the Authenticator app. The Authenticator app should now be able to satisfy MFA challenges for the guest account when signing into the target organization.

Microsoft Authenticator App Restored to Good Health

Moving to a new iPhone isn’t something people do every day and it’s easy to forget how to renew credentials in different services. Getting new QR codes for the Authenticator app is in that category. Fortunately, the process isn’t quite as painful as I first anticipated after restoring the backup to my new phone and everything is now working as expected.

PS. If you use the Authenticator app on an Apple Watch, remember that from January 2023, the Authenticator app no longer supports WatchOS. Microsoft says that WatchOS is “incompatible with Authenticator security features.” I read that to mean that some of the changes Microsoft made recently to harden Authenticator against MFA fatigue like number matching and additional context just don’t work in the constrained real estate available for watch devices.

]]>
https://office365itpros.com/2023/01/04/microsoft-authenticator-app-qr/feed/ 6 58395
Adding New Azure AD Users to Groups Automatically https://office365itpros.com/2022/12/05/dynamic-group-membership/?utm_source=rss&utm_medium=rss&utm_campaign=dynamic-group-membership https://office365itpros.com/2022/12/05/dynamic-group-membership/#comments Mon, 05 Dec 2022 01:00:00 +0000 https://office365itpros.com/?p=58175

Dynamic Group Membership is the Obvious But Not the Only Option

A member of the Microsoft Technical Community asks if it’s possible to automatically add newly-created accounts to an existing group. The initial response offered by the community focused on dynamic groups – either dynamic distribution lists or dynamic Azure AD groups.

It’s a reasonable suggestion. Dynamic distribution groups are part of base Exchange Online functionality and don’t require any additional licenses. Dynamic Azure AD groups require Azure AD Premium P1 licenses for every account covered by dynamic membership. In both cases, the trick is to make sure that the query used by Exchange Online or Azure AD to determine group membership finds the new account.

Dynamic Group Membership for Exchange Online Mailboxes

It’s possible to create a dynamic distribution group based on a simple query like “all mailboxes” that will automatically include new accounts (if they have mailboxes). Figure 1 shows the UX in the Exchange admin center (EAC) to define the membership of a new dynamic distribution list.

Figure 1: Dynamic membership settings for all mailboxes

The list works and email sent to it arrives in the inbox of every mailbox in the tenant, including shared mailboxes. This is because the recipient filter generated by Exchange Online for the dynamic distribution group selects all mail-enabled objects with a recipient type of ‘UserMailbox’ and only filters out some system mailboxes.

A dynamic distribution list like this is said to use a “canned” recipient filter because Exchange Online generates the filter based on the choices the administrator makes when they create the new list. You can only edit canned filters through the EAC. Exchange Online gives greater flexibility through the support of custom recipient filters. These filters can only be created using PowerShell, but they’re much more flexible in terms of selecting the set of mail-enabled objects to address through the list. A simple custom recipient filter to find just user mailboxes is shown below together with a test with the Get-Recipient cmdlet to prove that the filter works.

$Filter = "{RecipientTypeDetails -eq 'UserMailbox'}"
Get-Recipient -RecipientPreviewFilter $Filter

Dynamic Group Membership for Azure AD User Accounts

Dynamic Azure AD groups can be used with Microsoft 365 groups and Teams. These groups use different membership filters (query rules) to find the set of target objects. Instead of mail-enabled objects like mailboxes, the query against Azure AD focuses on user accounts rather than mailboxes. However, the same capability exists in that it’s possible to create a dynamic Azure AD group that includes all user accounts, including those newly created.

Again, the key is to construct a query rule that finds all user accounts – of the right type. When Azure AD is used for a Microsoft 365 tenant, there are many non-interactive user accounts created to give identities to objects such as shared mailboxes and room mailboxes. These are all considered “member” accounts and it’s easy to build a rule to find all member accounts. However, you probably want a more refined version that finds just the accounts used by humans.

Azure AD doesn’t have a human filter, so we need to construct something that Azure AD can use to find matching accounts in its directory. One approach is to use licenses for the check. You could look for accounts assigned Office 365 E3 licenses but would have to check for accounts with F1 or E5 licenses too. An easy change is to look for accounts that have any license that has at least one enabled service. For instance, accounts with Office 365 E3 or E5 licenses with the Exchange Online, Teams, Planner, or SharePoint Online service would all match. Figure 2 shows a test of the rule against a “real” user account and some other user accounts belonging to room and shared mailboxes. You can see that the real account passes the validation test while the others do not.

Testing the membership rule for a dynamic Azure AD group to find all user accounts
Figure 2: Testing the membership rule for a dynamic Azure AD group to find all user accounts

Azure AD accounts used by shared mailboxes must be assigned licenses when they need more than 50 GB of mailbox storage or an online archive. These accounts satisfy the membership rule, but that’s perhaps not important. If it is, some tweaking of the membership rule is necessary to remove the shared mailbox accounts.

Dynamic Group Membership of Org-Wide Teams

If your organization is smaller than 10,000 accounts, new Azure AD accounts automatically join the org-wide teams in the tenant (a tenant can support up to five org-wide teams). Org-wide teams are a special form of dynamic Microsoft 365 group whose membership is controlled by Teams rather than Azure AD, so Azure AD Premium P1 license are not required.

The PowerShell Alternative to Manage Dynamic Group Membership

If you don’t want to use a dynamic object, it’s certainly possible to use standard distribution lists or Microsoft 35 groups. In this scenario, the tenant takes the responsibility for maintaining group membership. Usually, PowerShell is used to add new accounts to group membership. You don’t have to worry about removing deleted accounts from the group as this happens automatically following an account deletion.

To add a new user to a distribution list, use the Add-DistributionGroupMember cmdlet:

Add-DistributionGroupMember -Identity "All Tenant Mailboxes" -Member Lotte.Vetler@office365itpros.com

To add a new user account to a Microsoft 365 group, either run the Add-UnifiedGroupLinks cmdlet (from the Exchange Online management module) or the New-MgGroupMember cmdlet (from the Microsoft Graph PowerShell SDK):

Add-UnifiedGroupLinks -Identity "All Tenant Accounts" -LinkType Member -Links Lotte.Vetler@office365itpros.com

New-MgGroupMember -GroupId "107fe4dd-809c-4ec9-a3a1-ab88c96e0a5e" -DirectoryObjectId (Get-MgUser -UserId Lotte.Vetler@office365itpros.com).Id

If the tenant creates user accounts programmatically with PowerShell, these commands can be added to that script. If not, a background scheduled job could find accounts that don’t exist in group membership and add them. See this article for more information about group management with the Microsoft Graph PowerShell SDK.

Many Possibilities to Ponder

A simple question required a long answer. That’s because the questioner didn’t specify what type of group that they wanted to add new accounts to. In any case, it’s nice to be able to debate the possibilities and then settle on the best course of action to take.


Insight about the various options to manage dynamic group membership for new accounts doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2022/12/05/dynamic-group-membership/feed/ 3 58175
The Fuss About the Azure AD Tenant Creation Setting https://office365itpros.com/2022/11/29/azure-ad-tenant-creation/?utm_source=rss&utm_medium=rss&utm_campaign=azure-ad-tenant-creation https://office365itpros.com/2022/11/29/azure-ad-tenant-creation/#comments Tue, 29 Nov 2022 01:00:00 +0000 https://office365itpros.com/?p=58083

Azure AD Tenant Creation is Useful

Updated: 14 December 2022

I really don’t know why so much fuss and bother erupted (mostly in Twitter) when a preview setting to control creation of new tenants appeared in the User settings section of the Azure AD admin center (Figure 1). The fact is that people have always been able to create new tenants. Developers, for instance, often take the opportunity to run the free Microsoft 365 tenant offered by Microsoft for development purposes. If you’re doing Graph-based development, you can keep the free tenant (complete with 25 Office 365 E5 licenses) going for as long as you want.

The setting to control if users can create new Azure AD tenants

Azure AD tenant creation
Figure 1: The setting to control if users can create new Azure AD tenants

Update: Microsoft formally documented the preview of the tenant creation control in message center notification MC485089 (14 Dec 2022). It is covered by Microsoft 365 roadmap item 109541 with general availability expected in March 2023.

Few users will find their way to the Azure AD admin center to create a new tenant. And if you restrict access to the administration portal using the setting in Figure 1, Azure AD blocks non-administrator access to the portal (Figure 2), so those that attempt to access the admin center cannot do very much.

Sorry! No access to the Azure AD admin center
Figure 2: Sorry! No access to the Azure AD admin center

Azure AD and Multiple Tenants

An important factor to consider is that Azure AD is a massive multi-tenant environment. A tenant is a logical division of work spanning user accounts, groups, applications, roles, and so on. A basic Azure AD tenant is free. The limitations that exist come through licensing.

Some organizations are perfectly happy with a single tenant; others will split work across multiple tenants, perhaps to accommodate operating units within the company or to respect geographical boundaries. From a Microsoft 365 perspective, a single tenant is the best option because it sets the foundation for easy collaboration and sharing across the entire organization. To enable data residency requirements, Microsoft 365 offers multi-geo support for Exchange Online, SharePoint Online, OneDrive for Business, and Teams.

Creating a New Azure AD Tenant

If users can create new tenants and have access to the Azure AD admin center, they can go to the overview section and select Manage tenants. They’ll see the set of tenants that their account can access, including the home tenant and tenants where they have guest membership. Selecting the Create option invokes a wizard to collect information about the new tenant. All that’s needed is:

  • An organization (tenant) display name. The name does not need to be unique.
  • An initial service domain. This is the sub-domain of onmicrosoft.com and must be unique.
  • The datacenter region to host the tenant.
  • The type of tenant. In this example, I use a regular Azure AD tenant rather than one used for Azure B2C.

In Figure 3, I’m creating a new Azure AD tenant called Office 365 for IT Pros. The wizard detects a problem with the service domain. I don’t know if someone else has a service domain called office365itpros.onmicrosoft.com, but I own office365itpros.com and the domain is registered to my Microsoft 365 tenant, so that might be where the problem lies. In any case, it’s easily fixed by choosing a different service domain. No relationship exists between the tenant display name and its service domain. And although Microsoft 365 uses the service domain for objects like Microsoft Online Email Routing Addresses (MOERA) and SharePoint Online site names, user principal names and user email addresses can use other domains registered for the tenant.

A problem with Azure AD tenant creation
Figure 3: A problem with Azure AD tenant creation

The user that creates a tenant becomes its first global administrator. This doesn’t involve creating a new member account in the tenant. Instead, Azure AD creates a guest account for the account that creates the tenant and assigns the global administrator role to the guest account.

Creating a new tenant takes just a few minutes. Once the tenant exists, you can sign in and begin working with the tenant. For instance, you can connect to the tenant with the Microsoft Graph PowerShell SDK.

Connect-MgGraph -TenantId Office365itpros2.onmicrosoft.com
Welcome To Microsoft Graph!

Get-MgOrganization | Format-Table DisplayName, VerifiedDomains

DisplayName            VerifiedDomains
-----------            ---------------
Office 365 for IT Pros {Office365itpros2.onmicrosoft.com}

Microsoft makes workload packs available for developer tenants to populate the tenant with objects like mailboxes and sites. A tenant created from the Azure AD admin center is bare-bones and completely separate to the tenant that the creating owner belongs to. No subscriptions or licenses are transferred. The only (tenuous) link connecting the two tenants is the guest account. Before any useful work can be done in the new tenant, the administrator must create objects like accounts, groups, apps, and configurations, and buy licenses and subscriptions.

A good reason to create a tenant is to have a baseline to compare settings against. Over time, a production tenant accrues updates and unless the organization practices good change management, it’s hard to know exactly what has been changed in different areas. A new tenant allows the organization to check the starting position and compare it to values in the production tenant. In addition, unlike developer tenants, which expire after 90 days if not used, tenants created in this manner don’t expire.

Azure AD Authorization Policy

Returning to the original point, all Azure AD tenants have a default authorization policy to hold the settings that control what users can do. These are the settings revealed in the Azure AD admin center. You can see the value of the settings through the Graph Explorer by running a query against https://graph.microsoft.com/beta/policies/authorizationPolicy/authorizationPolicy (Figure 4).

Viewing the default Azure AD authorization policy through Graph Explorer
Figure 4: Viewing the default Azure AD authorization policy through Graph Explorer

The policy shown in Figure 4 shows that the allowedToCreateTenants setting is False. This setting only applies to users. Administrators can still create tenants if they wish.

The authorization policy is also accessible via the Get-MgPolicyAuthorizationPolicy cmdlet. Running the cmdlet requires that the app has the Policy.Read.All permission. See this article for an explanation about how the SDK deals with permissions.

Get-MgPolicyAuthorizationPolicy | Select-Object -ExpandProperty DefaultUserRolePermissions | Format-List

AllowedToCreateApps                      : True
AllowedToCreateSecurityGroups            : True
AllowedToReadBitlockerKeysForOwnedDevice : True
AllowedToReadOtherUsers                  : True
AdditionalProperties                     : {[allowedToCreateTenants, True]}

To update the authorization policy, the app must hold the Policy.ReadWrite.Authorization permission. You can then create a hash table to hold the new settings and apply the settings by running the Update-MgPolicyAuthorizationPolicy cmdlet:

$RolePermissions = @{}
$RolePermissions["allowedToCreateTenants"] = $False
Update-MgPolicyAuthorizationPolicy -AuthorizationPolicyId "authorizationPolicy" 
-DefaultUserRolePermissions $RolePermissions
Get-MgPolicyAuthorizationPolicy | Select-Object -ExpandProperty DefaultUserRolePermissions | Format-List

AllowedToCreateApps                      : True
AllowedToCreateSecurityGroups            : True
AllowedToReadBitlockerKeysForOwnedDevice : True
AllowedToReadOtherUsers                  : True
AdditionalProperties                     : {[allowedToCreateTenants, False]}

Nothing Odd About Multiple Tenants

There’s nothing odd about having multiple Azure AD tenants, if you have good reason to run more than a single tenant. As noted above, Microsoft 365 runs best with a single tenant, but developers and other users might need access to their own space.


Learn how to exploit the data available to Microsoft 365 tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work.

]]>
https://office365itpros.com/2022/11/29/azure-ad-tenant-creation/feed/ 3 58083
Creating New Azure AD User Accounts and Updating Passwords with the Microsoft Graph PowerShell SDK https://office365itpros.com/2022/11/28/azure-ad-account-creation/?utm_source=rss&utm_medium=rss&utm_campaign=azure-ad-account-creation https://office365itpros.com/2022/11/28/azure-ad-account-creation/#comments Mon, 28 Nov 2022 01:00:00 +0000 https://office365itpros.com/?p=58066

Manage User Accounts with the New-MgUser and Update-MgUser Cmdlets

In March 2022, I wrote about the basics of Azure AD account management using the Microsoft Graph PowerShell SDK. One of the things that I left out of that article was Azure AD account creation. I omitted this detail because it was already covered in another article, where I compare creating an account using cmdlets from the old Azure AD module and the SDK. I’ll cover the basic points here

Connecting to the Microsoft Graph SDK

Before we can create or update accounts, we must connect to the SDK endpoint with the required permissions:

Connect-MgGraph -Scopes User.ReadWrite.All, Directory.ReadWrite.All
Select-MgProfile Beta

Password Profiles

The New-MgUser cmdlet creates a new account. To run New-MgUser, we need a password profile. A password profile is a Microsoft Graph resource that contains a password and associated settings. It can be as simple as a password with no settings, but a password profile can also include settings like ForceChangePasswordNextSignIn to force a user account to change their password after they next sign into Azure AD.

New-MgUser uses a hash table for the password profile. The example code shown below populates the hash table with a new password (generated using the GeneratePassword .NET method as a random 10-character string containing special characters, numbers, and upper- and lower-case letters). The ForceChangePasswordNextSignIn setting is True to force the new user to set a new password after they sign in.

Add-Type -AssemblyName 'System.Web'
$NewPassword = [System.Web.Security.Membership]::GeneratePassword(10, 3)
$NewPasswordProfile = @{}
$NewPasswordProfile["Password"]= $NewPassword
$NewPasswordProfile["ForceChangePasswordNextSignIn"] = $True

The hash table now contains values like this:

Name                           Value
----                           -----
Password                       4i_gb6OK?{
ForceChangePasswordNextSignIn  True

Creating a New Azure AD User Account with New-MgUser

To create the new account, run the New-MgUser cmdlet. It’s obviously important to include as many details as possible about the new user account, especially the settings exposed by Microsoft 365 in places like the user profile card or the Organization Explorer feature in Outlook and Teams.

# Azure AD Account Creation - the hard coded way
$DisplayName = "Jeff Atkinson"
$NewUser = New-MgUser -UserPrincipalName "Jeff.Atkinson@Office365ITPros.com" `
  -DisplayName "Jeff Atkinson (Information Technology)" `
  -PasswordProfile $NewPasswordProfile -AccountEnabled `
  -MailNickName Jeff.Atkinson -City NYC `
  -CompanyName "Office 365 for IT Pros" -Country "United States" `
  -Department "IT Operations" -JobTitle "GM Operations" `
  -BusinessPhones "+1 676 830 1101" -MobilePhone "+1 617 4466615" `
  -State "New York" -StreetAddress "1, Avenue of the Americas" `
  -Surname "Atkinson" -GivenName "Jeff" `
  -UsageLocation "US" -OfficeLocation "NYC"
If ($NewUser) { Write-Host ("Successfully added the {0} account" -f $NewUser.DisplayName) 
  } Else { Write-Host ("Failure adding the {0} account - exiting" -f $DisplayName); break }

The usage location is a two-character ISO-3166 country code to show where the account consumes services, and it’s important to set the value correctly so that the license assignment works properly. After creating a new account, you’ll need to assign it some licenses to allow access to Microsoft 365 services. See this article for more information.

The code to add a new account shown above is a one-off command. However, it’s the principal that counts and it is straightforward to take the code and amend it so that it uses parameters or input such as a CSV file (like that shown in Figure 1) holding details of new users. In the latter case, after loading the records into an array, you could then loop through the records to add each account. Here’s an example of doing just that:

CSV file to drive Azure AD account creation
Figure 1: CSV file to drive Azure AD account creation

# Azure AD account creation - driven by data imported from a CSV file
$Accounts = Import-CSV c:\temp\Accounts.CSV
ForEach ($Account in $Accounts) {
  $NewPassword = [System.Web.Security.Membership]::GeneratePassword(10, 3)
  $NewPasswordProfile = @{}
  $NewPasswordProfile["Password"]= $NewPassword
  $NewPasswordProfile["ForceChangePasswordNextSignIn"] = $True
  $MailNickname = $Account.First + "." + $Account.Surname
  $DisplayName = $Account.First + " " + $Account.Surname
  Write-Host ("Processing the {0} account" -f $DisplayName)
  $NewUser = New-MgUser -UserPrincipalName $Account.UserPrincipalName `
  -DisplayName $DisplayName `
  -PasswordProfile $NewPasswordProfile `
  -MailNickName $MailNickName -City $Account.City `
  -CompanyName $Account.Company -Country $Account.Country `
  -Department $Account.Department -JobTitle $Account.Title `
  -BusinessPhones $Account.Phone -MobilePhone $Account.Mobile `
  -State $Account.State -StreetAddress $Account.Street `
  -Surname $Account.Surname -GivenName $Account.First `
  -UsageLocation $Account.Location -OfficeLocation $Account.Office `
  -AccountEnabled
 If ($NewUser) { Write-Host ("Successfully added the {0} account" -f $NewUser.DisplayName) 
  } Else { Write-Host ("Failure adding the {0} account - exiting" -f $DisplayName); break }
}

Finishing up Azure AD Account Creation

To complete the account creation process, you might want to send email to the administrator accounts with details of the new account (Figure 2). This task is easily accomplished with a Graph method to create and send email (explained in this article).

Email notification about the creation of a new Azure AD user account

Azure AD account creation
Figure 2: Email notification about the creation of a new Azure AD user account

To help illustrate the flow of creating a new account complete with license assignment and email notification, I’ve uploaded a script to GitHub. The code is not a functional script because it contains once-off commands. Instead, it’s for you to play with and create your own version.

Updating a User Account with a New Password

To change an Azure AD account password, create a password profile as above and then run the Update-MgUser cmdlet. If you don’t want to force the user to create a new password after they sign in, make sure that the ForceChangePasswordNextSignIn setting in the password profile is false, and then run:

Update-MgUser -UserId Terry.Hegarty@Office365itpros.com -PasswordProfile $NewPassword

Updating a user’s password generates a continual access evaluation (CAE) event for CAE. This means that “enlightened” applications like the Office web apps learn about the existence of the new password and will force the user to reauthenticate with the new password to continue working.

Azure AD Account Creation Not Hard with the SDK

Creating a new Azure AD user account with the Microsoft Graph PowerShell SDK isn’t difficult. The hardest thing might be to come up with a good temporary password to assign to the account. Good luck if you’re moving scripts from the old Azure AD or MSOL modules before Microsoft deprecates these modules in 2023. It just takes a little time and maybe a lot of persistence.


Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2022/11/28/azure-ad-account-creation/feed/ 1 58066
How to Pause Membership Processing for Dynamic Group Membership https://office365itpros.com/2022/11/22/pause-membership-processing/?utm_source=rss&utm_medium=rss&utm_campaign=pause-membership-processing https://office365itpros.com/2022/11/22/pause-membership-processing/#comments Tue, 22 Nov 2022 01:00:00 +0000 https://office365itpros.com/?p=58033

New Pause Processing Toggle Appears in Entra ID Admin Center

Updated 22-Nov-2023

A recent Entra ID admin center update for Entra ID dynamic groups allows administrators to pause membership processing to resolve membership rules and identify group members. I can’t find any announcement about the change, and it’s not tagged as a preview, but a toggle is there to pause processing (Figure 1) and it works.

Pause processing for an Entra ID dynamic group

Pause membership processing
Figure 1: Pause processing for an Entra ID dynamic group

Switching the pause processing toggle back to off allows Entra ID to continue processing membership rules. The properties of a dynamic group tell you the current state of processing and when the last membership change happened. Common processing states for Entra ID dynamic groups are:

  • Succeeded: Entra ID has evaluated the membership query and the membership is up to date.
  • Evaluating: Entra ID is currently resolving the membership query to identify group members.
  • Processing: Entra ID is currently processing the membership.
  • Processing error: Entra ID was unable to evaluate the membership query.
  • Updates paused: An administrator has paused updates. The membership remains static until updates resume.
  • Not started: Entra ID has not yet started to evaluate the membership of a dynamic group.

Entra ID reassesses membership as demand on the service allows, with the goal of checking at least once daily. It’s therefore possible that Entra ID will not process changes made to user objects that bring them within the scope of a membership rule for up to 24 hours. My experience is that updates often occur earlier, but it’s wise to set this expectation.

Reporting Dynamic Membership Updates

To check the current situation with dynamic membership updates, we can use PowerShell to find all the dynamic groups in the tenant and report the timestamp for the last membership update, whether processing is enabled, and the current update status. Here’s how to do the job with the Microsoft Graph PowerShell SDK:

Connect-MgGraph -Scope Group.Read.All, GroupMember.Read.All
[array]$Groups = Get-MgBetaGroup -Filter "groupTypes/any(c:c eq 'DynamicMembership')" -All 
If (!($Groups)) { 
    Write-Host "No dynamic groups found"
} Else { 
   Write-Host ("Processing {0} dynamic groups" -f $Groups.count) }
$Report = [System.Collections.Generic.List[Object]]::new()
$Groups = $Groups | Sort-Object DisplayName
ForEach ($Group in $Groups) {
  $Options = $Group.ResourceProvisioningOptions -join ", "
  [array]$Members = Get-MgGroupMember -GroupId $Group.Id
  [array]$Owners = Get-MgGroupOwner -GroupId $Group.Id
  $DynamicData = Get-MgBetaGroup -GroupId $Group.Id -Property MembershipRuleProcessingStatus
  $DataLine = [PSCustomObject] @{
    Id              = $Group.Id
    DisplayName     = $Group.DisplayName
    Owners          = $Owners.Count
    Members         = $Members.Count
    ProcessingState = $Group.MembershipRuleProcessingState
    LastUpdate      = $DynamicData.MembershipRuleProcessingStatus.LastMembershipUpdated
    Status          = $DynamicData.MembershipRuleProcessingStatus.Status
    Options         = $Options }
  $Report.Add($DataLine)
}
$Report | Out-GridView

You can see that the code uses separate calls to the Get-MgBetaGroup cmdlet to fetch the property holding the membership rule processing status for the groups. For some reason, the original call to fetch a set of filtered groups fails if this property is included in the list to be retrieved. As revealed by the Graph X-Ray add-on, the same flow happens in the Entra ID admin center.

The code also includes calls to the Get-MgGroupOwner, Get-MgGroupMember, and Get-MgUser cmdlets to fetch the set of owners and members for each group. Although the Get-MgGroupOwner and Get-MgGroupMember cmdlets returns the set of owners and members respectively for a group, they return object identifiers instead of display names. While we can use the information to report counts, to report the owner names, we need to run Get-MgUser. See these articles for more information about using the Microsoft Graph PowerShell SDK to work with Entra ID user accounts and Entra ID Groups. Figure 2 shows the output of the report.

Figure 2: Reporting the membership processing state of Entra ID dynamic groups

Dynamic groups with paused membership updates show a last update of 1 January 2000. Most of the groups in Figure 2 have odd dates (1/1/0001). This situation occurred when I ran a script to pause membership processing for all dynamic groups. The next time Entra ID processes membership rules to validate group membership , it will update the date.

Dynamic Groups and Dynamic Distribution Lists

Entra ID dynamic groups and dynamic distribution lists are very different objects, but some people confuse the two or believe that the two objects are roughly the same. Apart from the requirement to have Entra ID Premium P1 licenses for Entra ID dynamic groups, the three biggest differences are:

  • Dynamic distribution lists don’t exist in Entra ID. They are an Exchange object.
  • Dynamic distribution lists support a wider set of member objects (any mail-enabled recipient).
  • Dynamic distribution lists support a wider set of properties for building custom membership filters (queries).

See this article for more information about the differences between the two types of dynamic group.

Dynamic Restrictions

Because of the processing load required to evaluate and process group membership, Entra ID restricts the number of dynamic groups and dynamic administrative units combined per tenant to 5,000. In December 2021, Microsoft changed the way that Exchange Online evaluates membership of dynamic distribution lists in a similar attempt to save resources.

Reasons to Pause Membership Processing

Although I appreciate Microsoft adding the extra flexibility in pausing membership processing, I’m struggling to find a good use case. One might be in a merger and acquisition scenario where the directory is in a state of flux, and you want consistency in dynamic group memberships for a period. Apart from that, I don’t know why Microsoft introduced the feature. However, it’s here now and available if you need it.


Learn how to exploit the data available to Microsoft 365 tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work.

]]>
https://office365itpros.com/2022/11/22/pause-membership-processing/feed/ 5 58033
Using PowerShell to Manage Azure AD Custom Security Attributes https://office365itpros.com/2022/11/11/custom-security-attributes-ps/?utm_source=rss&utm_medium=rss&utm_campaign=custom-security-attributes-ps https://office365itpros.com/2022/11/11/custom-security-attributes-ps/#comments Fri, 11 Nov 2022 01:00:00 +0000 https://office365itpros.com/?p=57859

Microsoft Graph PowerShell SDK Cmdlets Support Custom Security Attributes

Updated 12-Sep-2023

Introduced in preview in December 2021, I’m still looking for a good way to use Azure AD custom security attributes. Microsoft recently updated conditional access policies to support an app filter based on custom security attributes. That’s a nice example of what’s possible, but it’s probably a scenario limited to tenants that need such a capability.

In my original commentary on custom security attributes, I wondered if they might replace Exchange Online custom attributes. So far, I see little sign that this will happen, if only because the preview implementation only supports user and service principal objects. Exchange Online supports custom attributes for all mail-enabled objects, meaning that these attributes are more flexible. Marking Microsoft 365 groups so that changes to their membership can be monitored is just one recent practical example of custom attributes in use.

Marking objects is one thing. Being able to find the marked objects is equally important. The cmdlets in the Exchange Online management PowerShell support server-side filtering against custom attributes to make it easy and fast to search against the attributes. It therefore seemed like a good thing to check how to search Azure AD objects using values stored in Azure AD custom security attributes.

Assigning and Finding Custom Security Attributes

Cmdlets in the Microsoft Graph PowerShell SDK that operate against Azure AD user accounts and service principals (for enterprise and registered applications) support the custom security attributes. Microsoft’s documentation explains the basics. For example, to add custom security attributes to a user object, populate a hash table containing the attribute names and values and use it as input to the Update-MgUser cmdlet.

$Attributes = @{
	CustomSecurityAttributes = @{
	  Employees = @{
           "@odata.type" = "#Microsoft.DirectoryServices.CustomSecurityAttributeValue"
           JobCode = 'Principal'
           DateOfHire = '2-Nov-1983'
           "EmployeeNumber@odata.type" = "#Int32"
           EmployeeNumber = '150847'
           Executive = $true
	   }
	}
}

Update-MgUser -Userid Tony.Redmond@office365itpros.com -BodyParameter $Attributes

In this case, the attributes come from a set called Employees (Figure 1) that contains attributes defined as string, Boolean, and integer data types. You can see in the PowerShell snippet how to define the data types for Boolean and integer attributes.

Custom Security Attributes in the Azure AD admin center
Figure 1: Custom Security Attributes in the Azure AD admin center

If you make a mistake with an attribute value, update the parameter with the correct value and run Update-MgUser again. To remove an attribute value, pass a blank value (something like ‘ ‘) rather than $Null. The lack of support for the $Null PowerShell variable is an SDK foible.

To check that the custom security attributes are in place, run the Get-MgUser cmdlet against the user account and fetch the CustomSecurityAttributes property:

$User = Get-MgUser -UserId Tony.Redmond@office365itpros.com -Property CustomSecurityAttributes

$EmployeeData = $User.customsecurityattributes.additionalProperties['Employees']

Although it’s simple to retrieve the property, complexity lurks because the attribute values are stored in a system dictionary object called additionalProperties. Multiple sets of custom security attributes can be assigned to an object, so additionalProperties holds a separate hash table for each set. In this instance, we’re only interested in attributes from the Employees set, so we extract them into a separate hash table to make it more convenient to access individual attribute values. Each attribute that we want to use is easily fetched by a keyed lookup against the hash table as shown below.

$EmployeeData

Key            Value
---            -----
@odata.type    #microsoft.graph.customSecurityAttributeValue
Executive      True
JobCode        Principal
LegalEntity    RA Ireland
DateOfHire     2-Nov-1983
EmployeeNumber 150847

$EmployeeData['JobCode']

I’m sure that the structure we’ve just navigated to find custom security attributes makes sense in terms of the way that Microsoft generates the cmdlets in the Microsoft Graph PowerShell SDK from the underlying Graph APIs. However, compared to the ease of access to data using other PowerShell cmdlets, it’s too complex and clunky.

Searching All User Accounts for an Attribute Value

This feeling is confirmed when trying to find users with a specific value stored in an attribute. This code finds all user member accounts in the tenant with at least one assigned license (to exclude accounts like those used for shared and room mailboxes). The code then loops through all users to find those with custom security attributes from the Employees set and checks if the job code attribute has a certain value.

[array]$Users = Get-MgUser -Filter "assignedLicenses/`$count ne 0 and userType eq 'Member'" -ConsistencyLevel eventual -CountVariable Records -All -Property CustomSecurityAttributes, Id, DisplayName
ForEach ($User in $Users) {
    If ($User.customsecurityattributes.AdditionalProperties['Employees'] -ne $Null) {
        $EmployeeData = $User.customsecurityattributes.AdditionalProperties['Employees']
        If ($EmployeeData['JobCode'] -eq "Principal") {
          Write-Host $User.DisplayName "is a Principal" }
    }
}

I can’t find a filter for the Get-MgUser cmdlet to search for a value in a single attribute. I’ll keep trying, but for now I’m left with the technique explained above.

Using Custom Security Attributes with Service Principals

The same approach applies to updating the service principal for an enterprise or registered app with a custom security attribute. This code searches to find a specific application and then updates two attributes. The only changes are in the cmdlets used and that the AppDepartment attribute supports the storage of multiple values. This means that we must identify that the attribute is a collection of strings and pass the strings in an array:

$App = Get-MgServicePrincipal -Filter "displayname eq 'Graph Microsoft 365 Groups Membership Report'"
$Attributes = @{
	CustomSecurityAttributes = @{
	  Applications = @{
           "@odata.type" = "#Microsoft.DirectoryServices.CustomSecurityAttributeValue"
           "AppLevel@odata.type" = "#Int32"
           AppLevel = "1"
           "AppDepartment@odata.type" = "#Collection(String)"
           AppDepartment = @( "IT" )
	   }
	}
}

Finding applications marked with a certain attribute value follows the same path as explained above. Here’s what I did to find applications marked with IT in the AppDepartment attribute:

[array]$Apps = Get-MgServicePrincipal -All -Property CustomSecurityAttributes, DisplayName, Id, AppId
ForEach ($App in $Apps) {
    If ($App.customsecurityattributes.AdditionalProperties['Applications'] -ne $Null) {
       $AppAttributes = $App.customsecurityattributes.AdditionalProperties['Applications']
       If ($AppAttributes['AppDepartment'] -eq "IT") {
          Write-Host $App.DisplayName "is an IT application" }
    }
}

Looking Forward to Change

I’m not writing off Azure AD custom security attributes (but if you want to store employee data, there are some standard attributes available for user objects). I’m sure that the Entra ID team has many plans to use these attributes in different ways and that the Microsoft Graph PowerShell SDK developers could make the attributes easier to work with. At least, I hope so.


Keep up to date with developments like Azure AD custom security attributes by subscribing to the Office 365 for IT Pros eBook. Our monthly updates make sure that our subscribers understand the most important changes happening across Office 365.

]]>
https://office365itpros.com/2022/11/11/custom-security-attributes-ps/feed/ 2 57859
Azure AD Conditional Access Policies Get App Filter https://office365itpros.com/2022/10/31/conditional-access-app-filter/?utm_source=rss&utm_medium=rss&utm_campaign=conditional-access-app-filter https://office365itpros.com/2022/10/31/conditional-access-app-filter/#respond Mon, 31 Oct 2022 01:00:00 +0000 https://office365itpros.com/?p=57675

Custom Security Attributes Used for Conditional Access App Filters

In January 2022, I wrote about the introduction (in preview) of Azure AD custom security attributes. At the time, Microsoft positioned the new attributes as part of their Attribute-based Access Control initiative for Azure to give organizations the ability to manage resources at a fine-grained level. Not being an Azure expert, I tried the new custom security attributes out and felt that organizations would figure out ways to use them.

Lots of new stuff has happened recently with Azure AD conditional access policies, like the introduction of new checks for external user type and authentication strength. Now, Microsoft has added a filter for apps based on custom security attributes.

Mark Apps with Custom Security Attributes

The idea is simple. Organizations define custom security attributes to use to mark apps known to Azure AD. An app is an object and like any other Azure AD object, administrators can assign the app whatever custom attributes make sense. For instance, you could assign an attribute to indicate the department that uses an app or an attribute to mark an app as highly important. The point is that the custom attribute is then used by a filter (Figure 1) to identify apps that a conditional policy can allow or block access to.

 Defining an app filter for a conditional access policy
Figure 1: Defining an app filter for a conditional access policy

For now, app filters in conditional access policies can only use string custom security attributes, but you can select attributes from any attribute set defined in the organization. The app filter can be combined with any of the other controls available in a conditional access policy.

The value in this approach is that you don’t need to amend a conditional access policy to accommodate new or additional apps. Simply update the app with an appropriate value for the custom security attribute used by the app filter and the app immediately becomes within the policy scope. That’s a big advantage in large organizations that might have to manage hundreds (or conceivably, thousands) of applications.

Graph X-Ray in Windows Store

In other Azure AD news, the Graph X-Ray tool that exposes the Graph API calls made by (some parts of) the Azure AD admin center is now available in the Windows Store (Figure 2). I recommend this tool to anyone who’s getting acquainted with the Graph API calls used for objects like users and groups.

The Graph X-Ray tool in the Windows Store
Figure 2: The Graph X-Ray tool in the Windows Store

The Graph X-Ray tool helped us enormously when we upgraded the PowerShell examples using the soon-to-be-deprecated Azure AD module to Graph API calls or Microsoft Graph PowerShell SDK cmdlets for the 2023 edition of the Office 365 for IT Pros eBook. Sometimes you need just a little hint to understand what approach to take and the Graph X-Ray tool delivers more than its fair share of hints.

Cmd.Ms

From the same fertile mind as Graph X-Ray comes Cmd.ms, an elegantly simple idea that delivers great value. Microsoft 365, as you might have observed, spans a bunch of administrative portals and consoles and it’s sometimes difficult to remember the URI for a specific portal. You can go to the Microsoft 365 admin center and rely on the shortcuts available there to get you to the Teams admin center, Exchange admin center, SharePoint Online admin center, and so on, but what happens if you haven’t loaded the Microsoft 365 admin center or need to go somewhere that isn’t available as a shortcut? That’s where Cmd.ms comes in.

Essentially, Microsoft has defined a set of web shortcuts to the admin centers (Figure 3). Entering teams.cmd.ms brings you to Teams while admin.cmd.ms loads the Microsoft 365 admin center. It’s tremendously useful.

Cmd.ms shortcuts to Microsoft 365 web sites
Figure 3: Cmd.ms shortcuts to Microsoft 365 web sites

Cmd.ms add-ons are available for Edge, Chrome, and Firefox to provide autocomplete suggestions in the browser address bar.

The only issue I have is that Microsoft chose to use ad.cmd.ms to bring you to the Entra admin center and azad.cmd.ms to the Azure Active Directory admin center. I know Microsoft wants to emphasize the Entra brand, but it would be nice to have aad.cmd.ms used for Azure AD rather than azad.cmd.ms. It’s a small buggette.

Continued Evolution of Conditional Access

Returning to the original topic, there’s no doubt that Microsoft is putting a great deal of effort into improving the functionality of Azure AD conditional access policies. The recent batch of announcements underline this point. It’s all about erecting more efficient barriers to unauthorized access. Hopefully attackers can’t get into an Azure AD tenant. If they do, conditional access policies can help restrict their ability to compromise resources. That’s the logic underpinning the deployment of conditional access.

]]>
https://office365itpros.com/2022/10/31/conditional-access-app-filter/feed/ 0 57675
Azure AD Conditional Access Policies Add Check for External User Types https://office365itpros.com/2022/10/27/conditional-access-policy-external/?utm_source=rss&utm_medium=rss&utm_campaign=conditional-access-policy-external https://office365itpros.com/2022/10/27/conditional-access-policy-external/#comments Thu, 27 Oct 2022 01:00:00 +0000 https://office365itpros.com/?p=57630

New Conditional Access Policy Settings to Exert Granular Control Over External Users

Building on their recent work to improve Azure AD conditional access policies by adding a control to require specific authentication strengths for connections, Microsoft has released another interesting control (in preview). You can now differentiate between the different kinds of external users that connect to your tenant in a feature that Microsoft calls “fine-grained Azure B2B access control.”

Azure AD recognizes different kinds of connections based on the authentication flow, so it’s able to focus on connections such as B2B Collaboration like guests accessing a SharePoint Online site or when an account authenticated in another tenant uses B2B Direct Connect to access a Teams shared channel. The differentiation between connection types allows Azure AD to apply conditional access to impose conditions on specific connections.

Adding Control for External Users

To try the new control out, I created a new conditional access policy. Under the assignments section, I chose to include specific users. This option has always been available, but now you get to pick from the different types of external users supported by Azure AD (Figure 1).

Defining the types of external users for a conditional access policy
Figure 1: Defining the types of external users for a conditional access policy

For most Microsoft 365 tenants, the interesting options are B2B Collaboration and B2B Direct Connect. Guest accounts created using Azure B2B Collaboration have been in use since mid-2016 to support external access to resources in Outlook Groups, Teams, SharePoint Online, OneDrive for Business, Yammer, and Planner. The Azure B2B Collaboration policy is available to control the creation of guest accounts using a block list of domains. Even with a policy in place, tenants end up with large numbers of guest accounts and need to do some pruning to remove obsolete guests.

External accounts that use B2B Direct Connect to access Teams shared channels (the only workload currently supported) don’t have a presence in the tenant directory. Instead, these accounts authenticate against their own directory and present the credentials to gain access to the resources in the host tenant. If the cross-tenant access policies configured in both tenants permit access, the accounts can work with the resources.

The external user control includes other account types used in more specific circumstances. The point is that a lot of flexibility exists in the control of inbound connections. For instance, you can restrict the control to specific Microsoft 365 tenants (Figure 2) using either the tenant identifier or a registered domain for the tenant to add it to the policy (if you don’t know the tenant identifier, you can find it using an online service).

Adding an external Azure AD domain to a conditional access policy
Figure 2: Adding an external Azure AD domain to a conditional access policy

The new control works alongside the other controls available in a conditional access policy. In this instance, I configured the policy to apply to Office 365 apps and to require multi-factor authentication to grant access.

Planning Conditional Access Policies

An Azure AD tenant can support up to 195 conditional access policies. It takes planning to make sure that you don’t create a set of conditional access policies that conflict with each other and that each policy serves a well-defined purpose. For instance, the new ability to control external connections from specific tenants might tempt administrators to create to create multiple policies to control external access for external access from specific tenants. This is a bad idea and will probably be a maintenance nightmare. Try to use the one policy to handle external access from all partner tenants as it’s likely that much the same kind of controls will apply to all.

To make sure that other policies didn’t interfere with testing, I put any policy relating to external access into report-only mode.

Testing the Control

To test that the new policy worked as expected, I signed into Teams using an account belonging to the tenant specified in the policy. I then opened a shared channel and was immediately promoted with an MFA challenge. After satisfying the challenge, the client connected to the shared channel and the account could post messages. Figure 3 shows the authentication and conditional access details for a sign-in processed by the conditional access policy.

Azure AD sign-in record tracks application of the conditional access policy
Figure 3: Azure AD sign-in record tracks application of the conditional access policy

One More Control for Connections

Conditional access policies are not a universal panacea for keeping a Microsoft 365 tenant safe and secure from attackers. However, correctly configured and deployed, conditional access policies can stop people who shouldn’t access tenant resources from getting in. The new find-grained external access control is helpful in this respect. Remember that conditional access is an Azure AD Premium feature and deploy it alongside Exchange Online authentication policies to gain maximum protection from attacker probes.


Stay updated with developments across the Microsoft 365 ecosystem by subscribing to the Office 365 for IT Pros eBook. We do the research to make sure that our readers understand the technology.

]]>
https://office365itpros.com/2022/10/27/conditional-access-policy-external/feed/ 1 57630
Microsoft Hardens Authenticator App to Prevent MFA Fatigue https://office365itpros.com/2022/10/26/authenticator-app-mfa-fatigue/?utm_source=rss&utm_medium=rss&utm_campaign=authenticator-app-mfa-fatigue https://office365itpros.com/2022/10/26/authenticator-app-mfa-fatigue/#comments Wed, 26 Oct 2022 01:00:00 +0000 https://office365itpros.com/?p=57643

Number Matching and Geographic Context Now GA for Authenticator App

Updated 20 February 2023

Eleven months after releasing the features to preview, Microsoft has made number matching and additional context generally available with the tag line of advanced Microsoft Authenticator security features. The capabilities are available for multi-factor authentication (MFA) flows now and will be available for passwordless flows soon.

In a nutshell, these features relieve the MFA fatigue some users experience when they process MFA challenges to sign into Microsoft 365 and other apps. Instead of blindly responding to a prompt or “simple approval” (which could be hijacked by an attacker – see MITRE Att&ck technique T1621), the user is forced to respond to the challenge by entering a matching number. This addresses the problem (as experienced in the recent Uber compromise) where an account holder responds to an MFA challenge without putting their brain in gear. MFA fatigue is a very real and current problem.

Additional context allows the Authenticator app to display information about the location of the sign-in and the app provoking the challenge. The extra information helps the user to understand if the sign-in that provoked the challenge is valid. Together, Microsoft says that number matching and additional context help organizations to “prevent accidental [user] approvals in Microsoft Authenticator.

New UI in Azure AD Admin Center

Tenants can roll the features out to all users or a targeted group, Microsoft has refreshed the UI in Authentication Methods section under Security in the Azure AD admin center (Figure1). You can also configure the settings with a Graph API request, but I wouldn’t bother. The Azure AD admin center does everything you need.

Settings for the Authenticator App in the Azure AD Admin Center
Figure 1: Settings for the Authenticator App in the Azure AD Admin Center

Microsoft planned to implement number matching for all Authenticator users in February 2023 but rescheduled the deployment date to May 8, 2023. At that point, Microsoft will remove the UI to enable or disable this feature from the Azure AD admin center. and all authentication challenges using the Authenticator app will require users to respond to generated numbers rather than the traditional Deny/Approve choice. This is part of Microsoft’s ongoing campaign to increase security by default across Microsoft 365.

Responding to a Non-Fatigued MFA Challenge

I found that it took about ten minutes before Azure AD implemented the updated settings in its challenges. The number challenge uses the same UI as the preview (Figure 2).

Azure AD issues a numeric MFA challenge
Figure 2: Azure AD issues a numeric MFA challenge

Figure 3 shows how the Microsoft Authenticator app (for iOS) prompts the user to enter the number requested by Azure AD. You can also see the additional geographic (based on the IP address of the device used for the sign-in) and application context presented to allow the user to judge if the sign-in is legitimate.

The Authenticator app (on iOS) prompts for the numeric challenge
Figure 3: The Authenticator app (on iOS) prompts for the numeric challenge

Speaking of Authenticator on iOS, Microsoft says that the app now uses App Transport Security (ATS) for improved privacy and data integrity between Authenticator and web services like the Microsoft 365 apps.

They also say that Authenticator on Android allows users to search their accounts and that this capability is coming to iOS “soon.” I use Authenticator for multiple Microsoft 365 tenants, my Microsoft consumer account, and applications like Twitter and GitHub, so searching will be a nice addition.

Changes Improve Authenticator’s Resistance to Attack

Why is number matching and additional context important for Authenticator? At the TEC 2022 conference, Alex Weinert, Microsoft VP for Identity Security, appealed for Microsoft 365 tenants to deploy multi-factor authentication more broadly (i.e., to increase the overall level of protection from the current 26.84% of user accounts). MFA protects more administrator accounts (34.15%), but that’s hardly a reason to celebrate.

During his TEC session, Alex discussed the updates now available for Authenticator and stressed how these made the app less susceptible to attack and less likely for its users to succumb to the human weakness seen in MFA fatigue. I imagine that with these updates, Microsoft now regards Authenticator as having the same authentication strength as Windows Hello and FIDO-2 keys.

The nice thing about the cloud is that changes like this roll-out without any intervention required on the part of tenants. It’s entirely in your hands to decide whether to take advantage and make MFA challenges more resistant to attack. It makes sense to do so.


Learn how to exploit the data available to Microsoft 365 tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work.

]]>
https://office365itpros.com/2022/10/26/authenticator-app-mfa-fatigue/feed/ 5 57643
Report SSPR Status for Azure AD Accounts https://office365itpros.com/2022/10/20/azure-ad-sspr-accounts-not-enabled/?utm_source=rss&utm_medium=rss&utm_campaign=azure-ad-sspr-accounts-not-enabled https://office365itpros.com/2022/10/20/azure-ad-sspr-accounts-not-enabled/#comments Thu, 20 Oct 2022 01:00:00 +0000 https://office365itpros.com/?p=57562

Use Microsoft Graph PowerShell SDK Cmdlets to Report Accounts Not Yet Set Up for SSPR

A tweet by Nathan McNulty about the Get-MgReportAuthenticationMethodUserRegistrationDetail cmdlet attracted my attention. The cmdlet generates a report about the registered authentication methods for Azure AD accounts. Nathan used the cmdlet to identify accounts that aren’t set up for self-service password reset (SSPR) by filtering the results to find only member accounts where the IsSSPRCapable property is set to False. SSPR is a premium Azure AD feature.

Get-MgReportAuthenticationMethodUserRegistrationDetail outputs filtered results, but two problems exist before the data is really usable. First, the default output for the cmdlet is user identifiers (GUIDs) instead of human-friendly display names for each account. Second, while the filter can isolate member accounts, it can’t refine the query further to drop accounts created for shared mailboxes, resource mailboxes, and other purposes. The first issue is resolved by explicitly selecting the userPrincipalName and userDisplayName properties for output; the second takes more work.

Exploring a Solution

One potential solution is illustrated below. The script uses the Get-MgUser cmdlet to find all accounts with at least one assigned license (the set of returned accounts can include those used for shared mailboxes). Information about account identifiers and display names are loaded into a hash table to make it possible to lookup an identifier very quickly. We can then loop through the set returned by Get-MgReportAuthenticationMethodUserRegistrationDetail and check each account against the hash table. If a match occurs, we know that we have a licensed account that isn’t currently enabled for self-service password result and can report that fact.

Although the Get-MgReportAuthenticationMethodUserRegistrationDetail cmdlet can output user principal name and user display name properties, looking up the user account details against a table created by Get-MgUser allows us to drop the non-user accounts and lay the foundation for retrieving other data, as explained below. Here’s the code:

Connect-MgGraph -Scope Directory.Read.All, UserAuthenticationMethod.Read.All, AuditLog.Read.All
Select-MgProfile Beta

Write-Host "Finding licensed Azure AD accounts"
[array]$Users = Get-MgUser -Filter "assignedLicenses/`$count ne 0 and userType eq 'Member'" -ConsistencyLevel eventual -CountVariable Records -All
# Populate a hash table with the details
$UserTable = @{}
$Users.ForEach( { $UserTable.Add([String]$_.Id, $_.DisplayName) } )
Write-Host "Finding user accounts not capable of Self-Service Password Reset (SSPR)"
[array]$SSPRUsers = Get-MgReportAuthenticationMethodUserRegistrationDetail | Where-Object {$_.userType -eq 'member' -and $_.IsSSPRCapable -eq $False} | Select-Object Id, userDisplayName, userPrincipalName, DefaultMfaMethod, IsAdmin, IsMfaCapable, IsMfaRegistered, IsPasswordlessCapable, IsSSPRCapable         
Write-Host "Cross-checking against licensed users..."
[array]$NonSSPR = $Null
ForEach ($S in $SSPRUsers) {
  $DisplayName = $UserTable.Item($S.Id) 
  If ($DisplayName) {
     $NonSSPR += $DisplayName }
}
$PNonSSPR = ($NonSSPR.count/$Users.Count).toString("P")
Write-Host ("{0} out of {1} licensed accounts ({2}) are not enabled for Self-Service Password Reset" -f $NonSSPR.count, $Users.count, $PNonSSPR )
Write-Host ($NonSSPR -join ", ")

Only a list of account display names is output. When I ran the script in my tenant, the following output was generated:

Finding licensed Azure AD accounts
Finding user accounts not capable of Self-Service Password Reset (SSPR)
Cross-checking against licensed users...
23 out of 32 licensed accounts (71.88%) are not enabled for Self-Service Password Reset
Andy Ruth (Director), Ben James, Ben Owens (DCPG), Bruno Satlier, Chris Bishop, , Jackson Hoare, James Abrahams, Jeff Guillet, John C. Adams, Ken Bowers, Lotte Vetler, Marc Vigneau, Michael King, Paul Howett, Peter Bridges, Rene Artois, Sean Landy, Terry Hegarty, Tony Redmond (Office 365 for IT Pros), Vasil Michev (Technical Guru)…

Improving the Output

We can improve the output by including more information in the lookup table. A hash table is fast, but it’s limited to a key and a value, but the value can any PowerShell object. The hash table can then hold more information about each user. For example:

$UserTable = @{}
ForEach ($U in $Users) {
    $ReportLine  = [PSCustomObject] @{          
     Id                  = $U.Id
     DisplayName         = $U.DisplayName
     Department          = $U.Department
     Office              = $U.OfficeLocation  
     Country             = $U.Country
     }
    $UserTable.Add([String]$U.Id, $ReportLine) 
}

I’ve selected five properties for a user account. It’s easy to add more as necessary. With the hash table populated like this, we can grab the information from the PowerShell object in the value when a match occurs for an account and use it to build a nicer report.

ForEach ($S in $SSPRUsers) {
  $Data = $UserTable.Item($S.Id) 
  If ($Data) { # We found a match
     $ReportLine  = [PSCustomObject] @{  
       Id = $Data.Id
       DisplayName = $Data.DisplayName
       Department  = $Data.Department
       Office      = $Data.Office
       Country     = $Data.Country }
     $NonSSPRUsers.Add($ReportLine) }
}

Figure 1 shows the output of the report file.

Azure AD accounts that are not enabled for SSPR
Figure 1: A list of user accounts that don’t use SSPR

Checking Accounts Regularly

This is exactly the kind of check against user accounts that tenants might want to run regularly. A scheduled runbook executed by Azure Automation is a good way to process these kinds of operations and the code discussed here would move over easily to a runbook. In the interim, here’s the link to the full script in GitHub for you to improve and enhance it as you like.


Learn how to exploit the data available to Microsoft 365 tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work.

]]>
https://office365itpros.com/2022/10/20/azure-ad-sspr-accounts-not-enabled/feed/ 7 57562
Updating Microsoft 365 User Accounts to use a New Domain https://office365itpros.com/2022/10/18/update-user-email-upns/?utm_source=rss&utm_medium=rss&utm_campaign=update-user-email-upns https://office365itpros.com/2022/10/18/update-user-email-upns/#comments Tue, 18 Oct 2022 01:00:00 +0000 https://office365itpros.com/?p=57481

Update User Email Addresses and User Principal Names

A recent reader question asked about the best way to update a bunch of user accounts after the organization buys a new vanity domain and wants the domain used for email addresses and user principal names (sign-in addresses). This sometimes happens when a business goes through a rebranding exercise and ends up with a new name. The requirement to update email addresses and user principal names also occurs in tenant-to-tenant migrations.

Tenant-to-tenant migrations are a specialized kind of activity that’s usually managed with software built for this purpose. We won’t plunge into the challenges that these projects can encounter. Instead, we’ll focus on the scenario where someone in authority decides that all accounts should use different email addresses and user principal names.

Registered Domains

The first requirement is to add the domain to Office 365. Until this is done, you cannot use the domain. Once the domain is known to the tenant, it appears in the set of verified domains seen in the Microsoft 365 admin center (Figure 1).

Verified domains in a Microsoft 365 tenant.
Figure 1: Verified domains in a Microsoft 365 tenant

After verifying the domain for Microsoft 365, we can write some code to ask the administrator what domain to use. Here’s an example that uses the Get-MgOrganization cmdlet from the Microsoft Graph PowerShell SDK to fetch the verified domains:

Connect-MgGraph -Scopes Directory.Read.All, User.ReadWrite.All
# Get tenant information and the verified domains for the tenant
$TenantInfo = (Get-MgOrganization)
[array]$Domains = $TenantInfo.VerifiedDomains.Name
$DomainsList = $Domains -join ", "
Write-Host "Verified domains for this tenant:"
Write-Host "---------------------------------"
Write-Host ""
$Domains
Write-Host ""
$DomainToUse = Read-Host "What domain do you want to use for primary SMTP addresses and UPNs"
Write-Host ""
If ($DomainToUse -notin $Domains) {Write-Host ("The selected domain ({0}) is not in the set supported by the tenant ({1}). Please try again." -f $DomainToUse, $DomainsList); break }
$CompareDomain = "*" + $DomainToUse + "*"

Finding Mail Recipients

The next step is to find mail-enabled recipients that have email addresses that might need updating. This code finds user mailboxes, shared mailboxes, group mailboxes (for Microsoft 365 groups), distribution lists, and security-enabled distribution lists.

For each object, the code calculates a new primary SMTP address based on their existing address by swapping the existing domain for the new domain. A check makes sure that the new address isn’t already in use, and if it is, creates a new address by adding “.EXO” to the username. The code then checks if it’s necessary to update the user principal name for the Entra ID accounts used by user mailboxes and shared mailboxes. An account might already use the new domain, so the code checks the account’s current user principal name and updates it with the new domain if necessary.

The output is captured in a PowerShell list that’s exported to a CSV file.

If (!($DomainToUse)) {
   Write-Host "No domain to move to is defined. Please make sure that the $DomainToUse variable is defined"
   Break
} Else {
   Write-Host ("Processing accounts to move them to the {0} domain..." -f $DomainToUse)
}

[array]$Recipients = Get-Recipient -ResultSize Unlimited -RecipientTypeDetails UserMailbox, SharedMailbox, GroupMailbox, MailUniversalDistributionGroup, MailUniversalSecurityGroup, DynamicDistributionGroup

$i = 0
$Report = [System.Collections.Generic.List[Object]]::new()
ForEach ($R in $Recipients) {
     $i++
     If ($R.PrimarySmtpAddress.Split("@")[1] -ne $DomainToUse) { #Need to process this mailbox
      Write-Host ("Processing {0} {1} ({2}/{3})" -f $R.RecipientTypeDetails, $R.DisplayName, $i, $Recipients.Count)
      $NewUPN = $Null
      # Figure out new email address
      $NewAddress = $R.Alias + "@" + $DomainToUse
      # Check that the address is available
      $Status = Get-Recipient -Identity $NewAddress -ErrorAction SilentlyContinue
      # If we get a status the recipient address already exists, so create a new address
      If ($Status) { $NewAddress = $M.Alias + ".EXO@" + $DomainToUse }
      
      # Figure out if the account's user principal name needs to change
      If ($R.RecipientType -eq "SharedMailbox" -or $R.RecipientType -eq "UserMailbox") {
        $UPNDomain = $R.WindowsLiveId.Split("@")[1]
        If ($UPNDomain -ne $DomainToUse) { # New UPN needed
          $NewUPN = $R.WindowsLiveId.Split("@")[0] + "@" + $DomainToUse
          $Status = Get-MgUser -UserId $NewUPN -ErrorAction SilentlyContinue
          If ($Status) { # UPN already exists, so create a new one
            $NewUPN = $R.WindowsLiveId.Split("@")[0] + ".EXO@" + $DomainToUse }
          }
       }

      $ReportLine   = [PSCustomObject] @{ 
         DisplayName            = $R.DisplayName
         OldUPN                 = $R.WindowsLiveId
         NewUPN                 = $NewUPN
         PrimarySmtpAddress     = $R.PrimarySmtpAddress
         NewAddress             = $NewAddress
         Type                   = $R.RecipientTypeDetails
         Alias                  = $R.Alias
    }
    $Report.Add($ReportLine) }
}
$Report = $Report | Sort-Object Type
$Report | Export-CSV -NoTypeInformation c:\temp\MailObjects.Csv

Administrators can check the CSV to remove any mail-enabled recipients they don’t want to receive new email addresses (Figure 2).

Update User Email Addresses with a New Domain

The next step is reads in and processes an array of objects from the updated CSV file.

The code uses a Switch statement to check the object type and calls the appropriate cmdlet to assign the new primary SMTP address to the object. If the account used for a mailbox (user or shared) requires an update for its user principal name, we go ahead and do it.

The final step in the loop through the objects is to report what’s been done, noting the old and new SMTP address and the old and new user principal name.

# Process mail objects array to update primary SMTP addresses and UPNs as necessary
[array]$MailObjects = Import-CSV MailObjects.CSV
$Report = [System.Collections.Generic.List[Object]]::new()

Write-Host "Processing mail-enabled objects..."
$i = 0
ForEach ($M in $MailObjects)  {
   $i++
   Write-Host ("Processing {0} {1} ({2}/{3})" -f $M.Type, $M.DisplayName, $i, $MailObjects.Count)

   # Assign new primary SMTP Address
   Switch ($M.Type) {
      "DynamicDistributionGroup" { # Dynamic distribution list
        Set-DynamicDistributionGroup -Identity $M.PrimarySmtpAddress -PrimarySmtpAddress $NewAddress
     }
      "GroupMailbox" { # Microsoft 365 group
        Set-UnifiedGroup -Identity $M.PrimarySmtpAddress -PrimarySmtpAddress $NewAddress
     }
      "MailUniversalDistributionGroup" { # Distribution list
        Set-DistributionGroup -Identity $M.PrimarySmtpAddress -PrimarySmtpAddress $NewAddress
     }
      "MailUniversalSecurityGroup" { #Mail-enabled security group
        Set-DistributionGroup -Identity $M.PrimarySmtpAddress -PrimarySmtpAddress $NewAddress
     }
      "SharedMailbox" { # Shared mailbox
        Set-Mailbox -Identity $M.PrimarySmtpAddress -WindowsEmailAddress $NewAddress 
     }
      "UserMailbox" { # User mailbox
        Set-Mailbox -Identity $M.PrimarySmtpAddress -WindowsEmailAddress $NewAddress 
     }
    }

   # Update UPN if necessary
   If ($M.NewUPN) {  
     Update-MgUser -UserId $M.UPN -UserPrincipalName $M.NewUPN }

   $ReportLine   = [PSCustomObject] @{ 
          DisplayName            = $M.DisplayName
          OldUPN                 = $M.UPN
          NewUPN                 = $M.NewUPN
          OldPrimarySmtpAddress  = $M.PrimarySmtpAddress
          NewPrimarySmtpAddress  = $M.NewAddress
          Type                   = $M.Type
          Alias                  = $M.Alias
    }
    $Report.Add($ReportLine) 

} # End ForEach 
Write-Host "All done!"

Figure 3 shows an example of the report that allows administrators to check that the expected email addresses and user principal names are in place.

The updated accounts with new primary SMTP addresses and some new user principal names
Figure 3: The updated accounts with new primary SMTP addresses and some new user principal names

The User Issue

Updated user principal names take effect the next time users sign in. If you want to force the switchover, you could disconnect users from their current sessions by invalidating refresh tokens using the Graph revokeSignInSessions API. Invaliding access tokens forces users to reauthenticate and reconnect, and to do that, they must use their new user principal names.

Be aware that some issues exist when changing user principal names such as the need to set up the new user principal name on the Microsoft Authenticator app so that MFA challenges work It’s worthwhile reading through this Microsoft article to understand and test problems that users might encounter in your organization. Knowing what might happen and being prepared to fix the issues will ensure a smoother transition.

Any change to the way people sign-in is likely to cause some angst if it’s not communicated clearly so that everyone understands why the change is happening and what they must do to sign-in to access services.

Tidying Up Entra ID

The process outlined above takes care of the bulk of the work. If some Entra ID accounts that don’t have email addresses need to receive updated user principal names, you can do this with the Update-MgUser cmdlet from the Microsoft Graph PowerShell SDK.

Giving accounts new email addresses and user principal names isn’t a difficult technical challenge. The likely problems arise in preparation and communication. Isn’t that always the way?


Keep up with the changing world of the Microsoft 365 ecosystem by subscribing to the Office 365 for IT Pros eBook. Monthly updates mean that our subscribers learn about new developments as they happen.

]]>
https://office365itpros.com/2022/10/18/update-user-email-upns/feed/ 15 57481
Reporting Who Made License Assignments https://office365itpros.com/2022/10/14/azure-ad-license-assignment-report/?utm_source=rss&utm_medium=rss&utm_campaign=azure-ad-license-assignment-report https://office365itpros.com/2022/10/14/azure-ad-license-assignment-report/#comments Fri, 14 Oct 2022 01:00:00 +0000 https://office365itpros.com/?p=57437

Who Performed an Azure AD License Assignment?

After writing about how to detect underused (and expensive) licenses assigned to Azure AD accounts, I was asked if it was possible to report who assigned a license to accounts. It’s a good question that stumped me for a moment. There’s no obvious off-the-shelf indication of who assigned licenses to accounts in any Microsoft 365 administrative interface.

Azure AD Audit Data

License assignment is an Azure AD activity. It’s therefore possible to find information about these actions in the Azure AD audit log by searching for “Change user license” events. Unfortunately, these events only note that some sort of license assignment occurred. It doesn’t tell you what happened to licenses in terms of additions, removals, or disabling service plans in licenses. For that information, you need to find a matching “Update user” event where the license assignment detail is captured in the Modified Properties tab (Figure 1).

Azure AD license assignment details in the audit log
Figure 1: Azure AD license assignment details in the audit log

Unfortunately, the Get-MgAuditLogDirectoryAudit cmdlet doesn’t report the same level of detail about license assignments, so the Azure AD audit log isn’t a good source for reporting.

License Assignment Records in the Unified Audit Log

Azure AD is a source for the Office 365 (unified) audit log and the information ingested into the Office 365 audit log is more comprehensive albeit formatted in such a way that the data isn’t easy to fetch. However, we can find enough data to write a PowerShell script to create a basic report that contains enough information to at least give administrators some insight into who assigns licenses.

To create the report, the script:

  • Ran the Search-UnifiedAuditLog cmdlet to retrieve audit records for the Change user license and Update User actions.
  • Create separate arrays for both types of event.
  • For each Change user license event, see if there’s a matching Update user record. If one is found, extract the license assignment information from the record.
  • Report what’s been found.

Here’s the script to prove that the concept works:

# Azure AD license assignment script
$StartDate = (Get-Date).AddDays(-90)
$EndDate = (Get-Date).AddDays(1)
Write-Host "Searching for license assignment audit records"
[array]$Records = Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -Formatted -ResultSize 5000 -Operations "Change user license", "Update User" -SessionCommand ReturnLargeSet
If (!($Records)) { Write-Host "No audit records found... exiting... " ; break}

Write-Host ("Processing {0} records" -f $Records.count)
$Records = $Records | Sort-Object {$_.CreationDate -as [datetime]} -Descending
[array]$LicenseUpdates = $Records | Where-Object {$_.Operations -eq "Change user license."}
[array]$UserUpdates = $Records | Where-Object {$_.Operations -eq "Update user."}

$Report = [System.Collections.Generic.List[Object]]::new()

ForEach ($L in $LicenseUpdates) {
  $NewLicenses = $Null; $OldLicenses = $Null; $OldSkuNames = $Null; $NewSkuNames = $Null
  $AuditData = $L.AuditData | ConvertFrom-Json
  $CreationDate = Get-Date($L.CreationDate) -format s
  [array]$Detail = $UserUpdates | Where-Object {$_.CreationDate -eq $CreationDate -and $_.UserIds -eq $L.UserIds}
  If ($Detail) { # Found a user update record
     [int]$i = 0
     $LicenseData = $Detail[0].AuditData | ConvertFrom-Json
     [array]$OldLicenses = $LicenseData.ModifiedProperties | Where {$_.Name -eq 'AssignedLicense'} | Select-Object -ExpandProperty OldValue | Convertfrom-Json
     If ($OldLicenses) {
        [array]$OldSkuNames = $Null
        ForEach ($OSku in $OldLicenses) {
           $OldSkuName = $OldLicenses[$i].Substring(($OldLicenses[$i].IndexOf("=")+1), ($OldLicenses[$i].IndexOf(",")-$OldLicenses[$i].IndexOf("="))-1)
           $OldSkuNames += $OldSkuName
           $i++
         }
      $OldSkuNames = $OldSkuNames -join ", "
    }
    [array]$NewLicenses = $LicenseData.ModifiedProperties | Where {$_.Name -eq 'AssignedLicense'} | Select-Object -ExpandProperty NewValue | Convertfrom-Json
    If ($NewLicenses) {
        $i = 0
        [array]$NewSkuNames = $Null
        ForEach ($N in $NewLicenses) {
           $NewSkuName = $NewLicenses[$i].Substring(($NewLicenses[$i].IndexOf("=")+1), ($NewLicenses[$i].IndexOf(",")-$NewLicenses[$i].IndexOf("="))-1)
           $NewSkuNames += $NewSkuName
           $i++
         }
      $NewSkuNames = $NewSkuNames -join ", "
    }

  } # end if
  # Make sure output properties are strings
  [string]$NewLicensesOutput = $NewLicenses
  [string]$OldLicensesOutput = $OldLicenses
  [string]$NewSkuNamesOutput = $NewSkuNames
  [string]$OldSkuNamesOutput = $OldSkuNames
  $ReportLine   = [PSCustomObject] @{ 
     Operation      = $AuditData.Operation
     Timestamp      = Get-Date($AuditData.CreationTime) -format g
     'Assigned by'  = $AuditData.UserId
     'Assigned to'  = $AuditData.ObjectId 
     'Old SKU'      = $OldSkuNamesOutput
     'New SKU'      = $NewSkuNamesOutput
     'New licenses' = $NewLicensesOutput
     'Old licenses' = $OldLicensesOutput
  }
  $Report.Add($ReportLine)
}

$Report = $Report | Sort-Object {$_.TimeStamp -as [datetime]} 
$Report | Out-GridView

The output is sparse (Figure 2) but I reckon it is sufficient to understand what happens when a license assignment occurred. Events without any license detail appear to be when an administrator removes a license from an account or a service plan from a license.

Azure AD license assignment data extracted from the Office 365 audit log
Figure 2: License assignment data extracted from the Office 365 audit log

I didn’t bother attempting to parse out the license detail. The information returned by Azure AD includes all the licenses assigned to an account, so you’d end up with something like this for an account with three licenses. Splitting the individual licenses and disabled service plans out from this information is an exercise for the reader.

$NewLicenses.Split(',')
[SkuName=POWER_BI_STANDARD
 AccountId=a662313f-14fc-43a2-9a7a-d2e27f4f3478
 SkuId=a403ebcc-fae0-4ca2-8c8c-7a907fd6c235
 DisabledPlans=[]]
[SkuName=ENTERPRISEPACK
 AccountId=a662313f-14fc-43a2-9a7a-d2e27f4f3478
 SkuId=6fd2c87f-b296-42f0-b197-1e91e994b900
 DisabledPlans=[]]
[SkuName=TOPIC_EXPERIENCES
 AccountId=a662313f-14fc-43a2-9a7a-d2e27f4f3478
 SkuId=4016f256-b063-4864-816e-d818aad600c9
 DisabledPlans=[]]

Principle Proved

In any case, the answer to the question is that it’s possible to track and report Azure AD license assignments by using the audit log to extract events relating to these actions and parsing the information in the events. The resulting output might not be pretty (but could be cleaned up), but it’s enough to prove the principal.


Learn how to exploit the data available to Microsoft 365 tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work.

]]>
https://office365itpros.com/2022/10/14/azure-ad-license-assignment-report/feed/ 16 57437
Making Sure Apps Can Run Exchange Online Management Cmdlets https://office365itpros.com/2022/10/13/exchange-online-powershell-app/?utm_source=rss&utm_medium=rss&utm_campaign=exchange-online-powershell-app https://office365itpros.com/2022/10/13/exchange-online-powershell-app/#comments Thu, 13 Oct 2022 01:00:00 +0000 https://office365itpros.com/?p=57424

Using the Exchange.ManageAsApp Permission with Exchange Online PowerShell

Updated 7 December 2022

With the addition of support for managed identities in V3.0 of the Exchange Online management PowerShell module, developers might be more interested in creating Azure Automation runbooks that use the Exchange Online cmdlets to process data like mailboxes. In this discussion, when I refer to a managed identity, I mean a system-assigned managed identity working within an Azure Automation Account. Essentially, a managed identity is a service principal used to access Azure resources that Azure manages automatically. No access is available to the credentials for the managed identity. Like the service principals for other apps, managed identity service principals can hold permissions to allow them access to resources like apps.

As an example, it’s now easy to connect to Exchange Online in a runbook with a command like:

Connect-ExchangeOnline -ManagedIdentity -Organization office365itpros.onmicrosoft.com 

Exchange Online connects using the managed identity owned by the Azure Automation account that’s executing the runbook.

As noted above, before it can do anything interesting after connecting, the managed identity needs permissions. The essential permission for Exchange Online is Exchange.ManageAsApp, which allows an app to run Exchange Online cmdlets as if the app was an administrator account. Service principals for registered apps and managed identities both need this permission to do useful work with Exchange Online cmdlets.

Some Background

In November 2020, Microsoft announced the deprecation of the Outlook REST API. This was part of a wider effort to move developers away from legacy APIs to the Graph. Microsoft also considers Exchange Web Services (EWS) to be a legacy API, but in this instance, the Exchange team focused on the Outlook REST API, which the Graph Outlook Mail API replaces.

At the same time, Microsoft said that they “removed the Exchange app permission from the Azure portal.” The Exchange.ManageAsApp permission is one of the permissions in the Office 365 Exchange Online API. Microsoft’s action didn’t remove the ability to assign the permission to apps in the Azure AD admin center. It just made the process a little harder.

Assigning Exchange.ManageAsApp

To assign the Exchange.ManageAsApp permission to a registered app, select the app in the Registered Apps blade. Go to API permissions to add a permission as normal. When Azure AD displays the range of permissions to select from, click the APIs my organization uses tab, and then type Office 365 Exchange Online into the search box. Azure AD will find the Office 365 Exchange Online API (Figure 1). Note the application identifier shown here. We’ll need this later.

Finding the Office 365 Exchange Online API
Figure 1: Finding the Office 365 Exchange Online API

Now browse the set of permissions in the Office 365 Exchange Online API and select Exchange.ManageAsApp (Figure 2). Make sure that you’ve selected application permissions and click Add permission. When you return to the app details, consent to the assignment, just like you’d do for a Graph API permission.

Adding the Exchange.ManageAsApp permission
Figure 2: Adding the Exchange.ManageAsApp permission

The registered app can now run Exchange Online cmdlets as an administrator. That’s all well and good, but what about a managed identity?

Managed Identities are Different

Unlike registered apps, managed identities show up under the enterprise apps section of the Azure AD admin center. Open enterprise apps and apply a filter to find managed identities (Figure 3).

Selecting managed identities in the Azure AD admin center
Figure 3: Selecting managed identities in the Azure AD admin center

Azure AD lists the Azure automation accounts with managed identities. Select the automation account you want to work with. When you access its permissions, Azure AD tells you that: “The ability to consent to this application is disabled as the app does not require consent. Granting consent only applies to applications requiring permissions to access your resources.” In other words, you can’t assign an API to an automation account, or rather the service principal for the managed identity, through the Azure AD admin center.

Instead, you can do the job with PowerShell using cmdlets from the Microsoft Graph PowerShell SDK. Here’s how:

  • Note the name of the automation account used with the managed identity. In this example, the account name is “ExoAutomationAccount.”
  • Connect to the Graph with the AppRoleAssignment.ReadWrite.All permission.
  • Run the Get-MgServicePrincipal cmdlet to populate a variable with the service principal for the automation account. The filter passed to the cmdlet contains the name of the automation account.
  • Populate a variable with details of the Office 365 Exchange Online enterprise app. Microsoft installs this app for tenants to allow administrative apps to manage Exchange. The app id for the Office 365 Exchange Online app is always 00000002-0000-0ff1-ce00-000000000000.
  • Find the Manage Exchange As Application role in the set held by the Exchange Online application. This role holds the Exchange.ManageAsApp permission, so any app holding the role can use the permission.
  • Create the parameters to assign the role to the managed identity.
  • Use the New-MgServicePrincipalRoleAssignment cmdlet to assign the role.

Connect-MgGraph -Scopes AppRoleAssignment.ReadWrite.All
Select-MgProfile Beta
$ManagedIdentityApp = Get-MgServicePrincipal -Filter "displayName eq 'ExoAutomationAccount'"
$ExoApp = Get-MgServicePrincipal -Filter "AppId eq '00000002-0000-0ff1-ce00-000000000000'"
$AppPermission = $ExoApp.AppRoles | Where-Object {$_.DisplayName -eq "Manage Exchange As Application"}
$AppRoleAssignment = @{
"PrincipalId" = $ManagedIdentityApp.Id
"ResourceId" = $ExoApp.Id
"AppRoleId" = $AppPermission.Id
}
New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $ManagedIdentityApp.Id -BodyParameter $AppRoleAssignment

The new role assignment is effective immediately. If you make a mistake, you can remove the assignment with the Remove-MgServicePrincipalAppRoleAssignment cmdlet. Here’s how:

[Array]$SPPermissions = Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $ManagedIdentityApp.Id
$Role = $ExoApp.AppRoles | Where-Object {$_.DisplayName -eq "Manage Exchange As Application"}
$Assignment = $SpPermissions | Where-Object {$_.AppRoleId -eq $Role.Id}
Remove-MgServicePrincipalAppRoleAssignment -AppRoleAssignmentId $Assignment.Id -ServicePrincipalId $ManagedIdentityApp.Id

Administrator Role

The final step is to make sure that Exchange Online recognizes the automation account which hosts the managed identity as an Exchange administrator. This is done by assigning the Exchange Administrator role to the automation account’s app in the Azure AD admin center. Figure 4 shows how to add the assignment of the Exchange administrator role to the app owned by an automation account.

Making sure that the Managed Identity can act as an Exchange administrator
Figure 4: Making sure that the Managed Identity can act as an Exchange administrator

If you don’t assign the Exchange administrator role to the automation account’s app, you’ll see an error telling you that the role assigned to the app isn’t supported in this scenario when you execute the runbook. For example:

The role assigned to application 415e4ba8-635f-4689-b069-22dea1fcfdb3 isn’t supported in this scenario

Assignment a Small Pain

Perhaps Microsoft under-estimated the continuing need to assign the Exchange.ManageAsApp permission to apps when they made their November 2020 announcement. Although it’s a pain to have to go to PowerShell to assign the permission, it’s something that only needs to happen once, so it’s not too bad. I have other more serious things to moan about inside Microsoft 365.


Learn more about how the Microsoft 365 ecosystem really works on an ongoing basis by subscribing to the Office 365 for IT Pros eBook. Our monthly updates keep subscribers informed about what’s important across the Office 365 ecosystem.

]]>
https://office365itpros.com/2022/10/13/exchange-online-powershell-app/feed/ 10 57424
Microsoft Introduces Authentication Strength for Conditional Access Policies https://office365itpros.com/2022/10/10/authentication-strength-ca-policies/?utm_source=rss&utm_medium=rss&utm_campaign=authentication-strength-ca-policies https://office365itpros.com/2022/10/10/authentication-strength-ca-policies/#comments Mon, 10 Oct 2022 01:00:00 +0000 https://office365itpros.com/?p=57394

Allows CA Policies to Differentiate Between MFA Methods

Building off yesterday’s discussion about Azure AD authentication methods and the discussion at the TEC 2022 conference about the need to do better with MFA, Microsoft released an important improvement to MFA effectiveness this week by enhancing conditional access policies with authentication strength for MFA challenges.

Last year, Microsoft added number matching and additional context to the Authenticator app to help address the issue of MFA fatigue. This is when people mindlessly respond to MFA prompts without registering what they’re doing, something that attackers can exploit to compromise user accounts. However, even if people pay attention to MFA prompts, there’s no doubt that SMS-based challenges deliver weaker protection than other methods.

Expanding Conditional Access Policies

Conditional access (CA) policies operate by applying rules to connections to determine if a user can connect to the requested resource. For example, can they access an Office 365 application like OWA. Combined with authentication policies, CA policies can severely limit the ability of an attacker to compromise user accounts and stop incidents like the OAuth exploit against Exchange Online recently reported by the Microsoft 365 Defender Research Team.

CA policies have been able to insist that accounts use MFA for many years. Up to now, one kind of MFA has been as good as another. Microsoft now differentiates the strength of authentication gained through the available methods (Figure 1).

Azure AD authentication methods (source: Microsoft)
Figure 1: Azure AD authentication methods (source: Microsoft)

SMS is graded at medium level and its usability is high because most people have smartphones. I’m not quite sure why it shows up as medium availability. Microsoft defines this as “an indication of the user being able to use the authentication method, not of the service availability in Azure AD”. Most people I know are very able to use SMS given that it’s a messaging capability in general use since the mid-1990s.

In any case, Microsoft acknowledges the problems with SMS when it responds to an authentication challenge, and they want to encourage people to use more secure methods. In reality, this means that Microsoft wants people to use their Authenticator app, Windows Hello, or FIDO2 key.

Using Authentication Strength in CA Policies

To test the new capability, I created a CA policy to control access to Office 365 and set the policy to grant access based on the authentication strength of the user connection. The default strength is multifactor authentication, meaning any of the traditional methods like SMS will satisfy the condition. I selected the next step up, requiring the use of passwordless MFA (Figure 2).

electing authentication strength in a Conditional Access policy
Figure 2: Selecting authentication strength in a Conditional Access policy

The strongest method is phishing-resistant multifactor authentication. Using a FIDO2 key satisfies this requirement. At TEC 2022, Alex Weinert, Microsoft’s VP for Identity Security, said that the Authenticator app will meet this requirement “soon.”

Note the warning about cross-tenant access settings. These are the Azure AD Direct Connect policies that underpin Teams shared channels. A cross-tenant access policy setting controls if your tenant accepts the multifactor authentication performed by the home tenants of external users who participate in shared channels in your tenant. You should accept those claims to allow external users to continue to collaborate even if they don’t measure up to the authentication strength required for tenant users.

Effect of Authentication Strength

The effectiveness of authentication strength was immediate. Users configured to use the authenticator app continued have access while those who used SMS were allowed to connect and told to select a new authentication method (Figure 3).

A user with SMS-based MFA is invited to upgrade their authentication strength
Figure 3: A user with SMS-based MFA is invited to upgrade their authentication strength

In Figure 3, Azure AD shows that a FIDO2 key is the only available method. This was because the user account had the authenticator method but it needed to be fully configured. Once this was done, the user could connect successfully.

Like any other authentication failure due to a CA policy, details of the failed connection are in the Azure AD sign-in log (Figure 4).

Azure AD audit log failure event due to authentication strength failing a CA policy test
Figure 4: Azure AD audit log failure event due to authentication strength failing a CA policy test

Heading to the Sunny Highlands of Secure MFA

It will be interesting to see how many organizations try to move users away from SMS-based MFA to more secure authentication methods. Just because Microsoft wants this to happen is no reason why it will in the real world. Some customers will love the new capability and rush to embrace it, but I suspect that the real challenge that needs to be fought first is to increase the current percentage of Azure AD accounts protected by MFA from 26.64% to well north of 50%. After killing basic password authentication and pausing for a breath, moving to really secure MFA might be the next hill to climb.


Stay updated with developments across the Microsoft 365 ecosystem by subscribing to the Office 365 for IT Pros eBook. We do the research to make sure that our readers understand the technology.

]]>
https://office365itpros.com/2022/10/10/authentication-strength-ca-policies/feed/ 5 57394
Deep Dive into Entra ID Authentication Methods https://office365itpros.com/2022/10/07/authentication-methods-scripts/?utm_source=rss&utm_medium=rss&utm_campaign=authentication-methods-scripts https://office365itpros.com/2022/10/07/authentication-methods-scripts/#respond Fri, 07 Oct 2022 01:00:00 +0000 https://office365itpros.com/?p=57366

Managing Authentication Methods for an Entra ID User Account with PowerShell

Microsoft product manager Merill Fernando (of Graph X-Ray fame) posted an interesting tweet about a script he wrote to remove all the authentication methods from a user account. Entra ID supports a wide range of authentication methods (Figure 1) ranging from the classic username/password combination to using the Microsoft Authenticator app.

Entra ID Authentication Methods (source: Microsoft).
Figure 1:Entra ID Authentication Methods (source: Microsoft)

At the recent TEC conference, Microsoft VP for Identity Security Alex Weinert made a passionate plea for more Microsoft 365 tenants to secure their accounts with MFA. It’s shocking that only 26.64% of all user accounts use MFA. The figure for accounts holding an administrative role is higher at 34.15%, but that’s still poor. We need to do a better job of moving accounts to the right-hand methods shown in Figure 1.

Scripting Authentication Methods

Merill acknowledges that the script “is not pretty” because the Microsoft Graph does not currently support a way to find the default authentication method for an account. In short, the script attempts to delete an authentication method and if it fails it assumes that the method (like the Microsoft Authenticator app) is the default and leaves it to the last. You can only remove the default authentication method from an account if it’s the last and only method.

In any case, it’s a good script to have around just in case you need to reset an account. I’m not sure how often you’d want to do this, but I guess you might. All contributions to the admin toolbox are gratefully received.

Authentication Methods and the Microsoft Graph PowerShell SDK

Merill’s script uses cmdlets from the Microsoft Graph PowerShell SDK. I like the PowerShell SDK a lot, but sometimes it goes overboard in terms of the number of cmdlets it uses. I think this is due to the way that Microsoft generates the SDK modules and cmdlets from Graph APIs using a process called AutoRest. It’s nice to have a way to generate code automatically, but sometimes human intelligence could do better. Usually, Microsoft generates a new version of the SDK monthly, but sometimes errors creep in and several versions appear in a month (this just happened when versions 1.12 had several minor updates (current version is 1.12.3).

For instance, every authentication method has a separate cmdlet to add (New), update, and remove it from an account. The set of cmdlets used to remove methods in Merill’s script is:

  • Remove-MgUserAuthenticationFido2Method
  • Remove-MgUserAuthenticationEmailMethod
  • Remove-MgUserAuthenticationMicrosoftAuthenticatorMethod
  • Remove-MgUserAuthenticationPhoneMethod
  • Remove-MgUserAuthenticationSoftwareOathMethod
  • Remove-MgUserAuthenticationTemporaryAccessPassMetho
  • Remove-MgUserAuthenticationWindowHelloForBusinessMethod

Seven different cmdlets (you can’t remove the classic password method with one of these cmdlets), or 21 when you add the others for adding and updating methods. It would be simpler all round if the SDK consolidated everything so that we had one cmdlet to add, one to update, and one to remove authentication methods. However, I suspect that because separate API requests exist for each method, we are condemned to work with a confusing mass of cmdlets.

Reporting Authentication Methods

I decided that it would be a good idea to find out what authentication methods are in use. Microsoft makes this information available in the Entra ID admin center, but it’s no fun to simply accept what Microsoft wants to deliver in an admin portal. Instead, if we understand how the technology works, we can adapt it for our own purposes. For instance, I want to focus on tenant accounts rather than including guest accounts in the mix, and I want to extract some information about each authentication method to include in the report.

I already have a script to create an Authentication Method Report for Entra ID and another script to report administrator accounts that aren’t protected with MFA, but there’s always room for another (and this version extracts a little more information about each authentication method, like the phone number used for SMS challenges). Here are the important bits of the code (the full script is available from GitHub):

Write-Host "Finding licensed user accounts"
[array]$Users = Get-MgUser -Filter "assignedLicenses/`$count ne 0 and userType eq 'Member'" -ConsistencyLevel eventual -CountVariable Records -All
If (!($Users)) { Write-Host "No licensed users found... exiting!"; break }

$i = 0
$Report = [System.Collections.Generic.List[Object]]::new()
ForEach ($User in $Users) {
 $i++
 Write-Host ("Processing user {0} {1}/{2}." -f $User.DisplayName, $i, $Users.Count)
 $AuthMethods = Get-MgUserAuthenticationMethod -UserId $User.Id
 ForEach ($AuthMethod in $AuthMethods) {
  $P1 = $Null; $P2 = $Null
  $Method = $AuthMethod.AdditionalProperties['@odata.type']
  Switch ($Method) {
     "#microsoft.graph.passwordAuthenticationMethod" {
       $DisplayMethod = "Password"
       $P1 = "Traditional password"
     }
     "#microsoft.graph.microsoftAuthenticatorAuthenticationMethod" {
       $DisplayMethod = "Authenticator" 
       $P1 = $AuthMethod.AdditionalProperties['displayName']
       $P2 = $AuthMethod.AdditionalProperties['deviceTag'] + " " + $AuthMethod.AdditionalProperties['phoneAppVersion'] 
     }
     "#microsoft.graph.fido2AuthenticationMethod" {
       $DisplayMethod = "Fido 2 Key"
       $P1 = $AuthMethod.AdditionalProperties['displayName']
       $P2 = Get-Date($AuthMethod.AdditionalProperties['createdDateTime']) -format g
     }
     "#microsoft.graph.phoneAuthenticationMethod" {
       $DisplayMethod = "Phone" 
       $P1 = "Number: " + $AuthMethod.AdditionalProperties['phoneNumber']
       $P2 = "Type: " + $AuthMethod.AdditionalProperties['phoneType']
     }
    "#microsoft.graph.emailAuthenticationMethod" {
      $DisplayMethod = "Email"
      $P1 = "Address: " + $AuthMethod.AdditionalProperties['emailAddress']
     }
    "#microsoft.graph.passwordlessMicrosoftAuthenticatorAuthenticationMethod" {
      $DisplayMethod = "Passwordless"
      $P1 = $AuthMethod.AdditionalProperties['displayName']
      $P2 = Get-Date($AuthMethod.AdditionalProperties['createdDateTime']) -format g
    }
  }
  
  $ReportLine   = [PSCustomObject] @{ 
     User   = $User.DisplayName
     Method = $DisplayMethod
     Id     = $AuthMethod.Id
     P1     = $P1
     P2     = $P2 
     UserId = $User.Id }
  $Report.Add($ReportLine)
 } #End ForEach Authentication Method
} #End ForEach User

The code doesn’t include choices for every possible authentication method because examples aren’t available in my tenant. It’s easy to update the code to handle a method like the temporary pass. Figure 2 shows the output generated by the script.

Listing authentication methods found for Entra ID user accounts.
Figure 2: Listing authentication methods found for Entra ID user accounts

One thing that puzzles me is why my account has multiple methods listed for the Microsoft Authenticator app. Both relate to my iPhone 11, but Entra ID might have created the second record after I renamed the phone. It’s something to look at when the time is available.

You can analyze the data to get further insights. For instance:

Write-Host ""
Write-Host "Authentication Methods found"
Write-Host "----------------------------"
Write-Host ""
$Report | Group-Object Method | Sort-Object Count -Descending | Select Name, Count
Authentication Methods found
----------------------------

Name          Count
----          -----
Password         33
Phone            21
Email            11
Authenticator     5
Fido 2 Key        2
Passwordless      1

The other scripts show how to deal with other aspects of reporting that might be important to you, like checking accounts for administrative roles, date of last sign-in, and so on. The nice thing about PowerShell is its flexibility. Cut and paste from different scripts to create a new take and meet your requirements. That’s a great capability to have.


Learn more about how Entra ID and the Microsoft 365 applications really work on an ongoing basis by subscribing to the Office 365 for IT Pros eBook. Our monthly updates keep subscribers informed about what’s important across the Office 365 ecosystem.

]]>
https://office365itpros.com/2022/10/07/authentication-methods-scripts/feed/ 0 57366
Detect Underused Entra ID Accounts (with Expensive Licenses) https://office365itpros.com/2022/09/29/underused-accounts-report/?utm_source=rss&utm_medium=rss&utm_campaign=underused-accounts-report https://office365itpros.com/2022/09/29/underused-accounts-report/#comments Thu, 29 Sep 2022 01:00:00 +0000 https://office365itpros.com/?p=57256

Update the Microsoft 365 Licensing Report Script to Find Underused Accounts

In October 2021, I wrote about how to use the Microsoft Graph PowerShell SDK to create a licensing report for a Microsoft 365 tenant. That report lists the licenses assigned to each user account together with any disabled service plans for those licenses. It’s a valuable piece of information to help tenants manage license costs.

But we can do better. At least, that’s what some readers think. They’d like to know if people use their assigned licenses so that they can remove expensive licenses from accounts that aren’t active. One way to approach the problem is to use the Microsoft 365 User Activity Report script to identify people who haven’t been active in Exchange Online. SharePoint Online, Teams, OneDrive for Business, and Yammer over the last 180 days. The report already includes an assessment of whether an account is in use, so all you need to do is find those who aren’t active and consider removing their licenses.

Another solution to the problem is to update the licensing report script. To do this, I made several changes to the script (the updated version is available from GitHub).

Filtering for Licensed Accounts

The first change is to the filter used with the Get-MgUser cmdlet. The new filter selects only member accounts that have licenses. Previously, I selected all member accounts, but now we’re interested in chasing down underused licensed accounts. Here’s the command I used:

[Array]$Users = Get-MgUser -Filter "assignedLicenses/`$count ne 0 and userType eq 'Member'" -ConsistencyLevel eventual -CountVariable Records -All -Property signInActivity | Sort-Object DisplayName

The filter applied to Get-MgUser finds member accounts with at least one license. The command also retrieves the values of the signInActivity property for each account. This property holds the date and time for an account’s last interactive and non-interactive sign-ins. Here’s what the data for an account looks like:

LastNonInteractiveSignInDateTime  : 27/09/2022 13:04:58
LastNonInteractiveSignInRequestId : bcd2d562-76f0-4d29-a266-942f7ee31a00
LastSignInDateTime                : 11/05/2022 12:19:18
LastSignInRequestId               : 3f691116-5e0a-4c4c-a3a9-aecb3ae99800
AdditionalProperties              : {}

The last non-interactive sign-in might be something like a synchronization operation performed by the OneDrive sync client or a sign-in using an access token for the user account to another Microsoft 365 app. I’m not too interested in these sign-in activities as I want to know about licensed accounts that aren’t taking full advantage of their expensive licenses. Hence, we focus on the timestamp for the last interactive sign-in.

Update: Microsoft now supports a timestamp for the last successful sign in for Entra ID accounts. The LastSignInDateTime property can capture an unsuccessful sign-in, so using the new lastSuccessfulSignInDateTime property is a better choice in most situations. However, Entra ID only captures data for the property from December 1, 2023.

Calculating How Long Since an Account Sign-in

To detect an underused account, we need to define how to recognize such an account. To keep things simple, I define an underused account as being more that hasn’t signed in interactively for over 60 days. An account in this category costs $23/month if it holds an Office 365 E3 license while one assigned an E5 license costs $38/month. And that’s not taking any add-on licenses into account. At $30/month, we’ve already paid $60 for an underused account when it matches our criterion.

The script checks to see if any Entra ID sign-in information is available for the account (i.e., the account has signed in at least once). If it does, we extract the timestamp for the last interactive sign-in and compute how many days it is since that time. If not, we mark the account appropriately.

# Calculate how long it's been since someone signed in
  If ([string]::IsNullOrWhiteSpace($User.SignInActivity.LastSignInDateTime) -eq $False) {
    [datetime]$LastSignInDate = $User.SignInActivity.LastSignInDateTime
    $DaysSinceLastSignIn = ($CreationDate - $LastSignInDate).Days
    $LastAccess = Get-Date($User.SignInActivity.LastSignInDateTime) -format g
    If ($DaysSinceLastSignIn -gt 60) { $UnusedAccountWarning = ("Account unused for {0} days - check!" -f $DaysSinceLastSignIn) }
  }
  Else {
    $DaysSinceLastSignIn = "Unknown"
    $UnusedAccountWarning = ("Unknown last sign-in for account")
    $LastAccess = "Unknown"
  }

Note that it can take a couple of minutes before Entra ID updates the last interactive timestamp for an account. This is likely due to caching and the need to preserve service resources.

Reporting Underused Accounts

The last change is to the output routine where the script now reports the percentage of underused accounts that it finds. Obviously, it’s not ideal if this number is more than a few percent.

I usually pipe the output of reports to the Out-GridView cmdlet to check the data. Figure 1 shows the output from my tenant. Several underused accounts are identified, which is what I expect given the testing and non-production usage pattern within the tenant. Another advantage of Out-GridView is that it’s easy to sort the information to focus in on problem items as seen here.

Highlighting underused accounts with licenses
Figure 1: Highlighting underused accounts with licenses

Customizing the Output

Seeing that the script is PowerShell, it’s easy to adjust the code to meet the requirements of an organization. Some, for instance, might have a higher tolerance level before they consider an account underutilized and some might be more restrictive. Some might like to split the report up into departments and send the underused accounts found for each department to its manager for review. It’s PowerShell, so go crazy and make the data work for you.


Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2022/09/29/underused-accounts-report/feed/ 7 57256
Checking Audit Logs for Consent Permission Grants https://office365itpros.com/2022/09/23/consent-permission-grants/?utm_source=rss&utm_medium=rss&utm_campaign=consent-permission-grants https://office365itpros.com/2022/09/23/consent-permission-grants/#comments Fri, 23 Sep 2022 01:00:00 +0000 https://office365itpros.com/?p=56999

Looking for Consent Permission Grants for High-Priority Permissions

This week, I wrote about applications with high-priority permissions, defined as Microsoft Graph and other permissions that attackers could exploit to access, update, and exfiltrate data. For example, if an app holds the Mail.ReadWrite application permission, it can read and write all mailboxes in the tenant. The script generates a report that’s posted to a Teams channel to allow administrators to review the applications holding the specified permissions.

Illicit Consent Permission Grants

If you examine the information returned by Entra ID (Azure AD) for an app, you’ll see that although we can figure out the permissions assigned to the app, but Entra ID doesn’t tell you who consented to the permission and when they consented. This could be an important sign that an attacker has managed to achieve an illicit consent grant, defined as occurring when:

the attacker creates an Azure-registered application that requests access to data such as contact information, email, or documents. The attacker then tricks an end user into granting that application consent to access their data either through a phishing attack.”

An illicit consent grant for access to an individual user’s data is bad. One that results in an administrator granting consent to an application for a high-priority permission that the attacker can subsequently leverage could be catastrophic.

Checking Audit Records for Consent Permission Grants

One way to discover the last time when an adjustment occurred to app permissions is to search the Entra ID audit logs for records for the Consent to application action, filtering the set to find those matching the service principal identifier for an app. I didn’t do this in the script described in the article, Let’s explore how a consent check might work.

First, let’s assume that the script has run and detected an application holding some high-priority permissions. Its properties might look like this:

DisplayName        : MalwareExample
ServicePrincipalId : 6df52e04-63b2-4007-af69-40430ee5a1d1
Publisher          : Office 365 for IT Pros
Permissions        : Mail.ReadWrite, Mail.Send
SPType             : Application
CreatedDate        : 12/09/2022 22:41
RecentApp          : True

To scan the Entra ID audit logs for any consent granted records for this app, we could use a command like this to see if an audit record exists. The search goes back 30 days.

[array]$AuditRecords = Get-MgAuditLogDirectoryAudit -Filter "activityDisplayName eq 'Consent to application' AND result eq 'Success' AND targetResources/any(tr:tr/id eq '$($app.serviceprincipalid)')" -top 1

If an audit record is found, it will look like this:

ActivityDateTime     : 12/09/2022 21:42:49
ActivityDisplayName  : Consent to application
AdditionalDetails    : {User-Agent, AppId}
Category             : ApplicationManagement
CorrelationId        : 005cc13f-9fd5-4b95-89ce-19802a7a785f
Id                   : Directory_005cc13f-9fd5-4b95-89ce-19802a7a785f_72CWK_111329857
InitiatedBy          : Microsoft.Graph.PowerShell.Models.MicrosoftGraphAuditActivityInitiator1
LoggedByService      : Core Directory
OperationType        : Assign
Result               : success
ResultReason         :
TargetResources      : {6df52e04-63b2-4007-af69-40430ee5a1d1}
UserAgent            :
AdditionalProperties : {}

We can see that the consent was granted a minute after the creation date for the app. That could be a suspicious sign, but it might also be the result of granting permissions immediately after creating an app.

The InitiatedBy property is a complex object. Parsing it out, we can eventually discover who granted consent.

$AuditRecords.InitiatedBy.user.UserPrincipalName
Admin@Office365itpros.com

Unfortunately, that’s about all we can find from the audit log using the Get-MgAuditLogDirectoryAudit cmdlet. Some additional information is available in the Entra ID admin center (Figure 1).

Audit Log detail for a consent permission grant
Figure 1: Audit Log detail for a consent permission grant

Entra ID sends its audit data to the Microsoft 365 audit log and you can also search there using a command like this:

[array]$records = Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-90) -EndDate (Get-Date).AddDays(1) -Formatted -ResultSize 5000 -Operations "Consent to application"

The audit log stores information for 180 days (for accounts with Purview audit standard licenses) or 365 days (accounts with Purview audit advanced licenses). If you don’t find audit records in Entra ID, checking the audit log can deliver a result. The downside of using the audit log is that it’s likely going to be slower to find any records because there’s more data to search and a specific search filter isn’t available as it when using Get-MgAuditLogDirectoryAudit to check Entra ID audit records.

The audit data is useful information that could help identify any problematic consent grants that might turn out to be illicit, but the data are only effective if people pay attention to permissions granted to apps, especially the high-profile permissions.

Updating Code to Check for Consent Permission Grants

Now that we know how to query the Entra ID audit logs to find records for consent grants for an app, it would be easy to update the script to include the check in the code. The hardest part is probably the update to include the audit information in the HTML body of the message to post to Teams. I’ll leave the script update as an exercise for the reader!

The key point is that registered apps shouldn’t be left unsupervised. Whatever method you choose to check these apps, make sure it happens regularly and that someone is responsible for reviewing the reports and other outputs to detect any problems.


Learn more about how the Office 365 applications really work on an ongoing basis by subscribing to the Office 365 for IT Pros eBook. Our monthly updates keep subscribers informed about what’s important across the Office 365 ecosystem.

]]>
https://office365itpros.com/2022/09/23/consent-permission-grants/feed/ 8 56999
Updating Extension Attributes for Entra ID Registered Devices with the Microsoft Graph PowerShell SDK https://office365itpros.com/2022/09/06/entra-id-registered-devices/?utm_source=rss&utm_medium=rss&utm_campaign=entra-id-registered-devices https://office365itpros.com/2022/09/06/entra-id-registered-devices/#comments Tue, 06 Sep 2022 01:00:00 +0000 https://office365itpros.com/?p=56830

Registered Devices and Entra ID (Azure AD)

Updated 8 September 2023

According to Microsoft, the goal for Entra ID registered devices (workplace joined devices) is “to provide your users with support for bring your own device (BYOD) or mobile device scenarios. In these scenarios, a user can access your organization’s resources using a personal device.” Personally, I haven’t paid registered devices much attention over the years. Other topics occupied my time, and apart from going through the joining process to allow the organization to manage the device, ignored their existence.

Devices occupy their own area in the Entra ID admin center (Figure 1). The details displayed for each device are those gathered when the device registers with Entra ID. This accounts for some of the funky default device names generated by Windows. Entra ID doesn’t update devices records with details of O/S upgrades, so many of my devices appear to run Windows 10 when they’ve long since acquired Windows 11. The Entra ID admin center concentrates mainly on organizing device identities, which is what you’d expect from a directory.

Entra ID registered devices in the Entra ID admin center
Figure 1: Entra ID registered devices in the Entra ID admin center

Setting Extension Attributes for Entra ID Registered Devices

In any case, I thought that there might be some way to exploit the registered devices in Entra ID, similar in concept to the way that Exchange administrators often use custom attributes to mark mailboxes (here’s an example of using custom attributes to drive the membership of dynamic distribution groups).

This led me to the Graph API for Devices and a note in that page about using extension attributes. Organizations commonly use Entra ID extension attributes to store extra information about user objects. They’re also available for device objects, and it’s convenient to be able to use the extension attributes to store information that help administrators know who uses a device. Fifteen extension attributes (ExtensionAttribute1 through ExtensionAttribute15) are available.

It seemed to make sense to use the extension attributes to make the entries for registered devices more useful. I decided to populate six of the extension attributes with information about the user who registered a device. It’s not always the case that the registered owner is still the person who uses a device, but there’s a high probability that it is, especially in BYOD scenarios.

To test the theory, I wrote a script using the Microsoft Graph PowerShell SDK to:

  • Find all registered devices with the Get-MgDevice cmdlet.
  • For each device, extract the identifier for the user’s account. This is stored in an odd manner in the device record (at least, Microsoft could make it much simpler to find and use the identifier).
  • Use the Get-MgUser cmdlet to check the identifier against Entra ID and retrieve user details if a match is successful. The lookup fails if the user is no longer in Entra ID or their account belongs to another tenant (Entra ID can register devices for guest users).
  • Run Update-MgDevice to populate the extension attributes when we have an account match.

Connect-MgGraph -Scopes Directory.ReadWrite.All, Device.Read.All
[array]$Devices = Get-MgDevice -All

ForEach ($Device in $Devices) {
  If ($Device.PhysicalIds.count -gt 0) {
    Foreach ($X in $Device.PhysicalIds) { If ($X.SubString(0,10) -eq "[USER-GID]") { $UserGuid = $X } }
    $UserId = $UserGuid.substring(11,36)
    If ($UserId) { #We found a user identifier - try to resolve it against Entra ID
       [array]$User = Get-MgUser -UserId $UserId -ErrorAction SilentlyContinue }
       If ($User) { # Found a user in Entra ID
         Write-Host ("Device {0} owned by {1}" -f $Device.DisplayName, $User.DisplayName)
         $Attributes = @{
          "ExtensionAttributes" = @{
            "extensionAttribute1" = $User.DisplayName
            "extensionAttribute2" = $User.UserPrincipalName
            "extensionAttribute3" = $User.MobilePhone
            "extensionAttribute4" = $User.Department 
            "extensionAttribute5" = $User.City
            "extensionAttribute6" = $User.Country }
         }  | ConvertTo-Json
      Update-MgBetaDevice -DeviceId $Device.Id -BodyParameter $Attributes 
      }
       Else { Write-Host ("Device {0} owned by unknown user {1}" -f $Device.DisplayName, $UserId ) }
  } # End If Device PhysicalsId
} #End Foreach

Using Extension Attributes for Entra ID Registered Devices

After populating the device attributes, their values are available through the Entra ID admin center (Figure 2).

Populated extension attributes for an Entra ID registered device
Figure 2: Populated extension attributes for an Entra ID registered device

Even better, it’s easy to apply a filter against the extension attributes to find a subset of devices. In this example, I find all devices where the value of extensionAttribute6 is “Ireland.”

[array]$IrelandDevices = Get-MgDevice -Filter "extensionAttributes/extensionAttribute6 eq 'Ireland'" -CountVariable IrelandCount -ConsistencyLevel eventual

Custom Attributes for All

Even those running device management software like Intune might find value in being able to assign custom values to registered devices through PowerShell. The possibilities are endless. At least, that’s what I’ve heard.


Learn about exploiting Entra ID (Azure AD) and PowerShell by subscribing to the Office 365 for IT Pros eBook. Use our experience to understand what’s important and how best to protect your tenant.

]]>
https://office365itpros.com/2022/09/06/entra-id-registered-devices/feed/ 43 56830
Microsoft Sets Out to Block Unmanaged Azure AD Guest Accounts https://office365itpros.com/2022/09/05/azure-ad-unmanaged-accounts/?utm_source=rss&utm_medium=rss&utm_campaign=azure-ad-unmanaged-accounts https://office365itpros.com/2022/09/05/azure-ad-unmanaged-accounts/#comments Mon, 05 Sep 2022 01:00:00 +0000 https://office365itpros.com/?p=56856

Eliminating the Need for Azure AD Unmanaged Accounts

The language used in Microsoft’s September 2 announcement that tenants should “Say goodbye to unmanaged Azure AD accounts for B2B collaboration” created some confusion. The problem is that Microsoft never quite explained what unmanaged (or “viral”) Azure AD accounts are in their post. Some folks might be confused about what their initiative is all about.

The blog refers to people who used self-service sign-up to create Azure AD guest accounts “by validating ownership of their work email address when their domain is not verified in Azure AD.” This causes a problem because “users would create accounts in a tenant not managed by the IT department of their organization.”

Managed and Unmanaged Tenants

Azure AD is composed of many tenant directories. A managed tenant is one that has a global administrator (the manager) created to support a service like Microsoft 365 or Dynamics 365. All Microsoft 365 organizations have a managed Azure AD tenant.

When Microsoft introduced Azure B2B Collaboration in 2016, they created the ability of external users to sign up to be guest members in managed tenants using the email address of the external users to validate their existence. A guest account exists in the directory of the tenant where the guest accesses some resources, like documents in SharePoint Online or a team. Azure AD also attempts to link the guest account to the user’s real account in the directory of their source tenant. Some of these guests came from email domains that didn’t use Azure AD, and Microsoft uses unmanaged tenants based on the users’ email domains to store their accounts.

Let’s take a practical example. I add the email address of an external user to the membership of a Microsoft 365 group or team. This action causes Azure AD to create a guest account and generate an invitation to the external user to redeem the invitation and confirm their email address. The user receives the invitation by email and accepts it. Azure AD is now happy that the user is real and marks the guest account as having accepted the invitation. The external user can then use their guest account to participate in the group or guest and all is well.

This scheme works until the organization that owns the email domain decides to use Azure AD because they want to use a service like Microsoft 365. At that point, the organization must take over the unmanaged tenant and its unmanaged accounts. This process is well-known and documented, but it interferes with the smooth onboarding of organizations into services.

No More Azure AD Unmanaged Accounts

What’s happening now is that Microsoft is removing the need to create unmanaged accounts and tenants by removing the ability of external users to validate using email addresses. Instead, if an external user doesn’t come from:

  • Another Azure AD tenant.
  • A directory that federates with Azure AD, like Google.
  • Consumer Microsoft Services (MSA).

Microsoft will either use a one-time password (OTP) to validate their email address or require the user to create a consumer account using their email address. Microsoft stresses that unmanaged Azure AD accounts used by guest accounts already present in customer organizations will continue to work. The new redemption process (Figure 1) only applies to new guest accounts.

Azure AD B2B Collaboration invitation redemption flow (source: Microsoft)

Azure AD unmanaged accounts
Figure 1: Azure AD B2B Collaboration invitation redemption flow (source: Microsoft)

Cleaning up Azure AD Unmanaged Accounts

Microsoft’s post indicates that some tenants have thousands of unmanaged Azure AD accounts in their directories. As noted above, these accounts will continue to work, but if you want to clean them up (essentially to force people with unmanaged accounts to revalidate to Azure AD), Microsoft has a set of tools to help.

After reading the documentation, I tested the procedure on my tenant. Some PowerShell configuration is necessary. You must install:

In addition, before you run the Get-MsIdUnmanagedExternalUser cmdlet to find unmanaged (viral) accounts, you must import the msidentity.microsoft.graph module. Here’s what I did to run the code in my tenant:. As you can see, three accounts were identified.

Import-Module msidentitytools,microsoft.graph	

Connect-MgGraph -Scope User.Read.All
Select-MgProfile Beta
Get-MsIdUnmanagedExternalUser

Id                                   DisplayName                    Mail                   UserPrincipalName
--                                   -----------                    ----                   -----------------
39cac377-02cc-4919-ad44-d9f1a7cc5eae Glen Weaver                    gweaver@gwdevelop.com gweaver_gedevelop.com#EX...
3e97b38b-6031-4501-bdba-4d05fff67ec6 Michael Conroy                 michaeld@conroycons.com  michaeld_conroycons.com#EXT...
94687a75-7a3e-4001-b15e-7fc91cc7ac4e Norbert Platz                  n.platz@devs.de n.platz@devs.de#EX...

As you might expect, the three accounts belonged to email domains that didn’t use Azure AD. I had created the accounts in September 2016, soon after the introduction of Azure B2B Collaboration support for what was then Office 365 Groups (now Microsoft 365 Groups). The ExternalUserState property of each account was set to Accepted, meaning that the users had redeemed their invitation to be a guest user in my tenant.

To force the unmanaged accounts to go through Microsoft’s new redemption process, you run Get-MsIdUnmanagedExternalUser again and pipe the results to Reset-MsIdExternalUser. This action causes Azure AD to reissue the invitation to the email address for each account and reset the ExternalUserState property to PendingAcceptance. 

Get-MsIdUnmanagedExternalUser | Reset-MsIdExternalUser

Id                                   InviteRedeemUrl
--                                   ---------------
d325b1e4-c6d8-4d24-b384-05bd145abf6f https://login.microsoftonline.com/redeem?rd=https%3a%2f%2finvitations.microsoft...
bb1112fc-c0b1-4b0e-a43b-dfd0914c8345 https://login.microsoftonline.com/redeem?rd=https%3a%2f%2finvitations.microsoft...

It’s up to each external user to decide if they wish to redeem their invitation, and if they do, they must use the new redemption process. If a guest chooses not to redeem their invitation, you can consider removing their account after a reasonable period.

Not Too Much to Complain About

The advent of cross-tenant access policies means that guest accounts and Azure B2B Collaboration is less important than they were once. We all learn with experience, and it seems that Microsoft has learned that unmanaged tenants and unmanaged (or viral) accounts are not as good an idea as they seemed to be in 2016.

I can’t see a downside in what Microsoft is doing. They allow tenants to leave unmanaged guest accounts alone if they want to, and tools are available if an organization decides to move these accounts to a managed status. Apart from some minor disruption for those being asked to go through the invitation redemption process again, there’s not much to complain about.


So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across Office 365. Subscribe to the Office 365 for IT Pros eBook to receive monthly insights into what happens, why it happens, and what new features and capabilities mean for your tenant.

]]>
https://office365itpros.com/2022/09/05/azure-ad-unmanaged-accounts/feed/ 1 56856
Use Graph Explorer to Sign into Microsoft 365 Tenants as a Guest https://office365itpros.com/2022/08/29/graph-explorer-guest-access/?utm_source=rss&utm_medium=rss&utm_campaign=graph-explorer-guest-access https://office365itpros.com/2022/08/29/graph-explorer-guest-access/#comments Mon, 29 Aug 2022 01:00:00 +0000 https://office365itpros.com/?p=56726

And How to Block this Access

I recently noted that the Graph Explorer utility has some useful new features. Well, here’s another feature to consider that might be useful some day: you can sign into the Graph Explorer as a guest user in a tenant.

When you start Graph Explorer, you can run sample commands that operate against dummy data or sign into a tenant to use real data. Up to now, you sign in with a tenant account to access the data in that tenant, just like you’d sign into OWA or the Microsoft 365 admin center.

Signing into Graph Explorer as a Guest

However, if you add the name of a tenant where you have a guest account to the Graph Explorer URI, the tool will sign into that tenant. For example, the “normal” URI for the Graph Explorer is:

https://developer.microsoft.com/en-us/graph/graph-explorer

To sign into another tenant with a guest account, add the service domain or the tenant identifier. For example:

https://developer.microsoft.com/en-us/graph/graph-explorer?tenant=o365maestro.onmicrosoft.com

or

https://developer.microsoft.com/en-us/graph/graph-explorer?tenant=163e0495-8f47-46df-a823-685b61cc8d89

If you know a tenant’s domain, but not its service domain or tenant identifier, you can use this website to look up the tenant identifier.

When you sign in using the Graph Explorer, you can use your regular account. Azure AD will go through the normal sign-in process for the target tenant, including enforcement of multi-factor authentication as dictated by that tenant, and if your account is recognized as a guest, you’ll connect. To validate that you’re using a guest account, run the sample “my profile” query and you’ll see that the user principal name shown for the account belongs to a guest. For instance, Figure 1 shows that Chris.Bishop@office365itpros.com is connected to the O365Maestro tenant using the guest account Chris.Bishop_office365itpros.com#EXT#@o365maestro.onmicrosoft.com.

Connecting to the Graph Explorer with a guest account
Figure 1: Connecting to the Graph Explorer with a guest account

Guest accounts are limited in what they can do when connected to a tenant. In many cases, attempts to run queries such as listing all users or all groups in the tenant result in:

    "error": {
        "code": "Authorization_RequestDenied",
        "message": "Insufficient privileges to complete the operation.",

Which is a nice way of saying that although your account is a valued guest in the organization, that’s not an invitation to poke around and look for information. Other requests, such as anything to do with a mailbox, see errors like:

"error": {
        "code": "MailboxNotEnabledForRESTAPI",
        "message": "The mailbox is either inactive, soft-deleted, or is hosted on-premise.",

Guest accounts do have mailboxes, but they are special cloud-only mailboxes created and managed by the Microsoft 365 substrate to store compliance records like those captured for Teams chats and channel conversations.

Blocking Access

Although the Graph Explorer enables limited access to tenant data for guest accounts, you might not like the idea of guests connecting like this. Three solutions exist. First, you can block user access to the Graph Explorer Azure AD app. Open the Azure AD admin center, go to enterprise applications, and search for Graph Explorer. Open the app’s properties and update the Enabled for users to sign in setting to Off (Figure 2) and save the new app settings.

Block the Graph Explorer app to stop people using the app
Figure 2: Block the app to stop people using the Graph Explorer

This is a crude but effective mechanism that blocks all access to the Graph Explorer, including to administrators. Anyone attempting to access the app encounters the error:

AADSTS7000112: Application 'de8bc8b5-d9f9-48b1-a8ad-b748da725064'(Graph Explorer) is disabled.

Second, you can assign specific users and groups to the app. The Assignment required slider is visible in Figure 2. When set to Yes, administrators must explicitly allow users and groups access to the app (through the Users and groups menu option on the left-hand side). If a user who doesn’t have an assignment attempts to use the app, they get the following error when they try to sign in:

AADSTS50105: Your administrator has configured the application Graph Explorer ('de8bc8b5-d9f9-48b1-a8ad-b748da725064') to block users unless they are specifically granted ('assigned') access to the application. The signed in user 'Chris.Bishop@office365itpros.com' is blocked because they are not a direct member of a group with access, nor had access directly assigned by an administrator. Please contact your administrator to assign access to this application.

Conditional Access Block

Conditional access (CA) policies offer the most precise way to control access. A simple CA policy to block access to the Graph Explorer app for guest users (Figure 3) does the trick.

A Conditional Access policy to block guest access to the Graph Explorer
Figure 3: A Conditional Access policy to block guest access to the Graph Explorer

Tenant users can continue to sign in and use the Graph Explorer, but guests run into the CA block (Figure 4).

The CA policy blocks a guest from signing into the Graph Explorer
Figure 4: The CA policy blocks a guest from signing into the Graph Explorer

We can also confirm the effectiveness of the CA policy by searching the Azure AD audit logs to look for sign ins to the Graph Explorer app. Here’s how to do it with the Microsoft Graph PowerShell SDK:

Connect-MgGraph -Scopes "AuditLog.Read.All","Directory.Read.All"
Select-MgProfile Beta
[array]$Records = Get-MgAuditLogSignIn -Filter "(appdisplayname eq 'Graph Explorer' and signInEventTypes/any(t: t eq 'interactiveUser'))" -Sort "createdDateTime DESC"

$Records | Format-Table createddatetime, userprincipalname, ConditionalAccessStatus

CreatedDateTime     UserPrincipalName                          ConditionalAccessStatus
---------------     -----------------                          -----------------------
28/08/2022 15:25:24 chris.bishop@office365itpros.com           success
28/08/2022 15:15:52 tony.redmond@office365itpros.com           success
28/08/2022 15:01:56 warren.gatland@o365maestro.onmicrosoft.com failure

Unknown User Case for Guest Access

After noodling on this subject for a few days, I still haven’t come up with a good use case why the Graph Explorer supports guest access. I suppose it’s a good thing and I’m sure that an Azure AD development engineer put forward an excellent case to justify the feature, but I know many will disagree. Now if I could use this access to update the photo for my guest account (possible with Azure AD PowerShell but not with the Graph), I’d have a good use case!


Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2022/08/29/graph-explorer-guest-access/feed/ 4 56726
The Odd Azure AD Selected Visibility is Not Allowed Problem https://office365itpros.com/2022/08/19/azure-ad-admin-center-issues/?utm_source=rss&utm_medium=rss&utm_campaign=azure-ad-admin-center-issues https://office365itpros.com/2022/08/19/azure-ad-admin-center-issues/#comments Fri, 19 Aug 2022 01:00:00 +0000 https://office365itpros.com/?p=56571

Azure AD Admin Center Doesn’t Respect Sensitivity Label Settings

Last June, I tested the preview of nested dynamic Azure AD groups and encountered an odd “Per label policy, the selected visibility is not allowed” error when attempting to create new groups in the Azure AD admin center. Pressure of time forced me to ignore the problem and create the groups I wanted with PowerShell, but time allowed me this week to return to the problem.

It didn’t take long to reproduce the problem and track down the root cause (Figure 1).

Failed to create group because "the selected visibility is not allowed"
Figure 1: Failed to create group because “the selected visibility is not allowed”

Visibility Set to Private for New Microsoft 365 Groups

I used the Graph X-Ray tool to look at the PowerShell generated by the Azure AD admin center when it adds new Microsoft 365 groups. Here’s what Graph X-ray reported:

$params = @{
	DisplayName = "Viva Topics Management"
	MailEnabled = $true
	SecurityEnabled = $true
	GroupTypes = @(
		"Unified"
	)
	Description = "People who manage Viva Topics"
	MailNickname = "VivaTopicsManagement"
	AssignedLabels = @(
		@{
			LabelId = "e42fd42e-7240-4df0-9d8f-d14658bcf7ce"
		}
	)
	Visibility = "private" }}

I copied the command and ran it interactively and saw this error:

New-MgGroup -BodyParameter $params
New-MgGroup : Property visibility is not compliant with the assigned label.
At line:18 char:1
+ New-MgGroup -BodyParameter $params
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: ({ body = Micros...ftGraphGroup1 }:<>f__AnonymousType1`1) [New-MgGroup
   _Create1], RestException`1
    + FullyQualifiedErrorId : Request_BadRequest,Microsoft.Graph.PowerShell.Cmdlets.NewMgGroup_Create1

Changing the command to set the visibility property in the parameters to “Public” allowed the command to run successfully and create the group. This is what I expected because the container management settings for the sensitivity label chosen for the new group sets its visibility to Public.

The root cause is that the command generated by the Azure AD admin center sets the access type for the new group incorrectly. Instead of reading the group’s access type (visibility) from the sensitivity label, the command uses “Private” for the value. This means that the command works for any group created with a sensitivity label that sets the access type to Private but fails for public groups.

The Azure AD admin center UI doesn’t include a field to allow the visibility to be selected for a new group, so some overhaul of the UI is needed to display the visibility inherited when a sensitivity label is selected. In addition, Microsoft’s documentation for creating a new group in the Azure AD admin center doesn’t mention visibility at all, so there’s no hope in interpreting the error message.

Inconsistent Microsoft 365 Group Management

I’m unsure of how many new Microsoft 365 groups are created in the Azure AD admin center. My feeling is that most administrators create new groups through the Microsoft 365 admin center or a workload-specific portal like the Teams admin center or SharePoint Online admin center, or even a client like OWA or Teams. All these interfaces (and PowerShell) respect the container management controls imposed by sensitivity labels. If the Azure AD admin center was heavily used, I’m sure Microsoft would have heard about the problem before I reported it on August 15.

In any case, this is not the only example of inconsistency between the Azure AD admin center and the workload portals. Take the security enabled property for a group. This is set to $True by the Azure AD admin center and $False by the Microsoft 365 admin center. That doesn’t sound too serious, but it means that groups not created in the Azure AD admin center can’t be used for purposes like group-based license management. This is where you manage license assignments by allocating them to members of a group. It’s a convenient way to manage licenses (Figure 2).

Group-based license management in the Azure AD admin center
Figure 2: Group-based license management in the Azure AD admin center

The security enabled property isn’t exposed in the Azure AD admin center UI, so if you want to update a group to make it available for group-based license management, you need to use PowerShell. The steps are simple using the group management cmdlets from the Microsoft Graph PowerShell SDK. The first command finds the group to use. The second updates its SecurityEnabled property.

$Group = (Get-MgGroup -Filter "DisplayName eq 'HR Working Group'")
Update-MgGroup -GroupId $Group.Id -SecurityEnabled:$True

After the Update-MgGroup cmdlet runs, you should be able to use the group for group-based license management.

Small But Irritating Issues

Neither of the issues described here are earthshattering. Both can be worked around by competent tenant administrators who understand how Microsoft 365 works. The problem is that issues like this cause grief to inexperienced administrators and complicate the learning curve for cloud services. That’s a great pity.


Learn more about how the Office 365 applications really work on an ongoing basis by subscribing to the Office 365 for IT Pros eBook. Our monthly updates keep subscribers informed about what’s important across the Office 365 ecosystem.

]]>
https://office365itpros.com/2022/08/19/azure-ad-admin-center-issues/feed/ 1 56571
Microsoft Announces New Yammer Administrator Role https://office365itpros.com/2022/08/12/yammer-administrator-role/?utm_source=rss&utm_medium=rss&utm_campaign=yammer-administrator-role https://office365itpros.com/2022/08/12/yammer-administrator-role/#respond Fri, 12 Aug 2022 01:00:00 +0000 https://office365itpros.com/?p=56486

Now Available in Azure AD

On August 9, Microsoft announced the addition of the Yammer administrator role to Azure AD. Like the other workload-specific administrator roles such as Exchange administrator, Teams administrator, and SharePoint administrator, the new role helps to avoid a proliferation of user accounts assigned the all-powerful Global administrator role. Microsoft says that a user assigned the “Yammer admin role can manage all aspects of the Yammer service, long with modifying Yammer communities without becoming an owner or member of that community.” That’s a long-winded way of saying that accounts holding the Yammer administrator role can manage Yammer.

Interestingly, the Yammer administrator role works for all kinds of Yammer networks. When I first read about the role, I assumed that it was part of Native mode Yammer networks, which is where Yammer embraces Azure AD and the Microsoft 365 ecosystem fully, instead of the older non-native mode networks. Because the new role supports non-native networks, Microsoft can use it in their external Yammer networks such as the one used by the Microsoft Information Protection team for customer communications.

Assigning Yammer Administrators

The easiest way to assign an Azure AD administrative role is through the Azure AD admin center (Figure 1).

Yammer administrator assignments in the Azure AD admin center
Figure 1: Yammer administrator assignments in the Azure AD admin center

You can also assign Azure AD administrative roles with PowerShell, as described in this article. The identifier for the Yammer administrator role is efc777ab-ffb8-4c71-809c-7d1ff399ff54.

Auditing Administrator Assignments

Assigning users to the Yammer administrator role creates an audit event. To find the events in the audit log, you can search using the Microsoft Purview compliance portal or with PowerShell. For example:

$StartDate = (Get-Date).AddDays(-7); $EndDate = (Get-Date).AddDays(1)
[array]$Records = Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -Formatted -ResultSize 5000 -Operations "add member to role"
If ($Records) {
 ForEach ($Record in $Records) {
   $AuditData = $Record.Auditdata  | convertfrom-json
   [string]$UserName = $AuditData.ObjectId
   [string]$AdminRole = $AuditData.ModifiedProperties[1].NewValue
   If ($AdminRole -eq "Yammer Administrator") {
     Write-Host ("User {0} added to role {1} at {2}" -f $UserName, $AdminRole, $Record.CreationDate ) }
 }}
User Sean.Landy@office365itpros.com added to role Yammer Administrator at 11/08/2022 13:08:01
User Ben.James@Office365itpros.com added to role Yammer Administrator at 10/08/2022 18:02:35

Using the Yammer Administrator Role

It can take a couple of hours for Yammer to synchronize details of new role assignments from Azure AD. When this happens, the role assignees will see the Edit network admin settings option in their Settings menu (Figure 2). This brings up the normal Yammer configuration page to allow the user to amend network settings.

The Yammer administrator option shows up in the Yammer client UI
Figure 2: The Yammer administrator option shows up in the Yammer client UI

A clear distinction exists between accounts holding the Yammer administrator role (treated as verified admins) and those which gain administrative permission for Yammer through another route, like being a tenant administrator (Figure 3). The differences between the different kinds of Yammer admins are explained in this article. Existing holders of the Yammer verified admin role do not automatically receive the Azure AD role. If you want the two sets to match up, you’ll need to assign existing verified admins to the Azure AD Yammer administrator role.

Yammer administrators
Figure 3: Yammer administrators

It’s interesting that Microsoft choose to present the information in this fashion instead of showing all administrators as equal and hiding the complexities of revoking their access (if necessary) behind the scenes.

Bit by Bit, Yammer Becomes More Integrated

Ten years and more after its acquisition by Microsoft, Yammer is steadily becoming more integrated into the Microsoft 365 ecosystem. It’s just a pity that everything seems to take so long. Perhaps Yammer’s focus really is shifting to Viva. That wouldn’t be a bad thing, even if it slows down progress for the traditional browser client and backend.


So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across Office 365. Subscribe to the Office 365 for IT Pros eBook to receive monthly insights into what happens, why it happens, and what new features and capabilities mean for your tenant.

]]>
https://office365itpros.com/2022/08/12/yammer-administrator-role/feed/ 0 56486
How to Report Entra ID Admin Accounts Not Protected by MFA https://office365itpros.com/2022/07/06/entra-id-admin-roles-no-mfa/?utm_source=rss&utm_medium=rss&utm_campaign=entra-id-admin-roles-no-mfa https://office365itpros.com/2022/07/06/entra-id-admin-roles-no-mfa/#comments Wed, 06 Jul 2022 01:00:00 +0000 https://office365itpros.com/?p=55934

Using the Graph APIs Instead of MSOL Cmdlets

As regular readers of this blog know, I am keen that Microsoft 365 tenants protect user accounts with multi-factor authentication (MFA), especially user accounts with Entra ID admin roles. Despite all the promises made by security vendors about products to protect your tenants, the single most effective step that an organization can take is to make sure that accounts use MFA to sign into Microsoft 365. Some years ago, Microsoft reported that MFA blocks 99.9% of account compromise attacks. That statistic remains unquestioned.

Over the years, I’ve written several scripts to help tenant administrators understand the use of MFA within user accounts. The original 2018 article focused on reporting the enablement of accounts to use MFA. I followed up in 2019 with an article explaining how to find unprotected accounts that hold an administrative role. The downside of both articles is that the PowerShell code described in the text uses cmdlets from the old Microsoft Online Services (MSOL) module, which Microsoft is in the process of deprecating. The code still works, but the articles are good examples of advice you can find on the internet that is degrading and will soon be obsolete, as discussed yesterday.

Microsoft would like everyone to rewrite their PowerShell scripts to use Graph API requests or the cmdlets in the Microsoft Graph PowerShell SDK. The unavailability of equivalent API support undermined Microsoft’s aspiration. It’s been possible to report the authentication methods used by Entra ID accounts, but replicating a report showing administrative accounts and their MFA status has been harder.

Graph Registration Details

Enter the Microsoft Graph userRegistrationDetails resource type. This is a beta API that returns a list of authentication methods for users in a manner that’s easier to process than before. Two requests are available:

  • List: Returns the authentication methods for every user account in the organization. This isn’t as useful as it seems because the data returned includes guest accounts and unlicensed member accounts used for purposes like room mailboxes.
  • Get: Returns the authentication methods for a single user account. In practice, this API is much more useful.

The authentication methods data returned for a user account is shown below. In this case, the account is not MFA-enabled.

Name                           Value
----                           -----
userPrincipalName              Andy.Ruth@office365itpros.com
defaultMfaMethod               none
isMfaCapable                   False
isSsprCapable                  False
@odata.context                 https://graph.microsoft.com/beta/$metadata#reports/authenticationMethods/userRegistrationDetails/$entity
isSsprEnabled                  False
id                             fdc6b121-44b8-4262-9ca7-3603a16caa3e
methodsRegistered              {email}
isMfaRegistered                False
isPasswordlessCapable          False
isSsprRegistered               True
userDisplayName                Andy Ruth (Director)

The Microsoft Graph PowerShell SDK includes the Get-MgReportAuthenticationMethodUserRegistrationDetail cmdlet (you’ve got to love the automatically generated cmdlet names!). If you run the cmdlet without any parameters, you get the same data as returned by the List API. To return the data for an individual account, include a filter to specify their User Principal Name. For example:

Get-MgReportAuthenticationMethodUserRegistrationDetail -Filter "UserPrincipalName eq 'Sean.Landy@office365itpros.com'" | Fl

DefaultMfaMethod      : mobilePhone
Id                    : 08dda855-5dc3-4fdc-8458-cbc494a5a774
IsMfaCapable          : True
IsMfaRegistered       : True
IsPasswordlessCapable : False
IsSsprCapable         : False
IsSsprEnabled         : False
IsSsprRegistered      : True
MethodsRegistered     : {mobilePhone}
UserDisplayName       : Sean Landy
UserPrincipalName     : Sean.Landy@office365itpros.com
AdditionalProperties  : {}

Building a Report of Entra ID Admin Roles

Knowing how to get information about an individual account, we can consider how to check all user accounts paying special attention to those holding one or more administrative roles. I took these steps, using a mixture of Graph API requests and cmdlets from the Microsoft Graph PowerShell SDK:

  • Connect to the Microsoft Graph SDK. You’ll need to specify the UserAuthenticationMethod.Read.All and AuditLog.Read.All permissions.
  • Select the beta profile (Select-MgProfile Beta) to ensure that the Graph retrieves the registration information.
  • Run Get-MgDirectoryRole to find the set of available roles. Note: this cmdlet reports the set of administrative roles assigned in the tenant. There are usually a bunch of unassigned roles that don’t show up. The full set of roles defined in Entra ID can be found by running Get-MgDirectoryRoleTemplate | Select-Object DisplayName, Id | Sort-Object DisplayName. Don’t use role template identifiers as an input to Get-MgDirectoryRoleMember as they won’t work.
  • For each role, run Get-MgDirectoryRoleMember to find the current holders of the role.
  • Create an array of accounts that hold administrative roles.
  • Run Get-MgUser to find the set of licensed user accounts (it’s reasonable to assume that administrative accounts have at least one license).
  • For each account, get the authentication methods. You can use the Graph API request or the Get-MgReportAuthenticationMethodUserRegistrationDetail cmdlet.
  • Check if the account holds administrative roles and if so, fetch the roles held by the account.
  • Generate a report in an Excel worksheet (using the Import-Excel module).
  • Output a warning message if the script detects any unprotected administrative accounts (Figure 1).

Highlighting unprotected Entra ID accounts with administrative roles.

Entra ID admin roles.
Figure 1: Highlighting unprotected Entra ID admin roles

The Excel worksheet (Figure 2) is particularly useful in terms of being able to slice and dice the data to generate whatever report you need. For instance, you can see that many of the accounts listed use mobile phones (SMS messages) for the second authentication method. The Microsoft authenticator app is a better option, so you could pull the data about accounts currently using mobile phones to encourage them to move to the authenticator app.

Viewing details of Azure AD accounts and MFA methods in Excel
Figure 2: Viewing details of Entra ID admin roles for accounts

Download Script from GitHub

You can download the full script to report Entra ID admin roles and unprotected accounts from GitHub. Remember that this is code written to demonstrate a principal instead of an off-the-shelf solution. The code is basic PowerShell and can be altered to fit your needs. Enjoy

——————-

So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across Office 365. Subscribe to the Office 365 for IT Pros eBook to receive monthly insights into what happens, why it happens, and what new features and capabilities mean for your tenant.!

]]>
https://office365itpros.com/2022/07/06/entra-id-admin-roles-no-mfa/feed/ 22 55934
Imminent Deprecation of Azure AD PowerShell Modules Creates Knowledge Gap in Documentation https://office365itpros.com/2022/07/05/azure-ad-powershell-knowledge/?utm_source=rss&utm_medium=rss&utm_campaign=azure-ad-powershell-knowledge https://office365itpros.com/2022/07/05/azure-ad-powershell-knowledge/#comments Tue, 05 Jul 2022 01:00:00 +0000 https://office365itpros.com/?p=55878

Microsoft and Other Documentation Needs Updating

Microsoft announced their intention to retire the Azure AD Authentication Library (ADAL) and Azure AD Graph API in June 2020. A consequence of this decision meant the retirement of the Azure AD PowerShell modules, including the now very old Microsoft Online Services (MSOL) module. Even though Microsoft has pushed the retirement date out to June 30, 2023, the writing is on the wall for these modules.

Tenants will feel the first effect on March 31, 2023, when Microsoft 365 moves to a new license management platform. At that time, the PowerShell cmdlets for license assignment will stop working. Microsoft’s guidelines for converting from the old cmdlets to the Microsoft Graph PowerShell SDK include the blunt phrase that “there is currently no tool to automatically convert scripts.” In other words, it’s time to get coding, testing, and preparing to switchover. Good documentation is at a premium during conversions, and that’s an issue right now.

With two years’ warning, we can hardly plead a lack of awareness when the change happens., but with time running out, I don’t detect any great pressure. Maybe people will change when license management scripts and products stop working, or users report strange errors. It’s odd.

The Problem Lurking in Microsoft Documentation

One thing that might not be helping is the continued use of Azure AD and MSOL PowerShell examples in Microsoft documentation. After all, if the official documentation gives implicit approval to these modules, why bother changing? And to be fair, apart from license management, all the cmdlets will continue to work past the retirement date. There just won’t be any support or future updates.

Take the documentation to delete or restore user mailboxes in Exchange Online. This includes four references to the old Remove-MsolUser cmdlet as the text explains to readers how to remove an Azure AD user account permanently from the Azure AD recycle bin (Figure 1).

Microsoft documentation recommends the use of the obsolete Remove-MsolUser cmdlet

Azure AD PowerShell
Figure 1: Microsoft documentation recommends the use of the obsolete Remove-MsolUser cmdlet

It would be more impactful if the documentation used Microsoft Graph PowerShell SDK cmdlets or Graph API requests. For example, here’s how to use a Graph API request to retrieve the list of deleted Azure AD accounts in the recycle bin:

$Uri = "https://graph.microsoft.com/V1.0/directory/deletedItems/microsoft.graph.user"
[array]$DeletedUsers = Invoke-MgGraphRequest -Uri $Uri -Method Get
ForEach ($Account in $DeletedUsers.Value) { Write-Host $Account.displayname, $Account.id }
… select a user and then
Remove-MgDirectoryDeletedItem -DirectoryObjectId <Guid for deleted account>

This is an example where using a Graph API request is easier than the equivalent Microsoft Graph PowerShell SDK cmdlet (Get-MgDirectoryDeletedItem – see this example). The point is that if Microsoft documentation featured methods that customers can use in the future instead of the soon-to-be-deprecated modules, it would encourage tenant administrators to adopt those methods.

To be fair to Microsoft, they have tons of pages to update with new examples. However, it’s something that I think should happen sooner rather than later to nudge people along the path to where they should go.

The Problem with Azure AD Examples Across the Wider Internet

Much the same issue exists across the wider internet. Think of the average administrator who wants to find how to accomplish a task using the Azure AD PowerShell cmdlets. They’ll probably start by searching the internet to see how others did the same thing, and they’ll find many examples of old code. There’s always a big caveat about code you find on the internet: don’t trust anything until after fully testing the code. This advice still holds, but now we’re in a situation where the code that someone finds on August 20 to perform automated license assignment with PowerShell won’t work a week later.

I accept that the other cmdlets in the Azure AD and MSOL modules will continue to work after Microsoft retires the modules, but my point is still valid: examples based on soon-to-be-deprecated modules have a short shelf life.

You can’t expect blog writers to go back and revise every post with a reference to an Azure AD or MSOL cmdlet. Some might update some posts, but in general, it won’t happen. Nor should it. A blog post is a snippet of knowledge captured at a point in time rather than a definite statement about how something works that remains current. What’s different about this situation is that many posts face accelerated degeneration to a point where their content is no more than an interesting look back into the past.

Things Will Improve Over Time

Time heals many things. In this case, as time progresses and old blog posts decay, people will post new information and insights to renew the corpus of knowledge available on the internet. The Office 365 for IT Pros team is keenly aware of the issue and has generated several articles to help, including:

And of course, we’ve just released the 2023 edition of the Office 365 for IT Pros eBook containing over 250 examples of the Microsoft Graph PowerShell SDK cmdlets in action. In fact, we replaced every instance of using the Azure AD PowerShell cmdlets with the except of connecting to another tenant to update your guest account photo. The permissions model used by the Graph is completely different to the permissions granted when someone connects to Azure AD with Connect-AzureAD, and that’s why we had to leave that example. Over time, we hope to find a workaround and be able to remove the last vestige of Azure AD PowerShell.

]]>
https://office365itpros.com/2022/07/05/azure-ad-powershell-knowledge/feed/ 2 55878
Microsoft Previews Nested Dynamic Azure AD Groups https://office365itpros.com/2022/06/08/dynamic-azure-ad-group-members/?utm_source=rss&utm_medium=rss&utm_campaign=dynamic-azure-ad-group-members https://office365itpros.com/2022/06/08/dynamic-azure-ad-group-members/#comments Wed, 08 Jun 2022 01:00:00 +0000 https://office365itpros.com/?p=55392

Including Members from Other Groups in Membership of Dynamic Groups

Until recently, Dynamic Azure AD Groups have not supported the ability to include members from other groups in their membership (aka, nested groups). You can construct membership rules to include the same accounts in a dynamic group, but it’s easier to say, “include the members from groups 1, 2, and 3” if those groups already exist and include the necessary accounts.

In a June 6 announcement, Microsoft introduced a preview feature to allow the membership rules for dynamic Azure AD groups to use the memberOf attribute. In essence, memberOf instructs Azure AD to extract the membership of one to up to 50 groups and include the individual members of those groups in the dynamic membership.

Creating a New Azure AD Dynamic Group

Apparently, the preview feature is available worldwide. I had no success using it in the Azure AD admin center. Any attempt to create a new group (of any type) generated the error: “per label policy, the selected visibility is not allowed” (Figure 1). No doubt this is due to some configuration I have tweaked, but the error message is obscure, to say the least. (Update: I discovered the root cause of the problem, which Microsoft say they will fix).

Azure AD fails to create a group

per label policy, the selected visibility is not allowed
Figure 1: Azure AD fails to create a group

But where the will exists, you get the job done, and PowerShell came to the rescue. I created the new dynamic group with the following command. You can see that the membership rule is that the membership comes from any user members in the specified groups.

$Group = New-MgGroup -DisplayName "System Innovation" -Description "Dynamic group containing system innovators" -MailEnabled:$True -SecurityEnabled:$False -MailNickname SystemInnovators -GroupTypes "DynamicMembership", "Unified" -MembershipRule "user.memberOf -any (group.objectId -in ['ef4af711-bf83-4ba1-81be-fd98f4098d12',' d6279df7-2eff-4566-ba93-22aa9320385b','b07c7e05-10e0-47a4-acca-767621ac8ddc'])" -MembershipRuleProcessingState "On"

The groups added were:

  • A Microsoft 365 group with assigned membership.
  • A Microsoft 365 group with dynamic membership.
  • A distribution list with a fixed membership.

Microsoft’s documentation doesn’t include any reference to using distribution lists, but as Azure AD treats distribution lists like other groups, it seemed like they should work. After all, you can run the Get-MgGroupMember cmdlet (or Get-AzureADGroupMember if you still haven’t converted from the soon-to-be-deprecated module) against a distribution list and Azure AD is happy to list the members. And as it turns out, you can include the membership of distribution lists in dynamic Azure AD groups. After an hour or so, Azure AD resolved the rule and built the membership of the new dynamic group, including the removal of any duplicates (Figure 2).

Membership of the new dynamic Azure AD group
Figure 2: Membership of the new dynamic Azure AD group

Preview Limits

During the preview, a dynamic group can have up to 50 groups in its membership, and each tenant can have up to 500 dynamic groups that use the memberOf attribute in their membership rule. If you add security groups to the membership of a dynamic group, Azure AD includes only the direct members of the security group in the dynamic group’s membership.

In addition, Microsoft says that you can’t use a dynamic group that uses the memberOf attribute to define the membership of another group that also uses memberOf. The old and well-proven adage to keep it simple (stupid) rings loud and clear. Don’t nest groups inside groups and don’t over-complicate things. Perhaps more complicated arrangements might be possible in the future, but for the preview, don’t give Azure AD complex membership rules to resolve. For more information on including groups within the membership of dynamic groups, read Microsoft’s documentation.

Another issue is that the memberOf attribute can’t be used with other rules. For instance, let’s assume that you assemble a set of users drawn from the membership of several other group. You can’t add another filter to select people whose accounts match another attribute, such as the department or country.

For now, the rules editor doesn’t work for this type of dynamic group, nor does the other Validate Rules preview feature which allows administrators to check the effectiveness of a membership rule against an account that they know should be in a group’s membership (Figure 3).

Azure AD can't validate membership of a dynamic group
Figure 3: Azure AD can’t validate membership of a dynamic group

Dynamic Teams Work Too

I updated the group’s properties to enable it for Teams. Support for dynamic teams has been around since 2018, but it’s always wise to check. The good news is that the dynamic membership for the team appears as expected (Figure 4).

Teams membership roster for the dynamic Azure AD group
Figure 4: Teams membership roster for the dynamic Azure AD group

Solid Update

There’s no doubt that this is a good change. Anything that adds to the flexibility and capability of dynamic Azure AD groups is a good thing. The bad thing is that Microsoft requires Azure AD Premium P1 for dynamic groups (Exchange Online dynamic distribution lists don’t need additional licenses). The guidance is:

This feature requires an Azure AD Premium P1 license or Intune for Education for each unique user that is a member of one or more dynamic groups. You don’t have to assign licenses to users for them to be members of dynamic groups.

It would be nice if dynamic groups were included in Office 365 E3, but life is cruel sometimes…


So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across Office 365. Subscribe to the Office 365 for IT Pros eBook to receive monthly insights into what happens, why it happens, and what new features and capabilities mean for your tenant.

]]>
https://office365itpros.com/2022/06/08/dynamic-azure-ad-group-members/feed/ 6 55392
Guest Accounts Can’t Update Their Photos with the Microsoft Graph PowerShell SDK https://office365itpros.com/2022/06/07/azure-ad-guest-account-photo/?utm_source=rss&utm_medium=rss&utm_campaign=azure-ad-guest-account-photo https://office365itpros.com/2022/06/07/azure-ad-guest-account-photo/#respond Tue, 07 Jun 2022 01:00:00 +0000 https://office365itpros.com/?p=55373

Upgrading Scripts to Use New Cmdlets

Microsoft plans to deprecate the Azure AD and Microsoft Online Services (MSOL) PowerShell modules in late 2022 or early 2023. Apart from the license management cmdlets, the other cmdlets in these modules will continue to work but will be unsupported. Eventually, the cmdlets will stop working, so the time is ripe for upgrading scripts and seeking new ways of getting things done, like how to manage Azure AD guest accounts.

In most cases, it’s possible to upgrade scripts by replacing Azure AD cmdlets with cmdlets from the Microsoft Graph PowerShell SDK. As we prepare for the launch of the Office 365 for IT Pros (2023 Edition) eBook, we’re going through all the Azure AD and MSOL code examples in the book to replace as many as possible with either Microsoft Graph API queries or SDK cmdlets. Microsoft publishes a useful cmdlet map to help developers match old cmdlets with suitable replacements.

Sometimes, it’s not yet possible to replace a cmdlet because Microsoft doesn’t yet provide a direct equivalent in the Graph. In these instances, we’re leaving the older code in place because it will continue to work. When a suitable replacement is available, we’ll update the book with new Graph-based code.

It’s Good to Use Photos with Azure AD Accounts

We recommend that tenant administrators add photos for all Azure AD accounts, both regular user accounts and guest accounts. Having a face to recognize makes it easier to appreciate who’s involved in sharing a document or participating in a channel conversation in Teams. Tenant administrators are busy people, and even if they devote time to “guest hygiene” (cleaning up unwanted or obsolete guest accounts), they might not get around to adding photos for guest accounts via the Azure AD admin center or PowerShell. This is why it’s good if guest accounts can update their own photos.

In April 2021, I wrote about how to use cmdlets from the Azure AD PowerShell module to update the photo for your Azure AD guest account in another Microsoft 365 tenant. It’s a relatively straightforward procedure that’s facilitated by the way the Azure AD module works. Once you have a connection to a tenant, you can work with accounts and other objects the signed-in account can access. In this instance, your guest account.

Time moves on and it was time to upgrade the example showing how guests can upload their own photos. Unhappily, although the Set-MgUserPhotoContent cmdlet is available to replace the Set-AzureADUserThumbNailPhoto cmdlet, the technique of connecting to a target tenant with a guest account to update the account photo doesn’t work. At least, it doesn’t work unless the target tenant meets specific criteria.

Experimenting with the Microsoft Graph PowerShell SDK

I experimented with several target tenants where I have guest accounts to see what’s possible. The Connect-MgGraph cmdlet is happy to connect to a tenant and you can sign in with your guest account. At this point, things go wrong. Once you connect with the Azure AD module, you can update the guest account as described in the post referenced above.

However, the Microsoft Graph takes a more restrictive approach to permissions (or scope). Administrators must grant consent to the service principal used for interactive sessions with the Microsoft Graph PowerShell SDK for the permissions to interact with user accounts. In this case, consent must be in place for the User.ReadWrite.All permission before it’s possible to update a user account (or guest account) with a photo. Interactive sessions with the PowerShell SDK use delegated permissions, so even though the permission is User.ReadWrite.All (implying access to all mailboxes), the Graph constrains the scope of the permission to the signed-in user.

Our renowned technical editor, Vasil Michev, thought that he had solved the problem and published a note to that effect. Unhappily, further investigation proved that using the Microsoft Graph PowerShell SDK to connect to a target tenant to update the photo for a guest account only works if the service principal for the Microsoft Graph PowerShell enterprise app (application id 14d82eec-204b-4c2f-b7e8-296a70dab67e) has consent for the User.ReadWrite.All permission.

The Service Principal Question

Meeting these requirements means that someone has run Connect-MgGraph at some time in the past in the target tenant. This action creates the service principal if it’s not already known to Azure AD. If the service principal doesn’t exist in the target tenant, Connect-MgGraph cannot proceed until an administrator signs in to create the service principal (Figure 1). After the creation of the service principal, it can receive consent to use permissions, including User.ReadWrite.All.

Azure AD prompts to create the service principal for the Microsoft Graph PowerShell SDK

Azure AD Guest Account
Figure 1: Azure AD prompts to create the service principal for the Microsoft Graph PowerShell SDK

Although these conditions might exist for some tenants, there’s no guarantee that the service principal exists and holds the permission. For example, connecting to the Microsoft tenant with a guest account reveals that the User.Read permission is available, but the User.ReadWrite.All permission is not.

Disconnect-MgGraph
Connect-MgGraph -TenantId 72f988bf-86f1-41af-91ab-2d7cd011db47
Welcome To Microsoft Graph!
Get-Mgcontext

ClientId              : 14d82eec-204b-4c2f-b7e8-296a70dab67e
TenantId              : 72f988bf-86f1-41af-91ab-2d7cd011db47
CertificateThumbprint :
Scopes                : {openid, profile, User.Read, email}
AuthType              : Delegated
AuthProviderType      : InteractiveAuthenticationProvider
CertificateName       :
Account               :
AppName               : Microsoft Graph PowerShell
ContextScope          : CurrentUser
Certificate           :
PSHostVersion         : 5.1.22000.653
ClientTimeout         : 00:05:00

In a nutshell, it’s possible to use the Microsoft Graph PowerShell SDK to update photos for guest accounts in other tenants, but only when the conditions are exactly right.

That Permissioned Service Principal

In closing, let me note once again the cumulative nature of the service principal used by the Microsoft Graph PowerShell SDK. If an administrator consents to a permission for use with the Graph SDK, that permission remains assigned to the service principal unless an administrator removes it. Over time, permissions accrue, and the service principal becomes highly permissioned. This makes it easy for tenant administrators to run SDK cmdlets in interactive sessions, but it’s possibly not what you want to happen. On the upside, interactive sessions use delegated permissions rather than application permissions, so the Graph won’t allow the signed-in user to access data that they couldn’t otherwise open. Which is nice to know.

]]>
https://office365itpros.com/2022/06/07/azure-ad-guest-account-photo/feed/ 0 55373
Don’t Give Up on Entra ID Guest Accounts https://office365itpros.com/2022/06/06/entra-id-guest-accounts-valuable/?utm_source=rss&utm_medium=rss&utm_campaign=entra-id-guest-accounts-valuable https://office365itpros.com/2022/06/06/entra-id-guest-accounts-valuable/#comments Mon, 06 Jun 2022 01:00:00 +0000 https://office365itpros.com/?p=55362

Entra B2B Collaboration and Entra ID Guest Accounts Likely to be Dominant for Immediate Future

The introduction of shared channels in Teams is a big deal, as is the advent of Azure AD Direct Connect, the vital underpinning that enables tenants to accept the credentials granted to accounts in other Microsoft 365 tenants. Cross-tenant access policies control who can share channels in your organization and who can share channels in other organizations.

Some might have missed the news that cross-tenant access settings can also control Entra B2B collaboration (guest accounts). This is also a big step forward because it addresses the need some tenant administrators have expressed to be able to control the organizations where their users can join groups using guest accounts. Control over inbound guests has been available with the Entra B2B Collaboration policy for several years, so adding outbound control is welcome.

Sharing with Teams

Last week, Microsoft posted about the ways to collaborate externally using Teams. The descriptions of Azure AD Direct Connect, Azure B2B Collaboration, and external federation (one-to-one chat and calls with people in other organizations) were accurate, and the information was well-timed. Many Microsoft 365 tenants are figuring out their external collaboration strategy and wondering where to put their effort over the coming years,

I’ve heard a few commentators say that guest accounts now have limited usefulness. The theory is that everyone will move to shared channels. I think that’s overstating the case more than a little. Shared channels are very convenient, especially in their accessibility. There’s no need to switch to another tenant to access information. Shared channels are right there, listed by your favorite Teams client alongside the other teams and channels from your home tenant.

For now, shared channels are only sharable with accounts from other Microsoft 365 tenants. Shared channels will reveal their full potential when they support access from Microsoft Services Accounts (MSAs), a feature that Microsoft is working on.

Entra ID Guest Accounts Still Super-Valuable

But even when that happens, I think Entra ID guest accounts still continue to offer a lot of value. Switching to another tenant can slow access down a tad, but switching performance is much faster now than it used to be (even if it’s not one of the improvements listed in Microsoft’s latest news about Teams performance), and I think I am now immune to the need to switch to a tenant to collaborate with people in that organization. Muscle memory is a great thing and it’s one of the reasons why many of the teams I use feature many guest accounts (Figure 1).

Team with multiple Entra ID  guest accounts in its membership.
Figure 1: Entra ID Guest Accounts are a big part of a many teams

Once added to a tenant, a guest has full access to the teams and groups they are a member of. Within a single team, they can access up to 200 regular channels and, if added as a member, they can participate in another 30 private channels. A shared channel has its own roster of members and if a team supports multiple shared channels, the channel owner must share with external users in each channel.

Most of the external organizations I work with have teams with multiple channels. Some are channel-happy and explore the limits set by Teams. Others are more restrained. Even so, the teams usually have several channels to support discussions about different topics. Private channels don’t seem to be popular but are present and I use some in other tenants. Shared channels might be a better choice than private channels in many cases, especially when MSA support is available.

The Question of Guest Hygiene

Mention guest hygiene and you’re probably thinking about the mess unwelcome guests can make in your house. Microsoft says that using Entra ID B2B Collaboration means that organizations should implement “a guest hygiene process to remove guest accounts when no longer needed.” In other words, instead of remaining passive and letting guest accounts accumulate over time, organizations should review old guest accounts and figure out if they should remain.

A simple check against age will often highlight unused guest accounts. If you have Entra P2 licenses, Microsoft offers a more sophisticated account review process. No matter what approach you take, it’s a good idea to run an annual review to clean out old accounts.

Overall, it’s likely that Entra ID guest accounts will remain the foundation for most external collaboration based on Microsoft 365 groups and teams. Shared channels will nibble away and take some of the work, but it won’t take over from guest accounts in the immediate future.

]]>
https://office365itpros.com/2022/06/06/entra-id-guest-accounts-valuable/feed/ 2 55362
Why Teams Sometimes Won’t Allow External Users In https://office365itpros.com/2022/05/26/teams-blocks-external-users/?utm_source=rss&utm_medium=rss&utm_campaign=teams-blocks-external-users https://office365itpros.com/2022/05/26/teams-blocks-external-users/#comments Thu, 26 May 2022 01:00:00 +0000 https://office365itpros.com/?p=55245

Teams Blocks External Users as Guests and From Sharing Channels

A reader asked why Teams blocks external users. In this case, they had difficulties adding a new guest account to a team’s membership. Anytime they attempted to add the guest by typing in their email address, Teams responds with “we didn’t find any matches” (Figure 1).

Teams won't add a new guest
Figure 1: Teams won’t add a new guest

The error text isn’t very useful, and Microsoft could improve it. What it means is that Teams couldn’t match the email address of the external user against the set permitted for the team.

The usual problem is that something blocked guest access for the team. This can happen because:

  • The organization blocks guest access for all teams.
  • The organization uses sensitivity labels to control guest access, and the label assigned to the team blocks guests.
  • If the organization doesn’t use sensitivity labels, administrators can block guest access for a specific team by updating the Azure AD directory settings for the Microsoft 365 group (this is what sensitivity labels do when they block access).
  • The user attempting to add the guest doesn’t have the necessary permission. Normally, team owners can add guests, but the organization can restrict this capability to administrators.

If guests can join other teams, no organization-wide block on guests is present. If one is, administrators can lift it by updating the Microsoft 365 Groups settings in the Microsoft 365 admin center (Figure 2).

Organization setting allowing guests to join Teams and Microsoft 365 Groups
Figure 2: Organization setting allowing guests to join Teams and Microsoft 365 Groups

Container Management Blocks

If the organization uses sensitivity labels for container management, the block might be present because the team inherited the setting from its sensitivity label, so it’s the next thing to check. Go to the Information protection section of the Microsoft Purview Compliance portal and check the label assigned to the team. Its settings (or maybe just the description – Figure 3) will tell you if the label blocks guest members.

Sensitivity label settings could block guest access
Figure 3: Sensitivity label settings could block guest access

Not all organizations use sensitivity labels for container management. The block on guest access can be applied using PowerShell, so you’d need to check the group settings to make sure that they permit guest access (or not).

Finally, check the External collaboration settings under External identities in the Azure AD admin center to check that someone hasn’t restricted the ability of group owners to add guests.

Azure AD B2B Collaboration Blocked Domains

While discussing External collaboration settings, we should cover a related issue, which is when group owners can’t add a guest account because the Azure AD B2B collaboration policy blocks the guest’s domain. When this happens, Teams accepts the external email address, but then fails when it attempts to create the guest account (Figure 4).

Teams can't add a guest from a blocked domain
Figure 4: Teams can’t add a guest from a blocked domain

The solution is to amend the Azure B2B collaboration policy to remove the block on the domain. If this isn’t possible, the external person can never become a guest using an email address from the blocked domain.

Can’t Share a Shared Channel

Teams displays the unhelpful error text as a catch-all for multiple conditions. Teams flags the same error if you attempt to share a shared channel with an external user from another Microsoft 365 tenant when cross-tenant access settings don’t allow access from the external user’s domain (Figure 5).

Teams can't add an external user to a shared channel because no trust exists
Figure 5: Teams can’t add an external user to a shared channel because no trust exists

The same kind of logic applies. You asked Teams to share a channel. It checked the set of domains it can share channels with and found that the requested domain isn’t in the set, so issued the “we didn’t find any matches” error.

In this case, the solution is to amend the cross-tenant access settings in your tenant to allow inbound access for external users from the other domain, and to ask the administrator of the other domain to permit outbound access to your domain. Cross-tenant access works on a mutual trust basis, so you can’t share a channel with someone from another unless their tenant is happy for this to happen.

Take Your Time

After making any changes, it’s important to be patient and allow the changes to replicate within Azure AD and Teams. Eventually (after about 24 hours), the planets align, and permissions are in place, and you’ll be able to add external users as guests to team memberships or share channels with people in other tenants.


Learn about managing guest access for Teams and Microsoft 365 Groups and the rest of Office 365 by subscribing to the Office 365 for IT Pros eBook. Use our experience to understand what’s important and how best to protect your tenant.

]]>
https://office365itpros.com/2022/05/26/teams-blocks-external-users/feed/ 4 55245
Graph X-Ray Tool Helps PowerShell Developers Master the Graph https://office365itpros.com/2022/05/23/graph-x-ray-powershell/?utm_source=rss&utm_medium=rss&utm_campaign=graph-x-ray-powershell https://office365itpros.com/2022/05/23/graph-x-ray-powershell/#comments Mon, 23 May 2022 01:00:00 +0000 https://office365itpros.com/?p=55170

Even in First Release, Graph X-Ray Proves Its Worth

When Microsoft decided to build the administrative tools for Exchange Server 2007 around PowerShell, they realized that it would take time for administrators to become accustomed to PowerShell. Sensibly, Microsoft included a cmdlet logging facility in the Exchange Management Console (EMC) to allow administrators to see the PowerShell code used to execute different actions, such as creating a new mailbox. Cmdlet logging gave administrators prototype code to build scripts around and it’s still the best learning tool I have encountered in a Microsoft server product.

Roll on sixteen years and cmdlet logging doesn’t exist in the modern Exchange Admin Center (EAC). Many, including me, have moaned at Microsoft about this deficiency. One response is that EAC is no longer built on PowerShell, which makes it very difficult to generate and display PowerShell code for administrators to copy and reuse. It’s a great pity.

All of this brings me to a new browser extension called Graph X-Ray. Created by Microsoft employees but not a formal product, Graph X-Ray displays the Graph API commands run to execute actions in consoles like the Azure AD admin center and the Intune admin center. Not every action in these consoles depends on Graph APIs, but enough do in important areas like users, groups, and device management to make this an interesting facility.

Raw Graph or Graph SDK

Anyone developing code for Microsoft 365 can get value from Graph X-ray, whether you’re using compiled languages like C# or JavaScript or writing PowerShell scripts. Using Graph APIs in PowerShell normally means that scripts run faster, especially if the code must process more than a few objects. Scripters have the choice to include “raw API calls” or use cmdlets from the Microsoft Graph PowerShell SDK. The script to create a tenant configuration report is a good example of using raw API calls while the script to generate an Office 365 licensing report uses the SDK cmdlets. In either case, you need to understand how Graph API queries are formed and executed, and that’s where the Graph X-Ray extension proves its worth.

Restoring Deleted Microsoft 365 Groups

Take the example of restoring a deleted Microsoft 365 group. Before you can restore a group, you need to know what groups are in a soft-deleted state. Groups remain in the soft-deleted state for 30 days after deletion to allow administrators to restore groups using options in the Microsoft 365 and Azure AD admin centers. After the 30-day retention period lapses, Azure AD removes the groups permanently and they become irrecoverable.

In a large tenant, many groups might be waiting for permanent deletion, including inactive groups removed by the Microsoft 365 Groups Expiration policy. The Get-UnifiedGroup cmdlet can generate a list of soft-deleted groups using a command like this:

Get-UnifiedGroup -ResultSize Unlimited -IncludeSoftDeletedGroups:$True | ? {$_.WhenSoftDeleted -ne $Null} | Sort WhenSoftDeleted | Format-Table DisplayName, PrimarySmtpAddress, WhenSoftDeleted

The cmdlet works, but it’s slow. To speed things up, I tried using the Get-MgDirectoryDeletedItem SDK cmdlet. The cmdlet works when listing deleted user accounts, but no matter what I did, I couldn’t find a way to make it return a list of deleted groups.

Using the Graph X-Ray

I downloaded the Graph X-Ray extension for the Edge browser add-on (other versions are available for Chrome and a Microsoft Store app). To load the add-on, I opened the Developer Tools option in Edge and selected Graph X-Ray. A new blade opened in the browser to display the Graph API commands used for actions performed in the Azure AD admin center (Figure 1).

Viewing Graph commands with the Graph X-Ray extension
Figure 1: Viewing Graph commands with the Graph X-Ray extension

It’s important to emphasize that this is very much an MVP release. Things are by no means perfect, but enough value is present to allow Graph X-Ray to be very helpful. For example, the command reported when the Azure AD admin center lists deleted groups is:

Get-MgDirectoryDeletedItem -DirectoryObjectId $directoryObjectId -Property "id,displayName,mailEnabled,securityEnabled,groupTypes,onPremisesSyncEnabled,deletedDateTime,isAssignableToRole" -Sort "displayName%20asc" -Top 20

This is fine, but nowhere does it tell you how to populate the $directoryObjectId variable. On a more positive note, the raw Graph API query showed the structure needed to return deleted groups, and I was able to use that information to submit the query with the Invoke-MgGraphRequest SDK cmdlet, which worked. It’s worth noting that the Invoke-MgGraphRequest cmdlet exists to allow scripts to execute raw Graph API queries when an SDK cmdlet isn’t available (or doesn’t work).

Equipped with new-found knowledge about how to find deleted groups, I coded this script to report the set of soft-deleted groups including when each group is due for permanent deletion.

Connect-MgGraph
Select-MgProfile Beta
$uri = "https://graph.microsoft.com/beta/directory/deleteditems/microsoft.graph.group?`$select=id,displayName,groupTypes,deletedDateTime&`$orderBy=displayName%20asc&`$top=100"
[array]$Groups = (Invoke-MgGraphRequest -Uri $Uri).Value
If (!($Groups)) { write-Host "No deleted groups available for recovery" ; break }
$Report = [System.Collections.Generic.List[Object]]::new() # Create output file for report
$Now = Get-Date
ForEach ($Group in $Groups) {
     $PermanentRemovalDue = Get-Date($Group.deletedDateTime).AddDays(+30)
     $TimeTillRemoval = $PermanentRemovalDue - $Now
     $ReportLine = [PSCustomObject]@{ 
          Group                = $Group.DisplayName
          Id                   = $Group.Id
          Deleted              = $Group.deletedDateTime
          PermanentDeleteOn    = Get-Date($PermanentRemovalDue) -format g
          DaysRemaining        = $TimeTillRemoval.Days        } 
       $Report.Add($ReportLine) 
}
$Report | Sort {$_.PermanentDeleteOn -as [datetime]} | Out-GridView

The add-on includes the facility to download the commands it captures in a script (GraphXRaySession.PS1). There’s likely to be some duplication of commands in the downloaded script, but it’s great to have such an easy method to copy the commands for later use.

More Insight from Graph X-Ray

Moving on to restoring a soft-deleted group, Microsoft’s documentation for the Restore-MgDirectoryObject cmdlet is woefully deficient in terms of useful examples. An attempt to pass the identifier of a deleted group to the cmdlet failed:

Restore-MgDirectoryObject -DirectoryObjectId $GroupId
Restore-MgDirectoryObject : Resource '2eea84f2-eda3-4a72-8054-5b52c063ee3a' does not exist or one of its queried reference-property objects are not present.

Once again, I turned to Graph X-Ray to find out what command powered the restore deleted group option in the Azure AD admin center. The raw API reported by Graph X-Ray is a POST (update) query like this:

POST /directory/deleteditems/2eea84f2-eda3-4a72-8054-5b52c063ee3a/restore

It’s easy to take this command and repurpose it for use with the Invoke-MgGraphRequest cmdlet:

$uri = “https://graph.microsoft.com/beta/directory/deleteditems/8783e3dd-66fc-4841-861d-49976f0617c0/restore”
Invoke-MgGraphRequest -Method Post -Uri $Uri

More Please!

I wish Microsoft would provide similar insight across all the Microsoft 365 admin consoles. Being able to see the Graph API commands used to perform real-life actions is a powerful learning aid. If Microsoft is serious about driving the adoption of the Graph and the Graph SDK, they could do worse than invest in this kind of tooling. I hope that they do.


Keep up to date with developments like the Graph API commands by subscribing to the Office 365 for IT Pros eBook. Our monthly updates make sure that our subscribers understand the most important changes happening across Office 365.

]]>
https://office365itpros.com/2022/05/23/graph-x-ray-powershell/feed/ 1 55170
Use Entra ID Access Reviews to Check for Inactive Guests https://office365itpros.com/2022/05/17/entra-id-access-review/?utm_source=rss&utm_medium=rss&utm_campaign=entra-id-access-review https://office365itpros.com/2022/05/17/entra-id-access-review/#comments Tue, 17 May 2022 01:00:00 +0000 https://office365itpros.com/?p=55074

Remove Inactive Guests from Microsoft 365 Groups

Entra ID access reviews are a premium Identity Governance feature that helps organizations conduct periodic reviews of user and guest access to resources, including the membership of Microsoft 365 groups. Automation of this kind is most valuable in large enterprises where administrators can find it difficult to keep track of groups, guests, permissions, and role assignments. Further automation and reporting of access reviews are possible using a Graph API.

Tenants can enable a 30-day free trial of Entra P2, which you’ll need if you want to test access reviews before deciding to make a long-term commitment. Licensing for guest accounts in the groups within the scope of the review is covered by Entra ID’s Monthly Active User (MAU) billing model, which requires an Azure subscription.

Finding Inactive Guests

Soon after Microsoft introduced guest support for Office 365 Groups in late 2016, it became clear that not much administrative support was available to manage guest accounts. Since then, the number of guest accounts in tenants has exploded, largely due to the success of Teams, but also because SharePoint Online creates guest accounts for document sharing. However, the toolset available to manage the burgeoning guest accounts is still sparse.

Recently, Microsoft introduced a new preview feature for Entra ID access reviews to allow organizations to conduct an access review for inactive guest accounts, defined as “those who have not signed in either interactively or non-interactively to the tenant.”

Creating an access review to look for inactive guests is simple. The review covers:

  • All Microsoft 365 Groups with guest members, checking only guest users.
  • The period to determine inactivity can be anything from 1 to 730 days.

Other tabs have settings to cover whether the review is a one-off event or happens on a schedule, what to do if reviewers don’t respond, and what happens when the review period completes.

Figure 1 shows the access review I created to locate guests inactive for the last 365 days.

Creating a new Entra ID access review for inactive guests.
Figure 1: Creating an Entra ID access review for inactive guest accounts

Reviewing Inactive Guests

After creating the review, a background processing job locates Microsoft 365 groups in the tenant that have guest members. Entra ID uses sign-in records for the review period to determine if any guests in a group are deemed inactive, Entra ID sends email to the group owner (Figure 2) to ask them to review the inactive groups and decide if the membership in the group should continue for the inactive guests.

Email notification for a group owner to review inactive guests
Figure 2: Email notification for a group owner to review inactive guests

Clicking the Start review link in the message brings the group owner to a page in MyAccess.microsoft.com to allow them to see the inactive guests and make a decision for each (Figure 3). In this case, Entra ID was unable to find any sign-in data for the guest account.

Performing an  Entra ID access review for an inactive guest.
Figure 3: Performing an access review for an inactive guest

A group owner can decide to ignore the review, in which case the settings for the access review determines what happens. This might be to do nothing; it could also be to remove access for the inactive guest. It’s best if group owners perform the review, even if administrators might have to cajole them to do the work.

After the review period finishes, Entra ID implements the review decisions and removes the inactive guests or leaves them in place.

Sounds Good but What About Outlook Groups

Running an access review to remove inactive guests from group membership sounds like a great idea and the implementation works. However, there’s one big flaw in the scheme and that’s the dependency on sign-in data. This is understandable because it’s an acccount review and the best data available to Entra ID to figure out if a guest account is in use is their sign-in history.

The problem is that some guest accounts can be active without ever signing into a tenant. Guest members of Outlook groups (the original implementation of Office 365 groups) use email to communicate and don’t need to ever sign in to the tenant hosting the group unless they want to access other group resources, like its SharePoint Online site or Planner.

I have multiple Outlook groups in this category. The access review highlighted most of the guests in these groups. The only guests that the access review did not tag were those that sign into the tenant to use Teams or another application. Perhaps Microsoft will introduce additional checks to help detect truly inactive guest accounts when this feature moves from preview to generally available status.

The bulk of Microsoft 365 group activity now focuses on Teams, which is an application that signs in every hour during a session. There’s no danger that Entra ID won’t know when guest accounts used with Teams are inactive.

Do-It-Yourself Inactive Guest Reviews

You don’t need to pay for Entra ID access reviews to find potentially inactive guest accounts. Over the years, I’ve written about this topic, most recently to describe my approach to detecting, reporting, and managing inactive guest accounts using PowerShell. An even simpler approach is to create a report for all guest accounts over a certain age together with their group membership.


Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2022/05/17/entra-id-access-review/feed/ 2 55074
Track User Access to Teams Shared Channels with Entra ID Sign-In Logs https://office365itpros.com/2022/03/31/teams-shared-channels-access/?utm_source=rss&utm_medium=rss&utm_campaign=teams-shared-channels-access https://office365itpros.com/2022/03/31/teams-shared-channels-access/#comments Thu, 31 Mar 2022 01:00:00 +0000 https://office365itpros.com/?p=54326

Know Who’s Collaborating in Teams Shared Channels From Outside Your Tenant

Updated 4 March 2024

When Microsoft launched Teams shared channels into public preview (according to MC390413, shared channels will GA in mid-July 2022) the rubber hit the road as tenant administrators tried to figure out the complexities of managing shared channels in production use. It’s true that Microsoft conducted a long private preview with many customers to get shared channels to the point where they squashed obvious bugs and delivered usable software. However, once software is exposed to the kind of examination that an application with 270 million monthly active users can create, other questions bubble to the surface.

Which brings me to the topic of controlling user access to shared channels. The cross-tenant access settings in the External identities section of the Entra admin center control which tenants your organization can access using Entra ID B2B Direct Connect. This is the underlying authentication mechanism for Teams shared channels. It allows users to authenticate in their home tenant and use that authentication, including MFA and device state claims, to access resources in other tenants, if permitted by other tenants.

Entra ID Sign-Ins Track Cross-Tenant Access

Microsoft’s guidance for cross-tenant access settings advises that you can use Entra ID sign in logs to figure out user access to other tenants. It’s true that you can use the PowerShell snippet provided there, but I think we can do better.

The code uses the Get-MgBetaAuditLogSignIn cmdlet from the Microsoft Graph PowerShell SDK to look for sign in records where the resource tenant identifier (the organization delivering a resource like Teams) is not the same as the home tenant identifier (the organization hosting the sign in logs).

$TenantId = (Get-MgOrganization).Id
Get-MgBetaAuditLogSignIn -Filter "ResourceTenantId ne '$TenantId'" -All:$True

The code works (the All switch doesn’t need $True), but the result of the query is a set of sign-in records for both Entra ID B2B Collaboration (guest accounts) and Entra ID B2B Direct Connect. This is a better filter if you want to focus on access to Teams shared channels:

Get-MgBetaAuditLogSignIn -Filter "ResourceTenantId ne '$TenantId' and CrossTenantAccessType eq 'b2bDirectConnect'" -All

Next, although you might recognize the identifier for your tenant, it’s unlikely that you’ll know the identifiers for other tenants (like 22e90715-3da6-4a78-9ec6-b3282389492b). To translate these identifiers into human-friendly tenant names, we need another method.

We’re already connected to the Microsoft Graph, so we can use a Graph query to resolve the identifier into a tenant name.

Finding Tenant Names

Fortunately, a beta query called findTenantInformationByTenantId does the trick. There’s little documentation available, but by running it through the Invoke-MgGraphRequest cmdlet (runs any Graph query when an SDK cmdlet is unavailable), we can retrieve tenant data:

$ExternalTenantId = $Record.ResourceTenantId
$Uri = "https://graph.microsoft.com/beta/tenantRelationships/findTenantInformationByTenantId(tenantId='$ExternalTenantId')"
$ExternalTenantData = Invoke-MgGraphRequest -Uri $Uri -Method Get

The tenant information returned is:

Name                           Value
----                           -----
@odata.context                 https://graph.microsoft.com/beta/$metadata#microsoft.graph.tenantInformation
tenantId                       22e90715-3da6-4a78-9ec6-b3282389492b
displayName                    o365maestros
federationBrandName
defaultDomainName              o365maestros.onmicrosoft.com

I assume this web site, which can return the identifier of any Microsoft 365 tenant, uses a similar API.

Flow of the Script

The flow of the PowerShell script to analyze sign-in data is therefore:

  • Find sign-in records for Entra ID Direct Connect activity. If you want to process records for Azure B2B Collaboration, change the filter to remove the check against the CrossTenantAccessType property.
  • Extract data from each record, including resolving external tenant identifiers to tenant names.
  • Report.

In normal circumstances, the sign-in data will feature just a few tenants. It would be slow to run a query to resolve the tenant identifier for every record. To ensure performance, the script resolves a tenant name the first time it is encountered and stores the tenant name identifier and name in a hash table. When the script processes subsequent records for the same tenant, it reads the information from the hash table.

You can download the script from GitHub. Normal warnings apply: use at your peril, etc. and please fix my bugs…

Script Outputs

The output of the script is a PowerShell list containing details of sign-ins which use cross-tenant access to connect to Teams shared channels in external tenants (Figure 1).

Viewing information about user connects to Teams shared channels
Figure 1: Viewing information about user connects to Teams shared channels

The data can be parsed to reveal statistics like which tenants use cross-tenant access:

$Report | Group TenantName | Sort Count -Descending | Format-Table Name, Count

Or to reveal the names of the users who connect to external tenants:

$Report | Group User | Sort Count -Descending | Format-Table Name, Count

Name       Count
----       -----
Sean Landy     4
James Ryan     3
Ken Bowers     3

And so on. I’m sure you’ll find other ways to use the information to track what’s happening with Teams shared channels. The point is that the data is there if you need it. All that’s required is a little massaging of the information.


Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2022/03/31/teams-shared-channels-access/feed/ 10 54326
Assign Azure AD Roles to User Accounts with the Microsoft Graph PowerShell SDK https://office365itpros.com/2022/03/30/azure-ad-role-assignments/?utm_source=rss&utm_medium=rss&utm_campaign=azure-ad-role-assignments https://office365itpros.com/2022/03/30/azure-ad-role-assignments/#respond Wed, 30 Mar 2022 01:00:00 +0000 https://office365itpros.com/?p=54307

So Many Roles to Work With

A bunch of built-in Azure AD roles exist to help manage resources. Microsoft adds more to the list over time. Not all the roles exist in Microsoft 365 tenants, but you can expect to find at least twenty roles, including:

  • Global administrator.
  • Exchange administrator.
  • SharePoint administrator.
  • Teams administrator.
  • Compliance administrator.
  • Reports reader.
  • User administrator.
  • Helpdesk administrator.

These are Azure AD roles. Another set of compliance roles exist like “Fraud Investigators” and “Disposition Processors,” used to grant the ability to perform information governance and protection tasks. As explained in this post, the Exchange Online Get-RoleGroup cmdlet reveals both types, but for the purpose of this article, I focus on the assignable Azure AD roles.

Finding the List of Roles

In my tenant, there are 31 Azure AD roles in use. I know this because I ran the Get-MgDirectoryRole cmdlet. Here’s what I see (list trimmed for space):

[array]$DirectoryRoles = Get-MgDirectoryRole | Sort DisplayName
$DirectoryRoles | Format-Table DisplayName, Id

DisplayName                                Id
-----------                                --
Attack Simulation Administrator            f28eae22-7444-492d-bb0a-d3e41f8f9d4f
Attribute Assignment Administrator         b1042b1d-c84a-448e-9ca8-e6ad85e4fb65
Attribute Definition Administrator         327df668-5ea8-4ad8-b153-75d326b3c7d1
Azure AD Joined Device Local Administrator 268030c9-556f-47a6-a167-5970cb734558
Billing Administrator                      07308ce7-381b-4fb1-b31e-398b8a66c946
Compliance Administrator                   88b6939a-ef4b-4e8e-9aba-00f4f8447e66
Compliance Data Administrator              fdc30bad-3a16-4c24-ac89-79b73ad9468d
Customer LockBox Access Approver           1402c923-f478-4a9c-82b1-0511726c43bd

This is functionally equivalent to running the Get-AzureADDirectoryRole cmdlet from the soon-to-deprecated Azure AD module. Other Azure AD administrative roles exist but haven’t been used. You can see these roles by running the Get-MgDirectoryRoleTemplate cmdlet.

The role identifier is the important piece of information because we need this to assign a role to a user account. To store the role identifier in a variable to make it easier to use, we filter it from the set of roles. For instance, these commands return the role identifier for the Global administrator and Teams administrator roles:

$GlobalAdminRoleId = $DirectoryRoles | ? {$_.DisplayName -eq "Global administrator"} | Select -ExpandProperty Id
$TeamsAdminRoleId  = $DirectoryRoles | ? {$_.DisplayName -eq "Teams administrator"} | Select -ExpandProperty Id

Assigning Azure AD Roles to User Accounts

The New-MgDirectoryRoleMemberByRef cmdlet assign an Azure AD role to a user account. This cmdlet works like the New-MgGroupOwnerByRef cmdlet used to assign a new user to an Azure AD group (see this post about group management with the Microsoft Graph PowerShell SDK).

In this example, we first fetch the identifier for a user account and the set of current holders of the role. We then check the user identifier against the set of current role holders and if the account is not in the list, we assign the role using New-MgDirectoryRoleByRef:

$User = Get-MgUser -UserId Ken.Bowers@Office365itpros.com
$RoleMembers = Get-MgDirectoryRoleMember -DirectoryRoleId $TeamsAdminRoleId
If ($User.Id -notin $RoleMembers.Id) {
  Write-Host ("Adding user {0} to the Teams administrator role" -f $User.DisplayName)
  New-MgDirectoryRoleMemberByRef -DirectoryRoleId $TeamsAdminRoleId -BodyParameter @{"@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/$($user.Id)"}
}

By comparison, the equivalent Azure AD cmdlet is simpler:

Add-AzureADDirectoryRoleMember -ObjectId $TeamsAdminRoleId -RefObjectId $User.Id

To check that the correct account was added to the Azure AD role, fetch the set of updated role members and loop through the set to retrieve the display name of each role holder:

$RoleMembers = Get-MgDirectoryRoleMember -DirectoryRoleId $TeamsAdminRoleId
ForEach ($RoleMember in $RoleMembers) {
    Write-Host $RoleMember.AdditionalProperties["displayName"]}

Alternatively, check the assignments through Azure AD admin center (Figure 1):

Checking Azure AD role assignments in the Azure AD admin center
Figure 1: Checking Azure AD role assignments in the Azure AD admin center

There doesn’t appear to be a cmdlet available currently to remove a role assignment comparable to the Remove-AzureADDirectoryRoleMember cmdlet. This might be because Microsoft hasn’t yet added such a cmdlet to the SDK. Until they do, run the Azure AD cmdlet or use a graph API query to remove role assignments – or remove the assignments from the Azure AD admin center.

More Learning Required

Like many topics related to the Microsoft Graph PowerShell SDK, some more work is required to fully understand the intricacies of role assignment management, especially in deleting assignments. The good news is that this area is under active development. We can but wait for progress.


Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2022/03/30/azure-ad-role-assignments/feed/ 0 54307
Basic User Account Management with the Microsoft Graph PowerShell SDK https://office365itpros.com/2022/03/24/entra-id-user-accounts-powershell/?utm_source=rss&utm_medium=rss&utm_campaign=entra-id-user-accounts-powershell https://office365itpros.com/2022/03/24/entra-id-user-accounts-powershell/#comments Thu, 24 Mar 2022 01:00:00 +0000 https://office365itpros.com/?p=54188

Preparing to Migrate Away from Old AzureAD cmdlets

Updated: 15 March, 2023

Manage Entra ID user accounts

I received a lot of reaction when I described Microsoft’s new deprecation schedule for the AzureAD and MSOL modules. In summary, you have until 30 March 2024 to update scripts which assign licenses to user accounts. After this, Microsoft will disable the cmdlets. The other cmdlets will continue working after Microsoft deprecates the modules. However, they’ll be out of support, which is not a good foundation for PowerShell scripts used to automate administrative processes, like managing Entra ID user accounts.

With time running out, it’s obvious that tenants need to inventory and upgrade scripts. One reaction I received was that there’s a dearth of information to help people who are less familiar with PowerShell and might have inherited ownership of some scripts. My response is that the community will publish examples over time, just like they did when Microsoft launched the AzureAD module in 2016 and the Exchange Online management REST-based cmdlets at Ignite 2019. Let’s hope this is true.

Over on Practical365.com, I compare creating a new Entra ID user account and assigning licenses to the account using both the old AzureAD module and the Microsoft Graph PowerShell SDK. In this post, I consider some additional basic user account management actions.

Connections

The basics of using the Microsoft Graph PowerShell SDK (the SDK) is to connect. You can connect interactively (delegated access) or with certificate-based authentication (application access). You can also run SDK cmdlets in Azure Automation runbooks. The simplest approach is to run Connect-MgGraph interactively, which signs into the Graph using the account you signed into PowerShell with.

Scopes

SDK cmdlets interact with Microsoft Graph APIs. A big difference between the SDK and AzureAD modules is that the SDK forces you to request the set of Graph permissions you want to use. The SDK uses a service principal to hold the permissions, and over time, that service principal might become overly permissioned. It’s a thing to keep an eye on.

In this example, we define an array of Graph permissions we wish to use, and then connect. If you request a permission that the SDK service principal doesn’t already hold, you’ll see an administrator prompt for consent.

$RequiredScopes = @("Directory.AccessAsUser.All", "Directory.ReadWrite.All", "User.ReadWrite.All", “User.Read.All”)
Connect-MgGraph -Scopes $RequiredScopes -NoWelcome

Welcome To Microsoft Graph!

Updating Properties for Entra ID User Accounts

Let’s assume that you’ve created the Sue.Ricketts@Office365itpros.com account using the New-MgUser cmdlet as described in this article and stored the user identifier for the account in the $UserId variable.

$UserId = (Get-MgUser -UserId Sue.Ricketts@office365itpros.com).Id

To update the properties of a user account, run the Update-MgUser cmdlet.

Update-MgUser -UserId $UserId -JobTitle "Senior Editor" -State NY

Updating Email Properties for an Account

You can’t update the proxyAddresses property of a user account because the Graph treats it as read-only, possibly because Exchange Online takes care of email proxy address management. However, if you change the UserPrincipalName property of an account, Update-MgUser sets the primary SMTP address of the account to match the new user principal name. The logic here is likely that it is best practice to match the user principal name and primary SMTP address. In most cases, this is true and it’s a good idea to have the cmdlet behave like it does. However, in some circumstances, you might decide to have different values in these properties.

In both situations, you should use the Exchange Online Set-Mailbox cmdlet to update proxy addresses. For example, this command adds a new SMTP proxy address to the mailbox identified by the $UserId variable:

Set-Mailbox -Identity $UserId -EmailAddresses @{Add="Johnnie.West@Office365itpros.com"}

This command updates the primary SMTP address for the mailbox without changing the user principal name:

Set-Mailbox -Identity $UserId -WindowsEmailAddress Johnnie.West@Office365itpros.com

Exchange Online uses a dual-write mechanism to make sure that any change made to mailboxes happens simultaneously to the underlying user account.

Updating a User’s Manager

The manager of a user account is updated by reference (to their account) rather than simply updating a property. To update the manager of a user account, run the Set-MgUserManagerByRef cmdlet after storing the identifier of the manager’s account in a variable:

$ManagerId = (Get-MgUser -UserId Terry.Hegarty@office365itpros.com).Id
Set-MgUserManagerByRef -UserId $UserId `
   -AdditionalProperties @{
     "@odata.id" = "https://graph.microsoft.com/v1.0/users/$ManagerId" }

To check that the manager update was successful, we need to fetch the manager’s details (expanded into a dictionary object) and retrieve the property we want.

$ManagerData = Get-Mguser -UserId $UserId -ExpandProperty Manager
$ManagerData.Manager.AdditionalProperties['displayName']
Terry Hegarty

You can also use the Get-MgUserManager cmdlet to return the manager of an account.

Get-MgUserManager -UserId Chris.Bishop@Office365itpros.com | Select-Object @{n="DisplayName";e={$_.AdditionalProperties.displayName}},@{n="UserPrincipalName";e={$_.AdditionalProperties.userPrincipalName}}

DisplayName UserPrincipalName
----------- -----------------
James Ryan  James.Ryan@office365itpros.com

Obviously, Microsoft has made defining and retrieving the manager of an account more complex than it needs to be. It would be nice if they would hide the complexity in code and deliver some straightforward cmdlets that don’t create friction when the time comes to update scripts.

Another way of updating user account properties is with the Invoke-MgGraphRequest cmdlet, which runs a Graph API query. The advantage of this cmdlet is that if you can’t find a way to do something with an SDK cmdlet, you can refer to the Microsoft Graph documentation, find some example code, and run or repurpose it.

In this example, we create a hash table to hold the properties we want to update, convert the table to a JSON object, and pass it to a PATCH query run by Invoke-MgGraphRequest:

$Parameters = @{
   JobTitle = "Managing Editor, Periodicals"
   State = "Vermont"
   OfficeLocation = "Burlington" } | ConvertTo-Json
Invoke-MgGraphRequest -Method PATCH -Uri "https://graph.microsoft.com/v1.0/users/Sue.Ricketts@office365itpros.com" -Body $Parameters -ContentType "application/json; charset=utf-8"

Delete a User Account

The Remove-MgUser cmdlet soft-deletes a user account and moves it into Entra ID’s deleted items container, where it remains for 30 days until Entra ID permanently deletes the object. The cmdlet is very simple, and it doesn’t prompt for confirmation before proceeding to delete a user account.

Remove-MgUser -UserId $UserId

If you need to restore a soft-deleted account, run the Restore-MgUser cmdlet and pass the object identifier of the account you want to restore. See this article for information about how to list the set of soft-deleted user accounts.

Restore-MgUser -UserId $UserId

I’ve experienced some issues with the Restore-MgUser cmdlet in the 1.9.3 release of the SDK which I have reported to Microsoft. Basically, the cmdlet doesn’t work in this release. I’m sure the bug will be fixed soon.

Finding User Accounts

We’ve already seen how the Get-MgUser cmdlet fetches information for an individual user account. It also fetches sets of accounts. To fetch all the accounts in the tenant, run:

[array]$Users = Get-MgUser -All

I always specify that the variable used as the target for a set of objects is an array. This makes it easy to find how many objects are returned, as in:

Write-Host $Users.Count “User accounts found”

Note that unlike Graph API queries, the Get-MgUser cmdlet takes care of data pagination for the query and fetches all available objects.

If you don’t specify the All switch, the cmdlet fetches the first 100 accounts. You can fetch a specific number of accounts using the Top parameter, up to a maximum of 999.

[array]$Top500 = Get-MgUser -Top 500

The Filter parameter uses server-side filtering to restrict the amount of data returned. For instance, here’s how to find all the guest accounts in a tenant:

[array]$Guests = Get- MgUser -Filter "usertype eq 'Guest'" -All

While this filter returns the accounts who usage location (for Microsoft 365 services) is the U.S.

Get-MgUser -Filter "usagelocation eq 'US'"

You can combine properties in a filter. For example:

Get-MgUser -Filter "usagelocation eq 'US' and state eq 'NY'"

Another interesting filter is to find accounts created in a specific date range. This command finds all tenant non-guest accounts created between January 1, 2022 and Matrch 24. Note the trailing Z on the dates. The Graph won’t treat the date as valid if the Z is not present.

Get-MgUser -Filter "createdDateTime ge 2022-01-01T00:00:00Z and createdDateTime le 2022-03-24T00:00:00Z and usertype eq ‘Member’"

Support for SDK Problems via GitHub

Hopefully, the examples listed above are useful in terms of understanding the SDK cmdlets to perform basic management of Entra ID user accounts. If you run into a problem when converting scripts to use SDK cmdlets, you can report the problem (or browse the current known issues) on GitHub. Happy migration!

]]>
https://office365itpros.com/2022/03/24/entra-id-user-accounts-powershell/feed/ 9 54188
Delete and Restore Entra ID User Accounts with the Microsoft Graph PowerShell SDK https://office365itpros.com/2022/03/23/delete-entra-id-user-accounts/?utm_source=rss&utm_medium=rss&utm_campaign=delete-entra-id-user-accounts https://office365itpros.com/2022/03/23/delete-entra-id-user-accounts/#comments Wed, 23 Mar 2022 01:00:00 +0000 https://office365itpros.com/?p=54175

Understanding How to Delete Entra ID User Accounts and Restore Them Afterwards is a Critical Skill

According to message center notification MC344406 (18 March), in early April Microsoft plans to roll-out the capability of recovering deleted service principal objects. Service principals are critical parts of registered Entra ID apps, such as the apps used to execute Microsoft Graph API queries with PowerShell. They’re also used in Azure Automation accounts, the Microsoft Graph PowerShell SDK, and managed identities. In all cases, the service principals hold the permissions needed for an app or account to operate. The worldwide roll-out to all tenants should complete by late May.

When the capability is available, any time an administrator deletes a service principal (for instance, because a registered app is no longer needed) using the Entra admin center, PowerShell (using Remove-AzureADServicePrincipal), or the Microsoft Graph API, Entra ID will place the service principal into a soft-deleted state. This already happens today for user, group, device, and application objects.

Deleted Entra ID objects stay in the deleted items container for 30 days. When the retention period elapses (extending to maybe a few days afterwards), Entra ID proceeds to permanently delete the object.

During the retention period, administrators can restore an object, which makes it easy to recover if someone deletes an important item by accident. For now, the list deleted items API doesn’t support service principals, but it will after the roll-out. Figure 1 shows user objects in the deleted items container as viewed through the Graph Explorer.

Viewing deleted Entra ID user accounts via the Graph Explorer

Delete Entra ID user account
Figure 1: Viewing deleted Entra ID user accounts via the Graph Explorer

Using Old Azure AD Cmdlets

MC344406 features two cmdlets from the Azure AD Preview module:

In some respects, it’s odd that they use cmdlets based on the Azure AD Graph API because Microsoft has scheduled the Azure AD modules for retirement in March 2024.

Of course, apart from the licensing management cmdlets, the rest of the Azure AD cmdlets will continue to work after retirement, which makes it perfectly acceptable to specify the cmdlets now, especially if replacements in the Microsoft Graph PowerShell SDK are unavailable.

Using Microsoft Graph PowerShell SDK Cmdlets to Delete Entra ID User Accounts

The Microsoft Graph PowerShell SDK can be difficult to navigate. It’s composed of 38 separate sub-modules. Although cmdlets are gathered logically, it can still be hard to find the right cmdlet to do a job. As you’d expect, the current version (1.9.3) doesn’t appear to include cmdlets to handle soft-deleted service principal objects. For now, we can see how to perform common administrative actions with user accounts as a guide to what should be available for service principals.

With that in mind, here are the steps to soft-delete user accounts, list the accounts in the deleted items container, and hard-delete (permanently remove) an account.

Soft-Delete an Entra ID User Account

To soft-delete an Entra ID account, run the Remove-MgUser and pass the object identifier or user principal name of the account to delete. The cmdlet does not prompt for a confirmation and deletes the account immediately:

Remove-MgUser -UserId Sue.Ricketts@office365itpros.com

List Soft-Deleted Entra ID User Accounts

During the 30-day retention period in the deleted items container, you can recover the account from the Entra admin center or by running the Restore-MgUser cmdlet. Before we can run Restore-MgUser, we need to know the object identifiers of the objects in the deleted items container. This code:

  • Uses the Get-MgDirectoryDeletedItemAsUser cmdlet to fetch the list of deleted user accounts. The Property parameter can be ‘*’ to return all properties of the deleted objects, but in this case, I’ve chosen to limit the set of properties to those that I want to use.
  • Loops through the data returned by Entra ID to extract the properties we want to use. The different behaviour of the Azure AD cmdlets and the Microsoft Graph PowerShell SDK cmdlets is an example of why tenants need to plan the upgrade and testing of scripts which use old cmdlets.
  • Lists the soft-deleted user accounts.
[array]$DeletedItems = Get-MgDirectoryDeletedItemAsUser -All -Property Id, DisplayName, DeletedDateTime, UserPrincipalName, Usertype
If ($DeletedItems.count -eq 0) { 
   Write-Host "No deleted accounts found - exiting"; break 
}

$Report = [System.Collections.Generic.List[Object]]::new()

ForEach ($Item in $DeletedItems) {
    $DeletedDate = Get-Date($Item.deletedDateTime)
    $ReportLine = [PSCustomObject][Ordered]@{ 
           UserId   = $Item.Id
           Name     = $Item.displayName
           Deleted  = $DeletedDate
           "Days Since Deletion" = (New-TimeSpan $DeletedDate).Days
           Type     = $Item.userType
     }
    $Report.Add($ReportLine)
}

UserId                               Name                      Deleted             Days Since Deletion Type
------                               ----                      -------             ------------------- ----
92cef396-1bd3-4296-b06f-786e2ee09077 The Maestro of Office 365 19/02/2022 17:36:44                  31 Guest
c6133be4-71d4-47c4-b109-e37c0c93f8d3 Oisin Johnston            26/02/2022 18:13:26                  24 Member
2e9f1189-d2d9-4301-be57-2d66f3df6bb1 Jessica Chen (Marketing)  04/03/2022 11:52:48                  18 Member
8cd64635-bce6-4af0-8e64-3bebe354e9a4 Alex Redmond              05/03/2022 17:36:45                  17 Member
0f16501c-8302-468a-99a6-78c22b0903d2 Jennifer Caroline         18/03/2022 21:33:13                   3 Member
3a6116ab-0116-490e-bd60-7e0cd9f36c9d Sue Ricketts (Operations) 20/03/2022 19:53:29                   2 Member
4a25ccf0-17df-42cf-beeb-4fd449531b47 Stephen Rice              22/03/2022 19:30:06                   0 Guest

To restore a soft-deleted user account, run the Restore-MgDirectoryDeletedItem cmdlet and pass the account’s identifier. After restoring the account, remember to assign licenses to allow the account to access Microsoft 365 services.

Restore-MgDirectoryDeletedItem -DirectoryObjectId 3a6116ab-0116-490e-bd60-7e0cd9f36c9d

Remove Soft-Deleted Entra ID User Account

To remove a soft-deleted directory object, run the Remove-MgDirectoryDeletedItem cmdlet and pass the object identifier. Like Remove-MgUser, the cmdlet doesn’t ask for confirmation and permanent deletion happens immediately.

Remove-MgDirectoryDeletedItem -DirectoryObjectId f9d30b84-ad5f-4151-98f0-a55dafe30829

Time of Transition

We’re in a time of transition now as Microsoft does its best to retire the Azure AD modules and build the capabilities (and hopefully the documentation) of the Microsoft Graph PowerShell SDK. In the intervening period, any time you see an example using Azure AD cmdlets, try to convert it to use the SDK. It’s a great way to learn.


Keep up to date with developments like the Microsoft Graph PowerShell SDK by subscribing to the Office 365 for IT Pros eBook. Our monthly updates make sure that our subscribers understand the most important changes happening across Office 365.

]]>
https://office365itpros.com/2022/03/23/delete-entra-id-user-accounts/feed/ 10 54175
Why It’s Difficult to Transfer Membership Rules from Exchange Online to Azure AD https://office365itpros.com/2022/03/18/membership-rules-exchange-teams/?utm_source=rss&utm_medium=rss&utm_campaign=membership-rules-exchange-teams https://office365itpros.com/2022/03/18/membership-rules-exchange-teams/#comments Fri, 18 Mar 2022 01:00:00 +0000 https://office365itpros.com/?p=54000

Dynamic Distribution Lists to Dynamic Microsoft 365 Groups

Earlier this week, I described how to create a Microsoft 365 group and team from an Exchange Online dynamic distribution list. The code creates a group with static membership, but the input dynamic distribution list has its membership computed by Exchange Online using a recipient filter (aka a membership rule). Why can’t we take the filter used by the dynamic distribution list and apply it to create a dynamic Microsoft 365 group, which in turn becomes a team with dynamic membership. Well, as it turns out, it’s not quite as simple as taking a filter from one Microsoft 365 workload and using it in another.

Translating Recipient Filters for Dynamic Microsoft 365 Groups

Conceptually, it is possible to convert a dynamic distribution list to a be the membership rule for a dynamic Azure AD group. Two challenges exist: filter syntax and filter properties.

The query stored in a dynamic distribution list looks like this:

((((((Title -eq 'Architect') -or (Title -eq 'Senior Architect'))) -or (((Title -eq 'Principal Architect') -and (ExchangeUserAccountControl -ne 'AccountDisabled'))))) -and (-not(Name -like 'SystemMailbox{*')) -and (-not(Name -like 'CAS_{*')) -and (-not(RecipientTypeDetailsValue -eq 'MailboxPlan')) -and (-not(RecipientTypeDetailsValue -eq 'DiscoveryMailbox')) -and (-not(RecipientTypeDetailsValue -eq 'PublicFolderMailbox')) -and (-not(RecipientTypeDetailsValue -eq 'ArbitrationMailbox')) -and (-not(RecipientTypeDetailsValue -eq 'AuditLogMailbox')) -and (-not(RecipientTypeDetailsValue -eq 'AuxAuditLogMailbox')) -and (-not(RecipientTypeDetailsValue -eq 'SupervisoryReviewPolicyMailbox')))

We know this is a custom recipient filter created using PowerShell because the properties it uses are not covered by the precanned filters created using EAC. The custom filter comes first followed by a bunch of exclusions inserted by Exchange to make sure that system mailboxes are not in the returned set. Exchange adds these exclusions automatically when it saves the recipient filter for a dynamic distribution list.

It’s technically possible to take a recipient filter from a dynamic distribution list and parse it to extract the custom part using Regex expressions. By using a function to remove special characters, I was able to process the recipient filter shown above like this:

$Filter = (Get-DynamicDistributionGroup -Identity "System Architects").RecipientFilter
$i = $Filter.IndexOf("-and (ExchangeUser")
$f = $Filter.Substring(0,$i)
$ExoFilter = Remove-StringSpecialCharacter -String $f -SpecialCharacterToKeep '-', " "

The output is:

Title -eq Architect -or Title -eq Senior Architect -or Title -eq Principal Architect

However, a complicating factor is that Exchange has changed the format of the exclusions it inserts over time. This means that you can never be sure how the recipient filter is formatted, and my code didn’t work when tested against several other dynamic distribution lists in my tenant, some of which go back to 2014.

In any case, the output I generated isn’t a valid Azure AD filter, and some additional work is needed to make it work with a dynamic Azure AD group (team). Briefly:

  • Title is the name of the Exchange property. It is JobTitle in Azure AD. Also, user properties are prefixed with “User,” meaning that you end up with User.JobTitle.
  • The -eq and -or operators in Exchange lose the leading hyphen in Azure AD.

Different Filterable Properties

A more fundamental issue is that while Exchange supports many mail-enabled properties for custom recipient filters in dynamic distribution lists, the set of filterable properties don’t match the set available for Azure AD. You might be able to convert some queries, but you won’t be able to convert others. The difference is accounted for by the fact that Exchange queries against its own directory, which stores details of mail-enabled objects, while Azure AD queries its directory. The two directories have different schemas.

Once I realized the extent of the incompatibility between the two sets of properties, I stopped trying to figure out how an automatic conversion could be done. Too much time would be needed to figure out the permutations and combinations involved in formatting membership rules. And given the number of times a conversion might be necessary, the easiest solution is to let human administrators generate the membership rules.

Previewing Azure AD Filters

The GUI in the Azure AD admin center to deal with dynamic groups include a rules editor. You can paste the outline of a membership rule taken from an Exchange dynamic distribution list and modify it there. The Azure AD admin center also includes a nifty preview feature to validate that a membership rule works. After making whatever changes are necessary to create a valid rule for Azure AD, you can test the rule by nominating one or more users that you know should match the membership rule. Click the validate button and Azure AD will tell you if the directory can find the users you selected using the rule (Figure 1).

Azure AD checks the membership rule for a dynamic Microsoft 365 group
Membership filter
Figure 1: Azure AD checks the membership rule for a dynamic Microsoft 365 group

Exchange Online doesn’t have a similar way to validate the membership of a dynamic distribution list. Maybe that’s why Microsoft considers dynamic Azure AD groups to be a premium feature and charges accordingly.

Creating a Dynamic Azure AD Group with PowerShell

For the record, you can create a dynamic Azure AD group with PowerShell. In this instance, I use the New-MgGroup cmdlet from the Microsoft Graph PowerShell SDK (the New-AzureADMSGroup cmdlet from the preview version of the Azure AD module will work too). The important point is that the group has dynamic membership rather than static and has a rule to control the membership:

$Group = New-MgGroup -DisplayName "System Architects (Dynamic x2)" -Description "People with an architect job title" -MailEnabled:$True -SecurityEnabled:$True -MailNickName "System.Architects.Dynamic2" -GroupTypes "DynamicMembership", "Unified" -MembershipRule "(User.JobTitle -eq ""Architect"" or  User.JobTitle eq ""Senior Architect

After creating the dynamic Azure AD group, you can team-enable it with the New-Team cmdlet by passing the identifier of the newly created group.

New-Team -GroupId $Group.Id

Incompatible schemas, properties, and syntax might stop the automatic conversion of membership rules, but you can at least get the job done with a little manual effort.


Learn more about how the Office 365 applications really work on an ongoing basis by subscribing to the Office 365 for IT Pros eBook. Our monthly updates keep subscribers informed about what’s important across the Office 365 ecosystem.

]]>
https://office365itpros.com/2022/03/18/membership-rules-exchange-teams/feed/ 1 54000
Microsoft Sets New Deprecation Schedule for Azure AD PowerShell https://office365itpros.com/2022/03/17/azure-ad-powershell-deprecation/?utm_source=rss&utm_medium=rss&utm_campaign=azure-ad-powershell-deprecation https://office365itpros.com/2022/03/17/azure-ad-powershell-deprecation/#comments Thu, 17 Mar 2022 01:00:00 +0000 https://office365itpros.com/?p=54064

What You Need to Do Before Azure AD and MSOL Modules Retire

Azure AD PowerShell retirement

Microsoft has recently been beating the drum about the retirement of the Azure AD PowerShell module and its older Microsoft Online Services (MSOL) counterpart. On March 3, the Azure AD team posted in the Microsoft Technical Community to say that they had listened to customer feedback and pushed the termination of support out from the end of June to the end of 2022. On September 30, Microsoft set a new retirement date for the Azure AD and MSOL modules for June 30, 2023. Things tend to happen around the end of June to align with the end of Microsoft’s financial year and allow everyone to start the new year afresh.

The salient points in message center notification MC281145 are:

  • Reaffirmation that Microsoft will not retire the Azure AD Graph API on June 30, 2022. The Azure AD Graph is the component which underpins the Azure AD and MSOL modules. It’s a Graph API built especially for Azure AD before the Microsoft Graph established its position as the common API for Microsoft 365. The Azure AD team wants to deprecate their Graph API and embrace the Microsoft Graph, which is the basic reason for the planned deprecation of the Azure AD and MSOL modules.
  • Confirmation that the subset of the cmdlets in the Azure AD and MSOL modules which deal with user licensing will stop working earlier than the rest of the other cmdlets. Quite apart from the desire to move to the Microsoft Graph, these cmdlets are affected because Microsoft is moving to a new licensing management platform. Originally, the scheduled date for the transition was June 30, 2022. Microsoft pushed the date out eight weeks to August 26, 2022 and now it’s March 31, 2023. After this date, license management cmdlets like Get-AzureADSubscribedSKU won’t work.
  • Reconfirmation that the Microsoft Graph PowerShell SDK is the way forward.

Shifting Dates

The deprecation date for the Azure AD and MSOL modules is shifting. Originally, this was June 2022, then the end of 2022, and now it’s June 2023. Clearly, customer feedback has told Microsoft that it’s going to be difficult to update PowerShell scripts before Microsoft wants to retire these modules. ISV products which use the modules or the Azure AD Graph API must also be updated before the axe descends. See Microsoft’s FAQ for help in identifying other applications which use the Azure AD Graph API.

Update (July 29): Microsoft has pushed out the retirement of the Azure AD and MSOL license management cmdlets to 31 March 2023.

No matter which way you turn, the basic fact is that Microsoft will eventually retire the Azure AD and MSOL modules. It’s time to update scripts now, with the priority order being:

  • Scripts that manage licenses for Azure AD accounts (before August 26, 2022). This example of creating a license management report might help get you started.
  • Scripts that perform other Azure AD management operations (ideally before the end of 2022).

Microsoft Documents Its Migration Approach

To help, Microsoft has created some documentation for steps to migrate scripts. The most important statement is “There is currently no tool to automatically converts scripts in Azure AD PowerShell to Microsoft Graph PowerShell.” I doubt that any automatic script migration tool will appear. There are just too many variations in how people code with PowerShell to guarantee that a tool could handle even moderately complex scripts. The potential to create a support nightmare is one reason why I think Microsoft won’t produce a migration tool.

Which leaves us with Microsoft’s simple three-step approach to script migration:

  • Find the Microsoft Graph equivalent of your Azure AD PowerShell cmdlets from the Cmdlet map.
  • Select the Microsoft Graph cmdlet to view the reference documentation and get the new syntax for the cmdlet.
  • Update your existing scripts using the new syntax.

Testing might be a good fourth step to add. And before you start, you need to create an inventory of scripts which use Azure AD or MSOL cmdlets.

Migration Tips

At first glance, the process seems straightforward. In many cases, it is, and you won’t have huge difficulty in converting Get-AzureADUser with Get-MgUser. Microsoft notes some limitations, to which I add:

  • Don’t depend on the Microsoft Graph PowerShell SDK documentation for help with basic information like the format of input parameters. The documentation is machine-created and is shockingly bad in terms of its ability to guide people with real-life examples.
  • The SDK cmdlets are based on Graph API queries and often the documentation for those queries will help you understand how cmdlets work and the parameter values they expect.
  • The Graph Explorer is an excellent tool to help understand how Graph queries run and what they return.
  • Pay attention to parameters and switches. Some parameters of SDK cmdlets require a colon between the parameter and the value where an Azure AD or MSOL cmdlet does not. Some parameter and switch names change.
  • Don’t plan to run SDK scripts interactively. It will only lead to an accumulation of permissions for the service principal used by the SDK.
  • The SDK cmdlets handle pagination when necessary to retrieve all matching objects. Usually, there’s an -All parameter to help (like Get-MgGroup -All).
  • Sometimes you’ll need to use certificate-based authentication and a separate Azure AD registered to gain administrative access to data. The Teams tags report is a good example of when this technique is necessary.

We Feel Your Pain

The Office 365 for IT Pros eBook writers are busy converting script examples to use the Microsoft Graph PowerShell SDK. We plan to have everything done over the next few months. On one level, it’s a pain to be forced to find and upgrade scripts. On another, it’s an opportunity to revamp scripts to make them work better. Perhaps you might even consider moving some of your long-running scripts to Azure Automation?


So much change, all the time. It’s a challenge to stay abreast of all the updates Microsoft makes across Office 365. Subscribe to the Office 365 for IT Pros eBook to receive monthly insights into what happens, why it happens, and what new features and capabilities mean for your tenant.

]]>
https://office365itpros.com/2022/03/17/azure-ad-powershell-deprecation/feed/ 6 54064
Creating an Authentication Method Report for Entra IAccounts https://office365itpros.com/2022/03/03/azure-ad-accounts-authentication/?utm_source=rss&utm_medium=rss&utm_campaign=azure-ad-accounts-authentication https://office365itpros.com/2022/03/03/azure-ad-accounts-authentication/#comments Thu, 03 Mar 2022 01:00:00 +0000 https://office365itpros.com/?p=53754

Moving from Old Modules to the Microsoft Graph SDK for PowerShell

Update: This article describes a script to generate a report showing the MFA status for accounts and highlights administrative accounts that aren’t MFA-enabled.

Microsoft 365 tenants often create reports to understand the health of their Azure AD accounts. In June 2021, Microsoft announced that they are moving away from the Microsoft Online Services (MSOL) and Azure AD PowerShell modules and would deprecate the Azure AD Graph API at the end of June 2022. Customer feedback convinced Microsoft to push the deprecation date out to the end of 2022, and then to March 2024. However, the writing is on the wall for the Azure AD Graph API and it’s time to move to Graph-based interfaces.

Their future focus for PowerShell access to Entra ID data is the Microsoft Graph SDK for PowerShell. The only definite drop-dead date affecting the older modules is June 30, 2022, when Microsoft moves to a new license management platform. At this point, any cmdlet which retrieves license information for user accounts will cease working. Any scripts used for tenant license management that haven’t been updated now require urgent attention.

I have an MFA status script written in 2018. The script uses the MSOL cmdlets to check and report the “strong authentication methods” for each user account. The state of multi-factor authentication has moved on since 2018 to accommodate new methods like FIDO2 keys and passwordless authentication. These methods aren’t handled by the MSOL cmdlets. It’s therefore time to take a different approach.

Given Microsoft’s direction to use the Microsoft Graph SDK for PowerShell for Azure AD access, it seems like this is the right path to follow. I’ve done a reasonable amount with the SDK and have written several articles to report my progress. For example, here’s how to generate a licensing report for a tenant.

Not a Perfect SDK

The SDK is not perfect. Essentially, its cmdlets are wrappers around Graph API queries. If you’re used to dealing with Graph APIs, the SDK cmdlets will be second nature. If not, it will take time to become familiar.

Getting acquainted isn’t helped by the poor state of the documentation for the SDK cmdlets. Microsoft uses automatic text generation tools to create the documentation from source code. The result is often as useful as a snowball in a desert, especially in terms of practical examples and explanations about inputs. Again, if you know the Graph APIs, you probably won’t be surprised at what you find in the documentation, but the current state of the documentation is no credit to Microsoft and a barrier to adoption.

Steps in the Creation of the Report

The aim is to create a report showing the authentication methods used by Azure AD accounts and highlight accounts which might attention (hopefully, by enabling multi-factor authentication). According to a recent Microsoft report, only 22% of Microsoft 365 accounts use multi-factor authentication. Although this marks an improvement over the past, it’s still far too low a percentage. Perhaps people don’t know about recent improvements Microsoft has made in MFA processing to make it easier for people to use.

The script I wrote to create the report does the following:

  • Connects to the Graph endpoint with the following permissions: UserAuthenticationMethod.Read.All, Directory.Read.All, User.Read.All, Auditlog.Read.All. If the service principal for the SDK doesn’t have administrat9or consent of any permission, it must be granted at this point.
  • Set the Graph profile to beta to make sure that we have access to all the user account data.
  • Call the Get-MgUser cmdlet to fetch the set of Azure AD member accounts. The set returned excludes guest accounts but includes the accounts used for shared and resource mailboxes. To focus solely on licensed member accounts, you could apply a filter to look for accounts with at least one assigned license.
  • Loop through each account to check its authentication methods. To ensure that only active accounts are checked, the script calls the Get-MgAuditLogSignIn cmdlet to look for a sign-in record. This check can only go back 30 days.
  • For each active account, call the Get-MgUserAuthenticationMethod cmdlet to return the authentication methods used by the account. An account can use all the valid authentication methods from passwordless to the Microsoft authenticator app. The first time an account uses a method, Azure AD adds it to the list used by the account.
  • For each method, retrieve some information.
  • Report the results of checking each method.
  • After processing all user accounts, create a list of users for which an authentication method is available and check each account to see if one of the strong (MFA) methods is noted.
  • Create a final report and output it to a CSV file.

Figure 1 shows an example of the kind of output generated.

Azure AD Accounts and authentication methods
Figure 1: Authentication methods report for Azure AD accounts

You can download the script from GitHub. As always, the code is intended to illustrate a principal rather than being a fully-baked solution.

Automating the Check

A script like this is a good candidate for execution by an Azure Automation runbook. It can be run on a schedule and the results emailed to administrators for action. Getting a weekly or monthly reminder to improve the security posture of the organization by increasing the percentage of well-protected accounts can only be a good thing, can’t it?


Learn how to exploit the data available to Microsoft 365 tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work.

]]>
https://office365itpros.com/2022/03/03/azure-ad-accounts-authentication/feed/ 9 53754
Understanding How App Certification for Microsoft 365 Apps Works https://office365itpros.com/2022/02/23/app-certification-microsoft365/?utm_source=rss&utm_medium=rss&utm_campaign=app-certification-microsoft365 https://office365itpros.com/2022/02/23/app-certification-microsoft365/#comments Wed, 23 Feb 2022 01:00:00 +0000 https://office365itpros.com/?p=53581

The Many Ways to Stress the Need for Modern Authentication

By now, all Microsoft 365 tenant administrators should be aware that Microsoft is removing support for basic authentication for many Exchange Online connectivity protocols. The aim is to complete the process by October 2022. SMTP AUTH is an exception, but Microsoft will deal with it in time.

What you might not be aware of is that access to Microsoft 365 data using modern authentication requires that the developers must register their app with Azure AD. This applies to any Microsoft API, including the Outlook add-in model and the Graph APIs. If you’ve written PowerShell scripts which make Graph queries, you know that you must register an app to receive consent for the Graph permissions necessary to access the target data. This is a basic registration. Registrations for more sophisticated apps like those sourced from ISVs contain more information about the app, such as a redirect URL for the app. Registration for ISV apps usually happens during the app installation, including the creation of a service principal to allow the app to run with API permissions consented to by tenant administrators.

Previously, I’ve written about the need for tenants to clean out application crud from Azure AD. The crud is composed of unwanted apps and their service principals accumulated over time in Azure AD. Being able to fetch sign-in data for service principals via Graph queries makes it easier to add context to this exercise by knowing what service principals are active.

After cleaning out obsolete applications, Azure AD might be tidy, but do you know much about the apps which remain? The application governance add-on for Microsoft Defender for Cloud Apps might help, but only if your tenant has the necessary licenses.

Microsoft’s App Compliance Program

Fortunately, Microsoft has an App Compliance Program, part of their Zero Trust initiative to help customers verify apps they might want to run in their tenant. App developers go through the process to achieve app certification by providing information about the app and the data it accesses. The program has three phrases or levels:

Publisher verification: The app developer has a Microsoft developer network identity. The app supports modern authentication and is capable of multi-tenant activity. This is the entry-level participation in certification.

Publisher attestation: The app developer completes a questionnaire covering security, data handling, and compliance.

Microsoft 365 certification: Instead of the app developer reporting details of their app, third-party assessors audit the assertions to validate that the app meets Microsoft standards for security and compliance. The process occurs annually, and details gathered during the audit is available online. Figure 1 shows details of a Microsoft certified app in AppSource. The audit information is available through the Microsoft 365 certification link for the app.

App certification information in AppSource
Figure 1: App certification information in AppSource

The app certification information available online (Figure 2) includes detail of the app permissions, including the reason why the app developers need administrator consent to use the permission.

App certification includes documenting API permissions
Figure 2: App certification includes documenting API permissions

Obviously, app developers must invest time and effort to satisfy Microsoft criteria for app certification. However, once completed, they should reap the benefits gained by increased customer confidence in their product. At least, that’s the theory.

Downgraded Certification

In April 2020, I reviewed the new Manage Apps section in the Teams admin center and commented on the Microsoft 365 certified status of the Wrike app. The number of apps available for Teams continues to expand (from 462 in April 2020 to 1,402 as I write this in February 2022, or roughly 44 new apps monthly). Checking the online list of Teams apps, it looks like very few apps are Microsoft 365 certified. This begs the question why app developers feel it unnecessary to go through Microsoft’s audit process – or why publishers of apps like Wrike downgraded their apps from certified to publisher attestation.

I’m sure cost has something to do with it, along with a feeling that customers don’t go looking for apps which are Microsoft 365 certified. If a developer gains no business advantage by completing the full certification process for their apps, why bother? It’s a reasonable perspective. Microsoft would obviously like developers to go the whole hog, but this might be an uphill battle.

One way that customers might help persuade developers that app certification is worthwhile is to allow users to grant consent for apps from verified publishers when apps require only “low-impact” permissions. The idea is that if less friction exists to deploy and use an app, it will be more popular and profitable.

The consent settings for a tenant are available in the Azure AD admin center (Figure 3) and include the ability to define what you consider to be low-impact permissions. In this case, the selected option allows users to grant consent, but only for three low-impact permissions such as the ability to read a user’s profile. Tenants can define what they consider to be low-impact permissions through the Permissions Classifications option shown in Figure 3.

Azure AD Consent and Permissions settings
Figure 3: Azure AD Consent and Permissions settings

Some will be uneasy about the prospect of users granting consents to apps. The safeguard is that consent is only possible for verified publishers; the counterargument is that developers can attain verification too easily to make this status truly valuable. If Microsoft 365 certified apps were the threshold, a different story might ensue. Microsoft recommends that it’s OK to allow users to grant consent to apps, but without stronger controls, this might be a stretch for many organizations.

The Rocky Road to App Certification

The situation is complex. Microsoft wants everyone to use modern authentication to access Microsoft 365. Getting to that position means a great deal of change for clients, apps, users, and organizations. Certification helps customers understand and control the access apps have to data in their tenant. That’s goodness, but only if ISVs co-operate and certify their products. Time enables change. While that happens, keep your app repository clean and tidy. You know it makes sense.


Learn more about how Office 365 really works on an ongoing basis by subscribing to the Office 365 for IT Pros eBook. Our monthly updates keep subscribers informed about what’s important across the Office 365 ecosystem.

]]>
https://office365itpros.com/2022/02/23/app-certification-microsoft365/feed/ 1 53581
How Microsoft Teams Displays Local Time in Profile Cards https://office365itpros.com/2022/02/21/teams-time-zone-profile-cards/?utm_source=rss&utm_medium=rss&utm_campaign=teams-time-zone-profile-cards https://office365itpros.com/2022/02/21/teams-time-zone-profile-cards/#comments Mon, 21 Feb 2022 01:00:00 +0000 https://office365itpros.com/?p=53593

Teams Local Time a Useful Feature Dependent on Exchange Calendar Settings

On February 14, Microsoft updated the Teams profile card to display the profile owner’s local time and the time difference between you and them. To see a profile card, hover over a user’s thumbnail photo anywhere they appear in Teams, like a chat or channel conversation. As you can see in Figure 1, the local time for the chosen person and the difference to your time appear.

Local time information appears on a Teams profile card
Figure 1: Local time information appears on a Teams profile card

Obviously, if you’re trying to contact someone or arrange a meeting with them, knowing when they work is valuable information. It’s also a feature requested several times in the Teams feedback forum (here’s one example).

Administrators don’t have to do anything to make local time appear in profile cards. Like other information shown in the profile card, the local time depends on information already known about users. In this case, the working hours defined for user calendars. Apart from tenant accounts, local time information is also displayed for guest accounts, if an Exchange Online organization relationship exists to share calendar information between the tenant and the guest’s home domain. Local time information is not available for federated chat including chats with Teams consumer users.

Puzzled by Time Zones

All of this is wonderful until I saw some puzzling results, like people showing up as being in odd time zones or with time differences that just didn’t work. Take the local time shown for Vasil Michev’s guest account (Figure 2). I am in Dublin, Ireland and Vasil is in Sofia, Bulgaria. At 9:51am, Teams told me that Vasil’s local time was 07:51am and that he is two hours behind me. In other words, his time zone is somewhere in the mid-Atlantic. This is upsetting, because the technical editor for the Office 365 for IT Pros eBook shouldn’t be stuck in mid-ocean.

Teams displays an interesting local time zone for a Bulgarian user
Figure 2: Teams displays an interesting local time zone for a Bulgarian user

Why would someone’s time zone be four hours off? To answer the question, we need to know where Teams obtains its time zone information.

Two Time Zones for Outlook

It’s logical that Teams would use someone’s location to determine their time zone. Looking at the data, it seems OK.

Get-User -Identity Vasil.Michev | fl city, stateorprovince, countryorregion

City            : Sofia
StateOrProvince : Sofia
CountryOrRegion : Bulgaria

However, when you think about things a little more, mailboxes have a regional configuration. Perhaps this is where Teams fetches the time zone from. The problem with this theory is that guest accounts have only special cloud-only mailboxes used to store compliance records and other system data. You can’t query these mailboxes using the Get-MailboxRegionalConfiguration cmdlet, like you can for tenant mailboxes:

Get-MailboxRegionalConfiguration -Identity Ken.Bowers | fl

DateFormat                            : dd/MM/yyyy
Language                              : en-GB
DefaultFolderNameMatchingUserLanguage : False
TimeFormat                            : HH:mm
TimeZone                              : Eastern Standard Time

The answer therefore must be data that remote tenants can access, such as the calendar configuration. Each calendar has a time zone for working hours, which is used when scheduling meetings and to publish free/busy information. Organizations can share free/busy data with other Microsoft 365 tenants, and as it turns out, this is the data used by Teams.

Users can set the time zone for their calendar through Outlook or OWA settings. OWA is more intelligent about time zone settings than Outlook desktop is and detects if a difference exists between the regional time zone configured in the General tab and the calendar time zone. In Figure 3, we see that OWA offers to fix a mismatch detected between a user’s regional time zone (Eastern Standard Time or UTC -5) and  the time zone for their calendar meeting hours (UTC).

OWA calendar settings detect a time zone mismatch
Figure 3: OWA calendar settings detect a time zone mismatch

Although the two time zones are usually the same, they don’t need to be. By default, the time zone for the calendar is set to the tenant time zone during the creation of a new mailbox. Afterwards, the user can update the time zone to match their location, or an administrator can update the time zone using the Set-MailboxCalendarConfiguration cmdlet. For example:

Set-MailboxCalendarConfiguration -Identity Nikki.Patia -WorkingHoursTimeZone "FLE Standard Time"

Like any change to a mailbox or account setting, it can take some time before Teams clients refresh their cache to pick up the change. In testing, I found it could take several days before Teams reflected calendar adjustments in profile cards.

Checking Calendar Settings

Apart from running the Get-MailboxCalendarConfiguration cmdlet, administrators can use the Graph Explorer to check calendar settings for a user by running a Calendar API query, which is how Teams fetches user time zone information.

To run the query, open the Graph Explorer and sign into your account. In the request body, enter the request you want the Graph to process. In this case, we want to know about the calendar settings for two users defined in the schedules section. The start and end time can be any date.

{
    "schedules": [
        "jane.smith@office365itpros.com",
        "john.hopper@office365itpros.com"
    ],
    "startTime": {
        "dateTime": "2023-03-15T09:00:00",
        "timeZone": "GMT Standard Time"
    },
    "endTime": {
        "dateTime": "2023-03-15T18:00:00",
        "timeZone": "GMT Standard Time"
    },
    "availabilityViewInterval": 60
}

After populating the request body, run this POST query:

https://graph.microsoft.com/v1.0/me/calendar/getSchedule

The response gives the availability of the users for the requested time slot. However, we’re interested only in the time zone included in the response for each user. In this instance, we see that the user’s calendar time zone is Eastern Standard Time.

                ],
                "startTime": "08:00:00.0000000",
                "endTime": "17:00:00.0000000",
                "timeZone": {
                    "name": "Eastern Standard Time"
                }

We started off by reporting problems with the profile card for Vasil Michev’s guest account. My tenant has an Exchange Online federated relationship with Vasil’s tenant, so we can use the Calendar API to perform a free/busy lookup. This is how the Outlook scheduling assistant finds free time slots for meetings.

The lookup returned the following result shows that Vasil’s calendar uses a custom time zone to apply a four-hour time offset from UTC. This is why his profile card reports such an odd local time.

"startTime": "09:00:00.0000000",
                "endTime": "18:00:00.0000000",
                "timeZone": {
                    "@odata.type": "#microsoft.graph.customTimeZone",
                    "bias": -120,
                    "name": "Customized Time Zone",
                    "standardOffset": {
                        "time": "04:00:00.0000000",
                        "dayOccurrence": 5,
                        "dayOfWeek": "sunday",
                        "month": 10,
                        "year": 0

You might decide that it’s up to users to make sure that their calendars have the correct time zones set and administrators have no part to play to ensure no mismatches exist. However, if you decide that you’d like to know if any mailboxes have mismatched time zones, we can detect the condition with some PowerShell code. The script below:

  • Fetches user mailboxes.
  • Checks the regional configuration to get the time zone set for the user location.
  • Checks the calendar configuration to get the time zone set for meeting hours.
  • Checks the user account properties to get their physical address.
  • Reports any mismatches found between the two time zones.

$ModulesLoaded = Get-Module | Select Name
If (!($ModulesLoaded -match "ExchangeOnlineManagement")) {Write-Host "Please connect to the Exchange Online Management module and then restart the script"; break}
Write-Host "Finding user mailboxes..."
# OK, we seem to be fully connected and ready to go...
[array]$Mbx = Get-ExoMailbox -RecipientTypeDetails UserMailbox -ResultSize Unlimited
If (!($Mbx)) { Write-Host "Something happened and we found no user mailboxes - exiting" ; break }

$Report = [System.Collections.Generic.List[Object]]::new()
ForEach ($M in $Mbx) {
   Write-Host "Processing" $M.DisplayName
   $RegionalConfiguration = Get-MailboxRegionalConfiguration -Identity $M.UserPrincipalName
   $CalendarConfiguration = Get-MailboxCalendarConfiguration -Identity $M.UserPrincipalName
   $UserInfo = Get-User -Identity $M.UserPrincipalName
   $Status = $Null
   If ($CalendarConfiguration.WorkingHoursTimeZone -ne $RegionalConfiguration.TimeZone) {$Status = "Time zone mismatch"}

   $DataLine= [PSCustomObject][Ordered]@{ 
        Status          = $Status
        User            = $M.DisplayName
        UPN             = $M.UserPrincipalName
        CalendarZone    = $CalendarConfiguration.WorkingHoursTimeZone
        RegionalZone    = $RegionalConfiguration.TimeZone
        Language        = $RegionalConfiguration.Language
        DateFormat      = $RegionalConfiguration.DateFormat
        TimeFormat      = $RegionalConfiguration.TimeFormat
        Office          = $UserInfo.Office
        StreetAddress   = $UserInfo.StreetAddress
        City            = $UserInfo.City
        StateOrProvince = $UserInfo.StateOrProvince
        CountryOrRegion = $UserInfo.CountryOrRegion
        PostalCode      = $UserInfo.PostalCode
      
    }
    $Report.Add($DataLine)
}
$Report | Out-GridView

Figure 4 shows example output from the script.

Time zone mismatches reported by PowerShell
Figure 4: Time zone mismatches reported by PowerShell

The cmdlets to fetch regional and calendar configurations are not quick and the script can take between ten and fifteen seconds to process a mailbox. In fact, this is a great example of an Exchange Online script suitable for processing by Azure Automation.

Adjustments Best Left to Users

Although it would be easy to insert an extra line of PowerShell to fix time zone mismatches by adjusting the calendar time zone to match the regional time zone, that’s a call better left to users. They know their calendar and they know how they work. But with the information to hand, you can advise users with mismatched time zones to update their settings as necessary to make sure that Teams profile cards reflect accurate data. After all, you wouldn’t want people to see bad local times, would you?


Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2022/02/21/teams-time-zone-profile-cards/feed/ 27 53593
Understanding What’s in an Entra ID Access Token https://office365itpros.com/2022/02/17/understanding-entra-id-access-token/?utm_source=rss&utm_medium=rss&utm_campaign=understanding-entra-id-access-token https://office365itpros.com/2022/02/17/understanding-entra-id-access-token/#comments Thu, 17 Feb 2022 01:00:00 +0000 https://office365itpros.com/?p=53497

Critical Piece When Connecting to the Microsoft Graph

By now, most people who write PowerShell code to interact with Microsoft 365 workloads understand that sometimes it’s necessary to use Microsoft Graph API queries instead of “pure” PowerShell cmdlets. The Graph queries are usually faster and more reliable when retrieving large quantities of data, such as thousands of Microsoft 365 Groups. Over the last few years, as people have become more familiar with the Microsoft Graph, an increased number of scripts have replaced cmdlets with Graph queries. All these scripts use Entra ID (Azure AD) access tokens, as does any utility which interacts with the Microsoft Graph, like the Graph Explorer (Figure 1).

The Graph Explorer displays its Azure AD access token
Figure 1: The Graph Explorer displays its access token

In the remainder of this article, I explore what an Entra ID access token contains.

The Need for Access Tokens

Graph queries need authentication before they can run and the Graph API uses modern authentication. Entra ID registered applications bridge the gap between PowerShell and the Graph. The apps hold details used during authentication such as the app name, its identifier, the tenant identifier, and some credentials (app secret or certificate. The app also holds permissions granted to access data through Graph APIs and other APIs. When the time comes to authenticate, the service principal belonging to an app uses this information to request an access token from Entra ID. Once Entra ID issues the access token, requests issued to the Invoke-RestMethod or Invoke-WebRequest cmdlets can include the access token to prove that the app has permission to access information.

At first glance, an access token is a confused mass of text. Here’s how PowerShell reports the content of an access token:

eyJ0eXAiOiJKV1QiLCJub25jZSI6IlFQaVN1ck1VX3gtT2YzdzA1YV9XZzZzNFBZRFUwU2NneHlOeDE0eVctRWciLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1yNS1BVWliZkJpaTdOZDFqQmViYXhib1hXMCIsImtpZCI6Ik1yNS1BVWliZkJpaTdOZDFqQmViYXhib1hXMCJ9.eyJhdWQiOiJodHRwczovL2dyYXBoLm1pY3Jvc29mdC5jb20iLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9iNjYyMzEzZi0xNGZjLTQzYTItOWE3YS1kMmUyN2Y0ZjM0NzgvIiwiaWF0IjoxNjQ0ODQ1MDc3LCJuYmYiOjE2NDQ4NDUwNzcsImV4cCI6MTY0NDg0ODk3NywiYWlvIjoiRTJaZ1lEaW1McEgwTSt5QTk5NmczbWZUUXlYN0FBPT0iLCJhcHBfZGlzcGxheW5hbWUiOiJHZXRUZWFtc0xpc3QiLCJhcHBpZCI6IjgyYTIzMzFhLTExYjItNDY3MC1iMDYxLTg3YTg2MDgxMjhhNiIsImFwcGlkYWNyIjoiMSIsImlkcCI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0L2I2NjIzMTNmLTE0ZmMtNDNhMi05YTdhLWQyZTI3ZjRmMzQ3OC8iLCJpZHR5cCI6ImFwcCIsIm9pZCI6IjM4NTRiYjA4LTNjMmMtNGI1Ny05NWZjLTI0ZTA3OGQzODY4NSIsInJoIjoiMC5BVndBUHpGaXR2d1Vva09hZXRMaWYwODBlQU1BQUFBQUFBQUF3QUFBQUFBQUFBQmNBQUEuIiwicm9sZXMiOlsiVGVhbVNldHRpbmdzLlJlYWRXcml0ZS5BbGwiLCJUZWFtTWVtYmVyLlJlYWQuQWxsIiwiR3JvdXAuUmVhZC5BbGwiLCJEaXJlY3RvcnkuUmVhZC5BbGwiLCJUZWFtLlJlYWRCYXNpYy5BbGwiLCJUZWFtU2V0dGluZ3MuUmVhZC5BbGwiLCJPcmdhbml6YXRpb24uUmVhZC5BbGwiLCJBdWRpdExvZy5SZWFkLkFsbCJdLCJzdWIiOiIzODU0YmIwOC0zYzJjLTRiNTctOTVmYy0yNGUwNzhkMzg2ODUiLCJ0ZW5hbnRfcmVnaW9uX3Njb3BlIjoiRVUiLCJ0aWQiOiJiNjYyMzEzZi0xNGZjLTQzYTItOWE3YS1kMmUyN2Y0ZjM0NzgiLCJ1dGkiOiI3RVkyWnVXV2JFYVF0T3piVVlwOUFBIiwidmVyIjoiMS4wIiwid2lkcyI6WyIwOTk3YTFkMC0wZDFkLTRhY2ItYjQwOC1kNWNhNzMxMjFlOTAiXSwieG1zX3RjZHQiOjEzMDI1NDMzMTB9.N9yvmkCedti2fzT44VfBkN7GvuCInrIgiMgNxdyZeAyxnbdZjEhxHmNdU6HLLHQ3J-GonpPdt28dKwYxgLcrSibGzSPVHddh6MDPYutSwfIxh2oRanxhgFOWVJADfbFoCxsRFDhKJNT39bsauIUiRNzGzbb6dvWuZQ8LrgWjZzjae2qxVxj9jvYgjXEypeYZgLvPOzJiBCuluAMH3TjPuS-CuglFK_edn4CS-ztCwM0hmDFD5BLNZqng5P2KqGTEgjkMKoyIJ8yTGBJpASfdqqEFqWzQwcQ9ese924qNC3hJR_5TWHp2Fl73bpdhwBHRL5UwGTPi9_ysYdndKhXwgA

Deciphering an Access Token

Access tokens issued by Entra ID comply with the OAuth 2.0 bearer token standard (RFC6750) and are structured as JSON-formatted Web Tokens. We can’t see the JSON content because it is base64Url encoded and signed. However, if you paste the token into a site like https://jwt.ms/, the site will decrypt the list of claims included in the token and we’ll see something like the details shown below for the access token featured above:

{ "typ": "JWT", 
"nonce": "gq3zmJhybfXGDGqt6RO2PX9s0cimmRpSRrTO90sQ4w4", 
"alg": "RS256",
 "x5t": "Mr5-AUibfBii7Nd1jBebaxboXW0", 
"kid": "Mr5-AUibfBii7Nd1jBebaxboXW0" 
}.
{ "aud": "https://graph.microsoft.com", 
"iss": "https://sts.windows.net/a662313f-14fc-43a2-9a7a-d2e27f4f3478/", 
"iat": 1644833772, 
"nbf": 1644833772,
 "exp": 1644837672,
 "aio": "E2ZgYJif1+eocevtzqRIrgDGA2V3AQ==",
 "app_displayname": "ReportDLs", 
"appid": "76c31534-ca1f-4d46-959a-6159fcb2f77a", 
"appidacr": "1",
 "idp": "https://sts.windows.net/a662313f-14fc-43a2-9a7a-d2e27f4f3478/", 
"idtyp": "app",
 "oid": "4449ce36-3d83-46fb-9045-2d1721e8f032",
 "rh": "0.AVwAPzFitvwUokOaetLif080eAMAAAAAAAAAwAAAAAAAAABcAAA.",
 "roles": 
[ "Group.Read.All", "Directory.Read.All", "User.Read.All" ],
 "sub": "4449ce36-3d83-46fb-9045-2d1721e8f032", 
"tenant_region_scope": "EU", 
"tid": "a662313f-14fc-43a2-9a7a-d2e27f4f3478",
 "uti": "BU1RVc7mHkmBq2FMcZdTAA", 
"ver": "1.0", 
"wids": [ "0997a1d0-0d1d-4acb-b408-d5ca73121e90" ],
 "xms_tcdt": 1302543310 
}
.[Signature]

The deciphered token divides into three parts: header, payload, and signature. The aim of a token is not to hide information, so the signature is not protected by encryption. Instead, it’s signed using a private key by the issuer of the token. Details of the algorithm and private key used to sign an access token are in its header. An application can validate the signature of an access token if necessary, but this is not usually done when running a PowerShell script. The payload is the location for the claims made by the token and is the most interesting place to check.

Another way to check what’s in an access token is to use the JWTDetails PowerShell module, which is available in the PowerShell Gallery. To install this (very small) module, run:

Install-Module -Name JWTDetails -RequiredVersion 1.0.0 -Scope AllUsers

Afterward, you can examine a token with the Get-JWTDetails cmdlet. Here’s an example revealing that the access token issued to an app allows it to access Exchange Online using the IMAP4 or POP3 protocols:

Get-JWTDetails -Token $Token

aud             : https://outlook.office.com
iss             : https://sts.windows.net/b662313f-14fc-43a2-9a7a-d2e27f4f3478/
iat             : 1671891468
nbf             : 1671891468
exp             : 1671895368
aio             : E2ZgYDAQS/prW6b0Zsah6KMXtnTEAQA=
app_displayname : POP3 and IMAP4 OAuth 2.0 Authorization
appid           : 6a90af02-6ac1-405a-85e6-fb6ede844d92
appidacr        : 1
idp             : https://sts.windows.net/a662313f-14fc-43a2-9a7a-d2e27f4f3478/
oid             : b7483867-51b6-4fdf-8882-0c43aede8dd5
rh              : 0.AVwAPzFitvwUokOaetLif080eAIAAAAAAPEPzgAAAAAAAABcAAA.
roles           : {POP.AccessAsApp, IMAP.AccessAsApp}
sid             : 1475d8e7-2671-47e9-b538-0ea7b1d43d0c
sub             : b7483867-51b6-4fdf-8882-0c43aede8dd5
tid             : a662313f-14fc-43a2-9a7a-d2e27f4f3478
uti             : COCw22GGpESVXvfdhmEVAQ
ver             : 1.0
wids            : {0997a1d0-0d1d-4acb-b408-d5ca73121e90}
sig             : PdScMpYqwA25qJL1z8q589sz/Ma5CGQ4ea9Bi0lnO2yByrIs530emYPnFPfQNN9EPBIvv4EaAoTLomrw4RMBWYoQSAgkBUXVrYGnC
                  jzAU6a2ZNZgo7+AORHk4iyLO0FpbLEaMJvCvI5vWhP9PHOxnGLcIsCbOmyrCK6lxxIKtBx851EpLrhpyvJ3p05NSw0D/mKzXPRKtc
                  rzQcUwECxOUugbm1zdq8JaE/PmSggBb87VZy7p1S2BXhxQZ5QU17JeIADyhCGm1Ml+avuIHsVS2iat/LPEi/nktbrXMcOzROpUKyZ
                  /7uVhxQ0cscJ6WGxbd+zJm36s25Yp1vMzSHaRxQ==
expiryDateTime  : 24/10/2022 15:22:48
timeToExpiry    : 00:59:34.7611307

Claims and Scopes

The list of claims in the access token includes simple claims and scopes (groups of claims). A claim is an assertion about something related to the token. In this case, the claims tell us details like:

  • The tenant (tid).
  • The intended consumer of the token (aud): https://graph.microsoft.com.
  • The app name (app_displayname).
  • The app identifier (appid).
  • The security token service (STS) responsible for issuing the token (iss): https://sts.windows.net/a662313f-14fc-43a2-9a7a-d2e27f4f3478/.
  • The generation time for the token (iat).
  • The time when the token expires (exp). All dates are in Unix epoch time, so 1644837672 means 11:21:12 GMT on February 14, 2022. By default, access tokens issued by Entra ID last one hour, except those used by applications which support continual access evaluation (CAE), where Entra ID issues 28-hour access tokens because it can terminate access at any time and force the user to reauthenticate should a critical user event (like a password change) happen.
  • The identifier for the object in the Microsoft identity system used for authentication (oid). In this case, the script uses a registered Entra ID app, so the value is the service principal for the app. You can test this by running the Get-MgServicePrincipal cmdlet from the Microsoft Graph PowerShell SDK:

Get-MgServicePrincipal -Filter "Id eq '4449ce36-3d83-46fb-9045-2d1721e8f032'"

DisplayName Id                                   AppId                                SignInAudience ServicePrincipalTy
                                                                                                     pe
----------- --                                   -----                                -------------- ------------------
ReportDLs   4449ce36-3d83-46fb-9045-2d1721e8f032 77c31534-ca1f-4d46-959a-6159fcb2f77a AzureADMyOrg   Application

Scopes are a logical grouping of claims, and they can serve as a mechanism to limit access to resources. The roles claim contains a scope of Graph API permissions starting with Group.Read.All and ending with User.Read.All. We therefore know that this app has consent from the organization to use the permissions stated in the scope when it executes Graph API queries. The list of permissions is enough to allow the PowerShell script (in this case, one to generate a report of distribution list memberships) to query the Graph for a list of all groups and read the membership of each group.

From bitter experience, I know how easy it is to get Graph permissions wrong. One way to check is sign into the Graph Explorer and run the query (here’s an example) to check what permissions the Explorer uses to execute the query. However, you can also dump the access token to check that the set of permissions in the access token matches what you expect. It’s possible that you might have requested some application permissions for the app and failed to gain administrator consent for the request, meaning that the access token issued to the app by Entra ID won’t include the requested permissions.

Using the Access Token

Once we’re happy that we have a good access token, we can use it with Graph queries. Here’s how to fetch the list of distribution groups in a tenant. The access token is included in the $Headers variable passed to the Invoke-RestMethod cmdlet.

$Headers = @{Authorization = "Bearer $token"}

$Uri = "https://graph.microsoft.com/V1.0/groups?`$filter=Mailenabled eq true and not groupTypes/any(c:c+eq+'Unified')&`$count=true"
[array]$DLs = (Invoke-RestMethod -Uri $Uri -Headers $Headers -Method Get -ContentType "application/json")
$DLs = $DLs.Value

And if everything goes to plan, we should have a set of distribution lists to process. If not, it’s bound to be a problem with your access token, so it’s time to return to square one and restart the acquisition process.


Learn more about how Office 365 really works on an ongoing basis by subscribing to the Office 365 for IT Pros eBook. Our monthly updates keep subscribers informed about what’s important across the Office 365 ecosystem.

]]>
https://office365itpros.com/2022/02/17/understanding-entra-id-access-token/feed/ 6 53497
Managing Entra ID’s Keep Me Signed In (KMSI) Feature https://office365itpros.com/2022/02/14/azure-ad-keep-me-signed-in-kmsi/?utm_source=rss&utm_medium=rss&utm_campaign=azure-ad-keep-me-signed-in-kmsi https://office365itpros.com/2022/02/14/azure-ad-keep-me-signed-in-kmsi/#comments Mon, 14 Feb 2022 01:00:00 +0000 https://office365itpros.com/?p=53191

Feature Can Suppress Sign-in Prompts

Entra ID’s Keep Me Signed In (KMSI) feature uses a persistent cookie to allow users with member accounts in the tenant directory to close and resume browser sessions without needing to sign in again. Entra ID generates the persistent cookie if a user responds affirmatively to the Stay signed in? prompt after a successful authentication (Figure 1). Entra ID uses the persistent cookie to extend the user session (and thus avoid sign-in prompts) and revokes the cookie only after the user signs out.

The Keep Me Signed In (KMSI) prompt
Figure 1: The Keep Me Signed In (KMSI) prompt

According to Microsoft’s documentation, Entra ID hows the KMSI prompt only when “it can benefit the user” and doesn’t prompt guest accounts, if Entra ID considers the sign-in risk score to be high, if persistent browser session control is configured in a conditional access policy, and if accounts sign in via SSO or AD FS.

The Value of a Persisent Cookie

I understand the value of KMSI for users who work with Microsoft 365 apps through browser sessions. Some applications, like Planner, don’t have desktop clients, so you’re forced to use browser or mobile clients. SharePoint Online and OneDrive for Business are also in this category. However, if a high percentage of user interaction with these workloads is through Teams, I wonder how important persistent connectivity is for their browser sessions.

Overall, given the influence of Teams and mobile clients, the argument for facilitating persistent browser sessions weakens. A good case is arguable that it is better to disable KMSI and force users to reauthenticate if they close the browser as this removes the possibility of compromise should an attacker be able to access a workstation. Requiring reauthentication when opening a session to a Microsoft 365 application seems to take the proactive approach to security endorsed by Microsoft in their Zero Trust model. It also seems to be aligned with recent developments such as enabling continual access evaluation for critical Entra ID events in all Microsoft 365 tenants. In a nutshell, KMSI might not be as valuable as it once was.

Disabling Keep Me Signed In

Unless you deploy conditional access policies to control browser session persistence, the keep me signed in feature is either on or off for everyone in a tenant. If you decide to disable the feature, the way to do so is through Entra ID company branding. Tenants with Entra ID Premium or Microsoft 365 licenses can customize different graphic elements displayed on user sign-in screens, such as the background screen. Company branding is one of those often overlooked features that every tenant should use (Figure 2).

The effects of Azure AD company branding on sign-in screens
Figure 2: The effects of Entra ID company branding on sign-in screens

To apply custom branding, go to the Company branding section of the Entra admin center. You can then create elements for the default locale or for individual language-specific locales. Entra ID applies the default locale if custom elements aren’t available for a user’s selected language.

Applying custom branding is straightforward and requires just a few graphic files (PNG preferred, JPEG works fine):

  • A background image (1920×1080 pixels). This is the type of image used in Figure 2.
  • A banner logo (280×60 pixels). This is the type of image used at the top of the Enter password screen in Figure 2.
  • Square logo (240×240 pixels). This image appears elsewhere, like the bottom of email notifications sent when users share files.

Entra ID replaces its standard images with the custom images defined in company branding Figure 3 shows the properties for company branding applied to my tenant. The important point for this discission is that the option for users to remain signed in is off (at the bottom of the screen).

Custom elements for Azure AD company branding
Figure 3: Custom elements for company branding

When you disable KMSI, Microsoft notes:

Important: some features of SharePoint Online and Office 2010 have a dependency on users remaining signed in. If you hide this option, users may get additional and unexpected sign in prompts.

Given that Microsoft 365 no longer supports Office 2010, you can safely ignore that warning. I cannot find precise details of what SharePoint Online features the removal of KMSI affects, but so far, I have experienced few problems since I removed KMSI. OWA signs out automatically after a period of inactivity and sometimes users need to reenter credentials to keep a SharePoint Online session active, but that seems to be all. The rebuttal is that signing out and forcing users to reauthenticate after they leave browser sessions inactive for a while is a good thing. It’s less convenient for the users, but more secure for the organization,

It’s possible that the Entra ID warning is old and reflects concerns when Microsoft revamped the KMSI implementation in 2018. Although improvements in Azure, federation, and SharePoint Online since 2018 might have eliminated some or all of the difficulties reported in this Microsoft Technical Community discussion, it’s still worth reading to understand some of the complexities involved in authentication.

I obviously can’t test every authentication flow in use by tenants, so it’s important that anyone considering disabling KMSI should conduct a full suite of tests to validate whether this action causes problems for users.

Prioritizing Administrative Effort

One of the joys of working in the Microsoft 365 ecosystem is that there’s always something to investigate and debate. Disabling KMSI is probably an easier decision for cloud-only tenants. Hybrid deployments invariably introduce complications, especially in authentication. In those scenarios, it might be best to leave KMSI in place as there’s probably more urgent matters to deal with than plunging into the minutiae of testing authentication pathways.


Make sure that you’re not surprised about changes which appear inside Office 365 applications by subscribing to the Office 365 for IT Pros eBook. Our monthly updates make sure that our subscribers stay informed.

]]>
https://office365itpros.com/2022/02/14/azure-ad-keep-me-signed-in-kmsi/feed/ 2 53191
Microsoft Launches Azure AD Cross-Tenant Access Policies https://office365itpros.com/2022/02/08/azure-ad-cross-tenant-access/?utm_source=rss&utm_medium=rss&utm_campaign=azure-ad-cross-tenant-access https://office365itpros.com/2022/02/08/azure-ad-cross-tenant-access/#comments Tue, 08 Feb 2022 01:00:00 +0000 https://office365itpros.com/?p=53410

Laying the Foundation for New Collaboration Scenarios Like Teams Shared Channels

Updated: March 22, 2022

On February 7, Microsoft announced that Azure AD cross-tenant collaboration settings are available in preview. Part of Azure AD B2B Direct Connect, Azure AD cross-tenant access means that users can authenticate in their home tenant and use the credentials gained there to access resources in other tenants, subject to the collaboration settings now in preview. Microsoft says that the new settings allow organizations to control how users collaborate with other Azure AD organizations (Microsoft 365 tenants). Inbound and outbound controls are available to control access on a tenant-wide, group, or application basis, together with the ability to trust security claims from external organizations like multi-factor authentication and device compliance.

External Identities Settings

The new settings are available in the External identities section of the Azure AD admin center. The organizational settings tab is where you define settings for individual Azure AD tenants you want to collaborate with (Figure 1) while the default settings tab is where you define settings to apply to Azure AD tenants in general. Specific settings for another tenant take precedence over the default settings.

Azure AD cross-tenant access settings
Figure 1: Azure AD cross-tenant access settings

Being able to define how individual tenants interact with your tenant allows precise control over who can connect and what they can do. For instance, in Figure 2 we see that the inbound access settings for the O365Maestro tenant allow collaboration via just one application (Office 365). You can add other Microsoft applications to the mix or include other applications if registered in Azure AD.

Inbound access settings for an external Microsoft 365 tenant
Figure 2: Inbound access settings for another Azure AD organization

Note the external users and groups tab. By default, any user can connect with your organization. If you don’t want this to happen, the inbound access settings allow you to define exactly whom from another organization can collaborate with people in your organization. Likewise, the outbound access settings give you control over the people in your organization you want to collaborate outside your tenant. Again, because the whole idea of collaboration is to enable people to work together, the default is to allow everyone to collaborate with external organizations. However, sometimes control is necessary, and you might want to manage who connects with specific tenants, and this is where you can exert that control.

Accepting Security Claims

All Azure AD organizations apply the same fundamentals of authentication to allow users access to resources. It therefore makes sense to accept that a process performed for one tenant is valid for connection to another. If your security posture is higher (for instance, your tenant insists on connections from trusted devices), you can still insist that external connections meet this standard while at the same time accepting valid claims established when users sign into the other organization.

The Trust settings tab defines the set of security claims made by another tenant you are willing to accept. For example, let’s assume that the other tenant enforces MFA for all users. The trust settings for the tenant allows you to accept that the tenant has validated the user’s identity with MFA and won’t issue another challenge from your tenant. Reducing the necessity for multiple MFA challenges removes a major source of user irritation. By default, Azure AD accepts connections from another tenant based on that tenant’s assessment of MFA, compliant (trusted) devices, and hybrid Azure AD joined devices. You can enable or disable each of these claims as shown in Figure 3.

Figure 3: Trust settings for another Azure AD organization

Removing some friction from MFA challenges is a good thing. According to Microsoft, Azure AD customers secure only 22% of Azure AD accounts with MFA. That’s a horrible statistic (albeit showing steady growth over the past few years). The simple fact is that MFA helps accounts resist 99% of brute-force attacks designed to crack passwords, so this is an area where Microsoft 365 tenants need to do better.

Default Settings

If you don’t define settings for an Azure AD organization you want to collaborate with, Azure AD uses the default settings (Figure 4). Like those for individual tenants, the settings break down into B2B collaboration and Trust.

Default settings to control access to other Azure AD organizations
Figure 4: Default settings to control access to other Azure AD organizations

More information about configuring default and specific organization settings for cross-tenant access is available online. Like conditional access policies, it will take time to figure out the best approaches for configuring rules for inbound and outbound access. And like conditional access policies, no one wants to make the mistake of applying a change that blocks collaboration for everyone in a tenant. The advice is therefore to go slowly and understand exactly what effect a proposed change will have on users before proceeding.

Teams Connect the First for Cross-Tenant Access

Microsoft has said that people can connect using “native identities” to collaborate Teams shared channels (aka Teams Connect). It’s therefore no secret that Azure AD cross-tenant access is the foundation to allow users to use credentials obtained within their home tenant to connect with people in a shared channel in an external organization.

Teams shared channels (aka Teams Connect) are now in public preview. Many criticized Microsoft’s slowness in delivering shared channels but given that the feature depends on a new way of authentication and Azure AD collaboration that is only just available in preview, it’s understandable why the delay happened. After all, you don’t want authentication from another tenant to potentially compromise sensitive information stored in a shared channel. Teams Connect is likely the first app to exploit cross-tenant access. I don’t think it will be the last.

Azure AD cross-tenant access won’t mean that guest accounts will go away anytime soon. Many valid scenarios exist to demonstrate the usefulness of guest accounts. Cross-tenant access gives organizations a new way of collaborating to add to the methods enabled by guest accounts. It’s all goodness.


Keep up with the changing world of the Microsoft 365 ecosystem by subscribing to the Office 365 for IT Pros eBook. Monthly updates mean that our subscribers learn about new developments as they happen.

]]>
https://office365itpros.com/2022/02/08/azure-ad-cross-tenant-access/feed/ 11 53410
How to Exploit Entra ID Sign-in Data to Detect Problem Service Principals https://office365itpros.com/2022/02/03/service-principal-sign-in-data-detect-problem-apps/?utm_source=rss&utm_medium=rss&utm_campaign=service-principal-sign-in-data-detect-problem-apps https://office365itpros.com/2022/02/03/service-principal-sign-in-data-detect-problem-apps/#respond Thu, 03 Feb 2022 01:00:00 +0000 https://office365itpros.com/?p=53160

Spring Clean Time for Apps Coming Soon

Last year, I wrote about the need to review and clean up Entra ID integrated applications. That article describes how to extract information from Entra ID o a CSV file and use the CSV to create a Microsoft List. To make it easy to access the list, we create a channel tab in Teams. Everything works to identify suspect apps that might need removal. I think that you should perform such a review periodically. It just makes sense.

Another way to monitor potentially suspicious app activity is to review sign in data for service principals. The intention is to identify unrecognized service principals signing into the tenant and figure out what apps are involved. Sign-ins can originate from well-known service principals used by Microsoft apps, third-party apps, or the service principals automatically created by Entra ID when tenants register apps to interact with the Graph (for instance, to authenticate calls made to Graph APIs in PowerShell scripts). Sign-in data for service principals is available through the Entra admin center (Figure 1) and now it’s accessible using the Microsoft Graph List SignIns API.

Sign-in logs for service principals in the Azure AD admin center
Figure 1: Sign-in logs for service principals in the Entra admin center

The reason why this update is important is that access to sign-in data via the Graph makes it possible to download the information for analysis or store it for long-term retention in an external repository. Although you can download sign-in data as a CSV file from the Entra admin center, it’s more flexible to access the information via Graph queries, especially when you want to probe the activity patterns of certain service principals.

Getting Sign-In Data from the Graph

Any application which wants to interact with the Graph requires consent for permissions to access data. In this instance, consent is needed the Directory.Read.All and AuditLog.Read.All application permissions. Delegate permissions can also be used, and in this case the account used must hold an administrative role capable of accessing the Entra ID sign-in logs.

A suitably-permissioned application can issue queries against the SignIns API. To fetch service principal sign-in data, the query executed by the application must use a Lambda qualifier to filter data. Apart from setting a date range to search for sign-in data, the important point is to filter against the signInEventTypes property to select sign-in events for service principals. Here’s an example of a query to fetch sign-in data for between 17:30 and 22:3 on 19 January.

https://graph.microsoft.com/beta/auditLogs/signIns?&$filter=createdDateTime ge 2022-01-19T17:30:00Z and createdDateTime le 2022-01-19T22:30:00Z and signInEventTypes/any(x:x eq 'servicePrincipal')

To test the query (or one which suits your purposes), use the Graph Explorer to see what the query returns.

I wrote a simple PowerShell script (downloadable from GitHub) to fetch service principal sign-in data for the last seven days. A quick summary of the data revealed that many sign-ins came from an app named Office 365 Reports. Curiously, an app used by a PowerShell script that I had posted on GitHub also showed up with 22 sign-ins. The Information Barrier Processor is the app used by Microsoft 365 to check user accounts against information barrier policies to ensure that no one is communicating with anyone when they shouldn’t.

$Report | Group SpName | Sort Count -Descending | Select Name, Count

Name                                         Count
----                                         -----
Office 365 Reports                             369
Graph Microsoft 365 Groups Membership Report    22
Information Barrier Processor                   21
Security and Audit                               5
PS-Graph                                         1

Resolving the large set of sign-ins was easy. The data stored in the list (Figure 2) revealed the service principal to belong to an Office 365 Reporting app originally published by Cogmotive (acquired by Quadrotech and then by Quest Software). I haven’t used the app in years, but the sign-ins kept on coming.

Service Principal information
Figure 2: Service Principal information

Over time, it’s easy to accumulate crud in the form of service principals installed for one reason or another. Testing an ISV product is a classic example, which is a good reason to always conduct tests in a test tenant instead of the production tenant. Or if you stop using an app, remember to clean up by removing service principals and other app debris that the app vendor might leave behind.

The sign-ins for the app used by the PowerShell script probably exist because I shared a copy of the script with my tenant identifier, the app identifier, and the app secret in place. I quickly replaced the script with a copy containing obfuscated credentials, but failed to change the app secret, meaning that anyone with an original copy could run the code. Now alerted, I removed the app secret. My suspicions were confirmed when a batch of failed sign-ins subsequently occurred for the app. This goes to prove how easy it is to create a potential compromise if you’re not careful.

Removing a Service Principal with PowerShell

You can clean up unwanted service principals with either the Entra admin center or PowerShell. I always have a PowerShell session open, so I chose that route. In this example, we find the object identifier for a service principal using its display name as a filter for the Get-MgServicePrincipal cmdlet. When sure that this is the right service principal to remove, we use the object identifier to remove the service principal with the Remove-MgServicePrincipal cmdlet.

$SP = Get-MgServicePrinicpal -filter "displayname eq 'Office 365 Reports'"

$SP

DisplayName        Id                                   AppId                                SignInAudience     
-----------        --                                   -----                                --------------     
Office 365 Reports 9ac957ae-160b-48d3-9a6f-f4c27acca040 507bc9da-c4e2-40cb-96a7-ac90df92685c AzureADMultipleOrgs 

Remove-MgServicePrincipal -ServicePrincipalId $Sp.id

Adding Context

A list of service principals known to the tenant is a valuable input to a review for unwanted or unnecessary apps holding some form of consent (permissions) to organization data. Adding context to the data by knowing which service principals are actively signing into the tenant makes it easier to prioritize action. The data is there, it’s available, and it’s up to you to decide what to do with it.


Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2022/02/03/service-principal-sign-in-data-detect-problem-apps/feed/ 0 53160