Microsoft Entra ID – Office 365 for IT Pros https://office365itpros.com Mastering Office 365 and Microsoft 365 Mon, 26 Aug 2024 13:47:00 +0000 en-US hourly 1 https://i0.wp.com/office365itpros.com/wp-content/uploads/2024/06/cropped-Office-365-for-IT-Pros-2025-Edition-500-px.jpg?fit=32%2C32&ssl=1 Microsoft Entra ID – Office 365 for IT Pros https://office365itpros.com 32 32 150103932 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, so I updated the code 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 direct 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/ 15 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/#respond 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/ 0 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/#respond 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/ 0 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/ 4 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/ 11 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-MgBetaAdministrativeUnit cmdlet to create a new administrative unit (the beta module of the Microsoft Graph PowerShell SDK is needed because the feature is in preview).
  • Calculate the membership rule to find accounts belonging to the department.
  • Run the Update-MgBetaAdministrativeUnit 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-MgBetaAdministrativeUnit -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-MgBetaAdministrativeUnit -AdministrativeUnitId $NewAdminUnit.Id -BodyParameter $UpdateAUParameters
        } Catch {
            Write-Host ("Error updating {0} with dynamie properties" -f $NewAdminUnit.DisplayName )
        }
        Write-Host ("Created dynamic administrative unit for the {0} department called {1}" -f $Department, $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.

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/ 3 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. This action requires the Mail.Send.Shared Graph permission and uses the Send-MgUsermail cmdlet using a variety 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
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 betweenEntra 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.

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/ 4 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/ 6 744