Make Sure the Right People can use the Graph SDK
In September 2021, I first started to write about using the Microsoft Graph PowerShell SDK. In that first article, I noted the potential problem caused by the way that the service principal created to hold permissions assigned to the SDK accumulated over time. Eventually, anyone who signed into the SDK interactively using the Connect-MgGraph cmdlet ended up with access to a wide range of permissions. The same behavior persists in version 2 of the SDK. Given the importance of using the least permission model for administrative operations, the accrual of permissions is not a good thing.
The Microsoft Graph PowerShell SDK is not like other Microsoft 365 PowerShell modules like the Exchange Online or SharePoint Online management modules. These modules assume use by administrators and their cmdlets work on that basis. Of course, if someone without an administrator role signs in to use these modules, permissions limit them to accessing their own data.
In terms of how tenants use the Graph SDK, I think we can break usage down as follows:
- Interactive use: Testing SDK cmdlets and script development. This includes using the PowerShell ISE or Visual Studio code when writing scripts. Interactive use is limited to delegate permissions.
- Production scripts: Production scripts with SDK cmdlets that use registered apps with X.509 certificates for authentication. These scripts can use application permissions, so they have access to data across the tenant.
- Scheduled scripts: Azure Automation scheduled runbooks with managed identities for authentication.
Some argue that you should limit interactive access to the Microsoft Graph PowerShell SDK. I’m not sure that such a stance is reasonable. Developers need interactive access to test scripts, compare V1.0 cmdlets against beta cmdlets, and so on. A script that can’t run successfully interactively is unlikely to automagically fix its problems to run under Azure Automation. I also think that interactive access is useful for running one-off command scripts on an on-demand basis.
In summary, interactive access to the SDK is a necessary evil. This raises the question of how best to secure interactive access so that only the right people can run SDK cmdlets interactively. Here are some approaches to consider.
Restricting Access to the SDK App
The installation of the Microsoft Graph PowerShell SDK in a tenant creates an enterprise app in the tenant’s instance of Entra ID (Azure Active Directory). The app’s service principal holds the permissions assigned to the SDK. The SDK app always has an app identifier of 14d82eec-204b-4c2f-b7e8-296a70dab67e (Figure 1).
To restrict access to the SDK to selected users, edit the app’s properties and:
- Access the Users and groups tab to add access assignments for the people that you want to be able to sign in interactively to the SDK app. If you have Entra ID P1 licenses, you can create a security group to manage assignments and include global administrators and other selected users in the group membership. Add an assignment for the new security group.
- Move the Assignment required slider from No (the default) to Yes. This action tells Entra ID that only accounts with direct assignments for the app can use it.
- Save your changes.
Accounts with assignments can continue to sign in as normal. Entra ID blocks accounts without assignments and displays an error (Figure 2).
Using direct assignments to control access to the Microsoft Graph PowerShell SDK app is the simplest and quickest method to restrict access to selected accounts. All tenants should consider implementing this control.
Microsoft Platform Migration Planning and Consolidation
Simplify migration planning, overcome migration challenges, and finish projects faster while minimizing the costs, risks and disruptions to users.
Checking for Sign-Ins
To validate that only the approved list of accounts are using the SDK interactively, you can interrogate the Entra ID sign-in log to look for events associated with SDK. Here’s how to check for sign-in events using the Get-MgBetaAuditLogSignIn cmdlet and a filter to extract interactive user sign-ins for the SDK’s application identifier:
[array]$SDKRecords = Get-MgBetaAuditLogSignIn -Filter "(signInEventTypes/any(t:t eq 'interactiveuser')) and AppId eq '14d82eec-204b-4c2f-b7e8-296a70dab67e'" -Top 2000 -Sort "createdDateTime DESC" $SDKRecords | format-Table UserDisplayName, CreatedDateTime, IpAddress UserDisplayName CreatedDateTime IPAddress --------------- --------------- --------- Tony Redmond 27/07/2023 15:20:55 78.19.137.37 Hans Flick 27/07/2023 15:20:13 45.11.131.38 Hans Flick 27/07/2023 15:19:28 45.11.131.38 James Ryan 27/07/2023 15:18:38 13.22.127.27
Use a Registered App Instead of Microsoft’s Enterprise App
There’s no reason that the Graph SDK must be accessed through Microsoft’s default enterprise app. You can create a tenant-specific registered app and use that app instead. An enterprise app is one that is under the control of a publisher like Microsoft and can exist in multiple tenants. The service principal for the enterprise app is its instantiation within the tenant. A registered app is under the control of the tenant.
The idea here is that an organization can:
- Create a set of registered apps for different sets of accounts to use. For instance, create an app for help desk members to use.
- Assign just the permissions needed by the target set of users to their app. For example, the help desk app might be assigned the Directory.Read.All permission to allow those users to read full details for directory objects. If the help desk takes care of devices, tenant administrators can consent to the Devices.ReadWrite.All permission, and so on.
Essentially, creating registered apps for interactive Graph SDK access allows organizations to manage permissions at a more granular level.
To create a registered app for use with the SDK:
- Open the Entra ID admin center and open the Applications tab. Create a new app and give it a suitable name and save (register) the app.
- Access the app and open the Authentication tab. Select Add a platform and choose Mobile and desktop applications. Add the https://login.microsoftonline.com/common/oauth2/nativeclient redirect URI from the suggested list.
- Select Add URI and input http://localhost. I originally omitted this URI, but then encountered problems with a URI mismatch when attempting to sign in with the Connect-MgGraph cmdlet. The error message directed me to add the URI for localhost and the problem disappeared.
- Save the changes.
Figure 3 shows the redirect URIs added to a registered app for use with the Microsoft Graph PowerShell SDK.
With the app in place, the designated accounts can use the app to sign into the Graph SDK. To instruct the SDK not to use the default enterprise app, include the tenant identifier and application identifier in the connection. For example:
Connect-MgGraph -TenantId a662313f-14fc-43a2-9a7a-d2e27f4f3478 -AppId a69635f3-3ac8-4949-a82b-f31b396de57b
Users who are blocked from the default enterprise app can sign in using the registered app. When they do, they’ll find that the app has a basic set of permissions (scopes), as revealed by the Get-MgContext cmdlet:
Get-MgContext ClientId : a69635f3-3ac8-4949-a82b-f31b396de57b TenantId : a662313f-14fc-43a2-9a7a-d2e27f4f3478 Scopes : {openid, profile, User.Read, email} AuthType : Delegated TokenCredentialType : InteractiveBrowser CertificateThumbprint : CertificateSubjectName : Account : Tony.Redmond@office365itpros.com AppName : Microsoft Graph SDK Tenant Access ContextScope : CurrentUser Certificate : PSHostVersion : 5.1.22621.1778 ManagedIdentityId : ClientSecret : Get-MgUser -all Get-MgUser : Insufficient privileges to complete the operation. Status: 403 (Forbidden)
To allow the designated users to do real work with the SDK, a tenant administrator must update the app to add and grant consent for the necessary Graph permissions. It’s wise to keep an eye on the permissions that accrue for the app. Just like the default enterprise app, permissions tend to build up without ever being removed.
Sign-in records for the registered app using the Get-MgBetaAuditLogSignIn cmdlet in a similar manner to that described above.
Secure Access to the Microsoft Graph PowerShell SDK
I wonder how many tenants have never thought about access to the Graph SDK. In small tenants, where there are only a couple of administrators, there’s no harm in persisting with the default enterprise app if you take the time to restrict access to the app. In larger tenants, consider how many different kinds of people run SDK cmdlets and the tasks they perform, and then figure out if some registered apps with bespoke permission sets are a better answer.
The Real Person!
The Real Person!
Hi Tony! Great article!
Sorry for my ignorance but I still don’t understand what the problem with Scopes/Permission stacking is. In short, if the logged in user doesn’t have the administrative role that allows him to execute a certain action, he won’t be able to execute it. Thanks for your explanation!
You mean the problem with the SDK service principal gradually accumulating Graph permissions over time? Well, if the signed in users doesn’t have an administrative role to perform an admin action, they can’t, but they might be able to have access to other information that they should not be able to see. It all depends on the object, the permission, and the roles held by the user. The net is that because the SDK is so powerful, you’re better off restricting access to it.
This is a helpful article, thanks. I have a question about this statement:
“The Microsoft Graph PowerShell SDK is not like other Microsoft 365 PowerShell modules like the Exchange Online or SharePoint Online management modules. These modules assume use by administrators and their cmdlets work on that basis. Of course, if someone without an administrator role signs in to use these modules, permissions limit them to accessing their own data.”
Wouldn’t that be true with the Graph PowerShell SDK as well? If the SDK app grants a delegated API permission, but the account logging in doesn’t have an appropriate administrative role, it can’t get at the data that API permission exposes, correct? For example, if the SDK app grants the Policy.ReadWrite.ConditionalAccess permission, but I’m signing in to Graph PowerShell with an account that doesn’t have the role to create a CA policy, I can’t create a CA policy.
Interactive sessions with the Graph SDK use delegated permissions, so access to data is restricted to that belonging to the signed-in user (like mailbox contents) plus access granted by any management roles held by the account. To use application permissions, you need to run an (app-only mode) interactive session using an appid and app certificate. That way the application permissions granted to the app can be exploited. There’s an intersection of rights and roles that effectively dictate what can be done in any interactive session or app-only session.
Ok thanks, that’s how I thought I understood it. Good article!
Excellent info, very helpful and very much appreciated! We’ve created app registrations for many automated things but not for an interactive use case.
This paragraph confused me though: “The installation of the Microsoft Graph PowerShell SDK in a tenant creates an enterprise app in the tenant’s instance of Entra ID”. I don’t think most organizations are installing the SDK, and if they are I don’t think they need the Cloud App Administrator or Global Administrator role to create Enterprise App service principals. I’m guessing but most probably just run the Connect-MgGraph cmdlet & start scripting.
ps: Looks like the name has changed to “Microsoft Graph Command Line Tools” but the AppID is still 14d82eec-204b-4c2f-b7e8-296a70dab67e, so depending on when it was created it could have either name.
Installation of the enterprise app happens automatically the first time an administrator runs the Connect-MgGraph cmdlet after installing the SDK modules. To create the enterprise app, the account running Connect-mMgGraph needs to hold the global admin role or have consent for the Application.ReadWrite.All permission.
App names change all the time. It’s to confuse the innocent…
I guess you are right; not sure what I had done wrong but I followed once more the complete instructions and now it seems to work with out any issue. I think I forgot to specify the tenant id when connecting.
Thanks for this Tony, I was looking for such a solution.
but when I start to configure it I get: InteractiveBrowserCredential authentication failed: AADSTS50194: Application
‘0bcde2a8-1f10-4cfc-a8ce-8a8dc0972695′(Microsoft Graph for Collaboration Team) is not configured as a multi-tenant
application. Usage of the /common endpoint is not supported for such applications created after ’10/15/2018’. Use a
tenant-specific endpoint or configure the application to be multi-tenant.
Any suggestion for your end beside the remark in the error?
I’ve just repeated the instructions in the article to create a new registered app with the settings described in the text and the app works with PowerShell 5 and 7.
Can the account you’re trying to use sign into using the default Graph SDK app?
Did you set the mobile and desktop applications platform correctly? Are all the other settings as described in the article?
This isn’t a complex setup, so the likely cause is probably something simple.
I guess you are right; not sure what I had done wrong but I followed once more the complete instructions and now it seems to work with out any issue. I think I forgot to specify the tenant id when connecting.