azure - 使用二头肌的 secret 范围角色定义和分配

标签 azure azure-bicep

我正在尝试创建两个可重复使用的二头肌模块,以允许读取所选 keystore 中的特定 secret 。为此,我首先声明角色定义:

targetScope = 'subscription'

param subscriptionId string
param resourceGroupName string
param keyVaultName string
param allowedSecrets array

param managementGroupRoot string

var keyVaultScope = '/subscriptions/${subscriptionId}/resourcegroups/${resourceGroupName}/providers/Microsoft.KeyVault/vaults/${keyVaultName}'

var assignableScopes = [for secretName in allowedSecrets: '${keyVaultScope}/secrets/${secretName}']

var roleName = 'Limitied ${keyVaultName} secret reader ${managementGroupRoot}'

// Permissions based on Key Vault Secrets User
// https://www.azadvertizer.net/azrolesadvertizer/4633458b-17de-408a-b874-0445c86b69e6.html
resource key_vault_secrets_user_role_definition 'Microsoft.Authorization/roleDefinitions@2018-01-01-preview' existing = {
  name: '4633458b-17de-408a-b874-0445c86b69e6'
}

resource role_definition 'Microsoft.Authorization/roleDefinitions@2018-01-01-preview' = {
  name: guid(roleName)
  properties: {
    roleName: roleName
    description: 'Allows reading specific secrets in the ${keyVaultName} key vault in ${managementGroupRoot}'
    assignableScopes: assignableScopes
    permissions: key_vault_secrets_user_role_definition.properties.permissions
  }
}

output roleDefinitionId string = role_definition.id

角色定义创建效果很好,并且产生了以下角色定义:

{
    "assignableScopes": [
        "/subscriptions/subscriptionId/resourcegroups/resourceGroupName/providers/Microsoft.KeyVault/vaults/keyVaultName/secrets/secretName",
        "/subscriptions/subscriptionId/resourcegroups/resourceGroupName/providers/Microsoft.KeyVault/vaults/keyVaultName/secrets/anotherSecret"
    ],
    "description": "Allows reading specific secrets in the xxx} key vault in xxx",
    "id": "/subscriptions/xxx/providers/Microsoft.Authorization/roleDefinitions/xxx",
    "name": "c64aa8eb-479d-5c2d-8f25-b1acb151c0af",
    "permissions": [
        {
            "actions": [],
            "dataActions": [
                "Microsoft.KeyVault/vaults/secrets/getSecret/action",
                "Microsoft.KeyVault/vaults/secrets/readMetadata/action"
            ],
            "notActions": [],
            "notDataActions": []
        }
    ],
    "roleName": "Limitied key vault secret reader xxx",
    "roleType": "CustomRole",
    "type": "Microsoft.Authorization/roleDefinitions"
}

接下来,我想将此角色分配给服务主体。这里的细节我并不完全清楚,但由于我希望这个主体能够读取 n 个单独的 secret ,所以我假设我需要迭代可分配的范围.

为此,我有一个主文件:

targetScope = 'managementGroup'

resource roleDefinition 'Microsoft.Authorization/roleDefinitions@2018-01-01-preview' existing = {
  name: roleDefinitionId
}

module example 'module.bicep' = {
    name: 'example-${managementGroup().name}'
    scope: resourceGroup(keyVaultSubscriptionId, keyVaultResourceGroupName)
    params: {
      roleDefinitionId: roleDefinitionId
      assignableScopes: roleDefinition.properties.assignableScopes
      managementGroupName: managementGroup().name
      keyVaultName: keyVaultName
    }
  }

模块看起来像这样:

targetScope = 'resourceGroup'

param roleDefinitionId string
param assignableScopes array = []
param managementGroupName string
param keyVaultName string
param principalId string

// Full scope looks like this:
// '/subscriptions/<sub>/resourcegroups/<rg>/providers/Microsoft.KeyVault/vaults/<vault>/<secret>'
// Hence 8 is the secret name
// Also verifies that the secrets exist
var secretNames = [for scope in assignableScopes: split(scope, '/')[8]]
resource secretResources 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' existing = [for secret in secretNames: {
  name: '${keyVaultName}/${secret}'
}]

// Iterating the secretResources array is not supported, so we iterate the scope which they are based
resource regressionTestKeyVaultReaderAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = [for (scope, index) in assignableScopes: {
  name: guid(managementGroupName, principalId, scope)
  scope: secretResources[index] // Access by index and apply this role assignment to all assignable scopes
  properties: {
    principalId: principalId
    roleDefinitionId: roleDefinitionId
  }
}]

但是,此操作失败并出现以下错误:

ERROR: ***"code": "InvalidTemplate", "message": "Deployment template validation failed: 'The template resource 'exmaple-xxx' at line '97' and column '5' is not valid: Unable to evaluate template language function 'extensionResourceId': function requires exactly two multi-segmented arguments. The first must be the parent resource id while the second must be resource type including resource provider namespace. Current function arguments '/providers/Microsoft.Management/managementGroups/ESD,Microsoft.Authorization/roleDefinitions,/subscriptions/***/providers/Microsoft.Authorization/roleDefinitions/xxx'. Please see https://aka.ms/arm-template-expressions/#extensionresourceid for usage details.. Please see https://aka.ms/arm-template-expressions for usage details.'.", "additionalInfo": [***"type": "TemplateViolation", "info": ***"lineNumber": 97, "linePosition": 5, "path": "properties.template.resources[6]"***]***

我正在使用 az 在 GitHub 管道中部署,因此我尝试访问请求和响应,但无济于事:

$deployment = az deployment mg create | ConvertFrom-Json // additional params 

Write-Host "Request: $(ConvertTo-Json -InputObject $deployment.request)" // Request: null
Write-Host "Response: $(ConvertTo-Json -InputObject $deployment.response)" // Response: null

这个错误对我来说非常神秘,我真的不知道发生了什么,因为我什至没有使用正在引用的实用方法。我猜测向 ARM 的转换会在后台执行一些操作。 vscode 说一切都很好。

我做错了什么?我唯一的猜测是作业的范围部分,但我不知道如何纠正它。

任何帮助将不胜感激。

更新

我在尝试解决此问题时发现的一些其他信息。模板验证失败,部署甚至无法启动。我构建了主文件和模块二头肌文件,看看这是否会提供一些额外的上下文。该模块看起来不错,但 main 的模块资源有错误:

enter image description here

因此,这是在 main 文件中,targetScope = 'managementGroup',而 targetScope = 'resourceGroup' 的模块未显示任何验证构建时出错。

更新2

当编译到 ARM 时,我看到以下值从 main 传递到模块:

"assignableScopes": {
  "value": "[reference(extensionResourceId(managementGroup().id, 'Microsoft.Authorization/roleDefinitions', parameters('secretReaderRoleDefinitionId')), '2018-01-01-preview').assignableScopes]"
},

AFAICT 这是 3 个参数,我在 GitHub 管道中收到的错误是:

Unable to evaluate template language function 'extensionResourceId': function requires exactly two multi-segmented arguments.

阅读 docs 时,情况似乎并非如此。关于该功能。

更新3

该错误是在我在 ubuntu-latest 上运行的 GitHub 管道中产生的。我将在本地复制相同的命令,看看是否可以在出现运行程序问题时让它在这里工作。

更新4

在 GitHub 管道之外重现了完全相同的错误。

最佳答案

一些想法...

  1. 从安全角度来看,创建具有有限可分配范围的自定义 roleDef 并没有太多值(value),因为内置的 roleDef 具有相同的权限,具有更广泛的范围 - 并且分配权限的主体将是能够分配另一个。

  2. 如果您的目标是简单地迭代 secret 并将角色分配给这些 secret ,那么您所需要的只是这些 secret 的 ResourceId。看起来您正在尝试从 roleDefinition 中提取该列表(而不是传递给模板),这是可能的,但看起来有些复杂。这意味着每当您想要“调整”此部署时,您都必须定义新角色或修改现有角色,两者都会产生一些下游后果。租户中可以定义的自定义角色数量有限,当您更改它们时,您可能会无意中破坏现有分配(删除访问权限或无意中授予新角色访问权限)。

也就是说,我在您的代码中没有看到该特定错误,但也许还有其他一些错误 - 试试这个:

main.bicep

targetScope = 'managementGroup'

param roleDefinitionId string
param keyVaultSubscriptionId string
param keyVaultResourceGroupName string
param keyVaultName string
param principalId string

resource roleDefinition 'Microsoft.Authorization/roleDefinitions@2018-01-01-preview' existing = {
  scope: subscription(keyVaultSubscriptionId)
  name: roleDefinitionId
}

module example 'module.bicep' = {
    name: 'example-${managementGroup().name}'
    scope: resourceGroup(keyVaultSubscriptionId, keyVaultResourceGroupName)
    params: {
      roleDefinitionId: roleDefinitionId
      assignableScopes: roleDefinition.properties.assignableScopes
      keyVaultName: keyVaultName
      principalId: principalId
    }
  }

module.bicep

targetScope = 'resourceGroup'

param roleDefinitionId string
param assignableScopes array
param keyVaultName string
param principalId string

var secretNames = [for scope in assignableScopes: last(split(scope, '/'))]
resource secretResources 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' existing = [for secret in secretNames: {
  name: '${keyVaultName}/${secret}'
}]

resource roleDef 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
  name: roleDefinitionId
}

resource regressionTestKeyVaultReaderAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = [for (scope, index) in assignableScopes: {
  name: guid(roleDef.id, principalId, scope)
  scope: secretResources[index]
  properties: {
    principalId: principalId
    roleDefinitionId: roleDef.id
  }
}]

关于azure - 使用二头肌的 secret 范围角色定义和分配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73884372/

相关文章:

azure - 有关长期运行的 Azure 部署的指南

c# - Task.WaitAll 因异步/等待任务而挂起

c# - [Azure 数据目录] : Read the data from Azure data catalog glossary Unauthorized error?

azure - 使用网络角色和辅助角色

azure - 在不同环境之间移动突触工作空间的不同选项

azure - 使用二头肌创建 Azure ADF SHIR

Azure 自定义日志正式发布日期

azure - 尝试了解 Azure Durable Function 上下文

postgresql - Azure Database for PostgreSQL 灵活服务器部署失败,并出现数据库名称参数错误

azure-resource-manager - 在 ARM 模板中将平台设置为 64 位