Cannot modify this site because another operation is in progress

29 Feb 2020

Recently, I had a chance to setup Azure ResourceGroup Deployment task in the Azure Continuous Deployment(CD) - release pipeline for my team. That activity did taught me few trick/workarounds that I felt would be useful for fellow developers like me.

To enable the automated infrastructure/web app management using ARM template from the release pipeline, I had to convert all the manual settings configured on the portal by developers into ARM template and parameters. The big challenge was to make the web application template into a generic one, that can be used to deploy app service to multiple environments, just by changing its parameter file.

One challenging hurdle, was to generically set the multiple hostNameBinding without repeating or hard-coding it in the template file.

 First, I've moved all customDomain names information with the SSL binding information parameter JSON as follows

// {appname}.params.json

{
// other properties removed for brevity...

"customDomains": {
"value": [
    {
        "name": "dev1-xyz.abc.com",
        "sslState": "SniEnabled",
        "thumbprint": "SSLTHUMBPRINTHEXCHARACTERS",
        "toUpdate": true,
        "hostType": "Standard"
    },
    {
        "name": "dev2-xyz.abc.com",
        "sslState": "SniEnabled",
        "thumbprint": "SSLTHUMBPRINTHEXCHARACTERS",
        "toUpdate": true,
        "hostType": "Standard"
    }
]
}

// ...
}

Then in the template JSON file, I added a copy loop to assign the hostNameBindings as follows

{
// other properties removed for brevity...
{
    "type": "Microsoft.Web/sites/hostNameBindings", // Custom domain name & ssl bindings
    "apiVersion": "2018-11-01",
    "name": "[concat(variables('webAppName'), '/' , variables('customDomains')[CopyIndex()].name)]",
    "location": "[variables('location')]",
    "condition": "[greater(length(variables('customDomains')), 0)]",
    "dependsOn": [
        "[resourceId('Microsoft.Web/sites', variables('webAppName'))]"
    ],
    "properties": {
        "siteName": "[variables('webAppName')]",
        "hostNameType": "Verified",
        "sslState": "[variables('customDomains')[CopyIndex()].sslState]",
        "thumbprint": "[variables('customDomains')[CopyIndex()].thumbprint]",
        "toUpdate": true
    },
    "copy": {
        "name": "hostNameBindingsLoop", // Multiple hostname binding assignment loop
        "count": "[length(variables('customDomains'))]"
    }
}
}

From the theoretical view, all looked fine. But, when I executed above hostNameBindings in ARM template resulted in below error

Cannot modify this site because another operation is in progress. Details: Id: b47899ee-6cfd-4a03-839d-da6c2b7a025b, OperationName: Update, CreatedTime: 2/22/2020 11:11:35 AM

It was very strange for me to see some parallel executions causing conflict. Doesn't it suppose to understand & handle the conflicting operation automatically?

Since this error happened as soon as I've added this copy loop, I was fortunate to zero-in the location & reason of error quickly. I've surfed through stack-overflow for a solution to handle such issue.

Finally found a way to make this generic hostNameBindings loop to work out fine. Copy command contains mode property through which you can explicitly direct the ARM template processor to execute these operations serially.  So the final working hostNameBindings loop is as follows;

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

Hope above tip saves your time!

Related Posts