Azure Function Managed Identities 和 Key Vault 访问策略(先有鸡还是先有蛋)
Azure Function Managed Identities and Key Vault Access Poilcy (Chicken Egg Situation)
只是想知道是否有一种方法可以在函数使用的密钥保管库中存储存储帐户连接字符串。该函数依赖于 WEBSITE_CONTENTAZUREFILECONNECTIONSTRING
和 AzureWebJobsStorage
。我正在尝试将这些添加到密钥保管库中,但我遇到的问题是..
- 配置存储帐户
- 提供密钥保管库
- 提供功能应用程序
- 将函数的访问策略添加到 Key Vault
这里的问题是,创建函数应用程序时(第 3 步)它失败了,因为它无法访问密钥库(缺少访问策略)。我无法创建策略,因为函数需要存在。
其他人是如何解决这个问题的?我正在考虑预先创建 AD 应用程序(步骤 0)而不使用托管身份(这并不理想)。
如果您使用 Azure 门户部署资源,您可以部署一个没有 keyvault 引用的函数应用程序,然后手动配置 keyvault 引用。
如果您使用 ARM 模板部署资源,doc 已提及您的问题。
Of note, you will need to define your application settings as their own resource, rather than using a siteConfig
property in the site definition. This is because the site needs to be defined first so that the system-assigned identity is created with it and can be used in the access policy.
样本:
{
//...
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"name": "[variables('storageAccountName')]",
//...
},
{
"type": "Microsoft.Insights/components",
"name": "[variables('appInsightsName')]",
//...
},
{
"type": "Microsoft.Web/sites",
"name": "[variables('functionAppName')]",
"identity": {
"type": "SystemAssigned"
},
//...
"resources": [
{
"type": "config",
"name": "appsettings",
//...
"dependsOn": [
"[resourceId('Microsoft.Web/sites', variables('functionAppName'))]",
"[resourceId('Microsoft.KeyVault/vaults/', variables('keyVaultName'))]",
"[resourceId('Microsoft.KeyVault/vaults/secrets', variables('keyVaultName'), variables('storageConnectionStringName'))]",
"[resourceId('Microsoft.KeyVault/vaults/secrets', variables('keyVaultName'), variables('appInsightsKeyName'))]"
],
"properties": {
"AzureWebJobsStorage": "[concat('@Microsoft.KeyVault(SecretUri=', reference(variables('storageConnectionStringResourceId')).secretUriWithVersion, ')')]",
"WEBSITE_CONTENTAZUREFILECONNECTIONSTRING": "[concat('@Microsoft.KeyVault(SecretUri=', reference(variables('storageConnectionStringResourceId')).secretUriWithVersion, ')')]",
"APPINSIGHTS_INSTRUMENTATIONKEY": "[concat('@Microsoft.KeyVault(SecretUri=', reference(variables('appInsightsKeyResourceId')).secretUriWithVersion, ')')]",
"WEBSITE_ENABLE_SYNC_UPDATE_SITE": "true"
//...
}
},
{
"type": "sourcecontrols",
"name": "web",
//...
"dependsOn": [
"[resourceId('Microsoft.Web/sites', variables('functionAppName'))]",
"[resourceId('Microsoft.Web/sites/config', variables('functionAppName'), 'appsettings')]"
],
}
]
},
{
"type": "Microsoft.KeyVault/vaults",
"name": "[variables('keyVaultName')]",
//...
"dependsOn": [
"[resourceId('Microsoft.Web/sites', variables('functionAppName'))]"
],
"properties": {
//...
"accessPolicies": [
{
"tenantId": "[reference(concat('Microsoft.Web/sites/', variables('functionAppName'), '/providers/Microsoft.ManagedIdentity/Identities/default'), '2015-08-31-PREVIEW').tenantId]",
"objectId": "[reference(concat('Microsoft.Web/sites/', variables('functionAppName'), '/providers/Microsoft.ManagedIdentity/Identities/default'), '2015-08-31-PREVIEW').principalId]",
"permissions": {
"secrets": [ "get" ]
}
}
]
},
"resources": [
{
"type": "secrets",
"name": "[variables('storageConnectionStringName')]",
//...
"dependsOn": [
"[resourceId('Microsoft.KeyVault/vaults/', variables('keyVaultName'))]",
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
],
"properties": {
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountResourceId'),'2015-05-01-preview').key1)]"
}
},
{
"type": "secrets",
"name": "[variables('appInsightsKeyName')]",
//...
"dependsOn": [
"[resourceId('Microsoft.KeyVault/vaults/', variables('keyVaultName'))]",
"[resourceId('Microsoft.Insights/components', variables('appInsightsName'))]"
],
"properties": {
"value": "[reference(resourceId('microsoft.insights/components/', variables('appInsightsName')), '2015-05-01').InstrumentationKey]"
}
}
]
}
]
}
许多其他需要访问 Key Vault 的资源(包括服务总线、事件中心、API 管理)也存在同样的问题。
我们如何创建具有这些循环依赖关系的可重新部署的 ARM 模板?
使用客户管理的密钥加密的服务总线部署示例
- 首先在未加密的情况下创建服务总线,因为全新的资源无法访问密钥保管库密钥。它有一个托管的 identity/service 主体。
- KeyVault 已更新以授予对服务总线托管标识的访问权限。
- 服务总线更新为使用 Key Vault 访问进行加密。
- 如果因为需要更新基础结构而重新部署 ARM 模板,则服务总线重新部署会失败,因为无法删除加密
- 不支持检查资源是否已存在于 ARM 模板中以避免在后续部署中重复步骤 1
用户分配的托管标识似乎可以解决问题,但 ARM 模板不支持它们!
只是想知道是否有一种方法可以在函数使用的密钥保管库中存储存储帐户连接字符串。该函数依赖于 WEBSITE_CONTENTAZUREFILECONNECTIONSTRING
和 AzureWebJobsStorage
。我正在尝试将这些添加到密钥保管库中,但我遇到的问题是..
- 配置存储帐户
- 提供密钥保管库
- 提供功能应用程序
- 将函数的访问策略添加到 Key Vault
这里的问题是,创建函数应用程序时(第 3 步)它失败了,因为它无法访问密钥库(缺少访问策略)。我无法创建策略,因为函数需要存在。
其他人是如何解决这个问题的?我正在考虑预先创建 AD 应用程序(步骤 0)而不使用托管身份(这并不理想)。
如果您使用 Azure 门户部署资源,您可以部署一个没有 keyvault 引用的函数应用程序,然后手动配置 keyvault 引用。
如果您使用 ARM 模板部署资源,doc 已提及您的问题。
Of note, you will need to define your application settings as their own resource, rather than using a
siteConfig
property in the site definition. This is because the site needs to be defined first so that the system-assigned identity is created with it and can be used in the access policy.
样本:
{
//...
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"name": "[variables('storageAccountName')]",
//...
},
{
"type": "Microsoft.Insights/components",
"name": "[variables('appInsightsName')]",
//...
},
{
"type": "Microsoft.Web/sites",
"name": "[variables('functionAppName')]",
"identity": {
"type": "SystemAssigned"
},
//...
"resources": [
{
"type": "config",
"name": "appsettings",
//...
"dependsOn": [
"[resourceId('Microsoft.Web/sites', variables('functionAppName'))]",
"[resourceId('Microsoft.KeyVault/vaults/', variables('keyVaultName'))]",
"[resourceId('Microsoft.KeyVault/vaults/secrets', variables('keyVaultName'), variables('storageConnectionStringName'))]",
"[resourceId('Microsoft.KeyVault/vaults/secrets', variables('keyVaultName'), variables('appInsightsKeyName'))]"
],
"properties": {
"AzureWebJobsStorage": "[concat('@Microsoft.KeyVault(SecretUri=', reference(variables('storageConnectionStringResourceId')).secretUriWithVersion, ')')]",
"WEBSITE_CONTENTAZUREFILECONNECTIONSTRING": "[concat('@Microsoft.KeyVault(SecretUri=', reference(variables('storageConnectionStringResourceId')).secretUriWithVersion, ')')]",
"APPINSIGHTS_INSTRUMENTATIONKEY": "[concat('@Microsoft.KeyVault(SecretUri=', reference(variables('appInsightsKeyResourceId')).secretUriWithVersion, ')')]",
"WEBSITE_ENABLE_SYNC_UPDATE_SITE": "true"
//...
}
},
{
"type": "sourcecontrols",
"name": "web",
//...
"dependsOn": [
"[resourceId('Microsoft.Web/sites', variables('functionAppName'))]",
"[resourceId('Microsoft.Web/sites/config', variables('functionAppName'), 'appsettings')]"
],
}
]
},
{
"type": "Microsoft.KeyVault/vaults",
"name": "[variables('keyVaultName')]",
//...
"dependsOn": [
"[resourceId('Microsoft.Web/sites', variables('functionAppName'))]"
],
"properties": {
//...
"accessPolicies": [
{
"tenantId": "[reference(concat('Microsoft.Web/sites/', variables('functionAppName'), '/providers/Microsoft.ManagedIdentity/Identities/default'), '2015-08-31-PREVIEW').tenantId]",
"objectId": "[reference(concat('Microsoft.Web/sites/', variables('functionAppName'), '/providers/Microsoft.ManagedIdentity/Identities/default'), '2015-08-31-PREVIEW').principalId]",
"permissions": {
"secrets": [ "get" ]
}
}
]
},
"resources": [
{
"type": "secrets",
"name": "[variables('storageConnectionStringName')]",
//...
"dependsOn": [
"[resourceId('Microsoft.KeyVault/vaults/', variables('keyVaultName'))]",
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
],
"properties": {
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountResourceId'),'2015-05-01-preview').key1)]"
}
},
{
"type": "secrets",
"name": "[variables('appInsightsKeyName')]",
//...
"dependsOn": [
"[resourceId('Microsoft.KeyVault/vaults/', variables('keyVaultName'))]",
"[resourceId('Microsoft.Insights/components', variables('appInsightsName'))]"
],
"properties": {
"value": "[reference(resourceId('microsoft.insights/components/', variables('appInsightsName')), '2015-05-01').InstrumentationKey]"
}
}
]
}
]
}
许多其他需要访问 Key Vault 的资源(包括服务总线、事件中心、API 管理)也存在同样的问题。
我们如何创建具有这些循环依赖关系的可重新部署的 ARM 模板?
使用客户管理的密钥加密的服务总线部署示例
- 首先在未加密的情况下创建服务总线,因为全新的资源无法访问密钥保管库密钥。它有一个托管的 identity/service 主体。
- KeyVault 已更新以授予对服务总线托管标识的访问权限。
- 服务总线更新为使用 Key Vault 访问进行加密。
- 如果因为需要更新基础结构而重新部署 ARM 模板,则服务总线重新部署会失败,因为无法删除加密
- 不支持检查资源是否已存在于 ARM 模板中以避免在后续部署中重复步骤 1
用户分配的托管标识似乎可以解决问题,但 ARM 模板不支持它们!