Skip to main content

Azure Cloud Account

Connect an Azure subscription so DriftWise can scan live infrastructure and detect drift against your Terraform state. DriftWise discovers resources via Azure Resource Graph, which projects ARM data across every service the principal can see — there is no per-service enumeration. See Cloud Discovery for the full model.

Prerequisites

  • Azure CLI installed and authenticated (az login)
  • An Azure subscription with permission to create App Registrations
  • A DriftWise API key or OIDC login

1. Create a Service Principal

DriftWise authenticates as an Azure service principal with read-only access.

export AZURE_SUB_ID=$(az account show --query id -o tsv)

az ad sp create-for-rbac \
--name "driftwise-scanner" \
--role "Reader" \
--scopes "/subscriptions/$AZURE_SUB_ID" \
--output json

Save the output — you'll need these values:

SP output fieldDriftWise credential field
appIdClient ID
passwordClient Secret
tenantTenant ID
$AZURE_SUB_IDSubscription ID

The Reader role at subscription scope covers everything DriftWise needs. Resource Graph queries use Microsoft.ResourceGraph/resources/read, which Reader grants, and Resource Graph projects ARM data for every resource type the principal can see. No additional roles are required.

Scope matters

Grant Reader at the subscription (or management group) level. A resource-group-scoped Reader will only return resources inside that group — Resource Graph respects the caller's effective permissions.

2. Choose a Credential Type

Option A: Client Secret (simple, for dev/test)

Use the service principal credentials from step 1 directly. No additional setup needed.

warning

Client secrets expire after 1 year by default. Use OIDC Federation for production to avoid secret rotation.

Self-service setup — copy from the UI

Open Cloud Scan → + Add Account → Azure in DriftWise. The "Setup Instructions" panel renders the exact federated credential JSON with your org UUID already filled in (subject: org-<YOUR-ORG-UUID>) — copy from there instead of editing this template by hand. The block below is reference; the UI is canonical.

Configure the service principal to trust DriftWise's per-org federated identity instead of using a static secret:

# Replace <YOUR-ORG-UUID> with the value shown in the UI's Setup Instructions.
APP_ID=$(az ad sp list --display-name driftwise-scanner --query '[0].appId' -o tsv)

az ad app federated-credential create \
--id $APP_ID \
--parameters '{
"name": "driftwise-federation",
"issuer": "https://federation.driftwise.ai",
"subject": "org-<YOUR-ORG-UUID>",
"audiences": ["https://app.driftwise.ai/federation"]
}'
Values are not interchangeable between environments or orgs

The subject is per-DriftWise-org. If you connect a second DriftWise org, create a second federated credential with a different name and the new org's UUID. The issuer and audience are deployment-level and shared across orgs.

3. Add the account in DriftWise

Via the UI

If you didn't save the output from step 1, retrieve the values you need:

# Tenant ID
az account show --query tenantId -o tsv

# Client ID (appId of the service principal)
az ad sp list --display-name driftwise-scanner --query '[0].appId' -o tsv

# Subscription ID
az account show --query id -o tsv

Then:

  1. Open Cloud Scan in the left sidebar.
  2. Click + Add Account and select Microsoft Azure.
  3. Fill in the form:
    • Display Name — any label you'll recognize (e.g. Production Azure).
    • Subscription ID — the UUID of the subscription to scan.
    • Default Region — optional; used as default when starting scans.
    • Credential TypeOIDC Federation (recommended) or Client Secret.
    • Both credential types:
      • Tenant ID — paste the az account show --query tenantId output.
      • Client ID — paste the az ad sp list output (the SP's appId).
    • Client Secret only:
      • Client Secret — the password from az ad sp create-for-rbac output. Not recoverable after creation — regenerate with az ad sp credential reset if lost.
  4. Click Register Account. The backend validates against Azure before saving — a bad Tenant ID or Client ID fails here, not at first scan.

Via the API

Call POST /orgs/:id/accounts with provider: "azure" and one of two credential types:

  • credential_type: "azure_sp"credential_ref is a JSON-encoded object with client_id, client_secret, and tenant_id.
  • credential_type: "azure_oidc"credential_ref is a JSON-encoded object with client_id, tenant_id, and _credential_type: "azure_oidc" (see note below; no static secret).
The _credential_type field inside credential_ref

Today the server parses credential_ref independently of the top-level credential_type field, so OIDC credentials must also include _credential_type inside the inner JSON to trigger the federated code path. Without it, the request fails with client_secret is required because the parser falls through to the service-principal branch. This duplication will be removed once the backend merges the fields at request time.

The subscription ID comes from external_account_id on the enclosing account — it is not a field inside credential_ref. Credentials are validated against Azure before save, and the provider-reported subscription must match external_account_id. See the accounts tag of the API reference for the full request and response shapes.

Supported Resources

DriftWise discovers every resource Azure Resource Graph returns for the subscription — there is no per-type allowlist. Resources are normalized into eight broad categories (compute, storage, database, network, iam, serverless, messaging, other) regardless of provider. See Cloud Discovery for the full list with examples.

The underlying ARM resource type (for example Microsoft.Compute/virtualMachines) is preserved alongside the category for exact-match drift detection.

Because Resource Graph returns full ARM properties in one call, Azure scans have no separate enrichment phase — every row is stored with enrichment_status = n/a.

Required Permissions

PermissionPurpose
Microsoft.ResourceGraph/resources/readExecute Resource Graph queries
Microsoft.Resources/subscriptions/readCredential validation

Both are included in the built-in Reader role. Resource Graph queries reflect whatever ARM data the principal is authorized to see, so granting Reader at subscription scope transparently enables discovery of every service type in the subscription.

Reading Terraform state from Azure Blob

When you link an Azure cloud account to an Azure Blob state source, DriftWise uses the linked account's service principal (azure_sp) or OIDC-federated identity (azure_oidc) to read the blob. The Reader role above does NOT include blob data reads — ARM Reader and blob data-plane reads are different RBAC scopes.

Required role:

  • Storage Blob Data Reader on the storage account (or scoped to the specific container)

Grant at storage-account scope — DriftWise reads one blob per refresh and does not enumerate containers. The grant is read-only; DriftWise never writes, deletes, or acquires a lease on the blob.

DriftWise authenticates via Entra ID (AAD) tokens only. Storage account keys, shared-access signatures, and connection strings are not supported — use the RBAC model above instead.

If you prefer strict separation, register a second cloud account whose service principal has only the blob-read role, then link that account to the state source instead of the scanner account.

Sovereign Azure clouds (.blob.core.chinacloudapi.cn, .blob.core.usgovcloudapi.net, etc.) are not supported by the current driver — it constructs service URLs against blob.core.windows.net only. Reach out if you need sovereign cloud support.

Troubleshooting

Scan completes with 0 resources and 0 errors

Azure Resource Graph returns an empty result set — not an error — when the subscription has no resources the principal can see. The scan completes successfully and looks identical to a credentials-working-but-misconfigured state.

Check if the subscription actually has resources:

az resource list --subscription $AZURE_SUB_ID --output table

If this returns [], the subscription is empty — the scan is working correctly.

Other causes:

  • Wrong subscription — the account's external_account_id is the subscription the scan targets. If you set this to the wrong subscription ID, the scan queries an empty or different scope.
  • Multiple subscriptions — verify you're pointing at the right one: az account list --output table
  • Reader scoped too narrowly — if the SP's Reader role is on a resource group rather than the subscription, Resource Graph will only return resources inside that RG.

Scan completes with errors

Check the scan errors:

Diagnostic query — internal schema, column names may change
SELECT jsonb_pretty(scan_errors) FROM scan_runs WHERE id = '<scan_id>';

Common errors:

ErrorCauseFix
AuthenticationFailedClient secret is wrong or expiredRegenerate: az ad sp credential reset --name driftwise-scanner
AuthorizationFailedSP exists but has no role on the subscriptionGrant Reader: az role assignment create --assignee <appId> --role Reader --scope /subscriptions/$AZURE_SUB_ID
SubscriptionNotFoundSubscription ID is wrong or SP doesn't have accessVerify the subscription ID and role assignment
InvalidAuthenticationTokenTenant ID is wrongCheck az account show --query tenantId

Client secret expired

Azure SP secrets expire (default: 1 year). Regenerate:

az ad sp credential reset --name driftwise-scanner --output json

Update the client_secret in DriftWise with the new password value. Consider switching to OIDC Federation to avoid this entirely.

Scan stuck in "pending"

The scan worker hasn't claimed it. Check the worker logs:

kubectl logs -f deploy/scan-worker

If the worker is running but scans stay pending, check for stuck scans:

Diagnostic query — internal schema, column names may change
SELECT id, status, retry_count, started_at FROM scan_runs
WHERE status = 'running' AND started_at < NOW() - INTERVAL '10 minutes';

Some resources missing after creation

Azure Resource Graph has a propagation delay of up to several minutes after a resource is created, deleted, or modified. Re-run the scan, or let the next scheduled scan pick up the changes.