Skip to content

Microsoft 365

Walks you through creating the Azure AD app registration and API credentials Hal needs to monitor Microsoft 365 audit logs and Entra ID sign-in events — by clicking through the customer’s Azure portal yourself.

This is the manual path. Other methods produce the same Azure app registration without clicking the portal: Hal-Assisted drives the API through chat; Script is a PowerShell script you can audit and run yourself. Each method produces identical end-state credentials; you only need one.

Repeat this process for every Microsoft 365 tenant. A client may have one or more M365 tenants — each one needs its own app registration.

You will need Global Administrator access to each Microsoft 365 tenant.

When you’re done, you’ll enter these credentials directly on your MSP portal — one set per tenant.


Hal needs a single Azure AD app registration per tenant with two permission sets: one for M365 audit logs (Office 365 Management APIs) and one for Entra ID sign-in logs and security alerts (Microsoft Graph).

But the app registration alone isn’t enough — Microsoft also has to be emitting the events for Hal to ingest, and whether it is depends on the tenant’s current state.

Why different tenants need different commands

Hal’s monitoring depends on rich logs from Microsoft: M365 audit events, Entra ID sign-ins, risk detections, mailbox activity. Microsoft 365 tenants don’t ship in a uniform state, and which commands the tenant needs to reach a “logs flowing to Hal” state depends on where it is now. Two specific Microsoft-side variables to know about before running anything:

Dehydration. Microsoft optimizes tenant provisioning by starting each new Exchange Online configuration in a dehydrated state — most settings are read-only references to a shared template rather than mutable per-tenant rows. While dehydrated, you can’t toggle audit-logging configuration (or most other Exchange settings); the PowerShell command will error with something like “the operation can’t be performed because the organization is dehydrated.” The fix is Enable-OrganizationCustomization, which forks the template into mutable per-tenant state — informally called “hydrating” the tenant. On a tenant that was customized years ago, this command is a no-op; the tenant is already hydrated.

Unified Audit Logging (UAL) default state varies. Newer M365 tenants typically ship with UAL enabled by default; older tenants and tenants with custom configurations may not. Running Set-AdminAuditLogConfig -UnifiedAuditLogIngestionEnabled $true reaches the same end state in either case — it enables when off, and no-ops when already on.

So Step 1 below issues both commands defensively: check hydration, hydrate if needed, enable UAL, verify. The same sequence runs cleanly on every tenant and produces the same end state, regardless of where that tenant started.

A second wrinkle to note before you start: hydration takes time to propagate on Microsoft’s backend. Once you run Enable-OrganizationCustomization, the very next Set-AdminAuditLogConfig call may fail with the same dehydration error for anywhere from a few minutes to several hours while the tenant’s customization replicates internally. There’s no signal beyond “the command eventually succeeds.” Re-running periodically until it returns without error is the only path forward — and you’ll see the warning about this inline at the right step below.

Step 1: Enable Unified Audit Logging

You can do this from either Azure Cloud Shell (browser-based) or local Windows PowerShell. Pick whichever applies.

Option A: Azure Cloud Shell

Cloud Shell requires an Azure subscription with billing. If you don’t have one, skip to Option B.

  1. Launch Cloud Shell:

    • Windows 11 (Windows Terminal) — open Windows Terminal, click the new-tab dropdown, and select Azure Cloud Shell. On first launch you’ll get a device-code prompt — open https://login.microsoft.com/device in any browser and enter the code shown in the terminal. PowerShell is the default shell.
    • Any OS (browser) — go to https://shell.azure.com . When prompted to choose a shell, select PowerShell (not Bash — the cmdlets below are PowerShell-only).
  2. Run: Connect-ExchangeOnline -Device

    • The cmdlet prints a separate device code and a URL (https://login.microsoft.com/device ). Open the URL in any browser, enter the code, and authenticate as a tenant admin. The bare form (without -Device) silently tries to use Cloud Shell’s managed-identity token, which has the wrong audience for Exchange Online.
  3. Run: Get-OrganizationConfig | Select-Object IsDehydrated

    • If the result is True, run: Enable-OrganizationCustomization
    • This may take several minutes. Ignore errors if already enabled.
  4. Run: Set-AdminAuditLogConfig -UnifiedAuditLogIngestionEnabled $true

    If you just ran Enable-OrganizationCustomization in the previous step and this command errors (typically about dehydration or the organization not yet being customized), Microsoft’s backend hasn’t finished propagating the customization. Propagation can take anywhere from a few minutes to several hours — there’s no signal beyond “the command eventually succeeds.” Re-run the Set-AdminAuditLogConfig command periodically until it returns without error, then continue to step 5.
  5. Verify: Get-AdminAuditLogConfig | Select-Object UnifiedAuditLogIngestionEnabled

    • Should return True

Option B: Local Windows PowerShell (no Azure subscription needed)

Run on any Windows machine with PowerShell:

  1. Install-Module ExchangeOnlineManagement -Scope CurrentUser (the -Scope CurrentUser flag avoids needing to launch PowerShell as Administrator)

  2. Run: Connect-ExchangeOnline -UserPrincipalName admin@msp.com (substitute your admin UPN)

  3. Run: Get-OrganizationConfig | Select-Object IsDehydrated

    • If the result is True, run: Enable-OrganizationCustomization
    • This may take several minutes. Ignore errors if already enabled.
  4. Run: Set-AdminAuditLogConfig -UnifiedAuditLogIngestionEnabled $true

    If you just ran Enable-OrganizationCustomization in the previous step and this command errors (typically about dehydration or the organization not yet being customized), Microsoft’s backend hasn’t finished propagating the customization. Propagation can take anywhere from a few minutes to several hours — there’s no signal beyond “the command eventually succeeds.” Re-run the Set-AdminAuditLogConfig command periodically until it returns without error, then continue to step 5.
  5. Verify: Get-AdminAuditLogConfig | Select-Object UnifiedAuditLogIngestionEnabled

    • Should return True

Note: Events may take up to 24 hours to appear in the API after enabling audit logging for the first time.

Step 2: Create the App Registration

  1. Sign in to the Azure Portal (portal.azure.com ) as a Global Admin for this tenant
  2. Navigate to Azure Active Directory > App registrations > New registration
  3. Configure:
    • Name: hal-siem-log-collector
    • Supported account types: “Accounts in this organizational directory only”
  4. Click Register
  5. On the overview page, copy and save:
    • Application (client) ID
    • Directory (tenant) ID

Step 3: Create a Client Secret

  1. In the app registration, go to Certificates & secrets
  2. Click New client secret
    • Description: hal
    • Expires: 24 months (the maximum allowed in the portal)
  3. Click Add
  4. Immediately copy the secret Value — it will not be shown again
  5. Note the expiration date — Hal will alert you before it expires

Step 4: Grant API Permissions

Office 365 Management APIs (M365 audit logs)

  1. In the app registration, go to API permissions > Add a permission
  2. Select APIs my organization uses
  3. Search for and select Office 365 Management APIs
  4. Select Application permissions
  5. Check these permissions:
    • ActivityFeed.Read
    • ActivityFeed.ReadDlp
    • ServiceHealth.Read
  6. Click Add permissions

Microsoft Graph (sign-in logs, security alerts, directory audits)

  1. Click Add a permission again
  2. Select Microsoft Graph > Application permissions
  3. Add all of the following permissions:
    • Application.Read.All
    • AuditLog.Read.All
    • Directory.Read.All
    • IdentityRiskEvent.Read.All
    • IdentityRiskyUser.Read.All
    • MailboxSettings.Read
    • Policy.Read.All
    • Reports.Read.All
    • RoleManagement.Read.Directory
    • SecurityAlert.Read.All
    • SecurityIncident.Read.All
    • User.Read.All
  4. Click Grant admin consent for [this tenant’s name]
  5. Verify all permissions show a green checkmark under “Status”

Licensing note: Entra ID P1 is required for sign-in logs and directory audits. P2 is required for risk detections. If a tenant has P1 only, Hal will still monitor everything else — sign-in risk scores will show as “hidden” rather than rated.

Record these credentials for this tenant

FieldValue
Directory (tenant) ID(from Step 2.5)
Application (client) ID(from Step 2.5)
Client Secret Value(from Step 3.4)
Secret Expiration Date(from Step 3.5)

Then move to the next tenant and repeat from Step 1.


Activating monitoring

Once each tenant is set up, follow the tenant onboarding flow in your MSP portal to add the credentials. The portal validates them, starts log ingestion, and runs the first triage cycle within five minutes of activation. Historical data is backfilled up to 7 days.

Questions? Contact us.