The copy count must be a postive integer value and cannot exceed '800'

29 Feb 2020

This post is continuation of hostNameBindings implementation mentioned in this post. After fixing up issue created by parallel resource assignments error in the copy loop. I've got into another issue that took me couple of hours to find a solution, so I'm sharing it for your quick view.

Few of my development websites didn't contain any custom domain names. As the result, I've provide an empty customDomain array in parameter JSON that is referred in hostNameBindings copy loop.

// Parameters JSON
"customDomains": {
    "value": []
}

Then I've started receiving strange ARM template validation failure as follows

Deployment template validation failed: 'The template 'copy' definition at line '120' and column '67' has an invalid copy count. 
The copy count must be a postive integer value and cannot exceed '800'. Please see https://aka.ms/arm-copy for usage details.'.

Template is invalid.

I've been using for loop constructs for over a decade, and I was expecting a count=0 to terminate the loop immediately and do nothing. But instead ARM validation, chose to throw an error. That's crazy for any developer. BTW, I came to know that 0 is an integer but not a positive integer analyzing above error frown.

Adding condition statement to evaluate array size = 0 too didn't solve my problem.

"type": "Microsoft.Web/sites/hostNameBindings", // Custom domain name & ssl bindings
"condition": "[greater(length(variables('CustomDomains')), 0)]"

The ARM template validator is too short sighted; ignored the condition expression and failed the validation as the array looks empty. I believe conditions are evaluated only during ARM run-time.

After a long struggle to make array length = 0 to work, I've worked around in opposite direction to provide a safe default hostNameBindings value for the web app service. By default, I created another variable that dynamically calculates the azurewebsites.net domain name and appends that as the first entry in the hostNameBindings fixing this error as follows

"parameters":{
    "customDomains": {
        "type": "array",
        "metadata": {
            "description": "custom domain names for this site"
        }
    }
},


"variables": {    
    // Workaround: as the hostNameBindings copy loop doesn't allow empty array
    "dummyHostNameBinding": [
        {
            "name": "[concat(variables('webAppName'), '.azurewebsites.net')]",
            "sslState": "Disabled",
            "thumbprint": "[json('null')]",
            "toUpdate": true,
            "hostType": "Standard"
        }
    ],
    "finalCustomDomainList": "[concat(parameters('customDomains'), variables('dummyHostNameBinding'))]"
},

// Other properties removed for template brevity

"resources":[
{
    "type": "Microsoft.Web/sites/hostNameBindings", // Custom domain name & ssl bindings
    "apiVersion": "2018-11-01",
    "name": "[concat(variables('webAppName'), '/' , variables('finalCustomDomainList')[CopyIndex()].name)]",
    "location": "[variables('location')]",
    "condition": "[greater(length(variables('finalCustomDomainList')), 0)]",
    "dependsOn": [
        "[resourceId('Microsoft.Web/sites', variables('webAppName'))]"
    ],
    "properties": {
        "siteName": "[variables('webAppName')]",
        "hostNameType": "Verified",
        "sslState": "[variables('finalCustomDomainList')[CopyIndex()].sslState]",
        "thumbprint": "[variables('finalCustomDomainList')[CopyIndex()].thumbprint]",
        "toUpdate": true
    },
    "copy": {
        "name": "hostNameBindingsLoop", // Multiple hostname binding assignment loop
        "count": "[length(variables('finalCustomDomainList'))]",
        "mode": "Serial", // Parallel hostNamebindings assignment results in error, so run one by one
        "batchSize": 1
    }
}]

Hope above saves your coding day!

P.S: From "apiVersion": "2019-05-10" or later, the count value can be zero. These schema change is available for REST API version, it is not yet available in SDK and Powershell CLI. So we still have to go with the workaround.  

Reference: https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/copy-resources#copy-limits