Reverted multiple-scope due to complexity introduced. Added ability to restrict assignments to ServicePrincipal only.
This commit is contained in:
@@ -12,7 +12,7 @@ The constrained RBAC Administrator assignment is created only when `delegable_ro
|
||||
module "iam" {
|
||||
source = "../modules/simple-iam"
|
||||
|
||||
scopes = [data.azurerm_subscription.current.id]
|
||||
scope = data.azurerm_subscription.current.id
|
||||
principal_id = azuread_service_principal.sp.object_id
|
||||
|
||||
roles = [
|
||||
@@ -32,9 +32,9 @@ module "iam" {
|
||||
|
||||
## Inputs
|
||||
|
||||
- `scopes` (list(string)): Scope IDs at which to assign roles.
|
||||
- `scope` (string): Scope ID at which to assign roles.
|
||||
- `principal_id` (string): Object ID of the principal.
|
||||
- `roles` (list(string)): Unconditional role definition names to assign at each scope in `scopes`.
|
||||
- `roles` (list(string)): Unconditional role definition names to assign.
|
||||
- `delegable_roles` (list(string)): Role definition names allowed by the constrained RBAC Admin condition. When empty, RBAC Admin is not assigned.
|
||||
- `principal_type` (string): Passed to `azurerm_role_assignment.principal_type`.
|
||||
- `delegable_roles_to_sp_only` (bool): When true, RBAC Admin delegation can only assign/delete roles to principals of type ServicePrincipal.
|
||||
@@ -42,5 +42,5 @@ module "iam" {
|
||||
## Outputs
|
||||
|
||||
- `role_assignment_ids` (map(string))
|
||||
- `rbac_admin_role_assignment_id` (map(string))
|
||||
- `rbac_admin_role_assignment_id` (string|null)
|
||||
- `rbac_admin_condition` (string|null)
|
||||
|
||||
64
main.tf
64
main.tf
@@ -1,45 +1,12 @@
|
||||
locals {
|
||||
lookup_scope = var.scopes[0]
|
||||
|
||||
allowed_role_definition_ids_list = join(", ", [
|
||||
for name in var.delegable_roles :
|
||||
basename(data.azurerm_role_definition.allowed_for_rbac_admin_condition[name].id)
|
||||
basename(data.azurerm_role_definition.delegable[name].id)
|
||||
])
|
||||
|
||||
role_assignments = {
|
||||
for entry in flatten([
|
||||
for scope in var.scopes : [
|
||||
for role in var.roles : {
|
||||
key = "${scope}:${role}"
|
||||
scope = scope
|
||||
role = role
|
||||
}
|
||||
]
|
||||
]) :
|
||||
entry.key => {
|
||||
scope = entry.scope
|
||||
role = entry.role
|
||||
}
|
||||
}
|
||||
|
||||
rbac_admin_write_constraint_role_definition_ids = "@Request[Microsoft.Authorization/roleAssignments:RoleDefinitionId] ForAnyOfAnyValues:GuidEquals {${local.allowed_role_definition_ids_list}}"
|
||||
rbac_admin_delete_constraint_role_definition_ids = "@Resource[Microsoft.Authorization/roleAssignments:RoleDefinitionId] ForAnyOfAnyValues:GuidEquals {${local.allowed_role_definition_ids_list}}"
|
||||
|
||||
rbac_admin_write_constraint_principal_type = "@Request[Microsoft.Authorization/roleAssignments:PrincipalType] ForAnyOfAnyValues:StringEquals {'ServicePrincipal'}"
|
||||
rbac_admin_write_constraint_principal_type = "@Request[Microsoft.Authorization/roleAssignments:PrincipalType] ForAnyOfAnyValues:StringEquals {'ServicePrincipal'}"
|
||||
rbac_admin_delete_constraint_principal_type = "@Resource[Microsoft.Authorization/roleAssignments:PrincipalType] ForAnyOfAnyValues:StringEquals {'ServicePrincipal'}"
|
||||
|
||||
rbac_admin_write_constraint = (
|
||||
var.delegable_roles_to_sp_only ?
|
||||
"(${local.rbac_admin_write_constraint_role_definition_ids} AND ${local.rbac_admin_write_constraint_principal_type})" :
|
||||
"(${local.rbac_admin_write_constraint_role_definition_ids})"
|
||||
)
|
||||
|
||||
rbac_admin_delete_constraint = (
|
||||
var.delegable_roles_to_sp_only ?
|
||||
"(${local.rbac_admin_delete_constraint_role_definition_ids} AND ${local.rbac_admin_delete_constraint_principal_type})" :
|
||||
"(${local.rbac_admin_delete_constraint_role_definition_ids})"
|
||||
)
|
||||
|
||||
rbac_admin_condition = <<-EOT
|
||||
(
|
||||
(
|
||||
@@ -47,7 +14,10 @@ locals {
|
||||
)
|
||||
OR
|
||||
(
|
||||
${local.rbac_admin_write_constraint}
|
||||
(
|
||||
@Request[Microsoft.Authorization/roleAssignments:RoleDefinitionId] ForAnyOfAnyValues:GuidEquals {${local.allowed_role_definition_ids_list}}
|
||||
)
|
||||
${var.delegable_roles_to_sp_only ? "AND\n (${local.rbac_admin_write_constraint_principal_type})" : ""}
|
||||
)
|
||||
)
|
||||
AND
|
||||
@@ -57,7 +27,10 @@ locals {
|
||||
)
|
||||
OR
|
||||
(
|
||||
${local.rbac_admin_delete_constraint}
|
||||
(
|
||||
@Resource[Microsoft.Authorization/roleAssignments:RoleDefinitionId] ForAnyOfAnyValues:GuidEquals {${local.allowed_role_definition_ids_list}}
|
||||
)
|
||||
${var.delegable_roles_to_sp_only ? "AND\n (${local.rbac_admin_delete_constraint_principal_type})" : ""}
|
||||
)
|
||||
)
|
||||
EOT
|
||||
@@ -67,23 +40,22 @@ data "azurerm_role_definition" "rbac_admin" {
|
||||
for_each = length(var.delegable_roles) > 0 ? { this = true } : {}
|
||||
|
||||
name = "Role Based Access Control Administrator"
|
||||
scope = local.lookup_scope
|
||||
scope = var.scope
|
||||
}
|
||||
|
||||
data "azurerm_role_definition" "allowed_for_rbac_admin_condition" {
|
||||
|
||||
data "azurerm_role_definition" "delegable" {
|
||||
for_each = toset(var.delegable_roles)
|
||||
|
||||
name = each.value
|
||||
scope = local.lookup_scope
|
||||
scope = var.scope
|
||||
}
|
||||
|
||||
resource "azurerm_role_assignment" "role" {
|
||||
|
||||
for_each = local.role_assignments
|
||||
for_each = toset(var.roles)
|
||||
|
||||
scope = each.value.scope
|
||||
role_definition_name = each.value.role
|
||||
scope = var.scope
|
||||
role_definition_name = each.value
|
||||
principal_id = var.principal_id
|
||||
principal_type = var.principal_type
|
||||
skip_service_principal_aad_check = true
|
||||
@@ -91,9 +63,9 @@ resource "azurerm_role_assignment" "role" {
|
||||
|
||||
resource "azurerm_role_assignment" "rbac_admin" {
|
||||
|
||||
for_each = length(var.delegable_roles) > 0 ? toset(var.scopes) : toset([])
|
||||
for_each = length(var.delegable_roles) > 0 ? { this = true } : {}
|
||||
|
||||
scope = each.value
|
||||
scope = var.scope
|
||||
role_definition_id = data.azurerm_role_definition.rbac_admin["this"].id # Role Based Access Control Administrator
|
||||
principal_id = var.principal_id
|
||||
principal_type = var.principal_type
|
||||
|
||||
10
outputs.tf
10
outputs.tf
@@ -1,14 +1,14 @@
|
||||
output "role_assignment_ids" {
|
||||
value = { for key, ra in azurerm_role_assignment.role : key => ra.id }
|
||||
description = "IDs of unconditional role assignments, keyed by '${scope}:${role_definition_name}'."
|
||||
value = { for role, ra in azurerm_role_assignment.role : role => ra.id }
|
||||
description = "IDs of unconditional role assignments, keyed by role definition name."
|
||||
}
|
||||
|
||||
output "rbac_admin_role_assignment_id" {
|
||||
value = { for scope, ra in azurerm_role_assignment.rbac_admin : scope => ra.id }
|
||||
description = "IDs of constrained RBAC Administrator role assignments, keyed by scope. Empty when delegable_roles is empty."
|
||||
value = length(azurerm_role_assignment.rbac_admin) > 0 ? azurerm_role_assignment.rbac_admin["this"].id : null
|
||||
description = "ID of the constrained RBAC Administrator role assignment, or null when delegable_roles is empty."
|
||||
}
|
||||
|
||||
output "rbac_admin_condition" {
|
||||
value = length(var.delegable_roles) > 0 ? local.rbac_admin_condition : null
|
||||
description = "Rendered condition used for constrained RBAC Administrator assignments, or null when delegable_roles is empty."
|
||||
description = "Rendered condition used for constrained RBAC Administrator assignment, or null when delegable_roles is empty."
|
||||
}
|
||||
|
||||
16
variables.tf
16
variables.tf
@@ -1,14 +1,10 @@
|
||||
variable "scopes" {
|
||||
type = list(string)
|
||||
description = "Scope IDs at which to assign roles (subscription, resource group, resource, etc.)."
|
||||
variable "scope" {
|
||||
type = string
|
||||
description = "Scope ID at which to assign roles (subscription, resource group, resource, etc.)."
|
||||
|
||||
validation {
|
||||
condition = (
|
||||
length(var.scopes) > 0 &&
|
||||
alltrue([for scope in var.scopes : scope != null && trimspace(scope) != ""]) &&
|
||||
length(distinct(var.scopes)) == length(var.scopes)
|
||||
)
|
||||
error_message = "scopes must be a non-empty list of unique, non-empty strings."
|
||||
condition = var.scope != null && trimspace(var.scope) != ""
|
||||
error_message = "scope must be a non-empty string."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +16,7 @@ variable "principal_id" {
|
||||
variable "roles" {
|
||||
type = list(string)
|
||||
default = []
|
||||
description = "Unconditional role definition names to assign to principal_id at each scope in scopes."
|
||||
description = "Unconditional role definition names to assign to principal_id at scope."
|
||||
|
||||
validation {
|
||||
condition = length(distinct(var.roles)) == length(var.roles)
|
||||
|
||||
Reference in New Issue
Block a user