Surfaced during the adversarial review of LeanerCloud/cloud-commitments-cli#1817 (which closes LeanerCloud/cloud-commitments-cli#1621). Pre-existing; not introduced by that PR. Filed separately rather than widening LeanerCloud/cloud-commitments-cli#1817's scope.
What
terraform/modules/compute/azure/aks/ creates azurerm_user_assigned_identity.workload and, after LeanerCloud/cloud-commitments-cli#1817, grants it Key Vault Secrets User on the vault. But nothing in the module enables AAD Workload Identity, so no pod can obtain a token for that identity. Four pieces are required and three are missing:
| piece |
location |
state |
oidc_issuer_enabled = true |
azurerm_kubernetes_cluster.main (aks/main.tf:36-90) |
missing (defaults false) |
workload_identity_enabled = true |
same resource |
missing (defaults false) |
azurerm_federated_identity_credential binding the UAI to system:serviceaccount:<ns>:<project>-api |
anywhere |
missing (zero hits for azurerm_federated_identity_credential under terraform/) |
azure.workload.identity/use = "true" label on the pod template |
aks/main.tf:244-249 |
missing |
service-account annotation azure.workload.identity/client-id |
aks/main.tf:211 |
present |
Without the OIDC issuer, the add-on, and a federated credential, the mutating webhook never injects AZURE_CLIENT_ID / AZURE_FEDERATED_TOKEN_FILE, and the service-account annotation alone does nothing.
Verified by enumeration on the LeanerCloud/cloud-commitments-cli#1817 branch: rg 'oidc_issuer_enabled|workload_identity_enabled|federated_identity|azure.workload.identity' terraform/modules/compute/azure/aks/ returns only the line-211 annotation, and rg -c 'azurerm_federated_identity_credential' terraform/ returns zero.
Why it matters
After LeanerCloud/cloud-commitments-cli#1817 the AKS workload identity has a correct RBAC grant on an identity it cannot assume. The grant model is right; the reachable failure is untouched. Nothing catches this: every individual resource is valid, terraform validate passes, and the LeanerCloud/cloud-commitments-cli#1817 guard only bans the wrong grant shape.
Not currently urgent. The AKS module is gated count = var.compute_platform == "aks" ? 1 : 0 and all three Azure environments select container-apps (github-{dev,staging,prod}.tfvars:18), so the module is not deployed anywhere today. This is a latent defect that would bite the first time someone selects compute_platform = "aks", which is exactly when they would least expect a secrets 403.
Fix direction
Add to azurerm_kubernetes_cluster.main:
oidc_issuer_enabled = true
workload_identity_enabled = true
plus an azurerm_federated_identity_credential with subject = "system:serviceaccount:${namespace}:${project_name}-api" and audience = ["api://AzureADTokenExchange"], and the azure.workload.identity/use = "true" label on the pod template.
Verification bar
terraform validate will pass whether or not this works, so it proves nothing here. Either exercise a real cluster, or add a static assertion that the four pieces are present together, so that the presence of the UAI and its role assignment cannot again imply a working token path. Both directions: the assertion must fail when any one piece is removed, and pass on the complete wiring.
Surfaced during the adversarial review of LeanerCloud/cloud-commitments-cli#1817 (which closes LeanerCloud/cloud-commitments-cli#1621). Pre-existing; not introduced by that PR. Filed separately rather than widening LeanerCloud/cloud-commitments-cli#1817's scope.
What
terraform/modules/compute/azure/aks/createsazurerm_user_assigned_identity.workloadand, after LeanerCloud/cloud-commitments-cli#1817, grants itKey Vault Secrets Useron the vault. But nothing in the module enables AAD Workload Identity, so no pod can obtain a token for that identity. Four pieces are required and three are missing:oidc_issuer_enabled = trueazurerm_kubernetes_cluster.main(aks/main.tf:36-90)false)workload_identity_enabled = truefalse)azurerm_federated_identity_credentialbinding the UAI tosystem:serviceaccount:<ns>:<project>-apiazurerm_federated_identity_credentialunderterraform/)azure.workload.identity/use = "true"label on the pod templateaks/main.tf:244-249azure.workload.identity/client-idaks/main.tf:211Without the OIDC issuer, the add-on, and a federated credential, the mutating webhook never injects
AZURE_CLIENT_ID/AZURE_FEDERATED_TOKEN_FILE, and the service-account annotation alone does nothing.Verified by enumeration on the LeanerCloud/cloud-commitments-cli#1817 branch:
rg 'oidc_issuer_enabled|workload_identity_enabled|federated_identity|azure.workload.identity' terraform/modules/compute/azure/aks/returns only the line-211 annotation, andrg -c 'azurerm_federated_identity_credential' terraform/returns zero.Why it matters
After LeanerCloud/cloud-commitments-cli#1817 the AKS workload identity has a correct RBAC grant on an identity it cannot assume. The grant model is right; the reachable failure is untouched. Nothing catches this: every individual resource is valid,
terraform validatepasses, and the LeanerCloud/cloud-commitments-cli#1817 guard only bans the wrong grant shape.Not currently urgent. The AKS module is gated
count = var.compute_platform == "aks" ? 1 : 0and all three Azure environments selectcontainer-apps(github-{dev,staging,prod}.tfvars:18), so the module is not deployed anywhere today. This is a latent defect that would bite the first time someone selectscompute_platform = "aks", which is exactly when they would least expect a secrets 403.Fix direction
Add to
azurerm_kubernetes_cluster.main:plus an
azurerm_federated_identity_credentialwithsubject = "system:serviceaccount:${namespace}:${project_name}-api"andaudience = ["api://AzureADTokenExchange"], and theazure.workload.identity/use = "true"label on the pod template.Verification bar
terraform validatewill pass whether or not this works, so it proves nothing here. Either exercise a real cluster, or add a static assertion that the four pieces are present together, so that the presence of the UAI and its role assignment cannot again imply a working token path. Both directions: the assertion must fail when any one piece is removed, and pass on the complete wiring.