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.
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.
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/devicein 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).
- 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
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.
- The cmdlet prints a separate device code and a URL (
Run:
Get-OrganizationConfig | Select-Object IsDehydrated- If the result is
True, run:Enable-OrganizationCustomization - This may take several minutes. Ignore errors if already enabled.
- If the result is
Run:
Set-AdminAuditLogConfig -UnifiedAuditLogIngestionEnabled $trueIf you just ranEnable-OrganizationCustomizationin 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 theSet-AdminAuditLogConfigcommand periodically until it returns without error, then continue to step 5.Verify:
Get-AdminAuditLogConfig | Select-Object UnifiedAuditLogIngestionEnabled- Should return
True
- Should return
Option B: Local Windows PowerShell (no Azure subscription needed)
Run on any Windows machine with PowerShell:
Install-Module ExchangeOnlineManagement -Scope CurrentUser(the-Scope CurrentUserflag avoids needing to launch PowerShell as Administrator)Run:
Connect-ExchangeOnline -UserPrincipalName admin@msp.com(substitute your admin UPN)Run:
Get-OrganizationConfig | Select-Object IsDehydrated- If the result is
True, run:Enable-OrganizationCustomization - This may take several minutes. Ignore errors if already enabled.
- If the result is
Run:
Set-AdminAuditLogConfig -UnifiedAuditLogIngestionEnabled $trueIf you just ranEnable-OrganizationCustomizationin 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 theSet-AdminAuditLogConfigcommand periodically until it returns without error, then continue to step 5.Verify:
Get-AdminAuditLogConfig | Select-Object UnifiedAuditLogIngestionEnabled- Should return
True
- Should return
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
- Sign in to the Azure Portal (
portal.azure.com) as a Global Admin for this tenant - Navigate to Azure Active Directory > App registrations > New registration
- Configure:
- Name:
hal-siem-log-collector - Supported account types: “Accounts in this organizational directory only”
- Name:
- Click Register
- On the overview page, copy and save:
- Application (client) ID
- Directory (tenant) ID
Step 3: Create a Client Secret
- In the app registration, go to Certificates & secrets
- Click New client secret
- Description:
hal - Expires: 24 months (the maximum allowed in the portal)
- Description:
- Click Add
- Immediately copy the secret Value — it will not be shown again
- Note the expiration date — Hal will alert you before it expires
Step 4: Grant API Permissions
Office 365 Management APIs (M365 audit logs)
- In the app registration, go to API permissions > Add a permission
- Select APIs my organization uses
- Search for and select Office 365 Management APIs
- Select Application permissions
- Check these permissions:
ActivityFeed.ReadActivityFeed.ReadDlpServiceHealth.Read
- Click Add permissions
Microsoft Graph (sign-in logs, security alerts, directory audits)
- Click Add a permission again
- Select Microsoft Graph > Application permissions
- Add all of the following permissions:
Application.Read.AllAuditLog.Read.AllDirectory.Read.AllIdentityRiskEvent.Read.AllIdentityRiskyUser.Read.AllMailboxSettings.ReadPolicy.Read.AllReports.Read.AllRoleManagement.Read.DirectorySecurityAlert.Read.AllSecurityIncident.Read.AllUser.Read.All
- Click Grant admin consent for [this tenant’s name]
- 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
| Field | Value |
|---|---|
| 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.