diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ceafc2..c9d2c5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -154,3 +154,7 @@ * 1.15.2 -- 2022-04-26 - terraform-state - add readonly to group inf-terraform w + +* 1.15.3 -- 2022-04-27 + - terraform-state + - add r-inf-terraform assumable role for TF operations diff --git a/common/defaults.tf b/common/defaults.tf index 6a1f087..768a441 100644 --- a/common/defaults.tf +++ b/common/defaults.tf @@ -47,6 +47,8 @@ locals { ] "api_interval" = 3600 } + "force_detach_policies" = false + "max_session_duration" = 3600 } } diff --git a/common/version.tf b/common/version.tf index 63132b8..01b96da 100644 --- a/common/version.tf +++ b/common/version.tf @@ -1,3 +1,3 @@ locals { - _module_version = "1.15.2" + _module_version = "1.15.3" } diff --git a/terraform-state/README.md b/terraform-state/README.md index 00bd41b..9282fed 100644 --- a/terraform-state/README.md +++ b/terraform-state/README.md @@ -67,18 +67,25 @@ No modules. |------|------| | [aws_dynamodb_table.tfstate](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/dynamodb_table) | resource | | [aws_iam_group.terraform](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group) | resource | +| [aws_iam_group_policy_attachment.terraform_assume](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group_policy_attachment) | resource | | [aws_iam_group_policy_attachment.terraform_managed](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group_policy_attachment) | resource | | [aws_iam_group_policy_attachment.terraform_write](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group_policy_attachment) | resource | +| [aws_iam_policy.allow_assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_policy.tfstate](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_policy.tfstate_read](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_policy.tfstate_write](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_role.role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy_attachment.role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_kms_alias.tfstate_key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource | | [aws_kms_key.tfstate_key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource | | [aws_s3_bucket.tfstate](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | | [aws_s3_bucket_public_access_block.tfstate](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | | [aws_arn.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/arn) | data source | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | -| [aws_iam_policy.managed_policies](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy) | data source | +| [aws_iam_policy.group_managed_policies](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy) | data source | +| [aws_iam_policy.role_managed_policies](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy) | data source | +| [aws_iam_policy_document.allow_assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.allow_sts](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.tfstate](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.tfstate_kms](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.tfstate_read](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | @@ -93,6 +100,7 @@ No modules. | [account\_id](#input\_account\_id) | AWS Account ID (default will pull from current user) | `string` | `""` | no | | [component\_tags](#input\_component\_tags) | Additional tags for Components (s3, kms, ddb) | `map(map(string))` |
{
"ddb": {},
"kms": {},
"s3": {}
}
| no | | [kms\_tfstate\_key](#input\_kms\_tfstate\_key) | Terraform remote state KMS key alias | `string` | `"k-kms-inf-tfstate"` | no | +| [name](#input\_name) | Name suffix to use for policies, roles and groups (default: inf-terraform) | `string` | `"inf-terraform"` | no | | [override\_prefixes](#input\_override\_prefixes) | Override built-in prefixes by component (efs, s3, ebs, kms, role, policy, security-group). This should be used primarily for common infrastructure things | `map(string)` | `{}` | no | | [tags](#input\_tags) | AWS Tags to apply to appropriate resources (S3, KMS). Do not include safeguard tags here, use the data\_safeguard field for such things. | `map(string)` | `{}` | no | | [tfstate\_bucket](#input\_tfstate\_bucket) | Terraform remote state S3 bucket | `string` | `""` | no | @@ -106,6 +114,7 @@ No modules. | Name | Description | |------|-------------| +| [role\_arn](#output\_role\_arn) | Role ARN for Terraform | | [tfstate\_bucket\_arn](#output\_tfstate\_bucket\_arn) | Terraform state S3 bucket ARN | | [tfstate\_bucket\_id](#output\_tfstate\_bucket\_id) | Terraform state S3 bucket ID | | [tfstate\_dynamodb\_arn](#output\_tfstate\_dynamodb\_arn) | Terraform state DynamoDB table ARN | diff --git a/terraform-state/group.tf b/terraform-state/group.tf index c0fc394..c028f56 100644 --- a/terraform-state/group.tf +++ b/terraform-state/group.tf @@ -1,7 +1,7 @@ locals { - group_name = format("%v%v", lookup(local._prefixes, "group", ""), "inf-terraform") + group_name = format("%v%v", lookup(local._prefixes, "group", ""), var.name) group_managed_policies_names = ["ReadOnlyAccess"] - group_managed_policies = [for k, p in data.aws_iam_policy.managed_policies : p.arn] + group_managed_policies = [for k, p in data.aws_iam_policy.group_managed_policies : p.arn] } resource "aws_iam_group" "terraform" { @@ -20,8 +20,13 @@ resource "aws_iam_group_policy_attachment" "terraform_managed" { policy_arn = each.value } +resource "aws_iam_group_policy_attachment" "terraform_assume" { + group = aws_iam_group.terraform.name + policy_arn = aws_iam_policy.allow_assume_role.arn +} + -data "aws_iam_policy" "managed_policies" { +data "aws_iam_policy" "group_managed_policies" { for_each = toset(local.group_managed_policies_names) name = each.key } diff --git a/terraform-state/main.tf b/terraform-state/main.tf index fed1c0c..2efc38f 100644 --- a/terraform-state/main.tf +++ b/terraform-state/main.tf @@ -56,7 +56,7 @@ locals { tfstate_key_arn = aws_kms_key.tfstate_key.arn tfstate_bucket = var.tfstate_bucket != "" ? var.tfstate_bucket : format("%v-%v", var.tfstate_bucket_prefix, local.account_id) - tfstate_policy_name = format("%v%v", lookup(local._prefixes, "policy", ""), var.tfstate_bucket_prefix) + tfstate_policy_name = format("%v%v", lookup(local._prefixes, "policy", ""), var.name) base_tags = { "boc:tf_module_version" = local._module_version @@ -97,28 +97,6 @@ resource "aws_dynamodb_table" "tfstate" { } -# create iam policy for it, to apply to roles/groups as needed -resource "aws_iam_policy" "tfstate" { - name = local.tfstate_policy_name - path = "/" - description = "Access to tfstate resources" - policy = data.aws_iam_policy_document.tfstate.json -} - -resource "aws_iam_policy" "tfstate_read" { - name = format("%v-%v", local.tfstate_policy_name, "read") - path = "/" - description = "Access to tfstate resources (read)" - policy = data.aws_iam_policy_document.tfstate_read.json -} - -resource "aws_iam_policy" "tfstate_write" { - name = format("%v-%v", local.tfstate_policy_name, "write") - path = "/" - description = "Access to tfstate resources (write)" - policy = data.aws_iam_policy_document.tfstate_write.json -} - #--- # s3 #--- diff --git a/terraform-state/outputs.tf b/terraform-state/outputs.tf index e5a5fcf..6324bca 100644 --- a/terraform-state/outputs.tf +++ b/terraform-state/outputs.tf @@ -23,3 +23,7 @@ output "tfstate_region" { value = local.tfstate_region } +output "role_arn" { + description = "Role ARN for Terraform" + value = aws_iam_role.role.arn +} diff --git a/terraform-state/policy_data.tf b/terraform-state/policy.tf similarity index 66% rename from terraform-state/policy_data.tf rename to terraform-state/policy.tf index 00b58e9..29c5487 100644 --- a/terraform-state/policy_data.tf +++ b/terraform-state/policy.tf @@ -1,3 +1,39 @@ +locals { + iam_arn = format("arn:%v:iam::%v:%%v", data.aws_arn.current.partition, data.aws_caller_identity.current.account_id) +} + +# create iam policy for it, to apply to roles/groups as needed +resource "aws_iam_policy" "tfstate" { + name = local.tfstate_policy_name + path = "/" + description = "Access to tfstate resources" + policy = data.aws_iam_policy_document.tfstate.json +} + +resource "aws_iam_policy" "tfstate_read" { + name = format("%v-%v", local.tfstate_policy_name, "read") + path = "/" + description = "Access to tfstate resources (read)" + policy = data.aws_iam_policy_document.tfstate_read.json +} + +resource "aws_iam_policy" "tfstate_write" { + name = format("%v-%v", local.tfstate_policy_name, "write") + path = "/" + description = "Access to tfstate resources (write)" + policy = data.aws_iam_policy_document.tfstate_write.json +} + +resource "aws_iam_policy" "allow_assume_role" { + name = format("%v-%v", var.name, "allow-assume") + path = "/" + description = "Assume role for Terraform activity" + policy = data.aws_iam_policy_document.allow_assume_role.json +} + +#--- +# tfstate s3 bucket +#--- data "aws_iam_policy_document" "tfstate" { statement { sid = "TFRemoteStateList" @@ -129,3 +165,15 @@ data "aws_iam_policy_document" "tfstate_write" { resources = [aws_kms_key.tfstate_key.arn] } } + +#--- +# allow assume the role for use by tf, add to a policy to the group +#--- +data "aws_iam_policy_document" "allow_assume_role" { + statement { + sid = "AllowSTSAssumeTerraformRole" + effect = "Allow" + actions = ["sts:AssumeRole"] + resources = [aws_iam_role.role.arn] + } +} diff --git a/terraform-state/role.tf b/terraform-state/role.tf new file mode 100644 index 0000000..137f9a9 --- /dev/null +++ b/terraform-state/role.tf @@ -0,0 +1,60 @@ +locals { + role_name = format("%v%v", lookup(local._prefixes, "role", ""), var.name) + role_description = format("Role to be assumed Terraform execution %v", var.name) + role_managed_policies_names = ["AdministratorAccess"] + role_managed_policies = [for k, p in data.aws_iam_policy.role_managed_policies : p.arn] +} + + +data "aws_iam_policy" "role_managed_policies" { + for_each = toset(local.role_managed_policies_names) + name = each.key +} + +resource "aws_iam_role" "role" { + name = local.role_name + description = local.role_description + force_detach_policies = local._defaults["force_detach_policies"] + max_session_duration = local._defaults["max_session_duration"] + assume_role_policy = data.aws_iam_policy_document.allow_sts.json + + # dynamic "inline_policy" { + # for_each = var.inline_policies + # iterator = p + # content { + # name = p.value.name + # policy = p.value.policy + # } + # } + + lifecycle { + ignore_changes = [tags["boc:tf_module_version"]] + } + + tags = merge( + local.base_tags, + var.tags, + lookup(var.component_tags, "role", {}), + tomap({ Name = local.role_name }) + ) +} + +resource "aws_iam_role_policy_attachment" "role" { + for_each = { for p in local.role_managed_policies : p => p } + role = aws_iam_role.role.name + policy_arn = each.value +} + +data "aws_iam_policy_document" "allow_sts" { + statement { + sid = "AllowSTSAssume" + effect = "Allow" + actions = ["sts:AssumeRole"] + principals { + type = "AWS" + identifiers = [ + format(local.iam_arn, "root"), + ] + } + } +} diff --git a/terraform-state/variables.tf b/terraform-state/variables.tf index 5430c4d..aaa5218 100644 --- a/terraform-state/variables.tf +++ b/terraform-state/variables.tf @@ -48,3 +48,9 @@ variable "component_tags" { type = map(map(string)) default = { "s3" = {}, "kms" = {}, "ddb" = {} } } + +variable "name" { + description = "Name suffix to use for policies, roles and groups (default: inf-terraform)" + type = string + default = "inf-terraform" +}