Querying all Azure users with Python and MS Graph

I struggled a bit with this one, as i wasn’t used to Pagination. It took me a while of googling and reading up to piece this together and get the results i was looking for.

The issue with looking at all different code out there as a reference, is that MS makes enough changes making finding information which is still relevant kind of difficult.

Below is my ‘Generic’ Script to Query MS Graph and Return all users into a Json File. Prerequisites having an Enterprise App setup and registered in Azure with appropriate API permissions set.

import asyncio
import ssl
import pandas as pd
from datetime import datetime
from requests_ntlm import HttpNtlmAuth
from cryptography.utils import CryptographyDeprecationWarning
from azure.identity.aio import ClientSecretCredential
from msgraph import GraphServiceClient
from msgraph.generated.device_management.device_management_request_builder import DeviceManagementRequestBuilder, QueryParameters
from msgraph.generated.users.users_request_builder import UsersRequestBuilder
dt = datetime.now()
date = dt.date()

# Disable SSL verification
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE

# TODO Set Microsoft Graph Variables
client_id = "<>"
tenant_id = "<Your Tenenet ID>"
scope = ["api://<client_id>/graph_query"]
secret_id = "<>"
value = "<>"
redirect_uri = "https://login.microsoftonline.com/common/oauth2/nativeclient"



credential = ClientSecretCredential(tenant_id=tenant_id,
                                        client_id=client_id,
                                        client_secret=value,
                                        ssl_context=ssl_context
                                        )

client = GraphServiceClient(credential)
users_df = pd.DataFrame()
attributes = ['display_name','companyName', 'city','country','mail']
async def get_all_users(attributes):
    query_params = {
        "$select": ",".join(attributes)
    }
    request_config = UsersRequestBuilder.UsersRequestBuilderGetRequestConfiguration(
        query_parameters=query_params
    )
    # Authenticate
    client = GraphServiceClient(credentials=credential)

    users = []
    next_link = None

    try:
        # Get the first page of users
        initial_response = await client.users.get()
        if initial_response and initial_response.value:
            users.extend(initial_response.value)
            next_link = initial_response.odata_next_link

        # Iterate through subsequent pages using odata_next_link
        while next_link is not None:
            next_response = await client.users.with_url(next_link).get()
            if next_response and next_response.value:
                users.extend(next_response.value)
                next_link = next_response.odata_next_link
            else:
                next_link = None # Stop if no more users are returned

        # Process the retrieved users
        for user in users:
            users_df.loc[len(users_df), 'user'] = user

    except APIError as e:
        print(f'Error fetching users: {e.error.message}')

if __name__ == "__main__":
    asyncio.run(get_all_users(attributes=attributes))
    print(users_df)
    users_df.to_json(f"data/azure_users_{date}.json", index=False)