How to get all Graph API permissions required to run selected code using PowerShell

How to get all Graph API permissions required to run selected code using PowerShell

ยท

5 min read

Now that Microsoft Graph API is the main management tool for most of the Microsoft Cloud services, more and more admins will need to be able to work effectively with it.

Graph API can be quite hard to understand, mainly the scope/permission part of it. One thing is to write the correct code and the second is knowing, what permission will you need to run it successfully ๐Ÿ˜„

Not to mention running some not-very-well-documented 3rd party code.

In this post, I will show you my solution to this problem. And that is my PowerShell function Get-CodeGraphPermissionRequirement (part of the module MSGraphStuff).

๐Ÿ’ก
There is a related article that focuses on getting required Graph SDK modules

Introduction

Function Get-CodeGraphPermissionRequirement gets Graph API permissions (scopes) that are needed to run selected code.

Under the hood, it uses my other, more universal function Get-CodeDependency (part of DependencySearch module) that returns all code dependencies by analyzing its AST and doing some other magic, to search for all commands interacting with the Graph API.

Official Graph SDK commands and direct Graph API calls are both processed ๐Ÿ˜Ž

Get-CodeGraphPermissionRequirement is part of the module MSGraphStuff.


Main features

  • Supports getting permissions for official Mg* Graph SDK commands

    • Get-MgUser, Update-MgDevice, ...
  • Supports getting permissions for direct API calls invoked via Invoke-MsGraphRequest, Invoke-RestMethod, Invoke-WebRequest and their aliases

    • a.k.a. calling GET, POST, ... requests against https://graph.microsoft.com/v1.0/users and such

    • supports also usage of variables in place of IDs in the URI

      • a.k.a. v1.0/groups/$groupId/settings will be correctly recognized and processed
  • Supports recursive search across all code dependencies

    • so you can get the complete permissions list not just for the code itself, but for all its dependencies too
  • Recognizes v1.0 vs beta API calls

Drawbacks

  • Official command Find-MgGraphCommand is used to translate command/URI calls to required permissions

    • doesn't contain permissions for all commands/URIs (but this will be hopefully solved in the future)

    • returns all permissions that can be used, not just the least one (but I am trying to solve this on my own)

  • URIs passed via parameter splatting or through variables aren't detected right now

    • but you will be notified about such issue, so you can solve this manually

The output of the Get-CodeGraphPermissionRequirement

If we send the function results to Out-GridView to get a graphical representation, we can get results similar to this

As you can see there are several properties returned:

  • Command - detected command that interacts with Graph API

    • official Mg* commands or direct API web requests
  • Name - name of the Graph permission/scope that the command needs

  • Description - Graph permission description

  • FullDescription - Graph permission full description

  • Type - Graph permission type (application, delegated)

  • InvokedAs - the original code line where the command was found

    • helpful if some command was found multiple times to identify the calls, also in cases where URI cannot be recognized, to find such line and do the manual code analysis
  • DependencyPath - the whole path to the found command

    • useful when using goDeep parameter to understand where this command was found
  • ApiVersion - detected Api version (v1.0 or beta)

  • Method - request method (GET, POST, DELETE, ...)

  • Error - error message if there was some problem

๐Ÿ’ก
A detected command can be returned multiple times in case more than one permission is needed (one line per permission)!

Use cases

The following examples will be made against this test code

Can be downloaded from GitHub Gist.

Return Graph permissions required by selected code

The following code will return only application permissions for the selected script.

If there are some indirect dependencies (like calling another function that invokes some Graph API itself), they won't be returned!

Get-CodeGraphPermissionRequirement -scriptPath C:\scripts\someGraphRelatedCode.ps1 -permType "application" | Out-GridView

The result will look like this ๐Ÿ‘‡

Except for two command calls the Get-CodeGraphPermissionRequirement function resolved all needed permissions for us.

  • The first problematic one is Invoke-MgGraphRequest -Method PATCH -Uri "https://graph.microsoft.com/beta/identity/conditionalAccess/policies" -Body $body where the official Find-MgGraphCommand function doesn't return anything for this particular URI and PATCH method.

  • The second one is Invoke-MgGraphRequest -OutputType PSObject -Uri $msGraphPermissionsRequestUri where instead of plaintext URI is used some variable which is currently not supported by my function.

The last thing that may grab your attention is the missing output for the custom function Remove-O365OrphanedMailbox. Trust me, this function uses Graph API, but because we haven't specified goDeep parameter when calling Get-CodeGraphPermissionRequirement, just the specified code was checked, but none of the used functions/modules. We will look into this in the next example though.

Return ALL Graph permissions required to run selected code (direct and indirect)

The following code will return delegated and application permissions for selected script and all its dependencies (used functions/modules)

๐Ÿ’ก
Because locally available modules are cached to $availableModules variable, you can use such a variable in the next Get-CodeGraphPermissionRequirement calls to speed it up
# cache available modules to speed up repeated 'Get-CodeGraphPermissionRequirement' function invocations
$availableModules = @(Get-Module -ListAvailable)

Get-CodeGraphPermissionRequirement -scriptPath C:\scripts\someGraphRelatedCode.ps1 -goDeep -availableModules $availableModules -permType "application", "delegated" | Out-GridView

The result is very similar to the first example.

As you can see there are two differences though.

  • Delegated permissions are returned too

  • A lot of new commands were detected!

    • If we scroll to the right, we can see in the DependencyPath column that all these commands were found in the Remove-O365OrphanedMailbox function.

    • This function is defined outside the code we've analyzed, but thanks to goDeep parameter it was processed too!

๐Ÿ’ก
Use the VERBOSE parameter when calling Get-CodeGraphPermissionRequirement to get more details about what is going on under the hood

Returned permission optimization

As I said, my function uses an official Find-MgGraphCommand under the hood to retrieve command/URI permissions and this function is chatty. It returns more permissions that are needed to be more precise.

For example Get-MgUser command, do you really think, Directory.ReadWrite.All or Directory.ReadWrite.All are needed? I don't think so.

Therefore I remove such permission from the output automatically.

But if you for some reason want to retrieve all these permissions, just use dontFilterPermissions parameter.


Now that you know how to use this function don't be afraid to test it against your code. Hopefully, this will help you on your Graph API journey ๐Ÿ‘

Did you find this article valuable?

Support Ondrej Sebela by becoming a sponsor. Any amount is appreciated!

ย