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
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.
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 $trueNote
If 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 $trueNote
If 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 Microsoft Entra admin center (
entra.microsoft.com) as a Global Admin for this tenant - Browse to Entra ID > App registrations and select New registration
- 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)
- Name: the name Hal gives you in chat, of the form
- 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 (Microsoft 365 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: 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
| 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.
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.
- 01You ask us your questions. No deck, no demo dataset.
- 02You sign a short evaluation agreement, and we stand up your own instance.
- 03You connect one tenant from your own admin console. Hal watches it for 14 days.
- 04We walk through what he found. Keep going month to month, or revoke the scopes yourself and stop.