如何使用 ARM 部署具有托管 SSL 证书的应用服务

Sta*_*sen 5 azure azure-resource-manager azure-front-door azure-bicep

我想创建一个具有自定义主机名绑定和托管 SSL 证书的 Azure 应用服务。

当我创建单个 Bicep 模板时,只有在已创建主机名绑定的情况下才能部署证书资源。但要创建主机名绑定,我需要证书指纹。

更新同一模板中的主机名绑定也是不可能的,因为资源在模板中只能存在一次。

// 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
  }
}]
Run Code Online (Sandbox Code Playgroud)

是否有另一种方法可以创建单个部署模板来部署具有自定义主机名的托管 SSL 证书的 Azure 应用服务?

Sta*_*sen 10

更新同一模板中的主机名绑定也是不可能的,因为资源在模板中只能存在一次。

为了防止此错误,可以使用 Bicep 模块(或 ARM 嵌套模板)来部署资源。

那么解决方案就变成了这样:

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
}
Run Code Online (Sandbox Code Playgroud)

绑定证书到主机名.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
  }
}]
Run Code Online (Sandbox Code Playgroud)