AI security & operations for MSPs

Microsoft 365

Walks you through creating the Microsoft Entra 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.

Note

Build this registration to connect a tenant yourself. Tell Hal in chat that you are building it for the client: he gives it its name and opens a paste box on Settings, and once you have pasted its credential he checks it with Microsoft and stores it only after you confirm (Connecting the tenant to Hal, below). It is also the reference for what Hal-assisted onboarding creates: the same 15 application permissions and the same 24-month secret, and like his it carries the delegated User.Read scope, which Entra adds to every registration made in the portal. Script builds the registration from PowerShell with the 15 application permissions alone.

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

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

Record the values as you go: three of them are what you paste for Hal, and the table at the end lists them.


Hal needs a single Microsoft Entra app registration per tenant with two permission sets: one for Microsoft 365 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: Microsoft 365 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 Microsoft 365 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

    Note

    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

    Note

    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 Microsoft Entra admin center (entra.microsoft.com ) as a Global Admin for this tenant
  2. Browse to Entra ID > App registrations and select New registration
  3. Configure:
    • Name: the name Hal gives you in chat, of the form hal-siem-log-collector-<deployment code>-<4 hex>. Hal connects a registration only under a name of that form.
    • Supported account types: “Single tenant only” (your tenant)
  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 (Microsoft 365 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: sign-in logs need Entra ID P1, and directory audits need no premium license. Risk detections need P1 for the basic kinds and P2 for the premium kinds and for sign-in risk scores. If a tenant has P1 only, Hal still monitors everything else, and sign-in risk scores 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.


Connecting the tenant to Hal

In the portal chat, tell Hal you built the registration for the client. He opens a paste box for that client on Settings → Microsoft 365, open for 24 hours: paste the Directory (tenant) ID, the Application (client) ID and the secret’s Value (not its Secret ID), and Save. They go in that box, never in chat, and Hal holds them for an hour, so tell him done within it; after that, paste them again first.

Before anything is stored, Hal checks the registration with Microsoft. He refuses one that holds more than the permissions above, naming each extra and how to remove it; one under a name not of the form he gave; and a tenant already connected to another client. ActivityFeed.Read is the one permission he cannot connect without: a registration missing any other connects with a partial read, and he names what is missing. He reads the secret’s expiry from the registration, shows you the tenant, and stores it only after you confirm. Then ask him to start ingesting: the first cycle backfills six days of Microsoft 365 audit and 29 days of Entra sign-in and directory audit.

On this path Hal creates nothing in the tenant: the registration is yours. The one change he makes there is turning on the tenant’s audit-log subscription, which Microsoft needs on before it serves the log. When the secret nears expiry, add a new one to the registration and ask Hal to rotate the Microsoft 365 registration secret: he opens the box again, and you paste it the same way.

Or connect the tenant with Hal-assisted onboarding instead: Hal creates his own registration with the same permissions, and you may delete the one you built.

Questions? Contact us.

See what Hal surfaces on your own clients.

No deck. Ask us anything first. When you want to see Hal on your own tenants, we sign a short evaluation agreement and stand up your instance; you connect one tenant, Hal watches it for 14 days, and we walk you through what he found.

  1. 01You ask us your questions. No deck, no demo dataset.
  2. 02You sign a short evaluation agreement, and we stand up your own instance.
  3. 03You connect one tenant from your own admin console. Hal watches it for 14 days.
  4. 04We walk through what he found. Keep going month to month, or revoke the scopes yourself and stop.