From c6f8915eab40497c869d76887ab6e71a5df09e8d Mon Sep 17 00:00:00 2001 From: hacker65536 Date: Tue, 19 Mar 2024 16:55:53 +0900 Subject: [PATCH 01/19] support inline_policy --- locals.tf | 25 ++++++++++--------------- main.tf | 13 ++++++------- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/locals.tf b/locals.tf index a1e3d54..b398dac 100644 --- a/locals.tf +++ b/locals.tf @@ -32,11 +32,7 @@ locals { # pset_index is the corresponding index of the map of maps (which is the variable permission_sets) aws_managed_permission_sets = { for pset_name, pset_index in var.permission_sets : pset_name => pset_index if can(pset_index.aws_managed_policies) } customer_managed_permission_sets = { for pset_name, pset_index in var.permission_sets : pset_name => pset_index if can(pset_index.customer_managed_policies) } - - # ! NOT CURRENTLY SUPPORTED ! - # inline_policy_permission_sets = { for pset_name, pset_index in var.permission_sets : pset_name => pset_index if can(pset_index.inline_policy) } - - + inline_policy_permission_sets = { for pset_name, pset_index in var.permission_sets : pset_name => pset_index if can(pset_index.inline_policy) } # When using the 'for' expression in Terraform: # [ and ] produces a tuple @@ -67,17 +63,16 @@ locals { ] ]) - # ! NOT CURRENTLY SUPPORTED ! # - Inline Policy - - # pset_inline_policy_maps = flatten([ - # for pset_name, pset_index in local.inline_policy_permission_sets : [ - # for policy in pset_index.inline_policy : { - # pset_name = pset_name - # inline_policy = policy - # # path = path - # } if pset_index.inline_policy != null && can(pset_index.inline_policy) - # ] - # ]) + 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 + } + ] + ]) + } diff --git a/main.tf b/main.tf index d2989c7..a24a578 100644 --- a/main.tf +++ b/main.tf @@ -186,15 +186,14 @@ resource "aws_ssoadmin_customer_managed_policy_attachment" "pset_customer_manage } -# ! NOT CURRENTLY SUPPORTED ! # - Inline Policy - -# resource "aws_ssoadmin_permission_set_inline_policy" "pset_inline_policy" { -# for_each = { for pset_name, pset_index in var.permission_sets : pset_name => pset_index if can(pset_index.inline_policy) } +resource "aws_ssoadmin_permission_set_inline_policy" "pset_inline_policy" { + for_each = { for pset in local.pset_inline_policy_maps : pset.pset_name => pset } -# inline_policy = each.value.inline_policy[0] -# instance_arn = local.ssoadmin_instance_arn -# permission_set_arn = aws_ssoadmin_permission_set.pset[each.key].arn -# } + 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_account_assignment" "account_assignment" { for_each = local.principals_and_their_account_assignments // for_each arguement must be a map, or set of strings. Tuples won't work From df8006f5d9ffb55a8266358a96ece616a2c18c09 Mon Sep 17 00:00:00 2001 From: hacker65536 Date: Tue, 19 Mar 2024 17:25:59 +0900 Subject: [PATCH 02/19] add README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index dd3b1a5..f9d5436 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,7 @@ No modules. | [aws_ssoadmin_customer_managed_policy_attachment.pset_customer_managed_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_customer_managed_policy_attachment) | resource | | [aws_ssoadmin_managed_policy_attachment.pset_aws_managed_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_managed_policy_attachment) | resource | | [aws_ssoadmin_permission_set.pset](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_permission_set) | resource | +| [aws_ssoadmin_permission_set_inline_policy.pset_inline_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_permission_set_inline_policy) | resource | | [aws_identitystore_group.existing_sso_groups](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/identitystore_group) | data source | | [aws_identitystore_group.identity_store_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/identitystore_group) | data source | | [aws_identitystore_user.existing_sso_users](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/identitystore_user) | data source | From 27b2306df2ea5e1005888aa8db710cd3bd625a7b Mon Sep 17 00:00:00 2001 From: hacker65536 Date: Wed, 20 Mar 2024 06:32:50 +0900 Subject: [PATCH 03/19] fix data source and enable account name --- data.tf | 7 +++++-- locals.tf | 25 ++++++++++++++++++++++++- main.tf | 2 +- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/data.tf b/data.tf index 148a449..d905e5a 100644 --- a/data.tf +++ b/data.tf @@ -111,9 +111,12 @@ data "aws_identitystore_user" "identity_store_user" { # } data "aws_ssoadmin_permission_set" "existing_permission_sets" { - for_each = local.principals_and_their_account_assignments + for_each = toset(local.existing_permission_sets) instance_arn = local.ssoadmin_instance_arn - name = each.value.permission_set + name = each.value // Prevents failure if data fetch is attempted before Permission Sets are created depends_on = [aws_ssoadmin_permission_set.pset] } + + +data "aws_organizations_organization" "organization" {} diff --git a/locals.tf b/locals.tf index b398dac..a0380d0 100644 --- a/locals.tf +++ b/locals.tf @@ -79,6 +79,19 @@ locals { # - Account Assignments - locals { + accounts_non_master_ids_maps = { + for idx, account in data.aws_organizations_organization.organization.non_master_accounts : + account.name => account.id + // if account.status == "ACTIVE" && can(data.aws_organizations_organization.organization.non_master_accounts) + } + + accounts_ids_maps = merge( + { + // "${data.aws_organizations_organization.organization.master_account_name}"= "${data.aws_organizations_organization.organization.master_account_id}" = + + }, + local.accounts_non_master_ids_maps + ) # Create a new local variable by flattening the complex type given in the variable "account_assignments" # This will be a 'tuple' flatten_account_assignment_data = flatten([ @@ -88,7 +101,7 @@ locals { permission_set = pset principal_name = var.account_assignments[this_assignment].principal_name principal_type = var.account_assignments[this_assignment].principal_type - account_id = account + account_id = length(regexall("[0-9]{12}", account)) > 0 ? account : lookup(local.accounts_ids_maps, account, null) } ] ] @@ -101,6 +114,9 @@ locals { 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 } + existing_permission_sets = distinct([ + for pset in local.principals_and_their_account_assignments : pset.permission_set + ]) # iterates over account_assignents, sets that to be assignment.principal_name ONLY if the assignment.principal_type #is GROUP. Essentially stores all the possible 'assignments' (account assignments) that would be attached to a user group @@ -112,4 +128,11 @@ locals { # 'account_assignments_for_users' is effectively a list of principal names where the account type is USER account_assignments_for_users = [for assignment in var.account_assignments : assignment.principal_name if assignment.principal_type == "USER"] + + # account_info mappings + +} + +output "bbb" { + value = local.accounts_ids_maps } diff --git a/main.tf b/main.tf index a24a578..41f928e 100644 --- a/main.tf +++ b/main.tf @@ -199,7 +199,7 @@ resource "aws_ssoadmin_account_assignment" "account_assignment" { for_each = local.principals_and_their_account_assignments // for_each arguement must be a map, or set of strings. Tuples won't work instance_arn = local.ssoadmin_instance_arn - permission_set_arn = data.aws_ssoadmin_permission_set.existing_permission_sets[each.key].arn + permission_set_arn = data.aws_ssoadmin_permission_set.existing_permission_sets[each.value.permission_set].arn principal_id = each.value.principal_type == "GROUP" ? data.aws_identitystore_group.identity_store_group[each.value.principal_name].id : data.aws_identitystore_user.identity_store_user[each.value.principal_name].id principal_type = each.value.principal_type From c10c422bb597e2764571ad95a6812d6cb18c5ce4 Mon Sep 17 00:00:00 2001 From: hacker65536 Date: Wed, 20 Mar 2024 06:34:55 +0900 Subject: [PATCH 04/19] fix README --- README.md | 1 + locals.tf | 10 +--------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index f9d5436..dbe6514 100644 --- a/README.md +++ b/README.md @@ -144,6 +144,7 @@ No modules. | [aws_identitystore_group.identity_store_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/identitystore_group) | data source | | [aws_identitystore_user.existing_sso_users](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/identitystore_user) | data source | | [aws_identitystore_user.identity_store_user](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/identitystore_user) | data source | +| [aws_organizations_organization.organization](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/organizations_organization) | data source | | [aws_ssoadmin_instances.sso_instance](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssoadmin_instances) | data source | | [aws_ssoadmin_permission_set.existing_permission_sets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssoadmin_permission_set) | data source | diff --git a/locals.tf b/locals.tf index a0380d0..2528838 100644 --- a/locals.tf +++ b/locals.tf @@ -79,16 +79,15 @@ locals { # - Account Assignments - locals { + # account_info mappings accounts_non_master_ids_maps = { for idx, account in data.aws_organizations_organization.organization.non_master_accounts : account.name => account.id // if account.status == "ACTIVE" && can(data.aws_organizations_organization.organization.non_master_accounts) } - accounts_ids_maps = merge( { // "${data.aws_organizations_organization.organization.master_account_name}"= "${data.aws_organizations_organization.organization.master_account_id}" = - }, local.accounts_non_master_ids_maps ) @@ -128,11 +127,4 @@ locals { # 'account_assignments_for_users' is effectively a list of principal names where the account type is USER account_assignments_for_users = [for assignment in var.account_assignments : assignment.principal_name if assignment.principal_type == "USER"] - - # account_info mappings - -} - -output "bbb" { - value = local.accounts_ids_maps } From ce1a0d12a4082e1a9fad77707b6e1e20794f565f Mon Sep 17 00:00:00 2001 From: hacker65536 Date: Tue, 9 Apr 2024 15:17:15 +0900 Subject: [PATCH 05/19] fix --- main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.tf b/main.tf index 41f928e..fd4a9c3 100644 --- a/main.tf +++ b/main.tf @@ -201,7 +201,7 @@ resource "aws_ssoadmin_account_assignment" "account_assignment" { instance_arn = local.ssoadmin_instance_arn permission_set_arn = data.aws_ssoadmin_permission_set.existing_permission_sets[each.value.permission_set].arn - principal_id = each.value.principal_type == "GROUP" ? data.aws_identitystore_group.identity_store_group[each.value.principal_name].id : data.aws_identitystore_user.identity_store_user[each.value.principal_name].id + principal_id = each.value.principal_type == "GROUP" ? (aws_identitystore_group.sso_groups[each.value.principal_name].group_id != null ? aws_identitystore_group.sso_groups[each.value.principal_name].group_id : data.aws_identitystore_group.identity_store_group[each.value.principal_name].id) : (aws_identitystore_user.sso_users[each.value.principal_name].user_id != null ? aws_identitystore_user.sso_users[each.value.principal_name].user_id : data.aws_identitystore_user.identity_store_user[each.value.principal_name].id) principal_type = each.value.principal_type target_id = each.value.account_id From 620b386e2e31e8f5c63ab2575bb2e1f2ad9fd26e Mon Sep 17 00:00:00 2001 From: hacker65536 Date: Mon, 20 May 2024 13:37:35 +0900 Subject: [PATCH 06/19] refactoring --- locals.tf | 37 ++++++++++++++++++++++++++++++++----- main.tf | 30 ++++++++++++++++++++++++++++-- outputs.tf | 31 +++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 7 deletions(-) diff --git a/locals.tf b/locals.tf index 2528838..a5918e2 100644 --- a/locals.tf +++ b/locals.tf @@ -30,9 +30,14 @@ locals { # pset_name is the attribute name for each permission set map/object # pset_index is the corresponding index of the map of maps (which is the variable permission_sets) - aws_managed_permission_sets = { for pset_name, pset_index in var.permission_sets : pset_name => pset_index if can(pset_index.aws_managed_policies) } - customer_managed_permission_sets = { for pset_name, pset_index in var.permission_sets : pset_name => pset_index if can(pset_index.customer_managed_policies) } - inline_policy_permission_sets = { for pset_name, pset_index in var.permission_sets : pset_name => pset_index if can(pset_index.inline_policy) } + aws_managed_permission_sets = { for pset_name, pset_index in var.permission_sets : pset_name => pset_index if can(pset_index.aws_managed_policies) } + customer_managed_permission_sets = { for pset_name, pset_index in var.permission_sets : pset_name => pset_index if can(pset_index.customer_managed_policies) } + 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) } + + + # When using the 'for' expression in Terraform: # [ and ] produces a tuple @@ -73,13 +78,31 @@ locals { ] ]) + # - Permissions boundary - + 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 = pset_index.permissions_boundary.managed_policy_arn + } + ] + ]) + + 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 = pset_index.permissions_boundary.customer_managed_policy_reference + } + ] + ]) } # - Account Assignments - locals { - # account_info mappings + accounts_non_master_ids_maps = { for idx, account in data.aws_organizations_organization.organization.non_master_accounts : account.name => account.id @@ -87,10 +110,12 @@ locals { } accounts_ids_maps = merge( { - // "${data.aws_organizations_organization.organization.master_account_name}"= "${data.aws_organizations_organization.organization.master_account_id}" = + // require terraform-provider-aws v5.46.0 + "${data.aws_organizations_organization.organization.master_account_name}" = "${data.aws_organizations_organization.organization.master_account_id}" }, local.accounts_non_master_ids_maps ) + # Create a new local variable by flattening the complex type given in the variable "account_assignments" # This will be a 'tuple' flatten_account_assignment_data = flatten([ @@ -113,10 +138,12 @@ locals { 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 } + existing_permission_sets = distinct([ for pset in local.principals_and_their_account_assignments : pset.permission_set ]) + # iterates over account_assignents, sets that to be assignment.principal_name ONLY if the assignment.principal_type #is GROUP. Essentially stores all the possible 'assignments' (account assignments) that would be attached to a user group diff --git a/main.tf b/main.tf index fd4a9c3..d8eeef7 100644 --- a/main.tf +++ b/main.tf @@ -188,20 +188,46 @@ resource "aws_ssoadmin_customer_managed_policy_attachment" "pset_customer_manage # - Inline Policy - resource "aws_ssoadmin_permission_set_inline_policy" "pset_inline_policy" { - for_each = { for pset in local.pset_inline_policy_maps : pset.pset_name => pset } + 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 } +# - Permissions Boundary - +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) } + + 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 + + } +} + +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) } + + 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 + path = "/" + } + + } +} + resource "aws_ssoadmin_account_assignment" "account_assignment" { for_each = local.principals_and_their_account_assignments // for_each arguement must be a map, or set of strings. Tuples won't work instance_arn = local.ssoadmin_instance_arn permission_set_arn = data.aws_ssoadmin_permission_set.existing_permission_sets[each.value.permission_set].arn - principal_id = each.value.principal_type == "GROUP" ? (aws_identitystore_group.sso_groups[each.value.principal_name].group_id != null ? aws_identitystore_group.sso_groups[each.value.principal_name].group_id : data.aws_identitystore_group.identity_store_group[each.value.principal_name].id) : (aws_identitystore_user.sso_users[each.value.principal_name].user_id != null ? aws_identitystore_user.sso_users[each.value.principal_name].user_id : data.aws_identitystore_user.identity_store_user[each.value.principal_name].id) + principal_id = each.value.principal_type == "GROUP" ? data.aws_identitystore_group.identity_store_group[each.value.principal_name].id : data.aws_identitystore_user.identity_store_user[each.value.principal_name].id principal_type = each.value.principal_type target_id = each.value.account_id diff --git a/outputs.tf b/outputs.tf index 234989c..828f3ca 100644 --- a/outputs.tf +++ b/outputs.tf @@ -14,3 +14,34 @@ 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" } + + + +output "principals_and_their_account_assignments" { + value = local.principals_and_their_account_assignments + description = "Map of principals and their account assignments" + +} +/* debug output +output "accounts_ids_maps" { + value = local.accounts_ids_maps + description = "A map of account ids" +} + +output "pset_inline_policy_maps" { + value = local.pset_inline_policy_maps + description = "A map of inline policies for permission sets" + +} + +output "pset_permissions_boundary_aws_managed_maps" { + value = local.pset_permissions_boundary_aws_managed_maps + description = "A map of permissions boundary for permission" +} + +output "pset_permissions_boundary_customer_managed_maps" { + value = local.pset_permissions_boundary_customer_managed_maps + description = "A map of permissions boundary for permission" +} + +*/ From 0facb5d21dfcfeaabfb8ae63c3c4302c3b1cedb4 Mon Sep 17 00:00:00 2001 From: hacker65536 Date: Mon, 20 May 2024 16:28:03 +0900 Subject: [PATCH 07/19] fix --- .header.md | 19 +++++++++++++++++++ README.md | 21 +++++++++++++++++++++ locals.tf | 13 ++++++++----- main.tf | 10 +++++----- outputs.tf | 31 ------------------------------- 5 files changed, 53 insertions(+), 41 deletions(-) diff --git a/.header.md b/.header.md index bf7c0fe..e930a20 100644 --- a/.header.md +++ b/.header.md @@ -78,6 +78,25 @@ module "aws-iam-identity-center" { aws_managed_policies = ["arn:aws:iam::aws:policy/job-function/ViewOnlyAccess"] tags = { ManagedBy = "Terraform" } }, + CustomPermissionAccess = { + description = "Provides CustomPoweruser permissions.", + session_duration = "PT3H", // how long until session expires - this means 3 hours. max is 12 hours + aws_managed_policies = [ + "arn:aws:iam::aws:policy/ReadOnlyAccess", + "arn:aws:iam::aws:policy/AmazonS3FullAccess", + ] + inline_policy = data.aws_iam_policy_document.CustomPermissionInlinePolicy.json + permissions_boundary = { + // either managed_policy_arn or customer_managed_policy_reference + + // managed_policy_arn = "arn:aws:iam::aws:policy/PowerUserAccess" + customer_managed_policy_reference = { + name = "ExamplePermissionsBoundaryPolicy" + // path = "/" + } + } + tags = { ManagedBy = "Terraform" } + }, } // Assign users/groups access to accounts with the specified permissions diff --git a/README.md b/README.md index 44f1e17..2f757ab 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,25 @@ module "aws-iam-identity-center" { aws_managed_policies = ["arn:aws:iam::aws:policy/job-function/ViewOnlyAccess"] tags = { ManagedBy = "Terraform" } }, + CustomPermissionAccess = { + description = "Provides CustomPoweruser permissions.", + session_duration = "PT3H", // how long until session expires - this means 3 hours. max is 12 hours + aws_managed_policies = [ + "arn:aws:iam::aws:policy/ReadOnlyAccess", + "arn:aws:iam::aws:policy/AmazonS3FullAccess", + ] + inline_policy = data.aws_iam_policy_document.CustomPermissionInlinePolicy.json + permissions_boundary = { + // either managed_policy_arn or customer_managed_policy_reference + + // managed_policy_arn = "arn:aws:iam::aws:policy/PowerUserAccess" + customer_managed_policy_reference = { + name = "ExamplePermissionsBoundaryPolicy" + // path = "/" + } + } + tags = { ManagedBy = "Terraform" } + }, } // Assign users/groups access to accounts with the specified permissions @@ -140,6 +159,8 @@ No modules. | [aws_ssoadmin_managed_policy_attachment.pset_aws_managed_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_managed_policy_attachment) | resource | | [aws_ssoadmin_permission_set.pset](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_permission_set) | resource | | [aws_ssoadmin_permission_set_inline_policy.pset_inline_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_permission_set_inline_policy) | resource | +| [aws_ssoadmin_permissions_boundary_attachment.pset_permissions_boundary_aws_managed](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_permissions_boundary_attachment) | resource | +| [aws_ssoadmin_permissions_boundary_attachment.pset_permissions_boundary_customer_managed](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_permissions_boundary_attachment) | resource | | [aws_identitystore_group.existing_sso_groups](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/identitystore_group) | data source | | [aws_identitystore_group.identity_store_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/identitystore_group) | data source | | [aws_identitystore_user.existing_sso_users](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/identitystore_user) | data source | diff --git a/locals.tf b/locals.tf index a5918e2..4294438 100644 --- a/locals.tf +++ b/locals.tf @@ -83,7 +83,9 @@ locals { for pset_name, pset_index in local.permissions_boundary_aws_managed_permission_sets : [ { pset_name = pset_name - boundary = pset_index.permissions_boundary.managed_policy_arn + boundary = { + managed_policy_arn = pset_index.permissions_boundary.managed_policy_arn + } } ] ]) @@ -92,7 +94,9 @@ locals { for pset_name, pset_index in local.permissions_boundary_customer_managed_permission_sets : [ { pset_name = pset_name - boundary = pset_index.permissions_boundary.customer_managed_policy_reference + boundary = { + customer_managed_policy_reference = pset_index.permissions_boundary.customer_managed_policy_reference + } } ] ]) @@ -104,14 +108,13 @@ locals { locals { accounts_non_master_ids_maps = { - for idx, account in data.aws_organizations_organization.organization.non_master_accounts : - account.name => account.id + for idx, account in data.aws_organizations_organization.organization.non_master_accounts : account.name => account.id // if account.status == "ACTIVE" && can(data.aws_organizations_organization.organization.non_master_accounts) } accounts_ids_maps = merge( { // require terraform-provider-aws v5.46.0 - "${data.aws_organizations_organization.organization.master_account_name}" = "${data.aws_organizations_organization.organization.master_account_id}" + (data.aws_organizations_organization.organization.master_account_name) = (data.aws_organizations_organization.organization.master_account_id) }, local.accounts_non_master_ids_maps ) diff --git a/main.tf b/main.tf index d8eeef7..461cc6a 100644 --- a/main.tf +++ b/main.tf @@ -197,25 +197,25 @@ resource "aws_ssoadmin_permission_set_inline_policy" "pset_inline_policy" { # - Permissions Boundary - 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) } + 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 = 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) } + 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 - path = "/" + 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/outputs.tf b/outputs.tf index 828f3ca..234989c 100644 --- a/outputs.tf +++ b/outputs.tf @@ -14,34 +14,3 @@ 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" } - - - -output "principals_and_their_account_assignments" { - value = local.principals_and_their_account_assignments - description = "Map of principals and their account assignments" - -} -/* debug output -output "accounts_ids_maps" { - value = local.accounts_ids_maps - description = "A map of account ids" -} - -output "pset_inline_policy_maps" { - value = local.pset_inline_policy_maps - description = "A map of inline policies for permission sets" - -} - -output "pset_permissions_boundary_aws_managed_maps" { - value = local.pset_permissions_boundary_aws_managed_maps - description = "A map of permissions boundary for permission" -} - -output "pset_permissions_boundary_customer_managed_maps" { - value = local.pset_permissions_boundary_customer_managed_maps - description = "A map of permissions boundary for permission" -} - -*/ From a6e9e40bf743888e14df0f014f389d1208b85777 Mon Sep 17 00:00:00 2001 From: hacker65536 Date: Mon, 20 May 2024 19:27:09 +0900 Subject: [PATCH 08/19] update aws provider version --- README.md | 4 ++-- providers.tf | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2f757ab..e901d3f 100644 --- a/README.md +++ b/README.md @@ -134,14 +134,14 @@ See the `CONTRIBUTING.md` file for information on how to contribute. | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 0.14.0 | -| [aws](#requirement\_aws) | >= 4.35.0 | +| [aws](#requirement\_aws) | >= 5.46.0 | | [awscc](#requirement\_awscc) | >= 0.55.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 4.35.0 | +| [aws](#provider\_aws) | >= 5.46.0 | ## Modules diff --git a/providers.tf b/providers.tf index 0fd8221..fd4fcea 100644 --- a/providers.tf +++ b/providers.tf @@ -3,7 +3,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.35.0" + version = ">= 5.46.0" } awscc = { source = "hashicorp/awscc" From 34ee02009d89936ceb81e6c160a090f6afcd91d0 Mon Sep 17 00:00:00 2001 From: hacker65536 Date: Mon, 20 May 2024 19:56:21 +0900 Subject: [PATCH 09/19] revert update aws provider version --- README.md | 4 ++-- locals.tf | 13 +++---------- providers.tf | 2 +- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index e901d3f..2f757ab 100644 --- a/README.md +++ b/README.md @@ -134,14 +134,14 @@ See the `CONTRIBUTING.md` file for information on how to contribute. | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 0.14.0 | -| [aws](#requirement\_aws) | >= 5.46.0 | +| [aws](#requirement\_aws) | >= 4.35.0 | | [awscc](#requirement\_awscc) | >= 0.55.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 5.46.0 | +| [aws](#provider\_aws) | >= 4.35.0 | ## Modules diff --git a/locals.tf b/locals.tf index 4294438..d046ae7 100644 --- a/locals.tf +++ b/locals.tf @@ -107,17 +107,10 @@ locals { # - Account Assignments - locals { - accounts_non_master_ids_maps = { - for idx, account in data.aws_organizations_organization.organization.non_master_accounts : account.name => account.id - // if account.status == "ACTIVE" && can(data.aws_organizations_organization.organization.non_master_accounts) + accounts_ids_maps = { + for idx, account in data.aws_organizations_organization.organization.accounts : account.name => account.id + if account.status == "ACTIVE" && can(data.aws_organizations_organization.organization.accounts) } - accounts_ids_maps = merge( - { - // require terraform-provider-aws v5.46.0 - (data.aws_organizations_organization.organization.master_account_name) = (data.aws_organizations_organization.organization.master_account_id) - }, - local.accounts_non_master_ids_maps - ) # Create a new local variable by flattening the complex type given in the variable "account_assignments" # This will be a 'tuple' diff --git a/providers.tf b/providers.tf index fd4fcea..0fd8221 100644 --- a/providers.tf +++ b/providers.tf @@ -3,7 +3,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 5.46.0" + version = ">= 4.35.0" } awscc = { source = "hashicorp/awscc" From 2f362820951abbadfbac3b11976495a9d1207f20 Mon Sep 17 00:00:00 2001 From: hacker65536 Date: Wed, 22 May 2024 12:42:11 +0900 Subject: [PATCH 10/19] fix #33 --- main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.tf b/main.tf index 461cc6a..9b985c0 100644 --- a/main.tf +++ b/main.tf @@ -227,7 +227,7 @@ resource "aws_ssoadmin_account_assignment" "account_assignment" { instance_arn = local.ssoadmin_instance_arn permission_set_arn = data.aws_ssoadmin_permission_set.existing_permission_sets[each.value.permission_set].arn - principal_id = each.value.principal_type == "GROUP" ? data.aws_identitystore_group.identity_store_group[each.value.principal_name].id : data.aws_identitystore_user.identity_store_user[each.value.principal_name].id + principal_id = each.value.principal_type == "GROUP" ? (can(aws_identitystore_group.sso_groups[each.value.principal_name].group_id) ? aws_identitystore_group.sso_groups[each.value.principal_name].group_id : data.aws_identitystore_group.identity_store_group[each.value.principal_name].id) : (can(aws_identitystore_user.sso_users[each.value.principal_name].user_id) ? aws_identitystore_user.sso_users[each.value.principal_name].user_id : data.aws_identitystore_user.identity_store_user[each.value.principal_name].id) principal_type = each.value.principal_type target_id = each.value.account_id From f229bbfd6fbe1fef6368e2f71ae6876e2656a999 Mon Sep 17 00:00:00 2001 From: hacker65536 Date: Wed, 22 May 2024 14:47:22 +0900 Subject: [PATCH 11/19] fix --- data.tf | 2 +- locals.tf | 8 ++++++++ main.tf | 5 +++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/data.tf b/data.tf index d905e5a..a460f7c 100644 --- a/data.tf +++ b/data.tf @@ -115,7 +115,7 @@ data "aws_ssoadmin_permission_set" "existing_permission_sets" { instance_arn = local.ssoadmin_instance_arn name = each.value // Prevents failure if data fetch is attempted before Permission Sets are created - depends_on = [aws_ssoadmin_permission_set.pset] + //depends_on = [aws_ssoadmin_permission_set.pset] } diff --git a/locals.tf b/locals.tf index d046ae7..c5242df 100644 --- a/locals.tf +++ b/locals.tf @@ -139,6 +139,14 @@ locals { for pset in local.principals_and_their_account_assignments : pset.permission_set ]) + this_permission_sets = keys(var.permission_sets) + this_groups = [ + for group in var.sso_groups : group.group_name + ] + this_users = [ + for user in var.sso_users : user.user_name + ] + # iterates over account_assignents, sets that to be assignment.principal_name ONLY if the assignment.principal_type #is GROUP. Essentially stores all the possible 'assignments' (account assignments) that would be attached to a user group diff --git a/main.tf b/main.tf index 9b985c0..d706ef2 100644 --- a/main.tf +++ b/main.tf @@ -225,9 +225,10 @@ resource "aws_ssoadmin_account_assignment" "account_assignment" { for_each = local.principals_and_their_account_assignments // for_each arguement must be a map, or set of strings. Tuples won't work instance_arn = local.ssoadmin_instance_arn - permission_set_arn = data.aws_ssoadmin_permission_set.existing_permission_sets[each.value.permission_set].arn + permission_set_arn = contains(local.this_permission_sets, each.value.permission_set) ? aws_ssoadmin_permission_set.pset[each.value.permission_set].arn : data.aws_ssoadmin_permission_set.existing_permission_sets[each.value.permission_set].arn - principal_id = each.value.principal_type == "GROUP" ? (can(aws_identitystore_group.sso_groups[each.value.principal_name].group_id) ? aws_identitystore_group.sso_groups[each.value.principal_name].group_id : data.aws_identitystore_group.identity_store_group[each.value.principal_name].id) : (can(aws_identitystore_user.sso_users[each.value.principal_name].user_id) ? aws_identitystore_user.sso_users[each.value.principal_name].user_id : data.aws_identitystore_user.identity_store_user[each.value.principal_name].id) + + principal_id = each.value.principal_type == "GROUP" ? (contains(local.this_groups, each.value.principal_name) ? aws_identitystore_group.sso_groups[each.value.principal_name].group_id : data.aws_identitystore_group.identity_store_group[each.value.principal_name].id) : (contains(local.this_users, each.value.principal_name) ? aws_identitystore_user.sso_users[each.value.principal_name].user_id : data.aws_identitystore_user.identity_store_user[each.value.principal_name].id) principal_type = each.value.principal_type target_id = each.value.account_id From 2051fb27b1308074dd186ab8e196c3b226c26900 Mon Sep 17 00:00:00 2001 From: hacker65536 Date: Thu, 23 May 2024 14:37:29 +0900 Subject: [PATCH 12/19] fix --- data.tf | 2 +- locals.tf | 14 +++++++------- main.tf | 1 - 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/data.tf b/data.tf index a460f7c..d905e5a 100644 --- a/data.tf +++ b/data.tf @@ -115,7 +115,7 @@ data "aws_ssoadmin_permission_set" "existing_permission_sets" { instance_arn = local.ssoadmin_instance_arn name = each.value // Prevents failure if data fetch is attempted before Permission Sets are created - //depends_on = [aws_ssoadmin_permission_set.pset] + depends_on = [aws_ssoadmin_permission_set.pset] } diff --git a/locals.tf b/locals.tf index c5242df..f613b2d 100644 --- a/locals.tf +++ b/locals.tf @@ -134,11 +134,6 @@ locals { 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 } - - existing_permission_sets = distinct([ - for pset in local.principals_and_their_account_assignments : pset.permission_set - ]) - this_permission_sets = keys(var.permission_sets) this_groups = [ for group in var.sso_groups : group.group_name @@ -147,6 +142,11 @@ locals { for user in var.sso_users : user.user_name ] + // Permission sets created from other than this module. + existing_permission_sets = distinct([ + for pset in local.principals_and_their_account_assignments : pset.permission_set if !contains(local.this_permission_sets, pset.permission_set) + ]) + # iterates over account_assignents, sets that to be assignment.principal_name ONLY if the assignment.principal_type #is GROUP. Essentially stores all the possible 'assignments' (account assignments) that would be attached to a user group @@ -154,8 +154,8 @@ locals { # same thing, for sso_users but for USERs not GROUPs # 'account_assignments_for_groups' is effectively a list of principal names where the account type is GROUP - account_assignments_for_groups = [for assignment in var.account_assignments : assignment.principal_name if assignment.principal_type == "GROUP"] + account_assignments_for_groups = [for assignment in var.account_assignments : assignment.principal_name if(assignment.principal_type == "GROUP" && !contains(local.this_groups, assignment.principal_name))] # 'account_assignments_for_users' is effectively a list of principal names where the account type is USER - account_assignments_for_users = [for assignment in var.account_assignments : assignment.principal_name if assignment.principal_type == "USER"] + account_assignments_for_users = [for assignment in var.account_assignments : assignment.principal_name if assignment.principal_type == "USER" && !contains(local.this_users, assignment.principal_name)] } diff --git a/main.tf b/main.tf index d706ef2..9e9031c 100644 --- a/main.tf +++ b/main.tf @@ -203,7 +203,6 @@ resource "aws_ssoadmin_permissions_boundary_attachment" "pset_permissions_bounda permission_set_arn = aws_ssoadmin_permission_set.pset[each.key].arn permissions_boundary { managed_policy_arn = each.value.boundary.managed_policy_arn - } } From 4a55c75990e0985ca8740d2cdddbc73e95a2992a Mon Sep 17 00:00:00 2001 From: hacker65536 Date: Thu, 23 May 2024 14:55:00 +0900 Subject: [PATCH 13/19] fix example --- .header.md | 7 +++++-- README.md | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.header.md b/.header.md index e930a20..d13ddca 100644 --- a/.header.md +++ b/.header.md @@ -86,10 +86,13 @@ module "aws-iam-identity-center" { "arn:aws:iam::aws:policy/AmazonS3FullAccess", ] inline_policy = data.aws_iam_policy_document.CustomPermissionInlinePolicy.json - permissions_boundary = { - // either managed_policy_arn or customer_managed_policy_reference + // Only either managed_policy_arn or customer_managed_policy_reference can be specified. + // Before using customer_managed_policy_reference, first deploy the policy to the account. + // Don't in-place managed_policy_arn to/from customer_managed_policy_reference, delete it once. + permissions_boundary = { // managed_policy_arn = "arn:aws:iam::aws:policy/PowerUserAccess" + customer_managed_policy_reference = { name = "ExamplePermissionsBoundaryPolicy" // path = "/" diff --git a/README.md b/README.md index 2f757ab..58158c9 100644 --- a/README.md +++ b/README.md @@ -87,10 +87,13 @@ module "aws-iam-identity-center" { "arn:aws:iam::aws:policy/AmazonS3FullAccess", ] inline_policy = data.aws_iam_policy_document.CustomPermissionInlinePolicy.json - permissions_boundary = { - // either managed_policy_arn or customer_managed_policy_reference + // Only either managed_policy_arn or customer_managed_policy_reference can be specified. + // Before using customer_managed_policy_reference, first deploy the policy to the account. + // Don't in-place managed_policy_arn to/from customer_managed_policy_reference, delete it once. + permissions_boundary = { // managed_policy_arn = "arn:aws:iam::aws:policy/PowerUserAccess" + customer_managed_policy_reference = { name = "ExamplePermissionsBoundaryPolicy" // path = "/" From a6fa6c6827dfa31e030fd773caa9d86cc1a401ec Mon Sep 17 00:00:00 2001 From: hacker65536 Date: Wed, 5 Jun 2024 17:59:10 +0900 Subject: [PATCH 14/19] Fix only externally defined resources can be referenced from data sources. --- data.tf | 57 +++++++++++-------------------------------------------- locals.tf | 7 +++++++ main.tf | 8 +++++--- 3 files changed, 23 insertions(+), 49 deletions(-) diff --git a/data.tf b/data.tf index d905e5a..d75e33d 100644 --- a/data.tf +++ b/data.tf @@ -20,70 +20,42 @@ data "aws_ssoadmin_instances" "sso_instance" {} # user_name = "suchiha" # } -# - Fetch of SSO Groups to be used for group membership assignment - +# - Fetch of SSO Groups (externally defined) to be used for group membership assignment - data "aws_identitystore_group" "existing_sso_groups" { - for_each = local.users_and_their_groups + for_each = toset(local.existing_sso_groups) identity_store_id = local.sso_instance_id alternate_identifier { unique_attribute { attribute_path = "DisplayName" - attribute_value = each.value.group_name + attribute_value = each.value } } - // Prevents failure if data fetch is attempted before GROUPS are created - depends_on = [aws_identitystore_group.sso_groups] } -# - Fetch of SSO Users to be used for group membership assignment - +# - Fetch of SSO Users (externally defined) to be used for group membership assignment - data "aws_identitystore_user" "existing_sso_users" { - for_each = local.users_and_their_groups + for_each = toset(local.existing_sso_users) identity_store_id = local.sso_instance_id alternate_identifier { # Filter users by user_name (nuzumaki, suchiha, dovis, etc.) unique_attribute { attribute_path = "UserName" - attribute_value = each.value.user_name - } - } - // Prevents failure if data fetch is attempted before USERS are created - depends_on = [aws_identitystore_user.sso_users] -} - - -# - Fetch of SSO Groups to be used for account assignments (for GROUPS) - -data "aws_identitystore_group" "identity_store_group" { - for_each = toset(local.account_assignments_for_groups) - identity_store_id = local.sso_instance_id - - alternate_identifier { - unique_attribute { - attribute_path = "DisplayName" attribute_value = each.value } } - // Prevents failure if data fetch is attempted before GROUPS are created - depends_on = [aws_identitystore_group.sso_groups] } - -# - Fetch of SSO Groups to be used for account assignments (for USERS) - -data "aws_identitystore_user" "identity_store_user" { - for_each = toset(local.account_assignments_for_users) - identity_store_id = local.sso_instance_id - - alternate_identifier { - unique_attribute { - attribute_path = "UserName" - attribute_value = each.value - } - } - // Prevents failure if data fetch is attempted before USERS are created - depends_on = [aws_identitystore_user.sso_users] +# - Fetch of Permissions sets (externally defined) to be used for account assignment - +data "aws_ssoadmin_permission_set" "existing_permission_sets" { + for_each = toset(local.existing_permission_sets) + instance_arn = local.ssoadmin_instance_arn + name = each.value } + # The local variable 'principals_and_their_permission_sets' is a map of values for relevant user information. # It contians a list of all users with the name of their group_assignments appended to the end of the string. # This map is then fed into the 'aws_ssoadmin_permission_set' data source with the 'for_each'meta argument to @@ -110,13 +82,6 @@ data "aws_identitystore_user" "identity_store_user" { # account_ids = "111111111111" # } -data "aws_ssoadmin_permission_set" "existing_permission_sets" { - for_each = toset(local.existing_permission_sets) - instance_arn = local.ssoadmin_instance_arn - name = each.value - // Prevents failure if data fetch is attempted before Permission Sets are created - depends_on = [aws_ssoadmin_permission_set.pset] -} data "aws_organizations_organization" "organization" {} diff --git a/locals.tf b/locals.tf index f613b2d..877eb74 100644 --- a/locals.tf +++ b/locals.tf @@ -147,6 +147,13 @@ locals { for pset in local.principals_and_their_account_assignments : pset.permission_set if !contains(local.this_permission_sets, pset.permission_set) ]) + existing_sso_users = distinct([ + //for user_gourp in local.users_and_their_groups : user_gourp.user_name if !contains(local.this_users, user_group.user_name) + for k, v in local.users_and_their_groups : v.user_name if !contains(local.this_users, v.user_name) + ]) + existing_sso_groups = distinct([ + for k, v in local.users_and_their_groups : v.group_name if !contains(local.this_groups, v.group_name) + ]) # iterates over account_assignents, sets that to be assignment.principal_name ONLY if the assignment.principal_type #is GROUP. Essentially stores all the possible 'assignments' (account assignments) that would be attached to a user group diff --git a/main.tf b/main.tf index 9e9031c..889d28e 100644 --- a/main.tf +++ b/main.tf @@ -137,8 +137,10 @@ resource "aws_identitystore_group_membership" "sso_group_membership" { for_each = local.users_and_their_groups identity_store_id = local.sso_instance_id - group_id = data.aws_identitystore_group.existing_sso_groups[each.key].group_id - member_id = data.aws_identitystore_user.existing_sso_users[each.key].user_id + //group_id = data.aws_identitystore_group.existing_sso_groups[each.key].group_id + group_id = (contains(local.this_groups, each.value.group_name) ? aws_identitystore_group.sso_groups[each.value.group_name].group_id : data.aws_identitystore_group.existing_sso_groups[each.value.group_name].id) + //member_id = data.aws_identitystore_user.existing_sso_users[each.key].user_id + member_id = (contains(local.this_users, each.value.user_name) ? aws_identitystore_user.sso_users[each.value.user_name].user_id : data.aws_identitystore_user.existing_sso_users[each.value.user_name].id) } @@ -227,7 +229,7 @@ resource "aws_ssoadmin_account_assignment" "account_assignment" { permission_set_arn = contains(local.this_permission_sets, each.value.permission_set) ? aws_ssoadmin_permission_set.pset[each.value.permission_set].arn : data.aws_ssoadmin_permission_set.existing_permission_sets[each.value.permission_set].arn - principal_id = each.value.principal_type == "GROUP" ? (contains(local.this_groups, each.value.principal_name) ? aws_identitystore_group.sso_groups[each.value.principal_name].group_id : data.aws_identitystore_group.identity_store_group[each.value.principal_name].id) : (contains(local.this_users, each.value.principal_name) ? aws_identitystore_user.sso_users[each.value.principal_name].user_id : data.aws_identitystore_user.identity_store_user[each.value.principal_name].id) + principal_id = each.value.principal_type == "GROUP" ? (contains(local.this_groups, each.value.principal_name) ? aws_identitystore_group.sso_groups[each.value.principal_name].group_id : data.aws_identitystore_group.existing_sso_groups[each.value.principal_name].id) : (contains(local.this_users, each.value.principal_name) ? aws_identitystore_user.sso_users[each.value.principal_name].user_id : data.aws_identitystore_user.existing_sso_users[each.value.principal_name].id) principal_type = each.value.principal_type target_id = each.value.account_id From 46c0969c7a7a318c5ec3c717ceb4c8d3f60128fc Mon Sep 17 00:00:00 2001 From: hacker65536 Date: Thu, 6 Jun 2024 11:08:15 +0900 Subject: [PATCH 15/19] fix --- README.md | 2 -- locals.tf | 15 ++------------- main.tf | 4 +--- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 58158c9..6a1ab32 100644 --- a/README.md +++ b/README.md @@ -165,9 +165,7 @@ No modules. | [aws_ssoadmin_permissions_boundary_attachment.pset_permissions_boundary_aws_managed](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_permissions_boundary_attachment) | resource | | [aws_ssoadmin_permissions_boundary_attachment.pset_permissions_boundary_customer_managed](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_permissions_boundary_attachment) | resource | | [aws_identitystore_group.existing_sso_groups](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/identitystore_group) | data source | -| [aws_identitystore_group.identity_store_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/identitystore_group) | data source | | [aws_identitystore_user.existing_sso_users](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/identitystore_user) | data source | -| [aws_identitystore_user.identity_store_user](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/identitystore_user) | data source | | [aws_organizations_organization.organization](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/organizations_organization) | data source | | [aws_ssoadmin_instances.sso_instance](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssoadmin_instances) | data source | | [aws_ssoadmin_permission_set.existing_permission_sets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssoadmin_permission_set) | data source | diff --git a/locals.tf b/locals.tf index 877eb74..5176604 100644 --- a/locals.tf +++ b/locals.tf @@ -134,6 +134,7 @@ locals { 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 } + # List of permission sets, groups, and users that defined in this module this_permission_sets = keys(var.permission_sets) this_groups = [ for group in var.sso_groups : group.group_name @@ -142,27 +143,15 @@ locals { for user in var.sso_users : user.user_name ] - // Permission sets created from other than this module. + # For reference to resources that already exist in AWS existing_permission_sets = distinct([ for pset in local.principals_and_their_account_assignments : pset.permission_set if !contains(local.this_permission_sets, pset.permission_set) ]) - existing_sso_users = distinct([ - //for user_gourp in local.users_and_their_groups : user_gourp.user_name if !contains(local.this_users, user_group.user_name) for k, v in local.users_and_their_groups : v.user_name if !contains(local.this_users, v.user_name) ]) existing_sso_groups = distinct([ for k, v in local.users_and_their_groups : v.group_name if !contains(local.this_groups, v.group_name) ]) - # iterates over account_assignents, sets that to be assignment.principal_name ONLY if the assignment.principal_type - #is GROUP. Essentially stores all the possible 'assignments' (account assignments) that would be attached to a user group - - # same thing, for sso_users but for USERs not GROUPs - - # 'account_assignments_for_groups' is effectively a list of principal names where the account type is GROUP - account_assignments_for_groups = [for assignment in var.account_assignments : assignment.principal_name if(assignment.principal_type == "GROUP" && !contains(local.this_groups, assignment.principal_name))] - - # 'account_assignments_for_users' is effectively a list of principal names where the account type is USER - account_assignments_for_users = [for assignment in var.account_assignments : assignment.principal_name if assignment.principal_type == "USER" && !contains(local.this_users, assignment.principal_name)] } diff --git a/main.tf b/main.tf index 889d28e..f780032 100644 --- a/main.tf +++ b/main.tf @@ -137,9 +137,7 @@ resource "aws_identitystore_group_membership" "sso_group_membership" { for_each = local.users_and_their_groups identity_store_id = local.sso_instance_id - //group_id = data.aws_identitystore_group.existing_sso_groups[each.key].group_id - group_id = (contains(local.this_groups, each.value.group_name) ? aws_identitystore_group.sso_groups[each.value.group_name].group_id : data.aws_identitystore_group.existing_sso_groups[each.value.group_name].id) - //member_id = data.aws_identitystore_user.existing_sso_users[each.key].user_id + group_id = (contains(local.this_groups, each.value.group_name) ? aws_identitystore_group.sso_groups[each.value.group_name].group_id : data.aws_identitystore_group.existing_sso_groups[each.value.group_name].id) member_id = (contains(local.this_users, each.value.user_name) ? aws_identitystore_user.sso_users[each.value.user_name].user_id : data.aws_identitystore_user.existing_sso_users[each.value.user_name].id) } From 9fac78316f1f4eae4ffb6944066c8d8569c9a217 Mon Sep 17 00:00:00 2001 From: Kevon Mayers Date: Fri, 7 Jun 2024 17:16:38 -0400 Subject: [PATCH 16/19] update examples --- examples/create-users-and-groups/main.tf | 4 +- examples/inline-policy/.header.md | 1 + examples/inline-policy/README.md | 35 ++++++ examples/inline-policy/locals.tf | 14 +++ examples/inline-policy/main.tf | 149 +++++++++++++++++++++++ 5 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 examples/inline-policy/.header.md create mode 100644 examples/inline-policy/README.md create mode 100644 examples/inline-policy/locals.tf create mode 100644 examples/inline-policy/main.tf diff --git a/examples/create-users-and-groups/main.tf b/examples/create-users-and-groups/main.tf index e4751bc..9ddc4a0 100644 --- a/examples/create-users-and-groups/main.tf +++ b/examples/create-users-and-groups/main.tf @@ -24,14 +24,14 @@ module "aws-iam-identity-center" { // Create desired USERS in IAM Identity Center sso_users = { - NarutoUzumaki : { + nuzumaki : { group_membership = ["Admin", "Dev", "QA", "Audit"] user_name = "nuzumaki" given_name = "Naruto" family_name = "Uzumaki" email = "nuzumaki@hiddenleaf.village" }, - SasukeUchiha : { + suchiha : { group_membership = ["QA", "Audit"] user_name = "suchiha" given_name = "Sasuke" diff --git a/examples/inline-policy/.header.md b/examples/inline-policy/.header.md new file mode 100644 index 0000000..1dd0495 --- /dev/null +++ b/examples/inline-policy/.header.md @@ -0,0 +1 @@ +This directory contains examples of using the module to create users and groups and assign permissions with **Inline Policies**. diff --git a/examples/inline-policy/README.md b/examples/inline-policy/README.md new file mode 100644 index 0000000..db504f6 --- /dev/null +++ b/examples/inline-policy/README.md @@ -0,0 +1,35 @@ + +This directory contains examples of using the module to create users and groups and assign permissions with **Inline Policies**. + +## Requirements + +No requirements. + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | n/a | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [aws-iam-identity-center](#module\_aws-iam-identity-center) | ../.. | n/a | + +## Resources + +| Name | Type | +|------|------| +| [aws_iam_policy_document.restrictAccessInlinePolicy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_organizations_organization.org](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/organizations_organization) | data source | +| [aws_ssm_parameter.account1_account_id](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source | + +## Inputs + +No inputs. + +## Outputs + +No outputs. + \ No newline at end of file diff --git a/examples/inline-policy/locals.tf b/examples/inline-policy/locals.tf new file mode 100644 index 0000000..922f5e9 --- /dev/null +++ b/examples/inline-policy/locals.tf @@ -0,0 +1,14 @@ +# Fetch Account Id from SSM Parameter Store +data "aws_ssm_parameter" "account1_account_id" { + name = "tf-aws-iam-idc-module-testing-account1-account-id" // replace with your SSM Parameter Key +} + +locals { + # Account IDs + account1_account_id = nonsensitive(data.aws_ssm_parameter.account1_account_id.value) + # account1_account_id = "111111111111" + # account2_account_id = "222222222222" + # account3_account_id = "333333333333" + # account4_account_id = "444444444444" + +} diff --git a/examples/inline-policy/main.tf b/examples/inline-policy/main.tf new file mode 100644 index 0000000..b836ea8 --- /dev/null +++ b/examples/inline-policy/main.tf @@ -0,0 +1,149 @@ +data "aws_organizations_organization" "org" {} + +# Create Inline Policy +# IMPORTANT - This policy has an explicit deny. This is used as an example only. +# Ensure you understand the impact of this policy before deploying. +data "aws_iam_policy_document" "restrictAccessInlinePolicy" { + statement { + sid = "Restrict" + actions = [ + "*", + ] + effect = "Deny" + resources = [ + "*", + ] + condition { + test = "NotIpAddress" + variable = "aws:SourceIp" + values = [ + // replace with your own IP address + "0.0.0.0/0", + ] + } + condition { + test = "Bool" + variable = "aws:ViaAWSService" + values = [ + "false" + ] + } + condition { + test = "StringNotLike" + variable = "aws:userAgent" + values = [ + "*exec-env/CloudShell*" + ] + } + } +} + +# locals { +# active_accounts = [for a in data.aws_organizations_organization.org.accounts : a if a.status == "ACTIVE"] +# tags = { +# "Owner" = "SRE Team" +# } +# } + + +module "aws-iam-identity-center" { + source = "../.." // local example + # source = "aws-ia/iam-identity-center/aws" // remote example + + sso_groups = { + Admin : { + group_name = "Admin" + group_description = "Admin Group" + }, + Dev : { + group_name = "Dev" + group_description = "Dev Group" + }, + } + sso_users = { + nuzumaki : { + group_membership = ["Admin", "Dev", "AWSControlTowerAdmins"] + user_name = "nuzumaki" + given_name = "Naruto" + family_name = "Uzumaki" + email = "nuzumaki@hiddenleaf.village" + }, + suchiha : { + group_membership = ["Dev", "AWSControlTowerAdmins"] + user_name = "suchiha" + given_name = "Sasuke" + family_name = "Uchiha" + email = "suchiha@hiddenleaf.village" + }, + } + + permission_sets = { + AdministratorAccess = { + description = "Provides full access to AWS services and resources", + session_duration = "PT3H", + aws_managed_policies = ["arn:aws:iam::aws:policy/AdministratorAccess"] + inline_policy = data.aws_iam_policy_document.restrictAccessInlinePolicy.json + tags = { ManagedBy = "Terraform" } + }, + PowerUserAccess = { + description = "Provides full access to AWS services and resources, but does not allow management of Users and groups", + session_duration = "PT3H", + aws_managed_policies = ["arn:aws:iam::aws:policy/PowerUserAccess"] + tags = { ManagedBy = "Terraform" } + }, + ViewOnlyAccess = { + description = "This policy grants permissions to view resources and basic metadata across all AWS services", + session_duration = "PT3H", + aws_managed_policies = ["arn:aws:iam::aws:policy/job-function/ViewOnlyAccess"] + managed_policy_arn = "arn:aws:iam::aws:policy/job-function/ViewOnlyAccess" + + permissions_boundary = { + managed_policy_arn = "arn:aws:iam::aws:policy/job-function/ViewOnlyAccess" + } + tags = { ManagedBy = "Terraform" } + }, + ReadOnlyAccess = { + description = "This policy grants permissions to view resources and basic metadata across all AWS services", + session_duration = "PT3H", + aws_managed_policies = ["arn:aws:iam::aws:policy/job-function/ViewOnlyAccess"] + + managed_policy_arn = "arn:aws:iam::aws:policy/job-function/ViewOnlyAccess" + tags = { ManagedBy = "Terraform" } + }, + } + account_assignments = { + Admin : { + principal_name = "Admin" + principal_type = "GROUP" + permission_sets = [ + "AdministratorAccess", + "PowerUserAccess", + "ViewOnlyAccess", + // existing permission set + "AWSAdministratorAccess", + ] + account_ids = [ + // replace with your own account id + local.account1_account_id, + # "111111111111", + # "222222222222", + # "333333333333", + ] + }, + Dev : { + principal_name = "Dev" + principal_type = "GROUP" + permission_sets = [ + "PowerUserAccess", + "ViewOnlyAccess", + ] + account_ids = [ + // replace with your own account id + local.account1_account_id, + # "111111111111", + # "222222222222", + # "333333333333", + ] + }, + } +} From a2d8ff20986843895206ee3f536f5ea1cb8e8bf0 Mon Sep 17 00:00:00 2001 From: Kevon Mayers Date: Fri, 7 Jun 2024 17:16:48 -0400 Subject: [PATCH 17/19] update data.tf --- data.tf | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/data.tf b/data.tf index d75e33d..ab882b4 100644 --- a/data.tf +++ b/data.tf @@ -1,6 +1,9 @@ # Fetch existing SSO Instance data "aws_ssoadmin_instances" "sso_instance" {} +# Fetch existing AWS Organization +data "aws_organizations_organization" "organization" {} + # The local variable 'users_and_their_groups' is a map of values for relevant user information. # It contians a list of all users with the name of their group_assignments appended to the end of the string. @@ -84,4 +87,4 @@ data "aws_ssoadmin_permission_set" "existing_permission_sets" { -data "aws_organizations_organization" "organization" {} + From 081377ad2fd60191a384550ab9d4fbbd98fc0bdb Mon Sep 17 00:00:00 2001 From: Kevon Mayers Date: Fri, 7 Jun 2024 17:17:02 -0400 Subject: [PATCH 18/19] create test for inline policy --- test/inline_policy_test.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 test/inline_policy_test.go diff --git a/test/inline_policy_test.go b/test/inline_policy_test.go new file mode 100644 index 0000000..ed95a3a --- /dev/null +++ b/test/inline_policy_test.go @@ -0,0 +1,23 @@ +package test + +import ( + "testing" + + "github.com/gruntwork-io/terratest/modules/terraform" +) + +func TestAWSandInlinePolicies(t *testing.T) { + + terraformOptions := &terraform.Options{ + // The path to where your Terraform code is located + TerraformDir: "../examples/inline-policy", + + } + + // At the end of the test, run 'terraform destroy' to clean up any resources that were created + defer terraform.Destroy(t, terraformOptions) + + // Run 'terraform init' and 'terraform apply'. Fail the test if there are any errors. + terraform.InitAndApply(t, terraformOptions) + +} From 76c692b7889d86e820b80a560f293fdee6fae2c7 Mon Sep 17 00:00:00 2001 From: Kevon Mayers Date: Fri, 7 Jun 2024 17:23:10 -0400 Subject: [PATCH 19/19] bump version --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index a3dce6c..f9cece5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v0.0.2 +v0.0.3