fix(security/azure): switch Logic App scheduled tasks to user-assigned identities (unbreak Azure deploy)#142
Conversation
…d identities PR #74 introduced system-assigned managed identities on the three Logic App workflows (recommendations, ri_exchange, cleanup) and used `azurerm_logic_app_workflow.<name>[0].identity[0].principal_id` as the principal in the Key Vault role assignments. Every Azure deploy since then fails terraform plan with: Error: Missing required argument with module.compute_container_apps[0].azurerm_role_assignment.recommendations_kv_secrets_user[0], ... 379: principal_id = azurerm_logic_app_workflow.recommendations[0].identity[0].principal_id The argument "principal_id" is required, but no definition was found. In azurerm 3.117.1, `azurerm_logic_app_workflow.<x>.identity[0].principal_id` evaluates to null at plan time when the resource is being created from scratch, and `azurerm_role_assignment.principal_id` is a required string the validator can't accept null for. Result: the deploy is permanently broken on first apply with an SA-identity-based config. Fix: replace the system-assigned identity on each Logic App with a user-assigned managed identity. UA identities are first-class resources whose `principal_id` is populated at plan time, so the role assignment resolves cleanly. The same module already uses an UA identity for the Container App itself (azurerm_user_assigned_identity.container_app), so the pattern is consistent. Changes in scheduled-tasks.tf: - Added 3 azurerm_user_assigned_identity resources, one per workflow, gated on the same enable_scheduled_tasks / enable_ri_exchange_schedule flags as their workflows. - Each azurerm_logic_app_workflow's identity block changed from `type = "SystemAssigned"` to `type = "UserAssigned"; identity_ids = [...]` pointing at its UA identity. - Each azurerm_role_assignment's principal_id reads `azurerm_user_assigned_identity.<x>[0].principal_id` directly — no more identity[0] chain. - Each get-secret action's body JSON authentication block gains an `identity = <UA-id>` field. With UA identities the Logic Apps runtime needs to know which identity to use for the KV call (with SA there was exactly one, picked by default). Verified: `terraform validate` clean against terraform/environments/azure. The same flags + secret name plumbing in compute.tf and variables.tf are unchanged.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThe PR migrates Logic App workflows' managed identity from system-assigned to user-assigned identities, adds new Azure identity resources for each workflow, updates identity configurations and Key Vault authentication references, and adjusts role assignments accordingly. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
terraform/modules/compute/azure/container-apps/scheduled-tasks.tf (2)
4-10:⚠️ Potential issue | 🟡 MinorStale comment: still references system-assigned identity.
The header comment at line 6 states "system-assigned managed identity" but the implementation now uses user-assigned identities. Update the comment to reflect the actual architecture.
📝 Suggested fix
# SECURITY: The shared scheduled-task secret is NEVER interpolated into the # workflow definition or Terraform state. Each Logic App workflow has a -# system-assigned managed identity that holds "Key Vault Secrets User" on the +# user-assigned managed identity that holds "Key Vault Secrets User" on the # vault that stores `scheduled-task-secret`. At workflow runtime the first # action (`get-secret`) calls the Key Vault data-plane REST API authenticated # by the workflow's managed identity, and the call-endpoint action references🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@terraform/modules/compute/azure/container-apps/scheduled-tasks.tf` around lines 4 - 10, Update the header comment to replace "system-assigned managed identity" with the actual "user-assigned managed identity" architecture: state that each Logic App workflow is assigned a user-assigned managed identity which has the "Key Vault Secrets User" role on the vault storing `scheduled-task-secret`, and that at runtime the `get-secret` action calls the Key Vault data-plane REST API authenticated by that user-assigned identity and references `@body('get-secret')['value']` in the outgoing Authorization header.
140-142:⚠️ Potential issue | 🟡 MinorStale comment: references system-assigned identity.
This comment still says "system-assigned managed identity" but the implementation now uses user-assigned. Update for consistency.
📝 Suggested fix
# Step 1: Fetch the shared secret from Key Vault using the workflow's -# system-assigned managed identity. The secret value lives in the workflow +# user-assigned managed identity. The secret value lives in the workflow # run's transient state only — never in the workflow definition or TF state.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@terraform/modules/compute/azure/container-apps/scheduled-tasks.tf` around lines 140 - 142, Update the stale Step 1 comment that currently says "system-assigned managed identity" to reflect the implementation using a user-assigned managed identity: replace that phrase with "user-assigned managed identity" and adjust the surrounding wording so it clearly states the secret is fetched using the workflow's user-assigned managed identity and remains transient (not stored in workflow definition or TF state); locate and edit the comment block that begins "Step 1: Fetch the shared secret..." in the scheduled-tasks.tf file.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@terraform/modules/compute/azure/container-apps/scheduled-tasks.tf`:
- Around line 4-10: Update the header comment to replace "system-assigned
managed identity" with the actual "user-assigned managed identity" architecture:
state that each Logic App workflow is assigned a user-assigned managed identity
which has the "Key Vault Secrets User" role on the vault storing
`scheduled-task-secret`, and that at runtime the `get-secret` action calls the
Key Vault data-plane REST API authenticated by that user-assigned identity and
references `@body('get-secret')['value']` in the outgoing Authorization header.
- Around line 140-142: Update the stale Step 1 comment that currently says
"system-assigned managed identity" to reflect the implementation using a
user-assigned managed identity: replace that phrase with "user-assigned managed
identity" and adjust the surrounding wording so it clearly states the secret is
fetched using the workflow's user-assigned managed identity and remains
transient (not stored in workflow definition or TF state); locate and edit the
comment block that begins "Step 1: Fetch the shared secret..." in the
scheduled-tasks.tf file.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 70577e89-1c07-486f-88bb-220a6527c853
📒 Files selected for processing (1)
terraform/modules/compute/azure/container-apps/scheduled-tasks.tf
…ntity CodeRabbit nits: two security/architectural overview comments still described the original system-assigned identity approach from #74: - File header comment at the top of scheduled-tasks.tf - Step 1 comment above `azurerm_logic_app_action_custom.recommendations_get_secret` Both now describe the user-assigned identity wiring this PR introduces. The other remaining system-assigned references in the file are deliberate — they explain *why* the migration was needed (the SA `principal_id` plan-time-null bug from azurerm 3.117.1) and should stay. Comment-only change. No terraform plan output difference.
|
Pushed File header comment + the Step 1 comment above Comment-only change; no terraform plan diff. Ready to merge once CI catches up. |
Summary
Every Azure deploy since #74 fails terraform plan with:
…and the same for
cleanup_kv_secrets_user. (ri_exchangedoesn't fail becauseenable_ri_exchange_scheduleis currentlyfalse, so its role assignment has count=0 and isn't planned.)Root cause
In azurerm 3.117.1,
azurerm_logic_app_workflow.<x>.identity[0].principal_idevaluates to null at plan time when the resource is being created from scratch (no prior state).azurerm_role_assignment.principal_idis a required string the validator can't accept null for, so the plan fails before apply ever runs. There's no apply state for Terraform to learn the identity's principal_id from, so it never recovers.Fix
Switch the three Logic App workflows from system-assigned to user-assigned managed identities. UA identities are first-class
azurerm_user_assigned_identityresources whose.principal_idattribute is populated at plan time, so the role assignment resolves cleanly. The same module already uses this pattern for the Container App's own RBAC (azurerm_user_assigned_identity.container_appatmain.tf:242), so the codebase is now consistent.Changes in
scheduled-tasks.tf:azurerm_user_assigned_identityresources: one per workflow (recommendations,ri_exchange,cleanup), gated on the sameenable_scheduled_tasks/enable_ri_exchange_scheduleflags as their workflows.type = "SystemAssigned"→type = "UserAssigned"withidentity_ids = [azurerm_user_assigned_identity.<x>[0].id].principal_id: readsazurerm_user_assigned_identity.<x>[0].principal_iddirectly — no moreidentity[0]chain.get-secretaction body JSON: theauthenticationblock gainsidentity = <UA-resource-id>. With UA identities the Logic Apps runtime needs to know which identity to use for the KV call (with SA there was exactly one, picked by default).Variables, secret-name plumbing, and the precondition/validation block are unchanged. The change is contained to
scheduled-tasks.tf.Test plan
terraform validateclean againstterraform/environments/azure.principal_id.depends_on = [azurerm_role_assignment.<x>_kv_secrets_user]on each get-secret action is preserved.Out of scope
depends_onfrom the workflows themselves to the role assignments — already implemented at the*_get_secretlevel, which is the actual gate for runtime KV access.enable_scheduled_tasks; ri_exchange has its own flag) without complicating the role-assignment count expression.🤖 Generated with claude-flow
Summary by CodeRabbit