From 5cb1d26f2fe1f8c12c13764f42f5387bb115f10c Mon Sep 17 00:00:00 2001 From: badra001 Date: Tue, 29 Jul 2025 11:15:03 -0400 Subject: [PATCH] * 1.2.3 -- 2025-07-29 - add acmpca-iam-rolesanywhere example --- CHANGELOG.md | 4 + common/version.tf | 2 +- .../acmpca-iam-rolesanywhere/aws_config.tf | 13 +++ .../acmpca-iam-rolesanywhere/aws_config.tpl | 3 + .../acmpca-iam-rolesanywhere/certificate.tf | 28 +++++++ examples/acmpca-iam-rolesanywhere/data.ssm.tf | 42 ++++++++++ examples/acmpca-iam-rolesanywhere/data.tf | 7 ++ examples/acmpca-iam-rolesanywhere/locals.tf | 5 ++ examples/acmpca-iam-rolesanywhere/region.tf | 3 + examples/acmpca-iam-rolesanywhere/role.tf | 83 +++++++++++++++++++ examples/acmpca-iam-rolesanywhere/tf-run.data | 27 ++++++ .../acmpca-iam-rolesanywhere/variables.tf | 32 +++++++ examples/acmpca-iam-rolesanywhere/versions.tf | 33 ++++++++ 13 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 examples/acmpca-iam-rolesanywhere/aws_config.tf create mode 100644 examples/acmpca-iam-rolesanywhere/aws_config.tpl create mode 100644 examples/acmpca-iam-rolesanywhere/certificate.tf create mode 100644 examples/acmpca-iam-rolesanywhere/data.ssm.tf create mode 100644 examples/acmpca-iam-rolesanywhere/data.tf create mode 100644 examples/acmpca-iam-rolesanywhere/locals.tf create mode 100644 examples/acmpca-iam-rolesanywhere/region.tf create mode 100644 examples/acmpca-iam-rolesanywhere/role.tf create mode 100644 examples/acmpca-iam-rolesanywhere/tf-run.data create mode 100644 examples/acmpca-iam-rolesanywhere/variables.tf create mode 100644 examples/acmpca-iam-rolesanywhere/versions.tf diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c8595c..30b4587 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,3 +43,7 @@ * 1.2.2 -- 2025-03-18 - all - add validation of contact_email to include @ and in the census.gov domain + +* 1.2.3 -- 2025-07-29 + - add acmpca-iam-rolesanywhere example + diff --git a/common/version.tf b/common/version.tf index 3472979..4e4bfa7 100644 --- a/common/version.tf +++ b/common/version.tf @@ -1,3 +1,3 @@ locals { - _module_version = "1.2.2" + _module_version = "1.2.3" } diff --git a/examples/acmpca-iam-rolesanywhere/aws_config.tf b/examples/acmpca-iam-rolesanywhere/aws_config.tf new file mode 100644 index 0000000..36ffd0d --- /dev/null +++ b/examples/acmpca-iam-rolesanywhere/aws_config.tf @@ -0,0 +1,13 @@ +resource "local_file" "aws_config_file" { + filename = format("%v/%v.%v", "./certs", local.role_name, "aws_config") + file_permission = "0644" + directory_permission = "0755" + content = templatefile("aws_config.tpl", { + account_alias = var.account_alias + role_name = local.role_name + role_arn = aws_iam_role.role.arn + trust_anchor_arn = local.this_trust_arn + profile_arn = aws_rolesanywhere_profile.role.arn + region = var.region + }) +} diff --git a/examples/acmpca-iam-rolesanywhere/aws_config.tpl b/examples/acmpca-iam-rolesanywhere/aws_config.tpl new file mode 100644 index 0000000..351a0e8 --- /dev/null +++ b/examples/acmpca-iam-rolesanywhere/aws_config.tpl @@ -0,0 +1,3 @@ +[profile ${account_alias}.${role_name}] +region = ${region} +credential_process = aws_signing_helper credential-process --certificate CERTPATH/${role_name}.crt --private-key CERTPATH/${role_name}.key --trust-anchor-arn ${trust_anchor_arn} --profile-arn ${profile_arn} --role-arn ${role_arn} --region ${region} diff --git a/examples/acmpca-iam-rolesanywhere/certificate.tf b/examples/acmpca-iam-rolesanywhere/certificate.tf new file mode 100644 index 0000000..09b3e0f --- /dev/null +++ b/examples/acmpca-iam-rolesanywhere/certificate.tf @@ -0,0 +1,28 @@ +module "certificate" { + source = "git@github.e.it.census.gov:terraform-modules/aws-certificates//acmpca-iam-rolesanywhere" + + role_name = local.role_name + contact_email = var.contact_group_email + certificate_subject_ou = local.certificate_subject_ou["x509Subject/OU"] + validity_days = var.validity_days +} + +locals { + this_trust_arn = try(([for k, v in local.trust_ca[var.region] : v if v.ca_name == module.certificate.certificate_authority_name])[0].trust_arn, null) +} + +## output "certificate_subject" { +## value = module.certificate.certificate_subject +## } +## +## output "certificate_details" { +## value = module.certificate.certificate_details +## } +## +## output "certificate_issuer_details" { +## value = module.certificate.certificate_issuer_details +## } +## +## output "certificate_issuer_subject" { +## value = module.certificate.certificate_issuer_subject +## } diff --git a/examples/acmpca-iam-rolesanywhere/data.ssm.tf b/examples/acmpca-iam-rolesanywhere/data.ssm.tf new file mode 100644 index 0000000..26de7b8 --- /dev/null +++ b/examples/acmpca-iam-rolesanywhere/data.ssm.tf @@ -0,0 +1,42 @@ +data "aws_ssm_parameters_by_path" "trust_east" { + provider = aws.east + path = "/enterprise/rolesanywhere/trustanchor/" + recursive = true +} + +data "aws_ssm_parameters_by_path" "trust_west" { + provider = aws.west + path = "/enterprise/rolesanywhere/trustanchor/" + recursive = true +} + +locals { + trust_east_arns = zipmap(data.aws_ssm_parameters_by_path.trust_east.names, data.aws_ssm_parameters_by_path.trust_east.arns) + trust_east_values = zipmap(data.aws_ssm_parameters_by_path.trust_east.names, nonsensitive(data.aws_ssm_parameters_by_path.trust_east.values)) + _trust_east_ca = { for v in data.aws_ssm_parameters_by_path.trust_east.names : v => slice(reverse(split("/", v)), 0, 2) } + trust_east_ca = { for k, v in local._trust_east_ca : k => { + label = k + region = "us-gov-east-1" + ca_name = v[0] + ssm_parameter_name = k + ssm_paraemter_arn = local.trust_east_arns[k] + trust_arn = local.trust_east_values[k] + } } + + trust_west_arns = zipmap(data.aws_ssm_parameters_by_path.trust_west.names, data.aws_ssm_parameters_by_path.trust_west.arns) + trust_west_values = zipmap(data.aws_ssm_parameters_by_path.trust_west.names, nonsensitive(data.aws_ssm_parameters_by_path.trust_west.values)) + _trust_west_ca = { for v in data.aws_ssm_parameters_by_path.trust_west.names : v => slice(reverse(split("/", v)), 0, 2) } + trust_west_ca = { for k, v in local._trust_west_ca : k => { + label = k + region = "us-gov-west-1" + ca_name = v[0] + ssm_parameter_name = k + ssm_paraemter_arn = local.trust_west_arns[k] + trust_arn = local.trust_west_values[k] + } } + + trust_ca = { + "us-gov-east-1" = local.trust_east_ca + "us-gov-west-1" = local.trust_west_ca + } +} diff --git a/examples/acmpca-iam-rolesanywhere/data.tf b/examples/acmpca-iam-rolesanywhere/data.tf new file mode 100644 index 0000000..16506e6 --- /dev/null +++ b/examples/acmpca-iam-rolesanywhere/data.tf @@ -0,0 +1,7 @@ +data "aws_caller_identity" "current" {} + +data "aws_arn" "current" { + arn = data.aws_caller_identity.current.arn +} + +data "aws_region" "current" {} diff --git a/examples/acmpca-iam-rolesanywhere/locals.tf b/examples/acmpca-iam-rolesanywhere/locals.tf new file mode 100644 index 0000000..6e912e2 --- /dev/null +++ b/examples/acmpca-iam-rolesanywhere/locals.tf @@ -0,0 +1,5 @@ +locals { + base_tags = { + "boc:created_by" = "terraform" + } +} diff --git a/examples/acmpca-iam-rolesanywhere/region.tf b/examples/acmpca-iam-rolesanywhere/region.tf new file mode 100644 index 0000000..f617506 --- /dev/null +++ b/examples/acmpca-iam-rolesanywhere/region.tf @@ -0,0 +1,3 @@ +locals { + region = var.region +} diff --git a/examples/acmpca-iam-rolesanywhere/role.tf b/examples/acmpca-iam-rolesanywhere/role.tf new file mode 100644 index 0000000..c852876 --- /dev/null +++ b/examples/acmpca-iam-rolesanywhere/role.tf @@ -0,0 +1,83 @@ +locals { + role_name = format("r-%v", var.role_name) + certificate_subject_ou = { "x509Subject/OU" = lookup(var.certificate_conditions, "x509Subject/OU", format("IAM RolesAnywhere %v", data.aws_caller_identity.current.account_id)) } + certificate_conditions = merge( + local.certificate_subject_ou, + var.certificate_conditions, + { "x509Subject/CN" = local.role_name }, + ) +} + +resource "aws_rolesanywhere_profile" "role" { + name = local.role_name + enabled = true + role_arns = [aws_iam_role.role.arn] + + tags = merge( + local.base_tags, + # var.account_tags, + # var.infrastructure_tags, + # var.application_tags, + { Name = local.role_name }, + ) +} + +resource "aws_iam_role" "role" { + name = local.role_name + assume_role_policy = data.aws_iam_policy_document.role_anywhere_assume.json + managed_policy_arns = [] + # add policies as needed + + tags = merge( + local.base_tags, + # var.account_tags, + # var.infrastructure_tags, + # var.application_tags, + { Name = local.role_name }, + ) +} + + +data "aws_iam_policy_document" "role_anywhere_assume" { + statement { + sid = "RolesAnywhereTrust" + effect = "Allow" + actions = [ + "sts:AssumeRole", + "sts:TagSession", + "sts:SetSourceIdentity", + ] + principals { + type = "Service" + identifiers = ["rolesanywhere.amazonaws.com"] + } + condition { + test = "ForAnyValue:StringEquals" + variable = "aws:SourceArn" + values = concat(nonsensitive(data.aws_ssm_parameters_by_path.trust_east.values), nonsensitive(data.aws_ssm_parameters_by_path.trust_west.values)) + } + # include condition for certificate + dynamic "condition" { + for_each = local.certificate_conditions + iterator = c + content { + test = "StringEquals" + variable = format("aws:PrincipalTag/%v", c.key) + values = [c.value] + } + } + } +} + + +## "Condition": { +## "StringEquals": { +## "aws:PrincipalTag/x509Subject/CN": "onpremsrv01", +## "aws:PrincipalTag/x509Subject/OU": "SecOps" +## } +## } +# https://aws.amazon.com/blogs/security/extend-aws-iam-roles-to-workloads-outside-of-aws-with-iam-roles-anywhere/ +# https://docs.aws.amazon.com/rolesanywhere/latest/userguide/workload-identities.html + +# https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_control-access_monitor.html + diff --git a/examples/acmpca-iam-rolesanywhere/tf-run.data b/examples/acmpca-iam-rolesanywhere/tf-run.data new file mode 100644 index 0000000..b83d623 --- /dev/null +++ b/examples/acmpca-iam-rolesanywhere/tf-run.data @@ -0,0 +1,27 @@ +VERSION 2.1.1 +TAG setup +REMOTE-STATE +COMMAND tf-directory-setup.py -l none -f +COMMAND setup-new-directory.sh + +TAG links +LINKTOP includes.d/variables.account_tags.tf +LINKTOP includes.d/variables.account_tags.auto.tfvars +LINKTOP includes.d/variables.infrastructure_tags.tf +LINKTOP includes.d/variables.infrastructure_tags.auto.tfvars +LINKTOP includes.d/variables.application_tags.tf +LINKTOP includes.d/variables.application_tags.auto.tfvars +# LINKTOP provider_configs.d/provider.ldap_new.auto.tfvars +# LINKTOP provider_configs.d/provider.ldap_new.tf +# LINKTOP provider_configs.d/provider.ldap_new.variables.tf +COMMAND rm -f provider.ldap.* + +TAG init +COMMAND tf-init + +TAG start +#POLICY +ALL + +TAG state-link +COMMAND tf-directory-setup.py -l s3 diff --git a/examples/acmpca-iam-rolesanywhere/variables.tf b/examples/acmpca-iam-rolesanywhere/variables.tf new file mode 100644 index 0000000..e4b8dc2 --- /dev/null +++ b/examples/acmpca-iam-rolesanywhere/variables.tf @@ -0,0 +1,32 @@ +variable "role_name" { + description = "Role Name (without r- prefix)" + type = string +} + +variable "certificate_conditions" { + description = "Map of certificate conditions to be merged with x509Subject/CN={role_name}" + type = map(string) + default = {} +} + +variable "contact_group_email" { + description = "Email of contact group" + type = string +} + +variable "contact_users" { + description = "Username of contact(s)" + type = list(string) + default = [] +} + +variable "validity_days" { + description = "Number of days for which the certificate is valid (default: 365, max: 365)" + type = number + default = 365 + + validation { + condition = var.validity_days > 0 && var.validity_days <= 365 + error_message = "validity_days must be between 1 and 365" + } +} diff --git a/examples/acmpca-iam-rolesanywhere/versions.tf b/examples/acmpca-iam-rolesanywhere/versions.tf new file mode 100644 index 0000000..ab01aa2 --- /dev/null +++ b/examples/acmpca-iam-rolesanywhere/versions.tf @@ -0,0 +1,33 @@ +terraform { + required_version = ">= 1.0.0" + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.0" + } + # ldap = { + # source = "trevex/ldap" + # version = ">= 0.5.4" + # } + # external = { + # source = "hashicorp/external" + # version = ">= 1.0" + # } + # null = { + # source = "hashicorp/null" + # version = ">= 1.0" + # } + # random = { + # source = "hashicorp/random" + # version = ">= 1.0" + # } + # template = { + # source = "hashicorp/template" + # version = ">= 1.0" + # } + # infoblox = { + # source = "infobloxopen/infoblox" + # version = ">= 2.1.0" + # } + } +}