Safeguard
Cloud Security•

Turn On Microsoft Graph Activity Logs Before the Next App Breach

When the ConnectWise backup app was abused in October 2026, tenants without Graph activity logs couldn't see what it read. How to enable them, keep them and query them.

Safeguard Security Team
6 min read

On 15 October 2026, an attacker signed in to Microsoft 365 tenants as the ConnectWise Cloud Backup app and requested Microsoft Graph tokens with the permissions customers had granted it. Responders could see the token requests in service principal sign-in logs. A full record of what the app did with those tokens, request by request, existed only in tenants that were already collecting Microsoft Graph activity logs. Todyl's write-up of the incident lists enabling them as a step for future visibility.

That word "already" is the problem. Graph activity logs are not retroactive. This guide shows how to turn them on today, with config you can keep in source control, and the queries you'll want ready.

What's affected

Per Microsoft's data retention reference, Entra audit logs and sign-in logs are kept for 7 days on Entra ID Free and 30 days on P1 or P2. Graph activity logs are different in two ways:

  • They require an Entra ID P1 or P2 license.
  • Microsoft states that data "isn't retained unless it's archived to a storage account or integrated with analytics tools," and collection starts only when the log category is enabled in diagnostic settings.

So a tenant with P1 that never configured a diagnostic setting has sign-in records for an abused app, but no record of the API calls. Microsoft also notes the logs only cover requests against your own tenant; you can't see what a multitenant app did elsewhere.

How to check if you're exposed

In the Microsoft Entra admin center, sign in as at least a Security Administrator and go to Entra ID, Monitoring & health, Diagnostic settings. Open each setting and look for the MicrosoftGraphActivityLogs category. If no setting has it checked, you're not collecting.

If you do have a Log Analytics workspace attached, confirm data is arriving:

MicrosoftGraphActivityLogs
| where TimeGenerated > ago(1d)
| summarize Requests = count() by bin(TimeGenerated, 1h)

An empty result means the category is off or pointed somewhere else.

What to do now

  1. Pick a destination. Microsoft supports a Log Analytics workspace for querying, an Azure Storage account for long-term archive and Event Hubs for streaming to a SIEM. Most teams want a workspace plus a storage archive. Budget for volume: Microsoft's own estimate is about 15 GiB of Azure Monitor Logs per month for a 1,000-user tenant and about 1,200 GiB for 100,000 users.

  2. Enable it in the portal. In Diagnostic settings, select Add diagnostic setting, give it a name, tick MicrosoftGraphActivityLogs along with AuditLogs, SignInLogs, NonInteractiveUserSignInLogs and ServicePrincipalSignInLogs, select Send to Log Analytics workspace, choose the subscription and workspace, then Save.

  3. Or deploy it as code. The microsoft.aadiam/diagnosticSettings resource is deployed at tenant scope. This Bicep follows Microsoft's published resource format:

    targetScope = 'tenant'
    
    param workspaceId string
    
    resource entraLogs 'microsoft.aadiam/diagnosticSettings@2017-04-01' = {
      name: 'entra-to-law'
      properties: {
        workspaceId: workspaceId
        logs: [
          {
            category: 'AuditLogs'
            enabled: true
          }
          {
            category: 'SignInLogs'
            enabled: true
          }
          {
            category: 'NonInteractiveUserSignInLogs'
            enabled: true
          }
          {
            category: 'ServicePrincipalSignInLogs'
            enabled: true
          }
          {
            category: 'MicrosoftGraphActivityLogs'
            enabled: true
          }
        ]
      }
    }
    

    Deploy it with the Azure CLI tenant deployment command, with WORKSPACE_RESOURCE_ID set to your Log Analytics workspace resource ID:

    az deployment tenant create \
      --name entraDiagnostics \
      --location WestUS \
      --template-file main.bicep \
      --parameters workspaceId="$WORKSPACE_RESOURCE_ID"
    

    You can't filter Graph activity logs inside diagnostic settings. To cut cost, Microsoft points to workspace transformations or the Basic log data plan.

  4. Save the queries you'll need during an incident. All columns below come from Microsoft's schema for the table. First, everything one app did, by source IP and outcome:

    MicrosoftGraphActivityLogs
    | where TimeGenerated > ago(7d)
    | where AppId == "506e08f9-6e9a-4da4-942b-a099a03644ae"
    | summarize Requests = count() by IPAddress, RequestMethod, ResponseStatusCode, UserAgent
    | sort by Requests desc
    

    Then which resources it touched. This trims the version prefix the same way Microsoft's sample queries do:

    MicrosoftGraphActivityLogs
    | where TimeGenerated > ago(7d)
    | where AppId == "506e08f9-6e9a-4da4-942b-a099a03644ae"
    | extend Path = replace_string(replace_string(tostring(parse_url(RequestUri).Path), "v1.0/", ""), "beta/", "")
    | summarize Requests = count() by Path, RequestMethod
    | sort by Requests desc
    

    And tie each request back to the sign-in that issued its token, using Microsoft's documented join on SignInActivityId:

    MicrosoftGraphActivityLogs
    | where TimeGenerated > ago(7d)
    | join kind=leftouter (AADServicePrincipalSignInLogs | where TimeGenerated > ago(7d))
        on $left.SignInActivityId == $right.UniqueTokenIdentifier
    
  5. Archive audit and sign-in logs too. The same diagnostic setting can add a storage account so you aren't capped at 30 days the next time an incident starts on a Friday.

Why it matters

Third-party apps with application permissions read data without a user in the loop. Sign-in logs tell you a token was issued. Only Graph activity logs tell you whether the next request was a single profile lookup or a full mailbox export, and that difference decides whether you have a notifiable breach.

The ConnectWise case also shows the timing problem. ConnectWise's advisory asks customers to save sign-in and audit history quickly. Anything that expired before the diagnostic setting existed is gone.

Try it yourself

Enable the setting in a test tenant, wait for data (Microsoft says most events arrive within 30 minutes), make a few calls with Microsoft Graph Explorer, find its app ID in your sign-in logs and run the per-app query with that ID. Check that you can see each request path you made. If you can, your incident queries will work when you need them.

Click through it at your own pace, or open it full screen.

Quick check

If you enable Graph activity logs today, can you see API calls from last week?

No. Collection starts when the category is enabled in diagnostic settings, and Microsoft doesn't retain the data unless it's sent to a storage or analytics destination.

Which column links a Graph request to the sign-in that issued its token?

SignInActivityId in the activity log, joined to UniqueTokenIdentifier in the sign-in tables.

What license do Graph activity logs require?

Microsoft Entra ID P1 or P2, plus an Azure subscription for the destination.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.