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

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](https://www.powershellgallery.com/packages/MSGraphStuff)).

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">There is a related article that focuses on getting required <a target="_blank" rel="noopener noreferrer nofollow" href="https://doitpsway.com/how-to-get-all-graph-powershell-sdk-modules-required-to-run-selected-code-using-powershell" style="pointer-events: none">Graph SDK modules</a></div>
</div>

---

# 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](https://www.powershellgallery.com/packages/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](https://www.powershellgallery.com/packages/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

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710001800631/984f5798-a822-4dc2-b69e-e483ecd4d648.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710001841627/ccc40e14-ca0e-44e3-873a-d859a6d39cc3.png align="center")

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
    

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">A detected command can be returned multiple times in case more than one permission is needed (one line per permission)!</div>
</div>

---

# Use cases

The following examples will be made against this test code

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710001616381/33e8d17b-8acc-4b35-80e4-ed8410ef0301.png align="center")

Can be downloaded from [GitHub Gist](https://gist.github.com/ztrhgf/0e6d313f58130c19c976156b0e77723d).

## 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!

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

The result will look like this 👇

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710001800631/984f5798-a822-4dc2-b69e-e483ecd4d648.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710001841627/ccc40e14-ca0e-44e3-873a-d859a6d39cc3.png align="center")

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)

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Because locally available modules are cached to <code>$availableModules</code> variable, you can use such a variable in the next <code>Get-CodeGraphPermissionRequirement</code> calls to speed it up</div>
</div>

```powershell
# 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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710003196621/a47b30f1-08ea-4298-ad35-7224acd722c7.png align="center")

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.
        
        ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710146036551/8d5f72ae-5ffe-4301-aeb1-dcf074bd72d8.png align="center")
        
    * This function is defined outside the code we've analyzed, but thanks to `goDeep` parameter it was processed too!
        

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Use the VERBOSE parameter when calling <code>Get-CodeGraphPermissionRequirement</code> to get more details about what is going on under the hood</div>
</div>

---

# 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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1710147302323/d2b8aa25-fa71-4910-8128-c431d1e89b41.png align="center")

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 👍
