From 426a8274bb1abc1363d2eb70643cd14246545700 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Thu, 7 Aug 2025 10:10:42 -0500 Subject: [PATCH 01/21] feat: Remove `iam-eks-role` module --- .pre-commit-config.yaml | 2 +- README.md | 27 ------ examples/iam-eks-role/README.md | 62 -------------- examples/iam-eks-role/main.tf | 96 --------------------- examples/iam-eks-role/outputs.tf | 19 ----- examples/iam-eks-role/variables.tf | 0 examples/iam-eks-role/versions.tf | 14 --- modules/iam-eks-role/README.md | 132 ----------------------------- modules/iam-eks-role/main.tf | 82 ------------------ modules/iam-eks-role/outputs.tf | 19 ----- modules/iam-eks-role/variables.tf | 77 ----------------- modules/iam-eks-role/versions.tf | 10 --- wrappers/iam-eks-role/README.md | 100 ---------------------- wrappers/iam-eks-role/main.tf | 19 ----- wrappers/iam-eks-role/outputs.tf | 5 -- wrappers/iam-eks-role/variables.tf | 11 --- wrappers/iam-eks-role/versions.tf | 10 --- 17 files changed, 1 insertion(+), 684 deletions(-) delete mode 100644 examples/iam-eks-role/README.md delete mode 100644 examples/iam-eks-role/main.tf delete mode 100644 examples/iam-eks-role/outputs.tf delete mode 100644 examples/iam-eks-role/variables.tf delete mode 100644 examples/iam-eks-role/versions.tf delete mode 100644 modules/iam-eks-role/README.md delete mode 100644 modules/iam-eks-role/main.tf delete mode 100644 modules/iam-eks-role/outputs.tf delete mode 100644 modules/iam-eks-role/variables.tf delete mode 100644 modules/iam-eks-role/versions.tf delete mode 100644 wrappers/iam-eks-role/README.md delete mode 100644 wrappers/iam-eks-role/main.tf delete mode 100644 wrappers/iam-eks-role/outputs.tf delete mode 100644 wrappers/iam-eks-role/variables.tf delete mode 100644 wrappers/iam-eks-role/versions.tf diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b84d048d..b7848169 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/antonbabenko/pre-commit-terraform - rev: v1.99.4 + rev: v1.99.5 hooks: - id: terraform_fmt - id: terraform_wrapper_module_for_each diff --git a/README.md b/README.md index dff29526..40484902 100644 --- a/README.md +++ b/README.md @@ -130,32 +130,6 @@ module "iam_assumable_roles_with_saml" { } ``` -`iam-eks-role`: - -```hcl -module "iam_eks_role" { - source = "terraform-aws-modules/iam/aws//modules/iam-eks-role" - - role_name = "my-app" - - cluster_service_accounts = { - "cluster1" = ["default:my-app"] - "cluster2" = [ - "default:my-app", - "canary:my-app", - ] - } - - tags = { - Name = "eks-role" - } - - role_policy_arns = { - AmazonEKS_CNI_Policy = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy" - } -} -``` - `iam-github-oidc-provider`: ```hcl @@ -360,7 +334,6 @@ Use [iam-read-only-policy module](https://github.com/terraform-aws-modules/terra - [iam-assumable-role](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-assumable-role) - Create individual IAM role which can be assumed from specified ARNs (AWS accounts, IAM users, etc) - [iam-assumable-roles-with-saml](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-assumable-roles-with-saml) - Create several IAM roles which can be assumed by users with a SAML Identity Provider - [iam-assumable-roles](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-assumable-roles) - Create several IAM roles which can be assumed from specified ARNs (AWS accounts, IAM users, etc) -- [iam-eks-role](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-eks-role) - Create an IAM role that can be assumed by one or more EKS `ServiceAccount` - [iam-group-complete](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-group-complete) - IAM group with users who are allowed to assume IAM roles in another AWS account and have access to specified IAM policies - [iam-group-with-assumable-roles-policy](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-group-with-assumable-roles-policy) - IAM group with users who are allowed to assume IAM roles in the same or in separate AWS account - [iam-group-with-policies](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-group-with-policies) - IAM group with users who are allowed specified IAM policies (eg, "manage their own IAM user") diff --git a/examples/iam-eks-role/README.md b/examples/iam-eks-role/README.md deleted file mode 100644 index 06e8513c..00000000 --- a/examples/iam-eks-role/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# IAM EKS role - -Configuration in this directory creates an IAM role that can be assumed by multiple EKS `ServiceAccount`. - -# Usage - -To run this example you need to execute: - -```bash -$ terraform init -$ terraform plan -$ terraform apply -``` - -Run `terraform destroy` when you don't need these resources. - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0, < 6.0 | -| [random](#requirement\_random) | >= 2.0 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 4.0, < 6.0 | -| [random](#provider\_random) | >= 2.0 | - -## Modules - -| Name | Source | Version | -|------|--------|---------| -| [eks](#module\_eks) | terraform-aws-modules/eks/aws | ~> 18.0 | -| [iam\_eks\_role](#module\_iam\_eks\_role) | ../../modules/iam-eks-role | n/a | -| [iam\_eks\_role\_with\_assume\_wildcard](#module\_iam\_eks\_role\_with\_assume\_wildcard) | ../../modules/iam-eks-role | n/a | -| [iam\_eks\_role\_with\_self\_assume](#module\_iam\_eks\_role\_with\_self\_assume) | ../../modules/iam-eks-role | n/a | - -## Resources - -| Name | Type | -|------|------| -| [random_pet.this](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/pet) | resource | -| [aws_subnets.all](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnets) | data source | -| [aws_vpc.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc) | data source | - -## Inputs - -No inputs. - -## Outputs - -| Name | Description | -|------|-------------| -| [iam\_role\_arn](#output\_iam\_role\_arn) | ARN of IAM role | -| [iam\_role\_name](#output\_iam\_role\_name) | Name of IAM role | -| [iam\_role\_path](#output\_iam\_role\_path) | Path of IAM role | -| [iam\_role\_unique\_id](#output\_iam\_role\_unique\_id) | Unique ID of IAM role | - diff --git a/examples/iam-eks-role/main.tf b/examples/iam-eks-role/main.tf deleted file mode 100644 index 5f3823ec..00000000 --- a/examples/iam-eks-role/main.tf +++ /dev/null @@ -1,96 +0,0 @@ -provider "aws" { - region = "eu-west-1" -} - -module "iam_eks_role" { - source = "../../modules/iam-eks-role" - role_name = "my-app" - - cluster_service_accounts = { - (random_pet.this.id) = ["default:my-app"] - } - - tags = { - Name = "eks-role" - } - - role_policy_arns = { - AmazonEKS_CNI_Policy = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy" - } -} - -############################### -# IAM EKS role with self assume -############################### -module "iam_eks_role_with_self_assume" { - source = "../../modules/iam-eks-role" - role_name = "my-app-self-assume" - - allow_self_assume_role = true - cluster_service_accounts = { - (random_pet.this.id) = ["default:my-app"] - } - - tags = { - Name = "eks-role" - } - - role_policy_arns = { - AmazonEKS_CNI_Policy = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy" - } -} - -############################################# -# IAM EKS role with wildcard assume condition -############################################# -module "iam_eks_role_with_assume_wildcard" { - source = "../../modules/iam-eks-role" - role_name = "my-app-assume-wildcard" - - cluster_service_accounts = { - (random_pet.this.id) = ["default:my-app-prefix-*"] - } - assume_role_condition_test = "StringLike" - - tags = { - Name = "my-app-assume-wildcard" - } - - role_policy_arns = { - AmazonEKS_CNI_Policy = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy" - } -} - -################## -# Extra resources -################## - -resource "random_pet" "this" { - length = 2 -} - -module "eks" { - source = "terraform-aws-modules/eks/aws" - version = "~> 18.0" - - cluster_name = random_pet.this.id - cluster_version = "1.21" - - vpc_id = data.aws_vpc.default.id - subnet_ids = data.aws_subnets.all.ids -} - -################################################################## -# Data sources to get VPC, subnet, security group and AMI details -################################################################## - -data "aws_vpc" "default" { - default = true -} - -data "aws_subnets" "all" { - filter { - name = "vpc-id" - values = [data.aws_vpc.default.id] - } -} diff --git a/examples/iam-eks-role/outputs.tf b/examples/iam-eks-role/outputs.tf deleted file mode 100644 index edac4b57..00000000 --- a/examples/iam-eks-role/outputs.tf +++ /dev/null @@ -1,19 +0,0 @@ -output "iam_role_arn" { - description = "ARN of IAM role" - value = module.iam_eks_role.iam_role_arn -} - -output "iam_role_name" { - description = "Name of IAM role" - value = module.iam_eks_role.iam_role_name -} - -output "iam_role_path" { - description = "Path of IAM role" - value = module.iam_eks_role.iam_role_path -} - -output "iam_role_unique_id" { - description = "Unique ID of IAM role" - value = module.iam_eks_role.iam_role_unique_id -} diff --git a/examples/iam-eks-role/variables.tf b/examples/iam-eks-role/variables.tf deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/iam-eks-role/versions.tf b/examples/iam-eks-role/versions.tf deleted file mode 100644 index 3de64f64..00000000 --- a/examples/iam-eks-role/versions.tf +++ /dev/null @@ -1,14 +0,0 @@ -terraform { - required_version = ">= 1.0" - - required_providers { - aws = { - source = "hashicorp/aws" - version = ">= 4.0, < 6.0" - } - random = { - source = "hashicorp/random" - version = ">= 2.0" - } - } -} diff --git a/modules/iam-eks-role/README.md b/modules/iam-eks-role/README.md deleted file mode 100644 index 0d46f8f0..00000000 --- a/modules/iam-eks-role/README.md +++ /dev/null @@ -1,132 +0,0 @@ -# iam-eks-role - -Creates an IAM role that can be assumed by one or more EKS `ServiceAccount` in one or more EKS clusters. Unlike [iam-assumable-role-with-oidc](https://github.com/terraform-aws-modules/terraform-aws-iam/blob/master/modules/iam-assumable-role-with-oidc/), this module: - -- Does not require any knowledge of cluster OIDC information as `data` resources are used -- Supports assuming the role from multiple EKS clusters, for example used in DR or when a workload is spread across clusters -- Support multiple `ServiceAccount` in the same cluster, for example when a workload runs in multiple namespaces -- More suitable for non-cluster admins as implementation is simpler -- More suitable for when the IAM role and cluster resources are in separate Terraform configurations -- Does not support multiple regions, consider using [iam-role-for-service-accounts-eks](https://github.com/terraform-aws-modules/terraform-aws-iam/blob/master/modules/iam-role-for-service-accounts-eks/) if role needs to support clusters in multiple regions - -This module is for use with AWS EKS. For details of how a `ServiceAccount` in EKS can assume an IAM role, see the [EKS documentation](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html). - -Implementation notes: - -- The EKS cluster needs to exist first, in the current AWS account and region -- The key in the `cluster_service_accounts` is the exact name of the EKS cluster - -## Basic example - -To create an IAM role named `my-app` that can be assumed in EKS cluster `cluster1` by a `ServiceAccount` called `my-serviceaccount` in the `default` namespace: - -```hcl -module "iam_eks_role" { - source = "terraform-aws-modules/iam/aws//modules/iam-eks-role" - - role_name = "my-app" - - cluster_service_accounts = { - "cluster1" = ["default:my-serviceaccount"] - } -} -``` - -## Multi cluster example: - -To create an IAM role named `my-app` that can be assumed from: - -- EKS cluster `staging-main-1`, namespace `default`, `ServiceAccount` called `my-app-staging` -- EKS cluster `staging-backup-1`, namespace `default`, `ServiceAccount` called `my-app-staging` - -```hcl -module "iam_eks_role" { - source = "terraform-aws-modules/iam/aws//modules/iam-eks-role" - - role_name = "my-app" - - cluster_service_accounts = { - "staging-main-1" = ["default:my-app-staging"] - "staging-backup-1" = ["default:my-app-staging"] - } -} -``` - -## Multi `ServiceAccount` example - -To create an IAM role named `cloudwatch-exporter` that can be assumed in EKS cluster `production-main-1` from: - -- namespace `kube-system`, `ServiceAccount` called `cloudwatch-exporter` -- namespace `app1`, `ServiceAccount` called `cloudwatch-exporter` - -```hcl -module "iam_eks_role" { - source = "terraform-aws-modules/iam/aws//modules/iam-eks-role" - - role_name = "my-app" - - cluster_service_accounts = { - "production-main-1" = [ - "kube-system:cloudwatch-exporter", - "app1:cloudwatch-exporter", - ] - } -} -``` - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 4.0 | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | -| [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | -| [aws_eks_cluster.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/eks_cluster) | data source | -| [aws_iam_policy_document.assume_role_with_oidc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [allow\_self\_assume\_role](#input\_allow\_self\_assume\_role) | Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/) | `bool` | `false` | no | -| [assume\_role\_condition\_test](#input\_assume\_role\_condition\_test) | Name of the [IAM condition operator](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition_operators.html) to evaluate when assuming the role | `string` | `"StringEquals"` | no | -| [cluster\_service\_accounts](#input\_cluster\_service\_accounts) | EKS cluster and k8s ServiceAccount pairs. Each EKS cluster can have multiple k8s ServiceAccount. See README for details | `map(list(string))` | `{}` | no | -| [create\_role](#input\_create\_role) | Whether to create a role | `bool` | `true` | no | -| [force\_detach\_policies](#input\_force\_detach\_policies) | Whether policies should be detached from this role when destroying | `bool` | `false` | no | -| [max\_session\_duration](#input\_max\_session\_duration) | Maximum CLI/API session duration in seconds between 3600 and 43200 | `number` | `43200` | no | -| [role\_description](#input\_role\_description) | IAM Role description | `string` | `""` | no | -| [role\_name](#input\_role\_name) | Name of IAM role | `string` | `null` | no | -| [role\_name\_prefix](#input\_role\_name\_prefix) | IAM role name prefix | `string` | `null` | no | -| [role\_path](#input\_role\_path) | Path of IAM role | `string` | `"/"` | no | -| [role\_permissions\_boundary\_arn](#input\_role\_permissions\_boundary\_arn) | Permissions boundary ARN to use for IAM role | `string` | `""` | no | -| [role\_policy\_arns](#input\_role\_policy\_arns) | ARNs of any policies to attach to the IAM role | `map(string)` | `{}` | no | -| [tags](#input\_tags) | A map of tags to add the the IAM role | `map(any)` | `{}` | no | - -## Outputs - -| Name | Description | -|------|-------------| -| [iam\_role\_arn](#output\_iam\_role\_arn) | ARN of IAM role | -| [iam\_role\_name](#output\_iam\_role\_name) | Name of IAM role | -| [iam\_role\_path](#output\_iam\_role\_path) | Path of IAM role | -| [iam\_role\_unique\_id](#output\_iam\_role\_unique\_id) | Unique ID of IAM role | - diff --git a/modules/iam-eks-role/main.tf b/modules/iam-eks-role/main.tf deleted file mode 100644 index 2b4c1c6d..00000000 --- a/modules/iam-eks-role/main.tf +++ /dev/null @@ -1,82 +0,0 @@ -data "aws_caller_identity" "current" {} -data "aws_partition" "current" {} - -locals { - account_id = data.aws_caller_identity.current.account_id - partition = data.aws_partition.current.partition - role_name_condition = var.role_name != null ? var.role_name : "${var.role_name_prefix}*" -} - -data "aws_eks_cluster" "main" { - for_each = { for k, v in var.cluster_service_accounts : k => v if var.create_role } - - name = each.key -} - -data "aws_iam_policy_document" "assume_role_with_oidc" { - dynamic "statement" { - # https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/ - for_each = var.allow_self_assume_role ? [1] : [] - - content { - sid = "ExplicitSelfRoleAssumption" - effect = "Allow" - actions = ["sts:AssumeRole"] - - principals { - type = "AWS" - identifiers = ["*"] - } - - condition { - test = "ArnLike" - variable = "aws:PrincipalArn" - values = ["arn:${local.partition}:iam::${local.account_id}:role${var.role_path}${local.role_name_condition}"] - } - } - } - - dynamic "statement" { - for_each = { for k, v in var.cluster_service_accounts : k => v if var.create_role } - - content { - effect = "Allow" - actions = ["sts:AssumeRoleWithWebIdentity"] - - principals { - type = "Federated" - - identifiers = [ - "arn:${local.partition}:iam::${local.account_id}:oidc-provider/${replace(data.aws_eks_cluster.main[statement.key].identity[0].oidc[0].issuer, "https://", "")}" - ] - } - - condition { - test = var.assume_role_condition_test - variable = "${replace(data.aws_eks_cluster.main[statement.key].identity[0].oidc[0].issuer, "https://", "")}:sub" - values = [for s in statement.value : "system:serviceaccount:${s}"] - } - } - } -} - -resource "aws_iam_role" "this" { - count = var.create_role ? 1 : 0 - - assume_role_policy = data.aws_iam_policy_document.assume_role_with_oidc.json - description = var.role_description - force_detach_policies = var.force_detach_policies - max_session_duration = var.max_session_duration - name = var.role_name - name_prefix = var.role_name_prefix - path = var.role_path - permissions_boundary = var.role_permissions_boundary_arn - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "this" { - for_each = { for k, v in var.role_policy_arns : k => v if var.create_role } - - role = aws_iam_role.this[0].name - policy_arn = each.value -} diff --git a/modules/iam-eks-role/outputs.tf b/modules/iam-eks-role/outputs.tf deleted file mode 100644 index a6805310..00000000 --- a/modules/iam-eks-role/outputs.tf +++ /dev/null @@ -1,19 +0,0 @@ -output "iam_role_arn" { - description = "ARN of IAM role" - value = try(aws_iam_role.this[0].arn, "") -} - -output "iam_role_name" { - description = "Name of IAM role" - value = try(aws_iam_role.this[0].name, "") -} - -output "iam_role_path" { - description = "Path of IAM role" - value = try(aws_iam_role.this[0].path, "") -} - -output "iam_role_unique_id" { - description = "Unique ID of IAM role" - value = try(aws_iam_role.this[0].unique_id, "") -} diff --git a/modules/iam-eks-role/variables.tf b/modules/iam-eks-role/variables.tf deleted file mode 100644 index 69fc85e2..00000000 --- a/modules/iam-eks-role/variables.tf +++ /dev/null @@ -1,77 +0,0 @@ -variable "create_role" { - description = "Whether to create a role" - type = bool - default = true -} - -variable "role_name" { - description = "Name of IAM role" - type = string - default = null -} - -variable "role_path" { - description = "Path of IAM role" - type = string - default = "/" -} - -variable "role_permissions_boundary_arn" { - description = "Permissions boundary ARN to use for IAM role" - type = string - default = "" -} - -variable "role_description" { - description = "IAM Role description" - type = string - default = "" -} - -variable "role_name_prefix" { - description = "IAM role name prefix" - type = string - default = null -} - -variable "role_policy_arns" { - description = "ARNs of any policies to attach to the IAM role" - type = map(string) - default = {} -} - -variable "cluster_service_accounts" { - description = "EKS cluster and k8s ServiceAccount pairs. Each EKS cluster can have multiple k8s ServiceAccount. See README for details" - type = map(list(string)) - default = {} -} - -variable "tags" { - description = "A map of tags to add the the IAM role" - type = map(any) - default = {} -} - -variable "force_detach_policies" { - description = "Whether policies should be detached from this role when destroying" - type = bool - default = false -} - -variable "max_session_duration" { - description = "Maximum CLI/API session duration in seconds between 3600 and 43200" - type = number - default = 43200 -} - -variable "allow_self_assume_role" { - description = "Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/)" - type = bool - default = false -} - -variable "assume_role_condition_test" { - description = "Name of the [IAM condition operator](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition_operators.html) to evaluate when assuming the role" - type = string - default = "StringEquals" -} diff --git a/modules/iam-eks-role/versions.tf b/modules/iam-eks-role/versions.tf deleted file mode 100644 index d8dd1a44..00000000 --- a/modules/iam-eks-role/versions.tf +++ /dev/null @@ -1,10 +0,0 @@ -terraform { - required_version = ">= 1.0" - - required_providers { - aws = { - source = "hashicorp/aws" - version = ">= 4.0" - } - } -} diff --git a/wrappers/iam-eks-role/README.md b/wrappers/iam-eks-role/README.md deleted file mode 100644 index f91dbe06..00000000 --- a/wrappers/iam-eks-role/README.md +++ /dev/null @@ -1,100 +0,0 @@ -# Wrapper for module: `modules/iam-eks-role` - -The configuration in this directory contains an implementation of a single module wrapper pattern, which allows managing several copies of a module in places where using the native Terraform 0.13+ `for_each` feature is not feasible (e.g., with Terragrunt). - -You may want to use a single Terragrunt configuration file to manage multiple resources without duplicating `terragrunt.hcl` files for each copy of the same module. - -This wrapper does not implement any extra functionality. - -## Usage with Terragrunt - -`terragrunt.hcl`: - -```hcl -terraform { - source = "tfr:///terraform-aws-modules/iam/aws//wrappers/iam-eks-role" - # Alternative source: - # source = "git::git@github.com:terraform-aws-modules/terraform-aws-iam.git//wrappers/iam-eks-role?ref=master" -} - -inputs = { - defaults = { # Default values - create = true - tags = { - Terraform = "true" - Environment = "dev" - } - } - - items = { - my-item = { - # omitted... can be any argument supported by the module - } - my-second-item = { - # omitted... can be any argument supported by the module - } - # omitted... - } -} -``` - -## Usage with Terraform - -```hcl -module "wrapper" { - source = "terraform-aws-modules/iam/aws//wrappers/iam-eks-role" - - defaults = { # Default values - create = true - tags = { - Terraform = "true" - Environment = "dev" - } - } - - items = { - my-item = { - # omitted... can be any argument supported by the module - } - my-second-item = { - # omitted... can be any argument supported by the module - } - # omitted... - } -} -``` - -## Example: Manage multiple S3 buckets in one Terragrunt layer - -`eu-west-1/s3-buckets/terragrunt.hcl`: - -```hcl -terraform { - source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers" - # Alternative source: - # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//wrappers?ref=master" -} - -inputs = { - defaults = { - force_destroy = true - - attach_elb_log_delivery_policy = true - attach_lb_log_delivery_policy = true - attach_deny_insecure_transport_policy = true - attach_require_latest_tls_policy = true - } - - items = { - bucket1 = { - bucket = "my-random-bucket-1" - } - bucket2 = { - bucket = "my-random-bucket-2" - tags = { - Secure = "probably" - } - } - } -} -``` diff --git a/wrappers/iam-eks-role/main.tf b/wrappers/iam-eks-role/main.tf deleted file mode 100644 index 687be7a8..00000000 --- a/wrappers/iam-eks-role/main.tf +++ /dev/null @@ -1,19 +0,0 @@ -module "wrapper" { - source = "../../modules/iam-eks-role" - - for_each = var.items - - allow_self_assume_role = try(each.value.allow_self_assume_role, var.defaults.allow_self_assume_role, false) - assume_role_condition_test = try(each.value.assume_role_condition_test, var.defaults.assume_role_condition_test, "StringEquals") - cluster_service_accounts = try(each.value.cluster_service_accounts, var.defaults.cluster_service_accounts, {}) - create_role = try(each.value.create_role, var.defaults.create_role, true) - force_detach_policies = try(each.value.force_detach_policies, var.defaults.force_detach_policies, false) - max_session_duration = try(each.value.max_session_duration, var.defaults.max_session_duration, 43200) - role_description = try(each.value.role_description, var.defaults.role_description, "") - role_name = try(each.value.role_name, var.defaults.role_name, null) - role_name_prefix = try(each.value.role_name_prefix, var.defaults.role_name_prefix, null) - role_path = try(each.value.role_path, var.defaults.role_path, "/") - role_permissions_boundary_arn = try(each.value.role_permissions_boundary_arn, var.defaults.role_permissions_boundary_arn, "") - role_policy_arns = try(each.value.role_policy_arns, var.defaults.role_policy_arns, {}) - tags = try(each.value.tags, var.defaults.tags, {}) -} diff --git a/wrappers/iam-eks-role/outputs.tf b/wrappers/iam-eks-role/outputs.tf deleted file mode 100644 index ec6da5f4..00000000 --- a/wrappers/iam-eks-role/outputs.tf +++ /dev/null @@ -1,5 +0,0 @@ -output "wrapper" { - description = "Map of outputs of a wrapper." - value = module.wrapper - # sensitive = false # No sensitive module output found -} diff --git a/wrappers/iam-eks-role/variables.tf b/wrappers/iam-eks-role/variables.tf deleted file mode 100644 index a6ea0962..00000000 --- a/wrappers/iam-eks-role/variables.tf +++ /dev/null @@ -1,11 +0,0 @@ -variable "defaults" { - description = "Map of default values which will be used for each item." - type = any - default = {} -} - -variable "items" { - description = "Maps of items to create a wrapper from. Values are passed through to the module." - type = any - default = {} -} diff --git a/wrappers/iam-eks-role/versions.tf b/wrappers/iam-eks-role/versions.tf deleted file mode 100644 index d8dd1a44..00000000 --- a/wrappers/iam-eks-role/versions.tf +++ /dev/null @@ -1,10 +0,0 @@ -terraform { - required_version = ">= 1.0" - - required_providers { - aws = { - source = "hashicorp/aws" - version = ">= 4.0" - } - } -} From bccf1093832c6a0e5609fdbb53398370c5cec95d Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Thu, 7 Aug 2025 10:15:37 -0500 Subject: [PATCH 02/21] feat: Add bulk changes from `feat/upgrade` branch --- README.md | 376 ++++------- examples/README.md | 8 + examples/iam-account/README.md | 2 +- .../iam-assumable-role-with-oidc/README.md | 54 -- examples/iam-assumable-role-with-oidc/main.tf | 122 ---- .../iam-assumable-role-with-oidc/outputs.tf | 19 - .../iam-assumable-role-with-saml/README.md | 57 -- examples/iam-assumable-role-with-saml/main.tf | 58 -- .../iam-assumable-role-with-saml/outputs.tf | 19 - examples/iam-assumable-role/README.md | 64 -- examples/iam-assumable-role/main.tf | 218 ------ examples/iam-assumable-role/outputs.tf | 29 - .../iam-assumable-roles-with-saml/README.md | 67 -- .../iam-assumable-roles-with-saml/main.tf | 79 --- .../iam-assumable-roles-with-saml/outputs.tf | 62 -- .../saml-metadata.xml | 14 - examples/iam-assumable-roles/README.md | 63 -- examples/iam-assumable-roles/main.tf | 55 -- examples/iam-assumable-roles/outputs.tf | 77 --- examples/iam-github-oidc/README.md | 63 -- examples/iam-github-oidc/main.tf | 103 --- examples/iam-github-oidc/outputs.tf | 37 -- examples/iam-github-oidc/variables.tf | 0 examples/iam-group-complete/README.md | 54 -- examples/iam-group-complete/main.tf | 51 -- examples/iam-group-complete/outputs.tf | 14 - examples/iam-group-complete/variables.tf | 0 examples/iam-group-complete/versions.tf | 3 - .../README.md | 64 -- .../main.tf | 134 ---- .../outputs.tf | 24 - .../variables.tf | 0 examples/iam-group-with-policies/main.tf | 79 --- examples/iam-group-with-policies/outputs.tf | 19 - examples/iam-group-with-policies/variables.tf | 0 .../README.md | 20 +- examples/iam-group/main.tf | 97 +++ examples/iam-group/outputs.tf | 43 ++ .../variables.tf | 0 .../versions.tf | 0 examples/iam-oidc-provider/README.md | 58 ++ examples/iam-oidc-provider/main.tf | 61 ++ examples/iam-oidc-provider/outputs.tf | 28 + .../variables.tf | 0 .../versions.tf | 0 examples/iam-policy/README.md | 59 -- examples/iam-policy/main.tf | 61 -- examples/iam-policy/outputs.tf | 29 - examples/iam-policy/variables.tf | 0 examples/iam-read-only-policy/README.md | 19 +- examples/iam-read-only-policy/main.tf | 42 +- examples/iam-read-only-policy/outputs.tf | 21 +- .../README.md | 82 --- .../iam-role-for-service-accounts-eks/main.tf | 516 --------------- .../outputs.tf | 19 - .../variables.tf | 0 .../versions.tf | 10 - .../iam-role-for-service-accounts/README.md | 81 +++ .../iam-role-for-service-accounts/main.tf | 461 +++++++++++++ .../iam-role-for-service-accounts/outputs.tf | 36 + .../variables.tf | 0 .../versions.tf | 0 examples/iam-role-saml/README.md | 56 ++ examples/iam-role-saml/main.tf | 73 ++ examples/iam-role-saml/outputs.tf | 18 + .../saml-metadata.xml | 0 .../variables.tf | 0 .../versions.tf | 0 examples/iam-role/README.md | 69 ++ examples/iam-role/main.tf | 172 +++++ examples/iam-role/outputs.tf | 77 +++ .../variables.tf | 0 .../versions.tf | 0 examples/iam-user/README.md | 65 +- examples/iam-user/main.tf | 61 +- examples/iam-user/outputs.tf | 239 +++++-- modules/iam-account/README.md | 31 +- .../iam-assumable-role-with-oidc/README.md | 72 -- modules/iam-assumable-role-with-oidc/main.tf | 180 ----- .../iam-assumable-role-with-oidc/outputs.tf | 19 - .../iam-assumable-role-with-oidc/variables.tf | 123 ---- .../iam-assumable-role-with-saml/README.md | 65 -- modules/iam-assumable-role-with-saml/main.tf | 74 --- .../iam-assumable-role-with-saml/outputs.tf | 19 - .../iam-assumable-role-with-saml/variables.tf | 95 --- modules/iam-assumable-role/README.md | 92 --- modules/iam-assumable-role/main.tf | 284 -------- modules/iam-assumable-role/outputs.tf | 49 -- modules/iam-assumable-role/variables.tf | 190 ------ .../iam-assumable-roles-with-saml/README.md | 87 --- modules/iam-assumable-roles-with-saml/main.tf | 158 ----- .../iam-assumable-roles-with-saml/outputs.tf | 61 -- .../variables.tf | 152 ----- modules/iam-assumable-roles/README.md | 93 --- modules/iam-assumable-roles/main.tf | 274 -------- modules/iam-assumable-roles/outputs.tf | 77 --- modules/iam-assumable-roles/variables.tf | 180 ----- modules/iam-github-oidc-role/README.md | 109 --- modules/iam-github-oidc-role/main.tf | 84 --- modules/iam-github-oidc-role/variables.tf | 97 --- .../README.md | 53 -- .../main.tf | 34 - .../outputs.tf | 24 - .../variables.tf | 34 - .../versions.tf | 10 - modules/iam-group-with-policies/README.md | 62 -- modules/iam-group-with-policies/main.tf | 64 -- modules/iam-group-with-policies/outputs.tf | 19 - modules/iam-group-with-policies/policies.tf | 182 ----- modules/iam-group-with-policies/variables.tf | 65 -- modules/iam-group-with-policies/versions.tf | 10 - modules/iam-group/README.md | 105 +++ modules/iam-group/main.tf | 247 +++++++ modules/iam-group/outputs.tf | 47 ++ modules/iam-group/variables.tf | 91 +++ .../iam-group}/versions.tf | 0 .../README.md | 37 +- .../main.tf | 2 +- .../outputs.tf | 2 +- .../variables.tf | 10 - .../versions.tf | 0 modules/iam-policy/README.md | 51 -- modules/iam-policy/main.tf | 12 - modules/iam-policy/outputs.tf | 29 - modules/iam-policy/variables.tf | 41 -- modules/iam-policy/versions.tf | 10 - modules/iam-read-only-policy/README.md | 55 +- modules/iam-read-only-policy/main.tf | 84 ++- modules/iam-read-only-policy/outputs.tf | 30 +- modules/iam-read-only-policy/variables.tf | 33 +- .../iam-role-for-service-accounts-eks/main.tf | 88 --- .../outputs.tf | 19 - .../versions.tf | 10 - .../README.md | 181 +++-- modules/iam-role-for-service-accounts/main.tf | 165 +++++ .../iam-role-for-service-accounts/outputs.tf | 37 ++ .../policies.tf | 626 +----------------- .../variables.tf | 238 +++---- .../versions.tf | 0 modules/iam-role-oidc/README.md | 118 ++++ modules/iam-role-oidc/main.tf | 221 +++++++ .../outputs.tf | 17 +- modules/iam-role-oidc/variables.tf | 111 ++++ .../iam-role-oidc}/versions.tf | 0 modules/iam-role-saml/README.md | 89 +++ modules/iam-role-saml/main.tf | 116 ++++ modules/iam-role-saml/outputs.tf | 18 + modules/iam-role-saml/variables.tf | 87 +++ .../iam-role-saml}/versions.tf | 0 modules/iam-role/README.md | 104 +++ modules/iam-role/main.tf | 120 ++++ modules/iam-role/outputs.tf | 42 ++ modules/iam-role/variables.tf | 79 +++ .../versions.tf | 0 modules/iam-user/README.md | 104 +-- modules/iam-user/main.tf | 40 +- modules/iam-user/outputs.tf | 161 ++--- modules/iam-user/variables.tf | 93 +-- .../iam-assumable-role-with-oidc/README.md | 100 --- wrappers/iam-assumable-role-with-oidc/main.tf | 26 - .../iam-assumable-role-with-oidc/versions.tf | 10 - .../iam-assumable-role-with-saml/README.md | 100 --- wrappers/iam-assumable-role-with-saml/main.tf | 22 - .../iam-assumable-role-with-saml/versions.tf | 10 - wrappers/iam-assumable-role/main.tf | 37 -- wrappers/iam-assumable-role/versions.tf | 10 - .../iam-assumable-roles-with-saml/README.md | 100 --- .../iam-assumable-roles-with-saml/main.tf | 31 - .../iam-assumable-roles-with-saml/versions.tf | 10 - wrappers/iam-assumable-roles/main.tf | 35 - wrappers/iam-assumable-roles/versions.tf | 10 - wrappers/iam-github-oidc-provider/README.md | 100 --- wrappers/iam-github-oidc-role/main.tf | 21 - wrappers/iam-github-oidc-role/outputs.tf | 5 - wrappers/iam-github-oidc-role/variables.tf | 11 - wrappers/iam-github-oidc-role/versions.tf | 10 - .../README.md | 100 --- .../main.tf | 12 - .../outputs.tf | 5 - .../variables.tf | 11 - .../versions.tf | 10 - wrappers/iam-group-with-policies/README.md | 100 --- wrappers/iam-group-with-policies/main.tf | 17 - wrappers/iam-group-with-policies/outputs.tf | 5 - wrappers/iam-group-with-policies/variables.tf | 11 - wrappers/iam-group-with-policies/versions.tf | 10 - .../README.md | 8 +- wrappers/iam-group/main.tf | 20 + .../outputs.tf | 0 .../variables.tf | 0 .../iam-group}/versions.tf | 0 .../README.md | 8 +- .../main.tf | 6 +- .../outputs.tf | 0 .../variables.tf | 0 .../versions.tf | 0 wrappers/iam-policy/main.tf | 13 - wrappers/iam-policy/outputs.tf | 5 - wrappers/iam-policy/variables.tf | 11 - wrappers/iam-policy/versions.tf | 10 - wrappers/iam-read-only-policy/main.tf | 5 +- .../outputs.tf | 5 - .../variables.tf | 11 - .../versions.tf | 10 - .../README.md | 8 +- .../main.tf | 58 +- .../outputs.tf | 0 .../variables.tf | 0 .../versions.tf | 0 .../README.md | 8 +- wrappers/iam-role-oidc/main.tf | 24 + .../outputs.tf | 0 .../variables.tf | 0 .../iam-role-oidc}/versions.tf | 0 wrappers/iam-role-saml/README.md | 100 +++ wrappers/iam-role-saml/main.tf | 20 + .../outputs.tf | 0 .../variables.tf | 0 .../iam-role-saml}/versions.tf | 0 wrappers/{iam-policy => iam-role}/README.md | 8 +- wrappers/iam-role/main.tf | 18 + .../outputs.tf | 0 .../variables.tf | 0 .../iam-role}/versions.tf | 0 wrappers/iam-user/main.tf | 31 +- wrappers/iam-user/outputs.tf | 2 +- 226 files changed, 4404 insertions(+), 8567 deletions(-) create mode 100644 examples/README.md delete mode 100644 examples/iam-assumable-role-with-oidc/README.md delete mode 100644 examples/iam-assumable-role-with-oidc/main.tf delete mode 100644 examples/iam-assumable-role-with-oidc/outputs.tf delete mode 100644 examples/iam-assumable-role-with-saml/README.md delete mode 100644 examples/iam-assumable-role-with-saml/main.tf delete mode 100644 examples/iam-assumable-role-with-saml/outputs.tf delete mode 100644 examples/iam-assumable-role/README.md delete mode 100644 examples/iam-assumable-role/main.tf delete mode 100644 examples/iam-assumable-role/outputs.tf delete mode 100644 examples/iam-assumable-roles-with-saml/README.md delete mode 100644 examples/iam-assumable-roles-with-saml/main.tf delete mode 100644 examples/iam-assumable-roles-with-saml/outputs.tf delete mode 100644 examples/iam-assumable-roles-with-saml/saml-metadata.xml delete mode 100644 examples/iam-assumable-roles/README.md delete mode 100644 examples/iam-assumable-roles/main.tf delete mode 100644 examples/iam-assumable-roles/outputs.tf delete mode 100644 examples/iam-github-oidc/README.md delete mode 100644 examples/iam-github-oidc/main.tf delete mode 100644 examples/iam-github-oidc/outputs.tf delete mode 100644 examples/iam-github-oidc/variables.tf delete mode 100644 examples/iam-group-complete/README.md delete mode 100644 examples/iam-group-complete/main.tf delete mode 100644 examples/iam-group-complete/outputs.tf delete mode 100644 examples/iam-group-complete/variables.tf delete mode 100644 examples/iam-group-complete/versions.tf delete mode 100644 examples/iam-group-with-assumable-roles-policy/README.md delete mode 100644 examples/iam-group-with-assumable-roles-policy/main.tf delete mode 100644 examples/iam-group-with-assumable-roles-policy/outputs.tf delete mode 100644 examples/iam-group-with-assumable-roles-policy/variables.tf delete mode 100644 examples/iam-group-with-policies/main.tf delete mode 100644 examples/iam-group-with-policies/outputs.tf delete mode 100644 examples/iam-group-with-policies/variables.tf rename examples/{iam-group-with-policies => iam-group}/README.md (53%) create mode 100644 examples/iam-group/main.tf create mode 100644 examples/iam-group/outputs.tf rename examples/{iam-assumable-role-with-oidc => iam-group}/variables.tf (100%) rename examples/{iam-assumable-role-with-oidc => iam-group}/versions.tf (100%) create mode 100644 examples/iam-oidc-provider/README.md create mode 100644 examples/iam-oidc-provider/main.tf create mode 100644 examples/iam-oidc-provider/outputs.tf rename examples/{iam-assumable-role-with-saml => iam-oidc-provider}/variables.tf (100%) rename examples/{iam-assumable-role-with-saml => iam-oidc-provider}/versions.tf (100%) delete mode 100644 examples/iam-policy/README.md delete mode 100644 examples/iam-policy/main.tf delete mode 100644 examples/iam-policy/outputs.tf delete mode 100644 examples/iam-policy/variables.tf delete mode 100644 examples/iam-role-for-service-accounts-eks/README.md delete mode 100644 examples/iam-role-for-service-accounts-eks/main.tf delete mode 100644 examples/iam-role-for-service-accounts-eks/outputs.tf delete mode 100644 examples/iam-role-for-service-accounts-eks/variables.tf delete mode 100644 examples/iam-role-for-service-accounts-eks/versions.tf create mode 100644 examples/iam-role-for-service-accounts/README.md create mode 100644 examples/iam-role-for-service-accounts/main.tf create mode 100644 examples/iam-role-for-service-accounts/outputs.tf rename examples/{iam-assumable-role => iam-role-for-service-accounts}/variables.tf (100%) rename examples/{iam-assumable-role => iam-role-for-service-accounts}/versions.tf (100%) create mode 100644 examples/iam-role-saml/README.md create mode 100644 examples/iam-role-saml/main.tf create mode 100644 examples/iam-role-saml/outputs.tf rename examples/{iam-assumable-role-with-saml => iam-role-saml}/saml-metadata.xml (100%) rename examples/{iam-assumable-roles-with-saml => iam-role-saml}/variables.tf (100%) rename examples/{iam-assumable-roles-with-saml => iam-role-saml}/versions.tf (100%) create mode 100644 examples/iam-role/README.md create mode 100644 examples/iam-role/main.tf create mode 100644 examples/iam-role/outputs.tf rename examples/{iam-assumable-roles => iam-role}/variables.tf (100%) rename examples/{iam-assumable-roles => iam-role}/versions.tf (100%) delete mode 100644 modules/iam-assumable-role-with-oidc/README.md delete mode 100644 modules/iam-assumable-role-with-oidc/main.tf delete mode 100644 modules/iam-assumable-role-with-oidc/outputs.tf delete mode 100644 modules/iam-assumable-role-with-oidc/variables.tf delete mode 100644 modules/iam-assumable-role-with-saml/README.md delete mode 100644 modules/iam-assumable-role-with-saml/main.tf delete mode 100644 modules/iam-assumable-role-with-saml/outputs.tf delete mode 100644 modules/iam-assumable-role-with-saml/variables.tf delete mode 100644 modules/iam-assumable-role/README.md delete mode 100644 modules/iam-assumable-role/main.tf delete mode 100644 modules/iam-assumable-role/outputs.tf delete mode 100644 modules/iam-assumable-role/variables.tf delete mode 100644 modules/iam-assumable-roles-with-saml/README.md delete mode 100644 modules/iam-assumable-roles-with-saml/main.tf delete mode 100644 modules/iam-assumable-roles-with-saml/outputs.tf delete mode 100644 modules/iam-assumable-roles-with-saml/variables.tf delete mode 100644 modules/iam-assumable-roles/README.md delete mode 100644 modules/iam-assumable-roles/main.tf delete mode 100644 modules/iam-assumable-roles/outputs.tf delete mode 100644 modules/iam-assumable-roles/variables.tf delete mode 100644 modules/iam-github-oidc-role/README.md delete mode 100644 modules/iam-github-oidc-role/main.tf delete mode 100644 modules/iam-github-oidc-role/variables.tf delete mode 100644 modules/iam-group-with-assumable-roles-policy/README.md delete mode 100644 modules/iam-group-with-assumable-roles-policy/main.tf delete mode 100644 modules/iam-group-with-assumable-roles-policy/outputs.tf delete mode 100644 modules/iam-group-with-assumable-roles-policy/variables.tf delete mode 100644 modules/iam-group-with-assumable-roles-policy/versions.tf delete mode 100644 modules/iam-group-with-policies/README.md delete mode 100644 modules/iam-group-with-policies/main.tf delete mode 100644 modules/iam-group-with-policies/outputs.tf delete mode 100644 modules/iam-group-with-policies/policies.tf delete mode 100644 modules/iam-group-with-policies/variables.tf delete mode 100644 modules/iam-group-with-policies/versions.tf create mode 100644 modules/iam-group/README.md create mode 100644 modules/iam-group/main.tf create mode 100644 modules/iam-group/outputs.tf create mode 100644 modules/iam-group/variables.tf rename {examples/iam-github-oidc => modules/iam-group}/versions.tf (100%) rename modules/{iam-github-oidc-provider => iam-oidc-provider}/README.md (64%) rename modules/{iam-github-oidc-provider => iam-oidc-provider}/main.tf (80%) rename modules/{iam-github-oidc-provider => iam-oidc-provider}/outputs.tf (95%) rename modules/{iam-github-oidc-provider => iam-oidc-provider}/variables.tf (65%) rename modules/{iam-github-oidc-provider => iam-oidc-provider}/versions.tf (100%) delete mode 100644 modules/iam-policy/README.md delete mode 100644 modules/iam-policy/main.tf delete mode 100644 modules/iam-policy/outputs.tf delete mode 100644 modules/iam-policy/variables.tf delete mode 100644 modules/iam-policy/versions.tf delete mode 100644 modules/iam-role-for-service-accounts-eks/main.tf delete mode 100644 modules/iam-role-for-service-accounts-eks/outputs.tf delete mode 100644 modules/iam-role-for-service-accounts-eks/versions.tf rename modules/{iam-role-for-service-accounts-eks => iam-role-for-service-accounts}/README.md (56%) create mode 100644 modules/iam-role-for-service-accounts/main.tf create mode 100644 modules/iam-role-for-service-accounts/outputs.tf rename modules/{iam-role-for-service-accounts-eks => iam-role-for-service-accounts}/policies.tf (57%) rename modules/{iam-role-for-service-accounts-eks => iam-role-for-service-accounts}/variables.tf (67%) rename {examples/iam-group-with-assumable-roles-policy => modules/iam-role-for-service-accounts}/versions.tf (100%) create mode 100644 modules/iam-role-oidc/README.md create mode 100644 modules/iam-role-oidc/main.tf rename modules/{iam-github-oidc-role => iam-role-oidc}/outputs.tf (62%) create mode 100644 modules/iam-role-oidc/variables.tf rename {examples/iam-group-with-policies => modules/iam-role-oidc}/versions.tf (100%) create mode 100644 modules/iam-role-saml/README.md create mode 100644 modules/iam-role-saml/main.tf create mode 100644 modules/iam-role-saml/outputs.tf create mode 100644 modules/iam-role-saml/variables.tf rename {examples/iam-policy => modules/iam-role-saml}/versions.tf (100%) create mode 100644 modules/iam-role/README.md create mode 100644 modules/iam-role/main.tf create mode 100644 modules/iam-role/outputs.tf create mode 100644 modules/iam-role/variables.tf rename modules/{iam-assumable-role-with-oidc => iam-role}/versions.tf (100%) delete mode 100644 wrappers/iam-assumable-role-with-oidc/README.md delete mode 100644 wrappers/iam-assumable-role-with-oidc/main.tf delete mode 100644 wrappers/iam-assumable-role-with-oidc/versions.tf delete mode 100644 wrappers/iam-assumable-role-with-saml/README.md delete mode 100644 wrappers/iam-assumable-role-with-saml/main.tf delete mode 100644 wrappers/iam-assumable-role-with-saml/versions.tf delete mode 100644 wrappers/iam-assumable-role/main.tf delete mode 100644 wrappers/iam-assumable-role/versions.tf delete mode 100644 wrappers/iam-assumable-roles-with-saml/README.md delete mode 100644 wrappers/iam-assumable-roles-with-saml/main.tf delete mode 100644 wrappers/iam-assumable-roles-with-saml/versions.tf delete mode 100644 wrappers/iam-assumable-roles/main.tf delete mode 100644 wrappers/iam-assumable-roles/versions.tf delete mode 100644 wrappers/iam-github-oidc-provider/README.md delete mode 100644 wrappers/iam-github-oidc-role/main.tf delete mode 100644 wrappers/iam-github-oidc-role/outputs.tf delete mode 100644 wrappers/iam-github-oidc-role/variables.tf delete mode 100644 wrappers/iam-github-oidc-role/versions.tf delete mode 100644 wrappers/iam-group-with-assumable-roles-policy/README.md delete mode 100644 wrappers/iam-group-with-assumable-roles-policy/main.tf delete mode 100644 wrappers/iam-group-with-assumable-roles-policy/outputs.tf delete mode 100644 wrappers/iam-group-with-assumable-roles-policy/variables.tf delete mode 100644 wrappers/iam-group-with-assumable-roles-policy/versions.tf delete mode 100644 wrappers/iam-group-with-policies/README.md delete mode 100644 wrappers/iam-group-with-policies/main.tf delete mode 100644 wrappers/iam-group-with-policies/outputs.tf delete mode 100644 wrappers/iam-group-with-policies/variables.tf delete mode 100644 wrappers/iam-group-with-policies/versions.tf rename wrappers/{iam-github-oidc-role => iam-group}/README.md (91%) create mode 100644 wrappers/iam-group/main.tf rename wrappers/{iam-assumable-role-with-oidc => iam-group}/outputs.tf (100%) rename wrappers/{iam-assumable-role-with-oidc => iam-group}/variables.tf (100%) rename {modules/iam-assumable-role-with-saml => wrappers/iam-group}/versions.tf (100%) rename wrappers/{iam-assumable-role => iam-oidc-provider}/README.md (89%) rename wrappers/{iam-github-oidc-provider => iam-oidc-provider}/main.tf (58%) rename wrappers/{iam-assumable-role-with-saml => iam-oidc-provider}/outputs.tf (100%) rename wrappers/{iam-assumable-role-with-saml => iam-oidc-provider}/variables.tf (100%) rename wrappers/{iam-github-oidc-provider => iam-oidc-provider}/versions.tf (100%) delete mode 100644 wrappers/iam-policy/main.tf delete mode 100644 wrappers/iam-policy/outputs.tf delete mode 100644 wrappers/iam-policy/variables.tf delete mode 100644 wrappers/iam-policy/versions.tf delete mode 100644 wrappers/iam-role-for-service-accounts-eks/outputs.tf delete mode 100644 wrappers/iam-role-for-service-accounts-eks/variables.tf delete mode 100644 wrappers/iam-role-for-service-accounts-eks/versions.tf rename wrappers/{iam-role-for-service-accounts-eks => iam-role-for-service-accounts}/README.md (92%) rename wrappers/{iam-role-for-service-accounts-eks => iam-role-for-service-accounts}/main.tf (60%) rename wrappers/{iam-assumable-role => iam-role-for-service-accounts}/outputs.tf (100%) rename wrappers/{iam-assumable-role => iam-role-for-service-accounts}/variables.tf (100%) rename {modules/iam-assumable-role => wrappers/iam-role-for-service-accounts}/versions.tf (100%) rename wrappers/{iam-assumable-roles => iam-role-oidc}/README.md (89%) create mode 100644 wrappers/iam-role-oidc/main.tf rename wrappers/{iam-assumable-roles-with-saml => iam-role-oidc}/outputs.tf (100%) rename wrappers/{iam-assumable-roles-with-saml => iam-role-oidc}/variables.tf (100%) rename {modules/iam-assumable-roles-with-saml => wrappers/iam-role-oidc}/versions.tf (100%) create mode 100644 wrappers/iam-role-saml/README.md create mode 100644 wrappers/iam-role-saml/main.tf rename wrappers/{iam-assumable-roles => iam-role-saml}/outputs.tf (100%) rename wrappers/{iam-assumable-roles => iam-role-saml}/variables.tf (100%) rename {modules/iam-assumable-roles => wrappers/iam-role-saml}/versions.tf (100%) rename wrappers/{iam-policy => iam-role}/README.md (90%) create mode 100644 wrappers/iam-role/main.tf rename wrappers/{iam-github-oidc-provider => iam-role}/outputs.tf (100%) rename wrappers/{iam-github-oidc-provider => iam-role}/variables.tf (100%) rename {modules/iam-github-oidc-role => wrappers/iam-role}/versions.tf (100%) diff --git a/README.md b/README.md index 40484902..bb7e06d1 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,18 @@ -# AWS Identity and Access Management (IAM) Terraform module +# AWS IAM Terraform module -[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md) +Terraform module which creates AWS IAM resources. -## Features +### ⚠️ JUST FOR TESTING - DO NOT RELY ON THIS ⚠️ -1. **Cross-account access.** Define IAM roles using `iam_assumable_role` or `iam_assumable_roles` submodules in "resource AWS accounts (prod, staging, dev)" and IAM groups and users using `iam-group-with-assumable-roles-policy` submodule in "IAM AWS Account" to setup access controls between accounts. See [iam-group-with-assumable-roles-policy example](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-group-with-assumable-roles-policy) for more details. -2. **Individual IAM resources (users, roles, policies).** See usage snippets and [examples](https://github.com/terraform-aws-modules/terraform-aws-iam#examples) listed below. +[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md) ## Usage -`iam-account`: +Please refer to the AWS published [IAM Best Practices](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html) for up to date guidance on IAM best practices. + +### IAM Account + +Creates an account policy and account alias. Module instantiation is once per account. ```hcl module "iam_account" { @@ -17,329 +20,236 @@ module "iam_account" { account_alias = "awesome-company" - minimum_password_length = 37 - require_numbers = false + max_password_age = 90 + minimum_password_length = 24 + require_uppercase_characters = true + require_lowercase_characters = true + require_numbers = true + require_symbols = true + password_reuse_prevention = 3 + allow_users_to_change_password = true } ``` -`iam-assumable-role`: +### IAM Group -```hcl -module "iam_assumable_role" { - source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role" +Creates an IAM group with IAM policy attached that one or more users can be added to. - trusted_role_arns = [ - "arn:aws:iam::307990089504:root", - "arn:aws:iam::835367859851:user/anton", - ] - - create_role = true +```hcl +module "iam_group" { + source = "terraform-aws-modules/iam/aws//modules/iam-group" - role_name = "custom" - role_requires_mfa = true + name = "superadmins" - custom_role_policy_arns = [ - "arn:aws:iam::aws:policy/AmazonCognitoReadOnly", - "arn:aws:iam::aws:policy/AlexaForBusinessFullAccess", + users = [ + "user1", + "user2" ] - number_of_custom_role_policy_arns = 2 -} -``` - -`iam-assumable-role-with-oidc`: -```hcl -module "iam_assumable_role_with_oidc" { - source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role-with-oidc" - - create_role = true + enable_self_management_permissions = true + permission_statements = [ + { + sid = "AssumeRole" + actions = ["sts:AssumeRole"] + resources = ["arn:aws:iam::111111111111:role/admin"] + } + ] - role_name = "role-with-oidc" + policies = { + AdministratorAccess = "arn:aws:iam::aws:policy/AdministratorAccess", + } tags = { - Role = "role-with-oidc" + Terraform = "true" + Environment = "dev" } - - provider_url = "oidc.eks.eu-west-1.amazonaws.com/id/BA9E170D464AF7B92084EF72A69B9DC8" - - role_policy_arns = [ - "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy", - ] - number_of_role_policy_arns = 1 } ``` -`iam-assumable-role-with-saml`: +### IAM OIDC Provider -```hcl -module "iam_assumable_role_with_saml" { - source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role-with-saml" +Creates an OpenID connect provider. Useful for trusting external identity providers such as GitHub, Bitbucket, etc. - create_role = true +⚠️ An IAM provider is 1 per account per given URL. This module would be provisioned once per AWS account, and then one or more roles can be created with this provider as the trusted identity. + +```hcl +module "iam_oidc_provider" { + source = "terraform-aws-modules/iam/aws//modules/iam-oidc-provider" - role_name = "role-with-saml" + url = "https://token.actions.githubusercontent.com" tags = { - Role = "role-with-saml" + Terraform = "true" + Environment = "dev" } - - provider_id = "arn:aws:iam::235367859851:saml-provider/idp_saml" - - role_policy_arns = [ - "arn:aws:iam::aws:policy/ReadOnlyAccess" - ] - number_of_role_policy_arns = 1 } ``` -`iam-assumable-roles`: +### IAM ReadOnly Policy -```hcl -module "iam_assumable_roles" { - source = "terraform-aws-modules/iam/aws//modules/iam-assumable-roles" +Creates an IAM policy that allows read-only access to the list of AWS services provided. - trusted_role_arns = [ - "arn:aws:iam::307990089504:root", - "arn:aws:iam::835367859851:user/anton", - ] +```hcl +module "iam_read_only_policy" { + source = "terraform-aws-modules/iam/aws//modules/iam-read-only-policy" - create_admin_role = true + name = "example" + path = "/" + description = "My example read-only policy" - create_poweruser_role = true - poweruser_role_name = "developer" + allowed_services = ["rds", "dynamo", "health"] - create_readonly_role = true - readonly_role_requires_mfa = false + tags = { + Terraform = "true" + Environment = "dev" + } } ``` -`iam-assumable-roles-with-saml`: - -```hcl -module "iam_assumable_roles_with_saml" { - source = "terraform-aws-modules/iam/aws//modules/iam-assumable-roles-with-saml" - - create_admin_role = true +### IAM Role for Service Accounts (IRSA) - create_poweruser_role = true - poweruser_role_name = "developer" +Creates an IAM role that is suitable for EKS IAM role for service accounts (IRSA) with a set of pre-defined policies for common EKS addons. - create_readonly_role = true +```hcl +module "vpc_cni_irsa" { + source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts" - provider_id = "arn:aws:iam::235367859851:saml-provider/idp_saml" -} -``` + name = "vpc-cni" -`iam-github-oidc-provider`: + attach_vpc_cni_policy = true + vpc_cni_enable_ipv4 = true -```hcl -module "iam_github_oidc_provider" { - source = "terraform-aws-modules/iam/aws//modules/iam-github-oidc-provider" + oidc_providers = { + this = { + provider_arn = "arn:aws:iam::012345678901:oidc-provider/oidc.eks.us-east-1.amazonaws.com/id/5C54DDF35ER19312844C7333374CC09D" + namespace_service_accounts = ["kube-system:aws-node"] + } + } tags = { - Environment = "test" + Terraform = "true" + Environment = "dev" } } ``` -`iam-github-oidc-role`: +### OIDC IAM Role + +Creates an IAM role that trusts an OpenID connect provider. Useful for trusting external identity providers such as GitHub, Bitbucket, etc. ```hcl -module "iam_github_oidc_role" { - source = "terraform-aws-modules/iam/aws//modules/iam-github-oidc-role" +module "iam_oidc_role" { + source = "terraform-aws-modules/iam/aws//modules/iam-oidc-role" + + enable_github_oidc = true # This should be updated to suit your organization, repository, references/branches, etc. - subjects = ["terraform-aws-modules/terraform-aws-iam:*"] + oidc_subjects = ["terraform-aws-modules/terraform-aws-iam:*"] policies = { S3ReadOnly = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" } tags = { - Environment = "test" + Terraform = "true" + Environment = "dev" } } ``` -`iam-group-with-assumable-roles-policy`: - -```hcl -module "iam_group_with_assumable_roles_policy" { - source = "terraform-aws-modules/iam/aws//modules/iam-group-with-assumable-roles-policy" - - name = "production-readonly" - - assumable_roles = [ - "arn:aws:iam::835367859855:role/readonly" # these roles can be created using `iam_assumable_roles` submodule - ] - - group_users = [ - "user1", - "user2" - ] -} -``` +### SAML IAM Role -`iam-group-with-policies`: +Creates an IAM role that trusts a SAML provider. Useful for trusting external identity providers such as Okta, OneLogin, etc. ```hcl -module "iam_group_with_policies" { - source = "terraform-aws-modules/iam/aws//modules/iam-group-with-policies" +module "iam_role_saml" { + source = "terraform-aws-modules/iam/aws//modules/iam-role-saml" - name = "superadmins" - - group_users = [ - "user1", - "user2" - ] + name = "example" - attach_iam_self_management_policy = true + saml_provider_ids = ["arn:aws:iam::235367859851:saml-provider/idp_saml"] - custom_group_policy_arns = [ - "arn:aws:iam::aws:policy/AdministratorAccess", - ] + policies = { + ReadOnlyAccess = "arn:aws:iam::aws:policy/ReadOnlyAccess" + } - custom_group_policies = [ - { - name = "AllowS3Listing" - policy = data.aws_iam_policy_document.sample.json - } - ] + tags = { + Terraform = "true" + Environment = "dev" + } } ``` -`iam-policy`: +### IAM Role + +Creates an IAM role with a trust policy and (optional) IAM instance profile. Useful for service roles such as EC2, ECS, etc., or roles assumed across AWS accounts. ```hcl -module "iam_policy" { - source = "terraform-aws-modules/iam/aws//modules/iam-policy" +module "iam_role" { + source = "terraform-aws-modules/iam/aws//modules/iam-role" - name = "example" - path = "/" - description = "My example policy" + name = "example" - policy = < -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | - -## Providers - -No providers. - -## Modules - -| Name | Source | Version | -|------|--------|---------| -| [iam\_assumable\_role\_admin](#module\_iam\_assumable\_role\_admin) | ../../modules/iam-assumable-role-with-oidc | n/a | -| [iam\_assumable\_role\_inline\_policy](#module\_iam\_assumable\_role\_inline\_policy) | ../../modules/iam-assumable-role-with-oidc | n/a | -| [iam\_assumable\_role\_provider\_trust\_policy\_conditions](#module\_iam\_assumable\_role\_provider\_trust\_policy\_conditions) | ../../modules/iam-assumable-role-with-oidc | n/a | -| [iam\_assumable\_role\_self\_assume](#module\_iam\_assumable\_role\_self\_assume) | ../../modules/iam-assumable-role-with-oidc | n/a | - -## Resources - -No resources. - -## Inputs - -No inputs. - -## Outputs - -| Name | Description | -|------|-------------| -| [iam\_role\_arn](#output\_iam\_role\_arn) | ARN of IAM role | -| [iam\_role\_name](#output\_iam\_role\_name) | Name of IAM role | -| [iam\_role\_path](#output\_iam\_role\_path) | Path of IAM role | -| [iam\_role\_unique\_id](#output\_iam\_role\_unique\_id) | Unique ID of IAM role | - diff --git a/examples/iam-assumable-role-with-oidc/main.tf b/examples/iam-assumable-role-with-oidc/main.tf deleted file mode 100644 index d324bdfd..00000000 --- a/examples/iam-assumable-role-with-oidc/main.tf +++ /dev/null @@ -1,122 +0,0 @@ -provider "aws" { - region = "eu-west-1" -} - -############################### -# IAM assumable role for admin -############################### -module "iam_assumable_role_admin" { - source = "../../modules/iam-assumable-role-with-oidc" - - create_role = true - - role_name = "role-with-oidc" - - tags = { - Role = "role-with-oidc" - } - - provider_url = "oidc.circleci.com/org/" - - oidc_fully_qualified_audiences = [""] - - role_policy_arns = [ - "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser", - ] -} - -##################################### -# IAM assumable role with self assume -##################################### -module "iam_assumable_role_self_assume" { - source = "../../modules/iam-assumable-role-with-oidc" - - create_role = true - allow_self_assume_role = true - - role_name = "role-with-oidc-self-assume" - - tags = { - Role = "role-with-oidc-self-assume" - } - - provider_url = "oidc.circleci.com/org/" - - oidc_fully_qualified_audiences = [""] - - role_policy_arns = [ - "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser", - ] -} - -##################################### -# IAM assumable role with inline policy -##################################### -module "iam_assumable_role_inline_policy" { - source = "../../modules/iam-assumable-role-with-oidc" - - create_role = true - - role_name = "role-with-oidc-inline-policy" - - tags = { - Role = "role-with-oidc-inline-policy" - } - - provider_url = "oidc.circleci.com/org/" - - oidc_fully_qualified_audiences = [""] - - inline_policy_statements = [ - { - sid = "AllowECRPushPull" - actions = [ - "ecr:GetAuthorizationToken", - "ecr:BatchGetImage", - "ecr:GetDownloadUrlForLayer", - "ecr:BatchCheckLayerAvailability", - "ecr:DescribeImages", - "ecr:DescribeRepositories", - "ecr:GetDownloadUrlForLayer", - "ecr:ListImages", - "ecr:PutImage", - "ecr:InitiateLayerUpload", - "ecr:UploadLayerPart", - "ecr:CompleteLayerUpload" - ] - effect = "Allow" - resources = ["*"] - } - ] -} - -##################################### -# IAM assumable role with policy conditions -##################################### -module "iam_assumable_role_provider_trust_policy_conditions" { - source = "../../modules/iam-assumable-role-with-oidc" - - create_role = true - - role_name = "role-with-oidc-policy-conditions" - - tags = { - Role = "role-with-oidc-policy-conditions" - } - - provider_url = "oidc.circleci.com/org/" - - oidc_fully_qualified_audiences = [""] - - role_policy_arns = [ - "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy", - ] - - provider_trust_policy_conditions = [ - { - test = "StringLike" - variable = "aws:RequestTag/Environment" - values = ["example"] - } - ] -} diff --git a/examples/iam-assumable-role-with-oidc/outputs.tf b/examples/iam-assumable-role-with-oidc/outputs.tf deleted file mode 100644 index 69fbaa0d..00000000 --- a/examples/iam-assumable-role-with-oidc/outputs.tf +++ /dev/null @@ -1,19 +0,0 @@ -output "iam_role_arn" { - description = "ARN of IAM role" - value = module.iam_assumable_role_admin.iam_role_arn -} - -output "iam_role_name" { - description = "Name of IAM role" - value = module.iam_assumable_role_admin.iam_role_name -} - -output "iam_role_path" { - description = "Path of IAM role" - value = module.iam_assumable_role_admin.iam_role_path -} - -output "iam_role_unique_id" { - description = "Unique ID of IAM role" - value = module.iam_assumable_role_admin.iam_role_unique_id -} diff --git a/examples/iam-assumable-role-with-saml/README.md b/examples/iam-assumable-role-with-saml/README.md deleted file mode 100644 index f1d0d2ac..00000000 --- a/examples/iam-assumable-role-with-saml/README.md +++ /dev/null @@ -1,57 +0,0 @@ -# Individual IAM assumable role with SAML Identity Provider example - -Configuration in this directory creates a single IAM role which can be assumed by users with a SAML Identity Provider. - -# Usage - -To run this example you need to execute: - -```bash -$ terraform init -$ terraform plan -$ terraform apply -``` - -Run `terraform destroy` when you don't need these resources. - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 4.0 | - -## Modules - -| Name | Source | Version | -|------|--------|---------| -| [iam\_assumable\_role\_admin](#module\_iam\_assumable\_role\_admin) | ../../modules/iam-assumable-role-with-saml | n/a | -| [iam\_assumable\_role\_self\_assume](#module\_iam\_assumable\_role\_self\_assume) | ../../modules/iam-assumable-role-with-saml | n/a | - -## Resources - -| Name | Type | -|------|------| -| [aws_iam_saml_provider.idp_saml](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_saml_provider) | resource | -| [aws_iam_saml_provider.second_idp_saml](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_saml_provider) | resource | - -## Inputs - -No inputs. - -## Outputs - -| Name | Description | -|------|-------------| -| [iam\_role\_arn](#output\_iam\_role\_arn) | ARN of IAM role | -| [iam\_role\_name](#output\_iam\_role\_name) | Name of IAM role | -| [iam\_role\_path](#output\_iam\_role\_path) | Path of IAM role | -| [iam\_role\_unique\_id](#output\_iam\_role\_unique\_id) | Unique ID of IAM role | - diff --git a/examples/iam-assumable-role-with-saml/main.tf b/examples/iam-assumable-role-with-saml/main.tf deleted file mode 100644 index c7e18c81..00000000 --- a/examples/iam-assumable-role-with-saml/main.tf +++ /dev/null @@ -1,58 +0,0 @@ -provider "aws" { - region = "eu-west-1" -} - -resource "aws_iam_saml_provider" "idp_saml" { - name = "idp_saml" - saml_metadata_document = file("saml-metadata.xml") -} - -resource "aws_iam_saml_provider" "second_idp_saml" { - name = "second_idp_saml" - saml_metadata_document = file("saml-metadata.xml") -} - -############################### -# IAM assumable role for admin -############################### -module "iam_assumable_role_admin" { - source = "../../modules/iam-assumable-role-with-saml" - - create_role = true - - role_name = "role-with-saml" - - tags = { - Role = "role-with-saml" - } - - provider_id = aws_iam_saml_provider.idp_saml.id - provider_ids = [aws_iam_saml_provider.second_idp_saml.id] - - role_policy_arns = [ - "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy", - ] -} - -##################################### -# IAM assumable role with self assume -##################################### -module "iam_assumable_role_self_assume" { - source = "../../modules/iam-assumable-role-with-saml" - - create_role = true - allow_self_assume_role = true - - role_name = "role-with-saml-self-assume" - - tags = { - Role = "role-with-saml-self-assume" - } - - provider_id = aws_iam_saml_provider.idp_saml.id - provider_ids = [aws_iam_saml_provider.second_idp_saml.id] - - role_policy_arns = [ - "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy", - ] -} diff --git a/examples/iam-assumable-role-with-saml/outputs.tf b/examples/iam-assumable-role-with-saml/outputs.tf deleted file mode 100644 index 69fbaa0d..00000000 --- a/examples/iam-assumable-role-with-saml/outputs.tf +++ /dev/null @@ -1,19 +0,0 @@ -output "iam_role_arn" { - description = "ARN of IAM role" - value = module.iam_assumable_role_admin.iam_role_arn -} - -output "iam_role_name" { - description = "Name of IAM role" - value = module.iam_assumable_role_admin.iam_role_name -} - -output "iam_role_path" { - description = "Path of IAM role" - value = module.iam_assumable_role_admin.iam_role_path -} - -output "iam_role_unique_id" { - description = "Unique ID of IAM role" - value = module.iam_assumable_role_admin.iam_role_unique_id -} diff --git a/examples/iam-assumable-role/README.md b/examples/iam-assumable-role/README.md deleted file mode 100644 index 8a3b011e..00000000 --- a/examples/iam-assumable-role/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# Individual IAM assumable roles example - -Configuration in this directory creates several individual IAM roles which can be assumed from a defined list of [IAM ARNs](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html#identifiers-arns). - -The main difference between `iam-assumable-role` and `iam-assumable-roles` examples is that the former creates just a single role. - -# Usage - -To run this example you need to execute: - -```bash -$ terraform init -$ terraform plan -$ terraform apply -``` - -Run `terraform destroy` when you don't need these resources. - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 4.0 | - -## Modules - -| Name | Source | Version | -|------|--------|---------| -| [iam\_assumable\_role\_admin](#module\_iam\_assumable\_role\_admin) | ../../modules/iam-assumable-role | n/a | -| [iam\_assumable\_role\_custom](#module\_iam\_assumable\_role\_custom) | ../../modules/iam-assumable-role | n/a | -| [iam\_assumable\_role\_custom\_trust\_policy](#module\_iam\_assumable\_role\_custom\_trust\_policy) | ../../modules/iam-assumable-role | n/a | -| [iam\_assumable\_role\_inline\_policy](#module\_iam\_assumable\_role\_inline\_policy) | ../../modules/iam-assumable-role | n/a | -| [iam\_assumable\_role\_sts](#module\_iam\_assumable\_role\_sts) | ../../modules/iam-assumable-role | n/a | -| [iam\_policy](#module\_iam\_policy) | ../../modules/iam-policy | n/a | - -## Resources - -| Name | Type | -|------|------| -| [aws_iam_policy_document.custom_trust_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | - -## Inputs - -No inputs. - -## Outputs - -| Name | Description | -|------|-------------| -| [iam\_instance\_profile\_id](#output\_iam\_instance\_profile\_id) | IAM Instance profile's ID. | -| [iam\_role\_arn](#output\_iam\_role\_arn) | ARN of IAM role | -| [iam\_role\_name](#output\_iam\_role\_name) | Name of IAM role | -| [iam\_role\_path](#output\_iam\_role\_path) | Path of IAM role | -| [iam\_role\_unique\_id](#output\_iam\_role\_unique\_id) | Unique ID of IAM role | -| [role\_requires\_mfa](#output\_role\_requires\_mfa) | Whether admin IAM role requires MFA | - diff --git a/examples/iam-assumable-role/main.tf b/examples/iam-assumable-role/main.tf deleted file mode 100644 index 0721acb8..00000000 --- a/examples/iam-assumable-role/main.tf +++ /dev/null @@ -1,218 +0,0 @@ -provider "aws" { - region = "eu-west-1" -} - -############################### -# IAM assumable role for admin -############################### - -module "iam_assumable_role_admin" { - source = "../../modules/iam-assumable-role" - - # https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/ - allow_self_assume_role = true - - trusted_role_arns = [ - "arn:aws:iam::307990089504:root", - "arn:aws:iam::835367859851:user/anton", - ] - - trusted_role_services = [ - "codedeploy.amazonaws.com" - ] - - create_role = true - create_instance_profile = true - - role_name = "admin" - role_requires_mfa = true - - attach_admin_policy = true - - tags = { - Role = "Admin" - } -} - -########################################## -# IAM assumable role with custom policies -########################################## - -module "iam_assumable_role_custom" { - source = "../../modules/iam-assumable-role" - - create_role = true - - trusted_role_arns = [ - "arn:aws:iam::307990089504:root", - ] - - trusted_role_services = [ - "codedeploy.amazonaws.com" - ] - - trust_policy_conditions = [ - { - test = "StringEquals" - variable = "aws:PrincipalOrgID" - values = ["o-someorgid"] - } - ] - - role_name_prefix = "custom-" - role_requires_mfa = false - - role_sts_externalid = "some-id-goes-here" - - custom_role_policy_arns = [ - "arn:aws:iam::aws:policy/AmazonCognitoReadOnly", - "arn:aws:iam::aws:policy/AlexaForBusinessFullAccess", - module.iam_policy.arn - ] - # number_of_custom_role_policy_arns = 3 -} - -########################################## -# IAM assumable role with inline policy -########################################## - -module "iam_assumable_role_inline_policy" { - source = "../../modules/iam-assumable-role" - - trusted_role_arns = [ - "arn:aws:iam::307990089504:root", - ] - - trusted_role_services = [ - "codedeploy.amazonaws.com" - ] - - create_role = true - - role_name_prefix = "custom-" - role_requires_mfa = false - - role_sts_externalid = "some-id-goes-here" - - inline_policy_statements = [ - { - sid = "AllowECRPushPull" - actions = [ - "ecr:GetAuthorizationToken", - "ecr:BatchGetImage", - "ecr:GetDownloadUrlForLayer", - "ecr:BatchCheckLayerAvailability", - "ecr:DescribeImages", - "ecr:DescribeRepositories", - "ecr:GetDownloadUrlForLayer", - "ecr:ListImages", - "ecr:PutImage", - "ecr:InitiateLayerUpload", - "ecr:UploadLayerPart", - "ecr:CompleteLayerUpload" - ] - effect = "Allow" - resources = ["*"] - } - ] -} - -#################################################### -# IAM assumable role with multiple sts external ids -#################################################### - -module "iam_assumable_role_sts" { - source = "../../modules/iam-assumable-role" - - trusted_role_arns = [ - "arn:aws:iam::307990089504:root", - ] - - trusted_role_services = [ - "codedeploy.amazonaws.com" - ] - - create_role = true - - role_name = "custom_sts" - role_requires_mfa = true - - role_sts_externalid = [ - "some-id-goes-here", - "another-id-goes-here", - ] - - custom_role_policy_arns = [ - "arn:aws:iam::aws:policy/AmazonCognitoReadOnly", - "arn:aws:iam::aws:policy/AlexaForBusinessFullAccess", - module.iam_policy.arn - ] - # number_of_custom_role_policy_arns = 3 -} - -######################################### -# IAM assumable role with custom trust policy -######################################### - -module "iam_assumable_role_custom_trust_policy" { - source = "../../modules/iam-assumable-role" - - create_role = true - - role_name = "iam_assumable_role_custom_trust_policy" - - create_custom_role_trust_policy = true - custom_role_trust_policy = data.aws_iam_policy_document.custom_trust_policy.json - custom_role_policy_arns = ["arn:aws:iam::aws:policy/AmazonCognitoReadOnly"] -} - -data "aws_iam_policy_document" "custom_trust_policy" { - statement { - effect = "Allow" - actions = ["sts:AssumeRole"] - - condition { - test = "StringEquals" - variable = "sts:ExternalId" - values = ["some-ext-id"] - } - - condition { - test = "StringEquals" - variable = "aws:PrincipalOrgID" - values = ["o-someorgid"] - } - - principals { - type = "AWS" - identifiers = ["*"] - } - } -} - -######################################### -# IAM policy -######################################### - -module "iam_policy" { - source = "../../modules/iam-policy" - - name = "example" - path = "/" - description = "My example policy" - - policy = < -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 4.0 | - -## Modules - -| Name | Source | Version | -|------|--------|---------| -| [iam\_assumable\_roles\_with\_saml](#module\_iam\_assumable\_roles\_with\_saml) | ../../modules/iam-assumable-roles-with-saml | n/a | -| [iam\_assumable\_roles\_with\_saml\_custom](#module\_iam\_assumable\_roles\_with\_saml\_custom) | ../../modules/iam-assumable-roles-with-saml | n/a | -| [iam\_assumable\_roles\_with\_saml\_second\_provider](#module\_iam\_assumable\_roles\_with\_saml\_second\_provider) | ../../modules/iam-assumable-roles-with-saml | n/a | -| [iam\_assumable\_roles\_with\_saml\_with\_self\_assume](#module\_iam\_assumable\_roles\_with\_saml\_with\_self\_assume) | ../../modules/iam-assumable-roles-with-saml | n/a | - -## Resources - -| Name | Type | -|------|------| -| [aws_iam_saml_provider.idp_saml](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_saml_provider) | resource | -| [aws_iam_saml_provider.second_idp_saml](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_saml_provider) | resource | - -## Inputs - -No inputs. - -## Outputs - -| Name | Description | -|------|-------------| -| [admin\_iam\_role\_arn](#output\_admin\_iam\_role\_arn) | ARN of admin IAM role | -| [admin\_iam\_role\_name](#output\_admin\_iam\_role\_name) | Name of admin IAM role | -| [admin\_iam\_role\_path](#output\_admin\_iam\_role\_path) | Path of admin IAM role | -| [admin\_iam\_role\_unique\_id](#output\_admin\_iam\_role\_unique\_id) | Unique ID of IAM role | -| [poweruser\_iam\_role\_arn](#output\_poweruser\_iam\_role\_arn) | ARN of poweruser IAM role | -| [poweruser\_iam\_role\_name](#output\_poweruser\_iam\_role\_name) | Name of poweruser IAM role | -| [poweruser\_iam\_role\_path](#output\_poweruser\_iam\_role\_path) | Path of poweruser IAM role | -| [poweruser\_iam\_role\_unique\_id](#output\_poweruser\_iam\_role\_unique\_id) | Unique ID of IAM role | -| [readonly\_iam\_role\_arn](#output\_readonly\_iam\_role\_arn) | ARN of readonly IAM role | -| [readonly\_iam\_role\_name](#output\_readonly\_iam\_role\_name) | Name of readonly IAM role | -| [readonly\_iam\_role\_path](#output\_readonly\_iam\_role\_path) | Path of readonly IAM role | -| [readonly\_iam\_role\_unique\_id](#output\_readonly\_iam\_role\_unique\_id) | Unique ID of IAM role | - diff --git a/examples/iam-assumable-roles-with-saml/main.tf b/examples/iam-assumable-roles-with-saml/main.tf deleted file mode 100644 index a3fd92be..00000000 --- a/examples/iam-assumable-roles-with-saml/main.tf +++ /dev/null @@ -1,79 +0,0 @@ -provider "aws" { - region = "eu-west-1" -} - -resource "aws_iam_saml_provider" "idp_saml" { - name = "idp_saml" - saml_metadata_document = file("saml-metadata.xml") -} - -resource "aws_iam_saml_provider" "second_idp_saml" { - name = "second_idp_saml" - saml_metadata_document = file("saml-metadata.xml") -} - -############################### -# IAM assumable roles with SAML -############################### - -module "iam_assumable_roles_with_saml" { - source = "../../modules/iam-assumable-roles-with-saml" - - create_admin_role = true - - create_poweruser_role = true - poweruser_role_name = "developer" - - create_readonly_role = true - - provider_id = aws_iam_saml_provider.idp_saml.id -} - -############################### -# IAM assumable roles with SAML -############################### - -module "iam_assumable_roles_with_saml_second_provider" { - source = "../../modules/iam-assumable-roles-with-saml" - - create_admin_role = true - - create_poweruser_role = true - admin_role_name = "Admin-Role-Name" - poweruser_role_name = "Poweruser-Role-Name" - readonly_role_name = "Readonly-Role-Name" - - create_readonly_role = true - - provider_ids = [aws_iam_saml_provider.idp_saml.id, aws_iam_saml_provider.second_idp_saml.id] -} - -################################################################# -# Create custom role with SAML idp trust and additional policies -################################################################# -module "iam_assumable_roles_with_saml_custom" { - source = "../../modules/iam-assumable-roles-with-saml" - - create_poweruser_role = true - poweruser_role_name = "Billing-And-Support-Access" - poweruser_role_policy_arns = ["arn:aws:iam::aws:policy/job-function/Billing", "arn:aws:iam::aws:policy/AWSSupportAccess"] - - provider_id = aws_iam_saml_provider.idp_saml.id -} - -################################################ -# IAM assumable roles with SAML with self assume -################################################ -module "iam_assumable_roles_with_saml_with_self_assume" { - source = "../../modules/iam-assumable-roles-with-saml" - - create_admin_role = true - allow_self_assume_role = true - create_poweruser_role = true - admin_role_name = "Admin-Role-Name-Self-Assume" - poweruser_role_name = "Poweruser-Role-Name-Self-Assume" - readonly_role_name = "Readonly-Role-Name-Self-Assume" - create_readonly_role = true - - provider_id = aws_iam_saml_provider.idp_saml.id -} diff --git a/examples/iam-assumable-roles-with-saml/outputs.tf b/examples/iam-assumable-roles-with-saml/outputs.tf deleted file mode 100644 index 138c67b7..00000000 --- a/examples/iam-assumable-roles-with-saml/outputs.tf +++ /dev/null @@ -1,62 +0,0 @@ -# Admin -output "admin_iam_role_arn" { - description = "ARN of admin IAM role" - value = module.iam_assumable_roles_with_saml.admin_iam_role_arn -} - -output "admin_iam_role_name" { - description = "Name of admin IAM role" - value = module.iam_assumable_roles_with_saml.admin_iam_role_name -} - -output "admin_iam_role_path" { - description = "Path of admin IAM role" - value = module.iam_assumable_roles_with_saml.admin_iam_role_path -} - -output "admin_iam_role_unique_id" { - description = "Unique ID of IAM role" - value = module.iam_assumable_roles_with_saml.admin_iam_role_unique_id -} - -# Poweruser -output "poweruser_iam_role_arn" { - description = "ARN of poweruser IAM role" - value = module.iam_assumable_roles_with_saml.poweruser_iam_role_arn -} - -output "poweruser_iam_role_name" { - description = "Name of poweruser IAM role" - value = module.iam_assumable_roles_with_saml.poweruser_iam_role_name -} - -output "poweruser_iam_role_path" { - description = "Path of poweruser IAM role" - value = module.iam_assumable_roles_with_saml.poweruser_iam_role_path -} - -output "poweruser_iam_role_unique_id" { - description = "Unique ID of IAM role" - value = module.iam_assumable_roles_with_saml.poweruser_iam_role_unique_id -} - -# Readonly -output "readonly_iam_role_arn" { - description = "ARN of readonly IAM role" - value = module.iam_assumable_roles_with_saml.readonly_iam_role_arn -} - -output "readonly_iam_role_name" { - description = "Name of readonly IAM role" - value = module.iam_assumable_roles_with_saml.readonly_iam_role_name -} - -output "readonly_iam_role_path" { - description = "Path of readonly IAM role" - value = module.iam_assumable_roles_with_saml.readonly_iam_role_path -} - -output "readonly_iam_role_unique_id" { - description = "Unique ID of IAM role" - value = module.iam_assumable_roles_with_saml.readonly_iam_role_unique_id -} diff --git a/examples/iam-assumable-roles-with-saml/saml-metadata.xml b/examples/iam-assumable-roles-with-saml/saml-metadata.xml deleted file mode 100644 index c68d77c7..00000000 --- a/examples/iam-assumable-roles-with-saml/saml-metadata.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - MIIErDCCA5SgAwIBAgIOAU+PT8RBAAAAAHxJXEcwDQYJKoZIhvcNAQELBQAwgZAxKDAmBgNVBAMMH1NlbGZTaWduZWRDZXJ0XzAyU2VwMjAxNV8xODI2NTMxGDAWBgNVBAsMDzAwRDI0MDAwMDAwcEFvQTEXMBUGA1UECgwOU2FsZXNmb3JjZS5jb20xFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xCzAJBgNVBAgMAkNBMQwwCgYDVQQGEwNVU0EwHhcNMTUwOTAyMTgyNjUzWhcNMTcwOTAyMTIwMDAwWjCBkDEoMCYGA1UEAwwfU2VsZlNpZ25lZENlcnRfMDJTZXAyMDE1XzE4MjY1MzEYMBYGA1UECwwPMDBEMjQwMDAwMDBwQW9BMRcwFQYDVQQKDA5TYWxlc2ZvcmNlLmNvbTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzELMAkGA1UECAwCQ0ExDDAKBgNVBAYTA1VTQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJp/wTRr9n1IWJpkRTjNpep47OKJrD2E6rGbJ18TG2RxtIz+zCn2JwH2aP3TULh0r0hhcg/pecv51RRcG7O19DBBaTQ5+KuoICQyKZy07/yDXSiZontTwkEYs06ssTwTHUcRXbcwTKv16L7omt0MjIhTTGfvtLOYiPwyvKvzAHg4eNuAcli0duVM78UIBORtdmy9C9ZcMh8yRJo5aPBq85wsE3JXU58ytyZzCHTBLH+2xFQrjYnUSEW+FOEEpI7o33MVdFBvWWg1R17HkWzcve4C30lqOHqvxBzyESZ/N1mMlmSt8gPFyB+mUXY99StJDJpnytbY8DwSzMQUo/sOVB0CAwEAAaOCAQAwgf0wHQYDVR0OBBYEFByu1EQqRQS0bYQBKS9K5qwKi+6IMA8GA1UdEwEB/wQFMAMBAf8wgcoGA1UdIwSBwjCBv4AUHK7URCpFBLRthAEpL0rmrAqL7oihgZakgZMwgZAxKDAmBgNVBAMMH1NlbGZTaWduZWRDZXJ0XzAyU2VwMjAxNV8xODI2NTMxGDAWBgNVBAsMDzAwRDI0MDAwMDAwcEFvQTEXMBUGA1UECgwOU2FsZXNmb3JjZS5jb20xFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xCzAJBgNVBAgMAkNBMQwwCgYDVQQGEwNVU0GCDgFPj0/EQQAAAAB8SVxHMA0GCSqGSIb3DQEBCwUAA4IBAQA9O5o1tC71qJnkq+ABPo4A1aFKZVT/07GcBX4/wetcbYySL4Q2nR9pMgfPYYS1j+P2E3viPsQwPIWDUBwFkNsjjX5DSGEkLAioVGKRwJshRSCSynMcsVZbQkfBUiZXqhM0wzvoa/ALvGD+aSSb1m+x7lEpDYNwQKWaUW2VYcHWv9wjujMyy7dlj8E/jqM71mw7ThNl6k4+3RQ802dMa14txm8pkF0vZgfpV3tkqhBqtjBAicVCaveqr3r3iGqjvyilBgdY+0NR8szqzm7CD/Bkb22+/IgM/mXQuL9KHD/WADlSGmYKmG3SSahmcZxznYCnzcRNN9LVuXlz5cbljmBj - - - - urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified - - - - diff --git a/examples/iam-assumable-roles/README.md b/examples/iam-assumable-roles/README.md deleted file mode 100644 index 15902f75..00000000 --- a/examples/iam-assumable-roles/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# IAM assumable roles example - -Configuration in this directory creates several IAM roles which can be assumed from a defined list of [IAM ARNs](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html#identifiers-arns). - -# Usage - -To run this example you need to execute: - -```bash -$ terraform init -$ terraform plan -$ terraform apply -``` - -Run `terraform destroy` when you don't need these resources. - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | - -## Providers - -No providers. - -## Modules - -| Name | Source | Version | -|------|--------|---------| -| [iam\_assumable\_roles](#module\_iam\_assumable\_roles) | ../../modules/iam-assumable-roles | n/a | -| [iam\_assumable\_roles\_with\_self\_assume](#module\_iam\_assumable\_roles\_with\_self\_assume) | ../../modules/iam-assumable-roles | n/a | - -## Resources - -No resources. - -## Inputs - -No inputs. - -## Outputs - -| Name | Description | -|------|-------------| -| [admin\_iam\_role\_arn](#output\_admin\_iam\_role\_arn) | ARN of admin IAM role | -| [admin\_iam\_role\_name](#output\_admin\_iam\_role\_name) | Name of admin IAM role | -| [admin\_iam\_role\_path](#output\_admin\_iam\_role\_path) | Path of admin IAM role | -| [admin\_iam\_role\_requires\_mfa](#output\_admin\_iam\_role\_requires\_mfa) | Whether admin IAM role requires MFA | -| [admin\_iam\_role\_unique\_id](#output\_admin\_iam\_role\_unique\_id) | Unique ID of IAM role | -| [poweruser\_iam\_role\_arn](#output\_poweruser\_iam\_role\_arn) | ARN of poweruser IAM role | -| [poweruser\_iam\_role\_name](#output\_poweruser\_iam\_role\_name) | Name of poweruser IAM role | -| [poweruser\_iam\_role\_path](#output\_poweruser\_iam\_role\_path) | Path of poweruser IAM role | -| [poweruser\_iam\_role\_requires\_mfa](#output\_poweruser\_iam\_role\_requires\_mfa) | Whether poweruser IAM role requires MFA | -| [poweruser\_iam\_role\_unique\_id](#output\_poweruser\_iam\_role\_unique\_id) | Unique ID of IAM role | -| [readonly\_iam\_role\_arn](#output\_readonly\_iam\_role\_arn) | ARN of readonly IAM role | -| [readonly\_iam\_role\_name](#output\_readonly\_iam\_role\_name) | Name of readonly IAM role | -| [readonly\_iam\_role\_path](#output\_readonly\_iam\_role\_path) | Path of readonly IAM role | -| [readonly\_iam\_role\_requires\_mfa](#output\_readonly\_iam\_role\_requires\_mfa) | Whether readonly IAM role requires MFA | -| [readonly\_iam\_role\_unique\_id](#output\_readonly\_iam\_role\_unique\_id) | Unique ID of IAM role | - diff --git a/examples/iam-assumable-roles/main.tf b/examples/iam-assumable-roles/main.tf deleted file mode 100644 index 275ce2c4..00000000 --- a/examples/iam-assumable-roles/main.tf +++ /dev/null @@ -1,55 +0,0 @@ -provider "aws" { - region = "eu-west-1" -} - -###################### -# IAM assumable roles -###################### -module "iam_assumable_roles" { - source = "../../modules/iam-assumable-roles" - - trusted_role_arns = [ - "arn:aws:iam::307990089504:root", - "arn:aws:iam::835367859851:user/anton", - ] - - trusted_role_services = [ - "codedeploy.amazonaws.com" - ] - - create_admin_role = true - - create_poweruser_role = true - poweruser_role_name = "Billing-And-Support-Access" - poweruser_role_policy_arns = ["arn:aws:iam::aws:policy/job-function/Billing", "arn:aws:iam::aws:policy/AWSSupportAccess"] - - create_readonly_role = true - readonly_role_requires_mfa = false -} - -###################################### -# IAM assumable roles with self assume -###################################### -module "iam_assumable_roles_with_self_assume" { - source = "../../modules/iam-assumable-roles" - - trusted_role_arns = [ - "arn:aws:iam::307990089504:root", - "arn:aws:iam::835367859851:user/anton", - ] - - trusted_role_services = [ - "codedeploy.amazonaws.com" - ] - - create_admin_role = true - allow_self_assume_role = true - create_poweruser_role = true - admin_role_name = "Admin-Role-Name-Self-Assume" - poweruser_role_name = "Billing-And-Support-Access-Self-Assume" - poweruser_role_policy_arns = ["arn:aws:iam::aws:policy/job-function/Billing", "arn:aws:iam::aws:policy/AWSSupportAccess"] - readonly_role_name = "Read-Only-Role-Name-Self-Assume" - - create_readonly_role = true - readonly_role_requires_mfa = false -} diff --git a/examples/iam-assumable-roles/outputs.tf b/examples/iam-assumable-roles/outputs.tf deleted file mode 100644 index 56bff3b6..00000000 --- a/examples/iam-assumable-roles/outputs.tf +++ /dev/null @@ -1,77 +0,0 @@ -# Admin -output "admin_iam_role_arn" { - description = "ARN of admin IAM role" - value = module.iam_assumable_roles.admin_iam_role_arn -} - -output "admin_iam_role_name" { - description = "Name of admin IAM role" - value = module.iam_assumable_roles.admin_iam_role_name -} - -output "admin_iam_role_requires_mfa" { - description = "Whether admin IAM role requires MFA" - value = module.iam_assumable_roles.admin_iam_role_requires_mfa -} - -output "admin_iam_role_path" { - description = "Path of admin IAM role" - value = module.iam_assumable_roles.admin_iam_role_path -} - -output "admin_iam_role_unique_id" { - description = "Unique ID of IAM role" - value = module.iam_assumable_roles.admin_iam_role_unique_id -} - -# Poweruser -output "poweruser_iam_role_arn" { - description = "ARN of poweruser IAM role" - value = module.iam_assumable_roles.poweruser_iam_role_arn -} - -output "poweruser_iam_role_name" { - description = "Name of poweruser IAM role" - value = module.iam_assumable_roles.poweruser_iam_role_name -} - -output "poweruser_iam_role_requires_mfa" { - description = "Whether poweruser IAM role requires MFA" - value = module.iam_assumable_roles.poweruser_iam_role_requires_mfa -} - -output "poweruser_iam_role_path" { - description = "Path of poweruser IAM role" - value = module.iam_assumable_roles.poweruser_iam_role_path -} - -output "poweruser_iam_role_unique_id" { - description = "Unique ID of IAM role" - value = module.iam_assumable_roles.poweruser_iam_role_unique_id -} - -# Readonly -output "readonly_iam_role_arn" { - description = "ARN of readonly IAM role" - value = module.iam_assumable_roles.readonly_iam_role_arn -} - -output "readonly_iam_role_name" { - description = "Name of readonly IAM role" - value = module.iam_assumable_roles.readonly_iam_role_name -} - -output "readonly_iam_role_path" { - description = "Path of readonly IAM role" - value = module.iam_assumable_roles.readonly_iam_role_path -} - -output "readonly_iam_role_unique_id" { - description = "Unique ID of IAM role" - value = module.iam_assumable_roles.readonly_iam_role_unique_id -} - -output "readonly_iam_role_requires_mfa" { - description = "Whether readonly IAM role requires MFA" - value = module.iam_assumable_roles.readonly_iam_role_requires_mfa -} diff --git a/examples/iam-github-oidc/README.md b/examples/iam-github-oidc/README.md deleted file mode 100644 index 51a7f733..00000000 --- a/examples/iam-github-oidc/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# IAM GitHub OIDC - -- Creates an IAM identity provider for GitHub OIDC -- Creates an IAM role that trust the IAM GitHub OIDC provider - - GitHub reference: https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services - - AWS IAM role reference: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-idp_oidc.html#idp_oidc_Create_GitHub - -Note: an IAM provider is 1 per account per given URL. This module would be provisioned once per AWS account, and multiple roles created with this provider as the trusted identity (typically 1 role per GitHub repository). - -To run this example you need to execute: - -```bash -$ terraform init -$ terraform plan -$ terraform apply -``` - -Run `terraform destroy` when you don't need these resources. - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 4.0 | - -## Modules - -| Name | Source | Version | -|------|--------|---------| -| [iam\_github\_oidc\_provider](#module\_iam\_github\_oidc\_provider) | ../../modules/iam-github-oidc-provider | n/a | -| [iam\_github\_oidc\_provider\_disabled](#module\_iam\_github\_oidc\_provider\_disabled) | ../../modules/iam-github-oidc-provider | n/a | -| [iam\_github\_oidc\_role](#module\_iam\_github\_oidc\_role) | ../../modules/iam-github-oidc-role | n/a | -| [iam\_github\_oidc\_role\_disabled](#module\_iam\_github\_oidc\_role\_disabled) | ../../modules/iam-github-oidc-role | n/a | - -## Resources - -| Name | Type | -|------|------| -| [aws_iam_policy.additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | - -## Inputs - -No inputs. - -## Outputs - -| Name | Description | -|------|-------------| -| [iam\_role\_arn](#output\_iam\_role\_arn) | ARN of IAM role | -| [iam\_role\_name](#output\_iam\_role\_name) | Name of IAM role | -| [iam\_role\_path](#output\_iam\_role\_path) | Path of IAM role | -| [iam\_role\_unique\_id](#output\_iam\_role\_unique\_id) | Unique ID of IAM role | -| [provider\_arn](#output\_provider\_arn) | The ARN assigned by AWS for this provider | -| [provider\_url](#output\_provider\_url) | The URL of the identity provider. Corresponds to the iss claim | - diff --git a/examples/iam-github-oidc/main.tf b/examples/iam-github-oidc/main.tf deleted file mode 100644 index 80bff275..00000000 --- a/examples/iam-github-oidc/main.tf +++ /dev/null @@ -1,103 +0,0 @@ -provider "aws" { - region = local.region -} - -locals { - name = "ex-iam-github-oidc" - region = "eu-west-1" - - tags = { - Example = local.name - GithubRepo = "terraform-aws-iam" - GithubOrg = "terraform-aws-modules" - } -} - -################################################################################ -# GitHub OIDC Provider -# Note: This is one per AWS account -################################################################################ - -module "iam_github_oidc_provider" { - source = "../../modules/iam-github-oidc-provider" - - tags = local.tags -} - -module "iam_github_oidc_provider_disabled" { - source = "../../modules/iam-github-oidc-provider" - - create = false -} - -################################################################################ -# GitHub OIDC Role -################################################################################ - -module "iam_github_oidc_role" { - source = "../../modules/iam-github-oidc-role" - - name = local.name - - # This should be updated to suit your organization, repository, references/branches, etc. - subjects = [ - # You can prepend with `repo:` but it is not required - "repo:terraform-aws-modules/terraform-aws-iam:pull_request", - "terraform-aws-modules/terraform-aws-iam:ref:refs/heads/master", - ] - - # As per the Github documentation for security hardening with OpenID Connect - # (https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect) - # This document makes many references to the fact that you can leverage any of the available - # OIDC claims to when configuring the cloud providers trust relation. For example in - # https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect#customizing-the-token-claims - # it is specified that granular OIDC policies can be defined using additional OIDC token claims. - # In this example, we ensure that the OIDC token GitHub uses to assume the AWS IAM role has the correct - # `actor` scope. - additional_trust_policy_conditions = [ - { - test = "StringEquals" - variable = "${module.iam_github_oidc_provider.url}:actor" - # This should be the list of GitHub usernames for which you want to restrict - # access to the role. - values = ["username"] - } - ] - - policies = { - additional = aws_iam_policy.additional.arn - S3ReadOnly = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" - } - - tags = local.tags -} - -module "iam_github_oidc_role_disabled" { - source = "../../modules/iam-github-oidc-role" - - create = false -} - -################################################################################ -# Supporting Resources -################################################################################ - -resource "aws_iam_policy" "additional" { - name = "${local.name}-additional" - description = "Additional test policy" - - policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Action = [ - "ec2:Describe*", - ] - Effect = "Allow" - Resource = "*" - }, - ] - }) - - tags = local.tags -} diff --git a/examples/iam-github-oidc/outputs.tf b/examples/iam-github-oidc/outputs.tf deleted file mode 100644 index a578aad9..00000000 --- a/examples/iam-github-oidc/outputs.tf +++ /dev/null @@ -1,37 +0,0 @@ -################################################################################ -# GitHub OIDC Provider -################################################################################ - -output "provider_arn" { - description = "The ARN assigned by AWS for this provider" - value = module.iam_github_oidc_provider.arn -} - -output "provider_url" { - description = "The URL of the identity provider. Corresponds to the iss claim" - value = module.iam_github_oidc_provider.url -} - -################################################################################ -# GitHub OIDC Role -################################################################################ - -output "iam_role_arn" { - description = "ARN of IAM role" - value = module.iam_github_oidc_role.arn -} - -output "iam_role_name" { - description = "Name of IAM role" - value = module.iam_github_oidc_role.name -} - -output "iam_role_path" { - description = "Path of IAM role" - value = module.iam_github_oidc_role.path -} - -output "iam_role_unique_id" { - description = "Unique ID of IAM role" - value = module.iam_github_oidc_role.unique_id -} diff --git a/examples/iam-github-oidc/variables.tf b/examples/iam-github-oidc/variables.tf deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/iam-group-complete/README.md b/examples/iam-group-complete/README.md deleted file mode 100644 index 6c3f35c2..00000000 --- a/examples/iam-group-complete/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# Complete IAM group example - -Configuration in this directory creates IAM group with users who are allowed to assume IAM roles and extended with IAM policies. - -This is a combination of `iam-group-with-assumable-roles-policy` and `iam-group-with-policies` exampled. - -# Usage - -To run this example you need to execute: - -```bash -$ terraform init -$ terraform plan -$ terraform apply -``` - -Run `terraform destroy` when you don't need these resources. - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | - -## Providers - -No providers. - -## Modules - -| Name | Source | Version | -|------|--------|---------| -| [iam\_group\_complete](#module\_iam\_group\_complete) | ../../modules/iam-group-with-assumable-roles-policy | n/a | -| [iam\_group\_complete\_with\_custom\_policy](#module\_iam\_group\_complete\_with\_custom\_policy) | ../../modules/iam-group-with-policies | n/a | -| [iam\_user1](#module\_iam\_user1) | ../../modules/iam-user | n/a | -| [iam\_user2](#module\_iam\_user2) | ../../modules/iam-user | n/a | - -## Resources - -No resources. - -## Inputs - -No inputs. - -## Outputs - -| Name | Description | -|------|-------------| -| [assumable\_roles](#output\_assumable\_roles) | List of ARNs of IAM roles which members of IAM group can assume | -| [group\_users](#output\_group\_users) | List of IAM users in IAM group | -| [policy\_arn](#output\_policy\_arn) | Assume role policy ARN for IAM group | - diff --git a/examples/iam-group-complete/main.tf b/examples/iam-group-complete/main.tf deleted file mode 100644 index 60fc41ec..00000000 --- a/examples/iam-group-complete/main.tf +++ /dev/null @@ -1,51 +0,0 @@ -############ -# IAM users -############ -module "iam_user1" { - source = "../../modules/iam-user" - - name = "user1" - - create_iam_user_login_profile = false - create_iam_access_key = false -} - -module "iam_user2" { - source = "../../modules/iam-user" - - name = "user2" - - create_iam_user_login_profile = false - create_iam_access_key = false -} - -############################################################################################# -# IAM group where user1 and user2 are allowed to assume admin role in production AWS account -############################################################################################# -module "iam_group_complete" { - source = "../../modules/iam-group-with-assumable-roles-policy" - - name = "production-admins" - - assumable_roles = ["arn:aws:iam::111111111111:role/admin"] - - group_users = [ - module.iam_user1.iam_user_name, - module.iam_user2.iam_user_name, - ] -} - -#################################################### -# Extending policies of IAM group production-admins -#################################################### -module "iam_group_complete_with_custom_policy" { - source = "../../modules/iam-group-with-policies" - - name = module.iam_group_complete.group_name - - create_group = false - - custom_group_policy_arns = [ - "arn:aws:iam::aws:policy/AmazonS3FullAccess", - ] -} diff --git a/examples/iam-group-complete/outputs.tf b/examples/iam-group-complete/outputs.tf deleted file mode 100644 index 949a9f0c..00000000 --- a/examples/iam-group-complete/outputs.tf +++ /dev/null @@ -1,14 +0,0 @@ -output "group_users" { - description = "List of IAM users in IAM group" - value = module.iam_group_complete.group_users -} - -output "assumable_roles" { - description = "List of ARNs of IAM roles which members of IAM group can assume" - value = module.iam_group_complete.assumable_roles -} - -output "policy_arn" { - description = "Assume role policy ARN for IAM group" - value = module.iam_group_complete.policy_arn -} diff --git a/examples/iam-group-complete/variables.tf b/examples/iam-group-complete/variables.tf deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/iam-group-complete/versions.tf b/examples/iam-group-complete/versions.tf deleted file mode 100644 index 7117131f..00000000 --- a/examples/iam-group-complete/versions.tf +++ /dev/null @@ -1,3 +0,0 @@ -terraform { - required_version = ">= 1.0" -} diff --git a/examples/iam-group-with-assumable-roles-policy/README.md b/examples/iam-group-with-assumable-roles-policy/README.md deleted file mode 100644 index f6e00280..00000000 --- a/examples/iam-group-with-assumable-roles-policy/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# IAM group with assumable roles policy example - -Configuration in this directory creates IAM group with users who are allowed to assume IAM roles. - -# Usage - -To run this example you need to execute: - -```bash -$ terraform init -$ terraform plan -$ terraform apply -``` - -Run `terraform destroy` when you don't need these resources. - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 4.0 | -| [aws.production](#provider\_aws.production) | >= 4.0 | - -## Modules - -| Name | Source | Version | -|------|--------|---------| -| [iam\_assumable\_role\_custom](#module\_iam\_assumable\_role\_custom) | ../../modules/iam-assumable-role | n/a | -| [iam\_assumable\_roles\_in\_prod](#module\_iam\_assumable\_roles\_in\_prod) | ../../modules/iam-assumable-roles | n/a | -| [iam\_group\_with\_assumable\_roles\_policy\_production\_admin](#module\_iam\_group\_with\_assumable\_roles\_policy\_production\_admin) | ../../modules/iam-group-with-assumable-roles-policy | n/a | -| [iam\_group\_with\_assumable\_roles\_policy\_production\_custom](#module\_iam\_group\_with\_assumable\_roles\_policy\_production\_custom) | ../../modules/iam-group-with-assumable-roles-policy | n/a | -| [iam\_group\_with\_assumable\_roles\_policy\_production\_readonly](#module\_iam\_group\_with\_assumable\_roles\_policy\_production\_readonly) | ../../modules/iam-group-with-assumable-roles-policy | n/a | -| [iam\_user1](#module\_iam\_user1) | ../../modules/iam-user | n/a | -| [iam\_user2](#module\_iam\_user2) | ../../modules/iam-user | n/a | - -## Resources - -| Name | Type | -|------|------| -| [aws_caller_identity.iam](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | -| [aws_caller_identity.production](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | - -## Inputs - -No inputs. - -## Outputs - -| Name | Description | -|------|-------------| -| [assumable\_roles](#output\_assumable\_roles) | List of ARNs of IAM roles which members of IAM group can assume | -| [group\_users](#output\_group\_users) | List of IAM users in IAM group | -| [iam\_account\_id](#output\_iam\_account\_id) | IAM AWS account id (this code is managing resources in this account) | -| [policy\_arn](#output\_policy\_arn) | Assume role policy ARN for IAM group | -| [production\_account\_id](#output\_production\_account\_id) | Production AWS account id | - diff --git a/examples/iam-group-with-assumable-roles-policy/main.tf b/examples/iam-group-with-assumable-roles-policy/main.tf deleted file mode 100644 index eef55d8f..00000000 --- a/examples/iam-group-with-assumable-roles-policy/main.tf +++ /dev/null @@ -1,134 +0,0 @@ -provider "aws" { - region = "eu-west-1" -} - -provider "aws" { - region = "eu-west-1" - - assume_role { - role_arn = "arn:aws:iam::835367859851:role/anton-demo" - } - - alias = "production" -} - -data "aws_caller_identity" "iam" {} - -data "aws_caller_identity" "production" { - provider = aws.production -} - -############ -# IAM users -############ -module "iam_user1" { - source = "../../modules/iam-user" - - name = "user1" - - create_iam_user_login_profile = false - create_iam_access_key = false -} - -module "iam_user2" { - source = "../../modules/iam-user" - - name = "user2" - - create_iam_user_login_profile = false - create_iam_access_key = false -} - -##################################################################################### -# Several IAM assumable roles (admin, poweruser, readonly) in production AWS account -# Note: Anyone from IAM account can assume them. -##################################################################################### -module "iam_assumable_roles_in_prod" { - source = "../../modules/iam-assumable-roles" - - trusted_role_arns = [ - "arn:aws:iam::${data.aws_caller_identity.iam.account_id}:root", - ] - - create_admin_role = true - create_poweruser_role = true - - create_readonly_role = true - readonly_role_requires_mfa = false - - providers = { - aws = aws.production - } -} - -module "iam_assumable_role_custom" { - source = "../../modules/iam-assumable-role" - - trusted_role_arns = [ - "arn:aws:iam::${data.aws_caller_identity.iam.account_id}:root", - ] - - create_role = true - - role_name = "custom" - role_requires_mfa = true - - custom_role_policy_arns = [ - "arn:aws:iam::aws:policy/AmazonCognitoReadOnly", - "arn:aws:iam::aws:policy/AlexaForBusinessFullAccess", - ] - - providers = { - aws = aws.production - } -} - -################################################################################################ -# IAM group where user1 and user2 are allowed to assume readonly role in production AWS account -# Note: IAM AWS account is default, so there is no need to specify it here. -################################################################################################ -module "iam_group_with_assumable_roles_policy_production_readonly" { - source = "../../modules/iam-group-with-assumable-roles-policy" - - name = "production-readonly" - - assumable_roles = [module.iam_assumable_roles_in_prod.readonly_iam_role_arn] - - group_users = [ - module.iam_user1.iam_user_name, - module.iam_user2.iam_user_name, - ] -} - -################################################################################################ -# IAM group where user1 is allowed to assume admin role in production AWS account -# Note: IAM AWS account is default, so there is no need to specify it here. -################################################################################################ -module "iam_group_with_assumable_roles_policy_production_admin" { - source = "../../modules/iam-group-with-assumable-roles-policy" - - name = "production-admin" - assumable_roles_policy_name_suffix = "-assumable-roles" - - assumable_roles = [module.iam_assumable_roles_in_prod.admin_iam_role_arn] - - group_users = [ - module.iam_user1.iam_user_name, - ] -} - -################################################################################################ -# IAM group where user2 is allowed to assume custom role in production AWS account -# Note: IAM AWS account is default, so there is no need to specify it here. -################################################################################################ -module "iam_group_with_assumable_roles_policy_production_custom" { - source = "../../modules/iam-group-with-assumable-roles-policy" - - name = "production-custom" - - assumable_roles = [module.iam_assumable_role_custom.iam_role_arn] - - group_users = [ - module.iam_user2.iam_user_name, - ] -} diff --git a/examples/iam-group-with-assumable-roles-policy/outputs.tf b/examples/iam-group-with-assumable-roles-policy/outputs.tf deleted file mode 100644 index d123bd0e..00000000 --- a/examples/iam-group-with-assumable-roles-policy/outputs.tf +++ /dev/null @@ -1,24 +0,0 @@ -output "iam_account_id" { - description = "IAM AWS account id (this code is managing resources in this account)" - value = data.aws_caller_identity.iam.account_id -} - -output "production_account_id" { - description = "Production AWS account id" - value = data.aws_caller_identity.production.account_id -} - -output "group_users" { - description = "List of IAM users in IAM group" - value = module.iam_group_with_assumable_roles_policy_production_readonly.group_users -} - -output "assumable_roles" { - description = "List of ARNs of IAM roles which members of IAM group can assume" - value = module.iam_group_with_assumable_roles_policy_production_readonly.assumable_roles -} - -output "policy_arn" { - description = "Assume role policy ARN for IAM group" - value = module.iam_group_with_assumable_roles_policy_production_readonly.policy_arn -} diff --git a/examples/iam-group-with-assumable-roles-policy/variables.tf b/examples/iam-group-with-assumable-roles-policy/variables.tf deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/iam-group-with-policies/main.tf b/examples/iam-group-with-policies/main.tf deleted file mode 100644 index 7b27426f..00000000 --- a/examples/iam-group-with-policies/main.tf +++ /dev/null @@ -1,79 +0,0 @@ -############ -# IAM users -############ -module "iam_user1" { - source = "../../modules/iam-user" - - name = "user1" - - create_iam_user_login_profile = false - create_iam_access_key = false -} - -module "iam_user2" { - source = "../../modules/iam-user" - - name = "user2" - path = "/developers/" - - create_iam_user_login_profile = false - create_iam_access_key = false -} - -##################################################################################### -# IAM group for superadmins with full Administrator access -##################################################################################### -module "iam_group_superadmins" { - source = "../../modules/iam-group-with-policies" - - name = "superadmins" - - group_users = [ - module.iam_user1.iam_user_name, - module.iam_user2.iam_user_name, - ] - - custom_group_policy_arns = [ - "arn:aws:iam::aws:policy/AdministratorAccess", - ] -} - -##################################################################################### -# IAM group for users with custom access -##################################################################################### -module "iam_group_with_custom_policies" { - source = "../../modules/iam-group-with-policies" - - name = "custom" - path = "/custom/" - - group_users = [ - module.iam_user1.iam_user_name, - module.iam_user2.iam_user_name, - ] - - custom_group_policy_arns = [ - "arn:aws:iam::aws:policy/AmazonCognitoReadOnly", - "arn:aws:iam::aws:policy/AlexaForBusinessFullAccess", - ] - - custom_group_policies = [ - { - name = "AllowS3Listing" - policy = data.aws_iam_policy_document.sample.json - }, - ] -} - -###################### -# IAM policy (sample) -###################### -data "aws_iam_policy_document" "sample" { - statement { - actions = [ - "s3:ListBuckets", - ] - - resources = ["*"] - } -} diff --git a/examples/iam-group-with-policies/outputs.tf b/examples/iam-group-with-policies/outputs.tf deleted file mode 100644 index 6da717d0..00000000 --- a/examples/iam-group-with-policies/outputs.tf +++ /dev/null @@ -1,19 +0,0 @@ -output "iam_account_id" { - description = "IAM AWS account id" - value = module.iam_group_superadmins.aws_account_id -} - -output "group_arn" { - description = "IAM group arn" - value = module.iam_group_superadmins.group_arn -} - -output "group_users" { - description = "List of IAM users in IAM group" - value = module.iam_group_superadmins.group_users -} - -output "group_name" { - description = "IAM group name" - value = module.iam_group_superadmins.group_name -} diff --git a/examples/iam-group-with-policies/variables.tf b/examples/iam-group-with-policies/variables.tf deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/iam-group-with-policies/README.md b/examples/iam-group/README.md similarity index 53% rename from examples/iam-group-with-policies/README.md rename to examples/iam-group/README.md index ed1341ff..2d347247 100644 --- a/examples/iam-group-with-policies/README.md +++ b/examples/iam-group/README.md @@ -1,6 +1,6 @@ -# IAM group with policies example +# AWS IAM Group Example -Configuration in this directory creates IAM group with users who has specified IAM policies. +Configuration in this directory creates IAM group with users who are allowed to assume IAM roles and extended with IAM policies. # Usage @@ -32,8 +32,8 @@ Run `terraform destroy` when you don't need these resources. | Name | Source | Version | |------|--------|---------| -| [iam\_group\_superadmins](#module\_iam\_group\_superadmins) | ../../modules/iam-group-with-policies | n/a | -| [iam\_group\_with\_custom\_policies](#module\_iam\_group\_with\_custom\_policies) | ../../modules/iam-group-with-policies | n/a | +| [iam\_group](#module\_iam\_group) | ../../modules/iam-group | n/a | +| [iam\_group\_disabled](#module\_iam\_group\_disabled) | ../../modules/iam-group | n/a | | [iam\_user1](#module\_iam\_user1) | ../../modules/iam-user | n/a | | [iam\_user2](#module\_iam\_user2) | ../../modules/iam-user | n/a | @@ -41,7 +41,7 @@ Run `terraform destroy` when you don't need these resources. | Name | Type | |------|------| -| [aws_iam_policy_document.sample](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | ## Inputs @@ -51,8 +51,12 @@ No inputs. | Name | Description | |------|-------------| -| [group\_arn](#output\_group\_arn) | IAM group arn | -| [group\_name](#output\_group\_name) | IAM group name | +| [group\_arn](#output\_group\_arn) | The ARN assigned by AWS for this group | +| [group\_id](#output\_group\_id) | The group's ID | +| [group\_name](#output\_group\_name) | The group's name | +| [group\_policy\_arn](#output\_group\_policy\_arn) | The ARN assigned by AWS for this policy | +| [group\_policy\_id](#output\_group\_policy\_id) | The policy's ID | +| [group\_policy\_name](#output\_group\_policy\_name) | The policy's name | +| [group\_unique\_id](#output\_group\_unique\_id) | The unique ID assigned by AWS | | [group\_users](#output\_group\_users) | List of IAM users in IAM group | -| [iam\_account\_id](#output\_iam\_account\_id) | IAM AWS account id | diff --git a/examples/iam-group/main.tf b/examples/iam-group/main.tf new file mode 100644 index 00000000..fb4cee17 --- /dev/null +++ b/examples/iam-group/main.tf @@ -0,0 +1,97 @@ +provider "aws" { + region = "eu-west-1" +} + +locals { + name = "ex-${basename(path.cwd)}" + + tags = { + Example = local.name + GithubRepo = "terraform-aws-iam" + GithubOrg = "terraform-aws-modules" + } +} + +################################################################################ +# IAM Group +################################################################################ + +module "iam_group" { + source = "../../modules/iam-group" + + name = "production-admins" + + users = [ + module.iam_user1.name, + module.iam_user2.name, + ] + + permission_statements = [ + { + sid = "AssumeRole" + actions = ["sts:AssumeRole"] + resources = ["arn:aws:iam::111111111111:role/admin"] + } + ] + + policies = { + ReadOnlyAccess = "arn:aws:iam::aws:policy/ReadOnlyAccess" + } + + tags = local.tags +} + +module "iam_group_disabled" { + source = "../../modules/iam-group" + + create = false +} + +################################################################################ +# Supporting resources +################################################################################ + +module "iam_user1" { + source = "../../modules/iam-user" + + name = "${local.name}-user1" + + create_login_profile = false + create_access_key = false + + tags = local.tags +} + +module "iam_user2" { + source = "../../modules/iam-user" + + name = "${local.name}-user2" + + create_login_profile = false + create_access_key = false + + tags = local.tags +} + +resource "aws_iam_policy" "this" { + name = local.name + path = "/" + description = "Example policy" + + policy = <<-EOT + { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "ec2:Describe*" + ], + "Effect": "Allow", + "Resource": "*" + } + ] + } + EOT + + tags = local.tags +} diff --git a/examples/iam-group/outputs.tf b/examples/iam-group/outputs.tf new file mode 100644 index 00000000..45cecc9a --- /dev/null +++ b/examples/iam-group/outputs.tf @@ -0,0 +1,43 @@ +################################################################################ +# IAM Group +################################################################################ + +output "group_id" { + description = "The group's ID" + value = module.iam_group.id +} + +output "group_arn" { + description = "The ARN assigned by AWS for this group" + value = module.iam_group.arn +} + +output "group_name" { + description = "The group's name" + value = module.iam_group.name +} + +output "group_unique_id" { + description = " The unique ID assigned by AWS" + value = module.iam_group.unique_id +} + +output "group_users" { + description = "List of IAM users in IAM group" + value = module.iam_group.users +} + +output "group_policy_arn" { + description = "The ARN assigned by AWS for this policy" + value = module.iam_group.policy_arn +} + +output "group_policy_name" { + description = "The policy's name" + value = module.iam_group.policy_name +} + +output "group_policy_id" { + description = "The policy's ID" + value = module.iam_group.policy_id +} diff --git a/examples/iam-assumable-role-with-oidc/variables.tf b/examples/iam-group/variables.tf similarity index 100% rename from examples/iam-assumable-role-with-oidc/variables.tf rename to examples/iam-group/variables.tf diff --git a/examples/iam-assumable-role-with-oidc/versions.tf b/examples/iam-group/versions.tf similarity index 100% rename from examples/iam-assumable-role-with-oidc/versions.tf rename to examples/iam-group/versions.tf diff --git a/examples/iam-oidc-provider/README.md b/examples/iam-oidc-provider/README.md new file mode 100644 index 00000000..b81f8bcc --- /dev/null +++ b/examples/iam-oidc-provider/README.md @@ -0,0 +1,58 @@ +# AWS IAM OIDC Provider Example + +- Creates an IAM identity provider for GitHub OIDC +- Creates an IAM role that trust the IAM GitHub OIDC provider + - GitHub reference: https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services + - AWS IAM role reference: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-idp_oidc.html#idp_oidc_Create_GitHub + +Note: an IAM provider is 1 per account per given URL. This module would be provisioned once per AWS account, and multiple roles created with this provider as the trusted identity (typically 1 role per GitHub repository). + +To run this example you need to execute: + +```bash +$ terraform init +$ terraform plan +$ terraform apply +``` + +Run `terraform destroy` when you don't need these resources. + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 4.0 | + +## Providers + +No providers. + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [github\_oidc\_iam\_provider](#module\_github\_oidc\_iam\_provider) | ../../modules/iam-oidc-provider | n/a | +| [github\_oidc\_iam\_role](#module\_github\_oidc\_iam\_role) | ../../modules/iam-role-oidc | n/a | +| [oidc\_iam\_provider\_disabled](#module\_oidc\_iam\_provider\_disabled) | ../../modules/iam-oidc-provider | n/a | +| [oidc\_iam\_role\_disabled](#module\_oidc\_iam\_role\_disabled) | ../../modules/iam-role-oidc | n/a | + +## Resources + +No resources. + +## Inputs + +No inputs. + +## Outputs + +| Name | Description | +|------|-------------| +| [github\_oidc\_iam\_provider\_arn](#output\_github\_oidc\_iam\_provider\_arn) | The ARN assigned by AWS for this provider | +| [github\_oidc\_iam\_provider\_url](#output\_github\_oidc\_iam\_provider\_url) | The URL of the identity provider. Corresponds to the iss claim | +| [github\_oidc\_iam\_role\_arn](#output\_github\_oidc\_iam\_role\_arn) | ARN of IAM role | +| [github\_oidc\_iam\_role\_name](#output\_github\_oidc\_iam\_role\_name) | Name of IAM role | +| [github\_oidc\_iam\_role\_unique\_id](#output\_github\_oidc\_iam\_role\_unique\_id) | Unique ID of IAM role | + diff --git a/examples/iam-oidc-provider/main.tf b/examples/iam-oidc-provider/main.tf new file mode 100644 index 00000000..369de98a --- /dev/null +++ b/examples/iam-oidc-provider/main.tf @@ -0,0 +1,61 @@ +provider "aws" { + region = "eu-west-1" +} + +locals { + name = "ex-${basename(path.cwd)}" + + tags = { + Example = local.name + GithubRepo = "terraform-aws-iam" + GithubOrg = "terraform-aws-modules" + } +} + +################################################################################ +# OIDC Provider +# Note: This is one provider URL per AWS account +################################################################################ + +module "github_oidc_iam_provider" { + source = "../../modules/iam-oidc-provider" + + tags = local.tags +} + +module "oidc_iam_provider_disabled" { + source = "../../modules/iam-oidc-provider" + + create = false +} + +################################################################################ +# OIDC IAM Role +################################################################################ + +module "github_oidc_iam_role" { + source = "../../modules/iam-role-oidc" + + name = local.name + + enable_github_oidc = true + + # This should be updated to suit your organization, repository, references/branches, etc. + oidc_subjects = [ + # You can prepend with `repo:` but it is not required + "repo:terraform-aws-modules/terraform-aws-iam:pull_request", + "terraform-aws-modules/terraform-aws-iam:ref:refs/heads/master", + ] + + policies = { + S3ReadOnly = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" + } + + tags = local.tags +} + +module "oidc_iam_role_disabled" { + source = "../../modules/iam-role-oidc" + + create = false +} diff --git a/examples/iam-oidc-provider/outputs.tf b/examples/iam-oidc-provider/outputs.tf new file mode 100644 index 00000000..0b7bb465 --- /dev/null +++ b/examples/iam-oidc-provider/outputs.tf @@ -0,0 +1,28 @@ +################################################################################ +# GitHub OIDC Provider & Role +################################################################################ + +output "github_oidc_iam_provider_arn" { + description = "The ARN assigned by AWS for this provider" + value = module.github_oidc_iam_provider.arn +} + +output "github_oidc_iam_provider_url" { + description = "The URL of the identity provider. Corresponds to the iss claim" + value = module.github_oidc_iam_provider.url +} + +output "github_oidc_iam_role_arn" { + description = "ARN of IAM role" + value = module.github_oidc_iam_role.arn +} + +output "github_oidc_iam_role_name" { + description = "Name of IAM role" + value = module.github_oidc_iam_role.name +} + +output "github_oidc_iam_role_unique_id" { + description = "Unique ID of IAM role" + value = module.github_oidc_iam_role.unique_id +} diff --git a/examples/iam-assumable-role-with-saml/variables.tf b/examples/iam-oidc-provider/variables.tf similarity index 100% rename from examples/iam-assumable-role-with-saml/variables.tf rename to examples/iam-oidc-provider/variables.tf diff --git a/examples/iam-assumable-role-with-saml/versions.tf b/examples/iam-oidc-provider/versions.tf similarity index 100% rename from examples/iam-assumable-role-with-saml/versions.tf rename to examples/iam-oidc-provider/versions.tf diff --git a/examples/iam-policy/README.md b/examples/iam-policy/README.md deleted file mode 100644 index 6e9931ad..00000000 --- a/examples/iam-policy/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# IAM user example - -Configuration in this directory creates IAM policies. - -# Usage - -To run this example you need to execute: - -```bash -$ terraform init -$ terraform plan -$ terraform apply -``` - -Run `terraform destroy` when you don't need these resources. - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 4.0 | - -## Modules - -| Name | Source | Version | -|------|--------|---------| -| [iam\_policy](#module\_iam\_policy) | ../../modules/iam-policy | n/a | -| [iam\_policy\_from\_data\_source](#module\_iam\_policy\_from\_data\_source) | ../../modules/iam-policy | n/a | -| [iam\_policy\_optional](#module\_iam\_policy\_optional) | ../../modules/iam-policy | n/a | - -## Resources - -| Name | Type | -|------|------| -| [aws_iam_policy_document.bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | - -## Inputs - -No inputs. - -## Outputs - -| Name | Description | -|------|-------------| -| [arn](#output\_arn) | The ARN assigned by AWS to this policy | -| [description](#output\_description) | The description of the policy | -| [id](#output\_id) | The policy ID | -| [name](#output\_name) | The name of the policy | -| [path](#output\_path) | The path of the policy in IAM | -| [policy](#output\_policy) | The policy document | - diff --git a/examples/iam-policy/main.tf b/examples/iam-policy/main.tf deleted file mode 100644 index e583e828..00000000 --- a/examples/iam-policy/main.tf +++ /dev/null @@ -1,61 +0,0 @@ -provider "aws" { - region = "eu-west-1" -} - -data "aws_iam_policy_document" "bucket_policy" { - statement { - sid = "AllowFullS3Access" - actions = ["s3:ListAllMyBuckets"] - resources = ["*"] - } -} - -######################################### -# IAM policy -######################################### -module "iam_policy" { - source = "../../modules/iam-policy" - - name_prefix = "example-" - path = "/" - description = "My example policy" - - policy = < [aws](#provider\_aws) | >= 4.0 | +No providers. ## Modules | Name | Source | Version | |------|--------|---------| | [read\_only\_iam\_policy](#module\_read\_only\_iam\_policy) | ../../modules/iam-read-only-policy | n/a | +| [read\_only\_iam\_policy\_disabled](#module\_read\_only\_iam\_policy\_disabled) | ../../modules/iam-read-only-policy | n/a | | [read\_only\_iam\_policy\_doc](#module\_read\_only\_iam\_policy\_doc) | ../../modules/iam-read-only-policy | n/a | ## Resources -| Name | Type | -|------|------| -| [aws_ssoadmin_permission_set.example](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_permission_set) | resource | -| [aws_ssoadmin_permission_set_inline_policy.example](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssoadmin_permission_set_inline_policy) | resource | +No resources. ## Inputs @@ -51,9 +47,8 @@ No inputs. | Name | Description | |------|-------------| | [arn](#output\_arn) | The ARN assigned by AWS to this policy | -| [description](#output\_description) | The description of the policy | -| [id](#output\_id) | The policy ID | +| [id](#output\_id) | The policy's ID | | [name](#output\_name) | The name of the policy | -| [path](#output\_path) | The path of the policy in IAM | | [policy](#output\_policy) | The policy document | +| [policy\_json](#output\_policy\_json) | Policy document JSON | diff --git a/examples/iam-read-only-policy/main.tf b/examples/iam-read-only-policy/main.tf index 907d7f40..d5b77a07 100644 --- a/examples/iam-read-only-policy/main.tf +++ b/examples/iam-read-only-policy/main.tf @@ -3,6 +3,8 @@ provider "aws" { } locals { + name = "ex-${basename(path.cwd)}" + allowed_services_compute = ["ec2", "ecr", "eks", "ecs", "lambda", "autoscaling", "elasticloadbalancing"] allowed_services_networking = ["vpc", "route53", "route53domains", "route53resolver", "servicediscovery"] allowed_services_storage = ["s3", "backup", "dynamo", "dms", "elasticfilesystem"] @@ -22,45 +24,41 @@ locals { local.allowed_services_application, local.allowed_services_security ) + + tags = { + Example = local.name + GithubRepo = "terraform-aws-iam" + GithubOrg = "terraform-aws-modules" + } } +################################################################################ +# IAM Policy +################################################################################ + module "read_only_iam_policy" { source = "../../modules/iam-read-only-policy" - name = "example" - path = "/" + name = local.name + path = "/example/" description = "My read only example policy" allowed_services = local.allowed_services - tags = { - PolicyDescription = "My read only example policy" - } + tags = local.tags } module "read_only_iam_policy_doc" { source = "../../modules/iam-read-only-policy" - name = "only-doc-example" - path = "/" - description = "My read only example policy" - - create_policy = false - + create_policy = false allowed_services = local.allowed_services - tags = { - PolicyDescription = "My read only example policy" - } + tags = local.tags } -resource "aws_ssoadmin_permission_set" "example" { - name = "Example" - instance_arn = "arn:aws:sso:::instance/example" -} +module "read_only_iam_policy_disabled" { + source = "../../modules/iam-read-only-policy" -resource "aws_ssoadmin_permission_set_inline_policy" "example" { - inline_policy = module.read_only_iam_policy_doc.policy_json - instance_arn = aws_ssoadmin_permission_set.example.instance_arn - permission_set_arn = aws_ssoadmin_permission_set.example.arn + create = false } diff --git a/examples/iam-read-only-policy/outputs.tf b/examples/iam-read-only-policy/outputs.tf index 9ca3c915..146f778a 100644 --- a/examples/iam-read-only-policy/outputs.tf +++ b/examples/iam-read-only-policy/outputs.tf @@ -1,5 +1,9 @@ +################################################################################ +# IAM Policy +################################################################################ + output "id" { - description = "The policy ID" + description = "The policy's ID" value = module.read_only_iam_policy.id } @@ -8,22 +12,17 @@ output "arn" { value = module.read_only_iam_policy.arn } -output "description" { - description = "The description of the policy" - value = module.read_only_iam_policy.description -} - output "name" { description = "The name of the policy" value = module.read_only_iam_policy.name } -output "path" { - description = "The path of the policy in IAM" - value = module.read_only_iam_policy.path -} - output "policy" { description = "The policy document" value = module.read_only_iam_policy.policy } + +output "policy_json" { + description = "Policy document JSON" + value = module.read_only_iam_policy.policy_json +} diff --git a/examples/iam-role-for-service-accounts-eks/README.md b/examples/iam-role-for-service-accounts-eks/README.md deleted file mode 100644 index c08090d1..00000000 --- a/examples/iam-role-for-service-accounts-eks/README.md +++ /dev/null @@ -1,82 +0,0 @@ -# IAM Role for Service Accounts in EKS - -Configuration in this directory creates IAM roles that can be assumed by multiple EKS `ServiceAccount`s for various tasks. - -# Usage - -To run this example you need to execute: - -```bash -$ terraform init -$ terraform plan -$ terraform apply -``` - -Run `terraform destroy` when you don't need these resources. - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0, < 6.0 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 4.0, < 6.0 | - -## Modules - -| Name | Source | Version | -|------|--------|---------| -| [amazon\_managed\_service\_prometheus\_irsa\_role](#module\_amazon\_managed\_service\_prometheus\_irsa\_role) | ../../modules/iam-role-for-service-accounts-eks | n/a | -| [appmesh\_controller\_irsa\_role](#module\_appmesh\_controller\_irsa\_role) | ../../modules/iam-role-for-service-accounts-eks | n/a | -| [appmesh\_envoy\_proxy\_irsa\_role](#module\_appmesh\_envoy\_proxy\_irsa\_role) | ../../modules/iam-role-for-service-accounts-eks | n/a | -| [aws\_gateway\_controller\_irsa\_role](#module\_aws\_gateway\_controller\_irsa\_role) | ../../modules/iam-role-for-service-accounts-eks | n/a | -| [cert\_manager\_irsa\_role](#module\_cert\_manager\_irsa\_role) | ../../modules/iam-role-for-service-accounts-eks | n/a | -| [cloudwatch\_observability\_irsa\_role](#module\_cloudwatch\_observability\_irsa\_role) | ../../modules/iam-role-for-service-accounts-eks | n/a | -| [cluster\_autoscaler\_irsa\_role](#module\_cluster\_autoscaler\_irsa\_role) | ../../modules/iam-role-for-service-accounts-eks | n/a | -| [disabled](#module\_disabled) | ../../modules/iam-role-for-service-accounts-eks | n/a | -| [ebs\_csi\_irsa\_role](#module\_ebs\_csi\_irsa\_role) | ../../modules/iam-role-for-service-accounts-eks | n/a | -| [efs\_csi\_irsa\_role](#module\_efs\_csi\_irsa\_role) | ../../modules/iam-role-for-service-accounts-eks | n/a | -| [eks](#module\_eks) | terraform-aws-modules/eks/aws | ~> 19.21 | -| [external\_dns\_irsa\_role](#module\_external\_dns\_irsa\_role) | ../../modules/iam-role-for-service-accounts-eks | n/a | -| [external\_secrets\_irsa\_role](#module\_external\_secrets\_irsa\_role) | ../../modules/iam-role-for-service-accounts-eks | n/a | -| [fsx\_lustre\_csi\_irsa\_role](#module\_fsx\_lustre\_csi\_irsa\_role) | ../../modules/iam-role-for-service-accounts-eks | n/a | -| [fsx\_openzfs\_csi\_irsa\_role](#module\_fsx\_openzfs\_csi\_irsa\_role) | ../../modules/iam-role-for-service-accounts-eks | n/a | -| [iam\_eks\_role](#module\_iam\_eks\_role) | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | n/a | -| [iam\_policy](#module\_iam\_policy) | terraform-aws-modules/iam/aws//modules/iam-policy | n/a | -| [irsa\_role](#module\_irsa\_role) | ../../modules/iam-role-for-service-accounts-eks | n/a | -| [karpenter\_controller\_irsa\_role](#module\_karpenter\_controller\_irsa\_role) | ../../modules/iam-role-for-service-accounts-eks | n/a | -| [load\_balancer\_controller\_irsa\_role](#module\_load\_balancer\_controller\_irsa\_role) | ../../modules/iam-role-for-service-accounts-eks | n/a | -| [load\_balancer\_controller\_targetgroup\_binding\_only\_irsa\_role](#module\_load\_balancer\_controller\_targetgroup\_binding\_only\_irsa\_role) | ../../modules/iam-role-for-service-accounts-eks | n/a | -| [mountpoint\_s3\_csi\_irsa\_role](#module\_mountpoint\_s3\_csi\_irsa\_role) | ../../modules/iam-role-for-service-accounts-eks | n/a | -| [node\_termination\_handler\_irsa\_role](#module\_node\_termination\_handler\_irsa\_role) | ../../modules/iam-role-for-service-accounts-eks | n/a | -| [velero\_irsa\_role](#module\_velero\_irsa\_role) | ../../modules/iam-role-for-service-accounts-eks | n/a | -| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 5.0 | -| [vpc\_cni\_ipv4\_irsa\_role](#module\_vpc\_cni\_ipv4\_irsa\_role) | ../../modules/iam-role-for-service-accounts-eks | n/a | -| [vpc\_cni\_ipv6\_irsa\_role](#module\_vpc\_cni\_ipv6\_irsa\_role) | ../../modules/iam-role-for-service-accounts-eks | n/a | - -## Resources - -| Name | Type | -|------|------| -| [aws_iam_policy.additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | - -## Inputs - -No inputs. - -## Outputs - -| Name | Description | -|------|-------------| -| [iam\_role\_arn](#output\_iam\_role\_arn) | ARN of IAM role | -| [iam\_role\_name](#output\_iam\_role\_name) | Name of IAM role | -| [iam\_role\_path](#output\_iam\_role\_path) | Path of IAM role | -| [iam\_role\_unique\_id](#output\_iam\_role\_unique\_id) | Unique ID of IAM role | - diff --git a/examples/iam-role-for-service-accounts-eks/main.tf b/examples/iam-role-for-service-accounts-eks/main.tf deleted file mode 100644 index 17562e82..00000000 --- a/examples/iam-role-for-service-accounts-eks/main.tf +++ /dev/null @@ -1,516 +0,0 @@ -provider "aws" { - region = local.region -} - -data "aws_availability_zones" "available" {} - -locals { - name = "ex-irsa" - region = "eu-west-1" - - vpc_cidr = "10.0.0.0/16" - azs = slice(data.aws_availability_zones.available.names, 0, 3) - - tags = { - Example = local.name - GithubRepo = "terraform-aws-iam" - GithubOrg = "terraform-aws-modules" - } -} - -################################################################################ -# IRSA Roles -################################################################################ - -module "disabled" { - source = "../../modules/iam-role-for-service-accounts-eks" - - role_name = "disabled" - create_role = false -} - -module "irsa_role" { - source = "../../modules/iam-role-for-service-accounts-eks" - - role_name = local.name - allow_self_assume_role = true - - oidc_providers = { - one = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["default:my-app", "canary:my-app"] - } - two = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["default:blue", "canary:blue"] - } - } - - role_policy_arns = { - AmazonEKS_CNI_Policy = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy" - additional = aws_iam_policy.additional.arn - } - - tags = local.tags -} - -module "aws_gateway_controller_irsa_role" { - source = "../../modules/iam-role-for-service-accounts-eks" - - role_name = "aws-gateway-controller" - attach_aws_gateway_controller_policy = true - - oidc_providers = { - ex = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["aws-application-networking-system:gateway-api-controller"] - } - } - - tags = local.tags -} - -module "cert_manager_irsa_role" { - source = "../../modules/iam-role-for-service-accounts-eks" - - role_name = "cert-manager" - attach_cert_manager_policy = true - cert_manager_hosted_zone_arns = ["arn:aws:route53:::hostedzone/IClearlyMadeThisUp"] - - oidc_providers = { - ex = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["kube-system:cert-manager"] - } - } - - tags = local.tags -} - -module "cluster_autoscaler_irsa_role" { - source = "../../modules/iam-role-for-service-accounts-eks" - - role_name = "cluster-autoscaler" - attach_cluster_autoscaler_policy = true - cluster_autoscaler_cluster_names = [module.eks.cluster_name] - - oidc_providers = { - ex = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["kube-system:cluster-autoscaler"] - } - } - - tags = local.tags -} - -module "ebs_csi_irsa_role" { - source = "../../modules/iam-role-for-service-accounts-eks" - - role_name = "ebs-csi" - attach_ebs_csi_policy = true - - oidc_providers = { - ex = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["kube-system:ebs-csi-controller-sa"] - } - } - - tags = local.tags -} - -module "efs_csi_irsa_role" { - source = "../../modules/iam-role-for-service-accounts-eks" - - role_name = "efs-csi" - attach_efs_csi_policy = true - - oidc_providers = { - ex = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["kube-system:efs-csi-controller-sa"] - } - } - - tags = local.tags -} - -module "mountpoint_s3_csi_irsa_role" { - source = "../../modules/iam-role-for-service-accounts-eks" - - role_name = "mountpoint-s3-csi" - attach_mountpoint_s3_csi_policy = true - mountpoint_s3_csi_bucket_arns = ["arn:aws:s3:::mountpoint-s3-csi-bucket"] - mountpoint_s3_csi_path_arns = ["arn:aws:s3:::mountpoint-s3-csi-bucket/example/*"] - - oidc_providers = { - ex = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["kube-system:s3-csi-driver-sa"] - } - } - - tags = local.tags -} - -module "external_dns_irsa_role" { - source = "../../modules/iam-role-for-service-accounts-eks" - - role_name = "external-dns" - attach_external_dns_policy = true - external_dns_hosted_zone_arns = ["arn:aws:route53:::hostedzone/IClearlyMadeThisUp"] - - oidc_providers = { - ex = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["kube-system:external-dns"] - } - } - - tags = local.tags -} - -module "external_secrets_irsa_role" { - source = "../../modules/iam-role-for-service-accounts-eks" - - role_name = "external-secrets" - attach_external_secrets_policy = true - external_secrets_ssm_parameter_arns = ["arn:aws:ssm:*:*:parameter/foo"] - external_secrets_secrets_manager_arns = ["arn:aws:secretsmanager:*:*:secret:bar"] - external_secrets_kms_key_arns = ["arn:aws:kms:*:*:key/1234abcd-12ab-34cd-56ef-1234567890ab"] - external_secrets_secrets_manager_create_permission = false - - oidc_providers = { - ex = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["default:kubernetes-external-secrets"] - } - } - - tags = local.tags -} - -module "fsx_lustre_csi_irsa_role" { - source = "../../modules/iam-role-for-service-accounts-eks" - - role_name = "fsx-lustre-csi" - attach_fsx_lustre_csi_policy = true - - oidc_providers = { - ex = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["kube-system:fsx-csi-controller-sa"] - } - } -} - -module "fsx_openzfs_csi_irsa_role" { - source = "../../modules/iam-role-for-service-accounts-eks" - - role_name = "fsx-openzfs-csi" - attach_fsx_openzfs_csi_policy = true - - oidc_providers = { - ex = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["kube-system:fsx-openzfs-csi-controller-sa"] - } - } -} - -module "karpenter_controller_irsa_role" { - source = "../../modules/iam-role-for-service-accounts-eks" - - role_name = "karpenter-controller" - attach_karpenter_controller_policy = true - - karpenter_controller_cluster_name = module.eks.cluster_name - karpenter_controller_node_iam_role_arns = [module.eks.eks_managed_node_groups["default"].iam_role_arn] - - oidc_providers = { - ex = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["karpenter:karpenter"] - } - } - - tags = local.tags -} - -module "load_balancer_controller_irsa_role" { - source = "../../modules/iam-role-for-service-accounts-eks" - - role_name = "load-balancer-controller" - attach_load_balancer_controller_policy = true - - oidc_providers = { - ex = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["kube-system:aws-load-balancer-controller"] - } - } - - tags = local.tags -} - -module "load_balancer_controller_targetgroup_binding_only_irsa_role" { - source = "../../modules/iam-role-for-service-accounts-eks" - - role_name = "load-balancer-controller-targetgroup-binding-only" - attach_load_balancer_controller_targetgroup_binding_only_policy = true - - oidc_providers = { - ex = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["kube-system:aws-load-balancer-controller"] - } - } - - tags = local.tags -} - -module "appmesh_controller_irsa_role" { - source = "../../modules/iam-role-for-service-accounts-eks" - - role_name = "appmesh-controller" - attach_appmesh_controller_policy = true - - oidc_providers = { - ex = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["appmesh-system:appmesh-controller"] - } - } - - tags = local.tags -} - -module "appmesh_envoy_proxy_irsa_role" { - source = "../../modules/iam-role-for-service-accounts-eks" - - role_name = "appmesh-envoy-proxy" - attach_appmesh_envoy_proxy_policy = true - - oidc_providers = { - ex = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["appmesh-system:appmesh-envoy-proxy"] - } - } - - tags = local.tags -} - -module "amazon_managed_service_prometheus_irsa_role" { - source = "../../modules/iam-role-for-service-accounts-eks" - - role_name = "amazon-managed-service-prometheus" - attach_amazon_managed_service_prometheus_policy = true - - oidc_providers = { - ex = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["prometheus:amp-ingest"] - } - } - - tags = local.tags -} - -module "node_termination_handler_irsa_role" { - source = "../../modules/iam-role-for-service-accounts-eks" - - role_name = "node-termination-handler" - attach_node_termination_handler_policy = true - - oidc_providers = { - ex = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["kube-system:aws-node"] - } - } - - tags = local.tags -} - -module "velero_irsa_role" { - source = "../../modules/iam-role-for-service-accounts-eks" - - role_name = "velero" - attach_velero_policy = true - velero_s3_bucket_arns = ["arn:aws:s3:::velero-backups"] - - oidc_providers = { - ex = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["velero:velero"] - } - } - - tags = local.tags -} - -module "vpc_cni_ipv4_irsa_role" { - source = "../../modules/iam-role-for-service-accounts-eks" - - role_name = "vpc-cni-ipv4" - attach_vpc_cni_policy = true - vpc_cni_enable_ipv4 = true - vpc_cni_enable_cloudwatch_logs = true - - oidc_providers = { - ex = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["kube-system:aws-node"] - } - } - - tags = local.tags -} - -module "vpc_cni_ipv6_irsa_role" { - source = "../../modules/iam-role-for-service-accounts-eks" - - role_name = "vpc-cni-ipv6" - attach_vpc_cni_policy = true - vpc_cni_enable_ipv6 = true - - oidc_providers = { - ex = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["kube-system:aws-node"] - } - } - - tags = local.tags -} - -module "cloudwatch_observability_irsa_role" { - source = "../../modules/iam-role-for-service-accounts-eks" - - role_name = "cloudwatch-observability" - attach_cloudwatch_observability_policy = true - - oidc_providers = { - ex = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["amazon-cloudwatch:cloudwatch-agent"] - } - } - - tags = local.tags -} - -################################################################################ -# Custom IRSA Roles -################################################################################ - -# This is an example of a custom IRSA role which allows workloads with the specified serviceccount to perform actions in a S3 bucket. -module "iam_policy" { - source = "terraform-aws-modules/iam/aws//modules/iam-policy" - - name = "myapp" - path = "/" - description = "Example policy" - - policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Effect = "Allow" - Action = [ - "s3:*", - ] - Resource = "*" - } - ] - }) - -} - -module "iam_eks_role" { - source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" - role_name = "my-app" - - role_policy_arns = { - policy = module.iam_policy.arn - } - - oidc_providers = { - one = { - provider_arn = "arn:aws:iam::012345678901:oidc-provider/oidc.eks.us-east-1.amazonaws.com/id/5C54DDF35ER19312844C7333374CC09D" - namespace_service_accounts = ["default:my-app-staging", "canary:my-app-staging"] - } - two = { - provider_arn = "arn:aws:iam::012345678901:oidc-provider/oidc.eks.ap-southeast-1.amazonaws.com/id/5C54DDF35ER54476848E7333374FF09G" - namespace_service_accounts = ["default:my-app-staging"] - } - } -} - -################################################################################ -# Supporting Resources -################################################################################ - -module "vpc" { - source = "terraform-aws-modules/vpc/aws" - version = "~> 5.0" - - name = local.name - cidr = local.vpc_cidr - - azs = local.azs - private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)] - public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 48)] - - enable_nat_gateway = true - single_nat_gateway = true - - public_subnet_tags = { - "kubernetes.io/role/elb" = 1 - } - - private_subnet_tags = { - "kubernetes.io/role/internal-elb" = 1 - } - - tags = local.tags -} - -module "eks" { - source = "terraform-aws-modules/eks/aws" - version = "~> 19.21" - - cluster_name = local.name - cluster_version = "1.28" - - vpc_id = module.vpc.vpc_id - subnet_ids = module.vpc.private_subnets - - eks_managed_node_groups = { - default = {} - } - - tags = local.tags -} - -resource "aws_iam_policy" "additional" { - name = "${local.name}-additional" - description = "Additional test policy" - - policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Action = [ - "ec2:Describe*", - ] - Effect = "Allow" - Resource = "*" - }, - ] - }) - - tags = local.tags -} diff --git a/examples/iam-role-for-service-accounts-eks/outputs.tf b/examples/iam-role-for-service-accounts-eks/outputs.tf deleted file mode 100644 index f0754538..00000000 --- a/examples/iam-role-for-service-accounts-eks/outputs.tf +++ /dev/null @@ -1,19 +0,0 @@ -output "iam_role_arn" { - description = "ARN of IAM role" - value = module.irsa_role.iam_role_arn -} - -output "iam_role_name" { - description = "Name of IAM role" - value = module.irsa_role.iam_role_name -} - -output "iam_role_path" { - description = "Path of IAM role" - value = module.irsa_role.iam_role_path -} - -output "iam_role_unique_id" { - description = "Unique ID of IAM role" - value = module.irsa_role.iam_role_unique_id -} diff --git a/examples/iam-role-for-service-accounts-eks/variables.tf b/examples/iam-role-for-service-accounts-eks/variables.tf deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/iam-role-for-service-accounts-eks/versions.tf b/examples/iam-role-for-service-accounts-eks/versions.tf deleted file mode 100644 index 37e79a70..00000000 --- a/examples/iam-role-for-service-accounts-eks/versions.tf +++ /dev/null @@ -1,10 +0,0 @@ -terraform { - required_version = ">= 1.0" - - required_providers { - aws = { - source = "hashicorp/aws" - version = ">= 4.0, < 6.0" - } - } -} diff --git a/examples/iam-role-for-service-accounts/README.md b/examples/iam-role-for-service-accounts/README.md new file mode 100644 index 00000000..d3d5eef8 --- /dev/null +++ b/examples/iam-role-for-service-accounts/README.md @@ -0,0 +1,81 @@ +# AWS IAM Role for Service Accounts in EKS + +Configuration in this directory creates IAM roles that can be assumed by multiple EKS `ServiceAccount`s for various tasks. + +# Usage + +To run this example you need to execute: + +```bash +$ terraform init +$ terraform plan +$ terraform apply +``` + +Run `terraform destroy` when you don't need these resources. + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 4.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 4.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [amazon\_managed\_service\_prometheus\_irsa](#module\_amazon\_managed\_service\_prometheus\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | +| [appmesh\_controller\_irsa](#module\_appmesh\_controller\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | +| [appmesh\_envoy\_proxy\_irsa](#module\_appmesh\_envoy\_proxy\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | +| [cert\_manager\_irsa](#module\_cert\_manager\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | +| [cluster\_autoscaler\_irsa](#module\_cluster\_autoscaler\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | +| [disabled](#module\_disabled) | ../../modules/iam-role-for-service-accounts | n/a | +| [ebs\_csi\_irsa](#module\_ebs\_csi\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | +| [ebs\_csi\_irsa\_v2](#module\_ebs\_csi\_irsa\_v2) | ../../modules/iam-role-for-service-accounts | n/a | +| [efs\_csi\_irsa](#module\_efs\_csi\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | +| [eks](#module\_eks) | terraform-aws-modules/eks/aws | ~> 19.10 | +| [external\_dns\_irsa](#module\_external\_dns\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | +| [external\_secrets\_irsa](#module\_external\_secrets\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | +| [fsx\_lustre\_csi\_irsa](#module\_fsx\_lustre\_csi\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | +| [irsa](#module\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | +| [irsa\_v2\_custom\_policy](#module\_irsa\_v2\_custom\_policy) | ../../modules/iam-role-for-service-accounts | n/a | +| [irsa\_v2\_empty](#module\_irsa\_v2\_empty) | ../../modules/iam-role-for-service-accounts | n/a | +| [karpenter\_irsa](#module\_karpenter\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | +| [load\_balancer\_controller\_irsa](#module\_load\_balancer\_controller\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | +| [load\_balancer\_controller\_targetgroup\_binding\_only\_irsa](#module\_load\_balancer\_controller\_targetgroup\_binding\_only\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | +| [node\_termination\_handler\_irsa](#module\_node\_termination\_handler\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | +| [velero\_irsa](#module\_velero\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | +| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 3.0 | +| [vpc\_cni\_ipv4\_irsa](#module\_vpc\_cni\_ipv4\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | +| [vpc\_cni\_ipv6\_irsa](#module\_vpc\_cni\_ipv6\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | + +## Resources + +| Name | Type | +|------|------| +| [aws_iam_policy.additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | + +## Inputs + +No inputs. + +## Outputs + +| Name | Description | +|------|-------------| +| [irsa\_arn](#output\_irsa\_arn) | ARN of IAM role | +| [irsa\_iam\_policy](#output\_irsa\_iam\_policy) | The policy document | +| [irsa\_iam\_policy\_arn](#output\_irsa\_iam\_policy\_arn) | The ARN assigned by AWS to this policy | +| [irsa\_name](#output\_irsa\_name) | Name of IAM role | +| [irsa\_path](#output\_irsa\_path) | Path of IAM role | +| [irsa\_unique\_id](#output\_irsa\_unique\_id) | Unique ID of IAM role | + diff --git a/examples/iam-role-for-service-accounts/main.tf b/examples/iam-role-for-service-accounts/main.tf new file mode 100644 index 00000000..4e914dd6 --- /dev/null +++ b/examples/iam-role-for-service-accounts/main.tf @@ -0,0 +1,461 @@ +provider "aws" { + region = "eu-west-1" +} + +data "aws_availability_zones" "available" {} + +locals { + name = "ex-irsa" + + vpc_cidr = "10.0.0.0/16" + azs = slice(data.aws_availability_zones.available.names, 0, 3) + + tags = { + Example = local.name + GithubRepo = "terraform-aws-iam" + GithubOrg = "terraform-aws-modules" + } +} + +################################################################################ +# IRSAv2 Roles +################################################################################ + +module "irsa_v2_empty" { + source = "../../modules/iam-role-for-service-accounts" + + name = "${local.name}-v2" + + enable_irsa_v2 = true + + tags = local.tags +} + +module "ebs_csi_irsa_v2" { + source = "../../modules/iam-role-for-service-accounts" + + name = "ebs-csi-v2" + + enable_irsa_v2 = true + attach_ebs_csi_policy = true + + tags = local.tags +} + +module "irsa_v2_custom_policy" { + source = "../../modules/iam-role-for-service-accounts" + + name = "${local.name}-custom-name" + + enable_irsa_v2 = true + policy_statements = [ + { + sid = "DescribeEc2" + actions = ["ec2:Describe*"] + effect = "Allow" + resources = ["*"] + } + ] + + tags = local.tags +} + +################################################################################ +# IRSA Roles +################################################################################ + +module "disabled" { + source = "../../modules/iam-role-for-service-accounts" + + create = false +} + +module "irsa" { + source = "../../modules/iam-role-for-service-accounts" + + name = local.name + + oidc_providers = { + one = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["default:my-app", "canary:my-app"] + } + two = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["default:blue", "canary:blue"] + } + } + + policies = { + AmazonEKS_CNI_Policy = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy" + additional = aws_iam_policy.additional.arn + } + + tags = local.tags +} + +module "cert_manager_irsa" { + source = "../../modules/iam-role-for-service-accounts" + + name = "cert-manager" + + attach_cert_manager_policy = true + cert_manager_hosted_zone_arns = ["arn:aws:route53:::hostedzone/IClearlyMadeThisUp"] + + oidc_providers = { + this = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["kube-system:cert-manager"] + } + } + + tags = local.tags +} + +module "cluster_autoscaler_irsa" { + source = "../../modules/iam-role-for-service-accounts" + + name = "cluster-autoscaler" + + attach_cluster_autoscaler_policy = true + cluster_autoscaler_cluster_names = [module.eks.cluster_name] + + oidc_providers = { + this = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["kube-system:cluster-autoscaler"] + } + } + + tags = local.tags +} + +module "ebs_csi_irsa" { + source = "../../modules/iam-role-for-service-accounts" + + name = "ebs-csi" + + attach_ebs_csi_policy = true + + oidc_providers = { + this = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["kube-system:ebs-csi-controller-sa"] + } + } + + tags = local.tags +} + +module "efs_csi_irsa" { + source = "../../modules/iam-role-for-service-accounts" + + name = "efs-csi" + + attach_efs_csi_policy = true + + oidc_providers = { + this = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["kube-system:efs-csi-controller-sa"] + } + } + + tags = local.tags +} + +module "external_dns_irsa" { + source = "../../modules/iam-role-for-service-accounts" + + name = "external-dns" + + attach_external_dns_policy = true + external_dns_hosted_zone_arns = ["arn:aws:route53:::hostedzone/IClearlyMadeThisUp"] + + oidc_providers = { + this = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["kube-system:external-dns"] + } + } + + tags = local.tags +} + +module "external_secrets_irsa" { + source = "../../modules/iam-role-for-service-accounts" + + name = "external-secrets" + + attach_external_secrets_policy = true + external_secrets_ssm_parameter_arns = ["arn:aws:ssm:*:*:parameter/foo"] + external_secrets_secrets_manager_arns = ["arn:aws:secretsmanager:*:*:secret:bar"] + + oidc_providers = { + this = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["default:kubernetes-external-secrets"] + } + } + + tags = local.tags +} + +module "fsx_lustre_csi_irsa" { + source = "../../modules/iam-role-for-service-accounts" + + name = "fsx-lustre-csi" + + attach_fsx_lustre_csi_policy = true + + oidc_providers = { + this = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["kube-system:fsx-csi-controller-sa"] + } + } + + tags = local.tags +} + +module "karpenter_irsa" { + source = "../../modules/iam-role-for-service-accounts" + + name = "karpenter" + + attach_karpenter_policy = true + karpenter_cluster_name = module.eks.cluster_name + karpenter_node_iam_role_arns = [module.eks.eks_managed_node_groups["default"].iam_role_arn] + + oidc_providers = { + this = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["karpenter:karpenter"] + } + } + + tags = local.tags +} + +module "load_balancer_controller_irsa" { + source = "../../modules/iam-role-for-service-accounts" + + name = "load-balancer-controller" + + attach_load_balancer_controller_policy = true + + oidc_providers = { + this = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["kube-system:aws-load-balancer-controller"] + } + } + + tags = local.tags +} + +module "load_balancer_controller_targetgroup_binding_only_irsa" { + source = "../../modules/iam-role-for-service-accounts" + + name = "load-balancer-controller-targetgroup-binding-only" + + attach_load_balancer_controller_targetgroup_binding_only_policy = true + + oidc_providers = { + this = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["kube-system:aws-load-balancer-controller"] + } + } + + tags = local.tags +} + +module "appmesh_controller_irsa" { + source = "../../modules/iam-role-for-service-accounts" + + name = "appmesh-controller" + + attach_appmesh_controller_policy = true + + oidc_providers = { + this = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["appmesh-system:appmesh-controller"] + } + } + + tags = local.tags +} + +module "appmesh_envoy_proxy_irsa" { + source = "../../modules/iam-role-for-service-accounts" + + name = "appmesh-envoy-proxy" + + attach_appmesh_envoy_proxy_policy = true + + oidc_providers = { + this = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["appmesh-system:appmesh-envoy-proxy"] + } + } + + tags = local.tags +} + +module "amazon_managed_service_prometheus_irsa" { + source = "../../modules/iam-role-for-service-accounts" + + name = "amazon-managed-service-prometheus" + + attach_amazon_managed_service_prometheus_policy = true + + oidc_providers = { + this = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["prometheus:amp-ingest"] + } + } + + tags = local.tags +} + +module "node_termination_handler_irsa" { + source = "../../modules/iam-role-for-service-accounts" + + name = "node-termination-handler" + + attach_node_termination_handler_policy = true + + oidc_providers = { + this = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["kube-system:aws-node"] + } + } + + tags = local.tags +} + +module "velero_irsa" { + source = "../../modules/iam-role-for-service-accounts" + + name = "velero" + + attach_velero_policy = true + velero_s3_bucket_arns = ["arn:aws:s3:::velero-backups"] + + oidc_providers = { + this = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["velero:velero"] + } + } + + tags = local.tags +} + +module "vpc_cni_ipv4_irsa" { + source = "../../modules/iam-role-for-service-accounts" + + name = "vpc-cni-ipv4" + + attach_vpc_cni_policy = true + vpc_cni_enable_ipv4 = true + + oidc_providers = { + this = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["kube-system:aws-node"] + } + } + + tags = local.tags +} + +module "vpc_cni_ipv6_irsa" { + source = "../../modules/iam-role-for-service-accounts" + + name = "vpc-cni-ipv6" + + attach_vpc_cni_policy = true + vpc_cni_enable_ipv6 = true + + oidc_providers = { + this = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["kube-system:aws-node"] + } + } + + tags = local.tags +} + +################################################################################ +# Supporting Resources +################################################################################ + +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "~> 3.0" + + name = local.name + cidr = local.vpc_cidr + + azs = local.azs + private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)] + public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 48)] + + enable_nat_gateway = true + single_nat_gateway = true + enable_dns_hostnames = true + + public_subnet_tags = { + "kubernetes.io/role/elb" = 1 + } + + private_subnet_tags = { + "kubernetes.io/role/internal-elb" = 1 + } + + tags = local.tags +} + +module "eks" { + source = "terraform-aws-modules/eks/aws" + version = "~> 19.10" + + cluster_name = local.name + cluster_version = "1.25" + + vpc_id = module.vpc.vpc_id + subnet_ids = module.vpc.private_subnets + + eks_managed_node_groups = { + default = {} + } + + tags = local.tags +} + +resource "aws_iam_policy" "additional" { + name = "${local.name}-additional" + description = "Additional test policy" + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = [ + "ec2:Describe*", + ] + Effect = "Allow" + Resource = "*" + }, + ] + }) + + tags = local.tags +} diff --git a/examples/iam-role-for-service-accounts/outputs.tf b/examples/iam-role-for-service-accounts/outputs.tf new file mode 100644 index 00000000..0fbd18b1 --- /dev/null +++ b/examples/iam-role-for-service-accounts/outputs.tf @@ -0,0 +1,36 @@ +################################################################################ +# IAM Role +################################################################################ + +output "irsa_arn" { + description = "ARN of IAM role" + value = module.irsa.arn +} + +output "irsa_name" { + description = "Name of IAM role" + value = module.irsa.name +} + +output "irsa_path" { + description = "Path of IAM role" + value = module.irsa.path +} + +output "irsa_unique_id" { + description = "Unique ID of IAM role" + value = module.irsa.unique_id +} +################################################################################ +# IAM Policy +################################################################################ + +output "irsa_iam_policy_arn" { + description = "The ARN assigned by AWS to this policy" + value = module.irsa.iam_policy_arn +} + +output "irsa_iam_policy" { + description = "The policy document" + value = module.irsa.iam_policy +} diff --git a/examples/iam-assumable-role/variables.tf b/examples/iam-role-for-service-accounts/variables.tf similarity index 100% rename from examples/iam-assumable-role/variables.tf rename to examples/iam-role-for-service-accounts/variables.tf diff --git a/examples/iam-assumable-role/versions.tf b/examples/iam-role-for-service-accounts/versions.tf similarity index 100% rename from examples/iam-assumable-role/versions.tf rename to examples/iam-role-for-service-accounts/versions.tf diff --git a/examples/iam-role-saml/README.md b/examples/iam-role-saml/README.md new file mode 100644 index 00000000..38a2a193 --- /dev/null +++ b/examples/iam-role-saml/README.md @@ -0,0 +1,56 @@ +# AWS IAM Role w/ SAML Federation example + +Configuration in this directory creates an IAM role which can be assumed by users with a SAML Identity Provider. + +# Usage + +To run this example you need to execute: + +```bash +$ terraform init +$ terraform plan +$ terraform apply +``` + +Run `terraform destroy` when you don't need these resources. + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 4.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 4.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [iam\_role](#module\_iam\_role) | ../../modules/iam-role-saml | n/a | +| [iam\_role\_disabled](#module\_iam\_role\_disabled) | ../../modules/iam-role-oidc | n/a | +| [iam\_roles](#module\_iam\_roles) | ../../modules/iam-role-saml | n/a | + +## Resources + +| Name | Type | +|------|------| +| [aws_iam_saml_provider.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_saml_provider) | resource | + +## Inputs + +No inputs. + +## Outputs + +| Name | Description | +|------|-------------| +| [iam\_role\_arn](#output\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | +| [iam\_role\_name](#output\_iam\_role\_name) | The name of the IAM role | +| [iam\_role\_unique\_id](#output\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | + diff --git a/examples/iam-role-saml/main.tf b/examples/iam-role-saml/main.tf new file mode 100644 index 00000000..bfdc756d --- /dev/null +++ b/examples/iam-role-saml/main.tf @@ -0,0 +1,73 @@ +provider "aws" { + region = "eu-west-1" +} + +locals { + name = "ex-${basename(path.cwd)}" + + tags = { + Example = local.name + GithubRepo = "terraform-aws-iam" + GithubOrg = "terraform-aws-modules" + } +} + +################################################################################ +# IAM Role w/ SAML +################################################################################ + +module "iam_role" { + source = "../../modules/iam-role-saml" + + name = local.name + + saml_provider_ids = [aws_iam_saml_provider.this.id] + + policies = { + ReadOnlyAccess = "arn:aws:iam::aws:policy/ReadOnlyAccess" + } + + tags = local.tags +} + +module "iam_roles" { + source = "../../modules/iam-role-saml" + + for_each = { + admin = { + policies = { + AdministratorAccess = "arn:aws:iam::aws:policy/AdministratorAccess" + } + } + readonly = { + policies = { + ReadOnlyAccess = "arn:aws:iam::aws:policy/ReadOnlyAccess" + } + } + poweruser = { + PowerUserAccess = "arn:aws:iam::aws:policy/PowerUserAccess" + } + } + + name_prefix = "${each.key}-" + + saml_provider_ids = [aws_iam_saml_provider.this.id] + policies = each.value.policies + + tags = local.tags +} + +module "iam_role_disabled" { + source = "../../modules/iam-role-oidc" + + create = false +} + +################################################################################ +# Supporting Resources +################################################################################ + +resource "aws_iam_saml_provider" "this" { + name = "idp_saml" + saml_metadata_document = file("saml-metadata.xml") +} diff --git a/examples/iam-role-saml/outputs.tf b/examples/iam-role-saml/outputs.tf new file mode 100644 index 00000000..f924f3d0 --- /dev/null +++ b/examples/iam-role-saml/outputs.tf @@ -0,0 +1,18 @@ +################################################################################ +# IAM Role w/ OIDC +################################################################################ + +output "iam_role_name" { + description = "The name of the IAM role" + value = module.iam_role.name +} + +output "iam_role_arn" { + description = "The Amazon Resource Name (ARN) specifying the IAM role" + value = module.iam_role.arn +} + +output "iam_role_unique_id" { + description = "Stable and unique string identifying the IAM role" + value = module.iam_role.unique_id +} diff --git a/examples/iam-assumable-role-with-saml/saml-metadata.xml b/examples/iam-role-saml/saml-metadata.xml similarity index 100% rename from examples/iam-assumable-role-with-saml/saml-metadata.xml rename to examples/iam-role-saml/saml-metadata.xml diff --git a/examples/iam-assumable-roles-with-saml/variables.tf b/examples/iam-role-saml/variables.tf similarity index 100% rename from examples/iam-assumable-roles-with-saml/variables.tf rename to examples/iam-role-saml/variables.tf diff --git a/examples/iam-assumable-roles-with-saml/versions.tf b/examples/iam-role-saml/versions.tf similarity index 100% rename from examples/iam-assumable-roles-with-saml/versions.tf rename to examples/iam-role-saml/versions.tf diff --git a/examples/iam-role/README.md b/examples/iam-role/README.md new file mode 100644 index 00000000..1a84e667 --- /dev/null +++ b/examples/iam-role/README.md @@ -0,0 +1,69 @@ +# AWS IAM Role Example + +Configuration in this directory creates IAM roles with different options for permissions and role assumption. + +# Usage + +To run this example you need to execute: + +```bash +$ terraform init +$ terraform plan +$ terraform apply +``` + +Run `terraform destroy` when you don't need these resources. + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 4.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 4.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [iam\_role\_conditions](#module\_iam\_role\_conditions) | ../../modules/iam-role | n/a | +| [iam\_role\_disabled](#module\_iam\_role\_disabled) | ../../modules/iam-role | n/a | +| [iam\_role\_instance\_profile](#module\_iam\_role\_instance\_profile) | ../../modules/iam-role | n/a | +| [iam\_roles](#module\_iam\_roles) | ../../modules/iam-role | n/a | + +## Resources + +| Name | Type | +|------|------| +| [aws_iam_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | + +## Inputs + +No inputs. + +## Outputs + +| Name | Description | +|------|-------------| +| [conditions\_iam\_instance\_profile\_arn](#output\_conditions\_iam\_instance\_profile\_arn) | ARN assigned by AWS to the instance profile | +| [conditions\_iam\_instance\_profile\_id](#output\_conditions\_iam\_instance\_profile\_id) | Instance profile's ID | +| [conditions\_iam\_instance\_profile\_name](#output\_conditions\_iam\_instance\_profile\_name) | Name of IAM instance profile | +| [conditions\_iam\_instance\_profile\_unique\_id](#output\_conditions\_iam\_instance\_profile\_unique\_id) | Stable and unique string identifying the IAM instance profile | +| [conditions\_iam\_role\_arn](#output\_conditions\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | +| [conditions\_iam\_role\_name](#output\_conditions\_iam\_role\_name) | The name of the IAM role | +| [conditions\_iam\_role\_unique\_id](#output\_conditions\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | +| [instance\_profile\_iam\_instance\_profile\_arn](#output\_instance\_profile\_iam\_instance\_profile\_arn) | ARN assigned by AWS to the instance profile | +| [instance\_profile\_iam\_instance\_profile\_id](#output\_instance\_profile\_iam\_instance\_profile\_id) | Instance profile's ID | +| [instance\_profile\_iam\_instance\_profile\_name](#output\_instance\_profile\_iam\_instance\_profile\_name) | Name of IAM instance profile | +| [instance\_profile\_iam\_instance\_profile\_unique\_id](#output\_instance\_profile\_iam\_instance\_profile\_unique\_id) | Stable and unique string identifying the IAM instance profile | +| [instance\_profile\_iam\_role\_arn](#output\_instance\_profile\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | +| [instance\_profile\_iam\_role\_name](#output\_instance\_profile\_iam\_role\_name) | The name of the IAM role | +| [instance\_profile\_iam\_role\_unique\_id](#output\_instance\_profile\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | + diff --git a/examples/iam-role/main.tf b/examples/iam-role/main.tf new file mode 100644 index 00000000..6b58a2ec --- /dev/null +++ b/examples/iam-role/main.tf @@ -0,0 +1,172 @@ +provider "aws" { + region = "eu-west-1" +} + +data "aws_caller_identity" "current" {} + +locals { + name = "ex-${basename(path.cwd)}" + + tags = { + Example = local.name + GithubRepo = "terraform-aws-iam" + GithubOrg = "terraform-aws-modules" + } +} + +################################################################################ +# IAM Role +################################################################################ + +module "iam_role_instance_profile" { + source = "../../modules/iam-role" + + name = "${local.name}-instance-profile" + + # https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/ + allow_self_assume_role = true + create_instance_profile = true + + assume_role_policy_statements = [ + { + sid = "TrustRoleAndServiceToAssume" + principals = [ + { + type = "AWS" + identifiers = [ + "arn:aws:iam::307990089504:root", + "arn:aws:iam::835367859851:user/anton", + ] + }, + { + type = "Service" + identifiers = [ + "codedeploy.amazonaws.com", + ] + } + ] + } + ] + + policies = { + ReadOnlyAccess = "arn:aws:iam::aws:policy/ReadOnlyAccess" + } + + tags = local.tags +} + +module "iam_role_conditions" { + source = "../../modules/iam-role" + + name_prefix = "conditions-" + + assume_role_policy_statements = [ + { + sid = "TrustRoleAndServiceToAssume" + principals = [{ + type = "AWS" + identifiers = [ + "arn:aws:iam::835367859851:user/anton", + ] + }] + conditions = [{ + test = "StringEquals" + variable = "sts:ExternalId" + values = ["some-secret-id"] + }] + } + ] + + policies = { + AmazonCognitoReadOnly = "arn:aws:iam::aws:policy/AmazonCognitoReadOnly" + AlexaForBusinessFullAccess = "arn:aws:iam::aws:policy/AlexaForBusinessFullAccess" + custom = aws_iam_policy.this.arn + } + + tags = local.tags +} + +module "iam_roles" { + source = "../../modules/iam-role" + + for_each = { + admin = { + trusted_arns = [data.aws_caller_identity.current.arn] + policies = { + AdministratorAccess = "arn:aws:iam::aws:policy/AdministratorAccess" + } + } + readonly = { + trusted_arns = ["arn:aws:iam::835367859851:user/anton"] + policies = { + ReadOnlyAccess = "arn:aws:iam::aws:policy/ReadOnlyAccess" + } + } + poweruser = { + trusted_arns = [data.aws_caller_identity.current.arn] + PowerUserAccess = "arn:aws:iam::aws:policy/PowerUserAccess" + } + } + + name_prefix = "${each.key}-" + + assume_role_policy_statements = [ + { + sid = "TrustRoleAndServiceToAssume" + principals = [{ + type = "AWS" + identifiers = each.value.trusted_arns + }] + + conditions = [ + { + test = "Bool" + variable = "aws:MultiFactorAuthPresent" + values = ["true"] + }, + { + test = "NumericLessThan" + variable = "aws:MultiFactorAuthAge" + values = [86400] + } + ] + } + ] + + policies = each.value.policies + + tags = local.tags +} + +module "iam_role_disabled" { + source = "../../modules/iam-role" + + create = false +} + +################################################################################ +# Supporting resources +################################################################################ + +resource "aws_iam_policy" "this" { + name = local.name + path = "/" + description = "Example policy" + + policy = <<-EOT + { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "ec2:Describe*" + ], + "Effect": "Allow", + "Resource": "*" + } + ] + } + EOT + + tags = local.tags +} diff --git a/examples/iam-role/outputs.tf b/examples/iam-role/outputs.tf new file mode 100644 index 00000000..2e12e017 --- /dev/null +++ b/examples/iam-role/outputs.tf @@ -0,0 +1,77 @@ +################################################################################ +# IAM Role - Instance Profile +################################################################################ + +output "instance_profile_iam_role_name" { + description = "The name of the IAM role" + value = module.iam_role_instance_profile.name +} + +output "instance_profile_iam_role_arn" { + description = "The Amazon Resource Name (ARN) specifying the IAM role" + value = module.iam_role_instance_profile.arn +} + +output "instance_profile_iam_role_unique_id" { + description = "Stable and unique string identifying the IAM role" + value = module.iam_role_instance_profile.unique_id +} + +output "instance_profile_iam_instance_profile_arn" { + description = "ARN assigned by AWS to the instance profile" + value = module.iam_role_instance_profile.instance_profile_arn +} + +output "instance_profile_iam_instance_profile_id" { + description = "Instance profile's ID" + value = module.iam_role_instance_profile.instance_profile_id +} + +output "instance_profile_iam_instance_profile_name" { + description = "Name of IAM instance profile" + value = module.iam_role_instance_profile.instance_profile_name +} + +output "instance_profile_iam_instance_profile_unique_id" { + description = "Stable and unique string identifying the IAM instance profile" + value = module.iam_role_instance_profile.instance_profile_unique_id +} + +################################################################################ +# IAM Role - Conditions +################################################################################ + +output "conditions_iam_role_name" { + description = "The name of the IAM role" + value = module.iam_role_conditions.name +} + +output "conditions_iam_role_arn" { + description = "The Amazon Resource Name (ARN) specifying the IAM role" + value = module.iam_role_conditions.arn +} + +output "conditions_iam_role_unique_id" { + description = "Stable and unique string identifying the IAM role" + value = module.iam_role_conditions.unique_id +} + +output "conditions_iam_instance_profile_arn" { + description = "ARN assigned by AWS to the instance profile" + value = module.iam_role_conditions.instance_profile_arn +} + +output "conditions_iam_instance_profile_id" { + description = "Instance profile's ID" + value = module.iam_role_conditions.instance_profile_id +} + +output "conditions_iam_instance_profile_name" { + description = "Name of IAM instance profile" + value = module.iam_role_conditions.instance_profile_name +} + +output "conditions_iam_instance_profile_unique_id" { + description = "Stable and unique string identifying the IAM instance profile" + value = module.iam_role_conditions.instance_profile_unique_id +} diff --git a/examples/iam-assumable-roles/variables.tf b/examples/iam-role/variables.tf similarity index 100% rename from examples/iam-assumable-roles/variables.tf rename to examples/iam-role/variables.tf diff --git a/examples/iam-assumable-roles/versions.tf b/examples/iam-role/versions.tf similarity index 100% rename from examples/iam-assumable-roles/versions.tf rename to examples/iam-role/versions.tf diff --git a/examples/iam-user/README.md b/examples/iam-user/README.md index 038d1dea..91d3c4bc 100644 --- a/examples/iam-user/README.md +++ b/examples/iam-user/README.md @@ -1,6 +1,6 @@ -# IAM user example +# AWS IAM User Example -Configuration in this directory creates IAM user with a random password, a pair of IAM access/secret keys and uploads IAM SSH public key. +Configuration in this directory creates an IAM user with a random password, a pair of IAM access/secret keys and uploads IAM SSH public key. User password and secret key is encrypted using public key of keybase.io user named `test`. # Usage @@ -25,9 +25,7 @@ Run `terraform destroy` when you don't need these resources. ## Providers -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 4.0 | +No providers. ## Modules @@ -36,13 +34,11 @@ Run `terraform destroy` when you don't need these resources. | [iam\_user](#module\_iam\_user) | ../../modules/iam-user | n/a | | [iam\_user2](#module\_iam\_user2) | ../../modules/iam-user | n/a | | [iam\_user3](#module\_iam\_user3) | ../../modules/iam-user | n/a | -| [iam\_user4](#module\_iam\_user4) | ../../modules/iam-user | n/a | +| [iam\_user\_disabled](#module\_iam\_user\_disabled) | ../../modules/iam-user | n/a | ## Resources -| Name | Type | -|------|------| -| [aws_iam_policy.example](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy) | data source | +No resources. ## Inputs @@ -52,22 +48,49 @@ No inputs. | Name | Description | |------|-------------| -| [iam\_access\_key\_encrypted\_secret](#output\_iam\_access\_key\_encrypted\_secret) | The encrypted secret, base64 encoded | -| [iam\_access\_key\_id](#output\_iam\_access\_key\_id) | The access key ID | -| [iam\_access\_key\_key\_fingerprint](#output\_iam\_access\_key\_key\_fingerprint) | The fingerprint of the PGP key used to encrypt the secret | -| [iam\_access\_key\_secret](#output\_iam\_access\_key\_secret) | The access key secret | -| [iam\_access\_key\_ses\_smtp\_password\_v4](#output\_iam\_access\_key\_ses\_smtp\_password\_v4) | The secret access key converted into an SES SMTP password | -| [iam\_access\_key\_status](#output\_iam\_access\_key\_status) | Active or Inactive. Keys are initially active, but can be made inactive by other means. | +| [iam\_user2\_access\_key\_encrypted\_secret](#output\_iam\_user2\_access\_key\_encrypted\_secret) | The encrypted secret, base64 encoded | +| [iam\_user2\_access\_key\_encrypted\_ses\_smtp\_password\_v4](#output\_iam\_user2\_access\_key\_encrypted\_ses\_smtp\_password\_v4) | The encrypted secret access key converted into an SES SMTP password by applying AWS's Sigv4 conversion algorithm | +| [iam\_user2\_access\_key\_fingerprint](#output\_iam\_user2\_access\_key\_fingerprint) | The fingerprint of the PGP key used to encrypt the secret | +| [iam\_user2\_access\_key\_id](#output\_iam\_user2\_access\_key\_id) | The access key ID | +| [iam\_user2\_access\_key\_secret](#output\_iam\_user2\_access\_key\_secret) | The access key secret | +| [iam\_user2\_access\_key\_ses\_smtp\_password\_v4](#output\_iam\_user2\_access\_key\_ses\_smtp\_password\_v4) | The secret access key converted into an SES SMTP password by applying AWS's Sigv4 conversion algorithm | +| [iam\_user2\_access\_key\_status](#output\_iam\_user2\_access\_key\_status) | Active or Inactive. Keys are initially active, but can be made inactive by other means | +| [iam\_user2\_arn](#output\_iam\_user2\_arn) | The ARN assigned by AWS for this user | +| [iam\_user2\_login\_profile\_encrypted\_password](#output\_iam\_user2\_login\_profile\_encrypted\_password) | The encrypted password, base64 encoded | +| [iam\_user2\_login\_profile\_key\_fingerprint](#output\_iam\_user2\_login\_profile\_key\_fingerprint) | The fingerprint of the PGP key used to encrypt the password | +| [iam\_user2\_login\_profile\_password](#output\_iam\_user2\_login\_profile\_password) | The user password | +| [iam\_user2\_name](#output\_iam\_user2\_name) | The user's name | +| [iam\_user2\_ssh\_key\_fingerprint](#output\_iam\_user2\_ssh\_key\_fingerprint) | The MD5 message digest of the SSH public key | +| [iam\_user2\_ssh\_key\_public\_key\_id](#output\_iam\_user2\_ssh\_key\_public\_key\_id) | The unique identifier for the SSH public key | +| [iam\_user2\_unique\_id](#output\_iam\_user2\_unique\_id) | The unique ID assigned by AWS | +| [iam\_user3\_access\_key\_encrypted\_secret](#output\_iam\_user3\_access\_key\_encrypted\_secret) | The encrypted secret, base64 encoded | +| [iam\_user3\_access\_key\_encrypted\_ses\_smtp\_password\_v4](#output\_iam\_user3\_access\_key\_encrypted\_ses\_smtp\_password\_v4) | The encrypted secret access key converted into an SES SMTP password by applying AWS's Sigv4 conversion algorithm | +| [iam\_user3\_access\_key\_fingerprint](#output\_iam\_user3\_access\_key\_fingerprint) | The fingerprint of the PGP key used to encrypt the secret | +| [iam\_user3\_access\_key\_id](#output\_iam\_user3\_access\_key\_id) | The access key ID | +| [iam\_user3\_access\_key\_secret](#output\_iam\_user3\_access\_key\_secret) | The access key secret | +| [iam\_user3\_access\_key\_ses\_smtp\_password\_v4](#output\_iam\_user3\_access\_key\_ses\_smtp\_password\_v4) | The secret access key converted into an SES SMTP password by applying AWS's Sigv4 conversion algorithm | +| [iam\_user3\_access\_key\_status](#output\_iam\_user3\_access\_key\_status) | Active or Inactive. Keys are initially active, but can be made inactive by other means | +| [iam\_user3\_arn](#output\_iam\_user3\_arn) | The ARN assigned by AWS for this user | +| [iam\_user3\_login\_profile\_encrypted\_password](#output\_iam\_user3\_login\_profile\_encrypted\_password) | The encrypted password, base64 encoded | +| [iam\_user3\_login\_profile\_key\_fingerprint](#output\_iam\_user3\_login\_profile\_key\_fingerprint) | The fingerprint of the PGP key used to encrypt the password | +| [iam\_user3\_login\_profile\_password](#output\_iam\_user3\_login\_profile\_password) | The user password | +| [iam\_user3\_name](#output\_iam\_user3\_name) | The user's name | +| [iam\_user3\_ssh\_key\_fingerprint](#output\_iam\_user3\_ssh\_key\_fingerprint) | The MD5 message digest of the SSH public key | +| [iam\_user3\_ssh\_key\_public\_key\_id](#output\_iam\_user3\_ssh\_key\_public\_key\_id) | The unique identifier for the SSH public key | +| [iam\_user3\_unique\_id](#output\_iam\_user3\_unique\_id) | The unique ID assigned by AWS | +| [iam\_user\_access\_key\_encrypted\_secret](#output\_iam\_user\_access\_key\_encrypted\_secret) | The encrypted secret, base64 encoded | +| [iam\_user\_access\_key\_encrypted\_ses\_smtp\_password\_v4](#output\_iam\_user\_access\_key\_encrypted\_ses\_smtp\_password\_v4) | The encrypted secret access key converted into an SES SMTP password by applying AWS's Sigv4 conversion algorithm | +| [iam\_user\_access\_key\_fingerprint](#output\_iam\_user\_access\_key\_fingerprint) | The fingerprint of the PGP key used to encrypt the secret | +| [iam\_user\_access\_key\_id](#output\_iam\_user\_access\_key\_id) | The access key ID | +| [iam\_user\_access\_key\_secret](#output\_iam\_user\_access\_key\_secret) | The access key secret | +| [iam\_user\_access\_key\_ses\_smtp\_password\_v4](#output\_iam\_user\_access\_key\_ses\_smtp\_password\_v4) | The secret access key converted into an SES SMTP password by applying AWS's Sigv4 conversion algorithm | +| [iam\_user\_access\_key\_status](#output\_iam\_user\_access\_key\_status) | Active or Inactive. Keys are initially active, but can be made inactive by other means | | [iam\_user\_arn](#output\_iam\_user\_arn) | The ARN assigned by AWS for this user | | [iam\_user\_login\_profile\_encrypted\_password](#output\_iam\_user\_login\_profile\_encrypted\_password) | The encrypted password, base64 encoded | | [iam\_user\_login\_profile\_key\_fingerprint](#output\_iam\_user\_login\_profile\_key\_fingerprint) | The fingerprint of the PGP key used to encrypt the password | | [iam\_user\_login\_profile\_password](#output\_iam\_user\_login\_profile\_password) | The user password | | [iam\_user\_name](#output\_iam\_user\_name) | The user's name | +| [iam\_user\_ssh\_key\_fingerprint](#output\_iam\_user\_ssh\_key\_fingerprint) | The MD5 message digest of the SSH public key | +| [iam\_user\_ssh\_key\_public\_key\_id](#output\_iam\_user\_ssh\_key\_public\_key\_id) | The unique identifier for the SSH public key | | [iam\_user\_unique\_id](#output\_iam\_user\_unique\_id) | The unique ID assigned by AWS | -| [keybase\_password\_decrypt\_command](#output\_keybase\_password\_decrypt\_command) | Decrypt user password command | -| [keybase\_password\_pgp\_message](#output\_keybase\_password\_pgp\_message) | Encrypted password | -| [keybase\_secret\_key\_decrypt\_command](#output\_keybase\_secret\_key\_decrypt\_command) | Decrypt access secret key command | -| [keybase\_secret\_key\_pgp\_message](#output\_keybase\_secret\_key\_pgp\_message) | Encrypted access secret key | -| [pgp\_key](#output\_pgp\_key) | PGP key used to encrypt sensitive data for this user (if empty - secrets are not encrypted) | -| [policy\_arns](#output\_policy\_arns) | The list of ARNs of policies directly assigned to the IAM user | diff --git a/examples/iam-user/main.tf b/examples/iam-user/main.tf index 53094831..17b84982 100644 --- a/examples/iam-user/main.tf +++ b/examples/iam-user/main.tf @@ -2,9 +2,20 @@ provider "aws" { region = "eu-west-1" } -######################################### +locals { + name = "ex-${basename(path.cwd)}" + + tags = { + Example = local.name + GithubRepo = "terraform-aws-iam" + GithubOrg = "terraform-aws-modules" + } +} + +################################################################################ # IAM user, login profile and access key -######################################### +################################################################################ + module "iam_user" { source = "../../modules/iam-user" @@ -12,55 +23,51 @@ module "iam_user" { force_destroy = true # User "test" has uploaded his public key here - https://keybase.io/test/pgp_keys.asc - pgp_key = "keybase:test" - + pgp_key = "keybase:test" password_reset_required = false # SSH public key - upload_iam_user_ssh_key = true - + create_ssh_key = true ssh_public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0sUjdTEcOWYgQ7ESnHsSkvPUO2tEvZxxQHUZYh9j6BPZgfn13iYhfAP2cfZznzrV+2VMamMtfiAiWR39LKo/bMN932HOp2Qx2la14IbiZ91666FD+yZ4+vhR2IVhZMe4D+g8FmhCfw1+zZhgl8vQBgsRZIcYqpYux59FcPv0lP1EhYahoRsUt1SEU2Gj+jvgyZpe15lnWk2VzfIpIsZ++AeUqyHoJHV0RVOK4MLRssqGHye6XkA3A+dMm2Mjgi8hxoL5uuwtkIsAll0kSfL5O2G26nsxm/Fpcl+SKSO4gs01d9V83xiOwviyOxmoXzwKy4qaUGtgq1hWncDNIVG/aQ==" + + tags = local.tags } -################################################################### +################################################################################ # IAM user without pgp_key (IAM access secret will be unencrypted) -################################################################### +################################################################################ + module "iam_user2" { source = "../../modules/iam-user" name = "vasya.pupkin4" - create_iam_user_login_profile = false - create_iam_access_key = true + create_login_profile = false + create_access_key = true + + tags = local.tags } -################################################################### +################################################################################ # IAM user with inactive IAM access key -################################################################### +################################################################################ + module "iam_user3" { source = "../../modules/iam-user" name = "vasya.pupkin5" - create_iam_user_login_profile = false - create_iam_access_key = true - iam_access_key_status = "Inactive" -} - -################################################################### -# IAM user with IAM policy attached -################################################################### + create_login_profile = false + create_access_key = true + access_key_status = "Inactive" -data "aws_iam_policy" "example" { - name = "AmazonS3ReadOnlyAccess" + tags = local.tags } -module "iam_user4" { +module "iam_user_disabled" { source = "../../modules/iam-user" - name = "vasya.pupkin6" + create = false - create_iam_user_login_profile = false - create_iam_access_key = true - policy_arns = [data.aws_iam_policy.example.arn] + tags = local.tags } diff --git a/examples/iam-user/outputs.tf b/examples/iam-user/outputs.tf index 0adaf17a..bb3f5fe2 100644 --- a/examples/iam-user/outputs.tf +++ b/examples/iam-user/outputs.tf @@ -1,92 +1,245 @@ -output "iam_user_name" { - description = "The user's name" - value = module.iam_user.iam_user_name -} +################################################################################ +# IAM User +################################################################################ output "iam_user_arn" { description = "The ARN assigned by AWS for this user" - value = module.iam_user.iam_user_arn + value = module.iam_user.arn +} + +output "iam_user_name" { + description = "The user's name" + value = module.iam_user.name } output "iam_user_unique_id" { description = "The unique ID assigned by AWS" - value = module.iam_user.iam_user_unique_id + value = module.iam_user.unique_id +} + +output "iam_user_login_profile_password" { + description = "The user password" + value = module.iam_user.login_profile_password + sensitive = true } output "iam_user_login_profile_key_fingerprint" { description = "The fingerprint of the PGP key used to encrypt the password" - value = module.iam_user.iam_user_login_profile_key_fingerprint + value = module.iam_user.login_profile_key_fingerprint } output "iam_user_login_profile_encrypted_password" { description = "The encrypted password, base64 encoded" - value = module.iam_user.iam_user_login_profile_encrypted_password + value = module.iam_user.login_profile_encrypted_password } -output "iam_user_login_profile_password" { +output "iam_user_access_key_id" { + description = "The access key ID" + value = module.iam_user.access_key_id +} + +output "iam_user_access_key_secret" { + description = "The access key secret" + value = module.iam_user.access_key_secret + sensitive = true +} + +output "iam_user_access_key_fingerprint" { + description = "The fingerprint of the PGP key used to encrypt the secret" + value = module.iam_user.access_key_fingerprint +} + +output "iam_user_access_key_encrypted_secret" { + description = "The encrypted secret, base64 encoded" + value = module.iam_user.access_key_encrypted_secret +} + +output "iam_user_access_key_ses_smtp_password_v4" { + description = "The secret access key converted into an SES SMTP password by applying AWS's Sigv4 conversion algorithm" + value = module.iam_user.access_key_ses_smtp_password_v4 + sensitive = true +} + +output "iam_user_access_key_encrypted_ses_smtp_password_v4" { + description = "The encrypted secret access key converted into an SES SMTP password by applying AWS's Sigv4 conversion algorithm" + value = module.iam_user.access_key_encrypted_ses_smtp_password_v4 +} + +output "iam_user_access_key_status" { + description = "Active or Inactive. Keys are initially active, but can be made inactive by other means" + value = module.iam_user.access_key_status +} + +output "iam_user_ssh_key_public_key_id" { + description = "The unique identifier for the SSH public key" + value = module.iam_user.ssh_key_public_key_id +} + +output "iam_user_ssh_key_fingerprint" { + description = "The MD5 message digest of the SSH public key" + value = module.iam_user.ssh_key_fingerprint +} + +################################################################################ +# IAM User 2 +################################################################################ + +output "iam_user2_arn" { + description = "The ARN assigned by AWS for this user" + value = module.iam_user2.arn +} + +output "iam_user2_name" { + description = "The user's name" + value = module.iam_user2.name +} + +output "iam_user2_unique_id" { + description = "The unique ID assigned by AWS" + value = module.iam_user2.unique_id +} + +output "iam_user2_login_profile_password" { description = "The user password" - value = module.iam_user.iam_user_login_profile_password + value = module.iam_user2.login_profile_password sensitive = true } -output "iam_access_key_id" { +output "iam_user2_login_profile_key_fingerprint" { + description = "The fingerprint of the PGP key used to encrypt the password" + value = module.iam_user2.login_profile_key_fingerprint +} + +output "iam_user2_login_profile_encrypted_password" { + description = "The encrypted password, base64 encoded" + value = module.iam_user2.login_profile_encrypted_password +} + +output "iam_user2_access_key_id" { description = "The access key ID" - value = module.iam_user.iam_access_key_id + value = module.iam_user2.access_key_id } -output "iam_access_key_key_fingerprint" { +output "iam_user2_access_key_secret" { + description = "The access key secret" + value = module.iam_user2.access_key_secret + sensitive = true +} + +output "iam_user2_access_key_fingerprint" { description = "The fingerprint of the PGP key used to encrypt the secret" - value = module.iam_user.iam_access_key_key_fingerprint + value = module.iam_user2.access_key_fingerprint } -output "iam_access_key_encrypted_secret" { +output "iam_user2_access_key_encrypted_secret" { description = "The encrypted secret, base64 encoded" - value = module.iam_user.iam_access_key_encrypted_secret + value = module.iam_user2.access_key_encrypted_secret } -output "iam_access_key_secret" { - description = "The access key secret" - value = module.iam_user.iam_access_key_secret +output "iam_user2_access_key_ses_smtp_password_v4" { + description = "The secret access key converted into an SES SMTP password by applying AWS's Sigv4 conversion algorithm" + value = module.iam_user2.access_key_ses_smtp_password_v4 + sensitive = true +} + +output "iam_user2_access_key_encrypted_ses_smtp_password_v4" { + description = "The encrypted secret access key converted into an SES SMTP password by applying AWS's Sigv4 conversion algorithm" + value = module.iam_user2.access_key_encrypted_ses_smtp_password_v4 +} + +output "iam_user2_access_key_status" { + description = "Active or Inactive. Keys are initially active, but can be made inactive by other means" + value = module.iam_user2.access_key_status +} + +output "iam_user2_ssh_key_public_key_id" { + description = "The unique identifier for the SSH public key" + value = module.iam_user2.ssh_key_public_key_id +} + +output "iam_user2_ssh_key_fingerprint" { + description = "The MD5 message digest of the SSH public key" + value = module.iam_user2.ssh_key_fingerprint +} + +################################################################################ +# IAM User 3 +################################################################################ + +output "iam_user3_arn" { + description = "The ARN assigned by AWS for this user" + value = module.iam_user3.arn +} + +output "iam_user3_name" { + description = "The user's name" + value = module.iam_user3.name +} + +output "iam_user3_unique_id" { + description = "The unique ID assigned by AWS" + value = module.iam_user3.unique_id +} + +output "iam_user3_login_profile_password" { + description = "The user password" + value = module.iam_user3.login_profile_password sensitive = true } -output "iam_access_key_ses_smtp_password_v4" { - description = "The secret access key converted into an SES SMTP password" - value = module.iam_user.iam_access_key_ses_smtp_password_v4 +output "iam_user3_login_profile_key_fingerprint" { + description = "The fingerprint of the PGP key used to encrypt the password" + value = module.iam_user3.login_profile_key_fingerprint +} + +output "iam_user3_login_profile_encrypted_password" { + description = "The encrypted password, base64 encoded" + value = module.iam_user3.login_profile_encrypted_password +} + +output "iam_user3_access_key_id" { + description = "The access key ID" + value = module.iam_user3.access_key_id +} + +output "iam_user3_access_key_secret" { + description = "The access key secret" + value = module.iam_user3.access_key_secret sensitive = true } -output "iam_access_key_status" { - description = "Active or Inactive. Keys are initially active, but can be made inactive by other means." - value = module.iam_user.iam_access_key_status +output "iam_user3_access_key_fingerprint" { + description = "The fingerprint of the PGP key used to encrypt the secret" + value = module.iam_user3.access_key_fingerprint } -output "pgp_key" { - description = "PGP key used to encrypt sensitive data for this user (if empty - secrets are not encrypted)" - value = module.iam_user.pgp_key +output "iam_user3_access_key_encrypted_secret" { + description = "The encrypted secret, base64 encoded" + value = module.iam_user3.access_key_encrypted_secret } -output "keybase_password_decrypt_command" { - description = "Decrypt user password command" - value = module.iam_user.keybase_password_decrypt_command +output "iam_user3_access_key_ses_smtp_password_v4" { + description = "The secret access key converted into an SES SMTP password by applying AWS's Sigv4 conversion algorithm" + value = module.iam_user3.access_key_ses_smtp_password_v4 + sensitive = true } -output "keybase_password_pgp_message" { - description = "Encrypted password" - value = module.iam_user.keybase_password_pgp_message +output "iam_user3_access_key_encrypted_ses_smtp_password_v4" { + description = "The encrypted secret access key converted into an SES SMTP password by applying AWS's Sigv4 conversion algorithm" + value = module.iam_user3.access_key_encrypted_ses_smtp_password_v4 } -output "keybase_secret_key_decrypt_command" { - description = "Decrypt access secret key command" - value = module.iam_user.keybase_secret_key_decrypt_command +output "iam_user3_access_key_status" { + description = "Active or Inactive. Keys are initially active, but can be made inactive by other means" + value = module.iam_user3.access_key_status } -output "keybase_secret_key_pgp_message" { - description = "Encrypted access secret key" - value = module.iam_user.keybase_secret_key_pgp_message +output "iam_user3_ssh_key_public_key_id" { + description = "The unique identifier for the SSH public key" + value = module.iam_user3.ssh_key_public_key_id } -output "policy_arns" { - description = "The list of ARNs of policies directly assigned to the IAM user" - value = module.iam_user.policy_arns +output "iam_user3_ssh_key_fingerprint" { + description = "The MD5 message digest of the SSH public key" + value = module.iam_user3.ssh_key_fingerprint } diff --git a/modules/iam-account/README.md b/modules/iam-account/README.md index 10c0c4b1..6f2bb7d8 100644 --- a/modules/iam-account/README.md +++ b/modules/iam-account/README.md @@ -1,12 +1,31 @@ -# iam-account +# AWS IAM Account Terraform Module -Manage IAM account alias and password policy. +Creates an account policy and account alias. Module instantiation is once per account. + +## Usage + +```hcl +module "iam_account" { + source = "terraform-aws-modules/iam/aws//modules/iam-account" + + account_alias = "awesome-company" + + max_password_age = 90 + minimum_password_length = 24 + require_uppercase_characters = true + require_lowercase_characters = true + require_numbers = true + require_symbols = true + password_reuse_prevention = 3 + allow_users_to_change_password = true +} +``` ## Notes -* If IAM account alias was previously set (either via AWS console or during the creation of an account from AWS Organizations) you will see this error: +If IAM account alias was previously set (either via AWS console or during the creation of an account from AWS Organizations) you will see this error: ``` -* aws_iam_account_alias.this: Error creating account alias with name my-account-alias +aws_iam_account_alias.this: Error creating account alias with name my-account-alias ``` If you want to manage IAM alias using Terraform (otherwise why are you reading this?) you need to import this resource like this: @@ -73,3 +92,7 @@ No modules. | [caller\_identity\_user\_id](#output\_caller\_identity\_user\_id) | The unique identifier of the calling entity | | [iam\_account\_password\_policy\_expire\_passwords](#output\_iam\_account\_password\_policy\_expire\_passwords) | Indicates whether passwords in the account expire. Returns true if max\_password\_age contains a value greater than 0. Returns false if it is 0 or not present. | + +## License + +Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-iam/blob/master/LICENSE). diff --git a/modules/iam-assumable-role-with-oidc/README.md b/modules/iam-assumable-role-with-oidc/README.md deleted file mode 100644 index 58bddad9..00000000 --- a/modules/iam-assumable-role-with-oidc/README.md +++ /dev/null @@ -1,72 +0,0 @@ -# iam-assumable-role-with-oidc - -Creates single IAM role which can be assumed by trusted resources using OpenID Connect Federated Users. - -[Creating IAM OIDC Identity Providers](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html) - -This module supports IAM Roles for kubernetes service accounts as described in the [EKS documentation](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html). - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 4.0 | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | -| [aws_iam_role_policy.inline](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | -| [aws_iam_role_policy_attachment.custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | -| [aws_iam_policy_document.assume_role_with_oidc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_iam_policy_document.inline](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [allow\_self\_assume\_role](#input\_allow\_self\_assume\_role) | Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/) | `bool` | `false` | no | -| [aws\_account\_id](#input\_aws\_account\_id) | The AWS account ID where the OIDC provider lives, leave empty to use the account for the AWS provider | `string` | `""` | no | -| [create\_role](#input\_create\_role) | Whether to create a role | `bool` | `false` | no | -| [force\_detach\_policies](#input\_force\_detach\_policies) | Whether policies should be detached from this role when destroying | `bool` | `false` | no | -| [inline\_policy\_statements](#input\_inline\_policy\_statements) | List of inline policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) to attach to IAM role as an inline policy | `any` | `[]` | no | -| [max\_session\_duration](#input\_max\_session\_duration) | Maximum CLI/API session duration in seconds between 3600 and 43200 | `number` | `3600` | no | -| [number\_of\_role\_policy\_arns](#input\_number\_of\_role\_policy\_arns) | Number of IAM policies to attach to IAM role | `number` | `null` | no | -| [oidc\_fully\_qualified\_audiences](#input\_oidc\_fully\_qualified\_audiences) | The audience to be added to the role policy. Set to sts.amazonaws.com for cross-account assumable role. Leave empty otherwise. | `set(string)` | `[]` | no | -| [oidc\_fully\_qualified\_subjects](#input\_oidc\_fully\_qualified\_subjects) | The fully qualified OIDC subjects to be added to the role policy | `set(string)` | `[]` | no | -| [oidc\_subjects\_with\_wildcards](#input\_oidc\_subjects\_with\_wildcards) | The OIDC subject using wildcards to be added to the role policy | `set(string)` | `[]` | no | -| [provider\_trust\_policy\_conditions](#input\_provider\_trust\_policy\_conditions) | [Condition constraints](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#condition) applied to the trust policy |
list(object({
test = string
variable = string
values = list(string)
}))
| `[]` | no | -| [provider\_url](#input\_provider\_url) | URL of the OIDC Provider. Use provider\_urls to specify several URLs. | `string` | `""` | no | -| [provider\_urls](#input\_provider\_urls) | List of URLs of the OIDC Providers | `list(string)` | `[]` | no | -| [role\_description](#input\_role\_description) | IAM Role description | `string` | `""` | no | -| [role\_name](#input\_role\_name) | IAM role name | `string` | `null` | no | -| [role\_name\_prefix](#input\_role\_name\_prefix) | IAM role name prefix | `string` | `null` | no | -| [role\_path](#input\_role\_path) | Path of IAM role | `string` | `"/"` | no | -| [role\_permissions\_boundary\_arn](#input\_role\_permissions\_boundary\_arn) | Permissions boundary ARN to use for IAM role | `string` | `""` | no | -| [role\_policy\_arns](#input\_role\_policy\_arns) | List of ARNs of IAM policies to attach to IAM role | `list(string)` | `[]` | no | -| [tags](#input\_tags) | A map of tags to add to IAM role resources | `map(string)` | `{}` | no | - -## Outputs - -| Name | Description | -|------|-------------| -| [iam\_role\_arn](#output\_iam\_role\_arn) | ARN of IAM role | -| [iam\_role\_name](#output\_iam\_role\_name) | Name of IAM role | -| [iam\_role\_path](#output\_iam\_role\_path) | Path of IAM role | -| [iam\_role\_unique\_id](#output\_iam\_role\_unique\_id) | Unique ID of IAM role | - diff --git a/modules/iam-assumable-role-with-oidc/main.tf b/modules/iam-assumable-role-with-oidc/main.tf deleted file mode 100644 index 216b299f..00000000 --- a/modules/iam-assumable-role-with-oidc/main.tf +++ /dev/null @@ -1,180 +0,0 @@ -locals { - aws_account_id = var.aws_account_id != "" ? var.aws_account_id : data.aws_caller_identity.current.account_id - partition = data.aws_partition.current.partition - # clean URLs of https:// prefix - urls = [ - for url in compact(distinct(concat(var.provider_urls, [var.provider_url]))) : - replace(url, "https://", "") - ] - number_of_role_policy_arns = coalesce(var.number_of_role_policy_arns, length(var.role_policy_arns)) - role_name_condition = var.role_name != null ? var.role_name : "${var.role_name_prefix}*" -} - -data "aws_caller_identity" "current" {} -data "aws_partition" "current" {} - -data "aws_iam_policy_document" "assume_role_with_oidc" { - count = var.create_role ? 1 : 0 - - dynamic "statement" { - # https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/ - for_each = var.allow_self_assume_role ? [1] : [] - - content { - sid = "ExplicitSelfRoleAssumption" - effect = "Allow" - actions = ["sts:AssumeRole"] - - principals { - type = "AWS" - identifiers = ["*"] - } - - condition { - test = "ArnLike" - variable = "aws:PrincipalArn" - values = ["arn:${local.partition}:iam::${data.aws_caller_identity.current.account_id}:role${var.role_path}${local.role_name_condition}"] - } - } - } - - dynamic "statement" { - for_each = local.urls - - content { - effect = "Allow" - actions = ["sts:AssumeRoleWithWebIdentity", "sts:TagSession"] - - principals { - type = "Federated" - - identifiers = ["arn:${data.aws_partition.current.partition}:iam::${local.aws_account_id}:oidc-provider/${statement.value}"] - } - - dynamic "condition" { - for_each = length(var.oidc_fully_qualified_subjects) > 0 ? local.urls : [] - - content { - test = "StringEquals" - variable = "${statement.value}:sub" - values = var.oidc_fully_qualified_subjects - } - } - - dynamic "condition" { - for_each = length(var.oidc_subjects_with_wildcards) > 0 ? local.urls : [] - - content { - test = "StringLike" - variable = "${statement.value}:sub" - values = var.oidc_subjects_with_wildcards - } - } - - dynamic "condition" { - for_each = length(var.oidc_fully_qualified_audiences) > 0 ? local.urls : [] - - content { - test = "StringLike" - variable = "${statement.value}:aud" - values = var.oidc_fully_qualified_audiences - } - } - - dynamic "condition" { - for_each = var.provider_trust_policy_conditions - - content { - test = condition.value.test - values = condition.value.values - variable = condition.value.variable - } - } - } - } -} - -resource "aws_iam_role" "this" { - count = var.create_role ? 1 : 0 - - name = var.role_name - name_prefix = var.role_name_prefix - description = var.role_description - path = var.role_path - max_session_duration = var.max_session_duration - - force_detach_policies = var.force_detach_policies - permissions_boundary = var.role_permissions_boundary_arn - - assume_role_policy = data.aws_iam_policy_document.assume_role_with_oidc[0].json - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "custom" { - count = var.create_role ? local.number_of_role_policy_arns : 0 - - role = aws_iam_role.this[0].name - policy_arn = var.role_policy_arns[count.index] -} - -############################### -# IAM Role Inline policy -############################### - -locals { - create_iam_role_inline_policy = var.create_role && length(var.inline_policy_statements) > 0 -} - -data "aws_iam_policy_document" "inline" { - count = local.create_iam_role_inline_policy ? 1 : 0 - - dynamic "statement" { - for_each = var.inline_policy_statements - - content { - sid = try(statement.value.sid, null) - actions = try(statement.value.actions, null) - not_actions = try(statement.value.not_actions, null) - effect = try(statement.value.effect, null) - resources = try(statement.value.resources, null) - not_resources = try(statement.value.not_resources, null) - - dynamic "principals" { - for_each = try(statement.value.principals, []) - - content { - type = principals.value.type - identifiers = principals.value.identifiers - } - } - - dynamic "not_principals" { - for_each = try(statement.value.not_principals, []) - - content { - type = not_principals.value.type - identifiers = not_principals.value.identifiers - } - } - - dynamic "condition" { - for_each = try(statement.value.conditions, []) - - content { - test = condition.value.test - values = condition.value.values - variable = condition.value.variable - } - } - } - } -} - -resource "aws_iam_role_policy" "inline" { - count = local.create_iam_role_inline_policy ? 1 : 0 - - role = aws_iam_role.this[0].name - name_prefix = "${var.role_name}_inline_" - policy = data.aws_iam_policy_document.inline[0].json -} diff --git a/modules/iam-assumable-role-with-oidc/outputs.tf b/modules/iam-assumable-role-with-oidc/outputs.tf deleted file mode 100644 index a6805310..00000000 --- a/modules/iam-assumable-role-with-oidc/outputs.tf +++ /dev/null @@ -1,19 +0,0 @@ -output "iam_role_arn" { - description = "ARN of IAM role" - value = try(aws_iam_role.this[0].arn, "") -} - -output "iam_role_name" { - description = "Name of IAM role" - value = try(aws_iam_role.this[0].name, "") -} - -output "iam_role_path" { - description = "Path of IAM role" - value = try(aws_iam_role.this[0].path, "") -} - -output "iam_role_unique_id" { - description = "Unique ID of IAM role" - value = try(aws_iam_role.this[0].unique_id, "") -} diff --git a/modules/iam-assumable-role-with-oidc/variables.tf b/modules/iam-assumable-role-with-oidc/variables.tf deleted file mode 100644 index dc06d733..00000000 --- a/modules/iam-assumable-role-with-oidc/variables.tf +++ /dev/null @@ -1,123 +0,0 @@ -variable "create_role" { - description = "Whether to create a role" - type = bool - default = false -} - -variable "provider_url" { - description = "URL of the OIDC Provider. Use provider_urls to specify several URLs." - type = string - default = "" -} - -variable "provider_urls" { - description = "List of URLs of the OIDC Providers" - type = list(string) - default = [] -} - -variable "aws_account_id" { - description = "The AWS account ID where the OIDC provider lives, leave empty to use the account for the AWS provider" - type = string - default = "" -} - -variable "tags" { - description = "A map of tags to add to IAM role resources" - type = map(string) - default = {} -} - -variable "role_name" { - description = "IAM role name" - type = string - default = null -} - -variable "role_name_prefix" { - description = "IAM role name prefix" - type = string - default = null -} - -variable "role_description" { - description = "IAM Role description" - type = string - default = "" -} - -variable "role_path" { - description = "Path of IAM role" - type = string - default = "/" -} - -variable "role_permissions_boundary_arn" { - description = "Permissions boundary ARN to use for IAM role" - type = string - default = "" -} - -variable "max_session_duration" { - description = "Maximum CLI/API session duration in seconds between 3600 and 43200" - type = number - default = 3600 -} - -variable "role_policy_arns" { - description = "List of ARNs of IAM policies to attach to IAM role" - type = list(string) - default = [] -} - -variable "number_of_role_policy_arns" { - description = "Number of IAM policies to attach to IAM role" - type = number - default = null -} - -variable "inline_policy_statements" { - description = "List of inline policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) to attach to IAM role as an inline policy" - type = any - default = [] -} - -variable "oidc_fully_qualified_subjects" { - description = "The fully qualified OIDC subjects to be added to the role policy" - type = set(string) - default = [] -} - -variable "oidc_subjects_with_wildcards" { - description = "The OIDC subject using wildcards to be added to the role policy" - type = set(string) - default = [] -} - -variable "oidc_fully_qualified_audiences" { - description = "The audience to be added to the role policy. Set to sts.amazonaws.com for cross-account assumable role. Leave empty otherwise." - type = set(string) - default = [] -} - -variable "force_detach_policies" { - description = "Whether policies should be detached from this role when destroying" - type = bool - default = false -} - -variable "allow_self_assume_role" { - description = "Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/)" - type = bool - default = false -} - -variable "provider_trust_policy_conditions" { - description = "[Condition constraints](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#condition) applied to the trust policy" - type = list(object({ - test = string - variable = string - values = list(string) - })) - default = [] -} diff --git a/modules/iam-assumable-role-with-saml/README.md b/modules/iam-assumable-role-with-saml/README.md deleted file mode 100644 index 62d0fb78..00000000 --- a/modules/iam-assumable-role-with-saml/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# iam-assumable-role-with-saml - -Creates single IAM role which can be assumed by trusted resources using SAML Federated Users. - -[Creating IAM SAML Identity Providers](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_saml.html) -[Enabling SAML 2.0 Federated Users to Access the AWS Management Console](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_enable-console-saml.html) - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 4.0 | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | -| [aws_iam_role_policy_attachment.custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | -| [aws_iam_policy_document.assume_role_with_saml](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [allow\_self\_assume\_role](#input\_allow\_self\_assume\_role) | Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/) | `bool` | `false` | no | -| [aws\_saml\_endpoint](#input\_aws\_saml\_endpoint) | AWS SAML Endpoint | `string` | `"https://signin.aws.amazon.com/saml"` | no | -| [create\_role](#input\_create\_role) | Whether to create a role | `bool` | `false` | no | -| [force\_detach\_policies](#input\_force\_detach\_policies) | Whether policies should be detached from this role when destroying | `bool` | `false` | no | -| [max\_session\_duration](#input\_max\_session\_duration) | Maximum CLI/API session duration in seconds between 3600 and 43200 | `number` | `3600` | no | -| [number\_of\_role\_policy\_arns](#input\_number\_of\_role\_policy\_arns) | Number of IAM policies to attach to IAM role | `number` | `null` | no | -| [provider\_id](#input\_provider\_id) | ID of the SAML Provider. Use provider\_ids to specify several IDs. | `string` | `""` | no | -| [provider\_ids](#input\_provider\_ids) | List of SAML Provider IDs | `list(string)` | `[]` | no | -| [role\_description](#input\_role\_description) | IAM Role description | `string` | `""` | no | -| [role\_name](#input\_role\_name) | IAM role name | `string` | `null` | no | -| [role\_name\_prefix](#input\_role\_name\_prefix) | IAM role name prefix | `string` | `null` | no | -| [role\_path](#input\_role\_path) | Path of IAM role | `string` | `"/"` | no | -| [role\_permissions\_boundary\_arn](#input\_role\_permissions\_boundary\_arn) | Permissions boundary ARN to use for IAM role | `string` | `""` | no | -| [role\_policy\_arns](#input\_role\_policy\_arns) | List of ARNs of IAM policies to attach to IAM role | `list(string)` | `[]` | no | -| [tags](#input\_tags) | A map of tags to add to IAM role resources | `map(string)` | `{}` | no | -| [trusted\_role\_actions](#input\_trusted\_role\_actions) | Additional role actions | `list(string)` |
[
"sts:AssumeRoleWithSAML",
"sts:TagSession"
]
| no | - -## Outputs - -| Name | Description | -|------|-------------| -| [iam\_role\_arn](#output\_iam\_role\_arn) | ARN of IAM role | -| [iam\_role\_name](#output\_iam\_role\_name) | Name of IAM role | -| [iam\_role\_path](#output\_iam\_role\_path) | Path of IAM role | -| [iam\_role\_unique\_id](#output\_iam\_role\_unique\_id) | Unique ID of IAM role | - diff --git a/modules/iam-assumable-role-with-saml/main.tf b/modules/iam-assumable-role-with-saml/main.tf deleted file mode 100644 index e1d1902b..00000000 --- a/modules/iam-assumable-role-with-saml/main.tf +++ /dev/null @@ -1,74 +0,0 @@ -data "aws_caller_identity" "current" {} -data "aws_partition" "current" {} - -locals { - account_id = data.aws_caller_identity.current.account_id - identifiers = compact(distinct(concat(var.provider_ids, [var.provider_id]))) - number_of_role_policy_arns = coalesce(var.number_of_role_policy_arns, length(var.role_policy_arns)) - partition = data.aws_partition.current.partition - role_name_condition = var.role_name != null ? var.role_name : "${var.role_name_prefix}*" -} - -data "aws_iam_policy_document" "assume_role_with_saml" { - dynamic "statement" { - # https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/ - for_each = var.allow_self_assume_role ? [1] : [] - - content { - sid = "ExplicitSelfRoleAssumption" - effect = "Allow" - actions = ["sts:AssumeRole"] - - principals { - type = "AWS" - identifiers = ["*"] - } - - condition { - test = "ArnLike" - variable = "aws:PrincipalArn" - values = ["arn:${local.partition}:iam::${local.account_id}:role${var.role_path}${local.role_name_condition}"] - } - } - } - - statement { - effect = "Allow" - actions = compact(distinct(concat(["sts:AssumeRoleWithSAML"], var.trusted_role_actions))) - - principals { - type = "Federated" - identifiers = local.identifiers - } - - condition { - test = "StringEquals" - variable = "SAML:aud" - values = [var.aws_saml_endpoint] - } - } -} - -resource "aws_iam_role" "this" { - count = var.create_role ? 1 : 0 - - name = var.role_name - name_prefix = var.role_name_prefix - description = var.role_description - path = var.role_path - max_session_duration = var.max_session_duration - - force_detach_policies = var.force_detach_policies - permissions_boundary = var.role_permissions_boundary_arn - - assume_role_policy = data.aws_iam_policy_document.assume_role_with_saml.json - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "custom" { - count = var.create_role ? local.number_of_role_policy_arns : 0 - - role = aws_iam_role.this[0].name - policy_arn = var.role_policy_arns[count.index] -} diff --git a/modules/iam-assumable-role-with-saml/outputs.tf b/modules/iam-assumable-role-with-saml/outputs.tf deleted file mode 100644 index a6805310..00000000 --- a/modules/iam-assumable-role-with-saml/outputs.tf +++ /dev/null @@ -1,19 +0,0 @@ -output "iam_role_arn" { - description = "ARN of IAM role" - value = try(aws_iam_role.this[0].arn, "") -} - -output "iam_role_name" { - description = "Name of IAM role" - value = try(aws_iam_role.this[0].name, "") -} - -output "iam_role_path" { - description = "Path of IAM role" - value = try(aws_iam_role.this[0].path, "") -} - -output "iam_role_unique_id" { - description = "Unique ID of IAM role" - value = try(aws_iam_role.this[0].unique_id, "") -} diff --git a/modules/iam-assumable-role-with-saml/variables.tf b/modules/iam-assumable-role-with-saml/variables.tf deleted file mode 100644 index 42c33647..00000000 --- a/modules/iam-assumable-role-with-saml/variables.tf +++ /dev/null @@ -1,95 +0,0 @@ -variable "create_role" { - description = "Whether to create a role" - type = bool - default = false -} - -variable "provider_id" { - description = "ID of the SAML Provider. Use provider_ids to specify several IDs." - type = string - default = "" -} - -variable "provider_ids" { - description = "List of SAML Provider IDs" - type = list(string) - default = [] -} - -variable "aws_saml_endpoint" { - description = "AWS SAML Endpoint" - default = "https://signin.aws.amazon.com/saml" - type = string -} - -variable "tags" { - description = "A map of tags to add to IAM role resources" - type = map(string) - default = {} -} - -variable "role_name" { - description = "IAM role name" - type = string - default = null -} - -variable "role_name_prefix" { - description = "IAM role name prefix" - type = string - default = null -} - -variable "role_description" { - description = "IAM Role description" - type = string - default = "" -} - -variable "role_path" { - description = "Path of IAM role" - type = string - default = "/" -} - -variable "role_permissions_boundary_arn" { - description = "Permissions boundary ARN to use for IAM role" - type = string - default = "" -} - -variable "max_session_duration" { - description = "Maximum CLI/API session duration in seconds between 3600 and 43200" - type = number - default = 3600 -} - -variable "role_policy_arns" { - description = "List of ARNs of IAM policies to attach to IAM role" - type = list(string) - default = [] -} - -variable "number_of_role_policy_arns" { - description = "Number of IAM policies to attach to IAM role" - type = number - default = null -} - -variable "force_detach_policies" { - description = "Whether policies should be detached from this role when destroying" - type = bool - default = false -} - -variable "allow_self_assume_role" { - description = "Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/)" - type = bool - default = false -} - -variable "trusted_role_actions" { - description = "Additional role actions" - type = list(string) - default = ["sts:AssumeRoleWithSAML", "sts:TagSession"] -} diff --git a/modules/iam-assumable-role/README.md b/modules/iam-assumable-role/README.md deleted file mode 100644 index 822fcc76..00000000 --- a/modules/iam-assumable-role/README.md +++ /dev/null @@ -1,92 +0,0 @@ -# iam-assumable-role - -Creates single IAM role which can be assumed by trusted resources. - -Trusted resources can be any [IAM ARNs](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html#identifiers-arns) - typically, AWS accounts and users. - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 4.0 | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [aws_iam_instance_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_instance_profile) | resource | -| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | -| [aws_iam_role_policy.inline](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | -| [aws_iam_role_policy_attachment.admin](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.poweruser](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.readonly](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | -| [aws_iam_policy_document.assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_iam_policy_document.assume_role_with_mfa](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_iam_policy_document.inline](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [admin\_role\_policy\_arn](#input\_admin\_role\_policy\_arn) | Policy ARN to use for admin role | `string` | `"arn:aws:iam::aws:policy/AdministratorAccess"` | no | -| [allow\_self\_assume\_role](#input\_allow\_self\_assume\_role) | Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/) | `bool` | `false` | no | -| [attach\_admin\_policy](#input\_attach\_admin\_policy) | Whether to attach an admin policy to a role | `bool` | `false` | no | -| [attach\_poweruser\_policy](#input\_attach\_poweruser\_policy) | Whether to attach a poweruser policy to a role | `bool` | `false` | no | -| [attach\_readonly\_policy](#input\_attach\_readonly\_policy) | Whether to attach a readonly policy to a role | `bool` | `false` | no | -| [create\_custom\_role\_trust\_policy](#input\_create\_custom\_role\_trust\_policy) | Whether to create a custom\_role\_trust\_policy. Prevent errors with count, when custom\_role\_trust\_policy is computed | `bool` | `false` | no | -| [create\_instance\_profile](#input\_create\_instance\_profile) | Whether to create an instance profile | `bool` | `false` | no | -| [create\_role](#input\_create\_role) | Whether to create a role | `bool` | `false` | no | -| [custom\_role\_policy\_arns](#input\_custom\_role\_policy\_arns) | List of ARNs of IAM policies to attach to IAM role | `list(string)` | `[]` | no | -| [custom\_role\_trust\_policy](#input\_custom\_role\_trust\_policy) | A custom role trust policy. (Only valid if create\_custom\_role\_trust\_policy = true) | `string` | `""` | no | -| [force\_detach\_policies](#input\_force\_detach\_policies) | Whether policies should be detached from this role when destroying | `bool` | `false` | no | -| [inline\_policy\_statements](#input\_inline\_policy\_statements) | List of inline policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) to attach to IAM role as an inline policy | `any` | `[]` | no | -| [max\_session\_duration](#input\_max\_session\_duration) | Maximum CLI/API session duration in seconds between 3600 and 43200 | `number` | `3600` | no | -| [mfa\_age](#input\_mfa\_age) | Max age of valid MFA (in seconds) for roles which require MFA | `number` | `86400` | no | -| [number\_of\_custom\_role\_policy\_arns](#input\_number\_of\_custom\_role\_policy\_arns) | Number of IAM policies to attach to IAM role | `number` | `null` | no | -| [poweruser\_role\_policy\_arn](#input\_poweruser\_role\_policy\_arn) | Policy ARN to use for poweruser role | `string` | `"arn:aws:iam::aws:policy/PowerUserAccess"` | no | -| [readonly\_role\_policy\_arn](#input\_readonly\_role\_policy\_arn) | Policy ARN to use for readonly role | `string` | `"arn:aws:iam::aws:policy/ReadOnlyAccess"` | no | -| [role\_description](#input\_role\_description) | IAM Role description | `string` | `""` | no | -| [role\_name](#input\_role\_name) | IAM role name | `string` | `null` | no | -| [role\_name\_prefix](#input\_role\_name\_prefix) | IAM role name prefix | `string` | `null` | no | -| [role\_path](#input\_role\_path) | Path of IAM role | `string` | `"/"` | no | -| [role\_permissions\_boundary\_arn](#input\_role\_permissions\_boundary\_arn) | Permissions boundary ARN to use for IAM role | `string` | `""` | no | -| [role\_requires\_mfa](#input\_role\_requires\_mfa) | Whether role requires MFA | `bool` | `true` | no | -| [role\_requires\_session\_name](#input\_role\_requires\_session\_name) | Determines if the role-session-name variable is needed when assuming a role(https://aws.amazon.com/blogs/security/easily-control-naming-individual-iam-role-sessions/) | `bool` | `false` | no | -| [role\_session\_name](#input\_role\_session\_name) | role\_session\_name for roles which require this parameter when being assumed. By default, you need to set your own username as role\_session\_name | `list(string)` |
[
"${aws:username}"
]
| no | -| [role\_sts\_externalid](#input\_role\_sts\_externalid) | STS ExternalId condition values to use with a role (when MFA is not required) | `any` | `[]` | no | -| [tags](#input\_tags) | A map of tags to add to IAM role resources | `map(string)` | `{}` | no | -| [trust\_policy\_conditions](#input\_trust\_policy\_conditions) | [Condition constraints](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#condition) applied to the trust policy |
list(object({
test = string
variable = string
values = list(string)
}))
| `[]` | no | -| [trusted\_role\_actions](#input\_trusted\_role\_actions) | Additional trusted role actions | `list(string)` |
[
"sts:AssumeRole",
"sts:TagSession"
]
| no | -| [trusted\_role\_arns](#input\_trusted\_role\_arns) | ARNs of AWS entities who can assume these roles | `list(string)` | `[]` | no | -| [trusted\_role\_services](#input\_trusted\_role\_services) | AWS Services that can assume these roles | `list(string)` | `[]` | no | - -## Outputs - -| Name | Description | -|------|-------------| -| [iam\_instance\_profile\_arn](#output\_iam\_instance\_profile\_arn) | ARN of IAM instance profile | -| [iam\_instance\_profile\_id](#output\_iam\_instance\_profile\_id) | IAM Instance profile's ID. | -| [iam\_instance\_profile\_name](#output\_iam\_instance\_profile\_name) | Name of IAM instance profile | -| [iam\_instance\_profile\_path](#output\_iam\_instance\_profile\_path) | Path of IAM instance profile | -| [iam\_role\_arn](#output\_iam\_role\_arn) | ARN of IAM role | -| [iam\_role\_name](#output\_iam\_role\_name) | Name of IAM role | -| [iam\_role\_path](#output\_iam\_role\_path) | Path of IAM role | -| [iam\_role\_unique\_id](#output\_iam\_role\_unique\_id) | Unique ID of IAM role | -| [role\_requires\_mfa](#output\_role\_requires\_mfa) | Whether IAM role requires MFA | -| [role\_sts\_externalid](#output\_role\_sts\_externalid) | STS ExternalId condition value to use with a role | - diff --git a/modules/iam-assumable-role/main.tf b/modules/iam-assumable-role/main.tf deleted file mode 100644 index f25cecc9..00000000 --- a/modules/iam-assumable-role/main.tf +++ /dev/null @@ -1,284 +0,0 @@ -data "aws_caller_identity" "current" {} -data "aws_partition" "current" {} - -locals { - account_id = data.aws_caller_identity.current.account_id - partition = data.aws_partition.current.partition - role_sts_externalid = flatten([var.role_sts_externalid]) - role_name_condition = var.role_name != null ? var.role_name : "${var.role_name_prefix}*" - custom_role_trust_policy_condition = var.create_custom_role_trust_policy ? var.custom_role_trust_policy : "" -} - -data "aws_iam_policy_document" "assume_role" { - count = !var.create_custom_role_trust_policy && var.role_requires_mfa ? 0 : 1 - - dynamic "statement" { - # https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/ - for_each = var.allow_self_assume_role ? [1] : [] - - content { - sid = "ExplicitSelfRoleAssumption" - effect = "Allow" - actions = ["sts:AssumeRole"] - - principals { - type = "AWS" - identifiers = ["*"] - } - - condition { - test = "ArnLike" - variable = "aws:PrincipalArn" - values = ["arn:${local.partition}:iam::${local.account_id}:role${var.role_path}${local.role_name_condition}"] - } - } - } - - statement { - effect = "Allow" - actions = compact(distinct(concat(["sts:AssumeRole"], var.trusted_role_actions))) - - principals { - type = "AWS" - identifiers = var.trusted_role_arns - } - - principals { - type = "Service" - identifiers = var.trusted_role_services - } - - dynamic "condition" { - for_each = length(local.role_sts_externalid) != 0 ? [true] : [] - - content { - test = "StringEquals" - variable = "sts:ExternalId" - values = local.role_sts_externalid - } - } - - dynamic "condition" { - for_each = var.role_requires_session_name ? [1] : [] - - content { - test = "StringEquals" - variable = "sts:RoleSessionName" - values = var.role_session_name - } - } - - dynamic "condition" { - for_each = var.trust_policy_conditions - - content { - test = condition.value.test - variable = condition.value.variable - values = condition.value.values - } - } - } -} - -data "aws_iam_policy_document" "assume_role_with_mfa" { - count = !var.create_custom_role_trust_policy && var.role_requires_mfa ? 1 : 0 - - dynamic "statement" { - # https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/ - for_each = var.allow_self_assume_role ? [1] : [] - - content { - sid = "ExplicitSelfRoleAssumption" - effect = "Allow" - actions = ["sts:AssumeRole"] - - principals { - type = "AWS" - identifiers = ["*"] - } - - condition { - test = "ArnLike" - variable = "aws:PrincipalArn" - values = ["arn:${local.partition}:iam::${local.account_id}:role${var.role_path}${local.role_name_condition}"] - } - } - } - - statement { - effect = "Allow" - actions = compact(distinct(concat(["sts:AssumeRole"], var.trusted_role_actions))) - - principals { - type = "AWS" - identifiers = var.trusted_role_arns - } - - principals { - type = "Service" - identifiers = var.trusted_role_services - } - - condition { - test = "Bool" - variable = "aws:MultiFactorAuthPresent" - values = ["true"] - } - - condition { - test = "NumericLessThan" - variable = "aws:MultiFactorAuthAge" - values = [var.mfa_age] - } - - dynamic "condition" { - for_each = length(local.role_sts_externalid) != 0 ? [true] : [] - - content { - test = "StringEquals" - variable = "sts:ExternalId" - values = local.role_sts_externalid - } - } - - dynamic "condition" { - for_each = var.role_requires_session_name ? [1] : [] - - content { - test = "StringEquals" - variable = "sts:RoleSessionName" - values = var.role_session_name - } - } - - dynamic "condition" { - for_each = var.trust_policy_conditions - - content { - test = condition.value.test - variable = condition.value.variable - values = condition.value.values - } - } - } -} - -resource "aws_iam_role" "this" { - count = var.create_role ? 1 : 0 - - name = var.role_name - name_prefix = var.role_name_prefix - path = var.role_path - max_session_duration = var.max_session_duration - description = var.role_description - - force_detach_policies = var.force_detach_policies - permissions_boundary = var.role_permissions_boundary_arn - - assume_role_policy = coalesce( - local.custom_role_trust_policy_condition, - try(data.aws_iam_policy_document.assume_role_with_mfa[0].json, - data.aws_iam_policy_document.assume_role[0].json - ) - ) - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "custom" { - count = var.create_role ? coalesce(var.number_of_custom_role_policy_arns, length(var.custom_role_policy_arns)) : 0 - - role = aws_iam_role.this[0].name - policy_arn = element(var.custom_role_policy_arns, count.index) -} - -resource "aws_iam_role_policy_attachment" "admin" { - count = var.create_role && var.attach_admin_policy ? 1 : 0 - - role = aws_iam_role.this[0].name - policy_arn = var.admin_role_policy_arn -} - -resource "aws_iam_role_policy_attachment" "poweruser" { - count = var.create_role && var.attach_poweruser_policy ? 1 : 0 - - role = aws_iam_role.this[0].name - policy_arn = var.poweruser_role_policy_arn -} - -resource "aws_iam_role_policy_attachment" "readonly" { - count = var.create_role && var.attach_readonly_policy ? 1 : 0 - - role = aws_iam_role.this[0].name - policy_arn = var.readonly_role_policy_arn -} - -resource "aws_iam_instance_profile" "this" { - count = var.create_role && var.create_instance_profile ? 1 : 0 - name = var.role_name - path = var.role_path - role = aws_iam_role.this[0].name - - tags = var.tags -} - -############################### -# IAM Role Inline policy -############################### - -locals { - create_iam_role_inline_policy = var.create_role && length(var.inline_policy_statements) > 0 -} - -data "aws_iam_policy_document" "inline" { - count = local.create_iam_role_inline_policy ? 1 : 0 - - dynamic "statement" { - for_each = var.inline_policy_statements - - content { - sid = try(statement.value.sid, null) - actions = try(statement.value.actions, null) - not_actions = try(statement.value.not_actions, null) - effect = try(statement.value.effect, null) - resources = try(statement.value.resources, null) - not_resources = try(statement.value.not_resources, null) - - dynamic "principals" { - for_each = try(statement.value.principals, []) - - content { - type = principals.value.type - identifiers = principals.value.identifiers - } - } - - dynamic "not_principals" { - for_each = try(statement.value.not_principals, []) - - content { - type = not_principals.value.type - identifiers = not_principals.value.identifiers - } - } - - dynamic "condition" { - for_each = try(statement.value.conditions, []) - - content { - test = condition.value.test - values = condition.value.values - variable = condition.value.variable - } - } - } - } -} - -resource "aws_iam_role_policy" "inline" { - count = local.create_iam_role_inline_policy ? 1 : 0 - - role = aws_iam_role.this[0].name - name_prefix = "${try(coalesce(var.role_name, var.role_name_prefix), "")}_inline_" - policy = data.aws_iam_policy_document.inline[0].json -} diff --git a/modules/iam-assumable-role/outputs.tf b/modules/iam-assumable-role/outputs.tf deleted file mode 100644 index 117ff077..00000000 --- a/modules/iam-assumable-role/outputs.tf +++ /dev/null @@ -1,49 +0,0 @@ -output "iam_role_arn" { - description = "ARN of IAM role" - value = try(aws_iam_role.this[0].arn, "") -} - -output "iam_role_name" { - description = "Name of IAM role" - value = try(aws_iam_role.this[0].name, "") -} - -output "iam_role_path" { - description = "Path of IAM role" - value = try(aws_iam_role.this[0].path, "") -} - -output "iam_role_unique_id" { - description = "Unique ID of IAM role" - value = try(aws_iam_role.this[0].unique_id, "") -} - -output "role_requires_mfa" { - description = "Whether IAM role requires MFA" - value = var.role_requires_mfa -} - -output "iam_instance_profile_arn" { - description = "ARN of IAM instance profile" - value = try(aws_iam_instance_profile.this[0].arn, "") -} - -output "iam_instance_profile_name" { - description = "Name of IAM instance profile" - value = try(aws_iam_instance_profile.this[0].name, "") -} - -output "iam_instance_profile_id" { - description = "IAM Instance profile's ID." - value = try(aws_iam_instance_profile.this[0].id, "") -} - -output "iam_instance_profile_path" { - description = "Path of IAM instance profile" - value = try(aws_iam_instance_profile.this[0].path, "") -} - -output "role_sts_externalid" { - description = "STS ExternalId condition value to use with a role" - value = var.role_sts_externalid -} diff --git a/modules/iam-assumable-role/variables.tf b/modules/iam-assumable-role/variables.tf deleted file mode 100644 index 2ac20084..00000000 --- a/modules/iam-assumable-role/variables.tf +++ /dev/null @@ -1,190 +0,0 @@ -variable "trusted_role_actions" { - description = "Additional trusted role actions" - type = list(string) - default = ["sts:AssumeRole", "sts:TagSession"] -} - -variable "trusted_role_arns" { - description = "ARNs of AWS entities who can assume these roles" - type = list(string) - default = [] -} - -variable "trusted_role_services" { - description = "AWS Services that can assume these roles" - type = list(string) - default = [] -} - -variable "trust_policy_conditions" { - description = "[Condition constraints](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#condition) applied to the trust policy" - type = list(object({ - test = string - variable = string - values = list(string) - })) - default = [] -} - -variable "mfa_age" { - description = "Max age of valid MFA (in seconds) for roles which require MFA" - type = number - default = 86400 -} - -variable "max_session_duration" { - description = "Maximum CLI/API session duration in seconds between 3600 and 43200" - type = number - default = 3600 -} - -variable "create_role" { - description = "Whether to create a role" - type = bool - default = false -} - -variable "create_instance_profile" { - description = "Whether to create an instance profile" - type = bool - default = false -} - -variable "role_name" { - description = "IAM role name" - type = string - default = null -} - -variable "role_name_prefix" { - description = "IAM role name prefix" - type = string - default = null -} - -variable "role_path" { - description = "Path of IAM role" - type = string - default = "/" -} - -variable "role_requires_mfa" { - description = "Whether role requires MFA" - type = bool - default = true -} - -variable "role_permissions_boundary_arn" { - description = "Permissions boundary ARN to use for IAM role" - type = string - default = "" -} - -variable "tags" { - description = "A map of tags to add to IAM role resources" - type = map(string) - default = {} -} - -variable "custom_role_policy_arns" { - description = "List of ARNs of IAM policies to attach to IAM role" - type = list(string) - default = [] -} - -variable "custom_role_trust_policy" { - description = "A custom role trust policy. (Only valid if create_custom_role_trust_policy = true)" - type = string - default = "" -} - -variable "create_custom_role_trust_policy" { - description = "Whether to create a custom_role_trust_policy. Prevent errors with count, when custom_role_trust_policy is computed" - type = bool - default = false -} - -variable "number_of_custom_role_policy_arns" { - description = "Number of IAM policies to attach to IAM role" - type = number - default = null -} - -variable "inline_policy_statements" { - description = "List of inline policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) to attach to IAM role as an inline policy" - type = any - default = [] -} - -# Pre-defined policies -variable "admin_role_policy_arn" { - description = "Policy ARN to use for admin role" - type = string - default = "arn:aws:iam::aws:policy/AdministratorAccess" -} - -variable "poweruser_role_policy_arn" { - description = "Policy ARN to use for poweruser role" - type = string - default = "arn:aws:iam::aws:policy/PowerUserAccess" -} - -variable "readonly_role_policy_arn" { - description = "Policy ARN to use for readonly role" - type = string - default = "arn:aws:iam::aws:policy/ReadOnlyAccess" -} - -variable "attach_admin_policy" { - description = "Whether to attach an admin policy to a role" - type = bool - default = false -} - -variable "attach_poweruser_policy" { - description = "Whether to attach a poweruser policy to a role" - type = bool - default = false -} - -variable "attach_readonly_policy" { - description = "Whether to attach a readonly policy to a role" - type = bool - default = false -} - -variable "force_detach_policies" { - description = "Whether policies should be detached from this role when destroying" - type = bool - default = false -} - -variable "role_description" { - description = "IAM Role description" - type = string - default = "" -} - -variable "role_sts_externalid" { - description = "STS ExternalId condition values to use with a role (when MFA is not required)" - type = any - default = [] -} - -variable "allow_self_assume_role" { - description = "Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/)" - type = bool - default = false -} - -variable "role_requires_session_name" { - description = "Determines if the role-session-name variable is needed when assuming a role(https://aws.amazon.com/blogs/security/easily-control-naming-individual-iam-role-sessions/)" - type = bool - default = false -} - -variable "role_session_name" { - description = "role_session_name for roles which require this parameter when being assumed. By default, you need to set your own username as role_session_name" - type = list(string) - default = ["$${aws:username}"] -} diff --git a/modules/iam-assumable-roles-with-saml/README.md b/modules/iam-assumable-roles-with-saml/README.md deleted file mode 100644 index 4cc0e4ea..00000000 --- a/modules/iam-assumable-roles-with-saml/README.md +++ /dev/null @@ -1,87 +0,0 @@ -# iam-assumable-roles-with-saml - -Creates predefined IAM roles (admin, poweruser and readonly) which can be assumed by trusted resources using SAML Federated Users. - - -[Creating IAM SAML Identity Providers](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_saml.html) -[Enabling SAML 2.0 Federated Users to Access the AWS Management Console](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_enable-console-saml.html) - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 4.0 | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [aws_iam_role.admin](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | -| [aws_iam_role.poweruser](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | -| [aws_iam_role.readonly](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | -| [aws_iam_role_policy_attachment.admin](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.poweruser](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.readonly](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | -| [aws_iam_policy_document.assume_role_with_saml](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [admin\_role\_name](#input\_admin\_role\_name) | IAM role with admin access | `string` | `"admin"` | no | -| [admin\_role\_path](#input\_admin\_role\_path) | Path of admin IAM role | `string` | `"/"` | no | -| [admin\_role\_permissions\_boundary\_arn](#input\_admin\_role\_permissions\_boundary\_arn) | Permissions boundary ARN to use for admin role | `string` | `""` | no | -| [admin\_role\_policy\_arns](#input\_admin\_role\_policy\_arns) | List of policy ARNs to use for admin role | `list(string)` |
[
"arn:aws:iam::aws:policy/AdministratorAccess"
]
| no | -| [admin\_role\_tags](#input\_admin\_role\_tags) | A map of tags to add to admin role resource. | `map(string)` | `{}` | no | -| [allow\_self\_assume\_role](#input\_allow\_self\_assume\_role) | Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/) | `bool` | `false` | no | -| [aws\_saml\_endpoint](#input\_aws\_saml\_endpoint) | AWS SAML Endpoint | `string` | `"https://signin.aws.amazon.com/saml"` | no | -| [create\_admin\_role](#input\_create\_admin\_role) | Whether to create admin role | `bool` | `false` | no | -| [create\_poweruser\_role](#input\_create\_poweruser\_role) | Whether to create poweruser role | `bool` | `false` | no | -| [create\_readonly\_role](#input\_create\_readonly\_role) | Whether to create readonly role | `bool` | `false` | no | -| [force\_detach\_policies](#input\_force\_detach\_policies) | Whether policies should be detached from this role when destroying | `bool` | `false` | no | -| [max\_session\_duration](#input\_max\_session\_duration) | Maximum CLI/API session duration in seconds between 3600 and 43200 | `number` | `3600` | no | -| [poweruser\_role\_name](#input\_poweruser\_role\_name) | IAM role with poweruser access | `string` | `"poweruser"` | no | -| [poweruser\_role\_path](#input\_poweruser\_role\_path) | Path of poweruser IAM role | `string` | `"/"` | no | -| [poweruser\_role\_permissions\_boundary\_arn](#input\_poweruser\_role\_permissions\_boundary\_arn) | Permissions boundary ARN to use for poweruser role | `string` | `""` | no | -| [poweruser\_role\_policy\_arns](#input\_poweruser\_role\_policy\_arns) | List of policy ARNs to use for poweruser role | `list(string)` |
[
"arn:aws:iam::aws:policy/PowerUserAccess"
]
| no | -| [poweruser\_role\_tags](#input\_poweruser\_role\_tags) | A map of tags to add to poweruser role resource. | `map(string)` | `{}` | no | -| [provider\_id](#input\_provider\_id) | ID of the SAML Provider. Use provider\_ids to specify several IDs. | `string` | `""` | no | -| [provider\_ids](#input\_provider\_ids) | List of SAML Provider IDs | `list(string)` | `[]` | no | -| [readonly\_role\_name](#input\_readonly\_role\_name) | IAM role with readonly access | `string` | `"readonly"` | no | -| [readonly\_role\_path](#input\_readonly\_role\_path) | Path of readonly IAM role | `string` | `"/"` | no | -| [readonly\_role\_permissions\_boundary\_arn](#input\_readonly\_role\_permissions\_boundary\_arn) | Permissions boundary ARN to use for readonly role | `string` | `""` | no | -| [readonly\_role\_policy\_arns](#input\_readonly\_role\_policy\_arns) | List of policy ARNs to use for readonly role | `list(string)` |
[
"arn:aws:iam::aws:policy/ReadOnlyAccess"
]
| no | -| [readonly\_role\_tags](#input\_readonly\_role\_tags) | A map of tags to add to readonly role resource. | `map(string)` | `{}` | no | -| [trusted\_role\_actions](#input\_trusted\_role\_actions) | Additional role actions | `list(string)` |
[
"sts:AssumeRoleWithSAML",
"sts:TagSession"
]
| no | - -## Outputs - -| Name | Description | -|------|-------------| -| [admin\_iam\_role\_arn](#output\_admin\_iam\_role\_arn) | ARN of admin IAM role | -| [admin\_iam\_role\_name](#output\_admin\_iam\_role\_name) | Name of admin IAM role | -| [admin\_iam\_role\_path](#output\_admin\_iam\_role\_path) | Path of admin IAM role | -| [admin\_iam\_role\_unique\_id](#output\_admin\_iam\_role\_unique\_id) | Unique ID of IAM role | -| [poweruser\_iam\_role\_arn](#output\_poweruser\_iam\_role\_arn) | ARN of poweruser IAM role | -| [poweruser\_iam\_role\_name](#output\_poweruser\_iam\_role\_name) | Name of poweruser IAM role | -| [poweruser\_iam\_role\_path](#output\_poweruser\_iam\_role\_path) | Path of poweruser IAM role | -| [poweruser\_iam\_role\_unique\_id](#output\_poweruser\_iam\_role\_unique\_id) | Unique ID of IAM role | -| [readonly\_iam\_role\_arn](#output\_readonly\_iam\_role\_arn) | ARN of readonly IAM role | -| [readonly\_iam\_role\_name](#output\_readonly\_iam\_role\_name) | Name of readonly IAM role | -| [readonly\_iam\_role\_path](#output\_readonly\_iam\_role\_path) | Path of readonly IAM role | -| [readonly\_iam\_role\_unique\_id](#output\_readonly\_iam\_role\_unique\_id) | Unique ID of IAM role | - diff --git a/modules/iam-assumable-roles-with-saml/main.tf b/modules/iam-assumable-roles-with-saml/main.tf deleted file mode 100644 index 2b113237..00000000 --- a/modules/iam-assumable-roles-with-saml/main.tf +++ /dev/null @@ -1,158 +0,0 @@ -data "aws_caller_identity" "current" {} -data "aws_partition" "current" {} - -locals { - account_id = data.aws_caller_identity.current.account_id - identifiers = compact(distinct(concat(var.provider_ids, [var.provider_id]))) - partition = data.aws_partition.current.partition -} - -data "aws_iam_policy_document" "assume_role_with_saml" { - dynamic "statement" { - # https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/ - for_each = var.allow_self_assume_role && var.create_admin_role ? [1] : [] - - content { - sid = "ExplicitSelfAdminRoleAssumption" - effect = "Allow" - actions = ["sts:AssumeRole"] - - principals { - type = "AWS" - identifiers = ["*"] - } - - condition { - test = "ArnLike" - variable = "aws:PrincipalArn" - values = ["arn:${local.partition}:iam::${local.account_id}:role${var.admin_role_path}${var.admin_role_name}"] - } - } - } - - dynamic "statement" { - # https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/ - for_each = var.allow_self_assume_role && var.create_poweruser_role ? [1] : [] - - content { - sid = "ExplicitSelfPowerUserRoleAssumption" - effect = "Allow" - actions = ["sts:AssumeRole"] - - principals { - type = "AWS" - identifiers = ["*"] - } - - condition { - test = "ArnLike" - variable = "aws:PrincipalArn" - values = ["arn:${local.partition}:iam::${local.account_id}:role${var.poweruser_role_path}${var.poweruser_role_name}"] - } - } - } - - dynamic "statement" { - # https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/ - for_each = var.allow_self_assume_role && var.create_readonly_role ? [1] : [] - - content { - sid = "ExplicitSelfReadOnlyRoleAssumption" - effect = "Allow" - actions = ["sts:AssumeRole"] - - principals { - type = "AWS" - identifiers = ["*"] - } - - condition { - test = "ArnLike" - variable = "aws:PrincipalArn" - values = ["arn:${local.partition}:iam::${local.account_id}:role${var.readonly_role_path}${var.readonly_role_name}"] - } - } - } - - statement { - effect = "Allow" - actions = compact(distinct(concat(["sts:AssumeRoleWithSAML"], var.trusted_role_actions))) - - principals { - type = "Federated" - identifiers = local.identifiers - } - - condition { - test = "StringEquals" - variable = "SAML:aud" - values = [var.aws_saml_endpoint] - } - } -} - -# Admin -resource "aws_iam_role" "admin" { - count = var.create_admin_role ? 1 : 0 - - name = var.admin_role_name - path = var.admin_role_path - max_session_duration = var.max_session_duration - - force_detach_policies = var.force_detach_policies - permissions_boundary = var.admin_role_permissions_boundary_arn - assume_role_policy = data.aws_iam_policy_document.assume_role_with_saml.json - - tags = var.admin_role_tags -} - -resource "aws_iam_role_policy_attachment" "admin" { - count = var.create_admin_role ? length(var.admin_role_policy_arns) : 0 - - role = aws_iam_role.admin[0].name - policy_arn = element(var.admin_role_policy_arns, count.index) -} - -# Poweruser -resource "aws_iam_role" "poweruser" { - count = var.create_poweruser_role ? 1 : 0 - - name = var.poweruser_role_name - path = var.poweruser_role_path - max_session_duration = var.max_session_duration - - force_detach_policies = var.force_detach_policies - permissions_boundary = var.poweruser_role_permissions_boundary_arn - assume_role_policy = data.aws_iam_policy_document.assume_role_with_saml.json - - tags = var.poweruser_role_tags -} - -resource "aws_iam_role_policy_attachment" "poweruser" { - count = var.create_poweruser_role ? length(var.poweruser_role_policy_arns) : 0 - - role = aws_iam_role.poweruser[0].name - policy_arn = element(var.poweruser_role_policy_arns, count.index) -} - -# Readonly -resource "aws_iam_role" "readonly" { - count = var.create_readonly_role ? 1 : 0 - - name = var.readonly_role_name - path = var.readonly_role_path - max_session_duration = var.max_session_duration - - force_detach_policies = var.force_detach_policies - permissions_boundary = var.readonly_role_permissions_boundary_arn - assume_role_policy = data.aws_iam_policy_document.assume_role_with_saml.json - - tags = var.readonly_role_tags -} - -resource "aws_iam_role_policy_attachment" "readonly" { - count = var.create_readonly_role ? length(var.readonly_role_policy_arns) : 0 - - role = aws_iam_role.readonly[0].name - policy_arn = element(var.readonly_role_policy_arns, count.index) -} diff --git a/modules/iam-assumable-roles-with-saml/outputs.tf b/modules/iam-assumable-roles-with-saml/outputs.tf deleted file mode 100644 index 3b1c4d1a..00000000 --- a/modules/iam-assumable-roles-with-saml/outputs.tf +++ /dev/null @@ -1,61 +0,0 @@ -#Admin -output "admin_iam_role_arn" { - description = "ARN of admin IAM role" - value = try(aws_iam_role.admin[0].arn, "") -} - -output "admin_iam_role_name" { - description = "Name of admin IAM role" - value = try(aws_iam_role.admin[0].name, "") -} - -output "admin_iam_role_path" { - description = "Path of admin IAM role" - value = try(aws_iam_role.admin[0].path, "") -} - -output "admin_iam_role_unique_id" { - description = "Unique ID of IAM role" - value = try(aws_iam_role.admin[0].unique_id, "") -} - -output "poweruser_iam_role_arn" { - description = "ARN of poweruser IAM role" - value = try(aws_iam_role.poweruser[0].arn, "") -} - -output "poweruser_iam_role_name" { - description = "Name of poweruser IAM role" - value = try(aws_iam_role.poweruser[0].name, "") -} - -output "poweruser_iam_role_path" { - description = "Path of poweruser IAM role" - value = try(aws_iam_role.poweruser[0].path, "") -} - -output "poweruser_iam_role_unique_id" { - description = "Unique ID of IAM role" - value = try(aws_iam_role.poweruser[0].unique_id, "") -} - -# Readonly -output "readonly_iam_role_arn" { - description = "ARN of readonly IAM role" - value = try(aws_iam_role.readonly[0].arn, "") -} - -output "readonly_iam_role_name" { - description = "Name of readonly IAM role" - value = try(aws_iam_role.readonly[0].name, "") -} - -output "readonly_iam_role_path" { - description = "Path of readonly IAM role" - value = try(aws_iam_role.readonly[0].path, "") -} - -output "readonly_iam_role_unique_id" { - description = "Unique ID of IAM role" - value = try(aws_iam_role.readonly[0].unique_id, "") -} diff --git a/modules/iam-assumable-roles-with-saml/variables.tf b/modules/iam-assumable-roles-with-saml/variables.tf deleted file mode 100644 index 520af86c..00000000 --- a/modules/iam-assumable-roles-with-saml/variables.tf +++ /dev/null @@ -1,152 +0,0 @@ -variable "provider_id" { - description = "ID of the SAML Provider. Use provider_ids to specify several IDs." - type = string - default = "" -} - -variable "provider_ids" { - description = "List of SAML Provider IDs" - type = list(string) - default = [] -} - -variable "aws_saml_endpoint" { - description = "AWS SAML Endpoint" - default = "https://signin.aws.amazon.com/saml" - type = string -} - -variable "allow_self_assume_role" { - description = "Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/)" - type = bool - default = false -} - -variable "trusted_role_actions" { - description = "Additional role actions" - type = list(string) - default = ["sts:AssumeRoleWithSAML", "sts:TagSession"] -} - -# Admin -variable "create_admin_role" { - description = "Whether to create admin role" - type = bool - default = false -} - -variable "admin_role_name" { - description = "IAM role with admin access" - type = string - default = "admin" -} - -variable "admin_role_path" { - description = "Path of admin IAM role" - type = string - default = "/" -} - -variable "admin_role_policy_arns" { - description = "List of policy ARNs to use for admin role" - type = list(string) - default = ["arn:aws:iam::aws:policy/AdministratorAccess"] -} - -variable "admin_role_permissions_boundary_arn" { - description = "Permissions boundary ARN to use for admin role" - type = string - default = "" -} - -variable "admin_role_tags" { - description = "A map of tags to add to admin role resource." - type = map(string) - default = {} -} - -# Poweruser -variable "create_poweruser_role" { - description = "Whether to create poweruser role" - type = bool - default = false -} - -variable "poweruser_role_name" { - description = "IAM role with poweruser access" - type = string - default = "poweruser" -} - -variable "poweruser_role_path" { - description = "Path of poweruser IAM role" - type = string - default = "/" -} - -variable "poweruser_role_policy_arns" { - description = "List of policy ARNs to use for poweruser role" - type = list(string) - default = ["arn:aws:iam::aws:policy/PowerUserAccess"] -} - -variable "poweruser_role_permissions_boundary_arn" { - description = "Permissions boundary ARN to use for poweruser role" - type = string - default = "" -} - -variable "poweruser_role_tags" { - description = "A map of tags to add to poweruser role resource." - type = map(string) - default = {} -} - -# Readonly -variable "create_readonly_role" { - description = "Whether to create readonly role" - type = bool - default = false -} - -variable "readonly_role_name" { - description = "IAM role with readonly access" - type = string - default = "readonly" -} - -variable "readonly_role_path" { - description = "Path of readonly IAM role" - type = string - default = "/" -} - -variable "readonly_role_policy_arns" { - description = "List of policy ARNs to use for readonly role" - type = list(string) - default = ["arn:aws:iam::aws:policy/ReadOnlyAccess"] -} - -variable "readonly_role_permissions_boundary_arn" { - description = "Permissions boundary ARN to use for readonly role" - type = string - default = "" -} - -variable "readonly_role_tags" { - description = "A map of tags to add to readonly role resource." - type = map(string) - default = {} -} - -variable "max_session_duration" { - description = "Maximum CLI/API session duration in seconds between 3600 and 43200" - type = number - default = 3600 -} - -variable "force_detach_policies" { - description = "Whether policies should be detached from this role when destroying" - type = bool - default = false -} diff --git a/modules/iam-assumable-roles/README.md b/modules/iam-assumable-roles/README.md deleted file mode 100644 index 3780f916..00000000 --- a/modules/iam-assumable-roles/README.md +++ /dev/null @@ -1,93 +0,0 @@ -# iam-assumable-roles - -Creates predefined IAM roles (admin, poweruser and readonly) which can be assumed by trusted resources. - -Trusted resources can be any [IAM ARNs](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html#identifiers-arns) - typically, AWS accounts and users. - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 4.0 | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [aws_iam_role.admin](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | -| [aws_iam_role.poweruser](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | -| [aws_iam_role.readonly](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | -| [aws_iam_role_policy_attachment.admin](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.poweruser](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.readonly](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | -| [aws_iam_policy_document.assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_iam_policy_document.assume_role_with_mfa](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [admin\_role\_name](#input\_admin\_role\_name) | IAM role with admin access | `string` | `"admin"` | no | -| [admin\_role\_path](#input\_admin\_role\_path) | Path of admin IAM role | `string` | `"/"` | no | -| [admin\_role\_permissions\_boundary\_arn](#input\_admin\_role\_permissions\_boundary\_arn) | Permissions boundary ARN to use for admin role | `string` | `""` | no | -| [admin\_role\_policy\_arns](#input\_admin\_role\_policy\_arns) | List of policy ARNs to use for admin role | `list(string)` |
[
"arn:aws:iam::aws:policy/AdministratorAccess"
]
| no | -| [admin\_role\_requires\_mfa](#input\_admin\_role\_requires\_mfa) | Whether admin role requires MFA | `bool` | `true` | no | -| [admin\_role\_tags](#input\_admin\_role\_tags) | A map of tags to add to admin role resource. | `map(string)` | `{}` | no | -| [allow\_self\_assume\_role](#input\_allow\_self\_assume\_role) | Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/) | `bool` | `false` | no | -| [create\_admin\_role](#input\_create\_admin\_role) | Whether to create admin role | `bool` | `false` | no | -| [create\_poweruser\_role](#input\_create\_poweruser\_role) | Whether to create poweruser role | `bool` | `false` | no | -| [create\_readonly\_role](#input\_create\_readonly\_role) | Whether to create readonly role | `bool` | `false` | no | -| [force\_detach\_policies](#input\_force\_detach\_policies) | Whether policies should be detached from this role when destroying | `bool` | `false` | no | -| [max\_session\_duration](#input\_max\_session\_duration) | Maximum CLI/API session duration in seconds between 3600 and 43200 | `number` | `3600` | no | -| [mfa\_age](#input\_mfa\_age) | Max age of valid MFA (in seconds) for roles which require MFA | `number` | `86400` | no | -| [poweruser\_role\_name](#input\_poweruser\_role\_name) | IAM role with poweruser access | `string` | `"poweruser"` | no | -| [poweruser\_role\_path](#input\_poweruser\_role\_path) | Path of poweruser IAM role | `string` | `"/"` | no | -| [poweruser\_role\_permissions\_boundary\_arn](#input\_poweruser\_role\_permissions\_boundary\_arn) | Permissions boundary ARN to use for poweruser role | `string` | `""` | no | -| [poweruser\_role\_policy\_arns](#input\_poweruser\_role\_policy\_arns) | List of policy ARNs to use for poweruser role | `list(string)` |
[
"arn:aws:iam::aws:policy/PowerUserAccess"
]
| no | -| [poweruser\_role\_requires\_mfa](#input\_poweruser\_role\_requires\_mfa) | Whether poweruser role requires MFA | `bool` | `true` | no | -| [poweruser\_role\_tags](#input\_poweruser\_role\_tags) | A map of tags to add to poweruser role resource. | `map(string)` | `{}` | no | -| [readonly\_role\_name](#input\_readonly\_role\_name) | IAM role with readonly access | `string` | `"readonly"` | no | -| [readonly\_role\_path](#input\_readonly\_role\_path) | Path of readonly IAM role | `string` | `"/"` | no | -| [readonly\_role\_permissions\_boundary\_arn](#input\_readonly\_role\_permissions\_boundary\_arn) | Permissions boundary ARN to use for readonly role | `string` | `""` | no | -| [readonly\_role\_policy\_arns](#input\_readonly\_role\_policy\_arns) | List of policy ARNs to use for readonly role | `list(string)` |
[
"arn:aws:iam::aws:policy/ReadOnlyAccess"
]
| no | -| [readonly\_role\_requires\_mfa](#input\_readonly\_role\_requires\_mfa) | Whether readonly role requires MFA | `bool` | `true` | no | -| [readonly\_role\_tags](#input\_readonly\_role\_tags) | A map of tags to add to readonly role resource. | `map(string)` | `{}` | no | -| [trust\_policy\_conditions](#input\_trust\_policy\_conditions) | [Condition constraints](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#condition) applied to the trust policy |
list(object({
test = string
variable = string
values = list(string)
}))
| `[]` | no | -| [trusted\_role\_actions](#input\_trusted\_role\_actions) | Additional trusted role actions | `list(string)` |
[
"sts:AssumeRole",
"sts:TagSession"
]
| no | -| [trusted\_role\_arns](#input\_trusted\_role\_arns) | ARNs of AWS entities who can assume these roles | `list(string)` | `[]` | no | -| [trusted\_role\_services](#input\_trusted\_role\_services) | AWS Services that can assume these roles | `list(string)` | `[]` | no | - -## Outputs - -| Name | Description | -|------|-------------| -| [admin\_iam\_role\_arn](#output\_admin\_iam\_role\_arn) | ARN of admin IAM role | -| [admin\_iam\_role\_name](#output\_admin\_iam\_role\_name) | Name of admin IAM role | -| [admin\_iam\_role\_path](#output\_admin\_iam\_role\_path) | Path of admin IAM role | -| [admin\_iam\_role\_requires\_mfa](#output\_admin\_iam\_role\_requires\_mfa) | Whether admin IAM role requires MFA | -| [admin\_iam\_role\_unique\_id](#output\_admin\_iam\_role\_unique\_id) | Unique ID of IAM role | -| [poweruser\_iam\_role\_arn](#output\_poweruser\_iam\_role\_arn) | ARN of poweruser IAM role | -| [poweruser\_iam\_role\_name](#output\_poweruser\_iam\_role\_name) | Name of poweruser IAM role | -| [poweruser\_iam\_role\_path](#output\_poweruser\_iam\_role\_path) | Path of poweruser IAM role | -| [poweruser\_iam\_role\_requires\_mfa](#output\_poweruser\_iam\_role\_requires\_mfa) | Whether poweruser IAM role requires MFA | -| [poweruser\_iam\_role\_unique\_id](#output\_poweruser\_iam\_role\_unique\_id) | Unique ID of IAM role | -| [readonly\_iam\_role\_arn](#output\_readonly\_iam\_role\_arn) | ARN of readonly IAM role | -| [readonly\_iam\_role\_name](#output\_readonly\_iam\_role\_name) | Name of readonly IAM role | -| [readonly\_iam\_role\_path](#output\_readonly\_iam\_role\_path) | Path of readonly IAM role | -| [readonly\_iam\_role\_requires\_mfa](#output\_readonly\_iam\_role\_requires\_mfa) | Whether readonly IAM role requires MFA | -| [readonly\_iam\_role\_unique\_id](#output\_readonly\_iam\_role\_unique\_id) | Unique ID of IAM role | - diff --git a/modules/iam-assumable-roles/main.tf b/modules/iam-assumable-roles/main.tf deleted file mode 100644 index 73de0dde..00000000 --- a/modules/iam-assumable-roles/main.tf +++ /dev/null @@ -1,274 +0,0 @@ -data "aws_caller_identity" "current" {} -data "aws_partition" "current" {} - -locals { - account_id = data.aws_caller_identity.current.account_id - partition = data.aws_partition.current.partition -} - -data "aws_iam_policy_document" "assume_role" { - dynamic "statement" { - # https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/ - for_each = var.allow_self_assume_role && var.create_admin_role ? [1] : [] - - content { - sid = "ExplicitSelfAdminRoleAssumption" - effect = "Allow" - actions = ["sts:AssumeRole"] - - principals { - type = "AWS" - identifiers = ["*"] - } - - condition { - test = "ArnLike" - variable = "aws:PrincipalArn" - values = ["arn:${local.partition}:iam::${local.account_id}:role${var.admin_role_path}${var.admin_role_name}"] - } - } - } - - dynamic "statement" { - # https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/ - for_each = var.allow_self_assume_role && var.create_poweruser_role ? [1] : [] - - content { - sid = "ExplicitSelfPowerUserRoleAssumption" - effect = "Allow" - actions = ["sts:AssumeRole"] - - principals { - type = "AWS" - identifiers = ["*"] - } - - condition { - test = "ArnLike" - variable = "aws:PrincipalArn" - values = ["arn:${local.partition}:iam::${local.account_id}:role${var.poweruser_role_path}${var.poweruser_role_name}"] - } - } - } - - dynamic "statement" { - # https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/ - for_each = var.allow_self_assume_role && var.create_readonly_role ? [1] : [] - - content { - sid = "ExplicitSelfReadOnlyRoleAssumption" - effect = "Allow" - actions = ["sts:AssumeRole"] - - principals { - type = "AWS" - identifiers = ["*"] - } - - condition { - test = "ArnLike" - variable = "aws:PrincipalArn" - values = ["arn:${local.partition}:iam::${local.account_id}:role${var.readonly_role_path}${var.readonly_role_name}"] - } - } - } - - statement { - effect = "Allow" - actions = compact(distinct(concat(["sts:AssumeRole"], var.trusted_role_actions))) - - principals { - type = "AWS" - identifiers = var.trusted_role_arns - } - - principals { - type = "Service" - identifiers = var.trusted_role_services - } - - dynamic "condition" { - for_each = var.trust_policy_conditions - - content { - test = condition.value.test - variable = condition.value.variable - values = condition.value.values - } - } - } -} - -data "aws_iam_policy_document" "assume_role_with_mfa" { - dynamic "statement" { - # https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/ - for_each = var.allow_self_assume_role && var.create_admin_role ? [1] : [] - - content { - sid = "ExplicitSelfAdminRoleAssumption" - effect = "Allow" - actions = ["sts:AssumeRole"] - - principals { - type = "AWS" - identifiers = ["*"] - } - - condition { - test = "ArnLike" - variable = "aws:PrincipalArn" - values = ["arn:${local.partition}:iam::${local.account_id}:role${var.admin_role_path}${var.admin_role_name}"] - } - } - } - - dynamic "statement" { - # https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/ - for_each = var.allow_self_assume_role && var.create_poweruser_role ? [1] : [] - - content { - sid = "ExplicitSelfPowerUserRoleAssumption" - effect = "Allow" - actions = ["sts:AssumeRole"] - - principals { - type = "AWS" - identifiers = ["*"] - } - - condition { - test = "ArnLike" - variable = "aws:PrincipalArn" - values = ["arn:${local.partition}:iam::${local.account_id}:role${var.poweruser_role_path}${var.poweruser_role_name}"] - } - } - } - - dynamic "statement" { - # https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/ - for_each = var.allow_self_assume_role && var.create_readonly_role ? [1] : [] - - content { - sid = "ExplicitSelfReadOnlyRoleAssumption" - effect = "Allow" - actions = ["sts:AssumeRole"] - - principals { - type = "AWS" - identifiers = ["*"] - } - - condition { - test = "ArnLike" - variable = "aws:PrincipalArn" - values = ["arn:${local.partition}:iam::${local.account_id}:role${var.readonly_role_path}${var.readonly_role_name}"] - } - } - } - - statement { - effect = "Allow" - actions = compact(distinct(concat(["sts:AssumeRole"], var.trusted_role_actions))) - - principals { - type = "AWS" - identifiers = var.trusted_role_arns - } - - principals { - type = "Service" - identifiers = var.trusted_role_services - } - - condition { - test = "Bool" - variable = "aws:MultiFactorAuthPresent" - values = ["true"] - } - - condition { - test = "NumericLessThan" - variable = "aws:MultiFactorAuthAge" - values = [var.mfa_age] - } - - dynamic "condition" { - for_each = var.trust_policy_conditions - - content { - test = condition.value.test - variable = condition.value.variable - values = condition.value.values - } - } - } -} - -# Admin -resource "aws_iam_role" "admin" { - count = var.create_admin_role ? 1 : 0 - - name = var.admin_role_name - path = var.admin_role_path - max_session_duration = var.max_session_duration - - force_detach_policies = var.force_detach_policies - permissions_boundary = var.admin_role_permissions_boundary_arn - - assume_role_policy = var.admin_role_requires_mfa ? data.aws_iam_policy_document.assume_role_with_mfa.json : data.aws_iam_policy_document.assume_role.json - - tags = var.admin_role_tags -} - -resource "aws_iam_role_policy_attachment" "admin" { - count = var.create_admin_role ? length(var.admin_role_policy_arns) : 0 - - role = aws_iam_role.admin[0].name - policy_arn = element(var.admin_role_policy_arns, count.index) -} - -# Poweruser -resource "aws_iam_role" "poweruser" { - count = var.create_poweruser_role ? 1 : 0 - - name = var.poweruser_role_name - path = var.poweruser_role_path - max_session_duration = var.max_session_duration - - force_detach_policies = var.force_detach_policies - permissions_boundary = var.poweruser_role_permissions_boundary_arn - - assume_role_policy = var.poweruser_role_requires_mfa ? data.aws_iam_policy_document.assume_role_with_mfa.json : data.aws_iam_policy_document.assume_role.json - - tags = var.poweruser_role_tags -} - -resource "aws_iam_role_policy_attachment" "poweruser" { - count = var.create_poweruser_role ? length(var.poweruser_role_policy_arns) : 0 - - role = aws_iam_role.poweruser[0].name - policy_arn = element(var.poweruser_role_policy_arns, count.index) -} - -# Readonly -resource "aws_iam_role" "readonly" { - count = var.create_readonly_role ? 1 : 0 - - name = var.readonly_role_name - path = var.readonly_role_path - max_session_duration = var.max_session_duration - - force_detach_policies = var.force_detach_policies - permissions_boundary = var.readonly_role_permissions_boundary_arn - - assume_role_policy = var.readonly_role_requires_mfa ? data.aws_iam_policy_document.assume_role_with_mfa.json : data.aws_iam_policy_document.assume_role.json - - tags = var.readonly_role_tags -} - -resource "aws_iam_role_policy_attachment" "readonly" { - count = var.create_readonly_role ? length(var.readonly_role_policy_arns) : 0 - - role = aws_iam_role.readonly[0].name - policy_arn = element(var.readonly_role_policy_arns, count.index) -} diff --git a/modules/iam-assumable-roles/outputs.tf b/modules/iam-assumable-roles/outputs.tf deleted file mode 100644 index 5918ba46..00000000 --- a/modules/iam-assumable-roles/outputs.tf +++ /dev/null @@ -1,77 +0,0 @@ -#Admin -output "admin_iam_role_arn" { - description = "ARN of admin IAM role" - value = try(aws_iam_role.admin[0].arn, "") -} - -output "admin_iam_role_name" { - description = "Name of admin IAM role" - value = try(aws_iam_role.admin[0].name, "") -} - -output "admin_iam_role_path" { - description = "Path of admin IAM role" - value = try(aws_iam_role.admin[0].path, "") -} - -output "admin_iam_role_unique_id" { - description = "Unique ID of IAM role" - value = try(aws_iam_role.admin[0].unique_id, "") -} - -output "admin_iam_role_requires_mfa" { - description = "Whether admin IAM role requires MFA" - value = var.admin_role_requires_mfa -} - -# Poweruser -output "poweruser_iam_role_arn" { - description = "ARN of poweruser IAM role" - value = try(aws_iam_role.poweruser[0].arn, "") -} - -output "poweruser_iam_role_name" { - description = "Name of poweruser IAM role" - value = try(aws_iam_role.poweruser[0].name, "") -} - -output "poweruser_iam_role_path" { - description = "Path of poweruser IAM role" - value = try(aws_iam_role.poweruser[0].path, "") -} - -output "poweruser_iam_role_unique_id" { - description = "Unique ID of IAM role" - value = try(aws_iam_role.poweruser[0].unique_id, "") -} - -output "poweruser_iam_role_requires_mfa" { - description = "Whether poweruser IAM role requires MFA" - value = var.poweruser_role_requires_mfa -} - -# Readonly -output "readonly_iam_role_arn" { - description = "ARN of readonly IAM role" - value = try(aws_iam_role.readonly[0].arn, "") -} - -output "readonly_iam_role_name" { - description = "Name of readonly IAM role" - value = try(aws_iam_role.readonly[0].name, "") -} - -output "readonly_iam_role_path" { - description = "Path of readonly IAM role" - value = try(aws_iam_role.readonly[0].path, "") -} - -output "readonly_iam_role_unique_id" { - description = "Unique ID of IAM role" - value = try(aws_iam_role.readonly[0].unique_id, "") -} - -output "readonly_iam_role_requires_mfa" { - description = "Whether readonly IAM role requires MFA" - value = var.readonly_role_requires_mfa -} diff --git a/modules/iam-assumable-roles/variables.tf b/modules/iam-assumable-roles/variables.tf deleted file mode 100644 index 7f797550..00000000 --- a/modules/iam-assumable-roles/variables.tf +++ /dev/null @@ -1,180 +0,0 @@ -variable "trusted_role_actions" { - description = "Additional trusted role actions" - type = list(string) - default = ["sts:AssumeRole", "sts:TagSession"] -} - -variable "trusted_role_arns" { - description = "ARNs of AWS entities who can assume these roles" - type = list(string) - default = [] -} - -variable "trusted_role_services" { - description = "AWS Services that can assume these roles" - type = list(string) - default = [] -} - -variable "trust_policy_conditions" { - description = "[Condition constraints](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#condition) applied to the trust policy" - type = list(object({ - test = string - variable = string - values = list(string) - })) - default = [] -} - -variable "mfa_age" { - description = "Max age of valid MFA (in seconds) for roles which require MFA" - type = number - default = 86400 -} - -variable "allow_self_assume_role" { - description = "Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/)" - type = bool - default = false -} - -# Admin -variable "create_admin_role" { - description = "Whether to create admin role" - type = bool - default = false -} - -variable "admin_role_name" { - description = "IAM role with admin access" - type = string - default = "admin" -} - -variable "admin_role_path" { - description = "Path of admin IAM role" - type = string - default = "/" -} - -variable "admin_role_requires_mfa" { - description = "Whether admin role requires MFA" - type = bool - default = true -} - -variable "admin_role_policy_arns" { - description = "List of policy ARNs to use for admin role" - type = list(string) - default = ["arn:aws:iam::aws:policy/AdministratorAccess"] -} - -variable "admin_role_permissions_boundary_arn" { - description = "Permissions boundary ARN to use for admin role" - type = string - default = "" -} - -variable "admin_role_tags" { - description = "A map of tags to add to admin role resource." - type = map(string) - default = {} -} - -# Poweruser -variable "create_poweruser_role" { - description = "Whether to create poweruser role" - type = bool - default = false -} - -variable "poweruser_role_name" { - description = "IAM role with poweruser access" - type = string - default = "poweruser" -} - -variable "poweruser_role_path" { - description = "Path of poweruser IAM role" - type = string - default = "/" -} - -variable "poweruser_role_requires_mfa" { - description = "Whether poweruser role requires MFA" - type = bool - default = true -} - -variable "poweruser_role_policy_arns" { - description = "List of policy ARNs to use for poweruser role" - type = list(string) - default = ["arn:aws:iam::aws:policy/PowerUserAccess"] -} - -variable "poweruser_role_permissions_boundary_arn" { - description = "Permissions boundary ARN to use for poweruser role" - type = string - default = "" -} - -variable "poweruser_role_tags" { - description = "A map of tags to add to poweruser role resource." - type = map(string) - default = {} -} - -# Readonly -variable "create_readonly_role" { - description = "Whether to create readonly role" - type = bool - default = false -} - -variable "readonly_role_name" { - description = "IAM role with readonly access" - type = string - default = "readonly" -} - -variable "readonly_role_path" { - description = "Path of readonly IAM role" - type = string - default = "/" -} - -variable "readonly_role_requires_mfa" { - description = "Whether readonly role requires MFA" - type = bool - default = true -} - -variable "readonly_role_policy_arns" { - description = "List of policy ARNs to use for readonly role" - type = list(string) - default = ["arn:aws:iam::aws:policy/ReadOnlyAccess"] -} - -variable "readonly_role_permissions_boundary_arn" { - description = "Permissions boundary ARN to use for readonly role" - type = string - default = "" -} - -variable "readonly_role_tags" { - description = "A map of tags to add to readonly role resource." - type = map(string) - default = {} -} - -variable "max_session_duration" { - description = "Maximum CLI/API session duration in seconds between 3600 and 43200" - type = number - default = 3600 -} - -variable "force_detach_policies" { - description = "Whether policies should be detached from this role when destroying" - type = bool - default = false -} diff --git a/modules/iam-github-oidc-role/README.md b/modules/iam-github-oidc-role/README.md deleted file mode 100644 index 79120723..00000000 --- a/modules/iam-github-oidc-role/README.md +++ /dev/null @@ -1,109 +0,0 @@ -# IAM GitHub OIDC Role - -Creates an IAM role that trust the IAM GitHub OIDC provider. -- GitHub reference: https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services -- AWS IAM role reference: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-idp_oidc.html#idp_oidc_Create_GitHub - -## Usage - -### [GitHub Free, Pro, & Team](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services) - -The defaults provided by the module are suitable for GitHub Free, Pro, & Team, including use with the official [AWS GitHub action](https://github.com/aws-actions/configure-aws-credentials). - -```hcl -module "iam_github_oidc_role" { - source = "terraform-aws-modules/iam/aws//modules/iam-github-oidc-role" - - # This should be updated to suit your organization, repository, references/branches, etc. - subjects = ["terraform-aws-modules/terraform-aws-iam:*"] - - policies = { - S3ReadOnly = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" - } - - tags = { - Environment = "test" - } -} -``` - -### [GitHub Enterprise Server](https://docs.github.com/en/enterprise-server@3.7/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services) - -For GitHub Enterprise Server, users will need to provide value for the `audience` and `provider_url` to suit their `` installation: - -```hcl -module "iam_github_oidc_role" { - source = "terraform-aws-modules/iam/aws//modules/iam-github-oidc-role" - - audience = "https://mygithub.com/" - provider_url = "mygithub.com/_services/token" - - # This should be updated to suit your organization, repository, references/branches, etc. - subjects = ["/terraform-aws-iam:*"] - - policies = { - S3ReadOnly = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" - } - - tags = { - Environment = "test" - } -} -``` - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 4.0 | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | -| [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | -| [aws_iam_policy_document.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [additional\_trust\_policy\_conditions](#input\_additional\_trust\_policy\_conditions) | Additional conditions for the constraint to apply to the trust policy |
list(object({
test = string
variable = string
values = list(string)
}))
| `[]` | no | -| [audience](#input\_audience) | Audience to use for OIDC role. Defaults to `sts.amazonaws.com` for use with the [official AWS GitHub action](https://github.com/aws-actions/configure-aws-credentials) | `string` | `"sts.amazonaws.com"` | no | -| [create](#input\_create) | Controls if resources should be created (affects all resources) | `bool` | `true` | no | -| [description](#input\_description) | IAM Role description | `string` | `null` | no | -| [force\_detach\_policies](#input\_force\_detach\_policies) | Whether policies should be detached from this role when destroying | `bool` | `true` | no | -| [max\_session\_duration](#input\_max\_session\_duration) | Maximum CLI/API session duration in seconds between 3600 and 43200 | `number` | `null` | no | -| [name](#input\_name) | Name of IAM role | `string` | `null` | no | -| [name\_prefix](#input\_name\_prefix) | IAM role name prefix | `string` | `null` | no | -| [path](#input\_path) | Path of IAM role | `string` | `"/"` | no | -| [permissions\_boundary\_arn](#input\_permissions\_boundary\_arn) | Permissions boundary ARN to use for IAM role | `string` | `null` | no | -| [policies](#input\_policies) | Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no | -| [provider\_url](#input\_provider\_url) | The URL of the identity provider. Corresponds to the iss claim | `string` | `"token.actions.githubusercontent.com"` | no | -| [subject\_condition](#input\_subject\_condition) | Condition to use for the GitHub OIDC role. Defaults to `StringLike` | `string` | `"StringLike"` | no | -| [subjects](#input\_subjects) | List of GitHub OIDC subjects that are permitted by the trust policy. You do not need to prefix with `repo:` as this is provided. Example: `['my-org/my-repo:*', 'octo-org/octo-repo:ref:refs/heads/octo-branch']` | `list(string)` | `[]` | no | -| [tags](#input\_tags) | A map of tags to add to the resources created | `map(any)` | `{}` | no | - -## Outputs - -| Name | Description | -|------|-------------| -| [arn](#output\_arn) | ARN of IAM role | -| [name](#output\_name) | Name of IAM role | -| [path](#output\_path) | Path of IAM role | -| [unique\_id](#output\_unique\_id) | Unique ID of IAM role | - diff --git a/modules/iam-github-oidc-role/main.tf b/modules/iam-github-oidc-role/main.tf deleted file mode 100644 index 898f4696..00000000 --- a/modules/iam-github-oidc-role/main.tf +++ /dev/null @@ -1,84 +0,0 @@ -data "aws_partition" "current" {} -data "aws_caller_identity" "current" {} - -locals { - # Just extra safety incase someone passes in a url with `https://` - provider_url = replace(var.provider_url, "https://", "") - - account_id = data.aws_caller_identity.current.account_id - partition = data.aws_partition.current.partition -} - -################################################################################ -# GitHub OIDC Role -################################################################################ - -data "aws_iam_policy_document" "this" { - count = var.create ? 1 : 0 - - statement { - sid = "GithubOidcAuth" - effect = "Allow" - actions = [ - "sts:TagSession", - "sts:AssumeRoleWithWebIdentity" - ] - - principals { - type = "Federated" - identifiers = ["arn:${local.partition}:iam::${local.account_id}:oidc-provider/${local.provider_url}"] - } - - condition { - test = "ForAllValues:StringEquals" - variable = "${local.provider_url}:iss" - values = ["https://${local.provider_url}"] - } - - condition { - test = "ForAllValues:StringEquals" - variable = "${local.provider_url}:aud" - values = [var.audience] - } - - condition { - test = var.subject_condition - variable = "${local.provider_url}:sub" - # Strip `repo:` to normalize for cases where users may prepend it - values = [for subject in var.subjects : "repo:${trimprefix(subject, "repo:")}"] - } - - dynamic "condition" { - for_each = var.additional_trust_policy_conditions - - content { - test = condition.value.test - values = condition.value.values - variable = condition.value.variable - } - } - } -} - -resource "aws_iam_role" "this" { - count = var.create ? 1 : 0 - - name = var.name - name_prefix = var.name_prefix - path = var.path - description = var.description - - assume_role_policy = data.aws_iam_policy_document.this[0].json - max_session_duration = var.max_session_duration - permissions_boundary = var.permissions_boundary_arn - force_detach_policies = var.force_detach_policies - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "this" { - for_each = { for k, v in var.policies : k => v if var.create } - - role = aws_iam_role.this[0].name - policy_arn = each.value -} diff --git a/modules/iam-github-oidc-role/variables.tf b/modules/iam-github-oidc-role/variables.tf deleted file mode 100644 index 5cfe2d2e..00000000 --- a/modules/iam-github-oidc-role/variables.tf +++ /dev/null @@ -1,97 +0,0 @@ -variable "create" { - description = "Controls if resources should be created (affects all resources)" - type = bool - default = true -} - -variable "tags" { - description = "A map of tags to add to the resources created" - type = map(any) - default = {} -} - -################################################################################ -# GitHub OIDC Role -################################################################################ - -variable "name" { - description = "Name of IAM role" - type = string - default = null -} - -variable "path" { - description = "Path of IAM role" - type = string - default = "/" -} - -variable "permissions_boundary_arn" { - description = "Permissions boundary ARN to use for IAM role" - type = string - default = null -} - -variable "description" { - description = "IAM Role description" - type = string - default = null -} - -variable "name_prefix" { - description = "IAM role name prefix" - type = string - default = null -} - -variable "policies" { - description = "Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format" - type = map(string) - default = {} -} - -variable "force_detach_policies" { - description = "Whether policies should be detached from this role when destroying" - type = bool - default = true -} - -variable "max_session_duration" { - description = "Maximum CLI/API session duration in seconds between 3600 and 43200" - type = number - default = null -} - -variable "audience" { - description = "Audience to use for OIDC role. Defaults to `sts.amazonaws.com` for use with the [official AWS GitHub action](https://github.com/aws-actions/configure-aws-credentials)" - type = string - default = "sts.amazonaws.com" -} - -variable "subject_condition" { - description = "Condition to use for the GitHub OIDC role. Defaults to `StringLike`" - type = string - default = "StringLike" -} - -variable "subjects" { - description = "List of GitHub OIDC subjects that are permitted by the trust policy. You do not need to prefix with `repo:` as this is provided. Example: `['my-org/my-repo:*', 'octo-org/octo-repo:ref:refs/heads/octo-branch']`" - type = list(string) - default = [] -} - -variable "provider_url" { - description = "The URL of the identity provider. Corresponds to the iss claim" - type = string - default = "token.actions.githubusercontent.com" -} - -variable "additional_trust_policy_conditions" { - description = "Additional conditions for the constraint to apply to the trust policy" - type = list(object({ - test = string - variable = string - values = list(string) - })) - default = [] -} diff --git a/modules/iam-group-with-assumable-roles-policy/README.md b/modules/iam-group-with-assumable-roles-policy/README.md deleted file mode 100644 index a9b0db90..00000000 --- a/modules/iam-group-with-assumable-roles-policy/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# iam-group-with-assumable-roles-policy - -Creates IAM group with users who are allowed to assume IAM roles. This is typically done in resource AWS account where IAM users can jump into from IAM AWS account. - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 4.0 | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [aws_iam_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group) | resource | -| [aws_iam_group_membership.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group_membership) | resource | -| [aws_iam_group_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group_policy_attachment) | resource | -| [aws_iam_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy_document.assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [assumable\_roles](#input\_assumable\_roles) | List of IAM roles ARNs which can be assumed by the group | `list(string)` | `[]` | no | -| [assumable\_roles\_policy\_name\_suffix](#input\_assumable\_roles\_policy\_name\_suffix) | Append this name to the policy name that will be created for assuming the given roles (default: null -- the policy name will be group name) | `string` | `""` | no | -| [group\_users](#input\_group\_users) | List of IAM users to have in an IAM group which can assume the role | `list(string)` | `[]` | no | -| [name](#input\_name) | Name of IAM policy and IAM group | `string` | n/a | yes | -| [path](#input\_path) | Path of IAM policy and IAM group | `string` | `"/"` | no | -| [tags](#input\_tags) | A map of tags to add to all resources. | `map(string)` | `{}` | no | - -## Outputs - -| Name | Description | -|------|-------------| -| [assumable\_roles](#output\_assumable\_roles) | List of ARNs of IAM roles which members of IAM group can assume | -| [group\_arn](#output\_group\_arn) | IAM group arn | -| [group\_name](#output\_group\_name) | IAM group name | -| [group\_users](#output\_group\_users) | List of IAM users in IAM group | -| [policy\_arn](#output\_policy\_arn) | Assume role policy ARN of IAM group | - diff --git a/modules/iam-group-with-assumable-roles-policy/main.tf b/modules/iam-group-with-assumable-roles-policy/main.tf deleted file mode 100644 index 5dcff275..00000000 --- a/modules/iam-group-with-assumable-roles-policy/main.tf +++ /dev/null @@ -1,34 +0,0 @@ -data "aws_iam_policy_document" "assume_role" { - statement { - effect = "Allow" - actions = ["sts:AssumeRole"] - resources = var.assumable_roles - } -} - -resource "aws_iam_policy" "this" { - name = "${var.name}${var.assumable_roles_policy_name_suffix}" - path = var.path - description = "Allows to assume role in another AWS account" - policy = data.aws_iam_policy_document.assume_role.json - - tags = var.tags -} - -resource "aws_iam_group" "this" { - name = var.name - path = var.path -} - -resource "aws_iam_group_policy_attachment" "this" { - group = aws_iam_group.this.id - policy_arn = aws_iam_policy.this.id -} - -resource "aws_iam_group_membership" "this" { - count = length(var.group_users) > 0 ? 1 : 0 - - group = aws_iam_group.this.id - name = var.name - users = var.group_users -} diff --git a/modules/iam-group-with-assumable-roles-policy/outputs.tf b/modules/iam-group-with-assumable-roles-policy/outputs.tf deleted file mode 100644 index a172a664..00000000 --- a/modules/iam-group-with-assumable-roles-policy/outputs.tf +++ /dev/null @@ -1,24 +0,0 @@ -output "group_users" { - description = "List of IAM users in IAM group" - value = flatten(aws_iam_group_membership.this[*].users) -} - -output "assumable_roles" { - description = "List of ARNs of IAM roles which members of IAM group can assume" - value = var.assumable_roles -} - -output "policy_arn" { - description = "Assume role policy ARN of IAM group" - value = aws_iam_policy.this.arn -} - -output "group_name" { - description = "IAM group name" - value = aws_iam_group.this.name -} - -output "group_arn" { - description = "IAM group arn" - value = aws_iam_group.this.arn -} diff --git a/modules/iam-group-with-assumable-roles-policy/variables.tf b/modules/iam-group-with-assumable-roles-policy/variables.tf deleted file mode 100644 index b28e6337..00000000 --- a/modules/iam-group-with-assumable-roles-policy/variables.tf +++ /dev/null @@ -1,34 +0,0 @@ -variable "name" { - description = "Name of IAM policy and IAM group" - type = string -} - -variable "path" { - description = "Path of IAM policy and IAM group" - type = string - default = "/" -} - -variable "assumable_roles" { - description = "List of IAM roles ARNs which can be assumed by the group" - type = list(string) - default = [] -} - -variable "assumable_roles_policy_name_suffix" { - description = "Append this name to the policy name that will be created for assuming the given roles (default: null -- the policy name will be group name)" - type = string - default = "" -} - -variable "group_users" { - description = "List of IAM users to have in an IAM group which can assume the role" - type = list(string) - default = [] -} - -variable "tags" { - description = "A map of tags to add to all resources." - type = map(string) - default = {} -} diff --git a/modules/iam-group-with-assumable-roles-policy/versions.tf b/modules/iam-group-with-assumable-roles-policy/versions.tf deleted file mode 100644 index d8dd1a44..00000000 --- a/modules/iam-group-with-assumable-roles-policy/versions.tf +++ /dev/null @@ -1,10 +0,0 @@ -terraform { - required_version = ">= 1.0" - - required_providers { - aws = { - source = "hashicorp/aws" - version = ">= 4.0" - } - } -} diff --git a/modules/iam-group-with-policies/README.md b/modules/iam-group-with-policies/README.md deleted file mode 100644 index ea63516c..00000000 --- a/modules/iam-group-with-policies/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# iam-group-with-policies - -Creates IAM group with specified IAM policies, and add users into a group. - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 4.0 | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [aws_iam_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group) | resource | -| [aws_iam_group_membership.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group_membership) | resource | -| [aws_iam_group_policy_attachment.custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group_policy_attachment) | resource | -| [aws_iam_group_policy_attachment.custom_arns](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group_policy_attachment) | resource | -| [aws_iam_group_policy_attachment.iam_self_management](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group_policy_attachment) | resource | -| [aws_iam_policy.custom](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.iam_self_management](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | -| [aws_iam_policy_document.iam_self_management](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [attach\_iam\_self\_management\_policy](#input\_attach\_iam\_self\_management\_policy) | Whether to attach IAM policy which allows IAM users to manage their credentials and MFA | `bool` | `true` | no | -| [aws\_account\_id](#input\_aws\_account\_id) | AWS account id to use inside IAM policies. If empty, current AWS account ID will be used. | `string` | `""` | no | -| [create\_group](#input\_create\_group) | Whether to create IAM group | `bool` | `true` | no | -| [custom\_group\_policies](#input\_custom\_group\_policies) | List of maps of inline IAM policies to attach to IAM group. Should have `name` and `policy` keys in each element. | `list(map(string))` | `[]` | no | -| [custom\_group\_policy\_arns](#input\_custom\_group\_policy\_arns) | List of IAM policies ARNs to attach to IAM group | `list(string)` | `[]` | no | -| [enable\_mfa\_enforcement](#input\_enable\_mfa\_enforcement) | Determines whether permissions are added to the policy which requires the groups IAM users to use MFA | `bool` | `true` | no | -| [group\_users](#input\_group\_users) | List of IAM users to have in an IAM group which can assume the role | `list(string)` | `[]` | no | -| [iam\_self\_management\_policy\_name\_prefix](#input\_iam\_self\_management\_policy\_name\_prefix) | Name prefix for IAM policy to create with IAM self-management permissions | `string` | `"IAMSelfManagement-"` | no | -| [name](#input\_name) | Name of IAM group | `string` | `""` | no | -| [path](#input\_path) | Desired path for the IAM group | `string` | `"/"` | no | -| [tags](#input\_tags) | A map of tags to add to all resources. | `map(string)` | `{}` | no | - -## Outputs - -| Name | Description | -|------|-------------| -| [aws\_account\_id](#output\_aws\_account\_id) | IAM AWS account id | -| [group\_arn](#output\_group\_arn) | IAM group arn | -| [group\_name](#output\_group\_name) | IAM group name | -| [group\_users](#output\_group\_users) | List of IAM users in IAM group | - diff --git a/modules/iam-group-with-policies/main.tf b/modules/iam-group-with-policies/main.tf deleted file mode 100644 index ae7fe919..00000000 --- a/modules/iam-group-with-policies/main.tf +++ /dev/null @@ -1,64 +0,0 @@ -locals { - group_name = var.create_group ? aws_iam_group.this[0].id : var.name -} - -resource "aws_iam_group" "this" { - count = var.create_group ? 1 : 0 - - name = var.name - path = var.path -} - -resource "aws_iam_group_membership" "this" { - count = length(var.group_users) > 0 ? 1 : 0 - - group = local.group_name - name = var.name - users = var.group_users -} - -################################ -# IAM group policy attachments -################################ -resource "aws_iam_group_policy_attachment" "iam_self_management" { - count = var.attach_iam_self_management_policy ? 1 : 0 - - group = local.group_name - policy_arn = aws_iam_policy.iam_self_management[0].arn -} - -resource "aws_iam_group_policy_attachment" "custom_arns" { - count = length(var.custom_group_policy_arns) - - group = local.group_name - policy_arn = element(var.custom_group_policy_arns, count.index) -} - -resource "aws_iam_group_policy_attachment" "custom" { - count = length(var.custom_group_policies) - - group = local.group_name - policy_arn = element(aws_iam_policy.custom[*].arn, count.index) -} - -############### -# IAM policies -############### -resource "aws_iam_policy" "iam_self_management" { - count = var.attach_iam_self_management_policy ? 1 : 0 - - name_prefix = var.iam_self_management_policy_name_prefix - policy = data.aws_iam_policy_document.iam_self_management.json - - tags = var.tags -} - -resource "aws_iam_policy" "custom" { - count = length(var.custom_group_policies) - - name = var.custom_group_policies[count.index]["name"] - policy = var.custom_group_policies[count.index]["policy"] - description = lookup(var.custom_group_policies[count.index], "description", null) - - tags = var.tags -} diff --git a/modules/iam-group-with-policies/outputs.tf b/modules/iam-group-with-policies/outputs.tf deleted file mode 100644 index ee0835c0..00000000 --- a/modules/iam-group-with-policies/outputs.tf +++ /dev/null @@ -1,19 +0,0 @@ -output "aws_account_id" { - description = "IAM AWS account id" - value = local.aws_account_id -} - -output "group_arn" { - description = "IAM group arn" - value = try(aws_iam_group.this[0].arn, "") -} - -output "group_users" { - description = "List of IAM users in IAM group" - value = flatten(aws_iam_group_membership.this[*].users) -} - -output "group_name" { - description = "IAM group name" - value = try(aws_iam_group.this[0].name, var.name) -} diff --git a/modules/iam-group-with-policies/policies.tf b/modules/iam-group-with-policies/policies.tf deleted file mode 100644 index ca071f86..00000000 --- a/modules/iam-group-with-policies/policies.tf +++ /dev/null @@ -1,182 +0,0 @@ -data "aws_caller_identity" "current" { - count = var.aws_account_id == "" ? 1 : 0 -} - -data "aws_partition" "current" {} - -locals { - aws_account_id = try(data.aws_caller_identity.current[0].account_id, var.aws_account_id) - partition = data.aws_partition.current.partition -} - -# Allows MFA-authenticated IAM users to manage their own credentials on the My security credentials page -# https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_examples_aws_my-sec-creds-self-manage.html -data "aws_iam_policy_document" "iam_self_management" { - statement { - sid = "AllowViewAccountInfo" - - effect = "Allow" - - actions = [ - "iam:GetAccountSummary", - "iam:GetAccountPasswordPolicy", - "iam:ListAccountAliases", - "iam:ListVirtualMFADevices" - ] - - resources = ["*"] - } - - statement { - sid = "AllowManageOwnPasswords" - - effect = "Allow" - - actions = [ - "iam:ChangePassword", - "iam:GetLoginProfile", - "iam:GetUser", - "iam:UpdateLoginProfile" - ] - - resources = [ - "arn:${local.partition}:iam::${local.aws_account_id}:user/$${aws:username}", - "arn:${local.partition}:iam::${local.aws_account_id}:user/*/$${aws:username}" - ] - } - - statement { - sid = "AllowManageOwnAccessKeys" - - effect = "Allow" - - actions = [ - "iam:CreateAccessKey", - "iam:DeleteAccessKey", - "iam:ListAccessKeys", - "iam:UpdateAccessKey", - "iam:GetAccessKeyLastUsed", - "iam:TagUser", - "iam:ListUserTags", - "iam:UntagUser", - ] - - resources = [ - "arn:${local.partition}:iam::${local.aws_account_id}:user/$${aws:username}", - "arn:${local.partition}:iam::${local.aws_account_id}:user/*/$${aws:username}" - ] - } - - statement { - sid = "AllowManageOwnSigningCertificates" - - effect = "Allow" - - actions = [ - "iam:DeleteSigningCertificate", - "iam:ListSigningCertificates", - "iam:UpdateSigningCertificate", - "iam:UploadSigningCertificate" - ] - - resources = [ - "arn:${local.partition}:iam::${local.aws_account_id}:user/$${aws:username}", - "arn:${local.partition}:iam::${local.aws_account_id}:user/*/$${aws:username}" - ] - } - - statement { - sid = "AllowManageOwnSSHPublicKeys" - - effect = "Allow" - - actions = [ - "iam:DeleteSSHPublicKey", - "iam:GetSSHPublicKey", - "iam:ListSSHPublicKeys", - "iam:UpdateSSHPublicKey", - "iam:UploadSSHPublicKey" - ] - - resources = [ - "arn:${local.partition}:iam::${local.aws_account_id}:user/$${aws:username}", - "arn:${local.partition}:iam::${local.aws_account_id}:user/*/$${aws:username}" - ] - } - - statement { - sid = "AllowManageOwnGitCredentials" - - effect = "Allow" - - actions = [ - "iam:CreateServiceSpecificCredential", - "iam:DeleteServiceSpecificCredential", - "iam:ListServiceSpecificCredentials", - "iam:ResetServiceSpecificCredential", - "iam:UpdateServiceSpecificCredential" - ] - - resources = [ - "arn:${local.partition}:iam::${local.aws_account_id}:user/$${aws:username}", - "arn:${local.partition}:iam::${local.aws_account_id}:user/*/$${aws:username}" - ] - } - - statement { - sid = "AllowManageOwnVirtualMFADevice" - - effect = "Allow" - - actions = [ - "iam:CreateVirtualMFADevice" - ] - - resources = ["arn:${local.partition}:iam::${local.aws_account_id}:mfa/*"] - } - - statement { - sid = "AllowManageOwnUserMFA" - - effect = "Allow" - - actions = [ - "iam:DeactivateMFADevice", - "iam:EnableMFADevice", - "iam:ListMFADevices", - "iam:ResyncMFADevice" - ] - - resources = [ - "arn:${local.partition}:iam::${local.aws_account_id}:user/$${aws:username}", - "arn:${local.partition}:iam::${local.aws_account_id}:user/*/$${aws:username}" - ] - } - - dynamic "statement" { - for_each = var.enable_mfa_enforcement ? [1] : [] - - content { - sid = "DenyAllExceptListedIfNoMFA" - effect = "Deny" - not_actions = [ - "iam:CreateVirtualMFADevice", - "iam:EnableMFADevice", - "iam:GetUser", - "iam:GetMFADevice", - "iam:ListMFADevices", - "iam:ListVirtualMFADevices", - "iam:ResyncMFADevice", - "sts:GetSessionToken", - "iam:ChangePassword" - ] - resources = ["*"] - - condition { - test = "BoolIfExists" - variable = "aws:MultiFactorAuthPresent" - values = ["false"] - } - } - } -} diff --git a/modules/iam-group-with-policies/variables.tf b/modules/iam-group-with-policies/variables.tf deleted file mode 100644 index 59bf729d..00000000 --- a/modules/iam-group-with-policies/variables.tf +++ /dev/null @@ -1,65 +0,0 @@ -variable "create_group" { - description = "Whether to create IAM group" - type = bool - default = true -} - -variable "name" { - description = "Name of IAM group" - type = string - default = "" -} - -variable "path" { - description = "Desired path for the IAM group" - type = string - default = "/" -} - -variable "group_users" { - description = "List of IAM users to have in an IAM group which can assume the role" - type = list(string) - default = [] -} - -variable "custom_group_policy_arns" { - description = "List of IAM policies ARNs to attach to IAM group" - type = list(string) - default = [] -} - -variable "custom_group_policies" { - description = "List of maps of inline IAM policies to attach to IAM group. Should have `name` and `policy` keys in each element." - type = list(map(string)) - default = [] -} - -variable "enable_mfa_enforcement" { - description = "Determines whether permissions are added to the policy which requires the groups IAM users to use MFA" - type = bool - default = true -} - -variable "attach_iam_self_management_policy" { - description = "Whether to attach IAM policy which allows IAM users to manage their credentials and MFA" - type = bool - default = true -} - -variable "iam_self_management_policy_name_prefix" { - description = "Name prefix for IAM policy to create with IAM self-management permissions" - type = string - default = "IAMSelfManagement-" -} - -variable "aws_account_id" { - description = "AWS account id to use inside IAM policies. If empty, current AWS account ID will be used." - type = string - default = "" -} - -variable "tags" { - description = "A map of tags to add to all resources." - type = map(string) - default = {} -} diff --git a/modules/iam-group-with-policies/versions.tf b/modules/iam-group-with-policies/versions.tf deleted file mode 100644 index d8dd1a44..00000000 --- a/modules/iam-group-with-policies/versions.tf +++ /dev/null @@ -1,10 +0,0 @@ -terraform { - required_version = ">= 1.0" - - required_providers { - aws = { - source = "hashicorp/aws" - version = ">= 4.0" - } - } -} diff --git a/modules/iam-group/README.md b/modules/iam-group/README.md new file mode 100644 index 00000000..cb06157d --- /dev/null +++ b/modules/iam-group/README.md @@ -0,0 +1,105 @@ + +# AWS IAM Group Terraform Module + +Creates an IAM group with IAM policy attached that one or more users can be added to. + +## Usage + +```hcl +module "iam_group" { + source = "terraform-aws-modules/iam/aws//modules/iam-group" + + name = "superadmins" + + users = [ + "user1", + "user2" + ] + + enable_self_management_permissions = true + permission_statements = [ + { + sid = "AssumeRole" + actions = ["sts:AssumeRole"] + resources = ["arn:aws:iam::111111111111:role/admin"] + } + ] + + policies = { + AdministratorAccess = "arn:aws:iam::aws:policy/AdministratorAccess", + } + + tags = { + Terraform = "true" + Environment = "dev" + } +} +``` + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 4.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 4.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_iam_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group) | resource | +| [aws_iam_group_membership.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group_membership) | resource | +| [aws_iam_group_policy_attachment.additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group_policy_attachment) | resource | +| [aws_iam_group_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group_policy_attachment) | resource | +| [aws_iam_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_iam_policy_document.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [create](#input\_create) | Controls if resources should be created (affects all resources) | `bool` | `true` | no | +| [create\_policy](#input\_create\_policy) | Whether to create IAM policy for IAM group | `bool` | `true` | no | +| [enable\_mfa\_enforcment](#input\_enable\_mfa\_enforcment) | Determines whether permissions are added to the policy which requires the groups IAM users to use MFA | `bool` | `true` | no | +| [enable\_self\_management\_permissions](#input\_enable\_self\_management\_permissions) | Determines whether permissions are added to the policy which allow the groups IAM users to manage their credentials and MFA | `bool` | `true` | no | +| [name](#input\_name) | The group's name. The name must consist of upper and lowercase alphanumeric characters with no spaces. You can also include any of the following characters: `=,.@-_.` | `string` | `""` | no | +| [path](#input\_path) | Path in which to create the group | `string` | `null` | no | +| [permission\_statements](#input\_permission\_statements) | List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for the policy | `any` | `[]` | no | +| [policies](#input\_policies) | Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no | +| [policy\_description](#input\_policy\_description) | Description of the IAM policy | `string` | `null` | no | +| [policy\_name\_prefix](#input\_policy\_name\_prefix) | Name prefix for IAM policy | `string` | `null` | no | +| [policy\_path](#input\_policy\_path) | The IAM policy path | `string` | `null` | no | +| [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | +| [users](#input\_users) | A list of IAM User names to associate with the Group | `list(string)` | `[]` | no | +| [users\_account\_id](#input\_users\_account\_id) | An overriding AWS account ID where the group's users reside; leave empty to use the current account ID for the AWS provider | `string` | `null` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [arn](#output\_arn) | The ARN assigned by AWS for this group | +| [id](#output\_id) | The group's ID | +| [name](#output\_name) | The group's name | +| [policy\_arn](#output\_policy\_arn) | The ARN assigned by AWS for this policy | +| [policy\_id](#output\_policy\_id) | The policy's ID | +| [policy\_name](#output\_policy\_name) | The policy's name | +| [unique\_id](#output\_unique\_id) | The unique ID assigned by AWS | +| [users](#output\_users) | List of IAM users in IAM group | + + +## License + +Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-iam/blob/master/LICENSE). diff --git a/modules/iam-group/main.tf b/modules/iam-group/main.tf new file mode 100644 index 00000000..f2ce5642 --- /dev/null +++ b/modules/iam-group/main.tf @@ -0,0 +1,247 @@ +data "aws_partition" "current" {} +data "aws_caller_identity" "current" {} + +locals { + users_account_id = coalesce(var.users_account_id, data.aws_caller_identity.current.account_id) + + user_resources = [for pattern in ["user/$${aws:username}", "user/*/$${aws:username}"] : + "arn:${data.aws_partition.current.partition}:iam::${local.users_account_id}:${pattern}" + ] +} + +################################################################################ +# IAM Group +################################################################################ + +resource "aws_iam_group" "this" { + count = var.create ? 1 : 0 + + name = var.name + path = var.path +} + +resource "aws_iam_group_membership" "this" { + count = var.create && length(var.users) > 0 ? 1 : 0 + + group = aws_iam_group.this[0].id + name = var.name + users = var.users +} + +################################################################################ +# IAM Group Policy +################################################################################ + +locals { + create_policy = var.create && var.create_policy && (var.enable_self_management_permissions || length(var.permission_statements) > 0) +} + +# Allows MFA-authenticated IAM users to manage their own credentials on the My security credentials page +# https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_examples_aws_my-sec-creds-self-manage.html +data "aws_iam_policy_document" "this" { + count = local.create_policy ? 1 : 0 + + dynamic "statement" { + for_each = var.enable_self_management_permissions ? [1] : [] + + content { + sid = "ViewAccountInfo" + actions = [ + "iam:GetAccountPasswordPolicy", + "iam:ListVirtualMFADevices" + ] + resources = ["*"] + } + } + + dynamic "statement" { + for_each = var.enable_self_management_permissions ? [1] : [] + + content { + sid = "ManageOwnPasswords" + actions = [ + "iam:ChangePassword", + "iam:GetUser" + ] + resources = local.user_resources + } + } + + dynamic "statement" { + for_each = var.enable_self_management_permissions ? [1] : [] + + content { + sid = "ManageOwnAccessKeys" + actions = [ + "iam:CreateAccessKey", + "iam:DeleteAccessKey", + "iam:ListAccessKeys", + "iam:UpdateAccessKey" + ] + resources = local.user_resources + } + } + + dynamic "statement" { + for_each = var.enable_self_management_permissions ? [1] : [] + + content { + sid = "ManageOwnSigningCertificates" + actions = [ + "iam:DeleteSigningCertificate", + "iam:ListSigningCertificates", + "iam:UpdateSigningCertificate", + "iam:UploadSigningCertificate" + ] + resources = local.user_resources + } + } + + dynamic "statement" { + for_each = var.enable_self_management_permissions ? [1] : [] + + content { + sid = "ManageOwnSSHPublicKeys" + actions = [ + "iam:DeleteSSHPublicKey", + "iam:GetSSHPublicKey", + "iam:ListSSHPublicKeys", + "iam:UpdateSSHPublicKey", + "iam:UploadSSHPublicKey" + ] + resources = local.user_resources + } + } + + dynamic "statement" { + for_each = var.enable_self_management_permissions ? [1] : [] + + content { + sid = "ManageOwnGitCredentials" + actions = [ + "iam:CreateServiceSpecificCredential", + "iam:DeleteServiceSpecificCredential", + "iam:ListServiceSpecificCredentials", + "iam:ResetServiceSpecificCredential", + "iam:UpdateServiceSpecificCredential" + ] + resources = local.user_resources + } + } + + dynamic "statement" { + for_each = var.enable_self_management_permissions ? [1] : [] + + content { + sid = "ManageOwnVirtualMFADevice" + actions = ["iam:CreateVirtualMFADevice"] + resources = ["arn:${data.aws_partition.current.partition}:iam::${local.users_account_id}:mfa/*"] + } + } + + dynamic "statement" { + for_each = var.enable_self_management_permissions ? [1] : [] + + content { + sid = "ManageOwnUserMFA" + actions = [ + "iam:DeactivateMFADevice", + "iam:EnableMFADevice", + "iam:ListMFADevices", + "iam:ResyncMFADevice" + ] + resources = local.user_resources + } + } + + dynamic "statement" { + for_each = var.enable_self_management_permissions && var.enable_mfa_enforcment ? [1] : [] + + content { + sid = "DenyAllExceptListedIfNoMFA" + not_actions = [ + "iam:ChangePassword", + "iam:CreateVirtualMFADevice", + "iam:EnableMFADevice", + "iam:GetUser", + "iam:ListMFADevices", + "iam:ListVirtualMFADevices", + "iam:ResyncMFADevice", + "sts:GetSessionToken" + ] + resources = ["*"] + + condition { + test = "BoolIfExists" + variable = "aws:MultiFactorAuthPresent" + values = ["false"] + } + } + } + + dynamic "statement" { + for_each = var.permission_statements + + content { + sid = try(statement.value.sid, null) + actions = try(statement.value.actions, null) + not_actions = try(statement.value.not_actions, null) + effect = try(statement.value.effect, null) + resources = try(statement.value.resources, null) + not_resources = try(statement.value.not_resources, null) + + dynamic "principals" { + for_each = try(statement.value.principals, []) + + content { + type = principals.value.type + identifiers = principals.value.identifiers + } + } + + dynamic "not_principals" { + for_each = try(statement.value.not_principals, []) + + content { + type = not_principals.value.type + identifiers = not_principals.value.identifiers + } + } + + dynamic "condition" { + for_each = try(statement.value.conditions, []) + + content { + test = condition.value.test + values = condition.value.values + variable = condition.value.variable + } + } + } + } +} + +resource "aws_iam_policy" "this" { + count = local.create_policy ? 1 : 0 + + name_prefix = try(coalesce(var.policy_name_prefix, "${var.name}-"), null) + description = var.policy_description + path = coalesce(var.policy_path, var.path, "/") + policy = data.aws_iam_policy_document.this[0].json + + tags = var.tags +} + +resource "aws_iam_group_policy_attachment" "this" { + count = local.create_policy ? 1 : 0 + + group = aws_iam_group.this[0].id + policy_arn = aws_iam_policy.this[0].arn +} + +resource "aws_iam_group_policy_attachment" "additional" { + for_each = { for k, v in var.policies : k => v if var.create } + + group = aws_iam_group.this[0].id + policy_arn = each.value +} diff --git a/modules/iam-group/outputs.tf b/modules/iam-group/outputs.tf new file mode 100644 index 00000000..983d9a0d --- /dev/null +++ b/modules/iam-group/outputs.tf @@ -0,0 +1,47 @@ +################################################################################ +# IAM Group +################################################################################ + +output "id" { + description = "The group's ID" + value = try(aws_iam_group.this[0].id, null) +} + +output "arn" { + description = "The ARN assigned by AWS for this group" + value = try(aws_iam_group.this[0].arn, null) +} + +output "name" { + description = "The group's name" + value = try(aws_iam_group.this[0].name, null) +} + +output "unique_id" { + description = " The unique ID assigned by AWS" + value = try(aws_iam_group.this[0].unique_id, null) +} + +output "users" { + description = "List of IAM users in IAM group" + value = flatten(aws_iam_group_membership.this[*].users) +} + +################################################################################ +# IAM Group Policy +################################################################################ + +output "policy_arn" { + description = "The ARN assigned by AWS for this policy" + value = try(aws_iam_policy.this[0].arn, null) +} + +output "policy_name" { + description = "The policy's name" + value = try(aws_iam_policy.this[0].name, null) +} + +output "policy_id" { + description = "The policy's ID" + value = try(aws_iam_policy.this[0].id, null) +} diff --git a/modules/iam-group/variables.tf b/modules/iam-group/variables.tf new file mode 100644 index 00000000..ca455d3d --- /dev/null +++ b/modules/iam-group/variables.tf @@ -0,0 +1,91 @@ +variable "create" { + description = "Controls if resources should be created (affects all resources)" + type = bool + default = true +} + +variable "tags" { + description = "A map of tags to add to all resources" + type = map(string) + default = {} +} + +################################################################################ +# IAM Group +################################################################################ + +variable "name" { + description = "The group's name. The name must consist of upper and lowercase alphanumeric characters with no spaces. You can also include any of the following characters: `=,.@-_.`" + type = string + default = "" +} + +variable "path" { + description = "Path in which to create the group" + type = string + default = null +} + +variable "users" { + description = "A list of IAM User names to associate with the Group" + type = list(string) + default = [] +} + +################################################################################ +# IAM Group Policy +################################################################################ + +variable "create_policy" { + description = "Whether to create IAM policy for IAM group" + type = bool + default = true +} + +variable "enable_self_management_permissions" { + description = "Determines whether permissions are added to the policy which allow the groups IAM users to manage their credentials and MFA" + type = bool + default = true +} + +variable "enable_mfa_enforcment" { + description = "Determines whether permissions are added to the policy which requires the groups IAM users to use MFA" + type = bool + default = true +} + +variable "permission_statements" { + description = "List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for the policy" + type = any + default = [] +} + +variable "policy_name_prefix" { + description = "Name prefix for IAM policy" + type = string + default = null +} + +variable "policy_description" { + description = "Description of the IAM policy" + type = string + default = null +} + +variable "policy_path" { + description = "The IAM policy path" + type = string + default = null +} + +variable "policies" { + description = "Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format" + type = map(string) + default = {} +} + +variable "users_account_id" { + description = "An overriding AWS account ID where the group's users reside; leave empty to use the current account ID for the AWS provider" + type = string + default = null +} diff --git a/examples/iam-github-oidc/versions.tf b/modules/iam-group/versions.tf similarity index 100% rename from examples/iam-github-oidc/versions.tf rename to modules/iam-group/versions.tf diff --git a/modules/iam-github-oidc-provider/README.md b/modules/iam-oidc-provider/README.md similarity index 64% rename from modules/iam-github-oidc-provider/README.md rename to modules/iam-oidc-provider/README.md index 0a2244e6..7c5da1aa 100644 --- a/modules/iam-github-oidc-provider/README.md +++ b/modules/iam-oidc-provider/README.md @@ -1,14 +1,36 @@ -# IAM GitHub OIDC Provider +# AWS IAM OIDC Provider -Creates an IAM identity provider for GitHub OIDC. See more details here https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services +Creates an OpenID connect provider. Useful for trusting external identity providers such as GitHub, Bitbucket, etc. -Note: an IAM provider is 1 per account per given URL. This module would be provisioned once per AWS account, and multiple roles created with this provider as the trusted identity (typically 1 role per GitHub repository). +⚠️ An IAM provider is 1 per account per given URL. This module would be provisioned once per AWS account, and then one or more roles can be created with this provider as the trusted identity. ## Usage +### GitHub + +See more details [here](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services) + +```hcl +module "iam_oidc_provider" { + source = "terraform-aws-modules/iam/aws//modules/iam-oidc-provider" + + url = "https://token.actions.githubusercontent.com" + + tags = { + Environment = "test" + } +} +``` + +### Bitbucket + +See more details [here](https://support.atlassian.com/bitbucket-cloud/docs/integrate-pipelines-with-resource-servers-using-oidc/) + ```hcl -module "iam_github_oidc_provider" { - source = "terraform-aws-modules/iam/aws//modules/iam-github-oidc-provider" +module "iam_oidc_provider" { + source = "terraform-aws-modules/iam/aws//modules/iam-oidc-provider" + + url = "https://api.bitbucket.org/2.0/workspaces/example-workspace/pipelines-config/identity/oidc" tags = { Environment = "test" @@ -48,7 +70,6 @@ No modules. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [additional\_thumbprints](#input\_additional\_thumbprints) | List of additional thumbprints to add to the thumbprint list. | `list(string)` |
[
"6938fd4d98bab03faadb97b34396831e3780aea1",
"1c58a3a8518e8759bf075b76b750d4f2df264fcd"
]
| no | | [client\_id\_list](#input\_client\_id\_list) | List of client IDs (also known as audiences) for the IAM OIDC provider. Defaults to STS service if not values are provided | `list(string)` | `[]` | no | | [create](#input\_create) | Controls if resources should be created (affects all resources) | `bool` | `true` | no | | [tags](#input\_tags) | A map of tags to add to the resources created | `map(any)` | `{}` | no | @@ -61,3 +82,7 @@ No modules. | [arn](#output\_arn) | The ARN assigned by AWS for this provider | | [url](#output\_url) | The URL of the identity provider. Corresponds to the iss claim | + +## License + +Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-iam/blob/master/LICENSE). diff --git a/modules/iam-github-oidc-provider/main.tf b/modules/iam-oidc-provider/main.tf similarity index 80% rename from modules/iam-github-oidc-provider/main.tf rename to modules/iam-oidc-provider/main.tf index 1555ff54..b68afb44 100644 --- a/modules/iam-github-oidc-provider/main.tf +++ b/modules/iam-oidc-provider/main.tf @@ -15,7 +15,7 @@ resource "aws_iam_openid_connect_provider" "this" { url = var.url client_id_list = coalescelist(var.client_id_list, ["sts.${data.aws_partition.current.dns_suffix}"]) - thumbprint_list = distinct(concat(data.tls_certificate.this[0].certificates[*].sha1_fingerprint, var.additional_thumbprints)) + thumbprint_list = data.tls_certificate.this[0].certificates[*].sha1_fingerprint tags = var.tags } diff --git a/modules/iam-github-oidc-provider/outputs.tf b/modules/iam-oidc-provider/outputs.tf similarity index 95% rename from modules/iam-github-oidc-provider/outputs.tf rename to modules/iam-oidc-provider/outputs.tf index d388fb95..97d8b3e7 100644 --- a/modules/iam-github-oidc-provider/outputs.tf +++ b/modules/iam-oidc-provider/outputs.tf @@ -1,5 +1,5 @@ ################################################################################ -# GitHub OIDC Provider +# OIDC Provider ################################################################################ output "arn" { diff --git a/modules/iam-github-oidc-provider/variables.tf b/modules/iam-oidc-provider/variables.tf similarity index 65% rename from modules/iam-github-oidc-provider/variables.tf rename to modules/iam-oidc-provider/variables.tf index 76e5d0bd..e405bb93 100644 --- a/modules/iam-github-oidc-provider/variables.tf +++ b/modules/iam-oidc-provider/variables.tf @@ -21,13 +21,3 @@ variable "url" { type = string default = "https://token.actions.githubusercontent.com" } - -variable "additional_thumbprints" { - description = "List of additional thumbprints to add to the thumbprint list." - type = list(string) - # https://github.blog/changelog/2023-06-27-github-actions-update-on-oidc-integration-with-aws/ - default = [ - "6938fd4d98bab03faadb97b34396831e3780aea1", - "1c58a3a8518e8759bf075b76b750d4f2df264fcd" - ] -} diff --git a/modules/iam-github-oidc-provider/versions.tf b/modules/iam-oidc-provider/versions.tf similarity index 100% rename from modules/iam-github-oidc-provider/versions.tf rename to modules/iam-oidc-provider/versions.tf diff --git a/modules/iam-policy/README.md b/modules/iam-policy/README.md deleted file mode 100644 index 03abb122..00000000 --- a/modules/iam-policy/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# iam-policy - -Creates IAM policy. - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 4.0 | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [aws_iam_policy.policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [create\_policy](#input\_create\_policy) | Whether to create the IAM policy | `bool` | `true` | no | -| [description](#input\_description) | The description of the policy | `string` | `"IAM Policy"` | no | -| [name](#input\_name) | The name of the policy | `string` | `null` | no | -| [name\_prefix](#input\_name\_prefix) | IAM policy name prefix | `string` | `null` | no | -| [path](#input\_path) | The path of the policy in IAM | `string` | `"/"` | no | -| [policy](#input\_policy) | The path of the policy in IAM (tpl file) | `string` | `""` | no | -| [tags](#input\_tags) | A map of tags to add to all resources. | `map(string)` | `{}` | no | - -## Outputs - -| Name | Description | -|------|-------------| -| [arn](#output\_arn) | The ARN assigned by AWS to this policy | -| [description](#output\_description) | The description of the policy | -| [id](#output\_id) | The policy's ID | -| [name](#output\_name) | The name of the policy | -| [path](#output\_path) | The path of the policy in IAM | -| [policy](#output\_policy) | The policy document | - diff --git a/modules/iam-policy/main.tf b/modules/iam-policy/main.tf deleted file mode 100644 index c5028b38..00000000 --- a/modules/iam-policy/main.tf +++ /dev/null @@ -1,12 +0,0 @@ -resource "aws_iam_policy" "policy" { - count = var.create_policy ? 1 : 0 - - name = var.name - name_prefix = var.name_prefix - path = var.path - description = var.description - - policy = var.policy - - tags = var.tags -} diff --git a/modules/iam-policy/outputs.tf b/modules/iam-policy/outputs.tf deleted file mode 100644 index d8900802..00000000 --- a/modules/iam-policy/outputs.tf +++ /dev/null @@ -1,29 +0,0 @@ -output "id" { - description = "The policy's ID" - value = try(aws_iam_policy.policy[0].id, "") -} - -output "arn" { - description = "The ARN assigned by AWS to this policy" - value = try(aws_iam_policy.policy[0].arn, "") -} - -output "description" { - description = "The description of the policy" - value = try(aws_iam_policy.policy[0].description, "") -} - -output "name" { - description = "The name of the policy" - value = try(aws_iam_policy.policy[0].name, "") -} - -output "path" { - description = "The path of the policy in IAM" - value = try(aws_iam_policy.policy[0].path, "") -} - -output "policy" { - description = "The policy document" - value = try(aws_iam_policy.policy[0].policy, "") -} diff --git a/modules/iam-policy/variables.tf b/modules/iam-policy/variables.tf deleted file mode 100644 index 8e596143..00000000 --- a/modules/iam-policy/variables.tf +++ /dev/null @@ -1,41 +0,0 @@ -variable "create_policy" { - description = "Whether to create the IAM policy" - type = bool - default = true -} - -variable "name" { - description = "The name of the policy" - type = string - default = null -} - -variable "name_prefix" { - description = "IAM policy name prefix" - type = string - default = null -} - -variable "path" { - description = "The path of the policy in IAM" - type = string - default = "/" -} - -variable "description" { - description = "The description of the policy" - type = string - default = "IAM Policy" -} - -variable "policy" { - description = "The path of the policy in IAM (tpl file)" - type = string - default = "" -} - -variable "tags" { - description = "A map of tags to add to all resources." - type = map(string) - default = {} -} diff --git a/modules/iam-policy/versions.tf b/modules/iam-policy/versions.tf deleted file mode 100644 index d8dd1a44..00000000 --- a/modules/iam-policy/versions.tf +++ /dev/null @@ -1,10 +0,0 @@ -terraform { - required_version = ">= 1.0" - - required_providers { - aws = { - source = "hashicorp/aws" - version = ">= 4.0" - } - } -} diff --git a/modules/iam-read-only-policy/README.md b/modules/iam-read-only-policy/README.md index 2faeccce..fe12b0d1 100644 --- a/modules/iam-read-only-policy/README.md +++ b/modules/iam-read-only-policy/README.md @@ -1,7 +1,27 @@ -# iam-read-only-policy +### AWS IAM ReadOnly Policy Terraform Module -Creates IAM read-only policy for specified services. Default AWS read-only policies (arn:aws:iam::aws:policy/job-function/ViewOnlyAccess, arn:aws:iam::aws:policy/ReadOnlyAccess), being a one-size-fits-all type of policies, have a lot of things missing as well as something that you might not need. Also, AWS default policies are known for having [security issues](https://securityboulevard.com/2020/12/the-aws-managed-policies-trap/) -Thus this module is an attempt to build a better base for a customizable usable read-only policy. +Creates an IAM policy that allows read-only access to the list of AWS services provided. + +Default AWS read-only policies (arn:aws:iam::aws:policy/job-function/ViewOnlyAccess, arn:aws:iam::aws:policy/ReadOnlyAccess), being a one-size-fits-all type of policies, have a lot of things missing as well as something that you might not need. Also, AWS default policies are known for having [security issues](https://securityboulevard.com/2020/12/the-aws-managed-policies-trap/). Thus this module is an attempt to build a better base for a customizable usable read-only policy. + +## Usage + +```hcl +module "iam_read_only_policy" { + source = "terraform-aws-modules/iam/aws//modules/iam-read-only-policy" + + name = "example" + path = "/" + description = "My example read-only policy" + + allowed_services = ["rds", "dynamo", "health"] + + tags = { + Terraform = "true" + Environment = "dev" + } +} +``` ## Requirements @@ -26,27 +46,24 @@ No modules. | Name | Type | |------|------| | [aws_iam_policy.policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy_document.allowed_services](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_iam_policy_document.combined](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_iam_policy_document.console_services](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_iam_policy_document.logs_query](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_iam_policy_document.sts](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [additional\_policy\_json](#input\_additional\_policy\_json) | JSON policy document if you want to add custom actions | `string` | `"{}"` | no | +| [additional\_policy\_json](#input\_additional\_policy\_json) | JSON policy document if you want to add custom actions | `string` | `""` | no | | [allow\_cloudwatch\_logs\_query](#input\_allow\_cloudwatch\_logs\_query) | Allows StartQuery/StopQuery/FilterLogEvents CloudWatch actions | `bool` | `true` | no | | [allow\_predefined\_sts\_actions](#input\_allow\_predefined\_sts\_actions) | Allows GetCallerIdentity/GetSessionToken/GetAccessKeyInfo sts actions | `bool` | `true` | no | | [allow\_web\_console\_services](#input\_allow\_web\_console\_services) | Allows List/Get/Describe/View actions for services used when browsing AWS console (e.g. resource-groups, tag, health services) | `bool` | `true` | no | -| [allowed\_services](#input\_allowed\_services) | List of services to allow Get/List/Describe/View options. Service name should be the same as corresponding service IAM prefix. See what it is for each service here https://docs.aws.amazon.com/service-authorization/latest/reference/reference_policies_actions-resources-contextkeys.html | `list(string)` | n/a | yes | -| [create\_policy](#input\_create\_policy) | Whether to create the IAM policy | `bool` | `true` | no | +| [allowed\_services](#input\_allowed\_services) | List of services to allow Get/List/Describe/View options. Service name should be the same as corresponding service IAM prefix. See what it is for each service here https://docs.aws.amazon.com/service-authorization/latest/reference/reference_policies_actions-resources-contextkeys.html | `list(string)` | `[]` | no | +| [create](#input\_create) | Controls if resources should be created (affects all resources) | `bool` | `true` | no | +| [create\_policy](#input\_create\_policy) | Controls if IAM policy should be created. Set to `false` to generate the policy JSON without creating the policy itself | `bool` | `true` | no | | [description](#input\_description) | The description of the policy | `string` | `"IAM Policy"` | no | -| [name](#input\_name) | The name of the policy | `string` | `null` | no | -| [name\_prefix](#input\_name\_prefix) | IAM policy name prefix | `string` | `null` | no | -| [path](#input\_path) | The path of the policy in IAM | `string` | `"/"` | no | -| [tags](#input\_tags) | A map of tags to add to all resources. | `map(string)` | `{}` | no | +| [name](#input\_name) | Name to use on IAM policy created | `string` | `null` | no | +| [name\_prefix](#input\_name\_prefix) | Name prefix to use on IAM policy created | `string` | `null` | no | +| [path](#input\_path) | Path of IAM policy | `string` | `"/"` | no | +| [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | | [web\_console\_services](#input\_web\_console\_services) | List of web console services to allow | `list(string)` |
[
"resource-groups",
"tag",
"health",
"ce"
]
| no | ## Outputs @@ -54,10 +71,12 @@ No modules. | Name | Description | |------|-------------| | [arn](#output\_arn) | The ARN assigned by AWS to this policy | -| [description](#output\_description) | The description of the policy | | [id](#output\_id) | The policy's ID | | [name](#output\_name) | The name of the policy | -| [path](#output\_path) | The path of the policy in IAM | | [policy](#output\_policy) | The policy document | -| [policy\_json](#output\_policy\_json) | Policy document as json. Useful if you need document but do not want to create IAM policy itself. For example for SSO Permission Set inline policies | +| [policy\_json](#output\_policy\_json) | Policy document JSON | + +## License + +Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-iam/blob/master/LICENSE). diff --git a/modules/iam-read-only-policy/main.tf b/modules/iam-read-only-policy/main.tf index ea479884..9700293d 100644 --- a/modules/iam-read-only-policy/main.tf +++ b/modules/iam-read-only-policy/main.tf @@ -1,44 +1,44 @@ +################################################################################ +# IAM Policy +################################################################################ + resource "aws_iam_policy" "policy" { - count = var.create_policy ? 1 : 0 + count = var.create && var.create_policy ? 1 : 0 name = var.name name_prefix = var.name_prefix path = var.path description = var.description - policy = data.aws_iam_policy_document.combined.json + policy = data.aws_iam_policy_document.this[0].json tags = var.tags } -locals { - allowed_services = distinct(var.allowed_services) -} +data "aws_iam_policy_document" "this" { + count = var.create ? 1 : 0 -data "aws_iam_policy_document" "allowed_services" { + source_policy_documents = [var.additional_policy_json] dynamic "statement" { - for_each = toset(local.allowed_services) + for_each = toset(distinct(var.allowed_services)) + content { sid = replace(statement.value, "-", "") actions = [ "${statement.value}:List*", "${statement.value}:Get*", - "${statement.value}:BatchGet*", "${statement.value}:Describe*", "${statement.value}:View*", ] resources = ["*"] } } -} - -data "aws_iam_policy_document" "console_services" { - count = var.allow_web_console_services ? 1 : 0 dynamic "statement" { - for_each = toset(var.web_console_services) + for_each = { for k, v in toset(var.web_console_services) : k => v if var.allow_web_console_services } + content { sid = replace(statement.value, "-", "") @@ -51,42 +51,32 @@ data "aws_iam_policy_document" "console_services" { resources = ["*"] } } -} -data "aws_iam_policy_document" "sts" { - count = var.allow_predefined_sts_actions ? 1 : 0 - - statement { - sid = "STS" - actions = [ - "sts:GetAccessKeyInfo", - "sts:GetCallerIdentity", - "sts:GetSessionToken", - ] - resources = ["*"] - } -} + dynamic "statement" { + for_each = var.allow_predefined_sts_actions ? [1] : [] -data "aws_iam_policy_document" "logs_query" { - count = var.allow_cloudwatch_logs_query ? 1 : 0 - - statement { - sid = "AllowLogsQuery" - actions = [ - "logs:StartQuery", - "logs:StopQuery", - "logs:FilterLogEvents" - ] - resources = ["*"] + content { + sid = "STS" + actions = [ + "sts:GetAccessKeyInfo", + "sts:GetCallerIdentity", + "sts:GetSessionToken", + ] + resources = ["*"] + } } -} -data "aws_iam_policy_document" "combined" { - source_policy_documents = concat( - [data.aws_iam_policy_document.allowed_services.json], - data.aws_iam_policy_document.console_services[*].json, - data.aws_iam_policy_document.sts[*].json, - data.aws_iam_policy_document.logs_query[*].json, - [var.additional_policy_json] - ) + dynamic "statement" { + for_each = var.allow_cloudwatch_logs_query ? [1] : [] + + content { + sid = "AllowLogsQuery" + actions = [ + "logs:StartQuery", + "logs:StopQuery", + "logs:FilterLogEvents" + ] + resources = ["*"] + } + } } diff --git a/modules/iam-read-only-policy/outputs.tf b/modules/iam-read-only-policy/outputs.tf index c18731d5..9bb9591e 100644 --- a/modules/iam-read-only-policy/outputs.tf +++ b/modules/iam-read-only-policy/outputs.tf @@ -1,34 +1,28 @@ -output "policy_json" { - description = "Policy document as json. Useful if you need document but do not want to create IAM policy itself. For example for SSO Permission Set inline policies" - value = data.aws_iam_policy_document.combined.json -} +################################################################################ +# IAM Policy +################################################################################ output "id" { description = "The policy's ID" - value = try(aws_iam_policy.policy[0].id, "") + value = try(aws_iam_policy.policy[0].id, null) } output "arn" { description = "The ARN assigned by AWS to this policy" - value = try(aws_iam_policy.policy[0].arn, "") -} - -output "description" { - description = "The description of the policy" - value = try(aws_iam_policy.policy[0].description, "") + value = try(aws_iam_policy.policy[0].arn, null) } output "name" { description = "The name of the policy" - value = try(aws_iam_policy.policy[0].name, "") -} - -output "path" { - description = "The path of the policy in IAM" - value = try(aws_iam_policy.policy[0].path, "") + value = try(aws_iam_policy.policy[0].name, null) } output "policy" { description = "The policy document" - value = try(aws_iam_policy.policy[0].policy, "") + value = try(aws_iam_policy.policy[0].policy, null) +} + +output "policy_json" { + description = "Policy document JSON" + value = try(data.aws_iam_policy_document.this[0].json, "") } diff --git a/modules/iam-read-only-policy/variables.tf b/modules/iam-read-only-policy/variables.tf index 5bb6419a..488fa8ba 100644 --- a/modules/iam-read-only-policy/variables.tf +++ b/modules/iam-read-only-policy/variables.tf @@ -1,23 +1,39 @@ +variable "create" { + description = "Controls if resources should be created (affects all resources)" + type = bool + default = true +} + +variable "tags" { + description = "A map of tags to add to all resources" + type = map(string) + default = {} +} + +################################################################################ +# IAM Policy +################################################################################ + variable "create_policy" { - description = "Whether to create the IAM policy" + description = "Controls if IAM policy should be created. Set to `false` to generate the policy JSON without creating the policy itself" type = bool default = true } variable "name" { - description = "The name of the policy" + description = "Name to use on IAM policy created" type = string default = null } variable "name_prefix" { - description = "IAM policy name prefix" + description = "Name prefix to use on IAM policy created" type = string default = null } variable "path" { - description = "The path of the policy in IAM" + description = "Path of IAM policy" type = string default = "/" } @@ -31,18 +47,13 @@ variable "description" { variable "allowed_services" { description = "List of services to allow Get/List/Describe/View options. Service name should be the same as corresponding service IAM prefix. See what it is for each service here https://docs.aws.amazon.com/service-authorization/latest/reference/reference_policies_actions-resources-contextkeys.html" type = list(string) + default = [] } variable "additional_policy_json" { description = "JSON policy document if you want to add custom actions" type = string - default = "{}" -} - -variable "tags" { - description = "A map of tags to add to all resources." - type = map(string) - default = {} + default = "" } variable "allow_cloudwatch_logs_query" { diff --git a/modules/iam-role-for-service-accounts-eks/main.tf b/modules/iam-role-for-service-accounts-eks/main.tf deleted file mode 100644 index 6095f390..00000000 --- a/modules/iam-role-for-service-accounts-eks/main.tf +++ /dev/null @@ -1,88 +0,0 @@ -data "aws_partition" "current" {} -data "aws_caller_identity" "current" {} -data "aws_region" "current" {} - -locals { - account_id = data.aws_caller_identity.current.account_id - partition = data.aws_partition.current.partition - dns_suffix = data.aws_partition.current.dns_suffix - region = data.aws_region.current.name - role_name_condition = var.role_name != null ? var.role_name : "${var.role_name_prefix}*" -} - -data "aws_iam_policy_document" "this" { - count = var.create_role ? 1 : 0 - - dynamic "statement" { - # https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/ - for_each = var.allow_self_assume_role ? [1] : [] - - content { - sid = "ExplicitSelfRoleAssumption" - effect = "Allow" - actions = ["sts:AssumeRole"] - - principals { - type = "AWS" - identifiers = ["*"] - } - - condition { - test = "ArnLike" - variable = "aws:PrincipalArn" - values = ["arn:${local.partition}:iam::${local.account_id}:role${var.role_path}${local.role_name_condition}"] - } - } - } - - dynamic "statement" { - for_each = var.oidc_providers - - content { - effect = "Allow" - actions = ["sts:AssumeRoleWithWebIdentity"] - - principals { - type = "Federated" - identifiers = [statement.value.provider_arn] - } - - condition { - test = var.assume_role_condition_test - variable = "${replace(statement.value.provider_arn, "/^(.*provider/)/", "")}:sub" - values = [for sa in statement.value.namespace_service_accounts : "system:serviceaccount:${sa}"] - } - - # https://aws.amazon.com/premiumsupport/knowledge-center/eks-troubleshoot-oidc-and-irsa/?nc1=h_ls - condition { - test = var.assume_role_condition_test - variable = "${replace(statement.value.provider_arn, "/^(.*provider/)/", "")}:aud" - values = ["sts.amazonaws.com"] - } - - } - } -} - -resource "aws_iam_role" "this" { - count = var.create_role ? 1 : 0 - - name = var.role_name - name_prefix = var.role_name_prefix - path = var.role_path - description = var.role_description - - assume_role_policy = data.aws_iam_policy_document.this[0].json - max_session_duration = var.max_session_duration - permissions_boundary = var.role_permissions_boundary_arn - force_detach_policies = var.force_detach_policies - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "this" { - for_each = { for k, v in var.role_policy_arns : k => v if var.create_role } - - role = aws_iam_role.this[0].name - policy_arn = each.value -} diff --git a/modules/iam-role-for-service-accounts-eks/outputs.tf b/modules/iam-role-for-service-accounts-eks/outputs.tf deleted file mode 100644 index a6805310..00000000 --- a/modules/iam-role-for-service-accounts-eks/outputs.tf +++ /dev/null @@ -1,19 +0,0 @@ -output "iam_role_arn" { - description = "ARN of IAM role" - value = try(aws_iam_role.this[0].arn, "") -} - -output "iam_role_name" { - description = "Name of IAM role" - value = try(aws_iam_role.this[0].name, "") -} - -output "iam_role_path" { - description = "Path of IAM role" - value = try(aws_iam_role.this[0].path, "") -} - -output "iam_role_unique_id" { - description = "Unique ID of IAM role" - value = try(aws_iam_role.this[0].unique_id, "") -} diff --git a/modules/iam-role-for-service-accounts-eks/versions.tf b/modules/iam-role-for-service-accounts-eks/versions.tf deleted file mode 100644 index d8dd1a44..00000000 --- a/modules/iam-role-for-service-accounts-eks/versions.tf +++ /dev/null @@ -1,10 +0,0 @@ -terraform { - required_version = ">= 1.0" - - required_providers { - aws = { - source = "hashicorp/aws" - version = ">= 4.0" - } - } -} diff --git a/modules/iam-role-for-service-accounts-eks/README.md b/modules/iam-role-for-service-accounts/README.md similarity index 56% rename from modules/iam-role-for-service-accounts-eks/README.md rename to modules/iam-role-for-service-accounts/README.md index c572d798..674606e7 100644 --- a/modules/iam-role-for-service-accounts-eks/README.md +++ b/modules/iam-role-for-service-accounts/README.md @@ -1,7 +1,13 @@ -# IAM Role for Service Accounts in EKS +# AWS IAM Role for EKS Service Accounts Terraform Module -Creates an IAM role which can be assumed by AWS EKS `ServiceAccount`s with optional policies for commonly used controllers/custom resources within EKS. The optional policies supported include: +> [!TIP] +> Upgrade to use EKS Pod Identity instead of IRSA +> A similar module for EKS Pod Identity is available [here](https://github.com/terraform-aws-modules/terraform-aws-eks-pod-identity). + +> [!INFO] +> The [karpenter](https://github.com/terraform-aws-modules/terraform-aws-eks/tree/master/modules/karpenter) sub-module contains the necessary AWS resources for running Karpenter, including the Karpenter controller IAM role & policy +Creates an IAM role which can be assumed by AWS EKS `ServiceAccount`s with optional policies for commonly used controllers/custom resources within EKS. The optional policies supported include: - [Cert-Manager](https://cert-manager.io/docs/configuration/acme/dns01/route53/#set-up-an-iam-role) - [Cluster Autoscaler](https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/cloudprovider/aws/README.md) - [EBS CSI Driver](https://github.com/kubernetes-sigs/aws-ebs-csi-driver/blob/master/docs/example-iam-policy.json) @@ -13,20 +19,19 @@ Creates an IAM role which can be assumed by AWS EKS `ServiceAccount`s with optio - [Karpenter](https://github.com/aws/karpenter/blob/main/website/content/en/preview/getting-started/getting-started-with-karpenter/cloudformation.yaml) - [Load Balancer Controller](https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/main/docs/install/iam_policy.json) - [Load Balancer Controller Target Group Binding Only](https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.4/deploy/installation/#iam-permission-subset-for-those-who-use-targetgroupbinding-only-and-dont-plan-to-use-the-aws-load-balancer-controller-to-manage-security-group-rules) -- [App Mesh Controller](https://github.com/aws/aws-app-mesh-controller-for-k8s/blob/master/config/iam/controller-iam-policy.json) - - [App Mesh Envoy Proxy](https://raw.githubusercontent.com/aws/aws-app-mesh-controller-for-k8s/master/config/iam/envoy-iam-policy.json) - [Managed Service for Prometheus](https://docs.aws.amazon.com/prometheus/latest/userguide/set-up-irsa.html) - [Mountpoint S3 CSI Driver](https://github.com/awslabs/mountpoint-s3/blob/main/doc/CONFIGURATION.md#iam-permissions) - [Node Termination Handler](https://github.com/aws/aws-node-termination-handler#5-create-an-iam-role-for-the-pods) - [Velero](https://github.com/vmware-tanzu/velero-plugin-for-aws#option-1-set-permissions-with-an-iam-user) - [VPC CNI](https://docs.aws.amazon.com/eks/latest/userguide/cni-iam-role.html) - This module is intended to be used with AWS EKS. For details of how a `ServiceAccount` in EKS can assume an IAM role, see the [EKS documentation](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html). This module supports multiple `ServiceAccount`s across multiple clusters and/or namespaces. This allows for a single IAM role to be used when an application may span multiple clusters (e.g. for DR) or multiple namespaces (e.g. for canary deployments). For example, to create an IAM role named `my-app` that can be assumed from the `ServiceAccount` named `my-app-staging` in the namespace `default` and `canary` in a cluster in `us-east-1`; and also the `ServiceAccount` name `my-app-staging` in the namespace `default` in a cluster in `ap-southeast-1`, the configuration would be: +## Usage + ```hcl -module "iam_eks_role" { +module "irsa" { source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" role_name = "my-app" @@ -59,9 +64,9 @@ module "vpc_cni_irsa_role" { vpc_cni_enable_ipv4 = true oidc_providers = { - main = { + this = { provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["default:my-app", "canary:my-app"] + namespace_service_accounts = ["kube-system:aws-node"] } } } @@ -69,35 +74,47 @@ module "vpc_cni_irsa_role" { module "karpenter_irsa_role" { source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" - role_name = "karpenter_controller" - attach_karpenter_controller_policy = true - - karpenter_controller_cluster_name = module.eks.cluster_name - karpenter_controller_node_iam_role_arns = [module.eks.eks_managed_node_groups["default"].iam_role_arn] + role_name = "karpenter" - attach_vpc_cni_policy = true - vpc_cni_enable_ipv4 = true + attach_karpenter_policy = true + karpenter_cluster_name = module.eks.cluster_name + karpenter_node_iam_role_arns = [module.eks.eks_managed_node_groups["default"].iam_role_arn] oidc_providers = { - main = { + this = { provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["default:my-app", "canary:my-app"] + namespace_service_accounts = ["karpenter:karpenter"] } } } module "eks" { source = "terraform-aws-modules/eks/aws" - version = "~> 20.4" + version = "~> 21.0" - cluster_name = "my-cluster" - cluster_version = "1.29" + name = "my-cluster" + kubernetes_version = "1.33" vpc_id = module.vpc.vpc_id subnet_ids = module.vpc.private_subnets + addons = { + coredns = {} + kube-proxy = {} + vpc-cni = { + before_compute = true + } + } + eks_managed_node_groups = { - default = {} + example = { + ami_type = "AL2023_x86_64_STANDARD" + instance_types = ["m5.xlarge"] + + min_size = 1 + max_size = 2 + desired_size = 1 + } } } ``` @@ -124,51 +141,13 @@ No modules. | Name | Type | |------|------| -| [aws_iam_policy.amazon_managed_service_prometheus](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.appmesh_controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.appmesh_envoy_proxy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.aws_gateway_controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.cert_manager](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.cluster_autoscaler](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.ebs_csi](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.efs_csi](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.external_dns](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.external_secrets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.fsx_lustre_csi](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.fsx_openzfs_csi](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.karpenter_controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.load_balancer_controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.load_balancer_controller_targetgroup_only](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.mountpoint_s3_csi](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.node_termination_handler](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.velero](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [aws_iam_policy.vpc_cni](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy_attachment.additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_iam_role_policy_attachment.amazon_cloudwatch_observability](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.amazon_managed_service_prometheus](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.appmesh_controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.appmesh_envoy_proxy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.aws_gateway_controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.cert_manager](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.cluster_autoscaler](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.ebs_csi](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.efs_csi](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.external_dns](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.external_secrets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.fsx_lustre_csi](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.fsx_openzfs_csi](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.karpenter_controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.load_balancer_controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.load_balancer_controller_targetgroup_only](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.mountpoint_s3_csi](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.node_termination_handler](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.velero](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_role_policy_attachment.vpc_cni](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | | [aws_iam_policy_document.amazon_managed_service_prometheus](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_iam_policy_document.appmesh_controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_iam_policy_document.appmesh_envoy_proxy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.assume](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.aws_gateway_controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.cert_manager](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.cluster_autoscaler](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | @@ -178,7 +157,6 @@ No modules. | [aws_iam_policy_document.external_secrets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.fsx_lustre_csi](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.fsx_openzfs_csi](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_iam_policy_document.karpenter_controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.load_balancer_controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.load_balancer_controller_targetgroup_only](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.mountpoint_s3_csi](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | @@ -187,18 +165,14 @@ No modules. | [aws_iam_policy_document.velero](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.vpc_cni](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | -| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [allow\_self\_assume\_role](#input\_allow\_self\_assume\_role) | Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/) | `bool` | `false` | no | -| [amazon\_managed\_service\_prometheus\_workspace\_arns](#input\_amazon\_managed\_service\_prometheus\_workspace\_arns) | List of AMP Workspace ARNs to read and write metrics | `list(string)` |
[
"*"
]
| no | +| [amazon\_managed\_service\_prometheus\_workspace\_arns](#input\_amazon\_managed\_service\_prometheus\_workspace\_arns) | List of AMP Workspace ARNs to read and write metrics | `list(string)` | `[]` | no | | [assume\_role\_condition\_test](#input\_assume\_role\_condition\_test) | Name of the [IAM condition operator](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition_operators.html) to evaluate when assuming the role | `string` | `"StringEquals"` | no | | [attach\_amazon\_managed\_service\_prometheus\_policy](#input\_attach\_amazon\_managed\_service\_prometheus\_policy) | Determines whether to attach the Amazon Managed Service for Prometheus IAM policy to the role | `bool` | `false` | no | -| [attach\_appmesh\_controller\_policy](#input\_attach\_appmesh\_controller\_policy) | Determines whether to attach the Appmesh Controller policy to the role | `bool` | `false` | no | -| [attach\_appmesh\_envoy\_proxy\_policy](#input\_attach\_appmesh\_envoy\_proxy\_policy) | Determines whether to attach the Appmesh envoy proxy policy to the role | `bool` | `false` | no | | [attach\_aws\_gateway\_controller\_policy](#input\_attach\_aws\_gateway\_controller\_policy) | Determines whether to attach the AWS Gateway Controller IAM policy to the role | `bool` | `false` | no | | [attach\_cert\_manager\_policy](#input\_attach\_cert\_manager\_policy) | Determines whether to attach the Cert Manager IAM policy to the role | `bool` | `false` | no | | [attach\_cloudwatch\_observability\_policy](#input\_attach\_cloudwatch\_observability\_policy) | Determines whether to attach the Amazon CloudWatch Observability IAM policies to the role | `bool` | `false` | no | @@ -209,50 +183,45 @@ No modules. | [attach\_external\_secrets\_policy](#input\_attach\_external\_secrets\_policy) | Determines whether to attach the External Secrets policy to the role | `bool` | `false` | no | | [attach\_fsx\_lustre\_csi\_policy](#input\_attach\_fsx\_lustre\_csi\_policy) | Determines whether to attach the FSx for Lustre CSI Driver IAM policy to the role | `bool` | `false` | no | | [attach\_fsx\_openzfs\_csi\_policy](#input\_attach\_fsx\_openzfs\_csi\_policy) | Determines whether to attach the FSx for OpenZFS CSI Driver IAM policy to the role | `bool` | `false` | no | -| [attach\_karpenter\_controller\_policy](#input\_attach\_karpenter\_controller\_policy) | Determines whether to attach the Karpenter Controller policy to the role | `bool` | `false` | no | | [attach\_load\_balancer\_controller\_policy](#input\_attach\_load\_balancer\_controller\_policy) | Determines whether to attach the Load Balancer Controller policy to the role | `bool` | `false` | no | | [attach\_load\_balancer\_controller\_targetgroup\_binding\_only\_policy](#input\_attach\_load\_balancer\_controller\_targetgroup\_binding\_only\_policy) | Determines whether to attach the Load Balancer Controller policy for the TargetGroupBinding only | `bool` | `false` | no | | [attach\_mountpoint\_s3\_csi\_policy](#input\_attach\_mountpoint\_s3\_csi\_policy) | Determines whether to attach the Mountpoint S3 CSI IAM policy to the role | `bool` | `false` | no | | [attach\_node\_termination\_handler\_policy](#input\_attach\_node\_termination\_handler\_policy) | Determines whether to attach the Node Termination Handler policy to the role | `bool` | `false` | no | | [attach\_velero\_policy](#input\_attach\_velero\_policy) | Determines whether to attach the Velero IAM policy to the role | `bool` | `false` | no | | [attach\_vpc\_cni\_policy](#input\_attach\_vpc\_cni\_policy) | Determines whether to attach the VPC CNI IAM policy to the role | `bool` | `false` | no | -| [cert\_manager\_hosted\_zone\_arns](#input\_cert\_manager\_hosted\_zone\_arns) | Route53 hosted zone ARNs to allow Cert manager to manage records | `list(string)` |
[
"arn:aws:route53:::hostedzone/*"
]
| no | -| [cluster\_autoscaler\_cluster\_ids](#input\_cluster\_autoscaler\_cluster\_ids) | [Deprecated - use `cluster_autoscaler_cluster_names`] List of cluster names to appropriately scope permissions within the Cluster Autoscaler IAM policy | `list(string)` | `[]` | no | +| [cert\_manager\_hosted\_zone\_arns](#input\_cert\_manager\_hosted\_zone\_arns) | Route53 hosted zone ARNs to allow Cert manager to manage records | `list(string)` | `[]` | no | | [cluster\_autoscaler\_cluster\_names](#input\_cluster\_autoscaler\_cluster\_names) | List of cluster names to appropriately scope permissions within the Cluster Autoscaler IAM policy | `list(string)` | `[]` | no | -| [create\_role](#input\_create\_role) | Whether to create a role | `bool` | `true` | no | -| [ebs\_csi\_kms\_cmk\_ids](#input\_ebs\_csi\_kms\_cmk\_ids) | KMS CMK IDs to allow EBS CSI to manage encrypted volumes | `list(string)` | `[]` | no | -| [enable\_karpenter\_instance\_profile\_creation](#input\_enable\_karpenter\_instance\_profile\_creation) | Determines whether Karpenter will be allowed to create the IAM instance profile (v1beta1/v0.32+) | `bool` | `false` | no | -| [external\_dns\_hosted\_zone\_arns](#input\_external\_dns\_hosted\_zone\_arns) | Route53 hosted zone ARNs to allow External DNS to manage records | `list(string)` |
[
"arn:aws:route53:::hostedzone/*"
]
| no | -| [external\_secrets\_kms\_key\_arns](#input\_external\_secrets\_kms\_key\_arns) | List of KMS Key ARNs that are used by Secrets Manager that contain secrets to mount using External Secrets | `list(string)` |
[
"arn:aws:kms:*:*:key/*"
]
| no | -| [external\_secrets\_secrets\_manager\_arns](#input\_external\_secrets\_secrets\_manager\_arns) | List of Secrets Manager ARNs that contain secrets to mount using External Secrets | `list(string)` |
[
"arn:aws:secretsmanager:*:*:secret:*"
]
| no | +| [create](#input\_create) | Controls if resources should be created (affects all resources) | `bool` | `true` | no | +| [create\_policy](#input\_create\_policy) | Whether to create an IAM policy that is attached to the IAM role created | `bool` | `true` | no | +| [description](#input\_description) | Description of the role | `string` | `null` | no | +| [ebs\_csi\_kms\_cmk\_arns](#input\_ebs\_csi\_kms\_cmk\_arns) | KMS CMK ARNs to allow EBS CSI to manage encrypted volumes | `list(string)` | `[]` | no | +| [external\_dns\_hosted\_zone\_arns](#input\_external\_dns\_hosted\_zone\_arns) | Route53 hosted zone ARNs to allow External DNS to manage records | `list(string)` | `[]` | no | +| [external\_secrets\_kms\_key\_arns](#input\_external\_secrets\_kms\_key\_arns) | List of KMS Key ARNs that are used by Secrets Manager that contain secrets to mount using External Secrets | `list(string)` | `[]` | no | +| [external\_secrets\_secrets\_manager\_arns](#input\_external\_secrets\_secrets\_manager\_arns) | List of Secrets Manager ARNs that contain secrets to mount using External Secrets | `list(string)` | `[]` | no | | [external\_secrets\_secrets\_manager\_create\_permission](#input\_external\_secrets\_secrets\_manager\_create\_permission) | Determines whether External Secrets may use secretsmanager:CreateSecret | `bool` | `false` | no | -| [external\_secrets\_ssm\_parameter\_arns](#input\_external\_secrets\_ssm\_parameter\_arns) | List of Systems Manager Parameter ARNs that contain secrets to mount using External Secrets | `list(string)` |
[
"arn:aws:ssm:*:*:parameter/*"
]
| no | -| [force\_detach\_policies](#input\_force\_detach\_policies) | Whether policies should be detached from this role when destroying | `bool` | `true` | no | +| [external\_secrets\_ssm\_parameter\_arns](#input\_external\_secrets\_ssm\_parameter\_arns) | List of Systems Manager Parameter ARNs that contain secrets to mount using External Secrets | `list(string)` | `[]` | no | | [fsx\_lustre\_csi\_service\_role\_arns](#input\_fsx\_lustre\_csi\_service\_role\_arns) | Service role ARNs to allow FSx for Lustre CSI create and manage FSX for Lustre service linked roles | `list(string)` |
[
"arn:aws:iam::*:role/aws-service-role/s3.data-source.lustre.fsx.amazonaws.com/*"
]
| no | | [fsx\_openzfs\_csi\_service\_role\_arns](#input\_fsx\_openzfs\_csi\_service\_role\_arns) | Service role ARNs to allow FSx for OpenZFS CSI create and manage FSX for openzfs service linked roles | `list(string)` |
[
"arn:aws:iam::*:role/aws-service-role/fsx.amazonaws.com/*"
]
| no | -| [karpenter\_controller\_cluster\_id](#input\_karpenter\_controller\_cluster\_id) | [Deprecated - use `karpenter_controller_cluster_name`] The name of the cluster where the Karpenter controller is provisioned/managing | `string` | `"*"` | no | -| [karpenter\_controller\_cluster\_name](#input\_karpenter\_controller\_cluster\_name) | The name of the cluster where the Karpenter controller is provisioned/managing | `string` | `"*"` | no | -| [karpenter\_controller\_node\_iam\_role\_arns](#input\_karpenter\_controller\_node\_iam\_role\_arns) | List of node IAM role ARNs Karpenter can use to launch nodes | `list(string)` |
[
"*"
]
| no | -| [karpenter\_controller\_ssm\_parameter\_arns](#input\_karpenter\_controller\_ssm\_parameter\_arns) | List of SSM Parameter ARNs that contain AMI IDs launched by Karpenter | `list(string)` |
[
"arn:aws:ssm:*:*:parameter/aws/service/*"
]
| no | -| [karpenter\_sqs\_queue\_arn](#input\_karpenter\_sqs\_queue\_arn) | (Optional) ARN of SQS used by Karpenter when native node termination handling is enabled | `string` | `null` | no | -| [karpenter\_subnet\_account\_id](#input\_karpenter\_subnet\_account\_id) | Account ID of where the subnets Karpenter will utilize resides. Used when subnets are shared from another account | `string` | `""` | no | -| [karpenter\_tag\_key](#input\_karpenter\_tag\_key) | Tag key (`{key = value}`) applied to resources launched by Karpenter through the Karpenter provisioner | `string` | `"karpenter.sh/discovery"` | no | -| [load\_balancer\_controller\_targetgroup\_arns](#input\_load\_balancer\_controller\_targetgroup\_arns) | List of Target groups ARNs using Load Balancer Controller | `list(string)` |
[
"arn:aws:elasticloadbalancing:*:*:targetgroup/*/*"
]
| no | -| [max\_session\_duration](#input\_max\_session\_duration) | Maximum CLI/API session duration in seconds between 3600 and 43200 | `number` | `null` | no | +| [load\_balancer\_controller\_targetgroup\_arns](#input\_load\_balancer\_controller\_targetgroup\_arns) | List of Target groups ARNs using Load Balancer Controller | `list(string)` | `[]` | no | +| [max\_session\_duration](#input\_max\_session\_duration) | Maximum session duration (in seconds) that you want to set for the specified role. If you do not specify a value for this setting, the default maximum of one hour is applied. This setting can have a value from 1 hour to 12 hours | `number` | `null` | no | | [mountpoint\_s3\_csi\_bucket\_arns](#input\_mountpoint\_s3\_csi\_bucket\_arns) | S3 bucket ARNs to allow Mountpoint S3 CSI to list buckets | `list(string)` | `[]` | no | | [mountpoint\_s3\_csi\_kms\_arns](#input\_mountpoint\_s3\_csi\_kms\_arns) | KMS Key ARNs to allow Mountpoint S3 CSI driver to download and upload Objects of a S3 bucket using `aws:kms` SSE | `list(string)` | `[]` | no | | [mountpoint\_s3\_csi\_path\_arns](#input\_mountpoint\_s3\_csi\_path\_arns) | S3 path ARNs to allow Mountpoint S3 CSI driver to manage items at the provided path(s). This is required if `attach_mountpoint_s3_csi_policy = true` | `list(string)` | `[]` | no | -| [node\_termination\_handler\_sqs\_queue\_arns](#input\_node\_termination\_handler\_sqs\_queue\_arns) | List of SQS ARNs that contain node termination events | `list(string)` |
[
"*"
]
| no | -| [oidc\_providers](#input\_oidc\_providers) | Map of OIDC providers where each provider map should contain the `provider_arn` and `namespace_service_accounts` | `any` | `{}` | no | -| [policy\_name\_prefix](#input\_policy\_name\_prefix) | IAM policy name prefix | `string` | `"AmazonEKS_"` | no | -| [role\_description](#input\_role\_description) | IAM Role description | `string` | `null` | no | -| [role\_name](#input\_role\_name) | Name of IAM role | `string` | `null` | no | -| [role\_name\_prefix](#input\_role\_name\_prefix) | IAM role name prefix | `string` | `null` | no | -| [role\_path](#input\_role\_path) | Path of IAM role | `string` | `"/"` | no | -| [role\_permissions\_boundary\_arn](#input\_role\_permissions\_boundary\_arn) | Permissions boundary ARN to use for IAM role | `string` | `null` | no | -| [role\_policy\_arns](#input\_role\_policy\_arns) | ARNs of any policies to attach to the IAM role | `map(string)` | `{}` | no | -| [tags](#input\_tags) | A map of tags to add the the IAM role | `map(any)` | `{}` | no | -| [velero\_s3\_bucket\_arns](#input\_velero\_s3\_bucket\_arns) | List of S3 Bucket ARNs that Velero needs access to in order to backup and restore cluster resources | `list(string)` |
[
"*"
]
| no | +| [name](#input\_name) | Name to use on IAM role created | `string` | `null` | no | +| [node\_termination\_handler\_sqs\_queue\_arns](#input\_node\_termination\_handler\_sqs\_queue\_arns) | List of SQS ARNs that contain node termination events | `list(string)` | `[]` | no | +| [oidc\_providers](#input\_oidc\_providers) | Map of OIDC providers where each provider map should contain the `provider`, `provider_arn`, and `namespace_service_accounts` | `any` | `{}` | no | +| [override\_policy\_documents](#input\_override\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. In merging, statements with non-blank `sid`s will override statements with the same `sid` | `list(string)` | `[]` | no | +| [path](#input\_path) | Path of IAM role | `string` | `"/"` | no | +| [permissions\_boundary](#input\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no | +| [policies](#input\_policies) | Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no | +| [policy\_description](#input\_policy\_description) | IAM policy description | `string` | `null` | no | +| [policy\_name](#input\_policy\_name) | Name to use on IAM policy created | `string` | `null` | no | +| [policy\_path](#input\_policy\_path) | Path of IAM policy | `string` | `null` | no | +| [policy\_statements](#input\_policy\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage |
map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
variable = string
values = list(string)
})))
}))
| `null` | no | +| [source\_policy\_documents](#input\_source\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s | `list(string)` | `[]` | no | +| [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | +| [use\_name\_prefix](#input\_use\_name\_prefix) | Determines whether the IAM role/policy name (`name`/`policy_name`) is used as a prefix | `bool` | `true` | no | +| [velero\_s3\_bucket\_arns](#input\_velero\_s3\_bucket\_arns) | List of S3 Bucket ARNs that Velero needs access to in order to backup and restore cluster resources | `list(string)` | `[]` | no | | [vpc\_cni\_enable\_cloudwatch\_logs](#input\_vpc\_cni\_enable\_cloudwatch\_logs) | Determines whether to enable VPC CNI permission to create CloudWatch Log groups and publish network policy events | `bool` | `false` | no | | [vpc\_cni\_enable\_ipv4](#input\_vpc\_cni\_enable\_ipv4) | Determines whether to enable IPv4 permissions for VPC CNI policy | `bool` | `false` | no | | [vpc\_cni\_enable\_ipv6](#input\_vpc\_cni\_enable\_ipv6) | Determines whether to enable IPv6 permissions for VPC CNI policy | `bool` | `false` | no | @@ -261,8 +230,14 @@ No modules. | Name | Description | |------|-------------| -| [iam\_role\_arn](#output\_iam\_role\_arn) | ARN of IAM role | -| [iam\_role\_name](#output\_iam\_role\_name) | Name of IAM role | -| [iam\_role\_path](#output\_iam\_role\_path) | Path of IAM role | -| [iam\_role\_unique\_id](#output\_iam\_role\_unique\_id) | Unique ID of IAM role | +| [arn](#output\_arn) | ARN of IAM role | +| [iam\_policy](#output\_iam\_policy) | The policy document | +| [iam\_policy\_arn](#output\_iam\_policy\_arn) | The ARN assigned by AWS to this policy | +| [name](#output\_name) | Name of IAM role | +| [path](#output\_path) | Path of IAM role | +| [unique\_id](#output\_unique\_id) | Unique ID of IAM role | + +## License + +Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-iam/blob/master/LICENSE). diff --git a/modules/iam-role-for-service-accounts/main.tf b/modules/iam-role-for-service-accounts/main.tf new file mode 100644 index 00000000..87acf2c3 --- /dev/null +++ b/modules/iam-role-for-service-accounts/main.tf @@ -0,0 +1,165 @@ +data "aws_partition" "current" { + count = var.create ? 1 : 0 +} + +locals { + partition = try(data.aws_partition.current[0].partition, "") + dns_suffix = try(data.aws_partition.current[0].dns_suffix, "") +} + +################################################################################ +# IAM Role +################################################################################ + +data "aws_iam_policy_document" "assume" { + count = var.create ? 1 : 0 + + dynamic "statement" { + for_each = var.oidc_providers + + content { + effect = "Allow" + actions = ["sts:AssumeRoleWithWebIdentity"] + + principals { + type = "Federated" + identifiers = [statement.value.provider_arn] + } + + condition { + test = var.assume_role_condition_test + variable = "${replace(statement.value.provider_arn, "/^(.*provider/)/", "")}:sub" + values = [for sa in statement.value.namespace_service_accounts : "system:serviceaccount:${sa}"] + } + + # https://aws.amazon.com/premiumsupport/knowledge-center/eks-troubleshoot-oidc-and-irsa/?nc1=h_ls + condition { + test = var.assume_role_condition_test + variable = "${replace(statement.value.provider_arn, "/^(.*provider/)/", "")}:aud" + values = ["sts.amazonaws.com"] + } + } + } +} + +resource "aws_iam_role" "this" { + count = var.create ? 1 : 0 + + name = var.use_name_prefix ? null : var.name + name_prefix = var.use_name_prefix ? "${var.name}-" : null + path = var.path + description = var.description + + assume_role_policy = data.aws_iam_policy_document.assume[0].json + max_session_duration = var.max_session_duration + permissions_boundary = var.permissions_boundary + force_detach_policies = true + + tags = var.tags +} + +resource "aws_iam_role_policy_attachment" "additional" { + for_each = { for k, v in var.policies : k => v if var.create } + + role = aws_iam_role.this[0].name + policy_arn = each.value +} + +################################################################################ +# IAM Policy +################################################################################ + +locals { + create_policy = var.create && var.create_policy + + source_policy_documents = flatten(concat( + data.aws_iam_policy_document.aws_gateway_controller[*].json, + data.aws_iam_policy_document.cert_manager[*].json, + data.aws_iam_policy_document.cluster_autoscaler[*].json, + data.aws_iam_policy_document.ebs_csi[*].json, + data.aws_iam_policy_document.efs_csi[*].json, + data.aws_iam_policy_document.mountpoint_s3_csi[*].json, + data.aws_iam_policy_document.external_dns[*].json, + data.aws_iam_policy_document.external_secrets[*].json, + data.aws_iam_policy_document.fsx_lustre_csi[*].json, + data.aws_iam_policy_document.fsx_openzfs_csi[*].json, + data.aws_iam_policy_document.load_balancer_controller[*].json, + data.aws_iam_policy_document.load_balancer_controller_targetgroup_only[*].json, + data.aws_iam_policy_document.amazon_managed_service_prometheus[*].json, + data.aws_iam_policy_document.node_termination_handler[*].json, + data.aws_iam_policy_document.velero[*].json, + data.aws_iam_policy_document.vpc_cni[*].json, + var.source_policy_documents, + )) +} + +data "aws_iam_policy_document" "this" { + count = local.create_policy ? 1 : 0 + + source_policy_documents = local.source_policy_documents + override_policy_documents = var.override_policy_documents + + dynamic "statement" { + for_each = var.policy_statements != null ? var.policy_statements : {} + + content { + sid = try(coalesce(statement.value.sid, statement.key)) + actions = statement.value.actions + not_actions = statement.value.not_actions + effect = statement.value.effect + resources = statement.value.resources + not_resources = statement.value.not_resources + + dynamic "principals" { + for_each = statement.value.principals != null ? statement.value.principals : [] + + content { + type = principals.value.type + identifiers = principals.value.identifiers + } + } + + dynamic "not_principals" { + for_each = statement.value.not_principals != null ? statement.value.not_principals : [] + + content { + type = not_principals.value.type + identifiers = not_principals.value.identifiers + } + } + + dynamic "condition" { + for_each = statement.value.condition != null ? statement.value.condition : [] + + content { + test = condition.value.test + values = condition.value.values + variable = condition.value.variable + } + } + } + } +} + +locals { + policy_name = try(coalesce(var.policy_name, var.name), "") +} + +resource "aws_iam_policy" "this" { + count = local.create_policy ? 1 : 0 + + name = var.use_name_prefix ? null : local.policy_name + name_prefix = var.use_name_prefix ? "${local.policy_name}-" : null + path = coalesce(var.policy_path, var.path) + description = try(coalesce(var.policy_description, var.description), null) + policy = data.aws_iam_policy_document.this[0].json + + tags = var.tags +} + +resource "aws_iam_role_policy_attachment" "this" { + count = local.create_policy ? 1 : 0 + + role = aws_iam_role.this[0].name + policy_arn = aws_iam_policy.this[0].arn +} diff --git a/modules/iam-role-for-service-accounts/outputs.tf b/modules/iam-role-for-service-accounts/outputs.tf new file mode 100644 index 00000000..a1f0877b --- /dev/null +++ b/modules/iam-role-for-service-accounts/outputs.tf @@ -0,0 +1,37 @@ +################################################################################ +# IAM Role +################################################################################ + +output "arn" { + description = "ARN of IAM role" + value = try(aws_iam_role.this[0].arn, null) +} + +output "name" { + description = "Name of IAM role" + value = try(aws_iam_role.this[0].name, null) +} + +output "path" { + description = "Path of IAM role" + value = try(aws_iam_role.this[0].path, null) +} + +output "unique_id" { + description = "Unique ID of IAM role" + value = try(aws_iam_role.this[0].unique_id, null) +} + +################################################################################ +# IAM Policy +################################################################################ + +output "iam_policy_arn" { + description = "The ARN assigned by AWS to this policy" + value = try(aws_iam_policy.this[0].arn, null) +} + +output "iam_policy" { + description = "The policy document" + value = try(aws_iam_policy.this[0].policy, null) +} diff --git a/modules/iam-role-for-service-accounts-eks/policies.tf b/modules/iam-role-for-service-accounts/policies.tf similarity index 57% rename from modules/iam-role-for-service-accounts-eks/policies.tf rename to modules/iam-role-for-service-accounts/policies.tf index 5f48d33f..cd3d5527 100644 --- a/modules/iam-role-for-service-accounts-eks/policies.tf +++ b/modules/iam-role-for-service-accounts/policies.tf @@ -3,7 +3,7 @@ ################################################################################ data "aws_iam_policy_document" "aws_gateway_controller" { - count = var.create_role && var.attach_aws_gateway_controller_policy ? 1 : 0 + count = var.create && var.attach_aws_gateway_controller_policy ? 1 : 0 # https://github.com/aws/aws-application-networking-k8s/blob/v1.1.0/files/controller-installation/recommended-inline-policy.json statement { @@ -50,31 +50,13 @@ data "aws_iam_policy_document" "aws_gateway_controller" { } } -resource "aws_iam_policy" "aws_gateway_controller" { - count = var.create_role && var.attach_aws_gateway_controller_policy ? 1 : 0 - - name_prefix = "${var.policy_name_prefix}AWSGatewayController-" - path = var.role_path - description = "Provides permissions for the AWS Gateway Controller" - policy = data.aws_iam_policy_document.aws_gateway_controller[0].json - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "aws_gateway_controller" { - count = var.create_role && var.attach_aws_gateway_controller_policy ? 1 : 0 - - role = aws_iam_role.this[0].name - policy_arn = aws_iam_policy.aws_gateway_controller[0].arn -} - ################################################################################ # Cert Manager Policy ################################################################################ # https://cert-manager.io/docs/configuration/acme/dns01/route53/#set-up-an-iam-role data "aws_iam_policy_document" "cert_manager" { - count = var.create_role && var.attach_cert_manager_policy ? 1 : 0 + count = var.create && var.attach_cert_manager_policy ? 1 : 0 statement { actions = ["route53:GetChange"] @@ -96,31 +78,13 @@ data "aws_iam_policy_document" "cert_manager" { } } -resource "aws_iam_policy" "cert_manager" { - count = var.create_role && var.attach_cert_manager_policy ? 1 : 0 - - name_prefix = "${var.policy_name_prefix}Cert_Manager_Policy-" - path = var.role_path - description = "Cert Manager policy to allow management of Route53 hosted zone records" - policy = data.aws_iam_policy_document.cert_manager[0].json - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "cert_manager" { - count = var.create_role && var.attach_cert_manager_policy ? 1 : 0 - - role = aws_iam_role.this[0].name - policy_arn = aws_iam_policy.cert_manager[0].arn -} - ################################################################################ # Cluster Autoscaler Policy ################################################################################ # https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/cloudprovider/aws/README.md data "aws_iam_policy_document" "cluster_autoscaler" { - count = var.create_role && var.attach_cluster_autoscaler_policy ? 1 : 0 + count = var.create && var.attach_cluster_autoscaler_policy ? 1 : 0 statement { actions = [ @@ -140,8 +104,7 @@ data "aws_iam_policy_document" "cluster_autoscaler" { } dynamic "statement" { - # TODO - remove *_ids at next breaking change - for_each = toset(coalescelist(var.cluster_autoscaler_cluster_ids, var.cluster_autoscaler_cluster_names)) + for_each = toset(var.cluster_autoscaler_cluster_names) content { actions = [ @@ -160,31 +123,13 @@ data "aws_iam_policy_document" "cluster_autoscaler" { } } -resource "aws_iam_policy" "cluster_autoscaler" { - count = var.create_role && var.attach_cluster_autoscaler_policy ? 1 : 0 - - name_prefix = "${var.policy_name_prefix}Cluster_Autoscaler_Policy-" - path = var.role_path - description = "Cluster autoscaler policy to allow examination and modification of EC2 Auto Scaling Groups" - policy = data.aws_iam_policy_document.cluster_autoscaler[0].json - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "cluster_autoscaler" { - count = var.create_role && var.attach_cluster_autoscaler_policy ? 1 : 0 - - role = aws_iam_role.this[0].name - policy_arn = aws_iam_policy.cluster_autoscaler[0].arn -} - ################################################################################ # EBS CSI Policy ################################################################################ # https://github.com/kubernetes-sigs/aws-ebs-csi-driver/blob/master/docs/example-iam-policy.json data "aws_iam_policy_document" "ebs_csi" { - count = var.create_role && var.attach_ebs_csi_policy ? 1 : 0 + count = var.create && var.attach_ebs_csi_policy ? 1 : 0 statement { actions = [ @@ -354,7 +299,7 @@ data "aws_iam_policy_document" "ebs_csi" { } dynamic "statement" { - for_each = length(var.ebs_csi_kms_cmk_ids) > 0 ? [1] : [] + for_each = length(var.ebs_csi_kms_cmk_arns) > 0 ? [1] : [] content { actions = [ @@ -363,7 +308,7 @@ data "aws_iam_policy_document" "ebs_csi" { "kms:RevokeGrant", ] - resources = var.ebs_csi_kms_cmk_ids + resources = var.ebs_csi_kms_cmk_arns condition { test = "Bool" @@ -374,7 +319,7 @@ data "aws_iam_policy_document" "ebs_csi" { } dynamic "statement" { - for_each = length(var.ebs_csi_kms_cmk_ids) > 0 ? [1] : [] + for_each = length(var.ebs_csi_kms_cmk_arns) > 0 ? [1] : [] content { actions = [ @@ -385,36 +330,18 @@ data "aws_iam_policy_document" "ebs_csi" { "kms:DescribeKey", ] - resources = var.ebs_csi_kms_cmk_ids + resources = var.ebs_csi_kms_cmk_arns } } } -resource "aws_iam_policy" "ebs_csi" { - count = var.create_role && var.attach_ebs_csi_policy ? 1 : 0 - - name_prefix = "${var.policy_name_prefix}EBS_CSI_Policy-" - path = var.role_path - description = "Provides permissions to manage EBS volumes via the container storage interface driver" - policy = data.aws_iam_policy_document.ebs_csi[0].json - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "ebs_csi" { - count = var.create_role && var.attach_ebs_csi_policy ? 1 : 0 - - role = aws_iam_role.this[0].name - policy_arn = aws_iam_policy.ebs_csi[0].arn -} - ################################################################################ # EFS CSI Driver Policy ################################################################################ # https://github.com/kubernetes-sigs/aws-efs-csi-driver/blob/master/docs/iam-policy-example.json data "aws_iam_policy_document" "efs_csi" { - count = var.create_role && var.attach_efs_csi_policy ? 1 : 0 + count = var.create && var.attach_efs_csi_policy ? 1 : 0 statement { actions = [ @@ -461,31 +388,13 @@ data "aws_iam_policy_document" "efs_csi" { } } -resource "aws_iam_policy" "efs_csi" { - count = var.create_role && var.attach_efs_csi_policy ? 1 : 0 - - name_prefix = "${var.policy_name_prefix}EFS_CSI_Policy-" - path = var.role_path - description = "Provides permissions to manage EFS volumes via the container storage interface driver" - policy = data.aws_iam_policy_document.efs_csi[0].json - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "efs_csi" { - count = var.create_role && var.attach_efs_csi_policy ? 1 : 0 - - role = aws_iam_role.this[0].name - policy_arn = aws_iam_policy.efs_csi[0].arn -} - ################################################################################ # Mountpoint S3 CSI Driver Policy ################################################################################ #https://github.com/awslabs/mountpoint-s3/blob/main/doc/CONFIGURATION.md#iam-permissions data "aws_iam_policy_document" "mountpoint_s3_csi" { - count = var.create_role && var.attach_mountpoint_s3_csi_policy ? 1 : 0 + count = var.create && var.attach_mountpoint_s3_csi_policy ? 1 : 0 statement { sid = "MountpointFullBucketAccess" @@ -518,31 +427,13 @@ data "aws_iam_policy_document" "mountpoint_s3_csi" { } } -resource "aws_iam_policy" "mountpoint_s3_csi" { - count = var.create_role && var.attach_mountpoint_s3_csi_policy ? 1 : 0 - - name_prefix = "${var.policy_name_prefix}Mountpoint_S3_CSI-" - path = var.role_path - description = "Mountpoint S3 CSI driver policy to allow management of S3" - policy = data.aws_iam_policy_document.mountpoint_s3_csi[0].json - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "mountpoint_s3_csi" { - count = var.create_role && var.attach_mountpoint_s3_csi_policy ? 1 : 0 - - role = aws_iam_role.this[0].name - policy_arn = aws_iam_policy.mountpoint_s3_csi[0].arn -} - ################################################################################ # External DNS Policy ################################################################################ # https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/aws.md#iam-policy data "aws_iam_policy_document" "external_dns" { - count = var.create_role && var.attach_external_dns_policy ? 1 : 0 + count = var.create && var.attach_external_dns_policy ? 1 : 0 statement { actions = ["route53:ChangeResourceRecordSets"] @@ -560,31 +451,13 @@ data "aws_iam_policy_document" "external_dns" { } } -resource "aws_iam_policy" "external_dns" { - count = var.create_role && var.attach_external_dns_policy ? 1 : 0 - - name_prefix = "${var.policy_name_prefix}External_DNS_Policy-" - path = var.role_path - description = "External DNS policy to allow management of Route53 hosted zone records" - policy = data.aws_iam_policy_document.external_dns[0].json - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "external_dns" { - count = var.create_role && var.attach_external_dns_policy ? 1 : 0 - - role = aws_iam_role.this[0].name - policy_arn = aws_iam_policy.external_dns[0].arn -} - ################################################################################ # External Secrets Policy ################################################################################ # https://github.com/external-secrets/external-secrets#add-a-secret data "aws_iam_policy_document" "external_secrets" { - count = var.create_role && var.attach_external_secrets_policy ? 1 : 0 + count = var.create && var.attach_external_secrets_policy ? 1 : 0 statement { actions = ["ssm:DescribeParameters"] @@ -658,31 +531,13 @@ data "aws_iam_policy_document" "external_secrets" { } } -resource "aws_iam_policy" "external_secrets" { - count = var.create_role && var.attach_external_secrets_policy ? 1 : 0 - - name_prefix = "${var.policy_name_prefix}External_Secrets_Policy-" - path = var.role_path - description = "Provides permissions to for External Secrets to retrieve secrets from AWS SSM and AWS Secrets Manager" - policy = data.aws_iam_policy_document.external_secrets[0].json - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "external_secrets" { - count = var.create_role && var.attach_external_secrets_policy ? 1 : 0 - - role = aws_iam_role.this[0].name - policy_arn = aws_iam_policy.external_secrets[0].arn -} - ################################################################################ # FSx for Lustre CSI Driver Policy ################################################################################ # https://github.com/kubernetes-sigs/aws-fsx-csi-driver/blob/master/docs/README.md data "aws_iam_policy_document" "fsx_lustre_csi" { - count = var.create_role && var.attach_fsx_lustre_csi_policy ? 1 : 0 + count = var.create && var.attach_fsx_lustre_csi_policy ? 1 : 0 statement { actions = [ @@ -717,31 +572,13 @@ data "aws_iam_policy_document" "fsx_lustre_csi" { } } -resource "aws_iam_policy" "fsx_lustre_csi" { - count = var.create_role && var.attach_fsx_lustre_csi_policy ? 1 : 0 - - name_prefix = "${var.policy_name_prefix}FSx_Lustre_CSI_Policy-" - path = var.role_path - description = "Provides permissions to manage FSx Lustre volumes via the container storage interface driver" - policy = data.aws_iam_policy_document.fsx_lustre_csi[0].json - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "fsx_lustre_csi" { - count = var.create_role && var.attach_fsx_lustre_csi_policy ? 1 : 0 - - role = aws_iam_role.this[0].name - policy_arn = aws_iam_policy.fsx_lustre_csi[0].arn -} - ################################################################################ # FSx for OpenZFS CSI Driver Policy ################################################################################ # https://github.com/kubernetes-sigs/aws-fsx-openzfs-csi-driver/blob/main/docs/install.md data "aws_iam_policy_document" "fsx_openzfs_csi" { - count = var.create_role && var.attach_fsx_openzfs_csi_policy ? 1 : 0 + count = var.create && var.attach_fsx_openzfs_csi_policy ? 1 : 0 statement { actions = [ @@ -782,168 +619,13 @@ data "aws_iam_policy_document" "fsx_openzfs_csi" { } } -resource "aws_iam_policy" "fsx_openzfs_csi" { - count = var.create_role && var.attach_fsx_openzfs_csi_policy ? 1 : 0 - - name_prefix = "${var.policy_name_prefix}FSx_OpenZFS_CSI_Policy-" - path = var.role_path - description = "Provides permissions to manage FSx OpenZFS volumes via the container storage interface driver" - policy = data.aws_iam_policy_document.fsx_openzfs_csi[0].json - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "fsx_openzfs_csi" { - count = var.create_role && var.attach_fsx_openzfs_csi_policy ? 1 : 0 - - role = aws_iam_role.this[0].name - policy_arn = aws_iam_policy.fsx_openzfs_csi[0].arn -} - -################################################################################ -# Karpenter Controller Policy -################################################################################ - -locals { - # TODO - remove this at next breaking change - karpenter_controller_cluster_name = var.karpenter_controller_cluster_name != "*" ? var.karpenter_controller_cluster_name : var.karpenter_controller_cluster_id -} - -# https://github.com/aws/karpenter/blob/502d275cc330fb0f2435b124935c49632146d945/website/content/en/v0.19.0/getting-started/getting-started-with-eksctl/cloudformation.yaml#L34 -data "aws_iam_policy_document" "karpenter_controller" { - count = var.create_role && var.attach_karpenter_controller_policy ? 1 : 0 - - statement { - actions = [ - "ec2:CreateFleet", - "ec2:CreateLaunchTemplate", - "ec2:CreateTags", - "ec2:DescribeAvailabilityZones", - "ec2:DescribeImages", - "ec2:DescribeInstances", - "ec2:DescribeInstanceTypeOfferings", - "ec2:DescribeInstanceTypes", - "ec2:DescribeLaunchTemplates", - "ec2:DescribeSecurityGroups", - "ec2:DescribeSpotPriceHistory", - "ec2:DescribeSubnets", - "pricing:GetProducts", - ] - - resources = ["*"] - } - - statement { - actions = [ - "ec2:TerminateInstances", - "ec2:DeleteLaunchTemplate", - ] - resources = ["*"] - - condition { - test = "StringEquals" - variable = "ec2:ResourceTag/${var.karpenter_tag_key}" - values = [local.karpenter_controller_cluster_name] - } - } - - statement { - actions = ["ec2:RunInstances"] - resources = [ - "arn:${local.partition}:ec2:*:${local.account_id}:launch-template/*", - ] - - condition { - test = "StringEquals" - variable = "ec2:ResourceTag/${var.karpenter_tag_key}" - values = [local.karpenter_controller_cluster_name] - } - } - - statement { - actions = ["ec2:RunInstances"] - resources = [ - "arn:${local.partition}:ec2:*::image/*", - "arn:${local.partition}:ec2:*:${local.account_id}:instance/*", - "arn:${local.partition}:ec2:*:${local.account_id}:spot-instances-request/*", - "arn:${local.partition}:ec2:*:${local.account_id}:security-group/*", - "arn:${local.partition}:ec2:*:${local.account_id}:volume/*", - "arn:${local.partition}:ec2:*:${local.account_id}:network-interface/*", - "arn:${local.partition}:ec2:*:${coalesce(var.karpenter_subnet_account_id, local.account_id)}:subnet/*", - ] - } - - statement { - actions = ["ssm:GetParameter"] - resources = var.karpenter_controller_ssm_parameter_arns - } - - statement { - actions = ["iam:PassRole"] - resources = var.karpenter_controller_node_iam_role_arns - } - - dynamic "statement" { - for_each = var.enable_karpenter_instance_profile_creation ? [1] : [] - - content { - actions = [ - "iam:AddRoleToInstanceProfile", - "iam:CreateInstanceProfile", - "iam:DeleteInstanceProfile", - "iam:GetInstanceProfile", - "iam:RemoveRoleFromInstanceProfile", - "iam:TagInstanceProfile", - ] - resources = ["*"] - } - } - - statement { - actions = ["eks:DescribeCluster"] - resources = ["arn:${local.partition}:eks:${local.region}:${local.account_id}:cluster/${local.karpenter_controller_cluster_name}"] - } - - dynamic "statement" { - for_each = var.karpenter_sqs_queue_arn != null ? [1] : [] - - content { - actions = [ - "sqs:DeleteMessage", - "sqs:GetQueueAttributes", - "sqs:GetQueueUrl", - "sqs:ReceiveMessage", - ] - resources = [var.karpenter_sqs_queue_arn] - } - } -} - -resource "aws_iam_policy" "karpenter_controller" { - count = var.create_role && var.attach_karpenter_controller_policy ? 1 : 0 - - name_prefix = "${var.policy_name_prefix}Karpenter_Controller_Policy-" - path = var.role_path - description = "Provides permissions to handle node termination events via the Node Termination Handler" - policy = data.aws_iam_policy_document.karpenter_controller[0].json - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "karpenter_controller" { - count = var.create_role && var.attach_karpenter_controller_policy ? 1 : 0 - - role = aws_iam_role.this[0].name - policy_arn = aws_iam_policy.karpenter_controller[0].arn -} - ################################################################################ # AWS Load Balancer Controller Policy ################################################################################ # https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/main/docs/install/iam_policy.json data "aws_iam_policy_document" "load_balancer_controller" { - count = var.create_role && var.attach_load_balancer_controller_policy ? 1 : 0 + count = var.create && var.attach_load_balancer_controller_policy ? 1 : 0 statement { actions = ["iam:CreateServiceLinkedRole"] @@ -1212,24 +894,6 @@ data "aws_iam_policy_document" "load_balancer_controller" { } } -resource "aws_iam_policy" "load_balancer_controller" { - count = var.create_role && var.attach_load_balancer_controller_policy ? 1 : 0 - - name_prefix = "${var.policy_name_prefix}AWS_Load_Balancer_Controller-" - path = var.role_path - description = "Provides permissions for AWS Load Balancer Controller addon" - policy = data.aws_iam_policy_document.load_balancer_controller[0].json - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "load_balancer_controller" { - count = var.create_role && var.attach_load_balancer_controller_policy ? 1 : 0 - - role = aws_iam_role.this[0].name - policy_arn = aws_iam_policy.load_balancer_controller[0].arn -} - ################################################################################ # AWS Load Balancer Controller TargetGroup Binding Only Policy ################################################################################ @@ -1237,7 +901,7 @@ resource "aws_iam_role_policy_attachment" "load_balancer_controller" { # https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.4/guide/targetgroupbinding/targetgroupbinding/#reference # https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.4/deploy/installation/#setup-iam-manually data "aws_iam_policy_document" "load_balancer_controller_targetgroup_only" { - count = var.create_role && var.attach_load_balancer_controller_targetgroup_binding_only_policy ? 1 : 0 + count = var.create && var.attach_load_balancer_controller_targetgroup_binding_only_policy ? 1 : 0 statement { actions = [ @@ -1265,187 +929,13 @@ data "aws_iam_policy_document" "load_balancer_controller_targetgroup_only" { } } -resource "aws_iam_policy" "load_balancer_controller_targetgroup_only" { - count = var.create_role && var.attach_load_balancer_controller_targetgroup_binding_only_policy ? 1 : 0 - - name_prefix = "${var.policy_name_prefix}AWS_Load_Balancer_Controller_TargetGroup_Only-" - path = var.role_path - description = "Provides permissions for AWS Load Balancer Controller addon in TargetGroup binding only scenario" - policy = data.aws_iam_policy_document.load_balancer_controller_targetgroup_only[0].json - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "load_balancer_controller_targetgroup_only" { - count = var.create_role && var.attach_load_balancer_controller_targetgroup_binding_only_policy ? 1 : 0 - - role = aws_iam_role.this[0].name - policy_arn = aws_iam_policy.load_balancer_controller_targetgroup_only[0].arn -} - -################################################################################ -# Appmesh Controller -################################################################################ -# https://github.com/aws/eks-charts/tree/master/stable/appmesh-controller#prerequisites -# https://raw.githubusercontent.com/aws/aws-app-mesh-controller-for-k8s/master/config/iam/controller-iam-policy.json -data "aws_iam_policy_document" "appmesh_controller" { - count = var.create_role && var.attach_appmesh_controller_policy ? 1 : 0 - - statement { - actions = [ - "appmesh:ListVirtualRouters", - "appmesh:ListVirtualServices", - "appmesh:ListRoutes", - "appmesh:ListGatewayRoutes", - "appmesh:ListMeshes", - "appmesh:ListVirtualNodes", - "appmesh:ListVirtualGateways", - "appmesh:DescribeMesh", - "appmesh:DescribeVirtualRouter", - "appmesh:DescribeRoute", - "appmesh:DescribeVirtualNode", - "appmesh:DescribeVirtualGateway", - "appmesh:DescribeGatewayRoute", - "appmesh:DescribeVirtualService", - "appmesh:CreateMesh", - "appmesh:CreateVirtualRouter", - "appmesh:CreateVirtualGateway", - "appmesh:CreateVirtualService", - "appmesh:CreateGatewayRoute", - "appmesh:CreateRoute", - "appmesh:CreateVirtualNode", - "appmesh:UpdateMesh", - "appmesh:UpdateRoute", - "appmesh:UpdateVirtualGateway", - "appmesh:UpdateVirtualRouter", - "appmesh:UpdateGatewayRoute", - "appmesh:UpdateVirtualService", - "appmesh:UpdateVirtualNode", - "appmesh:DeleteMesh", - "appmesh:DeleteRoute", - "appmesh:DeleteVirtualRouter", - "appmesh:DeleteGatewayRoute", - "appmesh:DeleteVirtualService", - "appmesh:DeleteVirtualNode", - "appmesh:DeleteVirtualGateway" - ] - resources = ["*"] - } - - statement { - actions = [ - "iam:CreateServiceLinkedRole" - ] - resources = ["arn:${local.partition}:iam::*:role/aws-service-role/appmesh.${local.dns_suffix}/AWSServiceRoleForAppMesh"] - - condition { - test = "StringLike" - variable = "iam:AWSServiceName" - values = ["appmesh.${local.dns_suffix}"] - } - } - - statement { - actions = [ - "acm:ListCertificates", - "acm:DescribeCertificate", - "acm-pca:DescribeCertificateAuthority", - "acm-pca:ListCertificateAuthorities" - ] - resources = ["*"] - } - - statement { - actions = [ - "servicediscovery:CreateService", - "servicediscovery:DeleteService", - "servicediscovery:GetService", - "servicediscovery:GetInstance", - "servicediscovery:RegisterInstance", - "servicediscovery:DeregisterInstance", - "servicediscovery:ListInstances", - "servicediscovery:ListNamespaces", - "servicediscovery:ListServices", - "servicediscovery:GetInstancesHealthStatus", - "servicediscovery:UpdateInstanceCustomHealthStatus", - "servicediscovery:GetOperation", - "route53:GetHealthCheck", - "route53:CreateHealthCheck", - "route53:UpdateHealthCheck", - "route53:ChangeResourceRecordSets", - "route53:DeleteHealthCheck" - ] - resources = ["*"] - } -} - -resource "aws_iam_policy" "appmesh_controller" { - count = var.create_role && var.attach_appmesh_controller_policy ? 1 : 0 - - name_prefix = "${var.policy_name_prefix}Appmesh_Controller-" - path = var.role_path - description = "Provides permissions to for appmesh controller" - policy = data.aws_iam_policy_document.appmesh_controller[0].json - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "appmesh_controller" { - count = var.create_role && var.attach_appmesh_controller_policy ? 1 : 0 - - role = aws_iam_role.this[0].name - policy_arn = aws_iam_policy.appmesh_controller[0].arn -} - -################################################################################ -# Appmesh envoy proxy -################################################################################ -# https://github.com/aws/aws-app-mesh-controller-for-k8s/blob/f4a551399c4a4428d31692d0e6d944c2b78f2753/config/helm/appmesh-controller/README.md#with-irsa -# https://raw.githubusercontent.com/aws/aws-app-mesh-controller-for-k8s/master/config/iam/envoy-iam-policy.json -data "aws_iam_policy_document" "appmesh_envoy_proxy" { - count = var.create_role && var.attach_appmesh_envoy_proxy_policy ? 1 : 0 - - statement { - actions = [ - "appmesh:StreamAggregatedResources" - ] - resources = ["*"] - } - - statement { - actions = [ - "acm:ExportCertificate", - "acm-pca:GetCertificateAuthorityCertificate" - ] - resources = ["*"] - } -} - -resource "aws_iam_policy" "appmesh_envoy_proxy" { - count = var.create_role && var.attach_appmesh_envoy_proxy_policy ? 1 : 0 - - name_prefix = "${var.policy_name_prefix}Appmesh_Envoy_Proxy-" - path = var.role_path - description = "Provides permissions to for appmesh envoy proxy" - policy = data.aws_iam_policy_document.appmesh_envoy_proxy[0].json - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "appmesh_envoy_proxy" { - count = var.create_role && var.attach_appmesh_envoy_proxy_policy ? 1 : 0 - - role = aws_iam_role.this[0].name - policy_arn = aws_iam_policy.appmesh_envoy_proxy[0].arn -} - ################################################################################ # Amazon Managed Service for Prometheus Policy ################################################################################ # https://docs.aws.amazon.com/prometheus/latest/userguide/set-up-irsa.html data "aws_iam_policy_document" "amazon_managed_service_prometheus" { - count = var.create_role && var.attach_amazon_managed_service_prometheus_policy ? 1 : 0 + count = var.create && var.attach_amazon_managed_service_prometheus_policy ? 1 : 0 statement { actions = [ @@ -1460,31 +950,13 @@ data "aws_iam_policy_document" "amazon_managed_service_prometheus" { } } -resource "aws_iam_policy" "amazon_managed_service_prometheus" { - count = var.create_role && var.attach_amazon_managed_service_prometheus_policy ? 1 : 0 - - name_prefix = "${var.policy_name_prefix}Managed_Service_Prometheus_Policy-" - path = var.role_path - description = "Provides permissions to for Amazon Managed Service for Prometheus" - policy = data.aws_iam_policy_document.amazon_managed_service_prometheus[0].json - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "amazon_managed_service_prometheus" { - count = var.create_role && var.attach_amazon_managed_service_prometheus_policy ? 1 : 0 - - role = aws_iam_role.this[0].name - policy_arn = aws_iam_policy.amazon_managed_service_prometheus[0].arn -} - ################################################################################ # Node Termination Handler Policy ################################################################################ # https://github.com/aws/aws-node-termination-handler#5-create-an-iam-role-for-the-pods data "aws_iam_policy_document" "node_termination_handler" { - count = var.create_role && var.attach_node_termination_handler_policy ? 1 : 0 + count = var.create && var.attach_node_termination_handler_policy ? 1 : 0 statement { actions = [ @@ -1507,31 +979,13 @@ data "aws_iam_policy_document" "node_termination_handler" { } } -resource "aws_iam_policy" "node_termination_handler" { - count = var.create_role && var.attach_node_termination_handler_policy ? 1 : 0 - - name_prefix = "${var.policy_name_prefix}Node_Termination_Handler_Policy-" - path = var.role_path - description = "Provides permissions to handle node termination events via the Node Termination Handler" - policy = data.aws_iam_policy_document.node_termination_handler[0].json - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "node_termination_handler" { - count = var.create_role && var.attach_node_termination_handler_policy ? 1 : 0 - - role = aws_iam_role.this[0].name - policy_arn = aws_iam_policy.node_termination_handler[0].arn -} - ################################################################################ # Velero Policy ################################################################################ # https://github.com/vmware-tanzu/velero-plugin-for-aws#set-permissions-for-velero data "aws_iam_policy_document" "velero" { - count = var.create_role && var.attach_velero_policy ? 1 : 0 + count = var.create && var.attach_velero_policy ? 1 : 0 statement { sid = "Ec2ReadWrite" @@ -1568,30 +1022,12 @@ data "aws_iam_policy_document" "velero" { } } -resource "aws_iam_policy" "velero" { - count = var.create_role && var.attach_velero_policy ? 1 : 0 - - name_prefix = "${var.policy_name_prefix}Velero_Policy-" - path = var.role_path - description = "Provides Velero permissions to backup and restore cluster resources" - policy = data.aws_iam_policy_document.velero[0].json - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "velero" { - count = var.create_role && var.attach_velero_policy ? 1 : 0 - - role = aws_iam_role.this[0].name - policy_arn = aws_iam_policy.velero[0].arn -} - ################################################################################ # VPC CNI Policy ################################################################################ data "aws_iam_policy_document" "vpc_cni" { - count = var.create_role && var.attach_vpc_cni_policy ? 1 : 0 + count = var.create && var.attach_vpc_cni_policy ? 1 : 0 # arn:${local.partition}:iam::aws:policy/AmazonEKS_CNI_Policy dynamic "statement" { @@ -1657,24 +1093,6 @@ data "aws_iam_policy_document" "vpc_cni" { } } -resource "aws_iam_policy" "vpc_cni" { - count = var.create_role && var.attach_vpc_cni_policy ? 1 : 0 - - name_prefix = "${var.policy_name_prefix}CNI_Policy-" - path = var.role_path - description = "Provides the Amazon VPC CNI Plugin (amazon-vpc-cni-k8s) the permissions it requires to modify the IPv4/IPv6 address configuration on your EKS worker nodes" - policy = data.aws_iam_policy_document.vpc_cni[0].json - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "vpc_cni" { - count = var.create_role && var.attach_vpc_cni_policy ? 1 : 0 - - role = aws_iam_role.this[0].name - policy_arn = aws_iam_policy.vpc_cni[0].arn -} - ################################################################################ # Amazon CloudWatch Observability Policy ################################################################################ @@ -1683,7 +1101,7 @@ resource "aws_iam_role_policy_attachment" "amazon_cloudwatch_observability" { for_each = { for k, v in { CloudWatchAgentServerPolicy = "arn:${local.partition}:iam::aws:policy/CloudWatchAgentServerPolicy" AWSXrayWriteOnlyAccess = "arn:${local.partition}:iam::aws:policy/AWSXrayWriteOnlyAccess" - } : k => v if var.create_role && var.attach_cloudwatch_observability_policy } + } : k => v if var.create && var.attach_cloudwatch_observability_policy } role = aws_iam_role.this[0].name policy_arn = each.value diff --git a/modules/iam-role-for-service-accounts-eks/variables.tf b/modules/iam-role-for-service-accounts/variables.tf similarity index 67% rename from modules/iam-role-for-service-accounts-eks/variables.tf rename to modules/iam-role-for-service-accounts/variables.tf index 8dce3e2f..fa2a4ca4 100644 --- a/modules/iam-role-for-service-accounts-eks/variables.tf +++ b/modules/iam-role-for-service-accounts/variables.tf @@ -1,85 +1,137 @@ -variable "create_role" { - description = "Whether to create a role" +variable "create" { + description = "Controls if resources should be created (affects all resources)" type = bool default = true } -variable "role_name" { - description = "Name of IAM role" +variable "tags" { + description = "A map of tags to add to all resources" + type = map(string) + default = {} +} + +################################################################################ +# IAM Role +################################################################################ + +variable "name" { + description = "Name to use on IAM role created" type = string default = null } -variable "role_path" { +variable "use_name_prefix" { + description = "Determines whether the IAM role/policy name (`name`/`policy_name`) is used as a prefix" + type = bool + default = true +} + +variable "path" { description = "Path of IAM role" type = string default = "/" } -variable "role_permissions_boundary_arn" { - description = "Permissions boundary ARN to use for IAM role" +variable "description" { + description = "Description of the role" type = string default = null } -variable "role_description" { - description = "IAM Role description" - type = string +variable "max_session_duration" { + description = "Maximum session duration (in seconds) that you want to set for the specified role. If you do not specify a value for this setting, the default maximum of one hour is applied. This setting can have a value from 1 hour to 12 hours" + type = number default = null } -variable "role_name_prefix" { - description = "IAM role name prefix" +variable "permissions_boundary" { + description = "ARN of the policy that is used to set the permissions boundary for the IAM role" type = string default = null } -variable "policy_name_prefix" { - description = "IAM policy name prefix" +variable "assume_role_condition_test" { + description = "Name of the [IAM condition operator](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition_operators.html) to evaluate when assuming the role" type = string - default = "AmazonEKS_" -} - -variable "role_policy_arns" { - description = "ARNs of any policies to attach to the IAM role" - type = map(string) - default = {} + default = "StringEquals" } variable "oidc_providers" { - description = "Map of OIDC providers where each provider map should contain the `provider_arn` and `namespace_service_accounts`" + description = "Map of OIDC providers where each provider map should contain the `provider`, `provider_arn`, and `namespace_service_accounts`" type = any default = {} } -variable "tags" { - description = "A map of tags to add the the IAM role" - type = map(any) +variable "policies" { + description = "Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format" + type = map(string) default = {} } -variable "force_detach_policies" { - description = "Whether policies should be detached from this role when destroying" +################################################################################ +# IAM Policy +################################################################################ + +variable "create_policy" { + description = "Whether to create an IAM policy that is attached to the IAM role created" type = bool default = true } -variable "max_session_duration" { - description = "Maximum CLI/API session duration in seconds between 3600 and 43200" - type = number +variable "source_policy_documents" { + description = "List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s" + type = list(string) + default = [] +} + +variable "override_policy_documents" { + description = "List of IAM policy documents that are merged together into the exported document. In merging, statements with non-blank `sid`s will override statements with the same `sid`" + type = list(string) + default = [] +} + +variable "policy_statements" { + description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage" + type = map(object({ + sid = optional(string) + actions = optional(list(string)) + not_actions = optional(list(string)) + effect = optional(string, "Allow") + resources = optional(list(string)) + not_resources = optional(list(string)) + principals = optional(list(object({ + type = string + identifiers = list(string) + }))) + not_principals = optional(list(object({ + type = string + identifiers = list(string) + }))) + condition = optional(list(object({ + test = string + variable = string + values = list(string) + }))) + })) + default = null +} + +variable "policy_name" { + description = "Name to use on IAM policy created" + type = string default = null } -variable "assume_role_condition_test" { - description = "Name of the [IAM condition operator](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition_operators.html) to evaluate when assuming the role" +variable "policy_path" { + description = "Path of IAM policy" type = string - default = "StringEquals" + default = null } -variable "allow_self_assume_role" { - description = "Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/)" - type = bool - default = false +variable "policy_description" { + description = "IAM policy description" + type = string + default = null } ################################################################################ @@ -103,7 +155,7 @@ variable "attach_cert_manager_policy" { variable "cert_manager_hosted_zone_arns" { description = "Route53 hosted zone ARNs to allow Cert manager to manage records" type = list(string) - default = ["arn:aws:route53:::hostedzone/*"] + default = [] } # Cluster autoscaler @@ -113,12 +165,6 @@ variable "attach_cluster_autoscaler_policy" { default = false } -variable "cluster_autoscaler_cluster_ids" { - description = "[Deprecated - use `cluster_autoscaler_cluster_names`] List of cluster names to appropriately scope permissions within the Cluster Autoscaler IAM policy" - type = list(string) - default = [] -} - variable "cluster_autoscaler_cluster_names" { description = "List of cluster names to appropriately scope permissions within the Cluster Autoscaler IAM policy" type = list(string) @@ -132,19 +178,12 @@ variable "attach_ebs_csi_policy" { default = false } -variable "ebs_csi_kms_cmk_ids" { - description = "KMS CMK IDs to allow EBS CSI to manage encrypted volumes" +variable "ebs_csi_kms_cmk_arns" { + description = "KMS CMK ARNs to allow EBS CSI to manage encrypted volumes" type = list(string) default = [] } -# EFS CSI -variable "attach_efs_csi_policy" { - description = "Determines whether to attach the EFS CSI IAM policy to the role" - type = bool - default = false -} - # S3 CSI variable "attach_mountpoint_s3_csi_policy" { description = "Determines whether to attach the Mountpoint S3 CSI IAM policy to the role" @@ -170,6 +209,13 @@ variable "mountpoint_s3_csi_path_arns" { default = [] } +# EFS CSI +variable "attach_efs_csi_policy" { + description = "Determines whether to attach the EFS CSI IAM policy to the role" + type = bool + default = false +} + # External DNS variable "attach_external_dns_policy" { description = "Determines whether to attach the External DNS IAM policy to the role" @@ -180,7 +226,7 @@ variable "attach_external_dns_policy" { variable "external_dns_hosted_zone_arns" { description = "Route53 hosted zone ARNs to allow External DNS to manage records" type = list(string) - default = ["arn:aws:route53:::hostedzone/*"] + default = [] } # External Secrets @@ -193,19 +239,19 @@ variable "attach_external_secrets_policy" { variable "external_secrets_ssm_parameter_arns" { description = "List of Systems Manager Parameter ARNs that contain secrets to mount using External Secrets" type = list(string) - default = ["arn:aws:ssm:*:*:parameter/*"] + default = [] } variable "external_secrets_secrets_manager_arns" { description = "List of Secrets Manager ARNs that contain secrets to mount using External Secrets" type = list(string) - default = ["arn:aws:secretsmanager:*:*:secret:*"] + default = [] } variable "external_secrets_kms_key_arns" { description = "List of KMS Key ARNs that are used by Secrets Manager that contain secrets to mount using External Secrets" type = list(string) - default = ["arn:aws:kms:*:*:key/*"] + default = [] } variable "external_secrets_secrets_manager_create_permission" { @@ -240,62 +286,6 @@ variable "fsx_openzfs_csi_service_role_arns" { default = ["arn:aws:iam::*:role/aws-service-role/fsx.amazonaws.com/*"] } -# Karpenter controller -variable "attach_karpenter_controller_policy" { - description = "Determines whether to attach the Karpenter Controller policy to the role" - type = bool - default = false -} - -variable "karpenter_controller_cluster_id" { - description = "[Deprecated - use `karpenter_controller_cluster_name`] The name of the cluster where the Karpenter controller is provisioned/managing" - type = string - default = "*" -} - -variable "karpenter_controller_cluster_name" { - description = "The name of the cluster where the Karpenter controller is provisioned/managing" - type = string - default = "*" -} - -variable "karpenter_tag_key" { - description = "Tag key (`{key = value}`) applied to resources launched by Karpenter through the Karpenter provisioner" - type = string - default = "karpenter.sh/discovery" -} - -variable "karpenter_controller_ssm_parameter_arns" { - description = "List of SSM Parameter ARNs that contain AMI IDs launched by Karpenter" - type = list(string) - # https://github.com/aws/karpenter/blob/ed9473a9863ca949b61b9846c8b9f33f35b86dbd/pkg/cloudprovider/aws/ami.go#L105-L123 - default = ["arn:aws:ssm:*:*:parameter/aws/service/*"] -} - -variable "karpenter_controller_node_iam_role_arns" { - description = "List of node IAM role ARNs Karpenter can use to launch nodes" - type = list(string) - default = ["*"] -} - -variable "karpenter_subnet_account_id" { - description = "Account ID of where the subnets Karpenter will utilize resides. Used when subnets are shared from another account" - type = string - default = "" -} - -variable "karpenter_sqs_queue_arn" { - description = "(Optional) ARN of SQS used by Karpenter when native node termination handling is enabled" - type = string - default = null -} - -variable "enable_karpenter_instance_profile_creation" { - description = "Determines whether Karpenter will be allowed to create the IAM instance profile (v1beta1/v0.32+)" - type = bool - default = false -} - # AWS Load Balancer Controller variable "attach_load_balancer_controller_policy" { description = "Determines whether to attach the Load Balancer Controller policy to the role" @@ -314,21 +304,7 @@ variable "attach_load_balancer_controller_targetgroup_binding_only_policy" { variable "load_balancer_controller_targetgroup_arns" { description = "List of Target groups ARNs using Load Balancer Controller" type = list(string) - default = ["arn:aws:elasticloadbalancing:*:*:targetgroup/*/*"] -} - -# AWS Appmesh Controller -variable "attach_appmesh_controller_policy" { - description = "Determines whether to attach the Appmesh Controller policy to the role" - type = bool - default = false -} - -# AWS Appmesh envoy proxy -variable "attach_appmesh_envoy_proxy_policy" { - description = "Determines whether to attach the Appmesh envoy proxy policy to the role" - type = bool - default = false + default = [] } # Amazon Managed Service for Prometheus @@ -341,7 +317,7 @@ variable "attach_amazon_managed_service_prometheus_policy" { variable "amazon_managed_service_prometheus_workspace_arns" { description = "List of AMP Workspace ARNs to read and write metrics" type = list(string) - default = ["*"] + default = [] } # Velero @@ -354,7 +330,7 @@ variable "attach_velero_policy" { variable "velero_s3_bucket_arns" { description = "List of S3 Bucket ARNs that Velero needs access to in order to backup and restore cluster resources" type = list(string) - default = ["*"] + default = [] } # VPC CNI @@ -392,7 +368,7 @@ variable "attach_node_termination_handler_policy" { variable "node_termination_handler_sqs_queue_arns" { description = "List of SQS ARNs that contain node termination events" type = list(string) - default = ["*"] + default = [] } # Amazon CloudWatch Observability diff --git a/examples/iam-group-with-assumable-roles-policy/versions.tf b/modules/iam-role-for-service-accounts/versions.tf similarity index 100% rename from examples/iam-group-with-assumable-roles-policy/versions.tf rename to modules/iam-role-for-service-accounts/versions.tf diff --git a/modules/iam-role-oidc/README.md b/modules/iam-role-oidc/README.md new file mode 100644 index 00000000..257b6991 --- /dev/null +++ b/modules/iam-role-oidc/README.md @@ -0,0 +1,118 @@ +# AWS IAM OIDC Role Terraform Module + +Creates a single IAM role which can be assumed by trusted resources using OpenID connect federation. + +## Usage + +### [GitHub Free, Pro, & Team](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services) + +The defaults provided by the module are suitable for GitHub Free, Pro, & Team, including use with the official [AWS GitHub action](https://github.com/aws-actions/configure-aws-credentials). + +```hcl +module "iam_oidc_role" { + source = "terraform-aws-modules/iam/aws//modules/iam-oidc-role" + + enable_github_oidc = true + + # This should be updated to suit your organization, repository, references/branches, etc. + oidc_subjects = ["terraform-aws-modules/terraform-aws-iam:*"] + + policies = { + S3ReadOnly = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" + } + + tags = { + Environment = "test" + } +} +``` + +### [GitHub Enterprise Server](https://docs.github.com/en/enterprise-server@3.7/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services) + +For GitHub Enterprise Server, users will need to provide value for the `oidc_audience` and `provider_urls` to suit their `` installation: + +```hcl +module "iam_oidc_role" { + source = "terraform-aws-modules/iam/aws//modules/iam-oidc-role" + + enable_github_oidc = true + + oidc_audience = "https://mygithub.com/" + oidc_provider_urls = ["mygithub.com/_services/token"] + + # This should be updated to suit your organization, repository, references/branches, etc. + oidc_subjects = ["/terraform-aws-iam:*"] + + policies = { + S3ReadOnly = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" + } + + tags = { + Environment = "test" + } +} +``` + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 4.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 4.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_iam_policy_document.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [allow\_self\_assume\_role](#input\_allow\_self\_assume\_role) | Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/) | `bool` | `false` | no | +| [assume\_role\_policy\_statements](#input\_assume\_role\_policy\_statements) | List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for for trusted assume role policy | `any` | `[]` | no | +| [create](#input\_create) | Controls if resources should be created (affects all resources) | `bool` | `true` | no | +| [description](#input\_description) | Description of the role | `string` | `null` | no | +| [enable\_bitbucket\_oidc](#input\_enable\_bitbucket\_oidc) | Enable Bitbucket OIDC provider trust for the role | `bool` | `false` | no | +| [enable\_github\_oidc](#input\_enable\_github\_oidc) | Enable GitHub OIDC provider trust for the role | `bool` | `false` | no | +| [max\_session\_duration](#input\_max\_session\_duration) | Maximum session duration (in seconds) that you want to set for the specified role. If you do not specify a value for this setting, the default maximum of one hour is applied. This setting can have a value from 1 hour to 12 hours | `number` | `null` | no | +| [name](#input\_name) | Name to use on IAM role created | `string` | `null` | no | +| [name\_prefix](#input\_name\_prefix) | Name prefix to use on IAM role created | `string` | `null` | no | +| [oidc\_account\_id](#input\_oidc\_account\_id) | An overriding AWS account ID where the OIDC provider lives; leave empty to use the current account ID for the AWS provider | `string` | `null` | no | +| [oidc\_audiences](#input\_oidc\_audiences) | The audience to be added to the role policy. Set to sts.amazonaws.com for cross-account assumable role. Leave empty otherwise. | `set(string)` | `[]` | no | +| [oidc\_provider\_urls](#input\_oidc\_provider\_urls) | List of URLs of the OIDC Providers | `list(string)` | `[]` | no | +| [oidc\_subjects](#input\_oidc\_subjects) | The fully qualified OIDC subjects to be added to the role policy | `set(string)` | `[]` | no | +| [oidc\_wildcard\_subjects](#input\_oidc\_wildcard\_subjects) | The OIDC subject using wildcards to be added to the role policy | `set(string)` | `[]` | no | +| [path](#input\_path) | Path of IAM role | `string` | `"/"` | no | +| [permissions\_boundary](#input\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no | +| [policies](#input\_policies) | Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no | +| [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [arn](#output\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | +| [name](#output\_name) | The name of the IAM role | +| [unique\_id](#output\_unique\_id) | Stable and unique string identifying the IAM role | + + +## License + +Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-iam/blob/master/LICENSE). diff --git a/modules/iam-role-oidc/main.tf b/modules/iam-role-oidc/main.tf new file mode 100644 index 00000000..c57ddc1c --- /dev/null +++ b/modules/iam-role-oidc/main.tf @@ -0,0 +1,221 @@ +data "aws_caller_identity" "current" {} +data "aws_partition" "current" {} + +locals { + account_id = data.aws_caller_identity.current.account_id + partition = data.aws_partition.current.partition + + oidc_providers = [for url in var.oidc_provider_urls : replace(url, "https://", "")] + github_provider = coalesce(one(local.oidc_providers), "token.actions.githubusercontent.com") + bitbucket_provider = one(local.oidc_providers) + + name_condition = var.name != null ? var.name : "${var.name_prefix}*" +} + +################################################################################ +# IAM Role +################################################################################ + +data "aws_iam_policy_document" "this" { + count = var.create ? 1 : 0 + + dynamic "statement" { + # https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/ + for_each = var.allow_self_assume_role ? [1] : [] + + content { + sid = "ExplicitSelfRoleAssumption" + effect = "Allow" + actions = ["sts:AssumeRole"] + + principals { + type = "AWS" + identifiers = ["*"] + } + + condition { + test = "ArnLike" + variable = "aws:PrincipalArn" + values = ["arn:${local.partition}:iam::${local.account_id}:role${var.path}${local.name_condition}"] + } + } + } + + # Generic OIDC + dynamic "statement" { + for_each = !var.enable_github_oidc ? local.oidc_providers : [] + + content { + effect = "Allow" + actions = ["sts:AssumeRoleWithWebIdentity"] + + principals { + type = "Federated" + + identifiers = ["arn:${local.partition}:iam::${coalesce(var.oidc_account_id, local.account_id)}:oidc-provider/${statement.value}"] + } + + dynamic "condition" { + for_each = length(var.oidc_subjects) > 0 ? local.oidc_providers : [] + + content { + test = "StringEquals" + variable = "${statement.value}:sub" + values = var.oidc_subjects + } + } + + dynamic "condition" { + for_each = length(var.oidc_wildcard_subjects) > 0 ? local.oidc_providers : [] + + content { + test = "StringLike" + variable = "${statement.value}:sub" + values = var.oidc_wildcard_subjects + } + } + + dynamic "condition" { + for_each = length(var.oidc_audiences) > 0 ? local.oidc_providers : [] + + content { + test = "StringLike" + variable = "${statement.value}:aud" + values = var.oidc_audiences + } + } + } + } + + # GitHub OIDC + dynamic "statement" { + for_each = var.enable_github_oidc ? [1] : [] + + content { + sid = "GithubOidcAuth" + actions = [ + "sts:TagSession", + "sts:AssumeRoleWithWebIdentity" + ] + + principals { + type = "Federated" + identifiers = ["arn:${local.partition}:iam::${local.account_id}:oidc-provider/${local.github_provider}"] + } + + condition { + test = "ForAllValues:StringEquals" + variable = "token.actions.githubusercontent.com:iss" + values = ["http://token.actions.githubusercontent.com"] + } + + condition { + test = "ForAllValues:StringEquals" + variable = "${local.github_provider}:aud" + values = coalescelist(var.oidc_audiences, ["sts.amazonaws.com"]) + } + + condition { + test = "StringLike" + variable = "${local.github_provider}:sub" + # Strip `repo:` to normalize for cases where users may prepend it + values = [for subject in var.oidc_subjects : "repo:${trimprefix(subject, "repo:")}"] + } + } + } + + # Bitbucket OIDC + dynamic "statement" { + for_each = var.enable_bitbucket_oidc ? [1] : [] + + content { + sid = "BitbucketOidcAuth" + actions = [ + "sts:TagSession", + "sts:AssumeRoleWithWebIdentity" + ] + + principals { + type = "Federated" + identifiers = ["arn:${local.partition}:iam::${local.account_id}:oidc-provider/${local.bitbucket_provider}"] + } + + condition { + test = "ForAllValues:StringEquals" + variable = "${local.bitbucket_provider}:aud" + values = coalescelist(var.oidc_audiences, ["sts.amazonaws.com"]) + } + + condition { + test = "StringLike" + variable = "${local.bitbucket_provider}:sub" + values = var.oidc_subjects + } + } + } + + # Generic statements + dynamic "statement" { + for_each = var.assume_role_policy_statements + + content { + sid = try(statement.value.sid, null) + actions = try(statement.value.actions, ["sts:AssumeRole"]) + not_actions = try(statement.value.not_actions, null) + effect = try(statement.value.effect, null) + resources = try(statement.value.resources, null) + not_resources = try(statement.value.not_resources, null) + + dynamic "principals" { + for_each = try(statement.value.principals, []) + + content { + type = principals.value.type + identifiers = principals.value.identifiers + } + } + + dynamic "not_principals" { + for_each = try(statement.value.not_principals, []) + + content { + type = not_principals.value.type + identifiers = not_principals.value.identifiers + } + } + + dynamic "condition" { + for_each = try(statement.value.conditions, []) + + content { + test = condition.value.test + values = condition.value.values + variable = condition.value.variable + } + } + } + } +} + +resource "aws_iam_role" "this" { + count = var.create ? 1 : 0 + + name = var.name + name_prefix = var.name_prefix + path = var.path + description = var.description + + assume_role_policy = data.aws_iam_policy_document.this[0].json + max_session_duration = var.max_session_duration + permissions_boundary = var.permissions_boundary + force_detach_policies = true + + tags = var.tags +} + +resource "aws_iam_role_policy_attachment" "this" { + for_each = { for k, v in var.policies : k => v if var.create } + + policy_arn = each.value + role = aws_iam_role.this[0].name +} diff --git a/modules/iam-github-oidc-role/outputs.tf b/modules/iam-role-oidc/outputs.tf similarity index 62% rename from modules/iam-github-oidc-role/outputs.tf rename to modules/iam-role-oidc/outputs.tf index 29f9a29d..8410060f 100644 --- a/modules/iam-github-oidc-role/outputs.tf +++ b/modules/iam-role-oidc/outputs.tf @@ -1,23 +1,18 @@ ################################################################################ -# GitHub OIDC Role +# IAM Role ################################################################################ -output "arn" { - description = "ARN of IAM role" - value = try(aws_iam_role.this[0].arn, null) -} - output "name" { - description = "Name of IAM role" + description = "The name of the IAM role" value = try(aws_iam_role.this[0].name, null) } -output "path" { - description = "Path of IAM role" - value = try(aws_iam_role.this[0].path, null) +output "arn" { + description = "The Amazon Resource Name (ARN) specifying the IAM role" + value = try(aws_iam_role.this[0].arn, null) } output "unique_id" { - description = "Unique ID of IAM role" + description = "Stable and unique string identifying the IAM role" value = try(aws_iam_role.this[0].unique_id, null) } diff --git a/modules/iam-role-oidc/variables.tf b/modules/iam-role-oidc/variables.tf new file mode 100644 index 00000000..bff5d296 --- /dev/null +++ b/modules/iam-role-oidc/variables.tf @@ -0,0 +1,111 @@ +variable "create" { + description = "Controls if resources should be created (affects all resources)" + type = bool + default = true +} + +variable "tags" { + description = "A map of tags to add to all resources" + type = map(string) + default = {} +} + +################################################################################ +# IAM Role +################################################################################ + +variable "name" { + description = "Name to use on IAM role created" + type = string + default = null +} + +variable "name_prefix" { + description = "Name prefix to use on IAM role created" + type = string + default = null +} + +variable "path" { + description = "Path of IAM role" + type = string + default = "/" +} + +variable "description" { + description = "Description of the role" + type = string + default = null +} + +variable "max_session_duration" { + description = "Maximum session duration (in seconds) that you want to set for the specified role. If you do not specify a value for this setting, the default maximum of one hour is applied. This setting can have a value from 1 hour to 12 hours" + type = number + default = null +} + +variable "permissions_boundary" { + description = "ARN of the policy that is used to set the permissions boundary for the IAM role" + type = string + default = null +} + +variable "allow_self_assume_role" { + description = "Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/)" + type = bool + default = false +} + +variable "assume_role_policy_statements" { + description = "List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for for trusted assume role policy" + type = any + default = [] +} + +variable "policies" { + description = "Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format" + type = map(string) + default = {} +} + +variable "oidc_account_id" { + description = "An overriding AWS account ID where the OIDC provider lives; leave empty to use the current account ID for the AWS provider" + type = string + default = null +} + +variable "oidc_provider_urls" { + description = "List of URLs of the OIDC Providers" + type = list(string) + default = [] +} + +variable "oidc_subjects" { + description = "The fully qualified OIDC subjects to be added to the role policy" + type = set(string) + default = [] +} + +variable "oidc_wildcard_subjects" { + description = "The OIDC subject using wildcards to be added to the role policy" + type = set(string) + default = [] +} + +variable "oidc_audiences" { + description = "The audience to be added to the role policy. Set to sts.amazonaws.com for cross-account assumable role. Leave empty otherwise." + type = set(string) + default = [] +} + +variable "enable_github_oidc" { + description = "Enable GitHub OIDC provider trust for the role" + type = bool + default = false +} + +variable "enable_bitbucket_oidc" { + description = "Enable Bitbucket OIDC provider trust for the role" + type = bool + default = false +} diff --git a/examples/iam-group-with-policies/versions.tf b/modules/iam-role-oidc/versions.tf similarity index 100% rename from examples/iam-group-with-policies/versions.tf rename to modules/iam-role-oidc/versions.tf diff --git a/modules/iam-role-saml/README.md b/modules/iam-role-saml/README.md new file mode 100644 index 00000000..0b3db2bc --- /dev/null +++ b/modules/iam-role-saml/README.md @@ -0,0 +1,89 @@ +# AWS IAM SAML Role Terraform Module + +Creates single IAM role which can be assumed by trusted resources using SAML federation. + +[Creating IAM SAML Identity Providers](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_saml.html) +[Enabling SAML 2.0 Federated Users to Access the AWS Management Console](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_enable-console-saml.html) + +Creates an IAM role that trusts a SAML provider. Useful for trusting external identity providers such as Okta, OneLogin, etc. + +## Usage + +```hcl +module "iam_role_saml" { + source = "terraform-aws-modules/iam/aws//modules/iam-role-saml" + + name = "example" + + saml_provider_ids = ["arn:aws:iam::235367859851:saml-provider/idp_saml"] + + policies = { + ReadOnlyAccess = "arn:aws:iam::aws:policy/ReadOnlyAccess" + } + + tags = { + Terraform = "true" + Environment = "dev" + } +} +``` + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 4.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 4.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_iam_policy_document.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [allow\_self\_assume\_role](#input\_allow\_self\_assume\_role) | Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/) | `bool` | `false` | no | +| [assume\_role\_policy\_statements](#input\_assume\_role\_policy\_statements) | List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for for trusted assume role policy | `any` | `[]` | no | +| [create](#input\_create) | Controls if resources should be created (affects all resources) | `bool` | `true` | no | +| [description](#input\_description) | Description of the role | `string` | `null` | no | +| [max\_session\_duration](#input\_max\_session\_duration) | Maximum session duration (in seconds) that you want to set for the specified role. If you do not specify a value for this setting, the default maximum of one hour is applied. This setting can have a value from 1 hour to 12 hours | `number` | `null` | no | +| [name](#input\_name) | Name to use on IAM role created | `string` | `null` | no | +| [name\_prefix](#input\_name\_prefix) | Name prefix to use on IAM role created | `string` | `null` | no | +| [path](#input\_path) | Path of IAM role | `string` | `"/"` | no | +| [permissions\_boundary](#input\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no | +| [policies](#input\_policies) | Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no | +| [saml\_endpoints](#input\_saml\_endpoints) | List of AWS SAML endpoints | `list(string)` |
[
"https://signin.aws.amazon.com/saml"
]
| no | +| [saml\_provider\_ids](#input\_saml\_provider\_ids) | List of SAML provider IDs | `list(string)` | `[]` | no | +| [saml\_trust\_actions](#input\_saml\_trust\_actions) | Additional assume role trust actions for the SAML federated statement | `list(string)` | `[]` | no | +| [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [arn](#output\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | +| [name](#output\_name) | The name of the IAM role | +| [unique\_id](#output\_unique\_id) | Stable and unique string identifying the IAM role | + + +## License + +Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-iam/blob/master/LICENSE). diff --git a/modules/iam-role-saml/main.tf b/modules/iam-role-saml/main.tf new file mode 100644 index 00000000..e81cc669 --- /dev/null +++ b/modules/iam-role-saml/main.tf @@ -0,0 +1,116 @@ +data "aws_caller_identity" "current" {} +data "aws_partition" "current" {} + +locals { + name_condition = var.name != null ? var.name : "${var.name_prefix}*" +} + +################################################################################ +# IAM Role +################################################################################ + +data "aws_iam_policy_document" "this" { + count = var.create ? 1 : 0 + + dynamic "statement" { + # https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/ + for_each = var.allow_self_assume_role ? [1] : [] + + content { + sid = "ExplicitSelfRoleAssumption" + effect = "Allow" + actions = ["sts:AssumeRole"] + + principals { + type = "AWS" + identifiers = ["*"] + } + + condition { + test = "ArnLike" + variable = "aws:PrincipalArn" + values = ["arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:role${var.path}${local.name_condition}"] + } + } + } + + statement { + effect = "Allow" + actions = compact(distinct(concat(["sts:AssumeRoleWithSAML"], var.saml_trust_actions))) + + principals { + type = "Federated" + identifiers = var.saml_provider_ids + } + + condition { + test = "StringEquals" + variable = "SAML:aud" + values = var.saml_endpoints + } + } + + dynamic "statement" { + for_each = var.assume_role_policy_statements + + content { + sid = try(statement.value.sid, null) + actions = try(statement.value.actions, ["sts:AssumeRole"]) + not_actions = try(statement.value.not_actions, null) + effect = try(statement.value.effect, null) + resources = try(statement.value.resources, null) + not_resources = try(statement.value.not_resources, null) + + dynamic "principals" { + for_each = try(statement.value.principals, []) + + content { + type = principals.value.type + identifiers = principals.value.identifiers + } + } + + dynamic "not_principals" { + for_each = try(statement.value.not_principals, []) + + content { + type = not_principals.value.type + identifiers = not_principals.value.identifiers + } + } + + dynamic "condition" { + for_each = try(statement.value.conditions, []) + + content { + test = condition.value.test + values = condition.value.values + variable = condition.value.variable + } + } + } + } +} + +resource "aws_iam_role" "this" { + count = var.create ? 1 : 0 + + name = var.name + name_prefix = var.name_prefix + path = var.path + description = var.description + + assume_role_policy = data.aws_iam_policy_document.this[0].json + max_session_duration = var.max_session_duration + permissions_boundary = var.permissions_boundary + force_detach_policies = true + + tags = var.tags +} + +resource "aws_iam_role_policy_attachment" "this" { + for_each = { for k, v in var.policies : k => v if var.create } + + policy_arn = each.value + role = aws_iam_role.this[0].name +} diff --git a/modules/iam-role-saml/outputs.tf b/modules/iam-role-saml/outputs.tf new file mode 100644 index 00000000..8410060f --- /dev/null +++ b/modules/iam-role-saml/outputs.tf @@ -0,0 +1,18 @@ +################################################################################ +# IAM Role +################################################################################ + +output "name" { + description = "The name of the IAM role" + value = try(aws_iam_role.this[0].name, null) +} + +output "arn" { + description = "The Amazon Resource Name (ARN) specifying the IAM role" + value = try(aws_iam_role.this[0].arn, null) +} + +output "unique_id" { + description = "Stable and unique string identifying the IAM role" + value = try(aws_iam_role.this[0].unique_id, null) +} diff --git a/modules/iam-role-saml/variables.tf b/modules/iam-role-saml/variables.tf new file mode 100644 index 00000000..ef88f546 --- /dev/null +++ b/modules/iam-role-saml/variables.tf @@ -0,0 +1,87 @@ +variable "create" { + description = "Controls if resources should be created (affects all resources)" + type = bool + default = true +} + +variable "tags" { + description = "A map of tags to add to all resources" + type = map(string) + default = {} +} + +################################################################################ +# IAM Role +################################################################################ + +variable "name" { + description = "Name to use on IAM role created" + type = string + default = null +} + +variable "name_prefix" { + description = "Name prefix to use on IAM role created" + type = string + default = null +} + +variable "path" { + description = "Path of IAM role" + type = string + default = "/" +} + +variable "description" { + description = "Description of the role" + type = string + default = null +} + +variable "max_session_duration" { + description = "Maximum session duration (in seconds) that you want to set for the specified role. If you do not specify a value for this setting, the default maximum of one hour is applied. This setting can have a value from 1 hour to 12 hours" + type = number + default = null +} + +variable "permissions_boundary" { + description = "ARN of the policy that is used to set the permissions boundary for the IAM role" + type = string + default = null +} + +variable "allow_self_assume_role" { + description = "Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/)" + type = bool + default = false +} + +variable "assume_role_policy_statements" { + description = "List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for for trusted assume role policy" + type = any + default = [] +} + +variable "policies" { + description = "Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format" + type = map(string) + default = {} +} + +variable "saml_provider_ids" { + description = "List of SAML provider IDs" + type = list(string) + default = [] +} + +variable "saml_endpoints" { + description = "List of AWS SAML endpoints" + type = list(string) + default = ["https://signin.aws.amazon.com/saml"] +} + +variable "saml_trust_actions" { + description = "Additional assume role trust actions for the SAML federated statement" + type = list(string) + default = [] +} diff --git a/examples/iam-policy/versions.tf b/modules/iam-role-saml/versions.tf similarity index 100% rename from examples/iam-policy/versions.tf rename to modules/iam-role-saml/versions.tf diff --git a/modules/iam-role/README.md b/modules/iam-role/README.md new file mode 100644 index 00000000..c6290553 --- /dev/null +++ b/modules/iam-role/README.md @@ -0,0 +1,104 @@ +# AWS IAM Role Terraform Module + +Creates an IAM role with a trust policy and (optional) IAM instance profile. Useful for service roles such as EC2, ECS, etc., or roles assumed across AWS accounts. + +## Usage + +```hcl +module "iam_role" { + source = "terraform-aws-modules/iam/aws//modules/iam-role" + + name = "example" + + assume_role_policy_statements = [ + { + sid = "TrustRoleAndServiceToAssume" + principals = [{ + type = "AWS" + identifiers = [ + "arn:aws:iam::835367859851:user/anton", + ] + }] + conditions = [{ + test = "StringEquals" + variable = "sts:ExternalId" + values = ["some-secret-id"] + }] + } + ] + + policies = { + AmazonCognitoReadOnly = "arn:aws:iam::aws:policy/AmazonCognitoReadOnly" + AlexaForBusinessFullAccess = "arn:aws:iam::aws:policy/AlexaForBusinessFullAccess" + custom = aws_iam_policy.this.arn + } + + tags = { + Terraform = "true" + Environment = "dev" + } +} +``` + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 4.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 4.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_iam_instance_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_instance_profile) | resource | +| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_iam_policy_document.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [allow\_self\_assume\_role](#input\_allow\_self\_assume\_role) | Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/) | `bool` | `false` | no | +| [assume\_role\_policy\_statements](#input\_assume\_role\_policy\_statements) | List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for for trusted assume role policy | `any` | `[]` | no | +| [create](#input\_create) | Controls if resources should be created (affects all resources) | `bool` | `true` | no | +| [create\_instance\_profile](#input\_create\_instance\_profile) | Determines whether to create an instance profile | `bool` | `false` | no | +| [description](#input\_description) | Description of the role | `string` | `null` | no | +| [max\_session\_duration](#input\_max\_session\_duration) | Maximum session duration (in seconds) that you want to set for the specified role. If you do not specify a value for this setting, the default maximum of one hour is applied. This setting can have a value from 1 hour to 12 hours | `number` | `null` | no | +| [name](#input\_name) | Name to use on IAM role created | `string` | `null` | no | +| [name\_prefix](#input\_name\_prefix) | Name prefix to use on IAM role created | `string` | `null` | no | +| [path](#input\_path) | Path of IAM role | `string` | `"/"` | no | +| [permissions\_boundary](#input\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no | +| [policies](#input\_policies) | Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no | +| [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [arn](#output\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | +| [instance\_profile\_arn](#output\_instance\_profile\_arn) | ARN assigned by AWS to the instance profile | +| [instance\_profile\_id](#output\_instance\_profile\_id) | Instance profile's ID | +| [instance\_profile\_name](#output\_instance\_profile\_name) | Name of IAM instance profile | +| [instance\_profile\_unique\_id](#output\_instance\_profile\_unique\_id) | Stable and unique string identifying the IAM instance profile | +| [name](#output\_name) | The name of the IAM role | +| [unique\_id](#output\_unique\_id) | Stable and unique string identifying the IAM role | + + +## License + +Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-iam/blob/master/LICENSE). diff --git a/modules/iam-role/main.tf b/modules/iam-role/main.tf new file mode 100644 index 00000000..9221aaaf --- /dev/null +++ b/modules/iam-role/main.tf @@ -0,0 +1,120 @@ +data "aws_caller_identity" "current" {} +data "aws_partition" "current" {} + +locals { + name_condition = var.name != null ? var.name : "${var.name_prefix}*" +} + +################################################################################ +# IAM Role +################################################################################ + +data "aws_iam_policy_document" "this" { + count = var.create ? 1 : 0 + + dynamic "statement" { + # https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/ + for_each = var.allow_self_assume_role ? [1] : [] + + content { + sid = "ExplicitSelfRoleAssumption" + effect = "Allow" + actions = ["sts:AssumeRole"] + + principals { + type = "AWS" + identifiers = ["*"] + } + + condition { + test = "ArnLike" + variable = "aws:PrincipalArn" + values = ["arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:role${var.path}${local.name_condition}"] + } + } + } + + dynamic "statement" { + for_each = var.assume_role_policy_statements + + content { + sid = try(statement.value.sid, null) + actions = try(statement.value.actions, ["sts:AssumeRole"]) + not_actions = try(statement.value.not_actions, null) + effect = try(statement.value.effect, null) + resources = try(statement.value.resources, null) + not_resources = try(statement.value.not_resources, null) + + dynamic "principals" { + for_each = try(statement.value.principals, []) + + content { + type = principals.value.type + identifiers = principals.value.identifiers + } + } + + dynamic "not_principals" { + for_each = try(statement.value.not_principals, []) + + content { + type = not_principals.value.type + identifiers = not_principals.value.identifiers + } + } + + dynamic "condition" { + for_each = try(statement.value.conditions, []) + + content { + test = condition.value.test + values = condition.value.values + variable = condition.value.variable + } + } + } + } +} + +resource "aws_iam_role" "this" { + count = var.create ? 1 : 0 + + name = var.name + name_prefix = var.name_prefix + path = var.path + description = var.description + + assume_role_policy = data.aws_iam_policy_document.this[0].json + max_session_duration = var.max_session_duration + permissions_boundary = var.permissions_boundary + force_detach_policies = true + + tags = var.tags +} + +resource "aws_iam_role_policy_attachment" "this" { + for_each = { for k, v in var.policies : k => v if var.create } + + policy_arn = each.value + role = aws_iam_role.this[0].name +} + +################################################################################ +# IAM Instance Profile +################################################################################ + +resource "aws_iam_instance_profile" "this" { + count = var.create && var.create_instance_profile ? 1 : 0 + + role = aws_iam_role.this[0].name + + name = var.name + name_prefix = var.name_prefix + path = var.path + + tags = var.tags + + lifecycle { + create_before_destroy = true + } +} diff --git a/modules/iam-role/outputs.tf b/modules/iam-role/outputs.tf new file mode 100644 index 00000000..3933df07 --- /dev/null +++ b/modules/iam-role/outputs.tf @@ -0,0 +1,42 @@ +################################################################################ +# IAM Role +################################################################################ + +output "name" { + description = "The name of the IAM role" + value = try(aws_iam_role.this[0].name, null) +} + +output "arn" { + description = "The Amazon Resource Name (ARN) specifying the IAM role" + value = try(aws_iam_role.this[0].arn, null) +} + +output "unique_id" { + description = "Stable and unique string identifying the IAM role" + value = try(aws_iam_role.this[0].unique_id, null) +} + +################################################################################ +# IAM Instance Profile +################################################################################ + +output "instance_profile_arn" { + description = "ARN assigned by AWS to the instance profile" + value = try(aws_iam_instance_profile.this[0].arn, null) +} + +output "instance_profile_id" { + description = "Instance profile's ID" + value = try(aws_iam_instance_profile.this[0].id, null) +} + +output "instance_profile_name" { + description = "Name of IAM instance profile" + value = try(aws_iam_instance_profile.this[0].name, null) +} + +output "instance_profile_unique_id" { + description = "Stable and unique string identifying the IAM instance profile" + value = try(aws_iam_instance_profile.this[0].unique_id, null) +} diff --git a/modules/iam-role/variables.tf b/modules/iam-role/variables.tf new file mode 100644 index 00000000..7f9db51e --- /dev/null +++ b/modules/iam-role/variables.tf @@ -0,0 +1,79 @@ +variable "create" { + description = "Controls if resources should be created (affects all resources)" + type = bool + default = true +} + +variable "tags" { + description = "A map of tags to add to all resources" + type = map(string) + default = {} +} + +################################################################################ +# IAM Role +################################################################################ + +variable "name" { + description = "Name to use on IAM role created" + type = string + default = null +} + +variable "name_prefix" { + description = "Name prefix to use on IAM role created" + type = string + default = null +} + +variable "path" { + description = "Path of IAM role" + type = string + default = "/" +} + +variable "description" { + description = "Description of the role" + type = string + default = null +} + +variable "max_session_duration" { + description = "Maximum session duration (in seconds) that you want to set for the specified role. If you do not specify a value for this setting, the default maximum of one hour is applied. This setting can have a value from 1 hour to 12 hours" + type = number + default = null +} + +variable "permissions_boundary" { + description = "ARN of the policy that is used to set the permissions boundary for the IAM role" + type = string + default = null +} + +variable "allow_self_assume_role" { + description = "Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/)" + type = bool + default = false +} + +variable "assume_role_policy_statements" { + description = "List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for for trusted assume role policy" + type = any + default = [] +} + +variable "policies" { + description = "Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format" + type = map(string) + default = {} +} + +################################################################################ +# IAM Instance Profile +################################################################################ + +variable "create_instance_profile" { + description = "Determines whether to create an instance profile" + type = bool + default = false +} diff --git a/modules/iam-assumable-role-with-oidc/versions.tf b/modules/iam-role/versions.tf similarity index 100% rename from modules/iam-assumable-role-with-oidc/versions.tf rename to modules/iam-role/versions.tf diff --git a/modules/iam-user/README.md b/modules/iam-user/README.md index 44abbe7d..7db3b16a 100644 --- a/modules/iam-user/README.md +++ b/modules/iam-user/README.md @@ -1,24 +1,31 @@ -# iam-user +# AWS IAM User Terraform Module -Creates IAM user, IAM login profile, IAM access key and uploads IAM SSH user public key. All of these are optional resources. +Creates an IAM user with ability to create a login profile, access key, and SSH key. -## Notes for keybase users +## Usage -**If possible, always use PGP encryption to prevent Terraform from keeping unencrypted password and access secret key in state file.** +```hcl +module "iam_user" { + source = "terraform-aws-modules/iam/aws//modules/iam-user" -### Keybase pre-requisites + name = "vasya.pupkin" -When `pgp_key` is specified as `keybase:username`, make sure that that user has already uploaded public key to keybase.io. For example, user with username `test` has done it properly and you can [verify it here](https://keybase.io/test/pgp_keys.asc). + force_destroy = true + pgp_key = "keybase:test" + password_reset_required = false + + tags = { + Terraform = "true" + Environment = "dev" + } +} +``` + +### Keybase -### How to decrypt user's encrypted password and secret key +If possible, always use PGP encryption to prevent Terraform from keeping unencrypted password and access secret key in state file. -This module outputs commands and PGP messages which can be decrypted either using [keybase.io web-site](https://keybase.io/decrypt) or using command line to get user's password and user's secret key: -- `keybase_password_decrypt_command` -- `keybase_secret_key_decrypt_command` -- `keybase_ses_smtp_password_v4_decrypt_command` -- `keybase_password_pgp_message` -- `keybase_secret_key_pgp_message` -- `keybase_ses_smtp_password_v4_pgp_message` +When `pgp_key` is specified as `keybase:username`, make sure that that user has already uploaded public key to keybase.io. For example, user with username `test` has done it properly and you can [verify it here](https://keybase.io/test/pgp_keys.asc). ## Requirements @@ -43,58 +50,51 @@ No modules. | Name | Type | |------|------| | [aws_iam_access_key.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_access_key) | resource | -| [aws_iam_access_key.this_no_pgp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_access_key) | resource | | [aws_iam_user.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user) | resource | | [aws_iam_user_login_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user_login_profile) | resource | -| [aws_iam_user_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user_policy_attachment) | resource | | [aws_iam_user_ssh_key.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user_ssh_key) | resource | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [create\_iam\_access\_key](#input\_create\_iam\_access\_key) | Whether to create IAM access key | `bool` | `true` | no | -| [create\_iam\_user\_login\_profile](#input\_create\_iam\_user\_login\_profile) | Whether to create IAM user login profile | `bool` | `true` | no | -| [create\_user](#input\_create\_user) | Whether to create the IAM user | `bool` | `true` | no | -| [force\_destroy](#input\_force\_destroy) | When destroying this user, destroy even if it has non-Terraform-managed IAM access keys, login profile or MFA devices. Without force\_destroy a user with non-Terraform-managed access keys and login profile will fail to be destroyed. | `bool` | `false` | no | -| [iam\_access\_key\_status](#input\_iam\_access\_key\_status) | Access key status to apply. | `string` | `null` | no | -| [name](#input\_name) | Desired name for the IAM user | `string` | n/a | yes | -| [password\_length](#input\_password\_length) | The length of the generated password | `number` | `20` | no | -| [password\_reset\_required](#input\_password\_reset\_required) | Whether the user should be forced to reset the generated password on first login. | `bool` | `true` | no | +| [access\_key\_status](#input\_access\_key\_status) | Access key status to apply | `string` | `null` | no | +| [create](#input\_create) | Controls if resources should be created (affects all resources) | `bool` | `true` | no | +| [create\_access\_key](#input\_create\_access\_key) | Whether to create IAM access key | `bool` | `true` | no | +| [create\_login\_profile](#input\_create\_login\_profile) | Whether to create IAM user login profile | `bool` | `true` | no | +| [create\_ssh\_key](#input\_create\_ssh\_key) | Whether to upload a public ssh key to the IAM user | `bool` | `false` | no | +| [force\_destroy](#input\_force\_destroy) | When destroying this user, destroy even if it has non-Terraform-managed IAM access keys, login profile or MFA devices. Without force\_destroy a user with non-Terraform-managed access keys and login profile will fail to be destroyed | `bool` | `false` | no | +| [name](#input\_name) | Desired name for the IAM user | `string` | `""` | no | +| [password\_length](#input\_password\_length) | The length of the generated password | `number` | `null` | no | +| [password\_reset\_required](#input\_password\_reset\_required) | Whether the user should be forced to reset the generated password on first login | `bool` | `true` | no | | [path](#input\_path) | Desired path for the IAM user | `string` | `"/"` | no | -| [permissions\_boundary](#input\_permissions\_boundary) | The ARN of the policy that is used to set the permissions boundary for the user. | `string` | `""` | no | -| [pgp\_key](#input\_pgp\_key) | Either a base-64 encoded PGP public key, or a keybase username in the form `keybase:username`. Used to encrypt password and access key. | `string` | `""` | no | -| [policy\_arns](#input\_policy\_arns) | The list of ARNs of policies directly assigned to the IAM user | `list(string)` | `[]` | no | +| [permissions\_boundary](#input\_permissions\_boundary) | The ARN of the policy that is used to set the permissions boundary for the user | `string` | `null` | no | +| [pgp\_key](#input\_pgp\_key) | Either a base-64 encoded PGP public key, or a keybase username in the form `keybase:username`. Used to encrypt password and access key | `string` | `null` | no | | [ssh\_key\_encoding](#input\_ssh\_key\_encoding) | Specifies the public key encoding format to use in the response. To retrieve the public key in ssh-rsa format, use SSH. To retrieve the public key in PEM format, use PEM | `string` | `"SSH"` | no | | [ssh\_public\_key](#input\_ssh\_public\_key) | The SSH public key. The public key must be encoded in ssh-rsa format or PEM format | `string` | `""` | no | -| [tags](#input\_tags) | A map of tags to add to all resources. | `map(string)` | `{}` | no | -| [upload\_iam\_user\_ssh\_key](#input\_upload\_iam\_user\_ssh\_key) | Whether to upload a public ssh key to the IAM user | `bool` | `false` | no | +| [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | ## Outputs | Name | Description | |------|-------------| -| [iam\_access\_key\_encrypted\_secret](#output\_iam\_access\_key\_encrypted\_secret) | The encrypted secret, base64 encoded | -| [iam\_access\_key\_encrypted\_ses\_smtp\_password\_v4](#output\_iam\_access\_key\_encrypted\_ses\_smtp\_password\_v4) | The encrypted secret access key converted into an SES SMTP password by applying AWS's Sigv4 conversion algorithm | -| [iam\_access\_key\_id](#output\_iam\_access\_key\_id) | The access key ID | -| [iam\_access\_key\_key\_fingerprint](#output\_iam\_access\_key\_key\_fingerprint) | The fingerprint of the PGP key used to encrypt the secret | -| [iam\_access\_key\_secret](#output\_iam\_access\_key\_secret) | The access key secret | -| [iam\_access\_key\_ses\_smtp\_password\_v4](#output\_iam\_access\_key\_ses\_smtp\_password\_v4) | The secret access key converted into an SES SMTP password by applying AWS's Sigv4 conversion algorithm | -| [iam\_access\_key\_status](#output\_iam\_access\_key\_status) | Active or Inactive. Keys are initially active, but can be made inactive by other means. | -| [iam\_user\_arn](#output\_iam\_user\_arn) | The ARN assigned by AWS for this user | -| [iam\_user\_login\_profile\_encrypted\_password](#output\_iam\_user\_login\_profile\_encrypted\_password) | The encrypted password, base64 encoded | -| [iam\_user\_login\_profile\_key\_fingerprint](#output\_iam\_user\_login\_profile\_key\_fingerprint) | The fingerprint of the PGP key used to encrypt the password | -| [iam\_user\_login\_profile\_password](#output\_iam\_user\_login\_profile\_password) | The user password | -| [iam\_user\_name](#output\_iam\_user\_name) | The user's name | -| [iam\_user\_ssh\_key\_fingerprint](#output\_iam\_user\_ssh\_key\_fingerprint) | The MD5 message digest of the SSH public key | -| [iam\_user\_ssh\_key\_ssh\_public\_key\_id](#output\_iam\_user\_ssh\_key\_ssh\_public\_key\_id) | The unique identifier for the SSH public key | -| [iam\_user\_unique\_id](#output\_iam\_user\_unique\_id) | The unique ID assigned by AWS | -| [keybase\_password\_decrypt\_command](#output\_keybase\_password\_decrypt\_command) | Decrypt user password command | -| [keybase\_password\_pgp\_message](#output\_keybase\_password\_pgp\_message) | Encrypted password | -| [keybase\_secret\_key\_decrypt\_command](#output\_keybase\_secret\_key\_decrypt\_command) | Decrypt access secret key command | -| [keybase\_secret\_key\_pgp\_message](#output\_keybase\_secret\_key\_pgp\_message) | Encrypted access secret key | -| [keybase\_ses\_smtp\_password\_v4\_decrypt\_command](#output\_keybase\_ses\_smtp\_password\_v4\_decrypt\_command) | Decrypt SES SMTP password command | -| [keybase\_ses\_smtp\_password\_v4\_pgp\_message](#output\_keybase\_ses\_smtp\_password\_v4\_pgp\_message) | Encrypted SES SMTP password | -| [pgp\_key](#output\_pgp\_key) | PGP key used to encrypt sensitive data for this user (if empty - secrets are not encrypted) | -| [policy\_arns](#output\_policy\_arns) | The list of ARNs of policies directly assigned to the IAM user | +| [access\_key\_encrypted\_secret](#output\_access\_key\_encrypted\_secret) | The encrypted secret, base64 encoded | +| [access\_key\_encrypted\_ses\_smtp\_password\_v4](#output\_access\_key\_encrypted\_ses\_smtp\_password\_v4) | The encrypted secret access key converted into an SES SMTP password by applying AWS's Sigv4 conversion algorithm | +| [access\_key\_fingerprint](#output\_access\_key\_fingerprint) | The fingerprint of the PGP key used to encrypt the secret | +| [access\_key\_id](#output\_access\_key\_id) | The access key ID | +| [access\_key\_secret](#output\_access\_key\_secret) | The access key secret | +| [access\_key\_ses\_smtp\_password\_v4](#output\_access\_key\_ses\_smtp\_password\_v4) | The secret access key converted into an SES SMTP password by applying AWS's Sigv4 conversion algorithm | +| [access\_key\_status](#output\_access\_key\_status) | Active or Inactive. Keys are initially active, but can be made inactive by other means | +| [arn](#output\_arn) | The ARN assigned by AWS for this user | +| [login\_profile\_encrypted\_password](#output\_login\_profile\_encrypted\_password) | The encrypted password, base64 encoded | +| [login\_profile\_key\_fingerprint](#output\_login\_profile\_key\_fingerprint) | The fingerprint of the PGP key used to encrypt the password | +| [login\_profile\_password](#output\_login\_profile\_password) | The user password | +| [name](#output\_name) | The user's name | +| [ssh\_key\_fingerprint](#output\_ssh\_key\_fingerprint) | The MD5 message digest of the SSH public key | +| [ssh\_key\_public\_key\_id](#output\_ssh\_key\_public\_key\_id) | The unique identifier for the SSH public key | +| [unique\_id](#output\_unique\_id) | The unique ID assigned by AWS | + +## License + +Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-iam/blob/master/LICENSE). diff --git a/modules/iam-user/main.tf b/modules/iam-user/main.tf index 8c228408..9e756902 100644 --- a/modules/iam-user/main.tf +++ b/modules/iam-user/main.tf @@ -1,16 +1,24 @@ +################################################################################ +# User +################################################################################ + resource "aws_iam_user" "this" { - count = var.create_user ? 1 : 0 + count = var.create ? 1 : 0 name = var.name path = var.path - force_destroy = var.force_destroy permissions_boundary = var.permissions_boundary + force_destroy = var.force_destroy tags = var.tags } +################################################################################ +# User Login Profile +################################################################################ + resource "aws_iam_user_login_profile" "this" { - count = var.create_user && var.create_iam_user_login_profile ? 1 : 0 + count = var.create && var.create_login_profile ? 1 : 0 user = aws_iam_user.this[0].name pgp_key = var.pgp_key @@ -23,32 +31,26 @@ resource "aws_iam_user_login_profile" "this" { } } +################################################################################ +# Access Key +################################################################################ + resource "aws_iam_access_key" "this" { - count = var.create_user && var.create_iam_access_key && var.pgp_key != "" ? 1 : 0 + count = var.create && var.create_access_key ? 1 : 0 user = aws_iam_user.this[0].name pgp_key = var.pgp_key - status = var.iam_access_key_status + status = var.access_key_status } -resource "aws_iam_access_key" "this_no_pgp" { - count = var.create_user && var.create_iam_access_key && var.pgp_key == "" ? 1 : 0 - - user = aws_iam_user.this[0].name - status = var.iam_access_key_status -} +################################################################################ +# SSH Key +################################################################################ resource "aws_iam_user_ssh_key" "this" { - count = var.create_user && var.upload_iam_user_ssh_key ? 1 : 0 + count = var.create && var.create_ssh_key ? 1 : 0 username = aws_iam_user.this[0].name encoding = var.ssh_key_encoding public_key = var.ssh_public_key } - -resource "aws_iam_user_policy_attachment" "this" { - for_each = { for k, v in var.policy_arns : k => v if var.create_user } - - user = aws_iam_user.this[0].name - policy_arn = each.value -} diff --git a/modules/iam-user/outputs.tf b/modules/iam-user/outputs.tf index 021443b6..9b59874d 100644 --- a/modules/iam-user/outputs.tf +++ b/modules/iam-user/outputs.tf @@ -1,156 +1,93 @@ -locals { - has_encrypted_password = length(compact(aws_iam_user_login_profile.this[*].encrypted_password)) > 0 - has_encrypted_secret = length(compact(aws_iam_access_key.this[*].encrypted_secret)) > 0 - has_encrypted_ses_smtp_password_v4 = length(compact(aws_iam_access_key.this[*].encrypted_ses_smtp_password_v4)) > 0 +################################################################################ +# User +################################################################################ + +output "arn" { + description = "The ARN assigned by AWS for this user" + value = try(aws_iam_user.this[0].arn, null) } -output "iam_user_name" { +output "name" { description = "The user's name" - value = try(aws_iam_user.this[0].name, "") + value = try(aws_iam_user.this[0].name, null) } -output "iam_user_arn" { - description = "The ARN assigned by AWS for this user" - value = try(aws_iam_user.this[0].arn, "") +output "unique_id" { + description = "The unique ID assigned by AWS" + value = try(aws_iam_user.this[0].unique_id, null) } -output "iam_user_unique_id" { - description = "The unique ID assigned by AWS" - value = try(aws_iam_user.this[0].unique_id, "") +################################################################################ +# User Login Profile +################################################################################ + +output "login_profile_password" { + description = "The user password" + value = try(aws_iam_user_login_profile.this[0].password, null) + sensitive = true } -output "iam_user_login_profile_key_fingerprint" { +output "login_profile_key_fingerprint" { description = "The fingerprint of the PGP key used to encrypt the password" - value = try(aws_iam_user_login_profile.this[0].key_fingerprint, "") + value = try(aws_iam_user_login_profile.this[0].key_fingerprint, null) } -output "iam_user_login_profile_encrypted_password" { +output "login_profile_encrypted_password" { description = "The encrypted password, base64 encoded" - value = try(aws_iam_user_login_profile.this[0].encrypted_password, "") + value = try(aws_iam_user_login_profile.this[0].encrypted_password, null) } -output "iam_user_login_profile_password" { - description = "The user password" - value = lookup(try(aws_iam_user_login_profile.this[0], {}), "password", sensitive("")) - sensitive = true -} +################################################################################ +# Access Key +################################################################################ -output "iam_access_key_id" { +output "access_key_id" { description = "The access key ID" - value = try(aws_iam_access_key.this[0].id, aws_iam_access_key.this_no_pgp[0].id, "") + value = try(aws_iam_access_key.this[0].id, null) } -output "iam_access_key_secret" { +output "access_key_secret" { description = "The access key secret" - value = try(aws_iam_access_key.this_no_pgp[0].secret, "") + value = try(aws_iam_access_key.this[0].secret, null) sensitive = true } -output "iam_access_key_key_fingerprint" { +output "access_key_fingerprint" { description = "The fingerprint of the PGP key used to encrypt the secret" - value = try(aws_iam_access_key.this[0].key_fingerprint, "") + value = try(aws_iam_access_key.this[0].key_fingerprint, null) } -output "iam_access_key_encrypted_secret" { +output "access_key_encrypted_secret" { description = "The encrypted secret, base64 encoded" - value = try(aws_iam_access_key.this[0].encrypted_secret, "") + value = try(aws_iam_access_key.this[0].encrypted_secret, null) } -output "iam_access_key_ses_smtp_password_v4" { +output "access_key_ses_smtp_password_v4" { description = "The secret access key converted into an SES SMTP password by applying AWS's Sigv4 conversion algorithm" - value = try(aws_iam_access_key.this_no_pgp[0].ses_smtp_password_v4, "") + value = try(aws_iam_access_key.this[0].ses_smtp_password_v4, null) sensitive = true } -output "iam_access_key_encrypted_ses_smtp_password_v4" { +output "access_key_encrypted_ses_smtp_password_v4" { description = "The encrypted secret access key converted into an SES SMTP password by applying AWS's Sigv4 conversion algorithm" - value = try(aws_iam_access_key.this[0].encrypted_ses_smtp_password_v4, "") + value = try(aws_iam_access_key.this[0].encrypted_ses_smtp_password_v4, null) } -output "iam_access_key_status" { - description = "Active or Inactive. Keys are initially active, but can be made inactive by other means." - value = try(aws_iam_access_key.this[0].status, aws_iam_access_key.this_no_pgp[0].status, "") +output "access_key_status" { + description = "Active or Inactive. Keys are initially active, but can be made inactive by other means" + value = try(aws_iam_access_key.this[0].status, null) } -output "pgp_key" { - description = "PGP key used to encrypt sensitive data for this user (if empty - secrets are not encrypted)" - value = var.pgp_key -} +################################################################################ +# SSH Key +################################################################################ -output "keybase_password_decrypt_command" { - description = "Decrypt user password command" - value = !local.has_encrypted_password ? null : < Date: Thu, 7 Aug 2025 10:25:28 -0500 Subject: [PATCH 03/21] feat: Bump min supported versions of Terraform and AWS provider --- UPGRADE-6.0.md | 233 ++++++++++++++++++ examples/iam-account/README.md | 4 +- examples/iam-account/versions.tf | 4 +- examples/iam-group/README.md | 6 +- examples/iam-group/versions.tf | 4 +- examples/iam-oidc-provider/README.md | 4 +- examples/iam-oidc-provider/versions.tf | 4 +- examples/iam-read-only-policy/README.md | 4 +- examples/iam-read-only-policy/versions.tf | 4 +- .../iam-role-for-service-accounts/README.md | 6 +- .../iam-role-for-service-accounts/versions.tf | 4 +- examples/iam-role-saml/README.md | 6 +- examples/iam-role-saml/versions.tf | 4 +- examples/iam-role/README.md | 6 +- examples/iam-role/versions.tf | 4 +- examples/iam-user/README.md | 4 +- examples/iam-user/versions.tf | 4 +- modules/iam-account/README.md | 6 +- modules/iam-account/versions.tf | 4 +- modules/iam-group/README.md | 6 +- modules/iam-group/versions.tf | 4 +- modules/iam-oidc-provider/README.md | 6 +- modules/iam-oidc-provider/versions.tf | 4 +- modules/iam-read-only-policy/README.md | 6 +- modules/iam-read-only-policy/versions.tf | 4 +- .../iam-role-for-service-accounts/README.md | 6 +- .../iam-role-for-service-accounts/versions.tf | 4 +- modules/iam-role-oidc/README.md | 6 +- modules/iam-role-oidc/versions.tf | 4 +- modules/iam-role-saml/README.md | 6 +- modules/iam-role-saml/versions.tf | 4 +- modules/iam-role/README.md | 6 +- modules/iam-role/versions.tf | 4 +- modules/iam-user/README.md | 6 +- modules/iam-user/main.tf | 5 - modules/iam-user/versions.tf | 4 +- wrappers/iam-account/versions.tf | 4 +- wrappers/iam-group/versions.tf | 4 +- wrappers/iam-oidc-provider/versions.tf | 4 +- wrappers/iam-read-only-policy/versions.tf | 4 +- .../iam-role-for-service-accounts/versions.tf | 4 +- wrappers/iam-role-oidc/versions.tf | 4 +- wrappers/iam-role-saml/versions.tf | 4 +- wrappers/iam-role/versions.tf | 4 +- wrappers/iam-user/versions.tf | 4 +- 45 files changed, 332 insertions(+), 104 deletions(-) create mode 100644 UPGRADE-6.0.md diff --git a/UPGRADE-6.0.md b/UPGRADE-6.0.md new file mode 100644 index 00000000..86ab727d --- /dev/null +++ b/UPGRADE-6.0.md @@ -0,0 +1,233 @@ +# Upgrade from v5.x to v6.x + +If you have any questions regarding this upgrade process, please consult the [`examples`](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/) directory: + +If you find a bug, please open an issue with supporting configuration to reproduce. + +## List of backwards incompatible changes + +- `iam-assumable-role` has been renamed to `iam-role` +- `iam-assumable-role-with-oidc` has been renamed to `iam-role-oidc` +- `iam-assumable-role-with-saml` has been renamed to `iam-role-saml` +- `iam-assumable-roles` has been removed; `iam-role` should be used instead. See the [`iam-role` example](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-role) that shows an example replacement implementation. +- `iam-assumable-roles-with-saml` has been removed; `iam-role-saml` should be used instead. See the [`iam-role-saml` example](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-role-saml) that shows an example replacement implementation. +- `iam-github-oidc-provider` has been renamed to `iam-oidc-provider` +- `iam-github-oidc-role` has been removed; `iam-role-oidc` should be used instead. See the [`iam-oidc-provider` example](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-oidc-provider) +- `iam-group-with-assumable-roles-policy` has been removed; the renamed `iam-group` (was `iam-group-with-policies`) should be used instead +- `iam-eks-role` has been removed; `iam-role-for-service-accounts-eks` should be used instead +- `iam-policy` has been removed; the `aws_iam_policy` resource should be used directly instead + +## Additional changes + +### Modified + +- `iam-role` + - The use of individual variables to control/manipulate the assume role trust policy have been replaced by a generic `assume_role_policy_statements` variable. This allows for any number of custom statements to be added to the role's trust policy. + - `custom_role_policy_arns` has been renamed to `policies` and now accepts a map of `name`: `policy-arn` pairs; this allows for both existing policies and policies that will get created at the same time as the role. This also replaces the admin, readonly, and poweruser policy ARN variables and their associated `attach_*_policy` variables. + - Default create conditional is now `true` instead of `false` + - `force_detach_policies` has been removed; this is now always `true` +- `iam-role-oidc` + - `custom_role_policy_arns` has been renamed to `policies` and now accepts a map of `name`: `policy-arn` pairs; this allows for both existing policies and policies that will get created at the same time as the role. + - Default create conditional is now `true` instead of `false` + - `force_detach_policies` has been removed; this is now always `true` +- `iam-role-saml` + - `custom_role_policy_arns` has been renamed to `policies` and now accepts a map of `name`: `policy-arn` pairs; this allows for both existing policies and policies that will get created at the same time as the role. + - Default create conditional is now `true` instead of `false` + - `force_detach_policies` has been removed; this is now always `true` +- `iam-group` + - Policy management has been updated to support extending the policy created by the sub-module, as well as adding additional policies that will be attached to the group + - The role assumption permissions has been removed from the policy; users can extend the policy to add this if needed via `permission_statements` + - Default create conditional is now `true` instead of `false` + +### Variable and output changes + +1. Removed variables: + + - `iam-role` + - `trusted_role_actions` + - `trusted_role_arns` + - `trusted_role_services` + - `mfa_age` + - `role_requires_mfa` + - `custom_role_trust_policy` + - `number_of_custom_role_policy_arns` + - `admin_role_policy_arn` & `attach_admin_policy` + - `poweruser_role_policy_arn` & `attach_poweruser_policy` + - `readonly_role_policy_arn` & `attach_readonly_policy` + - `force_detach_policies` + - `role_sts_externalid` + - `iam-role-oidc` + - `force_detach_policies` + - `number_of_custom_role_policy_arns` + - `iam-role-saml` + - `force_detach_policies` + - `number_of_custom_role_policy_arns` + - `iam-group` + - `custom_group_policies` + - `assumable_roles` + +2. Renamed variables: + + - `iam-role` + - `create_role` -> `create` + - `role_name` -> `name` + - `role_name_prefix` -> `name_prefix` + - `role_description` -> `description` + - `role_path` -> `path` + - `role_permissions_boundary_arn` -> `permissions_boundary_arn` + - `custom_role_policy_arns` -> `policies` + - `iam-role-oidc` + - `create_role` -> `create` + - `role_name` -> `name` + - `role_name_prefix` -> `name_prefix` + - `role_description` -> `description` + - `role_path` -> `path` + - `role_permissions_boundary_arn` -> `permissions_boundary_arn` + - `custom_role_policy_arns` -> `policies` + - `iam-role-saml` + - `create_role` -> `create` + - `role_name` -> `name` + - `role_name_prefix` -> `name_prefix` + - `role_description` -> `description` + - `role_path` -> `path` + - `role_permissions_boundary_arn` -> `permissions_boundary_arn` + - `custom_role_policy_arns` -> `policies` + - `aws_saml_endpoint` -> `saml_endpoints` + - `trusted_role_actions` -> `saml_trust_actions` + - `iam-group` + - `create_group` -> `create` + - `group_users` -> `group` + - `custom_group_policy_arns` -> `policies` + - `attach_iam_self_management_policy` -> `create_policy` + - `iam_self_management_policy_name_prefix` -> `policy_name_prefix` + - `aws_account_id` -> `users_account_id` + +3. Added variables: + + - `iam-role` + - `assume_role_policy_statements` which allows for any number of custom statements to be added to the role's trust policy. This covers the majority of the variables that were removed + - `iam-role-oidc` + - `assume_role_policy_statements` which allows for any number of custom statements to be added to the role's trust policy. This covers the majority of the variables that were removed + - `iam-role-saml` + - `assume_role_policy_statements` which allows for any number of custom statements to be added to the role's trust policy. This covers the majority of the variables that were removed + - `iam-group` + - `permission_statements` which allows for any number of custom statements to be added to the role's trust policy. This covers the majority of the variables that were removed + - `path`/`policy_path` + - `create_policy` + - `enable_mfa_enforcment` + +4. Removed outputs: + + - `iam-role` + - `iam_role_path` + - `role_requires_mfa` + - `iam_instance_profile_path` + - `role_sts_externalid` + - `iam-role-oidc` + - `iam_role_path` + - `provider_url` (use `oidc_provider_urls` instead) + - `iam-role-saml` + - `iam_role_path` + - `provider_id` (use `saml_provider_ids` instead) + - `iam-group` + - `assumable_roles` + - `aws_account_id` + +5. Renamed outputs: + + - `iam-role` + - `iam_role_arn` -> `arn` + - `iam_role_name` -> `name` + - `iam_role_unique_id` -> `unique_id` + - `iam_instance_profile_arn` -> `instance_profile_arn` + - `iam_instance_profile_id` -> `instance_profile_id` + - `iam_instance_profile_name` -> `instance_profile_name` + - `iam_instance_profile_unique_id` -> `instance_profile_unique_id` + - `iam-role-oidc` + - `iam_role_arn` -> `arn` + - `iam_role_name` -> `name` + - `iam_role_unique_id` -> `unique_id` + - `aws_account_id` -> `oidc_account_id` + - `provider_urls` -> `oidc_provider_urls` + - `iam-role-oidc` + - `iam_role_arn` -> `arn` + - `iam_role_name` -> `name` + - `iam_role_unique_id` -> `unique_id` + - `aws_account_id` -> `oidc_account_id` + - `provider_ids` -> `saml_provider_ids` + - `iam-group` + - `group_id` -> `id` + - `group_name` -> `name` + - `group_arn` -> `arn` + - `group_users` -> `users` + +6. Added outputs: + + - `iam-group` + - `unique_id` + - `policy_id` + +### Diff of before <> after + +#### `iam-role` + +```diff +module "iam_role" { +- source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role" ++ source = "terraform-aws-modules/iam/aws//modules/iam-role" +- version = "~> 5.0" ++ version = "~> 6.0" + +- create_role = true ++ create = true # is now `true` by default + +- role_requires_mfa = true +- trusted_role_arns = [ +- "arn:aws:iam::307990089504:root", +- "arn:aws:iam::835367859851:user/anton", +- ] +- trusted_role_services = [ +- "codedeploy.amazonaws.com" +- ] +- role_sts_externalid = ["some-id-goes-here"] ++ assume_role_policy_statements = [ ++ { ++ sid = "TrustRoleAndServiceToAssume" ++ principals = [ ++ { ++ type = "AWS" ++ identifiers = [ ++ "arn:aws:iam::307990089504:root", ++ "arn:aws:iam::835367859851:user/anton", ++ ] ++ }, ++ { ++ type = "Service" ++ identifiers = ["codedeploy.amazonaws.com"] ++ } ++ ] ++ conditions = [{ ++ test = "StringEquals" ++ variable = "sts:ExternalId" ++ values = ["some-secret-id"] ++ }] ++ } ++ ] + +- attach_admin_policy = true +- custom_role_policy_arns = [ +- "arn:aws:iam::aws:policy/AmazonCognitoReadOnly", +- "arn:aws:iam::aws:policy/AlexaForBusinessFullAccess", +- module.iam_policy.arn +- ] ++ policies = { ++ AdministratorAccess = "arn:aws:iam::aws:policy/AdministratorAccess" ++ AmazonCognitoReadOnly = "arn:aws:iam::aws:policy/AmazonCognitoReadOnly" ++ AlexaForBusinessFullAccess = "arn:aws:iam::aws:policy/AlexaForBusinessFullAccess" ++ custom = module.iam_policy.arn ++ } +} + +### State Changes + +None diff --git a/examples/iam-account/README.md b/examples/iam-account/README.md index 695391f4..ca9445d3 100644 --- a/examples/iam-account/README.md +++ b/examples/iam-account/README.md @@ -19,8 +19,8 @@ Run `terraform destroy` when you don't need these resources. | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers diff --git a/examples/iam-account/versions.tf b/examples/iam-account/versions.tf index d8dd1a44..db13b0a8 100644 --- a/examples/iam-account/versions.tf +++ b/examples/iam-account/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } diff --git a/examples/iam-group/README.md b/examples/iam-group/README.md index 2d347247..0991a277 100644 --- a/examples/iam-group/README.md +++ b/examples/iam-group/README.md @@ -19,14 +19,14 @@ Run `terraform destroy` when you don't need these resources. | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 4.0 | +| [aws](#provider\_aws) | >= 6.0 | ## Modules diff --git a/examples/iam-group/versions.tf b/examples/iam-group/versions.tf index d8dd1a44..db13b0a8 100644 --- a/examples/iam-group/versions.tf +++ b/examples/iam-group/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } diff --git a/examples/iam-oidc-provider/README.md b/examples/iam-oidc-provider/README.md index b81f8bcc..e80ce875 100644 --- a/examples/iam-oidc-provider/README.md +++ b/examples/iam-oidc-provider/README.md @@ -22,8 +22,8 @@ Run `terraform destroy` when you don't need these resources. | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers diff --git a/examples/iam-oidc-provider/versions.tf b/examples/iam-oidc-provider/versions.tf index d8dd1a44..db13b0a8 100644 --- a/examples/iam-oidc-provider/versions.tf +++ b/examples/iam-oidc-provider/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } diff --git a/examples/iam-read-only-policy/README.md b/examples/iam-read-only-policy/README.md index 0188edad..054036e8 100644 --- a/examples/iam-read-only-policy/README.md +++ b/examples/iam-read-only-policy/README.md @@ -19,8 +19,8 @@ Run `terraform destroy` when you don't need these resources. | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers diff --git a/examples/iam-read-only-policy/versions.tf b/examples/iam-read-only-policy/versions.tf index d8dd1a44..db13b0a8 100644 --- a/examples/iam-read-only-policy/versions.tf +++ b/examples/iam-read-only-policy/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } diff --git a/examples/iam-role-for-service-accounts/README.md b/examples/iam-role-for-service-accounts/README.md index d3d5eef8..71e4e4f0 100644 --- a/examples/iam-role-for-service-accounts/README.md +++ b/examples/iam-role-for-service-accounts/README.md @@ -19,14 +19,14 @@ Run `terraform destroy` when you don't need these resources. | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 4.0 | +| [aws](#provider\_aws) | >= 6.0 | ## Modules diff --git a/examples/iam-role-for-service-accounts/versions.tf b/examples/iam-role-for-service-accounts/versions.tf index d8dd1a44..db13b0a8 100644 --- a/examples/iam-role-for-service-accounts/versions.tf +++ b/examples/iam-role-for-service-accounts/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } diff --git a/examples/iam-role-saml/README.md b/examples/iam-role-saml/README.md index 38a2a193..0bda0aa0 100644 --- a/examples/iam-role-saml/README.md +++ b/examples/iam-role-saml/README.md @@ -19,14 +19,14 @@ Run `terraform destroy` when you don't need these resources. | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 4.0 | +| [aws](#provider\_aws) | >= 6.0 | ## Modules diff --git a/examples/iam-role-saml/versions.tf b/examples/iam-role-saml/versions.tf index d8dd1a44..db13b0a8 100644 --- a/examples/iam-role-saml/versions.tf +++ b/examples/iam-role-saml/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } diff --git a/examples/iam-role/README.md b/examples/iam-role/README.md index 1a84e667..f9dd429f 100644 --- a/examples/iam-role/README.md +++ b/examples/iam-role/README.md @@ -19,14 +19,14 @@ Run `terraform destroy` when you don't need these resources. | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 4.0 | +| [aws](#provider\_aws) | >= 6.0 | ## Modules diff --git a/examples/iam-role/versions.tf b/examples/iam-role/versions.tf index d8dd1a44..db13b0a8 100644 --- a/examples/iam-role/versions.tf +++ b/examples/iam-role/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } diff --git a/examples/iam-user/README.md b/examples/iam-user/README.md index 91d3c4bc..10dd1ffd 100644 --- a/examples/iam-user/README.md +++ b/examples/iam-user/README.md @@ -20,8 +20,8 @@ Run `terraform destroy` when you don't need these resources. | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers diff --git a/examples/iam-user/versions.tf b/examples/iam-user/versions.tf index d8dd1a44..db13b0a8 100644 --- a/examples/iam-user/versions.tf +++ b/examples/iam-user/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } diff --git a/modules/iam-account/README.md b/modules/iam-account/README.md index 6f2bb7d8..1244aac1 100644 --- a/modules/iam-account/README.md +++ b/modules/iam-account/README.md @@ -45,14 +45,14 @@ Import successful! | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 4.0 | +| [aws](#provider\_aws) | >= 6.0 | ## Modules diff --git a/modules/iam-account/versions.tf b/modules/iam-account/versions.tf index d8dd1a44..db13b0a8 100644 --- a/modules/iam-account/versions.tf +++ b/modules/iam-account/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } diff --git a/modules/iam-group/README.md b/modules/iam-group/README.md index cb06157d..0e80e73b 100644 --- a/modules/iam-group/README.md +++ b/modules/iam-group/README.md @@ -41,14 +41,14 @@ module "iam_group" { | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 4.0 | +| [aws](#provider\_aws) | >= 6.0 | ## Modules diff --git a/modules/iam-group/versions.tf b/modules/iam-group/versions.tf index d8dd1a44..db13b0a8 100644 --- a/modules/iam-group/versions.tf +++ b/modules/iam-group/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } diff --git a/modules/iam-oidc-provider/README.md b/modules/iam-oidc-provider/README.md index 7c5da1aa..58450ade 100644 --- a/modules/iam-oidc-provider/README.md +++ b/modules/iam-oidc-provider/README.md @@ -43,15 +43,15 @@ module "iam_oidc_provider" { | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | | [tls](#requirement\_tls) | >= 3.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 4.0 | +| [aws](#provider\_aws) | >= 6.0 | | [tls](#provider\_tls) | >= 3.0 | ## Modules diff --git a/modules/iam-oidc-provider/versions.tf b/modules/iam-oidc-provider/versions.tf index 3501ad27..c46f0c4f 100644 --- a/modules/iam-oidc-provider/versions.tf +++ b/modules/iam-oidc-provider/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } tls = { source = "hashicorp/tls" diff --git a/modules/iam-read-only-policy/README.md b/modules/iam-read-only-policy/README.md index fe12b0d1..1a802e3f 100644 --- a/modules/iam-read-only-policy/README.md +++ b/modules/iam-read-only-policy/README.md @@ -28,14 +28,14 @@ module "iam_read_only_policy" { | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 4.0 | +| [aws](#provider\_aws) | >= 6.0 | ## Modules diff --git a/modules/iam-read-only-policy/versions.tf b/modules/iam-read-only-policy/versions.tf index d8dd1a44..db13b0a8 100644 --- a/modules/iam-read-only-policy/versions.tf +++ b/modules/iam-read-only-policy/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } diff --git a/modules/iam-role-for-service-accounts/README.md b/modules/iam-role-for-service-accounts/README.md index 674606e7..c92306f3 100644 --- a/modules/iam-role-for-service-accounts/README.md +++ b/modules/iam-role-for-service-accounts/README.md @@ -124,14 +124,14 @@ module "eks" { | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 4.0 | +| [aws](#provider\_aws) | >= 6.0 | ## Modules diff --git a/modules/iam-role-for-service-accounts/versions.tf b/modules/iam-role-for-service-accounts/versions.tf index d8dd1a44..db13b0a8 100644 --- a/modules/iam-role-for-service-accounts/versions.tf +++ b/modules/iam-role-for-service-accounts/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } diff --git a/modules/iam-role-oidc/README.md b/modules/iam-role-oidc/README.md index 257b6991..3379a1e3 100644 --- a/modules/iam-role-oidc/README.md +++ b/modules/iam-role-oidc/README.md @@ -58,14 +58,14 @@ module "iam_oidc_role" { | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 4.0 | +| [aws](#provider\_aws) | >= 6.0 | ## Modules diff --git a/modules/iam-role-oidc/versions.tf b/modules/iam-role-oidc/versions.tf index d8dd1a44..db13b0a8 100644 --- a/modules/iam-role-oidc/versions.tf +++ b/modules/iam-role-oidc/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } diff --git a/modules/iam-role-saml/README.md b/modules/iam-role-saml/README.md index 0b3db2bc..30102f88 100644 --- a/modules/iam-role-saml/README.md +++ b/modules/iam-role-saml/README.md @@ -33,14 +33,14 @@ module "iam_role_saml" { | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 4.0 | +| [aws](#provider\_aws) | >= 6.0 | ## Modules diff --git a/modules/iam-role-saml/versions.tf b/modules/iam-role-saml/versions.tf index d8dd1a44..db13b0a8 100644 --- a/modules/iam-role-saml/versions.tf +++ b/modules/iam-role-saml/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } diff --git a/modules/iam-role/README.md b/modules/iam-role/README.md index c6290553..28b80650 100644 --- a/modules/iam-role/README.md +++ b/modules/iam-role/README.md @@ -45,14 +45,14 @@ module "iam_role" { | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 4.0 | +| [aws](#provider\_aws) | >= 6.0 | ## Modules diff --git a/modules/iam-role/versions.tf b/modules/iam-role/versions.tf index d8dd1a44..db13b0a8 100644 --- a/modules/iam-role/versions.tf +++ b/modules/iam-role/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } diff --git a/modules/iam-user/README.md b/modules/iam-user/README.md index 7db3b16a..8e987a37 100644 --- a/modules/iam-user/README.md +++ b/modules/iam-user/README.md @@ -32,14 +32,14 @@ When `pgp_key` is specified as `keybase:username`, make sure that that user has | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 4.0 | +| [aws](#provider\_aws) | >= 6.0 | ## Modules diff --git a/modules/iam-user/main.tf b/modules/iam-user/main.tf index 9e756902..41ede7e9 100644 --- a/modules/iam-user/main.tf +++ b/modules/iam-user/main.tf @@ -24,11 +24,6 @@ resource "aws_iam_user_login_profile" "this" { pgp_key = var.pgp_key password_length = var.password_length password_reset_required = var.password_reset_required - - # TODO: Remove once https://github.com/hashicorp/terraform-provider-aws/issues/23567 is resolved - lifecycle { - ignore_changes = [password_reset_required] - } } ################################################################################ diff --git a/modules/iam-user/versions.tf b/modules/iam-user/versions.tf index d8dd1a44..db13b0a8 100644 --- a/modules/iam-user/versions.tf +++ b/modules/iam-user/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } diff --git a/wrappers/iam-account/versions.tf b/wrappers/iam-account/versions.tf index d8dd1a44..db13b0a8 100644 --- a/wrappers/iam-account/versions.tf +++ b/wrappers/iam-account/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } diff --git a/wrappers/iam-group/versions.tf b/wrappers/iam-group/versions.tf index d8dd1a44..db13b0a8 100644 --- a/wrappers/iam-group/versions.tf +++ b/wrappers/iam-group/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } diff --git a/wrappers/iam-oidc-provider/versions.tf b/wrappers/iam-oidc-provider/versions.tf index 3501ad27..c46f0c4f 100644 --- a/wrappers/iam-oidc-provider/versions.tf +++ b/wrappers/iam-oidc-provider/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } tls = { source = "hashicorp/tls" diff --git a/wrappers/iam-read-only-policy/versions.tf b/wrappers/iam-read-only-policy/versions.tf index d8dd1a44..db13b0a8 100644 --- a/wrappers/iam-read-only-policy/versions.tf +++ b/wrappers/iam-read-only-policy/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } diff --git a/wrappers/iam-role-for-service-accounts/versions.tf b/wrappers/iam-role-for-service-accounts/versions.tf index d8dd1a44..db13b0a8 100644 --- a/wrappers/iam-role-for-service-accounts/versions.tf +++ b/wrappers/iam-role-for-service-accounts/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } diff --git a/wrappers/iam-role-oidc/versions.tf b/wrappers/iam-role-oidc/versions.tf index d8dd1a44..db13b0a8 100644 --- a/wrappers/iam-role-oidc/versions.tf +++ b/wrappers/iam-role-oidc/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } diff --git a/wrappers/iam-role-saml/versions.tf b/wrappers/iam-role-saml/versions.tf index d8dd1a44..db13b0a8 100644 --- a/wrappers/iam-role-saml/versions.tf +++ b/wrappers/iam-role-saml/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } diff --git a/wrappers/iam-role/versions.tf b/wrappers/iam-role/versions.tf index d8dd1a44..db13b0a8 100644 --- a/wrappers/iam-role/versions.tf +++ b/wrappers/iam-role/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } diff --git a/wrappers/iam-user/versions.tf b/wrappers/iam-user/versions.tf index d8dd1a44..db13b0a8 100644 --- a/wrappers/iam-user/versions.tf +++ b/wrappers/iam-user/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } From 2d9d21371208a39d4eeb3ae1a387a6f10ccde22e Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Thu, 7 Aug 2025 11:59:47 -0500 Subject: [PATCH 04/21] chore: Update naming use and remove role self-assume --- examples/iam-role/main.tf | 2 - modules/iam-account/README.md | 13 ++--- modules/iam-account/main.tf | 14 ++++-- modules/iam-account/outputs.tf | 10 ++-- modules/iam-account/variables.tf | 14 ++++-- modules/iam-group/README.md | 5 +- modules/iam-group/main.tf | 40 +++++++++------ modules/iam-group/variables.tf | 34 +++++++++++-- modules/iam-oidc-provider/main.tf | 12 +++-- modules/iam-oidc-provider/variables.tf | 4 ++ modules/iam-read-only-policy/README.md | 2 +- modules/iam-read-only-policy/main.tf | 7 ++- modules/iam-read-only-policy/variables.tf | 8 +-- modules/iam-role-oidc/README.md | 5 +- modules/iam-role-oidc/main.tf | 60 ++++++++-------------- modules/iam-role-oidc/variables.tf | 38 +++++++++----- modules/iam-role-saml/README.md | 7 +-- modules/iam-role-saml/main.tf | 53 +++++--------------- modules/iam-role-saml/variables.tf | 38 +++++++++----- modules/iam-role/README.md | 7 +-- modules/iam-role/main.tf | 61 ++++++----------------- modules/iam-role/variables.tf | 38 +++++++++----- wrappers/iam-account/main.tf | 2 +- wrappers/iam-group/main.tf | 5 +- wrappers/iam-read-only-policy/main.tf | 2 +- wrappers/iam-role-oidc/main.tf | 5 +- wrappers/iam-role-saml/main.tf | 5 +- wrappers/iam-role/main.tf | 5 +- 28 files changed, 254 insertions(+), 242 deletions(-) diff --git a/examples/iam-role/main.tf b/examples/iam-role/main.tf index 6b58a2ec..3797e384 100644 --- a/examples/iam-role/main.tf +++ b/examples/iam-role/main.tf @@ -23,8 +23,6 @@ module "iam_role_instance_profile" { name = "${local.name}-instance-profile" - # https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/ - allow_self_assume_role = true create_instance_profile = true assume_role_policy_statements = [ diff --git a/modules/iam-account/README.md b/modules/iam-account/README.md index 1244aac1..fad8cb27 100644 --- a/modules/iam-account/README.md +++ b/modules/iam-account/README.md @@ -24,12 +24,14 @@ module "iam_account" { ## Notes If IAM account alias was previously set (either via AWS console or during the creation of an account from AWS Organizations) you will see this error: -``` + +```sh aws_iam_account_alias.this: Error creating account alias with name my-account-alias ``` If you want to manage IAM alias using Terraform (otherwise why are you reading this?) you need to import this resource like this: -``` + +```sh $ terraform import module.iam_account.aws_iam_account_alias.this this module.iam_account.aws_iam_account_alias.this: Importing from ID "this"... @@ -64,7 +66,6 @@ No modules. |------|------| | [aws_iam_account_alias.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_account_alias) | resource | | [aws_iam_account_password_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_account_password_policy) | resource | -| [aws_caller_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | ## Inputs @@ -72,10 +73,10 @@ No modules. |------|-------------|------|---------|:--------:| | [account\_alias](#input\_account\_alias) | AWS IAM account alias for this account | `string` | n/a | yes | | [allow\_users\_to\_change\_password](#input\_allow\_users\_to\_change\_password) | Whether to allow users to change their own password | `bool` | `true` | no | +| [create](#input\_create) | Determines whether resources will be created (affects all resources) | `bool` | `true` | no | | [create\_account\_password\_policy](#input\_create\_account\_password\_policy) | Whether to create AWS IAM account password policy | `bool` | `true` | no | -| [get\_caller\_identity](#input\_get\_caller\_identity) | Whether to get AWS account ID, User ID, and ARN in which Terraform is authorized | `bool` | `true` | no | | [hard\_expiry](#input\_hard\_expiry) | Whether users are prevented from setting a new password after their password has expired (i.e. require administrator reset) | `bool` | `false` | no | -| [max\_password\_age](#input\_max\_password\_age) | The number of days that an user password is valid. | `number` | `0` | no | +| [max\_password\_age](#input\_max\_password\_age) | The number of days that an user password is valid | `number` | `0` | no | | [minimum\_password\_length](#input\_minimum\_password\_length) | Minimum length to require for user passwords | `number` | `8` | no | | [password\_reuse\_prevention](#input\_password\_reuse\_prevention) | The number of previous passwords that users are prevented from reusing | `number` | `null` | no | | [require\_lowercase\_characters](#input\_require\_lowercase\_characters) | Whether to require lowercase characters for user passwords | `bool` | `true` | no | @@ -90,7 +91,7 @@ No modules. | [caller\_identity\_account\_id](#output\_caller\_identity\_account\_id) | The AWS Account ID number of the account that owns or contains the calling entity | | [caller\_identity\_arn](#output\_caller\_identity\_arn) | The AWS ARN associated with the calling entity | | [caller\_identity\_user\_id](#output\_caller\_identity\_user\_id) | The unique identifier of the calling entity | -| [iam\_account\_password\_policy\_expire\_passwords](#output\_iam\_account\_password\_policy\_expire\_passwords) | Indicates whether passwords in the account expire. Returns true if max\_password\_age contains a value greater than 0. Returns false if it is 0 or not present. | +| [iam\_account\_password\_policy\_expire\_passwords](#output\_iam\_account\_password\_policy\_expire\_passwords) | Indicates whether passwords in the account expire. Returns true if max\_password\_age contains a value greater than 0. Returns false if it is 0 or not present | ## License diff --git a/modules/iam-account/main.tf b/modules/iam-account/main.tf index f53d596a..a9fc9828 100644 --- a/modules/iam-account/main.tf +++ b/modules/iam-account/main.tf @@ -1,13 +1,19 @@ -data "aws_caller_identity" "this" { - count = var.get_caller_identity ? 1 : 0 -} +################################################################################ +# Alias +################################################################################ resource "aws_iam_account_alias" "this" { + count = var.create ? 1 : 0 + account_alias = var.account_alias } +################################################################################ +# Password Policy +################################################################################ + resource "aws_iam_account_password_policy" "this" { - count = var.create_account_password_policy ? 1 : 0 + count = var.create && var.create_account_password_policy ? 1 : 0 max_password_age = var.max_password_age minimum_password_length = var.minimum_password_length diff --git a/modules/iam-account/outputs.tf b/modules/iam-account/outputs.tf index c286d1e5..4254da21 100644 --- a/modules/iam-account/outputs.tf +++ b/modules/iam-account/outputs.tf @@ -1,19 +1,19 @@ output "caller_identity_account_id" { description = "The AWS Account ID number of the account that owns or contains the calling entity" - value = try(data.aws_caller_identity.this[0].account_id, "") + value = try(data.aws_caller_identity.this[0].account_id, null) } output "caller_identity_arn" { description = "The AWS ARN associated with the calling entity" - value = try(data.aws_caller_identity.this[0].arn, "") + value = try(data.aws_caller_identity.this[0].arn, null) } output "caller_identity_user_id" { description = "The unique identifier of the calling entity" - value = try(data.aws_caller_identity.this[0].user_id, "") + value = try(data.aws_caller_identity.this[0].user_id, null) } output "iam_account_password_policy_expire_passwords" { - description = "Indicates whether passwords in the account expire. Returns true if max_password_age contains a value greater than 0. Returns false if it is 0 or not present." - value = try(aws_iam_account_password_policy.this[0].expire_passwords, "") + description = "Indicates whether passwords in the account expire. Returns true if max_password_age contains a value greater than 0. Returns false if it is 0 or not present" + value = try(aws_iam_account_password_policy.this[0].expire_passwords, null) } diff --git a/modules/iam-account/variables.tf b/modules/iam-account/variables.tf index 7f58f8a3..b79b9755 100644 --- a/modules/iam-account/variables.tf +++ b/modules/iam-account/variables.tf @@ -1,14 +1,22 @@ -variable "get_caller_identity" { - description = "Whether to get AWS account ID, User ID, and ARN in which Terraform is authorized" +variable "create" { + description = "Determines whether resources will be created (affects all resources)" type = bool default = true } +################################################################################ +# Alias +################################################################################ + variable "account_alias" { description = "AWS IAM account alias for this account" type = string } +################################################################################ +# Password Policy +################################################################################ + variable "create_account_password_policy" { description = "Whether to create AWS IAM account password policy" type = bool @@ -16,7 +24,7 @@ variable "create_account_password_policy" { } variable "max_password_age" { - description = "The number of days that an user password is valid." + description = "The number of days that an user password is valid" type = number default = 0 } diff --git a/modules/iam-group/README.md b/modules/iam-group/README.md index 0e80e73b..87f3a4bb 100644 --- a/modules/iam-group/README.md +++ b/modules/iam-group/README.md @@ -77,11 +77,12 @@ No modules. | [enable\_self\_management\_permissions](#input\_enable\_self\_management\_permissions) | Determines whether permissions are added to the policy which allow the groups IAM users to manage their credentials and MFA | `bool` | `true` | no | | [name](#input\_name) | The group's name. The name must consist of upper and lowercase alphanumeric characters with no spaces. You can also include any of the following characters: `=,.@-_.` | `string` | `""` | no | | [path](#input\_path) | Path in which to create the group | `string` | `null` | no | -| [permission\_statements](#input\_permission\_statements) | List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for the policy | `any` | `[]` | no | +| [permission\_statements](#input\_permission\_statements) | List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for the policy |
list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
}))
| `null` | no | | [policies](#input\_policies) | Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no | | [policy\_description](#input\_policy\_description) | Description of the IAM policy | `string` | `null` | no | -| [policy\_name\_prefix](#input\_policy\_name\_prefix) | Name prefix for IAM policy | `string` | `null` | no | +| [policy\_name](#input\_policy\_name) | Name to use on IAM policy created | `string` | `null` | no | | [policy\_path](#input\_policy\_path) | The IAM policy path | `string` | `null` | no | +| [policy\_use\_name\_prefix](#input\_policy\_use\_name\_prefix) | Determines whether the IAM policy name (`policy_name`) is used as a prefix | `bool` | `true` | no | | [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | | [users](#input\_users) | A list of IAM User names to associate with the Group | `list(string)` | `[]` | no | | [users\_account\_id](#input\_users\_account\_id) | An overriding AWS account ID where the group's users reside; leave empty to use the current account ID for the AWS provider | `string` | `null` | no | diff --git a/modules/iam-group/main.tf b/modules/iam-group/main.tf index f2ce5642..14eec8a5 100644 --- a/modules/iam-group/main.tf +++ b/modules/iam-group/main.tf @@ -1,11 +1,16 @@ -data "aws_partition" "current" {} -data "aws_caller_identity" "current" {} +data "aws_partition" "current" { + count = var.create ? 1 : 0 +} +data "aws_caller_identity" "current" { + count = var.create ? 1 : 0 +} locals { - users_account_id = coalesce(var.users_account_id, data.aws_caller_identity.current.account_id) + partition = try(data.aws_partition.current[0].partition, "") + users_account_id = try(coalesce(var.users_account_id, data.aws_caller_identity.current[0].account_id), "") user_resources = [for pattern in ["user/$${aws:username}", "user/*/$${aws:username}"] : - "arn:${data.aws_partition.current.partition}:iam::${local.users_account_id}:${pattern}" + "arn:${local.partition}:iam::${local.users_account_id}:${pattern}" ] } @@ -34,6 +39,8 @@ resource "aws_iam_group_membership" "this" { locals { create_policy = var.create && var.create_policy && (var.enable_self_management_permissions || length(var.permission_statements) > 0) + + policy_name = try(coalesce(var.policy_name, var.name), "") } # Allows MFA-authenticated IAM users to manage their own credentials on the My security credentials page @@ -135,7 +142,7 @@ data "aws_iam_policy_document" "this" { content { sid = "ManageOwnVirtualMFADevice" actions = ["iam:CreateVirtualMFADevice"] - resources = ["arn:${data.aws_partition.current.partition}:iam::${local.users_account_id}:mfa/*"] + resources = ["arn:${local.partition}:iam::${local.users_account_id}:mfa/*"] } } @@ -180,18 +187,18 @@ data "aws_iam_policy_document" "this" { } dynamic "statement" { - for_each = var.permission_statements + for_each = var.permission_statements != null ? var.permission_statements : [] content { - sid = try(statement.value.sid, null) - actions = try(statement.value.actions, null) - not_actions = try(statement.value.not_actions, null) - effect = try(statement.value.effect, null) - resources = try(statement.value.resources, null) - not_resources = try(statement.value.not_resources, null) + sid = statement.value.sid + actions = statement.value.actions + not_actions = statement.value.not_actions + effect = statement.value.effect + resources = statement.value.resources + not_resources = statement.value.not_resources dynamic "principals" { - for_each = try(statement.value.principals, []) + for_each = statement.value.principals != null ? statement.value.principals : [] content { type = principals.value.type @@ -200,7 +207,7 @@ data "aws_iam_policy_document" "this" { } dynamic "not_principals" { - for_each = try(statement.value.not_principals, []) + for_each = statement.value.not_principals != null ? statement.value.not_principals : [] content { type = not_principals.value.type @@ -209,7 +216,7 @@ data "aws_iam_policy_document" "this" { } dynamic "condition" { - for_each = try(statement.value.conditions, []) + for_each = statement.value.condition != null ? statement.value.condition : [] content { test = condition.value.test @@ -224,8 +231,9 @@ data "aws_iam_policy_document" "this" { resource "aws_iam_policy" "this" { count = local.create_policy ? 1 : 0 - name_prefix = try(coalesce(var.policy_name_prefix, "${var.name}-"), null) description = var.policy_description + name = var.policy_use_name_prefix ? null : local.policy_name + name_prefix = var.policy_use_name_prefix ? "${local.policy_name}-" : null path = coalesce(var.policy_path, var.path, "/") policy = data.aws_iam_policy_document.this[0].json diff --git a/modules/iam-group/variables.tf b/modules/iam-group/variables.tf index ca455d3d..8d619c06 100644 --- a/modules/iam-group/variables.tf +++ b/modules/iam-group/variables.tf @@ -56,16 +56,42 @@ variable "enable_mfa_enforcment" { variable "permission_statements" { description = "List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for the policy" - type = any - default = [] + type = list(object({ + sid = optional(string) + actions = optional(list(string)) + not_actions = optional(list(string)) + effect = optional(string) + resources = optional(list(string)) + not_resources = optional(list(string)) + principals = optional(list(object({ + type = string + identifiers = list(string) + }))) + not_principals = optional(list(object({ + type = string + identifiers = list(string) + }))) + condition = optional(list(object({ + test = string + values = list(string) + variable = string + }))) + })) + default = null } -variable "policy_name_prefix" { - description = "Name prefix for IAM policy" +variable "policy_name" { + description = "Name to use on IAM policy created" type = string default = null } +variable "policy_use_name_prefix" { + description = "Determines whether the IAM policy name (`policy_name`) is used as a prefix" + type = bool + default = true +} + variable "policy_description" { description = "Description of the IAM policy" type = string diff --git a/modules/iam-oidc-provider/main.tf b/modules/iam-oidc-provider/main.tf index b68afb44..94184ff7 100644 --- a/modules/iam-oidc-provider/main.tf +++ b/modules/iam-oidc-provider/main.tf @@ -1,7 +1,13 @@ -data "aws_partition" "current" {} +data "aws_partition" "current" { + count = var.create ? 1 : 0 +} + +locals { + dns_suffix = try(data.aws_partition.current[0].dns_suffix, "") +} ################################################################################ -# GitHub OIDC Provider +# OIDC Provider ################################################################################ data "tls_certificate" "this" { @@ -14,7 +20,7 @@ resource "aws_iam_openid_connect_provider" "this" { count = var.create ? 1 : 0 url = var.url - client_id_list = coalescelist(var.client_id_list, ["sts.${data.aws_partition.current.dns_suffix}"]) + client_id_list = coalescelist(var.client_id_list, ["sts.${local.dns_suffix}"]) thumbprint_list = data.tls_certificate.this[0].certificates[*].sha1_fingerprint tags = var.tags diff --git a/modules/iam-oidc-provider/variables.tf b/modules/iam-oidc-provider/variables.tf index e405bb93..96525dc4 100644 --- a/modules/iam-oidc-provider/variables.tf +++ b/modules/iam-oidc-provider/variables.tf @@ -10,6 +10,10 @@ variable "tags" { default = {} } +################################################################################ +# OIDC Provider +################################################################################ + variable "client_id_list" { description = "List of client IDs (also known as audiences) for the IAM OIDC provider. Defaults to STS service if not values are provided" type = list(string) diff --git a/modules/iam-read-only-policy/README.md b/modules/iam-read-only-policy/README.md index 1a802e3f..baef2bdc 100644 --- a/modules/iam-read-only-policy/README.md +++ b/modules/iam-read-only-policy/README.md @@ -61,9 +61,9 @@ No modules. | [create\_policy](#input\_create\_policy) | Controls if IAM policy should be created. Set to `false` to generate the policy JSON without creating the policy itself | `bool` | `true` | no | | [description](#input\_description) | The description of the policy | `string` | `"IAM Policy"` | no | | [name](#input\_name) | Name to use on IAM policy created | `string` | `null` | no | -| [name\_prefix](#input\_name\_prefix) | Name prefix to use on IAM policy created | `string` | `null` | no | | [path](#input\_path) | Path of IAM policy | `string` | `"/"` | no | | [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | +| [use\_name\_prefix](#input\_use\_name\_prefix) | Determines whether the IAM policy name (`name`) is used as a prefix | `bool` | `true` | no | | [web\_console\_services](#input\_web\_console\_services) | List of web console services to allow | `list(string)` |
[
"resource-groups",
"tag",
"health",
"ce"
]
| no | ## Outputs diff --git a/modules/iam-read-only-policy/main.tf b/modules/iam-read-only-policy/main.tf index 9700293d..482edac4 100644 --- a/modules/iam-read-only-policy/main.tf +++ b/modules/iam-read-only-policy/main.tf @@ -5,12 +5,11 @@ resource "aws_iam_policy" "policy" { count = var.create && var.create_policy ? 1 : 0 - name = var.name - name_prefix = var.name_prefix + name = var.use_name_prefix ? null : var.name + name_prefix = var.use_name_prefix ? "${var.name}-" : null path = var.path description = var.description - - policy = data.aws_iam_policy_document.this[0].json + policy = data.aws_iam_policy_document.this[0].json tags = var.tags } diff --git a/modules/iam-read-only-policy/variables.tf b/modules/iam-read-only-policy/variables.tf index 488fa8ba..1ec3462e 100644 --- a/modules/iam-read-only-policy/variables.tf +++ b/modules/iam-read-only-policy/variables.tf @@ -26,10 +26,10 @@ variable "name" { default = null } -variable "name_prefix" { - description = "Name prefix to use on IAM policy created" - type = string - default = null +variable "use_name_prefix" { + description = "Determines whether the IAM policy name (`name`) is used as a prefix" + type = bool + default = true } variable "path" { diff --git a/modules/iam-role-oidc/README.md b/modules/iam-role-oidc/README.md index 3379a1e3..18562535 100644 --- a/modules/iam-role-oidc/README.md +++ b/modules/iam-role-oidc/README.md @@ -85,15 +85,13 @@ No modules. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [allow\_self\_assume\_role](#input\_allow\_self\_assume\_role) | Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/) | `bool` | `false` | no | -| [assume\_role\_policy\_statements](#input\_assume\_role\_policy\_statements) | List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for for trusted assume role policy | `any` | `[]` | no | +| [assume\_role\_policy\_statements](#input\_assume\_role\_policy\_statements) | List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for for trusted assume role policy |
list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
}))
| `null` | no | | [create](#input\_create) | Controls if resources should be created (affects all resources) | `bool` | `true` | no | | [description](#input\_description) | Description of the role | `string` | `null` | no | | [enable\_bitbucket\_oidc](#input\_enable\_bitbucket\_oidc) | Enable Bitbucket OIDC provider trust for the role | `bool` | `false` | no | | [enable\_github\_oidc](#input\_enable\_github\_oidc) | Enable GitHub OIDC provider trust for the role | `bool` | `false` | no | | [max\_session\_duration](#input\_max\_session\_duration) | Maximum session duration (in seconds) that you want to set for the specified role. If you do not specify a value for this setting, the default maximum of one hour is applied. This setting can have a value from 1 hour to 12 hours | `number` | `null` | no | | [name](#input\_name) | Name to use on IAM role created | `string` | `null` | no | -| [name\_prefix](#input\_name\_prefix) | Name prefix to use on IAM role created | `string` | `null` | no | | [oidc\_account\_id](#input\_oidc\_account\_id) | An overriding AWS account ID where the OIDC provider lives; leave empty to use the current account ID for the AWS provider | `string` | `null` | no | | [oidc\_audiences](#input\_oidc\_audiences) | The audience to be added to the role policy. Set to sts.amazonaws.com for cross-account assumable role. Leave empty otherwise. | `set(string)` | `[]` | no | | [oidc\_provider\_urls](#input\_oidc\_provider\_urls) | List of URLs of the OIDC Providers | `list(string)` | `[]` | no | @@ -103,6 +101,7 @@ No modules. | [permissions\_boundary](#input\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no | | [policies](#input\_policies) | Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no | | [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | +| [use\_name\_prefix](#input\_use\_name\_prefix) | Determines whether the IAM role name (`name`) is used as a prefix | `bool` | `true` | no | ## Outputs diff --git a/modules/iam-role-oidc/main.tf b/modules/iam-role-oidc/main.tf index c57ddc1c..a3779a0a 100644 --- a/modules/iam-role-oidc/main.tf +++ b/modules/iam-role-oidc/main.tf @@ -1,15 +1,17 @@ -data "aws_caller_identity" "current" {} -data "aws_partition" "current" {} +data "aws_caller_identity" "current" { + count = var.create ? 1 : 0 +} +data "aws_partition" "current" { + count = var.create ? 1 : 0 +} locals { - account_id = data.aws_caller_identity.current.account_id - partition = data.aws_partition.current.partition + account_id = try(data.aws_caller_identity.current[0].account_id, "") + partition = try(data.aws_partition.current[0].partition, "") oidc_providers = [for url in var.oidc_provider_urls : replace(url, "https://", "")] github_provider = coalesce(one(local.oidc_providers), "token.actions.githubusercontent.com") bitbucket_provider = one(local.oidc_providers) - - name_condition = var.name != null ? var.name : "${var.name_prefix}*" } ################################################################################ @@ -19,28 +21,6 @@ locals { data "aws_iam_policy_document" "this" { count = var.create ? 1 : 0 - dynamic "statement" { - # https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/ - for_each = var.allow_self_assume_role ? [1] : [] - - content { - sid = "ExplicitSelfRoleAssumption" - effect = "Allow" - actions = ["sts:AssumeRole"] - - principals { - type = "AWS" - identifiers = ["*"] - } - - condition { - test = "ArnLike" - variable = "aws:PrincipalArn" - values = ["arn:${local.partition}:iam::${local.account_id}:role${var.path}${local.name_condition}"] - } - } - } - # Generic OIDC dynamic "statement" { for_each = !var.enable_github_oidc ? local.oidc_providers : [] @@ -156,18 +136,18 @@ data "aws_iam_policy_document" "this" { # Generic statements dynamic "statement" { - for_each = var.assume_role_policy_statements + for_each = var.assume_role_policy_statements != null ? var.assume_role_policy_statements : [] content { - sid = try(statement.value.sid, null) - actions = try(statement.value.actions, ["sts:AssumeRole"]) - not_actions = try(statement.value.not_actions, null) - effect = try(statement.value.effect, null) - resources = try(statement.value.resources, null) - not_resources = try(statement.value.not_resources, null) + sid = statement.value.sid + actions = statement.value.actions + not_actions = statement.value.not_actions + effect = statement.value.effect + resources = statement.value.resources + not_resources = statement.value.not_resources dynamic "principals" { - for_each = try(statement.value.principals, []) + for_each = statement.value.principals != null ? statement.value.principals : [] content { type = principals.value.type @@ -176,7 +156,7 @@ data "aws_iam_policy_document" "this" { } dynamic "not_principals" { - for_each = try(statement.value.not_principals, []) + for_each = statement.value.not_principals != null ? statement.value.not_principals : [] content { type = not_principals.value.type @@ -185,7 +165,7 @@ data "aws_iam_policy_document" "this" { } dynamic "condition" { - for_each = try(statement.value.conditions, []) + for_each = statement.value.condition != null ? statement.value.condition : [] content { test = condition.value.test @@ -200,8 +180,8 @@ data "aws_iam_policy_document" "this" { resource "aws_iam_role" "this" { count = var.create ? 1 : 0 - name = var.name - name_prefix = var.name_prefix + name = var.use_name_prefix ? null : var.name + name_prefix = var.use_name_prefix ? "${var.name}-" : null path = var.path description = var.description diff --git a/modules/iam-role-oidc/variables.tf b/modules/iam-role-oidc/variables.tf index bff5d296..2f6f8410 100644 --- a/modules/iam-role-oidc/variables.tf +++ b/modules/iam-role-oidc/variables.tf @@ -20,10 +20,10 @@ variable "name" { default = null } -variable "name_prefix" { - description = "Name prefix to use on IAM role created" - type = string - default = null +variable "use_name_prefix" { + description = "Determines whether the IAM role name (`name`) is used as a prefix" + type = bool + default = true } variable "path" { @@ -50,16 +50,30 @@ variable "permissions_boundary" { default = null } -variable "allow_self_assume_role" { - description = "Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/)" - type = bool - default = false -} - variable "assume_role_policy_statements" { description = "List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for for trusted assume role policy" - type = any - default = [] + type = list(object({ + sid = optional(string) + actions = optional(list(string)) + not_actions = optional(list(string)) + effect = optional(string) + resources = optional(list(string)) + not_resources = optional(list(string)) + principals = optional(list(object({ + type = string + identifiers = list(string) + }))) + not_principals = optional(list(object({ + type = string + identifiers = list(string) + }))) + condition = optional(list(object({ + test = string + values = list(string) + variable = string + }))) + })) + default = null } variable "policies" { diff --git a/modules/iam-role-saml/README.md b/modules/iam-role-saml/README.md index 30102f88..a6b3f3b1 100644 --- a/modules/iam-role-saml/README.md +++ b/modules/iam-role-saml/README.md @@ -52,21 +52,17 @@ No modules. |------|------| | [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | | [aws_iam_policy_document.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [allow\_self\_assume\_role](#input\_allow\_self\_assume\_role) | Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/) | `bool` | `false` | no | -| [assume\_role\_policy\_statements](#input\_assume\_role\_policy\_statements) | List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for for trusted assume role policy | `any` | `[]` | no | +| [assume\_role\_policy\_statements](#input\_assume\_role\_policy\_statements) | List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for for trusted assume role policy |
list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
}))
| `null` | no | | [create](#input\_create) | Controls if resources should be created (affects all resources) | `bool` | `true` | no | | [description](#input\_description) | Description of the role | `string` | `null` | no | | [max\_session\_duration](#input\_max\_session\_duration) | Maximum session duration (in seconds) that you want to set for the specified role. If you do not specify a value for this setting, the default maximum of one hour is applied. This setting can have a value from 1 hour to 12 hours | `number` | `null` | no | | [name](#input\_name) | Name to use on IAM role created | `string` | `null` | no | -| [name\_prefix](#input\_name\_prefix) | Name prefix to use on IAM role created | `string` | `null` | no | | [path](#input\_path) | Path of IAM role | `string` | `"/"` | no | | [permissions\_boundary](#input\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no | | [policies](#input\_policies) | Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no | @@ -74,6 +70,7 @@ No modules. | [saml\_provider\_ids](#input\_saml\_provider\_ids) | List of SAML provider IDs | `list(string)` | `[]` | no | | [saml\_trust\_actions](#input\_saml\_trust\_actions) | Additional assume role trust actions for the SAML federated statement | `list(string)` | `[]` | no | | [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | +| [use\_name\_prefix](#input\_use\_name\_prefix) | Determines whether the IAM role name (`name`) is used as a prefix | `bool` | `true` | no | ## Outputs diff --git a/modules/iam-role-saml/main.tf b/modules/iam-role-saml/main.tf index e81cc669..7ea91dbe 100644 --- a/modules/iam-role-saml/main.tf +++ b/modules/iam-role-saml/main.tf @@ -1,10 +1,3 @@ -data "aws_caller_identity" "current" {} -data "aws_partition" "current" {} - -locals { - name_condition = var.name != null ? var.name : "${var.name_prefix}*" -} - ################################################################################ # IAM Role ################################################################################ @@ -12,28 +5,6 @@ locals { data "aws_iam_policy_document" "this" { count = var.create ? 1 : 0 - dynamic "statement" { - # https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/ - for_each = var.allow_self_assume_role ? [1] : [] - - content { - sid = "ExplicitSelfRoleAssumption" - effect = "Allow" - actions = ["sts:AssumeRole"] - - principals { - type = "AWS" - identifiers = ["*"] - } - - condition { - test = "ArnLike" - variable = "aws:PrincipalArn" - values = ["arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:role${var.path}${local.name_condition}"] - } - } - } - statement { effect = "Allow" actions = compact(distinct(concat(["sts:AssumeRoleWithSAML"], var.saml_trust_actions))) @@ -51,18 +22,18 @@ data "aws_iam_policy_document" "this" { } dynamic "statement" { - for_each = var.assume_role_policy_statements + for_each = var.assume_role_policy_statements != null ? var.assume_role_policy_statements : [] content { - sid = try(statement.value.sid, null) - actions = try(statement.value.actions, ["sts:AssumeRole"]) - not_actions = try(statement.value.not_actions, null) - effect = try(statement.value.effect, null) - resources = try(statement.value.resources, null) - not_resources = try(statement.value.not_resources, null) + sid = statement.value.sid + actions = statement.value.actions + not_actions = statement.value.not_actions + effect = statement.value.effect + resources = statement.value.resources + not_resources = statement.value.not_resources dynamic "principals" { - for_each = try(statement.value.principals, []) + for_each = statement.value.principals != null ? statement.value.principals : [] content { type = principals.value.type @@ -71,7 +42,7 @@ data "aws_iam_policy_document" "this" { } dynamic "not_principals" { - for_each = try(statement.value.not_principals, []) + for_each = statement.value.not_principals != null ? statement.value.not_principals : [] content { type = not_principals.value.type @@ -80,7 +51,7 @@ data "aws_iam_policy_document" "this" { } dynamic "condition" { - for_each = try(statement.value.conditions, []) + for_each = statement.value.condition != null ? statement.value.condition : [] content { test = condition.value.test @@ -95,8 +66,8 @@ data "aws_iam_policy_document" "this" { resource "aws_iam_role" "this" { count = var.create ? 1 : 0 - name = var.name - name_prefix = var.name_prefix + name = var.use_name_prefix ? null : var.name + name_prefix = var.use_name_prefix ? "${var.name}-" : null path = var.path description = var.description diff --git a/modules/iam-role-saml/variables.tf b/modules/iam-role-saml/variables.tf index ef88f546..065b44f2 100644 --- a/modules/iam-role-saml/variables.tf +++ b/modules/iam-role-saml/variables.tf @@ -20,10 +20,10 @@ variable "name" { default = null } -variable "name_prefix" { - description = "Name prefix to use on IAM role created" - type = string - default = null +variable "use_name_prefix" { + description = "Determines whether the IAM role name (`name`) is used as a prefix" + type = bool + default = true } variable "path" { @@ -50,16 +50,30 @@ variable "permissions_boundary" { default = null } -variable "allow_self_assume_role" { - description = "Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/)" - type = bool - default = false -} - variable "assume_role_policy_statements" { description = "List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for for trusted assume role policy" - type = any - default = [] + type = list(object({ + sid = optional(string) + actions = optional(list(string)) + not_actions = optional(list(string)) + effect = optional(string) + resources = optional(list(string)) + not_resources = optional(list(string)) + principals = optional(list(object({ + type = string + identifiers = list(string) + }))) + not_principals = optional(list(object({ + type = string + identifiers = list(string) + }))) + condition = optional(list(object({ + test = string + values = list(string) + variable = string + }))) + })) + default = null } variable "policies" { diff --git a/modules/iam-role/README.md b/modules/iam-role/README.md index 28b80650..16f15d05 100644 --- a/modules/iam-role/README.md +++ b/modules/iam-role/README.md @@ -65,26 +65,23 @@ No modules. | [aws_iam_instance_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_instance_profile) | resource | | [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | | [aws_iam_policy_document.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [allow\_self\_assume\_role](#input\_allow\_self\_assume\_role) | Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/) | `bool` | `false` | no | -| [assume\_role\_policy\_statements](#input\_assume\_role\_policy\_statements) | List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for for trusted assume role policy | `any` | `[]` | no | +| [assume\_role\_policy\_statements](#input\_assume\_role\_policy\_statements) | List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for for trusted assume role policy |
list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
}))
| `null` | no | | [create](#input\_create) | Controls if resources should be created (affects all resources) | `bool` | `true` | no | | [create\_instance\_profile](#input\_create\_instance\_profile) | Determines whether to create an instance profile | `bool` | `false` | no | | [description](#input\_description) | Description of the role | `string` | `null` | no | | [max\_session\_duration](#input\_max\_session\_duration) | Maximum session duration (in seconds) that you want to set for the specified role. If you do not specify a value for this setting, the default maximum of one hour is applied. This setting can have a value from 1 hour to 12 hours | `number` | `null` | no | | [name](#input\_name) | Name to use on IAM role created | `string` | `null` | no | -| [name\_prefix](#input\_name\_prefix) | Name prefix to use on IAM role created | `string` | `null` | no | | [path](#input\_path) | Path of IAM role | `string` | `"/"` | no | | [permissions\_boundary](#input\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no | | [policies](#input\_policies) | Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no | | [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | +| [use\_name\_prefix](#input\_use\_name\_prefix) | Determines whether the IAM role name (`name`) is used as a prefix | `bool` | `true` | no | ## Outputs diff --git a/modules/iam-role/main.tf b/modules/iam-role/main.tf index 9221aaaf..bf2eac70 100644 --- a/modules/iam-role/main.tf +++ b/modules/iam-role/main.tf @@ -1,52 +1,23 @@ -data "aws_caller_identity" "current" {} -data "aws_partition" "current" {} - -locals { - name_condition = var.name != null ? var.name : "${var.name_prefix}*" -} - ################################################################################ # IAM Role ################################################################################ data "aws_iam_policy_document" "this" { - count = var.create ? 1 : 0 - - dynamic "statement" { - # https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/ - for_each = var.allow_self_assume_role ? [1] : [] - - content { - sid = "ExplicitSelfRoleAssumption" - effect = "Allow" - actions = ["sts:AssumeRole"] - - principals { - type = "AWS" - identifiers = ["*"] - } - - condition { - test = "ArnLike" - variable = "aws:PrincipalArn" - values = ["arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:role${var.path}${local.name_condition}"] - } - } - } + count = var.create && var.assume_role_policy_statements != null ? 1 : 0 dynamic "statement" { - for_each = var.assume_role_policy_statements + for_each = var.assume_role_policy_statements != null ? var.assume_role_policy_statements : [] content { - sid = try(statement.value.sid, null) - actions = try(statement.value.actions, ["sts:AssumeRole"]) - not_actions = try(statement.value.not_actions, null) - effect = try(statement.value.effect, null) - resources = try(statement.value.resources, null) - not_resources = try(statement.value.not_resources, null) + sid = statement.value.sid + actions = statement.value.actions + not_actions = statement.value.not_actions + effect = statement.value.effect + resources = statement.value.resources + not_resources = statement.value.not_resources dynamic "principals" { - for_each = try(statement.value.principals, []) + for_each = statement.value.principals != null ? statement.value.principals : [] content { type = principals.value.type @@ -55,7 +26,7 @@ data "aws_iam_policy_document" "this" { } dynamic "not_principals" { - for_each = try(statement.value.not_principals, []) + for_each = statement.value.not_principals != null ? statement.value.not_principals : [] content { type = not_principals.value.type @@ -64,7 +35,7 @@ data "aws_iam_policy_document" "this" { } dynamic "condition" { - for_each = try(statement.value.conditions, []) + for_each = statement.value.condition != null ? statement.value.condition : [] content { test = condition.value.test @@ -79,12 +50,12 @@ data "aws_iam_policy_document" "this" { resource "aws_iam_role" "this" { count = var.create ? 1 : 0 - name = var.name - name_prefix = var.name_prefix + name = var.use_name_prefix ? null : var.name + name_prefix = var.use_name_prefix ? "${var.name}-" : null path = var.path description = var.description - assume_role_policy = data.aws_iam_policy_document.this[0].json + assume_role_policy = var.assume_role_policy_statements != null ? data.aws_iam_policy_document.this[0].json : null max_session_duration = var.max_session_duration permissions_boundary = var.permissions_boundary force_detach_policies = true @@ -108,8 +79,8 @@ resource "aws_iam_instance_profile" "this" { role = aws_iam_role.this[0].name - name = var.name - name_prefix = var.name_prefix + name = var.use_name_prefix ? null : var.name + name_prefix = var.use_name_prefix ? "${var.name}-" : null path = var.path tags = var.tags diff --git a/modules/iam-role/variables.tf b/modules/iam-role/variables.tf index 7f9db51e..fcbf07d7 100644 --- a/modules/iam-role/variables.tf +++ b/modules/iam-role/variables.tf @@ -20,10 +20,10 @@ variable "name" { default = null } -variable "name_prefix" { - description = "Name prefix to use on IAM role created" - type = string - default = null +variable "use_name_prefix" { + description = "Determines whether the IAM role name (`name`) is used as a prefix" + type = bool + default = true } variable "path" { @@ -50,16 +50,30 @@ variable "permissions_boundary" { default = null } -variable "allow_self_assume_role" { - description = "Determines whether to allow the role to be [assume itself](https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/)" - type = bool - default = false -} - variable "assume_role_policy_statements" { description = "List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for for trusted assume role policy" - type = any - default = [] + type = list(object({ + sid = optional(string) + actions = optional(list(string)) + not_actions = optional(list(string)) + effect = optional(string) + resources = optional(list(string)) + not_resources = optional(list(string)) + principals = optional(list(object({ + type = string + identifiers = list(string) + }))) + not_principals = optional(list(object({ + type = string + identifiers = list(string) + }))) + condition = optional(list(object({ + test = string + values = list(string) + variable = string + }))) + })) + default = null } variable "policies" { diff --git a/wrappers/iam-account/main.tf b/wrappers/iam-account/main.tf index a985c77f..641bcd9f 100644 --- a/wrappers/iam-account/main.tf +++ b/wrappers/iam-account/main.tf @@ -5,8 +5,8 @@ module "wrapper" { account_alias = try(each.value.account_alias, var.defaults.account_alias) allow_users_to_change_password = try(each.value.allow_users_to_change_password, var.defaults.allow_users_to_change_password, true) + create = try(each.value.create, var.defaults.create, true) create_account_password_policy = try(each.value.create_account_password_policy, var.defaults.create_account_password_policy, true) - get_caller_identity = try(each.value.get_caller_identity, var.defaults.get_caller_identity, true) hard_expiry = try(each.value.hard_expiry, var.defaults.hard_expiry, false) max_password_age = try(each.value.max_password_age, var.defaults.max_password_age, 0) minimum_password_length = try(each.value.minimum_password_length, var.defaults.minimum_password_length, 8) diff --git a/wrappers/iam-group/main.tf b/wrappers/iam-group/main.tf index 97d9c1e2..7be62384 100644 --- a/wrappers/iam-group/main.tf +++ b/wrappers/iam-group/main.tf @@ -9,11 +9,12 @@ module "wrapper" { enable_self_management_permissions = try(each.value.enable_self_management_permissions, var.defaults.enable_self_management_permissions, true) name = try(each.value.name, var.defaults.name, "") path = try(each.value.path, var.defaults.path, null) - permission_statements = try(each.value.permission_statements, var.defaults.permission_statements, []) + permission_statements = try(each.value.permission_statements, var.defaults.permission_statements, null) policies = try(each.value.policies, var.defaults.policies, {}) policy_description = try(each.value.policy_description, var.defaults.policy_description, null) - policy_name_prefix = try(each.value.policy_name_prefix, var.defaults.policy_name_prefix, null) + policy_name = try(each.value.policy_name, var.defaults.policy_name, null) policy_path = try(each.value.policy_path, var.defaults.policy_path, null) + policy_use_name_prefix = try(each.value.policy_use_name_prefix, var.defaults.policy_use_name_prefix, true) tags = try(each.value.tags, var.defaults.tags, {}) users = try(each.value.users, var.defaults.users, []) users_account_id = try(each.value.users_account_id, var.defaults.users_account_id, null) diff --git a/wrappers/iam-read-only-policy/main.tf b/wrappers/iam-read-only-policy/main.tf index 455f4cdc..3bcb7fa8 100644 --- a/wrappers/iam-read-only-policy/main.tf +++ b/wrappers/iam-read-only-policy/main.tf @@ -12,8 +12,8 @@ module "wrapper" { create_policy = try(each.value.create_policy, var.defaults.create_policy, true) description = try(each.value.description, var.defaults.description, "IAM Policy") name = try(each.value.name, var.defaults.name, null) - name_prefix = try(each.value.name_prefix, var.defaults.name_prefix, null) path = try(each.value.path, var.defaults.path, "/") tags = try(each.value.tags, var.defaults.tags, {}) + use_name_prefix = try(each.value.use_name_prefix, var.defaults.use_name_prefix, true) web_console_services = try(each.value.web_console_services, var.defaults.web_console_services, ["resource-groups", "tag", "health", "ce"]) } diff --git a/wrappers/iam-role-oidc/main.tf b/wrappers/iam-role-oidc/main.tf index c9f9ae7d..174996fb 100644 --- a/wrappers/iam-role-oidc/main.tf +++ b/wrappers/iam-role-oidc/main.tf @@ -3,15 +3,13 @@ module "wrapper" { for_each = var.items - allow_self_assume_role = try(each.value.allow_self_assume_role, var.defaults.allow_self_assume_role, false) - assume_role_policy_statements = try(each.value.assume_role_policy_statements, var.defaults.assume_role_policy_statements, []) + assume_role_policy_statements = try(each.value.assume_role_policy_statements, var.defaults.assume_role_policy_statements, null) create = try(each.value.create, var.defaults.create, true) description = try(each.value.description, var.defaults.description, null) enable_bitbucket_oidc = try(each.value.enable_bitbucket_oidc, var.defaults.enable_bitbucket_oidc, false) enable_github_oidc = try(each.value.enable_github_oidc, var.defaults.enable_github_oidc, false) max_session_duration = try(each.value.max_session_duration, var.defaults.max_session_duration, null) name = try(each.value.name, var.defaults.name, null) - name_prefix = try(each.value.name_prefix, var.defaults.name_prefix, null) oidc_account_id = try(each.value.oidc_account_id, var.defaults.oidc_account_id, null) oidc_audiences = try(each.value.oidc_audiences, var.defaults.oidc_audiences, []) oidc_provider_urls = try(each.value.oidc_provider_urls, var.defaults.oidc_provider_urls, []) @@ -21,4 +19,5 @@ module "wrapper" { permissions_boundary = try(each.value.permissions_boundary, var.defaults.permissions_boundary, null) policies = try(each.value.policies, var.defaults.policies, {}) tags = try(each.value.tags, var.defaults.tags, {}) + use_name_prefix = try(each.value.use_name_prefix, var.defaults.use_name_prefix, true) } diff --git a/wrappers/iam-role-saml/main.tf b/wrappers/iam-role-saml/main.tf index b27381f5..28ef496f 100644 --- a/wrappers/iam-role-saml/main.tf +++ b/wrappers/iam-role-saml/main.tf @@ -3,13 +3,11 @@ module "wrapper" { for_each = var.items - allow_self_assume_role = try(each.value.allow_self_assume_role, var.defaults.allow_self_assume_role, false) - assume_role_policy_statements = try(each.value.assume_role_policy_statements, var.defaults.assume_role_policy_statements, []) + assume_role_policy_statements = try(each.value.assume_role_policy_statements, var.defaults.assume_role_policy_statements, null) create = try(each.value.create, var.defaults.create, true) description = try(each.value.description, var.defaults.description, null) max_session_duration = try(each.value.max_session_duration, var.defaults.max_session_duration, null) name = try(each.value.name, var.defaults.name, null) - name_prefix = try(each.value.name_prefix, var.defaults.name_prefix, null) path = try(each.value.path, var.defaults.path, "/") permissions_boundary = try(each.value.permissions_boundary, var.defaults.permissions_boundary, null) policies = try(each.value.policies, var.defaults.policies, {}) @@ -17,4 +15,5 @@ module "wrapper" { saml_provider_ids = try(each.value.saml_provider_ids, var.defaults.saml_provider_ids, []) saml_trust_actions = try(each.value.saml_trust_actions, var.defaults.saml_trust_actions, []) tags = try(each.value.tags, var.defaults.tags, {}) + use_name_prefix = try(each.value.use_name_prefix, var.defaults.use_name_prefix, true) } diff --git a/wrappers/iam-role/main.tf b/wrappers/iam-role/main.tf index e6bab5ed..41fa32f6 100644 --- a/wrappers/iam-role/main.tf +++ b/wrappers/iam-role/main.tf @@ -3,16 +3,15 @@ module "wrapper" { for_each = var.items - allow_self_assume_role = try(each.value.allow_self_assume_role, var.defaults.allow_self_assume_role, false) - assume_role_policy_statements = try(each.value.assume_role_policy_statements, var.defaults.assume_role_policy_statements, []) + assume_role_policy_statements = try(each.value.assume_role_policy_statements, var.defaults.assume_role_policy_statements, null) create = try(each.value.create, var.defaults.create, true) create_instance_profile = try(each.value.create_instance_profile, var.defaults.create_instance_profile, false) description = try(each.value.description, var.defaults.description, null) max_session_duration = try(each.value.max_session_duration, var.defaults.max_session_duration, null) name = try(each.value.name, var.defaults.name, null) - name_prefix = try(each.value.name_prefix, var.defaults.name_prefix, null) path = try(each.value.path, var.defaults.path, "/") permissions_boundary = try(each.value.permissions_boundary, var.defaults.permissions_boundary, null) policies = try(each.value.policies, var.defaults.policies, {}) tags = try(each.value.tags, var.defaults.tags, {}) + use_name_prefix = try(each.value.use_name_prefix, var.defaults.use_name_prefix, true) } From e528d88ff6f794a9b414586368473cc8580c3888 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Fri, 8 Aug 2025 11:30:17 -0500 Subject: [PATCH 05/21] fix: Correct IAM policy statement variables --- .pre-commit-config.yaml | 2 +- examples/iam-account/README.md | 1 - examples/iam-account/main.tf | 5 ++- examples/iam-account/outputs.tf | 5 --- examples/iam-group/main.tf | 7 ++-- .../iam-role-for-service-accounts/README.md | 4 +- .../iam-role-for-service-accounts/main.tf | 37 +++++++++++++------ .../iam-role-for-service-accounts/outputs.tf | 1 + examples/iam-role-saml/main.tf | 6 ++- examples/iam-role/README.md | 16 ++++---- examples/iam-role/main.tf | 37 +++++++++---------- examples/iam-role/outputs.tf | 30 +++++++-------- modules/iam-account/README.md | 3 -- modules/iam-account/outputs.tf | 15 -------- modules/iam-group/README.md | 9 ++--- modules/iam-group/main.tf | 5 ++- modules/iam-group/variables.tf | 8 ++-- modules/iam-role-oidc/README.md | 2 +- modules/iam-role-oidc/main.tf | 4 +- modules/iam-role-oidc/variables.tf | 8 ++-- modules/iam-role-saml/README.md | 2 +- modules/iam-role-saml/main.tf | 4 +- modules/iam-role-saml/variables.tf | 8 ++-- modules/iam-role/README.md | 11 +++--- modules/iam-role/main.tf | 4 +- modules/iam-role/variables.tf | 8 ++-- 26 files changed, 116 insertions(+), 126 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b7848169..7f8a62bd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/antonbabenko/pre-commit-terraform - rev: v1.99.5 + rev: v1.100.0 hooks: - id: terraform_fmt - id: terraform_wrapper_module_for_each diff --git a/examples/iam-account/README.md b/examples/iam-account/README.md index ca9445d3..fd2bb428 100644 --- a/examples/iam-account/README.md +++ b/examples/iam-account/README.md @@ -44,6 +44,5 @@ No inputs. | Name | Description | |------|-------------| -| [caller\_identity\_account\_id](#output\_caller\_identity\_account\_id) | The ID of the AWS account | | [iam\_account\_password\_policy\_expire\_passwords](#output\_iam\_account\_password\_policy\_expire\_passwords) | Indicates whether passwords in the account expire. Returns true if max\_password\_age contains a value greater than 0. Returns false if it is 0 or not present. | diff --git a/examples/iam-account/main.tf b/examples/iam-account/main.tf index 9ac4f553..69774fff 100644 --- a/examples/iam-account/main.tf +++ b/examples/iam-account/main.tf @@ -2,9 +2,10 @@ provider "aws" { region = "eu-west-1" } -############## +################################################################################ # IAM account -############## +################################################################################ + module "iam_account" { source = "../../modules/iam-account" diff --git a/examples/iam-account/outputs.tf b/examples/iam-account/outputs.tf index 9841072c..e8e88ee8 100644 --- a/examples/iam-account/outputs.tf +++ b/examples/iam-account/outputs.tf @@ -1,8 +1,3 @@ -output "caller_identity_account_id" { - description = "The ID of the AWS account" - value = module.iam_account.caller_identity_account_id -} - output "iam_account_password_policy_expire_passwords" { description = "Indicates whether passwords in the account expire. Returns true if max_password_age contains a value greater than 0. Returns false if it is 0 or not present." value = module.iam_account.iam_account_password_policy_expire_passwords diff --git a/examples/iam-group/main.tf b/examples/iam-group/main.tf index fb4cee17..2f045b66 100644 --- a/examples/iam-group/main.tf +++ b/examples/iam-group/main.tf @@ -26,13 +26,12 @@ module "iam_group" { module.iam_user2.name, ] - permission_statements = [ - { - sid = "AssumeRole" + permission_statements = { + AssumeRole = { actions = ["sts:AssumeRole"] resources = ["arn:aws:iam::111111111111:role/admin"] } - ] + } policies = { ReadOnlyAccess = "arn:aws:iam::aws:policy/ReadOnlyAccess" diff --git a/examples/iam-role-for-service-accounts/README.md b/examples/iam-role-for-service-accounts/README.md index 71e4e4f0..41364a64 100644 --- a/examples/iam-role-for-service-accounts/README.md +++ b/examples/iam-role-for-service-accounts/README.md @@ -41,7 +41,7 @@ Run `terraform destroy` when you don't need these resources. | [ebs\_csi\_irsa](#module\_ebs\_csi\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | | [ebs\_csi\_irsa\_v2](#module\_ebs\_csi\_irsa\_v2) | ../../modules/iam-role-for-service-accounts | n/a | | [efs\_csi\_irsa](#module\_efs\_csi\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | -| [eks](#module\_eks) | terraform-aws-modules/eks/aws | ~> 19.10 | +| [eks](#module\_eks) | terraform-aws-modules/eks/aws | ~> 21.0 | | [external\_dns\_irsa](#module\_external\_dns\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | | [external\_secrets\_irsa](#module\_external\_secrets\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | | [fsx\_lustre\_csi\_irsa](#module\_fsx\_lustre\_csi\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | @@ -53,7 +53,7 @@ Run `terraform destroy` when you don't need these resources. | [load\_balancer\_controller\_targetgroup\_binding\_only\_irsa](#module\_load\_balancer\_controller\_targetgroup\_binding\_only\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | | [node\_termination\_handler\_irsa](#module\_node\_termination\_handler\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | | [velero\_irsa](#module\_velero\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | -| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 3.0 | +| [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 6.0 | | [vpc\_cni\_ipv4\_irsa](#module\_vpc\_cni\_ipv4\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | | [vpc\_cni\_ipv6\_irsa](#module\_vpc\_cni\_ipv6\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | diff --git a/examples/iam-role-for-service-accounts/main.tf b/examples/iam-role-for-service-accounts/main.tf index 4e914dd6..1c65d6fa 100644 --- a/examples/iam-role-for-service-accounts/main.tf +++ b/examples/iam-role-for-service-accounts/main.tf @@ -48,14 +48,13 @@ module "irsa_v2_custom_policy" { name = "${local.name}-custom-name" enable_irsa_v2 = true - policy_statements = [ - { - sid = "DescribeEc2" + policy_statements = { + DescribeEc2 = { actions = ["ec2:Describe*"] effect = "Allow" resources = ["*"] } - ] + } tags = local.tags } @@ -399,7 +398,7 @@ module "vpc_cni_ipv6_irsa" { module "vpc" { source = "terraform-aws-modules/vpc/aws" - version = "~> 3.0" + version = "~> 6.0" name = local.name cidr = local.vpc_cidr @@ -408,9 +407,8 @@ module "vpc" { private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)] public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 48)] - enable_nat_gateway = true - single_nat_gateway = true - enable_dns_hostnames = true + enable_nat_gateway = true + single_nat_gateway = true public_subnet_tags = { "kubernetes.io/role/elb" = 1 @@ -425,16 +423,31 @@ module "vpc" { module "eks" { source = "terraform-aws-modules/eks/aws" - version = "~> 19.10" + version = "~> 21.0" - cluster_name = local.name - cluster_version = "1.25" + name = local.name + kubernetes_version = "1.33" vpc_id = module.vpc.vpc_id subnet_ids = module.vpc.private_subnets + addons = { + coredns = {} + kube-proxy = {} + vpc-cni = { + before_compute = true + } + } + eks_managed_node_groups = { - default = {} + example = { + ami_type = "AL2023_x86_64_STANDARD" + instance_types = ["m5.xlarge"] + + min_size = 1 + max_size = 2 + desired_size = 1 + } } tags = local.tags diff --git a/examples/iam-role-for-service-accounts/outputs.tf b/examples/iam-role-for-service-accounts/outputs.tf index 0fbd18b1..3a82ead9 100644 --- a/examples/iam-role-for-service-accounts/outputs.tf +++ b/examples/iam-role-for-service-accounts/outputs.tf @@ -21,6 +21,7 @@ output "irsa_unique_id" { description = "Unique ID of IAM role" value = module.irsa.unique_id } + ################################################################################ # IAM Policy ################################################################################ diff --git a/examples/iam-role-saml/main.tf b/examples/iam-role-saml/main.tf index bfdc756d..fe2c3449 100644 --- a/examples/iam-role-saml/main.tf +++ b/examples/iam-role-saml/main.tf @@ -45,11 +45,13 @@ module "iam_roles" { } } poweruser = { - PowerUserAccess = "arn:aws:iam::aws:policy/PowerUserAccess" + policies = { + PowerUserAccess = "arn:aws:iam::aws:policy/PowerUserAccess" + } } } - name_prefix = "${each.key}-" + name = each.key saml_provider_ids = [aws_iam_saml_provider.this.id] policies = each.value.policies diff --git a/examples/iam-role/README.md b/examples/iam-role/README.md index f9dd429f..b3e79b55 100644 --- a/examples/iam-role/README.md +++ b/examples/iam-role/README.md @@ -32,7 +32,7 @@ Run `terraform destroy` when you don't need these resources. | Name | Source | Version | |------|--------|---------| -| [iam\_role\_conditions](#module\_iam\_role\_conditions) | ../../modules/iam-role | n/a | +| [iam\_role\_condition](#module\_iam\_role\_condition) | ../../modules/iam-role | n/a | | [iam\_role\_disabled](#module\_iam\_role\_disabled) | ../../modules/iam-role | n/a | | [iam\_role\_instance\_profile](#module\_iam\_role\_instance\_profile) | ../../modules/iam-role | n/a | | [iam\_roles](#module\_iam\_roles) | ../../modules/iam-role | n/a | @@ -52,13 +52,13 @@ No inputs. | Name | Description | |------|-------------| -| [conditions\_iam\_instance\_profile\_arn](#output\_conditions\_iam\_instance\_profile\_arn) | ARN assigned by AWS to the instance profile | -| [conditions\_iam\_instance\_profile\_id](#output\_conditions\_iam\_instance\_profile\_id) | Instance profile's ID | -| [conditions\_iam\_instance\_profile\_name](#output\_conditions\_iam\_instance\_profile\_name) | Name of IAM instance profile | -| [conditions\_iam\_instance\_profile\_unique\_id](#output\_conditions\_iam\_instance\_profile\_unique\_id) | Stable and unique string identifying the IAM instance profile | -| [conditions\_iam\_role\_arn](#output\_conditions\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | -| [conditions\_iam\_role\_name](#output\_conditions\_iam\_role\_name) | The name of the IAM role | -| [conditions\_iam\_role\_unique\_id](#output\_conditions\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | +| [condition\_iam\_instance\_profile\_arn](#output\_condition\_iam\_instance\_profile\_arn) | ARN assigned by AWS to the instance profile | +| [condition\_iam\_instance\_profile\_id](#output\_condition\_iam\_instance\_profile\_id) | Instance profile's ID | +| [condition\_iam\_instance\_profile\_name](#output\_condition\_iam\_instance\_profile\_name) | Name of IAM instance profile | +| [condition\_iam\_instance\_profile\_unique\_id](#output\_condition\_iam\_instance\_profile\_unique\_id) | Stable and unique string identifying the IAM instance profile | +| [condition\_iam\_role\_arn](#output\_condition\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | +| [condition\_iam\_role\_name](#output\_condition\_iam\_role\_name) | The name of the IAM role | +| [condition\_iam\_role\_unique\_id](#output\_condition\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | | [instance\_profile\_iam\_instance\_profile\_arn](#output\_instance\_profile\_iam\_instance\_profile\_arn) | ARN assigned by AWS to the instance profile | | [instance\_profile\_iam\_instance\_profile\_id](#output\_instance\_profile\_iam\_instance\_profile\_id) | Instance profile's ID | | [instance\_profile\_iam\_instance\_profile\_name](#output\_instance\_profile\_iam\_instance\_profile\_name) | Name of IAM instance profile | diff --git a/examples/iam-role/main.tf b/examples/iam-role/main.tf index 3797e384..8764b01d 100644 --- a/examples/iam-role/main.tf +++ b/examples/iam-role/main.tf @@ -25,9 +25,8 @@ module "iam_role_instance_profile" { create_instance_profile = true - assume_role_policy_statements = [ - { - sid = "TrustRoleAndServiceToAssume" + assume_role_policy_statements = { + TrustRoleAndServiceToAssume = { principals = [ { type = "AWS" @@ -44,7 +43,7 @@ module "iam_role_instance_profile" { } ] } - ] + } policies = { ReadOnlyAccess = "arn:aws:iam::aws:policy/ReadOnlyAccess" @@ -53,27 +52,26 @@ module "iam_role_instance_profile" { tags = local.tags } -module "iam_role_conditions" { +module "iam_role_condition" { source = "../../modules/iam-role" - name_prefix = "conditions-" + name = "condition" - assume_role_policy_statements = [ - { - sid = "TrustRoleAndServiceToAssume" + assume_role_policy_statements = { + TrustRoleAndServiceToAssume = { principals = [{ type = "AWS" identifiers = [ "arn:aws:iam::835367859851:user/anton", ] }] - conditions = [{ + condition = [{ test = "StringEquals" variable = "sts:ExternalId" values = ["some-secret-id"] }] } - ] + } policies = { AmazonCognitoReadOnly = "arn:aws:iam::aws:policy/AmazonCognitoReadOnly" @@ -101,22 +99,23 @@ module "iam_roles" { } } poweruser = { - trusted_arns = [data.aws_caller_identity.current.arn] - PowerUserAccess = "arn:aws:iam::aws:policy/PowerUserAccess" + trusted_arns = [data.aws_caller_identity.current.arn] + policies = { + PowerUserAccess = "arn:aws:iam::aws:policy/PowerUserAccess" + } } } - name_prefix = "${each.key}-" + name = each.key - assume_role_policy_statements = [ - { - sid = "TrustRoleAndServiceToAssume" + assume_role_policy_statements = { + TrustRoleAndServiceToAssume = { principals = [{ type = "AWS" identifiers = each.value.trusted_arns }] - conditions = [ + condition = [ { test = "Bool" variable = "aws:MultiFactorAuthPresent" @@ -129,7 +128,7 @@ module "iam_roles" { } ] } - ] + } policies = each.value.policies diff --git a/examples/iam-role/outputs.tf b/examples/iam-role/outputs.tf index 2e12e017..aaee498a 100644 --- a/examples/iam-role/outputs.tf +++ b/examples/iam-role/outputs.tf @@ -38,40 +38,40 @@ output "instance_profile_iam_instance_profile_unique_id" { } ################################################################################ -# IAM Role - Conditions +# IAM Role - Condition ################################################################################ -output "conditions_iam_role_name" { +output "condition_iam_role_name" { description = "The name of the IAM role" - value = module.iam_role_conditions.name + value = module.iam_role_condition.name } -output "conditions_iam_role_arn" { +output "condition_iam_role_arn" { description = "The Amazon Resource Name (ARN) specifying the IAM role" - value = module.iam_role_conditions.arn + value = module.iam_role_condition.arn } -output "conditions_iam_role_unique_id" { +output "condition_iam_role_unique_id" { description = "Stable and unique string identifying the IAM role" - value = module.iam_role_conditions.unique_id + value = module.iam_role_condition.unique_id } -output "conditions_iam_instance_profile_arn" { +output "condition_iam_instance_profile_arn" { description = "ARN assigned by AWS to the instance profile" - value = module.iam_role_conditions.instance_profile_arn + value = module.iam_role_condition.instance_profile_arn } -output "conditions_iam_instance_profile_id" { +output "condition_iam_instance_profile_id" { description = "Instance profile's ID" - value = module.iam_role_conditions.instance_profile_id + value = module.iam_role_condition.instance_profile_id } -output "conditions_iam_instance_profile_name" { +output "condition_iam_instance_profile_name" { description = "Name of IAM instance profile" - value = module.iam_role_conditions.instance_profile_name + value = module.iam_role_condition.instance_profile_name } -output "conditions_iam_instance_profile_unique_id" { +output "condition_iam_instance_profile_unique_id" { description = "Stable and unique string identifying the IAM instance profile" - value = module.iam_role_conditions.instance_profile_unique_id + value = module.iam_role_condition.instance_profile_unique_id } diff --git a/modules/iam-account/README.md b/modules/iam-account/README.md index fad8cb27..f2cd2ff8 100644 --- a/modules/iam-account/README.md +++ b/modules/iam-account/README.md @@ -88,9 +88,6 @@ No modules. | Name | Description | |------|-------------| -| [caller\_identity\_account\_id](#output\_caller\_identity\_account\_id) | The AWS Account ID number of the account that owns or contains the calling entity | -| [caller\_identity\_arn](#output\_caller\_identity\_arn) | The AWS ARN associated with the calling entity | -| [caller\_identity\_user\_id](#output\_caller\_identity\_user\_id) | The unique identifier of the calling entity | | [iam\_account\_password\_policy\_expire\_passwords](#output\_iam\_account\_password\_policy\_expire\_passwords) | Indicates whether passwords in the account expire. Returns true if max\_password\_age contains a value greater than 0. Returns false if it is 0 or not present | diff --git a/modules/iam-account/outputs.tf b/modules/iam-account/outputs.tf index 4254da21..0f5f32a3 100644 --- a/modules/iam-account/outputs.tf +++ b/modules/iam-account/outputs.tf @@ -1,18 +1,3 @@ -output "caller_identity_account_id" { - description = "The AWS Account ID number of the account that owns or contains the calling entity" - value = try(data.aws_caller_identity.this[0].account_id, null) -} - -output "caller_identity_arn" { - description = "The AWS ARN associated with the calling entity" - value = try(data.aws_caller_identity.this[0].arn, null) -} - -output "caller_identity_user_id" { - description = "The unique identifier of the calling entity" - value = try(data.aws_caller_identity.this[0].user_id, null) -} - output "iam_account_password_policy_expire_passwords" { description = "Indicates whether passwords in the account expire. Returns true if max_password_age contains a value greater than 0. Returns false if it is 0 or not present" value = try(aws_iam_account_password_policy.this[0].expire_passwords, null) diff --git a/modules/iam-group/README.md b/modules/iam-group/README.md index 87f3a4bb..4e00ec8a 100644 --- a/modules/iam-group/README.md +++ b/modules/iam-group/README.md @@ -17,13 +17,12 @@ module "iam_group" { ] enable_self_management_permissions = true - permission_statements = [ - { - sid = "AssumeRole" + permission_statements = { + AssumeRole = { actions = ["sts:AssumeRole"] resources = ["arn:aws:iam::111111111111:role/admin"] } - ] + } policies = { AdministratorAccess = "arn:aws:iam::aws:policy/AdministratorAccess", @@ -77,7 +76,7 @@ No modules. | [enable\_self\_management\_permissions](#input\_enable\_self\_management\_permissions) | Determines whether permissions are added to the policy which allow the groups IAM users to manage their credentials and MFA | `bool` | `true` | no | | [name](#input\_name) | The group's name. The name must consist of upper and lowercase alphanumeric characters with no spaces. You can also include any of the following characters: `=,.@-_.` | `string` | `""` | no | | [path](#input\_path) | Path in which to create the group | `string` | `null` | no | -| [permission\_statements](#input\_permission\_statements) | List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for the policy |
list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
}))
| `null` | no | +| [permission\_statements](#input\_permission\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage |
map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
variable = string
values = list(string)
})))
}))
| `null` | no | | [policies](#input\_policies) | Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no | | [policy\_description](#input\_policy\_description) | Description of the IAM policy | `string` | `null` | no | | [policy\_name](#input\_policy\_name) | Name to use on IAM policy created | `string` | `null` | no | diff --git a/modules/iam-group/main.tf b/modules/iam-group/main.tf index 14eec8a5..60241203 100644 --- a/modules/iam-group/main.tf +++ b/modules/iam-group/main.tf @@ -1,6 +1,7 @@ data "aws_partition" "current" { count = var.create ? 1 : 0 } + data "aws_caller_identity" "current" { count = var.create ? 1 : 0 } @@ -187,10 +188,10 @@ data "aws_iam_policy_document" "this" { } dynamic "statement" { - for_each = var.permission_statements != null ? var.permission_statements : [] + for_each = var.permission_statements != null ? var.permission_statements : {} content { - sid = statement.value.sid + sid = try(coalesce(statement.value.sid, statement.key)) actions = statement.value.actions not_actions = statement.value.not_actions effect = statement.value.effect diff --git a/modules/iam-group/variables.tf b/modules/iam-group/variables.tf index 8d619c06..17e62156 100644 --- a/modules/iam-group/variables.tf +++ b/modules/iam-group/variables.tf @@ -55,12 +55,12 @@ variable "enable_mfa_enforcment" { } variable "permission_statements" { - description = "List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for the policy" - type = list(object({ + description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage" + type = map(object({ sid = optional(string) actions = optional(list(string)) not_actions = optional(list(string)) - effect = optional(string) + effect = optional(string, "Allow") resources = optional(list(string)) not_resources = optional(list(string)) principals = optional(list(object({ @@ -73,8 +73,8 @@ variable "permission_statements" { }))) condition = optional(list(object({ test = string - values = list(string) variable = string + values = list(string) }))) })) default = null diff --git a/modules/iam-role-oidc/README.md b/modules/iam-role-oidc/README.md index 18562535..37a1d15f 100644 --- a/modules/iam-role-oidc/README.md +++ b/modules/iam-role-oidc/README.md @@ -85,7 +85,7 @@ No modules. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [assume\_role\_policy\_statements](#input\_assume\_role\_policy\_statements) | List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for for trusted assume role policy |
list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
}))
| `null` | no | +| [assume\_role\_policy\_statements](#input\_assume\_role\_policy\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage |
map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
variable = string
values = list(string)
})))
}))
| `null` | no | | [create](#input\_create) | Controls if resources should be created (affects all resources) | `bool` | `true` | no | | [description](#input\_description) | Description of the role | `string` | `null` | no | | [enable\_bitbucket\_oidc](#input\_enable\_bitbucket\_oidc) | Enable Bitbucket OIDC provider trust for the role | `bool` | `false` | no | diff --git a/modules/iam-role-oidc/main.tf b/modules/iam-role-oidc/main.tf index a3779a0a..a27fd1af 100644 --- a/modules/iam-role-oidc/main.tf +++ b/modules/iam-role-oidc/main.tf @@ -136,10 +136,10 @@ data "aws_iam_policy_document" "this" { # Generic statements dynamic "statement" { - for_each = var.assume_role_policy_statements != null ? var.assume_role_policy_statements : [] + for_each = var.assume_role_policy_statements != null ? var.assume_role_policy_statements : {} content { - sid = statement.value.sid + sid = try(coalesce(statement.value.sid, statement.key)) actions = statement.value.actions not_actions = statement.value.not_actions effect = statement.value.effect diff --git a/modules/iam-role-oidc/variables.tf b/modules/iam-role-oidc/variables.tf index 2f6f8410..9345605f 100644 --- a/modules/iam-role-oidc/variables.tf +++ b/modules/iam-role-oidc/variables.tf @@ -51,12 +51,12 @@ variable "permissions_boundary" { } variable "assume_role_policy_statements" { - description = "List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for for trusted assume role policy" - type = list(object({ + description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage" + type = map(object({ sid = optional(string) actions = optional(list(string)) not_actions = optional(list(string)) - effect = optional(string) + effect = optional(string, "Allow") resources = optional(list(string)) not_resources = optional(list(string)) principals = optional(list(object({ @@ -69,8 +69,8 @@ variable "assume_role_policy_statements" { }))) condition = optional(list(object({ test = string - values = list(string) variable = string + values = list(string) }))) })) default = null diff --git a/modules/iam-role-saml/README.md b/modules/iam-role-saml/README.md index a6b3f3b1..c2072acd 100644 --- a/modules/iam-role-saml/README.md +++ b/modules/iam-role-saml/README.md @@ -58,7 +58,7 @@ No modules. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [assume\_role\_policy\_statements](#input\_assume\_role\_policy\_statements) | List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for for trusted assume role policy |
list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
}))
| `null` | no | +| [assume\_role\_policy\_statements](#input\_assume\_role\_policy\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage |
map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
variable = string
values = list(string)
})))
}))
| `null` | no | | [create](#input\_create) | Controls if resources should be created (affects all resources) | `bool` | `true` | no | | [description](#input\_description) | Description of the role | `string` | `null` | no | | [max\_session\_duration](#input\_max\_session\_duration) | Maximum session duration (in seconds) that you want to set for the specified role. If you do not specify a value for this setting, the default maximum of one hour is applied. This setting can have a value from 1 hour to 12 hours | `number` | `null` | no | diff --git a/modules/iam-role-saml/main.tf b/modules/iam-role-saml/main.tf index 7ea91dbe..93745419 100644 --- a/modules/iam-role-saml/main.tf +++ b/modules/iam-role-saml/main.tf @@ -22,10 +22,10 @@ data "aws_iam_policy_document" "this" { } dynamic "statement" { - for_each = var.assume_role_policy_statements != null ? var.assume_role_policy_statements : [] + for_each = var.assume_role_policy_statements != null ? var.assume_role_policy_statements : {} content { - sid = statement.value.sid + sid = try(coalesce(statement.value.sid, statement.key)) actions = statement.value.actions not_actions = statement.value.not_actions effect = statement.value.effect diff --git a/modules/iam-role-saml/variables.tf b/modules/iam-role-saml/variables.tf index 065b44f2..f26b2185 100644 --- a/modules/iam-role-saml/variables.tf +++ b/modules/iam-role-saml/variables.tf @@ -51,12 +51,12 @@ variable "permissions_boundary" { } variable "assume_role_policy_statements" { - description = "List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for for trusted assume role policy" - type = list(object({ + description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage" + type = map(object({ sid = optional(string) actions = optional(list(string)) not_actions = optional(list(string)) - effect = optional(string) + effect = optional(string, "Allow") resources = optional(list(string)) not_resources = optional(list(string)) principals = optional(list(object({ @@ -69,8 +69,8 @@ variable "assume_role_policy_statements" { }))) condition = optional(list(object({ test = string - values = list(string) variable = string + values = list(string) }))) })) default = null diff --git a/modules/iam-role/README.md b/modules/iam-role/README.md index 16f15d05..5c5fca91 100644 --- a/modules/iam-role/README.md +++ b/modules/iam-role/README.md @@ -10,22 +10,21 @@ module "iam_role" { name = "example" - assume_role_policy_statements = [ - { - sid = "TrustRoleAndServiceToAssume" + assume_role_policy_statements = { + TrustRoleAndServiceToAssume = { principals = [{ type = "AWS" identifiers = [ "arn:aws:iam::835367859851:user/anton", ] }] - conditions = [{ + condition = [{ test = "StringEquals" variable = "sts:ExternalId" values = ["some-secret-id"] }] } - ] + } policies = { AmazonCognitoReadOnly = "arn:aws:iam::aws:policy/AmazonCognitoReadOnly" @@ -71,7 +70,7 @@ No modules. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [assume\_role\_policy\_statements](#input\_assume\_role\_policy\_statements) | List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for for trusted assume role policy |
list(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string)
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
values = list(string)
variable = string
})))
}))
| `null` | no | +| [assume\_role\_policy\_statements](#input\_assume\_role\_policy\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage |
map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
variable = string
values = list(string)
})))
}))
| `null` | no | | [create](#input\_create) | Controls if resources should be created (affects all resources) | `bool` | `true` | no | | [create\_instance\_profile](#input\_create\_instance\_profile) | Determines whether to create an instance profile | `bool` | `false` | no | | [description](#input\_description) | Description of the role | `string` | `null` | no | diff --git a/modules/iam-role/main.tf b/modules/iam-role/main.tf index bf2eac70..eb4aa854 100644 --- a/modules/iam-role/main.tf +++ b/modules/iam-role/main.tf @@ -6,10 +6,10 @@ data "aws_iam_policy_document" "this" { count = var.create && var.assume_role_policy_statements != null ? 1 : 0 dynamic "statement" { - for_each = var.assume_role_policy_statements != null ? var.assume_role_policy_statements : [] + for_each = var.assume_role_policy_statements != null ? var.assume_role_policy_statements : {} content { - sid = statement.value.sid + sid = try(coalesce(statement.value.sid, statement.key)) actions = statement.value.actions not_actions = statement.value.not_actions effect = statement.value.effect diff --git a/modules/iam-role/variables.tf b/modules/iam-role/variables.tf index fcbf07d7..42954371 100644 --- a/modules/iam-role/variables.tf +++ b/modules/iam-role/variables.tf @@ -51,12 +51,12 @@ variable "permissions_boundary" { } variable "assume_role_policy_statements" { - description = "List of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for for trusted assume role policy" - type = list(object({ + description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage" + type = map(object({ sid = optional(string) actions = optional(list(string)) not_actions = optional(list(string)) - effect = optional(string) + effect = optional(string, "Allow") resources = optional(list(string)) not_resources = optional(list(string)) principals = optional(list(object({ @@ -69,8 +69,8 @@ variable "assume_role_policy_statements" { }))) condition = optional(list(object({ test = string - values = list(string) variable = string + values = list(string) }))) })) default = null From dcdbae45edff707dab2ae5d988546cdf23cd8523 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Fri, 8 Aug 2025 13:09:19 -0500 Subject: [PATCH 06/21] feat: Merge `iam-role-oidc` and `iam-role-saml` into `iam-role` --- README.md | 94 ++++---- UPGRADE-6.0.md => docs/UPGRADE-6.0.md | 64 +----- examples/iam-oidc-provider/README.md | 3 +- examples/iam-oidc-provider/main.tf | 10 +- examples/iam-role-saml/README.md | 56 ----- examples/iam-role-saml/main.tf | 75 ------- examples/iam-role-saml/outputs.tf | 18 -- examples/iam-role-saml/variables.tf | 0 examples/iam-role-saml/versions.tf | 10 - examples/iam-role/README.md | 4 +- examples/iam-role/main.tf | 151 +++++++------ .../saml-metadata.xml | 0 modules/iam-role-oidc/README.md | 117 ---------- modules/iam-role-oidc/main.tf | 201 ------------------ modules/iam-role-oidc/outputs.tf | 18 -- modules/iam-role-oidc/variables.tf | 125 ----------- modules/iam-role-oidc/versions.tf | 10 - modules/iam-role-saml/README.md | 86 -------- modules/iam-role-saml/main.tf | 87 -------- modules/iam-role-saml/outputs.tf | 18 -- modules/iam-role-saml/variables.tf | 101 --------- modules/iam-role-saml/versions.tf | 10 - modules/iam-role/README.md | 100 ++++++++- modules/iam-role/main.tf | 155 +++++++++++++- modules/iam-role/outputs.tf | 24 --- modules/iam-role/variables.tf | 72 +++++++ wrappers/iam-role-oidc/README.md | 100 --------- wrappers/iam-role-oidc/main.tf | 23 -- wrappers/iam-role-oidc/outputs.tf | 5 - wrappers/iam-role-oidc/variables.tf | 11 - wrappers/iam-role-oidc/versions.tf | 10 - wrappers/iam-role-saml/README.md | 100 --------- wrappers/iam-role-saml/main.tf | 19 -- wrappers/iam-role-saml/outputs.tf | 5 - wrappers/iam-role-saml/variables.tf | 11 - wrappers/iam-role-saml/versions.tf | 10 - wrappers/iam-role/main.tf | 12 ++ 37 files changed, 474 insertions(+), 1441 deletions(-) rename UPGRADE-6.0.md => docs/UPGRADE-6.0.md (65%) delete mode 100644 examples/iam-role-saml/README.md delete mode 100644 examples/iam-role-saml/main.tf delete mode 100644 examples/iam-role-saml/outputs.tf delete mode 100644 examples/iam-role-saml/variables.tf delete mode 100644 examples/iam-role-saml/versions.tf rename examples/{iam-role-saml => iam-role}/saml-metadata.xml (100%) delete mode 100644 modules/iam-role-oidc/README.md delete mode 100644 modules/iam-role-oidc/main.tf delete mode 100644 modules/iam-role-oidc/outputs.tf delete mode 100644 modules/iam-role-oidc/variables.tf delete mode 100644 modules/iam-role-oidc/versions.tf delete mode 100644 modules/iam-role-saml/README.md delete mode 100644 modules/iam-role-saml/main.tf delete mode 100644 modules/iam-role-saml/outputs.tf delete mode 100644 modules/iam-role-saml/variables.tf delete mode 100644 modules/iam-role-saml/versions.tf delete mode 100644 wrappers/iam-role-oidc/README.md delete mode 100644 wrappers/iam-role-oidc/main.tf delete mode 100644 wrappers/iam-role-oidc/outputs.tf delete mode 100644 wrappers/iam-role-oidc/variables.tf delete mode 100644 wrappers/iam-role-oidc/versions.tf delete mode 100644 wrappers/iam-role-saml/README.md delete mode 100644 wrappers/iam-role-saml/main.tf delete mode 100644 wrappers/iam-role-saml/outputs.tf delete mode 100644 wrappers/iam-role-saml/variables.tf delete mode 100644 wrappers/iam-role-saml/versions.tf diff --git a/README.md b/README.md index bb7e06d1..fafbde8b 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,6 @@ Terraform module which creates AWS IAM resources. -### ⚠️ JUST FOR TESTING - DO NOT RELY ON THIS ⚠️ - [![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md) ## Usage @@ -47,13 +45,12 @@ module "iam_group" { ] enable_self_management_permissions = true - permission_statements = [ - { - sid = "AssumeRole" + permission_statements = { + AssumeRole = { actions = ["sts:AssumeRole"] resources = ["arn:aws:iam::111111111111:role/admin"] } - ] + } policies = { AdministratorAccess = "arn:aws:iam::aws:policy/AdministratorAccess", @@ -106,26 +103,38 @@ module "iam_read_only_policy" { } ``` -### IAM Role for Service Accounts (IRSA) +### IAM Role -Creates an IAM role that is suitable for EKS IAM role for service accounts (IRSA) with a set of pre-defined policies for common EKS addons. +Creates an IAM role with a trust policy and (optional) IAM instance profile. Useful for service roles such as EC2, ECS, etc., or roles assumed across AWS accounts. ```hcl -module "vpc_cni_irsa" { - source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts" - - name = "vpc-cni" +module "iam_role" { + source = "terraform-aws-modules/iam/aws//modules/iam-role" - attach_vpc_cni_policy = true - vpc_cni_enable_ipv4 = true + name = "example" - oidc_providers = { - this = { - provider_arn = "arn:aws:iam::012345678901:oidc-provider/oidc.eks.us-east-1.amazonaws.com/id/5C54DDF35ER19312844C7333374CC09D" - namespace_service_accounts = ["kube-system:aws-node"] + assume_role_policy_statements = { + TrustRoleAndServiceToAssume = { + principals = [{ + type = "AWS" + identifiers = [ + "arn:aws:iam::835367859851:user/anton", + ] + }] + conditions = [{ + test = "StringEquals" + variable = "sts:ExternalId" + values = ["some-secret-id"] + }] } } + policies = { + AmazonCognitoReadOnly = "arn:aws:iam::aws:policy/AmazonCognitoReadOnly" + AlexaForBusinessFullAccess = "arn:aws:iam::aws:policy/AlexaForBusinessFullAccess" + custom = aws_iam_policy.this.arn + } + tags = { Terraform = "true" Environment = "dev" @@ -133,13 +142,13 @@ module "vpc_cni_irsa" { } ``` -### OIDC IAM Role +### IAM Role - GitHub OIDC Creates an IAM role that trusts an OpenID connect provider. Useful for trusting external identity providers such as GitHub, Bitbucket, etc. ```hcl -module "iam_oidc_role" { - source = "terraform-aws-modules/iam/aws//modules/iam-oidc-role" +module "iam_role_github_oidc" { + source = "terraform-aws-modules/iam/aws//modules/iam-role" enable_github_oidc = true @@ -157,16 +166,17 @@ module "iam_oidc_role" { } ``` -### SAML IAM Role +### IAM Role - SAML 2.0 Creates an IAM role that trusts a SAML provider. Useful for trusting external identity providers such as Okta, OneLogin, etc. ```hcl module "iam_role_saml" { - source = "terraform-aws-modules/iam/aws//modules/iam-role-saml" + source = "terraform-aws-modules/iam/aws//modules/iam-role" name = "example" + enable_saml = true saml_provider_ids = ["arn:aws:iam::235367859851:saml-provider/idp_saml"] policies = { @@ -180,37 +190,24 @@ module "iam_role_saml" { } ``` -### IAM Role +### IAM Role for EKS Service Accounts (IRSA) -Creates an IAM role with a trust policy and (optional) IAM instance profile. Useful for service roles such as EC2, ECS, etc., or roles assumed across AWS accounts. +Creates an IAM role that is suitable for EKS IAM role for service accounts (IRSA) with a set of pre-defined policies for common EKS addons. ```hcl -module "iam_role" { - source = "terraform-aws-modules/iam/aws//modules/iam-role" +module "vpc_cni_irsa" { + source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts" - name = "example" + name = "vpc-cni" - assume_role_policy_statements = [ - { - sid = "TrustRoleAndServiceToAssume" - principals = [{ - type = "AWS" - identifiers = [ - "arn:aws:iam::835367859851:user/anton", - ] - }] - conditions = [{ - test = "StringEquals" - variable = "sts:ExternalId" - values = ["some-secret-id"] - }] - } - ] + attach_vpc_cni_policy = true + vpc_cni_enable_ipv4 = true - policies = { - AmazonCognitoReadOnly = "arn:aws:iam::aws:policy/AmazonCognitoReadOnly" - AlexaForBusinessFullAccess = "arn:aws:iam::aws:policy/AlexaForBusinessFullAccess" - custom = aws_iam_policy.this.arn + oidc_providers = { + this = { + provider_arn = "arn:aws:iam::012345678901:oidc-provider/oidc.eks.us-east-1.amazonaws.com/id/5C54DDF35ER19312844C7333374CC09D" + namespace_service_accounts = ["kube-system:aws-node"] + } } tags = { @@ -249,7 +246,6 @@ module "iam_user" { - [iam-read-only-policy](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-read-only-policy) - Create IAM read-only policy - [iam-role](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-role) - Create individual IAM role which can be assumed from specified ARNs (AWS accounts, IAM users, etc) - [iam-role-for-service-accounts](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-role-for-service-accounts) - Create IAM role for service accounts (IRSA) for use within EKS clusters -- [iam-role-saml](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-role-saml) - Create individual IAM role which can be assumed by users with a SAML Identity Provider - [iam-user](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-user) - Add IAM user, login profile and access keys (with PGP enabled or disabled) ## Authors diff --git a/UPGRADE-6.0.md b/docs/UPGRADE-6.0.md similarity index 65% rename from UPGRADE-6.0.md rename to docs/UPGRADE-6.0.md index 86ab727d..4b56ada7 100644 --- a/UPGRADE-6.0.md +++ b/docs/UPGRADE-6.0.md @@ -7,14 +7,14 @@ If you find a bug, please open an issue with supporting configuration to reprodu ## List of backwards incompatible changes - `iam-assumable-role` has been renamed to `iam-role` -- `iam-assumable-role-with-oidc` has been renamed to `iam-role-oidc` -- `iam-assumable-role-with-saml` has been renamed to `iam-role-saml` +- `iam-assumable-role-with-oidc` has been merged into `iam-role` +- `iam-assumable-role-with-saml` has been merged into `iam-role` - `iam-assumable-roles` has been removed; `iam-role` should be used instead. See the [`iam-role` example](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-role) that shows an example replacement implementation. -- `iam-assumable-roles-with-saml` has been removed; `iam-role-saml` should be used instead. See the [`iam-role-saml` example](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-role-saml) that shows an example replacement implementation. +- `iam-assumable-roles-with-saml` has been removed; `iam-role` should be used instead. See the [`iam-role` example](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-role-saml) that shows an example replacement implementation. - `iam-github-oidc-provider` has been renamed to `iam-oidc-provider` -- `iam-github-oidc-role` has been removed; `iam-role-oidc` should be used instead. See the [`iam-oidc-provider` example](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-oidc-provider) +- `iam-github-oidc-role` has been merged into `iam-role`. See the [`iam-oidc-provider` example](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-oidc-provider) - `iam-group-with-assumable-roles-policy` has been removed; the renamed `iam-group` (was `iam-group-with-policies`) should be used instead -- `iam-eks-role` has been removed; `iam-role-for-service-accounts-eks` should be used instead +- `iam-eks-role` has been removed; `iam-role-for-service-accounts` should be used instead - `iam-policy` has been removed; the `aws_iam_policy` resource should be used directly instead ## Additional changes @@ -26,14 +26,6 @@ If you find a bug, please open an issue with supporting configuration to reprodu - `custom_role_policy_arns` has been renamed to `policies` and now accepts a map of `name`: `policy-arn` pairs; this allows for both existing policies and policies that will get created at the same time as the role. This also replaces the admin, readonly, and poweruser policy ARN variables and their associated `attach_*_policy` variables. - Default create conditional is now `true` instead of `false` - `force_detach_policies` has been removed; this is now always `true` -- `iam-role-oidc` - - `custom_role_policy_arns` has been renamed to `policies` and now accepts a map of `name`: `policy-arn` pairs; this allows for both existing policies and policies that will get created at the same time as the role. - - Default create conditional is now `true` instead of `false` - - `force_detach_policies` has been removed; this is now always `true` -- `iam-role-saml` - - `custom_role_policy_arns` has been renamed to `policies` and now accepts a map of `name`: `policy-arn` pairs; this allows for both existing policies and policies that will get created at the same time as the role. - - Default create conditional is now `true` instead of `false` - - `force_detach_policies` has been removed; this is now always `true` - `iam-group` - Policy management has been updated to support extending the policy created by the sub-module, as well as adding additional policies that will be attached to the group - The role assumption permissions has been removed from the policy; users can extend the policy to add this if needed via `permission_statements` @@ -56,12 +48,6 @@ If you find a bug, please open an issue with supporting configuration to reprodu - `readonly_role_policy_arn` & `attach_readonly_policy` - `force_detach_policies` - `role_sts_externalid` - - `iam-role-oidc` - - `force_detach_policies` - - `number_of_custom_role_policy_arns` - - `iam-role-saml` - - `force_detach_policies` - - `number_of_custom_role_policy_arns` - `iam-group` - `custom_group_policies` - `assumable_roles` @@ -76,24 +62,6 @@ If you find a bug, please open an issue with supporting configuration to reprodu - `role_path` -> `path` - `role_permissions_boundary_arn` -> `permissions_boundary_arn` - `custom_role_policy_arns` -> `policies` - - `iam-role-oidc` - - `create_role` -> `create` - - `role_name` -> `name` - - `role_name_prefix` -> `name_prefix` - - `role_description` -> `description` - - `role_path` -> `path` - - `role_permissions_boundary_arn` -> `permissions_boundary_arn` - - `custom_role_policy_arns` -> `policies` - - `iam-role-saml` - - `create_role` -> `create` - - `role_name` -> `name` - - `role_name_prefix` -> `name_prefix` - - `role_description` -> `description` - - `role_path` -> `path` - - `role_permissions_boundary_arn` -> `permissions_boundary_arn` - - `custom_role_policy_arns` -> `policies` - - `aws_saml_endpoint` -> `saml_endpoints` - - `trusted_role_actions` -> `saml_trust_actions` - `iam-group` - `create_group` -> `create` - `group_users` -> `group` @@ -106,10 +74,6 @@ If you find a bug, please open an issue with supporting configuration to reprodu - `iam-role` - `assume_role_policy_statements` which allows for any number of custom statements to be added to the role's trust policy. This covers the majority of the variables that were removed - - `iam-role-oidc` - - `assume_role_policy_statements` which allows for any number of custom statements to be added to the role's trust policy. This covers the majority of the variables that were removed - - `iam-role-saml` - - `assume_role_policy_statements` which allows for any number of custom statements to be added to the role's trust policy. This covers the majority of the variables that were removed - `iam-group` - `permission_statements` which allows for any number of custom statements to be added to the role's trust policy. This covers the majority of the variables that were removed - `path`/`policy_path` @@ -123,12 +87,6 @@ If you find a bug, please open an issue with supporting configuration to reprodu - `role_requires_mfa` - `iam_instance_profile_path` - `role_sts_externalid` - - `iam-role-oidc` - - `iam_role_path` - - `provider_url` (use `oidc_provider_urls` instead) - - `iam-role-saml` - - `iam_role_path` - - `provider_id` (use `saml_provider_ids` instead) - `iam-group` - `assumable_roles` - `aws_account_id` @@ -143,18 +101,6 @@ If you find a bug, please open an issue with supporting configuration to reprodu - `iam_instance_profile_id` -> `instance_profile_id` - `iam_instance_profile_name` -> `instance_profile_name` - `iam_instance_profile_unique_id` -> `instance_profile_unique_id` - - `iam-role-oidc` - - `iam_role_arn` -> `arn` - - `iam_role_name` -> `name` - - `iam_role_unique_id` -> `unique_id` - - `aws_account_id` -> `oidc_account_id` - - `provider_urls` -> `oidc_provider_urls` - - `iam-role-oidc` - - `iam_role_arn` -> `arn` - - `iam_role_name` -> `name` - - `iam_role_unique_id` -> `unique_id` - - `aws_account_id` -> `oidc_account_id` - - `provider_ids` -> `saml_provider_ids` - `iam-group` - `group_id` -> `id` - `group_name` -> `name` diff --git a/examples/iam-oidc-provider/README.md b/examples/iam-oidc-provider/README.md index e80ce875..a039737b 100644 --- a/examples/iam-oidc-provider/README.md +++ b/examples/iam-oidc-provider/README.md @@ -34,9 +34,8 @@ No providers. | Name | Source | Version | |------|--------|---------| | [github\_oidc\_iam\_provider](#module\_github\_oidc\_iam\_provider) | ../../modules/iam-oidc-provider | n/a | -| [github\_oidc\_iam\_role](#module\_github\_oidc\_iam\_role) | ../../modules/iam-role-oidc | n/a | +| [github\_oidc\_iam\_role](#module\_github\_oidc\_iam\_role) | ../../modules/iam-role | n/a | | [oidc\_iam\_provider\_disabled](#module\_oidc\_iam\_provider\_disabled) | ../../modules/iam-oidc-provider | n/a | -| [oidc\_iam\_role\_disabled](#module\_oidc\_iam\_role\_disabled) | ../../modules/iam-role-oidc | n/a | ## Resources diff --git a/examples/iam-oidc-provider/main.tf b/examples/iam-oidc-provider/main.tf index 369de98a..df62e08a 100644 --- a/examples/iam-oidc-provider/main.tf +++ b/examples/iam-oidc-provider/main.tf @@ -30,11 +30,11 @@ module "oidc_iam_provider_disabled" { } ################################################################################ -# OIDC IAM Role +# GitHub OIDC IAM Role ################################################################################ module "github_oidc_iam_role" { - source = "../../modules/iam-role-oidc" + source = "../../modules/iam-role" name = local.name @@ -53,9 +53,3 @@ module "github_oidc_iam_role" { tags = local.tags } - -module "oidc_iam_role_disabled" { - source = "../../modules/iam-role-oidc" - - create = false -} diff --git a/examples/iam-role-saml/README.md b/examples/iam-role-saml/README.md deleted file mode 100644 index 0bda0aa0..00000000 --- a/examples/iam-role-saml/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# AWS IAM Role w/ SAML Federation example - -Configuration in this directory creates an IAM role which can be assumed by users with a SAML Identity Provider. - -# Usage - -To run this example you need to execute: - -```bash -$ terraform init -$ terraform plan -$ terraform apply -``` - -Run `terraform destroy` when you don't need these resources. - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.5.7 | -| [aws](#requirement\_aws) | >= 6.0 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 6.0 | - -## Modules - -| Name | Source | Version | -|------|--------|---------| -| [iam\_role](#module\_iam\_role) | ../../modules/iam-role-saml | n/a | -| [iam\_role\_disabled](#module\_iam\_role\_disabled) | ../../modules/iam-role-oidc | n/a | -| [iam\_roles](#module\_iam\_roles) | ../../modules/iam-role-saml | n/a | - -## Resources - -| Name | Type | -|------|------| -| [aws_iam_saml_provider.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_saml_provider) | resource | - -## Inputs - -No inputs. - -## Outputs - -| Name | Description | -|------|-------------| -| [iam\_role\_arn](#output\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | -| [iam\_role\_name](#output\_iam\_role\_name) | The name of the IAM role | -| [iam\_role\_unique\_id](#output\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | - diff --git a/examples/iam-role-saml/main.tf b/examples/iam-role-saml/main.tf deleted file mode 100644 index fe2c3449..00000000 --- a/examples/iam-role-saml/main.tf +++ /dev/null @@ -1,75 +0,0 @@ -provider "aws" { - region = "eu-west-1" -} - -locals { - name = "ex-${basename(path.cwd)}" - - tags = { - Example = local.name - GithubRepo = "terraform-aws-iam" - GithubOrg = "terraform-aws-modules" - } -} - -################################################################################ -# IAM Role w/ SAML -################################################################################ - -module "iam_role" { - source = "../../modules/iam-role-saml" - - name = local.name - - saml_provider_ids = [aws_iam_saml_provider.this.id] - - policies = { - ReadOnlyAccess = "arn:aws:iam::aws:policy/ReadOnlyAccess" - } - - tags = local.tags -} - -module "iam_roles" { - source = "../../modules/iam-role-saml" - - for_each = { - admin = { - policies = { - AdministratorAccess = "arn:aws:iam::aws:policy/AdministratorAccess" - } - } - readonly = { - policies = { - ReadOnlyAccess = "arn:aws:iam::aws:policy/ReadOnlyAccess" - } - } - poweruser = { - policies = { - PowerUserAccess = "arn:aws:iam::aws:policy/PowerUserAccess" - } - } - } - - name = each.key - - saml_provider_ids = [aws_iam_saml_provider.this.id] - policies = each.value.policies - - tags = local.tags -} - -module "iam_role_disabled" { - source = "../../modules/iam-role-oidc" - - create = false -} - -################################################################################ -# Supporting Resources -################################################################################ - -resource "aws_iam_saml_provider" "this" { - name = "idp_saml" - saml_metadata_document = file("saml-metadata.xml") -} diff --git a/examples/iam-role-saml/outputs.tf b/examples/iam-role-saml/outputs.tf deleted file mode 100644 index f924f3d0..00000000 --- a/examples/iam-role-saml/outputs.tf +++ /dev/null @@ -1,18 +0,0 @@ -################################################################################ -# IAM Role w/ OIDC -################################################################################ - -output "iam_role_name" { - description = "The name of the IAM role" - value = module.iam_role.name -} - -output "iam_role_arn" { - description = "The Amazon Resource Name (ARN) specifying the IAM role" - value = module.iam_role.arn -} - -output "iam_role_unique_id" { - description = "Stable and unique string identifying the IAM role" - value = module.iam_role.unique_id -} diff --git a/examples/iam-role-saml/variables.tf b/examples/iam-role-saml/variables.tf deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/iam-role-saml/versions.tf b/examples/iam-role-saml/versions.tf deleted file mode 100644 index db13b0a8..00000000 --- a/examples/iam-role-saml/versions.tf +++ /dev/null @@ -1,10 +0,0 @@ -terraform { - required_version = ">= 1.5.7" - - required_providers { - aws = { - source = "hashicorp/aws" - version = ">= 6.0" - } - } -} diff --git a/examples/iam-role/README.md b/examples/iam-role/README.md index b3e79b55..f84cf818 100644 --- a/examples/iam-role/README.md +++ b/examples/iam-role/README.md @@ -32,9 +32,10 @@ Run `terraform destroy` when you don't need these resources. | Name | Source | Version | |------|--------|---------| -| [iam\_role\_condition](#module\_iam\_role\_condition) | ../../modules/iam-role | n/a | | [iam\_role\_disabled](#module\_iam\_role\_disabled) | ../../modules/iam-role | n/a | +| [iam\_role\_github\_oidc](#module\_iam\_role\_github\_oidc) | ../../modules/iam-role | n/a | | [iam\_role\_instance\_profile](#module\_iam\_role\_instance\_profile) | ../../modules/iam-role | n/a | +| [iam\_role\_saml](#module\_iam\_role\_saml) | ../../modules/iam-role | n/a | | [iam\_roles](#module\_iam\_roles) | ../../modules/iam-role | n/a | ## Resources @@ -42,6 +43,7 @@ Run `terraform destroy` when you don't need these resources. | Name | Type | |------|------| | [aws_iam_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_saml_provider.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_saml_provider) | resource | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | ## Inputs diff --git a/examples/iam-role/main.tf b/examples/iam-role/main.tf index 8764b01d..fab42883 100644 --- a/examples/iam-role/main.tf +++ b/examples/iam-role/main.tf @@ -18,70 +18,6 @@ locals { # IAM Role ################################################################################ -module "iam_role_instance_profile" { - source = "../../modules/iam-role" - - name = "${local.name}-instance-profile" - - create_instance_profile = true - - assume_role_policy_statements = { - TrustRoleAndServiceToAssume = { - principals = [ - { - type = "AWS" - identifiers = [ - "arn:aws:iam::307990089504:root", - "arn:aws:iam::835367859851:user/anton", - ] - }, - { - type = "Service" - identifiers = [ - "codedeploy.amazonaws.com", - ] - } - ] - } - } - - policies = { - ReadOnlyAccess = "arn:aws:iam::aws:policy/ReadOnlyAccess" - } - - tags = local.tags -} - -module "iam_role_condition" { - source = "../../modules/iam-role" - - name = "condition" - - assume_role_policy_statements = { - TrustRoleAndServiceToAssume = { - principals = [{ - type = "AWS" - identifiers = [ - "arn:aws:iam::835367859851:user/anton", - ] - }] - condition = [{ - test = "StringEquals" - variable = "sts:ExternalId" - values = ["some-secret-id"] - }] - } - } - - policies = { - AmazonCognitoReadOnly = "arn:aws:iam::aws:policy/AmazonCognitoReadOnly" - AlexaForBusinessFullAccess = "arn:aws:iam::aws:policy/AlexaForBusinessFullAccess" - custom = aws_iam_policy.this.arn - } - - tags = local.tags -} - module "iam_roles" { source = "../../modules/iam-role" @@ -141,6 +77,88 @@ module "iam_role_disabled" { create = false } +################################################################################ +# IAM Role - Instance Profile +################################################################################ + +module "iam_role_instance_profile" { + source = "../../modules/iam-role" + + name = "${local.name}-instance-profile" + + create_instance_profile = true + + assume_role_policy_statements = { + TrustRoleAndServiceToAssume = { + principals = [ + { + type = "AWS" + identifiers = [ + "arn:aws:iam::307990089504:root", + "arn:aws:iam::835367859851:user/anton", + ] + }, + { + type = "Service" + identifiers = [ + "codedeploy.amazonaws.com", + ] + } + ] + } + } + + policies = { + ReadOnlyAccess = "arn:aws:iam::aws:policy/ReadOnlyAccess" + } + + tags = local.tags +} + +################################################################################ +# IAM Role - GitHub OIDC +################################################################################ + +module "iam_role_github_oidc" { + source = "../../modules/iam-role" + + name = local.name + + enable_github_oidc = true + + # This should be updated to suit your organization, repository, references/branches, etc. + oidc_subjects = [ + # You can prepend with `repo:` but it is not required + "repo:terraform-aws-modules/terraform-aws-iam:pull_request", + "terraform-aws-modules/terraform-aws-iam:ref:refs/heads/master", + ] + + policies = { + S3ReadOnly = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" + } + + tags = local.tags +} + +################################################################################ +# IAM Role - SAML 2.0 +################################################################################ + +module "iam_role_saml" { + source = "../../modules/iam-role" + + name = "${local.name}-saml" + + enable_saml = true + saml_provider_ids = [aws_iam_saml_provider.this.id] + + policies = { + ReadOnlyAccess = "arn:aws:iam::aws:policy/ReadOnlyAccess" + } + + tags = local.tags +} + ################################################################################ # Supporting resources ################################################################################ @@ -167,3 +185,8 @@ resource "aws_iam_policy" "this" { tags = local.tags } + +resource "aws_iam_saml_provider" "this" { + name = "idp_saml" + saml_metadata_document = file("saml-metadata.xml") +} diff --git a/examples/iam-role-saml/saml-metadata.xml b/examples/iam-role/saml-metadata.xml similarity index 100% rename from examples/iam-role-saml/saml-metadata.xml rename to examples/iam-role/saml-metadata.xml diff --git a/modules/iam-role-oidc/README.md b/modules/iam-role-oidc/README.md deleted file mode 100644 index 37a1d15f..00000000 --- a/modules/iam-role-oidc/README.md +++ /dev/null @@ -1,117 +0,0 @@ -# AWS IAM OIDC Role Terraform Module - -Creates a single IAM role which can be assumed by trusted resources using OpenID connect federation. - -## Usage - -### [GitHub Free, Pro, & Team](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services) - -The defaults provided by the module are suitable for GitHub Free, Pro, & Team, including use with the official [AWS GitHub action](https://github.com/aws-actions/configure-aws-credentials). - -```hcl -module "iam_oidc_role" { - source = "terraform-aws-modules/iam/aws//modules/iam-oidc-role" - - enable_github_oidc = true - - # This should be updated to suit your organization, repository, references/branches, etc. - oidc_subjects = ["terraform-aws-modules/terraform-aws-iam:*"] - - policies = { - S3ReadOnly = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" - } - - tags = { - Environment = "test" - } -} -``` - -### [GitHub Enterprise Server](https://docs.github.com/en/enterprise-server@3.7/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services) - -For GitHub Enterprise Server, users will need to provide value for the `oidc_audience` and `provider_urls` to suit their `` installation: - -```hcl -module "iam_oidc_role" { - source = "terraform-aws-modules/iam/aws//modules/iam-oidc-role" - - enable_github_oidc = true - - oidc_audience = "https://mygithub.com/" - oidc_provider_urls = ["mygithub.com/_services/token"] - - # This should be updated to suit your organization, repository, references/branches, etc. - oidc_subjects = ["/terraform-aws-iam:*"] - - policies = { - S3ReadOnly = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" - } - - tags = { - Environment = "test" - } -} -``` - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.5.7 | -| [aws](#requirement\_aws) | >= 6.0 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 6.0 | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | -| [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | -| [aws_iam_policy_document.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [assume\_role\_policy\_statements](#input\_assume\_role\_policy\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage |
map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
variable = string
values = list(string)
})))
}))
| `null` | no | -| [create](#input\_create) | Controls if resources should be created (affects all resources) | `bool` | `true` | no | -| [description](#input\_description) | Description of the role | `string` | `null` | no | -| [enable\_bitbucket\_oidc](#input\_enable\_bitbucket\_oidc) | Enable Bitbucket OIDC provider trust for the role | `bool` | `false` | no | -| [enable\_github\_oidc](#input\_enable\_github\_oidc) | Enable GitHub OIDC provider trust for the role | `bool` | `false` | no | -| [max\_session\_duration](#input\_max\_session\_duration) | Maximum session duration (in seconds) that you want to set for the specified role. If you do not specify a value for this setting, the default maximum of one hour is applied. This setting can have a value from 1 hour to 12 hours | `number` | `null` | no | -| [name](#input\_name) | Name to use on IAM role created | `string` | `null` | no | -| [oidc\_account\_id](#input\_oidc\_account\_id) | An overriding AWS account ID where the OIDC provider lives; leave empty to use the current account ID for the AWS provider | `string` | `null` | no | -| [oidc\_audiences](#input\_oidc\_audiences) | The audience to be added to the role policy. Set to sts.amazonaws.com for cross-account assumable role. Leave empty otherwise. | `set(string)` | `[]` | no | -| [oidc\_provider\_urls](#input\_oidc\_provider\_urls) | List of URLs of the OIDC Providers | `list(string)` | `[]` | no | -| [oidc\_subjects](#input\_oidc\_subjects) | The fully qualified OIDC subjects to be added to the role policy | `set(string)` | `[]` | no | -| [oidc\_wildcard\_subjects](#input\_oidc\_wildcard\_subjects) | The OIDC subject using wildcards to be added to the role policy | `set(string)` | `[]` | no | -| [path](#input\_path) | Path of IAM role | `string` | `"/"` | no | -| [permissions\_boundary](#input\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no | -| [policies](#input\_policies) | Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no | -| [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | -| [use\_name\_prefix](#input\_use\_name\_prefix) | Determines whether the IAM role name (`name`) is used as a prefix | `bool` | `true` | no | - -## Outputs - -| Name | Description | -|------|-------------| -| [arn](#output\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | -| [name](#output\_name) | The name of the IAM role | -| [unique\_id](#output\_unique\_id) | Stable and unique string identifying the IAM role | - - -## License - -Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-iam/blob/master/LICENSE). diff --git a/modules/iam-role-oidc/main.tf b/modules/iam-role-oidc/main.tf deleted file mode 100644 index a27fd1af..00000000 --- a/modules/iam-role-oidc/main.tf +++ /dev/null @@ -1,201 +0,0 @@ -data "aws_caller_identity" "current" { - count = var.create ? 1 : 0 -} -data "aws_partition" "current" { - count = var.create ? 1 : 0 -} - -locals { - account_id = try(data.aws_caller_identity.current[0].account_id, "") - partition = try(data.aws_partition.current[0].partition, "") - - oidc_providers = [for url in var.oidc_provider_urls : replace(url, "https://", "")] - github_provider = coalesce(one(local.oidc_providers), "token.actions.githubusercontent.com") - bitbucket_provider = one(local.oidc_providers) -} - -################################################################################ -# IAM Role -################################################################################ - -data "aws_iam_policy_document" "this" { - count = var.create ? 1 : 0 - - # Generic OIDC - dynamic "statement" { - for_each = !var.enable_github_oidc ? local.oidc_providers : [] - - content { - effect = "Allow" - actions = ["sts:AssumeRoleWithWebIdentity"] - - principals { - type = "Federated" - - identifiers = ["arn:${local.partition}:iam::${coalesce(var.oidc_account_id, local.account_id)}:oidc-provider/${statement.value}"] - } - - dynamic "condition" { - for_each = length(var.oidc_subjects) > 0 ? local.oidc_providers : [] - - content { - test = "StringEquals" - variable = "${statement.value}:sub" - values = var.oidc_subjects - } - } - - dynamic "condition" { - for_each = length(var.oidc_wildcard_subjects) > 0 ? local.oidc_providers : [] - - content { - test = "StringLike" - variable = "${statement.value}:sub" - values = var.oidc_wildcard_subjects - } - } - - dynamic "condition" { - for_each = length(var.oidc_audiences) > 0 ? local.oidc_providers : [] - - content { - test = "StringLike" - variable = "${statement.value}:aud" - values = var.oidc_audiences - } - } - } - } - - # GitHub OIDC - dynamic "statement" { - for_each = var.enable_github_oidc ? [1] : [] - - content { - sid = "GithubOidcAuth" - actions = [ - "sts:TagSession", - "sts:AssumeRoleWithWebIdentity" - ] - - principals { - type = "Federated" - identifiers = ["arn:${local.partition}:iam::${local.account_id}:oidc-provider/${local.github_provider}"] - } - - condition { - test = "ForAllValues:StringEquals" - variable = "token.actions.githubusercontent.com:iss" - values = ["http://token.actions.githubusercontent.com"] - } - - condition { - test = "ForAllValues:StringEquals" - variable = "${local.github_provider}:aud" - values = coalescelist(var.oidc_audiences, ["sts.amazonaws.com"]) - } - - condition { - test = "StringLike" - variable = "${local.github_provider}:sub" - # Strip `repo:` to normalize for cases where users may prepend it - values = [for subject in var.oidc_subjects : "repo:${trimprefix(subject, "repo:")}"] - } - } - } - - # Bitbucket OIDC - dynamic "statement" { - for_each = var.enable_bitbucket_oidc ? [1] : [] - - content { - sid = "BitbucketOidcAuth" - actions = [ - "sts:TagSession", - "sts:AssumeRoleWithWebIdentity" - ] - - principals { - type = "Federated" - identifiers = ["arn:${local.partition}:iam::${local.account_id}:oidc-provider/${local.bitbucket_provider}"] - } - - condition { - test = "ForAllValues:StringEquals" - variable = "${local.bitbucket_provider}:aud" - values = coalescelist(var.oidc_audiences, ["sts.amazonaws.com"]) - } - - condition { - test = "StringLike" - variable = "${local.bitbucket_provider}:sub" - values = var.oidc_subjects - } - } - } - - # Generic statements - dynamic "statement" { - for_each = var.assume_role_policy_statements != null ? var.assume_role_policy_statements : {} - - content { - sid = try(coalesce(statement.value.sid, statement.key)) - actions = statement.value.actions - not_actions = statement.value.not_actions - effect = statement.value.effect - resources = statement.value.resources - not_resources = statement.value.not_resources - - dynamic "principals" { - for_each = statement.value.principals != null ? statement.value.principals : [] - - content { - type = principals.value.type - identifiers = principals.value.identifiers - } - } - - dynamic "not_principals" { - for_each = statement.value.not_principals != null ? statement.value.not_principals : [] - - content { - type = not_principals.value.type - identifiers = not_principals.value.identifiers - } - } - - dynamic "condition" { - for_each = statement.value.condition != null ? statement.value.condition : [] - - content { - test = condition.value.test - values = condition.value.values - variable = condition.value.variable - } - } - } - } -} - -resource "aws_iam_role" "this" { - count = var.create ? 1 : 0 - - name = var.use_name_prefix ? null : var.name - name_prefix = var.use_name_prefix ? "${var.name}-" : null - path = var.path - description = var.description - - assume_role_policy = data.aws_iam_policy_document.this[0].json - max_session_duration = var.max_session_duration - permissions_boundary = var.permissions_boundary - force_detach_policies = true - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "this" { - for_each = { for k, v in var.policies : k => v if var.create } - - policy_arn = each.value - role = aws_iam_role.this[0].name -} diff --git a/modules/iam-role-oidc/outputs.tf b/modules/iam-role-oidc/outputs.tf deleted file mode 100644 index 8410060f..00000000 --- a/modules/iam-role-oidc/outputs.tf +++ /dev/null @@ -1,18 +0,0 @@ -################################################################################ -# IAM Role -################################################################################ - -output "name" { - description = "The name of the IAM role" - value = try(aws_iam_role.this[0].name, null) -} - -output "arn" { - description = "The Amazon Resource Name (ARN) specifying the IAM role" - value = try(aws_iam_role.this[0].arn, null) -} - -output "unique_id" { - description = "Stable and unique string identifying the IAM role" - value = try(aws_iam_role.this[0].unique_id, null) -} diff --git a/modules/iam-role-oidc/variables.tf b/modules/iam-role-oidc/variables.tf deleted file mode 100644 index 9345605f..00000000 --- a/modules/iam-role-oidc/variables.tf +++ /dev/null @@ -1,125 +0,0 @@ -variable "create" { - description = "Controls if resources should be created (affects all resources)" - type = bool - default = true -} - -variable "tags" { - description = "A map of tags to add to all resources" - type = map(string) - default = {} -} - -################################################################################ -# IAM Role -################################################################################ - -variable "name" { - description = "Name to use on IAM role created" - type = string - default = null -} - -variable "use_name_prefix" { - description = "Determines whether the IAM role name (`name`) is used as a prefix" - type = bool - default = true -} - -variable "path" { - description = "Path of IAM role" - type = string - default = "/" -} - -variable "description" { - description = "Description of the role" - type = string - default = null -} - -variable "max_session_duration" { - description = "Maximum session duration (in seconds) that you want to set for the specified role. If you do not specify a value for this setting, the default maximum of one hour is applied. This setting can have a value from 1 hour to 12 hours" - type = number - default = null -} - -variable "permissions_boundary" { - description = "ARN of the policy that is used to set the permissions boundary for the IAM role" - type = string - default = null -} - -variable "assume_role_policy_statements" { - description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage" - type = map(object({ - sid = optional(string) - actions = optional(list(string)) - not_actions = optional(list(string)) - effect = optional(string, "Allow") - resources = optional(list(string)) - not_resources = optional(list(string)) - principals = optional(list(object({ - type = string - identifiers = list(string) - }))) - not_principals = optional(list(object({ - type = string - identifiers = list(string) - }))) - condition = optional(list(object({ - test = string - variable = string - values = list(string) - }))) - })) - default = null -} - -variable "policies" { - description = "Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format" - type = map(string) - default = {} -} - -variable "oidc_account_id" { - description = "An overriding AWS account ID where the OIDC provider lives; leave empty to use the current account ID for the AWS provider" - type = string - default = null -} - -variable "oidc_provider_urls" { - description = "List of URLs of the OIDC Providers" - type = list(string) - default = [] -} - -variable "oidc_subjects" { - description = "The fully qualified OIDC subjects to be added to the role policy" - type = set(string) - default = [] -} - -variable "oidc_wildcard_subjects" { - description = "The OIDC subject using wildcards to be added to the role policy" - type = set(string) - default = [] -} - -variable "oidc_audiences" { - description = "The audience to be added to the role policy. Set to sts.amazonaws.com for cross-account assumable role. Leave empty otherwise." - type = set(string) - default = [] -} - -variable "enable_github_oidc" { - description = "Enable GitHub OIDC provider trust for the role" - type = bool - default = false -} - -variable "enable_bitbucket_oidc" { - description = "Enable Bitbucket OIDC provider trust for the role" - type = bool - default = false -} diff --git a/modules/iam-role-oidc/versions.tf b/modules/iam-role-oidc/versions.tf deleted file mode 100644 index db13b0a8..00000000 --- a/modules/iam-role-oidc/versions.tf +++ /dev/null @@ -1,10 +0,0 @@ -terraform { - required_version = ">= 1.5.7" - - required_providers { - aws = { - source = "hashicorp/aws" - version = ">= 6.0" - } - } -} diff --git a/modules/iam-role-saml/README.md b/modules/iam-role-saml/README.md deleted file mode 100644 index c2072acd..00000000 --- a/modules/iam-role-saml/README.md +++ /dev/null @@ -1,86 +0,0 @@ -# AWS IAM SAML Role Terraform Module - -Creates single IAM role which can be assumed by trusted resources using SAML federation. - -[Creating IAM SAML Identity Providers](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_saml.html) -[Enabling SAML 2.0 Federated Users to Access the AWS Management Console](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_enable-console-saml.html) - -Creates an IAM role that trusts a SAML provider. Useful for trusting external identity providers such as Okta, OneLogin, etc. - -## Usage - -```hcl -module "iam_role_saml" { - source = "terraform-aws-modules/iam/aws//modules/iam-role-saml" - - name = "example" - - saml_provider_ids = ["arn:aws:iam::235367859851:saml-provider/idp_saml"] - - policies = { - ReadOnlyAccess = "arn:aws:iam::aws:policy/ReadOnlyAccess" - } - - tags = { - Terraform = "true" - Environment = "dev" - } -} -``` - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.5.7 | -| [aws](#requirement\_aws) | >= 6.0 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 6.0 | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | -| [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_iam_policy_document.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [assume\_role\_policy\_statements](#input\_assume\_role\_policy\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage |
map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
variable = string
values = list(string)
})))
}))
| `null` | no | -| [create](#input\_create) | Controls if resources should be created (affects all resources) | `bool` | `true` | no | -| [description](#input\_description) | Description of the role | `string` | `null` | no | -| [max\_session\_duration](#input\_max\_session\_duration) | Maximum session duration (in seconds) that you want to set for the specified role. If you do not specify a value for this setting, the default maximum of one hour is applied. This setting can have a value from 1 hour to 12 hours | `number` | `null` | no | -| [name](#input\_name) | Name to use on IAM role created | `string` | `null` | no | -| [path](#input\_path) | Path of IAM role | `string` | `"/"` | no | -| [permissions\_boundary](#input\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no | -| [policies](#input\_policies) | Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no | -| [saml\_endpoints](#input\_saml\_endpoints) | List of AWS SAML endpoints | `list(string)` |
[
"https://signin.aws.amazon.com/saml"
]
| no | -| [saml\_provider\_ids](#input\_saml\_provider\_ids) | List of SAML provider IDs | `list(string)` | `[]` | no | -| [saml\_trust\_actions](#input\_saml\_trust\_actions) | Additional assume role trust actions for the SAML federated statement | `list(string)` | `[]` | no | -| [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | -| [use\_name\_prefix](#input\_use\_name\_prefix) | Determines whether the IAM role name (`name`) is used as a prefix | `bool` | `true` | no | - -## Outputs - -| Name | Description | -|------|-------------| -| [arn](#output\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | -| [name](#output\_name) | The name of the IAM role | -| [unique\_id](#output\_unique\_id) | Stable and unique string identifying the IAM role | - - -## License - -Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-iam/blob/master/LICENSE). diff --git a/modules/iam-role-saml/main.tf b/modules/iam-role-saml/main.tf deleted file mode 100644 index 93745419..00000000 --- a/modules/iam-role-saml/main.tf +++ /dev/null @@ -1,87 +0,0 @@ -################################################################################ -# IAM Role -################################################################################ - -data "aws_iam_policy_document" "this" { - count = var.create ? 1 : 0 - - statement { - effect = "Allow" - actions = compact(distinct(concat(["sts:AssumeRoleWithSAML"], var.saml_trust_actions))) - - principals { - type = "Federated" - identifiers = var.saml_provider_ids - } - - condition { - test = "StringEquals" - variable = "SAML:aud" - values = var.saml_endpoints - } - } - - dynamic "statement" { - for_each = var.assume_role_policy_statements != null ? var.assume_role_policy_statements : {} - - content { - sid = try(coalesce(statement.value.sid, statement.key)) - actions = statement.value.actions - not_actions = statement.value.not_actions - effect = statement.value.effect - resources = statement.value.resources - not_resources = statement.value.not_resources - - dynamic "principals" { - for_each = statement.value.principals != null ? statement.value.principals : [] - - content { - type = principals.value.type - identifiers = principals.value.identifiers - } - } - - dynamic "not_principals" { - for_each = statement.value.not_principals != null ? statement.value.not_principals : [] - - content { - type = not_principals.value.type - identifiers = not_principals.value.identifiers - } - } - - dynamic "condition" { - for_each = statement.value.condition != null ? statement.value.condition : [] - - content { - test = condition.value.test - values = condition.value.values - variable = condition.value.variable - } - } - } - } -} - -resource "aws_iam_role" "this" { - count = var.create ? 1 : 0 - - name = var.use_name_prefix ? null : var.name - name_prefix = var.use_name_prefix ? "${var.name}-" : null - path = var.path - description = var.description - - assume_role_policy = data.aws_iam_policy_document.this[0].json - max_session_duration = var.max_session_duration - permissions_boundary = var.permissions_boundary - force_detach_policies = true - - tags = var.tags -} - -resource "aws_iam_role_policy_attachment" "this" { - for_each = { for k, v in var.policies : k => v if var.create } - - policy_arn = each.value - role = aws_iam_role.this[0].name -} diff --git a/modules/iam-role-saml/outputs.tf b/modules/iam-role-saml/outputs.tf deleted file mode 100644 index 8410060f..00000000 --- a/modules/iam-role-saml/outputs.tf +++ /dev/null @@ -1,18 +0,0 @@ -################################################################################ -# IAM Role -################################################################################ - -output "name" { - description = "The name of the IAM role" - value = try(aws_iam_role.this[0].name, null) -} - -output "arn" { - description = "The Amazon Resource Name (ARN) specifying the IAM role" - value = try(aws_iam_role.this[0].arn, null) -} - -output "unique_id" { - description = "Stable and unique string identifying the IAM role" - value = try(aws_iam_role.this[0].unique_id, null) -} diff --git a/modules/iam-role-saml/variables.tf b/modules/iam-role-saml/variables.tf deleted file mode 100644 index f26b2185..00000000 --- a/modules/iam-role-saml/variables.tf +++ /dev/null @@ -1,101 +0,0 @@ -variable "create" { - description = "Controls if resources should be created (affects all resources)" - type = bool - default = true -} - -variable "tags" { - description = "A map of tags to add to all resources" - type = map(string) - default = {} -} - -################################################################################ -# IAM Role -################################################################################ - -variable "name" { - description = "Name to use on IAM role created" - type = string - default = null -} - -variable "use_name_prefix" { - description = "Determines whether the IAM role name (`name`) is used as a prefix" - type = bool - default = true -} - -variable "path" { - description = "Path of IAM role" - type = string - default = "/" -} - -variable "description" { - description = "Description of the role" - type = string - default = null -} - -variable "max_session_duration" { - description = "Maximum session duration (in seconds) that you want to set for the specified role. If you do not specify a value for this setting, the default maximum of one hour is applied. This setting can have a value from 1 hour to 12 hours" - type = number - default = null -} - -variable "permissions_boundary" { - description = "ARN of the policy that is used to set the permissions boundary for the IAM role" - type = string - default = null -} - -variable "assume_role_policy_statements" { - description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage" - type = map(object({ - sid = optional(string) - actions = optional(list(string)) - not_actions = optional(list(string)) - effect = optional(string, "Allow") - resources = optional(list(string)) - not_resources = optional(list(string)) - principals = optional(list(object({ - type = string - identifiers = list(string) - }))) - not_principals = optional(list(object({ - type = string - identifiers = list(string) - }))) - condition = optional(list(object({ - test = string - variable = string - values = list(string) - }))) - })) - default = null -} - -variable "policies" { - description = "Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format" - type = map(string) - default = {} -} - -variable "saml_provider_ids" { - description = "List of SAML provider IDs" - type = list(string) - default = [] -} - -variable "saml_endpoints" { - description = "List of AWS SAML endpoints" - type = list(string) - default = ["https://signin.aws.amazon.com/saml"] -} - -variable "saml_trust_actions" { - description = "Additional assume role trust actions for the SAML federated statement" - type = list(string) - default = [] -} diff --git a/modules/iam-role-saml/versions.tf b/modules/iam-role-saml/versions.tf deleted file mode 100644 index db13b0a8..00000000 --- a/modules/iam-role-saml/versions.tf +++ /dev/null @@ -1,10 +0,0 @@ -terraform { - required_version = ">= 1.5.7" - - required_providers { - aws = { - source = "hashicorp/aws" - version = ">= 6.0" - } - } -} diff --git a/modules/iam-role/README.md b/modules/iam-role/README.md index 5c5fca91..0db35625 100644 --- a/modules/iam-role/README.md +++ b/modules/iam-role/README.md @@ -1,9 +1,60 @@ -# AWS IAM Role Terraform Module +# AWS IAM OIDC Role Terraform Module -Creates an IAM role with a trust policy and (optional) IAM instance profile. Useful for service roles such as EC2, ECS, etc., or roles assumed across AWS accounts. +Creates a single IAM role which can be assumed by trusted resources using OpenID connect federation. ## Usage +### [GitHub Free, Pro, & Team](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services) + +The defaults provided by the module are suitable for GitHub Free, Pro, & Team, including use with the official [AWS GitHub action](https://github.com/aws-actions/configure-aws-credentials). + +```hcl +module "iam_oidc_role" { + source = "terraform-aws-modules/iam/aws//modules/iam-oidc-role" + + enable_github_oidc = true + + # This should be updated to suit your organization, repository, references/branches, etc. + oidc_subjects = ["terraform-aws-modules/terraform-aws-iam:*"] + + policies = { + S3ReadOnly = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" + } + + tags = { + Environment = "test" + } +} +``` + +### [GitHub Enterprise Server](https://docs.github.com/en/enterprise-server@3.7/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services) + +For GitHub Enterprise Server, users will need to provide value for the `oidc_audience` and `provider_urls` to suit their `` installation: + +```hcl +module "iam_oidc_role" { + source = "terraform-aws-modules/iam/aws//modules/iam-oidc-role" + + enable_github_oidc = true + + oidc_audience = "https://mygithub.com/" + oidc_provider_urls = ["mygithub.com/_services/token"] + + # This should be updated to suit your organization, repository, references/branches, etc. + oidc_subjects = ["/terraform-aws-iam:*"] + + policies = { + S3ReadOnly = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" + } + + tags = { + Environment = "test" + } +} +``` + +### Something + ```hcl module "iam_role" { source = "terraform-aws-modules/iam/aws//modules/iam-role" @@ -39,6 +90,33 @@ module "iam_role" { } ``` +### SAML 2.0 + +Creates an IAM role that trusts a SAML provider. Useful for trusting external identity providers such as Okta, OneLogin, etc. + +[Creating IAM SAML Identity Providers](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_saml.html) +[Enabling SAML 2.0 Federated Users to Access the AWS Management Console](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_enable-console-saml.html) + +```hcl +module "iam_role_saml" { + source = "terraform-aws-modules/iam/aws//modules/iam-role" + + name = "example" + + enable_saml = true + saml_provider_ids = ["arn:aws:iam::235367859851:saml-provider/idp_saml"] + + policies = { + ReadOnlyAccess = "arn:aws:iam::aws:policy/ReadOnlyAccess" + } + + tags = { + Terraform = "true" + Environment = "dev" + } +} +``` + ## Requirements @@ -64,7 +142,9 @@ No modules. | [aws_iam_instance_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_instance_profile) | resource | | [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | | [aws_iam_policy_document.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | ## Inputs @@ -74,11 +154,23 @@ No modules. | [create](#input\_create) | Controls if resources should be created (affects all resources) | `bool` | `true` | no | | [create\_instance\_profile](#input\_create\_instance\_profile) | Determines whether to create an instance profile | `bool` | `false` | no | | [description](#input\_description) | Description of the role | `string` | `null` | no | +| [enable\_bitbucket\_oidc](#input\_enable\_bitbucket\_oidc) | Enable Bitbucket OIDC provider trust for the role | `bool` | `false` | no | +| [enable\_github\_oidc](#input\_enable\_github\_oidc) | Enable GitHub OIDC provider trust for the role | `bool` | `false` | no | +| [enable\_oidc](#input\_enable\_oidc) | Enable OIDC provider trust for the role | `bool` | `false` | no | +| [enable\_saml](#input\_enable\_saml) | Enable SAML provider trust for the role | `bool` | `false` | no | | [max\_session\_duration](#input\_max\_session\_duration) | Maximum session duration (in seconds) that you want to set for the specified role. If you do not specify a value for this setting, the default maximum of one hour is applied. This setting can have a value from 1 hour to 12 hours | `number` | `null` | no | | [name](#input\_name) | Name to use on IAM role created | `string` | `null` | no | +| [oidc\_account\_id](#input\_oidc\_account\_id) | An overriding AWS account ID where the OIDC provider lives; leave empty to use the current account ID for the AWS provider | `string` | `null` | no | +| [oidc\_audiences](#input\_oidc\_audiences) | The audience to be added to the role policy. Set to sts.amazonaws.com for cross-account assumable role. Leave empty otherwise. | `set(string)` | `[]` | no | +| [oidc\_provider\_urls](#input\_oidc\_provider\_urls) | List of URLs of the OIDC Providers | `list(string)` | `[]` | no | +| [oidc\_subjects](#input\_oidc\_subjects) | The fully qualified OIDC subjects to be added to the role policy | `set(string)` | `[]` | no | +| [oidc\_wildcard\_subjects](#input\_oidc\_wildcard\_subjects) | The OIDC subject using wildcards to be added to the role policy | `set(string)` | `[]` | no | | [path](#input\_path) | Path of IAM role | `string` | `"/"` | no | | [permissions\_boundary](#input\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no | | [policies](#input\_policies) | Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no | +| [saml\_endpoints](#input\_saml\_endpoints) | List of AWS SAML endpoints | `list(string)` |
[
"https://signin.aws.amazon.com/saml"
]
| no | +| [saml\_provider\_ids](#input\_saml\_provider\_ids) | List of SAML provider IDs | `list(string)` | `[]` | no | +| [saml\_trust\_actions](#input\_saml\_trust\_actions) | Additional assume role trust actions for the SAML federated statement | `list(string)` | `[]` | no | | [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | | [use\_name\_prefix](#input\_use\_name\_prefix) | Determines whether the IAM role name (`name`) is used as a prefix | `bool` | `true` | no | @@ -87,10 +179,6 @@ No modules. | Name | Description | |------|-------------| | [arn](#output\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | -| [instance\_profile\_arn](#output\_instance\_profile\_arn) | ARN assigned by AWS to the instance profile | -| [instance\_profile\_id](#output\_instance\_profile\_id) | Instance profile's ID | -| [instance\_profile\_name](#output\_instance\_profile\_name) | Name of IAM instance profile | -| [instance\_profile\_unique\_id](#output\_instance\_profile\_unique\_id) | Stable and unique string identifying the IAM instance profile | | [name](#output\_name) | The name of the IAM role | | [unique\_id](#output\_unique\_id) | Stable and unique string identifying the IAM role | diff --git a/modules/iam-role/main.tf b/modules/iam-role/main.tf index eb4aa854..3873046f 100644 --- a/modules/iam-role/main.tf +++ b/modules/iam-role/main.tf @@ -1,10 +1,161 @@ +data "aws_caller_identity" "current" { + count = var.create ? 1 : 0 +} +data "aws_partition" "current" { + count = var.create ? 1 : 0 +} + +locals { + account_id = try(data.aws_caller_identity.current[0].account_id, "") + partition = try(data.aws_partition.current[0].partition, "") + + oidc_providers = [for url in var.oidc_provider_urls : replace(url, "https://", "")] + github_provider = coalesce(one(local.oidc_providers), "token.actions.githubusercontent.com") + bitbucket_provider = one(local.oidc_providers) +} + ################################################################################ # IAM Role ################################################################################ data "aws_iam_policy_document" "this" { - count = var.create && var.assume_role_policy_statements != null ? 1 : 0 + count = var.create ? 1 : 0 + + # Generic OIDC + dynamic "statement" { + for_each = var.enable_oidc ? local.oidc_providers : [] + + content { + effect = "Allow" + actions = ["sts:AssumeRoleWithWebIdentity"] + + principals { + type = "Federated" + + identifiers = ["arn:${local.partition}:iam::${coalesce(var.oidc_account_id, local.account_id)}:oidc-provider/${statement.value}"] + } + + dynamic "condition" { + for_each = length(var.oidc_subjects) > 0 ? local.oidc_providers : [] + + content { + test = "StringEquals" + variable = "${statement.value}:sub" + values = var.oidc_subjects + } + } + + dynamic "condition" { + for_each = length(var.oidc_wildcard_subjects) > 0 ? local.oidc_providers : [] + + content { + test = "StringLike" + variable = "${statement.value}:sub" + values = var.oidc_wildcard_subjects + } + } + + dynamic "condition" { + for_each = length(var.oidc_audiences) > 0 ? local.oidc_providers : [] + + content { + test = "StringLike" + variable = "${statement.value}:aud" + values = var.oidc_audiences + } + } + } + } + + # GitHub OIDC + dynamic "statement" { + for_each = var.enable_github_oidc ? [1] : [] + + content { + sid = "GithubOidcAuth" + actions = [ + "sts:TagSession", + "sts:AssumeRoleWithWebIdentity" + ] + + principals { + type = "Federated" + identifiers = ["arn:${local.partition}:iam::${local.account_id}:oidc-provider/${local.github_provider}"] + } + + condition { + test = "ForAllValues:StringEquals" + variable = "token.actions.githubusercontent.com:iss" + values = ["http://token.actions.githubusercontent.com"] + } + + condition { + test = "ForAllValues:StringEquals" + variable = "${local.github_provider}:aud" + values = coalescelist(var.oidc_audiences, ["sts.amazonaws.com"]) + } + + condition { + test = "StringLike" + variable = "${local.github_provider}:sub" + # Strip `repo:` to normalize for cases where users may prepend it + values = [for subject in var.oidc_subjects : "repo:${trimprefix(subject, "repo:")}"] + } + } + } + + # Bitbucket OIDC + dynamic "statement" { + for_each = var.enable_bitbucket_oidc ? [1] : [] + + content { + sid = "BitbucketOidcAuth" + actions = [ + "sts:TagSession", + "sts:AssumeRoleWithWebIdentity" + ] + + principals { + type = "Federated" + identifiers = ["arn:${local.partition}:iam::${local.account_id}:oidc-provider/${local.bitbucket_provider}"] + } + + condition { + test = "ForAllValues:StringEquals" + variable = "${local.bitbucket_provider}:aud" + values = coalescelist(var.oidc_audiences, ["sts.amazonaws.com"]) + } + + condition { + test = "StringLike" + variable = "${local.bitbucket_provider}:sub" + values = var.oidc_subjects + } + } + } + + # SAML + dynamic "statement" { + for_each = var.enable_saml ? [1] : [] + + content { + effect = "Allow" + actions = compact(distinct(concat(["sts:AssumeRoleWithSAML"], var.saml_trust_actions))) + + principals { + type = "Federated" + identifiers = var.saml_provider_ids + } + + condition { + test = "StringEquals" + variable = "SAML:aud" + values = var.saml_endpoints + } + } + } + # Generic statements dynamic "statement" { for_each = var.assume_role_policy_statements != null ? var.assume_role_policy_statements : {} @@ -55,7 +206,7 @@ resource "aws_iam_role" "this" { path = var.path description = var.description - assume_role_policy = var.assume_role_policy_statements != null ? data.aws_iam_policy_document.this[0].json : null + assume_role_policy = data.aws_iam_policy_document.this[0].json max_session_duration = var.max_session_duration permissions_boundary = var.permissions_boundary force_detach_policies = true diff --git a/modules/iam-role/outputs.tf b/modules/iam-role/outputs.tf index 3933df07..8410060f 100644 --- a/modules/iam-role/outputs.tf +++ b/modules/iam-role/outputs.tf @@ -16,27 +16,3 @@ output "unique_id" { description = "Stable and unique string identifying the IAM role" value = try(aws_iam_role.this[0].unique_id, null) } - -################################################################################ -# IAM Instance Profile -################################################################################ - -output "instance_profile_arn" { - description = "ARN assigned by AWS to the instance profile" - value = try(aws_iam_instance_profile.this[0].arn, null) -} - -output "instance_profile_id" { - description = "Instance profile's ID" - value = try(aws_iam_instance_profile.this[0].id, null) -} - -output "instance_profile_name" { - description = "Name of IAM instance profile" - value = try(aws_iam_instance_profile.this[0].name, null) -} - -output "instance_profile_unique_id" { - description = "Stable and unique string identifying the IAM instance profile" - value = try(aws_iam_instance_profile.this[0].unique_id, null) -} diff --git a/modules/iam-role/variables.tf b/modules/iam-role/variables.tf index 42954371..ba8eebc3 100644 --- a/modules/iam-role/variables.tf +++ b/modules/iam-role/variables.tf @@ -82,6 +82,78 @@ variable "policies" { default = {} } +variable "enable_oidc" { + description = "Enable OIDC provider trust for the role" + type = bool + default = false +} + +variable "oidc_account_id" { + description = "An overriding AWS account ID where the OIDC provider lives; leave empty to use the current account ID for the AWS provider" + type = string + default = null +} + +variable "oidc_provider_urls" { + description = "List of URLs of the OIDC Providers" + type = list(string) + default = [] +} + +variable "oidc_subjects" { + description = "The fully qualified OIDC subjects to be added to the role policy" + type = set(string) + default = [] +} + +variable "oidc_wildcard_subjects" { + description = "The OIDC subject using wildcards to be added to the role policy" + type = set(string) + default = [] +} + +variable "oidc_audiences" { + description = "The audience to be added to the role policy. Set to sts.amazonaws.com for cross-account assumable role. Leave empty otherwise." + type = set(string) + default = [] +} + +variable "enable_github_oidc" { + description = "Enable GitHub OIDC provider trust for the role" + type = bool + default = false +} + +variable "enable_bitbucket_oidc" { + description = "Enable Bitbucket OIDC provider trust for the role" + type = bool + default = false +} + +variable "enable_saml" { + description = "Enable SAML provider trust for the role" + type = bool + default = false +} + +variable "saml_provider_ids" { + description = "List of SAML provider IDs" + type = list(string) + default = [] +} + +variable "saml_endpoints" { + description = "List of AWS SAML endpoints" + type = list(string) + default = ["https://signin.aws.amazon.com/saml"] +} + +variable "saml_trust_actions" { + description = "Additional assume role trust actions for the SAML federated statement" + type = list(string) + default = [] +} + ################################################################################ # IAM Instance Profile ################################################################################ diff --git a/wrappers/iam-role-oidc/README.md b/wrappers/iam-role-oidc/README.md deleted file mode 100644 index dd8b19ad..00000000 --- a/wrappers/iam-role-oidc/README.md +++ /dev/null @@ -1,100 +0,0 @@ -# Wrapper for module: `modules/iam-role-oidc` - -The configuration in this directory contains an implementation of a single module wrapper pattern, which allows managing several copies of a module in places where using the native Terraform 0.13+ `for_each` feature is not feasible (e.g., with Terragrunt). - -You may want to use a single Terragrunt configuration file to manage multiple resources without duplicating `terragrunt.hcl` files for each copy of the same module. - -This wrapper does not implement any extra functionality. - -## Usage with Terragrunt - -`terragrunt.hcl`: - -```hcl -terraform { - source = "tfr:///terraform-aws-modules/iam/aws//wrappers/iam-role-oidc" - # Alternative source: - # source = "git::git@github.com:terraform-aws-modules/terraform-aws-iam.git//wrappers/iam-role-oidc?ref=master" -} - -inputs = { - defaults = { # Default values - create = true - tags = { - Terraform = "true" - Environment = "dev" - } - } - - items = { - my-item = { - # omitted... can be any argument supported by the module - } - my-second-item = { - # omitted... can be any argument supported by the module - } - # omitted... - } -} -``` - -## Usage with Terraform - -```hcl -module "wrapper" { - source = "terraform-aws-modules/iam/aws//wrappers/iam-role-oidc" - - defaults = { # Default values - create = true - tags = { - Terraform = "true" - Environment = "dev" - } - } - - items = { - my-item = { - # omitted... can be any argument supported by the module - } - my-second-item = { - # omitted... can be any argument supported by the module - } - # omitted... - } -} -``` - -## Example: Manage multiple S3 buckets in one Terragrunt layer - -`eu-west-1/s3-buckets/terragrunt.hcl`: - -```hcl -terraform { - source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers" - # Alternative source: - # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//wrappers?ref=master" -} - -inputs = { - defaults = { - force_destroy = true - - attach_elb_log_delivery_policy = true - attach_lb_log_delivery_policy = true - attach_deny_insecure_transport_policy = true - attach_require_latest_tls_policy = true - } - - items = { - bucket1 = { - bucket = "my-random-bucket-1" - } - bucket2 = { - bucket = "my-random-bucket-2" - tags = { - Secure = "probably" - } - } - } -} -``` diff --git a/wrappers/iam-role-oidc/main.tf b/wrappers/iam-role-oidc/main.tf deleted file mode 100644 index 174996fb..00000000 --- a/wrappers/iam-role-oidc/main.tf +++ /dev/null @@ -1,23 +0,0 @@ -module "wrapper" { - source = "../../modules/iam-role-oidc" - - for_each = var.items - - assume_role_policy_statements = try(each.value.assume_role_policy_statements, var.defaults.assume_role_policy_statements, null) - create = try(each.value.create, var.defaults.create, true) - description = try(each.value.description, var.defaults.description, null) - enable_bitbucket_oidc = try(each.value.enable_bitbucket_oidc, var.defaults.enable_bitbucket_oidc, false) - enable_github_oidc = try(each.value.enable_github_oidc, var.defaults.enable_github_oidc, false) - max_session_duration = try(each.value.max_session_duration, var.defaults.max_session_duration, null) - name = try(each.value.name, var.defaults.name, null) - oidc_account_id = try(each.value.oidc_account_id, var.defaults.oidc_account_id, null) - oidc_audiences = try(each.value.oidc_audiences, var.defaults.oidc_audiences, []) - oidc_provider_urls = try(each.value.oidc_provider_urls, var.defaults.oidc_provider_urls, []) - oidc_subjects = try(each.value.oidc_subjects, var.defaults.oidc_subjects, []) - oidc_wildcard_subjects = try(each.value.oidc_wildcard_subjects, var.defaults.oidc_wildcard_subjects, []) - path = try(each.value.path, var.defaults.path, "/") - permissions_boundary = try(each.value.permissions_boundary, var.defaults.permissions_boundary, null) - policies = try(each.value.policies, var.defaults.policies, {}) - tags = try(each.value.tags, var.defaults.tags, {}) - use_name_prefix = try(each.value.use_name_prefix, var.defaults.use_name_prefix, true) -} diff --git a/wrappers/iam-role-oidc/outputs.tf b/wrappers/iam-role-oidc/outputs.tf deleted file mode 100644 index ec6da5f4..00000000 --- a/wrappers/iam-role-oidc/outputs.tf +++ /dev/null @@ -1,5 +0,0 @@ -output "wrapper" { - description = "Map of outputs of a wrapper." - value = module.wrapper - # sensitive = false # No sensitive module output found -} diff --git a/wrappers/iam-role-oidc/variables.tf b/wrappers/iam-role-oidc/variables.tf deleted file mode 100644 index a6ea0962..00000000 --- a/wrappers/iam-role-oidc/variables.tf +++ /dev/null @@ -1,11 +0,0 @@ -variable "defaults" { - description = "Map of default values which will be used for each item." - type = any - default = {} -} - -variable "items" { - description = "Maps of items to create a wrapper from. Values are passed through to the module." - type = any - default = {} -} diff --git a/wrappers/iam-role-oidc/versions.tf b/wrappers/iam-role-oidc/versions.tf deleted file mode 100644 index db13b0a8..00000000 --- a/wrappers/iam-role-oidc/versions.tf +++ /dev/null @@ -1,10 +0,0 @@ -terraform { - required_version = ">= 1.5.7" - - required_providers { - aws = { - source = "hashicorp/aws" - version = ">= 6.0" - } - } -} diff --git a/wrappers/iam-role-saml/README.md b/wrappers/iam-role-saml/README.md deleted file mode 100644 index 70f3afa4..00000000 --- a/wrappers/iam-role-saml/README.md +++ /dev/null @@ -1,100 +0,0 @@ -# Wrapper for module: `modules/iam-role-saml` - -The configuration in this directory contains an implementation of a single module wrapper pattern, which allows managing several copies of a module in places where using the native Terraform 0.13+ `for_each` feature is not feasible (e.g., with Terragrunt). - -You may want to use a single Terragrunt configuration file to manage multiple resources without duplicating `terragrunt.hcl` files for each copy of the same module. - -This wrapper does not implement any extra functionality. - -## Usage with Terragrunt - -`terragrunt.hcl`: - -```hcl -terraform { - source = "tfr:///terraform-aws-modules/iam/aws//wrappers/iam-role-saml" - # Alternative source: - # source = "git::git@github.com:terraform-aws-modules/terraform-aws-iam.git//wrappers/iam-role-saml?ref=master" -} - -inputs = { - defaults = { # Default values - create = true - tags = { - Terraform = "true" - Environment = "dev" - } - } - - items = { - my-item = { - # omitted... can be any argument supported by the module - } - my-second-item = { - # omitted... can be any argument supported by the module - } - # omitted... - } -} -``` - -## Usage with Terraform - -```hcl -module "wrapper" { - source = "terraform-aws-modules/iam/aws//wrappers/iam-role-saml" - - defaults = { # Default values - create = true - tags = { - Terraform = "true" - Environment = "dev" - } - } - - items = { - my-item = { - # omitted... can be any argument supported by the module - } - my-second-item = { - # omitted... can be any argument supported by the module - } - # omitted... - } -} -``` - -## Example: Manage multiple S3 buckets in one Terragrunt layer - -`eu-west-1/s3-buckets/terragrunt.hcl`: - -```hcl -terraform { - source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers" - # Alternative source: - # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//wrappers?ref=master" -} - -inputs = { - defaults = { - force_destroy = true - - attach_elb_log_delivery_policy = true - attach_lb_log_delivery_policy = true - attach_deny_insecure_transport_policy = true - attach_require_latest_tls_policy = true - } - - items = { - bucket1 = { - bucket = "my-random-bucket-1" - } - bucket2 = { - bucket = "my-random-bucket-2" - tags = { - Secure = "probably" - } - } - } -} -``` diff --git a/wrappers/iam-role-saml/main.tf b/wrappers/iam-role-saml/main.tf deleted file mode 100644 index 28ef496f..00000000 --- a/wrappers/iam-role-saml/main.tf +++ /dev/null @@ -1,19 +0,0 @@ -module "wrapper" { - source = "../../modules/iam-role-saml" - - for_each = var.items - - assume_role_policy_statements = try(each.value.assume_role_policy_statements, var.defaults.assume_role_policy_statements, null) - create = try(each.value.create, var.defaults.create, true) - description = try(each.value.description, var.defaults.description, null) - max_session_duration = try(each.value.max_session_duration, var.defaults.max_session_duration, null) - name = try(each.value.name, var.defaults.name, null) - path = try(each.value.path, var.defaults.path, "/") - permissions_boundary = try(each.value.permissions_boundary, var.defaults.permissions_boundary, null) - policies = try(each.value.policies, var.defaults.policies, {}) - saml_endpoints = try(each.value.saml_endpoints, var.defaults.saml_endpoints, ["https://signin.aws.amazon.com/saml"]) - saml_provider_ids = try(each.value.saml_provider_ids, var.defaults.saml_provider_ids, []) - saml_trust_actions = try(each.value.saml_trust_actions, var.defaults.saml_trust_actions, []) - tags = try(each.value.tags, var.defaults.tags, {}) - use_name_prefix = try(each.value.use_name_prefix, var.defaults.use_name_prefix, true) -} diff --git a/wrappers/iam-role-saml/outputs.tf b/wrappers/iam-role-saml/outputs.tf deleted file mode 100644 index ec6da5f4..00000000 --- a/wrappers/iam-role-saml/outputs.tf +++ /dev/null @@ -1,5 +0,0 @@ -output "wrapper" { - description = "Map of outputs of a wrapper." - value = module.wrapper - # sensitive = false # No sensitive module output found -} diff --git a/wrappers/iam-role-saml/variables.tf b/wrappers/iam-role-saml/variables.tf deleted file mode 100644 index a6ea0962..00000000 --- a/wrappers/iam-role-saml/variables.tf +++ /dev/null @@ -1,11 +0,0 @@ -variable "defaults" { - description = "Map of default values which will be used for each item." - type = any - default = {} -} - -variable "items" { - description = "Maps of items to create a wrapper from. Values are passed through to the module." - type = any - default = {} -} diff --git a/wrappers/iam-role-saml/versions.tf b/wrappers/iam-role-saml/versions.tf deleted file mode 100644 index db13b0a8..00000000 --- a/wrappers/iam-role-saml/versions.tf +++ /dev/null @@ -1,10 +0,0 @@ -terraform { - required_version = ">= 1.5.7" - - required_providers { - aws = { - source = "hashicorp/aws" - version = ">= 6.0" - } - } -} diff --git a/wrappers/iam-role/main.tf b/wrappers/iam-role/main.tf index 41fa32f6..e42365f7 100644 --- a/wrappers/iam-role/main.tf +++ b/wrappers/iam-role/main.tf @@ -7,11 +7,23 @@ module "wrapper" { create = try(each.value.create, var.defaults.create, true) create_instance_profile = try(each.value.create_instance_profile, var.defaults.create_instance_profile, false) description = try(each.value.description, var.defaults.description, null) + enable_bitbucket_oidc = try(each.value.enable_bitbucket_oidc, var.defaults.enable_bitbucket_oidc, false) + enable_github_oidc = try(each.value.enable_github_oidc, var.defaults.enable_github_oidc, false) + enable_oidc = try(each.value.enable_oidc, var.defaults.enable_oidc, false) + enable_saml = try(each.value.enable_saml, var.defaults.enable_saml, false) max_session_duration = try(each.value.max_session_duration, var.defaults.max_session_duration, null) name = try(each.value.name, var.defaults.name, null) + oidc_account_id = try(each.value.oidc_account_id, var.defaults.oidc_account_id, null) + oidc_audiences = try(each.value.oidc_audiences, var.defaults.oidc_audiences, []) + oidc_provider_urls = try(each.value.oidc_provider_urls, var.defaults.oidc_provider_urls, []) + oidc_subjects = try(each.value.oidc_subjects, var.defaults.oidc_subjects, []) + oidc_wildcard_subjects = try(each.value.oidc_wildcard_subjects, var.defaults.oidc_wildcard_subjects, []) path = try(each.value.path, var.defaults.path, "/") permissions_boundary = try(each.value.permissions_boundary, var.defaults.permissions_boundary, null) policies = try(each.value.policies, var.defaults.policies, {}) + saml_endpoints = try(each.value.saml_endpoints, var.defaults.saml_endpoints, ["https://signin.aws.amazon.com/saml"]) + saml_provider_ids = try(each.value.saml_provider_ids, var.defaults.saml_provider_ids, []) + saml_trust_actions = try(each.value.saml_trust_actions, var.defaults.saml_trust_actions, []) tags = try(each.value.tags, var.defaults.tags, {}) use_name_prefix = try(each.value.use_name_prefix, var.defaults.use_name_prefix, true) } From a4ef1f577d47d20c0a1a0c695913a9346ff06ace Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Fri, 8 Aug 2025 13:40:38 -0500 Subject: [PATCH 07/21] fix: Update IRSA module to align with upstream changes --- .../iam-role-for-service-accounts/README.md | 6 +- .../iam-role-for-service-accounts/main.tf | 82 +++++++++---------- examples/iam-role/README.md | 21 +++-- examples/iam-role/outputs.tf | 69 ++++++++++++---- modules/iam-role/README.md | 4 + modules/iam-role/outputs.tf | 24 ++++++ 6 files changed, 140 insertions(+), 66 deletions(-) diff --git a/examples/iam-role-for-service-accounts/README.md b/examples/iam-role-for-service-accounts/README.md index 41364a64..9f5dccbc 100644 --- a/examples/iam-role-for-service-accounts/README.md +++ b/examples/iam-role-for-service-accounts/README.md @@ -33,9 +33,8 @@ Run `terraform destroy` when you don't need these resources. | Name | Source | Version | |------|--------|---------| | [amazon\_managed\_service\_prometheus\_irsa](#module\_amazon\_managed\_service\_prometheus\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | -| [appmesh\_controller\_irsa](#module\_appmesh\_controller\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | -| [appmesh\_envoy\_proxy\_irsa](#module\_appmesh\_envoy\_proxy\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | | [cert\_manager\_irsa](#module\_cert\_manager\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | +| [cloudwatch\_observability\_irsa](#module\_cloudwatch\_observability\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | | [cluster\_autoscaler\_irsa](#module\_cluster\_autoscaler\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | | [disabled](#module\_disabled) | ../../modules/iam-role-for-service-accounts | n/a | | [ebs\_csi\_irsa](#module\_ebs\_csi\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | @@ -45,12 +44,13 @@ Run `terraform destroy` when you don't need these resources. | [external\_dns\_irsa](#module\_external\_dns\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | | [external\_secrets\_irsa](#module\_external\_secrets\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | | [fsx\_lustre\_csi\_irsa](#module\_fsx\_lustre\_csi\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | +| [fsx\_openzfs\_csi\_irsa](#module\_fsx\_openzfs\_csi\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | | [irsa](#module\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | | [irsa\_v2\_custom\_policy](#module\_irsa\_v2\_custom\_policy) | ../../modules/iam-role-for-service-accounts | n/a | | [irsa\_v2\_empty](#module\_irsa\_v2\_empty) | ../../modules/iam-role-for-service-accounts | n/a | -| [karpenter\_irsa](#module\_karpenter\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | | [load\_balancer\_controller\_irsa](#module\_load\_balancer\_controller\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | | [load\_balancer\_controller\_targetgroup\_binding\_only\_irsa](#module\_load\_balancer\_controller\_targetgroup\_binding\_only\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | +| [mountpoint\_s3\_csi\_irsa](#module\_mountpoint\_s3\_csi\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | | [node\_termination\_handler\_irsa](#module\_node\_termination\_handler\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | | [velero\_irsa](#module\_velero\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | | [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 6.0 | diff --git a/examples/iam-role-for-service-accounts/main.tf b/examples/iam-role-for-service-accounts/main.tf index 1c65d6fa..0c634e64 100644 --- a/examples/iam-role-for-service-accounts/main.tf +++ b/examples/iam-role-for-service-accounts/main.tf @@ -163,6 +163,25 @@ module "efs_csi_irsa" { tags = local.tags } +module "mountpoint_s3_csi_irsa" { + source = "../../modules/iam-role-for-service-accounts" + + name = "mountpoint-s3-csi" + + attach_mountpoint_s3_csi_policy = true + mountpoint_s3_csi_bucket_arns = ["arn:aws:s3:::mountpoint-s3-csi-bucket"] + mountpoint_s3_csi_path_arns = ["arn:aws:s3:::mountpoint-s3-csi-bucket/example/*"] + + oidc_providers = { + ex = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["kube-system:s3-csi-driver-sa"] + } + } + + tags = local.tags +} + module "external_dns_irsa" { source = "../../modules/iam-role-for-service-accounts" @@ -217,19 +236,17 @@ module "fsx_lustre_csi_irsa" { tags = local.tags } -module "karpenter_irsa" { +module "fsx_openzfs_csi_irsa" { source = "../../modules/iam-role-for-service-accounts" - name = "karpenter" + name = "fsx-openzfs-csi" - attach_karpenter_policy = true - karpenter_cluster_name = module.eks.cluster_name - karpenter_node_iam_role_arns = [module.eks.eks_managed_node_groups["default"].iam_role_arn] + attach_fsx_openzfs_csi_policy = true oidc_providers = { - this = { + ex = { provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["karpenter:karpenter"] + namespace_service_accounts = ["kube-system:fsx-openzfs-csi-controller-sa"] } } @@ -270,40 +287,6 @@ module "load_balancer_controller_targetgroup_binding_only_irsa" { tags = local.tags } -module "appmesh_controller_irsa" { - source = "../../modules/iam-role-for-service-accounts" - - name = "appmesh-controller" - - attach_appmesh_controller_policy = true - - oidc_providers = { - this = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["appmesh-system:appmesh-controller"] - } - } - - tags = local.tags -} - -module "appmesh_envoy_proxy_irsa" { - source = "../../modules/iam-role-for-service-accounts" - - name = "appmesh-envoy-proxy" - - attach_appmesh_envoy_proxy_policy = true - - oidc_providers = { - this = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["appmesh-system:appmesh-envoy-proxy"] - } - } - - tags = local.tags -} - module "amazon_managed_service_prometheus_irsa" { source = "../../modules/iam-role-for-service-accounts" @@ -392,6 +375,23 @@ module "vpc_cni_ipv6_irsa" { tags = local.tags } +module "cloudwatch_observability_irsa" { + source = "../../modules/iam-role-for-service-accounts" + + name = "cloudwatch-observability" + + attach_cloudwatch_observability_policy = true + + oidc_providers = { + ex = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["amazon-cloudwatch:cloudwatch-agent"] + } + } + + tags = local.tags +} + ################################################################################ # Supporting Resources ################################################################################ diff --git a/examples/iam-role/README.md b/examples/iam-role/README.md index f84cf818..e68fe3f5 100644 --- a/examples/iam-role/README.md +++ b/examples/iam-role/README.md @@ -54,13 +54,13 @@ No inputs. | Name | Description | |------|-------------| -| [condition\_iam\_instance\_profile\_arn](#output\_condition\_iam\_instance\_profile\_arn) | ARN assigned by AWS to the instance profile | -| [condition\_iam\_instance\_profile\_id](#output\_condition\_iam\_instance\_profile\_id) | Instance profile's ID | -| [condition\_iam\_instance\_profile\_name](#output\_condition\_iam\_instance\_profile\_name) | Name of IAM instance profile | -| [condition\_iam\_instance\_profile\_unique\_id](#output\_condition\_iam\_instance\_profile\_unique\_id) | Stable and unique string identifying the IAM instance profile | -| [condition\_iam\_role\_arn](#output\_condition\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | -| [condition\_iam\_role\_name](#output\_condition\_iam\_role\_name) | The name of the IAM role | -| [condition\_iam\_role\_unique\_id](#output\_condition\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | +| [github\_oidc\_iam\_instance\_profile\_arn](#output\_github\_oidc\_iam\_instance\_profile\_arn) | ARN assigned by AWS to the instance profile | +| [github\_oidc\_iam\_instance\_profile\_id](#output\_github\_oidc\_iam\_instance\_profile\_id) | Instance profile's ID | +| [github\_oidc\_iam\_instance\_profile\_name](#output\_github\_oidc\_iam\_instance\_profile\_name) | Name of IAM instance profile | +| [github\_oidc\_iam\_instance\_profile\_unique\_id](#output\_github\_oidc\_iam\_instance\_profile\_unique\_id) | Stable and unique string identifying the IAM instance profile | +| [github\_oidc\_iam\_role\_arn](#output\_github\_oidc\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | +| [github\_oidc\_iam\_role\_name](#output\_github\_oidc\_iam\_role\_name) | The name of the IAM role | +| [github\_oidc\_iam\_role\_unique\_id](#output\_github\_oidc\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | | [instance\_profile\_iam\_instance\_profile\_arn](#output\_instance\_profile\_iam\_instance\_profile\_arn) | ARN assigned by AWS to the instance profile | | [instance\_profile\_iam\_instance\_profile\_id](#output\_instance\_profile\_iam\_instance\_profile\_id) | Instance profile's ID | | [instance\_profile\_iam\_instance\_profile\_name](#output\_instance\_profile\_iam\_instance\_profile\_name) | Name of IAM instance profile | @@ -68,4 +68,11 @@ No inputs. | [instance\_profile\_iam\_role\_arn](#output\_instance\_profile\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | | [instance\_profile\_iam\_role\_name](#output\_instance\_profile\_iam\_role\_name) | The name of the IAM role | | [instance\_profile\_iam\_role\_unique\_id](#output\_instance\_profile\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | +| [saml\_iam\_instance\_profile\_arn](#output\_saml\_iam\_instance\_profile\_arn) | ARN assigned by AWS to the instance profile | +| [saml\_iam\_instance\_profile\_id](#output\_saml\_iam\_instance\_profile\_id) | Instance profile's ID | +| [saml\_iam\_instance\_profile\_name](#output\_saml\_iam\_instance\_profile\_name) | Name of IAM instance profile | +| [saml\_iam\_instance\_profile\_unique\_id](#output\_saml\_iam\_instance\_profile\_unique\_id) | Stable and unique string identifying the IAM instance profile | +| [saml\_iam\_role\_arn](#output\_saml\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | +| [saml\_iam\_role\_name](#output\_saml\_iam\_role\_name) | The name of the IAM role | +| [saml\_iam\_role\_unique\_id](#output\_saml\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | diff --git a/examples/iam-role/outputs.tf b/examples/iam-role/outputs.tf index aaee498a..9240cf4b 100644 --- a/examples/iam-role/outputs.tf +++ b/examples/iam-role/outputs.tf @@ -38,40 +38,79 @@ output "instance_profile_iam_instance_profile_unique_id" { } ################################################################################ -# IAM Role - Condition +# IAM Role - GitHub OIDC ################################################################################ -output "condition_iam_role_name" { +output "github_oidc_iam_role_name" { description = "The name of the IAM role" - value = module.iam_role_condition.name + value = module.iam_role_github_oidc.name } -output "condition_iam_role_arn" { +output "github_oidc_iam_role_arn" { description = "The Amazon Resource Name (ARN) specifying the IAM role" - value = module.iam_role_condition.arn + value = module.iam_role_github_oidc.arn } -output "condition_iam_role_unique_id" { +output "github_oidc_iam_role_unique_id" { description = "Stable and unique string identifying the IAM role" - value = module.iam_role_condition.unique_id + value = module.iam_role_github_oidc.unique_id } -output "condition_iam_instance_profile_arn" { +output "github_oidc_iam_instance_profile_arn" { description = "ARN assigned by AWS to the instance profile" - value = module.iam_role_condition.instance_profile_arn + value = module.iam_role_github_oidc.instance_profile_arn } -output "condition_iam_instance_profile_id" { +output "github_oidc_iam_instance_profile_id" { description = "Instance profile's ID" - value = module.iam_role_condition.instance_profile_id + value = module.iam_role_github_oidc.instance_profile_id } -output "condition_iam_instance_profile_name" { +output "github_oidc_iam_instance_profile_name" { description = "Name of IAM instance profile" - value = module.iam_role_condition.instance_profile_name + value = module.iam_role_github_oidc.instance_profile_name } -output "condition_iam_instance_profile_unique_id" { +output "github_oidc_iam_instance_profile_unique_id" { description = "Stable and unique string identifying the IAM instance profile" - value = module.iam_role_condition.instance_profile_unique_id + value = module.iam_role_github_oidc.instance_profile_unique_id +} + +################################################################################ +# IAM Role - SAML 2.0 +################################################################################ + +output "saml_iam_role_name" { + description = "The name of the IAM role" + value = module.iam_role_saml.name +} + +output "saml_iam_role_arn" { + description = "The Amazon Resource Name (ARN) specifying the IAM role" + value = module.iam_role_saml.arn +} + +output "saml_iam_role_unique_id" { + description = "Stable and unique string identifying the IAM role" + value = module.iam_role_saml.unique_id +} + +output "saml_iam_instance_profile_arn" { + description = "ARN assigned by AWS to the instance profile" + value = module.iam_role_saml.instance_profile_arn +} + +output "saml_iam_instance_profile_id" { + description = "Instance profile's ID" + value = module.iam_role_saml.instance_profile_id +} + +output "saml_iam_instance_profile_name" { + description = "Name of IAM instance profile" + value = module.iam_role_saml.instance_profile_name +} + +output "saml_iam_instance_profile_unique_id" { + description = "Stable and unique string identifying the IAM instance profile" + value = module.iam_role_saml.instance_profile_unique_id } diff --git a/modules/iam-role/README.md b/modules/iam-role/README.md index 0db35625..35976b1b 100644 --- a/modules/iam-role/README.md +++ b/modules/iam-role/README.md @@ -179,6 +179,10 @@ No modules. | Name | Description | |------|-------------| | [arn](#output\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | +| [instance\_profile\_arn](#output\_instance\_profile\_arn) | ARN assigned by AWS to the instance profile | +| [instance\_profile\_id](#output\_instance\_profile\_id) | Instance profile's ID | +| [instance\_profile\_name](#output\_instance\_profile\_name) | Name of IAM instance profile | +| [instance\_profile\_unique\_id](#output\_instance\_profile\_unique\_id) | Stable and unique string identifying the IAM instance profile | | [name](#output\_name) | The name of the IAM role | | [unique\_id](#output\_unique\_id) | Stable and unique string identifying the IAM role | diff --git a/modules/iam-role/outputs.tf b/modules/iam-role/outputs.tf index 8410060f..3933df07 100644 --- a/modules/iam-role/outputs.tf +++ b/modules/iam-role/outputs.tf @@ -16,3 +16,27 @@ output "unique_id" { description = "Stable and unique string identifying the IAM role" value = try(aws_iam_role.this[0].unique_id, null) } + +################################################################################ +# IAM Instance Profile +################################################################################ + +output "instance_profile_arn" { + description = "ARN assigned by AWS to the instance profile" + value = try(aws_iam_instance_profile.this[0].arn, null) +} + +output "instance_profile_id" { + description = "Instance profile's ID" + value = try(aws_iam_instance_profile.this[0].id, null) +} + +output "instance_profile_name" { + description = "Name of IAM instance profile" + value = try(aws_iam_instance_profile.this[0].name, null) +} + +output "instance_profile_unique_id" { + description = "Stable and unique string identifying the IAM instance profile" + value = try(aws_iam_instance_profile.this[0].unique_id, null) +} From 0e979ff42a19ba12f96c42366eeb2cca67d970b8 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Fri, 8 Aug 2025 14:42:48 -0500 Subject: [PATCH 08/21] feat: Remove permission variable defaults, add updates from upstream issues/PRs --- modules/iam-group/main.tf | 19 ++- modules/iam-read-only-policy/main.tf | 2 + modules/iam-role/README.md | 5 + modules/iam-role/main.tf | 184 ++++++++++++++++++++++++--- modules/iam-role/variables.tf | 46 +++++++ wrappers/iam-role/main.tf | 3 + 6 files changed, 233 insertions(+), 26 deletions(-) diff --git a/modules/iam-group/main.tf b/modules/iam-group/main.tf index 60241203..0498f447 100644 --- a/modules/iam-group/main.tf +++ b/modules/iam-group/main.tf @@ -55,8 +55,10 @@ data "aws_iam_policy_document" "this" { content { sid = "ViewAccountInfo" actions = [ + "iam:GetAccountSummary", "iam:GetAccountPasswordPolicy", - "iam:ListVirtualMFADevices" + "iam:ListAccountAliases", + "iam:ListVirtualMFADevices", ] resources = ["*"] } @@ -69,7 +71,9 @@ data "aws_iam_policy_document" "this" { sid = "ManageOwnPasswords" actions = [ "iam:ChangePassword", - "iam:GetUser" + "iam:GetLoginProfile", + "iam:GetUser", + "iam:UpdateLoginProfile", ] resources = local.user_resources } @@ -84,7 +88,11 @@ data "aws_iam_policy_document" "this" { "iam:CreateAccessKey", "iam:DeleteAccessKey", "iam:ListAccessKeys", - "iam:UpdateAccessKey" + "iam:UpdateAccessKey", + "iam:GetAccessKeyLastUsed", + "iam:TagUser", + "iam:ListUserTags", + "iam:UntagUser", ] resources = local.user_resources } @@ -168,14 +176,15 @@ data "aws_iam_policy_document" "this" { content { sid = "DenyAllExceptListedIfNoMFA" not_actions = [ - "iam:ChangePassword", "iam:CreateVirtualMFADevice", "iam:EnableMFADevice", "iam:GetUser", + "iam:GetMFADevice", "iam:ListMFADevices", "iam:ListVirtualMFADevices", "iam:ResyncMFADevice", - "sts:GetSessionToken" + "sts:GetSessionToken", + "iam:ChangePassword", ] resources = ["*"] diff --git a/modules/iam-read-only-policy/main.tf b/modules/iam-read-only-policy/main.tf index 482edac4..f3e879e2 100644 --- a/modules/iam-read-only-policy/main.tf +++ b/modules/iam-read-only-policy/main.tf @@ -28,6 +28,7 @@ data "aws_iam_policy_document" "this" { actions = [ "${statement.value}:List*", "${statement.value}:Get*", + "${statement.value}:BatchGet*", "${statement.value}:Describe*", "${statement.value}:View*", ] @@ -44,6 +45,7 @@ data "aws_iam_policy_document" "this" { actions = [ "${statement.value}:List*", "${statement.value}:Get*", + "${statement.value}:BatchGet*", "${statement.value}:Describe*", "${statement.value}:View*", ] diff --git a/modules/iam-role/README.md b/modules/iam-role/README.md index 35976b1b..59d16c24 100644 --- a/modules/iam-role/README.md +++ b/modules/iam-role/README.md @@ -141,8 +141,10 @@ No modules. |------|------| | [aws_iam_instance_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_instance_profile) | resource | | [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy.inline](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | | [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_iam_policy_document.inline](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | @@ -151,6 +153,7 @@ No modules. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [assume\_role\_policy\_statements](#input\_assume\_role\_policy\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage |
map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
variable = string
values = list(string)
})))
}))
| `null` | no | +| [condition](#input\_condition) | [Condition constraints](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#condition) applied to the trust policy(s) enabled |
list(object({
test = string
variable = string
values = list(string)
}))
| `[]` | no | | [create](#input\_create) | Controls if resources should be created (affects all resources) | `bool` | `true` | no | | [create\_instance\_profile](#input\_create\_instance\_profile) | Determines whether to create an instance profile | `bool` | `false` | no | | [description](#input\_description) | Description of the role | `string` | `null` | no | @@ -158,6 +161,8 @@ No modules. | [enable\_github\_oidc](#input\_enable\_github\_oidc) | Enable GitHub OIDC provider trust for the role | `bool` | `false` | no | | [enable\_oidc](#input\_enable\_oidc) | Enable OIDC provider trust for the role | `bool` | `false` | no | | [enable\_saml](#input\_enable\_saml) | Enable SAML provider trust for the role | `bool` | `false` | no | +| [github\_provider](#input\_github\_provider) | The GitHub OIDC provider URL *without the `https://` prefix | `string` | `"token.actions.githubusercontent.com"` | no | +| [inline\_policy\_statements](#input\_inline\_policy\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for inline policy permissions |
map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
variable = string
values = list(string)
})))
}))
| `null` | no | | [max\_session\_duration](#input\_max\_session\_duration) | Maximum session duration (in seconds) that you want to set for the specified role. If you do not specify a value for this setting, the default maximum of one hour is applied. This setting can have a value from 1 hour to 12 hours | `number` | `null` | no | | [name](#input\_name) | Name to use on IAM role created | `string` | `null` | no | | [oidc\_account\_id](#input\_oidc\_account\_id) | An overriding AWS account ID where the OIDC provider lives; leave empty to use the current account ID for the AWS provider | `string` | `null` | no | diff --git a/modules/iam-role/main.tf b/modules/iam-role/main.tf index 3873046f..1497317d 100644 --- a/modules/iam-role/main.tf +++ b/modules/iam-role/main.tf @@ -10,7 +10,6 @@ locals { partition = try(data.aws_partition.current[0].partition, "") oidc_providers = [for url in var.oidc_provider_urls : replace(url, "https://", "")] - github_provider = coalesce(one(local.oidc_providers), "token.actions.githubusercontent.com") bitbucket_provider = one(local.oidc_providers) } @@ -26,8 +25,11 @@ data "aws_iam_policy_document" "this" { for_each = var.enable_oidc ? local.oidc_providers : [] content { - effect = "Allow" - actions = ["sts:AssumeRoleWithWebIdentity"] + effect = "Allow" + actions = [ + "sts:TagSession", + "sts:AssumeRoleWithWebIdentity", + ] principals { type = "Federated" @@ -59,11 +61,22 @@ data "aws_iam_policy_document" "this" { for_each = length(var.oidc_audiences) > 0 ? local.oidc_providers : [] content { - test = "StringLike" + test = "StringEquals" variable = "${statement.value}:aud" values = var.oidc_audiences } } + + # Generic conditions + dynamic "condition" { + for_each = var.condition + + content { + test = condition.value.test + variable = condition.value.variable + values = condition.value.values + } + } } } @@ -75,31 +88,57 @@ data "aws_iam_policy_document" "this" { sid = "GithubOidcAuth" actions = [ "sts:TagSession", - "sts:AssumeRoleWithWebIdentity" + "sts:AssumeRoleWithWebIdentity", ] principals { type = "Federated" - identifiers = ["arn:${local.partition}:iam::${local.account_id}:oidc-provider/${local.github_provider}"] + identifiers = ["arn:${local.partition}:iam::${local.account_id}:oidc-provider/${var.github_provider}"] } condition { test = "ForAllValues:StringEquals" - variable = "token.actions.githubusercontent.com:iss" - values = ["http://token.actions.githubusercontent.com"] + variable = "${var.github_provider}:iss" + values = ["https://${var.github_provider}"] } condition { test = "ForAllValues:StringEquals" - variable = "${local.github_provider}:aud" + variable = "${var.github_provider}:aud" values = coalescelist(var.oidc_audiences, ["sts.amazonaws.com"]) } - condition { - test = "StringLike" - variable = "${local.github_provider}:sub" - # Strip `repo:` to normalize for cases where users may prepend it - values = [for subject in var.oidc_subjects : "repo:${trimprefix(subject, "repo:")}"] + dynamic "condition" { + for_each = length(var.oidc_subjects) > 0 ? [1] : [] + + content { + test = "StringEquals" + variable = "${var.github_provider}:sub" + # Strip `repo:` to normalize for cases where users may prepend it + values = [for subject in var.oidc_subjects : "repo:${trimprefix(subject, "repo:")}"] + } + } + + dynamic "condition" { + for_each = length(var.oidc_wildcard_subjects) > 0 ? [1] : [] + + content { + test = "StringLike" + variable = "${var.github_provider}:sub" + # Strip `repo:` to normalize for cases where users may prepend it + values = [for subject in var.oidc_wildcard_subjects : "repo:${trimprefix(subject, "repo:")}"] + } + } + + # Generic conditions + dynamic "condition" { + for_each = var.condition + + content { + test = condition.value.test + variable = condition.value.variable + values = condition.value.values + } } } } @@ -112,7 +151,7 @@ data "aws_iam_policy_document" "this" { sid = "BitbucketOidcAuth" actions = [ "sts:TagSession", - "sts:AssumeRoleWithWebIdentity" + "sts:AssumeRoleWithWebIdentity", ] principals { @@ -126,10 +165,35 @@ data "aws_iam_policy_document" "this" { values = coalescelist(var.oidc_audiences, ["sts.amazonaws.com"]) } - condition { - test = "StringLike" - variable = "${local.bitbucket_provider}:sub" - values = var.oidc_subjects + dynamic "condition" { + for_each = length(var.oidc_subjects) > 0 ? [1] : [] + + content { + test = "StringEquals" + variable = "${local.bitbucket_provider}:sub" + values = var.oidc_subjects + } + } + + dynamic "condition" { + for_each = length(var.oidc_wildcard_subjects) > 0 ? [1] : [] + + content { + test = "StringLike" + variable = "${local.bitbucket_provider}:sub" + values = var.oidc_wildcard_subjects + } + } + + # Generic conditions + dynamic "condition" { + for_each = var.condition + + content { + test = condition.value.test + variable = condition.value.variable + values = condition.value.values + } } } } @@ -139,8 +203,13 @@ data "aws_iam_policy_document" "this" { for_each = var.enable_saml ? [1] : [] content { - effect = "Allow" - actions = compact(distinct(concat(["sts:AssumeRoleWithSAML"], var.saml_trust_actions))) + effect = "Allow" + actions = compact(distinct(concat( + [ + "sts:TagSession", + "sts:AssumeRoleWithSAML", + ], + var.saml_trust_actions))) principals { type = "Federated" @@ -152,6 +221,17 @@ data "aws_iam_policy_document" "this" { variable = "SAML:aud" values = var.saml_endpoints } + + # Generic conditions + dynamic "condition" { + for_each = var.condition + + content { + test = condition.value.test + variable = condition.value.variable + values = condition.value.values + } + } } } @@ -221,6 +301,68 @@ resource "aws_iam_role_policy_attachment" "this" { role = aws_iam_role.this[0].name } +################################################################################ +# IAM Role Inline policy +################################################################################ + +locals { + create_iam_role_inline_policy = var.create && length(var.inline_policy_statements) > 0 +} + +data "aws_iam_policy_document" "inline" { + count = local.create_iam_role_inline_policy ? 1 : 0 + + dynamic "statement" { + for_each = var.inline_policy_statements != null ? var.inline_policy_statements : {} + + content { + sid = try(coalesce(statement.value.sid, statement.key)) + actions = statement.value.actions + not_actions = statement.value.not_actions + effect = statement.value.effect + resources = statement.value.resources + not_resources = statement.value.not_resources + + dynamic "principals" { + for_each = statement.value.principals != null ? statement.value.principals : [] + + content { + type = principals.value.type + identifiers = principals.value.identifiers + } + } + + dynamic "not_principals" { + for_each = statement.value.not_principals != null ? statement.value.not_principals : [] + + content { + type = not_principals.value.type + identifiers = not_principals.value.identifiers + } + } + + dynamic "condition" { + for_each = statement.value.condition != null ? statement.value.condition : [] + + content { + test = condition.value.test + values = condition.value.values + variable = condition.value.variable + } + } + } + } +} + +resource "aws_iam_role_policy" "inline" { + count = local.create_iam_role_inline_policy ? 1 : 0 + + role = aws_iam_role.this[0].name + name = var.use_name_prefix ? null : var.name + name_prefix = var.use_name_prefix ? "${var.name}-" : null + policy = data.aws_iam_policy_document.inline[0].json +} + ################################################################################ # IAM Instance Profile ################################################################################ diff --git a/modules/iam-role/variables.tf b/modules/iam-role/variables.tf index ba8eebc3..582100e7 100644 --- a/modules/iam-role/variables.tf +++ b/modules/iam-role/variables.tf @@ -76,6 +76,16 @@ variable "assume_role_policy_statements" { default = null } +variable "condition" { + description = "[Condition constraints](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#condition) applied to the trust policy(s) enabled" + type = list(object({ + test = string + variable = string + values = list(string) + })) + default = [] +} + variable "policies" { description = "Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format" type = map(string) @@ -124,6 +134,12 @@ variable "enable_github_oidc" { default = false } +variable "github_provider" { + description = "The GitHub OIDC provider URL *without the `https://` prefix" + type = string + default = "token.actions.githubusercontent.com" +} + variable "enable_bitbucket_oidc" { description = "Enable Bitbucket OIDC provider trust for the role" type = bool @@ -154,6 +170,36 @@ variable "saml_trust_actions" { default = [] } +################################################################################ +# IAM Role Inline policy +################################################################################ + +variable "inline_policy_statements" { + description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for inline policy permissions" + type = map(object({ + sid = optional(string) + actions = optional(list(string)) + not_actions = optional(list(string)) + effect = optional(string, "Allow") + resources = optional(list(string)) + not_resources = optional(list(string)) + principals = optional(list(object({ + type = string + identifiers = list(string) + }))) + not_principals = optional(list(object({ + type = string + identifiers = list(string) + }))) + condition = optional(list(object({ + test = string + variable = string + values = list(string) + }))) + })) + default = null +} + ################################################################################ # IAM Instance Profile ################################################################################ diff --git a/wrappers/iam-role/main.tf b/wrappers/iam-role/main.tf index e42365f7..1e2451bc 100644 --- a/wrappers/iam-role/main.tf +++ b/wrappers/iam-role/main.tf @@ -4,6 +4,7 @@ module "wrapper" { for_each = var.items assume_role_policy_statements = try(each.value.assume_role_policy_statements, var.defaults.assume_role_policy_statements, null) + condition = try(each.value.condition, var.defaults.condition, []) create = try(each.value.create, var.defaults.create, true) create_instance_profile = try(each.value.create_instance_profile, var.defaults.create_instance_profile, false) description = try(each.value.description, var.defaults.description, null) @@ -11,6 +12,8 @@ module "wrapper" { enable_github_oidc = try(each.value.enable_github_oidc, var.defaults.enable_github_oidc, false) enable_oidc = try(each.value.enable_oidc, var.defaults.enable_oidc, false) enable_saml = try(each.value.enable_saml, var.defaults.enable_saml, false) + github_provider = try(each.value.github_provider, var.defaults.github_provider, "token.actions.githubusercontent.com") + inline_policy_statements = try(each.value.inline_policy_statements, var.defaults.inline_policy_statements, null) max_session_duration = try(each.value.max_session_duration, var.defaults.max_session_duration, null) name = try(each.value.name, var.defaults.name, null) oidc_account_id = try(each.value.oidc_account_id, var.defaults.oidc_account_id, null) From ad8ddbc0cfa69ee184ab9b21974d342ae6e48467 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Fri, 8 Aug 2025 14:55:59 -0500 Subject: [PATCH 09/21] fix: Remove legacy IRSAv2 artifact --- .../iam-role-for-service-accounts/README.md | 3 -- .../iam-role-for-service-accounts/main.tf | 42 ------------------- 2 files changed, 45 deletions(-) diff --git a/examples/iam-role-for-service-accounts/README.md b/examples/iam-role-for-service-accounts/README.md index 9f5dccbc..a4264438 100644 --- a/examples/iam-role-for-service-accounts/README.md +++ b/examples/iam-role-for-service-accounts/README.md @@ -38,7 +38,6 @@ Run `terraform destroy` when you don't need these resources. | [cluster\_autoscaler\_irsa](#module\_cluster\_autoscaler\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | | [disabled](#module\_disabled) | ../../modules/iam-role-for-service-accounts | n/a | | [ebs\_csi\_irsa](#module\_ebs\_csi\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | -| [ebs\_csi\_irsa\_v2](#module\_ebs\_csi\_irsa\_v2) | ../../modules/iam-role-for-service-accounts | n/a | | [efs\_csi\_irsa](#module\_efs\_csi\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | | [eks](#module\_eks) | terraform-aws-modules/eks/aws | ~> 21.0 | | [external\_dns\_irsa](#module\_external\_dns\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | @@ -46,8 +45,6 @@ Run `terraform destroy` when you don't need these resources. | [fsx\_lustre\_csi\_irsa](#module\_fsx\_lustre\_csi\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | | [fsx\_openzfs\_csi\_irsa](#module\_fsx\_openzfs\_csi\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | | [irsa](#module\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | -| [irsa\_v2\_custom\_policy](#module\_irsa\_v2\_custom\_policy) | ../../modules/iam-role-for-service-accounts | n/a | -| [irsa\_v2\_empty](#module\_irsa\_v2\_empty) | ../../modules/iam-role-for-service-accounts | n/a | | [load\_balancer\_controller\_irsa](#module\_load\_balancer\_controller\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | | [load\_balancer\_controller\_targetgroup\_binding\_only\_irsa](#module\_load\_balancer\_controller\_targetgroup\_binding\_only\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | | [mountpoint\_s3\_csi\_irsa](#module\_mountpoint\_s3\_csi\_irsa) | ../../modules/iam-role-for-service-accounts | n/a | diff --git a/examples/iam-role-for-service-accounts/main.tf b/examples/iam-role-for-service-accounts/main.tf index 0c634e64..1dadbf45 100644 --- a/examples/iam-role-for-service-accounts/main.tf +++ b/examples/iam-role-for-service-accounts/main.tf @@ -17,48 +17,6 @@ locals { } } -################################################################################ -# IRSAv2 Roles -################################################################################ - -module "irsa_v2_empty" { - source = "../../modules/iam-role-for-service-accounts" - - name = "${local.name}-v2" - - enable_irsa_v2 = true - - tags = local.tags -} - -module "ebs_csi_irsa_v2" { - source = "../../modules/iam-role-for-service-accounts" - - name = "ebs-csi-v2" - - enable_irsa_v2 = true - attach_ebs_csi_policy = true - - tags = local.tags -} - -module "irsa_v2_custom_policy" { - source = "../../modules/iam-role-for-service-accounts" - - name = "${local.name}-custom-name" - - enable_irsa_v2 = true - policy_statements = { - DescribeEc2 = { - actions = ["ec2:Describe*"] - effect = "Allow" - resources = ["*"] - } - } - - tags = local.tags -} - ################################################################################ # IRSA Roles ################################################################################ From f4a56427c48418bc30260346f9b9596868eba893 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Fri, 8 Aug 2025 15:19:22 -0500 Subject: [PATCH 10/21] feat: Update upgrade guide with diagram mapping old to new modules --- docs/UPGRADE-6.0.md | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/docs/UPGRADE-6.0.md b/docs/UPGRADE-6.0.md index 4b56ada7..e413ceb6 100644 --- a/docs/UPGRADE-6.0.md +++ b/docs/UPGRADE-6.0.md @@ -9,14 +9,48 @@ If you find a bug, please open an issue with supporting configuration to reprodu - `iam-assumable-role` has been renamed to `iam-role` - `iam-assumable-role-with-oidc` has been merged into `iam-role` - `iam-assumable-role-with-saml` has been merged into `iam-role` -- `iam-assumable-roles` has been removed; `iam-role` should be used instead. See the [`iam-role` example](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-role) that shows an example replacement implementation. -- `iam-assumable-roles-with-saml` has been removed; `iam-role` should be used instead. See the [`iam-role` example](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-role-saml) that shows an example replacement implementation. +- `iam-assumable-roles` has been removed; `iam-role` should be used instead +- `iam-assumable-roles-with-saml` has been removed; `iam-role` should be used instead - `iam-github-oidc-provider` has been renamed to `iam-oidc-provider` -- `iam-github-oidc-role` has been merged into `iam-role`. See the [`iam-oidc-provider` example](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-oidc-provider) -- `iam-group-with-assumable-roles-policy` has been removed; the renamed `iam-group` (was `iam-group-with-policies`) should be used instead -- `iam-eks-role` has been removed; `iam-role-for-service-accounts` should be used instead +- `iam-github-oidc-role` has been merged into `iam-role` +- `iam-group-with-policies` has been renamed to `iam-group` +- `iam-group-with-assumable-roles-policy` has been merged into `iam-group` +- `iam-eks-role` has been removed; `iam-role-for-service-accounts` or [`eks-pod-identity`](https://github.com/terraform-aws-modules/terraform-aws-eks-pod-identity) should be used instead - `iam-policy` has been removed; the `aws_iam_policy` resource should be used directly instead +```mermaid +stateDiagram + direction LR + + # Old + assumeRole: iam-assumable-role + assumeRoleOIDC: iam-assumable-role-with-oidc + assumeRoleSAML: iam-assumable-role-with-saml + assumeRoles: iam-assumable-roles + assumeRolesSAML: iam-assumable-roles-with-saml + githubOIDCProvider: iam-github-oidc-provider + githubOIDCRole: iam-github-oidc-role + groupWithAssumablePolicy: iam-group-with-assumable-roles-policy + groupWithPolicies: iam-group-with-policies + + # New + group: iam-group + oidcProvider: iam-oidc-provider + role: iam-role + + assumeRole --> role + assumeRoleOIDC --> role + assumeRoleSAML --> role + assumeRoles --> role + assumeRolesSAML --> role + githubOIDCRole --> role + + groupWithAssumablePolicy --> group + groupWithPolicies --> group + + githubOIDCProvider --> oidcProvider +``` + ## Additional changes ### Modified From bb2a4653342ddd5570cc27fa2751063bea0541b0 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Mon, 11 Aug 2025 10:17:26 -0500 Subject: [PATCH 11/21] chore: Add variable/output changes to upgrade guide --- .pre-commit-config.yaml | 2 +- README.md | 4 + docs/UPGRADE-6.0.md | 93 ++++++++++ .../iam-role-for-service-accounts/README.md | 4 + modules/iam-read-only-policy/README.md | 2 +- .../migrations.tf | 168 ++++++++++++++++++ modules/iam-user/README.md | 2 + modules/iam-user/main.tf | 7 + modules/iam-user/migrations.tf | 8 + modules/iam-user/variables.tf | 6 + wrappers/iam-user/main.tf | 1 + 11 files changed, 295 insertions(+), 2 deletions(-) create mode 100644 modules/iam-role-for-service-accounts/migrations.tf create mode 100644 modules/iam-user/migrations.tf diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7f8a62bd..868fb48d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: - '--args=--only=terraform_workspace_remote' - id: terraform_validate - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: check-merge-conflict - id: end-of-file-fixer diff --git a/README.md b/README.md index fafbde8b..687add65 100644 --- a/README.md +++ b/README.md @@ -192,6 +192,10 @@ module "iam_role_saml" { ### IAM Role for EKS Service Accounts (IRSA) +> [!TIP] +> Upgrade to use EKS Pod Identity instead of IRSA +> A similar module for EKS Pod Identity is available [here](https://github.com/terraform-aws-modules/terraform-aws-eks-pod-identity). + Creates an IAM role that is suitable for EKS IAM role for service accounts (IRSA) with a set of pre-defined policies for common EKS addons. ```hcl diff --git a/docs/UPGRADE-6.0.md b/docs/UPGRADE-6.0.md index e413ceb6..b8ebcb94 100644 --- a/docs/UPGRADE-6.0.md +++ b/docs/UPGRADE-6.0.md @@ -6,6 +6,8 @@ If you find a bug, please open an issue with supporting configuration to reprodu ## List of backwards incompatible changes +- `iam-account`: + - The `aws_caller_identity` data source and associated outputs have been removed. Users should instead use the data source directly in their configuration - `iam-assumable-role` has been renamed to `iam-role` - `iam-assumable-role-with-oidc` has been merged into `iam-role` - `iam-assumable-role-with-saml` has been merged into `iam-role` @@ -17,6 +19,10 @@ If you find a bug, please open an issue with supporting configuration to reprodu - `iam-group-with-assumable-roles-policy` has been merged into `iam-group` - `iam-eks-role` has been removed; `iam-role-for-service-accounts` or [`eks-pod-identity`](https://github.com/terraform-aws-modules/terraform-aws-eks-pod-identity) should be used instead - `iam-policy` has been removed; the `aws_iam_policy` resource should be used directly instead +- `iam-role-for-service-accounts`: + - Individual policy creation and attachment has been consolidated under one policy creation and attachment + - Default values that enable permissive permissions have been removed; users will need to be explicit about the scope of access (i.e. ARNs) they provide when enabling permissions + - AppMesh policy support has been removed due to service reaching end of support ```mermaid stateDiagram @@ -69,6 +75,8 @@ stateDiagram 1. Removed variables: + - `iam-account` + - `get_caller_identity` - `iam-role` - `trusted_role_actions` - `trusted_role_arns` @@ -85,6 +93,24 @@ stateDiagram - `iam-group` - `custom_group_policies` - `assumable_roles` + - `iam-oidc-provider` + - `additional_thumbprints` - no longer required by GitHub + - `iam-role-for-service-accounts` + - `cluster_autoscaler_cluster_ids` - use `cluster_autoscaler_cluster_names` instead + - `role_name_prefix` - functionality covered under `name` + - `policy_name_prefix` - functionality covered under `policy_name` + - `allow_self_assume_role` + - `attach_karpenter_controller_policy` + - `karpenter_controller_cluster_id` + - `karpenter_controller_cluster_name` + - `karpenter_tag_key` + - `karpenter_controller_ssm_parameter_arns` + - `karpenter_controller_node_iam_role_arns` + - `karpenter_subnet_account_id` + - `karpenter_sqs_queue_arn` + - `enable_karpenter_instance_profile_creation` + - `attach_appmesh_controller_policy` + - `attach_appmesh_envoy_proxy_policy` 2. Renamed variables: @@ -103,9 +129,29 @@ stateDiagram - `attach_iam_self_management_policy` -> `create_policy` - `iam_self_management_policy_name_prefix` -> `policy_name_prefix` - `aws_account_id` -> `users_account_id` + - `iam-read-only-policy` + - `name_prefix` (string) -> `use_name_prefix` (bool) + - `iam-role-for-service-accounts` + - `create_role` -> `create` + - `role_name` -> `name` + - `role_path` -> `path` + - `role_name_prefix` (string) -> `use_name_prefix` (bool) + - `role_permissions_boundary_arn` -> `permissions_boundary` + - `role_description` -> `description` + - `role_policy_arns` -> `policies` + - `ebs_csi_kms_cmk_ids` -> `ebs_csi_kms_cmk_arns` + - `iam-user` + - `create_user` -> `create` + - `create_iam_user_login_profile` -> `create_login_profile` + - `create_iam_access_key` -> `create_access_key` + - `iam_access_key_status` -> `access_key_status` + - `policy_arns` -> `policies` + - `upload_iam_user_ssh_key` -> `create_ssh_key` 3. Added variables: + - `iam-account` + - `create` - `iam-role` - `assume_role_policy_statements` which allows for any number of custom statements to be added to the role's trust policy. This covers the majority of the variables that were removed - `iam-group` @@ -113,9 +159,22 @@ stateDiagram - `path`/`policy_path` - `create_policy` - `enable_mfa_enforcment` + - `iam-read-only-policy` + - `create` + - `iam-role-for-service-accounts` + - `create_policy` + - `source_policy_documents` + - `override_policy_documents` + - `policy_statements` + - `policy_name` + - `policy_description` 4. Removed outputs: + - `iam-account` + - `caller_identity_account_id` + - `caller_identity_arn` + - `caller_identity_user_id` - `iam-role` - `iam_role_path` - `role_requires_mfa` @@ -124,6 +183,18 @@ stateDiagram - `iam-group` - `assumable_roles` - `aws_account_id` + - `iam-read-only-policy` + - `description` + - `path` + - `iam-user` + - `pgp_key` + - `keybase_password_decrypt_command` + - `keybase_password_pgp_message` + - `keybase_secret_key_decrypt_command` + - `keybase_secret_key_pgp_message` + - `keybase_ses_smtp_password_v4_decrypt_command` + - `keybase_ses_smtp_password_v4_pgp_message` + - `policy_arns` 5. Renamed outputs: @@ -140,6 +211,22 @@ stateDiagram - `group_name` -> `name` - `group_arn` -> `arn` - `group_users` -> `users` + - `iam-user` + - `iam_user_arn` -> `arn` + - `iam_user_name` -> `name` + - `iam_user_unique_id` -> `unique_id` + - `iam_user_login_profile_password` -> `login_profile_password` + - `iam_user_login_profile_key_fingerprint` -> `login_profile_key_fingerprint` + - `iam_user_login_profile_encrypted_password` -> `login_profile_encrypted_password` + - `iam_access_key_id` -> `access_key_id` + - `iam_access_key_secret` -> `access_key_secret` + - `iam_access_key_key_fingerprint` -> `access_key_key_fingerprint` + - `iam_access_key_encrypted_secret` -> `access_key_encrypted_secret` + - `iam_access_key_ses_smtp_password_v4` -> `access_key_ses_smtp_password_v4` + - `iam_access_key_encrypted_ses_smtp_password_v4` -> `access_key_encrypted_ses_smtp_password_v4` + - `iam_access_key_status` -> `access_key_status` + - `iam_user_ssh_key_ssh_public_key_id` -> `ssh_key_public_key_id` + - `iam_user_ssh_key_fingerprint` -> `ssh_key_fingerprint` 6. Added outputs: @@ -149,6 +236,12 @@ stateDiagram ### Diff of before <> after +#### `iam-account` + +None + + + #### `iam-role` ```diff diff --git a/examples/iam-role-for-service-accounts/README.md b/examples/iam-role-for-service-accounts/README.md index a4264438..dea87d3f 100644 --- a/examples/iam-role-for-service-accounts/README.md +++ b/examples/iam-role-for-service-accounts/README.md @@ -1,5 +1,9 @@ # AWS IAM Role for Service Accounts in EKS +> [!TIP] +> Upgrade to use EKS Pod Identity instead of IRSA +> A similar module for EKS Pod Identity is available [here](https://github.com/terraform-aws-modules/terraform-aws-eks-pod-identity). + Configuration in this directory creates IAM roles that can be assumed by multiple EKS `ServiceAccount`s for various tasks. # Usage diff --git a/modules/iam-read-only-policy/README.md b/modules/iam-read-only-policy/README.md index baef2bdc..d541f619 100644 --- a/modules/iam-read-only-policy/README.md +++ b/modules/iam-read-only-policy/README.md @@ -1,4 +1,4 @@ -### AWS IAM ReadOnly Policy Terraform Module +# AWS IAM ReadOnly Policy Terraform Module Creates an IAM policy that allows read-only access to the list of AWS services provided. diff --git a/modules/iam-role-for-service-accounts/migrations.tf b/modules/iam-role-for-service-accounts/migrations.tf new file mode 100644 index 00000000..afbb76d2 --- /dev/null +++ b/modules/iam-role-for-service-accounts/migrations.tf @@ -0,0 +1,168 @@ +################################################################################ +# Migrations: v5.60 -> v6.0 +################################################################################ + +# AWS Gateway Controller +moved { + from = aws_iam_policy.aws_gateway_controller + to = aws_iam_policy.this +} + +moved { + from = aws_iam_role_policy_attachment.aws_gateway_controller + to = aws_iam_policy.this +} + +# Cert Manager +moved { + from = aws_iam_policy.cert_manager + to = aws_iam_policy.this +} + +moved { + from = aws_iam_role_policy_attachment.cert_manager + to = aws_iam_policy.this +} + +# Cluster Autoscaler +moved { + from = aws_iam_policy.cluster_autoscaler + to = aws_iam_policy.this +} + +moved { + from = aws_iam_role_policy_attachment.cluster_autoscaler + to = aws_iam_policy.this +} + +# EBS CSI +moved { + from = aws_iam_policy.ebs_csi + to = aws_iam_policy.this +} + +moved { + from = aws_iam_role_policy_attachment.ebs_csi + to = aws_iam_policy.this +} + +# EFS CSI +moved { + from = aws_iam_policy.efs_csi + to = aws_iam_policy.this +} + +moved { + from = aws_iam_role_policy_attachment.efs_csi + to = aws_iam_policy.this +} + +# Mountpoint S3 CSI +moved { + from = aws_iam_policy.mountpoint_s3_csi + to = aws_iam_policy.this +} + +moved { + from = aws_iam_role_policy_attachment.mountpoint_s3_csi + to = aws_iam_policy.this +} + +# External DNS +moved { + from = aws_iam_policy.external_dns + to = aws_iam_policy.this +} + +moved { + from = aws_iam_role_policy_attachment.external_dns + to = aws_iam_policy.this +} + +# External Secrets +moved { + from = aws_iam_policy.external_secrets + to = aws_iam_policy.this +} + +moved { + from = aws_iam_role_policy_attachment.external_secrets + to = aws_iam_policy.this +} + +# FSx OpenZFS CSI +moved { + from = aws_iam_policy.fsx_openzfs_csi + to = aws_iam_policy.this +} + +moved { + from = aws_iam_role_policy_attachment.fsx_openzfs_csi + to = aws_iam_policy.this +} + +# AWS Load Balancer Controller +moved { + from = aws_iam_policy.load_balancer_controller + to = aws_iam_policy.this +} + +moved { + from = aws_iam_role_policy_attachment.load_balancer_controller + to = aws_iam_policy.this +} + +# AWS Load Balancer Controller - Target Group Binding Only +moved { + from = aws_iam_policy.load_balancer_controller_targetgroup_only + to = aws_iam_policy.this +} + +moved { + from = aws_iam_role_policy_attachment.load_balancer_controller_targetgroup_only + to = aws_iam_policy.this +} + +# Amazon Managed Service for Prometheus +moved { + from = aws_iam_policy.amazon_managed_service_prometheus + to = aws_iam_policy.this +} + +moved { + from = aws_iam_role_policy_attachment.amazon_managed_service_prometheus + to = aws_iam_policy.this +} + +# Node Termination Handler +moved { + from = aws_iam_policy.node_termination_handler + to = aws_iam_policy.this +} + +moved { + from = aws_iam_role_policy_attachment.node_termination_handler + to = aws_iam_policy.this +} + +# Velero +moved { + from = aws_iam_policy.velero + to = aws_iam_policy.this +} + +moved { + from = aws_iam_role_policy_attachment.velero + to = aws_iam_policy.this +} + +# VPC CNI +moved { + from = aws_iam_policy.vpc_cni + to = aws_iam_policy.this +} + +moved { + from = aws_iam_role_policy_attachment.vpc_cni + to = aws_iam_policy.this +} diff --git a/modules/iam-user/README.md b/modules/iam-user/README.md index 8e987a37..7bc05ea5 100644 --- a/modules/iam-user/README.md +++ b/modules/iam-user/README.md @@ -50,6 +50,7 @@ No modules. | Name | Type | |------|------| | [aws_iam_access_key.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_access_key) | resource | +| [aws_iam_role_policy_attachment.additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_iam_user.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user) | resource | | [aws_iam_user_login_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user_login_profile) | resource | | [aws_iam_user_ssh_key.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user_ssh_key) | resource | @@ -70,6 +71,7 @@ No modules. | [path](#input\_path) | Desired path for the IAM user | `string` | `"/"` | no | | [permissions\_boundary](#input\_permissions\_boundary) | The ARN of the policy that is used to set the permissions boundary for the user | `string` | `null` | no | | [pgp\_key](#input\_pgp\_key) | Either a base-64 encoded PGP public key, or a keybase username in the form `keybase:username`. Used to encrypt password and access key | `string` | `null` | no | +| [policies](#input\_policies) | Policies to attach to the IAM user in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no | | [ssh\_key\_encoding](#input\_ssh\_key\_encoding) | Specifies the public key encoding format to use in the response. To retrieve the public key in ssh-rsa format, use SSH. To retrieve the public key in PEM format, use PEM | `string` | `"SSH"` | no | | [ssh\_public\_key](#input\_ssh\_public\_key) | The SSH public key. The public key must be encoded in ssh-rsa format or PEM format | `string` | `""` | no | | [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | diff --git a/modules/iam-user/main.tf b/modules/iam-user/main.tf index 41ede7e9..3e538810 100644 --- a/modules/iam-user/main.tf +++ b/modules/iam-user/main.tf @@ -13,6 +13,13 @@ resource "aws_iam_user" "this" { tags = var.tags } +resource "aws_iam_role_policy_attachment" "additional" { + for_each = { for k, v in var.policies : k => v if var.create } + + role = aws_iam_user.this[0].name + policy_arn = each.value +} + ################################################################################ # User Login Profile ################################################################################ diff --git a/modules/iam-user/migrations.tf b/modules/iam-user/migrations.tf new file mode 100644 index 00000000..21de98a6 --- /dev/null +++ b/modules/iam-user/migrations.tf @@ -0,0 +1,8 @@ +################################################################################ +# Migrations: v5.60 -> v6.0 +################################################################################ + +moved { + from = aws_iam_access_key.this_no_pgp + to = aws_iam_access_key.this +} diff --git a/modules/iam-user/variables.tf b/modules/iam-user/variables.tf index 42a33152..01e88872 100644 --- a/modules/iam-user/variables.tf +++ b/modules/iam-user/variables.tf @@ -38,6 +38,12 @@ variable "force_destroy" { default = false } +variable "policies" { + description = "Policies to attach to the IAM user in `{'static_name' = 'policy_arn'}` format" + type = map(string) + default = {} +} + ################################################################################ # User Login Profile ################################################################################ diff --git a/wrappers/iam-user/main.tf b/wrappers/iam-user/main.tf index a27d828a..e4609233 100644 --- a/wrappers/iam-user/main.tf +++ b/wrappers/iam-user/main.tf @@ -15,6 +15,7 @@ module "wrapper" { path = try(each.value.path, var.defaults.path, "/") permissions_boundary = try(each.value.permissions_boundary, var.defaults.permissions_boundary, null) pgp_key = try(each.value.pgp_key, var.defaults.pgp_key, null) + policies = try(each.value.policies, var.defaults.policies, {}) ssh_key_encoding = try(each.value.ssh_key_encoding, var.defaults.ssh_key_encoding, "SSH") ssh_public_key = try(each.value.ssh_public_key, var.defaults.ssh_public_key, "") tags = try(each.value.tags, var.defaults.tags, {}) From a43eb3752ef02fc142edb5d47c88a3f6994029e7 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Tue, 12 Aug 2025 12:05:22 -0500 Subject: [PATCH 12/21] feat: Add inline policy support to IRSA sub-module --- .../iam-role-for-service-accounts/README.md | 3 + modules/iam-role-for-service-accounts/main.tf | 62 +++++++++++++++++++ .../variables.tf | 30 +++++++++ .../iam-role-for-service-accounts/main.tf | 1 + 4 files changed, 96 insertions(+) diff --git a/modules/iam-role-for-service-accounts/README.md b/modules/iam-role-for-service-accounts/README.md index c92306f3..3c56f951 100644 --- a/modules/iam-role-for-service-accounts/README.md +++ b/modules/iam-role-for-service-accounts/README.md @@ -143,6 +143,7 @@ No modules. |------|------| | [aws_iam_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy.inline](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | | [aws_iam_role_policy_attachment.additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_iam_role_policy_attachment.amazon_cloudwatch_observability](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | | [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | @@ -157,6 +158,7 @@ No modules. | [aws_iam_policy_document.external_secrets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.fsx_lustre_csi](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.fsx_openzfs_csi](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.inline](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.load_balancer_controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.load_balancer_controller_targetgroup_only](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.mountpoint_s3_csi](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | @@ -202,6 +204,7 @@ No modules. | [external\_secrets\_ssm\_parameter\_arns](#input\_external\_secrets\_ssm\_parameter\_arns) | List of Systems Manager Parameter ARNs that contain secrets to mount using External Secrets | `list(string)` | `[]` | no | | [fsx\_lustre\_csi\_service\_role\_arns](#input\_fsx\_lustre\_csi\_service\_role\_arns) | Service role ARNs to allow FSx for Lustre CSI create and manage FSX for Lustre service linked roles | `list(string)` |
[
"arn:aws:iam::*:role/aws-service-role/s3.data-source.lustre.fsx.amazonaws.com/*"
]
| no | | [fsx\_openzfs\_csi\_service\_role\_arns](#input\_fsx\_openzfs\_csi\_service\_role\_arns) | Service role ARNs to allow FSx for OpenZFS CSI create and manage FSX for openzfs service linked roles | `list(string)` |
[
"arn:aws:iam::*:role/aws-service-role/fsx.amazonaws.com/*"
]
| no | +| [inline\_policy\_statements](#input\_inline\_policy\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for inline policy permissions |
map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
variable = string
values = list(string)
})))
}))
| `null` | no | | [load\_balancer\_controller\_targetgroup\_arns](#input\_load\_balancer\_controller\_targetgroup\_arns) | List of Target groups ARNs using Load Balancer Controller | `list(string)` | `[]` | no | | [max\_session\_duration](#input\_max\_session\_duration) | Maximum session duration (in seconds) that you want to set for the specified role. If you do not specify a value for this setting, the default maximum of one hour is applied. This setting can have a value from 1 hour to 12 hours | `number` | `null` | no | | [mountpoint\_s3\_csi\_bucket\_arns](#input\_mountpoint\_s3\_csi\_bucket\_arns) | S3 bucket ARNs to allow Mountpoint S3 CSI to list buckets | `list(string)` | `[]` | no | diff --git a/modules/iam-role-for-service-accounts/main.tf b/modules/iam-role-for-service-accounts/main.tf index 87acf2c3..4567997f 100644 --- a/modules/iam-role-for-service-accounts/main.tf +++ b/modules/iam-role-for-service-accounts/main.tf @@ -163,3 +163,65 @@ resource "aws_iam_role_policy_attachment" "this" { role = aws_iam_role.this[0].name policy_arn = aws_iam_policy.this[0].arn } + +################################################################################ +# IAM Role Inline policy +################################################################################ + +locals { + create_iam_role_inline_policy = var.create && length(var.inline_policy_statements) > 0 +} + +data "aws_iam_policy_document" "inline" { + count = local.create_iam_role_inline_policy ? 1 : 0 + + dynamic "statement" { + for_each = var.inline_policy_statements != null ? var.inline_policy_statements : {} + + content { + sid = try(coalesce(statement.value.sid, statement.key)) + actions = statement.value.actions + not_actions = statement.value.not_actions + effect = statement.value.effect + resources = statement.value.resources + not_resources = statement.value.not_resources + + dynamic "principals" { + for_each = statement.value.principals != null ? statement.value.principals : [] + + content { + type = principals.value.type + identifiers = principals.value.identifiers + } + } + + dynamic "not_principals" { + for_each = statement.value.not_principals != null ? statement.value.not_principals : [] + + content { + type = not_principals.value.type + identifiers = not_principals.value.identifiers + } + } + + dynamic "condition" { + for_each = statement.value.condition != null ? statement.value.condition : [] + + content { + test = condition.value.test + values = condition.value.values + variable = condition.value.variable + } + } + } + } +} + +resource "aws_iam_role_policy" "inline" { + count = local.create_iam_role_inline_policy ? 1 : 0 + + role = aws_iam_role.this[0].name + name = var.use_name_prefix ? null : var.name + name_prefix = var.use_name_prefix ? "${var.name}-" : null + policy = data.aws_iam_policy_document.inline[0].json +} diff --git a/modules/iam-role-for-service-accounts/variables.tf b/modules/iam-role-for-service-accounts/variables.tf index fa2a4ca4..dc994bf1 100644 --- a/modules/iam-role-for-service-accounts/variables.tf +++ b/modules/iam-role-for-service-accounts/variables.tf @@ -377,3 +377,33 @@ variable "attach_cloudwatch_observability_policy" { type = bool default = false } + +################################################################################ +# IAM Role Inline policy +################################################################################ + +variable "inline_policy_statements" { + description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for inline policy permissions" + type = map(object({ + sid = optional(string) + actions = optional(list(string)) + not_actions = optional(list(string)) + effect = optional(string, "Allow") + resources = optional(list(string)) + not_resources = optional(list(string)) + principals = optional(list(object({ + type = string + identifiers = list(string) + }))) + not_principals = optional(list(object({ + type = string + identifiers = list(string) + }))) + condition = optional(list(object({ + test = string + variable = string + values = list(string) + }))) + })) + default = null +} diff --git a/wrappers/iam-role-for-service-accounts/main.tf b/wrappers/iam-role-for-service-accounts/main.tf index 6f852f80..866f680c 100644 --- a/wrappers/iam-role-for-service-accounts/main.tf +++ b/wrappers/iam-role-for-service-accounts/main.tf @@ -35,6 +35,7 @@ module "wrapper" { external_secrets_ssm_parameter_arns = try(each.value.external_secrets_ssm_parameter_arns, var.defaults.external_secrets_ssm_parameter_arns, []) fsx_lustre_csi_service_role_arns = try(each.value.fsx_lustre_csi_service_role_arns, var.defaults.fsx_lustre_csi_service_role_arns, ["arn:aws:iam::*:role/aws-service-role/s3.data-source.lustre.fsx.amazonaws.com/*"]) fsx_openzfs_csi_service_role_arns = try(each.value.fsx_openzfs_csi_service_role_arns, var.defaults.fsx_openzfs_csi_service_role_arns, ["arn:aws:iam::*:role/aws-service-role/fsx.amazonaws.com/*"]) + inline_policy_statements = try(each.value.inline_policy_statements, var.defaults.inline_policy_statements, null) load_balancer_controller_targetgroup_arns = try(each.value.load_balancer_controller_targetgroup_arns, var.defaults.load_balancer_controller_targetgroup_arns, []) max_session_duration = try(each.value.max_session_duration, var.defaults.max_session_duration, null) mountpoint_s3_csi_bucket_arns = try(each.value.mountpoint_s3_csi_bucket_arns, var.defaults.mountpoint_s3_csi_bucket_arns, []) From 858e08d26bfd68e33093c3214097a3dad05d6df3 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Tue, 12 Aug 2025 16:29:41 -0500 Subject: [PATCH 13/21] docs: Capture before/after in upgrade guide from testing --- docs/UPGRADE-6.0.md | 630 ++++++++++++++++-- examples/iam-role/main.tf | 20 + modules/iam-group/main.tf | 5 +- modules/iam-group/migrations.tf | 13 + modules/iam-role-for-service-accounts/main.tf | 2 +- modules/iam-role/README.md | 6 +- modules/iam-role/main.tf | 2 +- modules/iam-role/variables.tf | 6 +- 8 files changed, 633 insertions(+), 51 deletions(-) create mode 100644 modules/iam-group/migrations.tf diff --git a/docs/UPGRADE-6.0.md b/docs/UPGRADE-6.0.md index b8ebcb94..81ad43b4 100644 --- a/docs/UPGRADE-6.0.md +++ b/docs/UPGRADE-6.0.md @@ -6,6 +6,8 @@ If you find a bug, please open an issue with supporting configuration to reprodu ## List of backwards incompatible changes +- The ability to allow roles to assume their own roles has been removed. This was previously added as part of helping users mitigate https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/. Going forward, users will need to mitigate this on the application side (i.e. - do not have a role assume itself), or update the trust policy in their implementation to continue using this behavior. It is strongly recommended to mitigate this by not having the role assume itself. + - `iam-account`: - The `aws_caller_identity` data source and associated outputs have been removed. Users should instead use the data source directly in their configuration - `iam-assumable-role` has been renamed to `iam-role` @@ -61,15 +63,15 @@ stateDiagram ### Modified +- `iam-group` + - Policy management has been updated to support extending the policy created by the sub-module, as well as adding additional policies that will be attached to the group + - The role assumption permissions has been removed from the policy; users can extend the policy to add this if needed via `permission_statements` + - Default create conditional is now `true` instead of `false` - `iam-role` - The use of individual variables to control/manipulate the assume role trust policy have been replaced by a generic `assume_role_policy_statements` variable. This allows for any number of custom statements to be added to the role's trust policy. - `custom_role_policy_arns` has been renamed to `policies` and now accepts a map of `name`: `policy-arn` pairs; this allows for both existing policies and policies that will get created at the same time as the role. This also replaces the admin, readonly, and poweruser policy ARN variables and their associated `attach_*_policy` variables. - Default create conditional is now `true` instead of `false` - `force_detach_policies` has been removed; this is now always `true` -- `iam-group` - - Policy management has been updated to support extending the policy created by the sub-module, as well as adding additional policies that will be attached to the group - - The role assumption permissions has been removed from the policy; users can extend the policy to add this if needed via `permission_statements` - - Default create conditional is now `true` instead of `false` ### Variable and output changes @@ -77,6 +79,13 @@ stateDiagram - `iam-account` - `get_caller_identity` + - `iam-group` + - `custom_group_policies` + - `assumable_roles` + - `iam-oidc-provider` + - `additional_thumbprints` - no longer required by GitHub + - `iam-read-only-policy` + - None - `iam-role` - `trusted_role_actions` - `trusted_role_arns` @@ -90,11 +99,6 @@ stateDiagram - `readonly_role_policy_arn` & `attach_readonly_policy` - `force_detach_policies` - `role_sts_externalid` - - `iam-group` - - `custom_group_policies` - - `assumable_roles` - - `iam-oidc-provider` - - `additional_thumbprints` - no longer required by GitHub - `iam-role-for-service-accounts` - `cluster_autoscaler_cluster_ids` - use `cluster_autoscaler_cluster_names` instead - `role_name_prefix` - functionality covered under `name` @@ -111,17 +115,13 @@ stateDiagram - `enable_karpenter_instance_profile_creation` - `attach_appmesh_controller_policy` - `attach_appmesh_envoy_proxy_policy` + - `iam-user` + - None 2. Renamed variables: - - `iam-role` - - `create_role` -> `create` - - `role_name` -> `name` - - `role_name_prefix` -> `name_prefix` - - `role_description` -> `description` - - `role_path` -> `path` - - `role_permissions_boundary_arn` -> `permissions_boundary_arn` - - `custom_role_policy_arns` -> `policies` + - `iam-account` + - None - `iam-group` - `create_group` -> `create` - `group_users` -> `group` @@ -129,8 +129,18 @@ stateDiagram - `attach_iam_self_management_policy` -> `create_policy` - `iam_self_management_policy_name_prefix` -> `policy_name_prefix` - `aws_account_id` -> `users_account_id` + - `iam-oidc-provider` + - None - `iam-read-only-policy` - `name_prefix` (string) -> `use_name_prefix` (bool) + - `iam-role` + - `create_role` -> `create` + - `role_name` -> `name` + - `role_name_prefix` -> `name_prefix` + - `role_description` -> `description` + - `role_path` -> `path` + - `role_permissions_boundary_arn` -> `permissions_boundary_arn` + - `custom_role_policy_arns` -> `policies` - `iam-role-for-service-accounts` - `create_role` -> `create` - `role_name` -> `name` @@ -152,15 +162,17 @@ stateDiagram - `iam-account` - `create` - - `iam-role` - - `assume_role_policy_statements` which allows for any number of custom statements to be added to the role's trust policy. This covers the majority of the variables that were removed - `iam-group` - `permission_statements` which allows for any number of custom statements to be added to the role's trust policy. This covers the majority of the variables that were removed - `path`/`policy_path` - `create_policy` - `enable_mfa_enforcment` + - `iam-oidc-provider` + - None - `iam-read-only-policy` - `create` + - `iam-role` + - `assume_role_policy_statements` which allows for any number of custom statements to be added to the role's trust policy. This covers the majority of the variables that were removed - `iam-role-for-service-accounts` - `create_policy` - `source_policy_documents` @@ -168,6 +180,8 @@ stateDiagram - `policy_statements` - `policy_name` - `policy_description` + - `iam-user` + - None 4. Removed outputs: @@ -175,17 +189,21 @@ stateDiagram - `caller_identity_account_id` - `caller_identity_arn` - `caller_identity_user_id` - - `iam-role` - - `iam_role_path` - - `role_requires_mfa` - - `iam_instance_profile_path` - - `role_sts_externalid` - `iam-group` - `assumable_roles` - `aws_account_id` + - `iam-oidc-provider` + - None - `iam-read-only-policy` - `description` - `path` + - `iam-role` + - `iam_role_path` + - `role_requires_mfa` + - `iam_instance_profile_path` + - `role_sts_externalid` + - `iam-role-for-service-accounts` + - None - `iam-user` - `pgp_key` - `keybase_password_decrypt_command` @@ -198,6 +216,17 @@ stateDiagram 5. Renamed outputs: + - `iam-account` + - None + - `iam-group` + - `group_id` -> `id` + - `group_name` -> `name` + - `group_arn` -> `arn` + - `group_users` -> `users` + - `iam-oidc-provider` + - None + - `iam-read-only-policy` + - None - `iam-role` - `iam_role_arn` -> `arn` - `iam_role_name` -> `name` @@ -206,11 +235,8 @@ stateDiagram - `iam_instance_profile_id` -> `instance_profile_id` - `iam_instance_profile_name` -> `instance_profile_name` - `iam_instance_profile_unique_id` -> `instance_profile_unique_id` - - `iam-group` - - `group_id` -> `id` - - `group_name` -> `name` - - `group_arn` -> `arn` - - `group_users` -> `users` + - `iam-role-for-service-accounts` + - None - `iam-user` - `iam_user_arn` -> `arn` - `iam_user_name` -> `name` @@ -236,25 +262,25 @@ stateDiagram ### Diff of before <> after +> [!WARNING] +> Renamed variables are not exhaustively shown below. See sections above for mappings of renamed variables + #### `iam-account` None - - -#### `iam-role` +#### `iam-assumable-role` -> `iam-role` ```diff module "iam_role" { - source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role" + source = "terraform-aws-modules/iam/aws//modules/iam-role" -- version = "~> 5.0" +- version = "~> 5.60" + version = "~> 6.0" - create_role = true + create = true # is now `true` by default -- role_requires_mfa = true - trusted_role_arns = [ - "arn:aws:iam::307990089504:root", - "arn:aws:iam::835367859851:user/anton", @@ -263,9 +289,12 @@ module "iam_role" { - "codedeploy.amazonaws.com" - ] - role_sts_externalid = ["some-id-goes-here"] -+ assume_role_policy_statements = [ -+ { -+ sid = "TrustRoleAndServiceToAssume" ++ assume_role_policy_statements = { ++ TrustRoleAndServiceToAssume = { ++ actions = [ ++ "sts:AssumeRole", ++ "sts:TagSession", ++ ] + principals = [ + { + type = "AWS" @@ -279,13 +308,13 @@ module "iam_role" { + identifiers = ["codedeploy.amazonaws.com"] + } + ] -+ conditions = [{ ++ condition = [{ + test = "StringEquals" + variable = "sts:ExternalId" -+ values = ["some-secret-id"] ++ values = ["some-id-goes-here"] + }] + } -+ ] ++ } - attach_admin_policy = true - custom_role_policy_arns = [ @@ -300,7 +329,526 @@ module "iam_role" { + custom = module.iam_policy.arn + } } +``` + +##### State Changes + +```sh +terraform state mv "module.iam_role.aws_iam_role_policy_attachment.admin[0]" 'module.iam_role.aws_iam_role_policy_attachment.this["AdministratorAccess"]' + +# One move command for each ARN in prior custom_role_policy_arns +terraform state mv "module.iam_role.aws_iam_role_policy_attachment.custom[0]" 'module.iam_role.aws_iam_role_policy_attachment.this["AmazonCognitoReadOnly"]' +terraform state mv "module.iam_role.aws_iam_role_policy_attachment.custom[1]" 'module.iam_role.aws_iam_role_policy_attachment.this["AlexaForBusinessFullAccess"]' +terraform state mv "module.iam_role.aws_iam_role_policy_attachment.custom[2]" 'module.iam_role.aws_iam_role_policy_attachment.this["custom"]' +``` + +#### `iam-assumable-role-with-oidc` -> `iam-role` + +```diff +module "iam_role" { +- source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role-with-oidc" ++ source = "terraform-aws-modules/iam/aws//modules/iam-role" +- version = "~> 5.60" ++ version = "~> 6.0" + ++ enable_oidc = true + +- provider_url = "oidc.circleci.com/org/" ++ oidc_provider_urls = ["oidc.circleci.com/org/"] + +- oidc_fully_qualified_audiences = [""] ++ oidc_audiences = [""] + +- role_policy_arns = [ +- "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser", +- ] ++ policies = { ++ AmazonEC2ContainerRegistryPowerUser = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser" ++ } + +- provider_trust_policy_conditions = [ ++ condition = [ + { + test = "StringLike" + variable = "aws:RequestTag/Environment" + values = ["example"] + } + ] +} +``` + +##### State Changes + +```sh +# One move command for each ARN in prior custom_role_policy_arns +terraform state mv "module.iam_role.aws_iam_role_policy_attachment.custom[0]" 'module.iam_role.aws_iam_role_policy_attachment.this["AmazonEC2ContainerRegistryPowerUser"]' +``` + +#### `iam-assumable-role-with-saml` -> `iam-role` + +```diff +module "iam_role" { +- source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role-with-saml" ++ source = "terraform-aws-modules/iam/aws//modules/iam-role" +- version = "~> 5.60" ++ version = "~> 6.0" + ++ enable_saml = true +- provider_id = aws_iam_saml_provider.idp_saml.id +- provider_ids = [aws_iam_saml_provider.second_idp_saml.id] ++ saml_provider_ids = [ ++ aws_iam_saml_provider.idp_saml.id, ++ aws_iam_saml_provider.second_idp_saml.id ++ ] + +- role_policy_arns = [ +- "arn:aws:iam::aws:policy/ReadOnlyAccess", +- ] ++ policies = { ++ ReadOnlyAccess = "arn:aws:iam::aws:policy/ReadOnlyAccess" ++ } +} + +resource "aws_iam_saml_provider" "idp_saml" { + name = "idp_saml" + saml_metadata_document = file("saml-metadata.xml") +} + +resource "aws_iam_saml_provider" "second_idp_saml" { + name = "second_idp_saml" + saml_metadata_document = file("saml-metadata.xml") +} +``` + +##### State Changes + +```sh +# One move command for each ARN in prior custom_role_policy_arns +terraform state mv "module.iam_role.aws_iam_role_policy_attachment.custom[0]" 'module.iam_role.aws_iam_role_policy_attachment.this["ReadOnlyAccess"]' +``` + +#### `iam-assumable-roles` -> `iam-role` + +This migration is a bit more involved since its going from a module of multiple roles, to a module with one role. + +##### Before `v5.60` -### State Changes +```hcl +module "iam_assumable_roles" { + source = "terraform-aws-modules/iam/aws//modules/iam-assumable-roles" + version = "~> 5.60" + + trusted_role_arns = [ + "arn:aws:iam::307990089504:root", + "arn:aws:iam::835367859851:user/anton", + ] + + trusted_role_services = [ + "codedeploy.amazonaws.com" + ] + + create_admin_role = true + + create_poweruser_role = true + poweruser_role_name = "Billing-And-Support-Access" + poweruser_role_policy_arns = [ + "arn:aws:iam::aws:policy/job-function/Billing", + "arn:aws:iam::aws:policy/AWSSupportAccess", + ] +} +``` + +##### After `v6.0` + +```hcl +module "iam_role_admin" { + source = "terraform-aws-modules/iam/aws//modules/iam-role" + version = "~> 6.0" + + name = "admin" + + assume_role_policy_statements = { + TrustRoleAndServiceToAssume = { + actions = [ + "sts:AssumeRole", + "sts:TagSession", + ] + principals = [ + { + type = "AWS" + identifiers = [ + "arn:aws:iam::307990089504:root", + "arn:aws:iam::835367859851:user/anton", + ] + }, + { + type = "Service" + identifiers = ["codedeploy.amazonaws.com"] + } + ] + } + } + + policies = { + AdministratorAccess = "arn:aws:iam::aws:policy/AdministratorAccess" + } +} + +module "iam_role_poweruser" { + source = "terraform-aws-modules/iam/aws//modules/iam-role" + version = "~> 6.0" + + name = "Billing-And-Support-Access" + + assume_role_policy_statements = { + TrustRoleAndServiceToAssume = { + actions = [ + "sts:AssumeRole", + "sts:TagSession", + ] + principals = [ + { + type = "AWS" + identifiers = [ + "arn:aws:iam::307990089504:root", + "arn:aws:iam::835367859851:user/anton", + ] + }, + { + type = "Service" + identifiers = ["codedeploy.amazonaws.com"] + } + ] + } + } + + policies = { + PowerUserAccess = "arn:aws:iam::aws:policy/PowerUserAccess" + Billing = "arn:aws:iam::aws:policy/job-function/Billing" + AWSSupportAccess = "arn:aws:iam::aws:policy/AWSSupportAccess" + } +} +``` + +##### State Changes + +```sh +terraform state mv "module.iam_assumable_roles.aws_iam_role.admin[0]" "module.iam_role_admin.aws_iam_role.this[0]" +terraform state mv "module.iam_assumable_roles.aws_iam_role_policy_attachment.admin[0]" 'module.iam_role_admin.aws_iam_role_policy_attachment.this["AdministratorAccess"]' + +terraform state mv "module.iam_assumable_roles.aws_iam_role.poweruser[0]" "module.iam_role_poweruser.aws_iam_role.this[0]" +# One move command for each ARN in prior `poweruser_role_policy_arns` +terraform state mv "module.iam_assumable_roles.aws_iam_role_policy_attachment.poweruser[0]" 'module.iam_role_poweruser.aws_iam_role_policy_attachment.this["Billing"]' +terraform state mv "module.iam_assumable_roles.aws_iam_role_policy_attachment.poweruser[1]" 'module.iam_role_poweruser.aws_iam_role_policy_attachment.this["AWSSupportAccess"]' +``` + +#### `iam-assumable-roles-with-saml` -> `iam-role` + +This migration is a bit more involved since its going from a module of multiple roles, to a module with one role. + +##### Before `v5.60` + +```hcl +module "iam_assumable_roles" { + source = "terraform-aws-modules/iam/aws//modules/iam-assumable-roles-with-saml" + version = "~> 5.60" + + create_admin_role = true + + create_poweruser_role = true + poweruser_role_name = "developer" + + provider_id = aws_iam_saml_provider.idp_saml.id + provider_ids = [aws_iam_saml_provider.idp_saml.id, aws_iam_saml_provider.second_idp_saml.id] +} + +resource "aws_iam_saml_provider" "idp_saml" { + name = "idp_saml" + saml_metadata_document = file("saml-metadata.xml") +} + +resource "aws_iam_saml_provider" "second_idp_saml" { + name = "second_idp_saml" + saml_metadata_document = file("saml-metadata.xml") +} +``` + +##### After `v6.0` + +```hcl +module "iam_role_admin" { + source = "terraform-aws-modules/iam/aws//modules/iam-role" + version = "~> 6.0" + + name = "admin" + + enable_saml = true + saml_provider_ids = [ + aws_iam_saml_provider.idp_saml.id, + aws_iam_saml_provider.second_idp_saml.id + ] + + policies = { + AdministratorAccess = "arn:aws:iam::aws:policy/AdministratorAccess" + } +} + +module "iam_role_poweruser" { + source = "terraform-aws-modules/iam/aws//modules/iam-role" + version = "~> 6.0" + + name = "poweruser" + + enable_saml = true + saml_provider_ids = [ + aws_iam_saml_provider.idp_saml.id, + aws_iam_saml_provider.second_idp_saml.id + ] + + policies = { + PowerUserAccess = "arn:aws:iam::aws:policy/PowerUserAccess" + } +} +``` + +##### State Changes + +```sh +terraform state mv "module.iam_assumable_roles.aws_iam_role.admin[0]" "module.iam_role_admin.aws_iam_role.this[0]" +terraform state mv "module.iam_assumable_roles.aws_iam_role_policy_attachment.admin[0]" 'module.iam_role_admin.aws_iam_role_policy_attachment.this["AdministratorAccess"]' + +terraform state mv "module.iam_assumable_roles.aws_iam_role.poweruser[0]" "module.iam_role_poweruser.aws_iam_role.this[0]" +terraform state mv "module.iam_assumable_roles.aws_iam_role_policy_attachment.poweruser[0]" 'module.iam_role_poweruser.aws_iam_role_policy_attachment.this["PowerUserAccess"]' +``` + +#### `iam-eks-role` -> `iam-role-for-service-accounts` + +```diff +module "irsa" { +- source = "terraform-aws-modules/iam/aws//modules/iam-eks-role" ++ source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts" +- version = "~> 5.60" ++ version = "~> 6.0" + +- role_name = "example" ++ name = "example" + +- cluster_service_accounts = { +- example = ["default:my-app"] +- } ++ oidc_providers = { ++ example = { ++ provider_arn = module.eks.oidc_provider_arn ++ namespace_service_accounts = ["default:my-app"] ++ } ++ } + +- role_policy_arns = { ++ policies = { + AmazonEKS_CNI_Policy = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy" + } +} +``` + +#### `iam-github-oidc-role` -> `iam-role` + +```diff +module "iam_role" { +- source = "terraform-aws-modules/iam/aws//modules/iam-github-oidc-role" ++ source = "terraform-aws-modules/iam/aws//modules/iam-role" +- version = "~> 5.60" ++ version = "~> 6.0" + ++ enable_github_oidc = true + +- subjects = [ ++ oidc_subjects = [ + "terraform-aws-modules/terraform-aws-iam:pull_request", + "terraform-aws-modules/terraform-aws-iam:ref:refs/heads/master", + ] + +- additional_trust_policy_conditions = [ ++ condition = [ + { + test = "StringEquals" + variable = "token.actions.githubusercontent.com:actor" + values = ["username"] + } + ] + + policies = { + S3ReadOnly = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" + } +} +``` + +#### `iam-group-with-assumable-roles-policy` -> `iam-group` + +```diff +module "iam_group" { +- source = "terraform-aws-modules/iam/aws//modules/iam-group-with-assumable-roles-policy" ++ source = "terraform-aws-modules/iam/aws//modules/iam-group" +- version = "~> 5.60" ++ version = "~> 6.0" + + # To preserve backwards compatibility + policy_use_name_prefix = false + policy_description = "Allows to assume role in another AWS account" + enable_self_management_permissions = false + +- assumable_roles = ["arn:aws:iam::111111111111:role/admin"] ++ permission_statements = { ++ AssumeRole = { ++ effect = "Allow" ++ actions = ["sts:AssumeRole"] ++ resources = ["arn:aws:iam::111111111111:role/admin"] ++ } ++ } + +- group_users = [ ++ users = [ + module.iam_user.iam_user_name, + ] +} + +module "iam_user" { + source = "terraform-aws-modules/iam/aws//modules/iam-user" + version = "~> 5.60" + + name = "user" + + create_iam_user_login_profile = false + create_iam_access_key = false +} +``` + +#### `iam-group-with-policies` -> `iam-group` + +```diff +module "iam_group" { +- source = "terraform-aws-modules/iam/aws//modules/iam-group-with-policies" ++ source = "terraform-aws-modules/iam/aws//modules/iam-group" +- version = "~> 5.60" ++ version = "~> 6.0" + + # To preserve backwards compatibility + policy_name = "IAMSelfManagement" + +- group_users = [ ++ users = [ + module.iam_user.iam_user_name, + ] + +- custom_group_policy_arns = ["arn:aws:iam::aws:policy/AmazonS3FullAccess"] ++ policies = { ++ AmazonS3FullAccess = "arn:aws:iam::aws:policy/AmazonS3FullAccess" ++ } +} + +module "iam_user" { + source = "terraform-aws-modules/iam/aws//modules/iam-user" + version = "~> 5.60" + + name = "user" + + create_iam_user_login_profile = false + create_iam_access_key = false +} +``` + +##### State Changes + +```sh +# One move command for each ARN in prior `custom_group_policy_arns` +terraform state mv "module.iam_group.aws_iam_group_policy_attachment.custom_arns[0]" 'module.iam_group.aws_iam_group_policy_attachment.additional["AmazonS3FullAccess"]' +``` + +#### `iam-policy` + +##### Before `v5.60` + +```hcl +module "iam_policy" { + source = "terraform-aws-modules/iam/aws//modules/iam-policy" + version = "~> 5.60" + + name_prefix = "example-" + path = "/" + description = "My example policy" + + policy = <"] + oidc_audiences = [""] + + policies = { + AmazonEC2ContainerRegistryPowerUser = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser" + } + + tags = local.tags +} + ################################################################################ # IAM Role - SAML 2.0 ################################################################################ diff --git a/modules/iam-group/main.tf b/modules/iam-group/main.tf index 0498f447..3d440358 100644 --- a/modules/iam-group/main.tf +++ b/modules/iam-group/main.tf @@ -39,7 +39,7 @@ resource "aws_iam_group_membership" "this" { ################################################################################ locals { - create_policy = var.create && var.create_policy && (var.enable_self_management_permissions || length(var.permission_statements) > 0) + create_policy = var.create && var.create_policy && (var.enable_self_management_permissions || var.permission_statements != null) policy_name = try(coalesce(var.policy_name, var.name), "") } @@ -174,7 +174,8 @@ data "aws_iam_policy_document" "this" { for_each = var.enable_self_management_permissions && var.enable_mfa_enforcment ? [1] : [] content { - sid = "DenyAllExceptListedIfNoMFA" + sid = "DenyAllExceptListedIfNoMFA" + effect = "Deny" not_actions = [ "iam:CreateVirtualMFADevice", "iam:EnableMFADevice", diff --git a/modules/iam-group/migrations.tf b/modules/iam-group/migrations.tf new file mode 100644 index 00000000..0d33edb5 --- /dev/null +++ b/modules/iam-group/migrations.tf @@ -0,0 +1,13 @@ +################################################################################ +# Migrations: v5.60 -> v6.0 +################################################################################ + +moved { + from = aws_iam_policy.iam_self_management + to = aws_iam_policy.this +} + +moved { + from = aws_iam_group_policy_attachment.iam_self_management + to = aws_iam_group_policy_attachment.this +} diff --git a/modules/iam-role-for-service-accounts/main.tf b/modules/iam-role-for-service-accounts/main.tf index 4567997f..44a50340 100644 --- a/modules/iam-role-for-service-accounts/main.tf +++ b/modules/iam-role-for-service-accounts/main.tf @@ -169,7 +169,7 @@ resource "aws_iam_role_policy_attachment" "this" { ################################################################################ locals { - create_iam_role_inline_policy = var.create && length(var.inline_policy_statements) > 0 + create_iam_role_inline_policy = var.create && var.inline_policy_statements } data "aws_iam_policy_document" "inline" { diff --git a/modules/iam-role/README.md b/modules/iam-role/README.md index 59d16c24..4ade20e2 100644 --- a/modules/iam-role/README.md +++ b/modules/iam-role/README.md @@ -166,10 +166,10 @@ No modules. | [max\_session\_duration](#input\_max\_session\_duration) | Maximum session duration (in seconds) that you want to set for the specified role. If you do not specify a value for this setting, the default maximum of one hour is applied. This setting can have a value from 1 hour to 12 hours | `number` | `null` | no | | [name](#input\_name) | Name to use on IAM role created | `string` | `null` | no | | [oidc\_account\_id](#input\_oidc\_account\_id) | An overriding AWS account ID where the OIDC provider lives; leave empty to use the current account ID for the AWS provider | `string` | `null` | no | -| [oidc\_audiences](#input\_oidc\_audiences) | The audience to be added to the role policy. Set to sts.amazonaws.com for cross-account assumable role. Leave empty otherwise. | `set(string)` | `[]` | no | +| [oidc\_audiences](#input\_oidc\_audiences) | The audience to be added to the role policy. Set to sts.amazonaws.com for cross-account assumable role. Leave empty otherwise. | `list(string)` | `[]` | no | | [oidc\_provider\_urls](#input\_oidc\_provider\_urls) | List of URLs of the OIDC Providers | `list(string)` | `[]` | no | -| [oidc\_subjects](#input\_oidc\_subjects) | The fully qualified OIDC subjects to be added to the role policy | `set(string)` | `[]` | no | -| [oidc\_wildcard\_subjects](#input\_oidc\_wildcard\_subjects) | The OIDC subject using wildcards to be added to the role policy | `set(string)` | `[]` | no | +| [oidc\_subjects](#input\_oidc\_subjects) | The fully qualified OIDC subjects to be added to the role policy | `list(string)` | `[]` | no | +| [oidc\_wildcard\_subjects](#input\_oidc\_wildcard\_subjects) | The OIDC subject using wildcards to be added to the role policy | `list(string)` | `[]` | no | | [path](#input\_path) | Path of IAM role | `string` | `"/"` | no | | [permissions\_boundary](#input\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no | | [policies](#input\_policies) | Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no | diff --git a/modules/iam-role/main.tf b/modules/iam-role/main.tf index 1497317d..3efc0560 100644 --- a/modules/iam-role/main.tf +++ b/modules/iam-role/main.tf @@ -306,7 +306,7 @@ resource "aws_iam_role_policy_attachment" "this" { ################################################################################ locals { - create_iam_role_inline_policy = var.create && length(var.inline_policy_statements) > 0 + create_iam_role_inline_policy = var.create && var.inline_policy_statements != null } data "aws_iam_policy_document" "inline" { diff --git a/modules/iam-role/variables.tf b/modules/iam-role/variables.tf index 582100e7..f677218e 100644 --- a/modules/iam-role/variables.tf +++ b/modules/iam-role/variables.tf @@ -112,19 +112,19 @@ variable "oidc_provider_urls" { variable "oidc_subjects" { description = "The fully qualified OIDC subjects to be added to the role policy" - type = set(string) + type = list(string) default = [] } variable "oidc_wildcard_subjects" { description = "The OIDC subject using wildcards to be added to the role policy" - type = set(string) + type = list(string) default = [] } variable "oidc_audiences" { description = "The audience to be added to the role policy. Set to sts.amazonaws.com for cross-account assumable role. Leave empty otherwise." - type = set(string) + type = list(string) default = [] } From d9ec0af25108023c36c1ea3905da2c67106c39a0 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Tue, 12 Aug 2025 19:49:45 -0500 Subject: [PATCH 14/21] fix: Correct lint errors --- examples/iam-role/README.md | 8 ++++ examples/iam-role/main.tf | 2 +- examples/iam-role/outputs.tf | 39 +++++++++++++++++++ modules/iam-role-for-service-accounts/main.tf | 2 +- 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/examples/iam-role/README.md b/examples/iam-role/README.md index e68fe3f5..28a0c076 100644 --- a/examples/iam-role/README.md +++ b/examples/iam-role/README.md @@ -32,6 +32,7 @@ Run `terraform destroy` when you don't need these resources. | Name | Source | Version | |------|--------|---------| +| [iam\_role\_circleci\_oidc](#module\_iam\_role\_circleci\_oidc) | ../../modules/iam-role | n/a | | [iam\_role\_disabled](#module\_iam\_role\_disabled) | ../../modules/iam-role | n/a | | [iam\_role\_github\_oidc](#module\_iam\_role\_github\_oidc) | ../../modules/iam-role | n/a | | [iam\_role\_instance\_profile](#module\_iam\_role\_instance\_profile) | ../../modules/iam-role | n/a | @@ -54,6 +55,13 @@ No inputs. | Name | Description | |------|-------------| +| [circleci\_oidc\_iam\_instance\_profile\_arn](#output\_circleci\_oidc\_iam\_instance\_profile\_arn) | ARN assigned by AWS to the instance profile | +| [circleci\_oidc\_iam\_instance\_profile\_id](#output\_circleci\_oidc\_iam\_instance\_profile\_id) | Instance profile's ID | +| [circleci\_oidc\_iam\_instance\_profile\_name](#output\_circleci\_oidc\_iam\_instance\_profile\_name) | Name of IAM instance profile | +| [circleci\_oidc\_iam\_instance\_profile\_unique\_id](#output\_circleci\_oidc\_iam\_instance\_profile\_unique\_id) | Stable and unique string identifying the IAM instance profile | +| [circleci\_oidc\_iam\_role\_arn](#output\_circleci\_oidc\_iam\_role\_arn) | The Amazon Resource Name (ARN) specifying the IAM role | +| [circleci\_oidc\_iam\_role\_name](#output\_circleci\_oidc\_iam\_role\_name) | The name of the IAM role | +| [circleci\_oidc\_iam\_role\_unique\_id](#output\_circleci\_oidc\_iam\_role\_unique\_id) | Stable and unique string identifying the IAM role | | [github\_oidc\_iam\_instance\_profile\_arn](#output\_github\_oidc\_iam\_instance\_profile\_arn) | ARN assigned by AWS to the instance profile | | [github\_oidc\_iam\_instance\_profile\_id](#output\_github\_oidc\_iam\_instance\_profile\_id) | Instance profile's ID | | [github\_oidc\_iam\_instance\_profile\_name](#output\_github\_oidc\_iam\_instance\_profile\_name) | Name of IAM instance profile | diff --git a/examples/iam-role/main.tf b/examples/iam-role/main.tf index 50fb58f9..51b174df 100644 --- a/examples/iam-role/main.tf +++ b/examples/iam-role/main.tf @@ -144,7 +144,7 @@ module "iam_role_github_oidc" { # IAM Role - CircleCI OIDC ################################################################################ -module "iam_role_github_oidc" { +module "iam_role_circleci_oidc" { source = "../../modules/iam-role" name = local.name diff --git a/examples/iam-role/outputs.tf b/examples/iam-role/outputs.tf index 9240cf4b..db078f54 100644 --- a/examples/iam-role/outputs.tf +++ b/examples/iam-role/outputs.tf @@ -76,6 +76,45 @@ output "github_oidc_iam_instance_profile_unique_id" { value = module.iam_role_github_oidc.instance_profile_unique_id } +################################################################################ +# IAM Role - CircleCI OIDC +################################################################################ + +output "circleci_oidc_iam_role_name" { + description = "The name of the IAM role" + value = module.iam_role_circleci_oidc.name +} + +output "circleci_oidc_iam_role_arn" { + description = "The Amazon Resource Name (ARN) specifying the IAM role" + value = module.iam_role_circleci_oidc.arn +} + +output "circleci_oidc_iam_role_unique_id" { + description = "Stable and unique string identifying the IAM role" + value = module.iam_role_circleci_oidc.unique_id +} + +output "circleci_oidc_iam_instance_profile_arn" { + description = "ARN assigned by AWS to the instance profile" + value = module.iam_role_circleci_oidc.instance_profile_arn +} + +output "circleci_oidc_iam_instance_profile_id" { + description = "Instance profile's ID" + value = module.iam_role_circleci_oidc.instance_profile_id +} + +output "circleci_oidc_iam_instance_profile_name" { + description = "Name of IAM instance profile" + value = module.iam_role_circleci_oidc.instance_profile_name +} + +output "circleci_oidc_iam_instance_profile_unique_id" { + description = "Stable and unique string identifying the IAM instance profile" + value = module.iam_role_circleci_oidc.instance_profile_unique_id +} + ################################################################################ # IAM Role - SAML 2.0 ################################################################################ diff --git a/modules/iam-role-for-service-accounts/main.tf b/modules/iam-role-for-service-accounts/main.tf index 44a50340..5505aee1 100644 --- a/modules/iam-role-for-service-accounts/main.tf +++ b/modules/iam-role-for-service-accounts/main.tf @@ -169,7 +169,7 @@ resource "aws_iam_role_policy_attachment" "this" { ################################################################################ locals { - create_iam_role_inline_policy = var.create && var.inline_policy_statements + create_iam_role_inline_policy = var.create && var.inline_policy_statements != null } data "aws_iam_policy_document" "inline" { From 5e60ce7fd2b4a1819a9591e88c2dda572f8c0324 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Tue, 12 Aug 2025 19:52:35 -0500 Subject: [PATCH 15/21] feat: Faster CI; remove unecessary files due to disk space (but faster) --- .github/workflows/pre-commit.yml | 35 ++++---------------------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 68c43a43..793b8f4f 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -32,13 +32,8 @@ jobs: matrix: directory: ${{ fromJson(needs.collectInputs.outputs.directories) }} steps: - # https://github.com/orgs/community/discussions/25678#discussioncomment-5242449 - - name: Delete huge unnecessary tools folder - run: | - rm -rf /opt/hostedtoolcache/CodeQL - rm -rf /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk - rm -rf /opt/hostedtoolcache/Ruby - rm -rf /opt/hostedtoolcache/go + - name: Delete unnecessary files + uses: xd009642/ci-hoover@0.1.1 - name: Checkout uses: actions/checkout@v4 @@ -72,30 +67,8 @@ jobs: runs-on: ubuntu-latest needs: collectInputs steps: - # https://github.com/orgs/community/discussions/25678#discussioncomment-5242449 - - name: Delete huge unnecessary tools folder - run: | - df -h - rm -rf /opt/hostedtoolcache/CodeQL - rm -rf /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk - rm -rf /opt/hostedtoolcache/Ruby - rm -rf /opt/hostedtoolcache/go - # And a little bit more - sudo apt-get -qq remove -y 'azure-.*' - sudo apt-get -qq remove -y 'cpp-.*' - sudo apt-get -qq remove -y 'dotnet-runtime-.*' - sudo apt-get -qq remove -y 'google-.*' - sudo apt-get -qq remove -y 'libclang-.*' - sudo apt-get -qq remove -y 'libllvm.*' - sudo apt-get -qq remove -y 'llvm-.*' - sudo apt-get -qq remove -y 'mysql-.*' - sudo apt-get -qq remove -y 'postgresql-.*' - sudo apt-get -qq remove -y 'php.*' - sudo apt-get -qq remove -y 'temurin-.*' - sudo apt-get -qq remove -y kubectl firefox mono-devel - sudo apt-get -qq autoremove -y - sudo apt-get -qq clean - df -h + - name: Delete unnecessary files + uses: xd009642/ci-hoover@0.1.1 - name: Checkout uses: actions/checkout@v4 From 9b381af7c97ab871d77e583ec8b8a7501333b450 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Wed, 13 Aug 2025 12:11:42 -0500 Subject: [PATCH 16/21] feat: Last minute variable name changes for improved ergonomics --- README.md | 13 +++-- docs/UPGRADE-6.0.md | 47 +++++++++++----- examples/iam-group/main.tf | 2 +- examples/iam-role/main.tf | 4 +- modules/iam-account/README.md | 2 +- modules/iam-group/README.md | 4 +- modules/iam-group/main.tf | 4 +- modules/iam-group/variables.tf | 4 +- modules/iam-read-only-policy/README.md | 7 ++- modules/iam-read-only-policy/main.tf | 3 +- modules/iam-read-only-policy/variables.tf | 18 ++++-- .../iam-role-for-service-accounts/README.md | 54 ++++++++---------- modules/iam-role-for-service-accounts/main.tf | 13 +++-- .../variables.tf | 24 +++++++- modules/iam-role/README.md | 17 +++--- modules/iam-role/main.tf | 17 +++--- modules/iam-role/variables.tf | 28 ++++++++-- modules/iam-user/README.md | 2 +- modules/iam-user/variables.tf | 2 +- wrappers/iam-group/main.tf | 2 +- wrappers/iam-read-only-policy/main.tf | 27 ++++----- .../iam-role-for-service-accounts/main.tf | 9 ++- wrappers/iam-role/main.tf | 55 ++++++++++--------- wrappers/iam-user/main.tf | 2 +- 24 files changed, 218 insertions(+), 142 deletions(-) diff --git a/README.md b/README.md index 687add65..634077d6 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ module "iam_group" { ] enable_self_management_permissions = true - permission_statements = { + permissions = { AssumeRole = { actions = ["sts:AssumeRole"] resources = ["arn:aws:iam::111111111111:role/admin"] @@ -67,7 +67,8 @@ module "iam_group" { Creates an OpenID connect provider. Useful for trusting external identity providers such as GitHub, Bitbucket, etc. -⚠️ An IAM provider is 1 per account per given URL. This module would be provisioned once per AWS account, and then one or more roles can be created with this provider as the trusted identity. +> [!TIP] +> An IAM provider is 1 per account per given URL. This module would be provisioned once per AWS account, and then one or more roles can be created with this provider as the trusted identity. ```hcl module "iam_oidc_provider" { @@ -113,7 +114,7 @@ module "iam_role" { name = "example" - assume_role_policy_statements = { + trust_policy_permissions = { TrustRoleAndServiceToAssume = { principals = [{ type = "AWS" @@ -121,7 +122,7 @@ module "iam_role" { "arn:aws:iam::835367859851:user/anton", ] }] - conditions = [{ + condition = [{ test = "StringEquals" variable = "sts:ExternalId" values = ["some-secret-id"] @@ -153,7 +154,7 @@ module "iam_role_github_oidc" { enable_github_oidc = true # This should be updated to suit your organization, repository, references/branches, etc. - oidc_subjects = ["terraform-aws-modules/terraform-aws-iam:*"] + oidc_wildcard_subjects = ["terraform-aws-modules/terraform-aws-iam:*"] policies = { S3ReadOnly = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" @@ -200,7 +201,7 @@ Creates an IAM role that is suitable for EKS IAM role for service accounts (IRSA ```hcl module "vpc_cni_irsa" { - source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts" + source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts" name = "vpc-cni" diff --git a/docs/UPGRADE-6.0.md b/docs/UPGRADE-6.0.md index 81ad43b4..56d5a3b5 100644 --- a/docs/UPGRADE-6.0.md +++ b/docs/UPGRADE-6.0.md @@ -6,6 +6,8 @@ If you find a bug, please open an issue with supporting configuration to reprodu ## List of backwards incompatible changes +- Terraform `v1.5.7` is now minimum supported version +- AWS provider `v6.0.0` is now minimum supported version - The ability to allow roles to assume their own roles has been removed. This was previously added as part of helping users mitigate https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/. Going forward, users will need to mitigate this on the application side (i.e. - do not have a role assume itself), or update the trust policy in their implementation to continue using this behavior. It is strongly recommended to mitigate this by not having the role assume itself. - `iam-account`: @@ -21,11 +23,13 @@ If you find a bug, please open an issue with supporting configuration to reprodu - `iam-group-with-assumable-roles-policy` has been merged into `iam-group` - `iam-eks-role` has been removed; `iam-role-for-service-accounts` or [`eks-pod-identity`](https://github.com/terraform-aws-modules/terraform-aws-eks-pod-identity) should be used instead - `iam-policy` has been removed; the `aws_iam_policy` resource should be used directly instead -- `iam-role-for-service-accounts`: +- `iam-role-for-service-accounts-eks` has been renamed to `iam-role-for-service-accounts` - Individual policy creation and attachment has been consolidated under one policy creation and attachment - Default values that enable permissive permissions have been removed; users will need to be explicit about the scope of access (i.e. ARNs) they provide when enabling permissions - AppMesh policy support has been removed due to service reaching end of support +### Module Consolidation Map + ```mermaid stateDiagram direction LR @@ -63,15 +67,20 @@ stateDiagram ### Modified +- Variable definitions now contain detailed `object` types in place of the previously used any type + - `iam-group` - Policy management has been updated to support extending the policy created by the sub-module, as well as adding additional policies that will be attached to the group - - The role assumption permissions has been removed from the policy; users can extend the policy to add this if needed via `permission_statements` + - The role assumption permissions has been removed from the policy; users can extend the policy to add this if needed via `permissions` - Default create conditional is now `true` instead of `false` - `iam-role` - - The use of individual variables to control/manipulate the assume role trust policy have been replaced by a generic `assume_role_policy_statements` variable. This allows for any number of custom statements to be added to the role's trust policy. + - The use of individual variables to control/manipulate the assume role trust policy have been replaced by a generic `trust_policy_permissions` variable. This allows for any number of custom statements to be added to the role's trust policy. - `custom_role_policy_arns` has been renamed to `policies` and now accepts a map of `name`: `policy-arn` pairs; this allows for both existing policies and policies that will get created at the same time as the role. This also replaces the admin, readonly, and poweruser policy ARN variables and their associated `attach_*_policy` variables. - Default create conditional is now `true` instead of `false` - `force_detach_policies` has been removed; this is now always `true` + - Support for inline policies has been added +- `iam-role-for-service-accounts` + - Support for inline policies has been added ### Variable and output changes @@ -85,7 +94,7 @@ stateDiagram - `iam-oidc-provider` - `additional_thumbprints` - no longer required by GitHub - `iam-read-only-policy` - - None + - `additional_policy_json` - use `source_inline_policy_documents` or `override_inline_policy_documents` instead - `iam-role` - `trusted_role_actions` - `trusted_role_arns` @@ -150,6 +159,7 @@ stateDiagram - `role_description` -> `description` - `role_policy_arns` -> `policies` - `ebs_csi_kms_cmk_ids` -> `ebs_csi_kms_cmk_arns` + - `assume_role_condition_test` -> `trust_condition_test` - `iam-user` - `create_user` -> `create` - `create_iam_user_login_profile` -> `create_login_profile` @@ -163,7 +173,7 @@ stateDiagram - `iam-account` - `create` - `iam-group` - - `permission_statements` which allows for any number of custom statements to be added to the role's trust policy. This covers the majority of the variables that were removed + - `permissions` which allows for any number of custom statements to be added to the role's trust policy. This covers the majority of the variables that were removed - `path`/`policy_path` - `create_policy` - `enable_mfa_enforcment` @@ -171,15 +181,26 @@ stateDiagram - None - `iam-read-only-policy` - `create` + - `source_policy_documents` + - `override_policy_documents` - `iam-role` - - `assume_role_policy_statements` which allows for any number of custom statements to be added to the role's trust policy. This covers the majority of the variables that were removed + - `trust_policy_permissions` which allows for any number of custom statements to be added to the role's trust policy. This covers the majority of the variables that were removed + - `trust_policy_conditions` + - `create_inline_policy` + - `source_inline_policy_documents` + - `override_inline_policy_documents` + - `inline_policy_permissions` - `iam-role-for-service-accounts` - `create_policy` - `source_policy_documents` - `override_policy_documents` - - `policy_statements` + - `permissions` - `policy_name` - `policy_description` + - `create_inline_policy` + - `source_inline_policy_documents` + - `override_inline_policy_documents` + - `inline_policy_permissions` - `iam-user` - None @@ -289,7 +310,7 @@ module "iam_role" { - "codedeploy.amazonaws.com" - ] - role_sts_externalid = ["some-id-goes-here"] -+ assume_role_policy_statements = { ++ trust_policy_permissions = { + TrustRoleAndServiceToAssume = { + actions = [ + "sts:AssumeRole", @@ -367,7 +388,7 @@ module "iam_role" { + } - provider_trust_policy_conditions = [ -+ condition = [ ++ trust_policy_conditions = [ { test = "StringLike" variable = "aws:RequestTag/Environment" @@ -467,7 +488,7 @@ module "iam_role_admin" { name = "admin" - assume_role_policy_statements = { + trust_policy_permissions = { TrustRoleAndServiceToAssume = { actions = [ "sts:AssumeRole", @@ -500,7 +521,7 @@ module "iam_role_poweruser" { name = "Billing-And-Support-Access" - assume_role_policy_statements = { + trust_policy_permissions = { TrustRoleAndServiceToAssume = { actions = [ "sts:AssumeRole", @@ -668,7 +689,7 @@ module "iam_role" { ] - additional_trust_policy_conditions = [ -+ condition = [ ++ trust_policy_conditions = [ { test = "StringEquals" variable = "token.actions.githubusercontent.com:actor" @@ -697,7 +718,7 @@ module "iam_group" { enable_self_management_permissions = false - assumable_roles = ["arn:aws:iam::111111111111:role/admin"] -+ permission_statements = { ++ permissions = { + AssumeRole = { + effect = "Allow" + actions = ["sts:AssumeRole"] diff --git a/examples/iam-group/main.tf b/examples/iam-group/main.tf index 2f045b66..e4e57151 100644 --- a/examples/iam-group/main.tf +++ b/examples/iam-group/main.tf @@ -26,7 +26,7 @@ module "iam_group" { module.iam_user2.name, ] - permission_statements = { + permissions = { AssumeRole = { actions = ["sts:AssumeRole"] resources = ["arn:aws:iam::111111111111:role/admin"] diff --git a/examples/iam-role/main.tf b/examples/iam-role/main.tf index 51b174df..6038b4d9 100644 --- a/examples/iam-role/main.tf +++ b/examples/iam-role/main.tf @@ -44,7 +44,7 @@ module "iam_roles" { name = each.key - assume_role_policy_statements = { + trust_policy_permissions = { TrustRoleAndServiceToAssume = { principals = [{ type = "AWS" @@ -88,7 +88,7 @@ module "iam_role_instance_profile" { create_instance_profile = true - assume_role_policy_statements = { + trust_policy_permissions = { TrustRoleAndServiceToAssume = { principals = [ { diff --git a/modules/iam-account/README.md b/modules/iam-account/README.md index f2cd2ff8..0232eb83 100644 --- a/modules/iam-account/README.md +++ b/modules/iam-account/README.md @@ -6,7 +6,7 @@ Creates an account policy and account alias. Module instantiation is once per ac ```hcl module "iam_account" { - source = "terraform-aws-modules/iam/aws//modules/iam-account" + source = "terraform-aws-modules/iam/aws//modules/iam-account" account_alias = "awesome-company" diff --git a/modules/iam-group/README.md b/modules/iam-group/README.md index 4e00ec8a..f05a3efb 100644 --- a/modules/iam-group/README.md +++ b/modules/iam-group/README.md @@ -17,7 +17,7 @@ module "iam_group" { ] enable_self_management_permissions = true - permission_statements = { + permissions = { AssumeRole = { actions = ["sts:AssumeRole"] resources = ["arn:aws:iam::111111111111:role/admin"] @@ -76,7 +76,7 @@ No modules. | [enable\_self\_management\_permissions](#input\_enable\_self\_management\_permissions) | Determines whether permissions are added to the policy which allow the groups IAM users to manage their credentials and MFA | `bool` | `true` | no | | [name](#input\_name) | The group's name. The name must consist of upper and lowercase alphanumeric characters with no spaces. You can also include any of the following characters: `=,.@-_.` | `string` | `""` | no | | [path](#input\_path) | Path in which to create the group | `string` | `null` | no | -| [permission\_statements](#input\_permission\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage |
map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
variable = string
values = list(string)
})))
}))
| `null` | no | +| [permissions](#input\_permissions) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permissions |
map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
variable = string
values = list(string)
})))
}))
| `null` | no | | [policies](#input\_policies) | Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no | | [policy\_description](#input\_policy\_description) | Description of the IAM policy | `string` | `null` | no | | [policy\_name](#input\_policy\_name) | Name to use on IAM policy created | `string` | `null` | no | diff --git a/modules/iam-group/main.tf b/modules/iam-group/main.tf index 3d440358..cb00e18a 100644 --- a/modules/iam-group/main.tf +++ b/modules/iam-group/main.tf @@ -39,7 +39,7 @@ resource "aws_iam_group_membership" "this" { ################################################################################ locals { - create_policy = var.create && var.create_policy && (var.enable_self_management_permissions || var.permission_statements != null) + create_policy = var.create && var.create_policy && (var.enable_self_management_permissions || var.permissions != null) policy_name = try(coalesce(var.policy_name, var.name), "") } @@ -198,7 +198,7 @@ data "aws_iam_policy_document" "this" { } dynamic "statement" { - for_each = var.permission_statements != null ? var.permission_statements : {} + for_each = var.permissions != null ? var.permissions : {} content { sid = try(coalesce(statement.value.sid, statement.key)) diff --git a/modules/iam-group/variables.tf b/modules/iam-group/variables.tf index 17e62156..87b3bdd7 100644 --- a/modules/iam-group/variables.tf +++ b/modules/iam-group/variables.tf @@ -54,8 +54,8 @@ variable "enable_mfa_enforcment" { default = true } -variable "permission_statements" { - description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage" +variable "permissions" { + description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permissions" type = map(object({ sid = optional(string) actions = optional(list(string)) diff --git a/modules/iam-read-only-policy/README.md b/modules/iam-read-only-policy/README.md index d541f619..fd10d0ca 100644 --- a/modules/iam-read-only-policy/README.md +++ b/modules/iam-read-only-policy/README.md @@ -52,16 +52,17 @@ No modules. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [additional\_policy\_json](#input\_additional\_policy\_json) | JSON policy document if you want to add custom actions | `string` | `""` | no | | [allow\_cloudwatch\_logs\_query](#input\_allow\_cloudwatch\_logs\_query) | Allows StartQuery/StopQuery/FilterLogEvents CloudWatch actions | `bool` | `true` | no | | [allow\_predefined\_sts\_actions](#input\_allow\_predefined\_sts\_actions) | Allows GetCallerIdentity/GetSessionToken/GetAccessKeyInfo sts actions | `bool` | `true` | no | | [allow\_web\_console\_services](#input\_allow\_web\_console\_services) | Allows List/Get/Describe/View actions for services used when browsing AWS console (e.g. resource-groups, tag, health services) | `bool` | `true` | no | | [allowed\_services](#input\_allowed\_services) | List of services to allow Get/List/Describe/View options. Service name should be the same as corresponding service IAM prefix. See what it is for each service here https://docs.aws.amazon.com/service-authorization/latest/reference/reference_policies_actions-resources-contextkeys.html | `list(string)` | `[]` | no | | [create](#input\_create) | Controls if resources should be created (affects all resources) | `bool` | `true` | no | | [create\_policy](#input\_create\_policy) | Controls if IAM policy should be created. Set to `false` to generate the policy JSON without creating the policy itself | `bool` | `true` | no | -| [description](#input\_description) | The description of the policy | `string` | `"IAM Policy"` | no | +| [description](#input\_description) | The description of the policy | `string` | `null` | no | | [name](#input\_name) | Name to use on IAM policy created | `string` | `null` | no | -| [path](#input\_path) | Path of IAM policy | `string` | `"/"` | no | +| [override\_inline\_policy\_documents](#input\_override\_inline\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. In merging, statements with non-blank `sid`s will override statements with the same `sid` | `list(string)` | `[]` | no | +| [path](#input\_path) | Path of IAM policy | `string` | `null` | no | +| [source\_inline\_policy\_documents](#input\_source\_inline\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s | `list(string)` | `[]` | no | | [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | | [use\_name\_prefix](#input\_use\_name\_prefix) | Determines whether the IAM policy name (`name`) is used as a prefix | `bool` | `true` | no | | [web\_console\_services](#input\_web\_console\_services) | List of web console services to allow | `list(string)` |
[
"resource-groups",
"tag",
"health",
"ce"
]
| no | diff --git a/modules/iam-read-only-policy/main.tf b/modules/iam-read-only-policy/main.tf index f3e879e2..7a61badf 100644 --- a/modules/iam-read-only-policy/main.tf +++ b/modules/iam-read-only-policy/main.tf @@ -17,7 +17,8 @@ resource "aws_iam_policy" "policy" { data "aws_iam_policy_document" "this" { count = var.create ? 1 : 0 - source_policy_documents = [var.additional_policy_json] + source_policy_documents = var.source_inline_policy_documents + override_policy_documents = var.override_inline_policy_documents dynamic "statement" { for_each = toset(distinct(var.allowed_services)) diff --git a/modules/iam-read-only-policy/variables.tf b/modules/iam-read-only-policy/variables.tf index 1ec3462e..b53fea72 100644 --- a/modules/iam-read-only-policy/variables.tf +++ b/modules/iam-read-only-policy/variables.tf @@ -35,13 +35,13 @@ variable "use_name_prefix" { variable "path" { description = "Path of IAM policy" type = string - default = "/" + default = null } variable "description" { description = "The description of the policy" type = string - default = "IAM Policy" + default = null } variable "allowed_services" { @@ -50,10 +50,16 @@ variable "allowed_services" { default = [] } -variable "additional_policy_json" { - description = "JSON policy document if you want to add custom actions" - type = string - default = "" +variable "source_inline_policy_documents" { + description = "List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s" + type = list(string) + default = [] +} + +variable "override_inline_policy_documents" { + description = "List of IAM policy documents that are merged together into the exported document. In merging, statements with non-blank `sid`s will override statements with the same `sid`" + type = list(string) + default = [] } variable "allow_cloudwatch_logs_query" { diff --git a/modules/iam-role-for-service-accounts/README.md b/modules/iam-role-for-service-accounts/README.md index 3c56f951..104e2cb4 100644 --- a/modules/iam-role-for-service-accounts/README.md +++ b/modules/iam-role-for-service-accounts/README.md @@ -32,12 +32,9 @@ This module supports multiple `ServiceAccount`s across multiple clusters and/or ```hcl module "irsa" { - source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" - role_name = "my-app" + source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts" - role_policy_arns = { - policy = "arn:aws:iam::012345678901:policy/myapp" - } + name = "my-app" oidc_providers = { one = { @@ -49,42 +46,33 @@ module "irsa" { namespace_service_accounts = ["default:my-app-staging"] } } + + policies = { + policy = "arn:aws:iam::012345678901:policy/myapp" + } } ``` This module has been designed in conjunction with the [`terraform-aws-eks`](https://github.com/terraform-aws-modules/terraform-aws-eks) module to easily integrate with it: ```hcl -module "vpc_cni_irsa_role" { - source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" +module "ebs_csi_driver_irsa" { + source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts" - role_name = "vpc-cni" + name = "ebs-csi" - attach_vpc_cni_policy = true - vpc_cni_enable_ipv4 = true + attach_ebs_csi_policy = true oidc_providers = { this = { provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["kube-system:aws-node"] + namespace_service_accounts = ["kube-system:ebs-csi-controller-sa"] } } -} - -module "karpenter_irsa_role" { - source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" - role_name = "karpenter" - - attach_karpenter_policy = true - karpenter_cluster_name = module.eks.cluster_name - karpenter_node_iam_role_arns = [module.eks.eks_managed_node_groups["default"].iam_role_arn] - - oidc_providers = { - this = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["karpenter:karpenter"] - } + tags = { + Terraform = "true" + Environment = "dev" } } @@ -99,7 +87,10 @@ module "eks" { subnet_ids = module.vpc.private_subnets addons = { - coredns = {} + coredns = {} + aws-ebs-csi-driver = { + service_account_role_arn = module.ebs_csi_driver_irsa.iam_role_arn + } kube-proxy = {} vpc-cni = { before_compute = true @@ -173,7 +164,6 @@ No modules. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [amazon\_managed\_service\_prometheus\_workspace\_arns](#input\_amazon\_managed\_service\_prometheus\_workspace\_arns) | List of AMP Workspace ARNs to read and write metrics | `list(string)` | `[]` | no | -| [assume\_role\_condition\_test](#input\_assume\_role\_condition\_test) | Name of the [IAM condition operator](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition_operators.html) to evaluate when assuming the role | `string` | `"StringEquals"` | no | | [attach\_amazon\_managed\_service\_prometheus\_policy](#input\_attach\_amazon\_managed\_service\_prometheus\_policy) | Determines whether to attach the Amazon Managed Service for Prometheus IAM policy to the role | `bool` | `false` | no | | [attach\_aws\_gateway\_controller\_policy](#input\_attach\_aws\_gateway\_controller\_policy) | Determines whether to attach the AWS Gateway Controller IAM policy to the role | `bool` | `false` | no | | [attach\_cert\_manager\_policy](#input\_attach\_cert\_manager\_policy) | Determines whether to attach the Cert Manager IAM policy to the role | `bool` | `false` | no | @@ -194,6 +184,7 @@ No modules. | [cert\_manager\_hosted\_zone\_arns](#input\_cert\_manager\_hosted\_zone\_arns) | Route53 hosted zone ARNs to allow Cert manager to manage records | `list(string)` | `[]` | no | | [cluster\_autoscaler\_cluster\_names](#input\_cluster\_autoscaler\_cluster\_names) | List of cluster names to appropriately scope permissions within the Cluster Autoscaler IAM policy | `list(string)` | `[]` | no | | [create](#input\_create) | Controls if resources should be created (affects all resources) | `bool` | `true` | no | +| [create\_inline\_policy](#input\_create\_inline\_policy) | Determines whether to create an inline policy | `bool` | `false` | no | | [create\_policy](#input\_create\_policy) | Whether to create an IAM policy that is attached to the IAM role created | `bool` | `true` | no | | [description](#input\_description) | Description of the role | `string` | `null` | no | | [ebs\_csi\_kms\_cmk\_arns](#input\_ebs\_csi\_kms\_cmk\_arns) | KMS CMK ARNs to allow EBS CSI to manage encrypted volumes | `list(string)` | `[]` | no | @@ -204,7 +195,7 @@ No modules. | [external\_secrets\_ssm\_parameter\_arns](#input\_external\_secrets\_ssm\_parameter\_arns) | List of Systems Manager Parameter ARNs that contain secrets to mount using External Secrets | `list(string)` | `[]` | no | | [fsx\_lustre\_csi\_service\_role\_arns](#input\_fsx\_lustre\_csi\_service\_role\_arns) | Service role ARNs to allow FSx for Lustre CSI create and manage FSX for Lustre service linked roles | `list(string)` |
[
"arn:aws:iam::*:role/aws-service-role/s3.data-source.lustre.fsx.amazonaws.com/*"
]
| no | | [fsx\_openzfs\_csi\_service\_role\_arns](#input\_fsx\_openzfs\_csi\_service\_role\_arns) | Service role ARNs to allow FSx for OpenZFS CSI create and manage FSX for openzfs service linked roles | `list(string)` |
[
"arn:aws:iam::*:role/aws-service-role/fsx.amazonaws.com/*"
]
| no | -| [inline\_policy\_statements](#input\_inline\_policy\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for inline policy permissions |
map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
variable = string
values = list(string)
})))
}))
| `null` | no | +| [inline\_policy\_permissions](#input\_inline\_policy\_permissions) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for inline policy permissions |
map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
variable = string
values = list(string)
})))
}))
| `null` | no | | [load\_balancer\_controller\_targetgroup\_arns](#input\_load\_balancer\_controller\_targetgroup\_arns) | List of Target groups ARNs using Load Balancer Controller | `list(string)` | `[]` | no | | [max\_session\_duration](#input\_max\_session\_duration) | Maximum session duration (in seconds) that you want to set for the specified role. If you do not specify a value for this setting, the default maximum of one hour is applied. This setting can have a value from 1 hour to 12 hours | `number` | `null` | no | | [mountpoint\_s3\_csi\_bucket\_arns](#input\_mountpoint\_s3\_csi\_bucket\_arns) | S3 bucket ARNs to allow Mountpoint S3 CSI to list buckets | `list(string)` | `[]` | no | @@ -213,16 +204,19 @@ No modules. | [name](#input\_name) | Name to use on IAM role created | `string` | `null` | no | | [node\_termination\_handler\_sqs\_queue\_arns](#input\_node\_termination\_handler\_sqs\_queue\_arns) | List of SQS ARNs that contain node termination events | `list(string)` | `[]` | no | | [oidc\_providers](#input\_oidc\_providers) | Map of OIDC providers where each provider map should contain the `provider`, `provider_arn`, and `namespace_service_accounts` | `any` | `{}` | no | +| [override\_inline\_policy\_documents](#input\_override\_inline\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. In merging, statements with non-blank `sid`s will override statements with the same `sid` | `list(string)` | `[]` | no | | [override\_policy\_documents](#input\_override\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. In merging, statements with non-blank `sid`s will override statements with the same `sid` | `list(string)` | `[]` | no | | [path](#input\_path) | Path of IAM role | `string` | `"/"` | no | +| [permissions](#input\_permissions) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage |
map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
variable = string
values = list(string)
})))
}))
| `null` | no | | [permissions\_boundary](#input\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no | | [policies](#input\_policies) | Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no | | [policy\_description](#input\_policy\_description) | IAM policy description | `string` | `null` | no | | [policy\_name](#input\_policy\_name) | Name to use on IAM policy created | `string` | `null` | no | | [policy\_path](#input\_policy\_path) | Path of IAM policy | `string` | `null` | no | -| [policy\_statements](#input\_policy\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage |
map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
variable = string
values = list(string)
})))
}))
| `null` | no | +| [source\_inline\_policy\_documents](#input\_source\_inline\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s | `list(string)` | `[]` | no | | [source\_policy\_documents](#input\_source\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s | `list(string)` | `[]` | no | | [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | +| [trust\_condition\_test](#input\_trust\_condition\_test) | Name of the [IAM condition operator](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition_operators.html) to evaluate when assuming the role | `string` | `"StringEquals"` | no | | [use\_name\_prefix](#input\_use\_name\_prefix) | Determines whether the IAM role/policy name (`name`/`policy_name`) is used as a prefix | `bool` | `true` | no | | [velero\_s3\_bucket\_arns](#input\_velero\_s3\_bucket\_arns) | List of S3 Bucket ARNs that Velero needs access to in order to backup and restore cluster resources | `list(string)` | `[]` | no | | [vpc\_cni\_enable\_cloudwatch\_logs](#input\_vpc\_cni\_enable\_cloudwatch\_logs) | Determines whether to enable VPC CNI permission to create CloudWatch Log groups and publish network policy events | `bool` | `false` | no | diff --git a/modules/iam-role-for-service-accounts/main.tf b/modules/iam-role-for-service-accounts/main.tf index 5505aee1..8f292546 100644 --- a/modules/iam-role-for-service-accounts/main.tf +++ b/modules/iam-role-for-service-accounts/main.tf @@ -27,14 +27,14 @@ data "aws_iam_policy_document" "assume" { } condition { - test = var.assume_role_condition_test + test = var.trust_condition_test variable = "${replace(statement.value.provider_arn, "/^(.*provider/)/", "")}:sub" values = [for sa in statement.value.namespace_service_accounts : "system:serviceaccount:${sa}"] } # https://aws.amazon.com/premiumsupport/knowledge-center/eks-troubleshoot-oidc-and-irsa/?nc1=h_ls condition { - test = var.assume_role_condition_test + test = var.trust_condition_test variable = "${replace(statement.value.provider_arn, "/^(.*provider/)/", "")}:aud" values = ["sts.amazonaws.com"] } @@ -100,7 +100,7 @@ data "aws_iam_policy_document" "this" { override_policy_documents = var.override_policy_documents dynamic "statement" { - for_each = var.policy_statements != null ? var.policy_statements : {} + for_each = var.permissions != null ? var.permissions : {} content { sid = try(coalesce(statement.value.sid, statement.key)) @@ -169,14 +169,17 @@ resource "aws_iam_role_policy_attachment" "this" { ################################################################################ locals { - create_iam_role_inline_policy = var.create && var.inline_policy_statements != null + create_iam_role_inline_policy = var.create && var.create_inline_policy } data "aws_iam_policy_document" "inline" { count = local.create_iam_role_inline_policy ? 1 : 0 + source_policy_documents = var.source_inline_policy_documents + override_policy_documents = var.override_inline_policy_documents + dynamic "statement" { - for_each = var.inline_policy_statements != null ? var.inline_policy_statements : {} + for_each = var.inline_policy_permissions != null ? var.inline_policy_permissions : {} content { sid = try(coalesce(statement.value.sid, statement.key)) diff --git a/modules/iam-role-for-service-accounts/variables.tf b/modules/iam-role-for-service-accounts/variables.tf index dc994bf1..085388ed 100644 --- a/modules/iam-role-for-service-accounts/variables.tf +++ b/modules/iam-role-for-service-accounts/variables.tf @@ -50,7 +50,7 @@ variable "permissions_boundary" { default = null } -variable "assume_role_condition_test" { +variable "trust_condition_test" { description = "Name of the [IAM condition operator](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition_operators.html) to evaluate when assuming the role" type = string default = "StringEquals" @@ -90,7 +90,7 @@ variable "override_policy_documents" { default = [] } -variable "policy_statements" { +variable "permissions" { description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage" type = map(object({ sid = optional(string) @@ -382,7 +382,25 @@ variable "attach_cloudwatch_observability_policy" { # IAM Role Inline policy ################################################################################ -variable "inline_policy_statements" { +variable "create_inline_policy" { + description = "Determines whether to create an inline policy" + type = bool + default = false +} + +variable "source_inline_policy_documents" { + description = "List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s" + type = list(string) + default = [] +} + +variable "override_inline_policy_documents" { + description = "List of IAM policy documents that are merged together into the exported document. In merging, statements with non-blank `sid`s will override statements with the same `sid`" + type = list(string) + default = [] +} + +variable "inline_policy_permissions" { description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for inline policy permissions" type = map(object({ sid = optional(string) diff --git a/modules/iam-role/README.md b/modules/iam-role/README.md index 4ade20e2..c773567c 100644 --- a/modules/iam-role/README.md +++ b/modules/iam-role/README.md @@ -15,7 +15,7 @@ module "iam_oidc_role" { enable_github_oidc = true # This should be updated to suit your organization, repository, references/branches, etc. - oidc_subjects = ["terraform-aws-modules/terraform-aws-iam:*"] + oidc_wildcard_subjects = ["terraform-aws-modules/terraform-aws-iam:*"] policies = { S3ReadOnly = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" @@ -41,7 +41,7 @@ module "iam_oidc_role" { oidc_provider_urls = ["mygithub.com/_services/token"] # This should be updated to suit your organization, repository, references/branches, etc. - oidc_subjects = ["/terraform-aws-iam:*"] + oidc_wildcard_subjects = ["/terraform-aws-iam:*"] policies = { S3ReadOnly = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" @@ -61,7 +61,7 @@ module "iam_role" { name = "example" - assume_role_policy_statements = { + trust_policy_permissions = { TrustRoleAndServiceToAssume = { principals = [{ type = "AWS" @@ -152,9 +152,8 @@ No modules. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [assume\_role\_policy\_statements](#input\_assume\_role\_policy\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage |
map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
variable = string
values = list(string)
})))
}))
| `null` | no | -| [condition](#input\_condition) | [Condition constraints](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#condition) applied to the trust policy(s) enabled |
list(object({
test = string
variable = string
values = list(string)
}))
| `[]` | no | | [create](#input\_create) | Controls if resources should be created (affects all resources) | `bool` | `true` | no | +| [create\_inline\_policy](#input\_create\_inline\_policy) | Determines whether to create an inline policy | `bool` | `false` | no | | [create\_instance\_profile](#input\_create\_instance\_profile) | Determines whether to create an instance profile | `bool` | `false` | no | | [description](#input\_description) | Description of the role | `string` | `null` | no | | [enable\_bitbucket\_oidc](#input\_enable\_bitbucket\_oidc) | Enable Bitbucket OIDC provider trust for the role | `bool` | `false` | no | @@ -162,7 +161,7 @@ No modules. | [enable\_oidc](#input\_enable\_oidc) | Enable OIDC provider trust for the role | `bool` | `false` | no | | [enable\_saml](#input\_enable\_saml) | Enable SAML provider trust for the role | `bool` | `false` | no | | [github\_provider](#input\_github\_provider) | The GitHub OIDC provider URL *without the `https://` prefix | `string` | `"token.actions.githubusercontent.com"` | no | -| [inline\_policy\_statements](#input\_inline\_policy\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for inline policy permissions |
map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
variable = string
values = list(string)
})))
}))
| `null` | no | +| [inline\_policy\_permissions](#input\_inline\_policy\_permissions) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for inline policy permissions |
map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
variable = string
values = list(string)
})))
}))
| `null` | no | | [max\_session\_duration](#input\_max\_session\_duration) | Maximum session duration (in seconds) that you want to set for the specified role. If you do not specify a value for this setting, the default maximum of one hour is applied. This setting can have a value from 1 hour to 12 hours | `number` | `null` | no | | [name](#input\_name) | Name to use on IAM role created | `string` | `null` | no | | [oidc\_account\_id](#input\_oidc\_account\_id) | An overriding AWS account ID where the OIDC provider lives; leave empty to use the current account ID for the AWS provider | `string` | `null` | no | @@ -170,13 +169,17 @@ No modules. | [oidc\_provider\_urls](#input\_oidc\_provider\_urls) | List of URLs of the OIDC Providers | `list(string)` | `[]` | no | | [oidc\_subjects](#input\_oidc\_subjects) | The fully qualified OIDC subjects to be added to the role policy | `list(string)` | `[]` | no | | [oidc\_wildcard\_subjects](#input\_oidc\_wildcard\_subjects) | The OIDC subject using wildcards to be added to the role policy | `list(string)` | `[]` | no | -| [path](#input\_path) | Path of IAM role | `string` | `"/"` | no | +| [override\_inline\_policy\_documents](#input\_override\_inline\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. In merging, statements with non-blank `sid`s will override statements with the same `sid` | `list(string)` | `[]` | no | +| [path](#input\_path) | Path of IAM role | `string` | `null` | no | | [permissions\_boundary](#input\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no | | [policies](#input\_policies) | Policies to attach to the IAM role in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no | | [saml\_endpoints](#input\_saml\_endpoints) | List of AWS SAML endpoints | `list(string)` |
[
"https://signin.aws.amazon.com/saml"
]
| no | | [saml\_provider\_ids](#input\_saml\_provider\_ids) | List of SAML provider IDs | `list(string)` | `[]` | no | | [saml\_trust\_actions](#input\_saml\_trust\_actions) | Additional assume role trust actions for the SAML federated statement | `list(string)` | `[]` | no | +| [source\_inline\_policy\_documents](#input\_source\_inline\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s | `list(string)` | `[]` | no | | [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | +| [trust\_policy\_conditions](#input\_trust\_policy\_conditions) | [Condition constraints](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#condition) applied to the trust policy(s) enabled |
list(object({
test = string
variable = string
values = list(string)
}))
| `[]` | no | +| [trust\_policy\_permissions](#input\_trust\_policy\_permissions) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom trust policy permissions |
map(object({
sid = optional(string)
actions = optional(list(string))
not_actions = optional(list(string))
effect = optional(string, "Allow")
resources = optional(list(string))
not_resources = optional(list(string))
principals = optional(list(object({
type = string
identifiers = list(string)
})))
not_principals = optional(list(object({
type = string
identifiers = list(string)
})))
condition = optional(list(object({
test = string
variable = string
values = list(string)
})))
}))
| `null` | no | | [use\_name\_prefix](#input\_use\_name\_prefix) | Determines whether the IAM role name (`name`) is used as a prefix | `bool` | `true` | no | ## Outputs diff --git a/modules/iam-role/main.tf b/modules/iam-role/main.tf index 3efc0560..d2103cd8 100644 --- a/modules/iam-role/main.tf +++ b/modules/iam-role/main.tf @@ -69,7 +69,7 @@ data "aws_iam_policy_document" "this" { # Generic conditions dynamic "condition" { - for_each = var.condition + for_each = var.trust_policy_conditions content { test = condition.value.test @@ -132,7 +132,7 @@ data "aws_iam_policy_document" "this" { # Generic conditions dynamic "condition" { - for_each = var.condition + for_each = var.trust_policy_conditions content { test = condition.value.test @@ -187,7 +187,7 @@ data "aws_iam_policy_document" "this" { # Generic conditions dynamic "condition" { - for_each = var.condition + for_each = var.trust_policy_conditions content { test = condition.value.test @@ -224,7 +224,7 @@ data "aws_iam_policy_document" "this" { # Generic conditions dynamic "condition" { - for_each = var.condition + for_each = var.trust_policy_conditions content { test = condition.value.test @@ -237,7 +237,7 @@ data "aws_iam_policy_document" "this" { # Generic statements dynamic "statement" { - for_each = var.assume_role_policy_statements != null ? var.assume_role_policy_statements : {} + for_each = var.trust_policy_permissions != null ? var.trust_policy_permissions : {} content { sid = try(coalesce(statement.value.sid, statement.key)) @@ -306,14 +306,17 @@ resource "aws_iam_role_policy_attachment" "this" { ################################################################################ locals { - create_iam_role_inline_policy = var.create && var.inline_policy_statements != null + create_iam_role_inline_policy = var.create && var.create_inline_policy } data "aws_iam_policy_document" "inline" { count = local.create_iam_role_inline_policy ? 1 : 0 + source_policy_documents = var.source_inline_policy_documents + override_policy_documents = var.override_inline_policy_documents + dynamic "statement" { - for_each = var.inline_policy_statements != null ? var.inline_policy_statements : {} + for_each = var.inline_policy_permissions != null ? var.inline_policy_permissions : {} content { sid = try(coalesce(statement.value.sid, statement.key)) diff --git a/modules/iam-role/variables.tf b/modules/iam-role/variables.tf index f677218e..2107332c 100644 --- a/modules/iam-role/variables.tf +++ b/modules/iam-role/variables.tf @@ -29,7 +29,7 @@ variable "use_name_prefix" { variable "path" { description = "Path of IAM role" type = string - default = "/" + default = null } variable "description" { @@ -50,8 +50,8 @@ variable "permissions_boundary" { default = null } -variable "assume_role_policy_statements" { - description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage" +variable "trust_policy_permissions" { + description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom trust policy permissions" type = map(object({ sid = optional(string) actions = optional(list(string)) @@ -76,7 +76,7 @@ variable "assume_role_policy_statements" { default = null } -variable "condition" { +variable "trust_policy_conditions" { description = "[Condition constraints](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#condition) applied to the trust policy(s) enabled" type = list(object({ test = string @@ -174,7 +174,25 @@ variable "saml_trust_actions" { # IAM Role Inline policy ################################################################################ -variable "inline_policy_statements" { +variable "create_inline_policy" { + description = "Determines whether to create an inline policy" + type = bool + default = false +} + +variable "source_inline_policy_documents" { + description = "List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s" + type = list(string) + default = [] +} + +variable "override_inline_policy_documents" { + description = "List of IAM policy documents that are merged together into the exported document. In merging, statements with non-blank `sid`s will override statements with the same `sid`" + type = list(string) + default = [] +} + +variable "inline_policy_permissions" { description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for inline policy permissions" type = map(object({ sid = optional(string) diff --git a/modules/iam-user/README.md b/modules/iam-user/README.md index 7bc05ea5..8dbb3802 100644 --- a/modules/iam-user/README.md +++ b/modules/iam-user/README.md @@ -68,7 +68,7 @@ No modules. | [name](#input\_name) | Desired name for the IAM user | `string` | `""` | no | | [password\_length](#input\_password\_length) | The length of the generated password | `number` | `null` | no | | [password\_reset\_required](#input\_password\_reset\_required) | Whether the user should be forced to reset the generated password on first login | `bool` | `true` | no | -| [path](#input\_path) | Desired path for the IAM user | `string` | `"/"` | no | +| [path](#input\_path) | Desired path for the IAM user | `string` | `null` | no | | [permissions\_boundary](#input\_permissions\_boundary) | The ARN of the policy that is used to set the permissions boundary for the user | `string` | `null` | no | | [pgp\_key](#input\_pgp\_key) | Either a base-64 encoded PGP public key, or a keybase username in the form `keybase:username`. Used to encrypt password and access key | `string` | `null` | no | | [policies](#input\_policies) | Policies to attach to the IAM user in `{'static_name' = 'policy_arn'}` format | `map(string)` | `{}` | no | diff --git a/modules/iam-user/variables.tf b/modules/iam-user/variables.tf index 01e88872..b3945ee7 100644 --- a/modules/iam-user/variables.tf +++ b/modules/iam-user/variables.tf @@ -23,7 +23,7 @@ variable "name" { variable "path" { description = "Desired path for the IAM user" type = string - default = "/" + default = null } variable "permissions_boundary" { diff --git a/wrappers/iam-group/main.tf b/wrappers/iam-group/main.tf index 7be62384..1a0bd85f 100644 --- a/wrappers/iam-group/main.tf +++ b/wrappers/iam-group/main.tf @@ -9,7 +9,7 @@ module "wrapper" { enable_self_management_permissions = try(each.value.enable_self_management_permissions, var.defaults.enable_self_management_permissions, true) name = try(each.value.name, var.defaults.name, "") path = try(each.value.path, var.defaults.path, null) - permission_statements = try(each.value.permission_statements, var.defaults.permission_statements, null) + permissions = try(each.value.permissions, var.defaults.permissions, null) policies = try(each.value.policies, var.defaults.policies, {}) policy_description = try(each.value.policy_description, var.defaults.policy_description, null) policy_name = try(each.value.policy_name, var.defaults.policy_name, null) diff --git a/wrappers/iam-read-only-policy/main.tf b/wrappers/iam-read-only-policy/main.tf index 3bcb7fa8..a6df0657 100644 --- a/wrappers/iam-read-only-policy/main.tf +++ b/wrappers/iam-read-only-policy/main.tf @@ -3,17 +3,18 @@ module "wrapper" { for_each = var.items - additional_policy_json = try(each.value.additional_policy_json, var.defaults.additional_policy_json, "") - allow_cloudwatch_logs_query = try(each.value.allow_cloudwatch_logs_query, var.defaults.allow_cloudwatch_logs_query, true) - allow_predefined_sts_actions = try(each.value.allow_predefined_sts_actions, var.defaults.allow_predefined_sts_actions, true) - allow_web_console_services = try(each.value.allow_web_console_services, var.defaults.allow_web_console_services, true) - allowed_services = try(each.value.allowed_services, var.defaults.allowed_services, []) - create = try(each.value.create, var.defaults.create, true) - create_policy = try(each.value.create_policy, var.defaults.create_policy, true) - description = try(each.value.description, var.defaults.description, "IAM Policy") - name = try(each.value.name, var.defaults.name, null) - path = try(each.value.path, var.defaults.path, "/") - tags = try(each.value.tags, var.defaults.tags, {}) - use_name_prefix = try(each.value.use_name_prefix, var.defaults.use_name_prefix, true) - web_console_services = try(each.value.web_console_services, var.defaults.web_console_services, ["resource-groups", "tag", "health", "ce"]) + allow_cloudwatch_logs_query = try(each.value.allow_cloudwatch_logs_query, var.defaults.allow_cloudwatch_logs_query, true) + allow_predefined_sts_actions = try(each.value.allow_predefined_sts_actions, var.defaults.allow_predefined_sts_actions, true) + allow_web_console_services = try(each.value.allow_web_console_services, var.defaults.allow_web_console_services, true) + allowed_services = try(each.value.allowed_services, var.defaults.allowed_services, []) + create = try(each.value.create, var.defaults.create, true) + create_policy = try(each.value.create_policy, var.defaults.create_policy, true) + description = try(each.value.description, var.defaults.description, null) + name = try(each.value.name, var.defaults.name, null) + override_inline_policy_documents = try(each.value.override_inline_policy_documents, var.defaults.override_inline_policy_documents, []) + path = try(each.value.path, var.defaults.path, null) + source_inline_policy_documents = try(each.value.source_inline_policy_documents, var.defaults.source_inline_policy_documents, []) + tags = try(each.value.tags, var.defaults.tags, {}) + use_name_prefix = try(each.value.use_name_prefix, var.defaults.use_name_prefix, true) + web_console_services = try(each.value.web_console_services, var.defaults.web_console_services, ["resource-groups", "tag", "health", "ce"]) } diff --git a/wrappers/iam-role-for-service-accounts/main.tf b/wrappers/iam-role-for-service-accounts/main.tf index 866f680c..662810b2 100644 --- a/wrappers/iam-role-for-service-accounts/main.tf +++ b/wrappers/iam-role-for-service-accounts/main.tf @@ -4,7 +4,6 @@ module "wrapper" { for_each = var.items amazon_managed_service_prometheus_workspace_arns = try(each.value.amazon_managed_service_prometheus_workspace_arns, var.defaults.amazon_managed_service_prometheus_workspace_arns, []) - assume_role_condition_test = try(each.value.assume_role_condition_test, var.defaults.assume_role_condition_test, "StringEquals") attach_amazon_managed_service_prometheus_policy = try(each.value.attach_amazon_managed_service_prometheus_policy, var.defaults.attach_amazon_managed_service_prometheus_policy, false) attach_aws_gateway_controller_policy = try(each.value.attach_aws_gateway_controller_policy, var.defaults.attach_aws_gateway_controller_policy, false) attach_cert_manager_policy = try(each.value.attach_cert_manager_policy, var.defaults.attach_cert_manager_policy, false) @@ -25,6 +24,7 @@ module "wrapper" { cert_manager_hosted_zone_arns = try(each.value.cert_manager_hosted_zone_arns, var.defaults.cert_manager_hosted_zone_arns, []) cluster_autoscaler_cluster_names = try(each.value.cluster_autoscaler_cluster_names, var.defaults.cluster_autoscaler_cluster_names, []) create = try(each.value.create, var.defaults.create, true) + create_inline_policy = try(each.value.create_inline_policy, var.defaults.create_inline_policy, false) create_policy = try(each.value.create_policy, var.defaults.create_policy, true) description = try(each.value.description, var.defaults.description, null) ebs_csi_kms_cmk_arns = try(each.value.ebs_csi_kms_cmk_arns, var.defaults.ebs_csi_kms_cmk_arns, []) @@ -35,7 +35,7 @@ module "wrapper" { external_secrets_ssm_parameter_arns = try(each.value.external_secrets_ssm_parameter_arns, var.defaults.external_secrets_ssm_parameter_arns, []) fsx_lustre_csi_service_role_arns = try(each.value.fsx_lustre_csi_service_role_arns, var.defaults.fsx_lustre_csi_service_role_arns, ["arn:aws:iam::*:role/aws-service-role/s3.data-source.lustre.fsx.amazonaws.com/*"]) fsx_openzfs_csi_service_role_arns = try(each.value.fsx_openzfs_csi_service_role_arns, var.defaults.fsx_openzfs_csi_service_role_arns, ["arn:aws:iam::*:role/aws-service-role/fsx.amazonaws.com/*"]) - inline_policy_statements = try(each.value.inline_policy_statements, var.defaults.inline_policy_statements, null) + inline_policy_permissions = try(each.value.inline_policy_permissions, var.defaults.inline_policy_permissions, null) load_balancer_controller_targetgroup_arns = try(each.value.load_balancer_controller_targetgroup_arns, var.defaults.load_balancer_controller_targetgroup_arns, []) max_session_duration = try(each.value.max_session_duration, var.defaults.max_session_duration, null) mountpoint_s3_csi_bucket_arns = try(each.value.mountpoint_s3_csi_bucket_arns, var.defaults.mountpoint_s3_csi_bucket_arns, []) @@ -44,16 +44,19 @@ module "wrapper" { name = try(each.value.name, var.defaults.name, null) node_termination_handler_sqs_queue_arns = try(each.value.node_termination_handler_sqs_queue_arns, var.defaults.node_termination_handler_sqs_queue_arns, []) oidc_providers = try(each.value.oidc_providers, var.defaults.oidc_providers, {}) + override_inline_policy_documents = try(each.value.override_inline_policy_documents, var.defaults.override_inline_policy_documents, []) override_policy_documents = try(each.value.override_policy_documents, var.defaults.override_policy_documents, []) path = try(each.value.path, var.defaults.path, "/") + permissions = try(each.value.permissions, var.defaults.permissions, null) permissions_boundary = try(each.value.permissions_boundary, var.defaults.permissions_boundary, null) policies = try(each.value.policies, var.defaults.policies, {}) policy_description = try(each.value.policy_description, var.defaults.policy_description, null) policy_name = try(each.value.policy_name, var.defaults.policy_name, null) policy_path = try(each.value.policy_path, var.defaults.policy_path, null) - policy_statements = try(each.value.policy_statements, var.defaults.policy_statements, null) + source_inline_policy_documents = try(each.value.source_inline_policy_documents, var.defaults.source_inline_policy_documents, []) source_policy_documents = try(each.value.source_policy_documents, var.defaults.source_policy_documents, []) tags = try(each.value.tags, var.defaults.tags, {}) + trust_condition_test = try(each.value.trust_condition_test, var.defaults.trust_condition_test, "StringEquals") use_name_prefix = try(each.value.use_name_prefix, var.defaults.use_name_prefix, true) velero_s3_bucket_arns = try(each.value.velero_s3_bucket_arns, var.defaults.velero_s3_bucket_arns, []) vpc_cni_enable_cloudwatch_logs = try(each.value.vpc_cni_enable_cloudwatch_logs, var.defaults.vpc_cni_enable_cloudwatch_logs, false) diff --git a/wrappers/iam-role/main.tf b/wrappers/iam-role/main.tf index 1e2451bc..651a7b5a 100644 --- a/wrappers/iam-role/main.tf +++ b/wrappers/iam-role/main.tf @@ -3,30 +3,33 @@ module "wrapper" { for_each = var.items - assume_role_policy_statements = try(each.value.assume_role_policy_statements, var.defaults.assume_role_policy_statements, null) - condition = try(each.value.condition, var.defaults.condition, []) - create = try(each.value.create, var.defaults.create, true) - create_instance_profile = try(each.value.create_instance_profile, var.defaults.create_instance_profile, false) - description = try(each.value.description, var.defaults.description, null) - enable_bitbucket_oidc = try(each.value.enable_bitbucket_oidc, var.defaults.enable_bitbucket_oidc, false) - enable_github_oidc = try(each.value.enable_github_oidc, var.defaults.enable_github_oidc, false) - enable_oidc = try(each.value.enable_oidc, var.defaults.enable_oidc, false) - enable_saml = try(each.value.enable_saml, var.defaults.enable_saml, false) - github_provider = try(each.value.github_provider, var.defaults.github_provider, "token.actions.githubusercontent.com") - inline_policy_statements = try(each.value.inline_policy_statements, var.defaults.inline_policy_statements, null) - max_session_duration = try(each.value.max_session_duration, var.defaults.max_session_duration, null) - name = try(each.value.name, var.defaults.name, null) - oidc_account_id = try(each.value.oidc_account_id, var.defaults.oidc_account_id, null) - oidc_audiences = try(each.value.oidc_audiences, var.defaults.oidc_audiences, []) - oidc_provider_urls = try(each.value.oidc_provider_urls, var.defaults.oidc_provider_urls, []) - oidc_subjects = try(each.value.oidc_subjects, var.defaults.oidc_subjects, []) - oidc_wildcard_subjects = try(each.value.oidc_wildcard_subjects, var.defaults.oidc_wildcard_subjects, []) - path = try(each.value.path, var.defaults.path, "/") - permissions_boundary = try(each.value.permissions_boundary, var.defaults.permissions_boundary, null) - policies = try(each.value.policies, var.defaults.policies, {}) - saml_endpoints = try(each.value.saml_endpoints, var.defaults.saml_endpoints, ["https://signin.aws.amazon.com/saml"]) - saml_provider_ids = try(each.value.saml_provider_ids, var.defaults.saml_provider_ids, []) - saml_trust_actions = try(each.value.saml_trust_actions, var.defaults.saml_trust_actions, []) - tags = try(each.value.tags, var.defaults.tags, {}) - use_name_prefix = try(each.value.use_name_prefix, var.defaults.use_name_prefix, true) + create = try(each.value.create, var.defaults.create, true) + create_inline_policy = try(each.value.create_inline_policy, var.defaults.create_inline_policy, false) + create_instance_profile = try(each.value.create_instance_profile, var.defaults.create_instance_profile, false) + description = try(each.value.description, var.defaults.description, null) + enable_bitbucket_oidc = try(each.value.enable_bitbucket_oidc, var.defaults.enable_bitbucket_oidc, false) + enable_github_oidc = try(each.value.enable_github_oidc, var.defaults.enable_github_oidc, false) + enable_oidc = try(each.value.enable_oidc, var.defaults.enable_oidc, false) + enable_saml = try(each.value.enable_saml, var.defaults.enable_saml, false) + github_provider = try(each.value.github_provider, var.defaults.github_provider, "token.actions.githubusercontent.com") + inline_policy_permissions = try(each.value.inline_policy_permissions, var.defaults.inline_policy_permissions, null) + max_session_duration = try(each.value.max_session_duration, var.defaults.max_session_duration, null) + name = try(each.value.name, var.defaults.name, null) + oidc_account_id = try(each.value.oidc_account_id, var.defaults.oidc_account_id, null) + oidc_audiences = try(each.value.oidc_audiences, var.defaults.oidc_audiences, []) + oidc_provider_urls = try(each.value.oidc_provider_urls, var.defaults.oidc_provider_urls, []) + oidc_subjects = try(each.value.oidc_subjects, var.defaults.oidc_subjects, []) + oidc_wildcard_subjects = try(each.value.oidc_wildcard_subjects, var.defaults.oidc_wildcard_subjects, []) + override_inline_policy_documents = try(each.value.override_inline_policy_documents, var.defaults.override_inline_policy_documents, []) + path = try(each.value.path, var.defaults.path, null) + permissions_boundary = try(each.value.permissions_boundary, var.defaults.permissions_boundary, null) + policies = try(each.value.policies, var.defaults.policies, {}) + saml_endpoints = try(each.value.saml_endpoints, var.defaults.saml_endpoints, ["https://signin.aws.amazon.com/saml"]) + saml_provider_ids = try(each.value.saml_provider_ids, var.defaults.saml_provider_ids, []) + saml_trust_actions = try(each.value.saml_trust_actions, var.defaults.saml_trust_actions, []) + source_inline_policy_documents = try(each.value.source_inline_policy_documents, var.defaults.source_inline_policy_documents, []) + tags = try(each.value.tags, var.defaults.tags, {}) + trust_policy_conditions = try(each.value.trust_policy_conditions, var.defaults.trust_policy_conditions, []) + trust_policy_permissions = try(each.value.trust_policy_permissions, var.defaults.trust_policy_permissions, null) + use_name_prefix = try(each.value.use_name_prefix, var.defaults.use_name_prefix, true) } diff --git a/wrappers/iam-user/main.tf b/wrappers/iam-user/main.tf index e4609233..47748716 100644 --- a/wrappers/iam-user/main.tf +++ b/wrappers/iam-user/main.tf @@ -12,7 +12,7 @@ module "wrapper" { name = try(each.value.name, var.defaults.name, "") password_length = try(each.value.password_length, var.defaults.password_length, null) password_reset_required = try(each.value.password_reset_required, var.defaults.password_reset_required, true) - path = try(each.value.path, var.defaults.path, "/") + path = try(each.value.path, var.defaults.path, null) permissions_boundary = try(each.value.permissions_boundary, var.defaults.permissions_boundary, null) pgp_key = try(each.value.pgp_key, var.defaults.pgp_key, null) policies = try(each.value.policies, var.defaults.policies, {}) From 4502daca730c999b90fcfa76210219b6e0d952ab Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Wed, 13 Aug 2025 13:43:25 -0500 Subject: [PATCH 17/21] Apply suggestions from code review Co-authored-by: Anton Babenko <393243+antonbabenko@users.noreply.github.com> --- examples/iam-group/outputs.tf | 2 +- modules/iam-account/README.md | 2 +- modules/iam-oidc-provider/README.md | 4 ++-- modules/iam-role/README.md | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/iam-group/outputs.tf b/examples/iam-group/outputs.tf index 45cecc9a..83628da4 100644 --- a/examples/iam-group/outputs.tf +++ b/examples/iam-group/outputs.tf @@ -18,7 +18,7 @@ output "group_name" { } output "group_unique_id" { - description = " The unique ID assigned by AWS" + description = "The unique ID assigned by AWS" value = module.iam_group.unique_id } diff --git a/modules/iam-account/README.md b/modules/iam-account/README.md index 0232eb83..62d3979f 100644 --- a/modules/iam-account/README.md +++ b/modules/iam-account/README.md @@ -32,7 +32,7 @@ aws_iam_account_alias.this: Error creating account alias with name my-account-al If you want to manage IAM alias using Terraform (otherwise why are you reading this?) you need to import this resource like this: ```sh -$ terraform import module.iam_account.aws_iam_account_alias.this this +$ terraform import "module.iam_account.aws_iam_account_alias.this[0]" this module.iam_account.aws_iam_account_alias.this: Importing from ID "this"... module.iam_account.aws_iam_account_alias.this: Import complete! diff --git a/modules/iam-oidc-provider/README.md b/modules/iam-oidc-provider/README.md index 58450ade..8694aded 100644 --- a/modules/iam-oidc-provider/README.md +++ b/modules/iam-oidc-provider/README.md @@ -12,7 +12,7 @@ See more details [here](https://docs.github.com/en/actions/deployment/security-h ```hcl module "iam_oidc_provider" { - source = "terraform-aws-modules/iam/aws//modules/iam-oidc-provider" + source = "terraform-aws-modules/iam/aws//modules/iam-oidc-provider" url = "https://token.actions.githubusercontent.com" @@ -28,7 +28,7 @@ See more details [here](https://support.atlassian.com/bitbucket-cloud/docs/integ ```hcl module "iam_oidc_provider" { - source = "terraform-aws-modules/iam/aws//modules/iam-oidc-provider" + source = "terraform-aws-modules/iam/aws//modules/iam-oidc-provider" url = "https://api.bitbucket.org/2.0/workspaces/example-workspace/pipelines-config/identity/oidc" diff --git a/modules/iam-role/README.md b/modules/iam-role/README.md index c773567c..f277d1b4 100644 --- a/modules/iam-role/README.md +++ b/modules/iam-role/README.md @@ -10,7 +10,7 @@ The defaults provided by the module are suitable for GitHub Free, Pro, & Team, i ```hcl module "iam_oidc_role" { - source = "terraform-aws-modules/iam/aws//modules/iam-oidc-role" + source = "terraform-aws-modules/iam/aws//modules/iam-oidc-role" enable_github_oidc = true @@ -33,7 +33,7 @@ For GitHub Enterprise Server, users will need to provide value for the `oidc_aud ```hcl module "iam_oidc_role" { - source = "terraform-aws-modules/iam/aws//modules/iam-oidc-role" + source = "terraform-aws-modules/iam/aws//modules/iam-oidc-role" enable_github_oidc = true From 0bbb9f2f2cb475b3437967b648591203eccb42d5 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Wed, 13 Aug 2025 13:59:10 -0500 Subject: [PATCH 18/21] fix: Re-add `iam-policy` module --- README.md | 35 +++++++++++ docs/UPGRADE-6.0.md | 71 ++++------------------ examples/iam-policy/README.md | 57 ++++++++++++++++++ examples/iam-policy/main.tf | 72 ++++++++++++++++++++++ examples/iam-policy/outputs.tf | 23 +++++++ examples/iam-policy/variables.tf | 0 examples/iam-policy/versions.tf | 10 ++++ modules/iam-policy/README.md | 84 ++++++++++++++++++++++++++ modules/iam-policy/main.tf | 15 +++++ modules/iam-policy/outputs.tf | 23 +++++++ modules/iam-policy/variables.tf | 41 +++++++++++++ modules/iam-policy/versions.tf | 10 ++++ wrappers/iam-policy/README.md | 100 +++++++++++++++++++++++++++++++ wrappers/iam-policy/main.tf | 13 ++++ wrappers/iam-policy/outputs.tf | 5 ++ wrappers/iam-policy/variables.tf | 11 ++++ wrappers/iam-policy/versions.tf | 10 ++++ 17 files changed, 521 insertions(+), 59 deletions(-) create mode 100644 examples/iam-policy/README.md create mode 100644 examples/iam-policy/main.tf create mode 100644 examples/iam-policy/outputs.tf create mode 100644 examples/iam-policy/variables.tf create mode 100644 examples/iam-policy/versions.tf create mode 100644 modules/iam-policy/README.md create mode 100644 modules/iam-policy/main.tf create mode 100644 modules/iam-policy/outputs.tf create mode 100644 modules/iam-policy/variables.tf create mode 100644 modules/iam-policy/versions.tf create mode 100644 wrappers/iam-policy/README.md create mode 100644 wrappers/iam-policy/main.tf create mode 100644 wrappers/iam-policy/outputs.tf create mode 100644 wrappers/iam-policy/variables.tf create mode 100644 wrappers/iam-policy/versions.tf diff --git a/README.md b/README.md index 634077d6..28b7e221 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,40 @@ module "iam_oidc_provider" { } ``` +### IAM Policy + +Creates an IAM policy. + +```hcl +module "iam_policy" { + source = "terraform-aws-modules/iam/aws//modules/iam-policy" + + name = "example" + path = "/" + description = "My example policy" + + policy = <<-EOF + { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "ec2:Describe*" + ], + "Effect": "Allow", + "Resource": "*" + } + ] + } + EOF + + tags = { + Terraform = "true" + Environment = "dev" + } +} +``` + ### IAM ReadOnly Policy Creates an IAM policy that allows read-only access to the list of AWS services provided. @@ -248,6 +282,7 @@ module "iam_user" { - [iam-account](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-account) - Set AWS account alias and password policy - [iam-group](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-group) - IAM group with users who are allowed to assume IAM roles in another AWS account and have access to specified IAM policies - [iam-oidc-provider](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-oidc-provider) - Create an OpenID connect provider and IAM role which can be assumed from specified subjects federated from the OIDC provider +- [iam-policy](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-policy) - Create an IAM policy - [iam-read-only-policy](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-read-only-policy) - Create IAM read-only policy - [iam-role](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-role) - Create individual IAM role which can be assumed from specified ARNs (AWS accounts, IAM users, etc) - [iam-role-for-service-accounts](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-role-for-service-accounts) - Create IAM role for service accounts (IRSA) for use within EKS clusters diff --git a/docs/UPGRADE-6.0.md b/docs/UPGRADE-6.0.md index 56d5a3b5..36af4d28 100644 --- a/docs/UPGRADE-6.0.md +++ b/docs/UPGRADE-6.0.md @@ -22,7 +22,6 @@ If you find a bug, please open an issue with supporting configuration to reprodu - `iam-group-with-policies` has been renamed to `iam-group` - `iam-group-with-assumable-roles-policy` has been merged into `iam-group` - `iam-eks-role` has been removed; `iam-role-for-service-accounts` or [`eks-pod-identity`](https://github.com/terraform-aws-modules/terraform-aws-eks-pod-identity) should be used instead -- `iam-policy` has been removed; the `aws_iam_policy` resource should be used directly instead - `iam-role-for-service-accounts-eks` has been renamed to `iam-role-for-service-accounts` - Individual policy creation and attachment has been consolidated under one policy creation and attachment - Default values that enable permissive permissions have been removed; users will need to be explicit about the scope of access (i.e. ARNs) they provide when enabling permissions @@ -93,6 +92,8 @@ stateDiagram - `assumable_roles` - `iam-oidc-provider` - `additional_thumbprints` - no longer required by GitHub + - `iam-policy` + - None - `iam-read-only-policy` - `additional_policy_json` - use `source_inline_policy_documents` or `override_inline_policy_documents` instead - `iam-role` @@ -140,6 +141,8 @@ stateDiagram - `aws_account_id` -> `users_account_id` - `iam-oidc-provider` - None + - `iam-policy` + - `create_policy` -> `create` - `iam-read-only-policy` - `name_prefix` (string) -> `use_name_prefix` (bool) - `iam-role` @@ -179,6 +182,8 @@ stateDiagram - `enable_mfa_enforcment` - `iam-oidc-provider` - None + - `iam-policy` + - None - `iam-read-only-policy` - `create` - `source_policy_documents` @@ -215,6 +220,9 @@ stateDiagram - `aws_account_id` - `iam-oidc-provider` - None + - `iam-policy` + - `description` + - `path` - `iam-read-only-policy` - `description` - `path` @@ -246,6 +254,8 @@ stateDiagram - `group_users` -> `users` - `iam-oidc-provider` - None + - `iam-policy` + - None - `iam-read-only-policy` - None - `iam-role` @@ -786,64 +796,7 @@ terraform state mv "module.iam_group.aws_iam_group_policy_attachment.custom_arns #### `iam-policy` -##### Before `v5.60` - -```hcl -module "iam_policy" { - source = "terraform-aws-modules/iam/aws//modules/iam-policy" - version = "~> 5.60" - - name_prefix = "example-" - path = "/" - description = "My example policy" - - policy = < +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [aws](#requirement\_aws) | >= 4.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 4.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [iam\_policy](#module\_iam\_policy) | ../../modules/iam-policy | n/a | +| [iam\_policy\_disabled](#module\_iam\_policy\_disabled) | ../../modules/iam-policy | n/a | +| [iam\_policy\_from\_data\_source](#module\_iam\_policy\_from\_data\_source) | ../../modules/iam-policy | n/a | + +## Resources + +| Name | Type | +|------|------| +| [aws_iam_policy_document.bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | + +## Inputs + +No inputs. + +## Outputs + +| Name | Description | +|------|-------------| +| [arn](#output\_arn) | The ARN assigned by AWS to this policy | +| [id](#output\_id) | The policy ID | +| [name](#output\_name) | The name of the policy | +| [policy](#output\_policy) | The policy document | + diff --git a/examples/iam-policy/main.tf b/examples/iam-policy/main.tf new file mode 100644 index 00000000..d081bb6c --- /dev/null +++ b/examples/iam-policy/main.tf @@ -0,0 +1,72 @@ +provider "aws" { + region = "eu-west-1" +} + +locals { + name = "ex-${basename(path.cwd)}" + + tags = { + Example = local.name + GithubRepo = "terraform-aws-iam" + GithubOrg = "terraform-aws-modules" + } +} + +################################################################################ +# IAM Policy +################################################################################ + +module "iam_policy" { + source = "../../modules/iam-policy" + + name_prefix = "example-" + path = "/" + description = "My example policy" + + policy = <<-EOF + { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "ec2:Describe*" + ], + "Effect": "Allow", + "Resource": "*" + } + ] + } + EOF + + tags = local.tags +} + +module "iam_policy_from_data_source" { + source = "../../modules/iam-policy" + + name = "example_from_data_source" + path = "/" + description = "My example policy" + + policy = data.aws_iam_policy_document.bucket_policy.json + + tags = local.tags +} + +module "iam_policy_disabled" { + source = "../../modules/iam-policy" + + create = false +} + +################################################################################ +# Supporting resources +################################################################################ + +data "aws_iam_policy_document" "bucket_policy" { + statement { + sid = "AllowFullS3Access" + actions = ["s3:ListAllMyBuckets"] + resources = ["*"] + } +} diff --git a/examples/iam-policy/outputs.tf b/examples/iam-policy/outputs.tf new file mode 100644 index 00000000..15f3e0f7 --- /dev/null +++ b/examples/iam-policy/outputs.tf @@ -0,0 +1,23 @@ +################################################################################ +# IAM Policy +################################################################################ + +output "id" { + description = "The policy ID" + value = module.iam_policy.id +} + +output "arn" { + description = "The ARN assigned by AWS to this policy" + value = module.iam_policy.arn +} + +output "name" { + description = "The name of the policy" + value = module.iam_policy.name +} + +output "policy" { + description = "The policy document" + value = module.iam_policy.policy +} diff --git a/examples/iam-policy/variables.tf b/examples/iam-policy/variables.tf new file mode 100644 index 00000000..e69de29b diff --git a/examples/iam-policy/versions.tf b/examples/iam-policy/versions.tf new file mode 100644 index 00000000..d8dd1a44 --- /dev/null +++ b/examples/iam-policy/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.0" + } + } +} diff --git a/modules/iam-policy/README.md b/modules/iam-policy/README.md new file mode 100644 index 00000000..568c14ea --- /dev/null +++ b/modules/iam-policy/README.md @@ -0,0 +1,84 @@ +# AWS IAM Policy + +Creates an IAM policy. + +## Usage + +```hcl +module "iam_policy" { + source = "terraform-aws-modules/iam/aws//modules/iam-policy" + + name_prefix = "example-" + path = "/" + description = "My example policy" + + policy = <<-EOF + { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "ec2:Describe*" + ], + "Effect": "Allow", + "Resource": "*" + } + ] + } + EOF + + tags = { + Environment = "test" + } +} +``` + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 6.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_iam_policy.policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [create](#input\_create) | Whether to create the IAM policy | `bool` | `true` | no | +| [description](#input\_description) | The description of the policy | `string` | `null` | no | +| [name](#input\_name) | The name of the policy | `string` | `null` | no | +| [name\_prefix](#input\_name\_prefix) | IAM policy name prefix | `string` | `null` | no | +| [path](#input\_path) | The path of the policy in IAM | `string` | `null` | no | +| [policy](#input\_policy) | The path of the policy in IAM (tpl file) | `string` | `""` | no | +| [tags](#input\_tags) | A map of tags to add to all resources. | `map(string)` | `{}` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [arn](#output\_arn) | The ARN assigned by AWS to this policy | +| [id](#output\_id) | The policy's ID | +| [name](#output\_name) | The name of the policy | +| [policy](#output\_policy) | The policy document | + + +## License + +Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-iam/blob/master/LICENSE). diff --git a/modules/iam-policy/main.tf b/modules/iam-policy/main.tf new file mode 100644 index 00000000..6100a0c2 --- /dev/null +++ b/modules/iam-policy/main.tf @@ -0,0 +1,15 @@ +################################################################################ +# IAM Policy +################################################################################ + +resource "aws_iam_policy" "policy" { + count = var.create ? 1 : 0 + + description = var.description + name = var.name + name_prefix = var.name_prefix + path = var.path + policy = var.policy + + tags = var.tags +} diff --git a/modules/iam-policy/outputs.tf b/modules/iam-policy/outputs.tf new file mode 100644 index 00000000..8e2a38d6 --- /dev/null +++ b/modules/iam-policy/outputs.tf @@ -0,0 +1,23 @@ +################################################################################ +# IAM Policy +################################################################################ + +output "id" { + description = "The policy's ID" + value = try(aws_iam_policy.policy[0].id, null) +} + +output "arn" { + description = "The ARN assigned by AWS to this policy" + value = try(aws_iam_policy.policy[0].arn, null) +} + +output "name" { + description = "The name of the policy" + value = try(aws_iam_policy.policy[0].name, null) +} + +output "policy" { + description = "The policy document" + value = try(aws_iam_policy.policy[0].policy, null) +} diff --git a/modules/iam-policy/variables.tf b/modules/iam-policy/variables.tf new file mode 100644 index 00000000..a21b3a72 --- /dev/null +++ b/modules/iam-policy/variables.tf @@ -0,0 +1,41 @@ +variable "create" { + description = "Whether to create the IAM policy" + type = bool + default = true +} + +variable "name" { + description = "The name of the policy" + type = string + default = null +} + +variable "name_prefix" { + description = "IAM policy name prefix" + type = string + default = null +} + +variable "path" { + description = "The path of the policy in IAM" + type = string + default = null +} + +variable "description" { + description = "The description of the policy" + type = string + default = null +} + +variable "policy" { + description = "The path of the policy in IAM (tpl file)" + type = string + default = "" +} + +variable "tags" { + description = "A map of tags to add to all resources." + type = map(string) + default = {} +} diff --git a/modules/iam-policy/versions.tf b/modules/iam-policy/versions.tf new file mode 100644 index 00000000..db13b0a8 --- /dev/null +++ b/modules/iam-policy/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.5.7" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 6.0" + } + } +} diff --git a/wrappers/iam-policy/README.md b/wrappers/iam-policy/README.md new file mode 100644 index 00000000..6d951656 --- /dev/null +++ b/wrappers/iam-policy/README.md @@ -0,0 +1,100 @@ +# Wrapper for module: `modules/iam-policy` + +The configuration in this directory contains an implementation of a single module wrapper pattern, which allows managing several copies of a module in places where using the native Terraform 0.13+ `for_each` feature is not feasible (e.g., with Terragrunt). + +You may want to use a single Terragrunt configuration file to manage multiple resources without duplicating `terragrunt.hcl` files for each copy of the same module. + +This wrapper does not implement any extra functionality. + +## Usage with Terragrunt + +`terragrunt.hcl`: + +```hcl +terraform { + source = "tfr:///terraform-aws-modules/iam/aws//wrappers/iam-policy" + # Alternative source: + # source = "git::git@github.com:terraform-aws-modules/terraform-aws-iam.git//wrappers/iam-policy?ref=master" +} + +inputs = { + defaults = { # Default values + create = true + tags = { + Terraform = "true" + Environment = "dev" + } + } + + items = { + my-item = { + # omitted... can be any argument supported by the module + } + my-second-item = { + # omitted... can be any argument supported by the module + } + # omitted... + } +} +``` + +## Usage with Terraform + +```hcl +module "wrapper" { + source = "terraform-aws-modules/iam/aws//wrappers/iam-policy" + + defaults = { # Default values + create = true + tags = { + Terraform = "true" + Environment = "dev" + } + } + + items = { + my-item = { + # omitted... can be any argument supported by the module + } + my-second-item = { + # omitted... can be any argument supported by the module + } + # omitted... + } +} +``` + +## Example: Manage multiple S3 buckets in one Terragrunt layer + +`eu-west-1/s3-buckets/terragrunt.hcl`: + +```hcl +terraform { + source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers" + # Alternative source: + # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//wrappers?ref=master" +} + +inputs = { + defaults = { + force_destroy = true + + attach_elb_log_delivery_policy = true + attach_lb_log_delivery_policy = true + attach_deny_insecure_transport_policy = true + attach_require_latest_tls_policy = true + } + + items = { + bucket1 = { + bucket = "my-random-bucket-1" + } + bucket2 = { + bucket = "my-random-bucket-2" + tags = { + Secure = "probably" + } + } + } +} +``` diff --git a/wrappers/iam-policy/main.tf b/wrappers/iam-policy/main.tf new file mode 100644 index 00000000..80213cf0 --- /dev/null +++ b/wrappers/iam-policy/main.tf @@ -0,0 +1,13 @@ +module "wrapper" { + source = "../../modules/iam-policy" + + for_each = var.items + + create = try(each.value.create, var.defaults.create, true) + description = try(each.value.description, var.defaults.description, null) + name = try(each.value.name, var.defaults.name, null) + name_prefix = try(each.value.name_prefix, var.defaults.name_prefix, null) + path = try(each.value.path, var.defaults.path, null) + policy = try(each.value.policy, var.defaults.policy, "") + tags = try(each.value.tags, var.defaults.tags, {}) +} diff --git a/wrappers/iam-policy/outputs.tf b/wrappers/iam-policy/outputs.tf new file mode 100644 index 00000000..ec6da5f4 --- /dev/null +++ b/wrappers/iam-policy/outputs.tf @@ -0,0 +1,5 @@ +output "wrapper" { + description = "Map of outputs of a wrapper." + value = module.wrapper + # sensitive = false # No sensitive module output found +} diff --git a/wrappers/iam-policy/variables.tf b/wrappers/iam-policy/variables.tf new file mode 100644 index 00000000..a6ea0962 --- /dev/null +++ b/wrappers/iam-policy/variables.tf @@ -0,0 +1,11 @@ +variable "defaults" { + description = "Map of default values which will be used for each item." + type = any + default = {} +} + +variable "items" { + description = "Maps of items to create a wrapper from. Values are passed through to the module." + type = any + default = {} +} diff --git a/wrappers/iam-policy/versions.tf b/wrappers/iam-policy/versions.tf new file mode 100644 index 00000000..db13b0a8 --- /dev/null +++ b/wrappers/iam-policy/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.5.7" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 6.0" + } + } +} From 57d5136ef444c26b7b75d1ecd3d9501ed4f746f2 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Wed, 13 Aug 2025 14:15:23 -0500 Subject: [PATCH 19/21] fix: Update migration commands to remove --- docs/UPGRADE-6.0.md | 47 ++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/docs/UPGRADE-6.0.md b/docs/UPGRADE-6.0.md index 36af4d28..cff1a7f1 100644 --- a/docs/UPGRADE-6.0.md +++ b/docs/UPGRADE-6.0.md @@ -364,13 +364,16 @@ module "iam_role" { ##### State Changes +Remove all prior policy attachments (that are marked for deletion in `terraform plan`). +Policies will stay attached to the role but new attachment IDs will be created on next apply + ```sh -terraform state mv "module.iam_role.aws_iam_role_policy_attachment.admin[0]" 'module.iam_role.aws_iam_role_policy_attachment.this["AdministratorAccess"]' +terraform state rm "module.iam_role.aws_iam_role_policy_attachment.admin[0]" # One move command for each ARN in prior custom_role_policy_arns -terraform state mv "module.iam_role.aws_iam_role_policy_attachment.custom[0]" 'module.iam_role.aws_iam_role_policy_attachment.this["AmazonCognitoReadOnly"]' -terraform state mv "module.iam_role.aws_iam_role_policy_attachment.custom[1]" 'module.iam_role.aws_iam_role_policy_attachment.this["AlexaForBusinessFullAccess"]' -terraform state mv "module.iam_role.aws_iam_role_policy_attachment.custom[2]" 'module.iam_role.aws_iam_role_policy_attachment.this["custom"]' +terraform state rm "module.iam_role.aws_iam_role_policy_attachment.custom[0]" +terraform state rm "module.iam_role.aws_iam_role_policy_attachment.custom[1]" +terraform state rm "module.iam_role.aws_iam_role_policy_attachment.custom[2]" ``` #### `iam-assumable-role-with-oidc` -> `iam-role` @@ -410,9 +413,12 @@ module "iam_role" { ##### State Changes +Remove all prior policy attachments (that are marked for deletion in `terraform plan`). +Policies will stay attached to the role but new attachment IDs will be created on next apply + ```sh # One move command for each ARN in prior custom_role_policy_arns -terraform state mv "module.iam_role.aws_iam_role_policy_attachment.custom[0]" 'module.iam_role.aws_iam_role_policy_attachment.this["AmazonEC2ContainerRegistryPowerUser"]' +terraform state rm "module.iam_role.aws_iam_role_policy_attachment.custom[0]" ``` #### `iam-assumable-role-with-saml` -> `iam-role` @@ -453,9 +459,12 @@ resource "aws_iam_saml_provider" "second_idp_saml" { ##### State Changes +Remove all prior policy attachments (that are marked for deletion in `terraform plan`). +Policies will stay attached to the role but new attachment IDs will be created on next apply + ```sh # One move command for each ARN in prior custom_role_policy_arns -terraform state mv "module.iam_role.aws_iam_role_policy_attachment.custom[0]" 'module.iam_role.aws_iam_role_policy_attachment.this["ReadOnlyAccess"]' +terraform state rm "module.iam_role.aws_iam_role_policy_attachment.custom[0]" ``` #### `iam-assumable-roles` -> `iam-role` @@ -563,14 +572,17 @@ module "iam_role_poweruser" { ##### State Changes +Remove all prior policy attachments (that are marked for deletion in `terraform plan`). +Policies will stay attached to the role but new attachment IDs will be created on next apply + ```sh terraform state mv "module.iam_assumable_roles.aws_iam_role.admin[0]" "module.iam_role_admin.aws_iam_role.this[0]" -terraform state mv "module.iam_assumable_roles.aws_iam_role_policy_attachment.admin[0]" 'module.iam_role_admin.aws_iam_role_policy_attachment.this["AdministratorAccess"]' +terraform state rm "module.iam_assumable_roles.aws_iam_role_policy_attachment.admin[0]" terraform state mv "module.iam_assumable_roles.aws_iam_role.poweruser[0]" "module.iam_role_poweruser.aws_iam_role.this[0]" # One move command for each ARN in prior `poweruser_role_policy_arns` -terraform state mv "module.iam_assumable_roles.aws_iam_role_policy_attachment.poweruser[0]" 'module.iam_role_poweruser.aws_iam_role_policy_attachment.this["Billing"]' -terraform state mv "module.iam_assumable_roles.aws_iam_role_policy_attachment.poweruser[1]" 'module.iam_role_poweruser.aws_iam_role_policy_attachment.this["AWSSupportAccess"]' +terraform state rm "module.iam_assumable_roles.aws_iam_role_policy_attachment.poweruser[0]" +terraform state rm "module.iam_assumable_roles.aws_iam_role_policy_attachment.poweruser[1]" ``` #### `iam-assumable-roles-with-saml` -> `iam-role` @@ -644,12 +656,15 @@ module "iam_role_poweruser" { ##### State Changes +Remove all prior policy attachments (that are marked for deletion in `terraform plan`). +Policies will stay attached to the role but new attachment IDs will be created on next apply + ```sh terraform state mv "module.iam_assumable_roles.aws_iam_role.admin[0]" "module.iam_role_admin.aws_iam_role.this[0]" -terraform state mv "module.iam_assumable_roles.aws_iam_role_policy_attachment.admin[0]" 'module.iam_role_admin.aws_iam_role_policy_attachment.this["AdministratorAccess"]' +terraform state rm "module.iam_assumable_roles.aws_iam_role_policy_attachment.admin[0]" terraform state mv "module.iam_assumable_roles.aws_iam_role.poweruser[0]" "module.iam_role_poweruser.aws_iam_role.this[0]" -terraform state mv "module.iam_assumable_roles.aws_iam_role_policy_attachment.poweruser[0]" 'module.iam_role_poweruser.aws_iam_role_policy_attachment.this["PowerUserAccess"]' +terraform state rm "module.iam_assumable_roles.aws_iam_role_policy_attachment.poweruser[0]" ``` #### `iam-eks-role` -> `iam-role-for-service-accounts` @@ -789,9 +804,12 @@ module "iam_user" { ##### State Changes +Remove all prior policy attachments (that are marked for deletion in `terraform plan`). +Policies will stay attached to the role but new attachment IDs will be created on next apply + ```sh # One move command for each ARN in prior `custom_group_policy_arns` -terraform state mv "module.iam_group.aws_iam_group_policy_attachment.custom_arns[0]" 'module.iam_group.aws_iam_group_policy_attachment.additional["AmazonS3FullAccess"]' +terraform state rm "module.iam_group.aws_iam_group_policy_attachment.custom_arns[0]" ``` #### `iam-policy` @@ -823,6 +841,9 @@ module "iam_user" { ##### State Changes +Remove all prior policy attachments (that are marked for deletion in `terraform plan`). +Policies will stay attached to the role but new attachment IDs will be created on next apply + ```sh -terraform state mv "module.iam_user[0].aws_iam_user_policy_attachment.this[0]" "module.iam_user[0].aws_iam_user_policy_attachment.this["S3ReadOnly"]" +terraform state rm "module.iam_user[0].aws_iam_user_policy_attachment.this[0]" ``` From fbbfefb17f0aba4e5e9b407c8a4ee7d6989e656b Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Wed, 13 Aug 2025 14:18:13 -0500 Subject: [PATCH 20/21] fix: Correct example provider versions --- examples/iam-policy/README.md | 6 +++--- examples/iam-policy/versions.tf | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/iam-policy/README.md b/examples/iam-policy/README.md index 8c6428a5..d5973c02 100644 --- a/examples/iam-policy/README.md +++ b/examples/iam-policy/README.md @@ -19,14 +19,14 @@ Run `terraform destroy` when you don't need these resources. | Name | Version | |------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 4.0 | +| [terraform](#requirement\_terraform) | >= 1.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | >= 4.0 | +| [aws](#provider\_aws) | >= 6.0 | ## Modules diff --git a/examples/iam-policy/versions.tf b/examples/iam-policy/versions.tf index d8dd1a44..db13b0a8 100644 --- a/examples/iam-policy/versions.tf +++ b/examples/iam-policy/versions.tf @@ -1,10 +1,10 @@ terraform { - required_version = ">= 1.0" + required_version = ">= 1.5.7" required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.0" + version = ">= 6.0" } } } From 49e7d67dc85ec01a82d88c495da60dfa587013f7 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Wed, 13 Aug 2025 14:47:23 -0500 Subject: [PATCH 21/21] fix: Update migration commands --- docs/UPGRADE-6.0.md | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/docs/UPGRADE-6.0.md b/docs/UPGRADE-6.0.md index cff1a7f1..3cf9fb60 100644 --- a/docs/UPGRADE-6.0.md +++ b/docs/UPGRADE-6.0.md @@ -368,12 +368,8 @@ Remove all prior policy attachments (that are marked for deletion in `terraform Policies will stay attached to the role but new attachment IDs will be created on next apply ```sh -terraform state rm "module.iam_role.aws_iam_role_policy_attachment.admin[0]" - -# One move command for each ARN in prior custom_role_policy_arns -terraform state rm "module.iam_role.aws_iam_role_policy_attachment.custom[0]" -terraform state rm "module.iam_role.aws_iam_role_policy_attachment.custom[1]" -terraform state rm "module.iam_role.aws_iam_role_policy_attachment.custom[2]" +terraform state rm module.iam_role.aws_iam_role_policy_attachment.admin +terraform state rm module.iam_role.aws_iam_role_policy_attachment.custom ``` #### `iam-assumable-role-with-oidc` -> `iam-role` @@ -417,8 +413,7 @@ Remove all prior policy attachments (that are marked for deletion in `terraform Policies will stay attached to the role but new attachment IDs will be created on next apply ```sh -# One move command for each ARN in prior custom_role_policy_arns -terraform state rm "module.iam_role.aws_iam_role_policy_attachment.custom[0]" +terraform state rm module.iam_role.aws_iam_role_policy_attachment.custom ``` #### `iam-assumable-role-with-saml` -> `iam-role` @@ -463,8 +458,7 @@ Remove all prior policy attachments (that are marked for deletion in `terraform Policies will stay attached to the role but new attachment IDs will be created on next apply ```sh -# One move command for each ARN in prior custom_role_policy_arns -terraform state rm "module.iam_role.aws_iam_role_policy_attachment.custom[0]" +terraform state rm module.iam_role.aws_iam_role_policy_attachment.custom ``` #### `iam-assumable-roles` -> `iam-role` @@ -577,12 +571,10 @@ Policies will stay attached to the role but new attachment IDs will be created o ```sh terraform state mv "module.iam_assumable_roles.aws_iam_role.admin[0]" "module.iam_role_admin.aws_iam_role.this[0]" -terraform state rm "module.iam_assumable_roles.aws_iam_role_policy_attachment.admin[0]" +terraform state rm module.iam_assumable_roles.aws_iam_role_policy_attachment.admin terraform state mv "module.iam_assumable_roles.aws_iam_role.poweruser[0]" "module.iam_role_poweruser.aws_iam_role.this[0]" -# One move command for each ARN in prior `poweruser_role_policy_arns` -terraform state rm "module.iam_assumable_roles.aws_iam_role_policy_attachment.poweruser[0]" -terraform state rm "module.iam_assumable_roles.aws_iam_role_policy_attachment.poweruser[1]" +terraform state rm module.iam_assumable_roles.aws_iam_role_policy_attachment.poweruser ``` #### `iam-assumable-roles-with-saml` -> `iam-role` @@ -661,10 +653,10 @@ Policies will stay attached to the role but new attachment IDs will be created o ```sh terraform state mv "module.iam_assumable_roles.aws_iam_role.admin[0]" "module.iam_role_admin.aws_iam_role.this[0]" -terraform state rm "module.iam_assumable_roles.aws_iam_role_policy_attachment.admin[0]" +terraform state rm module.iam_assumable_roles.aws_iam_role_policy_attachment.admin terraform state mv "module.iam_assumable_roles.aws_iam_role.poweruser[0]" "module.iam_role_poweruser.aws_iam_role.this[0]" -terraform state rm "module.iam_assumable_roles.aws_iam_role_policy_attachment.poweruser[0]" +terraform state rm module.iam_assumable_roles.aws_iam_role_policy_attachment.poweruser ``` #### `iam-eks-role` -> `iam-role-for-service-accounts` @@ -808,8 +800,7 @@ Remove all prior policy attachments (that are marked for deletion in `terraform Policies will stay attached to the role but new attachment IDs will be created on next apply ```sh -# One move command for each ARN in prior `custom_group_policy_arns` -terraform state rm "module.iam_group.aws_iam_group_policy_attachment.custom_arns[0]" +terraform state rm module.iam_group.aws_iam_group_policy_attachment.custom_arns ``` #### `iam-policy` @@ -845,5 +836,5 @@ Remove all prior policy attachments (that are marked for deletion in `terraform Policies will stay attached to the role but new attachment IDs will be created on next apply ```sh -terraform state rm "module.iam_user[0].aws_iam_user_policy_attachment.this[0]" +terraform state rm module.iam_user[0].aws_iam_user_policy_attachment.this ```