Home > Blockchain >  How to deploy App Service with managed SSL certificate using ARM
How to deploy App Service with managed SSL certificate using ARM

Time:07-22

I want create an Azure App Service with a custom hostname binding and a managed SSL certificate.

When I create a single Bicep-template, the certificate resource can only be deployed if the hostname binding is already created. But to create a hostname binding, I need the certificate thumbprint.

Updating the hostname binding in the same template also is not possible, as a resource can only exist once in a template.

// hostname bindings must be deployed one by one to prevent Conflict (HTTP 429) errors.
@batchSize(1)
resource customHostnameWithoutSsl 'Microsoft.web/sites/hostnameBindings@2019-08-01' = [for fqdn in customHostnames: {
  name: '${webAppService.name}/${fqdn}'
  properties: {
    siteName: webAppService.name
    hostNameType: 'Verified'
    sslState: 'Disabled'
  }
}]

// Managed certificates can only be created once the hostname is added to the web app.
resource certificates 'Microsoft.Web/certificates@2022-03-01' = [for (fqdn, i) in customHostnames: {
  name: '${fqdn}-${webAppName}'
  location: location
  properties: {
    serverFarmId: appServicePlanResourceId
    canonicalName: fqdn
  }
  dependsOn: [ ]
}]

// sslState and thumbprint can only be set once the managed certificate is created
@batchSize(1)
resource customHostname 'Microsoft.web/sites/hostnameBindings@2019-08-01' = [for (fqdn, i) in customHostnames: {
  name: '${webAppService.name}/${fqdn}'
  properties: {
    siteName: webAppService.name
    hostNameType: 'Verified'
    sslState: 'SniEnabled'
    thumbprint: certificates[i].properties.thumbprint
  }
}]

Is there another way to create a single deployment template to deploy an Azure App Service with a managed SSL certificate for the custom hostname?

CodePudding user response:

Updating the hostname binding in the same template also is not possible, as a resource can only exist once in a template.

To prevent this error, the resource can be deployed using a Bicep module (or ARM nested template).

Then the solution becomes this:

webApp.bicep

@description('The name of the App Service Plan that this web app will be deployed to.')
param appServicePlanResourceId string

@description('The location that the resource will be deployed to')
param location string = resourceGroup().location

@description('The custom hostnames that you wish to add.')
param customHostnames array = []

@description('Deploy hostnames without SSL binding before creating the certificate. Required when hostname is not present yet.')
param redeployHostnames bool = false

resource webAppService 'Microsoft.Web/sites@2020-12-01' = {
  ...
}

// hostname bindings must be deployed one by one to prevent Conflict (HTTP 429) errors.
@batchSize(1)
resource customHostnameWithoutSsl 'Microsoft.web/sites/hostnameBindings@2019-08-01' = [for fqdn in customHostnames: if (redeployHostnames) {
  name: '${webAppService.name}/${fqdn}'
  properties: {
    siteName: webAppService.name
    hostNameType: 'Verified'
    sslState: 'Disabled'
  }
}]

// certificates must be bound via module/nested template, because each resource can only occur once in every template
// in this case the hostnameBindings would occur twice otherwise.
module certificateBindings './bindCertificateToHostname.bicep' = {
  name: '${deployment().name}-ssl'
  params: {
    appServicePlanResourceId: appServicePlanResourceId
    customHostnames: customHostnames
    location: location
    webAppName: webAppService.name
  }
  dependsOn: customHostnameWithoutSsl
}

bindCertificateToHostname.bicep

param webAppName string
param location string
param appServicePlanResourceId string
param customHostnames array

// Managed certificates can only be created once the hostname is added to the web app.
resource certificates 'Microsoft.Web/certificates@2022-03-01' = [for (fqdn, i) in customHostnames: {
  name: '${fqdn}-${webAppName}'
  location: location
  properties: {
    serverFarmId: appServicePlanResourceId
    canonicalName: fqdn
  }
}]

// sslState and thumbprint can only be set once the managed certificate is created
@batchSize(1)
resource customHostname 'Microsoft.web/sites/hostnameBindings@2019-08-01' = [for (fqdn, i) in customHostnames: {
  name: '${webAppName}/${fqdn}'
  properties: {
    siteName: webAppName
    hostNameType: 'Verified'
    sslState: 'SniEnabled'
    thumbprint: certificates[i].properties.thumbprint
  }
}]

CodePudding user response:

One of the workaround you can follow to achieve the above requirement ;

To deploy an app service with SSL certificate for the custom domain you can follow the complete configuration and template which is suggested by @bmoore-msft on this GitHub sample:-

Sample template.json:-

"resources": [
    {
        "type": "Microsoft.Web/serverfarms",
        "apiVersion": "2019-08-01",
        "name": "[variables('appServicePlanName')]",
        "location": "[parameters('location')]",
        "properties": {
            "name": "[variables('appServicePlanName')]"
        },
        "sku": {
            "name": "P1",
            "tier": "Premium",
            "size": "1",
            "family": "P",
            "capacity": "1"
        }
    },
    {
        "type": "Microsoft.Web/sites",
        "apiVersion": "2019-08-01",
        "name": "[parameters('webAppName')]",
        "location": "[parameters('location')]",
        "dependsOn": [
            "[resourceId('Microsoft.Web/serverFarms', variables('appServicePlanName'))]"
        ],
        "properties": {
            "name": "[parameters('webAppName')]",
            "serverFarmId": "[resourceId('Microsoft.Web/serverFarms', variables('appServicePlanName'))]"
        }
    },
    {
        "condition": "[variables('enableSSL')]",
        "type": "Microsoft.Web/certificates",
        "apiVersion": "2019-08-01",
        "name": "[variables('certificateName')]",
        "location": "[parameters('location')]",
        "dependsOn": [
            "[resourceId('Microsoft.Web/sites', parameters('webAppName'))]"
        ],
        "properties": {
            "keyVaultId": "[parameters('existingKeyVaultId')]",
            "keyVaultSecretName": "[parameters('existingKeyVaultSecretName')]",
            "serverFarmId": "[resourceId('Microsoft.Web/serverFarms', variables('appServicePlanName'))]"
        }
    },
    {
        "type": "Microsoft.Web/sites/hostnameBindings",
        "name": "[concat(parameters('webAppName'), '/', parameters('customHostname'))]",
        "apiVersion": "2019-08-01",
        "location": "[parameters('location')]",
        "dependsOn": [
            "[resourceId('Microsoft.Web/certificates', variables('certificateName'))]"
        ],
        "properties": {
            "sslState": "[if(variables('enableSSL'), 'SniEnabled', json('null'))]",
            "thumbprint": "[if(variables('enableSSL'), reference(resourceId('Microsoft.Web/certificates', variables('certificateName'))).Thumbprint, json('null'))]"
        }
    }

NOTE:- I am not able to test it with custom domain due to of some provision issue with our account

For more information please refer this SO THREAD| How to configure an App Service Managed Certificate

UPDATE:-

As suggested by @Stan Janssen updating the answer so it will be beneficial for other community members.

Followed by the SO THREAD which is commented by @Alex i have shared below, after using nested template in bicep resolved the issue.

example:-

  {
"apiVersion": "2015-01-01",
"name": "linkedTemplate",
"type": "Microsoft.Resources/deployments",
"dependsOn": [
"[concat('Microsoft.Web/sites/', variables('webAppName'))]",
"[concat('Microsoft.Web/certificates/',variables('certificateName'))]"
],
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "https://bitbucket.org/PrashantPratap/freesslcert/raw/master/FreeSSLCertNested.json",
"contentVersion": "1.0.0.0"
}
}
}
  • Related