Connect and Navigate the Microsoft Graph API with PowerShell

Connect and Navigate the Microsoft Graph API with PowerShell

Graph is Microsoft’s RESTful API that allows you to interface directly with Azure AD, Office 365, Intune, SharePoint, Teams, OneNote, and a whole lot more. By using the Invoke-RestMethod PowerShell cmdlet we can connect and interact directly with the Graph API. The Invoke-RestMethod cmdlet sends HTTP and HTTPS requests to Representational State Transfer (REST) web services that returns richly structured data. PowerShell formats the response based on the data type. For an RSS or ATOM feed, PowerShell returns the Item or Entry XML nodes. For JavaScript Object Notation (JSON) or XML, PowerShell converts (or deserializes) the content into objects.1 In this article, I will walk you through setting up the Azure Application, assigning proper  permissions, Authentication and finally running queries against the Graph API. Once you understand how to properly authenticate and format queries you will see how powerful Graph can be for you and your organization.

1. Application Registration

The first step to connect to Graph and make requests is to register a new Azure Active Directory Application. The application is used to connect to Graph and manage permissions.

Navigate to App Registrations in Azure and select “New Registration” (Azure Portal > Azure Active Directory > App Registration > New Application Registration)

Create a new Azure Active Directory application to connect to the Graph API with PowerShell
Creating a new Azure Active Directory Application in the Azure Portal

Next, give your application a name. In my example, I am naming my application, “TheLazyAdministrator-Test”. Then, specify who can use this application. Click on the “Register” button to create your new application.

Modify your Azure Active Directory application settings
Giving the Application a valid name and scope

You will now see your newly created application! Some key items regarding the application are the Application ID (which is the Client ID), the Directory ID (which is the Tenant ID) and Client Secret, which is required if you use the Application Permission type but not if you use the Delegated Permission Type. In the next section, we will walk through the permission type differences, permissions, and admin consent.

Creating an Azure Active Directory Application to connect to the Graph API using PowerShell

And finally we need to configure and get our applications redirect URI. Go to your Azure Applications overview page by going to Azure Active Directory > App Registrations. Click on “Add a Redirect URI”

Enter in ANY url as a redirect URI value. It DOES NOT have to even resolve! You could put http://localhost, in my example I put a URL that isn’t even valid on my website. Press ‘Save’ and take note of this URL.

2. Configuring App Permissions

Now that we have created an Application we need to configure its permissions. In the OAuth world, when Apps try to access information they must have the appropriate permissions to do.

Application Permissions vs Delegated Permissions

It is important to understand the difference between Application permissions and Delegated permissions as well as effective permissions. Effective permissions are the permissions that your app will have when making requests to the Graph API.

  • Application permissions are used by apps that run without a signed-in user present; for example, apps that run as background services or daemons. Application permissions can only be consented by an administrator. This permission type would be preferred for items like automated scripts and runbooks.
    • For application permissions, the effective permissions of your app will be the full level of privileges implied by the permission. For example, an app that has the User.ReadWrite.All application permission can update the profile of every user in the organization.
  • Delegated permissions are used by apps that have a signed-in user present. For these apps, either the user or an administrator consents to the permissions that the app requests and the app is delegated permission to act as the signed-in user when making calls to the target resource. Some delegated permissions can be consented to by non-administrative users, but some higher-privileged permissions require administrator consent.
    • For delegated permissions, the effective permissions of your app will be the least privileged intersection of the delegated permissions the app has been granted and the privileges of the currently signed-in user. Your app can never have more privileges than the signed-in user. Within organizations, the privileges of the signed-in user may be determined by policy or by membership in one or more administrator roles.2 For example, assume your app has been granted the User.ReadWrite.All delegated permission. This permission nominally grants your app permission to read and update the profile of every user in an organization. If the signed-in user is a global administrator, your app will be able to update the profile of every user in the organization. However, if the signed-in user isn’t in an administrator role, your app will be able to update only the profile of the signed-in user. It will not be able to update the profiles of other users in the organization because the user that it has permission to act on behalf of does not have those privileges.2

Permission Requirements

The Microsoft Graph documentation provides details on which permission levels are required or allowed for each permission type (application vs delegated). Some resources can only be reached using Delegated Permissions, others by Application permissions, and others by either of the two. Pictured below we can see the permissions listed to retrieve a list of all user objects for Application or Delegated. Permissions are listed from least privileged to most privileged.

Available permissions for the Graph API callAssigning Permissions

You can assign your application different permissions in the Azure Portal. First navigate to Azure Portal > Azure Active Directory >App Registrations and then select your application that you made above. Once you have selected your application, select “API Permissions” or “View API Permissions” (pictured below).

In my example I will give my application permission to return all groups that are in my tenant.

By default you will only have the User.Read permission assigned which allows you to sign in and read the user profile.

To assign a new permission to your application, click the “Add a permission” button.

In the “Request API Permissions” blade “Microsoft Graph”

Next, we will have to choose the permission type. Remember that some permissions are only available for application permission type, some for delegated permission type, and some are available for both types. If, for example, I wanted to be able to List All Groups in my organization, I can see in the Graph API Documentation that I can use either delegated permissions or application permissions. So now it all depends how I will use my Azure Application. In my example I will be selecting Delegated Permissions.

From looking at the documentation, to get all Groups in my tenant I need to at least assign the Group.Read.All permission. This is the least privileged permission I can assign. Once I find the permission and select the check box I can add the permission by selecting the “Add Permissions” button.
But since this permission type grants access to protected resources I need to grant Admin Consent. I go deeper into Admin Consent in the next section.

And now I can see in the permissions panel that I have properly granted consent to the application to read all groups in the tenant

So now you have successfully granted your Azure Application the permission to get all groups in your tenant. As you continue to use Graph, it is important to make sure you have the correct permission to access each resource. If you connect to Graph and then assign a new permission, you will need to re-connect to Graph to get an updated token. Make sure to pay close attention to the documentation to see which permission you need for each resource.

Admin Consent

Some high-privilege permissions can be set to admin-restricted and require an administrators consent to be granted. Examples of these kinds of permissions include the following:

  • Read all user’s full profiles by using User.Read.All
  • Write data to an organization’s directory by using Directory.ReadWrite.All
  • Read all groups in an organization’s directory by using Groups.Read.All

Below we can see 2 of my permissions require admin consent before applying. Groups.Read.All and User.Read.All. If I just assign the permission but forget to grant admin consent when its required, my application will not have that permission until its consented.

Azure Application permissions assigned for the Graph API
Application permissions assigned in the Azure Portal

To grant admin consent to my application for these permissions I can press the “Grant admin consent for TENANT” button at the bottom of the screen (Pictured below).

Granting admin consent for an Azure Application to connect to the Graph APIAnd now I see that my permissions have been granted and my application can now read all groups in my organization as well as read all user’s profiles.

3. Authentication and Authorization – Different Methods to Connect

There are multiple ways to authenticate to Graph with each has its own pros and cons. Below I will show you some of the methods, the benfits and use-cases, and finally the script on how you can use that authentication method to connect to Graph. Each authentication method is referred to as a Grant Type.

Device Code

OAuth 2.0 Device code flow grant type supports mult-factor authentication as you sign in using a web browser. You would not want to use this method of authentication for runbooks or any type of automated tasks as it requires user intervention to login to Graph. The Device Code grant type is used by browserless or input-constrained devices in the device flow to exchange a previously obtained device code for an access token.

For Device Code flow you do not need to know your Azure Applications client secret, but you do need to provide the client ID, and tenant name.

The first item we need to get is the Tenant Name. This can be found in Azure by going to Azure Active Directory > Custom Domain Names, and then finding the .onmicrosoft.com domain. Note this for later.

Second, we want to find the Client ID of our Azure Application that we created earlier. To find this go to Azure and then Azure Active Directory > App Registrations > select your application and then copy the Application (client) ID value:
Copy or save the code below. This is the script to authenticate and connect to graph using the Device Code Flow grant type. (You may need to change $RedirectURL to the redirect URL you set above when you created the azure AD application)

Modify the ClientID variable and the Tenant variable with your client ID you got above, and the tenant domain name.

Now if I run the script within PowerShell, the shell will display my device code and a winform to enter the code and sign in: (HINT: using the Set-Clipboard cmdlet within the script and string parsing, the code will automatically be sent to your clipboard. All you have to do is paste it in!)

After you sign in you will be greeted with a message saying you have signed into the application and you can close the window.

The shell will also show if we have successfully authenticated to Graph and retrieved a valid token. Pictured is an invalid login response and a valid response.

The token response is stored in a variable named $tokenResponse. Here we can see the scope (permissions we assigned earlier), expire date, token and more.

In the Data section you will see how to use the token to make API calls and begin to navigate and manipulate your data.

Authorization Code

The Authorization Code grant type is very similar to the Device Code grant type in that we sign in using a web browser. The Authorization Code grant type does require us to have not only our client ID, but also the client secret and redirect URI.

First, lets get the Client ID:

To find this go to Azure and then Azure Active Directory > App Registrations > select your application and then copy the Application (client) ID value:
Second, we will want the Client Secret.

In the Azure Portal, go to Azure Active Directory > App Registrations > and then Certificates and Secrets. Under “Client Secrets” click on the “New Client Secret” button to generate a new secret. Take note of this client secret as once you go away from this screen it will never display the secret again.

When you create a new client secret, give it a good description and select the expiration date

Copy the newly generated client secret. IMPORTANT: Once you click away, this client secret will never be able to be viewed again. Keep it safe and secure.

And finally we need to configure and get our applications redirect URI. Go to your Azure Applications overview page by going to Azure Active Directory > App Registrations. Click on “Add a Redirect URI”

Enter in ANY url as a redirect URI value. It DOES NOT have to even resolve! You could put http://localhost, in my example I put a URL that isn’t even valid on my website. Press ‘Save’ and take note of this URL.

Copy or save the code below. This is the script to authenticate and connect to graph using the Authorization Code grant type.

Next, in the clientID variable, enter your client ID that you got above. Do the same for the clientsecret variable and the redirectURI variable

When you now run the script it will prompt you to login and approve the permissions. Here we can see the permissions we assigned the app in our earlier steps.

The token response is stored in a variable named $tokenResponse. Here we can see the scope (permissions we assigned earlier), expire date, token and more.

In the Data section you will see how to use the token to make API calls and begin to navigate and manipulate your data.

Password

The Password grant type allows you to request a token for Delegated calls to the Graph API. In this script example you will see that the client secret and the users password is hard coded in. You should avoid this in any production environment. Encrypt the client secret as well as the password, you can store it in Azure Key Vault, but do not hard code it in plain text. The client secret should be treated similarly as a password.

This is beneficial because some permissions are only available using delegated permissions (like sending a message to a Team channel)

First, lets get the Client ID:

To find this go to Azure and then Azure Active Directory > App Registrations > select your application and then copy the Application (client) ID value:
Second, we will want the Client Secret.

In the Azure Portal, go to Azure Active Directory > App Registrations > and then Certificates and Secrets. Under “Client Secrets” click on the “New Client Secret” button to generate a new secret. Take note of this client secret as once you go away from this screen it will never display the secret again.

When you create a new client secret, give it a good description and select the expiration date

Copy the newly generated client secret. IMPORTANT: Once you click away, this client secret will never be able to be viewed again. Keep it safe and secure.

Lastly, we need to get is the Tenant Name. This can be found in Azure by going to Azure Active Directory > Custom Domain Names, and then finding the .onmicrosoft.com domain. Note this for later.

Copy or save the code below. This is the script to authenticate and connect to graph using the Password grant type.

Now we will want to enter the clientID into the clientID variable, and the same with the tenant name, and client secret. For the username and password variable, enter the user that you are using to delegate for. So if you were going to send a message to Teams, this user would be the sender.

When we run the script it will use those variables and automatically authenticate to the Graph API. We can confirm this by seeing what is returned in the $TokenResponse variable

In the Data section you will see how to use the token to make API calls and begin to navigate and manipulate your data.

Client Credentials

The Client Credentials grant type is best for any automated tasks, runbooks or scripts as it can authenticate to Graph without any user intervention. In this script example you will see that the client secret is hard coded in. You should avoid this in any production environment. Encrypt the client secret, store it in Azure Key Vault, but do not hard code it in plain text. The client secret should be treated similarly as a password.

First, lets get the Client ID:

To find this go to Azure and then Azure Active Directory > App Registrations > select your application and then copy the Application (client) ID value:
Second, we will want the Client Secret.

In the Azure Portal, go to Azure Active Directory > App Registrations > and then Certificates and Secrets. Under “Client Secrets” click on the “New Client Secret” button to generate a new secret. Take note of this client secret as once you go away from this screen it will never display the secret again.

When you create a new client secret, give it a good description and select the expiration date

Copy the newly generated client secret. IMPORTANT: Once you click away, this client secret will never be able to be viewed again. Keep it safe and secure.

Lastly, we need to get is the Tenant Name. This can be found in Azure by going to Azure Active Directory > Custom Domain Names, and then finding the .onmicrosoft.com domain. Note this for later.

Copy or save the code below. This is the script to authenticate and connect to graph using the Client Credentials grant type.

Now we will want to enter the clientID into the clientID variable, and the same with the tenant name, and client secret

When we run the script it will use those variables and automatically authenticate to the Graph API. We can confirm this by seeing what is returned in the $TokenResponse variable

3. Playing With Graph Data – Get, Put, Post, Delete, and Patch

Now that we have created the Azure Application to allow us to connect to Graph, and have learned the multiple different ways to connect and authenticate to Graph, it’s time to learn some of the items we can do in Graph. This is where Graph shines and you can see how much more powerful it can be than just connecting to exchange online using PowerShell.

In my examples you will notice that my access token is always looking for the variable, ‘TokenResponse’ and the property, ‘access_token’. If you look in the different authentication methods above, the token is always stored in a variable named TokenResponse so if you just copied the code above you will be able to run these different items with minimal changes to the code.

Get All Tenant Groups

To get all Groups in a tenant we can find the documentation here. The least privileged permission we can grant our application is Group.Read.All and Directory.Read.All. By going into my application permissions I can see that I have these permissions assigned so I know that I will be able to make this call without issues.

The documentation also shows us the HTTP request which is https://graph.microsoft.com/v1.0/Groups/

Since I have already connected to graph using one of the grant types above and retrieved a valid token, I can use that access token to send an HTTP request to Graph and list all Groups.

Create a File in OneDrive

To create a new a file in OneDrive, we can find the documentation here. The least privileged permission we can grant our application is Files.ReadWrite because in this example I will be using the delegate permission and creating a new file in my OneDrive. By going into my application permissions I can see that I have these permissions assigned so I know that I will be able to make this call without issues.

Since I will be creating a new file in my OneDrive I need to authenticate to Graph with a delegate which means I can use Device Code flow, Authorization Code or Password. The account that I use to sign in will be the account with the new file in their OneDrive. You could use an Application grant type or even delegate to create a file in other users OneDrives, in that case you would want to modify the below URL and assign the following permissions: Files.ReadWrite.All, Sites.ReadWrite.All

From looking at the Application API permissions I can see that I have the proper Files.ReadWrite permission needed

Next, if we look at my OneDrive we can see the current files that reside in there

Since this file will be created in my root OneDrive the API Url will be ‘https://graph.microsoft.com/v1.0/me/drive/root:/FileTESTING.txt:/content’

The new file will be named FileTESTING.txt and we can see it will be at my root due to the /me/drive/root:

The body variable will contain the body of my new text file. In my case it will read:

So all together the entire script will be:

And now I can see my new file as well as the content

Post a Teams Message

List all Teams

The first task we need to do is to return a list of all of our Teams and their ID. For this we will be using the beta API URL. The PowerShell script will be the following:

By seeing whats contained in the $Teams variable, we can see that we have a table of all of our Teams, ID and their descriptions.

List all Team Channels

If I want to post a message to the Management Team, I will then need to select the proper Teams channel. In the script below I can do another API call to that Teams using the ID for the Management Team I got above

I can then see all my channels in the $TeamChannels variable

Post Message to Teams Channel

Now that I have the Teams ID and the channel ID, I can start crafting the message that I want to send to Microsoft Teams.

First I will check the documentation here to review the required permissions. Currently only Delegated permission type is available with the permission Group.ReadWrite.All. Since it only supports Delegated permissions I can authenticate to Graph by either Device Code flow, Authorization Code or Password.

In Azure Active Directory I add the permission, ‘Group.ReadWrite.All’ to my application and save. Since this affects all Groups it requires Admin Consent

Once and admin has granted consent to the application the warning icon will change to a green check mark. Now that my application has the correct permission I can authenticate to Graph.

Once I have authenticated to Graph and retrieved my token I can then modify the following script to fit my needs. The first ID, “fa9c9332-a85d-4349-aa15-f287793bde8f” is the ID of the management Team we got earlier. The second ID, “19:d18e13e980fd423da52c3cd12592566a@thread.skype” is the General chat ID we got in the second step. The content is the message content.

Running the script I will get a response back

And if I go into my Microsoft Teams I can see the message is in the Management Team and in the General channel. Since this uses delegate permissions it appears to be sent as me as I signed into Graph using my account.

Get all Recent Emails for a User

Get All Users

The first item we have to do is return a list of all of our users and select the ID of the user we want to view recent emails of. If you wanted to view your emails the HTTP request would just go to https://graph.microsoft.com/v1.0/me/message. According to the documentation, we need to assign our application the following permissions (Application): User.Read.All and Directory.Read.All

Once I grant my application the proper permissions and authenticate to Graph using one of my authentication methods above (in my example since I assigned Application type permissions, I authenticated to Graph using Client Credentials) I can run the following PowerShell code to return a list of all my users:

In my case I am going to be retrieving all recent emails for the user, “Gary Snale” so I will take note of his ID value.

Get Another Users Recent E-Mails

Next, from looking at the Graph documentation I need to have the following permission to retrieve a users emails: Mail.Read

Once I have assigned the proper permissions I can run the following PowerShell code to retrieve emails for a user (NOTE: notice how I entered the users ID into the HTTP URL, you can also use the users UserPrincipalName):

NOTE: Notice the isRead property. This will change when the user has read the message. This does not rely on a read receipt and will work with every email.

You can also see the body of the email but since my email was sent in HTML the body is entirely HTML. It can be saved as a HTML file and opened without issues or you can do some string parsing to piece out the body content

Once I have assigned the proper permissions I can run the following PowerShell code to send an Email as myself

Send an Email

Using Graph we can send emails to internal and external recipients either from us or another user. We have a wide array of message options exposed to us which can be found here. Some of the items include:

  • Read Receipt
  • Importance
  • Marking message as a draft
  • Don’t save to sent items folder
  • Attachments
  • and more…

This will make more sense below when we create the JSON body of our email.

Send an Email as Myself

From looking at the documentation the permission I need to send an e-mail using Graph is Mail.Send

Since this be sent from myself I need to authenticate using a delegated grant type so I can do Device Code, Auth Code, or Password. Once I authenticate to Graph I can modify the following PowerShell code to send an e-mail from myself: (NOTE: Notice the URL is /me/Sendmail)

In Outlook I can now see my e-mail

Send an Email From Another User

In the other examples I show how you can do tasks as other users, or on behalf of other users using the users’ ID value. In this example I will instead do the users’ UserPrincipalName (UPN). Most items will accept the ID value or UPN value. You will want to confirm by checking the documentation for each call. For sending an e-mail from another user the documentation shows that I can use either the users’ ID or UPN:

The documentation also shows that the permission I need is Mail.Send

Since I am connecting to Graph using client credentials as shown above, I granted the Mail.Send permission as a Application permission. The Endpoint we will be calling is https://graph.microsoft.com/v1.0/users/Gsnale@bwya77.com/sendMail. Notice that the user I am going to be sending the e-mail from is Gsnale@bwya77.com.

The entire PowerShell script to send the email would be the following:

Some of the resources I am calling in the email are:

  • SavetoSentItems: The e-mail will not be saved to the Sent Items folder
  • isDraft: By default if you do not specify this to False, it will create the email and save to the Drafts folder and NOT send.
  • importance: Setting the importance of the e-mail to ‘High’

Now if I go to Outlook I can see that I have a new e-mail with the proper subject,

Get Calendar Events

Using Graph we can get calendar events for ourselves or other users in our enviornment. Looking at the documentation we need the Calanders.Read permission.

I am going to be getting my calendar events so I granted the permission as delegated and authenticated to Graph using device code flow ( but I can use Device Code, Auth Code, or Password). Once I am authenticated to Graph I can begin my API request by using the following PowerShell code:

Because of my custom Select properties I can get a clean response, start, and end time

OneDrive

Get OneDrive Contents

To list all OneDrive content, according to the documentation, we will need the Files.ReadWrite permission of the Files.ReadWrite.All Permission (If I am going to list the contents on another users OneDrive).

Once I have the correct permission(s) and authenticate to Graph, I can run the following PowerShell script to return all OneDrive items. This script will work recursively so it will return every item. In my example I am returning my OneDrive items so I authenticated to Graph using device code flow ( but I can use Device Code, Auth Code, or Password).

The shell will display my OneDrive items and because of my Select-Object statement, I also see the name, size and ID of each file

Rename OneDrive File

Now, using the data I was able to retrieve above, I will rename test3.txt to new-file-name.txt. According to the documentation, the permission I need to achieve this is Files.ReadWrite

The one item we need is the files “ID” value to put into the HTTP request.

The entire PowerShell code would be the following:

the shell wont display any verbose message if it’s successful but I can list all OneDrive items again to see that it has been renamed

And I can see in the web client that the file has been renamed as well

Get all Intune Compliance Policies

I can also leverage Graph to list all of my Intune Compliance Policies. From referencing the documentation the permission needed is DeviceManagementConfiguration.Read.All. Another item to note that this call will only work with a delegated permission type so I can authenticate to Graph using Device Code, Auth Code, or Password. This permission does require admin consent.

Once I am authenticated to Graph I can run the following PowerShell code to return a list of all my Compliance Policies and their configuration properties

The shell will display the policies and properties

Other Graph Examples

On my GitHub I have tons of other API calls including uploading a file to OneDrive, get tenant security alerts, and more.

Sources

1: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-restmethod?view=powershell-6

2: https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent

3. https://docs.microsoft.com/en-us/graph/

2 thoughts on “Connect and Navigate the Microsoft Graph API with PowerShell

Leave a Reply

Your email address will not be published. Required fields are marked *