Microsoft 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 Microsoft 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
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
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
Changes Coming to Smoothen Edges in Microsoft Authenticator App https://office365itpros.com/2025/07/21/microsoft-authenticator-updates/?utm_source=rss&utm_medium=rss&utm_campaign=microsoft-authenticator-updates https://office365itpros.com/2025/07/21/microsoft-authenticator-updates/#respond Mon, 21 Jul 2025 07:00:00 +0000 https://office365itpros.com/?p=70111

Better Use of Number Matching and a Refined First Run Experience

Microsoft Authenticator app

The developers of the Microsoft Authenticator app have certainly been busy recently. Following on their announcement that a September 2025 update will make backup and restore easier, we now have the news released in message center notification MC1117816 (18 July 2025) that an update around the same time to simplify the sign-in experience and improve onboarding for users.

Modified Number Matching

In 2022, Microsoft added number matching to the Microsoft Authenticator app to reduce “MFA fatigue,” a symptom that can happen when users are asked to approve a stream of multifactor authentication challenges and can do so with a simple click. If a user responds with a series of clicks (without too much thinking), it makes it easier for an attacker to slip a bad challenge into the stream. Displaying a number and asking the user to match the number from a set of choices forces the user to pay attention. If they don’t, they probably won’t satisfy the challenge. Number matching became generally available in May 2023.

Good as number matching is at seizing user attention, it can sometimes run into difficulties. The most obvious is when Authenticator responds to a sign-in request for an app running on the app. The notification that a response is necessary can appear over the sign-in screen, meaning that the user can’t see the number they need to enter to satisfy the response.

To solve the problem, Microsoft is replacing the number choice with a simple yes or no. The experience is seamless on Android because all apps will pick up the new mechanism. On iOS, users of the Single Sign On (SSO) plug-in will still need to switch to the Authenticator app to complete the sign-in, but number matching won’t be required.

Users signing in from another device will still use number matching to satisfy the multifactor authentication challenge.

I have not seen the change in action, but I am familiar with the issue that Microsoft is attempting to solve. Indeed, so many notifications can pop-up on a busy device that tracking down authentication requests can be challenging. Anything that’s done to smoothen the user experience will be welcome.

Improving the First-Run Experience (FRX)

Microsoft is also making changes to the initial setup of the Authenticator app to give Entra ID accounts priority over personal accounts. I think this makes sense. The more that can be done to make multifactor authentication seamless for Entra ID users, the better the chances of driving the adoption of multifactor authentication in Microsoft 365 tenants. Attackers still target vulnerable accounts with techniques like password sprays.

According to Microsoft, 99.9% of compromised Entra ID accounts don’t use multifactor authentication. That figure should be sobering enough for any tenant administrator to take immediate action to improve their security stance by insisting that all user accounts are protected with multifactor authentication.

And if the Microsoft Authenticator app is easier for people to use, the resistance to moving from more traditional methods of satisfying challenges, like SMS, will be reduced. Some nagging is likely still needed (here’s a script to help) to convince tenant users to adopt strong multifactor authentication methods, but anything to remove barriers is a good idea.

Microsoft also says that they plan to make the option to scan a QR code more obvious. Again, this is goodness because many sites use QR codes as part of the multifactor authentication enrolment process.

Not Big Changes

Neither of the changes described here are in the category of earthshattering updates. Instead, the changes refine how the Microsoft Authenticator app works to make it easier for the average person to use. That’s a good thing, and I look forward to seeing the changes appear in September 2025.


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/07/21/microsoft-authenticator-updates/feed/ 0 70111
Easier Configuration Promised for the Microsoft Authenticator App https://office365itpros.com/2025/07/10/authenticator-app-backup/?utm_source=rss&utm_medium=rss&utm_campaign=authenticator-app-backup https://office365itpros.com/2025/07/10/authenticator-app-backup/#respond Thu, 10 Jul 2025 07:00:00 +0000 https://office365itpros.com/?p=69977

Authenticator Embraces a New Method for Account Backup and Restore

My article about adding QR codes to the Microsoft Authenticator app for Entra ID guest accounts is one of the more popular on the Office365itpros.com site. Given the increasing use of multifactor authentication to protect Microsoft 365 accounts and the need for stronger authentication methods to replace insecure SMS-based challenges, it’s unsurprising that the Authenticator app is a popular choice. The app is easy to use and it’s a strong authentication method, so many boxes are ticked.

Where the Authenticator app falls down is when a user gets a new phone, either by choice or through necessity. The gloss of buying a brand-new iPhone is diminished by the pain of reconfiguring the authenticator app to regain access to accounts. Microsoft wants to remove that pain with a “more seamless and secure backup and restore experience using iCloud and iCloud Keychain.”

The change is reported in message center notification MC1111780 (8 July 2025) and will be delivered in an app update that’s expected to roll out in September 2025 with full worldwide deployment scheduled to complete in October 2025. Tenant administrators cannot affect the progress of the roll out, and the change is effective after the installation of the updated app on an iOS device (the Authenticator app also supports iPad devices).

Eliminating the Need for a Microsoft Personal Account

Today, the Authenticator app needs a Microsoft personal account (Figure 1) to backup account names and third-party time-based one-time password (TOTP) credentials used by sites like GitHub and Twitter (the site issues a challenge that is satisfied by a six-digit number generated by the Authenticator app).

The recovery account is going away for Authenticator app backups.
Figure 1: The recovery account is going away for Authenticator app backups

Instead of using a Microsoft account for backup and recovery, Authenticator will use the iCloud keychain. Setup of new devices is therefore performed completely within the iOS ecosystem, so it’s smoother and less prone to error. Users don’t have to do anything to benefit from the update. It is enabled automatically if the device runs iOS 16.0 or later and the user’s iOS account enables iCloud and iCloud keychain. It’s likely that relatively few iOS users don’t have these components enabled. Apple is very successful at convincing iOS users to move to new versions of the operating system, so the iOS 16.0 requirement is unlikely to be an issue either, especially in corporate environments.

After the update, Authenticator backs up all account names and third-party TOTP credentials using the iCloud keychain. Nothing else is backed up, specifically Entra ID credentials are not stored, so after moving to a new iOS device, users must sign into their accounts to complete setup.

A Need for User Communication

During the period between now and September 2025, Microsoft will flag the upcoming change with messages in the Authenticator app to inform users about a “new way to backup your account” on its main screen. The settings screen will have a message about replacing the existing iCloud backup mechanism with an enhanced version. It’s possible that users will generate some help desk calls when they read these messages, so organizations should consider some proactive communications to explain what’s happening in non-technical, practical terms.

Finding iOS Devices That Might be Affected

With an eye on communications, the need exists to identify the users of iOS devices that might use the Authenticator app. One of the advantages of having a large repository of PowerShell scripts is the availability of code that can be repurposed. The trick is to figure out what bits to use.

After thinking about it, I decided to reuse some code to report user-preferred authentication methods to find users who’ve opted to use push-based methods. The devices in use can be Android or iOS, so it’s necessary to refine the set to select those who use iOS. The Get-MobileDevice and Get-MobileDevice Statistics cmdlets reveal the operating system used by devices that synchronize with Exchange Online with apps like Outlook for iOS. By checking the devices used by the folks who’ve signed up for push-based methods, we can find and report the people who are actively using iOS. You can download the script from the Office 365 for IT Pros repository. Some sample output is shown below.

Users of iOS devices that are actively in use
---------------------------------------------

User         UPN                                DeviceOS
----         ---                                --------
Jeff Guillet Jeff.Guillet@office365itpros.com   iOS 18.5 22F76
John James   John.James@office365itpros.com     iOS 18.5 22F76
Tony Redmond Tony.Redmond@office365itpros.com   iOS 18.5 22F76

This is a good example of using different sources of Microsoft 365 data to answer a question. Of course, you must know about the sources available to you, but that comes with experience.

Looking Forward to the Upgraded Authenticator App

I’m looking forward to the upgraded Authenticator app. My iPhone 14 is showing signs of age and it’s time to consider moving to a new iOS device (I’ve never used Android). If Microsoft’s promise is correct, the transition should be easier than ever before, and that’s a worthwhile change.


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/07/10/authenticator-app-backup/feed/ 0 69977
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
An Account Blocked by MACE Credential Revocation is A Good Way to Start a Saturday Morning https://office365itpros.com/2025/04/21/leaked-credentials-sign-in-metrics/?utm_source=rss&utm_medium=rss&utm_campaign=leaked-credentials-sign-in-metrics https://office365itpros.com/2025/04/21/leaked-credentials-sign-in-metrics/#comments Mon, 21 Apr 2025 07:00:00 +0000 https://office365itpros.com/?p=68924

Leaked Credentials and Sign-in Metrics Make for an Interesting Day

Last Saturday morning was interesting. After barely being able to ingest my first coffee, I noticed that Teams was complaining that it couldn’t sign in. A quick check against the Entra ID sign-in log revealed that the problem was that Teams had blocked the user (me) because of risk detected by the home tenant (Figure 1). In other words, something had happened to make Entra ID consider my account to be at risk.

Entra sign-in log entry for a blocked account due to leaked credentials,
Figure 1: Entra sign-in log entry for a blocked account due to leaked credentials

When unexpected things happen, my go-to source is the audit log. The root of the problem occured at 05:01 UTC when a component called MACE Credential Revocation changed the StsRefreshTokensValidFrom property for my account to set a new date and time from which refresh tokens can be used (thus forcing reauthentication). The action was captured in a Update StsRefreshTokenValidFrom Timestamp event.

MACE Credential Revocation is not something I have encountered before. The MACE part of the name means Microsoft Account Compromise Exchange and it’s part of Entra ID used to distribute leaked credential notifications. It is an Entra ID enterprise app with an application identifier of 7d636ec3-f39c-44f5-8b73-fa28a0e0c5bc. Interestingly, the service principal for the app was created in my tenant at at 19 April 2025 05:00;42 by a process called Microsoft Azure AD Internal – Jit Provisioning. It looks like Microsoft flipped a switch to enable some new Entra ID processing that immediately caused accounts to be flagged with leaked credentials. As I later discovered, it seems like many tenants had the same problem.

Despite my tenant being affected, nothing relating to a problem with Entra ID appeared in the service health dashboard, but I have heard from others who received the following statement from Microsoft support:

On Friday 4/18/25, Microsoft identified that it was internally logging a subset of short-lived user refresh tokens for a small percentage of users, whereas our standard logging process is to only log metadata about such tokens. The internal logging issue was immediately corrected, and the team performed a procedure to invalidate these tokens to protect customers.  As part of the invalidation process, we inadvertently generated alerts in Entra ID Protection indicating the user’s credentials may have been compromised. These alerts were sent between 4/20/25 4AM UTC and 4/20/25 9AM UTC. We have no indication of unauthorized access to these tokens – and if we determine there were any unauthorized access, we will invoke our standard security incident response and communication processes.  

The odd thing is that Microsoft refers to alerts being sent on Sunday (April 20) rather than Saturday (April 19), which is when the problem occurred. The mystery deepens.

Leaked Credentials

Managing risky users with Entra ID Protection is an Entra ID P2 feature. Being able to detect problems by monitoring for different signs of potential compromise is one of the reasons why tenants might consider the Microsoft E5 Security add-on. In my case, risky users is covered by the Microsoft 365 E5 license. Checking the Risky Users section of the Entra admin center, I duly found that my account was in the bad accounts list because of “leaked credentials” (Figure 2).

Risky User Details.
Figure 2: Risky User Details

Leaked credentials aren’t a huge problem for accounts protected by strong multi-factor authentication and are considered a “nonpremium” issue in the list of risk detections. I wanted to find out more and use the link to Microsoft Defender to see if it could throw more light onto the subject (Figure 3).

Microsoft 365 Defender's report about the leaked credentials.
Figure 2: Microsoft 365 Defender’s report about the leaked credentials

Remediation is a Password Change

Despite feeling that the warning was a false positive, you shouldn’t take chances with passwords. The easiest way to remediate the risk and unblock the account is to reset the password, so I used the self-service password reset feature to set a new account password (secure reset of a password is always accepted as a remediation). Reporting the detection of risky users and their remediation can be done through PowerShell.

Just one account was affected in my tenant. Others tenants reported that access for multiple accounts was blocked. The evidence to date indicates that Microsoft noticed a problem, took action to fix the problem, and made the problem worse by causing alerts to fire about perfectly good user accounts. In one way, it’s good that this happened over a holiday period when administrators have time and space to fix accounts, but you have to ask how a remediation process can suddenly make Entra ID believe that user accounts suffer from leaked credentials. It’s all very odd.

Success Metrics for Sign-ins

All of which brings me to some interesting metrics that are available through the Graph beta endpoint. All require the Reports.Read.All permission. For instance, the MFA sign in success metrics report “the number of times users successfully completed interactive MFA sign-ins using the Microsoft Entra MFA cloud service during a specified time period.” The metrics can be reported for up to 30 days using intervals of 5, 10, 15, or 30-minute intervals. Each item reported therefore represents the number of activities that occurred in an interval.

For example, here’s the code to extract the MFA sign-in success metrics at 30-minute intervals for the last 29 days.

$StartDate = $StartDate = ((Get-Date).AddDays(-29).ToString("yyyy-MM-ddTHH:mm:ssZ"))
$EndDate = ((Get-Date).ToString("yyyy-MM-ddTHH:mm:ssZ"))
$Uri = ("https://graph.microsoft.com/beta/reports/serviceActivity/getMetricsForMfaSignInSuccess(inclusiveIntervalStartDateTime={0},exclusiveIntervalEndDateTime={1},aggregationIntervalInMinutes=30)" -f $StartDate, $EndDate)
$Data = Invoke-MgGraphRequest -Uri $Uri -Method Get | Select-Object -ExpandProperty Value
$Data

Name                           Value
----                           -----
value                          0
intervalStartDateTime          17/04/2025 11:30:00
value                          0
intervalStartDateTime          17/04/2025 12:00:00
value                          0
intervalStartDateTime          17/04/2025 12:30:00
value                          0
intervalStartDateTime          17/04/2025 13:00:00
value                          0
…

1,390 objects were duly written into the output array. That’s too many to check manually, so I created a quick loop to report the intervals when successful MFA operations occurred:

ForEach ($Item in $Data) {
  If ($Item.Value -as [int] -ne 0) {
     Write-Host ("{0} successful MFA sign-ins during interval starting {1}" -f $Item.Value, $Item.intervalStartDateTime) -ForegroundColor Cyan
  }
}

Remember that these represent operations where a user goes through the full MFA process. If someone authenticates because their credentials haven’t expired, that operation doesn’t count.

Possibly of more interest is the API to get conditional access blocked sign in metrics, or “the number of user sign-in attempts that were blocked by a Conditional Access policy during a specific period.” The command is similar:

$Uri = ("https://graph.microsoft.com/beta/reports/serviceActivity/getMetricsForConditionalAccessBlockedSignIn(inclusiveIntervalStartDateTime={0},exclusiveIntervalEndDateTime={1},aggregationIntervalInMinutes=30)" -f $StartDate, $EndDate)
$Data = Invoke-MgGraphRequest -Uri $Uri -Method Get | Select-Object -ExpandProperty Value

Once again, we need a little help to extract the intervals when something interesting might have happened to guide us where to check in the Entra sign-in log:

  If ($Item.Value -as [int] -ne 0) {
     Write-Host ("{0} Blocked Conditional access sign-ins at {1}" -f $Item.Value, $Item.intervalStartDateTime) -ForegroundColor Red
  }
}

Interestingly, no metrics for blocked conditional access sign-ins are detected in my tenant. I guess the folks who secured a copy of my leaked credentials haven’t tried to use them yet only to find that MFA will block their efforts.


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/04/21/leaked-credentials-sign-in-metrics/feed/ 1 68924
How to Report the Sponsors of Entra ID Guest Accounts https://office365itpros.com/2025/04/18/guest-accounts-sponsors/?utm_source=rss&utm_medium=rss&utm_campaign=guest-accounts-sponsors https://office365itpros.com/2025/04/18/guest-accounts-sponsors/#respond Fri, 18 Apr 2025 07:00:00 +0000 https://office365itpros.com/?p=68894

Sponsors Are The People Who Invite Guests to Join a Tenant

Nearly two years ago, Entra ID added the ability to assign sponsors to guest accounts. A sponsor is someone in the tenant who can attest to the need to give an external person a guest account (or so the theory goes). Since then, Entra ID changed its processing so that the person who invites someone to join a tenant as a guest automatically becomes their sponsor less another person is explicitly selected during the invitation process. In most cases, no one will bother changing the sponsor (or know that they can), and the person who issues the invite is the sponsor.

Assessing whether old guest accounts should remain in a tenant is a good practice to perform periodically. It’s easy to create a report about guest accounts that includes details like the date created, last sign in date, days since the last sign in, any groups a guest belongs to, and so on. If you then decide to remove some guest accounts, you might like to flag the decision to the sponsors for those accounts.

Finding Guest Accounts

The Microsoft Graph PowerShell SDK has changed a lot since I originally wrote about sponsors, so here’s some new code to report guests and their sponsors (you can download the script from GitHub).

The first task is to find guest accounts and retrieve their sponsors. This is easily done by running the Get-MgUser cmdlet with a suitable filter and making sure to retrieve and expand the Sponsors property:

Write-Host "Finding guest accounts to analyze..." -ForegroundColor Green
[array]$Guests = Get-MgUser -Filter "userType eq 'Guest'" -All -Property Id, DisplayName, Sponsors, CreatedDateTime, SignInActivity, Mail -ExpandProperty Sponsors | Sort-Object DisplayName
If (!($Guests)) { 
    Write-Host "No guest accounts found." -ForegroundColor Red
}

Reporting Guest Accounts and Their Sponsors

After that, it’s a matter of looping through the guest accounts to extract and report the relevant information. Here’s the code:

Write-Host ("Checking {0} guest accounts..." -f $Guests.Count) -ForegroundColor Green
$Report = [System.Collections.Generic.List[Object]]::new()

ForEach ($Guest in $Guests) {
    $SponsorNames = $null
    If ($Null -eq $Guest.Sponsors.Id) {
        $SponsorNames = "No sponsor assigned"
    } Else {
        $SponsorNames = $Guest.Sponsors.additionalProperties.displayName -join ", "
    }

    $SignInDate = $null
    If ([string]::IsNullOrEmpty($Guest.SignInActivity.LastSuccessfulSignInDateTime)) {
        $SignInDate = "No sign-in activity"
        [int]$DaysSinceSignIn = (New-TimeSpan $Guest.CreatedDateTime).Days
    } Else {
        $SignInDate = Get-Date($Guest.SignInActivity.LastSuccessfulSignInDateTime) -format 'dd-MMM-yyyy HH:mm'  
        [int]$DaysSinceSignIn = (New-TimeSpan $SignInDate).Days
    }

    $ReportLine = [PSCustomObject] @{
        Name                 = $Guest.DisplayName
        Email                = $Guest.Mail
        'Sponsor Names'      = $SponsorNames
        Created              = Get-Date($Guest.CreatedDateTime) -format 'dd-MMM-yyyy HH:mm'
        'Last Sign In'       = $SignInDate
        'Days Since Sign In' = $DaysSinceSignIn.ToString()
    }
    $Report.Add($ReportLine)
}

$Report | Out-GridView -Title "Entra ID Guest Account Sponsors"

The number of days since sign in is calculated from the last successful sign-in date recorded by Entra ID for the account. If this information isn’t available (because the sign-in occurred before Entra introduced the last successful sign-in date property in late 2023), the creation date for the account is used. Figure 1 is an example of the output report.

Reporting guest accounts and their sponsors.
Figure 1: Reporting guest accounts and their sponsors

Some guest accounts don’t have sponsors because they were added to the tenant before Entra ID updated its processes to make the person who invites a guest their sponsor.

Figuring Out Old Guests

Because we compute the number of days since the last sign-in, it’s easy to list the set of guests that haven’t signed in since a set threshold. After that, it’s up to you how to contact the sponsors to ask them what to do with their old guests.

# List all the guest accounts (and their sponsors) that haven't signed in for more than the threshold number of days
$OldGuests = $Report | Where-Object {$_.'Days Since Sign In' -as [int] -gt $Threshold}
Write-Host ""
Write-Host ("The following guest accounts have not signed in for more than {0}} days:" -f $Threshold) -ForegroundColor Red
Write-Host ""
$OldGuests | Format-Table Name, 'Sponsor Names', 'Days Since Sign In', 'Last Sign In' -AutoSize

Tenants don’t have to use the sponsor information if they don’t want to. However, given that Entra ID now populates the sponsor data for new guest accounts, it seems like a pity not to use 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/2025/04/18/guest-accounts-sponsors/feed/ 0 68894
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
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
Use Protected Actions to Stop Attackers Hard-Deleting Entra ID Accounts https://office365itpros.com/2025/02/11/entra-id-protected-action/?utm_source=rss&utm_medium=rss&utm_campaign=entra-id-protected-action https://office365itpros.com/2025/02/11/entra-id-protected-action/#respond Tue, 11 Feb 2025 07:00:00 +0000 https://office365itpros.com/?p=68004

Enforcing Strong MFA Through Protected Actions Might Block Bad Actors

A January 25, 2025 blog about how attackers leverage the User.DeleteRestore.All Graph permission attracted my attention. The idea advanced is that if attackers can wreak havoc if they can secure the User.DeleteRestore.All permission. There’s no doubt that this assertion is correct. Attackers being able to permanently remove soft-deleted user accounts from the Entra ID recycle bin is enough to give any tenant administrator a severe headache.

Protected actions might offer some protection against attacker destruction. When I first covered this capability in May 2023, it was early days for the feature. Essentially, a protected action is associated with a conditional access policy through an authentication context so that any attempt to perform the protected access invokes the conditions set in the policy. For instance, the condition could be that the user must authenticate themselves with a strong phishing-resistant authentication method like a FIDO2 key or passkey.

The idea here is that the attacker might not be able to satisfy the authentication challenge with the required strong method and is therefore blocked from performing the protected action. That theory falls down if the attacker has gained sufficient control over the tenant to update conditional access policies (unless you block update access to conditional access policies with a protected action).

Creating a CA Policy for Protected Actions

Microsoft’s documentation for adding a protected action is straightforward. To add a protected action to guard against unexpected removal of soft-deleted user accounts, create a conditional access policy for Entra ID to invoke when a connection attempts to us an authentication context (basically, when something happens – like accessing a sensitive SharePoint Online site). The CA policy (Figure 1) is very simple and associates an authentication context with strong authentication. You could add other policy requirements to grant access to make it harder for attackers. For instance, require the use of a compliant device.

Conditional access policy to enable protected actions.
Figure 1: Conditional access policy to enable protected actions

The magic comes when you link the authentication context with one or more permissions. The set of supported permissions for protected actions has grown over time and includes microsoft.directory/deletedItems/delete, the permission to permanently delete objects from Entra ID. Deleted user accounts normally remain in a soft-deleted state in the Entra ID recycle bin for 30 days following deletion. Soft-deleted means that the accounts are recoverable. However, if the account is removed from the recycle bin, it is hard-deleted and irrecoverable.

To link the authentication context, go to the Roles & admins section under Identity in the Entra admin center and select Protected actions. Find the authentication context to link, select the permission, and save (Figure 2).

Linking a protected action to an authentication context.
Figure 2: Linking a protected action to an authentication context

You can’t link a protected action to multiple authentication contexts. If you make a mistake and link the protected action to the wrong authentication context, you must remove the protected action and add it to the correct authentication context.

Testing the Protected Action

To test the effectiveness of the protected action, you need an account that holds an administrative role that would normally allow the holder to permanently remove soft-deleted user accounts that doesn’t meet the requirements of the CA policy. I used an account holding the Global administrator role that used SMS to satisfy an MFA challenge. Entra ID doesn’t consider SMS to be an authentication strength that meets the criteria of passwordless MFA, so any attempt by this global administrator to remove a soft-deleted account fails (Figure 3).

The effect of a protected action to stop permanent removal of a user account.
Figure 3: The effect of a protected action to stop permanent removal of a user account

The block also works if an attempt is made to remove an account using the Graph Permanently delete an item API or the Remove-MgDirectoryDeletedItem cmdlet from the Microsoft Graph PowerShell SDK.

Get-MgDirectoryDeletedItemAsUser | Format-Table Id, displayname

Id                                   DisplayName
--                                   -----------
63699f2f-a46a-4e99-a068-47a773f9af11 Annie Colonna
f13e62ff-b43c-44e7-a821-48db196b84d9 Cathy Lin
889bad5f-d7f7-4731-bf07-af2894f345b2 Joanne Crispa

Remove-MgDirectoryDeletedItem -DirectoryObjectId f13e62ff-b43c-44e7-a821-48db196b84d9

Remove-MgDirectoryDeletedItem_Delete: Operation requires conditional access and client does not support it. Client must be configured to support conditional access claims challenges to proceed.

Insisting on Stringent Conditions is Sometimes Good

Any organization that has deployed conditional access policies should consider using protected actions. The necessary policies are easy to implement, and it makes sense to insist on stringent conditions before destructive actions like permanent removal of user accounts are possible.

The only issue I encountered during testing was that Entra ID didn’t respect the disabling of the CA policy used for protected actions. No matter what, the policy remained in force until I removed the protected action. It’s possible that the underlying cause was a timing issue generated by multiple changes to settings over a short period and that everything would have worked more smoothly if I was more patient. In any case, the issue shouldn’t be a problem in practical terms because it’s highly unlikely that anyone will disable and enable a CA policy to turn protected actions off and on several times in quick succession, but it’s definitely something that the developers should investigate.


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/02/11/entra-id-protected-action/feed/ 0 68004
Microsoft Introduces People Administrator Role https://office365itpros.com/2025/02/06/people-administrator-role/?utm_source=rss&utm_medium=rss&utm_campaign=people-administrator-role https://office365itpros.com/2025/02/06/people-administrator-role/#comments Thu, 06 Feb 2025 07:00:00 +0000 https://office365itpros.com/?p=67940

People Administrator is the 116th Entra ID Role

Message center notification MC992218 (30 January 2025) announces the arrival of the new People administrator role for Entra ID. There’s nothing remarkable about Microsoft creating a new Entra ID roles because People administrator is the 116th role.

Not every role is used inside every Microsoft 365 tenant. Right now, my tenant uses 36 roles. In fact, the way that things work is that Microsoft defines roles as sets of permissions to allow accounts or apps to perform specific actions. The roles are published as templates which are common across Entra ID and then become “real” roles when first assigned to a holder.

To see the full set of roles, run the Get-MgDirectoryRoleTemplate cmdlet, while to see the set used in a tenant, run Get-MgDirectoryRole. Microsoft documentation also lists the available roles.

New Role to Manage People Settings

People administrator sounds as if it’s like User administrator. In a way, that’s true, but only if equate people with users and assume that the two roles do the same thing, which they don’t. One way of comparing the new roles is that User administrator is all about maintaining user accounts and their attributes whereas People administrator assigns “permissions for managing people-related settings and profile photos without needing the high privileges of Global admin or User admin roles.” In other words, the new role is the implementation of the principle of least privilege to manage settings for the user profile card.

People settings have a very specific meaning within Microsoft 365 and are closely related to the profile card. Along with a bunch of information extracted from different parts of Microsoft 365, you’ll find the user’s photo, pronouns, pronunciation, and custom profiles set by the tenant.

What Assignees with the People Administrator Role Can Do

Holders of the People administrator role will be able to:

  • Upload new photos on behalf of users. This capability is controlled by the new user photo update policy. People administrator joins the set of default Entra ID roles allowed to update photos.
  • Enable personal pronouns for display on the profile card (Figure 1)
  • Enable pronunciation recordings for the profile card.
  • Define custom properties for display on the profile card. For instance, many tenants use custom properties to reveal organizational information like cost center designations.

A Customized Microsoft 365 User Profile Card.

People adnministrator role.
Figure 1: A Customized Microsoft 365 User Profile Card

Apart from photos, people administrators cannot update the values that appear in the profile card. Other roles, like User administrator, are required to update settings like change a phone number or a surname.

Processes such as those that update user photos from central (often HR) systems can use the new role instead of a higher-permissioned role like User administrator. This is likely the most important point to review and amend. The other functionality enabled by people administrator are, for now at least, one-off operations. After all, tenants don’t usually customize the properties shown on the people card every week or decide to pronouns off or on periodically.

The interesting aspect of this development is that Microsoft is obviously dedicating resources to building out capabilities around what they call “people-related tasks.” Giving users the ability to record their personal name pronunciation was the most recent example before now. I don’t know what else Microsoft has up their sleeves in this respect, but it’s certainly an interesting area to watch.

Check Your Role Assignments

The advent of the People administrator role is a reminder that some active role assignments might be for elevated roles that aren’t absolutely necessary. It’s probably a good idea to review the set of active and eligible role assignments to decide which remain justified and valid and if any assignments are no longer necessary or require adjustment. Any assignment that’s been in place for a year or more deserves a check. Leaving things alone is a recipe for permission inflation and that’s a horrible thing.


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/02/06/people-administrator-role/feed/ 1 67940
Interpreting SignIn Audit Records for Service Principals https://office365itpros.com/2025/02/05/service-principal-signins/?utm_source=rss&utm_medium=rss&utm_campaign=service-principal-signins https://office365itpros.com/2025/02/05/service-principal-signins/#respond Wed, 05 Feb 2025 07:00:00 +0000 https://office365itpros.com/?p=67974

Service Principal SignIn Audit Records Available for 30 Days

In August 2022, I wrote about the experience of developing and using Azure Automation runbooks. Move forward to today and one of the topics discussed in that article was raised again when I was asked if tenant sign-in logs capture details of access to enterprise apps from inside and outside the organization.

My response was “of course” because Entra ID captures all sign-ins for a tenant, including those for enterprise apps, or rather, the service principals that are the instantiation of enterprise apps within a tenant. Tenants keep sign-in records in audit logs for 30 days and those logs are available through the Entra Audit Logs Graph API, specifically for the signIn resource type and List SignIns API. The Microsoft Graph PowerShell SDK implements the List Signins API with the Get-MgAuditLogSignin cmdlet.

Beta API Supports Filtering SignIn Audit Records by Event Type

The ability to filter sign-in audit records by the type is only available through the beta API. This was also true in August 2022 and it’s a little odd that Microsoft hasn’t upgraded the V1.0 API to support filtering to find sign in records for non-interactive access, managed identities, or service principals. In any case, to filter by signInEventTypes, you need to access the beta endpoint or use the Get-MgBetaAuditLogSignIn cmdlet from the Microsoft Graph PowerShell SDK. For example, this command finds the last 5,000 sign-in audit logs generated for service principals:

[array]$AuditRecords = Get-MgBetaAuditLogSignIn -Filter "(signInEventTypes/any(t:t eq 'servicePrincipal'))" -Top 5000 -Sort "createdDateTime DESC"

Reviewing Service Principal SignIn Audit Records

Once the audit log records are extracted, the task is to interpret the Service Principal signins. Things to look for include:

  • Unexpected service principals. Attackers often exploit apps in their attempts to compromise tenants, so the appearance of an unexpected service principal is always worth investigation.
  • Access to service principals for enterprise apps coming from outside the organization.
  • Unexpected access to registered apps from both inside and outside the organization.
  • Use of client secrets (app secrets) to authenticate. This is undesirable unless the app is being tested and isn’t yet in production. Any app that’s in production should use a more secure authentication method like an X.509 certificate.

To help answer these questions, I wrote a script (available from the Office 365 for IT Pros GitHub repository) to parse audit records. The output of the script is an Excel worksheet (or CSV file if the ImportExcel module is not installed on the workstation). Figure 1 shows some sample data from my tenant.

Reviewing data extracted from service principal signin audit logs.
Figure 1: Reviewing data extracted from service principal sign-in audit logs

Reviewing the data, I found:

  • Adobe still uses client secrets to access the Adobe Acrobat enterprise app.
  • Some people still try to use old authentication details for apps that were inadvertently revealed in articles. I don’t consider this to be evidence of anything other than people running code that they’ve found to see what happens, but it does demonstrate how authentication information can be used. The audit records show that people in Warsaw, Frankfurt, and Bengaluru tried to access apps over the last 30 days only to find that the published app secret had either expired or been replaced.
  • Running declarative Copilot agents created using Copilot Studio generates a service principal for an enterprise app. The one in my tenant is named 383b6826-fc95-4359-bef6-27680c152c33 (Power Virtual Agents). I assume that the app is used to enable single sign on for agents, but I do not know if the same app is used in all tenants. The app is assigned the Cloud Application Administrator and Reports Reader roles but has no other permissions. The IP addresses used by the agents recorded in the audit records are all owned by Microsoft, indicating that the processing occurs within their datacenters (as you might expect). This is an example of a service principal that appears within a tenant without any notice.

The Worth of Service Principal SignIn Audit Reviews

I’m always relieved to answer a question. In this case, the exercise to prove how Entra ID audit log sign-in records capture information about internal and external access via service principals revealed some interesting information. It just goes to demonstrate that reviewing audit data is something that tenant administrators should do regularly.


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/02/05/service-principal-signins/feed/ 0 67974
Entra ID Allows People to Update their User Principal Names https://office365itpros.com/2025/01/24/update-user-principal-names/?utm_source=rss&utm_medium=rss&utm_campaign=update-user-principal-names https://office365itpros.com/2025/01/24/update-user-principal-names/#comments Fri, 24 Jan 2025 07:00:00 +0000 https://office365itpros.com/?p=67809

No Good Reason Why Users Can Update User Principal Names

Update 24 January 14:00 UTC: Microsoft appears to have reacted and has blocked the ability of users to update their UPNs. Here’s what the Entra admin center now displays when an attempt is made.

Update 26 January: An update released on Twitter by Alex Simons, Microsoft VP of Product Management, Microsoft Identity and Network Access Division, said:

“It was a bug caused by recent update to the service. When we the learned from Entra Advisors about the issue, team immediately rolled back to a known good build.

Fix in the newer build is completed and being rolled out slowly. We will do an internal investigation and PIR to identify the root cause and make process changes to prevent this (and any similar class of issues) from happening in future. Will also review log files for inappropriate usage and notify customers if found.”


It’s unclear if Microsoft has updated the default permissions assigned to Entra user accounts, but it is now possible for unprivileged users to update user principal names through interfaces like the Entra admin center and PowerShell. To be clear, an unprivileged user can update the user principal name for their Entra ID account and not for other accounts. Nevertheless, I can’t think of a good reason why any organization would want to allow people to update something fundamental like a user principal name, but they can.

To test the theory, I created a new account for Eric Hammon and attempted to sign into the Entra admin center. I then navigated to the Users section to access the account properties. As you can see from Figure 1, the user principal name is editable.

Properties of the Eric Hammond Entra account.

Update user principal name
Figure 1: Properties of the Eric Hammond Entra account

I went ahead and updated the account to make the user principal name Eric.B.Hammond@office365itpros.com. For good measure, I uploaded a new user photo. The update proceeded without a problem and the result is shown in Figure 2.

Account properties after updating the user principal name and photo.
Figure 2: Account properties after updating the user principal name and photo

A side effect of updating a user principal name is that the user’s primary SMTP address also changes. This is because of the dual write arrangement between Exchange Online and Entra ID whereby updates to mail-related properties occur in both directories. The update to the user principal name also updates the account’s Mail property, and this ripples through to Exchange Online, meaning that the full set of proxy addresses includes a new primary SMTP address (indicated by SMTP:). The previous primary SMTP address is preserved as a proxy to make sure that Exchange Online can deliver messages addressed to the old primary SMTP address.

Get-Mailbox -identity eric.b.hammond@office365itpros.com | Select-Object -ExpandProperty emailaddresses
SIP:eric.b.hammond@office365itpros.com
SMTP:Eric.B.Hammond@office365itpros.com
smtp:Eric.A.Hammond@office365itpros.com
smtp:Eric.Hammond@office365itpros.com

If administrators reverse the the user principal name, the extra email proxy address will remain in place unless it is explicitly removed. The possibilities of impersonation are obvious. For instance, some could change their user principal name to CEO@office365itpros.com to get that email address and the change their user principal name back to revert to the original value. If administrators aren’t checking audit logs, they might miss this change.

Update User Principal Name with PowerShell

After validating that it is possible for a user to update their user principal name and photo via the Entra admin center, I tried with the Microsoft Graph PowerShell SDK (Figure 3). I expected this to work because much of the Entra admin center is built on top of the Microsoft Graph, especially anything to do with user accounts and groups (you can validate this by running the Graph X-Ray tool).

Updating a user principal name with the Microsoft Graph PowerShell SDK.
Figure 3: Updating a user principal name with the Microsoft Graph PowerShell SDK

Essentially, these tests indicate that any tool based on the Microsoft Graph Users API will allow users to update their user principal name. I’m not bothered by the Entra admin center allowing people to update their photo because that facility is available elsewhere, notably in OWA and the new Outlook for Windows.

Blocking Access to the Entra Admin Center

Some control can be exerted for the Entra admin center by setting the option to restrict access to users that hold administrative roles (Figure 4).

 Restricting user access to the Entra admin center.
Figure 4: Restricting user access to the Entra admin center

This is only a partial block because accounts with relatively unprivileged roles, like Reports Reader, can still access the Entra admin center and update their user principal names. On the other hand, it does block casual access and is therefore a recommended setting to have in place.

Blocking Access to the Microsoft Graph PowerShell SDK

The ability to create an interactive session with the Microsoft Graph PowerShell SDK is governed by controls on the Microsoft Graph Command Line Tools enterprise app. Like other enterprise apps created by third parties for use in multiple Entra ID tenants, the instantiation for the app is a service principal that holds the consented permissions available in Graph SDK sessions. It can also hold a set of users and groups who are allowed to access the app. By default, the list of users assigned to the app is empty, which means that any user can run the Connect-MgGraph cmdlet in a PowerShell session to connect to the Graph.

Obviously, allowing open access to such a powerful capability isn’t a good idea, and tenants should take steps to secure access to the Microsoft Graph Command Line Tools app. With controls in place, anyone who isn’t on the approved list will see an AADSTS50105 error and be blocked from access (Figure 5).

A user is blocked when they attempt to create an interactive Graph SDK session.
Figuire 5: A user is blocked when they attempt to create an interactive Graph SDK session

And if you’re blocking access to PowerShell for the Graph SDK, consider doing the same for other Microsoft 365 PowerShellmodules.

No Apparent Justification for People to Update User Principal Names

Microsoft doesn’t make changes without reason, so something must have happened to convince the Entra ID developers to allow users to update user principal names. I can’t think of a convincing reason for such a change, but perhaps the logic will become apparent over time. In the meantime, if you don’t like people being able to change user principal names, consider applying the blocks described above.


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/01/24/update-user-principal-names/feed/ 23 67809
Final Days for the MSOnline and AzureAD PowerShell Modules https://office365itpros.com/2025/01/15/msonline-module-retirement/?utm_source=rss&utm_medium=rss&utm_campaign=msonline-module-retirement https://office365itpros.com/2025/01/15/msonline-module-retirement/#respond Wed, 15 Jan 2025 07:00:00 +0000 https://office365itpros.com/?p=67685

Time Ebbing Away Before AzureAD and MSOnline Module Retirement

On January 13, 2025 Microsoft posted what I am sure they hope will be the last notification about retirement details for the MSOnline and AzureAD PowerShell modules. This has been a long-running saga that’s taken almost as long as the effort to eradicate basic authentication for Exchange Online connection protocols.

The original August 2021 announcement that Microsoft intended to retire the two modules set a date of June 30, 2022. Following customer feedback that the date was too aggressive, Microsoft pushed the date out by a year and then by another nine months. The transition to a new Microsoft 365 licensing platform in mid-2024 forced people to take notice when PowerShell cmdlets stopped being able to assign or update licenses. Microsoft has now set what they say is the final schedule for the retirement (Figure 1).

The final schedule to retire the MSOnline and AzureAD modules (source: Microsoft).

MSOnline module retirement
Figure 1: The final schedule to retire the MSOnline and AzureAD modules (source: Microsoft)

Final Red Flags for MSOnline Module Retirement

The end of support for both modules is March 30, 2025, just eleven weeks away. But the really interesting note here is the temporary outage tests Microsoft plans for the MSOnline module cmdlets starting on January 20, 2025. What this means is that the MSOnline cmdlets will stop working at least twice between January 20 and February 28. The outages will last between three and eight hours and happen at different times during the day.

The two short outages will be followed in March 2025 by a longer outage. Microsoft hasn’t said how long the longer outage will last. What they have said is that the outages are “To ensure that customers are ready for this retirement of MSOnline PowerShell.”

Customers might view the outages in a different light, especially if the outages stop production scripts running. But to be fair to Microsoft, they have been up front and patient as the process for the retirement of the MSOnline and AzureAD modules unfolded since August 2021. The outages are no more than a final red flag warning to tenants. If you ignore the warnings, be prepared for disruption when the MSOnline module retirement finally completes sometime in April 2025. At that point, all the MSOnline cmdlets will stop working permanently.

AzureAD Module Retirement Follows in Third Quarter

To allow customers to focus on upgrading scripts from the older MSOnline module, Microsoft is targeting the third quarter of 2025 for the final retirement of the AzureAD module. Remember, its cmdlets have already lost their license management capability, so scripts used for other purposes such as reporting accounts and groups need to be upgraded now.

Upgrade to Entra PowerShell or the Microsoft Graph PowerShell SDK

Two upgrade options are available:

Microsoft built the Entra module from Graph SDK components wrapped up with some tweaks to make it slightly easier to migrate from MSOnline or AzureAD. Although I respect the opinion of those who advocate for the Entra module as the best migration target, I think this approach is a very short-term tactical step. You might end up being able to migrate scripts, but in doing so you’ll miss the opportunity to master the Graph SDK (and also be able to migrate your scripts).

Missing out on the Graph SDK might not sound like such a big deal, especially when the Entra module is available to handle the immediate need to migrate scripts before the old modules stop working. However, mastering the Graph SDK opens up the opportunity to use PowerShell to interact with many other forms of Microsoft 365 data instead of “just Entra ID.” The same techniques learned to interact with users, groups, and devices can be applied to teams, SharePoint Online sites, OneDrive for Business accounts, Exchange mailboxes, Planner plans and tasks, and so on. Understanding how the Microsoft Graph works is the better strategic choice for the longer term.

Whatever choice you make, time is ebbing away. If you need help to migrate, consider investing in a copy of the Office 365 for IT Pros eBook, which includes the Automating Microsoft 365 with PowerShell eBook (also available separately as an eBook or paperback). The hundreds of practical examples contained in these eBooks include many worked-out solutions for applying the Microsoft Graph PowerShell SDK to solve problems.

]]>
https://office365itpros.com/2025/01/15/msonline-module-retirement/feed/ 0 67685
Use the Microsoft Graph to Report Service Principal Sign-In Activity https://office365itpros.com/2024/11/21/service-principal-sign-in-activity/?utm_source=rss&utm_medium=rss&utm_campaign=service-principal-sign-in-activity https://office365itpros.com/2024/11/21/service-principal-sign-in-activity/#comments Thu, 21 Nov 2024 07:00:00 +0000 https://office365itpros.com/?p=67161

Gain Insight from Service Principal Sign-in Activity

Before an app can be used in an Entra ID tenant, it must be registered and have a unique identifier. Apps can be owned by the tenant or created by third parties. In both cases, a service principal for the app is required to access tenant resources. The service principal is the security principal for the app and defines who can access the app and what resources the app can access. Managed identities also have service principals to allow them to access resources.

All Microsoft 365 tenants have many service principals created for apps, including many created for Microsoft first-party apps. To find out how many Microsoft apps are known within your tenant, you can run this code to find the service principals belonging to the tenant used by Microsoft to host its services.

[array]$ServicePrincipals = Get-MgServicePrincipal -All -PageSize 500 | Sort-Object AppId
$MicrosoftApps = $ServicePrincipals | Where-Object {$_.AppOwnerOrganizationId -eq 'f8cdef31-a31e-4b4a-93e4-5f571e91255a'}

$MicrosoftApps.count
563

This isn’t the full picture because Microsoft uses other tenants to host its apps, like 9188040d-6c67-4c5b-b112-36a304b66dad (Microsoft accounts). In any case, many apps owned by Microsoft show up in Microsoft 365 tenants. The more Microsoft services you consume, the more apps you’ll find.

The Entra Admin Preview Feature for Service Principal Sign-in Activity

A recent discussion on BlueSky (my account is @office365itpros.bsky.social) alerted me to an Entra ID preview Usage & insights feature (Figure 1) to give administrators a view into service principal sign-in activity. This is important because if an attacker can compromise a privileged account in a tenant, they can create an app, give it permissions, and use the app to exfiltrate data. Keeping a wary eye on app activity is a good idea, as is reviewing the set of permissions held by apps (here’s a PowerShell script to report app permissions).

Service principal sign-in activity in the Entra admin center
Figure 1: Service principal sign-in activity in the Entra admin center

Whenever a feature turns up in the Entra admin center, there’s usually a Graph API (listServicePrincipalSignInActivities), and wherever there’s a Graph API, there might be a Microsoft Graph PowerShell SDK cmdlet (Get-MgBetaReportServicePrincipalSignInActivity), and with a cmdlet, we can retrieve and analyze data.

Writing a Script to Report Service Principals Sign-in Activity

The script I wrote (downloadable from GitHub) does the following:

  • Runs Get-MgServicePrincipal to retrieve the set of service principals known in the tenant.
  • Build a hash table of application identifiers and display names (sign-in records for service principals don’t include the app name).
  • Runs Get-MgBetaReportServicePrincipalSignInActivity to find sign-in activity for service principals when the last sign-in date is more than a year old.
  • Creates a report about the service principals and exports the data to a CSV file.
  • Generates some statistics such as the tenants that own apps, total service principals, etc.

Here’s what I found in my tenant:

Some notes about service principals for the Office 365 for IT Pros tenant
-------------------------------------------------------------------------

Service Principals by owning tenant

Tenant Name                        Tenant ID                            Number of Apps
-----------                        ---------                            --------------
Microsoft Services                 f8cdef31-a31e-4b4a-93e4-5f571e91255a            563
Office 365 for IT Pros             a662313f-14fc-43a2-9a7a-d2e27f4f3478             58
Microsoft                          72f988bf-86f1-41af-91ab-2d7cd011db47             19
Microsoft Accounts                 9188040d-6c67-4c5b-b112-36a304b66dad              2
PRDTRS01                           cdc5aeea-15c5-4db6-b079-fcadd2505dc2              2
trustportal                        7579c9b7-9fa5-4860-b7ac-742d42053c54              2
Adobe Inc                          f889b897-fa4a-4d20-b6dd-182555a5b308              1
Apple Inc.                         e0fad04c-a04c-41ab-b35e-dc523af755a1              1
Office 365 Customer Success Center d25014ba-ff6e-4f21-a7a7-698d6e524490              1
Microsoft Community & Event Tenant b4c9f32e-da17-4ded-9c95-ce9da38f25d9              1
Microsoft                          0d2db716-b331-4d7b-aa37-7f1ac9d35dae              1
PnP                                73da091f-a58d-405f-9015-9bd386425255              1
LinkedIn Production                658728e7-1632-412a-9815-fe53f53ec58b              1
AdobeExternal                      55aa7ab7-a04b-4623-ba3b-04cda52e667f              1
Credly                             54e44946-b280-4ccf-b102-2224d7008f17              1
Merill                             10407d69-1ba5-4bec-8ebe-9af2f0b9e06a              1
eventpoint                         0e45e1a3-686e-44ec-8f47-5daa29692074              1
mspmecloud                         975f013f-7f24-47e8-a7d3-abc4752bf346              1
Adobe                              fa7b1b5a-7b34-4387-94ae-d2c178decee1              1

Total Service Principals 668
Service Principals with no sign-ins in the last year 90
Service Principals with sign-ins in the last year 578
Number of apps with no service principal 46

The tenant names include Apple (used to reset authentication methods for Apple devices during the Exchange basic authentication retirement project) and several for Adobe (one of which is likely to connect SharePoint Online to the Adobe Cloud). The LinkedIn tenant likely hosts the app to connect LinkedIn data with the Microsoft 365 profile card. The PnP tenant is for the app used by the PnP PowerShell module, and the Merill tenant is home of many tools authored by Merill Fernando. This entry might be used to document conditional access policies in PowerPoint.

A total of 46 sign-in activity records for service principals could not be associated with a current service principal. This might be due to a bug in the preview feature, but it could also be due to the removal of apps by developers.

A list of the identifiers for Microsoft apps is available online. From the list I found a number of apps that are no longer in the set of service principals, including Office Online Client Microsoft Entra ID- Augmentation Loop (2abdc806-e091-4495-9b10-b04d93c3f040), OfficeShredderWacClient (4d5c2d63-cf83-4365-853c-925fd1a64357), Office Online Client Microsoft Entra ID- Loki (b23dd4db-9142-4734-867f-3577f640ad0c), and Microsoft Authentication Broker (29d9ed98-a469-4536-ade2-f981bc1d605e).

New Tools, New Insights

The nice thing about new tools is that they open up new opportunities to use data to gain additional insights into what happens in a tenant. Now that I can monitor and analyze service principal sign-in activity with PowerShell, I’ll be doing it regularly.


Need more help to write PowerShell 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/2024/11/21/service-principal-sign-in-activity/feed/ 2 67161
Manage PIM Role Assignments with the Microsoft Graph PowerShell SDK https://office365itpros.com/2024/11/14/pim-role-assignment-powershell/?utm_source=rss&utm_medium=rss&utm_campaign=pim-role-assignment-powershell https://office365itpros.com/2024/11/14/pim-role-assignment-powershell/#comments Thu, 14 Nov 2024 07:00:00 +0000 https://office365itpros.com/?p=67043

Add Eligible and Active PIM Role Assignment Requests

I recently wrote about Microsoft’s recommendation to use the UnifiedRoleDefinition Graph API instead of the older DirectoryRole API. In that article, I show how to use the Microsoft Graph PowerShell SDK to make role assignments to user accounts. Assignments made in this manner are effective immediately. The assignments are permanent and last until an administrator removes them from accounts.

In many Microsoft 365 tenants where a limited set of administrators run operations, permanent role assignments work well. However, in larger tenants, some additional control is often desirable. Microsoft’s answer is Entra ID Privileged Identity Management (PIM), designed to enable administrators “manage, control, and monitor access to important resources in your organization.” PIM assignments can be permanent, but more commonly the assignments are time-limited to allow administrators to perform tasks on a just-in-time basis without their account needing elevated permissions on an ongoing basis. PIM is not part of the basic Entra ID license granted with Microsoft 365 and administrators need a license like Entra ID P2 to use PIM. See this page for more licensing information.

Microsoft’s Recommendation to use Entra Admin Center to Manage PIM Role Assignments

The PIM overview contains the interesting recommendation that tenants should use “PIM to manage active role assignments over using the unifiedRoleAssignment or the directoryRole resource types to manage them directly.” In other words, Microsoft thinks it better to use the GUI built into the Entra admin center to create and manage PIM role assignments. The reason for this might be that the GUI includes guardrails to stop administrators from making mistakes, which is something to avoid when assigning privileged roles.

In any case, PIM organizes role assignments into two categories:

  • Eligible assignments are roles granted to users, groups, or service principals (apps) that are not active. These assignments must be activated by the holder (principal) before they can perform the privileged tasks enabled by the role. By default, eligible assignments are activated for a maximum of 8 hours, after which the activation can be extended or renewed.
  • Active assignments are roles that are currently available for use. An active assignment can be permanent, but more often in PIM it is time-limited.

Both categories have a schedule, and Graph APIs and SDK cmdlets are available to add requests to add, update, and remove assignments from the schedules.

Creating an Eligible PIM Role Assignment

Here’s the PowerShell code to create a new eligible assignment schedule request to add a user account to the User administrator role. Before the New-MgRoleManagementDirectoryRoleEligibilityScheduleRequest cmdlet can run, a certain amount of setup is necessary to fetch the identifiers for the account and role and define the period during which the assignment is eligible. You also need to decide whether the assignment is for the entire directory or an administrative unit.

$User = Get-MgUser -UserId Lotte.Vetler@office365itpros.com
[array]$DirectoryRoles = Get-MgRoleManagementDirectoryRoleDefinition | Sort-Object DisplayName
$UserAdminRoleId = $DirectoryRoles | Where-Object {$_.DisplayName -eq "User administrator"} | Select-Object -ExpandProperty Id
[string]$StartAssignmentDate = Get-Date -format "yyyy-MM-ddTHH:mm:ssZ"
[string]$EndAssignmentDate = (Get-Date).AddDays(30).ToString("yyyy-MM-ddTHH:mm:ssZ")

$ScheduleInfo = @{}
$ScheduleInfo.Add("startDateTime", $StartAssignmentDate)

$ExpirationInfo = @{}
$ExpirationInfo.Add("type", "afterDateTime")
$ExpirationInfo.Add("endDateTime", $EndAssignmentDate)

$ScheduleInfo.Add("expiration", $ExpirationInfo)

$AssignmentParameters = @{}
$AssignmentParameters.Add("action", "adminAssign")
$AssignmentParameters.Add("justification", "Assign User administrator role to user")
$AssignmentParameters.Add("roleDefinitionId", $UserAdminRoleId)
$AssignmentParameters.Add("directoryScopeId", "/")
$AssignmentParameters.Add("principalId", $User.Id)
$AssignmentParameters.Add("scheduleInfo", $ScheduleInfo)

$Status = New-MgRoleManagementDirectoryRoleEligibilityScheduleRequest -BodyParameter $AssignmentParameters
If ($Status.Id) {
   Write-Host ("Assignment for user administrator role for {0} added to eligibility schedule" -f $User.displayName)
}

The values in the hash table holding the parameters for the new assignment looks like this:

$AssignmentParameters

Name                           Value
----                           -----
justification                  Assign User administrator role to user
scheduleInfo                   {[startDateTime, 2024-11-12T17:51:03Z], [expiration, System.Collections.Hashtable]}
directoryScopeId               /
roleDefinitionId               fe930be7-5e62-47db-91af-98c3a49a38b1
principalId                    ce0e26f8-da88-4efa-90ad-d16df1d9500d
action                         adminAssign

The result of a successful assignment as seen in the Entra admin center looks like the example shown in Figure 1.

The eligible role assignment created by PowerShell

PIM Role assignment
Figure 1: The eligible role assignment created by PowerShell

The assigned user receives email about the assignment and can use the link in the message to activate their assignment (Figure 2). See this article about approval workflows that you might like to use to control activations.

Email informing user that they've received a PIM role assignment
Figure 2: Email informing user that they’ve received a PIM role assignment

Accounts holding the Privileged Role Administrator or Global Administrator role also receive email to inform them about the new assignment.

Creating an Active PIM Role Assignment

The code to create a PIM active role assignment request is like that used for the PIM eligible role assignment request. In this example, we create an active role assignment schedule request for the Groups administrator role and limit the assignment to a six hour period from now. The duration is expressed in ISO8601 duration format, so PT6H means six hours.

$GroupsAdminRoleId = $DirectoryRoles | Where-Object {$_.DisplayName -eq "Groups administrator"} | Select-Object -ExpandProperty Id
[string]$StartAssignmentDate = Get-Date -format "yyyy-MM-ddTHH:mm:ssZ"
$ScheduleInfo = @{}
$ScheduleInfo.Add("startDateTime", $StartAssignmentDate)

$ExpirationInfo = @{}
$ExpirationInfo.Add("type", "afterDuration")
$ExpirationInfo.Add("duration","PT6H")

$ScheduleInfo.Add("expiration", $ExpirationInfo)

$AssignmentParameters = @{}
$AssignmentParameters.Add("action", "adminAssign")
$AssignmentParameters.Add("justification", "Assign Groups administrator role to user")
$AssignmentParameters.Add("roleDefinitionId", $GroupsAdminRoleId)
$AssignmentParameters.Add("directoryScopeId", "/")
$AssignmentParameters.Add("principalId", $User.Id)
$AssignmentParameters.Add("scheduleInfo", $ScheduleInfo)
$Status = New-MgRoleManagementDirectoryRoleAssignmentScheduleRequest -BodyParameter $AssignmentParameters
If ($Status.Id) {
   Write-Host ("Assignment for Groups administrator role for {0} added to active schedule" -f $User.displayName)
}

To remove a role assignment from a schedule, create another role assignment schedule request and state the action to be “adminRemove” rather than “adminAssign.” For example, the request to remove the assignment request created above is:

$AssignmentParameters = @{}
$AssignmentParameters.Add("action", "adminRemove")
$AssignmentParameters.Add("justification", "Remove Groups administrator role to user")
$AssignmentParameters.Add("roleDefinitionId", $GroupsAdminRoleId)
$AssignmentParameters.Add("directoryScopeId", "/")
$AssignmentParameters.Add("principalId", $User.Id)
$Status = New-MgRoleManagementDirectoryRoleAssignmentScheduleRequest -BodyParameter $AssignmentParameters#
If ($Status.Status -eq "Revoked") { Write-Host "Active assignment revoked" }

Required Permissions for PIM

Adding role assignments requires the RoleManagement.ReadWrite.Directory permission. If you’re only reading role information, the RoleManagement.Read.Directory permission is sufficient. In addition, when using delegated permissions, read operations are only possible when the signed-in account holds one of the Global Reader, Security Operator, Security Reader, Security Administrator, or Privileged Role Administrator roles. Write operations, like adding a new role assignment to a schedule, require the signed-in account to hold the Privileged Role Administrator (or Global administrator) role.

Most Will Use the Entra Admin Center

Although it’s straightforward to create and manage PIM role assignment schedule requests with PowerShell, it’s easier to use the Entra admin center. Microsoft has done the work to create and refine the GUI and create the necessary checks to make sure that administrators don’t do something silly. I suspect that most administrators will interact with PIM through the Entra admin center, but it’s nice to know that the option to automate with PowerShell exists too.


Need more advice about how to write PowerShell 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/2024/11/14/pim-role-assignment-powershell/feed/ 4 67043
Microsoft Recommends the UnifiedRoleDefinition Graph API for Role Assignment Automation https://office365itpros.com/2024/11/11/unifiedroledefinition-api/?utm_source=rss&utm_medium=rss&utm_campaign=unifiedroledefinition-api https://office365itpros.com/2024/11/11/unifiedroledefinition-api/#comments Mon, 11 Nov 2024 07:00:00 +0000 https://office365itpros.com/?p=66984

A New Graph API to Replace Two Existing APIs

The Graph change log update posted on October 21, 2024 contains a simple and blunt recommendation for developers to use the unifiedRoleDefinition Graph resource type (API) instead of the directoryRole and directoryRoleTemplate resource types (Figure 1). Sounds good. Using one API to replace two should simplify development, and the new API is more flexible and delivers more information about Entra ID roles.

Microsoft recommendation to use the unifiedRoleDefinition API
Figure 1: Microsoft recommendation to use the unifiedRoleDefinition API

The Impact on PowerShell Scripts

But then you look under the surface and understand that this change affects PowerShell scripts that use Microsoft Graph PowerShell SDK cmdlets like Get-MgDirectoryRole (find Entra ID roles) and Get-MgDirectoryRoleMember (find the membership of a role group). This March 2022 article explains how to use those and other SDK cmdlets to assign roles to user accounts.

In effect, if you want to follow Microsoft’s recommendation, you need to upgrade scripts to use cmdlets like Get-MgRoleManagementDirectoryRoleDefinition instead (or the beta version, which provides more information). There’s no necessity to upgrade scripts for the moment, but this issue could arise in the future if Microsoft decides to deprecate the older APIs. All of which creates the question of why Microsoft didn’t simply upgrade the old APIs instead?

I’m sure that there’s good technical reasons for introducing a new API. It’s cleaner for a start because it avoids all the messiness involved in supporting backwards compatibility for an indeterminate period. Professional developers are used to changing APIs and, while they’ll grumble at the need to do more work, the change will happen. Those who write PowerShell might be less happy. There’s a lot happening in the Entra space to force change in scripts already and introducing another change to the mix now sounds like a bad idea that demonstrates a certain lack of sympathy for part-time PowerShell developers, many of whom are struggling to move over to the Graph SDK.

Example PowerShell Cmdlet Changes

Let’s look at the script code changes that might be required. I’ll use the code in the March 2022 article as the base. First, we find the set of Entra ID roles in the tenant.

[array]$DirectoryRoles = Get-MgDirectoryRole | Sort-Object DisplayName

The equivalent with the unifiedRoleDefinition API is:

[array]$DirectoryRoles = Get-MgRoleManagementDirectoryRoleDefinition | Sort-Object DisplayName

In my tenant, the first command returns 36 roles, the second 113. This is because the new API returns all roles and role templates.

Interestingly, the Graph X-Ray tool reveals that the Entra admin center uses the beta version of the cmdlet (Get-MgBetaRoleManagementDirectoryRoleDefinition) when it fetches roles for the All roles page.

Each role has an identifier which is needed to find information about role assignments. To find the identifier for a role, use the role name to filter the set of roles fetched above. For instance, here’s how to find the identifier for the Teams administrator role. This code remains the same:

$TeamsAdminRoleId  = $DirectoryRoles | Where-Object {$_.DisplayName -eq "Teams administrator"} | Select -ExpandProperty Id

Note that the two APIs return different identifiers for roles. If you have hard-coded role identifiers in scripts, the values will need to be updated to match the API.

We want to make a new role assignment to add a user account to the role. To start, we find the set of current role members. With the old API, the command is:

[array]$RoleMembers = Get-MgDirectoryRoleMember -DirectoryRoleId $TeamsAdminRoleId

With the new, the command is:

[array]$RoleMembers = Get-MgRoleManagementDirectoryRoleAssignment -Filter "roleDefinitionId eq '$TeamsAdminRoleId'" -ExpandProperty "principal"

One thing to note is that the information returned for role assignments in the older API only includes user accounts, and the additionalProperties property includes details of the accounts, which means that you can do something like this to see the names of the assignees:

ForEach ($Member in $RoleMembers) {
   Write-Output $Member.AdditionalProperties.displayName
}

This doesn’t happen with the new API. I think two reasons exist why this is so. First, the API includes groups and service principals in role assignments. Group-based role assignments are relatively new and are a good way to manage assignments, especially in large organizations. Being able to understand service principal assignments is for objects like Azure automation accounts used to execute runbooks or Entra registered apps that need to act in an administrative role. In the assignments listed in Figure 2, only two are user accounts.. Second, the API includes support for scoping (full directory, administrative units, or applications).

Assignments for the Exchange administrator role
Figure 2: Assignments for the Exchange administrator role

Making a Role Assignment

The last example from the article is to assign a role to a user account. The code first checks if the user account already has an assignment, and if not, calls the New-DirectoryRoleMemberByRef cmdlet to make the assignment:

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)"}

To make a role assignment with the new API, create a hash table to hold the parameters, including the identifier for the role to assign, the identifier of the account or service principal to receive the role assignment, and the directory scope. In this case, the scope is “/”, meaning the entire directory. If you want to limit the role assignment to specific administrative units, include the identifiers for the administrative units instead.

If ($User.Id -notin $RoleMembers.Id) {
  $Assignment = @{}
  $Assignment.Add("@odata.type","#microsoft.graph.unifiedRoleAssignment")
  $Assignment.Add("roleDefinitionId", $TeamsAdminRoleId)
  $Assignment.Add("principalId", $User.Id)
  $Assignment.Add("directoryScopeId", "/")
  New-MgRoleManagementDirectoryRoleAssignment -BodyParameter $Assignment
}

Id                                            PrincipalId                          RoleDefinitionId                     DirectoryScopeId AppScopeId
--                                            -----------                          ----------------                     ---------------- ----------
3ywjKSOT_UKt4h0JevPk3oeS4FkbrPdPgKMI0NHu2Tk-1 59e09287-ac1b-4ff7-80a3-08d0d1eed939 29232cdf-9323-42fd-ade2-1d097af3e4de /

$Assignment

Name                           Value
----                           -----
roleDefinitionId               4c962061-2581-417f-938a-7cc1b38fc2a2
directoryScopeId               /
principalId                    59e09287-ac1b-4ff7-80a3-08d0d1eed939
@odata.type                    #microsoft.graph.unifiedRoleAssignment


In terms of Privileged Identity Management, the resulting assignment is active and permanent, so the new holder can use it immediately and will show up in administrative interfaces as a role holder (and in scripts like my reporting administrative role assignments script, which uses the unifiedRoleDefinition API).

Adding role assignments requires the RoleManagement.ReadWrite.Directory permission. If you’re only reading role information, the RoleManagement.Read.Directory permission is sufficient. In addition, when using delegated permissions, read operations are only possible when the signed-in account holds one of the Global Reader, Security Operator, Security Reader, Security Administrator, or Privileged Role Administrator roles. Write operations require the signed-in account to hold the Privileged Role Administrator (or Global administrator) role.

More to Come

There’s lots more to investigate in terms of exploiting the UnifiedRoleDefinition API to automate the management of Entra role assignments. We’ll get to that in future articles. In the meantime, the message is that Microsoft is focused on using the UnifiedRoleDefinition API going forward, so that’s where your focus should be too.


Need more advice about how to write PowerShell 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/2024/11/11/unifiedroledefinition-api/feed/ 1 66984
Why Are Per-User MFA Settings Available in the Entra Admin Center? https://office365itpros.com/2024/10/30/per-user-mfa-entra-admin/?utm_source=rss&utm_medium=rss&utm_campaign=per-user-mfa-entra-admin https://office365itpros.com/2024/10/30/per-user-mfa-entra-admin/#comments Wed, 30 Oct 2024 07:00:00 +0000 https://office365itpros.com/?p=66839

Conditional Access Still Preferred Over Per-User MFA

I was asked if the existence of an option to manage per-user MFA in the Entra admin center (Figure 1) means that Microsoft plans better support for this option. The answer is an emphatic no. Microsoft continues to emphasize the use of conditional access policies to enforce multifactor authentication, the logic being that conditional access policies are much more flexible and effective than the somewhat blunt nature of the on-off per-user MFA option.

Per-user MFA management option in the Entra admin center
Figure 1: Per-user MFA management option in the Entra admin center

Largely because per-user MFA originally appeared as a feature bundled with the Office 365 E3 and E5 licenses, the ability to manage user MFA settings was already available through the configure multifactor authentication (MFA) page in the Microsoft 365 admin center. The Microsoft 365 admin center refers to per-user MFA as “legacy.” Curiously, the Entra admin center isn’t so presumptive and restricts itself to a link to the MFA deployment planning guide.

Same User Interface for Per-User MFA

Both the Microsoft 365 admin center and Entra admin center use much the same interface to permit administrators to configure per-user MFA, and both display details of guest and member accounts to configure. Seeing guest accounts in the list sometimes confuses administrators, but it’s because you can enable per-user MFA for a guest account in exactly the same way as for a member account.

If Microsoft wants to focus on conditional access policies as the basis for enabling and enforcing multifactor authentication for Entra ID accounts, why does the option to manage per-user MFA exist in the Entra admin center? You might ask the same question about why Microsoft added a Graph API to deliver the ability to report the per-user MFA state for accounts.

Conditional Access Remains the Strategic Direction

In both cases, I think it’s a simple realization that customers use per-user MFA for their own reasons and that it’s better to have people use per-user MFA than not. Perhaps an organization doesn’t have the Entra P1 licenses necessary to use conditional access policies (a situation more usual in the SME sector than in enterprise tenants). Perhaps they haven’t had the chance to figure out what conditional access policies are needed to protect access for different groups of accounts and apps. Conditional access policies can be complex and it’s easy to develop policies that conflict with each other or block access in unexpected situations.

Microsoft’s direction over the long term remains focused on conditional access policies. Even in the documentation for per-user MFA, Microsoft emphasizes that “The best way to protect users with Microsoft Entra MFA is to create a Conditional Access policy.” To back the assertion up, Microsoft continue to add new features to govern conditional access (with the side effect of increasing the potential for policy complexity) and continues to emphasize the need for strong authentication methods like the authenticator app or passkeys.

Supporting the use of conditional access, Entra ID recommendations include a specific recommendation covering migration from per-user MFA to conditional access. Another recommendation covers movement away from SMS and voice as authentication methods.

Including the option to manage legacy per-user MFA or report the state of per-user MFA for individual accounts doesn’t affect Microsoft’s strategic direction for controlling connectivity to Entra ID tenants. It might just slow progress of some organizations to fully embracing conditional access.

Old Feature on the Way Out

I don’t know why Microsoft chose to include the option to manage per-user MFA in the Entra admin center. Given the long-term direction for Entra, it seems odd to include a legacy feature where a perfectly good admin console supports management of the feature. But perhaps it’s just a matter of adding coverage to the console where administrators might logically look for MFA management. In any case, the important point is that there’s no change of direction. The original method to manage MFA is on the way out. The only question is when Microsoft will announce the date for the axe to descend.


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/2024/10/30/per-user-mfa-entra-admin/feed/ 4 66839
How to Restore the Service Plan for a Microsoft 365 Product License https://office365itpros.com/2024/10/29/enable-service-plan/?utm_source=rss&utm_medium=rss&utm_campaign=enable-service-plan https://office365itpros.com/2024/10/29/enable-service-plan/#respond Tue, 29 Oct 2024 07:00:00 +0000 https://office365itpros.com/?p=66823

Reasons Exist to Disable Service Plans and Enable Service Plans

Plenty of articles are available on the internet to explain how to disable a service plan from a Microsoft 365 license. In this respect, a service plan controls a component within a Microsoft 365 product (SKU), such as Exchange Online, Sway, Microsoft Bookings, or Viva Engage. Some Microsoft products include many service plans. For instance, Office 365 E3 spans 38 service plans, which are referred to as apps when viewing user account details in the Microsoft 365 admin center. Some apps, like OneDrive for Business, are practically impossible to remove because of dependencies that exist across different Microsoft 365 components.

It’s common for organizations to disable service plans when they decide that there’s no need for an app. After Microsoft renamed Yammer to be Viva Engage, they introduced a new Viva Engage core service plan, and quite a few organizations promptly disabled the new plan because they never used Yammer and had no plans to use Yammer. Another example is Clipchamp, a very competent video editing application that’s part of Office 365 and higher licenses. Although Clipchamp offers a more comprehensive array of options to edit videos than is found in the Stream app, I’ve heard companies say that they have no interest in users creating and editing videos. Clipchamp is useful to the corporate marketing team, but no one else needs it, so the organization disables the Clipchamp service plan for most accounts.

Disable a Service Plan

To disable a service plan, uncheck the app from the set listed for the user account in the Microsoft 365 admin center. In Figure 1, the Microsoft Bookings service plan is disabled. To disable Clipchamp, all that’s required is to uncheck the entry for the Microsoft Clipchamp service plan.

Disabling a service plan in the Microsoft 365 admin centre

Enable service plan
Figure 1: Where to enable service plans in the Microsoft 365 admin center

Using the admin center is fine to disable or enable a service plan for just a few accounts. Automating the process with PowerShell is better when dealing with more accounts.

To disable a service plan, we need to know the identifier for the product SKU assigned to the user account and the identifier for the service plan. The easiest way to find this information is to consult Microsoft’s product names and identifiers page. Open the page and download the CSV file (frequently used by scripts such as the Microsoft 365 licensing report). Then search the file for the product name (like Office 365 E3) to find the SKU identifier (6fd2c87f-b296-42f0-b197-1e91e994b900). The service plans included in the SKU are also listed, and you should also be able to find the service plan identifier for the app you want to remove.

However, the August 19, 2024, version of the file doesn’t list Clipchamp for either Office 365 E3 or E5. Omissions like this sometimes happen, but it’s easy to find the service plan identifier for Clipchamp in other product SKUs. The identifier is a1ace008-72f3-4ea0-8dac-33b3a23a2472.

To confirm that these identifiers are correct, run the Get-MgUserLicenseDetail cmdlet against an account that you know has an Office 365 E3 license and check for a Clipchamp service plan:

Get-MgUserLicenseDetail -UserId Ben.James@office365itpros.com | Where-Object {$_.SkuId -eq '6fd2c87f-b296-42f0-b197-1e91e994b900'} | Select-Object -ExpandProperty ServicePlans | Where-Object {$_.ServicePlanName -like "*ClipChamp*"}

AppliesTo ProvisioningStatus  ServicePlanId                        ServicePlanName
--------- ------------------  -------------                        ---------------
User      PendingProvisioning a1ace008-72f3-4ea0-8dac-33b3a23a2472 CLIPCHAMP

The Set-MgUserLicense cmdlet manages license details and takes two arrays as input parameters. The first array (AddLicenses) hold details of licenses (SKUs) to add or modify. The second (RemoveLicenses) holds details of licenses to remove. In this case, we want to modify a license that the user account already has, so we’ll use the AddLicenses array to specify the SKU to update along with the service plan that we wish to disable. An account might already have some disabled service plans for the target SKU, so the first step is to find if any exist and store the identifiers for any previously-disabled service plans in an array. It’s critical to check for previously-disabled service plans as otherwise Entra ID will reenable these plans if you run Set-MgUserLicense to disable other plans.

[array]$DisabledServicePlans = Get-MgUserLicenseDetail -UserId Ben.James@office365itpros.com | Where-Object {$_.SkuId -eq '6fd2c87f-b296-42f0-b197-1e91e994b900'} | Select-Object -ExpandProperty ServicePlans | Where-Object {$_.ProvisioningStatus -eq "Disabled"} | Select-Object -ExpandProperty ServicePlanId

Now add the service plan for Clipchamp to the array:

$DisabledServicePlans += "a1ace008-72f3-4ea0-8dac-33b3a23a2472"

Finally, run the Set-MgUserLicense cmdlet to update the Office 365 E3 SKU:

Set-MgUserLicense -UserId Ben.James@Office365itpros.com -AddLicenses @{SkuId = '6fd2c87f-b296-42f0-b197-1e91e994b900'; DisabledPlans = $DisabledServicePlans} -RemoveLicenses @()

Enable Service Plans for Microsoft 365 Licenses

To restore a service plan, use the same code to find the set of disabled service plans, remove the service plan identifier from the array, and run Set-MgUserLicense. This code restores Clipchamp to the set of service plans available to the user:

$DisabledServicePlans = $DisabledServicePlans -ne "a1ace008-72f3-4ea0-8dac-33b3a23a2472"
Set-MgUserLicense -UserId Ben.James@Office365itpros.com -AddLicenses @{SkuId = '6fd2c87f-b296-42f0-b197-1e91e994b900'; DisabledPlans = $DisabledServicePlans } -RemoveLicenses @()

If you don’t fetch details of disabled service plans and pass an empty array, Entra ID will restore all disabled service plans. This might or might not be what you intended.

Understand the Identifiers

Disabling and restoring service plans for Microsoft 365 licenses through PowerShell might seem complicated. In reality, it’s simple, once you understand how product (SKU) identifiers and service plans work.


Need more advice about how to write PowerShell 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/2024/10/29/enable-service-plan/feed/ 0 66823
How to Set Directory Synchronization Features with the Graph https://office365itpros.com/2024/10/24/directory-synchronization-features/?utm_source=rss&utm_medium=rss&utm_campaign=directory-synchronization-features https://office365itpros.com/2024/10/24/directory-synchronization-features/#comments Thu, 24 Oct 2024 07:00:00 +0000 https://office365itpros.com/?p=66787

UPN and sAMAccountName Updates and Entra ID Directory Synchronization Features

The other day, I received a note from an Office 365 for IT Pros reader to say that they’d perused the book to seek advice about how best to handle the situation when someone needs to change their name, usually because of marriage or divorce. The reader says that their usual practice is to change the user’s email address in Active Directory, but that they avoid changing the user principal name and sAMAccountName because changing “either or both of those attributes breaks their connection with Microsoft 365 services when the sync occurs.”

Microsoft documents issues that can occur when a user principal name changes, and there are quite a few forum discussions about changing attributes in Active Directory (here’s an example). We don’t cover directory synchronization in the Office 365 for IT Pros eBook. We used to, but then relegated the coverage to the companion volume, and then we dropped the companion volume because most of its material had aged significantly.

Use a Deprecated Module to Set Directory Synchronization Features

Seeing that I had no good answer for our reader, I pushed the question to Brian Desmond, who looks after the Entra ID chapter in the book. His response was “Changing the UPN or sAMAccountName [for a user account] should not break the sync process because Entra Connect uses their objectGUID in AD as the anchor. That said, you need to turn on the SynchronizeUpnForManagedUsers feature for that change to work right.”

Brian went on to reference the Set-MsolDirSyncFeature cmdlet as the way to enable the SynchronizeUpnForManagedUsers feature. The cmdlet is from the MSOL (Microsoft Online Services) module, which is deprecated and due for final retirement on March 30, 2025. The question then is how to set the feature without using a soon-to-be-removed cmdlet?

The Graph Answer for Managing Directory Synchronization Features

The answer is to use the UpdateonPremisesDirectorySynchronization Graph API to update the properties of the onPremisesDirectorySynchronizationFeature resource type, where we discover that synchronizeUpnForManagedUsersEnabled is a Boolean property.

Where there’s a Graph API, there’s a Microsoft Graph PowerShell SDK cmdlet. In this case, the Update-MgDirectoryOnPremiseSynchronization cmdlet (I’ve already flagged the error in referring to “OnPremises” as “OnPremise;” and yes, these things matter).

Here’s how to update two directory synchronization feature settings with the Graph SDK cmdlet. First, find the identifier for the directory synchronization object in the tenant:

$SyncId = Get-MgDirectoryOnPremiseSynchronization | Select-Object -ExpandProperty Id

Now build a hash table for the features to enable (or disable). The keys for the hash table must match (including casing) the properties described here.

$Features = @{}
$Features.Add("softMatchOnUpnEnabled",$true)
$Features.Add("synchronizeUpnForManagedUsersEnabled",$true)

Finally, build another hash table to hold the parameters for the update cmdlet and run the cmdlet:

$Parameters = @{}
$Parameters.Add("features",$Features)
Update-MgDirectoryOnPremiseSynchronization -OnPremisesDirectorySynchronizationId $SyncId -BodyParameter $Parameters

To check the current state of the directory synchronization settings, run the Get-MgDirectoryOnPremiseSynchronization cmdlet:

Get-MgDirectoryOnPremiseSynchronization | Select-Object -ExpandProperty Features | fl

BlockCloudObjectTakeoverThroughHardMatchEnabled  : False
BlockSoftMatchEnabled                            : False
BypassDirSyncOverridesEnabled                    : False
CloudPasswordPolicyForPasswordSyncedUsersEnabled : False
ConcurrentCredentialUpdateEnabled                : False
ConcurrentOrgIdProvisioningEnabled               : False
DeviceWritebackEnabled                           : False
DirectoryExtensionsEnabled                       : False
FopeConflictResolutionEnabled                    : False
GroupWriteBackEnabled                            : False
PasswordSyncEnabled                              : False
PasswordWritebackEnabled                         : False
QuarantineUponProxyAddressesConflictEnabled      : False
QuarantineUponUpnConflictEnabled                 : False
SoftMatchOnUpnEnabled                            : True
SynchronizeUpnForManagedUsersEnabled             : True
UnifiedGroupWritebackEnabled                     : False
UserForcePasswordChangeOnLogonEnabled            : False
UserWritebackEnabled                             : False
AdditionalProperties                             : {}

Entra PowerShell Module’s Directory Synchronization Feature Cmdlets

And because Microsoft introduced the Entra PowerShell module in preview in June 2024 specifically to help customers migrate away from the deprecated AzureAD and MSOL modules, there’s also the Set-EntraDirSyncFeature cmdlet. Microsoft handcrafted the cmdlets in the Entra module to make them more PowerShell-like than Graph-like, so this cmdlet is the easiest one to use.

The Entra module includes directory synchronization feature cmdlets
Figure 1: The Entra module includes directory synchronization feature cmdlets

To make the change, I installed the latest version of the Entra preview module (Figure 1) from the PowerShell gallery, and then ran:

Import-Module Microsoft.Graph.Entra
Connect-Entra -Scopes OnPremDirectorySynchronization.ReadWrite.All
Set-EntraDirSyncFeature -Feature SynchronizeUpnForManagedUsers -Enabled:$true

The Get-EntraDirSyncFeature cmdlet reveals the current state for directory synchronization features:

Get-EntraDirSyncFeature

Enabled DirSyncFeature
------- --------------
  False BlockCloudObjectTakeoverThroughHardMatch
  False BlockSoftMatch
  False BypassDirSyncOverrides
  False CloudPasswordPolicyForPasswordSyncedUsers
  False ConcurrentCredentialUpdate
  False ConcurrentOrgIdProvisioning
  False DeviceWriteback
  False DirectoryExtensions
  False FopeConflictResolution
  False GroupWriteBack
  False PasswordSync
  False PasswordWriteback
  False QuarantineUponProxyAddressesConflict
  False QuarantineUponUpnConflict
   True SoftMatchOnUpn
   True SynchronizeUpnForManagedUsers
  False UnifiedGroupWriteback
  False UserForcePasswordChangeOnLogon
  False UserWriteback

Each directory synchronization feature must be managed separately. You can’t enable or disable several features in one operation.

Any Lingering Synchronization Issues?

Although I discovered how to replace the old MSOL cmdlet with a new Entra cmdlet to set directory synchronization features, I still didn’t find out if people encounter synchronization issues after updating on-premises user account properties like the user principal name and sAMAccountName. If you’ve had problems that you couldn’t resolve, note them as a comment. Maybe someone else will have a solution.


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/2024/10/24/directory-synchronization-features/feed/ 3 66787
How to Force Users to Sign in Weekly https://office365itpros.com/2024/10/23/revoke-access-for-user-accounts/?utm_source=rss&utm_medium=rss&utm_campaign=revoke-access-for-user-accounts https://office365itpros.com/2024/10/23/revoke-access-for-user-accounts/#comments Wed, 23 Oct 2024 07:00:00 +0000 https://office365itpros.com/?p=66774

Revoke Access for User Accounts at a Good Time

A recent question in the Facebook Office 365 Technical Discussions group covered the situation where a conditional access policy imposes a 7-day sign-in frequency. I’m not a great fan of short sign in frequencies because I think the constant nagging for authentication is very distracting for users. If you use a strong authentication method with multifactor authentication, like the Microsoft authenticator app, a more relaxed regime is often justified. Equally so, the security requirements of some organization mandate a higher security profile with more frequent authentication.

In this instance, a strong authentication method is in use (Yubiko FIDO2 keys). The problem is that the week-long sign-in period finishes at different points during the week and can disrupt users at important points in their work. My favorite example is when the CEO is preparing to join a critical Teams call with some investors and is suddenly prompted to reauthenticate. Computer systems have no mercy or appreciation of when people don’t need to be disturbed. All that Entra ID knows is that the 7-day period is up, and the user must reauthenticate.

The ask is therefore how to force reauthentication at a suitable point during the working week, like early on Monday morning. If everyone starts the week off by authenticating, Entra ID won’t bother them until the following Monday.

Azure Automation Runbook to Revoke User Sessions

After thinking about the problem, the simplest solution seems to be to revoke user tokens early every Monday morning. The easiest way to do this is with a PowerShell script that runs as a scheduled task. My preference is always to use Azure Automation for scheduled tasks. Many people like to use the Windows Scheduler to run PowerShell scripts, but I think that Azure Automation is a much better and more secure option.

The outline of the solution is as follows: The PowerShell code to revoke access from user accounts executes as a runbook belonging to an Azure Automation account. To process runbooks, you need an Azure automation account associated with an Azure account with a paid-for subscription.

The code uses cmdlets from the Microsoft Graph PowerShell. The modules containing the cmdlets must be loaded as resources in the automation account. The modules are:

  • Microsoft.Graph.Authentication
  • Microsoft.Graph.Users
  • Microsoft.Graph.Users.Actions
  • Microsoft.Graph.Groups

The automation account uses a managed identity to connect to the Microsoft Graph. To process the user accounts, the automation account must have consent to use the Users.ReadWrite.All and User.RevokeSessions.All application permissions. This article explains how to assign permissions to automation accounts. The automation account must also hold at least the User administrator role.

After authenticating, the runbook finds the set of target accounts to process. If access is to be revoked for every account, the Get-MgUser cmdlet can retrieve the accounts. To avoid the potential of locking everyone out of the tenant, I use a group to identify the set of accounts (a dynamic group would be a good choice), so the Get-MgGroupMember cmdlet fetches the set of accounts to process.

Revoking User Sessions

For each account, the Revoke-MgUserSignInSession cmdlet revokes access and forces the user to reauthenticate. Here’s the code:

Connect-MgGraph -Identity -NoWelcome
# Get users to process
[array]$Users = Get-MgGroupMember -GroupId bdae941b-389d-4972-a78a-9ef2b7dc4c7a -All

ForEach ($User in $Users) {
    $RevokeStatus = Revoke-MgUserSignInSession -UserId $User.Id
    If ($RevokeStatus.Value -eq $true) {
        Write-Output ("Access revoked for user {0}" -f $User.additionalProperties.displayName)
    }
}

Test the code in Azure automation to make sure that everything works (Figure 1). Also make sure that the effect of revoking sessions for user accounts has the desired effect.

Testing the runbook to revoke access for user accounts
Figure 1: Testing the runbook to revoke access for user accounts

Scheduling the Revoke Runbook

When the code runs as expected, publish the runbook and link it to a schedule in the automation account. If an appropriate schedule isn’t available, you’ll need to create one. Figure 2 shows a schedule to execute linked runbooks at 7:00 every Monday.

Weekly schedule to revoke access for user accounts at 7AM on Monday
Figure 2: Weekly schedule to revoke access for user accounts at 7AM on Monday

Everything Worked as Expected

Everything worked as expected for me. Entra ID terminated user sessions to force the users to reauthenticate. The scheduled job made sure that the process happened every Monday morning to allow people to work for the entire week without Entra ID demanding their credentials. If you’re going to look for frequent reauthentication, I guess you should minimize the pain.


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/2024/10/23/revoke-access-for-user-accounts/feed/ 6 66774
Microsoft Graph Doesn’t Support Custom Attributes for Groups https://office365itpros.com/2024/10/21/container-management-label-graph/?utm_source=rss&utm_medium=rss&utm_campaign=container-management-label-graph https://office365itpros.com/2024/10/21/container-management-label-graph/#respond Mon, 21 Oct 2024 07:00:00 +0000 https://office365itpros.com/?p=66745

Detecting Changes in Container Management Labels

Using sensitivity labels to control the settings of Microsoft 365 groups, teams, and sites is a very powerful management tool. Since introducing the mechanism, Microsoft has steadily expanded the set of controls that container management labels can apply, like the privacy mode for a group or team. The latest update introduced settings to control the discoverability of private teams.

Nice as it is for Microsoft to continue evolving container management labels by expanding settings, one thing they haven’t done is to provide any method to lock a container management label in place. New groups, teams, or sites can receive a label during the creation process, or a label can be assigned afterwards. And here lies the problem. Any group owner can change the assigned label and apply a label with more restrictive or more permissive settings. Figure 1 shows the option for a team owner to select a container management label.

Selecting a container management label for a team
Figure 1: Selecting a container management label for a team

The logic here is probably that group owners can change the individual settings controlled by labels, such as external access or sharing modes. If this is the case, why shouldn’t a group owner be able to change all the settings controlled by a label by simply switching out the existing label for another label?

My perspective is different. I think that if an organization assigns a container management label to a group, team, or site, it has a good reason to do so. For example, a new site might hold very confidential information that mandates restricted access. To meet this requirement, the person creating the site chooses an appropriate label. Once that label is in place, it should not be changed without some kind of oversight and approval, and that’s what’s missing in Microsoft 365.

PowerShell to Check Assigned Container Management Labels

Fortunately, it’s possible to create a solution in PowerShell to monitor the labels assigned to containers and highlight inconsistencies. The script discussed in this article uses cmdlets from the Exchange Online management module like Get-UnifiedGroup and Set-UnifiedGroup to retrieve and update labels, and uses a custom attribute to store details of the label that should be assigned to a group.

Recently, I was asked if it was possible to update the script to use the Microsoft Graph PowerShell SDK. The original script (written in early 2021) works, but the Get-UnifiedGroup cmdlet is “heavy” in processing terms. Using the Get-MgGroup cmdlet to find a set of groups is usually faster, especially as the number of groups to be processed climbs past a few hundred.

Updating Sensitivity Labels for Groups

It’s certainly possible to use Get-MgGroup or a Graph API request to find groups and include the assignedLabels property in the information returned for each group:

[array]$Groups = Get-MgGroup -All -PageSize 500 -Filter "(groupTypes/any(c:c eq 'unified'))" 
-Property DisplayName, Id, assignedLabels

An example of using a Graph API request is explained in this article about reporting the labels assigned to groups.

In passing, assignedLabels indicates that an object can be assigned multiple sensitivity labels; this is true, but only when the labels don’t encrypt content. Container management labels can also be used for information protection and to encrypt content using rights management. However, I recommend that organizations use separate sets of sensitivity labels for container management and information protection. This scheme makes labels easier to manage.

The Update-MgGroup cmdlet can be used to assign a label to a group. This code creates a hash table to hold the GUID for the label to assign to a group. It then creates another hash table to hold the parameters for the update and includes the label information (in an array, because there might be multiple labels). Finally, Update-MgGroup applies the label.

$DefaultSensitivityLabel = "e42fd42e-7240-4df0-9d8f-d14658bcf7ce" # Guid for General Access label
$AssignedLabels = @{}
$AssignedLabels.Add("LabelId", $DefaultSensitivityLabel)
$Parameters = @{}
$Parameters.Add("assignedLabels", @($AssignedLabels))

Update-MgGroup -GroupId $Group.Id -BodyParameter $Parameters

But then we run into the problem of how to store details of the label that’s just been assigned. We need this information to check in the future if someone changed the label. The problem is that the Groups resource type in the Microsoft Graph doesn’t support the custom attributes available in Exchange Online and accessible using Get-UnifiedGroup and Set-UnifiedGroup. User accounts does support the 15 custom attributes in the onPremisesExtensionAttributes resource type.

Next Step to Find the Right Extensibility Option

I don’t know why Microsoft decided not to support the custom attributes for Microsoft 365 groups. It’s possible that the Entra ID designers didn’t see the need for these attributes because they weren’t aware of how organizations use custom attributes to store information about groups. Another reason might be that Entra ID supports several group types and a common schema is used for all types.

In any case, other extensibility options exist for the Graph, including directory extensions. The next step is to review each option and figure out which is the best choice. Thinking cap on!


Need more advice about how to write PowerShell 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/2024/10/21/container-management-label-graph/feed/ 0 66745
Adding a Custom Test to the Maester Tool https://office365itpros.com/2024/10/07/custom-maester-test/?utm_source=rss&utm_medium=rss&utm_campaign=custom-maester-test https://office365itpros.com/2024/10/07/custom-maester-test/#respond Mon, 07 Oct 2024 07:00:00 +0000 https://office365itpros.com/?p=66568

Create a Custom Maester Test with PowerShell and the Graph

I last wrote about the Maester tool in April 2024. At that time, Maester had just been released as a community-based framework for automated testing of a Microsoft 365 tenant against well-established frameworks like MITRE. Maester has come a long way since, and it was great to catch up with Merill Fernando and Thomas Naunheim at the TEC 2024 conference in Dallas to assess its current state.

Merill has great information-packed demos, even if they are delivered at dazzling speed. The ability to create custom Maester tests grabbed my attention this time around. Out-of-the-box, Maester comes with a set of tests based on Microsoft recommendations for Entra ID and another based on the Entra ID Security Config Advisor (EIDSCA), another community-driven project.

Regular Maester Tests

Great value can be derived from the results generated by a Maester using its default tests. You might not agree with some of the measured conditions. Last time round, my tenant failed 42 tests. The latest run failed 97. For example, my tenant failed test MS.AAD.3.7 because I don’t have a conditional access policy in place to require managed devices for authentication (Figure 1).

The reasons why a Maester test failed
Figure 1: The reasons why a Maester test failed

Insisting on managed devices is important in some contexts and less important in others. This underlines the need for Maester reports to be treated as a guideline rather than creating the absolute necessity to pass all tests. No one gets extra brownie points for achieving a perfect Maester score, and it might be the case that achieving such a state might be more painful than useful, which can sometimes be the case when seeking better tenant security. As I have noted in the past, cranking up the signin frequency interval enforced by a conditional access policy might seem like a good idea, but it’s not if forcing users to constantly reauthenticate gets in the way of real work.

In any case, the results of a Maester run creates a nice benchmark to measure a tenant against. How the tenant administrators decide to use that benchmark is entirely up to them.

Creating a Custom Maester Test

Coming back to the topic of this article, tenants can add custom tests for Maester to assess. Essentially, if you can grab some data like tenant settings or another type of value by running a Graph API request in PowerShell, a test can assess the data returned by the Graph and either pass or fail.

As an example, I wrote a test to check that the ability for end users to create new Microsoft 365 groups (and teams) is disabled. Allowing people to create new groups is a one-way ticket to team sprawl and excessive digital rot, so it seems like a good thing to test. The setting is in the Entra ID Groups policy and can be fetched and assessed as shown in the code below.

Describe "RAEntraConfig" -Tag "Privilege", "Office 365 for IT Pros" {
    It "OFFICE365.Test01: Check 'Group creation should be blocked for non-authorized users" {
	 
	$Uri = "https://graph.microsoft.com/beta/settings"
        $Result = Invoke-MgGraphRequest -Method Get -Uri $Uri

	$GroupSettings = $Result.Value | Where-Object { $_.displayName -eq 'Group.Unified' }

        $GroupCreationControl = $GroupSettings.values | Where-Object {$_.Name -eq 'EnableGroupCreation'} | Select-Object -ExpandProperty Value
        If ($GroupCreationControl -eq $false) {
           $TestResult = $true
        } else {
           $TestResult = $false
        }

	If ($null -ne $TestResult) {
            $TestResult | Should -Be $true -Because "User ability to create Microsoft 365 Groups is disabled."
        }
	
    }
}

Only one test is present, but you can add multiple tests to the same file. Maester can process custom tests separately or include them in a full run. Figure 2 shows the output from the custom test. It’s functional and not as pretty or informative as Figure 1, but any administrator will know what the test measures.

The result of a custom Maester test
Figure 2: The result of a custom Maester test

If you develop a test that you think would be of interest to other organizations, you can create an issue in the Maester GitHub repository to explain the test and share the code.

Stretching and Expanding Maester

Best practice is a nebulous concept at best. In the cloud, things often change faster than the proponents of best practice can cope. Having a community-driven project like Maester available to assess your tenant is a good way to get a snapshot of how the tenant measures up against security frameworks. Being able to add your own custom Maester tests makes the tool so much better.


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/2024/10/07/custom-maester-test/feed/ 0 66568
The New Entra ID Photo Update Settings Policy for User Profile Photos https://office365itpros.com/2024/09/16/photo-update-settings-policy/?utm_source=rss&utm_medium=rss&utm_campaign=photo-update-settings-policy https://office365itpros.com/2024/09/16/photo-update-settings-policy/#comments Mon, 16 Sep 2024 07:00:00 +0000 https://office365itpros.com/?p=66363

Photo Update Settings Policy is Long-term Unified Replacement for Other Controls

Given the historical foundation of Microsoft 365 in several on-premises applications, it probably wasn’t surprising that we ended up with a confusing mish-mash of routes by which it was possible to update the profile photos for user accounts through SharePoint, Exchange, Teams, Delve, PowerShell, and so on. Looking back, it took a surprising amount of time before Microsoft acknowledged that the situation was untenable.

A new approach that worked across Microsoft 365 was necessary. That process began in October 2023 with the retirement of the Exchange Online cmdlets to update photos for mailboxes. Entra ID is now the definitive source of photo information for user accounts and groups and the foundation for the new approach is a set of Graph APIs surfaced as cmdlets in the Microsoft Graph PowerShell SDK, like Set-MgUserPhotoContent.

A New Photo Update Settings Policy to Control User Profile Updates

In June 2024, Microsoft introduced a new Entra ID policy based on the photoUpdateSettings resource to control who can update photos and the allowed sources for updates. Managing the photo update settings policy requires the PeopleSettings.ReadWrite.All scope. The settings for a tenant can be retrieved as follows:

$Uri = "https://graph.microsoft.com/beta/admin/people/photoupdatesettings"
$Settings = Invoke-MgGraphrequest -Uri $Uri -Method Get
$Settings

Name                           Value
----                           -----
allowedRoles                   {}
@odata.context                 https://graph.microsoft.com/beta/$metadata#admin/people/photoUpdateSettings/$entity
Source

The settings shown above are the default. The supported values are described in the photoUpdateSettings documentation.

Controlling From Where Photos Can Be Updated

The source for photo updates can be undefined, meaning that photo updates can be sourced from applications running in either the cloud or on-premises (synchronized to Entra ID from Active Directory). Alternatively, you can set the source to be either cloud or on-premises. For example, to update the settings so that photo changes are only possible through cloud applications, create a hash table with a single item to change the source to cloud and use the hash table as the payload to patch the policy:

$Body = @{}
$Body.Add("Source", "Cloud")
$Settings = Invoke-MgGraphrequest -Uri $Uri -Method Patch -Body $Body

Like any update to an Entra ID policy, it can take 24 hours before the policy update is effective across a tenant.

Controlling Who Can Update Photos

By default, any user can update the photo for their account and the value for AllowedRoles is blank. If you want to restrict who can update photos, you can select one or more directory roles and include the GUIDs for these roles in the AllowedRoles property (a string collection).

The roles defined in AllowedRoles must hold the permission to set user photos. In Graph terms, these permissions are either microsoft.directory/users/photo/update or microsoft.directory/users/allProperties/allTasks (only held by the Global administrator role). The following roles can be used:

  • Directory writers (9360feb5-f418-4baa-8175-e2a00bac4301).
  • Intune administrator (3a2c62db-5318-420d-8d74-23affee5d9d5).
  • Partner Tier1 Support (4ba39ca4-527c-499a-b93d-d9b492c50246) – not intended for general use.
  • Partner Tier2 Support (e00e864a-17c5-4a4b-9c06-f5b95a8d5bd8) – not intended for general use
  • User administrator (fe930be7-5e62-47db-91af-98c3a49a38b1).
  • Global administrator (62e90394-69f5-4237-9190-012177145e10).

All are privileged roles, meaning that these are roles that enjoy a heightened level of access to sensitive information.

To update the photo settings policy to confine updates to specific roles, create a hash table to hold the GUIDs of the selected roles. Create a second hash table to hold the payload to update the settings and include the hash table with the roles. Finally, patch the policy.

$Roles = @{}
$Roles.Add("62e90394-69f5-4237-9190-012177145e10", $null)
$Roles.Add("fe930be7-5e62-47db-91af-98c3a49a38b1", $null)
$Body =@{}
$Body.Add("allowedRoles", $Roles)
$Settings = Invoke-MgGraphrequest -Uri $Uri -Method Patch -Body $Body

To reverse the restriction by removing the roles, run this code:

$Body = '{
  "allowedRoles": []
}'
$Settings = Invoke-MgGraphrequest -Uri $Uri -Method Patch -Body $Body

The result of limiting photo updates for user accounts to the user administrator and global administrator roles means that after the new policy percolates throughout the tenant, any account that doesn’t hold a specified role cannot change their profile photo.

The Teams client is probably the best example. The implementation here is not yet optimal. The block on photo updates imposed by an OWA mailbox policy causes Teams to inform the user that administrative restrictions stop photo updates. If the photo update settings policy restricts updates to specific roles, Teams allows the user to go through the process of selecting and uploading a photo before failing (Figure 1).

A failure to update a profile photo due to policy restrictions

Photo settings
 policy
Figure 1: A failure to update a profile photo due to policy restrictions

An Early Implementation of the Photo Update Settings Policy

Inconsistencies appear in the early stages of implementation. It will take time for Microsoft to update clients to allow and block profile updates based on the photo settings policy. And it will take time for tenants to move from the previous block imposed by OWA mailbox policies. In doing so, you’ll notice that the only restriction supported by the new policy is through roles. The OWA mailbox policy setting allows per-user control and multiple policies can exist within a tenant. The current situation therefore delivers a less granular policy.

Maybe a less granular mechanism will be acceptable if it helps with the rationalization of photo updates across Microsoft 365. However, I can’t help thinking that this is a retrograde step. Perhaps Microsoft will address the need for more granular control through Entra ID administrative units, which seems to be the answer for this kind of requirement everywhere else in Entra ID.


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/2024/09/16/photo-update-settings-policy/feed/ 20 66363
Why Entra ID can Restore Some Types of Deleted Groups and Not Others https://office365itpros.com/2024/08/28/restore-deleted-groups-issues/?utm_source=rss&utm_medium=rss&utm_campaign=restore-deleted-groups-issues https://office365itpros.com/2024/08/28/restore-deleted-groups-issues/#comments Wed, 28 Aug 2024 07:00:00 +0000 https://office365itpros.com/?p=66170

Ability to Restore Deleted Groups Depends on Graph APIs

Yesterday, I covered a gap that exists between the Purview development group and the Exchange Online development group when it comes to applying scoped roles to audit log searches. Today, a blog post by ex-MVP Tony Murray-Smith reminds me about another functionality gap that exists in the area of groups. The problem described occurred when a user deleted a security group by mistake only to discover that the Entra admin center doesn’t support a method to restore deleted groups of this type.

In fact, Microsoft 365 groups are the only type of group that Entra supports for restoration via its admin center. There’s no way to restore a deleted distribution list, dynamic distribution list, security group, or mail-enabled security group. Apart from dynamic distribution lists, these objects are recognized by Entra ID and accessible through the Groups API. However, the only group objects supported by the List Deleted Items and Restore Deleted Items (directory objects) APIs remain Microsoft 365 groups. And if a Graph API isn’t available to support restoration, the administrative portals cannot create functionality from thin air.

This situation has persisted since the introduction of cmdlets to restore deleted Microsoft 365 groups in 2017 followed by a GUI option in the Exchange admin center, Microsoft 365 admin center, and Entra admin center. Microsoft subsequently removed the option to restore deleted groups from the new EAC, so the current GUI-based options to restore deleted Microsoft 365 groups are in the Entra admin center and Microsoft 365 admin center. And if you want to use PowerShell, there’s the Restore-MgDirectoryDeletedItem cmdlet.

Option to restore deleted groups in the Microsoft 365 admin center
Figure 1: Option to restore deleted groups in the Microsoft 365 admin center

The Gap Between the Exchange DS and Entra ID

The question is why Entra ID only supports the restoration of Microsoft 365 groups. I think the answer lies in two parts. First, the desire within Microsoft to make its brand-new cloud-only Office 365 groups (now Microsoft 365 groups) the “best group for everything” following their launch at the Ignite conference in May 2015.

The infrastructure to fully support Microsoft 365 groups took time to develop, and building the capability to reconnect all the different resources that a group might use made the process more complicated for Microsoft 365 groups. Being able to restore SharePoint Online, Teams, the group mailbox, and so on was a big undertaking that Microsoft quickly discovered needed to be tackled after the launch of Office 365 groups, especially after some early customers discovered that they couldn’t be restored. The functionality duly arrived in 2017. The campaign to make Microsoft 365 groups do everything is far less intense now than it was some years ago, but its legacy is evident sometimes.

The EXODS Objects

The second issue is heritage. Distribution lists and mail-enabled security groups originated in Exchange Server. Exchange Online still has its own directory (EXODS) to store details for mail-enabled objects. Synchronization and dual-write update operations keep Entra ID and EXODS aligned so that updates performed in one directory synchronize immediately to the other. The Graph APIs support distribution lists and security groups, including mail-enabled security groups, but Entra ID and the Graph APIs ignore dynamic distribution lists and can’t update settings for distribution lists and mail-enabled security groups because these objects are homed within Exchange Online.

Good reasons exist for why the differentiation exists. Dynamic distribution lists require Exchange Online to resolve their membership because the membership supports objects like mail-enabled public folders that don’t exist in Entra ID. Dynamic distribution lists also support nested lists. Regular distribution lists and their mail-enabled security group variants have many settings that aren’t supported in Entra ID, like message approval.

As far as I can remember, it has never been possible to restore deleted distribution lists (and some of the online answers are very misleading, like this example). Once an administrator removes a distribution list, it’s gone. The only thing that can be done is to recreate the distribution list from scratch. That might be possible if someone knows the membership and the list settings, but that might not be the case.

Some Work Necessary in This Area

Microsoft should do some work to make it possible to restore all forms of deleted groups. That work will need contributions from teams responsible for Entra ID, the Graph API, and Exchange Online. Mistakes do happen and administrators remove important distribution lists or mail-enabled security groups when they shouldn’t. Being told that it’s necessary to recreate an object from scratch is a royal pain, and it’s something that shouldn’t still be a problem in 2024. Customers assume that if they can restore one type of deleted group, they should be able to restore any type of deleted group.

Then again, other pains exist around distribution list management, like the Microsoft’s failure to produce a utility to move distribution lists from on-premises servers to the cloud. Tim McMichael’s DLConversionV2 solution is the best available. He’ll be discussing distribution list management at TEC 2024 in Dallas in October. Maybe I should ask Tim about restoring groups that aren’t Microsoft 365 groups.


Learn about using Exchange Online 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/2024/08/28/restore-deleted-groups-issues/feed/ 2 66170
Reporting Entra ID Administrative Role Assignments https://office365itpros.com/2024/08/20/administrative-role-assignments/?utm_source=rss&utm_medium=rss&utm_campaign=administrative-role-assignments https://office365itpros.com/2024/08/20/administrative-role-assignments/#respond Tue, 20 Aug 2024 07:00:00 +0000 https://office365itpros.com/?p=66014

Look Out for Synchronized On-Premises Accounts Holding Administrative Role Assignments

An August 2 post by SpecterOps highlights the dangers for hybrid Microsoft 365 organizations of synchronizing on-premises accounts to Entra ID that hold administrative roles. This is a long-running story. Following the SolarWinds debacle, Microsoft explicitly recommended in December 2020 that “synchronized objects hold no privileges beyond a user in Microsoft 365, either directly or via inclusion in trusted roles or groups.”

Even with all the warnings, it seems that some organizations never received the memo or continue to allow synchronized accounts to hold Entra administrative roles. That’s a great pity, just like the failure to enable strong multifactor authentication to protect user accounts.

This brings me to the question of how to scan for accounts synchronized from on-premises that hold Entra ID administrative roles. As always, it’s great to build off community ideas. In this case, Nathan McNulty tweeted some Microsoft Graph PowerShell SDK code to find accounts holding administrative roles. Nathan doesn’t seem to have published anything in his GitHub repository, so my version is available from the Office 365 for IT Pros repository.

Previous Script to Report Administrative Roles

I’ve been down the road of reporting privileged role assignments before when I wrote a script in 2023 to look for assignments for the Global administrator and Exchange administrator roles. The information about role assignments is then used to disable PowerShell for all accounts except the holders of the two roles. The 2023 script used cmdlets from the now-retired AzureADPreview module. The code is now updated to use cmdlets from the Microsoft Graph PowerShell SDK instead.

Processing Steps to Find and Report Administrative Roles

The script to report administrative roles handles both Privileged Identity Management (PIM) and non-PIM assignments and does the following:

  • Administrative role assignments can be scoped to the entire directory or specific administrative units. The assignments stores GUIDs for administrative units. To make sure that the report can include the display name for administrative units, the script loads details of administrative units into a hash table. As the script processes each assignment, it looks the administrative unit to find its display name.
  • Runs the Get-MgBetaRoleManagementDirectoryRoleAssignmentSchedule cmdlet to retrieve the set of PIM active assignments. These are role assignments that are currently in use.
  • Parses each assignment to find it if is to a user, group, or service principal. If a group, expand the set of group members. Add what’s found to a PowerShell list.
  • Runs the Get-MgBetaRoleManagementDirectoryRoleEligibilitySchedule cmdlet to find the set of eligible assignments. Parse what’s found and add to the PowerShell list.
  • If the script can’t find any PIM assignments, the likelihood is that the tenant doesn’t use PIM, so the script finds the set of members assigned to each directory role and report that.
  • Outputs details of the assignments via the Out-GridView cmdlet plus either a CSV file or Excel worksheet (Figure 1), depending on whether the ImportExcel module is available.
  • Finally, the script lists any on-premises users found with administrative roles.
An Excel worksheet listing Entra ID administrative role assignments
Figure: An Excel worksheet listing Entra ID administrative role assignments

Feel Free to Improve the Code

No doubt some improvements can be made to the code to make administrative role assignments easier to understand. The point is that the script is relatively simple PowerShell that should be easy to work with and update. It is best to make the changes to the script code in GitHub so that everyone benefits from your inspiration.


Learn more about how Microsoft 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 Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2024/08/20/administrative-role-assignments/feed/ 0 66014
Adding Details of Authentication Methods to the Tenant Passwords and MFA Report https://office365itpros.com/2024/06/25/authentication-methods-v13/?utm_source=rss&utm_medium=rss&utm_campaign=authentication-methods-v13 https://office365itpros.com/2024/06/25/authentication-methods-v13/#comments Tue, 25 Jun 2024 07:00:00 +0000 https://office365itpros.com/?p=65312

Revealing Full Details of Authentication Methods and Why This Might Be a Privacy Issue

Soon after releasing V1.2 of the Tenant Passwords and MFA Report (to add details about per-user MFA states), I was asked if it was possible to add more information for authentication methods, like the phone number used for SMS responses. My response was that I had covered the topic of reporting the details of authentication methods in a previous article and it was simply a matter of using the code from that article, updating it slightly to deal with the device-based passkeys recently introduced for Entra ID.

Not everyone likes cracking open a PowerShell script to insert code that they didn’t write. I don’t like messing with other peoples’ code either and will usually write my own version when necessary. In any case, I found some time and upgraded the script to include the expanded details, available in V1.3 of the script in GitHub.

Reporting Authentication Methods

Figure 1 shows the information about authentication methods registered for a user account in V1.2 of the report. The information given use the names from the MethodsRegistered property returned by the Get-MgBetaReportAuthenticationMethodUserRegistrationDetail cmdlet from the Microsoft Graph PowerShell SDK.

 Reporting the authentication methods registered for a user account.
Figure 1: Reporting the authentication methods registered for a user account

The problem is that the names aren’t very user-friendly. If you’re used to working with authentication methods, you probably recognize the values and understand what they mean. If not, this information might be useless.

More detail about the methods is available by running the Get-MgUserAuthenticationMethod cmdlet. Even so, some manipulation is necessary to generate human-friendly output. I’d done most of the work before, so it was easy to generate more information for each method. For instance, in Figure 2 you can see the mobile phone number used for SMS challenges and the version of the Authenticator app used for push notifications.

Expanded details of a user account's registered authentication methods.
Figure 2: Expanded details of a user account’s registered authentication methods

Because the script captures details in a PowerShell list, it’s also possible to query the list to find information like who uses a YubiKey FIDO2 key with a command like:

$Report | Where-Object {$_.'Authentication Methods' -like "*Yubikey*"}

The Privacy Issue

All was going well when I realized that the information generated about authentication methods might include some PII data, like the mobile phone number used for SMS responses. In most instances, I don’t think this will be a problem because details like mobile phone numbers are often included in the properties of Entra ID user accounts. The email addresses used to recover passwords via the Self-Service Password Reset (SSPR) feature are often personal accounts, so they might be more of an issue.

However, the regulations covering access to PII differs from country to country and it’s a good idea to cover all bases. The script now has a PrivacyFlag parameter. It’s a switch parameter, so the value is false by default. If set to true by including the parameter when running the script or by setting the flag explicitly, the script generates the names of the authentication methods without any details.

$PrivacyFlag = $true

On to The Next Version

I am sure that many other good ideas about how to add value to a report like this exist within the community. If you do, suggest the change through the Office 365 for IT Pros GitHub repository (for this script or any of our other scripts). Many people create a fork of our repository and work on updates that way. Whatever’s easier for you…


Learn more about how Microsoft 365 applications and Entra ID 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/2024/06/25/authentication-methods-v13/feed/ 1 65312
Per-User MFA State Added to Tenant Passwords and MFA Report https://office365itpros.com/2024/06/14/per-user-mfa-state/?utm_source=rss&utm_medium=rss&utm_campaign=per-user-mfa-state https://office365itpros.com/2024/06/14/per-user-mfa-state/#comments Fri, 14 Jun 2024 07:00:00 +0000 https://office365itpros.com/?p=65168

Per-User MFA State Available for User Accounts Through the Graph

On June 10, 2024, the Microsoft Graph changelog included some interesting additions to the beta version of the authentication resource type to make the settings for per-user MFA retrievable for user accounts. Until now, it’s been possible to see this information through the Entra admin center but not to fetch it programmatically.

The addition of the per-user MFA state is interesting because Microsoft is doing its level best to eliminate per-user MFA from Entra ID. Today, Office 365 E3 and above licenses include the ability to use per-user MFA when connecting to Office 365 services. Per-user Entra ID MFA covers all connections processed by the Microsoft identity service.

Microsoft’s long-term plan for enforcement of multifactor authentication is to use conditional access policies. They’ve put enormous effort over the past few years to build out the capabilities of these policies. The latest update was the ability to block connections using the device code authentication flow, something that all tenants should consider unless a solid business need exists to support device code authentication.

Moving Away from Per-User MFA

To make the transition easier for tenants, some Microsoft-managed conditional access policies are available to organizations with Entra ID P1 or P2 licenses, including one to assist the migration of per-user MFA. A potential issue for those with Office 365 MFA is that moving to conditional access policies requires Entra ID P1 licenses. This isn’t a problem if the organization has purchased Entra ID P1 for other reasons, like self-service password reset, but it is a hurdle to overcome for others. Security defaults is another option for tenants who don’t want to use conditional access policies, especially in the small to medium-sized sectors.

Knowing who still uses per-user MFA is invaluable information for anyone planning to migrate. It’s possible to get details about per-user MFA through the Users section of the Entra admin center, but the user interface is antiquated and unwieldy and the list includes both member and guest accounts (Figure 1).

 Per-user MFA state viewed through the Entra admin center.
Figure 1: Per-user MFA state viewed through the Entra admin center

Being able to extract the information via the Graph allows us to do something like this to find licensed member accounts, check each account for its per-user MFA state, and report the findings. The new capability is in beta for now with no indication of when the V1.0 (production) will support it. Reading the per-user MFA state requires consent to use the Policy.ReadWrite.AuthenticationMethod application permission.

Connect-MgGraph -Scope Policy.ReadWrite.AuthenticationMethod, User.Read.All -NoWelcome

# Get licensed users
[array]$Users = Get-MgUser -Filter "assignedLicenses/`$count ne 0 and userType eq 'Member'" 
-ConsistencyLevel eventual -CountVariable UsersFound -All -PageSize 999
    
If ($Users) {
    Write-Host ("{0} users found" -f $Users.Count)
} Else {
    Write-Host "No users found"
    Break
}

$Report = [System.Collections.Generic.List[Object]]::new()
Foreach ($User in $Users){
  $Uri = ("https://graph.microsoft.com/beta/users/{0}/authentication/requirements" -f $user.id)
  $Data = Invoke-MgGraphRequest -Uri $Uri -Method Get
  $ReportLine = [PSCustomObject][ordered]@{
    User                = $User.UserPrincipalName
    Name                = $User.displayName
    "MFA State"         = $Data.PerUserMfaState
  }
  $Report.Add($ReportLine)
}

$Report | Export-CSV -Path "C:\Temp\MFAState.csv" -NoTypeInformation -Encoding utf8

Accounts can be in one of three states for per-user MFA: disabled, enabled, or enforced. To update the per-user MFA state for an account, use the Patch method:

$Body= @{}
$Body.Add("perUserMFAState", "enabled")

$Uri = ("https://graph.microsoft.com/beta/users/{0}/authentication/requirements" -f $user.id)
Invoke-MgGraphRequest -Uri $Uri -Method Patch -Body $Body

Enhancing the User Passwords and MFA Report

Being able to generate a quick report of per-user MFA states is nice; integrating that data with other sources to create a comprehensive view of account password and MFA properties is even better. In January, I wrote about the inability to query the Graph to find which accounts use MFA. This is because when you use conditional access policies, MFA is the outcome of an assessment against policy for inbound connections rather than a fixed property of user accounts (like per-user MFA). The script I described in the article therefore uses information from several sources, including Entra ID sign-in logs, to report registration of MFA methods, and password change information. The report shows the last time when accounts successfully used MFA to connect, which is the acid test to know if an account uses MFA or not. User registration of MFA methods is one step along the path; using those methods when connecting to Entra ID is what we want to see.

Now that per-user MFA state information is available, I have updated the script (available from GitHub) to include that data. The HTML report generated by the script highlights accounts enabled or enforced for per-user MFA (Figure 2).

User Password and Authentication report with per-user MFA state.
Figure 2: User Password and Authentication report with per-user MFA state

The end of the report includes a summary of the findings (Figure 3), including the number of accounts in the enabled or enforced per-user MFA states and the display names of the users in those categories.

Summary for the User Passwords and Authentication report.
Figure 3: Summary for the User Passwords and Authentication report

The report also generates a CSV file for you to slice and dice the data as you wish.

Nice Addition to Entra ID Data

Being able to report per-user MFA states is a nice addition to the data available to Entra ID administrators. Whether it will convince organizations currently using per-user MFA to move to conditional access policies remains to be seen.


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/2024/06/14/per-user-mfa-state/feed/ 31 65168
Report Delegated Permission Assignments for Users and Apps https://office365itpros.com/2024/06/06/delegated-permissions-report/?utm_source=rss&utm_medium=rss&utm_campaign=delegated-permissions-report https://office365itpros.com/2024/06/06/delegated-permissions-report/#comments Thu, 06 Jun 2024 07:00:00 +0000 https://office365itpros.com/?p=65048

Extract and Report Delegated Permission Assignments with the Microsoft Graph PowerShell SDK

When discussing permissions used to retrieve data with Graph API requests (including cmdlets from the Microsoft Graph PowerShell SDK), most of the time we refer to application permissions rather than delegate permissions. The reason is simple: when automating operations with PowerShell, tenant administrators usually process data drawn from multiple sources, like all user mailboxes or all groups. This level of processing requires application permissions.

Delegated permissions (also called scopes) allow apps to access information on behalf of the signed in user. Anything that user can access, the app can too. Usually, the user is the owner of a resource (like their mailbox), but they can gain access to information through an RBAC role, such as Teams administrator.

Delegated permissions are granted by a specific resource (like a Graph API) and represent the operations that an app can perform for the user. For instance, the Mail.Read scope allows an app to read messages in the user’s mailbox. The grant of consent for a delegated permission usually happens when a user signs into an app and the app discovers that consent for the required permission is not granted. At this point, Entra ID displays the consent prompt window to allow the user to give consent for the app to use the permission and proceed.

Reporting Permissions

Application permissions assigned to apps can be checked by examining the app role assignments for service principals. It’s a good idea to inventory app permissions periodically to ensure that apps don’t have high-profile permissions without good reason.

To report delegated permissions, we need to check delegated permission grants (otherwise called OAuth2 permission grants). These are delegated permissions granted for a client application to access an API on behalf of a signed-in user. The Microsoft Graph PowerShell SDK cmdlet used for this purpose is Get-MgOauth2PermissionGrant. The Directory.Read.All permission is required to read details of delegated permissions and user accounts.

Interpreting a Delegated Permission for Users

After connecting, run the Get-MgUser cmdlet to create an array of user accounts to query. Usually, I apply a filter to find licensed accounts. Once you have a set of accounts, it’s a matter of looping through the set to find the delegated permissions for each account:

[array]$Permissions = Get-MgUserOauth2PermissionGrant -UserId $User.Id -All

An individual permission assignment looks like this:

$Permission | Format-List

ClientId             : 5482d706-b547-4b9d-b159-b91a5776e0e9
ConsentType          : Principal
Id                   : BteCVEe1nUuxWbkaV3bg6YnEoxRs7QVAltG-nFdw96NYzfTvuBuZSJTeeV9la0oY
PrincipalId          : eff4cd58-1bb8-4899-94de-795f656b4a18
ResourceId           : 14a3c489-ed6c-4005-96d1-be9c5770f7a3
Scope                :  openid profile User.ReadWrite User.ReadBasic.All Sites.ReadWrite.All Contacts.ReadWrite People.Read Notes.ReadWrite.All Tasks.ReadWrite Mail.ReadWrite Files.ReadWrite.All Calendars.ReadWrite Group.Read.All Group.ReadWrite.All Directory.AccessAsUser.All Directory.ReadWrite.All User.ReadWrite.All IdentityRiskEvent.Read.All Reports.Read.All AuditLog.Read.All User.Read SecurityEvents.ReadWrite.All offline_access TeamSettings.Read.All TeamSettings.ReadWrite.All Mail.ReadBasic Chat.Read Chat.ReadBasic Analytics.Read
AdditionalProperties : {}
  • The client identifier points to the service principal for the client app. In this case, it is the Graph Explorer.
  • The principal identifier points to the identifier for the user account. Because we’re listing delegated permissions by user, the consent type for the permission is always Principal, meaning that the app is limited to impersonating the specific user. If the consent type is AllPrincipals, meaning that the app can use the consent to impersonate all users, the principal identifier would be empty.
  • The resource identifier points to the service principal for the resource. In this example, the resource identifier points to “Microsoft Graph” (the Graph API). The set of permissions (Scope) confirm this because they are Graph permissions. As you can see, the Graph Explorer has consent for many permissions. This is a normal situation if developers use the Graph Explorer to test different Graph APIs.

Processing Delegated Permissions for AllPrincipals

After processing the delegated permission assignments for user accounts, we process those for all principals (any user). The set of assignments is found with:

[array]$AppGrants = Get-MgOauth2PermissionGrant -filter "consentType eq 'AllPrincipals'" -All

Steps in the Script

The steps in the script are as follows:

  • Find the set of user accounts.
  • For each account, check if any delegated permissions exist.
  • For each permission, check the client app and resource.
  • Find the set of delegated permissions for all principals.
  • Do much the same as for individual assignments.
  • Report what’s been found.

Figure 1 shows the output generated.

Delegated permissions report.
Figure 1: Delegated permissions report

You can download the full script from GitHub.

Interpreting the Results

It’s inevitable that delegated permissions will accumulate over time. Looking at the results from my tenant, I see evidence of the iOS account migration to modern authentication from 2021, apps from conference organizers like Sessionize and Community Days, the app used to register for the Microsoft Technical Community, and so on. All these assignments are understandable. The question is whether the assignments are needed any longer and if not, should they be removed. That’s up to you…


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/2024/06/06/delegated-permissions-report/feed/ 2 65048
Block Device Code Authentication Requests with Conditional Access https://office365itpros.com/2024/05/13/device-code-authentication/?utm_source=rss&utm_medium=rss&utm_campaign=device-code-authentication https://office365itpros.com/2024/05/13/device-code-authentication/#comments Mon, 13 May 2024 06:00:00 +0000 https://office365itpros.com/?p=64682

The Device Code Authentication Flow

In late February 2024, Microsoft introduced a preview setting for Entra ID conditional access policies to block authentication flows. Although the setting covers the device code and authentication transfer flows, my feeling is that Microsoft has the device code flow squarely in their sights, saying: “Device code flow is a high-risk authentication flow that might be used as part of a phishing attack or to access corporate resources on unmanaged devices.”

The device code authentication flow is defined in RFC8628. It exists in Entra ID to support devices that don’t have the ability to sign into Entra ID in a more orthodox manner, like a TV set. The mechanism works by allowing an app running on the device to post a request to the Entra login endpoint. The request includes the app identifier and the resource that the app wishes to access. The response is a direction to open a verification URL (normally https://microsoft.com/devicelogin) and input a 9-character code included in the response. If someone goes ahead and opens the page in a browser and inputs the code, the authentication request is successful and Entra ID issues an access token. The app polls for a successful outcome and proceeds if an access token becomes available.

The problem here is that attackers can exploit the flow by:

  • Starting an app and requesting authentication.
  • Asking the victim to open a browser and input the code. Obviously, some social engineering is in play here and the attacker probably prepared the victim to be ready to action a request.
  • If the victim complies, the app is signed into the victim’s account and can use the permissions held by that account.

Detecting Device Code Authentication

It’s entirely possible that your tenant has never used device code authentication. A quick check is possible by checking the Entra ID sign-in logs as follows:

[array]$SignIns = Get-MgBetaAuditLogSignin -Filter "AuthenticationProtocol eq 'devicecode'"

$SignIns | Format-Table CreatedDateTime, ResourceDisplayName, UserDisplayName

CreatedDateTime     ResourceDisplayName UserDisplayName
---------------     ------------------- ---------------
01/05/2024 20:18:34 Microsoft Graph     Lotte Vetler
01/05/2024 20:15:51 Microsoft Graph     James Abrahams
27/04/2024 22:52:57 Microsoft Graph     Jane Sixsmith

The Entra ID sign-in logs are available for 30 days, so the data only covers that period. Nevertheless, it might be helpful in finding who uses the device code authentication flow and what resources they connect to.

Blocking the Device Code Authentication Flow

Returning to the original theme, support in conditional access policies for blocking selected authentication flows means that it’s easy to block device code authentications with a conditional access policy (follow the Microsoft instructions documented here).

Here’s an example of the policy in action. I attempt to start an interactive Microsoft Graph PowerShell SDK session by running the Connect-MgGraph cmdlet with the DeviceCode parameter. Entra ID responds with the instruction to open the browser and enter a code. But the authentication flow cannot complete because the block imposed by the conditional access policy and the attempt times out:

Connect-MgGraph -NoWelcome -DeviceCode
To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code FYK8NA4XS to authenticate.
Connect-MgGraph: Authentication timed out after 120 seconds due to inactivity. Please try again.

The browser interaction works, and the user is then prompted to sign-in to the requesting app. At this point, Entra ID checks the connection and the policy restrictions kick in. The user sees an error like that shown in Figure 1.

Device code authentication flow blocked by a conditional access policy
Figure 1: Device code authentication flow blocked by a conditional access policy

In passing, remember to consider securing interactive Microsoft Graph PowerShell SDK sessions to known users. Not everyone needs to run interactive PowerShell sessions to execute Graph requests.

Tightening Control over Inbound Connections

Microsoft continues to add new features to conditional access policies to examine different aspects of inbound connections. I can’t imagine that it will be long before blocking authentication flows becomes a generally available feature, but that’s no reason not to use the feature now to tighten security a tad. And remember, when you create a new conditional access policy, always add an exclusion for a breakglass account.


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/2024/05/13/device-code-authentication/feed/ 1 64682
Update Entra ID User Role Permissions to Secure Your Tenant https://office365itpros.com/2024/05/09/user-role-permissions/?utm_source=rss&utm_medium=rss&utm_campaign=user-role-permissions https://office365itpros.com/2024/05/09/user-role-permissions/#comments Thu, 09 May 2024 07:00:00 +0000 https://office365itpros.com/?p=64648

Make Your Tenant More Manageable by Tightening User Role Permissions

The ability of non-privileged user accounts to perform certain administrative tasks in an Entra ID tenant (Microsoft 365 tenant) is controlled by the user role permissions policy. This policy exists in every Entra ID tenant, and it comes with some default settings that are just plain silly for all but test tenants.

The settings I’m concerned about are found in the User settings page (Figure 1).

User role permissions in the Entra admin center
Figure 1: User role permission settings in the Entra admin center

Apps, Tenants, and Security Groups

Three settings are up for debate. Should users be able to create registered apps, tenants, and security groups.

Only administrators should add registered apps to a tenant. Registered apps are enormously useful, especially the creation of an integrated Entra ID identity configuration that can authenticate against the Graph and other APIs. Attackers love apps too, and they like creating apps within compromised tenants and then assigning those apps the necessary permissions to exfiltrate data. The potential for app abuse is too high to allow “normal” users to create new apps might have made sense when attackers weren’t quite so interested in their use as an attack vector. The current threat horizon is such that it’s unwise to allow non-administrators to create new apps.

The same is true for tenants. What regular Microsoft 365 user sets out to create a new Entra ID tenant as part of their daily activities? The answer is none. Creating new tenants might be something that’s useful as part of a development project, but tenants created from the Entra admin center have no licenses and aren’t particularly useful. Developers are better off working against a Microsoft 365 development tenant. They’ll get 25 licenses to work with and the tenant will automatically renew if they work with Graph APIs. If someone can make a good case to create a new tenant, let them make it to a tenant administrator.

I’m less strict about restricting users from creating security groups. However, because security groups are used to control access to resources, it seems to make sense to restrict their creation too. And most Microsoft 365 tenants suffer from a surplus of groups caused by unrestricted creation of Teams. Why add to the debris accumulating in a tenant?

I suspect that Microsoft chose the default settings with the best intentions at a time when threat was less evident. It’s regrettable that the settings remain so permissive. My position is therefore that tenants should update the default settings and impose control over creation of apps, tenants, and security groups. Feel free to disagree.

Using PowerShell to Update User Role Permissions

It’s easy to correct the settings in the Entra admin center. To make sure that the settings are not changed, you could use an Azure Automation scheduled runbook to update the settings periodically. Changes to the authorization policy require consent for the Policy.ReadWrite.Authorization permission. Here’s the necessary Microsoft Graph PowerShell SDK code to disable the ability for users to:

  • Create new Entra ID registered apps (AllowedToCreateApps)
  • Create security groups (AllowedToCreateSecurityGroups)
  • Create new tenants (AllowedToCreateTenants)

Connect-MgGraph –NoWelcome -Scopes Policy.ReadWrite.Authorization
# Create hash table for body
$BodyParameters = @{}
# Create hash table to hold role permissions for tenant users
$RolePermissions = @{}
$RolePermissions.Add("AllowedToCreateTenants", $false)
$RolePermissions.Add("AllowedToCreateApps", $false)
$RolePermissions.Add("AllowedToCreateSecurityGroups", $false)
# Add the role permissions to the body
$BodyParameters.Add("DefaultUserRolePermissions", $RolePermissions)
# Update default authorization policy
Update-MgPolicyAuthorizationPolicy -BodyParameter $BodyParameters 
# Check the results
Get-MgPolicyAuthorizationPolicy | Select-Object -ExpandProperty DefaultUserRolePermissions | Format-List Allowed*

AllowedToCreateApps                      : False
AllowedToCreateSecurityGroups            : False
AllowedToCreateTenants                   : False
AllowedToReadBitlockerKeysForOwnedDevice : True
AllowedToReadOtherUsers                  : True

For a detailed description of the user role permissions, see this page. Note the admonition not to change the allowedToReadOtherUsers to false. Doing so will have “unfortunate effects.”

Take Control Over Your Tenant

The temptation exists not to change default settings in an administrative portal unless the obvious need exists. That’s a reasonable position to take, but the simple fact is that the three default settings discussed here are outdated and illogical. Take control of your tenant and make sure to disable these capabilities. There’s no point in allowing people create objects unless there’s good reason to do so.


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/2024/05/09/user-role-permissions/feed/ 1 64648
Microsoft Launches Support for Entra ID External Authentication Methods https://office365itpros.com/2024/05/08/entra-id-authentication-methods/?utm_source=rss&utm_medium=rss&utm_campaign=entra-id-authentication-methods https://office365itpros.com/2024/05/08/entra-id-authentication-methods/#respond Wed, 08 May 2024 07:00:00 +0000 https://office365itpros.com/?p=64731

Advancing MFA with Entra ID Authentication Backed by Nine ISVs

Earlier this year, Microsoft reported that the percentage of Entra ID accounts using multifactor authentication had reached 38%. That figure isn’t very impressive, but at least it represents a twelve-point increase since 2022. I guess some haven’t yet read the memo explaining that multifactor authentication is still the best way to avoid account compromise (something Microsoft discovered for themselves with the Midnight Blizzard affair).

To be fair to Microsoft, they continue to push the boundaries to make multifactor authentication easier and more secure to use, notably in the recent announcement of preview support for device-bound passkeys in the Authenticator app. However, I’m not sure that phishing-resident passkeys will be the thing that forces the decision in many companies. Just getting people to use normal challenge-response with the Authenticator app would be a great step forward.

Leveraging Third-Party Authentication Solutions

Which brings me to the May 2 announcement about Entra ID’s support for external authentication methods. This public preview demonstrates how to integrate and use third-party multifactor authentication solutions with Entra ID by defining the solutions as valid authentication methods, just like the out-of-the-box methods like SMS (which shouldn’t be used now) and the Authenticator app (Figure 1). The preview is due for availability in mid-May. This initiative replaces the previous custom control solution, which never left preview.

Entra ID authentication methods.
Figure 1: Entra ID authentication methods

Entra ID is quite capable of handling multifactor authentication and many Microsoft 365 tenants have bought fully into the Entra ID stack and have no need for third-party enhancements. But equally so, it’s common to find that large organizations select a multifactor authentication solution for use across multiple platforms, multiple applications, and multiple clients. In these scenarios, it makes sense for Entra ID to be able to hand off a connection to a third-party solution to perform the multifactor challenge and response before returning a response for Entra ID to verify and accept.

Microsoft points out that the integration between Entra ID and the third-party authenticators uses industry standards and are managed in the same way as native Entra ID authentication methods. Entra ID handles multifactor authentication through conditional access policies, which dictate when connections must satisfy multifactor authentication. Conditional access policies can insist on a certain strength of authentication (like phishing-proof), but it’s usually sufficient for a connection to satisfy an authentication method to proceed.

Because Entra ID and third-party authentication methods indicate if a connection satisfies a multifactor challenge, it means that Entra solutions like Privileged Identity Management (PIM) respond to authentication in the same way. The result is that companies can leverage their investment in third-party authentication solutions alongside Entra ID, which is the best of both worlds.

Nine ISVs to Support Entra ID Authentication

Microsoft’s announcement details support for nine third-party authentication solutions including:

  • Cisco Duo
  • Entrust Identity
  • HYPR Authenticate
  • Ping Identity
  • RSA
  • Silverfort advanced MFA
  • Symantec VIP
  • Thales STA
  • TrustBuilder MFA

It’s a good lineup and more solutions are likely to be added. Microsoft documentation is available for ISVs to know how to connect their solutions to Entra ID by defining an authentication method.

Increasing the MFA Percentage

I’m looking forward to seeing how many Microsoft 365 tenants integrate third-party authentication solutions with Entra ID. The logic behind leveraging what’s already deployed is inescapable. All that’s needed now is implementation, where it makes sense (of course).

One more comment. Adding authentication methods to Entra ID expands the capability to handle inbound connections more thoroughly. It does nothing to manage the number of accounts configured with authentication methods, the conditional policies to enforce multifactor authentication, or the reporting of who uses multifactor authentication. In other words, a shiny new authentication method is nice, but it does nothing to drive that MFA percentage higher. That takes more effort, persistence, and (often) executive buy in.


Make sure that you’re not surprised about changes that appear inside Entra ID or the Microsoft 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/2024/05/08/entra-id-authentication-methods/feed/ 0 64731
How to Remove a Single Service Plan from User Accounts with PowerShell https://office365itpros.com/2024/04/23/remove-service-plan-powershell-2/?utm_source=rss&utm_medium=rss&utm_campaign=remove-service-plan-powershell-2 https://office365itpros.com/2024/04/23/remove-service-plan-powershell-2/#comments Tue, 23 Apr 2024 07:00:00 +0000 https://office365itpros.com/?p=64426

Remove Service Plans with the Microsoft Graph PowerShell SDK

In 2021, I wrote about how to remove a single service plan from multiple Entra ID user accounts with PowerShell. The original script used cmdlets from the Microsoft Online Services (MSOL) module. To cover all bases, I updated the post with versions of the script using cmdlets from the AzureAD and the Microsoft Graph PowerShell SDK. Microsoft has deprecated the MSOL and AzureAD modules and the final retirement of these modules is due on March 30, 2025.

The problem with updating a script to replace cmdlets is the tendency to keep the same flow and logic. In other words, the script that started off using MSOL cmdlets behaves in much the same way when updated to use Graph SDK cmdlets. It’s natural that things happen in this way because those updating the code want to get the work done as quickly as possible. Who has the time to sit back and ask if code can be improved during script updates, even if new tools like GitHub Copilot are available.

I’ve been using GitHub Copilot integrated into Visual Studio Code for the last month or so. I’m not sure that Copilot has created any great new code in my scripts, but it certainly has an uncanny ability to auto-complete lines of code and comments, just like Word does when I write. I like GitHub Copilot and recommend the combination of it and Visual Studio Code to anyone who writes PowerShell for Microsoft 365.

What the Script Does to Remove Service Plans from Accounts

Which brings us neatly to some upgrades for the version of the script based on the Microsoft Graph PowerShell SDK. The original script:

  • Lists the set of subscriptions (bought products) found in the tenant and asks the administrator to select a product to modify.
  • Lists the set of service plans for the selected product and asks the administrator to select the service plan to disable. For example, a tenant might decide that they don’t wish to use Viva Engage, so they will remove the Viva Engage Core and Viva Engage Seeded service plans from all accounts with the selected product. This is exactly what happens when an administrator edits a user account with the Microsoft 365 admin center and removes access to some of the apps listed for the user (Figure 1). Obviously, it’s much faster to use PowerShell to remove service plans from multiple accounts.
  • Runs a cmdlet to disable the selected service plan for all user accounts that have the selected license.

Removing service plans from an Entra ID account.

How to remove service plans with PowerShell
Figure 1: Removing service plans from an Entra ID account

There’s not much in terms of cmdlets in the script. Get-MgSubscribedSku returns the set of products and service plans. Get-MgUser finds user accounts and Set-MgUserLicense disables the selected service plan for each account. It’s all very straightforward.

Upgrading the Script to Remove Service Plans Faster

Then someone complained that they couldn’t get the script to work in their tenant. Perhaps consent had not been granted for the Directory.ReadWrite.All permission (scope), which is necessary to read the set of subscribed products, read user information, and update user licenses. Or perhaps the person used an interactive session, and the signed-in account didn’t hold one of the necessary administrative roles (remember, delegated permissions are used for Graph SDK interactive sessions). For whatever reason, it was good enough to check the code to see if any improvements were possible.

I found four areas to update:

  1. Some products (like Office 365 E3 or Microsoft 365 E5) are composite licenses that span many service plans. Each service plan has a target. User service plans can be disabled or enabled on a per-user basis. Company service plans are managed at the tenant level. The new code makes sure that the script only lists user service plans for the user to select.
  2. The cmdlets in the MSOL and AzureAD modules didn’t boast good server-side filtering capabilities to find accounts assigned specific licenses, so filtering happens client-side. The complex filters supported by the Graph for user accounts allows the Get-MgUser cmdlet to find the precise set of accounts with the selected license. This change makes the script much more efficient in large tenants.
  3. The previous iteration of the script didn’t check if a service plan was already disabled before attempting to disable a plan. It does now.
  4. The previous iteration didn’t handle errors well and the report generated by the script could include items where the removal of a service plan didn’t work. Better error handling sorted this problem.

You can download the updated script from GitHub.

The Principle is Proved, Now Let Your Imagination Run Wild

The script to remove service plans is intended to demonstrate a principle of license management for Microsoft 365 user accounts. It would be easy to amend the script in different ways. For instance, you could allow the administrator to select multiple service plans to remove or eliminate the need to select a product and find a target service plan in any of the licenses assigned to a user. It’s PowerShell, so let your imagination run wild and improve the script to meet the needs of your tenant.


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/2024/04/23/remove-service-plan-powershell-2/feed/ 5 64426
Removing Licenses from Entra ID Accounts When a Replacement License Exists https://office365itpros.com/2024/04/19/license-management-switch/?utm_source=rss&utm_medium=rss&utm_campaign=license-management-switch https://office365itpros.com/2024/04/19/license-management-switch/#comments Fri, 19 Apr 2024 07:00:00 +0000 https://office365itpros.com/?p=64105

License Management is All a Matter of Identifiers (GUIDs)

Entra ID license management with PowerShell.

A reader asked how to use the Graph SDK to remove the Exchange Online Plan 2 license from 2,000 users who have been upgraded to the Microsoft 365 E3 license. This can be a challenging task if you’re not accustomed to dealing with Microsoft 365 licenses using Graph SDK cmdlets and understand how to use the identifiers assigned to products and service plans.

The task seems complete but it’s not complicated, especially since Microsoft supported license stacking for Exchange Online (the ability for an account to have multiple Exchange Online licenses). In this instance, the accounts have Exchange Online Plan 2 licenses purchased prior to the upgrade to Microsoft 365 E3. The E3 licenses also have an Exchange Online Plan 2 service plan, meaning that if you remove the Exchange Online Plan 2 license from accounts, the accounts retain access to their mailboxes through the Exchange Online Plan 2 service plan included in Microsoft 365 E3.

License Management Based on Licenses and Service Plans

I admit that it can be confusing when a product is licensed through a separate license and a service plan that’s part of a license. For instance, the Exchange Online Plan 2 product has an identifier of 19ec0d23-8335-4cbd-94ac-6050e30712fa. The product contains a single service plan, also called Exchange Online Plan 2 with an identifier of efb87545-963c-4e0d-99df-69c6916d9eb0.

You might ask why two objects involved with licensing exist with the same name. The good news is that this doesn’t happen very often. The easiest way to think about it is that a license can be purchased by customers and becomes part of the tenant subscriptions. Service plans cannot be purchased and are always part of a license. The good thing about service plans is that they can be selectively disabled and enabled to remove access to functionality. For instance, Office 365 E5 spans over 50 service plans, including Exchange Online Plan 2.

To complete the task, we need to:

  • Find user accounts with the Exchange Online Plan 2 license.
  • Check if those accounts have a Microsoft 365 E3 license.
  • If they have, remove the Exchange Online Plan 2 license. Users will continue to have access to their mailboxes through the Exchange Online Plan 2 service plan included in Microsoft 365 E3.

Removing Licenses from Accounts

The first step is to find the product identifier for the Exchange Online Plan 2 license. This code uses the Get-MgSubscribedSku cmdlet to find the set of subscriptions (licenses) in a tenant and extracts the Exchange Online Plan 2 identifier before calling Get-MgUser to find the accounts with the license.

$ExchangePlan2SKU = Get-MgSubscribedSku -All | Where-Object SkuPartNumber -eq ‘EXCHANGEENTERPRISE’ | Select-Object -ExpandProperty SkuId
[array]$Users = Get-MgUser -Filter "assignedLicenses/any(s:s/skuId eq $ExchangePlan2SKU)" -All -Property Id, displayName, assignedLicenses

The next step is a matter of looping through the set of accounts to find if they have a Microsoft 365 E3 license. If they do, the script runs the Set-MgUserLicense cmdlet to remove the Exchange Online Plan 2 license.

$Microsoft365E3SkuId = "05e9a617-0261-4cee-bb44-138d3ef5d965"
ForEach ($User in $Users) {
  If ($Microsoft365E3SkuId -in $User.AssignedLicenses.SkuId) {
     Write-Host ("Removing the Exchange Online Plan 2 license from the {0} account" -f $User.displayName)
     Set-MgUserLicense -UserId $User.Id -RemoveLicenses $ExchangePlan2SKU -AddLicenses @()

  } Else {
     Write-Host ("The {0} account doesn't have a Microsoft 365 E3 license" -f $User.displayName)
 }
}

That’s it. The code is simple even if the concepts of licenses and service plans take a little time to get to know

If you use group-based licensing rather than direct assignments, the task is even easier because it then becomes a matter of removing users from the group that controls the Exchange Online Plan 2 license..

License Management is a Core Competence

License management is a core competence for Microsoft 365 tenant administrators. It’s a good idea to become accustomed to working with license assignments through PowerShell. Apart from being able to generate reports from the license information stored in user accounts, knowing how to manipulate licenses will help the tenant manage licenses efficiently.


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/2024/04/19/license-management-switch/feed/ 1 64105
Microsoft Graph Activity Logs Hit General Availability https://office365itpros.com/2024/04/18/graph-activity-logs-ga/?utm_source=rss&utm_medium=rss&utm_campaign=graph-activity-logs-ga https://office365itpros.com/2024/04/18/graph-activity-logs-ga/#respond Thu, 18 Apr 2024 08:00:00 +0000 https://office365itpros.com/?p=64465

Graph Activity Logs for Security Analysis and Threat Hunting

On April 11 2024, Microsoft announced the general availability of Microsoft Graph activity logs, explained as: “visibility into HTTP requests made to the Microsoft Graph service in your tenant. In other words, every time an app generates a HTTP request to a Graph API, the service captures a log record. This covers Microsoft, third-party, and tenant apps, including the Graph requests run by cmdlets in Graph-based PowerShell modules like Microsoft Teams or the Microsoft Graph PowerShell SDK.

Microsoft says that: “With rapidly growing security threats and an increasing number of attacks, this log data source allows you to perform security analysis, threat hunting, and monitor application activity…

Graph Activity Logs Are Another Trail to Follow

In essence, the Graph activity logs give security analysts another audit trail to follow when looking for signs of anomalous activity within a tenant either before or after an attack occurs. Some configuration is needed to use the logs. You’ll need an Entra ID P1 license to access the logs through the Monitoring & health section of the Entra admin center (Figure 1).

Checking a Microsoft Graph activity log record in the Entra admin center.
Figure 1: Checking a Microsoft Graph activity log record in the Entra admin center

The highlighted Graph request in Figure 1 uses the group delta API to check for new groups information. The fact that security analysts see Graph requests when reviewing log data creates a need for a level of familiarity with how Graph APIs work and what the expected pattern of requests is. In addition, security analysts will need to understand the context of when requests happen and what a request does. For instance, opening the Microsoft 365 admin center generates a blizzard of Graph requests to fetch information about multiple objects. Even writing this article in a Word document generated many log entries that appear to be Data Loss Prevention checks.

It’s sometimes possible to extract the request from an audit record and run it, just to see what happens. For example, I copied a request and ran it in an interactive Microsoft Graph PowerShell SDK session like this:

Uri = "https://graph.microsoft.com/beta/groups/5aabcff4-118b-40f4-a033-2fd1c8d7cf6e/?`$select=expirationDateTime,assignedLabels"
$data = invoke-MgGraphRequest -Method get -Uri $Uri
$data

Name                           Value
----                           -----
@odata.context                 https://graph.microsoft.com/beta/$metadata#groups(expirationDateTime,assignedLabels)/$e…
assignedLabels                 {Non-business use}
expirationDateTime             02/01/2026 11:20:14

The request fetches the sensitivity label and expiration date properties for a Microsoft 365 group. It’s the kind of request used to fetch group properties for display in an admin console. The only strange thing is that the request fetches just two properties where it could have retrieved many more.

If you want to retain log data for more than 30 days, you’ll need to offload the data into something like a Log Analytics workspace and pay for that through an Azure subscription. Microsoft Sentinel seems like a good place to work with this data, and it might well be the case that the sheer amount of Graph log data generated in tenants will create a case to use a tool like Security Copilot to extract and understand important events.

Good Examples of Graph Activity Logs in Action

I am no expert in the art of analyzing security logs. If you want to read about the potential insights that the Graph activity logs might uncover, read these posts (part 1 and part 2) by Security MVP Faben Bader. They helped me understand the potential of using Graph activity logs to track threat within a tenant. Another valuable post by Bert-Jan Pals includes many practical examples of using KQL to query the Graph activity logs to summarize and report data.

The Difference with Audit Logs

With Graph activity logs now available, does the need for the Microsoft 365 unified audit log diminish? The answer is no. Graph activity logs capture details about HTTP requests to Graph endpoints. The unified audit log ingests events capturing details about 1,600+ actions taken by workloads within a Microsoft 365 tenant, including Entra ID. Some of the workloads don’t use Graph APIs or partially use Graph APIs. Exchange Online management is an example as is SharePoint Online management. The initial support for Graph-based management for SharePoint Online tenant settings hasn’t progressed since its 2022 debut and Exchange Online has not embraced Graph APIs for management yet (mailbox contents are accessible through the Graph).

Eventually, Microsoft 365 might get to a point where all actions taken by all apps result in Graph requests. We’re still a while away from that point and until then, a combination of log sources and data is needed to build as close to a complete picture of what happens inside a tenant as possible. Some events are not logged: an egregious example is running audit log searches, which have never been captured and won’t be until Microsoft delivers roadmap item 392841 in June 2024.

Microsoft 365 Auditing is a Fragmented Space

The current auditing setup around Microsoft 365 is fragmented. Some audit information needs premium licenses. Entra ID audit data is kept for 30 days and then discarded while the unified audit log can keep information for up to 365 days. PowerShell can get at some data and not others, and the Kusto Query Language (KQL) is similarly handicapped. Graph APIs are available for some data but not others.

A truly unified auditing framework that ingested details from all available sources into a common database and made the information accessible through PowerShell, a Graph API, and KQL would be appreciated. But given the number of different Microsoft development groups involved in this space, I doubt that we will see any progress towards unified cloud auditing soon. This should not stop you from investigating Graph activity logs. Security analysts will welcome the extra detail, if they can understand what that detail means.


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/2024/04/18/graph-activity-logs-ga/feed/ 0 64465
Maester: Microsoft Security Test Automation Framework https://office365itpros.com/2024/04/15/maester-tool-community/?utm_source=rss&utm_medium=rss&utm_campaign=maester-tool-community https://office365itpros.com/2024/04/15/maester-tool-community/#comments Mon, 15 Apr 2024 08:00:00 +0000 https://office365itpros.com/?p=64451

A Community-Driven Security Configuration Analyzer for Entra ID Tenants

The irrepressible Merill Fernando, a product manager in the Microsoft Entra ID organization, came together with Security MVPs Faben Bader and Thomas Naunheim to create the Maester tool. Announced on April 10, Maester is labeled as a “Microsoft Security test automation framework” and installation instructions are available here. It is a great example of a community-driven project.

Maester is built using Pester and Microsoft Graph APIs. Basically, it runs a bunch of tests against an Entra ID tenant (usually a Microsoft 365 tenant) and measures tenant security configuration settings against the MITRE ATT&CK framework using the Entra ID Security Configuration Analyzer. The output is a report telling the administrator what tests passed and what failed. In my case, the first run of Maester said that my tenant failed 42 tests (Figure 1).

Maester reports the results of a tenant scan.
Figure 1: The Maester tool reports the results of a tenant scan

On the surface, failing 42 tests seems like a dreadful outcome and it did generate some concern. However, like anything else that measures something against benchmarks, you need to understand what’s being measured, why a configuration is in a certain state, and if the current settings are valid or should be adjusted.

Conditional Access Policies and Break Glass Accounts

If you use conditional access policies to check inbound connections, at least one break glass account should exist to prevent the possibility of policy misconfiguration locking everyone out (this happens – all the time). I’ve written a PowerShell script to check conditional access policies to make sure that they include exclusions for break glass accounts, adding the accounts to policies when necessary.

Unhappily, my script (which runs regularly as an Azure Automation scheduled job) only processes enabled (active) conditional access policies and ignores those that are in the report-only state. The lack of break glass accounts on some policies in report only mode caused Maester to be unhappy (Figure 2).

Details of a Maester failed test.

Maester tool output
Figure 2: Details of a Maester failed test

To make Maester happy, I adjusted the script to update all conditional access policies.

Another fail reported by Maester said that no conditional access policy existed to require multi-factor authentication for guest accounts. Obviously, something odd happened behind the scenes because that exact policy is in place since January 2022.

Use Your Knowledge to Put Tool Recommendations into Context

The point is that you should never accept a recommendation made by software unconditionally. Always be suspicious until the recommendation is proven, just like you should be suspicious of any text created by generative AI. Context is invaluable and tenant administrators know far more about their business and operations than any tool can aspire to learn.

An example is the use of Entra ID License Utilization Insights where Maester reported the same figures calculated by the Entra admin center to say that I have 5 Entra P1 licenses but 42 active B2B users that need these licenses because they use conditional access policies (to mandate MFA, see above). But my tenant is configured to use the monthly active user billing model for premium features and I pay for this usage every month through an Azure subscription. Microsoft has some work to do to get its insights sorted out, and anything built on top of their data will be flawed until the data is corrected.

Good Links to the Graph Explorer and Graph APIs

We’re discussing the V0.1 release of a community project here and some bugs are expected. To be more positive, I like the way that Maester includes links to the Graph Explorer when it’s possible to use the Explorer to patch configurations with a Graph request. An example is where the access granted to directory information for guest account is unrestricted. The recommendation is to restrict access to prevent guest accounts being able to enumerate directory information, which means that guest accounts should have a restricted access role (GUID 2af84b1e-32c8-42b7-82bc-daa82404023b instead of the default (10dae51f-b6af-4016-8d66-8c2a99b929b3).

It’s easy to fix this problem in the Entra admin center, but who can resist the chance to run a Graph request instead of clicking a button? The link provided opens the Graph Explorer with the request to list the authorization policy (Figure 3). This is a GET transaction so it only fetches the data to check, but for extra marks you can add a request body and PATCH the policy. A future version of Maester might do that work for you if the developers don’t think it too dangerous.

Maester brings you to the right place in Graph Explorer.
Figure 3: Maester brings you to the right place in Graph Explorer

Support the Maester Tool!

It would be easy to keep nitpicking but that’s not the right thing to do. Community projects need to be cherished and supported. Things will improve in time as people find glitches to fix and knowledge grows. The important thing is that Maester is a new tool for Microsoft 365 tenant administrators to use to improve their knowledge of Entra ID security features that can make their tenant more secure and harder to compromise. That’s always a good thing, which is why I like Maester.


Make sure that you’re not surprised about changes that appear inside Microsoft 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/2024/04/15/maester-tool-community/feed/ 5 64451
How to Convert an Entra ID External Account to Internal https://office365itpros.com/2024/03/21/convert-to-internal-user/?utm_source=rss&utm_medium=rss&utm_campaign=convert-to-internal-user https://office365itpros.com/2024/03/21/convert-to-internal-user/#comments Thu, 21 Mar 2024 08:00:00 +0000 https://office365itpros.com/?p=64207

Use the Entra Admin Center or PowerShell to Convert to Internal User Accounts

Many Microsoft 365 tenants support a mixture of internal and external accounts. Internal accounts are member accounts that authenticate with the tenant. External accounts authenticate somewhere else, such as another Microsoft 365 tenant. The most common form of external accounts found in Microsoft 365 tenants are guest accounts created to participate in team or group memberships or for sharing. Other examples are the accounts synchronized into a tenant directory through membership of a Microsoft 365 multi-tenant organization (MTO).

The Convert to Internal User Feature (Preview)

A recent preview feature introduced by the Entra ID team allows organizations to convert accounts from external to internal. In effect, the code takes an external account identity, breaks the link to the original account, and makes the account local. The original account remains intact and is not removed, so some cleanup might be necessary to remove duplicates.

The Entra admin center includes an option in the user account overview to convert the account (Figure 1). The option is only available for external accounts.

The convert to internal user option in the Entra admin center.

Convert to internal user
Figure 1: The convert to internal user option in the Entra admin center

Selecting the option displays a dialog to allow the administrator to specify the user principal name, password, and (optionally) email address for the converted account (Figure 2).

Adding properties to convert an external user to be internal.
Figure 2: Adding properties to convert an external user to be internal

The conversion process preserves the account’s membership in Microsoft 365 groups and teams. However, some background synchronization must happen to make sure that all workloads recognize that the account is now internal. In most cases, signing out of all services should be enough (you can force this by revoking the account’s access token), but you might need to remove the Teams cache to force a rebuild of team rosters.

Convert to Internal User Accounts with PowerShell

Being able to convert an external account to internal through a portal is great for a one-off operation, such as when a contractor joins the organization as a permanent employee. It’s not so good when dealing with large-scale account changes like those that happen during corporate mergers and acquisitions. This is where the automation capabilities of PowerShell are invaluable.

The steps needed to convert an external account to internal with PowerShell are straightforward:

  • Connect to the Microsoft Graph. My example uses an interactive Microsoft Graph PowerShell SDK session.
  • Find the source account and check that it is an external identity. My test is that an account is external if the email address for the account doesn’t belong to any of the tenant’s registered domains.
  • Figure out the new user principal name, email address, and a temporary password. Create a password profile to force the user to create a new password the next time they sign in.
  • Call the convertExternalToInternalMemberUser API to make the change. The API is currently accessed through the beta endpoint. The new User-ConvertToInternal.ReadWrite.All Graph permission allows access to the API.
  • If everything works, update the account’s Mail property and revoke the account’s access token.

Here’s the code that does most of the work:

$PasswordProfile = @{}
$PasswordProfile.Add('password',$NewPassword)
$PasswordProfile.Add('forceChangePasswordNextSignIn', $true)

# Create the parameters to convert the account
$NewAccountParameters = @{}
$NewAccountParameters.Add('userPrincipalName', $NewUserPrincipalName)
$NewAccountParameters.Add('passwordProfile', $PasswordProfile)

Write-Host "Switching the account to be internal..."
# Switch the account to make it internal
$Uri = ("https://graph.microsoft.com/Beta/users/{0}/convertExternalToInternalMemberUser" -f $SourceUser.Id)
$NewAccount = Invoke-MgGraphRequest -Uri $Uri -Body $NewAccountParameters -Method POST -ContentType "application/json"

# If we get back some account details, check to make sure that they're what we expect
If ($NewAccount) {
    $CheckNewAccount = Get-MgUser -UserId $SourceUser.Id -Property id, displayName, userPrincipalName, UserType
    If ($CheckNewAccount.usertype -eq 'Member' -and $CheckNewAccount.UserPrincipalName -eq $NewUserPrincipalName) {
        Update-MgUser -UserId $CheckNewAccount.Id -Mail $NewUserPrincipalName
        $RevokeStatus = Revoke-MgUserSignInSession -UserId $CheckNewAccount.Id
        Write-Host ("{0} is now a {1} account" -f $CheckNewAccount.UserPrincipalName, $CheckNewAccount.userType)
        Write-Host ("The temporary password for the account is {0}" -f $NewPassword)
        Write-Host ("Remember to assign some licenses to the converted account and to remove it from the previous source.")
    }
}

You can download the full script from GitHub.

Some Cleanup Necessary

Being able to switch a user account from external to internal is a useful feature. Remember that some cleanup is necessary to make the newly switched account a full member of the organization. It’s important to assign licenses to the account after its conversion as otherwise the account won’t be able to access Microsoft 365 services. In addition, some adjustments might be necessary to ensure that the account properties are fully populated so that the Microsoft 365 profile card displays correct information and functionality like dynamic groups and dynamic administrative units pick up the new account as appropriate.


Learn more about how the Entra ID and the rest of 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/2024/03/21/convert-to-internal-user/feed/ 5 64207
Finding Devices Used for Multifactor Authentication https://office365itpros.com/2024/03/14/entra-id-registered-device-mfa/?utm_source=rss&utm_medium=rss&utm_campaign=entra-id-registered-device-mfa https://office365itpros.com/2024/03/14/entra-id-registered-device-mfa/#respond Thu, 14 Mar 2024 01:00:00 +0000 https://office365itpros.com/?p=64067

Track Down Unused Entra ID Registered Devices By Using Entra ID Sign-In Data

At the end of January, I wrote about how to use multiple sources of data to figure out which user accounts use multifactor authentication. The basic idea is to combine information about authentication methods defined for accounts with Entra ID sign-in logs and sign-in activity noted in Entra ID account properties to build a picture of actual multifactor authentication activity.

A reader question asked if the same technique could be used to identify which devices people use for multifactor authentication. The scenario described featured a user with two registered phones but only uses one device. The desire is to review which devices have been used in the last 30 days, possibly with an eye to remove the unused devices.

A Lack of Device Information in Entra ID Sign-In Logs

Unfortunately, Entra ID does not capture device information for a large proportion of its sign-in records. Some of this is deliberate, such as the removal of PII data from sign-ins for guest accounts. In other cases, Entra ID simply fails to capture the device information. After poking around logs for a couple of hours, I can discern no reliable pattern of when Entra ID captures device information and when it doesn’t.

I decided to download the sign-in data from the Entra admin center as a CSV file as described in the original article, and edit the file to remove the first “incoming token type” column. I then imported the file into an array and sorted it to find the unique instances of device identifiers. Finally, I ran the Get-MgDevice cmdlet to retrieve the set of registered devices.

Write-Host "Loading data"
[array]$Data = Import-Csv $InputDataFile | Sort-Object {$_.'Date (UTC)' -as [datetime]} -Descending
# Retrieve devices found in sign in logs
[array]$FoundDevices = $Data | Sort-Object 'Device ID' -Unique 
$FoundDevices = $FoundDevices | Where-Object {($_.'Device ID' -ne "{PII Removed}")}  | Select-Object -ExpandProperty 'Device ID'
# Retrieve known devices
[array]$KnownDevices = Get-MgDevice -All

Reporting Found Devices

The result is two arrays: one holding the device identifiers for the devices used for sign-ins; the other holding information about registered devices. To create a report, the script loops through the devices used for sign-ins and fetches information about the device and the last time it was used. In both cases, simple lookups against the arrays fetch the information needed for the report. Here’s the code:

$Report = [System.Collections.Generic.List[Object]]::new()
ForEach ($Device in $FoundDevices) {
    If (!([string]::IsNullOrWhiteSpace($Device))) {
        $DeviceDetails = $KnownDevices | Where-Object {$_.DeviceId -eq $Device}
        $DataDetails = $Data | Where-Object {$_.'Device ID' -eq $Device} | Select-Object -First 1
        $SignInDate = Get-Date $DataDetails.'Date (UTC)' -format 'dd-MMM-yyyy HH:mm'
        $RegisteredDate = Get-Date $DeviceDetails.RegistrationDateTime -format 'dd-MMM-yyyy HH:mm'
        $ReportLine = [PSCustomObject][Ordered]@{
            SignIn          = $SignInDate
            Device          = $Device
            'Device name'   = $DeviceDetails.displayName
            Id              = $DeviceDetails.Id
            OS              = $DeviceDetails.OperatingSystem
            Version         = $DeviceDetails.OperatingSystemVersion
            Registered      = $RegisteredDate
            'User agent'    = $DataDetails.'User agent'
            User            = $DataDetails.User
            UPN             = $DataDetails.userName
            Resource        = $DataDetails.Resource
            ClientApp       = $DataDetails.'Client App'
            }
        $Report.Add($ReportLine)
        }
}

The script also checks for the registered owner of the device using the Get-MgDeviceRegisteredOwner cmdlet (see this article for details). To keep things simple, I don’t show that code here.

The output report looks like the data shown in Figure 1.

Entra ID Registered devices used for multifactor authentication.
Figure 1: Entra ID registered devices used for multifactor authentication

Now we know which devices have been used for multifactor authentication. Entra ID keeps sign-in data for a maximum of a month, so the generated report covers that period if that date range option is selected when downloading the data from the Entra admin center.

To report the registered devices that aren’t detected using multifactor authentication, the script creates an array by filtering registered devices against the set found in the sign-in data and reports what it finds:

[array]$UnusedDevices = $KnownDevices | Where-Object {$_.Id -notin $FoundDevices} | Sort-Object DisplayName

Write-Host ""
Write-Host "The following devices cannot be found in a sign-in log"
Write-Host "------------------------------------------------------"

$UnusedDevices | Format-Table Id, DisplayName, OperatingSystem, RegistrationDateTime

It’s important to emphasize that the lack of evidence supporting the usage of these devices might be due to Entra ID not noting device information in sign-in records. In other words, the script can only generate evidence based on available data and it will probably take more investigation to determine exactly which devices are in active use. But at least we have a start.

You can download the script from GitHub.

A Partial Answer

It’s disappointing to discover that Entra ID doesn’t log device information for every sign-in record. No doubt good reasons exist why logging doesn’t happen. In any case, some information is available, and the script is a good example of extending an existing idea to cover a different scenario This is only possible when you have a good understanding of how components Entra ID and PowerShell work, but have I said that I know a good book to help with that challenge?


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/2024/03/14/entra-id-registered-device-mfa/feed/ 0 64067
Reporting Soft-Deleted Entra ID Objects https://office365itpros.com/2024/02/28/report-soft-deleted-entra-id-object/?utm_source=rss&utm_medium=rss&utm_campaign=report-soft-deleted-entra-id-object https://office365itpros.com/2024/02/28/report-soft-deleted-entra-id-object/#respond Wed, 28 Feb 2024 01:00:00 +0000 https://office365itpros.com/?p=63870

Contemplating the Best Way to Report Soft-Deleted Entra ID Objects

The Microsoft Technical Community article about keeping track of object deletions in Entra ID contains some interesting information. I want to take the opportunity to throw some additional light on the topic.

Use the Unified Audit Log to Track Entra ID Object Deletions

First, although the article covers KQL queries to find information about Entra ID audit log entries ingested into Microsoft Sentinel, it doesn’t cover how to retrieve the same information through the unified audit log. Every Microsoft 365 tenant with Office 365 E3 or above has access to the unified audit log, so you don’t have to invest in Sentinel licenses to find events for soft- or hard-deletions of objects like users and groups. The events can be retrieved using the Search-UnifiedAuditLog PowerShell cmdlet or through the Audit search feature in the Microsoft Purview Compliance portal.

[array]$Operations = "Delete group", "Delete application", "Delete administrative unit", "Delete user", "Remove service principal"
[array]$Records = Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-90) -EndDate (Get-Date).AddDays(1) -ResultSize 5000 -Operations $Operations -SessionCommand ReturnLargeSet -Formatted
$Records | group operations -NoElement | Sort-Object count | Format-Table Name, Count -AutoSize

Name                                  Count
----                                  -----
Delete administrative unit.               2
Delete application.                       2
Delete user.                              5
Remove service principal credentials.     5
Remove service principal.                 5
Delete group.                             7

The nice thing is that Microsoft 365 retains audit log events for 90 days (Purview Audit standard) or 365 days (Purview Audit premium), so the information is available for longer than it is in the Entra ID audit logs (maximum 30 days). By all means, use Microsoft Sentinel if you have it, but the point is that you don’t need to spend any extra money to track down audit events for Entra ID object deletions.

Report Entra ID Soft-Deleted Objects with PowerShell

Entra ID stores its soft-deleted objects in a recycle bin. The objects remain in the recycle bin for 30 days after which Entra ID hard-deletes (permanently removes) the objects. The Entra admin center has options to recover soft-deleted users, groups, and applications. Any soft-deleted object can be recovered using the Restore-MgDirectoryObject cmdlet from the Microsoft Graph PowerShell SDK. See this page for more information, and here are articles detailing how to restore soft-deleted Microsoft 365 groups and user accounts.

Before you can run the Restore-MgDirectoryObject cmdlet, you need to know the identifier for the object to restore. The article provided a PowerShell script to help. I thought the code was OK but could be improved by:

  1. Using production and not beta cmdlets. For instance, use Get-MgDirectoryDeletedItemAsUser to find soft-deleted user objects instead of Get-MgBetaDirectoryDeletedItemAsUser. Beta cmdlets are great, and I use them all the time, but if a cmdlet is available in the production (V1.0) endpoint, it’s better to use that version. Curiously, the Property parameter must be used with the Get-MgDirectoryDeletedItemAsUser cmdlet to fetch the DeletedDateTime property. This doesn’t happen with the other cmdlets.
  2. Including the All parameter for the cmdlets. The Graph APIs automatically limit the number of objects returned by a call (usually a page of between 100 and 300 objects). Developers use a process called pagination to fetch all available objects if necessary. Many Graph SDK cmdlets do the heavy lifting of pagination by including the All parameter to instruct the Graph to fetch all matching objects. It’s unlikely that you’ll need the cmdlets to fetch more than one page of deleted objects, but you might run into a situation where several hundred soft-deleted user or group objects are available, and it’s nice to be sure to fetch them all.
  3. Including a type in the output. As written, the script reported basic details of all soft-deleted objects and left it to the administrator to figure out what type of object each was. By including the object type in the report, it makes the job of an administrator easier.

Figure 1 shows the output of the script.

Reporting soft-deleted Entra ID objects.

Report soft-deleted Entra ID object
Figure 1: Output of the script to report soft-deleted Entra ID objects

Download the Script to Report Soft-Deleted Entra ID Objects

You can download my version of the script to report Entra ID soft-deleted objects from GitHub. I tested using V2.15 of the Microsoft Graph PowerShell SDK. The V1.0 version of the cmdlets might not be available in an earlier version. The normal caveats apply. This I script is for demonstration purposes only. Don’t expect bulletproof code!


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/2024/02/28/report-soft-deleted-entra-id-object/feed/ 0 63870
Microsoft Releases Entra ID License Utilization Insights https://office365itpros.com/2024/02/26/entra-id-usage-insights/?utm_source=rss&utm_medium=rss&utm_campaign=entra-id-usage-insights https://office365itpros.com/2024/02/26/entra-id-usage-insights/#respond Mon, 26 Feb 2024 01:00:00 +0000 https://office365itpros.com/?p=63904

Entra ID Usage Insights for Premium Licenses

A February 20 Microsoft Technical Community post covering the introduction of Microsoft Entra License Utilization Insights began by saying that over 800,000 organizations use Entra ID before announcing the preview of a new Entra ID License utilization portal. When I read this first, I assumed that the new portal would help customers manage all licenses assigned to Entra ID accounts but that’s not the case. Instead, the new portal (or rather, a new page in the Entra admin center) focuses on Entra ID premium licenses.

Entra ID premium licenses are available separately or as part of a package such as Enterprise Mobility and Security or Microsoft 365 E3 or E5. Although these licenses enable access to a range of features, the most common reason why Microsoft 365 tenants need Entra ID premium licenses is for conditional access policies. Currently, Microsoft says that 38% of Entra ID accounts use multifactor authentication. Demand is likely to grow in the future when Microsoft deprecates the per-user multifactor authentication capabilities available in Office 365 E3 and E5 and forces tenants to use conditional access policies instead.

Microsoft says that the new page (Figure 1) allows administrators to view usage details for Entra ID premium licenses. The preview is limited to support for conditional access (P1) and risk-based conditional access (P2).

Entra ID License Utilization Insights in the Entra admin center.

Entra ID usage insights.
Figure 1: Entra ID Usage Insights in the Entra admin center

Microsoft expects to add support for more features when the feature reaches general availability. They claim that usage insights will help tenants to understand the number of available premium licenses and the value gained by users from these licenses. And of course, if any over-usage is detected, Microsoft will be happy to bring that salient fact to the attention of tenant administrators.

A More Restrictive Regime Coming?

At present, I Microsoft does not enforces licensing requirements for Entra ID premium features with the same precision as happens for Microsoft 365 licenses.  For instance, Entra ID processes connections that require multifactor authentication no matter if the account has a premium license. The advent of this license utilization page might be a pointer to a more restrictive regime that’s coming, including for premium features consumed by guest users (which should now be covered by Monthly Active User (MAU) pricing).

For instance, my tenant has five Microsoft 365 E5 licenses among the licensing mix, so that means that the tenant has five Entra ID P2 licenses. The insights page tells me that there are 11 accounts using conditional access and 45 users using conditional access B2B (guests). Costs for the guests are covered by MAU pricing tied to an Azure subscription, but I seem to have a deficit of six Entra ID P1 licenses to license multifactor authentication for the excess user accounts. Obviously, this is something that I will deal with immediately.

Graph Access to Entra ID Usage Insights

In addition to the page in the Entra admin center, Microsoft has a Graph API to access the usage insights (through the beta endpoint because it’s a preview feature). Here’s how to access the data using the Microsoft Graph PowerShell SDK:

[array]$Data = Invoke-MgGraphRequest -Method Get -Uri "https://graph.microsoft.com/beta/reports/azureADPremiumLicenseInsight" -OutputType PSObject

$Data

@odata.context            : https://graph.microsoft.com/beta/$metadata#reports/azureADPremiumLicenseInsight/$entity
entitledP1LicenseCount    : 0
entitledP2LicenseCount    : 5
entitledTotalLicenseCount : 5
p1FeatureUtilizations     : @{conditionalAccess=; conditionalAccessGuestUsers=}
p2FeatureUtilizations     : @{riskBasedConditionalAccess=; riskBasedConditionalAccessGuestUsers=}

To get the counts of user accessing the licensed features, we can do something like this:

Write-Host ("The tenant has {0} member accounts and {1} guest accounts that use conditional access" -f $Data.p1FeatureUtilizations.conditionalaccess.usercount, $Data.p1FeatureUtilizations.conditionalaccessguestusers.usercount )

The tenant has 11 member accounts and 45 guest accounts that use conditional access

Here’s how to find which accounts actively use multifactor authentication to access your tenant.

Entra ID Usage Insights Serve Microsoft More Than Tenants

It’s natural that the Entra ID development group should take care of their license consumption and revenue, so the provision of a page to make tenants aware of the consumption is unsurprising. I think this is a forerunner of a more restrictive regime for Entra ID premium licensing, which again is unsurprising. I just hope that it doesn’t put tenants off from using multifactor authentication to protect user accounts. Given today’s threat horizon, multifactor authentication is more of a must-have than an added extra and it would be nice if Microsoft supported tenants to use more multifactor authentication rather than putting barriers in the way.


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/2024/02/26/entra-id-usage-insights/feed/ 0 63904
Checking Out Entra Identity Secure Score https://office365itpros.com/2024/02/13/entra-identity-secure-score/?utm_source=rss&utm_medium=rss&utm_campaign=entra-identity-secure-score https://office365itpros.com/2024/02/13/entra-identity-secure-score/#respond Tue, 13 Feb 2024 01:00:00 +0000 https://office365itpros.com/?p=63655

Entra Identity Secure Score Includes a Check for Expiring Application Credentials

In January, I wrote about a script to analyze the credentials (certificates and secrets) for Entra ID registered apps and report expired credentials. It’s a nice example of creating an automated process with the Microsoft Graph PowerShell SDK. In this case, to save busy administrators from having to check individual applications in the Entra admin center to make sure that their credentials don’t expire. Running a scheduled Azure Automation job to send email to administrators to draw their attention to apps whose credentials are about to expire (within 30 days) seems better than checking each application.

The Entra admin center is a complex place that hosts many features, some of which are only available to tenants with Entra P1 or P2 licenses. Fortunately, I have Microsoft 365 E5 licenses, and both Entra P1 and P2 are included in the E5 product, so I get to see the Entra admin center in its full glory.

What is Entra Identity Secure Score?

All of which brings me to the Identity Secure Score blade, which Microsoft has recently overhauled (Figure 1). To be honest, I had ignored Identity Secure Score to this point, mostly through sheer ignorance (here’s Microsoft’s documentation), so it was nice to see that the tenant’s score wasn’t disastrous.

Entra Identity Secure Score in the Entra admin center
Figure 1: Entra Identity Secure Score in the Entra admin center

Microsoft computes secure scores daily (around 1AM Pacific), so changes made in response to the set of recommendations listed for a tenant don’t impact the displayed score immediately. You need to wait, but patience is a virtue.

Updating Expired Credentials

One of the ways you can improve your tenant’s secure score is to renew expiring credentials for applications. Clicking the link in the secure score overview brings up details of applications flagged by Entra as having expired or expiring credentials (Figure 2). Based on the information I can see for my tenant, it seems like a 30-day window is used to detect expiring credentials.

Renew expiring credentials for Entra Identity Secure Score.
Figure 2: Renew expiring credentials for Entra Identity Secure Score

You can select an application to see its details and update credentials (and remove expired credentials) to complete the recommended action. Updating a credential means that you add a new secret or certificate, and that action has consequences because anything that relies on a specific secret or certificate, like a PowerShell runbook that executes through Azure Automation will need to be updated. Then again, an expired or expiring credential must be renewed anyway and I’m sure those who look after the applications will be ready to swing into action and do whatever’s necessary.

Apps versus the Microsoft Graph PowerShell SDK

My recent focus on finding and reporting expired and expiring credentials made me think about my use of apps. Because I write exclusively in PowerShell, I use apps as a mechanism to authenticate and obtain an access token with the necessary permissions to do work with Graph APIs. Until the Microsoft Graph PowerShell SDK came along, an app was the only way for PowerShell developers to authenticate and get an access token, so we all learned the steps necessary to pass the app identifier, credentials, and tenant identifier to ask Entra ID for a token. This process soon became second nature.

The Microsoft Graph PowerShell SDK changed everything. For interactive access, the SDK has a service principal to hold delegated permissions. For non-interactive access, the SDK can use apps, much like we did before. However, it’s easier to authenticate, especially when using managed identities with Azure Automation. SDK cmdlets are available for almost every Graph API request you might want to make. After you learn their foibles, the SDK cmdlets are almost as easy to work with as standard PowerShell. And in terms of the Graph, many SDK cmdlets that fetch objects take care of pagination, so that’s another item developers don’t need to worry about.

The upshot is that I use the Microsoft Graph PowerShell SDK as the norm these days and only resort to an app when forced to, possibly because a new API hasn’t made its way into the SDK through Microsoft’s AutoRest process. The SDK is improving steadily. It has reduced the need to check for expiring credentials, but don’t expect the need to disappear anytime soon.


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/2024/02/13/entra-identity-secure-score/feed/ 0 63655
Why MFA, Conditional Access, and Sensitivity Labels can Combine to Give Outlook a Problem https://office365itpros.com/2024/02/12/conditional-access-mfa-email/?utm_source=rss&utm_medium=rss&utm_campaign=conditional-access-mfa-email https://office365itpros.com/2024/02/12/conditional-access-mfa-email/#comments Mon, 12 Feb 2024 01:00:00 +0000 https://office365itpros.com/?p=63638

Conditional Access MFA Gives Outlook Desktop a Problem with Protected Email

I think most Microsoft 365 tenant administrators would agree that multifactor authentication (MFA) is a good thing. MFA stops bad guys compromising accounts even if they have the password. Microsoft’s recent little bother with Midnight Blizzard could have been cut off had the account whose password was uncovered by a password spray attack been protected with MFA.

Sensitivity labels are also good in terms of their ability to protect sensitive Office documents and PDF files with encryption. The usage rights assigned in sensitivity labels stop people who don’t have access from being able to decrypt and view the content of protected files.

Two good things create a warm feeling of snug protection, or so it might seem. That is, until conditional access policies get in the way. Specifically, conditional access policies that insist on MFA for all cloud apps without exclusions. This seems like a very good kind of policy because it enforces MFA before users can connect to OWA, the new Outlook “Monarch” client, SharePoint Online, Teams, and so on. However, “all cloud apps” means all cloud apps, including the Microsoft Rights Management Services app. This is a multi-tenant app that exists in tenants that use Microsoft Information Protection, the basis of the encryption applied by sensitivity labels to protect files.

Get-MgServicePrincipal -filter "displayname eq 'Microsoft Rights Management Services'" | Format-Table DisplayName, AppId, SignInAudience

DisplayName                          AppId                                SignInAudience
-----------                          -----                                --------------
Microsoft Rights Management Services 00000012-0000-0000-c000-000000000000 AzureADMultipleOrgs

Let’s assume that you deploy a conditional access policy to enforce MFA for all cloud apps. With this configuration in place, users generate and send some protected email by applying sensitivity labels with encryption. Some messages go to external recipients, but that’s OK because the usage rights defined in the labels allow the external recipients to access the content.

The Problem with MFA for All Cloud Apps

All works wonderfully if the external recipients use OWA, Monarch, or Outlook Mobile to read the messages. Decryption for these clients is managed by Exchange Online, which obtains the necessary use licenses to allow the clients to access the content. However, Outlook desktop (Win32) uses a different scheme and must obtain use licenses from Microsoft Rights Management Services running on the originating (your) tenant. This is when you see the dialog telling you that Outlook is configuring the computer for Information Rights Management (Figure 1).

Outlook desktop configures itself for Rights management.
Figure 1: Outlook configures itself for Rights management.

But the conditional access policy in the sending tenant insists on MFA for all cloud apps and there’s no way for Outlook to satisfy an MFA challenge in your tenant. Deprived of the use license, Outlook falls back to displaying the RPMSG wrapper for the message (Figure 2).

Outlook desktop can't fetch a use license so falls back to the protected wrapper.

Conditional access mfa
Figure 2: Outlook desktop can’t fetch a use license so falls back to the protected wrapper

Clicking the read the message link brings the user to the Office 365 Message Encryption portal, where they can read the message. This proves that the usage rights given to the user allow access. The problem lies with not being able to obtain the use license due to the MFA challenge.

Reading the protected content in the OME portal.
Figure 3: Reading the protected content in the OME portal

Excluding Microsoft Rights Management Services

The simple solution is to exclude the Microsoft Rights Management Services app from all conditional access policies that enforce MFA for user connections. This is easily done by editing policies through the Entra admin center (Figure 4).

Configuring an exclusion in a conditional access policy for the Microsoft Rights Management Services app.
Figure 4: Configuring an exclusion in a conditional access policy for the Microsoft Rights Management Services app

PowerShell makes it easy to scan and update conditional access policies in the tenant. A similar approach to the one to add breakglass accounts to conditional access policies can be used to add an exclusion to policies.

The script (available from GitHub) performs these steps.

  • Connects to the Microsoft Graph PowerShell SDK.
  • Runs the Get-MgIdentityConditionalAccessPolicy cmdlet to find the set of enabled conditional access policies.
  • Checks each policy to see if an exclusion for the Microsoft Rights Management Services app is present.
  • If no exclusion is present, the script checks if the policy uses MFA (with or without authentication strength) as a control.
  • If the policy applies MFA, the script checks if a forced password change is set (this eliminates the possibility of adding an app exclusion) and that the policy doesn’t use an authentication context. Both prevent the addition of an excluded app to the policy.

Once it’s sure that an exclusion is possible, the script adds the exclusion. Figure 5 shows the script in action.

Running the script to update conditional access policies with an app exclusion.
Figure 5: Running the script to update conditional access policies with an app exclusion.

It’s an Ecosystem Thing

It’s unfortunate when a clash occurs between two important parts of the Microsoft 365 ecosystem. It’s a reminder to us all about the importance of taking a holistic view of functionality instead of focusing on a single workload. Some will think that this problem is something that Microsoft testing should have found. That’s a fair perspective, and Microsoft’s documentation does cover some potential issues with conditional access and encrypted documents, but it’s unlikely that the testing regime considers how sensitivity labels work with Outlook desktop for external recipients when MFA is involved.

Any debate must be tempered by the realization that the clash appeared due to the increased usage of multifactor authentication (due to incessant campaigning by Microsoft) allied to increased use of sensitivity labels to protect information. Both are good trends.


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/2024/02/12/conditional-access-mfa-email/feed/ 5 63638
Reporting App Permissions Used by Managed Identities https://office365itpros.com/2024/02/06/managed-identity-permissions/?utm_source=rss&utm_medium=rss&utm_campaign=managed-identity-permissions https://office365itpros.com/2024/02/06/managed-identity-permissions/#respond Tue, 06 Feb 2024 01:00:00 +0000 https://office365itpros.com/?p=63576

Managed Identity Permissions Gather Like Moss on a Tree

A side effect of running the Microsoft Graph PowerShell SDK cmdlets in interactive sessions is that the service principal for the SDK app can accrue a collection of Graph permissions over time. Although only delegated permissions are available in SDK interactive sessions, the fact remains that the SDK app tends to become highly permissioned over time unless someone takes the time to review and remove unneeded permissions regularly. Because the SDK app can hold so many permissions, some organizations secure access to the app so that only nominated accounts can use it.

Azure Automation Accounts used by Managed Identities Also Accrue Permissions

If assigned the necessary permissions, runbooks executed by Azure Automation accounts that use managed identities for authentication can include cmdlets from modules like the SDK, Teams, and Exchange Online management. Like the app used for SDK interactive sessions, the service principals for the Azure Automation accounts hold the consented permissions, and like the SDK, these service principals can collect a wide variety of permissions over time. Figure 1 shows some of the permissions held by one of the Azure Automation accounts I use in my tenant.

Managed identity permissions viewed through the Entra admin center.
Figure 1: Managed identity permissions viewed through the Entra admin center

It’s not just Graph permissions that the service principal might hold consent for. If a runbook needs to execute cmdlets from the Teams or Exchange Online PowerShell modules, the automation account must have consent to use the appropriate permission (like the Exchange Manage As App permission). The automation account must also be a member of the Exchange Administrator role group.

Checking Managed Identity Permissions

All of which leads to the conclusion that Azure Automation accounts used with Microsoft 365 might hold many permissions and become a target for attackers. As we know from the recent Midnight Blizzard attack against Microsoft, attackers can exploit a highly-permissioned app to exfiltrate data. It therefore pays to review the permissions used for managed identities periodically.

Yesterday, I discussed the Export-MsIdAppConsentGrantReport cmdlet and its usefulness to tenant administrators to track OAuth permissions assigned to apps. The cmdlet includes the service principals for managed identities but doesn’t have a way to filter on just those service principals, so we employ the same kind of technique as used to report app permissions using a filter to find the set of service principals for managed identities:

[array]$ManagedIdentities = Get-MgServicePrincipal -Filter "servicePrincipalType eq 'ManagedIdentity'" | Sort-Object DisplayName

After finding the set of managed identities, it’s a matter of querying the service principal for each managed identity to discover the set of permissions it has and resolving the identifier for each permission to translate the GUID to a human-friendly permission. To do the job, I usually build a hash table containing the GUIDs (keys) and permission names (values). It’s much faster to use a GUID to look up a hash table than search through the set of permissions known to the Graph, Exchange Online, and Teams.

Throwing everything together into a PowerShell script (available from GitHub) gives the result shown in Figure 2. You can see that I added a suffix to show the source (like [Teams]) for non-Graph permissions.

Report showing Managed Identity Permissions for a Tenant.
Figure 2: Report showing Managed Identity Permissions for a Tenant

I didn’t bother adding the code to send the report out via email. That’s easily accomplished by copying the mail send code from the script to report app permissions. Remember to change the message subject and report title!

Automation Preserves Administrator Sanity

Only four managed identities are present in my tenant so checking the permissions assigned to each through the Entra admin center isn’t hard and doesn’t take much time. However, keeping an eye on permission acquisition by managed identities is boring and likely to be a task postponed in favor of tackling some more interesting work. Because automation cuts through the drudge, it’s invaluable for tenant administration. I hear that’s the promise of Copilot too…


Learn more about how the Microsoft 365 applications 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/2024/02/06/managed-identity-permissions/feed/ 0 63576
New MSIdentityTools Cmdlet to Report OAuth Permissions https://office365itpros.com/2024/02/05/export-msidappconsentgrantreport/?utm_source=rss&utm_medium=rss&utm_campaign=export-msidappconsentgrantreport https://office365itpros.com/2024/02/05/export-msidappconsentgrantreport/#respond Mon, 05 Feb 2024 00:06:00 +0000 https://office365itpros.com/?p=63591

The Export-MsIdAppConsentGrantReport Cmdlet Makes it Easier for Tenant Administrators to Track OAuth Permissions for Apps

As readers of my articles know, I have often discussed the topic of monitoring and checking OAuth permissions assigned to apps, usually using the Microsoft Graph PowerShell SDK to fetch and interpret permissions in a way that makes sense to tenant administrators. A recent example is an article about how to generate a report about OAuth permissions.

The need to understand the permissions assigned to apps was underscored by the recent Midnight Blizzard attack on Microsoft corporate mailboxes. The fact that an OAuth app can exist with permissions necessary to exfiltrate email and attachments from mailboxes without Microsoft’s administrators and security professionals detecting its presence for several months, highlights the challenge facing every tenant administrator.

A New MsIdentityTools Cmdlet

And that’s why the creation of the Export-MsIdAppConsentGrantReport cmdlet is such welcome news. Not every tenant administrator can master the PowerShell cmdlets used to interrogate apps or understand the data that comes back. It’s a lot easier when a single cmdlet does the job. Export-MsIdAppConsentGrantReport is part of the MSIdentity Tools module, developed and maintained by members of the Entra ID product group to help with different aspects of directory management.

You can get version 2.0.52 of the MsIdentityTools module by installing it from the PowerShell gallery.

Install-Module -Name MSIdentityTools -Force -Scope AllUsers -RequiredVersion 2.0.52

Because of a dependency, the MSIdentityTools module also installs the Microsoft.Graph.Authentication module (part of the Microsoft Graph PowerShell SDK). Oddly, it installs version 2.9.1 of the Authentication module instead of the current version (2.12). Apart from occupying some extra disk space, no great harm is done and MSIdentityTools is happy to use 2.12.

Running Export-MsIdAppConsentGrantReport

Generating a report with the Export-MsIdAppConsentGrantReport cmdlet is easy. This code connects to the Microsoft Graph PowerShell SDK, imports the ImportExcel module (needed to generate an Excel worksheet), and creates the report in the form of a worksheet:

Connect-MgGraph -Scopes Directory.Read.All -NoWelcome
Import-Module ImportExcel
Export-MsIdAppConsentGrantReport -ReportOutputType ExcelWorkbook -ExcelWorkbookPath c:\temp\OAuthAppPermissionsReport.xlsx

The cmdlet uses Microsoft Graph API calls to read and analyze information about service principals. It then calls cmdlets from the ImportExcel module to generate a multi-sheet workbook. Figure 1 shows one of the sheets listing Graph and other permissions (like the right for an app to run cmdlets from the Teams PowerShell module as an administrator).

Excel worksheet generated by the Export-MsIdAppConsentGrantReport cmdlet
Figure 1: Excel worksheet generated by the Export-MsIdAppConsentGrantReport cmdlet

Even better, the Export-MsIdAppConsentGrantReport cmdlet can generate its data as a PowerShell object:

[array]$AppData = Export-MsIdAppConsentGrantReport -ReportOutputType PowerShellObjects

The reason why this facility is so good is that the cmdlet does a lot of heavy lifting to fetch information about service principals and permissions and delivers them in an array that’s easy for PowerShell scripts to consume. In effect, this eliminates a lot of code in scripts like those that I’ve written to report permission assignments. Instead of running Get-MgServicePrincipal and parsing the results to find and interpret data, developers can run Export-MsIdAppConsentGrantReport and use its output instead.

For example, this command finds the service principals that hold the Mail.Send permission. This is a high-priority permission because Mail.Send allows the app to send email from any mailbox unless limited by RBAC for Applications.

$Appdata | Where-Object Permission -match 'Mail.Send' | Format-Table ClientDisplayName, Appid, Permissiontype

ClientDisplayName                                                 AppId                                PermissionType
-----------------                                                 -----                                --------------
MalwareExample                                                    d868053d-58bc-4010-a659-23de72d14669 Application
PowerShellGraph                                                   8f005189-8c58-4fb5-a226-8851e13490cb Application
MailSendApp                                                       970e01d1-ce75-46ba-a054-4b61c787f682 Application
ExoAutomationAccount_Y6LgjDYIfPnxmFzrqdbaClsnTD/gN4BNnVMywiju5hk= 45923847-be5b-4e29-98c5-bc9ab0b5dc95 Application
ManagedIdentitiesAutomation                                       b977a222-3534-4625-980d-e2f864d3a2d5 Application
Microsoft Graph PowerShell SDK Cert                               d86b1929-b818-411b-834a-206385bf5347 Application
PnP Management Shell                                              31359c7f-bd7e-475c-86db-fdb8c937548e Delegated-AllPr…
MailSendAppDelegate                                               0fb521aa-8d32-4c0b-b124-565a1d8c4abe Delegated-AllPr…
MailSendAppDelegate                                               0fb521aa-8d32-4c0b-b124-565a1d8c4abe Delegated-AllPr…
PowerShellGraph                                                   8f005189-8c58-4fb5-a226-8851e13490cb Delegated-AllPr…
IMAP access to Shared Mailbox                                     6a90af02-6ac1-405a-85e6-fb6ede844d92 Delegated-AllPr…
Microsoft Graph Command Line Tools                                14d82eec-204b-4c2f-b7e8-296a70dab67e Delegated-AllPr…
Microsoft Graph Command Line Tools                                14d82eec-204b-4c2f-b7e8-296a70dab67e Delegated-AllPr…

Notice that some duplicates are present. These are probably due to a glitch in the cmdlet that will be squashed soon.

Because the array is a PowerShell object, you can export it in whatever format you want, including CSV, Excel, and HTML.

Not a Panacea, Just a Tool

The Export-MsIdAppConsentGrantReport cmdlet is a valuable contribution to the tenant administrator toolbox, but it’s not a silver bullet that will stop over permissioned OAuth apps. It’s also not a replacement for administrators acquiring knowledge about how Entra ID apps acquire and use permissions (application and delegated) and how to extract that information from Entra ID using Graph API requests or Microsoft Graph PowerShell SDK cmdlets. Think of Export-MsIdAppConsentGrantReport as a useful tool, no more, no less. It’s great to have.


Make sure that you’re not surprised about changes that 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/2024/02/05/export-msidappconsentgrantreport/feed/ 0 63591
Graph User.ReadBasic.All Application Permission Available https://office365itpros.com/2024/01/30/user-readbasic-all-permission/?utm_source=rss&utm_medium=rss&utm_campaign=user-readbasic-all-permission https://office365itpros.com/2024/01/30/user-readbasic-all-permission/#respond Tue, 30 Jan 2024 01:00:00 +0000 https://office365itpros.com/?p=63521

Controlling Application Access to Entra ID User Account Information

Message center notification MC704030 (5 January 2024) brings important news for developers that the User.ReadBasic.All permission is now available for both delegated and application usage. The permission restricts the ability of an app to retrieve properties of Entra ID user accounts to a basic set instead of all properties. The Microsoft Graph access model is based on least permissions, but if an app can always retrieve everything known about a user account, that hardly fits the definition of “least.”

Until now, the User.ReadBasic.All has been available for delegated permissions, meaning that it works in the context of the signed-in user and restricts the user to viewing the limited set of properties for other users. This is good, but many apps, including those run by PowerShell scripts, process user details and some of those apps don’t need access to all account properties. That’s where the User.ReadBasic.All permission comes in.

Testing the Effect of the User.ReadBasic.All Permission

Let’s examine what using the new permission means in practice. First, create a registered app in Entra ID. You can also use an existing app. The important thing is to ensure that the application permissions consented for the app include User.ReadBasic.All and no other permission that could allow the app to access full user information (like Directory.Read.All).

Fetch an access token for the app as normal and examine the access token to ensure that the permissions specified in the token are as expected. The access token shown through jwt.io in Figure 1 has two permissions: User.ReadBasic.All and Group.Read.All. The latter won’t allow the app to fetch extended user information, so it’s good to test.

An access token with the User.ReadBasic.All permission.
Figure 1: An access token with the User.ReadBasic.All permission

Running a Graph query to find properties for a specified user account returns the following:

$Uri = "https://graph.microsoft.com/v1.0/Users/21a3ce8f-6d55-4e57-9210-d85a2f1618ec?`$Select=businessPhones,displayname, givenname, jobtitle, mail,mobilephone,officelocation,preferredlanguage, surname, userprincipalname, id, department, city, stateorprovince, country, employeeid, employeetype, employeehiredate"
[array]$user = Get-GraphData -AccessToken $Token -Uri $Uri
$User | Format-List

businessPhones    : {}
displayName       : "Popeye" Doyle
givenName         : Jimmy
jobTitle          :
mail              : Popeye.Doyle@o365maestro.onmicrosoft.com
mobilePhone       :
officeLocation    :
preferredLanguage :
surname           : Doyle
userPrincipalName : Popeye.Doyle@o365maestro.onmicrosoft.com
id                : 21a3ce8f-6d55-4e57-9210-d85a2f1618ec
department        :
city              :
country           :
employeeId        :
employeeType      :
employeeHireDate  :

You can see that even though the query requested a bunch of user properties, the restricted permission limited the Graph to returning only some properties. If an app needs to retrieve all properties, it needs consent for the User.Read.All permission. The same query run by an app with User.Read.All permission returns this set:

businessPhones    : {1 404 14746141}
displayName       : "Popeye" Doyle
givenName         : Jimmy
jobTitle          : Chief Police Sleuth
mail              : Popeye.Doyle@o365maestro.onmicrosoft.com
mobilePhone       : 1 405 1461511
officeLocation    : Manhatten
preferredLanguage :
surname           : Doyle
userPrincipalName : Popeye.Doyle@o365maestro.onmicrosoft.com
id                : 21a3ce8f-6d55-4e57-9210-d85a2f1618ec
department        : Detectives
city              : NYC
country           : United States
employeeId        : 1461431
employeeType      : Permanent
employeeHireDate  : 06/06/2023 23:00:00

The difference in output is obvious!

Filtering with User.ReadBasic.All

Before rushing to limit all applications, make sure that you understand what properties each application needs to process. Also, in MC704030, Microsoft notes that they fixed a bug that allowed apps to filter on properties that should have been blocked (like the employee hire date property). Attempts to filter against unauthorized properties now generate a 403 “insufficient privileges” error. This can cause unexpected results.

For example, the code below uses a reasonably common query to find user accounts with assigned licenses that are member of the tenant rather than guest accounts. After using certificate-based authentication to sign into the Microsoft Graph PowerShell SDK (to use application rather than delegated permissions), the script runs the Get-MgUser cmdlet, and the cmdlet fails due to insufficient privileges.

Connect-MgGraph -TenantId $TenantId -AppId $AppId -CertificateThumbprint $CertificateThumbprint

(Get-MgContext).Scopes
Group.Read.All
User.ReadBasic.All

[array]$Users = Get-MgUser -Filter "assignedLicenses/`$count ne 0 and userType eq 'Member'" -ConsistencyLevel eventual -CountVariable Records -All -Property Id, userprincipalname, displayname, mail, city, country, usagelocation, usertype, signinactivity | Sort-Object displayName
Get-MgUser_List: Insufficient privileges to complete the operation.

The result is exactly what is expected. The Graph declines to run the query because of the filters against the assignedLicenses and UserType properties, neither of which are in the restricted set allowed by User.ReadBasic.All.

Pause Before Using User.ReadBasic.All

Like any improvement made by Microsoft, care must be exercised about taking advantage of the improvement. As in this case, implementing a change with the best intentions might have unfortunate side effects like stopping applications working. That’s always a bad thing.


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/2024/01/30/user-readbasic-all-permission/feed/ 0 63521
Exchange Online Optimizes Online Address Book Lookups https://office365itpros.com/2024/01/26/get-mgdomainnamereference/?utm_source=rss&utm_medium=rss&utm_campaign=get-mgdomainnamereference https://office365itpros.com/2024/01/26/get-mgdomainnamereference/#respond Fri, 26 Jan 2024 01:00:00 +0000 https://office365itpros.com/?p=63233

Directory Lookups, the Address Book, and the Get-MgDomainNameReference Cmdlet

The news published in message center notification MC706449 (13 January 2024) is surprising only because people must still be accessing elements like the Global Address List online. This is surprising because I assume most people use Outlook in cached Exchange mode and take advantage of the Offline Address List (OAB). Access to the online address book is only necessary to find details of recipients added since Outlook last downloaded and applied an OAB update.

In any case, Microsoft wants people to stop browsing online address books and use search instead. They don’t want people doing what they call “infinite browsing,” which I assume means that users start scrolling through the address list to find interesting entries. Such activity causes the client to make multiple calls to fetch directory information.

Moving to a search-first posture makes sense and it’s the way things work with OWA and Outlook Monarch. Basically, Microsoft wants Outlook users to construct a search (like find people with “Tony” as their first name) and use the search to find matching entries. Microsoft says that they’ve improved search performance to ensure that users get fast results. In a further change, to encourage people to change habits, directory lookups against online address lists return only the first 500 entries, even if more exist.

Another tweak is that if you attempt to use a very broad search and more than 5,000 entries result, Outlook won’t show anything and you’ll be forced to narrow the search to see results. These changes have no effect on lookups against the OAB.

Finding Numbers of Directory Entries

Five hundred sounds like a lot of entries but the number is easily exceeded when you consider the number of mail-enabled objects that appear in address lists. Even though my tenant supports just 35 mailboxes, 490 mail-enabled objects are in the GAL:

[array]$MEObjects = Get-Recipient -ResultSize Unlimited -Filter {HiddenFromAddressListsEnabled -eq $False}
$MEObjects.count
490

$MeObjects | Group-Object RecipientTypeDetails -NoElement | Sort-Object Count -Descending | Format-Table Name, Count -AutoSize

Name                           Count
----                           -----
GroupMailbox                     174
GuestMailUser                    125
MailUniversalDistributionGroup    60
UserMailbox                       35
DynamicDistributionGroup          24
MailUser                          18
RoomMailbox                       17
MailUniversalSecurityGroup        12
SharedMailbox                     10
RoomList                           5
PublicFolder                       4
SchedulingMailbox                  4
EquipmentMailbox                   2

Fortunately, I use the OAB and search rather than browse to find entries, so MC706449 doesn’t affect me.

Issue with Domain Name References

Also related to the directory, last week, I discussed how to report issues to the Microsoft Graph PowerShell SDK development team. I suggested that browsing the reported issues is a good way to learn about how people use the SDK. Taking my own advice, I came to issue 2494, which discusses a problem with the Get-MgDomainNameReference cmdlet. The cmdlet is derived from the list domainNameReferences Graph API, which retrieves a list of directory objects referencing a specified registered domain belonging to a tenant. To see the valid domains for your domain, run the Get-MgDomain cmdlet:

Get-MgDomain | Format-Table Id, Isdefault

Id                                 IsDefault
--                                 ---------
microsoft365itpros.com                 False
office365itpros.com                    True
office365itpros.onmicrosoft.com        False
office365exchangebook.com              False
office365itproebook.com                False

For instance, if you ask for directory objects referencing office365itpros.com, Entra ID should retrieve a list of all user and group objects referencing the domain, such as in an object’s email address or user principal name. Figure 1 shows the Graph Explorer retrieving a list of office365itpros.com objects.

Using the Graph Explorer to access the domain name references API.

Get-MgDomainNameReference
Figure 1: Using the Graph Explorer to access the domain name references API

Here’s an example of the data returned for a user account:

Name                           Value
----                           -----
mail                           Ben.Owens@office365itpros.com
surname                        Owens
id                             a3eeaea5-409f-4b89-b039-1bb68276e97d
displayName                    Ben Owens (DCPG)
givenName                      Ben
jobTitle                       Chief bottle washer
businessPhones                 {}
officeLocation                 Moscow
@odata.type                    #microsoft.graph.user
userPrincipalName              Ben.Owens@office365itpros.com
preferredLanguage              en-US

The equivalent query can be run using the PowerShell Invoke-MgGraphRequest cmdlet:

$Uri = "https://graph.microsoft.com/v1.0/domains/office365itpros.com/domainNameReferences"

$Data = Invoke-MgGraphRequest -Uri $Uri -Method GET
Data

Name                           Value
----                           -----
@odata.context                 https://graph.microsoft.com/v1.0/$metadata#directoryObjects
value                          {a3eeaea5-409f-4b89-b039-1bb68276e97d, 96155a51-6885-4c8f-a8b6-e1614af08675, 67105a51-e…

What’s odd is that the query returns 300 items as a default and doesn’t include a nextlink pointer if further pages of data are available for retrieval:

$Items = $Data.Value
$items.count
300

Because the Get-MgDomainNameReference cmdlet is derived from the Graph query, it also returns 300 items, even if the All parameter is passed to instruct the cmdlet to retrieve all available pages:

[array]$DomainNames = Get-MgDomainNameReference -DomainId office365itpros.com -All
$DomainNames.count
300

You can increase the page size to retrieve up to 999 items, but that’s the limit. We can go no further because of the lack of a nextlink.

[array]$DomainNames = Get-MgDomainNameReference -DomainId office365itpros.com -PageSize 999
$DomainNames.count
424

SDK Cmdlets Depend on Underlying APIs

The same results occur in both the V1.0 and beta APIs. The original problem reported in issue 2494 was that the user accounts for shared mailboxes are not in the returned set. Perhaps the problem all along was the inability of the API to retrieve the complete set of available items? Who knows… Microsoft generates the SDK cmdlets from the underlying Graph APIs, so when a Graph API has a problem, it also shows up in the SDK.


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/2024/01/26/get-mgdomainnamereference/feed/ 0 63233
How to Update Tenant Corporate Branding for the Entra ID Sign-in Screen with PowerShell https://office365itpros.com/2024/01/25/corporate-branding-for-entra-id/?utm_source=rss&utm_medium=rss&utm_campaign=corporate-branding-for-entra-id https://office365itpros.com/2024/01/25/corporate-branding-for-entra-id/#comments Thu, 25 Jan 2024 01:00:00 +0000 https://office365itpros.com/?p=63398

Use Graph SDK Cmdlets to Apply Annual Updates to Corporate Branding for Entra ID Sign-in Screen

Back in 2020, I took the first opportunity to apply corporate branding to a Microsoft 365 tenant and added custom images to the Entra ID web sign-in process. Things have moved on and company branding has its own section in the Entra ID admin center with accompanying documentation. Figure 1 shows some custom branding elements (background screen, banner logo, and sign-in page text) in action.

Corporate branding applied to the Entra ID sign-in screen.

Corporate Branding for Entra ID.
Figure 1: Corporate branding applied to the Entra ID sign-in screen

Entra ID displays the custom elements after the initial generic sign-in screen when a user enters their user principal name (UPN). The UPN allows Entra ID to identify which tenant the account comes from and if any custom branding should be displayed.

Company branding is available to any tenant with Entra ID P1 or P2 licenses. The documentation mentions that Office 365 licenses are needed to customize branding for the Office apps. This mention is very non-specific. I assume it means Office 365 E3 and above enterprise tenants can customize branding to appear in the web Office apps. Certainly, no branding I have attempted has ever affected the desktop Office apps.

Scripting the Annual Branding Refresh

Every year, I like to refresh the custom branding elements, if only to update the sign-in text to display the correct year. It’s certainly easy to make the changes through the Entra ID admin center (Figure 2), but I like to do it with PowerShell because I can schedule an Azure Automation job to run at midnight on January 1 and have the site customized for the year.

Editing corporate branding settings in the Entra ID admin center.
Figure 2: Editing corporate branding settings in the Entra ID admin center

The Graph APIs include the organizational branding resource type to hold details of a tenant’s branding (either default or custom). Updating the properties of the organizational branding resource type requires the Organization.Rewrite.All permission. Properties are divided into string types (like the sign-in text) and stream types (like the background image).

The script/runbook executes the following steps:

  • Connects to the Graph using a managed identity.
  • Retrieves details of the current sign-in text using the Get-MgOrganizationBranding cmdlet.
  • Checks if the sign-in text has the current year. If not, update the sign-in text and run the Update-MgOrganizationBranding cmdlet to refresh the setting. The maximum size of the sign-in text is 1024 characters. The new sign-in text should be displayed within 15 minutes.
  • Checks if a new background image is available. The code below uses a location on a local disk to allow the script to run interactively. To allow the Azure Automation runbook to find the image, it must be stored in a network location like a web server. The background image should be sized 1920 x 1080 pixels and must be less than 300 KB. Entra ID refuses to upload larger files.
  • If a new image is available, update the branding configuration by running the Invoke-MgGraphRequest cmdlet. I’d like to use the Set-MgOrganizationBrandingLocalizationBackgroundImage cmdlet from the SDK, but it has many woes (issue #2541), not least the lack of a content type parameter to indicate the type of image being passed. A new background image takes longer to distribute across Microsoft’s network but should be available within an hour of the update.

Connect-MgGraph -Scopes Organization.ReadWrite.All -NoWelcome 
# If running in Azure Automation, use Connect-MgGraph -Scopes Organization.ReadWrite.All -NoWelcome -Identity

$TenantId = (Get-MgOrganization).Id
# Get current sign-in text
[string]$SignInText = (Get-MgOrganizationBranding -OrganizationId $TenantId -ErrorAction SilentlyContinue).SignInPageText 
If ($SignInText.Length -eq 0) {
   Write-Host "No branding information found - exiting" ; break
}
[string]$CurrentYear = Get-Date -format yyyy
$DefaultYearImage = "c:\temp\DefaultYearImage.jpg"
$YearPresent = $SignInText.IndexOf($CurrentYear)
If ($YearPresent -gt 0) {
    Write-Output ("Year found in sign in text is {0}. No update necessary" -f $CurrentYear)
} Else {
    Write-Output ("Updating copyright date for tenant to {0}" -f $CurrentYear )
    $YearPosition = $SignInText.IndexOf('202')
    $NewSIT = $SignInText.SubString(0, ($YearPosition)) + $CurrentYear
    # Create hash table for updated parameters
    $BrandingParams = @{}
    $BrandingParams.Add("signInPageText",$NewSIT)
    Update-MgOrganizationBranding -OrganizationId $TenantId -BodyParameter $BrandingParams
    If (Test-Path $DefaultYearImage) {
        Write-Output "Updating background image..."
        $Uri = ("https://graph.microsoft.com/v1.0/organization/{0}/branding/localizations/0/backgroundImage" -f $TenantId)
        Invoke-MgGraphRequest -Method PUT -Uri $Uri -InputFilePath $DefaultYearImage -ContentType "image/jpg"
    } Else {
        Write-Output "No new background image available to update"
    }
}

The script is available in GitHub.

Figure 2 shows the updated sign-in screen (I deliberately updated the year to 2025).

The refreshed corporate branding for the Entra ID sign-in screen.

Corporate branding Entra Id
Figure 3: The refreshed corporate branding for the Entra ID sign-in screen.

If you run the code in Azure Automation, the account must have the Microsoft.Graph.Authentication and Microsoft.Graph.Identity.DirectoryManagement modules loaded as resources in the automation account to use the cmdlets in the script.

Full Corporate Branding Possible

The documentation describes a bunch of other settings that can be tweaked to apply full custom branding to a tenant. Generally, I prefer to keep customization light to reduce ongoing maintenance, but I know that many organizations are strongly attached to corporate logos, colors, and so on.

Corporate Branding for Entra ID Isn’t Difficult

Applying customizations to the Entra ID sign-in screens is not complicated. Assuming you have some appropriate images to use, updating takes just a few minutes with the Entra ID admin center. I only resorted to PowerShell to process the annual update, but you could adopt it to have different sign-in screens for various holidays, company celebrations, and so on.


Learn about using Entra ID and the rest of the Microsoft 365 ecosystem 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/2024/01/25/corporate-branding-for-entra-id/feed/ 1 63398
Microsoft Encourages More Performant Membership Rules for Dynamic Groups https://office365itpros.com/2024/01/19/dynamic-group-rule-builder/?utm_source=rss&utm_medium=rss&utm_campaign=dynamic-group-rule-builder https://office365itpros.com/2024/01/19/dynamic-group-rule-builder/#comments Fri, 19 Jan 2024 01:00:00 +0000 https://office365itpros.com/?p=63327

Dynamic Group Rule Builder Blocks Contains Operators

It was interesting to read message center notification MC705357 (January 9, 2024) and learn that Microsoft implemented a change to the dynamic group rule builder GUI in both the Entra ID and Intune admin centers to “encourage performant dynamic group rules.” In other words, Microsoft detected that some of the membership rules created for dynamic groups are not as efficient as they might be.

In this instance, Microsoft removed the ability to use the ‘contains’ and ‘notContains’ operators from the dynamic group rule builder. The logic is that these operators are “less performant.” Microsoft says that rules containing the contains or notContains operators “should only be used when absolutely necessary.” The change is effective now.

Membership Rule Processing

Entra ID processes membership rules by querying its database to compute the set of members for dynamic groups. This processing happens in the background. Membership changes due to updated rules or the addition of new objects to process usually happen reasonably quickly, but as the number of dynamic groups (including those used by dynamic teams) plus dynamic administrative units grow, the resources consumed to update group memberships must be noticeable, even in an infrastructure like Microsoft 365.

If the unavailability of system resources slow the processing updates, inaccuracies grow in group memberships. Those inaccuracies might or might not affect users. For instance, administrators change the properties of an account to bring it within the scope of a membership rule for a Microsoft 365 group. The user can’t access group resources like documents in its SharePoint Online site or channel conversations until Entra ID processes the membership change.

No Effect on Existing Dynamic Groups

An important point to realize is that the change does not affect dynamic groups that have rules that use the “less performant” operators. Entra ID will continue to use these rules to process membership updates. The change only kicks in if you want to update the membership rule. At that point, you’ll discover that the admin center displays an error to say that some items could not be displayed in the rule builder. This is because of the presence of either the contains or notContains operator in the rule (Figure 1).

The Dynamic group rule builder processes a group membership rule that uses the contains operator.
Figure 1: A group membership rule that uses the contains operator

Use the Dynamic Group Rule Builder to Change Rules

It’s good that the change has no impact on existing groups, but what happens when you create new dynamic groups or need to change the membership rule for an existing group? Two options are available:

Edit the membership rule without using the rule builder. Click the Edit icon and compose the rule. Often this is the quickest and simplest way to proceed. As shown in Figure 1, the contains and notContains operators can be included in the rule. In this case, the rule finds any member or guest account that has the string “United” in the country property, so it finds accounts with a country property like “United States” and “United Kingdom.”

Remove the membership rule and replace it with another rule. To do this, edit the rule to remove it. When you exit the editor, the rule builder recognizes that the contains operators are not present and allows you to compose a new rule. In Figure 2, I’ve updated the rule to do an equals comparison against the expected strings. Another way of doing the same thing is to use the in operator to compare against a set of values. For example, (user.country -in [“United Status”,”United Kingdom”])

Changing the membership rule to use a different approach.
Figure 2: Changing the membership rule to use a different approach

It’s not always possible to change a rule that uses the contains operator to gain the same effect. In these situations, the only alternative is to edit the rule manually.

An Innocuous Change

Some might ask why Microsoft removed the ability to create a type of rule that still works. It’s clear that something provoked the decision, probably telemetry that identified a performance issue caused by these rules. It would have been much worse if Microsoft had stopped rules working and forced customers to update rules to a supported configuration. This change shouldn’t have much impact, once you understand the options.


Make sure that you’re not surprised about changes that appear inside Entra ID and Microsoft 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/2024/01/19/dynamic-group-rule-builder/feed/ 7 63327
How to Report Expiring Credentials for Entra ID Apps https://office365itpros.com/2024/01/16/app-credential-expiration/?utm_source=rss&utm_medium=rss&utm_campaign=app-credential-expiration https://office365itpros.com/2024/01/16/app-credential-expiration/#comments Tue, 16 Jan 2024 01:00:00 +0000 https://office365itpros.com/?p=63246

Use the Microsoft Graph to Report App Credential Expiration Dates

A reader asks if it’s possible to notify administrators when app secrets expire or are close to expiring. App secrets (also called client secrets) are a mechanism to allow Entra ID registered applications to prove their identity and secure an access token to allow them to retrieve data. App secrets last between three and twenty-four months are not intended for production use. Instead, app secrets are a good way to test an application to make sure that it can access the right data using the right permissions before moving it from development to production.

An app can use both secrets and certificates (Figure 1). Both methods work, but once an application reaches production, it should move away from app secrets and use certificate-based authentication or, if the app runs as an Azure Automation runbook, a managed identity.

An Entra ID registered app can use both secrets and certificates to authenticate.
Figure 1: An Entra ID registered app can use both secrets and certificates to authenticate

Keeping track of expiring app secrets is a good idea. Developers might not have access to the app to replace an expired secret and have to ask an administrator to create a new secret. And if an app does get through into production while using app secrets, the check might reveal its existence and prompt developers to use a more secure authentication method.

Certificates do have expiration dates and are more likely to be used for production than app secrets, so paying attention to certificate expiration can be extremely important. Finally, it’s good to remove expired credentials from apps because once a credential expires, it becomes unusable debris. Being proactive and reviewing apps periodically helps to identify issues before they occur, which is always a positive thing to do.

Creating a Script to Report App Credential Expiration

The steps involved in creating a script to find applications and report their app credential expiration dates are straightforward. Here’s an outline.

  • Connect to the Graph endpoint with the Application.Read.All permission. If you want to email the report, the script uses the Mail.Send permission.
  • Run the Get-MgApplication cmdlet to find the set of registered apps. I usually reduce the set by only processing single-organization apps (the sign-in audience is “AzureADMyOrg”) and by removing the apps used by the SharePoint Framework to make calls to Graph APIs. The resulting set are those created by developers for use within the tenant.
  • For each credential, find its expiration date and report if it is expired, active, or about to expire (within 30 days of expiration).
  • Email the report to tenant administrators.

The PowerShell is relatively simple. The only oddity I found in the data is that some app secrets have expiration dates of 31-Dec-2299, which seems just a tad long (Figure 2 shows the excessively long-lasting expiration dates in an emailed report). While I can’t remember this, apparently it was possible to set expiration for an app secret to be “never” in the past, which results in the 2299 date. The Entra ID admin center doesn’t allow a never-expiring app secret now, and certainly no secret created since March 2021 has such an expiration. Never-expiring app secrets might be convenient for development purposes, but if you don’t like them, you can easily fix the problem by removing the secret and replacing it with a secret configured with a more reasonable expiration date. Any apps that use the deleted secret must be updated to use the new secret.

Email to administrators with information about app credential expiration dates/
Figure 2: Email to administrators with information about app credential expiration dates

Points About Retrieving App Credential Expiration Dates

By default, the Get-MgApplication cmdlet does not retrieve details of app secrets and credentials when it retrieves information for a registered app. To get this data, you must include the keyCredentials (certificates) and passwordCredentials (app secrets) properties in the set requested when running the cmdlet.

Microsoft’s documentation doesn’t explain this, so working things out took a little while and some experimentation with the Graph Explorer. Even the Graph X-Ray extension, which gives an insight into the Graph commands used by the Entra ID admin center to process objects, comes up with a blank when exposing the credentials for an application, but I got there in the end.

Run Periodic Checks to Make Sure that App Credentials Don’t Expire

I like to run jobs that check settings on a periodic basis using scheduled Azure Automation runbooks. The code for the script (downloadable from GitHub) is easily adapted to access the Graph witha managed identity. One advantage of using a runbook is that the script can send email from any account, unless RBAC for Applications restricts access to certain mailboxes, which is probably how things should be configured.


Learn more about how the Microsoft 365 applications 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/2024/01/16/app-credential-expiration/feed/ 14 63246
Mastering Microsoft Graph PowerShell SDK Foibles https://office365itpros.com/2024/01/12/user-extension-attributes-sdk/?utm_source=rss&utm_medium=rss&utm_campaign=user-extension-attributes-sdk https://office365itpros.com/2024/01/12/user-extension-attributes-sdk/#respond Fri, 12 Jan 2024 01:00:00 +0000 https://office365itpros.com/?p=63200

Microsoft 365 Groups, Entra ID, and User Extension Attributes

Last year, I wrote about some of the foibles encountered by scripters as they work with the Microsoft Graph PowerShell SDK. At the time, we were waiting for V2 of the SDK, which duly arrived in July 2023. At the time of writing, the current version of the SDK is 2.11.1, meaning that updates appear frequently.

A foible that recently came to my attention is that the fifteen custom single-value attributes available for customers to populate for Exchange Online mailboxes are synchronized to Entra ID and available through the Get-MgUser cmdlet, but the same attributes are not available for Entra ID groups even though they exist in Exchange. The five multi-value custom attributes available for mail-enabled objects in Exchange do not synchronize with Entra ID.

Custom attributes allow organizations to store whatever data they like for mailboxes, groups, and other mail-enabled objects. Mailboxes are linked to user accounts and Entra ID synchronizes the 15 custom attributes to OnPremisesExtensionAttributes for Entra ID accounts. An Entra ID account does not have to be mailbox-enabled to use these attributes, but most are.

Figure 1 shows the extension attributes for my account as viewed through the Entra ID admin center. Extension attribute 9 is used to hold details of my favorite drink, which is then exposed to those who need to know by customizing the Microsoft 365 user profile card.

User extension attributes for an account shown in the Entra ID admin center.
Figure 1: User extension attributes shown in the Entra ID admin center

Another example of using custom attributes is to set an expiration date for guest accounts that can then be actioned by processes to detect and remove expired accounts.

Entra ID groups don’t currently support custom attributes and that’s where the problem lies. Given that Microsoft 365 groups and distribution lists support these attributes and show up as Entra ID groups, it seems like a gap exists in the connection between Exchange Online and Entra ID. The Graph APIs can’t make data appear where it not present.

I’ve asked Microsoft why groups don’t support custom attributes and discussions continue. Hopefully, Microsoft will close the gap in the future. In the meantime, if you need to work with custom attributes for groups, use the Exchange Online cmdlets.

Reporting Graph SDK Problems

I reported the problem with custom attributes for groups to Microsoft via the PowerShell GitHub repro. This is the right place to report issues and suggestions for the Microsoft Graph PowerShell SDK. The SDK development team monitors the issues that come in and will respond. Before you add a new issue, it’s worthwhile scanning the set of existing issues to see if someone else reported the same problem. Reading problems can also be a good way to learn how SDK cmdlets work and how people are using them to solve problems.

Understanding Graph Permissions

Apps, including the Microsoft Graph PowerShell SDK, need permissions (scopes) to access data via Graph APIs. It’s sometimes difficult to understand what permission is needed to do something, especially when contemplating interactive sessions (delegate permissions and administrative roles assigned to the signed-in account) versus other forms of use like certificate-based authentication, Azure Automation runbooks, and registered apps, all of which use application permissions. Administrative roles can also come into the frame too. The bottom line is that picking the right permissions – and the least-permissioned of those permissions -can take some effort.

This article covers how to use Graph SDK cmdlets like Find-MgGraphPermission to find the right permissions. Christian Rittler used Find-MgGraphPermission to create a useful function called Get-GraphScriptPermission that accepts a script block as input and parses the cmdlets in the script block to find the required permissions. The idea is that instead of checking individual cmdlets, you can check what permissions are needed for an entire script. For example, this code creates a script block containing SDK cmdlets to retrieve user accounts and check each account to find if a manager exists.

$Script = {
[array]$Users = Get-MgUser -All -Filter "userType eq 'Member'"
ForEach ($User in $Users) {
   $Manager = Get-MgUser -UserId $User.Id | Select-Object userPrincipalName, @{n="Manager";e={(Get-MgUserManager -UserId $_.Id).AdditionalProperties.userPrincipalName}}
   If ($Manager) {
      Write-Host ("User {0}'s manager is {1}" -f $User.displayName, $Manager.Manager)
   }
 }
}

To use the function, call it and pass the variable containing the script block. The output lists the cmdlets found and the permissions needed.

Get-GraphScriptPermission -Script $Script

Cmdlet : Get-MgUser
Source : Microsoft.Graph.Users
Verb   : Get
Type   : MgUser
Scopes : DeviceManagementApps.Read.All (admin: True), DeviceManagementApps.ReadWrite.All (admin: True),DeviceManagementConfiguration.Read.All (admin: False), DeviceManagementConfiguration.Read.All (admin: True), DeviceManagementConfiguration.ReadWrite.All (admin: True), DeviceManagementManagedDevices.Read.All (admin:False), DeviceManagementManagedDevices.ReadWrite.All (admin: False),DeviceManagementManagedDevices.ReadWrite.All (admin: True), DeviceManagementServiceConfig.Read.All (admin: False), DeviceManagementServiceConfig.Read.All (admin: True), DeviceManagementServiceConfig.ReadWrite.All (admin: False), DeviceManagementServiceConfig.ReadWrite.All (admin: True), Directory.Read.All (admin: False), Directory.ReadWrite.All (admin: False), User.Read (admin: False), User.Read.All (admin: False), User.Read.All (admin: True), User.ReadBasic.All (admin: False), User.ReadWrite (admin: False), User.ReadWrite.All (admin:False), User.ReadWrite.All (admin: True)

Cmdlet : Get-MgUserManager
Source : Microsoft.Graph.Users
Verb   : Get
Type   : MgUserManager
Scopes : Directory.Read.All (admin: True), Directory.ReadWrite.All (admin: True), User.Read.All (admin: True), User.ReadWrite.All (admin: True)

When a permission has admin: True, it means that the account running the code must hold a suitable administrative role to use the cmdlet. Many of the scopes listed for Get-MgUser can be used without an administrative role to allow users to retrieve details of their account, but an administrative role is needed to run Get-MgUserManager.

I amended the original function to generate scopes as strings rather than an array along with some other minor changes. You can download my version from GitHub, use the original, or create your own.

In the past, developers had to consult the documentation for the underlying Graph APIs to find details of required permissions. Microsoft has started to include this information in the documentation for the Graph SDK cmdlets, and that’s a welcome step forward.

SDK Improving Slowly

There’s no doubt that the Graph SDK is improving all the time, albeit slowly, especially with the retirement of the MSOL and Azure AD modules fast approaching (March 30, 2024). Perhaps this is familiarity talking and someone will less experience of dealing with SDK foibles, permissions, and missing features might not be quite so positive. But nothing is perfect (especially software). Upwards and onwards.

]]>
https://office365itpros.com/2024/01/12/user-extension-attributes-sdk/feed/ 0 63200
Managing Passwords for Entra ID Accounts with PowerShell https://office365itpros.com/2024/01/08/password-profiles/?utm_source=rss&utm_medium=rss&utm_campaign=password-profiles https://office365itpros.com/2024/01/08/password-profiles/#respond Mon, 08 Jan 2024 01:00:00 +0000 https://office365itpros.com/?p=63070

Using Password Profiles for Entra ID Accounts

Although passwordless authentication is in the future for many Entra ID accounts, the indications are that it will take time for Microsoft 365 tenants to get to the point where going passwordless is possible. The ongoing struggle to encourage tenants to adopt multifactor authentication (MFA) as the norm is one such indication. All of which means that tenant administrators will need to manage Entra ID account passwords for some time to come.

The Microsoft 365 admin center and Entra ID admin center both include facilities to reset user account passwords. The Entra ID option is effective but basic. As shown in Figure 1, Entra ID generates a temporary password and shows it to the administrator. The user must reset their password when they next sign in.

Resetting a user account password in the Entra ID admin center.

Password profiles.
Figure 1: Resetting a user account password in the Entra ID admin center

The Microsoft 365 admin center option is more flexible because the administrator can choose what password to set, whether the user must reset their password at first sign-in, and can have Microsoft 365 email the password to the administrator’s mailbox.

Nice as it is to have administrative GUIs for password management, automation through PowerShell is often more important for tenant operations. The Microsoft Graph PowerShell SDK contains capabilities to add passwords to new accounts or update passwords for existing accounts.

Generating User Account Passwords

To start, we need a password. Subject to the Entra ID password limitations, you can make up and assign any kind of password to an account. However, it’s better if the password is complex enough to provide protection until the account owner resets the password. There are many examples of password generators for PowerShell available. One thing to be aware of is that some code works for PowerShell 5 but not for PowerShell 7. For instance, the first of the three examples in this article doesn’t work when run on PowerShell 7. The other two examples do work and the last is a good basis to start with.

Adding a Password to a New User Account

To create a password for a new user account, we need a hash table to hold a “password profile.” A password profile is a Graph resource type representing password settings for an account. To create a random password, I generated it using the function described in the article mentioned above. In this case, the profile tells Entra ID the value to use to set the account password and to require the account to change the password the next time they sign in.

$NewPassword = Get-RandomPassword 8

$NewPasswordProfile = @{}
$NewPasswordProfile.Add("Password", $NewPassword)
$NewPasswordProfile.Add("ForceChangePasswordNextSignIn",$True)

The New-MgUser cmdlet takes the password profile as the value for the PasswordProfile parameter along with all the other parameters passed to create an account:

$NewUser = New-MgUser -UserPrincipalName "Ann.Conroy@office365itpros.com" `
  -DisplayName "Ann Conroy (GM Datacenters)" `
  -PasswordProfile $NewPasswordProfile -AccountEnabled `
  -MailNickName Ann.Conroy -City NYC `
  -CompanyName "Office 365 for IT Pros" -Country "United States" `
  -Department "IT Operations" -JobTitle "GM Datacenter Operations" `
  -BusinessPhones "+1 676 830 1201" -MobilePhone "+1 617 4466515" `
  -State "New York" -StreetAddress "1, Avenue of the Americas" `
  -Surname "Conroy" -GivenName "Ann" `
  -UsageLocation "US" -OfficeLocation "NYC" -PreferredLanguage 'en-US'

Because the ForceChangePasswordNextSignIn setting is true, the user can use the assigned password to sign in, whereupon Entra ID forces them to set a new password (Figure 2).

Password profile settings prompt a user to change their password.
Figure 2: A user is prompted to change their password

See this article for more information about creating new Entra ID accounts.

Updating a Password for a User Account

Updating a user account with a new password follows the same path. Create a password profile containing the parameters and run the Update-MgUser cmdlet to change the password. 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.

$PasswordProfile = @{}
$PasswordProfile.Add($NewPasswordProfile.Add("Password", $UpdatedPassword)
Update-MgUser -UserId $NewUser.Id -PasswordProfile $PasswordProfile

If you subsequently want a user to set up multifactor authentication (MFA) for their account, use a different password profile where the forceChangePasswordNextSignInWithMfa setting is $True. Don’t include a password value in the profile.

After updating the account, the next time the user attempts to sign in, Entra ID prompts them to configure an authentication method and then forces a password change. Here’s an example of a password profile to force an account to configure MFA:

$MFAResetProfile = @{}
$MFAResetProfile.Add("ForceChangePasswordNextSignIn",$true)
$MFAResetProfile.Add("ForceChangePasswordNextSignInWithMFA",$true)
Update-MgUser -UserId $UserId -PasswordProfile $MFAResetProfile

Disabling Password Expiration

Microsoft recommends that organizations do not force users to change passwords and that they disable the requirement to change passwords in the password expiration policy (accessed through the Security and Privacy tab of Org settings in the Microsoft 365 admin center). This setting applies to all user accounts. You can disable password expiration for an account as follows:

Update-MgUser -UserId Ann.Conroy@Office365itpros.com -PasswordPolicies DisablePasswordExpiration

Disabling password expiration isn’t something I would do without the additional protection afforded by MFA, especially for accounts holding administrative roles. Microsoft’s initiative to roll out managed conditional access policies to eligible tenants (those with Entra ID premium licenses) is yet another attempt to increase the percentage of accounts protected by MFA. Expect to see more efforts in this space as 2024 develops.


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/2024/01/08/password-profiles/feed/ 0 63070
Reporting Entra ID Admin Consent Requests https://office365itpros.com/2023/12/22/admin-consent-requests/?utm_source=rss&utm_medium=rss&utm_campaign=admin-consent-requests https://office365itpros.com/2023/12/22/admin-consent-requests/#comments Fri, 22 Dec 2023 01:00:00 +0000 https://office365itpros.com/?p=62930

Use PowerShell to Find and Report Details of Admin Consent Requests

Dinesh asked “How can I generate a report of Admin Consent Requests received by Entra ID? I’m specifically looking for information such as who sent the consent request, which application was involved, what API permissions the application requested, and how many users have already requested the consent.”

I was busy and didn’t pay too much attention to the question apart from offering some suggestions about using Fiddler (or even Graph X-Ray) to see what requests the Entra ID admin center generated. Like in many situations with Microsoft 365, the key to starting a PowerShell script is to find out what cmdlet to fetch information with.

In any case, I was delighted when Dinesh reported that he had found the necessary cmdlet (Get-MgIdentityGovernanceAppConsentRequest from the Microsoft Graph PowerShell SDK) to answer his question. It’s always great (and far too rare) when someone who asks a question goes ahead to do the necessary research to answer their own question.

Workflow for Admin Consent Requests

Administrator consent requests are an Entra ID workflow to allow users to request administrators to grant consent for enterprise applications that they want to use. You do not want end users to grant consent for applications to access data, but you also don’t want to get in the way of people doing real work. The answer is to enable the workflow to permit users to submit requests for administrator approval.

When the workflow is active, when users attempt to use an enterprise application with permissions that are not yet approved, Entra ID prompts the user to request approval. Figure 1 shows what happens when a user attempts to sign into the Microsoft Technical Community.

A user requests consent for permissions for an application.

Admin consent request.
Figure 1: A user requests consent for permissions for an application

The first time this happens in a tenant, the application attempts to create a service principal as its representation in the tenant. This cannot happen until consent is gained for the permissions it needs. In this case, the user cannot grant consent, so Entra ID routes the request to the users identified as approvers. Requests arrive via email (Figure 2). The user who generates the request also receives email notification that their request is under review.

Email notification to administrator seeking consent for application permissions.
Figure 2: Email notification to administrator seeking consent for application permissions

Oddly, the request email shows the alternative email address for the requestor instead of their primary SMTP address. This might be a glitch. In any case, when the reviewer opens the request in the Entra ID admin center, they see details of the application (Figure 3). To approve the request, they must sign in to see the requested permissions and proceed to give or refuse consent.

Reviewing a user request for consent for application permissions.
Figure 3: Reviewing a user request for consent for application permissions

The user who generates a request receives an email notification to tell them about the reviewer’s decision. Overall, it’s a simple but effective workflow.

The Code

Dinesh’s code works and is a good example of extracting and processing Entra ID information. I reworked it a little to add a check for high-profile permissions that should draw additional attention from administrators. These permissions include the ability to read everything from the directory, access all users, groups, sites, and so on. The data returned for consent requests includes some user details (user principal name and identifier). I added a call to Get-MgUser to retrieve other details that might be useful such as their department, job title, and country.

You can download the script from GitHub. Normal caveats apply – better error checking and formatting of the output would be beneficial. Nevertheless, the code proves the principles involved in using PowerShell to retrieve and process admin consent requests.

The Power of Community

I receive many requests for assistance, some of which are along the lines of “please write a script for me.” I ignore these requests because I am not in the business of doing work that other people should do for themselves. It’s always better when someone works out how to accomplish a task using their own brainpower, just like Dinesh did.


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/12/22/admin-consent-requests/feed/ 10 62930
Threat Actors Increase Misuse of OAuth Applications https://office365itpros.com/2023/12/15/oauth-apps-security/?utm_source=rss&utm_medium=rss&utm_campaign=oauth-apps-security https://office365itpros.com/2023/12/15/oauth-apps-security/#comments Fri, 15 Dec 2023 01:00:00 +0000 https://office365itpros.com/?p=62850

OAuth Apps Used to Automate Financially-Driven Attacks

The December 12, 2023 post for the Microsoft security blog covers how “Threat actors misuse OAuth applications to automate financially driven attacks.” The article describes how threat actors use OAuth applications planted in tenants to create virtual machines for crypto-mining, sending phishing email for business email compromise (BEC) attacks, and standard spamming. One of the targeted organizations ran up Azure compute fees of $1.5 million dollars for virtual machine usage.

Microsoft notes that most of the compromised accounts penetrated by attackers did not use multi-factor authentication (MFA). Enabling MFA is one thing. Making sure that the policies are enforced is another. The recent initiative to deploy Microsoft managed conditional access policies to “eligible tenants” might help, even if people still misunderstand and assume that all tenants receive these policies. Eligibility is determined by the presence of the necessary Entra ID P1 or P2 licenses in a tenant. If you don’t have at least Entra ID P1, you can’t use conditional access policies, and the Microsoft-managed policies won’t show up.

Compromised Accounts Create OAuth Apps

In all cases, attackers must compromise an account before they can create an OAuth app in the target tenant. The attacker’s task is easier if the compromised account has permissions, but even if the account is just a regular user account, it’s still useful because the attacker can use it to read directory information. To do this, the attacker must assign permissions to the app and seek consent to use those permissions to access data.

In some cases, tenants allow users to grant permission to apps. It’s best to configure the Entra ID user consent settings for a tenant to remove the ability of users to grant consent and either require administrator approval for all consents or to limit user consent for selected permissions to apps from verified publishers (Figure 1). In this context, the selected permissions are “low-impact,” meaning that they’re usually the permissions needed by users to access their own data but nothing else.

User consent settings for OAuth apps.
Figure 1: User consent settings for OAuth apps

Reviewing App Permissions with Microsoft Defender for Cloud Apps

Tools are available to review the OAuth apps in a tenant. Microsoft Defender for Cloud Apps (now part of Microsoft Defender XDR) offers the ability to review the settings and consented permissions for OAuth apps. It can be surprising to discover how many OAuth apps exist in a tenant. For instance, Figure 2 shows the settings for the Microsoft Tech Community app. When people join the Microsoft Tech Community, they sign in and consent to access. That consent allows the app to read their profile details and email address.

Settings for an OAuth app in Microsoft Defender for Cloud Apps.
Figure 2: Settings for an OAuth app in Microsoft Defender for Cloud Apps

Other apps do much the same thing, including apps used to register people for technical conferences like Microsoft Ignite.

Checking Consent Grants

Microsoft also recommends that administrators review consent grants within a tenant. If you allow users to grant consent for apps to receive some low-level permissions, administrators won’t know about these grants. However, they can check the audit log to find out when apps receive new permissions. I wrote about how to interrogate the audit log to find consent grant events a couple of years ago. Microsoft’s blog prompted me to go back and review the text.

The article contains a script that I’ve refreshed in two ways. First, I updated the use of the Search-UnifiedAuditLog cmdlet to accommodate the change Microsoft recently made to the way that the cmdlet works. That change was unannounced and can lead to situations where scripts that used to work perfectly well now do not retrieve all matching audit events. As Microsoft shows no appetite for reverting to previous behavior, it’s important to check scripts that use the Search-UnifiedAuditLog cmdlet to make sure that they work as expected.

Second, instead of simply returning an app identifier, the script now resolves the identifier into an app name. This can either be the name of a registered app (created in the tenant) or the service principal for an enterprise app. As you can see in Figure 3, it’s obviously easier to recognize an app name than it is to interpret the GUID returned for an app identifier!

Audit log events for permission consents to OAuth apps.
Figure 3: Audit log events for permission consents to OAuth apps

The number of permission grants to the Microsoft Graph Command Line Tools app is high. This is the service principal used to hold permissions granted for interactive sessions with the Microsoft Graph PowerShell SDK. Over time, this service principal can accrue many permissions and it’s wise to keep an eye on this aspect and remove permissions (or reset the service principal) if necessary. Also consider securing access to the Graph SDK so that only selected users can run interactive sessions.

You can download the updated script from GitHub.

Attempts to Compromise Accounts Continue

Threat isn’t going to go away. Blocking basic authentication for Exchange Online connection protocols removed some tried-and-trusted methods for attackers to compromise user accounts. Attackers simply changed gear and try other methods to compromise accounts and they will keep on trying until they get in. That’s why it’s so important to use multi-factor authentication with a strong authentication method (like the Microsoft authenticator app or FIDO2 key) to stop attacks. But it’s also important to verify afterwards and make sure that no sneaky OAuth app appears in your tenant.


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/12/15/oauth-apps-security/feed/ 2 62850
Entra ID Improves Registered App Security https://office365itpros.com/2023/12/11/app-instance-property-lock/?utm_source=rss&utm_medium=rss&utm_campaign=app-instance-property-lock https://office365itpros.com/2023/12/11/app-instance-property-lock/#comments Mon, 11 Dec 2023 01:00:00 +0000 https://office365itpros.com/?p=62783

Changes to App Instance Property Lock and Sign-In Audience

In March 2023, I wrote about a preview feature that allows application developers to lock the properties of service principal objects using the app instance property lock. That feature is now embedded in Entra ID and according to a recent “what’s new in Entra ID” post in the Microsoft Technical Community, “starting March 2024, new applications created using (the) Microsoft Graph application API will have “App instance lock” enabled by default.”

The same post also says that the default sign-in audience for new Entra ID apps will be “AzureADMyOrg” (just the owning tenant) rather than “AzureADandPersonalMicrosoftAccount.” That’s a good idea because most Entra ID apps are created for exclusive use within a tenant.

Both changes are intended to reduce the potential attack surface exposed through Entra ID apps. The first limits what administrators can do to service principals created for enterprise apps in their tenant and closes a hole exploited by attackers in the past. The second makes it more likely that app creators will opt to restrict access to their apps to the owning tenant. Given the number of apps that exist in Microsoft 365 tenants, both are welcome changes.

Locking App Properties

Only the app developer can choose to use the app instance property lock. This decision typically made by developers of multi-tenant enterprise applications of the type distributed by Microsoft, Adobe, and other software vendors. Entra ID creates a service principal within the tenant where the app runs to hold permissions assigned by the host tenant. The service principal inherits properties from the enterprise app, but if the app instance lock is not in force, the credentials used by the app can be changed using Graph API requests or Microsoft Graph PowerShell SDK cmdlets. If an attacker gains access to a tenant, they could therefore create credentials to allow them to use the app and the permissions assigned to the app. These permissions could allow extensive access to user data, such as all sites, all accounts, all mailboxes, and so on.

Tenants can set the app instance property lock for their own apps. New apps created using the Entra ID admin center set the app instance property lock by default for all supported properties, but older apps probably don’t have the lock enabled. I’m not sure when Entra ID changed the default behavior, but the apps created in my tenant prior to September 2023 do not have the lock enabled. You can update an app by selecting its Authentication properties and then App Instance Property Lock (Figure 1).

Updating the app instance property lock for a registered Entra ID app.
Figure 1: Updating the app instance property lock for a registered Entra ID app

Some apps that show up in a tenant’s app registration list are not created by the tenant. For instance, two apps called SharePoint Online Client Extensibility Web Application Principal and SharePoint Online Client Extensibility Web Application Principal Helper are created automatically for use with the SharePoint Framework to access Microsoft Graph and third-party APIs. It’s unclear why Microsoft doesn’t use a multi-tenant enterprise app instead.

Updating the App Instance Property Lock

Given that new apps have the app instance property lock set, it’s probably a good idea (and can do no harm) to update existing apps to set the lock. This is easily done with the Microsoft Graph PowerShell SDK by:

  • Run Get-MgApplication to find the set of apps.
  • Check each app to see if the lock is set.
  • If not, call Update-MgApplication to set the lock.

Here’s some example code to illustrate the principal:

ForEach ($App in $Apps) {
  $ServiceLock = $App | Select-Object -ExpandProperty ServicePrincipalLockConfiguration
  Write-Host ("Now processing {0}" -f $App.displayName)
  If ($ServiceLock.IsEnabled -eq $True) {
    Write-Host ("The {0} app is already enabled" -f $App.displayName) -ForegroundColor Red
  } Else {
    Write-Host ("App Instance Property Lock Not enabled for {0}; updating app" -f $App.displayName)
    Update-MgApplication -ApplicationId $App.Id -ServicePrincipalLockConfiguration $AppInstanceLockConfiguration
}

You can download the full script from GitHub. The script includes some setup that’s necessary such as signing into the Graph SDK with the necessary permission and creating a hash table containing the parameters for use by Update-MgApplication. The script also generates a report about the apps it updates.

Maintain Your Apps

The changes Microsoft is making is a good reminder that it’s important to keep an eye on the apps registered in a tenant to ensure their security and that they have appropriate credentials and permissions, and to remove unrequired apps. I know I could do a better job of app maintenance, but at least the app instance property lock is set for all apps now.


Keep up to date with developments in Entra ID 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/2023/12/11/app-instance-property-lock/feed/ 1 62783
Entra ID Captures Timestamp for Last Successful Sign In for User Accounts https://office365itpros.com/2023/12/08/lastsuccessfulsignindatetime/?utm_source=rss&utm_medium=rss&utm_campaign=lastsuccessfulsignindatetime https://office365itpros.com/2023/12/08/lastsuccessfulsignindatetime/#comments Fri, 08 Dec 2023 01:00:00 +0000 https://office365itpros.com/?p=62759

Big Difference Between Last Sign in and Last Successful Sign In

Yesterday, I saw a tweet from Entra ID program manager Merill Ferando announcing that the Graph signInActivity resource type (beta) now supports the lastSuccessfulSignInDateTime property. This is good news because it makes it much easier to find out when a user last successfully accessed a tenant. Being forced to filter the Entra ID sign-in logs to separate out bad attempts to sign-in from successful attempts has long been a frustration for administrators (here’s an example).

Using the LastSignInDateTime Property

Until now, the signInActivity resource supported the LastSignInDateTime property. The property is useful when reviewing account activity. For instance, this PowerShell snippet finds user accounts with a sign-in in the last 14 days.

[array]$Users = Get-MgUser -Filter "signInActivity/lastSignInDateTime ge $([datetime]::UtcNow.AddDays(-14).ToString("s"))Z" -All `
-Property displayname, Id, userPrincipalName, SignInActivity, userType | `
Sort-Object DisplayName | Select-Object @{n="Last Sign in";e={$_.SignInActivity.lastSignInDateTime}}, DisplayName, Id, UserPrincipalName, UserType
[array]$TenantUsers = $Users | Where-Object {$_.UserType -eq "Member"} | Sort-Object {$_.'Last Signin' -as [datetime] } -Descending
$TenantUsers | Format-Table 'Last Sign in', DisplayName, UserPrincipalName

Last Sign in         DisplayName                      UserPrincipalName
-----------          -----------                      -----------------
06/12/2023 13:03:57  Lotte Vetler                     Lotte.Vetler@office365itpros.com
06/12/2023 13:01:22  Chris Bishop                     Chris.Bishop@office365itpros.com
04/12/2023 22:04:43  Rene Artois                      Rene.Artois@office365itpros.com

More developed examples include using the lastSignInDateTime property to find underused accounts, or reporting the timestamp when assessing if guest accounts are in active use.

The difference between lastSignInDateTime and lastSuccessfulSignInDateTime property is:

  • lastSignInDateTime is the timestamp for the last interactive sign-in for a user account. An attempted sign-in might be unsuccessful (for example, a multi-factor authentication challenge fails), but Entra ID still updates the timestamp.
  • lastSuccessfulSignInDateTime is the timestamp for the last successful sign-in (interactive or non-interactive) for a user account.

Taking the example above, some of the timestamps reported might not represent successful sign ins, and that’s the issue the new property aims to address.

Caveats for LastSuccessfulSignInDateTime

Before we all get excited, some caveats exist:

  • Tenants need Entra ID P1 licenses to access sign-in reports via the Graph. If you attempt to run the example code described here and the tenant doesn’t have an Entra ID P1 license, you’ll see a “Neither tenant is B2C or tenant doesn’t have premium license” error. Microsoft’s documentation is unclear about whether the account used needs a license or the existence of Entra ID P1 in the tenant is sufficient. To be sure, use a licensed account.
  • The last successful sign in timestamp is currently available only through the beta endpoint. There’s no indication when it might be available through the V1.0 API endpoint. Some tenants have restrictions governing code written against the beta endpoint.
  • The Get-MgBetaUser cmdlet in the Microsoft Graph PowerShell SDK supports the last successful timestamp using SDK V2.11.1 or later.

$User = Get-MgBetaUser -Userid aff4cd58-1bb8-4899-94de-795f656b4a18 -Property SigninActivity

$User.signinactivity | Select-Object Last*

LastNonInteractiveSignInDateTime  : 15/12/2023 19:08:20
LastNonInteractiveSignInRequestId : c8c27d68-1a8f-4b33-a04d-4439404f1500
LastSignInDateTime                : 15/12/2023 14:46:43
LastSignInRequestId               : 1ebe266d-c3cd-479b-b7e6-abc0be5ace00
LastSuccessfulSignInDateTime      : 15/12/2023 19:08:20
LastSuccessfulSignInRequestId     : c8c27d68-1a8f-4b33-a04d-4439404f1500

Microsoft’s documentation says that from December 1, 2023, Entra ID captures the lastSuccessfulSignInDateTime property for user accounts. However, I see the property populated for accounts from mid-November. The difference can be accounted for by the time required to deploy changes across all Microsoft 365 tenants.

Population of the lastSuccessfulSignInDateTime property is not retrospective, so the only values available are from December 1, 2023. Currently, the property is available only through the beta API. Access to sign-in activity logs requires Entra ID P1 licenses.

Testing the LastSuccessfulSignInDateTime Property

There’s nothing like writing a PowerShell script to exercise a new property. I wrote a script (downloadable from GitHub) to find user accounts with licenses and report the lastSuccessfulSignInDateTime and lastSignInDateTime properties for each account. The script also computes the number of days since a last successful sign in and last sign in. As you can see from Figure 1, a difference does exist between the two properties.

Differences between the lastSuccessfulSignInDateTime and lastSignInDateTime properties.
Figure 1: Differences between the lastSuccessfulSignInDateTime and lastSignInDateTime properties

As noted above, the new property is only available through the beta endpoint. If this causes you a problem, you’ll have to wait for Microsoft to apply the necessary magic to upgrade the signInActivity resource type in the V1.0 endpoint. If not, consider reviewing scripts that perform activity date checks for user and guest accounts to figure out if reporting successful sign-in actions makes a difference to the accuracy of the script output.


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/12/08/lastsuccessfulsignindatetime/feed/ 11 62759
Exclude Breakglass Accounts from Conditional Access Policies with PowerShell https://office365itpros.com/2023/12/07/conditional-access-policies-break/?utm_source=rss&utm_medium=rss&utm_campaign=conditional-access-policies-break https://office365itpros.com/2023/12/07/conditional-access-policies-break/#comments Thu, 07 Dec 2023 01:00:00 +0000 https://office365itpros.com/?p=62735

Check Conditional Access Policies and Add Breakglass Accounts if Necessary

Breakglass accounts (or as Microsoft calls them, “emergency access accounts”) are intended for emergency use, such as when other administrative accounts are compromised or are locked out. Conditional access policies control inbound connection attempts and can lock everyone out if misconfigured. That’s why most experienced administrators make sure to exclude breakglass accounts from conditional access processing. Excluding the breakglass accounts means that Entra ID never imposes conditional access control on their connections. In effect, it guarantees access through breakglass accounts when all others fail. Well, if you remember the password for the breakglass accounts…

Best Laid Plans and Conditional Access Policy Exclusions

The best laid plans of mice and men often come undone and someone fails to insert the necessary exclusions into a conditional access policy. Given Microsoft’s ongoing focus on moving tenants to conditional access to enforce multi-factor authentication, the risk of being locked out due to a bad policy setting is obvious.

Automation through PowerShell offers a solution. The processing is simple:

  • Find all conditional access policies in the tenant.
  • Check if the necessary exclusions exist.
  • If not, and the policy is active, add the exclusions and update the policy.

Alternatively, you could update all policies with a missing exclusion even if they are disabled or in report only mode.

Exclusions can be declared as individual user accounts or groups. In this scenario, something like a security group is overkill. The set of breakglass accounts should be limited to as few as possible and they don’t change over time unless necessary following the use of an account for emergency access to a tenant. In other circumstances, a group is a good way to exclude a set of user accounts from a conditional access policy.

Using the Microsoft Graph PowerShell SDK to Work with Conditional Access Policies

A script to check and update conditional access policies can use Graph API requests or cmdlets from the Microsoft Graph PowerShell SDK. This example uses the SDK. First, connect to the Graph endpoint with the necessary permissions:

Connect-MgGraph -NoWelcome -Scopes Policy.ReadWrite.ConditionalAccess

The next step is to declare the breakglass accounts. I do this by including the object identifiers for the accounts in a simple array. I also declare the same values in a structure to pass to the Update-MgIdentityConditionalAccessPolicy cmdlet to update user account exclusions in a conditional access policy. The structure is a PowerShell representation of the body posted to the underlying Graph API request. If you want to use a group, the parameters will include the object identifier of the group in the excludeGroups section of the structure.

[array]$BreakGlassUsers = "91813a30-f048-48f1-a0f2-fd7c72020515", "b7289bc7-7e4e-44e2-ae1b-7e13e94e3749"
$Parameters = @{
    Conditions = @{
        users = @{  
            excludeUsers = @(
                "91813a30-f048-48f1-a0f2-fd7c72020515"
                "b7289bc7-7e4e-44e2-ae1b-7e13e94e3749"
            )
        }
    }
}

With everything prepared, the script runs the Get-MgIdentityConditionalAccessPolicy cmdlet to find the set of conditional access policies before looping through each policy to check the exclusions. If the breakglass accounts are not present and the policy is active, the script runs the Update-MgIdentityConditionalAccessPolicy cmdlet to add the exclusions.

[array]$Policies = Get-MgIdentityConditionalAccessPolicy | Sort-Object DisplayName
ForEach ($Policy in $Policies) {
    Write-Host ("Checking conditional access policy {0}" -f $Policy.displayName)
    [array]$ExcludedUsers = $Policy.conditions.users.excludeUsers
    ForEach ($User in $BreakGlassUsers) {
        If ($User -notin $ExcludedUsers) {
           Write-Host ("Can't find user {0} in CA policy {1}" -f (Get-MgUser -UserId $User).DisplayName, $Policy.DisplayName)
           If ($Policy.State -eq 'enabled') {
              Write-Host "Policy is enabled so updating it with break glass accounts" -ForegroundColor Red
              Update-MgIdentityConditionalAccessPolicy -BodyParameter $Parameters -ConditionalAccessPolicyId $Policy.Id
           }
        }
    }
}

If you use a group instead of user accounts, the check should be against $Policy.conditions.users.excludeGroups. Figure 1 shows the script in action. This kind of check to make sure that everything’s OK is a classic example of something that should run on a scheduled basis, preferably using Azure Automation rather than Windows Scheduler.

The script runs to update exclusions for conditional access policies.
Figure 1: The script runs to update exclusions for conditional access policies

You can download the script from GitHub.

No Excuse for Running into Conditional Access Problems

With so much experience about configuring and using conditional access policies in production plus tools like ID PowerToys to document policy settings, lack of knowledge is no excuse for misconfiguring policies. But life is hard sometimes and we all make mistakes, and that’s why it’s good to automate checks to make sure that anticipated backstops work when needed.


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/12/07/conditional-access-policies-break/feed/ 1 62735
Reasons to Pause Membership Processing for Entra ID Dynamic Groups https://office365itpros.com/2023/11/23/pause-membership-processing-2/?utm_source=rss&utm_medium=rss&utm_campaign=pause-membership-processing-2 https://office365itpros.com/2023/11/23/pause-membership-processing-2/#respond Thu, 23 Nov 2023 01:00:00 +0000 https://office365itpros.com/?p=62560

Pause Membership Processing to Prevent Inconsistent Changes

A year ago, I wrote about the newly-introduced ability to pause membership processing for Entra ID (then Azure AD) dynamic groups. At the time, I noted that Microsoft had not announced the change. Pausing membership processing is now documented in the page covering the creation of dynamic groups. There we learn that accounts holding the global administrator, group administrator, user administrator, or Intune administrator roles can pause and resume dynamic group processing. However, nothing is said about when it’s a good idea to pause membership processing for dynamic groups.

Usually, Entra ID processes the membership rules for dynamic groups to update membership when service demand allows. The longest that changes for the membership of a dynamic group should remain unprocessed is 24 hours, and Microsoft sets that expectation when viewing group properties in the Entra ID admin center (Figure 1).

Properties of an Entra ID dynamic group

Pause membership processing
Figure 1: Properties of an Entra ID dynamic group

In my experience, membership updates happen much faster. This theory is easily tested by making some changes to user accounts that affect the membership of a dynamic group and seeing how long Entra ID takes to process the changes. The properties of a dynamic group tells you when that processing occurred, but the best test is to check the membership to make sure that the changes are reflected in the set of members.

If Entra ID is unable to process membership changes within 24 hours, it flags the problem on top of the All Groups section of the Entra ID admin center, saying that “Dynamic group memberships have not been updated due to system delays.”

When to Pause Membership Processing

Returning to the original point, when should administrators pause membership processing for dynamic groups? The simple answer is “when the directory is in a state of ongoing change.”

Constant change is the nature of a directory like Entra ID. “Ongoing change” means that some form of event happens to cause extensive change affecting many user accounts. Change of this nature can cause a higher processing load for Entra ID to process the signals it receives about account updates.

Often changes to user accounts affect the properties most commonly used to build membership rules for dynamic groups, like department, job title, office location, or tenant-specific values stored in the fifteen custom (“extension”) attributes. Examples include:

  • Corporate restructuring: This includes department splits and merges, or name changes.
  • HR changes: Changes to properties such as job code and titles that might be used by membership rules.
  • Merger and acquisition: Large numbers of user accounts might join or leave an organization.
  • Office relocation: A new office is opened or one is closed, causing the physical location of people to change.

Usually, it’s possible to apply changes to Entra ID user accounts quickly, especially if scripted with PowerShell. However, the involvement of other systems that produce feeds into the directory might slow things down. In this situation, it might be wise to pause membership processing for dynamic groups until the directory stabilizes.

Pausing Membership Processing

It’s easy to implement a general pause for membership processing for all dynamic groups with a few lines of PowerShell. This code finds all dynamic groups and pauses membership processing for each group.

[array]$Groups = Get-MgGroup -Filter "groupTypes/any(c:c eq 'DynamicMembership')" -All | Sort-Object DisplayName
[int]$i = 0
ForEach ($Group in $Groups) {
  $i++
  Write-Host ("Pausing membership processing for group {0} ({1}/{2})" -f $Group.displayName, $i, $Groups.count)
  Update-MgGroup -GroupId $Group.Id -MembershipRuleProcessingState Paused
}

To reverse the process, run the same loop through the set of dynamic groups but this time set the MembershipRuleProcessingState parameter to On.

Update-MgGroup -GroupId $Group.Id -MembershipRuleProcessingState On

A side effect of pausing and restarting membership processing is that Entra ID resets the last update timestamp to 1/1/0001 (see this article). The next time Entra ID processes membership rules for a dynamic group, it stamps the group with a current timestamp.

No Need to Pause Membership Processing in Normal Circumstances

Entra ID copes easily with the normal load generated by day-to-day membership rule evaluation for dynamic groups. There is no need to pause membership processing in normal circumstances. However, if extensive changes are planned for user accounts, it’s wise to consider pausing membership updates until the directory settles down and everyone has time to think.


Learn about using Entra ID 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/11/23/pause-membership-processing-2/feed/ 0 62560
Reporting User and Group Assignments for Enterprise Applications https://office365itpros.com/2023/11/22/enterprise-app-assignments/?utm_source=rss&utm_medium=rss&utm_campaign=enterprise-app-assignments https://office365itpros.com/2023/11/22/enterprise-app-assignments/#respond Wed, 22 Nov 2023 01:00:00 +0000 https://office365itpros.com/?p=62501

How to Find and Document Assignments for Entra ID Enterprise Applications

A reader asked:

I am trying to execute Microsoft Graph that it can grab all my Enterprise Applications in my tenancy and export to CSV the application name and user and groups assigned to the groups.”

There’s a couple of things to unpack here before discussing potential answers. First, enterprise applications are Entra ID registered applications. Companies like Microsoft or Apple create enterprise applications for use in multiple tenants. For example, if you signed up to attend a Microsoft conference using your Entra ID credentials, the process is handled by an enterprise app called Microsoft Events. The home tenant identifier registered for the app is 72f988bf-86f1-41af-91ab-2d7cd011db47, which this site tells us is the identifier for Microsoft’s tenant.

Often enterprise applications act as an entry point to a service. For example, the properties of the IdPowerToys app (Figure 1) contain a link to the site where the service runs to document conditional access policies in PowerPoint.

Enterprise app registration for the idPowerToys app
Figure 1: Enterprise app registration for the idPowerToys app

Service Principals

When an enterprise application is used within a tenant, Entra ID creates a service principal to hold the permissions and assignments for the application within that tenant. If you want, the service principal is the instantiation of the application within the tenant that holds permissions and other information for the application. Other objects, like Azure Automation accounts also have service principals used to hold permissions and roles, such as those needed to access user data via Graph APIs.

By default, enterprise applications are accessible by all users. To control access, administrators can update application properties to require assignment. This means that Entra ID will only issue an access token for the application to users and groups granted access through assignment. It is the way to lock down access to enterprise applications.

Finding Enterprise Applications

To answer the question, we must find the set of enterprise applications in the tenant that are homed in other tenants. The way to do this is to run the Get-MgServicePrincipal cmdlet from the Microsoft Graph PowerShell SDK. Two steps are necessary. First, find the service principals known in the tenant. Second, filter the set to extract those with a tenant identifier that is not the same as your tenant:

[array]$ServicePrincipals = Get-MgServicePrincipal -All
[array]$EnterpriseApps = $ServicePrincipals | Where-Object {$_.AppOwnerOrganizationId -ne $TenantId} | Sort-Object DisplayName

The filter shown above creates a set of enterprise apps. If you want to further refine the filter to only find apps where role assignment is required, change it to:

 [array]$EnterpriseApps = $ServicePrincipals | Where-Object {$_.AppOwnerOrganizationId -ne $TenantId -and $_.AppRoleAssignmentRequired -eq $True} | `
        Sort-Object DisplayName

The next step is to loop through the set of apps and run the Get-MgServicePrincipalAppRoleAssignedTo cmdlet to check if any assignments exist. If any do, it’s easy to grab the details for a report.

ForEach ($App in $EnterpriseApps) {
    [array]$Assignments = Get-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $App.Id | Where-Object {$_.PrincipalType -ne 'ServicePrincipal'}
    If ($Assignments) {
        $i++
        Write-Host ("Found assignments for {0}" -f $App.DisplayName)
        ForEach ($Assignment in $Assignments) {
            $ReportLine = [PSCustomObject]@{
                TimeStamp   = $Assignment.CreatedDateTime  
                Id          = $Assignment.Id
                DisplayName = $Assignment.PrincipalDisplayName 
                UserId      = $Assignment.PrincipalId
                Type        = $Assignment.PrincipalType
                Resource    = $Assignment.ResourceDisplayName
                ResourceId  = $Assignment.ResourceId
            }
            $Report.Add($ReportLine)
        }
    }

Note the filter used with the Get-MgServicePrincipalAppRoleAssignedTo cmdlet. This removes assignments to service principals such as those used to hold permissions for Azure Automation accounts. Here’s an example of an assignment to an Azure Automation account to allow it to act like an account holding the Exchange Administrator role.

TimeStamp   : 28/01/2022 15:47:35
Id          : ag5Go0LJzUWdGNo2BTCsaYJIbAAI79JLkTVN2fzhjh0
DisplayName : ExoAutomationAccount_Y6LgjDYIfPnxmFzrqdbaClsnTD/gN4BNnVMywiju5hk=
UserId      : a3460e6a-c942-45cd-9d18-da360530ac69
Type        : ServicePrincipal
Resource    : Office 365 Exchange Online
ResourceId  : dacf6086-a190-467a-aadd-d519472b8d1d

You can download the script I used from GitHub.

The Output

After filtering, what remains are the app assignments to users and groups, the details of which the script captures and reports. Figure 2 shows an example of the output.

Enterprise apps and user/group assignments
Figure 1: Enterprise apps and user/group assignments

My name features heavily in the list because I installed many of the apps in my tenant. Some of the apps and associated assignments are quite old, a fact that underlines the need to review and remove unused or obsolete apps periodically. The duplicate entries for the Graph Explorer is due to an assignment captured when the app was first installed followed by an explicit assignment to prevent access to the app to anyone but my account.

None of this is particularly difficult to do. The trick, as is often the case with Microsoft 365, is to know where to start looking. And perhaps some luck when navigating through the documentation!


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/11/22/enterprise-app-assignments/feed/ 0 62501
Microsoft-Managed Conditional Access Policies Coming to Eligible Tenants https://office365itpros.com/2023/11/13/conditional-access-policy-msft/?utm_source=rss&utm_medium=rss&utm_campaign=conditional-access-policy-msft https://office365itpros.com/2023/11/13/conditional-access-policy-msft/#comments Mon, 13 Nov 2023 01:00:00 +0000 https://office365itpros.com/?p=62396

Increase MFA Usage with a Conditional Access Policy

Updated: February 6, 2024

On November 6, Alex Weinert, Microsoft’s VP for Identity Security, announced the “auto-rollout of Microsoft Entra Conditional Access policies that will automatically protect tenants based on risk signals, licensing, and usage.” The text explains that Microsoft will deploy up to three conditional access policies to “eligible tenants” (those with Entra ID P1 licenses to allow them to use conditional access policies). The Microsoft-managed conditional access policies require account sign-ins to use multi-factor authentication (MFA) to access specific forms of data, such as Microsoft 365 admin centers.

Update: Microsoft’s roll-out continues. Read this post to find out more.

Microsoft says that their “data tells us they [the policies] would increase an organization’s security posture.” Microsoft also points to a May 2023 study by Cornell University that finds MFA reduces the risk of account compromise by 99.22%. This is broadly in line with previous assertions about the effectiveness of MFA in stopping password spray and other attacks.

The aim of the initiative is to increase the overall usage of MFA across Microsoft from the poor levels reported over the last few years. At the TEC 2022 conference, Alex Weinert reported the figure to be 26.18% for all Microsoft 365 accounts and 34.15% for accounts holding an administrative role. Since then, Microsoft has rolled out new features to drive MFA usage and improve security, such as hardening the authenticator app, including authenticator lite in Outlook mobile, and pushing registration campaigns to encourage users to move from insecure MFA response methods to the authenticator app.

New Conditional Access Policies Deployed to Tenants

Initially, Microsoft will deploy three conditional access policies to tenants, who’ll receive a notification when the policies are present. A 90-day countdown starts after which Microsoft will automatically enable the policies. During this period, administrators can go to the Entra ID admin center (Figure 1) to review the policy settings and decide whether to tweak the policy settings.

If your tenant is eligible, Microsoft-managed conditional access policies will show up here soon

Conditional access policy
Figure 1: If your tenant is eligible, Microsoft-managed conditional access policies will show up here soon

For instance, Microsoft recommends that you exclude break glass accounts from the set of users covered by the policies to avoid encountering access problems if you need to use the break glass accounts.

Initially, the Microsoft-managed policies are in the report-only state. If administrators leave the policies alone, Microsoft will automatically enable the policies after the 90-day countdown lapses. If you don’t want Microsoft to do this, set the policy to Off. The first order of business is therefore to keep an eye on notifications posted by Microsoft and then review whatever policies appear in your tenant. Of course, there’s nothing to stop you from putting these policies into operation immediately.

Microsoft-Managed Conditional Access Policies

Table 1 lists the three initial Microsoft-managed policies. You can see that the policies focus on tenants with Microsoft Entra ID Premium licenses. That’s because these licenses are necessary to manage conditional access policies. Entra ID Premium P1 is included the Microsoft 365 E3 and Microsoft 365 Business Standard products. Entra ID Premium P2 is included in Microsoft 365 E5.

Conditional access policyEligible tenantsWhat the policy does
Require multifactor authentication for admin portalsTenants with Entra ID Premium P1 and P2 licenses where security defaults aren’t enabled.Requires MFA when an account holding any of 14 designated administrative roles signs into a Microsoft administrator portal (like the Entra ID admin center or Microsoft 365 admin center). See this article for more information about why this policy is very useful.
Require multifactor authentication for per-user multifactor authentication usersTenants with Entra ID Premium P1 and P2 licenses where security defaults aren’t enabled and there are less than 500 per-user MFA enabled/enforced users.Requires MFA for all cloud apps.
Require multifactor authentication for high-risk sign-insTenants with Entra ID Premium P2 licenses where there are enough P2 licenses to enable the policy for all users.Requires MFA and reauthentication when Entra ID detects high-risk sign-ins.
Table 1: Microsoft-managed conditional access policies

See the documentation for more details about the Microsoft-managed conditional access policies.

The Case of Per-User MFA

The fact that Microsoft has chosen to include a managed conditional access policy for per-user MFA users deserves some comment. Microsoft says that this policy “helps organizations transition to Conditional Access.” Essentially, what they’re saying is that they don’t want customers to use per-user MFA any longer. This is the form of MFA included in licenses like Office 365 E3. Administrators manage per-user MFA by selecting users and enabling MFA for them (Figure 2).

Managing per-user MFA for Office 365 accounts
Figure 2: Managing per-user MFA for Office 365 accounts

Microsoft believes that enforcing MFA through conditional access policies is a better and more robust mechanism that results in better tenant security. Administrators don’t have to worry about enabling MFA for users when creating accounts nor do they have to deal with user queries about MFA on an individual level. MFA is enforced by policy and once the policy settings work, the policy serves as many accounts as the tenant has.

Sounds good. The downside is that to move away from per-user MFA, Microsoft forces customers to purchase Entra ID Premium licenses if their base product licenses (like Microsoft 365 E3) don’t include a Microsoft Azure multi-factor authentication service plan. I think this is wrong and believe that if Microsoft really wants people to move away from per0-user MFA, they should receive free Entra ID Premium P1 licenses. That’s unlikely to happen, but it would be the right thing to do.

I support greater use of MFA within Microsoft 365. Protect yourself and protect your tenant by enabling and using MFA to protect all user accounts. You know it makes sense.

]]>
https://office365itpros.com/2023/11/13/conditional-access-policy-msft/feed/ 7 62396
How to Block User Access to Microsoft 365 PowerShell Modules https://office365itpros.com/2023/10/12/block-powershell-m365/?utm_source=rss&utm_medium=rss&utm_campaign=block-powershell-m365 https://office365itpros.com/2023/10/12/block-powershell-m365/#comments Thu, 12 Oct 2023 01:00:00 +0000 https://office365itpros.com/?p=61922

Use Enterprise Applications to Block PowerShell Modules

A question arose about the best way to block Microsoft 365 user accounts from being able to run PowerShell. It seemed like a worthy problem to consider. In some cases an obvious answer exists, like stopping Exchange Online users from accessing PowerShell, but that’s a technique that only works for Exchange, and the block needs to be imposed for every new mailbox. We need something more generic that works across Microsoft 365.

Microsoft documents a process to block access to PowerShell for EDU tenants. The script to block PowerShell uses cmdlets from the Azure AD module, which Microsoft is deprecating with retirement scheduled for March 30, 2024. A replacement script using Microsoft Graph PowerShell SDK cmdlets is needed. Fortunately, I’ve been down this path with an article covering secure access to the SDK and can reuse many of the concepts explained there.

Update August 23, 2024: Microsoft now documents how to use the EXOModuleEnabled setting for accounts to control access to Exchange Online PowerShell. The technique described here works for other PowerShell modules.

Key Steps to Block PowerShell Modules

Every application that authenticates against Entra ID is known to the directory. Some applications are created within a tenant (registered apps). Others are created by companies like Microsoft as multi-tenant applications that can run anywhere. These are enterprise applications. The PowerShell modules that connect to Microsoft 365 endpoints like Exchange or Teams authenticate using enterprise applications created by Microsoft. The Microsoft Graph PowerShell SDK is the most obvious of these applications, but other applications exist for the Exchange Online management module, SharePoint Online management module, and the Microsoft Teams module.

Most administrators are unaware that these PowerShell enterprise applications exist. The applications don’t show up in the Entra ID admin center because normally they do not have a service principal. Applications use service principals to store permissions, like the Graph permissions used by the Microsoft Graph PowerShell SDK. Applications without service principals use roles instead.

For instance, when you run the Connect-ExchangeOnline cmdlet to connect to Exchange Online, the ability to work with Exchange data is gated by the roles possessed by the signed-in user account. If the account holds the Exchange administrator or Global administrator role, they can manage all aspects of Exchange Online (this also applies to Azure Automation accounts). If not, they can manage their own mailbox.

The key steps to restrict access to a PowerShell module are:

  • Find the application identifier for the module. We’ll get to doing that in a minute.
  • Create a service principal for the application.
  • Update the service principal so that it uses application role assignments.
  • Create a security group to manage assignments of permission to use the module.
  • Add the security group as an assignment to the service principal.

Finding Application Identifiers for PowerShell Modules

The first step is to find the application identifiers. The easiest way to do this is to check the Entra ID sign-in logs for events when people connect using a PowerShell module. Figure 1 shows an example of a sign-in event logged when an administrator connected with the SharePoint Online management module. We can see that the application identifier is 9bc3ab49-b65d-410a-85ad-de819febfddc.

Finding the application identifier for a PowerShell module from an Entra ID sign-in event

Block PowerShell access
Figure 1: Finding the application identifier for a PowerShell module from an Entra ID sign-in event

Application identifiers for other modules include:

  • Exchange Online management: fb78d390-0c51-40cd-8e17-fdbfab77341b (covers both regular Exchange and the compliance endpoint).
  • Microsoft Teams: 12128f48-ec9e-42f0-b203-ea49fb6af367
  • Azure: 1950a258-227b-4e31-a9cf-717495945fc2
  • Microsoft Graph PowerShell SDK: 14d82eec-204b-4c2f-b7e8-296a70dab67e

Example: Block Access to Exchange Online PowerShell

Now that we know the application identifiers, we can go ahead and create the service principal for the modules to block. Here are the PowerShell commands to connect an interactive Graph session and create a block for Exchange Online:

# Connect to the Grph
Connect-MgGraph -Scopes Directory.ReadWrite.All, Group.ReadWrite.All, Application.ReadWrite.All

# Create security group to control access to Exchange Online PowerShell
$Group = New-MgGroup -DisplayName "Allow access to EXO PowerShell" -MailEnabled:$False -SecurityEnabled:$True -MailNickName 'EXO.PowerShell'

# Create the service principal for the Exchange Online PowerShell app
$ServicePrincipal = New-MgServicePrincipal -Appid 'fb78d390-0c51-40cd-8e17-fdbfab77341b'

# Check that the Service Principal exists
Get-MgServicePrincipal -ServicePrincipalId $ServicePrincipal.Id | Format-Table DisplayName, Id, AppId

DisplayName                                  Id                                   AppId
-----------                                  --                                   -----
Microsoft Exchange REST API Based PowerShell 8d32ebd2-7295-4236-a3da-7c45be69a0b3 fb78d390-0c51-40cd-8e17-fdbfab77341b

# Update the Service Principal so that it requires application role assignments
Update-MgServicePrincipal -ServicePrincipalId $ServicePrincipal.Id -AppRoleAssignmentRequired:$True

# Add the security group as an assignment to the service principal
New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $ServicePrincipal.Id -AppRoleId ([Guid]::Empty.ToString()) -ResourceId $ServicePrincipal.Id -PrincipalId $Group.Id

After running these commands, no one can run the Connect-ExchangeOnline cmdlet to connect to Exchange unless they are added to the security group (Figure 2).

Members of the security group permitted to run Exchange Online PowerShell
Figure 2: Members of the security group permitted to run Exchange Online PowerShell

Entra ID rejects connection attempts from unauthorized accounts with an AADSTS50105 error (Figure 3). The “Microsoft Exchange REST API Based PowerShell” name is assigned to the enterprise application by Microsoft.

Error when attempting to run the Exchange Online PowerShell module
Figure 3: Error when attempting to run the Exchange Online PowerShell module

Discovering Who Accesses PowerShell

Often it’s simple to know who should be allowed to be members of the security group controlling access to a module. The tenant administrator, any administrators for a workload (like Teams service administrators), break glass accounts, service accounts such as those used by Azure Automation, and so on. But to be definite, we should review the Entra ID sign-in logs to see who uses a module.

This command retrieves the last 5,000 sign-in records and filters them for any sign-in for the Exchange Online application:

[array]$AuditRecords = Get-MgAuditLogSignIn -Top 5000 -Sort "createdDateTime DESC" -Filter "AppId eq 'fb78d390-0c51-40cd-8e17-fdbfab77341b'"

A simple Group-Object command gives the answer:

$AuditRecords | Group-Object UserPrincipalName -NoElement | Sort-Object Count -Descending| Select-Object Name, Count

Name                               Count
----                               -----
tony.redmond@office365itpros.com      10
EXOAdmin@office365itpros.com           7
James.Atkinson@office365itpros.com     3

You can then decide if any or all of the people who have accessed the module should be added to the security group. To check another module, replace the application identifier in the Get-MgAuditLogSignIn command.

Should My Tenant Block PowerShell?

The factors driving the decision to block PowerShell access for user accounts will differ from organization to organization. At least now you know the best way to block the most common PowerShell modules used with Microsoft 365 and how to find out who’s using the modules.


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/10/12/block-powershell-m365/feed/ 16 61922
How to Remove Licenses From Disabled Accounts with PowerShell https://office365itpros.com/2023/10/11/disabled-accounts-licenses/?utm_source=rss&utm_medium=rss&utm_campaign=disabled-accounts-licenses https://office365itpros.com/2023/10/11/disabled-accounts-licenses/#comments Wed, 11 Oct 2023 01:00:00 +0000 https://office365itpros.com/?p=61870

The Reasons for Disabled Accounts

Many reasons exist why organizations disable user accounts, including when employees go on sabbaticals, take time off due to illness, or have leave following childbirth. A less innocuous explanation is when employees are suspended for some reason. In all cases, accounts might remain in a disabled state for long periods.

Disabling an account means that Entra ID won’t let the user sign into their account. Data remains online and accessible for corporate purposes such as eDiscovery. Here’s how to disable an account using the Update-MgUser cmdlet from the Microsoft Graph PowerShell SDK:

Update-MgUser -UserId Andy.Ruth@office365itpros.com -AccountEnabled:$False

When the user returns, run Update-MgUser again to restore access by setting the AccountEnabled property to $True. To find the set of disabled accounts, run the Get-MgUser cmdlet like this:

Get-MgUser -Filter "accountEnabled eq false" -Property AccountEnabled, Id, DisplayName -All

Licensing of Disabled Accounts

Because accounts might be disabled for a long time, thoughts turn to the monthly license charges levied by Microsoft. If someone’s away for six months, should the organization pay for six months’ of charges. If the account has a Microsoft 365 E3 license and perhaps an add-on license (like SharePoint-Syntex advanced management) and a Teams calling plan, the costs could mount to $300 or thereabouts while the user is away.

One or two accounts incurring charges without use might not be a big deal. Interest about controlling license costs mounts as the number of disabled accounts mount. Twenty disabled accounts means $6,000 over six months. At that point, it might be worthwhile taking action to remove licenses from disabled accounts until their owners return to work.

Removing Exchange Online Licenses Leads to Disabled Mailboxes

Before rushing to remove all licenses from disabled accounts, let me sound a note of caution about removing products that include Exchange Online. An Exchange Online service plan is included in many Office 365 and Microsoft 365 products. For instance, Exchange Online Plan 2 (necessary for option such as archive mailboxes) is part of the Office 365 E3 and Office 365 E5 products. If you remove disable the Exchange Online service plan or remove the license for a product that includes Exchange Online from an account, the mailbox goes into a disabled state. One way to find mailboxes without licenses is to use the Get-EXOMailbox cmdlet to check if mailboxes have a valid SKU (product license):

Get-EXOMailbox -Filter {SkuAssigned -eq $True} | Format-Table DisplayName, UserPrincipalName, ExternalDirectoryObjectId

Exchange Online permanently removes disabled mailboxes after 30 days. To move from the disabled state, the owner’s account must be assigned a license that includes an Exchange Online service plan.

When removing licenses from disabled accounts, it’s important to check for Exchange Online to make sure that a removal doesn’t lead to potential data loss. Two options are available:

  • Retain assigned licenses that include Exchange Online for disabled accounts.
  • Replace the assigned license with a lower-cost license that includes Exchange Online. For example, you could assign inexpensive Office 365 E1 or F3 licenses to keep account mailboxes in a healthy state.

Exchange Online supports license stacking, meaning that it’s possible to assign multiple licenses to accounts that include an Exchange Online service plan. When this happens, Exchange Online uses the most functional plan.

Scripting License Removal

This article covers the basics of license management with the Microsoft Graph PowerShell SDK. The outline of a script to find and remove licenses from disabled accounts might include the following steps:

  • Connect to the Graph.
  • Define exclusions for licenses that should not be removed from accounts (those with Exchange Online).
  • Find disabled accounts.
  • Loop through each account to examine the assigned licenses and decide if any can be removed.
  • Run the Set-MgUserLicense cmdlet to remove the licenses.
  • Report the actions taken.

If an organization uses group-based licensing, Set-MgUserLicense cannot remove licenses assigned using this mechanism. Instead, the correct approach is to remove the account from the group used by Entra ID to control license assignments.

My version of a script to process license removals for disabled accounts can be downloaded from GitHub. It includes code to exclude licenses containing Exchange Online service plans. As mentioned earlier, the alternative is to replace licenses with a cheaper version. The code to do this would be simple to add. The script excludes licenses assigned through group-based licenses. Again, it would be easy to add code to remove accounts from the groups used to assign licenses. Figure 1 shows the script in action.

Removing licenses from disabled accounts
Figure 1: Removing licenses from disabled accounts

The Shared Mailbox Approach

Another way to handle the question of what to do with mailboxes belonging to long-term absentees is to turn them into shared mailboxes for the duration of their owner’s absence. When the owner returns, revert the shared mailbox to make it a regular mailbox again. This technique preserves the mailbox because shared mailboxes don’t need licenses. Here’s what you do:

  1. Convert the mailbox into a shared mailbox.
  2. Disable the account and change the password.
  3. Remove all licenses.
  4. (Optional) Hide the shared mailbox from Exchange address lists.
  5. (Optional) Remove the shared mailbox from distribution lists so that mail doesn’t pile up in the mailbox during the owner’s absence.

When the user returns:

  1. Convert the shared mailbox to a regular mailbox.
  2. Enable the account and assign a new password.
  3. Assign licenses to the account.
  4. Unhide (if necessary) the mailbox.
  5. Restore distribution list membership.

Check and Verify Before Use

Remember that the script illustrates the principles behind license removal for disabled accounts. It is not a production-ready solution. Like any code downloaded from the internet, you should verify and test the script and adapt it to meet your needs (especially because it removes licenses from accounts). The nice thing is that everything’s done in PowerShell, so please go ahead and modify the code as you wish.


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/10/11/disabled-accounts-licenses/feed/ 1 61870
How to Create Dynamic Microsoft 365 Groups (and Teams) for Departments https://office365itpros.com/2023/10/10/dynamic-microsoft-365-groups/?utm_source=rss&utm_medium=rss&utm_campaign=dynamic-microsoft-365-groups https://office365itpros.com/2023/10/10/dynamic-microsoft-365-groups/#respond Tue, 10 Oct 2023 01:00:00 +0000 https://office365itpros.com/?p=61844

Create Dynamic Microsoft 365 Groups and Teams with PowerShell

No sooner had I published the article about creating dynamic administrative units with PowerShell, the first email arrived asking if the same was possible for dynamic Microsoft 365 groups. The answer is “of course,” but with the caveat that it’s not just a matter of some minor updates to the script.

That being said, the outline for the script to create dynamic groups is broadly the same:

  • Find the licensed users in the tenant and extract a list of departments. The departments should be accurate and some care should be taken to eliminate inconsistencies. For instance, some people might be listed as belonging to IT while others belong to the Information Technology department. Decide on one value and apply it to all.
  • You might not want to create groups for all departments. The script defines an array of excluded departments that are removed from the set to process.
  • Find the set of dynamic Microsoft 365 groups. We need this information to check if a dynamic group already exists for a department.
  • For each department, check if a group already exists. If not, define some parameters for the new group, including the membership rule that Entra ID uses to calculate the group members, and run the New-MgGroup cmdlet to create the group.
  • Following a successful creation, proceed to team-enable the new group by running the New-MgTeam cmdlet. This is an optional step, but seeing that Teams is the heaviest workload for Microsoft 365 groups, it seemed like a good thing to include.

Let’s examine some of the steps.

Scripting the Creation of a Dynamic Microsoft 365 Group

Here’s an example of creating a new dynamic Microsoft 365 group for the department whose name is stored in the $Dept variable:

Write-Host ("Checking groups for department {0}" -f $Dept)
$Description = ("Dynamic Microsoft 365 group created for the {0} department on {1}" -f $Dept, (Get-Date))
$DisplayName = ("{0} Dynamic group" -f $Dept)
$MailNickName = ("Dynamic.{0}.Group" -f ($Dept -replace " ",""))
$MembershipRule = '(User.Department -eq "' + $Dept +'")'

If ($DisplayName -in $Groups.DisplayName) {
   Write-Host ("Group already exists for {0}" -f $Dept) -ForegroundColor Red
} Else {
# Create the new dynamic Microsoft 365 Group
   $NewGroup = New-MgGroup -DisplayName $DisplayName -Description $Description ` 
   -MailEnabled:$True -SecurityEnabled:$False `
   -MailNickname $MailNickName -GroupTypes "DynamicMembership", "Unified" `
   -MembershipRule $MembershipRule -MembershipRuleProcessingState "On"
}

Wait Before Progressing to Teams

Flushed with the successful creation, you might want to rush to team-enable the new group. However, it’s best to wait 10-15 seconds before proceeding to allow Teams to learn about the new group from Entra ID. If you attempt to team-enable a group immediately after creation, you’ll probably see an error like this:

Failed to execute Templates backend request CreateTeamFromGroupWithTemplateRequest. Request Url: https://teams.microsoft.com/fabric/emea/templates/api/groups/bab7a3a8-2e30-4996-9405-48ca395b99c6/team, Request Method: PUT, Response Status Code: NotFound, Response Headers: Strict-Transport-Security: max-age=2592000
x-operationid: a228258204c3466dbd64c4d88373a416
x-telemetryid: 00-a228258204c3466dbd64c4d88373a416-82a9b5015f332574-01
X-MSEdge-Ref: Ref A: FC01DAADBD0D4A1A9ECBB9826707CC17 Ref B: DB3EDGE2518 Ref C: 2023-10-04T15:00:51Z
Date: Wed, 04 Oct 2023 15:00:52 GMT
ErrorMessage : {"errors":[{"message":"Failed to execute GetGroupMembersMezzoCountAsync.","errorCode":"Unknown"}],"operationId":"a228258204c3466dbd64c4d88373a416"}

Team-Enabling a Group

To team-enable a group, run the New-MgTeam cmdlet and provide a hash table containing information to allow Teams to find the new group (the Graph URI for the group) plus the Teams template to use. This code does the trick.

$GroupUri = "https://graph.microsoft.com/v1.0/groups('" + $NewGroup.Id + "')"
$NewTeamParams = @{
   "template@odata.bind"="https://graph.microsoft.com/v1.0/teamsTemplates('standard')"
   "group@odata.bind"="$($GroupUri)"
}
$NewTeam = New-MgTeam -BodyParameter $NewTeamParams
If ($NewTeam) {
   Write-Host ("Successfully team-enabled the {0}" -f $NewGroup.DisplayName)
}

Checking Groups Post-Creation

Figure 1 shows some of the dynamic Microsoft 365 groups created in my tenant. Note the groups for “Information Technology” and the “IT Department.” Obviously my checking of user departments was deficient prior to running the script. The fix is easy though. Decide on which department name to use and update user accounts to have that. Then remove the now-obsolete group. Entra ID will make sure that the accounts with reassigned departments show up in the correct group membership.

Dynamic Microsoft 365 groups created for departments
Figure 1: Dynamic Microsoft 365 groups created for departments

In this case, only one account had “IT Department,” so I quickly updated its department property with:

Update-MgUser -UserId Jack.Smith@office365itpros.com -Department "Information Technology"

I then removed the IT Department dynamic group:

$Group = Get-MgGroup -Filter "displayName eq 'IT Department Dynamic Group'"
Remove-MgGroup -GroupId $Group.Id

Soon afterwards, the membership of the Information Department Dynamic group was correct (Figure 2) and all was well.

Membership of a dynamic Microsoft 365 group for a department
Figure 2: Membership of a dynamic Microsoft 365 group for a department

You can download the complete script from GitHub. It would be easy to adapt the code to run as an Azure Automation runbook to scan for new departments and create groups as necessary.

Simple PowerShell Results in Big Benefits

Scripting the creation of dynamic Microsoft 365 groups for each department in a tenant isn’t too difficult. The membership rule is simple but could be expanded to include different criteria. Once the groups are created, they should be self-maintaining. That is, if you make sure that the department property for user accounts is accurate.


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 like dynamic Microsoft 365 groups work.

]]>
https://office365itpros.com/2023/10/10/dynamic-microsoft-365-groups/feed/ 0 61844
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
Microsoft Updates Entra ID Cross-Tenant Access Management https://office365itpros.com/2023/09/13/cross-tenant-access-settings/?utm_source=rss&utm_medium=rss&utm_campaign=cross-tenant-access-settings https://office365itpros.com/2023/09/13/cross-tenant-access-settings/#respond Wed, 13 Sep 2023 01:00:00 +0000 https://office365itpros.com/?p=61557

Improvements to Cross-Tenant Access Settings Based on Customer Feedback

Microsoft launched Azure AD (Entra ID) cross-tenant access settings in February 2022 to support the introduction of Teams shared channels. The new mechanism established a way for tenants to trust each other and created the basis for sharing. Reflecting the experience of how customers use cross-tenant access settings (Figure 1) in production, on August 30 Microsoft announced some changes due to roll out soon.

Cross-tenant access settings for an Entra ID tenant
Figure 1: Cross-tenant access settings for an Entra ID tenant

The changes are:

  • Custom roles for cross-access tenant policy management.
  • New method of storing partner policies.
  • Integration of blocks in cross-tenant access settings when sending B2B invitations.

Like any other change, these updates might not affect how you work. I think it’s fair to say that the larger the tenant, the more important the updates are to you. But let’s consider what the changes do.

Custom Roles for Cross-Tenant Policy Management

Up to now, only users holding the global administrator or security administrator roles can manage cross-tenant access settings. For most tenants, this arrangement works well. Creating a new cross-tenant arrangement is not something that happens every day and requires coordination with the administrators of the other tenant.

Tenants with Entra ID Premium P1 or P2 licenses can create custom administrative roles to allow users perform specific management tasks for Entra ID. This capability now extends to cross-tenant access policy management where roles such as “Cross-tenant policy reader” might be created to allow users to review but not update settings. Again, this isn’t something that every tenant needs or wants, but at least tenants now have the flexibility to use a custom role to manage cross-tenant access settings if they see value in it.

New Method of Storing Partner Policies

According to Microsoft’s posts, some tenants need to manage cross-tenant access settings for thousands of partners (hopefully, they don’t do this manually and use some form of automation such as PowerShell scripts). Microsoft noted that the way Entra ID stored cross-tenant policy configurations limited the number of individual partner policies that a tenant could manage. Accordingly, a change is rolling out to change the way Entra ID stores policy configurations so that each partner tenant has its own policy. Microsoft says that the new mechanism is scalable and should be capable of storing as many policies as tenants need.

The change to the way Entra ID stores partner policies is happening behind the scenes and shouldn’t be noticed by tenants. Graph APIs interact with policies in the same way for both the old and new storage, so the changeover shouldn’t cause any disruption.

Integration of Blocks with B2B Invitations

The Entra ID B2B Collaboration policy for a tenant can contain a blocklist of tenants that applications aren’t allowed to invite as guest members. For instance, if you add Gmail.com to the blocklist, Entra ID blocks team owners if they attempt to invite people with Gmail.com addresses to become guest members.

The problem is that up to now, Entra ID didn’t check cross-tenant access settings when it assessed whether to send an invitation to a new guest. This meant that you could get into a situation where cross-tenant access settings blocked Contoso.com but the B2B collaboration policy did not. Teams or other applications that use B2B collaboration could go ahead and invite people from Contoso.com to become guest members, but when the Contoso.com users attempted to redeem their invitations and access resources, cross-tenant access settings blocked their attempt.

Obviously, blocking guests who seemed to receive perfectly good invitations is a recipe for frustration for team owners or people who want to share documents and folders from SharePoint Online. The change now being introduced means that Entra ID checks both the B2B Collaboration policy and cross-tenant access settings before deciding to issue an invitation or block a user from an external tenant. It’s a logical way to close a disconnect between two parts of Entra ID.

Learning Through Experience

Cross-tenant access settings are becoming increasingly important. The latest advance is the cross-tenant synchronization used by Microsoft 365 multi-tenant organizations. Synchronization can’t happen if cross-tenant settings aren’t configured correctly. It’s good to see these changes ironing out real-life defects.


Make sure that you’re not surprised about changes that 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/2023/09/13/cross-tenant-access-settings/feed/ 0 61557
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
Monitor and Report Additions to Teams Membership https://office365itpros.com/2023/08/22/teams-membership-monitoring/?utm_source=rss&utm_medium=rss&utm_campaign=teams-membership-monitoring https://office365itpros.com/2023/08/22/teams-membership-monitoring/#respond Tue, 22 Aug 2023 01:00:00 +0000 https://office365itpros.com/?p=61294

Use PowerShell and the Audit Log to Find Targeted Accounts in Teams Memberships

A request came into the Office 365 Technical Discussions Facebook group for a way to monitor member additions to teams. The idea is that if a team owner adds an account with a specific attribute in the display name, something picks up the addition and notifies someone that the action happened.

PowerShell is the normal way to answer questions of this nature. That is, if you can get at the data. In this instance, the unified audit log captures events for team membership additions, so the raw data exists, even if a little manipulation is necessary to extract the information we need (thankfully, the needed manipulation is less than in other scenarios, such as tracking updates for properties of user accounts).

Specifying User Accounts to Monitor in Teams Memberships

The first thing to do is identify the set of users to check for. The original request didn’t specify what kind of attribute to look for in the display name, so the solution outlined here assumes that it’s a string after the combination of first name and last name. For instance, “Tom Smith (Project Management).”

Identifying the accounts to monitor is a key part of the solution. Here’s the code to use the Get-MgUser cmdlet with the Search parameter to find licensed member accounts that include “Project” in the display name.

[array]$Users = Get-MgUser -Search "displayName:Project" -Filter "assignedLicenses/`$count ne 0 and userType eq 'Member'" -ConsistencyLevel Eventual
If (!($Users)) { 
    Throw "No users found"
}

There might be many user accounts that need to be monitored. To speed things up when we check audit records, the script creates a hash table composed of the user principal name and display name.

$UserLookup = @{}
ForEach ($User in $Users) {
   $UserLookup.Add($User.UserPrincipalName, $User.DisplayName)
}

Searching the Audit Log for Additions to Teams Memberships

Next, the script calls the Search-UnifiedAuditLog cmdlet to look for MemberAdded events generated by Teams over the past seven days:

$StartDate = (Get-Date).AddDays(-7)
$EndDate = (Get-Date).AddDays(1)
[array]$Records = Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -Formatted -ResultSize 5000 -RecordType MicrosoftTeams -Operations MemberAdded -SessionCommand ReturnLargeSet
$Records = $Records | Sort-Object Identity -Unique

To check the audit events, the script converts the AuditData property for each event from JSON and examines what’s stored in the Members property (an array). For each item in Members, the script looks up the hash table to see if the account is monitored, and if so, captures details of the event in a list:

$Report = [System.Collections.Generic.List[Object]]::new()  
ForEach ($Rec in $Records) {
    $Role = $Null
    $AuditData = $Rec.AuditData | ConvertFrom-Json
    # Check the members noted as added to a group
    ForEach ($Member in $AuditData.Members) {
        If ($UserLookup[$Member.Upn]) {
           Write-Host ("User {0} added to team {1}" -f $Member.DisplayName, $AuditData.TeamName) 
           Switch ($Member.Role) {
            "1"  { $Role = "Member" }
            "2"  { $Role = "Owner"}
            "3"  { $Role = "Guest" }
           }
           $ReportLine = [PSCustomObject]@{
             Date = $AuditData.CreationTime
             User = $Member.Upn   
             Name = $Member.DisplayName
             Team = $AuditData.TeamName
             Role = $Role
             AddedBy = $AuditData.UserId
           }
          $Report.Add($ReportLine)
        }
    }
}

Here’s an example of the output:

Date    : 20/08/2023 12:12:55
User    : Hans.Geering@office365itpros.com
Name    : Hans Geering (Project Management)
Team    : Office 365 Adoption
Role    : Member
AddedBy : Tony.Redmond@office365itpros.com

Sharing the Results

To share the results, we send email from a shared mailbox. Two ways are available to sent the message. You can use an app with consent to use the Mail.Send application permission (which allows the app to send email from any mailbox). Alternatively, you can use the Mail.Send.Shared permission in an interactive session. In either case, the Send-MgUsermail cmdlet sends the message using a variation of the code explained in this article. Figure 1 shows an example of an email sent to the designated recipient (which should probably be a distribution list in production) to report results.

Email to report additions made to Teams memberships
Figure 1: Email to report additions made to Teams memberships

Posting the information to a Teams channel is another way to share details about new membership additions. Another option is to upload the file to a SharePoint Online document library, a topic explored in this article when Azure Automation runs a script to create content like a report. Monitoring for changes in a Microsoft 365 tenant is the kind of task that is well suited to Azure Automation, and it’s the way that I would go in production.

You can download the sample script from GitHub. Feel free to change (hopefully improve) the code.


Learn about using the Graph SDK, the unified audit log, 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/22/teams-membership-monitoring/feed/ 0 61294
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
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
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
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
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
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
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
Reporting Operating System Versions for Registered Devices https://office365itpros.com/2023/01/31/entra-id-registered-devices-os/?utm_source=rss&utm_medium=rss&utm_campaign=entra-id-registered-devices-os https://office365itpros.com/2023/01/31/entra-id-registered-devices-os/#comments Tue, 31 Jan 2023 01:00:00 +0000 https://office365itpros.com/?p=58916

Know What Operating System Used by Entra ID Registered Devices

After reading an article about populating extension attributes for registered devices, a reader asked me how easy it would be to create a report about the operating systems used for registered devices. Microsoft puts a lot of effort into encouraging customers to upgrade to Windows 11 and it’s a good idea to know what’s the device inventory. Of course, products like Intune have the ability to report this kind of information, but it’s more fun (and often more flexible) when you can extract the information yourself.

As it turns out, reporting the operating systems used by registered devices is very easy because the Microsoft Graph reports this information in the set of properties retrieved by the Get-MgDevice cmdlet from the Microsoft Graph PowerShell SDK.

PowerShell Script to Report Entra ID Registered Devices

The script described below creates a report of all registered devices and sorts the output by the last sign in date. Microsoft calls this property ApproximateLastSignInDateTime. As the name indicates, the property stores the approximate date for the last sign in. Entra ID doesn’t update the property every time someone uses the device to connect. I don’t have a good rule for when property updates occur. It’s enough (and approximate) that the date is somewhat accurate for the purpose of identifying if a device is in use, which is why the script sorts devices by that date.

Any Windows device that hasn’t been used to sign into Entra ID in the last six months is likely not active. This isn’t true for mobile phones because they seem to sign in once and never appear again. The report generated for my tenant still has a record for a Windows Phone which last signed in on 2 December 2015. I think I can conclude that it’s safe to remove this device from my inventory.

Figuring Out Device Owners

In the last script I wrote using the Get-MgDevice cmdlet, I figured out the owner of the device by extracting the user identifier from the PhysicalIds property. While this approach works, it’s complicated. A much better approach is to use the Get-MgDeviceRegisteredOwner cmdlet which returns the user identifier for the user account of the registered owner. With this identifier, we can retrieve any account property that makes sense, such as the display name, user principal name, department, city, and country. You could easily add other properties that make sense to your organization. See this article for more information about using the Get-MgUser cmdlet to interact with user accounts.

The Big Caveat About Operating System Information

The problem that exists in using registered devices to report operating system information is that it’s not accurate. The operating system details noted for a device are accurate at the point of registration but degrade over time. If you want to generate accurate reports, you need to use the Microsoft Graph API for Intune.

With that caveat in mind, here’s the code to report the operating system information for Entra ID registered devices:

Connect-MgGraph -Scope User.Read.All, Directory.Read.All

Write-Host "Finding registered devices"
[array]$Devices = Get-MgDevice -All -PageSize 999
If (!($Devices)) { Write-Host "No registered devices found - exiting" ; break }
Write-Host ("Processing details for {0} devices" -f $Devices.count)
$Report = [System.Collections.Generic.List[Object]]::new() 
$i = 0
ForEach ($Device in $Devices) {
  $i++
  Write-Host ("Reporting device {0} ({1}/{2})" -f $Device.DisplayName, $i, $Devices.count)
  $DeviceOwner = $Null
  Try {
    [array]$OwnerIds = Get-MgDeviceRegisteredOwner -DeviceId $Device.Id
    $DeviceOwner = Get-MgUser -UserId $OwnerIds[0].Id -Property Id, displayName, Department, OfficeLocation, City, Country, UserPrincipalName} 
 } Catch {}

  $ReportLine = [PSCustomObject][Ordered]@{
   Device             = $Device.DisplayName
   Id                 = $Device.Id
   LastSignIn         = $Device.ApproximateLastSignInDateTime
   Owner              = $DeviceOwner.DisplayName
   OwnerUPN           = $DeviceOwner.UserPrincipalName
   Department         = $DeviceOwner.Department
   Office             = $DeviceOwner.OfficeLocation
   City               = $DeviceOwner.City
   Country            = $DeviceOwner.Country
   "Operating System" = $Device.OperatingSystem
   "O/S Version"      = $Device.OperatingSystemVersion
   Registered         = $Device.RegistrationDateTime
   "Account Enabled"  = $Device.AccountEnabled
   DeviceId           = $Device.DeviceId
   TrustType          = $Device.TrustType }
  $Report.Add($ReportLine)

} #End Foreach Device

# Sort in order of last signed in date
$Report = $Report | Sort-Object {$_.LastSignIn -as [datetime]} -Descending

$Report | Out-GridView

Figure 1 is an example of the report as viewed through the Out-GridView cmdlet.

Reporting operating system information for Entra ID registered devices
Figure 1: Reporting operating system information for Entra ID registered devices

You can download the latest version of the script from GitHub.

An Incomplete Help

I’ve no idea whether this script will help anyone. It’s an incomplete answer to a question. However, even an incomplete answer can be useful in the right circumstances. After all, it’s just PowerShell, so use the code 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/2023/01/31/entra-id-registered-devices-os/feed/ 1 58916
Fetching Group Membership Information for an Entra ID User Account https://office365itpros.com/2023/01/30/group-membership-with-the-graph/?utm_source=rss&utm_medium=rss&utm_campaign=group-membership-with-the-graph https://office365itpros.com/2023/01/30/group-membership-with-the-graph/#comments Mon, 30 Jan 2023 01:00:00 +0000 https://office365itpros.com/?p=58839

Discover Group Membership with the Graph SDK

Group membership with the Graph SDK

I’ve updated some scripts recently to remove dependencies on the Azure AD and Microsoft Online Services (MSOL) modules, which are due for deprecation on June 30, 2023 (retirement happens at the end of March for the license management cmdlets). In most cases, the natural replacement is cmdlets from the Microsoft Graph PowerShell SDK.

One example is when retrieving the groups a user account belongs to. This is an easy task when dealing with the membership of individual groups using cmdlets like:

  • Get-DistributionGroupMember (fetch distribution list members).
  • Get-DynamicDistributionGroupMember (fetch dynamic distribution group members).
  • Get-UnifiedGroupLinks (fetch members of a Microsoft 365 group).
  • Get-MgGroupMember (fetch members of an Entra ID group).

Things are a little more complex when answering a question like “find all the groups that Sean Landy belongs to.” Let’s see how we can answer the request.

The Exchange Online Approach

One method of attacking the problem often found in Exchange scripts is to use the Get-Recipient cmdlet with a filter based on the distinguished name of the mailbox belonging to an account: For example, this code reports a user’s membership of Microsoft 365 groups:

$User = Get-EXOMailbox -Identity Sean.Landy
$DN = $User.DistinguishedName
$Groups = (Get-Recipient -ResultSize Unlimited -RecipientTypeDetails GroupMailbox -Filter "Members -eq '$DN'" )
Write-Host (“User is a member of {0} groups” -f $Groups.count)

The method works if the distinguished name doesn’t include special characters like apostrophes for users with names like Linda O’Shea. In these cases, extra escaping is required to make PowerShell handle the name correctly. This problem will reduce when Microsoft switches the naming mechanism for Exchange Online objects to be based on the object identifier instead of mailbox display name. However, there’s still many objects out there with distinguished names based on display names.

The Graph API Request

As I go through scripts, I check if I can remove cmdlets from other modules to make future maintenance easier. Using Get-Recipient means that a script must connect to the Exchange Online management module, so let’s remove that need by using a Graph API request. Here’s what we can do, using the Invoke-MgGraphRequest cmdlet to run the request:

$UserId = $User.ExternalDirectoryObjectId
$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 $UserId)
[array]$Data = Invoke-MgGraphRequest -Uri $Uri
[array]$Groups = $Data.Value
Write-Host (“User is a member of {0} groups” -f $Groups.count) 

We get the same result (always good) and the Graph request runs about twice as fast as Get-Recipient does.

Because the call is limited to Microsoft 365 groups, I don’t have to worry about transitive membership. If I did, then I’d use the group transitive memberOf API.

Using the SDK Get-MgUserMemberOf Cmdlet

The Microsoft Graph PowerShell SDK contains cmdlets based on Graph requests. The equivalent cmdlet is Get-MgUserMemberOf. This returns memberships of all group types known to Entra ID, so it includes distribution lists and security groups. To return the set of Microsoft 365 groups, apply a filter after retrieving the group information from the Graph.

[array]$Groups = Get-MgUserMemberOf -UserId $UserId -All | Where-Object {$_.AdditionalProperties["groupTypes"] -eq "Unified"}
Write-Host (“User is a member of {0} groups” -f $Groups.count) 

Notice that the filter looks for a specific type of group in a value in the AdditionalProperties property of each group. If you run Get-MgUserMemberOf without any other processing. the cmdlet appears to return a simple list of group identifiers. For example:

$Groups

Id                                   DeletedDateTime
--                                   ---------------
b62b4985-bcc3-42a6-98b6-8205279a0383
64d314bb-ea0c-46de-9044-ae8a61612a6a
87b6079d-ddd4-496f-bff6-28c8d02e9f8e
82ae842d-61a6-4776-b60d-e131e2d5749c

However, the AdditionalProperties property is also available for each group. This property contains a hash table holding other group properties that can be interrogated. For instance, here’s how to find out whether the group supports private or public access:

$Groups[0].AdditionalProperties['visibility']
Private

When looking up a property in the hash table, remember to use the exact form of the key. For instance, this works to find the display name of a group:

$Groups[0].AdditionalProperties['displayName']

But this doesn’t because the uppercase D creates a value not found in the hash table:

$Groups[0].AdditionalProperties['DisplayName']

People starting with the Microsoft Graph PowerShell SDK are often confused when they see just the group identifiers apparently returned by cmdlets like Get-MgUserMemberOf, Get-MgGroup, and Get-MgGroupMember because they don’t see or grasp the importance of the AdditionalProperties property. It literally contains the additional properties for the group excepting the group identifier.

Here’s another example of using information from AdditionalProperties. The details provided for a group don’t include its owners. To fetch the owner information for a group, run the Get-MgGroupOwner cmdlet like this:

$Group = $Groups[15]
[array]$Owners = Get-MgGroupOwner -GroupId $Group.Id | Select-Object -ExpandProperty AdditionalProperties
$OwnersOutput = $Owners.displayName -join ", "
Write-Host (“The owners of the {0} group are {1}” -f $Group.AdditionalProperties[‘displayName’], $OwnersOutput)

If necessary, use the Get-MgGroupTransitiveMember cmdlet to fetch transitive memberships of groups.

The Graph SDK Should be More Intelligent

It would be nice if the Microsoft Graph PowerShell SDK didn’t hide so much valuable information in AdditionalProperties and wasn’t quite so picky about the exact format of property names. Apparently, the SDK cmdlets behave in this manner because it’s how Graph API requests work when they return sets of objects. That assertion might well be true, but it would be nice if the SDK applied some extra intelligence in the way it handles data.


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/30/group-membership-with-the-graph/feed/ 4 58839
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
Outlook Org Explorer Gives More Reasons to Pay Attention to User Data https://office365itpros.com/2022/05/25/org-explorer-outlook/?utm_source=rss&utm_medium=rss&utm_campaign=org-explorer-outlook https://office365itpros.com/2022/05/25/org-explorer-outlook/#comments Wed, 25 May 2022 01:00:00 +0000 https://office365itpros.com/?p=55213

Org Explorer Brings Data from Multiple Microsoft 365 Sources

Updated 28 February 2023

About 18 months ago, I wrote about the importance of maintaining user account attributes in Entra ID. At the time, my focus was on Teams, because the application exposes where someone fits in the organizational structure when viewing their details. If you use Exchange Online dynamic distribution lists, the queries used to resolve list membership also depend on accurate directory data.

Organizational information is also available in the Office 365 profile card (which now shows local time information for users to make meetings easier to arrange). And now, organizational views are coming to Outlook desktop clients.

Introducing Outlook’s Org Explorer

Announced in message center post MC315746 (last updated January 21, 2022) and in preview since February (see Microsoft 365 roadmap item 84785), a new Org Explorer tab is available in Outlook’s navigation bar in Insider builds. Microsoft originally disclosed the feature in July 2021. At that time, Microsoft said that the Org Explorer is available to users with an Microsoft 365 E3 or E5 or Microsoft 365 Business license.

Update: 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. It’s odd that Microsoft would change the license requirements in mid-course, but they can do so at any time before a feature becomes generally available, which is the case here.

Oddly enough, given that OWA usually picks up new features first, the Outlook Org Explorer isn’t yet available in OWA, or the preview build of the One Outlook (“Monarch”) client.

Choosing Org Explorer opens what feels like a web page. The content shown on the page combines organizational information, personal information (like their address), presence information, and people insights derived from the Microsoft Graph from user activity (Figure 1). The user picker at the top right-hand conner can only search for user accounts within the tenant. In this instance, the person is an individual contributor without any direct reports. However, their manager appears at the top of the screen.

Using the Outlook Org Explorer
Figure 1: Using the Outlook Org Explorer

The Outlook Org Explorer tells you how many people report to the person in focus. You can expand the raw count to see the full set. Navigation down through the organization works well but navigating back up a level or two doesn’t work as well, even when attempting to move from a user with a direct manager.

Exchange Online must cache the information displayed by the Org Explorer. Changes made to reporting relationships didn’t appear for several hours after the update. Caching data is reasonable because the Org Explorer shows a lot of information extracted from different sources. I’m sure a background process collects the data periodically to make it available to Outlook.

Roaming Signatures Coming Closer

Also for Outlook,. Microsoft has been working on roaming signatures for Outlook desktop clients for several years, Message Center post MC305463 (15 December 2021) announced a delay for Roaming Signatures, and Microsoft later said that the new target date is July 2022. The good news is that the latest Insider builds and the One Outlook preview both include a way to insert Outlook Web Signatures into a message (Figure 2).

Inserting an OWA signature into Outlook desktop
Figure 2: Inserting an OWA signature into Outlook desktop

Outlook web signatures are no more than the signature defined for OWA (which can also be set for a mailbox using PowerShell). The good news is that the method works, which means that you can insert OWA signatures into Outlook very easily.

The latest version of OWA (and the One Outlook preview) allow users to define multiple web signatures. In the past, OWA had just one signature, but that seems to be in the past. In addition to being able to define multiple signatures (and insert any of the signatures into a message), users can choose default signatures for new messages and replies.

OWA setup for signatures
Figure 3: OWA setup for signatures

This flurry of change in OWA and Outlook points to OWA mailbox-based signatures being the way forward. No doubt Microsoft will reveal all in July. It will be nice to only have to define signatures in one place and have all Outlook clients use those signatures.


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/25/org-explorer-outlook/feed/ 2 55213
Basic Entra ID Group Management with the Microsoft Graph PowerShell SDK https://office365itpros.com/2022/03/29/create-entra-id-group/?utm_source=rss&utm_medium=rss&utm_campaign=create-entra-id-group https://office365itpros.com/2022/03/29/create-entra-id-group/#comments Tue, 29 Mar 2022 01:00:00 +0000 https://office365itpros.com/?p=54278

Create Entra ID Groups with SDK Cmdlets

Updated 28 December 2023

Last week, I discussed how to perform basic Entra ID user account management operations using cmdlets from the Microsoft Graph PowerShell SDK. Now it’s time to discuss the management of group objects.

Use the Entra ID admin center to create Entra ID groups and manage them afterward.
Figure 1: Use the Entra ID admin center to create Entra ID groups and manage them afterward

To work with the cmdlets discussed here, you should connect to the Microsoft Graph with the Group.ReadWrite.All and GroupMember.ReadWrite.All permissions:

$RequiredScopes = @("Group.ReadWrite.All", "GroupMember.ReadWrite.All", "User.ReadWrite.All")
Connect-MgGraph -Scopes $RequiredScopes -NoWelcome

See this post for more information about connecting to the Graph and permissions.

Creating Different Kinds of Entra ID Groups

Entra ID groups include:

  • Microsoft 365 groups/Teams (including groups used by Yammer).
  • Security groups.
  • Dynamic groups (including those used by Microsoft 365 groups/Teams).
  • Distribution lists.
  • Mail-enabled security groups.

The New-MgGroup cmdlet can create Microsoft 365 groups, dynamic groups, and security groups. It can’t create distribution lists or mail-enabled security groups., nor can Graph API requests or Graph SDK cmdlets update the membership of distribution lists or mail-enabled security groups because these group types are essentially Exchange Online rather than Entra ID objects.

Although New-MgGroup can create groups of different types, it is often better to use the dedicated cmdlet for a particular type of group to ensure that Microsoft 365 performs all the necessary provisioning, like New-UnifiedGroup or New-Team.

Here’s an example of using New-MgGroup to create a Microsoft 365 group (the key point is to set the GroupTypes parameter to be “Unified”):

New-MgGroup -DisplayName "Sales Operations Team" -GroupTypes Unified -MailNickName Sales.Operations -MailEnabled:$True -SecurityEnabled:$False -Description "A group for Sales Operation management"

It’s a good idea to capture the result of the cmdlet in a variable. If the command is successful, you’ll then have a variable containing properties of the new group including its identifier. As we’ll see, you’ll need the identifier to interact with the group using other SDK cmdlets.

The downside of creating a Microsoft 365 group using the New-MgGroup cmdlet is that you will probably end up fixing up some of the group’s properties afterwards. For instance, New-MgGroup adds the signed-in account as the group owner, which you might not want. In addition, you can’t update properties like group privacy or assign a sensitivity label, so these must be set afterwards.

Creating a Dynamic Microsoft 365 Group

One scenario where New-MgGroup scores is where you want to create a dynamic Microsoft 365 Group, as this cannot be done using the New-UnifiedGroup cmdlet. This command creates a group using a membership rule to find people whose usage location (for Microsoft 365 services) is the U.S:

$Group = New-MgGroup -DisplayName "U.S. Based Employees" -Description "Dynamic group containing U.S. Based Employees" -MailEnabled:$True -SecurityEnabled:$False -MailNickname US.Employees -GroupTypes "DynamicMembership", "Unified" -MembershipRule "(User.usagelocation -eq ""US"")" -MembershipRuleProcessingState "On"

Update Group Properties

PowerShell modules like Exchange Online and Azure AD usually include Set- cmdlets to update the properties of objects. The SDK uses Update- cmdlets, so to update a group, you run the Update-MgGroup cmdlet. For example, this command updates a group’s description:

Update-MgGroup -GroupId dc9e6f8b-6734-4180-af25-aa40fae79280 -Description "People lucky enough to have Office 365 E5 licenses"

Currently, the Microsoft Graph Groups API treats a group’s proxyAddresses property as read-only, which means that you can’t add or remove a proxy address using the Update-MgGroup cmdlet. Use an Exchange Online cmdlet like Set-UnifiedGroup instead.

Updating Group Membership

The New-MgGroupMember cmdlet populates the group membership. In this example, we get the group identifier, use Get-MgUser to find a set of suitable group members, and finally add them to the group:

$GroupId = (Get-MgGroup -Filter "displayName eq 'Sales Operations Team'").Id
[array]$Users = Get-MgUser -Filter "department eq 'Sales'"
ForEach ($User in $Users) {
  New-MgGroupMember -GroupId $GroupId -DirectoryObjectId $User.Id }

In the past, checking that the right members are present afterwards is not as simple as it should be. Instead of Get-MgGroupMember returning a list of group members, you must pipe the output to the Get-MgUser cmdlet:

Get-MgGroupMember -GroupId $GroupId -All | ForEach {Get-MgUser -UserId $_.Id}

You can look at the AdditionalProperties property retuned by Get-MgGroup to find information about the group members. For example, this command returns a list of display names for group members:

$GroupData = Get-MgGroupmember -GroupId $GroupId
$GroupData.AdditionalProperties.displayName

Adding a group owner is a little complicated because the owner is stored by reference to its object rather than as a simple property. The New-MgGroupOwnerByRef cmdlet requires the identifier for the owner’s account to be passed in a hash table:

New-MgGroupOwnerByRef -GroupId $GroupId -AdditionalProperties @{"@odata.id"="https://graph.microsoft.com/v1.0/users/2a3b60f2-b36b-4758-8533-77180031f3d4"}

To remove a member from a Microsoft 365 group, use the Remove-MgGroupMemberByRef cmdlet. This cmdlet doesn’t work with distribution lists or mail-enabled security groups. This command removes the user object pointed to by the GUID from a target group identified by the $GroupId variable.

Remove-MgGroupMemberByRef -DirectoryObjectId 08dda855-5dc3-4fdc-8458-cbc494a5a774 -GroupId $GroupId

Removing Groups

The Remove-MgGroup cmdlet removes a Microsoft 365 group or security group. For example:

Remove-MgGroup -GroupId f6dd8a3e-d50c-4af2-a9cf-f4adf71ec82b

The cmdlet can’t remove distribution lists or mail-enabled security groups. You must do this with the Exchange Online cmdlets.

Restore Deleted Groups

Deleted groups remain in a soft-deleted state for 30 days following their deletion. During this time, it’s possible to restore the group using the Restore-MgGroup cmdlet. To find the set of soft-deleted groups awaiting permanent removal, take the code to find soft-deleted users in this article and amend the Get-MgDirectoryDeletedItem cmdlet to look for microsoft.graph.group objects instead of microsoft.graph.user.

The report lists the set of soft-deleted groups, including their identifiers. To restore a group, run the Restore-MgDirectoryDeletedItem-MgGroup cmdlet and pass the identifier of the group:

Restore-MgDirectoryDeletedItem -DirectoryObjectId 4e9393c3-67e9-4f95-a0df-70103a667c0a

Finding Group Objects

The Get-MgGroup cmdlet fetches details of Entra ID groups. To retrieve a single group, use its display name as a filter:

Get-MgGroup -Filter "DisplayName eq 'Leadership Team'"

You can also search using the StartsWith filter:

 Get-MgGroup -Filter "startsWith(displayname, 'Leadership')"

If you add the All parameter, you’ll get all the groups in the tenant.

[array]$Groups = Get-MgGroup -All

The command returns groups of all types. To filter out the various types of groups, we can check different properties to identify each type of group. Table 1 lists useful properties to check.

PropertyUsed by
MailEnabled = TrueDistribution lists
Microsoft 365 groups
Mail-enabled security groups
SecurityEnabled = TrueSecurity groups
Mail-enabled security groups
GroupTypes = UnifiedMicrosoft 365 groups
GroupTypes = DynamicMembershipDynamic groups
GroupTypes = Unified, DynamicMembershipDynamic Microsoft 365 groups
ResourceProvisioningOptions = TeamTeam-enabled Microsoft 365 groups
Table 1: Filters for different types of Entra ID groups

The simplest filters are those which find groups based on a property. For example, to find all security-enabled groups:

Get-MgGroup -Filter “securityEnabled eq true” -All

Find all mail-enabled groups:

Get-MgGroup -Filter “mailEnabled eq true” -All

The GroupTypes and ResourceProvisioningOptions properties require complex filters with Lambda operators. For example, to find the set of Microsoft 365 groups in the tenant:

[array]$M365Groups = Get-MgGroup -Filter "groupTypes/any(c:c eq 'unified')" -All 

To find the set of dynamic Microsoft 365 Groups:

Get-MgGroup -Filter "groupTypes/any(c:c eq 'dynamicmembership') and groupTypes/any(x:x eq 'unified')" -All

To find the set of Microsoft 365 groups enabled for Teams:

[array]$Teams = Get-MgGroup -Filter "resourceProvisioningOptions/Any(x:x eq 'Team')" -All

In addition, client-side filter can refine the results returned by the server. For instance, after fetching all security-enabled groups, we use a client-side filter to find the set with dynamic membership:

[array]$SecurityGroups = Get-MgGroup -Filter “securityEnabled eq true” -All 
[array]$DynamicSecurityGroups = $SecurityGroups | ? {$_.GroupTypes -eq “DynamicMembership”}

Filter Speed

The filters used by Get-MgGroup are server-side, meaning that the data is filtered when the server returns it to PowerShell. Because they’re Graph-based and return fewer properties than cmdlets like Get-UnifiedGroup, these commands are very fast, which makes them worth considering if you have scripts which fetch subsets of groups for processing.

As an example, I replaced calls to the Get-UnifiedGroup and Get-AzureADMSGroup cmdlets in a script to report Microsoft 365 groups under the control of the Groups expiration policy with Get-MgGroup and processing time fell from 30 seconds to 1.9 seconds.

Complex Queries Against Entra ID Groups

Get-MgGroup supports complex queries against Entra ID. Essentially, a complex query is one that the Microsoft Graph resolves against a separate property store. It’s not always obvious when a complex query is necessary. Microsoft could hide this need in code instead of forcing PowerShell coders to remember when they must add the ConsistencyLevel parameter to mark a query as complex. Searching the display name of groups for a term is an example of a complex query.

[array]$Groups = Get-MgGroup -Search '"displayname:Office 365"' -ConsistencyLevel Eventual

Another example is to use the Filter parameter to find groups which start with a value. For instance, we might want to find groups whose display name starts with Office:

[array]$Groups = Get-MgGroup -Filter "startsWith(DisplayName, 'Office')" -ConsistencyLevel Eventual

An Evolving Story

Microsoft’s documentation for migration of Azure AD cmdlets admits “There is currently no tool to automatically converts scripts in Azure AD PowerShell to Microsoft Graph PowerShell.” I don’t anticipate that such a tool will appear. As described here, its Graph foundation mean that the ways of performing actions against Entra ID groups with the Microsoft Graph PowerShell SDK differ from how things work with the Azure AD module. It will take time and effort to master the details. Hopefully, the advice contained here helps that evolution.


Keep up to date with developments like the migration from the cmdlets in the Azure AD module to the Microsoft Graph SDK for PowerShell 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/29/create-entra-id-group/feed/ 6 54278
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
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
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
How to Create an Entra ID B2B Collaboration Policy https://office365itpros.com/2021/05/10/entra-id-b2b-collaboration-policy/?utm_source=rss&utm_medium=rss&utm_campaign=entra-id-b2b-collaboration-policy https://office365itpros.com/2021/05/10/entra-id-b2b-collaboration-policy/#comments Mon, 10 May 2021 01:20:00 +0000 https://office365itpros.com/?p=49667

Deny Guests from Some Domains or Use an Allow List

Updated: 5 September 2023

The ability for applications to use Entra ID B2B collaboration to add guest users is governed by external collaboration settings, aka the Entra ID B2B collaboration policy (previously the Azure AD B2B Collaboration policy). The settings are available through the External identities section of the Entra ID admin center, where they are found under Collaboration restrictions (Figure 1).

Entra ID External Collaboration Settings
Figure 1: Entra ID External Collaboration Settings

Three options are available:

  • Allow guest accounts from any external domain. This is the default.
  • Deny access to guest accounts from specified domains (deny list).
  • Allow access only to guest accounts from specified domains (allow list).

The total size of the policy must be less than 25 KB (25,000 characters). Each domain in an allow or deny list counts against the limit as do other policy settings. Allowing 1,000 bytes for all other settings, an average of 15 characters per domain means that an allow or deny list can accommodate up to 1,600 domains. You can only choose to have a policy with an allow or a deny list and cannot have some domains in a deny list and others in an allow list.

In my case, I use the middle approach to block guest accounts from certain domains. For instance, these might be domains belonging to direct competitors or domains used for consumer rather than business purposes. In Figure 1, you can see that I’ve decided to block access to guests with Google.com and Yahoo.com email addresses.

Entra ID Blocks Bad Guests

Entra ID applies the block rather than applications. For example, in Figure 2, I’ve tried to add a new guest account to Teams, which doesn’t object when I enter tredmondxxxx@yahoo.com to identify the guest. The block descends when Teams tries to create the new guest account in Entra ID. The “Something went wrong” is an uncertain error, but it should be enough for the administrator to know what’s going on when they learn where the guest comes from. OWA doesn’t object to the email address for a new guest but is no more definite in its error (Figure 3). Again, this is because the application fails to create a new guest account in Entra ID.

Teams can't add a new guest account because the Entra ID B2B collaboration policy blocks the user's domain
Figure 1: Teams can’t add a new guest account because the Entra ID B2B collaboration policy blocks the user’s domain

OWA runs into the same problem when a group owner attempts to add a new guest account
Figure 3: OWA runs into the same problem when a group owner attempts to add a new guest account

Knowing What Domains Guests Come From

Before going ahead to update your external collaboration settings, it’s a good idea to understand where current guest accounts come from. This code scans down through guest accounts found in Entra ID to capture details of each user’s home domain. It then populates a hash table with the domain information to create a count for each, followed by sorting in descending order to discover the most popular domains:

$Domains = [System.Collections.Generic.List[Object]]::new()
Connect-MgGraph -NoWelcome -Scopes Directory.Read.All
[array]$Guests = (Get-MgUser -All -Filter "UserType eq 'Guest'" | Select-Object Displayname, UserPrincipalName, Mail, Id | Sort DisplayName)

ForEach ($Guest in $Guests) {
   $Domain = ($Guest.UserPrincipalName.Split("#EXT#")[0]).Split("_")[1]
   $Domains.Add($Domain)
}

$DomainsCount = @{}
$Domains = $Domains | Sort-Object
$Domains | ForEach {$DomainsCount[$_]++}
$DomainsCount = $DomainsCount.GetEnumerator() | Sort-Object -Property Value -Descending
$DomainsCount


Name                           Value
----                           -----
microsoft.com                  59
outlook.com                    11
quest.com                      6
hotmail.com                    5
gmail.com                      4
emea.teams.ms                  4

Now you know what domains are actively in use, you can decide which you might like to ban. Remember that putting a domain on the deny list stops only the creation of new guest accounts. Existing guest accounts remain in the membership of groups and teams. If you want to purge accounts from unwanted domains, you need to find the groups (teams) with guest members and examine each guest to decide if they can stay or be removed. It’s easy enough to find guests from banned domains with PowerShell, or so the saying goes…


The Office 365 for IT Pros eBook is packed full of practical information like this. Learn from the pros by subscribing to Office 365 for IT Pros and receive monthly updates during your subscription period.

]]>
https://office365itpros.com/2021/05/10/entra-id-b2b-collaboration-policy/feed/ 10 49667
How to Review and Clean Up Entra ID Enterprise Apps https://office365itpros.com/2021/04/28/enterprise-apps-cleanup/?utm_source=rss&utm_medium=rss&utm_campaign=enterprise-apps-cleanup https://office365itpros.com/2021/04/28/enterprise-apps-cleanup/#comments Wed, 28 Apr 2021 01:55:00 +0000 https://office365itpros.com/?p=49485

Remove Old, Obsolete, or Unwanted Enterprise Apps

Vasil Michev’s article about creating a Graph-based PowerShell script to generate an inventory of Entra ID integrated apps and their permissions caused me to think about this dark corner. Microsoft’s Philippe Signoret covers some of the same ground with his PowerShell script, used in Microsoft’s documentation for detection and remediation of illicit consent grants, a topic which became more important in the light of the SolarWinds hack last year. As discussed here, you can also interrogate the Microsoft 365 audit log to report events captured when apps receive consent.

In any case, the point is that apps with permissions exist in a tenant and it’s good to know what the apps are, why they have permissions, and if they are still needed. An increasing number of ISV and other apps use the Graph APIs to interact with Microsoft 365 data. Each of these apps needs an OAuth 2.0 consent to interact with the Graph and ends up as an Entra ID integrated app (more commonly known as enterprise apps). By running Vasil’s script, I found 58 apps in my tenant. Based on what I see in other tenants, this is not uncommon.

Update: You can use Get-MgServicePrincipal cmdlet from the Microsoft Graph PowerShell SDK to return a list of enterprise apps known in a tenant.

Reviewing Enterprise Apps

Although you could review the set of enterprise apps through the Entra ID admin center (Figure 1), it’s often easier to perform a review using a shared resource like the CSV file generated by the script.

Enterprise apps in the Entra ID admin center.

Azure AD app permissions
Figure 1: Enterprise apps in the Entra ID admin center

To make the data easier to work with, after running the script to generate the CSV files, I converted the CSV to an Excel worksheet formatted as a table and imported it into Microsoft Lists in Teams. Storing the data in a list accessed through a tab in a channel makes the information very accessible to people who might know what function apps serve (if any). I added a couple of fields to track the apps during the review, including creating a category to classify the apps and a notes field to capture comments made by reviewers. Here are the set of categories I used:

  • Microsoft apps.
  • Trial apps installed for testing purposes.
  • ISV apps still in use.
  • Tenant Apps registered to use PowerShell to call Microsoft Graph APIs.
  • Apps requiring further investigation.
  • Unwanted apps which can be removed.

Figure 2 shows how the list of apps for review appears in Teams.

Reviewing details of enterprise apps in a list in Teams.
Figure 2: Reviewing details of enterprise apps in a list in Teams

Looking through the set of apps uncovered some interesting items. For instance, a bunch of apps exist to help with registering users for conferences. If you’ve ever attended a Microsoft event like Ignite, you’ll probably find an app called “Microsoft Events” with permissions to read user profiles. Sessionize.com has an app with the same permissions to help people like me submit sessions to conferences, while the EventPoint sign-in app seems to serve the same purpose while demanding access to users’ email addresses. And finally, the Nubelus app is, I think, used by the European Collaboration Summit, but limits itself to delegated permissions for selected users (me, in this case).

Each app needs careful examination to understand its purpose, who uses the app, and the permissions it holds. Bringing the information about the apps into the list made that review quicker and easier.

Focusing on Problem Apps

The highlighted app (CXP Previews Portal is a good example of a questionable app. Examining details of the app (Figure 3), we discover that its home page is http://bf.net.nz/, located in New Zealand and that its creation date (in the tenant) was 20 December 2016. Access is valid until 18 June 2017, so it is obvious that this app is unused and a prime candidate for renewal. The other information captured for the app makes me think that this app is used to gain access to some Microsoft previews (CXP is a Microsoft acronym for Customer Experience Program). All in all, this app is a great candidate for removal.

Examining details of an enterprise app.
Figure 3: Examining details of an enterprise app

In total, the review highlighted 16 unwanted apps which could be removed immediately along with several others which needed more investigation. These apps belong to trials that I had signed up for in the past (like the four apps registered for Office365mon.com), others for services I looked at but never used, like Microsoft FastTrack, and some were old Microsoft pilot apps, like CollabDB, part of the Project Osaka initiative from 2017. I remembered some apps, while others needed an internet search to fill in the gaps. In many cases, several years (going back to 2015) had lapsed since the app was granted permissions.

Removing Unwanted Enterprise Apps

To remove an app, go to the Enterprise applications section of the Entra ID admin center and select the app. Click properties in the left-hand pane to reveal the option to delete the app (Figure 4). Click Delete and confirm to remove the app. The Entra ID admin center lists 50 apps in its UI, so if your tenant has more than 50 apps, you must search using the app id in to view its properties.

Removing an enterprise app.
Figure 4: Removing an enterprise app

If you remove an app in error, it’s easy for an administrator to grant consent to the app and its required permissions the next time the app is needed.

After removing the 16 unwanted apps, my set of enterprise apps is now down to 42. I’m now gathering information about the seven apps which need further investigation (if I were bold, I would delete the apps to see what happens, but that’s seldom a good plan).

Time for an Enterprise App Spring Clean

What this exercise proves is that the set of apps integrated with Entra ID tends to grow over time and is not managed in any way by Entra ID. It’s up to administrators to audit the set of apps in their tenant and decide which apps remain useful and which can be discarded. Apart from cleaning out old apps, the purpose of the audit is to ensure that bad actors can’t leave highly permissioned apps behind to use after an initial visit.

The script described in Vasil’s article is a good starting point for an audit. Putting the results of the script into a Microsoft list makes the app more accessible and easier to work with. At the end of the day, humans must decide what apps to keep. Based on my experience, it should be possible to remove between 30-40% from a tenant. Your mileage may vary!


Apart from users and groups, it’s often surprising how little attention the contents of Entra ID receives from tenant administrators. Learn more by subscribing to the Office 365 for IT Pros eBook. We might not cover everything there, but what we do cover is important…

]]>
https://office365itpros.com/2021/04/28/enterprise-apps-cleanup/feed/ 13 49485
Keeping an Accurate Microsoft 365 Tenant Directory is Important https://office365itpros.com/2020/11/25/entra-id-account-properties/?utm_source=rss&utm_medium=rss&utm_campaign=entra-id-account-properties https://office365itpros.com/2020/11/25/entra-id-account-properties/#comments Wed, 25 Nov 2020 09:09:19 +0000 https://office365itpros.com/?p=35252

Cherish the Accuracy of Entra ID Account Properties

Every Microsoft 365 tenant uses Azure Active Directory to store information about the tenant configuration, accounts, and groups. Maintaining accurate Entra ID user account properties is important. Whether data comes from an external source like a HR feed or is maintained manually, people depend on directory information to find others, or even understand how the organization works. If the data in your directory is inaccurate, some features won’t work properly or at all. For example:

  • The people card (which makes the Intelligent Search of Microsoft 365 rather stupid)
  • Teams organization tab (Figure 1) because reporting relationships won’t be correct.
  • Dynamic distribution lists and dynamic Microsoft 365 groups because the right people won’t be found by the queries underpinning dynamic lists and groups.

The Teams organization tab depends on accurate Entra ID account properties.
Figure 1: The Teams organization tab depends on accurate Entra ID account properties

It’s always been important to maintain an accurate directory. Perhaps it was less so in the on-premises world where fewer application features are built with an expectation that directory data is accurate, but it’s obvious that Microsoft 365 just works better with a solid directory.

Setting Goals for a Healthy Directory

You can invest in a product like Hyperfish to help analyze and maintain your Entra ID data, but before you rush into acquiring a sticking plaster to cure your directory woes, it’s a good idea to set down some threshold for directory quality. For example, you could say that your baseline measurement for a healthy directory is that all the properties displayed on the people card should be fully populated for every user account. Separate guidelines might be defined for guest accounts and groups.

Figure 2 shows a customized people card. Being able to customize the people card using Microsoft Graph commands allows tenants to expose the information they consider essential in the card, and it’s important to consider customization when setting your threshold.

Entra ID user account information is shown in the Microsoft 365 people card.
Figure 2: Entra ID user account information is shown in the Microsoft 365 people card

Checking Entra ID Account Properties with PowerShell

Setting an aspirational goal is nice, achieving that goal is even better. We need to understand how healthy our directory is in terms of missing properties that show up in the people card. Fortunately, this is easy to create a PowerShell script to:

  • Find mailbox-enabled user accounts in Entra ID.
  • Check accounts for missing properties (like not having values in the Office or Title properties).
  • Report what needs to be done in terms of account updates.

I’ve written a quick and dirty script which you can download from GitHub. It uses the Get-User cmdlet from the Exchange Online Management module to fetch account information. The Get-MgUser cmdlet from the Microsoft Graph PowerShell SDK could also be used, but it’s easier to filter out mailbox-enabled accounts with Get-User, which exposes the Entra ID user properties we want to check. Remember that you’ll need to modify the script to suit the circumstances in your organization. For instance, if you place particular importance on a specific property, you might want to amend the script to include that property in the checks.

Figure 3 shows how the script reports the problems it finds with missing properties in user accounts. The results shown here are from a small test tenant so it’s unsurprising to discover that so many accounts have missing properties. It’s reasonable to expect better results in a production tenant.

PowerShell finds some missing values for Entra ID account properties.
Figure 3: PowerShell finds some missing values for Entra ID account properties

To make it easy for administrators to track down and fix missing properties. a CSV file is also generated with details of the accounts which need adjustment (Figure 4).

Viewing the CSV file of missing directory properties
Figure 4: Viewing the CSV file of missing directory properties

Although it can be a boring task, maintaining the accuracy of Entra ID user data can be boring. It’s much more interesting to read the Office 365 for IT Pros eBook and learn about changes in Office 365 through the updates we release every month.

]]>
https://office365itpros.com/2020/11/25/entra-id-account-properties/feed/ 3 35252
The 1-2-3 of Exchange Online Certificate Based Authentication for PowerShell https://office365itpros.com/2020/08/13/exchange-online-cba/?utm_source=rss&utm_medium=rss&utm_campaign=exchange-online-cba https://office365itpros.com/2020/08/13/exchange-online-cba/#comments Thu, 13 Aug 2020 09:13:13 +0000 https://office365itpros.com/?p=21438

Exchange Online CBA One Way to Replace Basic Authentication

Updated 1-Nov-2023

The writing team for the Office 365 for IT Pros eBook very much appreciate the work done by our redoubtable technical editor, Vasil Michev, when he probes and questions text in the book chapters. It’s nice to get some of our own back when Vasil commits himself to print.

Take his article on certificate-based authentication for Exchange Online PowerShell. There’s a lot of good stuff here, but Vasil’s mind runs at such a fast rate that he sometimes omits details when he explains something that end up stopping people being able to master a topic. This is when people with slower CPUs, like me, step in to ask irritating questions.

Three Steps to Certificate-Based Authentication

Batch jobs that need to interact with Exchange Online PowerShell cannot use basic authentication any longer. The solution is to use certificate-based authentication instead. The mechanism might seem complex when you first read Microsoft’s instructions, but it can be boiled down to three points:

  • An app registered with Entra ID. The app is the entry point to Exchange Online PowerShell because it is granted permission (as a service principal) to perform administrative actions. You might have used apps registered with Entra ID to interact with the Microsoft Graph where these apps hold the Graph API permissions necessary to access data.
  • A self-signed X.509 certificate used to authenticate the application against Azure AD when access is needed.
  • Microsoft 365 is a massive multi-tenant environment, so you need to tell Exchange Online which tenant to connect to be able to run PowerShell in a background process.

The same process to enable certificate-based authentication can be used with other Microsoft 365 modules that support certificate-based authentication such as the Microsoft Teams module.

Creating the App and Assigning Permissions and Role

Creating the app and assigning the necessary Exchange.AsManageApp permission and administrative role is quickly done in the Entra ID admin center. These are one-time operations that don’t need to be automated in code. However, it’s worth noting that role assignments can be made with PowerShell. It’s useful to know how to do this because you might use the technique in future to assign a role to a user.

This code fetches the object identifier for the app (it’s called “Exo Background process”) and assigns the Exchange Administrator directory role to the app.

$ExoAppSp = (Get-MgServicePrincipal -Filter "DisplayName eq 'Exo Background Process'").Id
$ExoRoleId = (Get-MgDirectoryRole | Where-Object {$_.DisplayName -eq "Exchange  Administrator"}).Id
$NewAssignee = @{
  "@odata.id" = ("https://graph.microsoft.com/v1.0/directoryObjects/{0}" -f $ExoAppSp)
  }
New-MgDirectoryRoleMemberByRef -DirectoryRoleId $ExoRoleId -BodyParameter $NewAssignee

The same approach is used to assign a role to a user. The difference is that the object identifier for the user is fetched with the Get-MgUser cmdlet. This example shows how to assign the Global Reader role to a user.

$UserId = (Get-MgUser -UserId Otto.Flick@Office365itpros.com).Id
$RoleId = (Get-MgDirectoryRole |  Where-Object {$_.DisplayName -eq "Global Reader"}).Id
$NewAssignee = @{
  "@odata.id" = ("https://graph.microsoft.com/v1.0/directoryObjects/{0}" -f $UserId)
  }
New-MgDirectoryRoleMemberByRef -DirectoryRoleId $RoleId -BodyParameter $NewAssignee

With that diversion taken care of, we can proceed to obtaining a self-signed X.509 certificate, which is where people sometimes become stuck (I did).

Creating the Certificate

You need to upload a suitable X.509 self-signed certificate to the Azure AD app to create an association between the two. Certificates are not the easiest of objects to work with, but in this case it’s straightforward.

If you don’t have a suitable certificate to hand, you must generate one. Vasil was short on detail on this point (until I asked him how he had generated a certificate). Microsoft recommends using either the Create-SelfSignedCertificate.ps1 script or the MakeCert command-line utility. These are certainly viable options, but the easiest way is to run the New-SelfSignedCertificate cmdlet using a command like this:

New-SelfSignedCertificate -Subject "Exo Background Process" -CertStoreLocation "cert:\CurrentUser\My" -KeySpec KeyExchange -FriendlyName "For EXO V2 Background Jobs"

This command creates a certificate valid for a year in the personal store of the user, which is fine for testing (Figure 1). Obviously, in a production environment, you’d create the certificate in the personal store of the account that will be used to run batch jobs.

 self-signed certificate created with the New-SelfSignedCertificate cmdlet

Exchange Online CBA
Figure 1: A self-signed certificate created with the New-SelfSignedCertificate cmdlet

To associate the certificate with the app, export the certificate as a DER-encoded binary X.509 file (Figure 2). You can call the file anything you like, but you should give it a .CER extension.

Exporting the self-signed certificate
Figure 2: Exporting the self-signed certificate

Finally, upload the exported file to the app in the Azure AD portal. This will generate a thumbprint that you need to note (Figure 3).

Importing the self-signed certificate into the registered Azure AD app
Figure 3: Importing the self-signed certificate into the registered Azure AD app

Bringing it Together with Exchange Online CBA

After setting up the app, the three vital pieces of information we need to connect are:

  • The AppId of the application you created.
  • The thumbprint of the certificate loaded into the app.
  • The service domain for your tenant (like tenant.onmicrosoft.com).

With these values, you can connect to Exchange Online using certificate-based authentication with a command like:

Connect-ExchangeOnline -CertificateThumbprint "40EED7993F65D8CF13D5ABAC87F3AAD307012D22" -AppId "b83c46c6-044e-40e5-929c-634f80045a11" -ShowBanner:$false -Organization tenant.onmicrosoft.com

There are obviously more complications that await the unwary along the way, but this is enough to connect and play with Exchange Online PowerShell in batch jobs. Vasil’s post contains a lot of detail and there will be more articles and guidance published as we approach the deadline for basic authentication to disappear next year, which is a good reason to subscribe to a reliable source of information like the Office 365 for IT Pros eBook.

]]>
https://office365itpros.com/2020/08/13/exchange-online-cba/feed/ 18 21438
Block Guest Members for Individual Microsoft 365 Groups and Teams https://office365itpros.com/2018/11/04/block-guest-access-to-teams/?utm_source=rss&utm_medium=rss&utm_campaign=block-guest-access-to-teams https://office365itpros.com/2018/11/04/block-guest-access-to-teams/#comments Sun, 04 Nov 2018 18:40:26 +0000 https://office365foritpros.com/?p=900

Block Guest Access to Teams with Group Settings

Updated 10-Oct-2023

Block guest access to Teams

By default, Microsoft 365 tenants can add guest users (people with accounts outside your tenant) to the membership of Microsoft 365 Groups (and Teams). In this article, we’ll explore how to block guests for individual groups and teams.

Tenants control guest access through the Azure Active Directory policy for Groups, which has two relevant settings:

  • AllowToAddGuests: Controls if group (or team) owners can add guest users to membership. The default is True.
  • AllowGuestsToAccessGroups: Controls if guest accounts can access resources through Office 365 Groups. The default is True.

Settings in the Entra ID directory policy for Microsoft 365 Groups can be changed through PowerShell. For instance, to block group owners from being able to add guests, you change the value of AllowToAddGuests to False. These command fetch the current settings, update the value, and update the policy (assuming that you have already created a tenant policy):

Connect-MgGraph -Scopes Group.Read.All, Directory.ReadWrite.All

$TenantSettings = Get-MgBetaDirectorySetting | Where-Object {$_.DisplayName -eq "Group.Unified"}
$Values = $TenantSettings.Values
($Values | Where-Object Name -eq 'AllowToAddGuests').Value = "false"
Update-MgBetaDirectorySetting -DirectorySettingId $TenantSettings.Id -Values $Values

Guests who are members of groups can continue to use their membership. The block simply stops group owners adding new guests. See this article for more information about configuring and managing the settings of the Entra ID policy for Microsoft 365 groups.

Block Guest Access to Teams and Groups on an Individual Basis

The normal course of events is to allow guest users for groups and selectively block access for specific groups that hold confidential information. It’s relatively easy to find and block access to selected groups. In the following example, the code:

  • Find the group policy template object for the tenant.
  • Finds a set of Microsoft 365 groups whose classification is set to “Secret.” You could use whatever filter you like to find the set of target groups.
  • Checks if an existing custom setting exists for a group. If one isn’t present, the code applies a new setting to block guest access. If one is, the setting is updated to block guest access.
$GroupTemplate = (Get-MgBetaDirectorySettingTemplate | Where-Object {$_.DisplayName -eq "Group.Unified.Guest"})
[array]$Groups = (Get-UnifiedGroup -ResultSize Unlimited | Where-Object {$_.Classification -eq "Secret"})

ForEach ($Group in $Groups) {
    $GroupSettings = Get-MgGroupSetting -GroupId $Group.ExternalDirectoryObjectId 
    If ($GroupSettings) {
       # Policy settings already exist for the group - so update them
       $GroupSettings = Get-MgGroupSetting -GroupId $Group.ExternalDirectoryObjectId
       Update-MgGroupSetting -GroupId $Group.ExternalDirectoryObjectId -TemplateId $GroupTemplateId `
         -GroupSettingId $GroupSettings.Id -Values (@{'name'='AllowToAddGuests';'value'='false'}) | ConvertTo-Json
       Write-Host ("External Guest accounts blocked for {0}" -f $Group.DisplayName) 
    } Else {
       # Settings do not exist for the group - so create a new settings object and update
       $Status = New-MgGroupSetting -GroupId $Group.ExternalDirectoryObjectId -TemplateId $GroupTemplateId `
           -Values (@{'name'='AllowToAddGuests';'value'='false'}) | ConvertTo-Json
       Write-Host ("New settings created and guests blocked for {0}" -f $Group.DisplayName) 
    }
}

The process of updating the directory setting to block guests in teams and groups happens when you apply a sensitivity label that blocks guests in teams and group.

Block Guest Access to Teams Individually Trumps Tenant Setting

Some people would like to reverse the process and block guest access to all groups except on a selective basis. This isn’t possible because the tenant-level block trumps settings at an individual group level. Once you set AllowToAddGuests to False at the tenant level, the policy stops any group owner from adding guests to group membership. Only administrators keep the ability to add guests, and they can only do so through an admin interface like running the Add-UnifiedGroupLinks cmdlet or updating group membership in the Microsoft 365 Admin Center or Entra ID admin center.

If you want to block access for guests to all but a small set of groups, you must leave AllowToAddGuests as True at the tenant level and then block all but the set of groups you want to allow guests to join.

Block Guest Access to Teams Through Sensitivity Labels

Generally available from June 2020, if you enable sensitivity labels for use with Groups, Teams, and Sites, the container settings in the labels can be used to block guest users. For example, you can have a label called Confidential which, when applied to a group, stops new guests being added. Existing guests aren’t removed, but you can find them as described here.


This is the kind of topic we cover in Managing Groups chapter of the Office 365 for IT Pros eBook. You can find a lot more about managing Groups there.

]]>
https://office365itpros.com/2018/11/04/block-guest-access-to-teams/feed/ 3 900
How to Create Org-Wide Teams in Microsoft Teams https://office365itpros.com/2018/10/10/create-org-wide-team/?utm_source=rss&utm_medium=rss&utm_campaign=create-org-wide-team https://office365itpros.com/2018/10/10/create-org-wide-team/#comments Wed, 10 Oct 2018 08:30:42 +0000 https://office365foritpros.com/?p=744

Creating Org-wide Team to Communicate with Everyone in Small Organizations

In the latest update distributed to Microsoft 365 tenants, Microsoft includes the ability to create org-wide teams, but only if your tenant has fewer than 10,000 user accounts (see below). If your tenant is under the threshold, you can create up to five org-wide teams.

Company-wide Communications

An org-wide team is designed to facilitate tenant-wide communications for small to medium companies without the need for an administrator or team owner to manually add all the employees to the team membership, including the need to check for new employees and add them periodically. As we explain in the Office 365 for IT Pros eBook, the process of creating a team and populating its membership with PowerShell is not difficult, but some work needs to be done to maintain the membership afterwards.

To create an org-wide team, choose Join or create a team as usual, opt to create a team from scratch, and then choose org-wide from the type of teams available (Figure 1). The choice only appears to global tenant administrators.

Creating a new org-wide team in Microsoft Teams
Figure 1: Creating a new org-wide team

Automatically-Generated Membership

When you create an org-wide team, Teams adds all the global admins as team owners. It then adds all “active users” as members. The theory is that accounts that don’t have Office 365, Microsoft 365, or Teams licenses are excluded, as are guest users, and in the past it was certainly true that some odd accounts turned up in org-wide teams, including:

  • Shared mailboxes.
  • Room and resource mailboxes.
  • Service accounts (if they have an Microsoft 365 license).
  • Mailboxes used for purposes such as DLP incident reports. These accounts might be licensed, but they shouldn’t really feature in a team.
  • Accounts that have a suitable license (like Office 365 E3) but where the Teams service plan is disabled.

Although Microsoft has now fixed the underlying bugs which caused these accounts to be included in org-wide teams, it is still good practice to check the membership after the team is created and remove any account that doesn’t belong. And like for any team with a large membership, consider updating team settings to stop members posting in the General channel, adding channels, or even using @team mentions (because they generate notifications for everyone in the team).

Org-wide teams are indicated by the presence of the Org-wide tag. In addition, as you can see in Figure 2, the Manage team option displays a banner to inform users that the membership of the team is automatically adjusted in line with changes made to Entra ID.

Membership of an org-wide team
Figure 2: Membership of an org-wide team

On an ongoing basis, employees leave and join the company and people lose or gain Teams licenses. When someone leaves the company and their Entra ID account is removed, their membership of the team is also removed. To handle new joiners and people who gain or lose Teams licenses, a background process scans the accounts in the tenant periodically (expect weekly) and adds or removes the user as required. Unlike normal teams, members can’t choose to leave an org-wide team.

But I Already Have an Org-Wide Team

If you already created and use an all-employees team without benefit of Microsoft’s new feature, a tenant administrator can convert the team into an org-wide team and gain benefit of the automatic membership management. To do this, select the team you want to convert and then use the Edit team feature to change the privacy setting to org-wide. When you save the setting, Teams updates the membership with all valid accounts. Any users not included in the automatic membership remain in place, including guest users. You can also change an org-wide team to be a private or public team using the same approach, and in this case, the existing membership stays in place but the automatic background refresh of membership is disabled.

Alternatives to Creating Org-Wide Teams

Larger tenants who have more accounts than the maximum limit for org-wide team membership can consider:

  • Using dynamic Teams to support discussions for different parts of the organization. For example, you might have a team for each department or each country. Dynamic teams are based on dynamic Microsoft 365 groups.
  • Using Viva Engage communities for company-wide communications and collaboration. Viva Engage can easily scale up to handle very large organizations with hundreds of thousands of users.

Remember, a regular team can support up to 25,000 members, so you can always use PowerShell to generate and manage a regular team which effectively serves as an org-wide team.

P.S. The formal documentation for org-wide teams is online. But the book tells you what really happens…


We have over 200 pages of content about Teams in the Office 365 for IT Pros ebook. Shouldn’t you be staying updated with developments about Teams and the rest of Office 365? Subscribe today!

]]>
https://office365itpros.com/2018/10/10/create-org-wide-team/feed/ 11 744