Querying Microsoft Graph API with Python | by Ephraim Mwai | Towards Data Science

In order to deliver a rich data-driven experience to your users, you are likely to integrate custom-built applications with your organization’s data. Microsoft Graph, a REST API, offers the ability to interact with data in Office 365. In this post, I will illustrate connecting to your Azure Active Directory (Azure AD) using python. The main steps are setting up an enterprise application on Azure and writing code to handle the data.
Get this service offering here
Login to Azure using a Global admin account.
On the left pane navigate to >> Azure Active Directory >> Enterprise Applications >> +New application >> Add you own App (Application your are Developing) >> +New Registration
After Successfully Registering an app you will see the below;
On the right, Add a redirect URL with the below template
https://login.microsoftonline.com/<DIRECTORY (tenant) ID>/oauth2/token
Certificates & Secrets
Create a Client Secret and note it.
API — Permissions
By default, Microsoft Graph User.Read permissions are added. For demonstration, I will query O365 Planner (Trello equivalent ) data. The below are permissions granted with consent to an account with NO 2 MFA. For more information see docs
Let's dive into the python script;
#import librariesimport requestsimport jsonimport pandas as pdimport pyodbc
Create a token data dictionary to use in the requests
app_id = 'xxxxxx' #Application Id - on the azure app overview pageclient_secret = 'xxxxx' #Use the redirect URL to create a token urltoken_url = 'https://login.microsoftonline.com/<DIRECTORY (tenant) ID>/oauth2/token'token_data = { ‘grant_type’: ‘password’, ‘client_id’: app_id, ‘client_secret’: client_secret, ‘resource’: ‘https://graph.microsoft.com', ‘scope’:’https://graph.microsoft.com', ‘username’:’john.doe@companyxxx.com’, #Account with no 2MFA ‘password’:’Happy2020’,}token_r = requests.post(token_url, data=token_data)token = token_r.json().get(‘access_token’)
Test the token
token_r.content
Create a plan on Office 365 Planner and create a group dictionary as below:
#groups listgroup_ids = {‘plannergroup01’:’xxxx-xxx–xxx-xxxx–xx',’test’:’xxxx-xxx–xxx-xxxx–xx’}#define required listsuserId,displayName,mailAddress,plans_data,planId,PlanGrpOwnerId,PlanTitle,PlanCreatedBy,bucketId,bucketName,bucketPlanId,taskId,taskPlanId,taskBucketId,taskName,taskPercentComplete,taskStartDateTime,taskDueDateTime,taskCompleteDateTime,taskIdAssignment,taskUserIdAssignment = [],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]
Users Query:
# Use the token using microsoft graph endpointsusers_url = ‘https://graph.microsoft.com/v1.0/users?$top=500'headers = { ‘Authorization’: ‘Bearer {}’.format(token)}user_response_data = json.loads(requests.get(users_url, headers=headers).text)# user_response_data[‘@odata.nextLink’]#initial user data#get all usersfor user in user_response_data[‘value’]: userId.append(user[‘id’]) displayName.append(user[‘displayName’]) mailAddress.append(user[‘userPrincipalName’]) users_dict = {‘userId’:userId,’displayName’:displayName,’mailAddress’:mailAddress}users_df = pd.DataFrame(data=users_dict)#additional user query for pagingwhile ‘@odata.nextLink’ in user_response_data: user_response_data = json.loads(requests.get(users_url, headers=headers).text) if ‘@odata.nextLink’ in user_response_data:  users_url = user_response_data[‘@odata.nextLink’]  for user in user_response_data[‘value’]: userId.append(user[‘id’]) displayName.append(user[‘displayName’]) mailAddress.append(user[‘userPrincipalName’])users_dict = {‘userId’:userId,’displayName’:displayName,’mailAddress’:mailAddress}users_df = pd.DataFrame(data=users_dict)users_df.head()
Planner plans Query:
#get all plansfor key, value in group_ids.items():  plans_url = ‘https://graph.microsoft.com/v1.0/groups/'+ value +’/planner/plans?$top=500'  plans_response_data = json.loads(requests.get(plans_url, headers=headers).text)for plan in plans_response_data[‘value’]: planId.append(plan[‘id’]) PlanGrpOwnerId.append(plan[‘owner’]) PlanTitle.append(plan[‘title’]) PlanCreatedBy.append(plan[‘createdBy’][‘user’][‘id’]) plans_dict = {‘planId’:planId,’PlanGrpOwnerId’:PlanGrpOwnerId,’PlanTitle’:PlanTitle,’PlanCreatedBy’:PlanCreatedBy}plans_df = pd.DataFrame(data=plans_dict)
Planner Buckets Query:
#get all bucketsfor plan_id in planId: buckets_url = ‘https://graph.microsoft.com/v1.0/planner/plans/'+ plan_id +’/buckets’ buckets_response_data = json.loads(requests.get(buckets_url, headers=headers).text)  for bucket in buckets_response_data[‘value’]: bucketId.append(bucket[‘id’]) bucketName.append(bucket[‘name’]) bucketPlanId.append(bucket[‘planId’])  bucket_dict = {‘bucketId’:bucketId,’bucketName’:bucketName,’bucketPlanId’:bucketPlanId} bucket_df = pd.DataFrame(data=bucket_dict)
Planner Tasks Query:
#get all tasksfor plan_id in planId: tasks_url = ‘https://graph.microsoft.com/v1.0/planner/plans/'+ plan_id +’/tasks’ tasks_response_data = json.loads(requests.get(tasks_url, headers=headers).text)  for task in tasks_response_data[‘value’]: taskId.append(task[‘id’]) taskPlanId.append(task[‘planId’]) taskBucketId.append(task[‘bucketId’]) taskName.append(task[‘title’]) taskPercentComplete.append(str(task[‘percentComplete’])) if task[‘startDateTime’] is not None: taskStartDateTime.append(datetime.strptime(task[‘startDateTime’], ‘%Y-%m-%dT%H:%M:%SZ’)) else: taskStartDateTime.append(0) if task[‘dueDateTime’] is not None: taskDueDateTime.append(datetime.strptime(task[‘dueDateTime’], ‘%Y-%m-%dT%H:%M:%SZ’)) else: taskDueDateTime.append(0)# if task[‘completedDateTime’] is not None:# complete_time = task[‘completedDateTime’].replace(task[‘completedDateTime’].split(‘:’)[-1],’00Z’)# taskCompleteDateTime.append(datetime.strptime(complete_time, ‘%Y-%m-%dT%H:%M:%S%z’))# else:# taskCompleteDateTime.append(str(0))  for assignment in task[‘assignments’]: taskIdAssignment.append(task[‘id’])  taskUserIdAssignment.append(assignment)tasks_dict = {‘taskId’:taskId,’taskPlanId’:taskPlanId,’taskBucketId’:taskBucketId,’taskName’:taskName,’taskPercentComplete’:taskPercentComplete,’taskStartDateTime’:taskStartDateTime,’taskDueDateTime’:taskDueDateTime,’taskCompleteDateTime’:0} task_df = pd.DataFrame(data=tasks_dict)
Insert user data into a database:
The below function can be replicated for all data frames created above. In my case, I am using SQL Server 2017.
server = 'SERVERNAME\INSTANCEID,64346'database = 'GRAPHAPI' username = 'uname' password = 'Happy2020'def insert_user_db(users_df,server,database,username,password):#Create a connection stringcnxn = pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER='+server+';DATABASE='+database+';UID='+username+';PWD='+ password)    cursor = cnxn.cursor()  for index in range(users_df.shape[0]):  #insert into table  try: insert_query = “INSERT INTO GRAPHAPI.dbo.[DIM_User] ([userId],[displayName],[mailAddress]) VALUES (?,?,?)” cursor.execute(insert_query,users_df[‘userId’][index],users_df[‘displayName’][index],users_df[‘mailAddress’][index])except:cnxn.rollback()finally:cnxn.commit() cnxn.close()#Call the functioninsert_user_db(users_df,server,database,username,password)
With the wide range of Microsoft suite services, there are many other application scenarios for Graph API requests. This tremendous amount of data provide business intelligence for an organization and would enable developers build data-driven applications.
Like what you read? Follow me on LinkedIn or Medium