azure managed identity review - tfsugjp/skills GitHub Wiki

Azure Managed Identity Review Plugin

日本語

Summary

Add an azure-managed-identity-review plugin whose skill reviews an Azure managed identity across every resource, app setting, code path, and federated identity credential (FIC) that shares it. It reports grants missing for each consumer's audience and grants leaked to consumers that should not hold them, from Bicep, Terraform, and the live subscription. The review is read-only; fixes are proposed as IaC changes.

Tracking

Type Issue
Parent Issue #91
Sub-issue Issue #92
Sub-issue Issue #93
Sub-issue Issue #94
Sub-issue Issue #95

Background

  • A user-assigned identity is often created for one resource (for example a storage account's customer-managed key in Key Vault) and reused by others (a Function App, a GitHub Actions workflow through an FIC).
  • Grants made for the first purpose do not cover the other consumers' audiences (Service Bus, SQL, Cosmos DB data plane, custom api:// apps), and every grant on the identity is usable by every consumer and every trusted external workload.
  • Local E2E runs under the developer's own credentials (DefaultAzureCredential, local.settings.json without __credential), so missing grants show up only after deployment.

Design

  • Placement: new plugin plugins/azure-managed-identity-review/ with one skill and one script, scripts/msi_review.py (Python 3.9+, standard library only).
  • static <path>: compiles Bicep entry files with the Bicep CLI, reads ARM JSON (nested deployments, ARM expressions, --parameters), parses Terraform (*.tf, or terraform show -json with --tf-plan-json), and scans source code and local.settings.json for credentials, token scopes, and service hosts (--map <dir>=<resource>).
  • live --subscription <id>: read-only az calls only (Resource Graph through az rest with a UTF-8 body file, role assignments without Microsoft Graph lookups, FICs, masked app settings, Cosmos DB data-plane assignments). --static <path> adds drift findings; --record/--replay store masked output.
  • Model: identities, consumers (including CMK, Key Vault reference, image pull, and kubelet slots), targets with the identity actually used (connection __clientId, AZURE_CLIENT_ID, a custom client ID setting, or the system-assigned default), grants, and FICs. Role IDs and names are verified with az role definition list.
  • Coverage: a grant covers a target at the target resource or a broader scope that contains it (resource groups compared when known, unresolved scopes never count; Key Vault secret and key operations need different roles); a grant on a child (container, queue, secret, Foundry project) counts only when the consumer's endpoint points into it. Target names come from endpoints, Key Vault references, registry login servers, and CMK vault URIs; Event Hubs and Service Bus are told apart by resource type.
  • Rules: MIR000 unresolved reference, MIR001 shared identity, MIR002 target without a covering grant, MIR003 broad, privileged, or single-consumer grant on a shared identity, MIR004 FIC on a shared identity or non-standard FIC, MIR005 missing, unintended, or unmatched client ID, MIR006 non-RBAC grant to verify, MIR007 local development masks gaps, MIR008 drift between Azure and IaC.
  • Output: Markdown report per identity (consumers and their uses, grants and who needs them, FICs, findings with fixes) or --json; --resource reviews a resource's identities and all their other consumers. Exit code 0, 1 (warnings or errors), or 2 (input error).

Implementation phases

  1. Static inventory for Bicep/ARM and Terraform (#92).
  2. Read-only live collector with record and replay (#93).
  3. Rules MIR000-MIR008 with fixtures and unit tests (#94).
  4. SKILL.md, references, plugin manifests, marketplaces, and repository docs (#95).

Verification

  • python3 scripts/validate_marketplaces.py succeeds.
  • The unit tests pass locally and in the existing Ubuntu CI job: the canonical scenario in compiled ARM, Terraform, and Bicep with code; identity selection; non-RBAC targets; a clean configuration; unresolved references; Terraform plan JSON; recorded live output with drift; and a positive fixture for every rule.
  • A read-only live run against a real subscription confirms the az arguments, masking, and identity inference.
⚠️ **GitHub.com Fallback** ⚠️