Skip to content

Instantly share code, notes, and snippets.

@NickDarvey
Last active October 30, 2021 08:20
Show Gist options
  • Save NickDarvey/814f0f34c03dff9f8fbe56310f0bef28 to your computer and use it in GitHub Desktop.
Save NickDarvey/814f0f34c03dff9f8fbe56310f0bef28 to your computer and use it in GitHub Desktop.
Deploy an Azure Web App with a bound App Service Managed Certificate (TLS/SSL) in one ARM template
{
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
},
"variables": {
"environmentName": "[resourceGroup().name]",
"location": "[resourceGroup().location]",
"solutionName": "example",
"solutionDomain": "on.example.com",
"hostingPlanName": "[concat(variables('solutionName'), uniqueString(resourceGroup().id))]",
"idAppName": "id",
"idAppSubdomain": "id",
"idAppDomain": "[concat(variables('idAppSubdomain'), '.',variables('solutionDomain'))]",
"idAppWebAppName": "[concat(variables('solutionName'), variables('idAppName'), uniqueString(resourceGroup().id))]"
},
"resources": [
{
"type": "Microsoft.Network/dnsZones",
"apiVersion": "2018-05-01",
"name": "[variables('solutionDomain')]",
"location": "global",
"properties": {
"zoneType": "Public"
}
},
{
"type": "Microsoft.Web/serverfarms",
"apiVersion": "2018-11-01",
"name": "[variables('hostingPlanName')]",
"location": "[variables('location')]",
"properties": {
"name": "[variables('hostingPlanName')]"
},
"sku": {
"name": "[parameters('webServerFarmSkuName')]"
},
"tags": {
"environment": "[variables('environmentName')]"
}
},
{
"type": "Microsoft.Web/sites",
"apiVersion": "2018-11-01",
"name": "[variables('idAppWebAppName')]",
"location": "[variables('location')]",
"dependsOn": [
"[concat('Microsoft.Web/serverfarms/', variables('hostingPlanName'))]"
],
"properties": {
"name": "[variables('idAppWebAppName')]",
"siteConfig": {
"appSettings": [
]
},
"serverFarmId": "[resourceId('Microsoft.Web/serverFarms',variables('hostingPlanName'))]",
"clientAffinityEnabled": false
},
"tags": {
"environment": "[variables('environmentName')]"
}
},
{
"type": "Microsoft.Network/dnszones/CNAME",
"apiVersion": "2018-05-01",
"name": "[concat(variables('solutionDomain'), '/', variables('idAppSubdomain'))]",
"dependsOn": [
"[concat('Microsoft.Network/dnszones/', variables('solutionDomain'))]",
"[concat('Microsoft.Web/sites/', variables('idAppWebAppName'))]"
],
"properties": {
"TTL": 3600,
"CNAMERecord": {
"cname": "[reference(resourceId('Microsoft.Web/sites/', variables('idAppWebAppName')), '2018-11-01').defaultHostName]"
}
}
},
{
"type": "Microsoft.Network/dnszones/TXT",
"apiVersion": "2018-05-01",
"name": "[concat(variables('solutionDomain'), '/', 'asuid.', variables('idAppSubdomain'))]",
"dependsOn": [
"[concat('Microsoft.Network/dnszones/', variables('solutionDomain'))]",
"[concat('Microsoft.Web/sites/', variables('idAppWebAppName'))]"
],
"properties": {
"TTL": 3600,
"TXTRecords": [
{
"value": [ "[reference(resourceId('Microsoft.Web/sites/', variables('idAppWebAppName')), '2018-11-01').customDomainVerificationId]" ]
}
]
}
},
{
"type": "Microsoft.Web/sites/hostnameBindings",
"name": "[concat(variables('idAppWebAppName'), '/', variables('idAppDomain'))]",
"apiVersion": "2016-03-01",
"location": "[resourceGroup().location]",
"dependsOn": [
"[concat('Microsoft.Web/sites/', variables('idAppWebAppName'))]",
"[concat('Microsoft.Network/dnszones/',variables('solutionDomain'),'/CNAME/', variables('idAppSubdomain'))]"
]
},
{
"type": "Microsoft.Web/certificates",
"apiVersion": "2019-08-01",
"name": "[variables('idAppDomain')]",
"location": "[variables('location')]",
"properties": {
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms/', variables('hostingPlanName'))]",
"canonicalName": "[variables('idAppDomain')]"
},
"dependsOn": [
"[concat('Microsoft.Web/sites/',variables('idAppWebAppName'))]",
"[concat('Microsoft.Web/sites/',variables('idAppWebAppName'),'/hostnameBindings/', variables('idAppDomain'))]"
]
},
{
"comments": "This is a nested template because it requires the initial hostnamebinding and certificate to be deployed first, and you can't declare the same resource twice.",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2019-10-01",
"name": "idAppWebAppTlsHostnameBinding",
"dependsOn": [
"[concat('Microsoft.Web/certificates/', variables('idAppDomain'))]"
],
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [
{
"type": "Microsoft.Web/sites/hostnameBindings",
"name": "[concat(variables('idAppWebAppName'), '/', variables('idAppDomain'))]",
"apiVersion": "2016-03-01",
"location": "[variables('location')]",
"properties": {
"sslState": "SniEnabled",
"thumbprint": "[reference(resourceId('Microsoft.Web/certificates', variables('idAppDomain'))).Thumbprint]"
}
}
]
}
}
}
],
"outputs": {
}
}
@NickDarvey
Copy link
Author

I wanted to be able to spin up new environments in one click, for things like new-environment-per-PR. Not quite there yet, but I feel this is getting close. It will at least reduce my RTO to something very tiny!

There's a chicken-egg thing here with the Azure DNS zone [concat('Microsoft.Network/dnszones/', variables('solutionDomain'))], because (AFAIK) you won't know its nameservers till after deployment (they have a number which seems to change with each zone I make, perhaps someone could figure out the partitioning scheme...). This means you can't go and add your NS records in your DNS manager for your root till after a first deployment, and your first deployment will fail because the TXT records won't be there for the hostnamebinding verification. I'm not sure of a way around that, yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment