获取在Bicep模块中创建时的函数应用默认主机密钥

huangapple go评论106阅读模式
英文:

Get Function App Default Host Key When Created in Bicep Module

问题

我之前创建了可工作的 Bicep 模板,用于创建一个函数应用程序,然后仅在函数应用程序直接作为主模板中的资源创建时使用密钥,而不是在模块内部创建。

这次,我有一个"主" Bicep 模板,调用一个 Bicep 模块来创建一个函数应用程序。然后在主模板中,我需要将函数应用程序的密钥传递给另一个模块,该模块将其存储为一个名为 "apim" 的机密值。

ChatGPT 建议如下:

  1. // 创建函数应用程序
  2. module functionApp 'br:azcontreg.azurecr.io/bicep/modules/functionapp:v23.01.17.01' = {
  3. name: 'functionApp'
  4. params: {
  5. functionAppName: functionAppName
  6. appServicePlanName: appServicePlanName
  7. appServicePlanResourceGroup: appServicePlanResourceGroup
  8. storageAccountName: storageAccountName
  9. storageAccountResourceGroup: storageAccountResourceGroup
  10. }
  11. }
  12. // 检索函数应用程序的密钥
  13. output functionAppKeys object = listKeys(functionApp.outputs.id, '2019-08-01')
  14. // 创建 API 管理的命名值
  15. module apimNamedValue 'br:azcontreg.azurecr.io/bicep/modules/apimnamedvalue:v23.01.17.01' = {
  16. name: 'apimNamedValue'
  17. scope: resourceGroup('rg-apim-${subscription().displayName}')
  18. dependsOn: [
  19. functionApp
  20. keyVault
  21. ]
  22. params: {
  23. name: '${functionApp.outputs.appServiceName}-key'
  24. secret: true
  25. value: functionAppKeys.keys[0].value
  26. environment: environment
  27. }
  28. }

问题是,这会导致以下编译错误:

此表达式正在用作函数 "listKeys" 的参数,该函数要求在部署开始时可以计算的值。可以在开始时计算的 functionApp 的属性包括 "name"。bicep(BCP181)

负责创建函数应用程序的模块包含以下内容:

  1. resource appService 'Microsoft.Web/sites@2021-03-01' = {
  2. name: name
  3. kind: appKind
  4. location: location
  5. tags: tags
  6. identity: {
  7. type: !empty(identityName) ? 'SystemAssigned, UserAssigned' : 'SystemAssigned'
  8. userAssignedIdentities: !empty(identityName) ? {
  9. '${appServiceIdentity.id}': {}
  10. } : null
  11. }
  12. properties: {
  13. httpsOnly: true
  14. reserved: false
  15. serverFarmId: appServicePlan.id
  16. virtualNetworkSubnetId: !empty(vnetName) && !empty(vnetSubnetName) ? vnetSubnet.id : null
  17. }
  18. resource appServiceAppSettings 'config' = {
  19. name: 'appsettings'
  20. properties: appSettingsInternal
  21. }
  22. resource appServiceSlotConfigNames 'config' = {
  23. name: 'slotConfigNames'
  24. properties: {
  25. appSettingNames: deploymentSlotSettingNames
  26. }
  27. }
  28. resource appServiceWeb 'config' = {
  29. name: 'web'
  30. properties: {
  31. alwaysOn: alwaysOn
  32. ftpsState: 'FtpsOnly'
  33. healthCheckPath: healthCheckPath
  34. ipSecurityRestrictions: ipSecurityRestrictions
  35. use32BitWorkerProcess: use32BitProcess
  36. vnetRouteAllEnabled: vnetRouteAll
  37. }
  38. }
  39. }
  40. output appServiceId string = appService.id

这是从主.bicep 中调用的:

  1. module functionApp 'br:crbicepregistryprod001.azurecr.io/bicep/modules/appservice:v23.04.13.01' = {
  2. name: 'functionApp'
  3. params: {
  4. name: functionAppName
  5. appServicePlanScope: functionAppServicePlanEnv.rg
  6. appServicePlanName: functionAppServicePlanEnv.name
  7. appKind: 'functionapp'
  8. vnetName: 'vnet-${environment}-uksouth'
  9. vnetSubnetName: functionAppServicePlanEnv.subnet
  10. ipSecurityRestrictions: functionAppIpSecurityRestrictions
  11. appSettings: functionAppSettings
  12. enableSlot: false
  13. alwaysOn: true
  14. healthCheckPath: '/health'
  15. }
  16. dependsOn: [
  17. appInsights
  18. keyVault
  19. ]
  20. }

后来,主.bicep 使用以下方式调用新模块:

  1. module apimNamedValue 'br:crbicepregistryprod001.azurecr.io/bicep/modules/apimnamedvalueforlistkeys:v23.06.13.02' = {
  2. name: 'apimNamedValue'
  3. scope: resourceGroup('rg-apim-${subscription().displayName}')
  4. dependsOn: [
  5. functionApp
  6. keyVault
  7. ]
  8. params: {
  9. name: '${functionApp.outputs.appServiceName}-key'
  10. secret: true
  11. resourceId: functionApp.outputs.appServiceId
  12. environment: environment
  13. }
  14. }

用于使用 listKeys 获取密钥的新模块如下所示:

  1. @description('要部署的NamedValue的名称')
  2. param name string
  3. @description('要在listkeys函数中使用的资源 ID')
  4. param resourceId string
  5. @description('用于listkeys函数的 API 版本')
  6. param apiVersion string = '2022-09-01'
  7. @description('指定值是否为机密。默认为 false')
  8. param secret bool = false
  9. @description('指定值是否为密钥保管库引用。还意味着值是机密的。默认为 false')
  10. param keyVaultReference bool = false
  11. @description('要部署到的 APIM 环境')
  12. @allowed([
  13. 'dev'
  14. 'int'
  15. 'act'
  16. 'prod'
  17. ])
  18. param environment string = 'dev'
  19. var apimName = {
  20. dev: 'apim-dev-002'
  21. int: 'apim-int-001'
  22. act: 'apim-act-001'
  23. prod: 'apim-prod-002'
  24. }[environment]
  25. var value = listKeys(resourceId, apiVersion)
  26. resource apim 'Microsoft.ApiManagement/service@2021-08-01' existing = {
  27. name: apimName
  28. resource apimNamedValue 'namedValues' = {
  29. name: name
  30. properties: {
  31. displayName: name
  32. secret: secret || keyVaultReference
  33. keyVault: keyVaultReference ? {
  34. secretIdentifier: value
  35. } : null
  36. value: !keyVaultReference ? value : null
  37. }
  38. }
  39. }
  40. output namedValueId string = apim::apimNamedValue.id
  41. output namedValueName string = apim
  42. <details>
  43. <summary>英文:</summary>
  44. I have previously created working bicep templates that create a function app and later use the keys but only where the function app is created as a resource directly within the main template - rather than within a module.
  45. This time, I have a &quot;main&quot; bicep template that calls a bicep module to create a function app. Later in the main template, I need to pass the function app&#39;s key to another module that stores as a secret apim named value.
  46. ChatGPT suggested the following:
  47. ````json
  48. // Create the Function App
  49. module functionApp &#39;br:azcontreg.azurecr.io/bicep/modules/functionapp:v23.01.17.01&#39; = {
  50. name: &#39;functionApp&#39;
  51. params: {
  52. functionAppName: functionAppName
  53. appServicePlanName: appServicePlanName
  54. appServicePlanResourceGroup: appServicePlanResourceGroup
  55. storageAccountName: storageAccountName
  56. storageAccountResourceGroup: storageAccountResourceGroup
  57. }
  58. }
  59. // Retrieve the Function App keys
  60. output functionAppKeys object = listKeys(functionApp.outputs.id, &#39;2019-08-01&#39;)
  61. // Create the API Management named value
  62. module apimNamedValue &#39;br:azcontreg.azurecr.io/bicep/modules/apimnamedvalue:v23.01.17.01&#39; = {
  63. name: &#39;apimNamedValue&#39;
  64. scope: resourceGroup(&#39;rg-apim-${subscription().displayName}&#39;)
  65. dependsOn: [
  66. functionApp
  67. keyVault
  68. ]
  69. params: {
  70. name: &#39;${functionApp.outputs.appServiceName}-key&#39;
  71. secret: true
  72. value: functionAppKeys.keys[0].value
  73. environment: environment
  74. }
  75. }

Problem is, this gives the following compile error:

> This expression is being used in an argument of the function
> "listKeys", which requires a value that can be calculated at the start
> of the deployment. Properties of functionApp which can be calculated
> at the start include "name".bicep(BCP181)

The module that provisions the function app contains the following:

  1. resource appService &#39;Microsoft.Web/sites@2021-03-01&#39; = {
  2. name: name
  3. kind: appKind
  4. location: location
  5. tags: tags
  6. identity: {
  7. type: !empty(identityName) ? &#39;SystemAssigned, UserAssigned&#39; : &#39;SystemAssigned&#39;
  8. userAssignedIdentities: !empty(identityName) ? {
  9. &#39;${appServiceIdentity.id}&#39;: {}
  10. } : null
  11. }
  12. properties: {
  13. httpsOnly: true
  14. reserved: false
  15. serverFarmId: appServicePlan.id
  16. virtualNetworkSubnetId: !empty(vnetName) &amp;&amp; !empty(vnetSubnetName) ? vnetSubnet.id : null
  17. }
  18. resource appServiceAppSettings &#39;config&#39; = {
  19. name: &#39;appsettings&#39;
  20. properties: appSettingsInternal
  21. }
  22. resource appServiceSlotConfigNames &#39;config&#39; = {
  23. name: &#39;slotConfigNames&#39;
  24. properties: {
  25. appSettingNames: deploymentSlotSettingNames
  26. }
  27. }
  28. resource appServiceWeb &#39;config&#39; = {
  29. name: &#39;web&#39;
  30. properties: {
  31. alwaysOn: alwaysOn
  32. ftpsState: &#39;FtpsOnly&#39;
  33. healthCheckPath: healthCheckPath
  34. ipSecurityRestrictions: ipSecurityRestrictions
  35. use32BitWorkerProcess: use32BitProcess
  36. vnetRouteAllEnabled: vnetRouteAll
  37. }
  38. }
  39. }
  40. output appServiceId string = appService.id

This is called from the main.bicep with the following:

  1. module functionApp &#39;br:crbicepregistryprod001.azurecr.io/bicep/modules/appservice:v23.04.13.01&#39; = {
  2. name: &#39;functionApp&#39;
  3. params: {
  4. name: functionAppName
  5. appServicePlanScope: functionAppServicePlanEnv.rg
  6. appServicePlanName: functionAppServicePlanEnv.name
  7. appKind: &#39;functionapp&#39;
  8. vnetName: &#39;vnet-${environment}-uksouth&#39;
  9. vnetSubnetName: functionAppServicePlanEnv.subnet
  10. ipSecurityRestrictions: functionAppIpSecurityRestrictions
  11. appSettings: functionAppSettings
  12. enableSlot: false
  13. alwaysOn: true
  14. healthCheckPath: &#39;/health&#39;
  15. }
  16. dependsOn: [
  17. appInsights
  18. keyVault
  19. ]
  20. }

Main.bicep later calls the new module with the following:

  1. module apimNamedValue &#39;br:crbicepregistryprod001.azurecr.io/bicep/modules/apimnamedvalueforlistkeys:v23.06.13.02&#39; = {
  2. name: &#39;apimNamedValue&#39;
  3. scope: resourceGroup(&#39;rg-apim-${subscription().displayName}&#39;)
  4. dependsOn: [
  5. functionApp
  6. keyVault
  7. ]
  8. params: {
  9. name: &#39;${functionApp.outputs.appServiceName}-key&#39;
  10. secret: true
  11. resourceId: functionApp.outputs.appServiceId
  12. environment: environment
  13. }
  14. }

The new module to obtain the key using listKeys is as follows:

  1. @description(&#39;Name of the NamedValue to deploy&#39;)
  2. param name string
  3. @description(&#39;Resource Id to be used in the listkeys function&#39;)
  4. param resourceId string
  5. @description(&#39;Used in listkeys function&#39;)
  6. param apiVersion string = &#39;2022-09-01&#39;
  7. @description(&#39;Specify if the value is secret. Defaults to false&#39;)
  8. param secret bool = false
  9. @description(&#39;Specify if the value is a key vault reference. Also implies value is secret. Defaults to false&#39;)
  10. param keyVaultReference bool = false
  11. @description(&#39;APIM environment to deploy to&#39;)
  12. @allowed([
  13. &#39;dev&#39;
  14. &#39;int&#39;
  15. &#39;act&#39;
  16. &#39;prod&#39;
  17. ])
  18. param environment string = &#39;dev&#39;
  19. var apimName = {
  20. dev: &#39;apim-dev-002&#39;
  21. int: &#39;apim-int-001&#39;
  22. act: &#39;apim-act-001&#39;
  23. prod: &#39;apim-prod-002&#39;
  24. }[environment]
  25. var value = listKeys(resourceId, apiVersion)
  26. resource apim &#39;Microsoft.ApiManagement/service@2021-08-01&#39; existing = {
  27. name: apimName
  28. resource apimNamedValue &#39;namedValues&#39; = {
  29. name: name
  30. properties: {
  31. displayName: name
  32. secret: secret || keyVaultReference
  33. keyVault: keyVaultReference ? {
  34. secretIdentifier: value
  35. } : null
  36. value: !keyVaultReference ? value : null
  37. }
  38. }
  39. }
  40. output namedValueId string = apim::apimNamedValue.id
  41. output namedValueName string = apim::apimNamedValue.name
  42. output namedValueNameFormatted string = &#39;{{${apim::apimNamedValue.name}}}&#39;

If I drill into the resource group deployment in the Azure portal I see:

获取在Bicep模块中创建时的函数应用默认主机密钥

If I then click the error details link, I see:

  1. {
  2. &quot;code&quot;: &quot;BadRequest&quot;,
  3. &quot;message&quot;: &quot;&quot;
  4. }

I guess the error state is “Not Found” because the module is running in the scope of the apim’s resource group but it’s trying to access the key of the function app which is in a different resource group?

答案1

得分: 1

传递密钥/机密可能会很棘手,特别是当一切都模块化时。

我会考虑创建一个新的模块变体 br:azcontreg.azurecr.io/bicep/modules/apimnamedvaluefromkey,该模块接受额外的两个参数,用于资源ID和API版本。然后,您可以将 listkeys 移入该模块,它不会遭受名称计算错误,因为它已经抽象化了。

英文:

Passing around keys/secrets can be tricky, especially when everything modularised.

I'd be tempted to create a new module variant br:azcontreg.azurecr.io/bicep/modules/apimnamedvaluefromkey that takes an extra 2 parameters for the resourceid and api version. Then you can move the listkeys into that module and it won't suffer the name calculation error as it's abstracted.

答案2

得分: 1

已通过将函数应用程序密钥存储在相同资源组的密钥保管库中解决。然后,apim命名值引用了密钥保管库的秘密。

以下是对初始apim命名值模块的调用。

  1. module apimNamedValue 'br:regname.azurecr.io/bicep/modules/apimnamedvalue:v23.05.04.01' = {
  2. name: 'apimNamedValue'
  3. scope: resourceGroup('rg-apim-${subscription().displayName}')
  4. dependsOn: [
  5. functionApp
  6. keyVault
  7. ]
  8. params: {
  9. name: '${functionApp.outputs.appServiceName}-key'
  10. secret: true
  11. keyVaultReference: true
  12. value: '${keyVault.outputs.keyVaultId}/secrets/${secretName}/'
  13. environment: environment
  14. }
  15. }
英文:

Solved by storing the function app key in a key vault of the same resource group. The apim named value then referenced the key vault secret.

Below is the call to the initial apim named value module.

  1. module apimNamedValue &#39;br:regname.azurecr.io/bicep/modules/apimnamedvalue:v23.05.04.01&#39; = {
  2. name: &#39;apimNamedValue&#39;
  3. scope: resourceGroup(&#39;rg-apim-${subscription().displayName}&#39;)
  4. dependsOn: [
  5. functionApp
  6. keyVault
  7. ]
  8. params: {
  9. name: &#39;${functionApp.outputs.appServiceName}-key&#39;
  10. secret: true
  11. keyVaultReference: true
  12. value: &#39;${keyVault.outputs.keyVaultId}/secrets/${secretName}/&#39;
  13. environment: environment
  14. }
  15. }

huangapple
  • 本文由 发表于 2023年6月13日 18:22:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/76463916.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定