首页 > 解决方案 > App Service Managed Identity 和 Key Vault 的正确方式

问题描述

我目前正在尝试使用 azure bicep 部署资源组,但是,我在使用 azure 应用程序服务的密钥库时遇到了问题。我想知道我是否真的以正确的方式这样做。我有一个主要的二头肌文件,大致如下:

// params removed for brevity...

targetScope = 'subscription'

resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = {
  name: 'rg-${appName}-${region}'
  location: 'centralus'
}

module appServicePlan 'appplan.bicep' = {
  params: {
    sku: appServicePlanSku
    appName: appName
    region: region
  }
  scope: rg
  name: 'AppServicePlanDeploy'
}

module keyVault 'keyvault.bicep' = {
  params: {
    keyVaultName: keyVaultName
    sqlPassword: sqlServerPassword
    webSiteManagedId: webSite.outputs.webAppPrincipal
  }
  scope: rg
  name: 'KeyVaultDeploy'
  dependsOn: [
    webSite
  ]
}

module ai 'ai.bicep' = {
  scope: rg
  name: 'ApplicationInsightsDeploy'
  params: {
    name: appName
    region: region
    keyVaultName: keyVault.outputs.keyVaultName
  }
  dependsOn: [
    keyVault
  ]
}

resource kv 'Microsoft.KeyVault/vaults@2019-09-01' existing = {
  name: keyVaultName
  scope: rg
}

module sql 'sqlserver.bicep' = {
  scope: rg
  name: 'SQLServerDeploy'
  params: {
    appName: appName
    region: region
    sqlPassword: kv.getSecret('sqlPassword')
    sqlCapacitity: sqlCapacitity
    sqlSku: sqlSku
    sqlTier: sqlTier
  }
  dependsOn: [
    keyVault
  ]
}

module webSite 'site.bicep' = {
  params: {
    appName: appName
    region: region
    keyVaultName: keyVaultName
    serverFarmId: appServicePlan.outputs.appServicePlanId
  }
  scope: rg
  name: 'AppServiceDeploy'
  dependsOn: [
    appServicePlan
  ]
}

我的问题来自于 site.bicep 的实现,我首先从导出的变量中传递秘密 uri,最后创建 web 应用程序作为应用程序洞察力、sql 等......在我们使用之前,所有这些都需要设置并在 keyvault 中他们导出的秘密 uri 以构建配置。我有一些类似的东西:site.bicep(之前):

  properties: {
    serverFarmId: serverFarmId
    keyVaultReferenceIdentity: userAssignedId
    siteConfig: {
      appSettings: [
        {
          name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
          value: '@Microsoft.KeyVault(SecretUri=${appInsightsConnectionString})'
        }
        {
          name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
          value: '@Microsoft.KeyVault(SecretUri=${appInsightsKey})'
        }
      ]
      netFrameworkVersion: 'v5.0'
    }
  
}

此实现的唯一问题是密钥库必须在网站之前构建,因为 sql、ai 和其他服务会将它们的值存储在密钥库中,以供 Web 应用程序通过它们各自的 uri 使用。问题在于,KeyVault 理所当然地不知道哪个 azure 服务可以访问它的密钥。

我的问题是在密钥库之前构建 Web 应用程序的解决方案是解决这个问题的唯一方法吗?我在 Web 应用程序上使用托管身份,如果可能,我希望继续这样做。我的最终解决方案有点像这样:

site.bicep(最终)

// params removed for brevity...
resource webApplication 'Microsoft.Web/sites@2020-12-01' = {
  name: 'app-${appName}-${region}'
  location: resourceGroup().location
  tags: {
    'hidden-related:${resourceGroup().id}/providers/Microsoft.Web/serverfarms/appServicePlan': 'Resource'
  }
  identity: {
    type: 'SystemAssigned'
  }
  properties: {
    serverFarmId: serverFarmId
    siteConfig: {
      appSettings: [
        {
          name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
          value: '@Microsoft.KeyVault(SecretUri=${keyVaultName}.vault.azure.net/secrets/aiConnectionString)'
        }
        {
          name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
          value: '@Microsoft.KeyVault(SecretUri=${keyVaultName}.vault.azure.net/secrets/aiInstrumentationKey)'
        }
        {
          name: 'AngularConfig:ApplicationInsightsKey'
          value: '@Microsoft.KeyVault(SecretUri=${keyVaultName}.vault.azure.net/secrets/aiInstrumentationKey)'
        }
      ]
      netFrameworkVersion: 'v5.0'
    }
  }
}

output webAppPrincipal string = webApplication.identity.principalId

和 KeyVault 将需要一个 dependsOn 网站

keyVault.bicep(最终):

resource keyVault 'Microsoft.KeyVault/vaults@2019-09-01' = {
  name: keyVaultName
  location: resourceGroup().location
  properties: {
    enabledForDeployment: true
    enabledForTemplateDeployment: true
    enabledForDiskEncryption: true
    enableRbacAuthorization: true
    tenantId: subscription().tenantId
    sku: {
      name: 'standard'
      family: 'A'
    }
    accessPolicies: [
      {
        tenantId: subscription().tenantId
        objectId: webSiteManagedId
        permissions: {
          keys: [
            'get'
          ]
          secrets: [
            'list'
            'get'
          ]
        }
      }
    ]
  }
}

标签: azureazure-web-app-serviceazure-keyvaultazure-managed-identityazure-bicep

解决方案


只需将您accessPolicies视为单独的资源并在创建 Key Vault 和应用服务时添加它们。同样适用于配置部分和连接字符串。在此处查看文档。

在 ARM 模板中,您可以使用嵌套模板实现相同的效果。在二头肌中它是一样的,但你将它们声明为通常包含父名称的单独资源(例如name: '${kv.name}/add'name: '${webSite.name}/connectionstrings'

样本

第 1 步:创建没有配置部分的应用服务

 resource webSite 'Microsoft.Web/sites@2020-12-01' = {
      name: webSiteName
      location: location
      properties: {
        serverFarmId: hostingPlan.id
        siteConfig:{
          netFrameworkVersion: 'v5.0'
        }
      }
      identity: {
        type:'SystemAssigned'
      }
    }

步骤 2:创建没有访问策略的 Key Vault

resource kv 'Microsoft.KeyVault/vaults@2019-09-01' = {
  name: keyVaultName
  location: location
  properties:{
    sku:{
      family: 'A'
      name: 'standard'
    }
    tenantId: tenantId
    enabledForTemplateDeployment: true
    accessPolicies:[
    ]
  }
}

第 3 步:创建新的访问策略并引用 Web Apps Managed Identity

resource keyVaultAccessPolicy 'Microsoft.KeyVault/vaults/accessPolicies@2021-06-01-preview' = {
  name: '${kv.name}/add'
  properties: {
      accessPolicies: [
          {
              tenantId: tenantId
              objectId: webSite.identity.principalId
              permissions: {
                keys: [
                  'get'
                ]
                secrets: [
                  'list'
                  'get'
                ]
              }
          }
      ]
  }
}

第 4 步:更新 Webb 应用程序配置部分

resource webSiteConnectionStrings 'Microsoft.Web/sites/config@2020-06-01' = {
  name: '${webSite.name}/connectionstrings'
  properties: {
    DefaultConnection: {
      value: '@Microsoft.KeyVault(SecretUri=${keyVaultName}.vault.azure.net/secrets/aiConnectionString)'
      type: 'SQLAzure'
    }
  }
}

推荐阅读