diff --git a/README.md b/README.md index 97e73e37..3cc30f08 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,29 @@ module "iam_assumable_role_with_oidc" { } ``` +`iam-assumable-role-with-saml`: +```hcl +module "iam_assumable_role_with_saml" { + source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role-with-saml" + version = "~> 3.0" + + create_role = true + + role_name = "role-with-saml" + + tags = { + Role = "role-with-saml" + } + + 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`: ```hcl module "iam_assumable_roles" { @@ -255,6 +278,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-role-with-saml](https://github.com/terraform-aws-modules/terraform-aws-iam/tree/master/examples/iam-assumable-role-with-saml) - Create individual IAM role 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-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-saml/README.md b/examples/iam-assumable-role-with-saml/README.md new file mode 100644 index 00000000..d171efd5 --- /dev/null +++ b/examples/iam-assumable-role-with-saml/README.md @@ -0,0 +1,54 @@ +# 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 | >= 0.12.6 | +| aws | >= 2.23 | + +## Providers + +| Name | Version | +|------|---------| +| aws | >= 2.23 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| iam_assumable_role_admin | ../../modules/iam-assumable-role-with-saml | | + +## Resources + +| Name | +|------| +| [aws_iam_saml_provider](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_saml_provider) | + +## Inputs + +No input. + +## Outputs + +| Name | Description | +|------|-------------| +| 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-saml/main.tf b/examples/iam-assumable-role-with-saml/main.tf new file mode 100644 index 00000000..902bce8a --- /dev/null +++ b/examples/iam-assumable-role-with-saml/main.tf @@ -0,0 +1,35 @@ +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", + ] +} diff --git a/examples/iam-assumable-role-with-saml/outputs.tf b/examples/iam-assumable-role-with-saml/outputs.tf new file mode 100644 index 00000000..00d3c9d0 --- /dev/null +++ b/examples/iam-assumable-role-with-saml/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/examples/iam-assumable-role-with-saml/saml-metadata.xml b/examples/iam-assumable-role-with-saml/saml-metadata.xml new file mode 100644 index 00000000..c68d77c7 --- /dev/null +++ b/examples/iam-assumable-role-with-saml/saml-metadata.xml @@ -0,0 +1,14 @@ + + + + + + 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-role-with-saml/variables.tf b/examples/iam-assumable-role-with-saml/variables.tf new file mode 100644 index 00000000..e69de29b diff --git a/examples/iam-assumable-role-with-saml/versions.tf b/examples/iam-assumable-role-with-saml/versions.tf new file mode 100644 index 00000000..fff6b757 --- /dev/null +++ b/examples/iam-assumable-role-with-saml/versions.tf @@ -0,0 +1,7 @@ +terraform { + required_version = ">= 0.12.6" + + required_providers { + aws = ">= 2.23" + } +} diff --git a/modules/iam-assumable-role-with-saml/README.md b/modules/iam-assumable-role-with-saml/README.md new file mode 100644 index 00000000..e5bae744 --- /dev/null +++ b/modules/iam-assumable-role-with-saml/README.md @@ -0,0 +1,60 @@ +# 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 | >= 0.12.6 | +| aws | >= 2.23 | + +## Providers + +| Name | Version | +|------|---------| +| aws | >= 2.23 | + +## Modules + +No Modules. + +## Resources + +| Name | +|------| +| [aws_iam_policy_document](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | +| [aws_iam_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | +| [aws_iam_role_policy_attachment](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| aws\_saml\_endpoint | AWS SAML Endpoint | `string` | `"https://signin.aws.amazon.com/saml"` | no | +| create\_role | Whether to create a role | `bool` | `false` | no | +| force\_detach\_policies | Whether policies should be detached from this role when destroying | `bool` | `false` | no | +| max\_session\_duration | Maximum CLI/API session duration in seconds between 3600 and 43200 | `number` | `3600` | no | +| number\_of\_role\_policy\_arns | Number of IAM policies to attach to IAM role | `number` | `null` | no | +| provider\_id | ID of the SAML Provider. Use provider\_ids to specify several IDs. | `string` | `""` | no | +| provider\_ids | List of SAML Provider IDs | `list(string)` | `[]` | no | +| role\_description | IAM Role description | `string` | `""` | no | +| role\_name | IAM role name | `string` | `null` | no | +| role\_name\_prefix | IAM role name prefix | `string` | `null` | 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 | +|------|-------------| +| 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/modules/iam-assumable-role-with-saml/main.tf b/modules/iam-assumable-role-with-saml/main.tf new file mode 100644 index 00000000..eb9d803b --- /dev/null +++ b/modules/iam-assumable-role-with-saml/main.tf @@ -0,0 +1,48 @@ +locals { + 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)) +} + +data "aws_iam_policy_document" "assume_role_with_saml" { + statement { + effect = "Allow" + + actions = ["sts:AssumeRoleWithSAML"] + + 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 = join("", aws_iam_role.this.*.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 new file mode 100644 index 00000000..b3d143c1 --- /dev/null +++ b/modules/iam-assumable-role-with-saml/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-saml/variables.tf b/modules/iam-assumable-role-with-saml/variables.tf new file mode 100644 index 00000000..e363ed48 --- /dev/null +++ b/modules/iam-assumable-role-with-saml/variables.tf @@ -0,0 +1,83 @@ +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 +} diff --git a/modules/iam-assumable-role-with-saml/versions.tf b/modules/iam-assumable-role-with-saml/versions.tf new file mode 100644 index 00000000..fff6b757 --- /dev/null +++ b/modules/iam-assumable-role-with-saml/versions.tf @@ -0,0 +1,7 @@ +terraform { + required_version = ">= 0.12.6" + + required_providers { + aws = ">= 2.23" + } +}