diff --git a/README.md b/README.md index 6a918c32..cc021d63 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,28 @@ module "iam_assumable_role" { } ``` +`iam-assumable-role-with-oidc`: +```hcl +module "iam_assumable_role" { + source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role" + version = "~> 2.0" + + create_role = true + + role_name = "role-with-oidc" + + tags = { + Role = "role-with-oidc" + } + + provider_url = "oidc.eks.eu-west-1.amazonaws.com/id/BA9E170D464AF7B92084EF72A69B9DC8" + + role_policy_arns = [ + "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy", + ] +} +``` + `iam-assumable-roles`: ```hcl module "iam_assumable_roles" { @@ -231,6 +253,7 @@ Use [iam-policy module](https://github.com/terraform-aws-modules/terraform-aws-i * [iam-account](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-account) - Set AWS account alias and password policy * [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-role-with-oidc](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-assumable-role-with-oidc) - Create individual IAM role which can be assumed from specified subjects federated with a OIDC 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-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-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 diff --git a/examples/iam-assumable-role-with-oidc/README.md b/examples/iam-assumable-role-with-oidc/README.md new file mode 100644 index 00000000..9b4f75d8 --- /dev/null +++ b/examples/iam-assumable-role-with-oidc/README.md @@ -0,0 +1,29 @@ +# 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. + + +## Outputs + +| Name | Description | +|------|-------------| +| role\_requires\_mfa | Whether admin IAM role requires MFA | +| this\_iam\_role\_arn | ARN of IAM role | +| this\_iam\_role\_name | Name of IAM role | +| this\_iam\_role\_path | Path of IAM role | + + diff --git a/examples/iam-assumable-role-with-oidc/main.tf b/examples/iam-assumable-role-with-oidc/main.tf new file mode 100644 index 00000000..3ed49e86 --- /dev/null +++ b/examples/iam-assumable-role-with-oidc/main.tf @@ -0,0 +1,24 @@ +provider "aws" { + region = "eu-west-1" +} + +############################### +# IAM assumable role for admin +############################### +module "iam_assumable_role_admin" { + source = "../../modules/iam-assumable-role-with-iodc" + + create_role = true + + role_name = "role-with-oidc" + + tags = { + Role = "role-with-oidc" + } + + provider_url = "oidc.eks.eu-west-1.amazonaws.com/id/BA9E170D464AF7B92084EF72A69B9DC8" + + role_policy_arns = [ + "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy", + ] +} diff --git a/examples/iam-assumable-role-with-oidc/outputs.tf b/examples/iam-assumable-role-with-oidc/outputs.tf new file mode 100644 index 00000000..00d3c9d0 --- /dev/null +++ b/examples/iam-assumable-role-with-oidc/outputs.tf @@ -0,0 +1,14 @@ +output "this_iam_role_arn" { + description = "ARN of IAM role" + value = module.iam_assumable_role_admin.this_iam_role_arn +} + +output "this_iam_role_name" { + description = "Name of IAM role" + value = module.iam_assumable_role_admin.this_iam_role_name +} + +output "this_iam_role_path" { + description = "Path of IAM role" + value = module.iam_assumable_role_admin.this_iam_role_path +} diff --git a/modules/iam-assumable-role-with-iodc/README.md b/modules/iam-assumable-role-with-iodc/README.md new file mode 100644 index 00000000..33a95118 --- /dev/null +++ b/modules/iam-assumable-role-with-iodc/README.md @@ -0,0 +1,40 @@ +# 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/en_pv/eks/latest/userguide/iam-roles-for-service-accounts.html). + + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|:----:|:-----:|:-----:| +| aws\_account\_id | The AWS account ID where the OIDC provider lives, leave empty to use the account fo the AWS provider | string | `""` | no | +| create\_role | Whether to create a role | bool | `"false"` | no | +| max\_session\_duration | Maximum CLI/API session duration in seconds between 3600 and 43200 | number | `"3600"` | no | +| oidc\_fully\_qualified\_subjects | The fully qualified OIDC subjects to be added to the role policy | list(string) | `[]` | no | +| oidc\_subjects\_with\_wildcards | The OIDC subject using wildcards to be added to the role policy | list(string) | `[]` | no | +| provider\_url | URL of the OIDC Provider | string | n/a | yes | +| role\_name | IAM role name | string | `""` | no | +| role\_path | Path of IAM role | string | `"/"` | no | +| role\_permissions\_boundary\_arn | Permissions boundary ARN to use for IAM role | string | `""` | no | +| role\_policy\_arns | List of ARNs of IAM policies to attach to IAM role | list(string) | `[]` | no | +| tags | A map of tags to add to IAM role resources | map(string) | `{}` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| admin\_iam\_role\_arn | ARN of admin IAM role | +| admin\_iam\_role\_name | Name of admin IAM role | +| admin\_iam\_role\_path | Path of admin IAM role | +| poweruser\_iam\_role\_arn | ARN of poweruser IAM role | +| poweruser\_iam\_role\_name | Name of poweruser IAM role | +| poweruser\_iam\_role\_path | Path of poweruser IAM role | +| readonly\_iam\_role\_arn | ARN of readonly IAM role | +| readonly\_iam\_role\_name | Name of readonly IAM role | +| readonly\_iam\_role\_path | Path of readonly IAM role | + + diff --git a/modules/iam-assumable-role-with-iodc/main.tf b/modules/iam-assumable-role-with-iodc/main.tf new file mode 100644 index 00000000..41e1173a --- /dev/null +++ b/modules/iam-assumable-role-with-iodc/main.tf @@ -0,0 +1,62 @@ +locals { + aws_account_id = var.aws_account_id != "" ? var.aws_account_id : data.aws_caller_identity.current.account_id +} + +data "aws_caller_identity" "current" {} + +data "aws_iam_policy_document" "assume_role_with_oidc" { + count = var.create_role ? 1 : 0 + + statement { + effect = "Allow" + + actions = ["sts:AssumeRoleWithWebIdentity"] + + principals { + type = "Federated" + + identifiers = [ + "arn:aws:iam::${local.aws_account_id}:oidc-provider/${var.provider_url}" + ] + } + + dynamic "condition" { + for_each = var.oidc_fully_qualified_subjects + content { + test = "StringEquals" + variable = "${var.provider_url}:sub" + values = [condition.value] + } + } + + dynamic "condition" { + for_each = var.oidc_subjects_with_wildcards + content { + test = "StringLike" + variable = "${var.provider_url}:sub" + values = [condition.value] + } + } + } +} + +resource "aws_iam_role" "this" { + count = var.create_role ? 1 : 0 + + name = var.role_name + path = var.role_path + max_session_duration = var.max_session_duration + + permissions_boundary = var.role_permissions_boundary_arn + + assume_role_policy = join("", data.aws_iam_policy_document.assume_role_with_oidc.*.json) + + tags = var.tags +} + +resource "aws_iam_role_policy_attachment" "custom" { + count = var.create_role && length(var.role_policy_arns) > 0 ? length(var.role_policy_arns) : 0 + + role = join("", aws_iam_role.this.*.name) + policy_arn = var.role_policy_arns[count.index] +} diff --git a/modules/iam-assumable-role-with-iodc/outputs.tf b/modules/iam-assumable-role-with-iodc/outputs.tf new file mode 100644 index 00000000..b3d143c1 --- /dev/null +++ b/modules/iam-assumable-role-with-iodc/outputs.tf @@ -0,0 +1,14 @@ +output "this_iam_role_arn" { + description = "ARN of IAM role" + value = element(concat(aws_iam_role.this.*.arn, [""]), 0) +} + +output "this_iam_role_name" { + description = "Name of IAM role" + value = element(concat(aws_iam_role.this.*.name, [""]), 0) +} + +output "this_iam_role_path" { + description = "Path of IAM role" + value = element(concat(aws_iam_role.this.*.path, [""]), 0) +} diff --git a/modules/iam-assumable-role-with-iodc/variables.tf b/modules/iam-assumable-role-with-iodc/variables.tf new file mode 100644 index 00000000..e3ec8123 --- /dev/null +++ b/modules/iam-assumable-role-with-iodc/variables.tf @@ -0,0 +1,65 @@ +variable "create_role" { + description = "Whether to create a role" + type = bool + default = false +} + +variable "provider_url" { + description = "URL of the OIDC Provider" + type = string +} + +variable "aws_account_id" { + description = "The AWS account ID where the OIDC provider lives, leave empty to use the account fo 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 = "" +} + +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 "oidc_fully_qualified_subjects" { + description = "The fully qualified OIDC subjects to be added to the role policy" + type = list(string) + default = [] +} + +variable "oidc_subjects_with_wildcards" { + description = "The OIDC subject using wildcards to be added to the role policy" + type = list(string) + default = [] +} +