Microsoft Graph API

Use the Microsoft Graph API integration to interact with Microsoft APIs that do not have dedicated integrations in Cortex XSOAR, for example, Mail Single-User, etc.

Utilities · Microsoft Graph API

Details

IDMicrosoft Graph API
ProviderMicrosoft
CategoryUtilities
From Version5.0.0
Docker Imagedemisto/crypto:1.0.0.10120494
Supported ModulesAgentix Cloud Runtime Security Cloud Posture Security XSIAM EDR Cortex Cloud

README

Use the Microsoft Graph API integration to interact with Microsoft APIs that do not have dedicated integrations in Cortex XSOAR, for example, Mail Single-User, etc.


Note: In this documentation, we will use the Application resource type as an example.

Authorization

In order to use the integration, there are 2 application authentication methods available.

Note: Depending on the authentication method that you use, the integration parameters might change.

Cortex XSOAR Azure app

In this method, the device authorization grant flow is used.

To configure the integration:

  1. The Application ID integration parameter should be set to 8922dd2d-7539-4711-b839-374f86083959 (the Cortex XSOAR Azure app ID).

  2. The Scope integration parameter should be set according to the requested OAuth2 permissions types to grant access to in Microsoft identity platform, for more details see the Microsoft documentation.
    For example, if we wish to use the List applications API, we need at least the Application.Read.All scope.

  3. The Application Secret and the Tenant ID integration parameters should be left blank.

  4. Run the msgraph-api-auth-start command - you will be prompted to open the page https://microsoft.com/devicelogin and enter the generated code.

  5. Run the msgraph-api-auth-complete command

  6. Run the msgraph-api-test command to ensure connectivity to Microsoft.

Self Deployed Azure app

For more information, refer to the following article.

Configure the Azure app

  1. Register the app.
  2. Add the requested API permissions according to the APIs you wish to use.
    For example, according to the Create application API documentation in order to create applications we need the Application.ReadWrite.All application permission.
  3. Grant admin consent for the chosen permissions.

Note: The integration stores in cache the API access token based on the permissions it is first run with, so if the permissions are modified, it is recommended to create a new instance of the integration.

Configure Microsoft Graph API on Cortex XSOAR

  1. Navigate to Settings > Integrations > Servers & Services.
  2. Search for Microsoft Graph API.
  3. Click Add instance to create and configure a new integration instance.
    4.

    Parameter Description Required
    Azure Cloud See option table below. False
    Application ID   False
    Application Secret (Required for using Self Deployed Azure app)   False
    Tenant ID (Required for using Self Deployed Azure app)   False
    Application redirect URI (for Self Deployed - Authorization Code Flow)   False
    Authorization code (for Self Deployed - Authorization Code Flow)   False
    Certificate Thumbprint   False
    Private Key   False
    Certificate Thumbprint Used for certificate authentication. As appears in the “Certificates & secrets” page of the app. False
    Private Key Used for certificate authentication. The private key of the registered certificate. False
    Use a self-deployed Azure Application Select this checkbox if you are using a self-deployed Azure application. False
    Use Azure Managed Identities Relevant only if the integration is running on Azure VM. If selected, authenticates based on the value provided for the Azure Managed Identities Client ID field. If no value is provided for the Azure Managed Identities Client ID field, authenticates based on the System Assigned Managed Identity. For additional information, see the Help tab. False
    Azure Managed Identities Client ID The Managed Identities client ID for authentication - relevant only if the integration is running on Azure VM. False
    Azure AD endpoint Azure AD endpoint associated with a national cloud. See note below. False
    Scope (Required for using Cortex XSOAR Azure app) A space-separated list of scopes that you want to consent to. False
    Trust any certificate (not secure)   False
    Use system proxy settings   False

    Azure cloud options

    Azure Cloud Description
    Worldwide The publicly accessible Azure Cloud
    US GCC Azure cloud for the USA Government Cloud Community (GCC)
    US GCC-High Azure cloud for the USA Government Cloud Community High (GCC-High)
    DoD Azure cloud for the USA Department of Defense (DoD)
    Germany Azure cloud for the German Government
    China Azure cloud for the Chinese Government
    Custom Custom endpoint configuration to the Azure cloud. See note below.
  • Note: In most cases, setting Azure cloud is preferred to setting Azure AD endpoint. Only use it in cases where a custom URL is required for accessing a national cloud.
  1. Click Test to validate the URLs, token, and connection.

Commands

You can execute these commands from the Cortex XSOAR CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

msgraph-api-auth-start


Run this command to start the authorization process and follow the instructions in the command results.

msgraph-api-auth-complete


Run this command to complete the authorization process.
Should be used after running the msgraph-api-auth-start command.

msgraph-api-test


Tests connectivity to Microsoft.

msgraph-api-request


Run a Microsoft Graph API query.

Base Command

msgraph-api-request

Input

Argument Name Description Required
resource The resource in Microsoft Graph to refer. Required
http_method The HTTP method used for the request to Microsoft Graph. Possible values are: GET, POST, DELETE, PUT, PATCH. Default is GET. Optional
api_version The version of the Microsoft Graph API to use. Possible values are: v1.0, beta. Default is v1.0. Optional
request_body The request body (required for POST queries). Optional
odata OData system query options, e.g., $filter=startswith(givenName, ‘J’). For more details see https://docs.microsoft.com/en-us/graph/query-parameters. It is recommended to use the $top query option to limit the result. Optional
populate_context If “true” will populate the API response to the context data. Possible values are: true, false. Default is true. Optional
headers A comma-separated list of headers to send in the GET request, for example: ConsistencyLevel:eventual,User-Agent:MyApp/1.0. Optional

Context Output

The context data output depends on the resource executed.
The populate_context argument sets whether to output to the context data, under the path MicrosoftGraph.
For resources which return a large response, we recommend to narrow the results by using the odata argument or outputting to the context data using Extend Context.

msgraph-api-auth-reset


Run this command if for some reason you need to rerun the authentication process.

Base Command

msgraph-api-auth-reset

Input

There are no input arguments for this command.

Context Output

There is no context output for this command.

msgraph-api-generate-login-url


Generate the login URL used for Authorization code flow.

Base Command

msgraph-api-generate-login-url

Input

There are no input arguments for this command.

Context Output

There is no context output for this command.

Command Example

msgraph-api-generate-login-url

Human Readable Output

Authorization instructions

  1. Click on the login URL to sign in and grant Cortex XSOAR permissions for your Azure Service Management.
    You will be automatically redirected to a link with the following structure:
    REDIRECT_URI?code=AUTH_CODE&session_state=SESSION_STATE
  2. Copy the AUTH_CODE (without the code= prefix, and the session_state parameter)
    and paste it in your instance configuration under the Authorization code parameter.

Usage

Let’s say we want to list all the applications.

We can see that according to the HTTP request:

  • The HTTP method is GET
  • The resource is /applications

So in order to list all the applications using the integration, we would run the command: !msgraph-api resource=/applications http_method=GET

Configuration parameters

  • azure_cloud — Azure Cloud
  • app_id — Application ID
  • credentials
  • tenant_id — Tenant ID (Required for using Self Deployed Azure app)
  • redirect_uri — Application redirect URI (for Self Deployed - Authorization Code Flow)
  • auth_code
  • creds_certificate — Certificate Thumbprint
  • certificate_thumbprint — Certificate Thumbprint
  • private_key — Private Key
  • self_deployed — Use a self-deployed Azure Application
  • use_managed_identities — Use Azure Managed Identities
  • managed_identities_client_id
  • azure_ad_endpoint — Azure AD endpoint
  • scope — Scope (Required for using Cortex XSOAR Azure app)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • app_secret — Application Secret (Deprecated)

Commands (6)

  • msgraph-api-auth-complete

    Run this command to complete the authorization process. Should be used after running the msgraph-auth-start command.

  • msgraph-api-auth-reset

    Run this command if for some reason you need to rerun the authentication process.

  • msgraph-api-auth-start

    Run this command to start the authorization process and follow the instructions in the command results.

  • msgraph-api-generate-login-url

    Generate the login URL used for Authorization code flow.

  • msgraph-api-request

    Run a Microsoft Graph API query.

  • msgraph-api-test

    Tests connectivity to Microsoft.

from typing import Any

import demistomock as demisto
import urllib3
from CommonServerPython import *
from MicrosoftApiModule import *  # noqa: E402

from CommonServerUserPython import *

urllib3.disable_warnings()


class MsGraphClient:
    def __init__(
        self,
        app_id: str,
        scope: str,
        app_secret: str,
        tenant_id: str,
        verify: bool,
        proxy: bool,
        azure_cloud: AzureCloud,
        auth_code: str,
        redirect_uri: str,
        certificate_thumbprint: str | None = None,
        private_key: str | None = None,
        managed_identities_client_id: str | None = None,
    ):
        # Build a dynamic permission scope for the Microsoft Graph API based on the selected Azure cloud.
        scope = azure_cloud.endpoints.microsoft_graph_resource_id.rstrip("/") + "/.default"
        client_args = {
            "base_url": azure_cloud.endpoints.microsoft_graph_resource_id.rstrip("/"),
            "auth_id": app_id,
            "scope": scope,
            "enc_key": app_secret,
            "tenant_id": tenant_id,
            "verify": verify,
            "proxy": proxy,
            "self_deployed": True,
            "grant_type": AUTHORIZATION_CODE if auth_code and redirect_uri else CLIENT_CREDENTIALS,
            "ok_codes": (200, 201, 202, 204),
            "azure_ad_endpoint": azure_cloud.endpoints.active_directory,
            "private_key": private_key,
            "certificate_thumbprint": certificate_thumbprint,
            "managed_identities_client_id": managed_identities_client_id,
            "managed_identities_resource_uri": Resources.graph,
            "azure_cloud": azure_cloud,
            "auth_code": auth_code,
            "redirect_uri": redirect_uri,
            "command_prefix": "msgraph-api",
        }
        if not ((app_secret or (certificate_thumbprint and private_key)) and tenant_id):
            client_args["grant_type"] = DEVICE_CODE
            client_args["token_retrieval_url"] = urljoin(
                azure_cloud.endpoints.active_directory, "/organizations/oauth2/v2.0/token"
            )
            client_args["scope"] = scope
        self.ms_client = MicrosoftClient(**client_args)  # type: ignore[arg-type]

    def generic_request(
        self,
        resource: str,
        http_method: str = "GET",
        api_version: str = "v1.0",
        odata: str | None = None,
        request_body: dict | None = None,
        headers: dict | None = None,
    ):
        url_suffix = urljoin(api_version, resource)
        if odata:
            url_suffix += f"?{odata}"
        res = self.ms_client.http_request(
            method=http_method, url_suffix=url_suffix, json_data=request_body, resp_type="resp", headers=headers
        )
        return res.json() if res.content else None


def start_auth(client: MsGraphClient) -> CommandResults:  # pragma: no cover
    result = client.ms_client.start_auth("!msgraph-api-auth-complete")
    return CommandResults(readable_output=result)


def complete_auth(client: MsGraphClient):  # pragma: no cover
    client.ms_client.get_access_token()
    return "Authorization completed successfully."


def test_module(client: MsGraphClient, managed_identities_client_id: str | None) -> str:  # pragma: no cover
    if client.ms_client.grant_type == CLIENT_CREDENTIALS or managed_identities_client_id:
        client.ms_client.get_access_token()
        return "ok"
    else:
        raise DemistoException(
            "The *Test* button is not available when using `Cortex XSOAR Azure app`, "
            "`self-deployed - Device Code Flow` or "
            " `self-deployed - Authorization Code Flow`. "
            "Use the !msgraph-api-test command instead once all relevant parameters have been entered."
        )


def test_command(client: MsGraphClient) -> CommandResults:  # pragma: no cover
    client.ms_client.get_access_token()
    return CommandResults(readable_output="```✅ Success!```")


def generic_command(client: MsGraphClient, args: dict[str, Any]) -> CommandResults:
    request_body = args.get("request_body")
    results: dict
    if request_body and isinstance(request_body, str):
        try:
            request_body = json.loads(request_body)
        except json.decoder.JSONDecodeError as e:
            raise ValueError(f"Invalid request body - {e!s}")
    headers = args.get("headers")

    http_method = args.get("http_method", "GET")

    response = client.generic_request(
        resource=args.get("resource", ""),
        http_method=http_method,
        api_version=args.get("api_version", "v1.0"),
        odata=args.get("odata", ""),
        request_body=request_body,
        headers=dict(subString.split(":") for subString in headers.split(",")) if headers else None,
    )

    if not response:
        results = {
            "readable_output": "The API query ran successfully and returned no content.",
        }
    else:
        results = {"raw_response": response}

        if argToBoolean(args.get("populate_context", "true")):
            outputs = get_response_outputs(response)
            if outputs is True:
                return CommandResults(readable_output="The API query ran successfully and returned no content.")
            results["outputs"] = outputs
            results["outputs_prefix"] = "MicrosoftGraph"

    return CommandResults(**results)  # type: ignore[arg-type]


def get_response_outputs(response: dict) -> Union[dict, list]:
    if "value" in response:
        return response["value"]
    res = dict(response)
    res.pop("@odata.context", None)
    return res


def main() -> None:  # pragma: no cover
    params = demisto.params()
    command = demisto.command()
    demisto.debug(f"Command being called is {command}")

    scope = "offline_access "
    if params.get("scope"):
        scope += params.get("scope")

    azure_cloud = get_azure_cloud(params, "MicrosoftGraphAPI")
    app_secret = params.get("app_secret") or (params.get("credentials") or {}).get("password")
    app_secret = app_secret if isinstance(app_secret, str) else ""
    certificate_thumbprint = params.get("creds_certificate", {}).get("identifier") or params.get("certificate_thumbprint")
    private_key = replace_spaces_in_credential(params.get("creds_certificate", {}).get("password")) or params.get("private_key")
    managed_identities_client_id = get_azure_managed_identities_client_id(params)
    auth_code = params.get("auth_code", {}).get("password", "")
    redirect_uri = params.get("redirect_uri", "")

    try:
        client = MsGraphClient(
            app_id=params.get("app_id"),
            scope=scope,
            app_secret=app_secret,
            tenant_id=params.get("tenant_id"),
            verify=not params.get("insecure", False),
            proxy=params.get("proxy", False),
            azure_cloud=azure_cloud,
            certificate_thumbprint=certificate_thumbprint,
            private_key=private_key,
            managed_identities_client_id=managed_identities_client_id,
            auth_code=auth_code,
            redirect_uri=redirect_uri,
        )

        if command == "test-module":
            result = test_module(client, managed_identities_client_id)
            return_results(result)
        elif command == "msgraph-api-request":
            return_results(generic_command(client, demisto.args()))
        elif command == "msgraph-api-auth-start":
            return_results(start_auth(client))
        elif command == "msgraph-api-auth-complete":
            return_results(complete_auth(client))
        elif command == "msgraph-api-test":
            return_results(test_command(client))
        elif command == "msgraph-api-auth-reset":
            return_results(reset_auth())
        elif command == "msgraph-api-generate-login-url":
            return_results(generate_login_url(client.ms_client))
    except Exception as e:
        return_error(f"Failed to execute {demisto.command()} command. Error: {e!s}")


if __name__ in ("__main__", "__builtin__", "builtins"):
    main()