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 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b84d048d..868fb48d 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.100.0 hooks: - id: terraform_fmt - id: terraform_wrapper_module_for_each @@ -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 dff29526..28b7e221 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,16 @@ -# AWS Identity and Access Management (IAM) Terraform module +# AWS IAM Terraform module + +Terraform module which creates AWS IAM resources. [![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md) -## Features +## Usage -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. +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. -## Usage +### IAM Account -`iam-account`: +Creates an account policy and account alias. Module instantiation is once per account. ```hcl module "iam_account" { @@ -17,356 +18,274 @@ 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 + permissions = { + 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 +> [!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. - role_name = "role-with-saml" +```hcl +module "iam_oidc_provider" { + source = "terraform-aws-modules/iam/aws//modules/iam-oidc-provider" + + 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`: - -```hcl -module "iam_assumable_roles" { - source = "terraform-aws-modules/iam/aws//modules/iam-assumable-roles" - - trusted_role_arns = [ - "arn:aws:iam::307990089504:root", - "arn:aws:iam::835367859851:user/anton", - ] - - create_admin_role = true - - create_poweruser_role = true - poweruser_role_name = "developer" - - create_readonly_role = true - readonly_role_requires_mfa = false -} -``` +### IAM Policy -`iam-assumable-roles-with-saml`: +Creates an IAM policy. ```hcl -module "iam_assumable_roles_with_saml" { - source = "terraform-aws-modules/iam/aws//modules/iam-assumable-roles-with-saml" - - create_admin_role = true +module "iam_policy" { + source = "terraform-aws-modules/iam/aws//modules/iam-policy" - create_poweruser_role = true - poweruser_role_name = "developer" + name = "example" + path = "/" + description = "My example policy" - create_readonly_role = true + policy = <<-EOF + { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "ec2:Describe*" + ], + "Effect": "Allow", + "Resource": "*" + } + ] + } + EOF - provider_id = "arn:aws:iam::235367859851:saml-provider/idp_saml" + tags = { + Terraform = "true" + Environment = "dev" + } } ``` -`iam-eks-role`: +### IAM ReadOnly Policy + +Creates an IAM policy that allows read-only access to the list of AWS services provided. ```hcl -module "iam_eks_role" { - source = "terraform-aws-modules/iam/aws//modules/iam-eks-role" +module "iam_read_only_policy" { + source = "terraform-aws-modules/iam/aws//modules/iam-read-only-policy" - role_name = "my-app" + name = "example" + path = "/" + description = "My example read-only policy" - cluster_service_accounts = { - "cluster1" = ["default:my-app"] - "cluster2" = [ - "default:my-app", - "canary:my-app", - ] - } + allowed_services = ["rds", "dynamo", "health"] tags = { - Name = "eks-role" - } - - role_policy_arns = { - AmazonEKS_CNI_Policy = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy" + Terraform = "true" + Environment = "dev" } } ``` -`iam-github-oidc-provider`: +### 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_github_oidc_provider" { - source = "terraform-aws-modules/iam/aws//modules/iam-github-oidc-provider" +module "iam_role" { + source = "terraform-aws-modules/iam/aws//modules/iam-role" + + name = "example" + + trust_policy_permissions = { + 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 = { - Environment = "test" + Terraform = "true" + Environment = "dev" } } ``` -`iam-github-oidc-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_github_oidc_role" { - source = "terraform-aws-modules/iam/aws//modules/iam-github-oidc-role" +module "iam_role_github_oidc" { + source = "terraform-aws-modules/iam/aws//modules/iam-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_wildcard_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" +### IAM Role - SAML 2.0 - assumable_roles = [ - "arn:aws:iam::835367859855:role/readonly" # these roles can be created using `iam_assumable_roles` submodule - ] - - group_users = [ - "user1", - "user2" - ] -} -``` - -`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" - - name = "superadmins" +module "iam_role_saml" { + source = "terraform-aws-modules/iam/aws//modules/iam-role" - group_users = [ - "user1", - "user2" - ] + name = "example" - attach_iam_self_management_policy = true + enable_saml = 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 for EKS Service Accounts (IRSA) -```hcl -module "iam_policy" { - source = "terraform-aws-modules/iam/aws//modules/iam-policy" +> [!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). - name = "example" - path = "/" - description = "My example policy" - - policy = < role + assumeRoleOIDC --> role + assumeRoleSAML --> role + assumeRoles --> role + assumeRolesSAML --> role + githubOIDCRole --> role + + groupWithAssumablePolicy --> group + groupWithPolicies --> group + + githubOIDCProvider --> oidcProvider +``` + +## Additional changes + +### 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 `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 `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 + +1. Removed variables: + + - `iam-account` + - `get_caller_identity` + - `iam-group` + - `custom_group_policies` + - `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` + - `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-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` + - `iam-user` + - None + +2. Renamed variables: + + - `iam-account` + - None + - `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` + - `iam-oidc-provider` + - None + - `iam-policy` + - `create_policy` -> `create` + - `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` + - `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` + - `assume_role_condition_test` -> `trust_condition_test` + - `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-group` + - `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` + - `iam-oidc-provider` + - None + - `iam-policy` + - None + - `iam-read-only-policy` + - `create` + - `source_policy_documents` + - `override_policy_documents` + - `iam-role` + - `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` + - `permissions` + - `policy_name` + - `policy_description` + - `create_inline_policy` + - `source_inline_policy_documents` + - `override_inline_policy_documents` + - `inline_policy_permissions` + - `iam-user` + - None + +4. Removed outputs: + + - `iam-account` + - `caller_identity_account_id` + - `caller_identity_arn` + - `caller_identity_user_id` + - `iam-group` + - `assumable_roles` + - `aws_account_id` + - `iam-oidc-provider` + - None + - `iam-policy` + - `description` + - `path` + - `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` + - `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: + + - `iam-account` + - None + - `iam-group` + - `group_id` -> `id` + - `group_name` -> `name` + - `group_arn` -> `arn` + - `group_users` -> `users` + - `iam-oidc-provider` + - None + - `iam-policy` + - None + - `iam-read-only-policy` + - None + - `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-for-service-accounts` + - None + - `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: + + - `iam-group` + - `unique_id` + - `policy_id` + +### Diff of before <> after + +> [!WARNING] +> Renamed variables are not exhaustively shown below. See sections above for mappings of renamed variables + +#### `iam-account` + +None + +#### `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.60" ++ version = "~> 6.0" + +- create_role = true ++ create = true # is now `true` by default + +- 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"] ++ trust_policy_permissions = { ++ 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"] ++ } ++ ] ++ condition = [{ ++ test = "StringEquals" ++ variable = "sts:ExternalId" ++ values = ["some-id-goes-here"] ++ }] ++ } ++ } + +- 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 + +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 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` + +```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 = [ ++ trust_policy_conditions = [ + { + test = "StringLike" + variable = "aws:RequestTag/Environment" + values = ["example"] + } + ] +} +``` + +##### 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 rm module.iam_role.aws_iam_role_policy_attachment.custom +``` + +#### `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 + +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 rm module.iam_role.aws_iam_role_policy_attachment.custom +``` + +#### `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` + +```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" + + trust_policy_permissions = { + 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" + + trust_policy_permissions = { + 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 + +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 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 +``` + +#### `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 + +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 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 +``` + +#### `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 = [ ++ trust_policy_conditions = [ + { + 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"] ++ permissions = { ++ 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 + +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 rm module.iam_group.aws_iam_group_policy_attachment.custom_arns +``` + +#### `iam-policy` + +None + +#### `iam-read-only-policy` + +None + +#### `iam-role-for-service-accounts` + +None + +#### `iam-user` + +```diff +module "iam_user" { + source = "terraform-aws-modules/iam/aws//modules/iam-user" +- version = "~> 5.60" ++ version = "~> 6.0" + +- policy_arns = ["arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"] ++ policies = { ++ S3ReadOnly = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" ++ } +} +``` + +##### 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 rm module.iam_user[0].aws_iam_user_policy_attachment.this +``` diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 00000000..f417c0ad --- /dev/null +++ b/examples/README.md @@ -0,0 +1,8 @@ +# Examples + +Please note - the examples provided serve two primary means: + +1. Show users working examples of the various ways in which the module can be configured and features supported +2. A means of testing/validating module changes + +Please do not mistake the examples provided as "best practices". It is up to users to consult the AWS service documentation for best practices, usage recommendations, etc. diff --git a/examples/iam-account/README.md b/examples/iam-account/README.md index 05f3f02d..fd2bb428 100644 --- a/examples/iam-account/README.md +++ b/examples/iam-account/README.md @@ -1,4 +1,4 @@ -# IAM account example +# AWS IAM Account Example Configuration in this directory sets [AWS account alias](https://docs.aws.amazon.com/IAM/latest/UserGuide/console_account-alias.html) (also known as Console Account alias) and configures password policy. @@ -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 @@ -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-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-assumable-role-with-oidc/README.md b/examples/iam-assumable-role-with-oidc/README.md deleted file mode 100644 index 1b0ac0c6..00000000 --- a/examples/iam-assumable-role-with-oidc/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# Individual IAM assumable role example - -Configuration in this directory creates a single IAM role which can be assumed by trusted resources using OpenID Connect Federated Users. - -# 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\_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-assumable-roles/variables.tf b/examples/iam-assumable-roles/variables.tf deleted file mode 100644 index e69de29b..00000000 diff --git a/examples/iam-assumable-roles/versions.tf b/examples/iam-assumable-roles/versions.tf deleted file mode 100644 index d8dd1a44..00000000 --- a/examples/iam-assumable-roles/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/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/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-github-oidc/versions.tf b/examples/iam-github-oidc/versions.tf deleted file mode 100644 index d8dd1a44..00000000 --- a/examples/iam-github-oidc/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/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-assumable-roles-policy/versions.tf b/examples/iam-group-with-assumable-roles-policy/versions.tf deleted file mode 100644 index d8dd1a44..00000000 --- a/examples/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/examples/iam-group-with-policies/README.md b/examples/iam-group-with-policies/README.md deleted file mode 100644 index ed1341ff..00000000 --- a/examples/iam-group-with-policies/README.md +++ /dev/null @@ -1,58 +0,0 @@ -# IAM group with policies example - -Configuration in this directory creates IAM group with users who has specified 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\_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\_user1](#module\_iam\_user1) | ../../modules/iam-user | n/a | -| [iam\_user2](#module\_iam\_user2) | ../../modules/iam-user | n/a | - -## Resources - -| Name | Type | -|------|------| -| [aws_iam_policy_document.sample](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | - -## Inputs - -No inputs. - -## Outputs - -| Name | Description | -|------|-------------| -| [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 | -| [iam\_account\_id](#output\_iam\_account\_id) | IAM AWS account id | - 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/versions.tf b/examples/iam-group-with-policies/versions.tf deleted file mode 100644 index d8dd1a44..00000000 --- a/examples/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/examples/iam-group/README.md b/examples/iam-group/README.md new file mode 100644 index 00000000..0991a277 --- /dev/null +++ b/examples/iam-group/README.md @@ -0,0 +1,62 @@ +# AWS IAM Group Example + +Configuration in this directory creates IAM group with users who are allowed to assume IAM roles and extended with 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.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 6.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [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 | + +## Resources + +| Name | Type | +|------|------| +| [aws_iam_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | + +## Inputs + +No inputs. + +## Outputs + +| Name | Description | +|------|-------------| +| [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 | + diff --git a/examples/iam-group/main.tf b/examples/iam-group/main.tf new file mode 100644 index 00000000..e4e57151 --- /dev/null +++ b/examples/iam-group/main.tf @@ -0,0 +1,96 @@ +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, + ] + + permissions = { + 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..83628da4 --- /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-saml/versions.tf b/examples/iam-group/versions.tf similarity index 61% rename from examples/iam-assumable-role-with-saml/versions.tf rename to examples/iam-group/versions.tf index d8dd1a44..db13b0a8 100644 --- a/examples/iam-assumable-role-with-saml/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 new file mode 100644 index 00000000..a039737b --- /dev/null +++ b/examples/iam-oidc-provider/README.md @@ -0,0 +1,57 @@ +# 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.5.7 | +| [aws](#requirement\_aws) | >= 6.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 | n/a | +| [oidc\_iam\_provider\_disabled](#module\_oidc\_iam\_provider\_disabled) | ../../modules/iam-oidc-provider | 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..df62e08a --- /dev/null +++ b/examples/iam-oidc-provider/main.tf @@ -0,0 +1,55 @@ +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 +} + +################################################################################ +# GitHub OIDC IAM Role +################################################################################ + +module "github_oidc_iam_role" { + 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 +} 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/versions.tf b/examples/iam-oidc-provider/versions.tf similarity index 61% rename from examples/iam-assumable-role/versions.tf rename to examples/iam-oidc-provider/versions.tf index d8dd1a44..db13b0a8 100644 --- a/examples/iam-assumable-role/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-policy/README.md b/examples/iam-policy/README.md index 6e9931ad..d5973c02 100644 --- a/examples/iam-policy/README.md +++ b/examples/iam-policy/README.md @@ -1,4 +1,4 @@ -# IAM user example +# IAM Policy Example Configuration in this directory creates IAM policies. @@ -19,22 +19,22 @@ 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 | 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 | -| [iam\_policy\_optional](#module\_iam\_policy\_optional) | ../../modules/iam-policy | n/a | ## Resources @@ -51,9 +51,7 @@ 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 | | [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 index e583e828..d081bb6c 100644 --- a/examples/iam-policy/main.tf +++ b/examples/iam-policy/main.tf @@ -2,17 +2,20 @@ provider "aws" { region = "eu-west-1" } -data "aws_iam_policy_document" "bucket_policy" { - statement { - sid = "AllowFullS3Access" - actions = ["s3:ListAllMyBuckets"] - resources = ["*"] +locals { + name = "ex-${basename(path.cwd)}" + + tags = { + Example = local.name + GithubRepo = "terraform-aws-iam" + GithubOrg = "terraform-aws-modules" } } -######################################### -# IAM policy -######################################### +################################################################################ +# IAM Policy +################################################################################ + module "iam_policy" { source = "../../modules/iam-policy" @@ -20,24 +23,22 @@ module "iam_policy" { path = "/" description = "My example policy" - policy = < [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 | +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-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-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..dea87d3f --- /dev/null +++ b/examples/iam-role-for-service-accounts/README.md @@ -0,0 +1,82 @@ +# 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 + +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 | +|------|--------|---------| +| [amazon\_managed\_service\_prometheus\_irsa](#module\_amazon\_managed\_service\_prometheus\_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 | +| [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 | +| [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 | +| [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 | +| [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..1dadbf45 --- /dev/null +++ b/examples/iam-role-for-service-accounts/main.tf @@ -0,0 +1,432 @@ +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" + } +} + +################################################################################ +# 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 "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" + + 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 "fsx_openzfs_csi_irsa" { + source = "../../modules/iam-role-for-service-accounts" + + 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"] + } + } + + 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 "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 +} + +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 +################################################################################ + +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "~> 6.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 = "~> 21.0" + + 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 = { + example = { + ami_type = "AL2023_x86_64_STANDARD" + instance_types = ["m5.xlarge"] + + min_size = 1 + max_size = 2 + desired_size = 1 + } + } + + 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..3a82ead9 --- /dev/null +++ b/examples/iam-role-for-service-accounts/outputs.tf @@ -0,0 +1,37 @@ +################################################################################ +# 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-roles-with-saml/versions.tf b/examples/iam-role-for-service-accounts/versions.tf similarity index 61% rename from examples/iam-assumable-roles-with-saml/versions.tf rename to examples/iam-role-for-service-accounts/versions.tf index d8dd1a44..db13b0a8 100644 --- a/examples/iam-assumable-roles-with-saml/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/README.md b/examples/iam-role/README.md new file mode 100644 index 00000000..28a0c076 --- /dev/null +++ b/examples/iam-role/README.md @@ -0,0 +1,86 @@ +# 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.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 6.0 | + +## Modules + +| 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 | +| [iam\_role\_saml](#module\_iam\_role\_saml) | ../../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_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 + +No inputs. + +## Outputs + +| 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 | +| [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 | +| [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 | +| [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/main.tf b/examples/iam-role/main.tf new file mode 100644 index 00000000..6038b4d9 --- /dev/null +++ b/examples/iam-role/main.tf @@ -0,0 +1,212 @@ +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_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] + policies = { + PowerUserAccess = "arn:aws:iam::aws:policy/PowerUserAccess" + } + } + } + + name = each.key + + trust_policy_permissions = { + TrustRoleAndServiceToAssume = { + principals = [{ + type = "AWS" + identifiers = each.value.trusted_arns + }] + + condition = [ + { + 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 +} + +################################################################################ +# IAM Role - Instance Profile +################################################################################ + +module "iam_role_instance_profile" { + source = "../../modules/iam-role" + + name = "${local.name}-instance-profile" + + create_instance_profile = true + + trust_policy_permissions = { + 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 - CircleCI OIDC +################################################################################ + +module "iam_role_circleci_oidc" { + source = "../../modules/iam-role" + + name = local.name + + enable_oidc = true + oidc_provider_urls = ["oidc.circleci.com/org/"] + oidc_audiences = [""] + + policies = { + AmazonEC2ContainerRegistryPowerUser = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser" + } + + 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 +################################################################################ + +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 +} + +resource "aws_iam_saml_provider" "this" { + name = "idp_saml" + saml_metadata_document = file("saml-metadata.xml") +} diff --git a/examples/iam-role/outputs.tf b/examples/iam-role/outputs.tf new file mode 100644 index 00000000..db078f54 --- /dev/null +++ b/examples/iam-role/outputs.tf @@ -0,0 +1,155 @@ +################################################################################ +# 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 - GitHub OIDC +################################################################################ + +output "github_oidc_iam_role_name" { + description = "The name of the IAM role" + value = module.iam_role_github_oidc.name +} + +output "github_oidc_iam_role_arn" { + description = "The Amazon Resource Name (ARN) specifying the IAM role" + value = module.iam_role_github_oidc.arn +} + +output "github_oidc_iam_role_unique_id" { + description = "Stable and unique string identifying the IAM role" + value = module.iam_role_github_oidc.unique_id +} + +output "github_oidc_iam_instance_profile_arn" { + description = "ARN assigned by AWS to the instance profile" + value = module.iam_role_github_oidc.instance_profile_arn +} + +output "github_oidc_iam_instance_profile_id" { + description = "Instance profile's ID" + value = module.iam_role_github_oidc.instance_profile_id +} + +output "github_oidc_iam_instance_profile_name" { + description = "Name of IAM instance profile" + value = module.iam_role_github_oidc.instance_profile_name +} + +output "github_oidc_iam_instance_profile_unique_id" { + description = "Stable and unique string identifying the IAM instance profile" + 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 +################################################################################ + +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/examples/iam-assumable-role-with-saml/saml-metadata.xml b/examples/iam-role/saml-metadata.xml similarity index 100% rename from examples/iam-assumable-role-with-saml/saml-metadata.xml rename to examples/iam-role/saml-metadata.xml diff --git a/examples/iam-assumable-roles-with-saml/variables.tf b/examples/iam-role/variables.tf similarity index 100% rename from examples/iam-assumable-roles-with-saml/variables.tf rename to examples/iam-role/variables.tf diff --git a/examples/iam-assumable-role-with-oidc/versions.tf b/examples/iam-role/versions.tf similarity index 61% rename from examples/iam-assumable-role-with-oidc/versions.tf rename to examples/iam-role/versions.tf index d8dd1a44..db13b0a8 100644 --- a/examples/iam-assumable-role-with-oidc/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 038d1dea..10dd1ffd 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 @@ -20,14 +20,12 @@ 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 | +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/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 10c0c4b1..62d3979f 100644 --- a/modules/iam-account/README.md +++ b/modules/iam-account/README.md @@ -1,17 +1,38 @@ -# 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. -## Notes +## Usage + +```hcl +module "iam_account" { + source = "terraform-aws-modules/iam/aws//modules/iam-account" + + account_alias = "awesome-company" -* 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: + 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 +} ``` -* aws_iam_account_alias.this: Error creating account alias with name my-account-alias + +## 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: -``` -$ terraform import module.iam_account.aws_iam_account_alias.this this + +```sh +$ 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! @@ -26,14 +47,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 @@ -45,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 @@ -53,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 | @@ -68,8 +88,9 @@ 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. | +| [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-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..0f5f32a3 100644 --- a/modules/iam-account/outputs.tf +++ b/modules/iam-account/outputs.tf @@ -1,19 +1,4 @@ -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, "") -} - -output "caller_identity_arn" { - description = "The AWS ARN associated with the calling entity" - value = try(data.aws_caller_identity.this[0].arn, "") -} - -output "caller_identity_user_id" { - description = "The unique identifier of the calling entity" - value = try(data.aws_caller_identity.this[0].user_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 = 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-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-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-oidc/versions.tf b/modules/iam-assumable-role-with-oidc/versions.tf deleted file mode 100644 index d8dd1a44..00000000 --- a/modules/iam-assumable-role-with-oidc/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-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-with-saml/versions.tf b/modules/iam-assumable-role-with-saml/versions.tf deleted file mode 100644 index d8dd1a44..00000000 --- a/modules/iam-assumable-role-with-saml/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-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-role/versions.tf b/modules/iam-assumable-role/versions.tf deleted file mode 100644 index d8dd1a44..00000000 --- a/modules/iam-assumable-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/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-with-saml/versions.tf b/modules/iam-assumable-roles-with-saml/versions.tf deleted file mode 100644 index d8dd1a44..00000000 --- a/modules/iam-assumable-roles-with-saml/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-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-assumable-roles/versions.tf b/modules/iam-assumable-roles/versions.tf deleted file mode 100644 index d8dd1a44..00000000 --- a/modules/iam-assumable-roles/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-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/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-github-oidc-role/versions.tf b/modules/iam-github-oidc-role/versions.tf deleted file mode 100644 index d8dd1a44..00000000 --- a/modules/iam-github-oidc-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/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..f05a3efb --- /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 + permissions = { + 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.5.7 | +| [aws](#requirement\_aws) | >= 6.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 6.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 | +| [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 | +| [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 | + +## 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..cb00e18a --- /dev/null +++ b/modules/iam-group/main.tf @@ -0,0 +1,266 @@ +data "aws_partition" "current" { + count = var.create ? 1 : 0 +} + +data "aws_caller_identity" "current" { + count = var.create ? 1 : 0 +} + +locals { + 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:${local.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 || var.permissions != null) + + 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 +# 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:GetAccountSummary", + "iam:GetAccountPasswordPolicy", + "iam:ListAccountAliases", + "iam:ListVirtualMFADevices", + ] + resources = ["*"] + } + } + + dynamic "statement" { + for_each = var.enable_self_management_permissions ? [1] : [] + + content { + sid = "ManageOwnPasswords" + actions = [ + "iam:ChangePassword", + "iam:GetLoginProfile", + "iam:GetUser", + "iam:UpdateLoginProfile", + ] + 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", + "iam:GetAccessKeyLastUsed", + "iam:TagUser", + "iam:ListUserTags", + "iam:UntagUser", + ] + 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:${local.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" + 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"] + } + } + } + + dynamic "statement" { + for_each = var.permissions != null ? var.permissions : {} + + 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_policy" "this" { + count = local.create_policy ? 1 : 0 + + 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 + + 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/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-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..87b3bdd7 --- /dev/null +++ b/modules/iam-group/variables.tf @@ -0,0 +1,117 @@ +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 "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)) + 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 "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 + 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/modules/iam-group/versions.tf b/modules/iam-group/versions.tf new file mode 100644 index 00000000..db13b0a8 --- /dev/null +++ b/modules/iam-group/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.5.7" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 6.0" + } + } +} diff --git a/modules/iam-github-oidc-provider/README.md b/modules/iam-oidc-provider/README.md similarity index 59% rename from modules/iam-github-oidc-provider/README.md rename to modules/iam-oidc-provider/README.md index 0a2244e6..8694aded 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" @@ -21,15 +43,15 @@ module "iam_github_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 @@ -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 53% rename from modules/iam-github-oidc-provider/main.tf rename to modules/iam-oidc-provider/main.tf index 1555ff54..94184ff7 100644 --- a/modules/iam-github-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,8 +20,8 @@ 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}"]) - thumbprint_list = distinct(concat(data.tls_certificate.this[0].certificates[*].sha1_fingerprint, var.additional_thumbprints)) + 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-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..96525dc4 100644 --- a/modules/iam-github-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) @@ -21,13 +25,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 74% rename from modules/iam-github-oidc-provider/versions.tf rename to modules/iam-oidc-provider/versions.tf index 3501ad27..c46f0c4f 100644 --- a/modules/iam-github-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-policy/README.md b/modules/iam-policy/README.md index 03abb122..568c14ea 100644 --- a/modules/iam-policy/README.md +++ b/modules/iam-policy/README.md @@ -1,20 +1,51 @@ -# iam-policy +# AWS IAM Policy -Creates 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.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 @@ -30,11 +61,11 @@ No modules. | 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 | +| [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` | `"/"` | 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 | @@ -43,9 +74,11 @@ 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 | + +## 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 index c5028b38..6100a0c2 100644 --- a/modules/iam-policy/main.tf +++ b/modules/iam-policy/main.tf @@ -1,12 +1,15 @@ +################################################################################ +# IAM Policy +################################################################################ + resource "aws_iam_policy" "policy" { - count = var.create_policy ? 1 : 0 + count = var.create ? 1 : 0 + description = var.description name = var.name name_prefix = var.name_prefix path = var.path - description = var.description - - policy = var.policy + policy = var.policy tags = var.tags } diff --git a/modules/iam-policy/outputs.tf b/modules/iam-policy/outputs.tf index d8900802..8e2a38d6 100644 --- a/modules/iam-policy/outputs.tf +++ b/modules/iam-policy/outputs.tf @@ -1,29 +1,23 @@ +################################################################################ +# 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) } diff --git a/modules/iam-policy/variables.tf b/modules/iam-policy/variables.tf index 8e596143..a21b3a72 100644 --- a/modules/iam-policy/variables.tf +++ b/modules/iam-policy/variables.tf @@ -1,4 +1,4 @@ -variable "create_policy" { +variable "create" { description = "Whether to create the IAM policy" type = bool default = true @@ -19,13 +19,13 @@ variable "name_prefix" { variable "path" { description = "The path of the policy in IAM" type = string - default = "/" + default = null } variable "description" { description = "The description of the policy" type = string - default = "IAM Policy" + default = null } variable "policy" { diff --git a/modules/iam-policy/versions.tf b/modules/iam-policy/versions.tf index d8dd1a44..db13b0a8 100644 --- a/modules/iam-policy/versions.tf +++ b/modules/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" } } } diff --git a/modules/iam-read-only-policy/README.md b/modules/iam-read-only-policy/README.md index 2faeccce..fd10d0ca 100644 --- a/modules/iam-read-only-policy/README.md +++ b/modules/iam-read-only-policy/README.md @@ -1,21 +1,41 @@ -# 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 | 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 @@ -26,27 +46,25 @@ 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 | | [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 | -| [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 | +| [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` | `null` | no | +| [name](#input\_name) | Name to use on IAM policy created | `string` | `null` | 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 | ## Outputs @@ -54,10 +72,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..7a61badf 100644 --- a/modules/iam-read-only-policy/main.tf +++ b/modules/iam-read-only-policy/main.tf @@ -1,24 +1,28 @@ +################################################################################ +# 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 + 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.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.source_inline_policy_documents + override_policy_documents = var.override_inline_policy_documents dynamic "statement" { - for_each = toset(local.allowed_services) + for_each = toset(distinct(var.allowed_services)) + content { sid = replace(statement.value, "-", "") @@ -32,61 +36,49 @@ data "aws_iam_policy_document" "allowed_services" { 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, "-", "") actions = [ "${statement.value}:List*", "${statement.value}:Get*", + "${statement.value}:BatchGet*", "${statement.value}:Describe*", "${statement.value}:View*", ] resources = ["*"] } } -} -data "aws_iam_policy_document" "sts" { - count = var.allow_predefined_sts_actions ? 1 : 0 + dynamic "statement" { + for_each = var.allow_predefined_sts_actions ? [1] : [] - statement { - sid = "STS" - actions = [ - "sts:GetAccessKeyInfo", - "sts:GetCallerIdentity", - "sts:GetSessionToken", - ] - resources = ["*"] + content { + sid = "STS" + actions = [ + "sts:GetAccessKeyInfo", + "sts:GetCallerIdentity", + "sts:GetSessionToken", + ] + resources = ["*"] + } } -} -data "aws_iam_policy_document" "logs_query" { - count = var.allow_cloudwatch_logs_query ? 1 : 0 + dynamic "statement" { + for_each = var.allow_cloudwatch_logs_query ? [1] : [] - statement { - sid = "AllowLogsQuery" - actions = [ - "logs:StartQuery", - "logs:StopQuery", - "logs:FilterLogEvents" - ] - resources = ["*"] + content { + sid = "AllowLogsQuery" + actions = [ + "logs:StartQuery", + "logs:StopQuery", + "logs:FilterLogEvents" + ] + 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] - ) -} 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..b53fea72 100644 --- a/modules/iam-read-only-policy/variables.tf +++ b/modules/iam-read-only-policy/variables.tf @@ -1,48 +1,65 @@ +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" - 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" { - description = "The path of the policy in IAM" + 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" { 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 "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 "tags" { - description = "A map of tags to add to all resources." - type = map(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-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-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 53% rename from modules/iam-role-for-service-accounts-eks/README.md rename to modules/iam-role-for-service-accounts/README.md index c572d798..104e2cb4 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,26 +19,22 @@ 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" { - source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" - role_name = "my-app" +module "irsa" { + 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 = { @@ -44,60 +46,66 @@ module "iam_eks_role" { 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 = { - main = { + this = { provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["default:my-app", "canary:my-app"] + 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_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] - - attach_vpc_cni_policy = true - vpc_cni_enable_ipv4 = true - - oidc_providers = { - main = { - provider_arn = module.eks.oidc_provider_arn - namespace_service_accounts = ["default:my-app", "canary:my-app"] - } + tags = { + Terraform = "true" + Environment = "dev" } } 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 = {} + aws-ebs-csi-driver = { + service_account_role_arn = module.ebs_csi_driver_irsa.iam_role_arn + } + 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 + } } } ``` @@ -107,14 +115,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 @@ -124,51 +132,14 @@ 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.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.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 +149,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.karpenter_controller](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 | @@ -187,18 +158,13 @@ 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 | -| [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 | +| [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 | | [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 +175,50 @@ 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\_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 | +| [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 | +| [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 | | [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\_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 | +| [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 | | [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 +227,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..8f292546 --- /dev/null +++ b/modules/iam-role-for-service-accounts/main.tf @@ -0,0 +1,230 @@ +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.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.trust_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.permissions != null ? var.permissions : {} + + 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 +} + +################################################################################ +# IAM Role Inline policy +################################################################################ + +locals { + 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_permissions != null ? var.inline_policy_permissions : {} + + 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/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-github-oidc-role/outputs.tf b/modules/iam-role-for-service-accounts/outputs.tf similarity index 57% rename from modules/iam-github-oidc-role/outputs.tf rename to modules/iam-role-for-service-accounts/outputs.tf index 29f9a29d..a1f0877b 100644 --- a/modules/iam-github-oidc-role/outputs.tf +++ b/modules/iam-role-for-service-accounts/outputs.tf @@ -1,5 +1,5 @@ ################################################################################ -# GitHub OIDC Role +# IAM Role ################################################################################ output "arn" { @@ -21,3 +21,17 @@ 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 63% rename from modules/iam-role-for-service-accounts-eks/variables.tf rename to modules/iam-role-for-service-accounts/variables.tf index 8dce3e2f..085388ed 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 "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 = "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 "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) + 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 @@ -401,3 +377,51 @@ variable "attach_cloudwatch_observability_policy" { type = bool default = false } + +################################################################################ +# IAM Role Inline policy +################################################################################ + +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) + 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/modules/iam-role-for-service-accounts/versions.tf b/modules/iam-role-for-service-accounts/versions.tf new file mode 100644 index 00000000..db13b0a8 --- /dev/null +++ b/modules/iam-role-for-service-accounts/versions.tf @@ -0,0 +1,10 @@ +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 new file mode 100644 index 00000000..f277d1b4 --- /dev/null +++ b/modules/iam-role/README.md @@ -0,0 +1,200 @@ +# 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_wildcard_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_wildcard_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" + + name = "example" + + trust_policy_permissions = { + 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 = { + Terraform = "true" + Environment = "dev" + } +} +``` + +### 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 + +| 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_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 | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [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 | +| [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\_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 | +| [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 | `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 | +| [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 + +| 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..d2103cd8 --- /dev/null +++ b/modules/iam-role/main.tf @@ -0,0 +1,387 @@ +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://", "")] + 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_oidc ? local.oidc_providers : [] + + content { + effect = "Allow" + actions = [ + "sts:TagSession", + "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 = "StringEquals" + variable = "${statement.value}:aud" + values = var.oidc_audiences + } + } + + # Generic conditions + dynamic "condition" { + for_each = var.trust_policy_conditions + + content { + test = condition.value.test + variable = condition.value.variable + values = condition.value.values + } + } + } + } + + # 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/${var.github_provider}"] + } + + condition { + test = "ForAllValues:StringEquals" + variable = "${var.github_provider}:iss" + values = ["https://${var.github_provider}"] + } + + condition { + test = "ForAllValues:StringEquals" + variable = "${var.github_provider}:aud" + values = coalescelist(var.oidc_audiences, ["sts.amazonaws.com"]) + } + + 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.trust_policy_conditions + + content { + test = condition.value.test + variable = condition.value.variable + values = condition.value.values + } + } + } + } + + # 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"]) + } + + 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.trust_policy_conditions + + content { + test = condition.value.test + variable = condition.value.variable + values = condition.value.values + } + } + } + } + + # SAML + dynamic "statement" { + for_each = var.enable_saml ? [1] : [] + + content { + effect = "Allow" + actions = compact(distinct(concat( + [ + "sts:TagSession", + "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 conditions + dynamic "condition" { + for_each = var.trust_policy_conditions + + content { + test = condition.value.test + variable = condition.value.variable + values = condition.value.values + } + } + } + } + + # Generic statements + dynamic "statement" { + for_each = var.trust_policy_permissions != null ? var.trust_policy_permissions : {} + + 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 +} + +################################################################################ +# IAM Role Inline policy +################################################################################ + +locals { + 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_permissions != null ? var.inline_policy_permissions : {} + + 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 +################################################################################ + +resource "aws_iam_instance_profile" "this" { + count = var.create && var.create_instance_profile ? 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 + 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..2107332c --- /dev/null +++ b/modules/iam-role/variables.tf @@ -0,0 +1,229 @@ +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 = null +} + +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 "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)) + 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 "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 + 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) + 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 = list(string) + default = [] +} + +variable "oidc_wildcard_subjects" { + description = "The OIDC subject using wildcards to be added to the role policy" + 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 = list(string) + default = [] +} + +variable "enable_github_oidc" { + description = "Enable GitHub OIDC provider trust for the role" + type = bool + 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 + 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 Role Inline policy +################################################################################ + +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) + 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 +################################################################################ + +variable "create_instance_profile" { + description = "Determines whether to create an instance profile" + type = bool + default = false +} diff --git a/modules/iam-role/versions.tf b/modules/iam-role/versions.tf new file mode 100644 index 00000000..db13b0a8 --- /dev/null +++ b/modules/iam-role/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.5.7" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 6.0" + } + } +} diff --git a/modules/iam-user/README.md b/modules/iam-user/README.md index 44abbe7d..8dbb3802 100644 --- a/modules/iam-user/README.md +++ b/modules/iam-user/README.md @@ -1,38 +1,45 @@ -# 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 | 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 @@ -43,58 +50,53 @@ 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_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_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 | -| [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 | +| [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` | `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 | | [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..3e538810 100644 --- a/modules/iam-user/main.tf +++ b/modules/iam-user/main.tf @@ -1,54 +1,58 @@ +################################################################################ +# 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 } +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 +################################################################################ + 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 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] - } } +################################################################################ +# 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/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/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 : <