Auto-detect RBAC roles for JSON-driven policy assignments via azapi¶
Context and Problem Statement¶
JSON-driven policy assignments in the postdeployment layer support customer-supplied policies via JSON files. When such an assignment uses a SystemAssigned managed identity (required for DeployIfNotExists and Modify effects that perform remediation), Azure only auto-creates the managed identity's role assignments when an operator saves the assignment through the Azure portal. In a Terraform-only deployment, that portal save never happens — the managed identity is created but has no role assignments and therefore cannot perform remediation.
For policies we deploy from code (our own definitions), a workable pattern was to hardcode the required role assignments in main_role_assignements.tf. But this pattern cannot be applied to user-supplied policies, where the caller provides the policy definition ID in a JSON file and we have no prior knowledge of which roles it needs.
How should we create RBAC role assignments for user-supplied JSON policy assignments without requiring a manual portal save or hardcoded Terraform blocks?
Decision Drivers¶
- Eliminate the manual portal save step: role assignments for new JSON policy assignments should be created automatically during
terraform apply roleDefinitionIdsis a required field in the Azure Policy schema for DeployIfNotExists and Modify effects — the authoritative source of required roles already lives in the policy definition itself- The codebase already uses
azapito introspect policy resources (policy_definition_references,policy_reference_idsmodules), so a consistent pattern exists - The existing hardcoded assignments in
main_role_assignements.tf(for 3 known policies) must continue to work during and after the change (no breaking changes)
Considered Options¶
- Auto-detect from policy definitions via azapi — read
roleDefinitionIdsfrom the policy rule at plan time and create role assignments dynamically - JSON extension field — add a
required_rbac_role_definition_idsfield to each assignment JSON file that operators populate manually
Decision Outcome¶
Chosen option: "Auto-detect from policy definitions via azapi", because the required roles are already encoded in the policy definition and are a schema requirement — having operators duplicate this information in the JSON files would be both redundant and error-prone. The auto-detection approach requires zero manual maintenance for new assignments.
Consequences¶
- Good, because new JSON policy assignments with SystemAssigned identity get role assignments automatically, with no code changes required
- Good, because the source of truth for required roles remains the policy definition itself (Azure's own schema)
- Good, because it follows existing
azapipatterns already present in the codebase - Good, because opting out is controlled by the
auto_rbac_rolesmodule-level variable (defaulttrue), making the opt-out explicit at the call site in Terraform code rather than scattered across JSON data files - Good, because the opt-in default means new JSON policy assignments automatically get role assignments with no extra configuration
- Bad, because adding role assignments from azapi data sources introduces a dependency on the Azure API at plan time (policy definitions must already exist or be read from Azure)
- Bad, because call sites that do not use JSON-driven assignments (definition-only calls, exemption-only calls) must explicitly pass
auto_rbac_roles = falseto avoid unnecessary azapi reads
Validation¶
terraform validatemust pass on the postdeployment layer after implementation- Call sites that are not JSON-driven assignments (e.g.
templates/policy_definitions, exemption-onlymodules/policycalls) must passauto_rbac_roles = false - The new
policy_rbac_rolesmodule must handle both single policy definitions and policy set definitions (aggregating roles across all member definitions)
Pros and Cons of the Options¶
Auto-detect from policy definitions via azapi¶
- Good, because no manual role list maintenance when adding new policy assignments
- Good, because
roleDefinitionIdsis authoritative — it is required by Azure's own policy schema for DINE/Modify effects - Good, because the
azapidata source pattern is already established in the codebase - Good, because works for both single definitions and policy sets (aggregates member definitions)
- Good, because the opt-out is a module-level
auto_rbac_rolesvariable (defaulttrue) — explicit at the Terraform call site and easy to audit - Neutral, because non-assignment call sites (definition-only, exemption-only) must pass
auto_rbac_roles = falseto suppress unnecessary azapi reads - Bad, because plan-time Azure API reads for policy definitions add latency and require network access
JSON extension field¶
- Good, because it is explicit and visible in version control exactly which roles each assignment needs
- Good, because it works entirely offline without Azure API reads at plan time
- Bad, because operators must manually look up and maintain
roleDefinitionIds— duplicating information already in the policy definition - Bad, because it is easy to get wrong or become stale if policy definitions are updated upstream
More Information¶
See the brainstorm document for full design details, including module structure and the two-level lookup for policy sets:
docs/brainstorms/2026-03-20-policy-rbac-auto-detection-brainstorm.md