How to Collect Azure AD Sign-In Logs | Microsoft Graph API Tutorial

Step-by-step guide to ingesting Azure AD sign-in logs using Microsoft Graph API. Monitor user authentication events, detect failed logins, and track risky sign-ins with OAuth2 and OData filtering.

Overview

Azure AD sign-in logs capture every user authentication event in your Microsoft 365 and Azure environment. Use this guide to collect sign-in data for security monitoring, compliance reporting, and detecting authentication anomalies like failed logins or risky sign-ins.

Common Use Cases:

  • Detect failed authentication attempts and brute force attacks
  • Monitor risky sign-ins flagged by Azure AD Identity Protection
  • Track user login patterns and geographic anomalies
  • Generate compliance reports for authentication activity

Prerequisites

Before configuring Edge Delta, complete the Azure AD application setup:

  1. Register an Azure AD application with Application permissions (not Delegated)
  2. Grant the AuditLog.Read.All permission and obtain admin consent
  3. Create a client secret and note your Tenant ID, Client ID, and Client Secret

For detailed setup instructions, see Microsoft Graph API Integration.

Configuration

Basic Sign-In Logs Collection

nodes:
- name: azure_ad_signin_logs
  type: http_pull_input
  endpoint: https://graph.microsoft.com/v1.0/auditLogs/signIns
  method: GET
  pull_interval: 5m

  authorization:
    strategy: oauth_client_credentials
    client_credentials:
      token_url: https://login.microsoftonline.com/YOUR_TENANT_ID/oauth2/v2.0/token
      client_id: YOUR_CLIENT_ID
      client_secret: YOUR_CLIENT_SECRET
      scopes:
        - https://graph.microsoft.com/.default
      header_templates:
        - header: Authorization
          value: Bearer $ACCESS_TOKEN

  parameters:
    - name: "$orderby"
      value: "createdDateTime asc"
    - name: "$top"
      value: "50"

  parameter_expressions:
    - name: "$filter"
      value_expression: Concat(["createdDateTime ge ", FormatTime(Now() - Duration("5m10s"), "%Y-%m-%dT%H:%M:%SZ")], "")

Monitor Failed Sign-Ins Only

Filter for failed authentication attempts to detect potential attacks:

nodes:
- name: failed_signin_monitor
  type: http_pull_input
  endpoint: https://graph.microsoft.com/v1.0/auditLogs/signIns
  method: GET
  pull_interval: 5m

  authorization:
    strategy: oauth_client_credentials
    client_credentials:
      token_url: https://login.microsoftonline.com/YOUR_TENANT_ID/oauth2/v2.0/token
      client_id: YOUR_CLIENT_ID
      client_secret: YOUR_CLIENT_SECRET
      scopes:
        - https://graph.microsoft.com/.default
      header_templates:
        - header: Authorization
          value: Bearer $ACCESS_TOKEN

  parameters:
    - name: "$orderby"
      value: "createdDateTime asc"
    - name: "$top"
      value: "100"
    - name: "$filter"
      value: "status/errorCode ne 0"

Monitor High-Risk Sign-Ins

Capture sign-ins flagged by Azure AD Identity Protection:

nodes:
- name: risky_signin_monitor
  type: http_pull_input
  endpoint: https://graph.microsoft.com/v1.0/auditLogs/signIns
  method: GET
  pull_interval: 5m

  authorization:
    strategy: oauth_client_credentials
    client_credentials:
      token_url: https://login.microsoftonline.com/YOUR_TENANT_ID/oauth2/v2.0/token
      client_id: YOUR_CLIENT_ID
      client_secret: YOUR_CLIENT_SECRET
      scopes:
        - https://graph.microsoft.com/.default
      header_templates:
        - header: Authorization
          value: Bearer $ACCESS_TOKEN

  parameters:
    - name: "$orderby"
      value: "createdDateTime asc"
    - name: "$top"
      value: "50"
    - name: "$filter"
      value: "riskLevelAggregated eq 'high' or riskLevelAggregated eq 'medium'"

OData Filter Examples

Use these filters in the $filter parameter to narrow results:

FilterDescription
status/errorCode eq 0Successful sign-ins only
status/errorCode ne 0Failed sign-ins only
riskLevelAggregated eq 'high'High-risk sign-ins
appDisplayName eq 'Microsoft 365'Specific application
startswith(userPrincipalName, 'admin')Admin user sign-ins
location/countryOrRegion eq 'US'Sign-ins from specific country

Combine multiple conditions:

$filter: "status/errorCode ne 0 and createdDateTime ge 2024-01-01T00:00:00Z"

Sample Sign-In Log

{
  "id": "SignIn_EFGH5678_20240101_001",
  "createdDateTime": "2024-01-01T10:05:00Z",
  "userPrincipalName": "john.doe@company.com",
  "userDisplayName": "John Doe",
  "appDisplayName": "Microsoft 365",
  "status": {
    "errorCode": 0,
    "failureReason": null,
    "additionalDetails": "MFA completed successfully"
  },
  "location": {
    "city": "Seattle",
    "state": "Washington",
    "countryOrRegion": "US"
  },
  "deviceDetail": {
    "browser": "Chrome 120.0.0.0",
    "operatingSystem": "Windows 11"
  },
  "riskLevelAggregated": "low",
  "conditionalAccessStatus": "success"
}

Key Fields for Analysis

FieldDescriptionUse Case
status.errorCode0 = success, non-zero = failureDetect failed logins
riskLevelAggregatednone, low, medium, high, hiddenIdentity Protection alerts
location.countryOrRegionGeographic locationDetect anomalous locations
conditionalAccessStatussuccess, failure, notAppliedConditional Access monitoring
deviceDetail.browserBrowser informationDevice fingerprinting

Troubleshooting

IssueSolution
401 UnauthorizedVerify tenant ID, client ID, and secret are correct
403 ForbiddenEnsure AuditLog.Read.All permission has admin consent
Empty resultsSign-in logs may have 2-5 minute processing delay
Missing fieldsSome fields require Azure AD Premium P1/P2 license