Microsoft Graph and Python
Using Microsoft Graph and Python to access Microsoft 365 Data and Services
Why you should read this post
If you are interested in Microsoft Graph
and how it can work with Python
this post gives two examples of an art of the possible. In this post i will be addressing the following topics:
- Using MSAL libraries in Python
- Using 2 different Authentication Flows to gain access to Microsoft Graph resources
- Bonus - Access to my GitHub repo where you can get the code as well as a Jupyter Notebook to runs this on your own
Wiring this all up
This is not very onerous, in fact if you have ever played with Python, its the simplicity that is really astounding. Basically you need to work the the below libraries to get this sorted:
- Microsoft Authentication Library - used to do the authentication to Azure Active Directory which is needed for getting the token needed for Microsoft Graph
- Requests - needed to send your calls to the URI endpoints
- JWT (Optional) - for token display and handling demo purposes only
- Pandas (Optional) - used for displaying my payload returned from the graph in a Dataframe (Think excel rows)
import msal
import jwt
import json
import requests
import pandas as pd
from datetime import datetime
from configparser import ConfigParser
config = ConfigParser()
config.read('config.ini')
I also employ json, datetime, and configparser for utilities within my code and I will explain why as I go along. I will be showing 2 methods of approach here as mentioned. App Permission to retrieve a list of users from the Graph as well as Delegated Permission using Device Flow authentication to do the same.
Using Application Permission
The things to pay close attention to in the below code snippet is teh msal.ConfidentialApplication. Thats why you should always use the Graph SDK, this one line of code in additonan to the “accessToken” acquiring th token under the “try” block is responsible for all the magic. Specifically, I am using Client Credential Provider Auth Flow and supplying it the identification of the App using the Client ID and Secret. Want more details on Auth Flow click here please
def msgraph_auth():
global accessToken
global requestHeaders
global tokenExpiry
tenantID = config['apppermissiononly'] ['tenantID']
authority = config['apppermissiononly'] ['authority'] + tenantID
clientID = config['apppermissiononly'] ['clientID']
clientSecret = config['apppermissiononly'] ['clientSecret']
scope = ['https://graph.microsoft.com/.default']
app = msal.ConfidentialClientApplication(clientID, authority=authority, client_credential = clientSecret)
try:
accessToken = app.acquire_token_silent(scope, account=None)
if not accessToken:
try:
accessToken = app.acquire_token_for_client(scopes=scope)
if accessToken['access_token']:
print('New access token retreived....')
requestHeaders = {'Authorization': 'Bearer ' + accessToken['access_token']}
else:
print('Error Caught: Check the Config File to make sure tenantID, clientID and clientSecret is correct')
except:
pass
else:
print('Token retreived from MSAL Cache....')
return
except Exception as err:
print(err)
def msgraph_request(resource,requestHeaders):
# Request
results = requests.get(resource, headers=requestHeaders).json()
return results
Next its a matter of calling those functions, one for the Auth, and the ther to get the query results.
# Call the Authenticate against Graph Function
msgraph_auth()
#going after the V1 endpoint for users
queryResults = msgraph_request(graphURI +'/v1.0/users',requestHeaders)
# Send the results to a Pandas Dataframe
try:
df = pd.read_json(json.dumps(queryResults['value']))
# set ID column as index in Pandas Dataframe
df = df.set_index('id')
# print the entire datafarame from Pandas
print(str(df))
#print(str(df['displayName'] + " " + df['mail']))
except:
print(json.dumps(queryResults, indent=2))
Watch it run
One you invoke the python executable you get the token and Pandas gives you a beautiful dataframe to see the results.
Using Delegated (Interactive) Permission
So, in delegated, we are not running unattended, we do need to authentiate. In this scenario we are going to use “Device Code” Auth Flow. This means that when we need to get the token, AAD will challenge us to enter a code at a certain URL, we will need to authenticate using our credentials and consent to the Application and scope it is asking for. We’re also using the PublicClientApplication here using just the Client ID, everything else for this Auth, we will explicitly consent for. See below…
app = msal.PublicClientApplication(clientID, authority=authority)
flow = app.initiate_device_flow(scopes=SCOPES)
if 'user_code' not in flow:
raise Exception('Failed to create device flow')
print(flow['message'])
then we pretty much make the same calls as before except for our token we are asking for it via device flow andd we inspect the acccess token in our callback and go to town getting our results.
result = app.acquire_token_by_device_flow(flow)
if 'access_token' in result:
result = requests.get(f'{ENDPOINT}/users', headers={'Authorization': 'Bearer ' + result['access_token']}).json()
#Going after a specific user Diego S
#result = requests.get(f'{ENDPOINT}/users/fa2480cc-103b-4b53-8ba6-da9720c81c2d/presence', headers={'Authorization': 'Bearer ' + result['access_token']})
#result.raise_for_status()
#print(result.json())
df = pd.read_json(json.dumps(result['value']))
# set ID column as index in Pandas Dataframe
df = df.set_index('id')
# print the entire datafarame from Pandas
print(str(df))
#print(str(df['displayName'] + " " + df['mail']))
else:
raise Exception('no access token in result')
Summary
This code as well as the Jupyter Notebook which allows you to run this all on your own in your own envionment may be found in this GitHub Repo. I do hope you enjoyed this little post on how to get started using Python with Microsoft Graph. Oh, one more thing 💡 you may have noticed me using config file variable when I was assigning Client ID, Tenant ID and the lot, that file is not in the repo for obvious reasons but an example of how that file is constructed is below for reference:
[apppermissiononly]
tenantID = redacted-1e7e2f9de3e2
authority = https://login.microsoftonline.com/
clientID = redacted-654fbbfaa793
clientSecret = totally_redacted_rubbish
[delegatedpermissiononly]
tenantID = rubbish-1e7e2f9de3e2
authority = https://login.microsoftonline.com/
clientID = more-rubbbish-215b06
I would suggest however that you apply a more secure way of storing your configurables and credentials by using something like Azure Key Vault. This is demoware mind you 😜
Chat about this?
Engage with me | Click |
---|---|
@fabianwilliams | |
Fabian G. Williams |
Or use the share buttons at the top of the page! Thanks
Cheers! Fabs