Graph Batch Endpoint

This is only a small blog post but maybe for most of you very helpful especially if you work a lot with Microsoft Graphs. Often the problem is you want to run multiple calls and then you have to loop through the single items or have a long line of calls.

During writing another blog post, I found out that there is a batch endpoint for MS Graph. In this blog, I will show you how you can use it and give you also an example script.

How can I use this endpoint?

The usage is very easy. What you have to do is build a JSON where the different calls are listed. One example is from the Intune Portal to get a summary of the tenant state. This JSON looks like this:

{
    "requests": [
        {
            "id": "getDeviceComplianceStateSummary",
            "method": "GET",
            "url": "/deviceManagement/deviceCompliancePolicyDeviceStateSummary",
            "headers": {"x-ms-command-name": "fetchDeviceComplianceStateSummaryBatch"},
        },
        {
            "id": "fetchSubscriptionState",
            "method": "GET",
            "url": "/deviceManagement/subscriptionState",
            "headers": {"x-ms-command-name": "fetchSubscriptionStateBatch"},
        },
        {
            "id": "getFailedAppCount",
            "method": "POST",
            "url": "/deviceManagement/reports/getFailedMobileAppsSummaryReport",
            "body": {"filter": ""},
            "headers": {"Content-Type": "application/json", "x-ms-command-name": "fetchFailedAppCountBatch"},
        },
        {
            "id": "getDeviceConfigPolicySummary",
            "method": "POST",
            "url": "/deviceManagement/reports/getDeviceConfigurationPolicyStatusSummary",
            "body": {
                "filter": "(PolicyBaseTypeName eq 'DeviceManagementConfigurationPolicy') or (PolicyBaseTypeName eq 'Microsoft.Management.Services.Api.DeviceConfiguration') or (PolicyBaseTypeName eq 'Microsoft.Management.Services.Api.DeviceManagementIntent')"
            },
            "headers": {"Content-Type": "application/json", "x-ms-command-name": "fetchDeviceConfigPolicySummary"},
        },
    ]
}

You see in the end you only have to define the method, header, url and optimonal the body. It is very simple

How can I use it in a script?

Attached you can find an example script from me on how you can use it in Python:

import requests
from azure.identity import InteractiveBrowserCredential

credential = InteractiveBrowserCredential()
token = credential.get_token("https://graph.microsoft.com/.default")

def call_graph(access_token: str, url: str, body, method: str = "GET"):
    headers = {
        "Authorization": f"Bearer {access_token}",
        "Content-Type": "application/json",
    }
    if method == "GET":
        response = requests.get(
            url,
            headers=headers,
        )
    else:
        response = requests.post(
            url,
            headers=headers,
            json=body,
        )
    response.raise_for_status()
    return response.json()



url = "https://graph.microsoft.com/beta/$batch"
body = {
    "requests": [
        {
            "id": "getDeviceComplianceStateSummary",
            "method": "GET",
            "url": "/deviceManagement/deviceCompliancePolicyDeviceStateSummary",
            "headers": {"x-ms-command-name": "fetchDeviceComplianceStateSummaryBatch"},
        },
        {
            "id": "fetchSubscriptionState",
            "method": "GET",
            "url": "/deviceManagement/subscriptionState",
            "headers": {"x-ms-command-name": "fetchSubscriptionStateBatch"},
        },
        {
            "id": "getFailedAppCount",
            "method": "POST",
            "url": "/deviceManagement/reports/getFailedMobileAppsSummaryReport",
            "body": {"filter": ""},
            "headers": {"Content-Type": "application/json", "x-ms-command-name": "fetchFailedAppCountBatch"},
        },
        {
            "id": "getDeviceConfigPolicySummary",
            "method": "POST",
            "url": "/deviceManagement/reports/getDeviceConfigurationPolicyStatusSummary",
            "body": {
                "filter": "(PolicyBaseTypeName eq 'DeviceManagementConfigurationPolicy') or (PolicyBaseTypeName eq 'Microsoft.Management.Services.Api.DeviceConfiguration') or (PolicyBaseTypeName eq 'Microsoft.Management.Services.Api.DeviceManagementIntent')"
            },
            "headers": {"Content-Type": "application/json", "x-ms-command-name": "fetchDeviceConfigPolicySummary"},
        },
    ]
}
response = call_graph(token.token, url, body, "POST")
print(response)

Some of the content is also base64 encoded. You can decode this with:

import base64
dec = base64.b64decode(encoded_string)