Universal Cloud REST API connector workflow for Microsoft 365 Defender

You can customize your workflow and workflow parameters based on the default workflow.

A workflow is an XML document that describes the alert retrieval process. The workflow defines one or more parameters, which can be explicitly assigned values in the workflow XML or can derive values from the workflow parameter values XML document. The workflow consists of multiple actions that run sequentially.

Microsoft 365 Defender default workflow

Use the following XML to populate the Workflow field in the Universal Cloud REST API connector parameters section.

Click the Copy to clipboard icon at the upper right of the code block, and then paste the content into the Workflow field.

<?xml version="1.0" encoding="UTF-8" ?>
<Workflow name="Microsoft365Defender" version="1.0" xmlns="http://qradar.ibm.com/UniversalCloudRESTAPI/Workflow/V2">
    <Parameters>
        <Parameter name="tenantid" label="Tenant ID" required="true" />
        <Parameter name="clientid" label="Client ID" required="true" />
        <Parameter name="clientsecret" label="Client Secret" required="true" secret="true" />
        <Parameter name="msidentityhost" label="Microsoft identity platform" required="true" />
        <Parameter name="msapihost" label="Microsoft API" required="true" />
    </Parameters>

    <Actions>
        <ClearStatus />

        <!-- Initialize bookmark to 1 week back in milliseconds -->
        <Initialize path="/alertFromPreformat" value="${time() - (60000 * 60 * 24 * 7)}" />
        <FormatDate pattern="yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" timeZone="UTC" time="${/alertFromPreformat}" savePath="/alertFrom" />
        <Set path="/toPreformat" value="${time()}" />
        <FormatDate pattern="yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" timeZone="UTC" time="${/toPreformat}" savePath="/alertTo" />

        <!-- Filter for alerts -->
        <Set path="/alertFilter" value="(createdDateTime ge ${/alertFrom}) and (createdDateTime lt ${/alertTo})"/>

        <!-- Alerts endpoint -->
        <Set path="/alerts/initquery" value="https://${/msapihost}/beta/security/alerts_v2"/>

        <!-- Paging URL -->
        <Set path="/alerts/queryToExecute" value="${/alerts/initquery}"/>

        <Set path="/alertCount" value="0"/>

        <Set path="/alertSort" value="lastUpdateDateTime asc"/>

        <!-- Request OAuth2 token -->
        <CallEndpoint url="https://${/msidentityhost}/${/tenantid}/oauth2/token" method="POST" savePath="/graph/auth">
            <RequestHeader name="Accept" value="application/json" />
            <UrlEncodedFormRequestBody>
                <Parameter name="grant_type" value="client_credentials" />
                <Parameter name="client_id" value="${/clientid}" />
                <Parameter name="client_secret" value="${/clientsecret}" />
                <Parameter name="resource" value="https://${/msapihost}/" />
            </UrlEncodedFormRequestBody>
        </CallEndpoint>

        <!-- Catch any status code other than 200 -->
        <If condition="/graph/auth/status_code != 200">
            <Abort reason="Failed to request OAuth2 token https://${/msidentityhost}/${/tenantid}/oauth2/token: ${/graph/auth/status_code} ${/graph/auth/status_message}" />
        </If>

        <DoWhile condition="exists /alerts/response/body/'@odata.nextLink'"> <!-- nextLink contains all skip information required, directly query if needed. -->
            <CallEndpoint url="${/alerts/queryToExecute}" method="GET" savePath="/alerts/response">
                <BearerAuthentication token="${/graph/auth/body/access_token}" />

                <QueryParameter name="filter" value="${/alertFilter}" omitIfEmpty="true" />
                <QueryParameter name="orderby" value="${/alertSort}" omitIfEmpty="true" />

                <RequestHeader name="Accept" value="application/json" />
            </CallEndpoint>

            <!-- Catch any status code other than 200 -->
            <If condition="/alerts/response/status_code != 200">
                <Abort reason="${/alerts/response/body/error/code}: ${/alerts/response/body/error/message}" />
            </If>
            <Else>
                <SetStatus type="INFO" message="Successfully queried for events." />
            </Else>

            <Set path="/alertCount" value="${/alertCount + count(/alerts/response/body/value)}"/>
            <Log type="DEBUG" message="We received a total of ${/alertCount} alert(s)." />

            <!-- Post the alerts -->
            <If condition="${count(/alerts/response/body/value)} > 0">
                <PostEvents path="/alerts/response/body/value" source="${/client_id}" />
            </If>

            <!-- If there's a @odata.nextLink, update paging URL -->
            <If condition="exists /alerts/response/body/'@odata.nextLink'">
                <Set path="/alerts/queryToExecute" value="${/alerts/response/body/'@odata.nextLink'}" />
                <!-- Set these to empty since they're included in @odata.nextLink attribute -->
                <Set path="/alertFilter" value=""/>
                <Set path="/alertSort" value=""/>
            </If>
        </DoWhile>

        <!-- Update the bookmarks-->
        <ParseDate pattern="yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" timeZone="UTC" date="${/alertTo}" savePath="/alertFromPreformat" />
        <Set path="/alertFromPreformat" value="${/alertFromPreformat + 1}"/>
        <Delete path="/graph/auth" />
        <Delete path="/alerts/response" />
        <Delete path="/alerts/initquery" />
        <Delete path="/alerts/queryToExecute" />
    </Actions>

    <Tests>
        <DNSResolutionTest host="${/msidentityhost}"/>
        <DNSResolutionTest host="${/msapihost}"/>
        <TCPConnectionTest host="${/msidentityhost}"/>
        <TCPConnectionTest host="${/msapihost}"/>
        <SSLHandshakeTest host="${/msidentityhost}"/>
        <SSLHandshakeTest host="${/msapihost}"/>
        <HTTPConnectionThroughProxyTest url="https://${/msidentityhost}"/>
        <HTTPConnectionThroughProxyTest url="https://${/msapihost}"/>
    </Tests>
</Workflow>

Microsoft 365 Defender default workflow parameters

Use the following XML to populate the Workflow Parameter Values field in the Universal Cloud REST API connector parameters section.

Click the Copy to clipboard icon at the upper right of the code block, and then paste the content to a text file. Replace the values for <tenantid>, <clientid> and <clientsecret> with your own values. Then copy the updated content into the Workflow Parameter Values field.

<?xml version="1.0" encoding="UTF-8" ?>
<WorkflowParameterValues xmlns="http://qradar.ibm.com/UniversalCloudRESTAPI/WorkflowParameterValues/V2">
	<Value name="tenantid" value=""/>
	<Value name="clientid" value=""/>
	<Value name="clientsecret" value=""/>
	<Value name="msidentityhost" value="login.microsoftonline.com"/>
	<Value name="msapihost" value="graph.microsoft.com"/>
</WorkflowParameterValues>