Skip to content

Microsoft Azure

Private Beta

Cloud Security is currently in Private Beta. Features, APIs, and configuration formats described here may change before general availability. Contact us if you would like access.

Collects the Azure estate — VMs and scale sets, storage, Key Vault, SQL/Cosmos, AKS, networking and NSGs, Azure OpenAI — plus the tenant's Entra ID directory (users, groups, service principals, app registrations, roles) and, where licensed, Conditional Access and sign-in activity.

Auth model: an Entra ID app registration (service principal) with a client secret, granted the Reader RBAC role on your subscriptions and Microsoft Graph application permissions on the tenant.

Directory only, no Azure subscription?

If you have Entra ID / Microsoft 365 but no Azure infrastructure to enumerate, use the Entra ID provider instead — same app registration, none of the ARM setup.

Prerequisites

  • Permission to create an app registration in the tenant (Application Developer or higher).
  • Permission to grant tenant-wide admin consent for Graph application permissions (Privileged Role Administrator or Global Administrator).
  • Owner or User Access Administrator on each subscription you want read, to assign Reader.

Required permissions

Grant Where Why Preflight check
Reader RBAC role On each subscription (or a management group above them) Reads every resource in the subscription, and the Defender/Resource Graph security views arm_reader
Directory.Read.All (application) Microsoft Graph Users, groups, service principals, app registrations, domains, directory roles graph_directory

Optional permissions

Grant Unlocks Preflight check
Policy.Read.All (application) Conditional Access policy posture (collected during the sweep)
AuditLog.Read.All (application) Last-sign-in / dormancy enrichment on identities. Requires an Entra ID P1 or P2 licence — without the licence the report is unavailable regardless of consent signin_activity
RoleManagement.Read.Directory (application) Directory role assignments and PIM eligibility (collected during the sweep)
Application.Read.All (application) Fuller app-registration / service-principal credential detail (collected during the sweep)
AdministrativeUnit.Read.All (application) Administrative-unit scoping (collected during the sweep)
AgentIdentity.Read.All (application) Source-asserted AI-agent identities in the directory (collected during the sweep)
(covered by Reader) Defender for Cloud vulnerability assessments via Azure Resource Graph defender_vuln

A denied Graph permission 403s only its own collector — that surface goes unobserved while everything else still collects.

Create the app registration

TENANT_ID=$(az account show --query tenantId -o tsv)
SUB_ID=<your-subscription-id>

# 1. App registration + service principal
APP_ID=$(az ad app create --display-name lc-cloudsec --query appId -o tsv)
az ad sp create --id "$APP_ID"

# 2. Client secret (note the expiry you choose; --append preserves existing ones)
az ad app credential reset --id "$APP_ID" --years 2 --append \
  --display-name lc-cloudsec --query password -o tsv     # capture this once

# 3. RBAC Reader on the subscription (repeat per subscription)
az role assignment create --assignee "$APP_ID" --role Reader \
  --scope "/subscriptions/${SUB_ID}"

# 4. Microsoft Graph application permissions
GRAPH=00000003-0000-0000-c000-000000000000
az ad app permission add --id "$APP_ID" --api "$GRAPH" --api-permissions \
  7ab1d382-f21e-4acd-a863-ba3e13f7da61=Role   # Directory.Read.All
az ad app permission add --id "$APP_ID" --api "$GRAPH" --api-permissions \
  246dd0d5-5bd0-4def-940b-0421030a5b68=Role   # Policy.Read.All
az ad app permission add --id "$APP_ID" --api "$GRAPH" --api-permissions \
  b0afded3-3588-46d8-8b3d-9842eff778da=Role   # AuditLog.Read.All

# 5. Tenant-wide admin consent (needs a privileged admin)
az ad app permission admin-consent --id "$APP_ID"

In the portal

Microsoft Entra ID → App registrations → New registration → then Certificates & secrets → New client secret (copy the Value, not the ID) → API permissions → Add a permission → Microsoft Graph → Application permissions → add the permissions above → Grant admin consent for \<tenant> (the status column must read Granted) → finally Subscriptions → \<sub> → Access control (IAM) → Add role assignment → Reader → your app.

credential reset clears existing secrets

Without --append, az ad app credential reset removes every existing password and certificate on the app before adding the new one. Always pass --append when the app already carries credentials something else depends on.

Application permissions, not delegated

Graph permissions must be added under Application permissions. Delegated permissions require a signed-in user and will leave the graph_directory check failing even after consent.

Create the credentials secret

{"client_id": "<application-client-id>", "client_secret": "<the-secret-value>"}
limacharlie hive set --hive-name secret --key azure-sp \
    --input-file azure-secret.json --enabled

Create the provider record

provider.yaml:

provider_type: azure
azure_tenant_id: "<tenant-id>"
azure_client_id: "<application-client-id>"
azure_subscription_id: "<subscription-id>"
credentials: hive://secret/azure-sp
internal_domains: [example.com, example.onmicrosoft.com]
refresh: 6h

azure_subscription_id is an anchor, not a limit

The collector enumerates every subscription the service principal can see (subscriptions check). Assign Reader on each subscription you want swept; the one named here is simply the anchor used for scoping and probing. If the tenant-wide subscriptions read is denied, the sweep falls back to the single configured subscription.

In the web app: Add provider → Azure, then set Tenant ID, Client ID, Subscription ID, Credentials, and Refresh interval.

Verify

limacharlie cloudsec provider test --input-file provider.yaml
Check Required Meaning if it fails
auth The client ID/secret pair was rejected, or the secret expired.
arm_reader Reader is not assigned on the configured subscription — no resource inventory.
graph_directory Directory.Read.All not consented — no identity inventory.
subscriptions Subscription fan-out disabled; only the configured subscription is swept.
defender_vuln Workload vulnerability findings unavailable.
signin_activity Last-sign-in and dormancy enrichment unavailable (usually a missing Entra ID P1/P2 licence).

Troubleshooting

provider test result Cause Fix
auth fails with invalid_client The secret ID was stored instead of the secret Value, or the secret expired Re-mint (az ad app credential reset) and store the new value
graph_directory fails after adding permissions Admin consent not granted, or permissions added as Delegated Grant tenant-wide admin consent; confirm the permissions are under Application
arm_reader fails Reader assigned at the wrong scope, or not yet propagated Assign Reader on the subscription (or a management group above it); retry after a minute
signin_activity fails with a licence error Sign-in activity requires Entra ID P1/P2 Either accept the degrade or add the licence
Directory data appears twice An azure and an entra record both cover the tenant This is handled automatically: the Azure connection defers its tenant-global directory collectors to the standalone Entra record
A scale set / App Service is missing The resource type may need quota or a supported SKU in that subscription Confirm the resource is visible to the SP with az resource list under the same identity