Azure.Deployments.Templates rejects copy.count: 0; real ARM accepts as no-op
- Dominant language
- Bicep
- Stars
- 3.6k
- Forks
- 830
- Avg merge
- 1d 21m
- Merged PRs (30d)
- 79
Description
## Summary
`Azure.Deployments.Templates` (the ARM template-expression engine, NuGet `Azure.Deployments.Templates@1.590.0`) rejects deployments where a resource's `copy.count` evaluates to `0`. Real Azure ARM accepts the same template and treats it as a no-op (no resources deployed for that copy). This causes a divergence between local processing of ARM/Bicep templates and the live service.
This bug surfaces in real Bicep templates whenever a `param items array = []` default is iterated with `[for item in items: ...]` — a common "optionally deploy these resources" pattern. The compiled ARM contains `"count": "[length(parameters('items'))]"` which evaluates to 0 when no items are passed.
## Repro
Submit the following template to a host that uses `TemplateEngine.ProcessTemplateLanguageExpressions(...)` (or any consumer of this library on the deploy path) with empty `parameters`:
```json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": { "items": { "type": "array", "defaultValue": [] } },
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2023-05-01",
"name": "[concat('repro', copyIndex())]",
"location": "westus",
"sku": { "name": "Standard_LRS" },
"kind": "StorageV2",
"copy": { "name": "storageCopy", "count": "[length(parameters('items'))]" }
}
]
}
```
### Actual
`InvalidTemplateDeployment` thrown by the engine:
```
The template 'copy' definition at line '...' has an invalid copy count of: '0'.
The copy count must be a positive integer value and cannot exceed '800'.
Please see https://aka.ms/arm-resource-loops for usage details.
```
### Expected
Engine should accept and produce zero resources (deployment succeeds, nothing deployed for that copy). This matches:
- Real Azure ARM behaviour (verified against a live subscription)
- The documented contract: ["If you provide a value of zero for count, the resource isn't deployed."](https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/copy-resources)
## Sanity check
Same template with `parameters: { items: { value: ["a"] } }` (count = 1) processes correctly and emits one resource. So the bug is specifically the lower-bound check on count, which appears to enforce `count > 0` rather than `count >= 0`.
## Where this surfaced
Found in the [Topaz Azure emulator](https://github.com/TheCloudTheory/Topaz/issues/161), which uses this library to evaluate ARM expressions locally. Topaz's `/validate` endpoint accepts the template (it only does syntax checks), but `PUT` and `whatIf` go through `ProcessTemplateLanguageExpressions` and fail. The Topaz maintainer can work around it by pre-rewriting count-zero copy blocks, but the real fix belongs here — the library should match real ARM.
## Note on intake
Filing here per the [nuget.org listing's project metadata](https://www.nuget.org/packages/Azure.Deployments.Templates/), which points at internal Azure DevOps for source; the package description notes it's not officially supported. Happy to re-route if the Bicep team isn't the right intake — please redirect.
Contributor guide
Assessment
This issue has not been assessed yet.