The symptom: a pipeline or az deployment group create stops with DeploymentFailed and "At least one resource deployment operation failed". That code tells you nothing useful. The real error sits on the individual operation that failed, and with Bicep modules it can be one or two levels down.
The commands below use rg-prod and my-deployment. Replace them with your own names.
Find the failed deployment
az deployment group list -g rg-prod \
--query "sort_by([?properties.provisioningState=='Failed'], &properties.timestamp)[-5:].{name:name, time:properties.timestamp, code:properties.error.code}" \
-o table
If the deployment isn't listed, it probably failed before it started. Validation and preflight errors don't appear in deployment history. Skip to errors that never reach history.
Get the real error
Start with the top-level error and its details:
az deployment group show -g rg-prod -n my-deployment --query properties.error -o json
Then list only the failed operations, with the resource each one was working on:
az deployment operation group list -g rg-prod -n my-deployment \
--query "[?properties.provisioningState=='Failed'].{resource:properties.targetResource.resourceName, type:properties.targetResource.resourceType, code:properties.statusMessage.error.code, message:properties.statusMessage.error.message}" \
-o json
The PowerShell equivalent:
Get-AzResourceGroupDeploymentOperation -ResourceGroupName rg-prod -DeploymentName my-deployment |
Where-Object ProvisioningState -eq 'Failed' |
Format-List
Follow nested deployments down
Each Bicep module runs as its own deployment. If the failed operation has the type Microsoft.Resources/deployments, its resource value is the name of a nested deployment. Run the same query against that name:
az deployment operation group list -g rg-prod -n <module-deployment-name> \
--query "[?properties.provisioningState=='Failed'].{resource:properties.targetResource.resourceName, type:properties.targetResource.resourceType, code:properties.statusMessage.error.code, message:properties.statusMessage.error.message}" \
-o json
Repeat until the type is a real resource. A module scoped to another resource group has its history there, so change -g to match.
Other scopes
| Scope | Show the deployment | List operations |
|---|---|---|
| Resource group | az deployment group show -g <rg> -n <name> | az deployment operation group list -g <rg> -n <name> |
| Subscription | az deployment sub show -n <name> | az deployment operation sub list -n <name> |
| Management group | az deployment mg show -m <mg> -n <name> | az deployment operation mg list -m <mg> -n <name> |
| Tenant | az deployment tenant show -n <name> | az deployment operation tenant list -n <name> |
Error code to fix
Microsoft's list of common codes is longer than this. These are the ones you'll usually hit:
| Error code | Cause | Fix |
|---|---|---|
DeploymentFailed | Generic wrapper | Read the operations, as above |
InvalidTemplate, InvalidTemplateDeployment | Syntax, parameter or template validation failure | Read the message; run validate |
AuthorizationFailed | The identity lacks the action at this scope, or a provider isn't registered | Check role and scope: RBAC cheat sheet |
LinkedAuthorizationFailed | No permission on a linked resource, such as joining a subnet, or it's in another tenant | Grant the action named in the message on the linked scope |
RequestDisallowedByPolicy | An Azure Policy denied the resource | Find the policy assignment |
MissingSubscriptionRegistration, NoRegisteredProviderFound | Resource provider isn't registered, or the type or API version isn't available there | Register the provider and check supported locations |
SkuNotAvailable | The size isn't offered in that region or zone | Pick another size: VM allocation errors |
OperationNotAllowed, ResourceQuotaExceeded | Quota exceeded, or the operation isn't allowed on that SKU | Check usage; free capacity or request more |
Conflict | The operation isn't allowed in the resource's current state | Wait, or change state first (for example, deallocate before a disk resize) |
AnotherOperationInProgress | Another operation is running on the same resource | Wait and retry; serialize the pipeline |
DeploymentActiveAndUneditable | A deployment with this name is still running | Wait, or cancel it |
DeploymentQuotaExceeded | The resource group reached 800 deployments in history | Delete old history |
DeploymentNameLengthLimitExceeded | Deployment name is over 64 characters | Shorten it, including generated module names |
ParentResourceNotFound | Child resource deployed before its parent exists | Add parent or a dependency |
ResourceNotFound, InvalidResourceReference | A reference points to something missing or not yet created | Fix the name or add a dependency |
PropertyChangeNotAllowed | A property can't be changed after creation | Keep the old value, or recreate the resource |
StorageAccountAlreadyTaken | Storage account names are globally unique | Use another name, for example with uniqueString() |
SubnetIsFull | No free addresses in the subnet | Use another subnet or expand the range |
ScopeLocked | A resource lock blocks the change | Manage locks safely |
Quick checks for common fixes
# Provider registration state, then register
az provider show -n Microsoft.ContainerService --query registrationState -o tsv
az provider register -n Microsoft.ContainerService --wait
# VM quota and size availability
az vm list-usage -l eastus -o table
az vm list-skus -l eastus --size Standard_D4s_v5 --all -o table
# What's running right now in this resource group
az deployment group list -g rg-prod \
--filter "provisioningState eq 'Running'" --query "[].name" -o tsv
Cancel a running deployment only if you're sure it's stuck or wrong:
az deployment group cancel -g rg-prod -n my-deployment
Warning
Cancelling doesn't roll back. Resources that were already created or changed stay as they are. Review the state and run
what-ifbefore you deploy again.
Errors that never reach history
Validation and preflight failures are in the Activity Log, not in deployment history. To reproduce one and see the message directly:
az deployment group validate -g rg-prod -f main.bicep -p params.json
To read it from the log:
az monitor activity-log list -g rg-prod --offset 6h --status Failed \
--query "[].{time:eventTimestamp, op:operationName.value, resource:resourceId, msg:properties.statusMessage}" \
-o json
To collect everything from a single deployment attempt, use its correlation ID:
corr=$(az deployment group show -g rg-prod -n my-deployment --query properties.correlationId -o tsv)
az monitor activity-log list --correlation-id "$corr" --offset 7d --status Failed -o json
When the message is still too vague
Debug logging records the request and response of each operation. You can only turn it on with Azure PowerShell, not the CLI:
New-AzResourceGroupDeployment -Name debug-run -ResourceGroupName rg-prod `
-TemplateFile main.bicep -DeploymentDebugLogLevel All
Then read the content with PowerShell or the CLI:
az deployment operation group list -g rg-prod -n debug-run --query "[].properties.response" -o json
az deployment operation group list -g rg-prod -n debug-run --query "[].properties.request" -o json
Caution
Debug output stays in deployment history and can include passwords and other secure values you passed in. Delete the entry when you're done. This removes the history record, not the deployed resources:
az deployment group delete -g rg-prod -n debug-run
Clear deployment history
Resource Manager normally deletes old deployments automatically as you near the 800 limit. That can still fail, for example if there's a CanNotDelete lock on the resource group, if you opted out, or if many deployments run at once.
az lock list -g rg-prod -o table
az deployment group delete -g rg-prod -n <old-deployment-name>
Prevent it next time
- Gate pipelines on validate and what-if. This catches template, policy and many permission errors before anything changes:
az bicep build --file main.bicep
az deployment group validate -g rg-prod -f main.bicep -p params.json
az deployment group what-if -g rg-prod -f main.bicep -p params.json
- Print the failed operations in the pipeline. Add the operations query as a step that runs on failure, so the real code is in the job log.
- Use unique, short deployment names, such as the build ID. This avoids
DeploymentActiveAndUneditablefrom a re-used name and keeps each run traceable. - Register providers ahead of time in your subscription setup.
- Stop drift and leftovers with what-if gates and deployment stacks.