diff --git a/modules/aws/identity/identity-center/locals.tf b/modules/aws/identity/identity-center/locals.tf new file mode 100644 index 0000000..5145e5e --- /dev/null +++ b/modules/aws/identity/identity-center/locals.tf @@ -0,0 +1,123 @@ +locals { + # Retrieve the first SSO instance ARN and identity store ID + ssoadmin_instance_arn = tolist(data.aws_ssoadmin_instances.sso_instance.arns)[0] + sso_instance_id = tolist(data.aws_ssoadmin_instances.sso_instance.identity_store_ids)[0] + + # Filter permission sets based on the presence of different policy types + aws_managed_permission_sets = { + for pset_name, pset_index in var.permission_sets : + pset_name => pset_index + if length(coalesce(pset_index.aws_managed_policies, [])) > 0 + } + + inline_policy_permission_sets = { + for pset_name, pset_index in var.permission_sets : + pset_name => pset_index + if can(pset_index.inline_policy) + } + + permissions_boundary_aws_managed_permission_sets = { + for pset_name, pset_index in var.permission_sets : + pset_name => pset_index + if can(pset_index.permissions_boundary.managed_policy_arn) + } + + permissions_boundary_customer_managed_permission_sets = { + for pset_name, pset_index in var.permission_sets : + pset_name => pset_index + if can(pset_index.permissions_boundary.customer_managed_policy_reference) + } + + customer_managed_permission_sets = { + for pset_name, pset_index in var.permission_sets : + pset_name => pset_index + if length(coalesce(pset_index.customer_managed_policies, [])) > 0 + } + + # Map AWS managed policies to permission sets + pset_aws_managed_policy_maps = flatten([ + for pset_name, pset_index in local.aws_managed_permission_sets : [ + for policy in pset_index.aws_managed_policies : { + pset_name = pset_name + policy_arn = policy + } if can(policy) + ] + ]) + + # Map customer managed policies to permission sets + pset_customer_managed_policy_maps = flatten([ + for pset_name, pset_index in local.customer_managed_permission_sets : [ + for policy in pset_index.customer_managed_policies : { + pset_name = pset_name + policy_name = policy.name + path = policy.path + } if can(policy) + ] + ]) + + # Map inline policies to permission sets + pset_inline_policy_maps = flatten([ + for pset_name, pset_index in local.inline_policy_permission_sets : [ + { + pset_name = pset_name + inline_policy = pset_index.inline_policy + } + ] + ]) + + # Map AWS managed permissions boundaries to permission sets + pset_permissions_boundary_aws_managed_maps = flatten([ + for pset_name, pset_index in local.permissions_boundary_aws_managed_permission_sets : [ + { + pset_name = pset_name + boundary = { + managed_policy_arn = pset_index.permissions_boundary.managed_policy_arn + } + } + ] + ]) + + # Map customer managed permissions boundaries to permission sets + pset_permissions_boundary_customer_managed_maps = flatten([ + for pset_name, pset_index in local.permissions_boundary_customer_managed_permission_sets : [ + { + pset_name = pset_name + boundary = { + customer_managed_policy_reference = pset_index.permissions_boundary.customer_managed_policy_reference + } + } + ] + ]) + + # Filter active accounts from the organization + active_accounts = [ + for account in data.aws_organizations_organization.organization.accounts : + account if account.status == "ACTIVE" + ] + + # Map account names to IDs for active accounts + accounts_ids_maps = { + for account in local.active_accounts : + account.name => account.id + } + + # Flatten account assignments data + flatten_account_assignment_data = flatten([ + for this_assignment in keys(var.account_assignments) : [ + for account in var.account_assignments[this_assignment].account_ids : [ + for pset in var.account_assignments[this_assignment].permission_sets : { + permission_set = pset + principal_name = var.account_assignments[this_assignment].principal_name + principal_type = var.account_assignments[this_assignment].principal_type + account_id = length(regexall("[0-9]{12}", account)) > 0 ? account : lookup(local.accounts_ids_maps, account, null) + } + ] + ] + ]) + + # Map principals and their account assignments + principals_and_their_account_assignments = { + for s in local.flatten_account_assignment_data : + format("Type:%s__Principal:%s__Permission:%s__Account:%s", s.principal_type, s.principal_name, s.permission_set, s.account_id) => s + } +} diff --git a/modules/aws/identity/identity-center/main.tf b/modules/aws/identity/identity-center/main.tf new file mode 100644 index 0000000..d594bc8 --- /dev/null +++ b/modules/aws/identity/identity-center/main.tf @@ -0,0 +1,23 @@ +data "aws_ssoadmin_instances" "sso_instance" {} +data "aws_organizations_organization" "organization" {} + +resource "aws_identitystore_group" "sso_groups" { + for_each = var.sso_groups == null ? {} : var.sso_groups + identity_store_id = local.sso_instance_id + display_name = each.value.group_name + description = each.value.group_description +} + +resource "aws_ssoadmin_account_assignment" "account_assignment" { + for_each = local.principals_and_their_account_assignments + + instance_arn = local.ssoadmin_instance_arn + permission_set_arn = aws_ssoadmin_permission_set.pset[each.value.permission_set].arn + + principal_type = each.value.principal_type + + principal_id = aws_identitystore_group.sso_groups[each.value.principal_name].group_id + + target_id = each.value.account_id + target_type = "AWS_ACCOUNT" +} diff --git a/modules/aws/identity/identity-center/outputs.tf b/modules/aws/identity/identity-center/outputs.tf new file mode 100644 index 0000000..30b3035 --- /dev/null +++ b/modules/aws/identity/identity-center/outputs.tf @@ -0,0 +1,15 @@ +output "account_assignment_data" { + value = local.flatten_account_assignment_data + description = "Tuple containing account assignment data" + +} + +output "principals_and_assignments" { + value = local.principals_and_their_account_assignments + description = "Map containing account assignment data" +} + +output "sso_groups_ids" { + value = { for k, v in aws_identitystore_group.sso_groups : k => v.group_id } + description = "A map of SSO groups ids created by this module" +} diff --git a/modules/aws/identity/identity-center/permission_set.tf b/modules/aws/identity/identity-center/permission_set.tf new file mode 100644 index 0000000..da9e6af --- /dev/null +++ b/modules/aws/identity/identity-center/permission_set.tf @@ -0,0 +1,66 @@ +resource "aws_ssoadmin_permission_set" "pset" { + for_each = var.permission_sets + name = each.key + + instance_arn = local.ssoadmin_instance_arn + description = lookup(each.value, "description", null) + relay_state = lookup(each.value, "relay_state", null) + session_duration = lookup(each.value, "session_duration", null) + tags = merge(lookup(each.value, "tags", {}), var.tags_all) +} + + +resource "aws_ssoadmin_managed_policy_attachment" "pset_aws_managed_policy" { + for_each = { for pset in local.pset_aws_managed_policy_maps : "${pset.pset_name}.${pset.policy_arn}" => pset } + + instance_arn = local.ssoadmin_instance_arn + managed_policy_arn = each.value.policy_arn + permission_set_arn = aws_ssoadmin_permission_set.pset[each.value.pset_name].arn + + depends_on = [aws_ssoadmin_account_assignment.account_assignment] +} + + +resource "aws_ssoadmin_customer_managed_policy_attachment" "pset_customer_managed_policy" { + for_each = { for pset in local.pset_customer_managed_policy_maps : "${pset.pset_name}.${pset.policy_name}" => pset } + + instance_arn = local.ssoadmin_instance_arn + permission_set_arn = aws_ssoadmin_permission_set.pset[each.value.pset_name].arn + customer_managed_policy_reference { + name = each.value.policy_name + path = each.value.path + } + +} + +resource "aws_ssoadmin_permission_set_inline_policy" "pset_inline_policy" { + for_each = { for pset in local.pset_inline_policy_maps : pset.pset_name => pset if can(pset.inline_policy) } + + inline_policy = each.value.inline_policy + instance_arn = local.ssoadmin_instance_arn + permission_set_arn = aws_ssoadmin_permission_set.pset[each.key].arn +} + +resource "aws_ssoadmin_permissions_boundary_attachment" "pset_permissions_boundary_aws_managed" { + for_each = { for pset in local.pset_permissions_boundary_aws_managed_maps : pset.pset_name => pset if can(pset.boundary.managed_policy_arn) } + + instance_arn = local.ssoadmin_instance_arn + permission_set_arn = aws_ssoadmin_permission_set.pset[each.key].arn + permissions_boundary { + managed_policy_arn = each.value.boundary.managed_policy_arn + } +} + +resource "aws_ssoadmin_permissions_boundary_attachment" "pset_permissions_boundary_customer_managed" { + for_each = { for pset in local.pset_permissions_boundary_customer_managed_maps : pset.pset_name => pset if can(pset.boundary.customer_managed_policy_reference) } + + instance_arn = local.ssoadmin_instance_arn + permission_set_arn = aws_ssoadmin_permission_set.pset[each.key].arn + permissions_boundary { + customer_managed_policy_reference { + name = each.value.boundary.customer_managed_policy_reference.name + path = can(each.value.boundary.customer_managed_policy_reference.path) ? each.value.boundary.customer_managed_policy_reference.path : "/" + } + + } +} diff --git a/modules/aws/identity/identity-center/variables.tf b/modules/aws/identity/identity-center/variables.tf new file mode 100644 index 0000000..8f435b9 --- /dev/null +++ b/modules/aws/identity/identity-center/variables.tf @@ -0,0 +1,31 @@ +variable "sso_groups" { + description = "Names of the groups you wish to create in IAM Identity Center." + type = map(object({ + group_name = string + group_description = optional(string, null) + })) + default = {} +} + +variable "permission_sets" { + description = "Permission Sets that you wish to create in IAM Identity Center. This variable is a map of maps containing Permission Set names as keys. See permission_sets description in README for information about map values." + type = any + default = {} +} + +variable "account_assignments" { + description = "List of maps containing mapping between user/group, permission set and assigned accounts list. See account_assignments description in README for more information about map values." + type = map(object({ + principal_name = string + principal_type = optional(string, "GROUP") + permission_sets = list(string) + account_ids = list(string) + })) + default = {} +} + +variable "tags_all" { + description = "A map of tags to add to all resources." + type = map(string) + default = {} +} diff --git a/modules/aws/identity/identity-center/versions.tf b/modules/aws/identity/identity-center/versions.tf new file mode 100644 index 0000000..9416453 --- /dev/null +++ b/modules/aws/identity/identity-center/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">=1.3" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">=4.0" + } + } +}