├── VERSION ├── .github ├── CODEOWNERS ├── dependabot.yml ├── workflows │ ├── yaml.integration.yaml │ ├── github.repo.yaml │ ├── github.pr.yaml │ └── terraform.integration.yaml ├── auto-assign.yaml ├── labeler.yaml ├── labels.yaml └── labels.common.yaml ├── examples ├── account-alias │ ├── outputs.tf │ ├── versions.tf │ └── main.tf ├── account-contacts │ ├── outputs.tf │ ├── versions.tf │ └── main.tf ├── account-password-policy │ ├── outputs.tf │ ├── versions.tf │ └── main.tf ├── iam-oidc-identity-providers │ ├── outputs.tf │ ├── versions.tf │ └── main.tf ├── iam-saml-identity-providers │ ├── outputs.tf │ ├── versions.tf │ ├── main.tf │ └── saml-idp-metadata.xml └── github-trusted-iam-roles │ ├── versions.tf │ ├── outputs.tf │ └── main.tf ├── modules ├── iam-role │ ├── migrations.tf │ ├── versions.tf │ ├── locals.tf │ ├── resource-group.tf │ ├── policies.tf │ ├── outputs.tf │ ├── main.tf │ ├── trusted-service-policies.tf │ ├── trusted-saml-provider-policies.tf │ ├── trusted-iam-entity-policies.tf │ └── trusted-oidc-provider-policies.tf ├── iam-user │ ├── migrations.tf │ ├── versions.tf │ ├── resource-group.tf │ ├── policies.tf │ ├── main.tf │ ├── credentials.tf │ ├── outputs.tf │ └── variables.tf ├── iam-predefined-policies │ ├── migrations.tf │ ├── versions.tf │ ├── policies │ │ ├── self-service-access-key.json │ │ ├── self-service-ssh-public-key.json │ │ ├── self-service-password.json │ │ └── self-service-mfa.json │ ├── resource-group.tf │ ├── outputs.tf │ ├── main.tf │ ├── variables.tf │ └── README.md ├── iam-service-linked-role │ ├── migrations.tf │ ├── versions.tf │ ├── resource-group.tf │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── README.md ├── region │ ├── versions.tf │ ├── ses.tf │ ├── ebs.tf │ ├── resource-group.tf │ ├── cloudwatch.tf │ ├── vpc.tf │ ├── resource-explorer.tf │ ├── ec2.tf │ ├── main.tf │ ├── service-quotas.tf │ ├── outputs.tf │ └── variables.tf ├── iam-group │ ├── versions.tf │ ├── main.tf │ ├── policies.tf │ ├── outputs.tf │ ├── variables.tf │ └── README.md ├── iam-policy │ ├── versions.tf │ ├── resource-group.tf │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── README.md ├── iam-saml-identity-provider │ ├── versions.tf │ ├── resource-group.tf │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── README.md ├── account │ ├── versions.tf │ ├── cost.tf │ ├── sts.tf │ ├── s3.tf │ ├── ec2.tf │ ├── regions.tf │ ├── cost-optimization-hub.tf │ ├── main.tf │ ├── support-app.tf │ ├── contacts.tf │ └── outputs.tf └── iam-oidc-identity-provider │ ├── versions.tf │ ├── resource-group.tf │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── README.md ├── .editorconfig ├── .pre-commit-config.yaml ├── .yamllint.yaml ├── .gitignore ├── .tflint.hcl ├── README.md └── LICENSE /VERSION: -------------------------------------------------------------------------------- 1 | 0.33.2 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @posquit0 2 | -------------------------------------------------------------------------------- /examples/account-alias/outputs.tf: -------------------------------------------------------------------------------- 1 | output "account" { 2 | description = "The configuration of the AWS account." 3 | value = module.account 4 | } 5 | -------------------------------------------------------------------------------- /examples/account-contacts/outputs.tf: -------------------------------------------------------------------------------- 1 | output "account" { 2 | description = "The configuration of the AWS account." 3 | value = module.account 4 | } 5 | -------------------------------------------------------------------------------- /examples/account-password-policy/outputs.tf: -------------------------------------------------------------------------------- 1 | output "account" { 2 | description = "The configuration of the AWS account." 3 | value = module.account 4 | } 5 | -------------------------------------------------------------------------------- /modules/iam-role/migrations.tf: -------------------------------------------------------------------------------- 1 | # 2022-11-24 2 | moved { 3 | from = aws_resourcegroups_group.this[0] 4 | to = module.resource_group[0].aws_resourcegroups_group.this 5 | } 6 | -------------------------------------------------------------------------------- /modules/iam-user/migrations.tf: -------------------------------------------------------------------------------- 1 | # 2022-11-24 2 | moved { 3 | from = aws_resourcegroups_group.this[0] 4 | to = module.resource_group[0].aws_resourcegroups_group.this 5 | } 6 | -------------------------------------------------------------------------------- /examples/iam-oidc-identity-providers/outputs.tf: -------------------------------------------------------------------------------- 1 | output "oidc_providers" { 2 | description = "A list of IAM OIDC Identity Providers." 3 | value = module.oidc_provider 4 | } 5 | -------------------------------------------------------------------------------- /examples/iam-saml-identity-providers/outputs.tf: -------------------------------------------------------------------------------- 1 | output "saml_providers" { 2 | description = "A list of IAM SAML Identity Providers." 3 | value = module.saml_provider 4 | } 5 | -------------------------------------------------------------------------------- /modules/iam-predefined-policies/migrations.tf: -------------------------------------------------------------------------------- 1 | # 2022-11-24 2 | moved { 3 | from = aws_resourcegroups_group.this[0] 4 | to = module.resource_group[0].aws_resourcegroups_group.this 5 | } 6 | -------------------------------------------------------------------------------- /modules/iam-service-linked-role/migrations.tf: -------------------------------------------------------------------------------- 1 | # 2022-11-24 2 | moved { 3 | from = aws_resourcegroups_group.this[0] 4 | to = module.resource_group[0].aws_resourcegroups_group.this 5 | } 6 | -------------------------------------------------------------------------------- /modules/iam-role/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.12" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 6.12" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/iam-user/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.12" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 6.12" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/region/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.12" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 6.21" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/account-alias/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~> 1.5" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 5.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/iam-group/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.12" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 6.12" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/iam-policy/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.12" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 6.12" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/account-contacts/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~> 1.5" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 5.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/account-password-policy/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~> 1.5" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 5.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/github-trusted-iam-roles/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~> 1.5" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 5.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/iam-predefined-policies/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.12" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 6.12" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/iam-role/locals.tf: -------------------------------------------------------------------------------- 1 | data "aws_caller_identity" "this" {} 2 | data "aws_partition" "this" {} 3 | 4 | locals { 5 | account_id = data.aws_caller_identity.this.account_id 6 | partition = data.aws_partition.this.partition 7 | } 8 | -------------------------------------------------------------------------------- /modules/iam-service-linked-role/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.12" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 6.12" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/iam-oidc-identity-providers/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~> 1.5" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 5.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/iam-saml-identity-providers/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~> 1.5" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 5.0" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/iam-saml-identity-provider/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.12" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 6.12" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: github-actions 5 | directory: / 6 | schedule: 7 | interval: daily 8 | 9 | - package-ecosystem: terraform 10 | directories: 11 | - /modules/* 12 | schedule: 13 | interval: weekly 14 | -------------------------------------------------------------------------------- /examples/github-trusted-iam-roles/outputs.tf: -------------------------------------------------------------------------------- 1 | output "oidc_providers" { 2 | description = "A list of IAM OIDC Identity Providers." 3 | value = module.oidc_provider 4 | } 5 | 6 | output "roles" { 7 | description = "A list of IAM Roles." 8 | value = module.role 9 | } 10 | -------------------------------------------------------------------------------- /modules/account/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.12" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 6.12" 8 | } 9 | awscc = { 10 | source = "hashicorp/awscc" 11 | version = ">= 1.55" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /modules/iam-oidc-identity-provider/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.12" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 6.12" 8 | } 9 | tls = { 10 | source = "hashicorp/tls" 11 | version = ">= 4.1" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /modules/region/ses.tf: -------------------------------------------------------------------------------- 1 | ################################################### 2 | # AWS SES (Simple Email Service) 3 | ################################################### 4 | 5 | resource "aws_sesv2_account_suppression_attributes" "this" { 6 | region = var.region 7 | 8 | suppressed_reasons = var.ses.suppression_reasons 9 | } 10 | -------------------------------------------------------------------------------- /modules/account/cost.tf: -------------------------------------------------------------------------------- 1 | ################################################### 2 | # Cost Allocation Tags 3 | ################################################### 4 | 5 | resource "aws_ce_cost_allocation_tag" "this" { 6 | for_each = toset(var.cost.cost_allocation_tags) 7 | 8 | tag_key = each.value 9 | status = "Active" 10 | } 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Top-most EditorConfig file 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_style = space 8 | indent_size = 2 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.{tf,tfvars}] 13 | indent_size = 2 14 | indent_style = space 15 | 16 | [.gitignore] 17 | end_of_line = 18 | -------------------------------------------------------------------------------- /examples/account-alias/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "us-east-1" 3 | } 4 | 5 | 6 | ################################################### 7 | # AWS Account 8 | ################################################### 9 | 10 | module "account" { 11 | source = "../../modules/account" 12 | # source = "tedilabs/account/aws//modules/account" 13 | # version = "~> 0.26.0" 14 | 15 | # This is alias for the AWS account. 16 | name = "example-210925" 17 | } 18 | -------------------------------------------------------------------------------- /modules/account/sts.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | global_endpoint_token_version = { 3 | "v1" = "v1Token" 4 | "v2" = "v2Token" 5 | } 6 | } 7 | 8 | 9 | ################################################### 10 | # AWS STS (Security Token Service) 11 | ################################################### 12 | 13 | resource "aws_iam_security_token_service_preferences" "this" { 14 | global_endpoint_token_version = local.global_endpoint_token_version[var.sts_global_endpoint_token_version] 15 | } 16 | -------------------------------------------------------------------------------- /modules/iam-predefined-policies/policies/self-service-access-key.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "AllowManageOwnAccessKeys", 6 | "Effect": "Allow", 7 | "Action": [ 8 | "iam:CreateAccessKey", 9 | "iam:ListAccessKeys", 10 | "iam:UpdateAccessKeys", 11 | "iam:DeleteAccessKey" 12 | ], 13 | "Resource": [ 14 | "arn:aws:iam::*:user/${aws:username}", 15 | "arn:aws:iam::*:user/*/${aws:username}" 16 | ] 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /modules/account/s3.tf: -------------------------------------------------------------------------------- 1 | ################################################### 2 | # Account-level Public Access Block for S3 3 | ################################################### 4 | 5 | resource "aws_s3_account_public_access_block" "this" { 6 | account_id = data.aws_caller_identity.this.account_id 7 | 8 | block_public_acls = !var.s3_public_access_enabled 9 | block_public_policy = !var.s3_public_access_enabled 10 | ignore_public_acls = !var.s3_public_access_enabled 11 | restrict_public_buckets = !var.s3_public_access_enabled 12 | } 13 | -------------------------------------------------------------------------------- /modules/account/ec2.tf: -------------------------------------------------------------------------------- 1 | ################################################### 2 | # Account-level Spot Data Feed Subscription for EC2 3 | ################################################### 4 | 5 | # INFO: Need permission to S3 ACL 6 | # https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/spot-data-feeds.html#using-spot-instances-dfs3 7 | resource "aws_spot_datafeed_subscription" "this" { 8 | count = var.ec2_spot_datafeed_subscription.enabled ? 1 : 0 9 | 10 | bucket = var.ec2_spot_datafeed_subscription.s3_bucket.name 11 | prefix = var.ec2_spot_datafeed_subscription.s3_bucket.key_prefix 12 | } 13 | -------------------------------------------------------------------------------- /modules/iam-predefined-policies/policies/self-service-ssh-public-key.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "AllowManageOwnSSHPublicKeys", 6 | "Effect": "Allow", 7 | "Action": [ 8 | "iam:DeleteSSHPublicKey", 9 | "iam:GetSSHPublicKey", 10 | "iam:ListSSHPublicKeys", 11 | "iam:UpdateSSHPublicKey", 12 | "iam:UploadSSHPublicKey" 13 | ], 14 | "Resource": [ 15 | "arn:aws:iam::*:user/${aws:username}", 16 | "arn:aws:iam::*:user/*/${aws:username}" 17 | ] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.github/workflows/yaml.integration.yaml: -------------------------------------------------------------------------------- 1 | name: Integration - YAML 2 | 3 | 4 | on: 5 | push: 6 | branches: 7 | - main 8 | paths: 9 | - "**.yaml" 10 | - "**.yml" 11 | 12 | pull_request: 13 | paths: 14 | - "**.yaml" 15 | - "**.yml" 16 | 17 | workflow_dispatch: {} 18 | 19 | 20 | concurrency: 21 | group: integration-yaml-${{ github.ref }} 22 | cancel-in-progress: true 23 | 24 | 25 | jobs: 26 | lint: 27 | name: Lint (yamllint) 28 | uses: tedilabs/github-actions/.github/workflows/yaml.yamllint.yaml@main 29 | 30 | with: 31 | yamllint_version: latest 32 | yamllint_config_file: .yamllint.yaml 33 | yamllint_target_dir: ./ 34 | secrets: 35 | token: ${{ secrets.GITHUB_TOKEN }} 36 | -------------------------------------------------------------------------------- /modules/account/regions.tf: -------------------------------------------------------------------------------- 1 | data "aws_regions" "this" { 2 | # Always true 3 | all_regions = aws_account_region.this != null 4 | 5 | filter { 6 | name = "opt-in-status" 7 | values = ["opt-in-not-required", "opted-in"] 8 | } 9 | } 10 | 11 | locals { 12 | all_available_regions = data.aws_regions.this.names 13 | } 14 | 15 | 16 | ################################################### 17 | # Regions 18 | ################################################### 19 | 20 | # INFO: Not supported attributes 21 | # - `account_id` 22 | # INFO: Not supported idempotent operation 23 | # TODO: How to manage disabled region? 24 | resource "aws_account_region" "this" { 25 | for_each = var.additional_regions 26 | 27 | region_name = each.key 28 | enabled = each.value 29 | } 30 | -------------------------------------------------------------------------------- /modules/iam-policy/resource-group.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | resource_group_name = (var.resource_group.name != "" 3 | ? var.resource_group.name 4 | : join(".", [ 5 | local.metadata.package, 6 | local.metadata.module, 7 | replace(local.metadata.name, "/[^a-zA-Z0-9_\\.-]/", "-"), 8 | ]) 9 | ) 10 | } 11 | 12 | 13 | module "resource_group" { 14 | source = "tedilabs/misc/aws//modules/resource-group" 15 | version = "~> 0.12.0" 16 | 17 | count = (var.resource_group.enabled && var.module_tags_enabled) ? 1 : 0 18 | 19 | name = local.resource_group_name 20 | description = var.resource_group.description 21 | 22 | query = { 23 | resource_tags = local.module_tags 24 | } 25 | 26 | module_tags_enabled = false 27 | tags = merge( 28 | local.module_tags, 29 | var.tags, 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /modules/iam-role/resource-group.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | resource_group_name = (var.resource_group.name != "" 3 | ? var.resource_group.name 4 | : join(".", [ 5 | local.metadata.package, 6 | local.metadata.module, 7 | replace(local.metadata.name, "/[^a-zA-Z0-9_\\.-]/", "-"), 8 | ]) 9 | ) 10 | } 11 | 12 | 13 | module "resource_group" { 14 | source = "tedilabs/misc/aws//modules/resource-group" 15 | version = "~> 0.12.0" 16 | 17 | count = (var.resource_group.enabled && var.module_tags_enabled) ? 1 : 0 18 | 19 | name = local.resource_group_name 20 | description = var.resource_group.description 21 | 22 | query = { 23 | resource_tags = local.module_tags 24 | } 25 | 26 | module_tags_enabled = false 27 | tags = merge( 28 | local.module_tags, 29 | var.tags, 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /modules/iam-user/resource-group.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | resource_group_name = (var.resource_group.name != "" 3 | ? var.resource_group.name 4 | : join(".", [ 5 | local.metadata.package, 6 | local.metadata.module, 7 | replace(local.metadata.name, "/[^a-zA-Z0-9_\\.-]/", "-"), 8 | ]) 9 | ) 10 | } 11 | 12 | 13 | module "resource_group" { 14 | source = "tedilabs/misc/aws//modules/resource-group" 15 | version = "~> 0.12.0" 16 | 17 | count = (var.resource_group.enabled && var.module_tags_enabled) ? 1 : 0 18 | 19 | name = local.resource_group_name 20 | description = var.resource_group.description 21 | 22 | query = { 23 | resource_tags = local.module_tags 24 | } 25 | 26 | module_tags_enabled = false 27 | tags = merge( 28 | local.module_tags, 29 | var.tags, 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /modules/iam-group/main.tf: -------------------------------------------------------------------------------- 1 | ################################################### 2 | # IAM Group 3 | ################################################### 4 | 5 | resource "aws_iam_group" "this" { 6 | name = var.name 7 | path = var.path 8 | } 9 | 10 | 11 | ################################################### 12 | # IAM Policy for AssumeRole 13 | ################################################### 14 | 15 | data "aws_iam_policy_document" "assume_role" { 16 | statement { 17 | effect = "Allow" 18 | actions = ["sts:AssumeRole"] 19 | resources = var.assumable_roles 20 | } 21 | } 22 | 23 | resource "aws_iam_group_policy" "assume_role" { 24 | count = length(var.assumable_roles) > 0 ? 1 : 0 25 | 26 | group = aws_iam_group.this.id 27 | name = "assume-role" 28 | policy = data.aws_iam_policy_document.assume_role.json 29 | } -------------------------------------------------------------------------------- /modules/iam-predefined-policies/resource-group.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | resource_group_name = (var.resource_group.name != "" 3 | ? var.resource_group.name 4 | : join(".", [ 5 | local.metadata.package, 6 | local.metadata.module, 7 | replace(local.metadata.name, "/[^a-zA-Z0-9_\\.-]/", "-"), 8 | ]) 9 | ) 10 | } 11 | 12 | 13 | module "resource_group" { 14 | source = "tedilabs/misc/aws//modules/resource-group" 15 | version = "~> 0.12.0" 16 | 17 | count = (var.resource_group.enabled && var.module_tags_enabled) ? 1 : 0 18 | 19 | name = local.resource_group_name 20 | description = var.resource_group.description 21 | 22 | query = { 23 | resource_tags = local.module_tags 24 | } 25 | 26 | module_tags_enabled = false 27 | tags = merge( 28 | local.module_tags, 29 | var.tags, 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /modules/iam-service-linked-role/resource-group.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | resource_group_name = (var.resource_group.name != "" 3 | ? var.resource_group.name 4 | : join(".", [ 5 | local.metadata.package, 6 | local.metadata.module, 7 | replace(local.metadata.name, "/[^a-zA-Z0-9_\\.-]/", "-"), 8 | ]) 9 | ) 10 | } 11 | 12 | 13 | module "resource_group" { 14 | source = "tedilabs/misc/aws//modules/resource-group" 15 | version = "~> 0.12.0" 16 | 17 | count = (var.resource_group.enabled && var.module_tags_enabled) ? 1 : 0 18 | 19 | name = local.resource_group_name 20 | description = var.resource_group.description 21 | 22 | query = { 23 | resource_tags = local.module_tags 24 | } 25 | 26 | module_tags_enabled = false 27 | tags = merge( 28 | local.module_tags, 29 | var.tags, 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /modules/region/ebs.tf: -------------------------------------------------------------------------------- 1 | ################################################### 2 | # Default Encryption for EBS 3 | ################################################### 4 | 5 | resource "aws_ebs_encryption_by_default" "this" { 6 | region = var.region 7 | 8 | enabled = var.ebs.default_encryption.enabled 9 | } 10 | 11 | resource "aws_ebs_default_kms_key" "this" { 12 | count = var.ebs.default_encryption.kms_key != null ? 1 : 0 13 | 14 | region = var.region 15 | 16 | key_arn = var.ebs.default_encryption.kms_key 17 | } 18 | 19 | 20 | ################################################### 21 | # Public Access Block for EBS Snapshots 22 | ################################################### 23 | 24 | resource "aws_ebs_snapshot_block_public_access" "this" { 25 | region = var.region 26 | 27 | state = var.ebs.snapshot_public_access_mode 28 | } 29 | -------------------------------------------------------------------------------- /modules/iam-oidc-identity-provider/resource-group.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | resource_group_name = (var.resource_group.name != "" 3 | ? var.resource_group.name 4 | : join(".", [ 5 | local.metadata.package, 6 | local.metadata.module, 7 | replace(local.metadata.name, "/[^a-zA-Z0-9_\\.-]/", "-"), 8 | ]) 9 | ) 10 | } 11 | 12 | 13 | module "resource_group" { 14 | source = "tedilabs/misc/aws//modules/resource-group" 15 | version = "~> 0.12.0" 16 | 17 | count = (var.resource_group.enabled && var.module_tags_enabled) ? 1 : 0 18 | 19 | name = local.resource_group_name 20 | description = var.resource_group.description 21 | 22 | query = { 23 | resource_tags = local.module_tags 24 | } 25 | 26 | module_tags_enabled = false 27 | tags = merge( 28 | local.module_tags, 29 | var.tags, 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /modules/iam-saml-identity-provider/resource-group.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | resource_group_name = (var.resource_group.name != "" 3 | ? var.resource_group.name 4 | : join(".", [ 5 | local.metadata.package, 6 | local.metadata.module, 7 | replace(local.metadata.name, "/[^a-zA-Z0-9_\\.-]/", "-"), 8 | ]) 9 | ) 10 | } 11 | 12 | 13 | module "resource_group" { 14 | source = "tedilabs/misc/aws//modules/resource-group" 15 | version = "~> 0.12.0" 16 | 17 | count = (var.resource_group.enabled && var.module_tags_enabled) ? 1 : 0 18 | 19 | name = local.resource_group_name 20 | description = var.resource_group.description 21 | 22 | query = { 23 | resource_tags = local.module_tags 24 | } 25 | 26 | module_tags_enabled = false 27 | tags = merge( 28 | local.module_tags, 29 | var.tags, 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /modules/region/resource-group.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | resource_group_name = (var.resource_group.name != "" 3 | ? var.resource_group.name 4 | : join(".", [ 5 | local.metadata.package, 6 | local.metadata.module, 7 | replace(local.metadata.name, "/[^a-zA-Z0-9_\\.-]/", "-"), 8 | ]) 9 | ) 10 | } 11 | 12 | 13 | module "resource_group" { 14 | source = "tedilabs/misc/aws//modules/resource-group" 15 | version = "~> 0.12.0" 16 | 17 | count = (var.resource_group.enabled && var.module_tags_enabled) ? 1 : 0 18 | 19 | region = var.region 20 | 21 | name = local.resource_group_name 22 | description = var.resource_group.description 23 | 24 | query = { 25 | resource_tags = local.module_tags 26 | } 27 | 28 | module_tags_enabled = false 29 | tags = merge( 30 | local.module_tags, 31 | var.tags, 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /modules/iam-predefined-policies/outputs.tf: -------------------------------------------------------------------------------- 1 | output "policies" { 2 | description = "A list of policies which are managed by this module." 3 | value = { 4 | for name, policy in module.iam_policy : 5 | name => { 6 | id = policy.id 7 | arn = policy.arn 8 | name = policy.name 9 | path = policy.path 10 | description = policy.description 11 | } 12 | } 13 | } 14 | 15 | output "resource_group" { 16 | description = "The resource group created to manage resources in this module." 17 | value = merge( 18 | { 19 | enabled = var.resource_group.enabled && var.module_tags_enabled 20 | }, 21 | (var.resource_group.enabled && var.module_tags_enabled 22 | ? { 23 | arn = module.resource_group[0].arn 24 | name = module.resource_group[0].name 25 | } 26 | : {} 27 | ) 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /examples/account-password-policy/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "us-east-1" 3 | } 4 | 5 | 6 | ################################################### 7 | # AWS Account 8 | ################################################### 9 | 10 | module "account" { 11 | source = "../../modules/account" 12 | # source = "tedilabs/account/aws//modules/account" 13 | # version = "~> 0.26.0" 14 | 15 | # This is alias for the AWS account. 16 | name = "example-210925" 17 | 18 | password_policy = { 19 | minimum_password_length = 8 20 | require_numbers = true 21 | require_symbols = true 22 | require_lowercase_characters = true 23 | require_uppercase_characters = false 24 | 25 | allow_users_to_change_password = true 26 | hard_expiry = false 27 | max_password_age = 90 28 | password_reuse_prevention = 0 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/iam-saml-identity-providers/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "us-east-1" 3 | } 4 | 5 | 6 | ################################################### 7 | # IAM SAML Identity Providers 8 | ################################################### 9 | 10 | locals { 11 | providers = [ 12 | { 13 | name = "test" 14 | metadata_document = file("${path.module}/saml-idp-metadata.xml") 15 | }, 16 | ] 17 | } 18 | 19 | module "saml_provider" { 20 | source = "../../modules/iam-saml-identity-provider" 21 | # source = "tedilabs/account/aws//modules/iam-saml-identity-provider" 22 | # version = "~> 0.23.0" 23 | 24 | for_each = { 25 | for provider in try(local.providers, []) : 26 | provider.name => provider 27 | } 28 | 29 | name = each.key 30 | metadata_document = each.value.metadata_document 31 | 32 | tags = { 33 | "project" = "terraform-aws-account-examples" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.github/workflows/github.repo.yaml: -------------------------------------------------------------------------------- 1 | name: GitHub - Repository 2 | 3 | 4 | on: 5 | push: 6 | branches: 7 | - main 8 | paths: 9 | - .github/labels*.yaml 10 | - .github/workflows/github.repo.yaml 11 | 12 | pull_request: 13 | paths: 14 | - .github/labels*.yaml 15 | - .github/workflows/github.repo.yaml 16 | 17 | workflow_dispatch: {} 18 | 19 | 20 | jobs: 21 | sync-labels: 22 | name: Sync Labels 23 | strategy: 24 | matrix: 25 | config_file: 26 | - .github/labels.yaml 27 | - .github/labels.common.yaml 28 | uses: tedilabs/github-actions/.github/workflows/github.repo.sync-labels.yaml@main 29 | 30 | permissions: 31 | contents: read 32 | issues: write 33 | 34 | with: 35 | config_file: ${{ matrix.config_file }} 36 | skip_delete: true 37 | dry_run: ${{ github.event_name == 'pull_request' }} 38 | secrets: 39 | token: ${{ secrets.GITHUB_TOKEN }} 40 | -------------------------------------------------------------------------------- /.github/auto-assign.yaml: -------------------------------------------------------------------------------- 1 | runOnDraft: false 2 | # A list of keywords to be skipped the process that add reviewers if pull requests include it 3 | skipKeywords: 4 | - wip 5 | 6 | # Set to true to add reviewers to pull requests 7 | addReviewers: true 8 | # A number of reviewers added to the pull request 9 | # Set 0 to add all the reviewers (default: 0) 10 | numberOfReviewers: 1 11 | useReviewGroups: true 12 | reviewGroups: 13 | owners: 14 | - posquit0 15 | maintainers: 16 | - posquit0 17 | 18 | # true: Add assignees to pull requests 19 | # 'author': Set the PR creator as the assignee. 20 | addAssignees: 'author' 21 | # A number of assignees to add to the pull request 22 | # Set to 0 to add all of the assignees. 23 | # Uses numberOfReviewers if unset. 24 | # numberOfAssignees: 2 25 | useAssigneeGroups: false 26 | # assigneeGroups: 27 | # groupA: 28 | # - assigneeA 29 | # - assigneeB 30 | # - assigneeC 31 | # groupB: 32 | # - assigneeD 33 | # - assigneeE 34 | # - assigneeF 35 | -------------------------------------------------------------------------------- /modules/region/cloudwatch.tf: -------------------------------------------------------------------------------- 1 | ################################################### 2 | # CloudWatch OAM (Observability Access Manager) 3 | ################################################### 4 | 5 | # TODO: Move to other module 6 | module "cloudwatch_oam_sink" { 7 | count = var.cloudwatch.oam_sink != null ? 1 : 0 8 | 9 | source = "tedilabs/observability/aws//modules/cloudwatch-oam-sink" 10 | version = "~> 0.2.0" 11 | 12 | name = var.cloudwatch.oam_sink.name 13 | telemetry_types = var.cloudwatch.oam_sink.telemetry_types 14 | 15 | allowed_source_accounts = var.cloudwatch.oam_sink.allowed_source_accounts 16 | allowed_source_organizations = var.cloudwatch.oam_sink.allowed_source_organizations 17 | allowed_source_organization_paths = var.cloudwatch.oam_sink.allowed_source_organization_paths 18 | 19 | resource_group_enabled = false 20 | module_tags_enabled = false 21 | 22 | tags = merge( 23 | local.module_tags, 24 | var.tags, 25 | var.cloudwatch.oam_sink.tags, 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /modules/region/vpc.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | block_public_access_mode = { 3 | "BIDIRECTIONAL" = "block-bidirectional" 4 | "INGRESS" = "block-ingress" 5 | "OFF" = "off" 6 | } 7 | } 8 | 9 | 10 | ################################################### 11 | # Availability Zone Group for VPC 12 | ################################################### 13 | 14 | resource "aws_ec2_availability_zone_group" "this" { 15 | for_each = var.vpc.availability_zone_groups 16 | 17 | region = var.region 18 | 19 | group_name = each.key 20 | opt_in_status = each.value ? "opted-in" : "not-opted-in" 21 | } 22 | 23 | 24 | ################################################### 25 | # Block Public Access for VPC 26 | ################################################### 27 | 28 | resource "aws_vpc_block_public_access_options" "this" { 29 | count = var.vpc.block_public_access_mode != "OFF" ? 1 : 0 30 | 31 | region = var.region 32 | 33 | internet_gateway_block_mode = local.block_public_access_mode[var.vpc.block_public_access_mode] 34 | } 35 | -------------------------------------------------------------------------------- /examples/iam-oidc-identity-providers/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "us-east-1" 3 | } 4 | 5 | 6 | ################################################### 7 | # IAM OIDC Identity Providers 8 | ################################################### 9 | 10 | locals { 11 | providers = [ 12 | { 13 | url = "https://token.actions.githubusercontent.com" 14 | audiences = ["sts.amazonaws.com"] 15 | }, 16 | ] 17 | } 18 | 19 | module "oidc_provider" { 20 | source = "../../modules/iam-oidc-identity-provider" 21 | # source = "tedilabs/account/aws//modules/iam-oidc-identity-provider" 22 | # version = "~> 0.23.0" 23 | 24 | for_each = { 25 | for provider in try(local.providers, []) : 26 | provider.url => provider 27 | } 28 | 29 | url = each.key 30 | audiences = try(each.value.audiences, null) 31 | 32 | thumbprints = try(each.value.thumbprints, null) 33 | auto_thumbprint_enabled = try(each.value.auto_thumbprint_enabled, true) 34 | 35 | tags = { 36 | "project" = "terraform-aws-account-examples" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /modules/iam-predefined-policies/policies/self-service-password.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "AllowViewAccountInfo", 6 | "Effect": "Allow", 7 | "Action": [ 8 | "iam:GetAccountPasswordPolicy", 9 | "iam:GetAccountSummary" 10 | ], 11 | "Resource": "*" 12 | }, 13 | { 14 | "Sid": "AllowManageOwnPassword", 15 | "Effect": "Allow", 16 | "Action": [ 17 | "iam:GetUser", 18 | "iam:ChangePassword" 19 | ], 20 | "Resource": [ 21 | "arn:aws:iam::*:user/${aws:username}", 22 | "arn:aws:iam::*:user/*/${aws:username}" 23 | ] 24 | }, 25 | { 26 | "Sid": "AllowManageOwnLoginProfile", 27 | "Effect": "Allow", 28 | "Action": [ 29 | "iam:CreateLoginProfile", 30 | "iam:GetLoginProfile", 31 | "iam:UpdateLoginProfile" 32 | ], 33 | "Resource": [ 34 | "arn:aws:iam::*:user/${aws:username}", 35 | "arn:aws:iam::*:user/*/${aws:username}" 36 | ] 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /modules/iam-saml-identity-provider/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | metadata = { 3 | package = "terraform-aws-account" 4 | version = trimspace(file("${path.module}/../../VERSION")) 5 | module = basename(path.module) 6 | name = var.name 7 | } 8 | module_tags = var.module_tags_enabled ? { 9 | "module.terraform.io/package" = local.metadata.package 10 | "module.terraform.io/version" = local.metadata.version 11 | "module.terraform.io/name" = local.metadata.module 12 | "module.terraform.io/full-name" = "${local.metadata.package}/${local.metadata.module}" 13 | "module.terraform.io/instance" = local.metadata.name 14 | } : {} 15 | } 16 | 17 | 18 | ################################################### 19 | # SAML Identity Provider 20 | ################################################### 21 | 22 | resource "aws_iam_saml_provider" "this" { 23 | name = var.name 24 | saml_metadata_document = var.metadata_document 25 | 26 | tags = merge( 27 | { 28 | "Name" = local.metadata.name 29 | }, 30 | local.module_tags, 31 | var.tags, 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /modules/iam-policy/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | metadata = { 3 | package = "terraform-aws-account" 4 | version = trimspace(file("${path.module}/../../VERSION")) 5 | module = basename(path.module) 6 | name = var.name 7 | } 8 | module_tags = var.module_tags_enabled ? { 9 | "module.terraform.io/package" = local.metadata.package 10 | "module.terraform.io/version" = local.metadata.version 11 | "module.terraform.io/name" = local.metadata.module 12 | "module.terraform.io/full-name" = "${local.metadata.package}/${local.metadata.module}" 13 | "module.terraform.io/instance" = local.metadata.name 14 | } : {} 15 | } 16 | 17 | 18 | ################################################### 19 | # IAM Policy 20 | ################################################### 21 | 22 | resource "aws_iam_policy" "this" { 23 | name = local.metadata.name 24 | path = var.path 25 | description = var.description 26 | 27 | policy = var.policy 28 | 29 | tags = merge( 30 | { 31 | "Name" = local.metadata.name 32 | }, 33 | local.module_tags, 34 | var.tags, 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /modules/iam-saml-identity-provider/outputs.tf: -------------------------------------------------------------------------------- 1 | output "id" { 2 | description = "The ID of this provider." 3 | value = aws_iam_saml_provider.this.id 4 | } 5 | 6 | output "arn" { 7 | description = "The ARN assigned by AWS for this provider." 8 | value = aws_iam_saml_provider.this.arn 9 | } 10 | 11 | output "name" { 12 | description = "The name of the identity provider." 13 | value = aws_iam_saml_provider.this.name 14 | } 15 | 16 | output "expire_at" { 17 | description = "The expiration date and time for the SAML provider in RFC1123 format, e.g., `Mon, 02 Jan 2006 15:04:05 MST`." 18 | value = aws_iam_saml_provider.this.valid_until 19 | } 20 | 21 | output "resource_group" { 22 | description = "The resource group created to manage resources in this module." 23 | value = merge( 24 | { 25 | enabled = var.resource_group.enabled && var.module_tags_enabled 26 | }, 27 | (var.resource_group.enabled && var.module_tags_enabled 28 | ? { 29 | arn = module.resource_group[0].arn 30 | name = module.resource_group[0].name 31 | } 32 | : {} 33 | ) 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /modules/iam-policy/outputs.tf: -------------------------------------------------------------------------------- 1 | output "id" { 2 | description = "The ID of this IAM policy." 3 | value = aws_iam_policy.this.policy_id 4 | } 5 | 6 | output "arn" { 7 | description = "The ARN assigned by AWS for this IAM policy." 8 | value = aws_iam_policy.this.arn 9 | } 10 | 11 | output "name" { 12 | description = "The URL of the IAM policy." 13 | value = aws_iam_policy.this.name 14 | } 15 | 16 | output "path" { 17 | description = "The path of the IAM policy." 18 | value = aws_iam_policy.this.path 19 | } 20 | 21 | output "description" { 22 | description = "The description of the IAM policy." 23 | value = aws_iam_policy.this.description 24 | } 25 | 26 | output "resource_group" { 27 | description = "The resource group created to manage resources in this module." 28 | value = merge( 29 | { 30 | enabled = var.resource_group.enabled && var.module_tags_enabled 31 | }, 32 | (var.resource_group.enabled && var.module_tags_enabled 33 | ? { 34 | arn = module.resource_group[0].arn 35 | name = module.resource_group[0].name 36 | } 37 | : {} 38 | ) 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /modules/account/cost-optimization-hub.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | savings_estimation_mode = { 3 | AFTER_DISCOUNTS = "AfterDiscounts" 4 | BEFORE_DISCOUNTS = "BeforeDiscounts" 5 | } 6 | } 7 | 8 | 9 | ################################################### 10 | # Configurations for Cost Optimization Hub 11 | ################################################### 12 | 13 | resource "aws_costoptimizationhub_enrollment_status" "this" { 14 | count = var.cost_optimization_hub.enabled ? 1 : 0 15 | 16 | include_member_accounts = var.cost_optimization_hub.scope == "ORGANIZATION" 17 | 18 | lifecycle { 19 | ignore_changes = [ 20 | include_member_accounts, 21 | ] 22 | } 23 | } 24 | 25 | resource "aws_costoptimizationhub_preferences" "this" { 26 | count = length(aws_costoptimizationhub_enrollment_status.this) > 0 ? 1 : 0 27 | 28 | member_account_discount_visibility = (var.cost_optimization_hub.scope == "ORGANIZATION" 29 | ? (var.cost_optimization_hub.allow_member_account_discount_visibility ? "All" : "None") 30 | : null 31 | ) 32 | savings_estimation_mode = local.savings_estimation_mode[var.cost_optimization_hub.savings_estimation_mode] 33 | } 34 | -------------------------------------------------------------------------------- /.github/labeler.yaml: -------------------------------------------------------------------------------- 1 | # Modules 2 | ":floppy_disk: account": 3 | - changed-files: 4 | - any-glob-to-any-file: 5 | - modules/account/**/* 6 | 7 | ":floppy_disk: iam-group": 8 | - changed-files: 9 | - any-glob-to-any-file: 10 | - modules/iam-group/**/* 11 | 12 | ":floppy_disk: iam-oidc-identity-provider": 13 | - changed-files: 14 | - any-glob-to-any-file: 15 | - modules/iam-oidc-identity-provider/**/* 16 | 17 | ":floppy_disk: iam-policy": 18 | - changed-files: 19 | - any-glob-to-any-file: 20 | - modules/iam-policy/**/* 21 | 22 | ":floppy_disk: iam-predefined-policies": 23 | - changed-files: 24 | - any-glob-to-any-file: 25 | - modules/iam-predefined-policies/**/* 26 | 27 | ":floppy_disk: iam-role": 28 | - changed-files: 29 | - any-glob-to-any-file: 30 | - modules/iam-role/**/* 31 | 32 | ":floppy_disk: iam-saml-identity-provider": 33 | - changed-files: 34 | - any-glob-to-any-file: 35 | - modules/iam-saml-identity-provider/**/* 36 | 37 | ":floppy_disk: iam-user": 38 | - changed-files: 39 | - any-glob-to-any-file: 40 | - modules/iam-user/**/* 41 | 42 | ":floppy_disk: region": 43 | - changed-files: 44 | - any-glob-to-any-file: 45 | - modules/region/**/* 46 | -------------------------------------------------------------------------------- /modules/iam-service-linked-role/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | metadata = { 3 | package = "terraform-aws-account" 4 | version = trimspace(file("${path.module}/../../VERSION")) 5 | module = basename(path.module) 6 | name = trimsuffix(var.aws_service, ".amazonaws.com") 7 | } 8 | module_tags = var.module_tags_enabled ? { 9 | "module.terraform.io/package" = local.metadata.package 10 | "module.terraform.io/version" = local.metadata.version 11 | "module.terraform.io/name" = local.metadata.module 12 | "module.terraform.io/full-name" = "${local.metadata.package}/${local.metadata.module}" 13 | "module.terraform.io/instance" = local.metadata.name 14 | } : {} 15 | } 16 | 17 | 18 | ################################################### 19 | # Service Linked Role 20 | ################################################### 21 | 22 | resource "aws_iam_service_linked_role" "this" { 23 | aws_service_name = var.aws_service 24 | custom_suffix = var.custom_suffix != "" ? var.custom_suffix : null 25 | description = var.description 26 | 27 | tags = merge( 28 | { 29 | "Name" = local.metadata.name 30 | }, 31 | local.module_tags, 32 | var.tags, 33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /modules/iam-role/policies.tf: -------------------------------------------------------------------------------- 1 | ################################################### 2 | # Managed Policies of IAM Role 3 | ################################################### 4 | 5 | resource "aws_iam_role_policy_attachment" "managed" { 6 | for_each = var.exclusive_policy_management_enabled ? [] : toset(var.policies) 7 | 8 | role = aws_iam_role.this.id 9 | policy_arn = each.key 10 | } 11 | 12 | resource "aws_iam_role_policy_attachments_exclusive" "this" { 13 | count = var.exclusive_policy_management_enabled ? 1 : 0 14 | 15 | role_name = aws_iam_role.this.name 16 | policy_arns = var.policies 17 | } 18 | 19 | 20 | ################################################### 21 | # Inline Policies of IAM Role 22 | ################################################### 23 | 24 | resource "aws_iam_role_policy" "inline" { 25 | for_each = var.exclusive_inline_policy_management_enabled ? {} : var.inline_policies 26 | 27 | role = aws_iam_role.this.id 28 | name = each.key 29 | policy = each.value 30 | } 31 | 32 | resource "aws_iam_role_policies_exclusive" "this" { 33 | count = var.exclusive_inline_policy_management_enabled ? 1 : 0 34 | 35 | role_name = aws_iam_role.this.name 36 | policy_names = keys(var.inline_policies) 37 | } 38 | -------------------------------------------------------------------------------- /modules/iam-user/policies.tf: -------------------------------------------------------------------------------- 1 | ################################################### 2 | # Managed Policies of IAM User 3 | ################################################### 4 | 5 | resource "aws_iam_user_policy_attachment" "managed" { 6 | for_each = var.exclusive_policy_management_enabled ? [] : toset(var.policies) 7 | 8 | user = aws_iam_user.this.name 9 | policy_arn = each.key 10 | } 11 | 12 | resource "aws_iam_user_policy_attachments_exclusive" "this" { 13 | count = var.exclusive_policy_management_enabled ? 1 : 0 14 | 15 | user_name = aws_iam_user.this.name 16 | policy_arns = var.policies 17 | } 18 | 19 | 20 | ################################################### 21 | # Inline Policies of IAM User 22 | ################################################### 23 | 24 | resource "aws_iam_user_policy" "inline" { 25 | for_each = var.exclusive_inline_policy_management_enabled ? {} : var.inline_policies 26 | 27 | user = aws_iam_user.this.name 28 | name = each.key 29 | policy = each.value 30 | } 31 | 32 | resource "aws_iam_user_policies_exclusive" "this" { 33 | count = var.exclusive_inline_policy_management_enabled ? 1 : 0 34 | 35 | user_name = aws_iam_user.this.name 36 | policy_names = keys(var.inline_policies) 37 | } 38 | -------------------------------------------------------------------------------- /modules/account/main.tf: -------------------------------------------------------------------------------- 1 | data "aws_caller_identity" "this" {} 2 | 3 | 4 | ################################################### 5 | # AWS Account Alias 6 | ################################################### 7 | 8 | resource "aws_iam_account_alias" "this" { 9 | account_alias = var.name 10 | } 11 | 12 | 13 | ################################################### 14 | # Password Policy for AWS Account and IAM Users 15 | ################################################### 16 | 17 | resource "aws_iam_account_password_policy" "this" { 18 | minimum_password_length = var.password_policy.minimum_password_length 19 | require_numbers = var.password_policy.require_numbers 20 | require_symbols = var.password_policy.require_symbols 21 | require_lowercase_characters = var.password_policy.require_lowercase_characters 22 | require_uppercase_characters = var.password_policy.require_uppercase_characters 23 | allow_users_to_change_password = var.password_policy.allow_users_to_change_password 24 | hard_expiry = var.password_policy.hard_expiry 25 | max_password_age = var.password_policy.max_password_age 26 | password_reuse_prevention = var.password_policy.password_reuse_prevention 27 | } 28 | -------------------------------------------------------------------------------- /modules/iam-group/policies.tf: -------------------------------------------------------------------------------- 1 | ################################################### 2 | # Managed Policies of IAM Group 3 | ################################################### 4 | 5 | resource "aws_iam_group_policy_attachment" "managed" { 6 | for_each = var.exclusive_policy_management_enabled ? [] : toset(var.policies) 7 | 8 | group = aws_iam_group.this.id 9 | policy_arn = each.key 10 | } 11 | 12 | resource "aws_iam_group_policy_attachments_exclusive" "this" { 13 | count = var.exclusive_policy_management_enabled ? 1 : 0 14 | 15 | group_name = aws_iam_group.this.name 16 | policy_arns = var.policies 17 | } 18 | 19 | 20 | ################################################### 21 | # Inline Policies of IAM Group 22 | ################################################### 23 | 24 | resource "aws_iam_group_policy" "inline" { 25 | for_each = var.exclusive_inline_policy_management_enabled ? {} : var.inline_policies 26 | 27 | group = aws_iam_group.this.id 28 | name = each.key 29 | policy = each.value 30 | } 31 | 32 | resource "aws_iam_group_policies_exclusive" "this" { 33 | count = var.exclusive_inline_policy_management_enabled ? 1 : 0 34 | 35 | group_name = aws_iam_group.this.name 36 | policy_names = keys(var.inline_policies) 37 | } 38 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | default_install_hook_types: 2 | - pre-commit 3 | - commit-msg 4 | 5 | repos: 6 | - repo: https://github.com/antonbabenko/pre-commit-terraform 7 | rev: v1.104.0 8 | hooks: 9 | - id: terraform_fmt 10 | name: (terraform) Format .tf files with `terraform fmt` 11 | args: 12 | - --args=-diff 13 | - id: terraform_validate 14 | name: (terraform) Check with `terraform validate` 15 | args: 16 | - --hook-config=--retry-once-with-cleanup=true 17 | - --tf-init-args=-upgrade 18 | - id: terraform_tflint 19 | name: (terraform) Check with `tflint` 20 | args: 21 | - --args=--config=__GIT_WORKING_DIR__/.tflint.hcl 22 | files: ^modules/ 23 | - id: terraform_docs 24 | name: (terraform) Generate docs with `terraform-docs` 25 | args: ["--args=--sort-by required"] 26 | 27 | - repo: https://github.com/pre-commit/pre-commit-hooks 28 | rev: v6.0.0 29 | hooks: 30 | - id: trailing-whitespace 31 | 32 | - repo: https://github.com/adrienverge/yamllint 33 | rev: v1.37.1 34 | hooks: 35 | - id: yamllint 36 | name: (yaml) Check with `yamllint` 37 | 38 | - repo: https://github.com/compilerla/conventional-pre-commit 39 | rev: v4.3.0 40 | hooks: 41 | - id: conventional-pre-commit 42 | name: (commit-message) Check conventional commit 43 | stages: [commit-msg] 44 | args: [] 45 | -------------------------------------------------------------------------------- /.yamllint.yaml: -------------------------------------------------------------------------------- 1 | yaml-files: 2 | - '*.yaml' 3 | - '*.yml' 4 | 5 | rules: 6 | braces: 7 | min-spaces-inside: 0 8 | max-spaces-inside: 1 9 | min-spaces-inside-empty: 0 10 | max-spaces-inside-empty: 0 11 | brackets: 12 | min-spaces-inside: 0 13 | max-spaces-inside: 1 14 | min-spaces-inside-empty: 0 15 | max-spaces-inside-empty: 0 16 | colons: 17 | max-spaces-before: 0 18 | max-spaces-after: 1 19 | commas: 20 | max-spaces-before: 0 21 | comments: 22 | level: warning 23 | require-starting-space: true 24 | min-spaces-from-content: 1 25 | comments-indentation: disable 26 | document-end: disable 27 | document-start: disable 28 | empty-lines: 29 | level: warning 30 | max: 2 31 | max-start: 0 32 | max-end: 1 33 | empty-values: 34 | forbid-in-block-mappings: true 35 | forbid-in-flow-mappings: true 36 | hyphens: 37 | max-spaces-after: 1 38 | indentation: 39 | spaces: consistent 40 | indent-sequences: false 41 | key-duplicates: enable 42 | key-ordering: disable 43 | line-length: disable 44 | new-line-at-end-of-file: enable 45 | # Use UNIX new line characters `\n` instead of DOS new line characters `\r\n` 46 | new-lines: 47 | type: unix 48 | octal-values: disable 49 | quoted-strings: 50 | quote-type: any 51 | required: false 52 | trailing-spaces: enable 53 | truthy: disable 54 | -------------------------------------------------------------------------------- /modules/iam-predefined-policies/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | metadata = { 3 | package = "terraform-aws-account" 4 | version = trimspace(file("${path.module}/../../VERSION")) 5 | module = basename(path.module) 6 | name = basename(path.module) 7 | } 8 | module_tags = var.module_tags_enabled ? { 9 | "module.terraform.io/package" = local.metadata.package 10 | "module.terraform.io/version" = local.metadata.version 11 | "module.terraform.io/name" = local.metadata.module 12 | "module.terraform.io/full-name" = "${local.metadata.package}/${local.metadata.module}" 13 | "module.terraform.io/instance" = local.metadata.name 14 | } : {} 15 | } 16 | 17 | 18 | ################################################### 19 | # IAM Predefined Policies 20 | ################################################### 21 | 22 | module "iam_policy" { 23 | source = "../iam-policy" 24 | 25 | for_each = { 26 | for item in var.enabled_policies : 27 | item.policy => item 28 | } 29 | 30 | name = coalesce(each.value.name, each.key) 31 | path = each.value.path 32 | description = each.value.description 33 | 34 | policy = file("${path.module}/policies/${each.key}.json") 35 | 36 | resource_group = { 37 | enabled = false 38 | } 39 | 40 | tags = merge( 41 | { 42 | "Name" = coalesce(each.value.name, each.key) 43 | }, 44 | local.module_tags, 45 | var.tags, 46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /modules/iam-oidc-identity-provider/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | metadata = { 3 | package = "terraform-aws-account" 4 | version = trimspace(file("${path.module}/../../VERSION")) 5 | module = basename(path.module) 6 | name = trimprefix(var.url, "https://") 7 | } 8 | module_tags = var.module_tags_enabled ? { 9 | "module.terraform.io/package" = local.metadata.package 10 | "module.terraform.io/version" = local.metadata.version 11 | "module.terraform.io/name" = local.metadata.module 12 | "module.terraform.io/full-name" = "${local.metadata.package}/${local.metadata.module}" 13 | "module.terraform.io/instance" = local.metadata.name 14 | } : {} 15 | } 16 | 17 | 18 | ################################################### 19 | # OIDC Identity Provider 20 | ################################################### 21 | 22 | data "tls_certificate" "this" { 23 | count = var.auto_thumbprint_enabled ? 1 : 0 24 | 25 | url = var.url 26 | } 27 | 28 | resource "aws_iam_openid_connect_provider" "this" { 29 | url = var.url 30 | 31 | client_id_list = var.audiences 32 | thumbprint_list = setunion( 33 | var.thumbprints, 34 | (var.auto_thumbprint_enabled 35 | ? data.tls_certificate.this[*].certificates[0].sha1_fingerprint 36 | : [] 37 | ) 38 | ) 39 | 40 | tags = merge( 41 | { 42 | "Name" = local.metadata.name 43 | }, 44 | local.module_tags, 45 | var.tags, 46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /examples/account-contacts/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "us-east-1" 3 | } 4 | 5 | 6 | ################################################### 7 | # AWS Account 8 | ################################################### 9 | 10 | module "account" { 11 | source = "../../modules/account" 12 | # source = "tedilabs/account/aws//modules/account" 13 | # version = "~> 0.26.0" 14 | 15 | # This is alias for the AWS account. 16 | name = "example-210925" 17 | 18 | primary_contact = { 19 | name = "John Doe" 20 | company_name = "Example Inc." 21 | country_code = "US" 22 | state = "California" 23 | city = "Los Angeles" 24 | district = "Downtown" 25 | address_line_1 = "123 Main St." 26 | address_line_2 = "Suite 456" 27 | address_line_3 = "Floor 7" 28 | postal_code = "90012" 29 | phone = "+1-555-555-5555" 30 | website_url = "https://example.com" 31 | } 32 | 33 | billing_contact = { 34 | name = "John Doe" 35 | title = "Billing Manager" 36 | email = "john.doe@example.com" 37 | phone = "+1-555-555-5555" 38 | } 39 | operation_contact = { 40 | name = "Peter Smith" 41 | title = "Operation Manager" 42 | email = "peter.smith@example.com" 43 | phone = "+1-333-333-3333" 44 | } 45 | security_contact = { 46 | name = "Bob Smith" 47 | title = "CISO" 48 | email = "bob.smith@example.com" 49 | phone = "+1-222-222-2222" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /modules/region/resource-explorer.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | resource_explorer_views = (var.resource_explorer.enabled 3 | ? var.resource_explorer.views 4 | : [] 5 | ) 6 | } 7 | 8 | 9 | ################################################### 10 | # Resource Explorer 11 | ################################################### 12 | 13 | resource "aws_resourceexplorer2_index" "this" { 14 | count = var.resource_explorer.enabled ? 1 : 0 15 | 16 | region = var.region 17 | 18 | type = var.resource_explorer.index_type 19 | 20 | tags = merge( 21 | { 22 | "Name" = local.metadata.name 23 | }, 24 | local.module_tags, 25 | var.tags, 26 | ) 27 | } 28 | 29 | resource "aws_resourceexplorer2_view" "this" { 30 | for_each = { 31 | for view in local.resource_explorer_views : 32 | view.name => view 33 | } 34 | 35 | region = aws_resourceexplorer2_index.this[0].region 36 | 37 | name = each.key 38 | default_view = each.value.is_default 39 | scope = each.value.scope 40 | 41 | dynamic "filters" { 42 | for_each = each.value.filter_queries 43 | iterator = query 44 | 45 | content { 46 | filter_string = query.value 47 | } 48 | } 49 | 50 | dynamic "included_property" { 51 | for_each = each.value.additional_resource_attributes 52 | iterator = attribute 53 | 54 | content { 55 | name = attribute.value 56 | } 57 | } 58 | 59 | tags = merge( 60 | { 61 | "Name" = each.key 62 | }, 63 | local.module_tags, 64 | var.tags, 65 | ) 66 | } 67 | -------------------------------------------------------------------------------- /modules/iam-oidc-identity-provider/outputs.tf: -------------------------------------------------------------------------------- 1 | output "id" { 2 | description = "The ID of this provider." 3 | value = aws_iam_openid_connect_provider.this.id 4 | } 5 | 6 | output "arn" { 7 | description = "The ARN assigned by AWS for this provider." 8 | value = aws_iam_openid_connect_provider.this.arn 9 | } 10 | 11 | output "url" { 12 | description = "The URL of the identity provider." 13 | value = var.url 14 | } 15 | 16 | output "urn" { 17 | description = "The URN of the identity provider." 18 | value = trimprefix(trimprefix(aws_iam_openid_connect_provider.this.url, "http://"), "https://") 19 | } 20 | 21 | output "audiences" { 22 | description = "A list of audiences (also known as client IDs) for the IAM OIDC provider." 23 | value = aws_iam_openid_connect_provider.this.client_id_list 24 | } 25 | 26 | output "thumbprints" { 27 | description = "A list of server certificate thumbprints for the OpenID Connect (OIDC) identity provider's server certificate(s)." 28 | value = aws_iam_openid_connect_provider.this.thumbprint_list 29 | } 30 | 31 | output "resource_group" { 32 | description = "The resource group created to manage resources in this module." 33 | value = merge( 34 | { 35 | enabled = var.resource_group.enabled && var.module_tags_enabled 36 | }, 37 | (var.resource_group.enabled && var.module_tags_enabled 38 | ? { 39 | arn = module.resource_group[0].arn 40 | name = module.resource_group[0].name 41 | } 42 | : {} 43 | ) 44 | ) 45 | } 46 | -------------------------------------------------------------------------------- /.github/workflows/github.pr.yaml: -------------------------------------------------------------------------------- 1 | name: GitHub - Pull Request 2 | 3 | 4 | on: 5 | pull_request_target: {} 6 | 7 | 8 | jobs: 9 | auto-assign: 10 | name: Auto Assign 11 | uses: tedilabs/github-actions/.github/workflows/github.pr.auto-assign.yaml@main 12 | if: ${{ github.event_name == 'pull_request_target' }} 13 | 14 | permissions: 15 | contents: read 16 | pull-requests: write 17 | 18 | with: 19 | config_file: .github/auto-assign.yaml 20 | secrets: 21 | token: ${{ secrets.GITHUB_TOKEN }} 22 | 23 | auto-label: 24 | name: Auto Label 25 | uses: tedilabs/github-actions/.github/workflows/github.pr.auto-label.yaml@main 26 | if: ${{ github.event_name == 'pull_request_target' }} 27 | 28 | permissions: 29 | contents: read 30 | issues: write 31 | pull-requests: write 32 | 33 | with: 34 | labeler_config_file: .github/labeler.yaml 35 | labeler_dot_included: true 36 | labeler_sync_labels: true 37 | pr_size_xs_label: 'size/XS' 38 | pr_size_s_label: 'size/S' 39 | pr_size_m_label: 'size/M' 40 | pr_size_l_label: 'size/L' 41 | pr_size_xl_label: 'size/XL' 42 | secrets: 43 | token: ${{ secrets.GITHUB_TOKEN }} 44 | 45 | welcome: 46 | name: Welcome for First Pull Request 47 | uses: tedilabs/github-actions/.github/workflows/github.issue.welcome.yaml@main 48 | if: ${{ github.event_name == 'pull_request_target' && github.event.action == 'opened' }} 49 | 50 | secrets: 51 | token: ${{ secrets.GITHUB_TOKEN }} 52 | -------------------------------------------------------------------------------- /modules/iam-service-linked-role/outputs.tf: -------------------------------------------------------------------------------- 1 | output "aws_service" { 2 | description = "The AWS service principal to which this role is attached." 3 | value = aws_iam_service_linked_role.this.aws_service_name 4 | } 5 | 6 | output "name" { 7 | description = "IAM Role name." 8 | value = aws_iam_service_linked_role.this.name 9 | } 10 | 11 | output "arn" { 12 | description = "The ARN assigned by AWS for this role." 13 | value = aws_iam_service_linked_role.this.arn 14 | } 15 | 16 | output "unique_id" { 17 | description = "The unique ID assigned by AWS." 18 | value = aws_iam_service_linked_role.this.unique_id 19 | } 20 | 21 | output "path" { 22 | description = "The path of the role." 23 | value = aws_iam_service_linked_role.this.path 24 | } 25 | 26 | output "description" { 27 | description = "The description of the role." 28 | value = aws_iam_service_linked_role.this.description 29 | } 30 | 31 | output "created_at" { 32 | description = "The creation date of the IAM role." 33 | value = aws_iam_service_linked_role.this.create_date 34 | } 35 | 36 | output "resource_group" { 37 | description = "The resource group created to manage resources in this module." 38 | value = merge( 39 | { 40 | enabled = var.resource_group.enabled && var.module_tags_enabled 41 | }, 42 | (var.resource_group.enabled && var.module_tags_enabled 43 | ? { 44 | arn = module.resource_group[0].arn 45 | name = module.resource_group[0].name 46 | } 47 | : {} 48 | ) 49 | ) 50 | } 51 | -------------------------------------------------------------------------------- /modules/region/ec2.tf: -------------------------------------------------------------------------------- 1 | ################################################### 2 | # Public Access Block for EC2 AMI 3 | ################################################### 4 | 5 | resource "aws_ec2_image_block_public_access" "this" { 6 | region = var.region 7 | 8 | state = var.ec2.ami_public_access_mode 9 | } 10 | 11 | 12 | ################################################### 13 | # Instance Metadata Defaults for EC2 14 | ################################################### 15 | 16 | resource "aws_ec2_instance_metadata_defaults" "this" { 17 | count = anytrue([ 18 | for k, v in var.ec2.instance_metadata_defaults : 19 | v != null 20 | ]) ? 1 : 0 21 | 22 | region = var.region 23 | 24 | http_endpoint = (var.ec2.instance_metadata_defaults.http_enabled != null 25 | ? (var.ec2.instance_metadata_defaults.http_enabled ? "enabled" : "disabled") 26 | : "no-preference" 27 | ) 28 | http_tokens = (var.ec2.instance_metadata_defaults.http_token_required != null 29 | ? (var.ec2.instance_metadata_defaults.http_token_required ? "required" : "optional") 30 | : "no-preference" 31 | ) 32 | http_put_response_hop_limit = coalesce(var.ec2.instance_metadata_defaults.http_put_response_hop_limit, -1) 33 | 34 | instance_metadata_tags = (var.ec2.instance_metadata_defaults.instance_tags_enabled != null 35 | ? (var.ec2.instance_metadata_defaults.instance_tags_enabled ? "enabled" : "disabled") 36 | : "no-preference" 37 | ) 38 | } 39 | 40 | 41 | ################################################### 42 | # Serial Consol Access for EC2 43 | ################################################### 44 | 45 | resource "aws_ec2_serial_console_access" "this" { 46 | region = var.region 47 | 48 | enabled = var.ec2.serial_console_enabled 49 | } 50 | -------------------------------------------------------------------------------- /modules/iam-group/outputs.tf: -------------------------------------------------------------------------------- 1 | output "arn" { 2 | description = "The ARN of the IAM group." 3 | value = aws_iam_group.this.arn 4 | } 5 | 6 | output "id" { 7 | description = "The ID of the IAM group." 8 | value = aws_iam_group.this.id 9 | } 10 | 11 | output "unique_id" { 12 | description = "The unique ID assigned by AWS." 13 | value = aws_iam_group.this.unique_id 14 | } 15 | 16 | output "name" { 17 | description = "The IAM group name." 18 | value = aws_iam_group.this.name 19 | } 20 | 21 | output "path" { 22 | description = "The path of the IAM group." 23 | value = aws_iam_group.this.path 24 | } 25 | 26 | output "assumable_roles" { 27 | description = "A set of ARNs of IAM roles which members of IAM group can assume." 28 | value = toset(var.assumable_roles) 29 | } 30 | 31 | output "exclusive_policy_management_enabled" { 32 | description = "Whether exclusive policy management is enabled for the IAM group." 33 | value = var.exclusive_policy_management_enabled 34 | } 35 | 36 | output "exclusive_inline_policy_management_enabled" { 37 | description = "Whether exclusive inline policy management is enabled for the IAM group." 38 | value = var.exclusive_inline_policy_management_enabled 39 | } 40 | 41 | output "policies" { 42 | description = "A set of ARNs of IAM policies which are atached to IAM group." 43 | value = (var.exclusive_policy_management_enabled 44 | ? toset(aws_iam_group_policy_attachments_exclusive.this[0].policy_arns) 45 | : toset(values(aws_iam_group_policy_attachment.managed)[*].policy_arn) 46 | ) 47 | } 48 | 49 | output "inline_policies" { 50 | description = "A set of names of inline IAM polices which are attached to IAM group." 51 | value = toset(keys(var.inline_policies)) 52 | } 53 | -------------------------------------------------------------------------------- /.github/labels.yaml: -------------------------------------------------------------------------------- 1 | # Warning 2 | - color: "ee0701" 3 | description: "Categorize to note any change that requires extra attention." 4 | name: ":warning: breaking change" 5 | - color: "ee0701" 6 | description: "Categorize bug reports." 7 | name: ":warning: bug" 8 | - color: "ee0701" 9 | description: "Categorize vulnerability reports." 10 | name: ":warning: vulnerability" 11 | 12 | # Task Types 13 | - color: "1d76db" 14 | description: "Improvements or additions to documentation." 15 | name: ":pencil2: documentation" 16 | - color: "1d76db" 17 | description: "A new feature of the project." 18 | name: ":pencil2: feature" 19 | - color: "1d76db" 20 | description: "Any dependency updates, housekeeping, or maintenance work." 21 | name: ":pencil2: maintenance" 22 | 23 | # Highlight 24 | - color: "0e8a16" 25 | description: "Good for newcomers." 26 | name: ":fire: good first issue" 27 | - color: "0e8a16" 28 | description: "Extra attention is needed." 29 | name: ":fire: help wanted" 30 | 31 | # Cancel 32 | - color: "b60205" 33 | description: "This issue or pull request already exists." 34 | name: ":pray: duplicate" 35 | - color: "b60205" 36 | description: "This issue or pull request is invalid." 37 | name: ":pray: invalid" 38 | - color: "b60205" 39 | description: "This will not be worked on." 40 | name: ":pray: wontfix" 41 | 42 | # Size 43 | - color: "cfd3d7" 44 | description: "Extra Small size issue or PR." 45 | name: "size/XS" 46 | - color: "cfd3d7" 47 | description: "Small size issue or PR." 48 | name: "size/S" 49 | - color: "cfd3d7" 50 | description: "Medium size issue or PR." 51 | name: "size/M" 52 | - color: "cfd3d7" 53 | description: "Large size issue or PR." 54 | name: "size/L" 55 | - color: "cfd3d7" 56 | description: "Extra Large size issue or PR." 57 | name: "size/XL" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### OSX ### 2 | # General 3 | .DS_Store 4 | .AppleDouble 5 | .LSOverride 6 | 7 | # Icon must end with two \r 8 | Icon 9 | 10 | # Thumbnails 11 | ._* 12 | 13 | # Files that might appear in the root of a volume 14 | .DocumentRevisions-V100 15 | .fseventsd 16 | .Spotlight-V100 17 | .TemporaryItems 18 | .Trashes 19 | .VolumeIcon.icns 20 | .com.apple.timemachine.donotpresent 21 | 22 | # Directories potentially created on remote AFP share 23 | .AppleDB 24 | .AppleDesktop 25 | Network Trash Folder 26 | Temporary Items 27 | .apdisk 28 | 29 | 30 | ### Terraform ### 31 | # Lock file 32 | .terraform.lock.hcl 33 | 34 | # Local .terraform directories 35 | **/.terraform/* 36 | 37 | # .tfstate files 38 | *.tfstate 39 | *.tfstate.* 40 | 41 | # Crash log files 42 | crash.log 43 | 44 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 45 | # .tfvars files are managed as part of configuration and so should be included in 46 | # version control. 47 | # 48 | # example.tfvars 49 | 50 | # Ignore override files as they are usually used to override resources locally and so 51 | # are not checked in 52 | override.tf 53 | override.tf.json 54 | *_override.tf 55 | *_override.tf.json 56 | 57 | # Include override files you do wish to add to version control using negated pattern 58 | # !example_override.tf 59 | 60 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 61 | # example: *tfplan* 62 | 63 | 64 | ### Vim ### 65 | # Swap 66 | [._]*.s[a-v][a-z] 67 | !*.svg # comment out if you don't need vector files 68 | [._]*.sw[a-p] 69 | [._]s[a-rt-v][a-z] 70 | [._]ss[a-gi-z] 71 | [._]sw[a-p] 72 | 73 | # Session 74 | Session.vim 75 | Sessionx.vim 76 | 77 | # Temporary 78 | .netrwhist 79 | *~ 80 | # Auto-generated tag files 81 | tags 82 | # Persistent undo 83 | [._]*.un~ 84 | -------------------------------------------------------------------------------- /modules/account/support-app.tf: -------------------------------------------------------------------------------- 1 | ################################################### 2 | # Account Alias for Support App 3 | ################################################### 4 | 5 | resource "awscc_supportapp_account_alias" "this" { 6 | count = var.support_app.account_alias != null ? 1 : 0 7 | 8 | account_alias = var.support_app.account_alias 9 | } 10 | 11 | 12 | ################################################### 13 | # Slack Workspace Authorization for Support App 14 | ################################################### 15 | 16 | # INFO: Not supported attributes 17 | # - `version_id` 18 | resource "awscc_supportapp_slack_workspace_configuration" "this" { 19 | for_each = var.support_app.slack_workspaces 20 | 21 | team_id = each.value 22 | } 23 | 24 | 25 | ################################################### 26 | # Slack Workspace Authorization for Support App 27 | ################################################### 28 | 29 | resource "awscc_supportapp_slack_channel_configuration" "this" { 30 | for_each = { 31 | for configuration in var.support_app.slack_channel_configurations : 32 | configuration.name => configuration 33 | } 34 | 35 | channel_name = each.key 36 | team_id = awscc_supportapp_slack_workspace_configuration.this[each.value.workspace].team_id 37 | channel_id = each.value.channel 38 | 39 | 40 | ## Permissions 41 | # TODO: Use default role with `permission` variable 42 | channel_role_arn = each.value.channel_role 43 | 44 | 45 | ## Notification 46 | notify_on_case_severity = lower(each.value.notification_case_severity) 47 | notify_on_add_correspondence_to_case = each.value.notification_on_add_correspondence_to_case 48 | notify_on_create_or_reopen_case = each.value.notification_on_create_or_reopen_case 49 | notify_on_resolve_case = each.value.notification_on_resolve_case 50 | } 51 | -------------------------------------------------------------------------------- /modules/iam-user/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | metadata = { 3 | package = "terraform-aws-account" 4 | version = trimspace(file("${path.module}/../../VERSION")) 5 | module = basename(path.module) 6 | name = var.name 7 | } 8 | module_tags = var.module_tags_enabled ? { 9 | "module.terraform.io/package" = local.metadata.package 10 | "module.terraform.io/version" = local.metadata.version 11 | "module.terraform.io/name" = local.metadata.module 12 | "module.terraform.io/full-name" = "${local.metadata.package}/${local.metadata.module}" 13 | "module.terraform.io/instance" = local.metadata.name 14 | } : {} 15 | } 16 | 17 | 18 | ################################################### 19 | # IAM User 20 | ################################################### 21 | 22 | resource "aws_iam_user" "this" { 23 | name = var.name 24 | path = var.path 25 | force_destroy = var.force_destroy 26 | 27 | permissions_boundary = var.permissions_boundary 28 | 29 | tags = merge( 30 | { 31 | "Name" = local.metadata.name 32 | }, 33 | local.module_tags, 34 | var.tags, 35 | ) 36 | } 37 | 38 | resource "aws_iam_user_group_membership" "this" { 39 | user = aws_iam_user.this.name 40 | groups = var.groups 41 | } 42 | 43 | 44 | ################################################### 45 | # IAM Policy for AssumeRole 46 | ################################################### 47 | 48 | data "aws_iam_policy_document" "assume_role" { 49 | statement { 50 | effect = "Allow" 51 | actions = ["sts:AssumeRole"] 52 | resources = var.assumable_roles 53 | } 54 | } 55 | 56 | resource "aws_iam_user_policy" "assume_role" { 57 | count = length(var.assumable_roles) > 0 ? 1 : 0 58 | 59 | user = aws_iam_user.this.name 60 | name = "assume-role" 61 | policy = data.aws_iam_policy_document.assume_role.json 62 | } -------------------------------------------------------------------------------- /modules/iam-saml-identity-provider/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "(Required) The name of the identity provider." 3 | type = string 4 | nullable = false 5 | } 6 | 7 | variable "metadata_document" { 8 | description = "(Optional) A XML document generated by an identity provider that supports SAML 2.0." 9 | type = string 10 | nullable = false 11 | } 12 | 13 | variable "tags" { 14 | description = "(Optional) A map of tags to add to all resources." 15 | type = map(string) 16 | default = {} 17 | nullable = false 18 | } 19 | 20 | variable "module_tags_enabled" { 21 | description = "(Optional) Whether to create AWS Resource Tags for the module informations." 22 | type = bool 23 | default = true 24 | nullable = false 25 | } 26 | 27 | 28 | ################################################### 29 | # Resource Group 30 | ################################################### 31 | 32 | variable "resource_group" { 33 | description = < provider 27 | } 28 | 29 | url = each.key 30 | audiences = try(each.value.audiences, null) 31 | 32 | thumbprints = try(each.value.thumbprints, null) 33 | auto_thumbprint_enabled = try(each.value.auto_thumbprint_enabled, true) 34 | 35 | tags = { 36 | "project" = "terraform-aws-account-examples" 37 | } 38 | } 39 | 40 | 41 | ################################################### 42 | # IAM Roles 43 | ################################################### 44 | 45 | locals { 46 | roles = [ 47 | { 48 | name = "github-readonly" 49 | policies = ["arn:aws:iam::aws:policy/ReadOnlyAccess"] 50 | }, 51 | ] 52 | } 53 | 54 | module "role" { 55 | source = "../../modules/iam-role" 56 | # source = "tedilabs/account/aws//modules/iam-role" 57 | # version = "~> 0.23.0" 58 | 59 | for_each = { 60 | for role in try(local.roles, []) : 61 | role.name => role 62 | } 63 | 64 | name = each.key 65 | description = try(each.value.description, "Managed by Terraform.") 66 | path = try(each.value.path, "/") 67 | 68 | trusted_oidc_provider_policies = [ 69 | { 70 | url = "token.actions.githubusercontent.com" 71 | conditions = [ 72 | { 73 | key = "aud" 74 | condition = "StringEquals" 75 | values = ["sts.amazonaws.com"] 76 | }, 77 | { 78 | key = "sub" 79 | condition = "StringLike" 80 | values = ["repo:tedilabs/*"] 81 | }, 82 | ] 83 | } 84 | ] 85 | 86 | assumable_roles = try(each.value.assumable_roles, []) 87 | policies = try(each.value.policies, []) 88 | inline_policies = { 89 | for name, path in try(each.value.inline_policies, {}) : 90 | name => file(path) 91 | } 92 | 93 | tags = { 94 | "project" = "terraform-aws-account-examples" 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /modules/iam-user/credentials.tf: -------------------------------------------------------------------------------- 1 | ################################################### 2 | # Login Profile for IAM User 3 | ################################################### 4 | 5 | resource "aws_iam_user_login_profile" "this" { 6 | count = var.console_access.enabled ? 1 : 0 7 | 8 | user = aws_iam_user.this.name 9 | pgp_key = var.pgp_key 10 | 11 | password_length = var.console_access.password_length 12 | password_reset_required = var.console_access.password_reset_required 13 | 14 | lifecycle { 15 | ignore_changes = [ 16 | pgp_key, 17 | password_length, 18 | password_reset_required, 19 | ] 20 | } 21 | } 22 | 23 | 24 | ################################################### 25 | # Access Keys for IAM User 26 | ################################################### 27 | 28 | resource "aws_iam_access_key" "this" { 29 | count = length(var.access_keys) 30 | 31 | user = aws_iam_user.this.name 32 | pgp_key = var.pgp_key 33 | 34 | status = var.access_keys[count.index].enabled ? "Active" : "Inactive" 35 | 36 | lifecycle { 37 | ignore_changes = [ 38 | pgp_key, 39 | ] 40 | } 41 | } 42 | 43 | 44 | ################################################### 45 | # SSH Keys for IAM User 46 | ################################################### 47 | 48 | resource "aws_iam_user_ssh_key" "this" { 49 | for_each = { 50 | for ssh_key in var.ssh_keys : 51 | md5(ssh_key.public_key) => ssh_key 52 | } 53 | 54 | username = aws_iam_user.this.name 55 | 56 | public_key = each.value.public_key 57 | encoding = each.value.encoding 58 | status = each.value.enabled ? "Active" : "Inactive" 59 | } 60 | 61 | 62 | ################################################### 63 | # Service Specified Credentials for IAM User 64 | ################################################### 65 | 66 | resource "aws_iam_service_specific_credential" "this" { 67 | for_each = { 68 | for credential in var.service_credentials : 69 | credential.service => credential 70 | } 71 | 72 | user_name = aws_iam_user.this.name 73 | 74 | service_name = each.key 75 | status = each.value.enabled ? "Active" : "Inactive" 76 | } 77 | 78 | 79 | ################################################### 80 | # X.509 Signing Certificates for IAM User 81 | ################################################### 82 | 83 | resource "aws_iam_signing_certificate" "this" { 84 | for_each = { 85 | for certificate in var.signing_certificates : 86 | md5(certificate.certificate) => certificate 87 | } 88 | 89 | user_name = aws_iam_user.this.name 90 | 91 | certificate_body = each.value.certificate 92 | status = each.value.enabled ? "Active" : "Inactive" 93 | } 94 | -------------------------------------------------------------------------------- /modules/iam-oidc-identity-provider/variables.tf: -------------------------------------------------------------------------------- 1 | variable "url" { 2 | description = "(Required) The secure OpenID Connect URL for authentication requests. Correspond to the `iss` claim. Maximum 255 characters. URL must begin with `https://`." 3 | type = string 4 | 5 | validation { 6 | condition = startswith(var.url, "https://") 7 | error_message = "The value of `url` must bigin with `https://`." 8 | } 9 | } 10 | 11 | variable "audiences" { 12 | description = "(Optional) A list of audiences (also known as client IDs) for the IAM OIDC provider. When a mobile or web app registers with an OpenID Connect provider, they establish a value that identifies the application. This is the value that's sent as the `client_id` parameter on OAuth requests. Defaults to STS service(`sts.amazonaws.com`) if not values are provided." 13 | type = set(string) 14 | default = ["sts.amazonaws.com"] 15 | nullable = false 16 | } 17 | 18 | variable "thumbprints" { 19 | description = "(Optional) A list of server certificate thumbprints for the OpenID Connect (OIDC) identity provider's server certificate(s)." 20 | type = list(string) 21 | default = [] 22 | nullable = false 23 | } 24 | 25 | variable "auto_thumbprint_enabled" { 26 | description = "(Optional) Whether to automatically calculate thumbprint of the server certificate." 27 | type = bool 28 | default = true 29 | nullable = false 30 | } 31 | 32 | variable "tags" { 33 | description = "(Optional) A map of tags to add to all resources." 34 | type = map(string) 35 | default = {} 36 | nullable = false 37 | } 38 | 39 | variable "module_tags_enabled" { 40 | description = "(Optional) Whether to create AWS Resource Tags for the module informations." 41 | type = bool 42 | default = true 43 | nullable = false 44 | } 45 | 46 | 47 | ################################################### 48 | # Resource Group 49 | ################################################### 50 | 51 | variable "resource_group" { 52 | description = < 8 | ## Requirements 9 | 10 | | Name | Version | 11 | |------|---------| 12 | | [terraform](#requirement\_terraform) | >= 1.12 | 13 | | [aws](#requirement\_aws) | >= 6.12 | 14 | 15 | ## Providers 16 | 17 | No providers. 18 | 19 | ## Modules 20 | 21 | | Name | Source | Version | 22 | |------|--------|---------| 23 | | [iam\_policy](#module\_iam\_policy) | ../iam-policy | n/a | 24 | | [resource\_group](#module\_resource\_group) | tedilabs/misc/aws//modules/resource-group | ~> 0.12.0 | 25 | 26 | ## Resources 27 | 28 | No resources. 29 | 30 | ## Inputs 31 | 32 | | Name | Description | Type | Default | Required | 33 | |------|-------------|------|---------|:--------:| 34 | | [enabled\_policies](#input\_enabled\_policies) | (Optional) List of IAM policies to enable as map. `policy` is required. `name`, `path`, `description` are optional. |
list(object({
policy = string
name = optional(string, "")
path = optional(string, "/managed/")
description = optional(string, "Managed by Terraform.")
}))
| `[]` | no | 35 | | [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no | 36 | | [resource\_group](#input\_resource\_group) | (Optional) A configurations of Resource Group for this module. `resource_group` as defined below.
(Optional) `enabled` - Whether to create Resource Group to find and group AWS resources which are created by this module. Defaults to `true`.
(Optional) `name` - The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. If not provided, a name will be generated using the module name and instance name.
(Optional) `description` - The description of Resource Group. Defaults to `Managed by Terraform.`. |
object({
enabled = optional(bool, true)
name = optional(string, "")
description = optional(string, "Managed by Terraform.")
})
| `{}` | no | 37 | | [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | 38 | 39 | ## Outputs 40 | 41 | | Name | Description | 42 | |------|-------------| 43 | | [policies](#output\_policies) | A list of policies which are managed by this module. | 44 | | [resource\_group](#output\_resource\_group) | The resource group created to manage resources in this module. | 45 | 46 | -------------------------------------------------------------------------------- /modules/region/service-quotas.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | # PRs to add more of these mappings are very welcome. For more information 3 | # on how to find the Service Code and Quota Code, see the README.md! 4 | quota_codes = { 5 | # EC2 6 | "ec2/eips-per-region" = "L-0263D0A3" 7 | # FMS 8 | "fms/accounts-per-organization-filter" = "L-0C779DC8" 9 | "fms/applications-per-application-list" = "L-A423D1D9" 10 | "fms/rule-groups-per-dns-firewall-policy" = "L-5A240155" 11 | "fms/policies-per-region" = "L-0B28E140" 12 | "fms/primary-security-groups-per-common-security-group-policy" = "L-2898441F" 13 | "fms/protocols-per-protocol-list" = "L-1513E67B" 14 | "fms/tags-per-resource-tags-filter" = "L-CDB85E02" 15 | "fms/rule-groups-per-waf-policy" = "L-F8EEB3E5" 16 | # IAM 17 | "iam/customer-managed-policies-per-account" = "L-E95E4862" 18 | "iam/instance-profiles-per-account" = "L-6E65F664" 19 | "iam/managed-policies-per-role" = "L-0DA4ABF3" 20 | "iam/managed-policies-per-user" = "L-4019AD8B" 21 | "iam/roles-per-account" = "L-FE177D64" 22 | "iam/role-trust-policy-length" = "L-C07B4B0D" 23 | # Resource Groups 24 | "resource-groups/resource-groups-per-account" = "L-2BAA18A0" 25 | # Route53 26 | "route53/health-checks-per-region" = "L-ACB674F3" 27 | "route53/hosted-zones-per-region" = "L-4EA4796A" 28 | "route53/delegation-sets-per-region" = "L-A72C7724" 29 | # SES 30 | "ses/sending-emails-per-day" = "L-804C8AE8" 31 | "ses/sending-emails-per-second" = "L-CDEF9B6B" 32 | # SSO 33 | "sso/permission-sets-total" = "L-B44C7A29" 34 | "sso/permission-sets-per-aws-account" = "L-89954265" 35 | "sso/aws-accounts-or-applications-total" = "L-0299121C" 36 | # VPC 37 | "vpc/rules-per-nacl" = "L-2AEEBF1A" 38 | "vpc/rules-per-security-group" = "L-0EA8095F" 39 | "vpc/security-groups-per-eni" = "L-2AFB9258" 40 | "vpc/nat-gateways-per-az" = "L-FE5A380F" 41 | "vpc/subnets-per-vpc" = "L-407747CB" 42 | "vpc/internet-gateways-per-region" = "L-A4707A72" 43 | "vpc/vpcs-per-region" = "L-F678F1CE" 44 | # VPC IPAM (IP Address Manager) 45 | "vpc-ipam/ipams-per-region" = "L-F8B4A9E6" 46 | "vpc-ipam/scopes-per-ipam" = "L-F493CFD2" 47 | "vpc-ipam/pools-per-scope" = "L-7319AFC3" 48 | "vpc-ipam/cidrs-per-pool" = "L-0BC051D6" 49 | "vpc-ipam/pool-depth" = "L-047C0565" 50 | } 51 | } 52 | 53 | resource "aws_servicequotas_service_quota" "this" { 54 | for_each = { 55 | for code, quota in var.service_quotas.requests : 56 | code => { 57 | service_code = split("/", code)[0] 58 | quota_code = split("/", code)[1] 59 | value = quota 60 | } 61 | } 62 | 63 | region = var.region 64 | 65 | service_code = each.value.service_code 66 | quota_code = ( 67 | var.service_quotas.code_translation_enabled 68 | ? local.quota_codes[each.key] 69 | : each.value.quota_code 70 | ) 71 | value = each.value.value 72 | } 73 | -------------------------------------------------------------------------------- /modules/iam-role/outputs.tf: -------------------------------------------------------------------------------- 1 | output "arn" { 2 | description = "The ARN of the IAM role." 3 | value = aws_iam_role.this.arn 4 | } 5 | 6 | output "id" { 7 | description = "The ID of the IAM role." 8 | value = aws_iam_role.this.id 9 | } 10 | 11 | output "unique_id" { 12 | description = "The unique ID assigned by AWS." 13 | value = aws_iam_role.this.unique_id 14 | } 15 | 16 | output "name" { 17 | description = "IAM Role name." 18 | value = aws_iam_role.this.name 19 | } 20 | 21 | output "description" { 22 | description = "The description of the role." 23 | value = aws_iam_role.this.description 24 | } 25 | 26 | output "path" { 27 | description = "The path of the IAM role." 28 | value = aws_iam_role.this.path 29 | } 30 | 31 | output "assumable_roles" { 32 | description = "A set of ARNs of IAM roles which members of IAM role can assume." 33 | value = toset(var.assumable_roles) 34 | } 35 | 36 | output "exclusive_policy_management_enabled" { 37 | description = "Whether exclusive policy management is enabled for the IAM role." 38 | value = var.exclusive_policy_management_enabled 39 | } 40 | 41 | output "exclusive_inline_policy_management_enabled" { 42 | description = "Whether exclusive inline policy management is enabled for the IAM role." 43 | value = var.exclusive_inline_policy_management_enabled 44 | } 45 | 46 | output "policies" { 47 | description = "A set of ARNs of IAM policies which are atached to IAM role." 48 | value = (var.exclusive_policy_management_enabled 49 | ? toset(aws_iam_role_policy_attachments_exclusive.this[0].policy_arns) 50 | : toset(values(aws_iam_role_policy_attachment.managed)[*].policy_arn) 51 | ) 52 | } 53 | 54 | output "inline_policies" { 55 | description = "A set of names of inline IAM polices which are attached to IAM role." 56 | value = toset(keys(var.inline_policies)) 57 | } 58 | 59 | output "created_at" { 60 | description = "Creation date of the IAM role." 61 | value = aws_iam_role.this.create_date 62 | } 63 | 64 | output "instance_profile" { 65 | description = < 9 | ## Requirements 10 | 11 | | Name | Version | 12 | |------|---------| 13 | | [terraform](#requirement\_terraform) | >= 1.12 | 14 | | [aws](#requirement\_aws) | >= 6.12 | 15 | 16 | ## Providers 17 | 18 | | Name | Version | 19 | |------|---------| 20 | | [aws](#provider\_aws) | 6.12.0 | 21 | 22 | ## Modules 23 | 24 | | Name | Source | Version | 25 | |------|--------|---------| 26 | | [resource\_group](#module\_resource\_group) | tedilabs/misc/aws//modules/resource-group | ~> 0.12.0 | 27 | 28 | ## Resources 29 | 30 | | Name | Type | 31 | |------|------| 32 | | [aws_iam_saml_provider.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_saml_provider) | resource | 33 | 34 | ## Inputs 35 | 36 | | Name | Description | Type | Default | Required | 37 | |------|-------------|------|---------|:--------:| 38 | | [metadata\_document](#input\_metadata\_document) | (Optional) A XML document generated by an identity provider that supports SAML 2.0. | `string` | n/a | yes | 39 | | [name](#input\_name) | (Required) The name of the identity provider. | `string` | n/a | yes | 40 | | [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no | 41 | | [resource\_group](#input\_resource\_group) | (Optional) A configurations of Resource Group for this module. `resource_group` as defined below.
(Optional) `enabled` - Whether to create Resource Group to find and group AWS resources which are created by this module. Defaults to `true`.
(Optional) `name` - The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. If not provided, a name will be generated using the module name and instance name.
(Optional) `description` - The description of Resource Group. Defaults to `Managed by Terraform.`. |
object({
enabled = optional(bool, true)
name = optional(string, "")
description = optional(string, "Managed by Terraform.")
})
| `{}` | no | 42 | | [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | 43 | 44 | ## Outputs 45 | 46 | | Name | Description | 47 | |------|-------------| 48 | | [arn](#output\_arn) | The ARN assigned by AWS for this provider. | 49 | | [expire\_at](#output\_expire\_at) | The expiration date and time for the SAML provider in RFC1123 format, e.g., `Mon, 02 Jan 2006 15:04:05 MST`. | 50 | | [id](#output\_id) | The ID of this provider. | 51 | | [name](#output\_name) | The name of the identity provider. | 52 | | [resource\_group](#output\_resource\_group) | The resource group created to manage resources in this module. | 53 | 54 | -------------------------------------------------------------------------------- /modules/iam-role/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | metadata = { 3 | package = "terraform-aws-account" 4 | version = trimspace(file("${path.module}/../../VERSION")) 5 | module = basename(path.module) 6 | name = var.name 7 | } 8 | module_tags = var.module_tags_enabled ? { 9 | "module.terraform.io/package" = local.metadata.package 10 | "module.terraform.io/version" = local.metadata.version 11 | "module.terraform.io/name" = local.metadata.module 12 | "module.terraform.io/full-name" = "${local.metadata.package}/${local.metadata.module}" 13 | "module.terraform.io/instance" = local.metadata.name 14 | } : {} 15 | } 16 | 17 | 18 | ################################################### 19 | # IAM Role 20 | ################################################### 21 | 22 | # INFO: Not supported attributes 23 | # - `name_prefix` 24 | # INFO: Deprecated attributes 25 | # - `inline_policy` 26 | # - `managed_policy_arns` 27 | resource "aws_iam_role" "this" { 28 | name = local.metadata.name 29 | path = var.path 30 | description = var.description 31 | max_session_duration = var.max_session_duration 32 | force_detach_policies = var.force_detach_policies 33 | permissions_boundary = var.permissions_boundary 34 | 35 | assume_role_policy = data.aws_iam_policy_document.trusted_entities.json 36 | 37 | tags = merge( 38 | { 39 | "Name" = local.metadata.name 40 | }, 41 | local.module_tags, 42 | var.tags, 43 | ) 44 | } 45 | 46 | 47 | ################################################### 48 | # IAM Policy for Trusted Entities 49 | ################################################### 50 | 51 | data "aws_iam_policy_document" "trusted_entities" { 52 | source_policy_documents = concat( 53 | values(data.aws_iam_policy_document.trusted_iam_entity_policies)[*].json, 54 | values(data.aws_iam_policy_document.trusted_service_policies)[*].json, 55 | values(data.aws_iam_policy_document.trusted_oidc_provider_policies)[*].json, 56 | values(data.aws_iam_policy_document.trusted_saml_provider_policies)[*].json, 57 | ) 58 | } 59 | 60 | 61 | ################################################### 62 | # IAM Policy for AssumeRole 63 | ################################################### 64 | 65 | data "aws_iam_policy_document" "assume_role" { 66 | statement { 67 | effect = "Allow" 68 | actions = ["sts:AssumeRole"] 69 | resources = var.assumable_roles 70 | } 71 | } 72 | 73 | resource "aws_iam_role_policy" "assume_role" { 74 | count = length(var.assumable_roles) > 0 ? 1 : 0 75 | 76 | role = aws_iam_role.this.id 77 | name = "assume-role" 78 | policy = data.aws_iam_policy_document.assume_role.json 79 | } 80 | 81 | 82 | ################################################### 83 | # IAM Instance Profile 84 | ################################################### 85 | 86 | resource "aws_iam_instance_profile" "this" { 87 | count = var.instance_profile.enabled ? 1 : 0 88 | 89 | role = aws_iam_role.this.name 90 | 91 | name = coalesce(var.instance_profile.name, local.metadata.name) 92 | path = var.instance_profile.path 93 | 94 | tags = merge( 95 | { 96 | "Name" = coalesce(var.instance_profile.name, local.metadata.name) 97 | }, 98 | local.module_tags, 99 | var.tags, 100 | var.instance_profile.tags, 101 | ) 102 | } 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # terraform-aws-account 2 | 3 | ![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/tedilabs/terraform-aws-account?color=blue&sort=semver&style=flat-square) 4 | ![GitHub](https://img.shields.io/github/license/tedilabs/terraform-aws-account?color=blue&style=flat-square) 5 | [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=flat-square)](https://github.com/pre-commit/pre-commit) 6 | 7 | Terraform module which creates Account and IAM related resources on AWS. 8 | 9 | - [account](./modules/account) 10 | - [iam-group](./modules/iam-group) 11 | - [iam-oidc-identity-provider](./modules/iam-oidc-identity-provider) 12 | - [iam-policy](./modules/iam-policy) 13 | - [iam-predefined-policies](./modules/iam-predefined-policies) 14 | - [iam-role](./modules/iam-role) 15 | - [iam-saml-identity-provider](./modules/iam-saml-identity-provider) 16 | - [iam-service-linked-role](./modules/iam-service-linked-role) 17 | - [iam-user](./modules/iam-user) 18 | - [region](./modules/region) 19 | 20 | 21 | ## Target AWS Services 22 | 23 | Terraform Modules from [this package](https://github.com/tedilabs/terraform-aws-account) were written to manage the following AWS Services with Terraform. 24 | 25 | - **AWS Account** 26 | - Account Settings 27 | - Region Settings 28 | - **AWS IAM** 29 | - User 30 | - Group 31 | - Role 32 | - Service-linked Role 33 | - Policy 34 | - OpenID Connect Identity Provider 35 | - SAML Identity Provider 36 | - **AWS Resource Explorer** 37 | - Index 38 | - View 39 | 40 | 41 | ## Examples 42 | 43 | ### Account 44 | 45 | - [Alias for the AWS Account](./examples/account-alias) 46 | - [Contacts for the AWS Account](./examples/account-contacts) 47 | - [Password Policy for the AWS Account](./examples/account-password-policy) 48 | 49 | 50 | ### IAM 51 | 52 | - [OIDC Identity Providers](./examples/iam-oidc-identity-providers) 53 | - [SAML Identity Providers](./examples/iam-saml-identity-providers) 54 | 55 | 56 | ## Other Terraform Modules from Tedilabs 57 | 58 | Enjoying [terraform-aws-account](https://github.com/tedilabs/terraform-aws-account)? Check out some of our other modules: 59 | 60 | - [AWS Container](https://github.com/tedilabs/terraform-aws-container) - A package of Terraform Modules to manage AWS Container resources. 61 | - [AWS Domain](https://github.com/tedilabs/terraform-aws-domain) - A package of Terraform Modules to manage AWS Domain resources. 62 | - [AWS Load Balancer](https://github.com/tedilabs/terraform-aws-load-balancer) - A package of Terraform Modules to manage AWS Load Balancer resources. 63 | - [AWS Network](https://github.com/tedilabs/terraform-aws-network) - A package of Terraform Modules to manage AWS Network resources. 64 | - [AWS Organization](https://github.com/tedilabs/terraform-aws-organization) - A package of Terraform Modules to manage AWS Organization resources. 65 | - [AWS Security](https://github.com/tedilabs/terraform-aws-security) - A package of Terraform Modules to manage AWS Security resources. 66 | 67 | Or check out [the full list](https://github.com/search?q=org%3Atedilabs+topic%3Aterraform-module&type=repositories) 68 | 69 | 70 | ## Self Promotion 71 | 72 | Like this project? Follow the repository on [GitHub](https://github.com/tedilabs/terraform-aws-account). And if you're feeling especially charitable, follow **[posquit0](https://github.com/posquit0)** on GitHub. 73 | 74 | 75 | ## License 76 | 77 | Provided under the terms of the [Apache License](LICENSE). 78 | 79 | Copyright © 2021-2025, [Byungjin Park](https://www.posquit0.com). -------------------------------------------------------------------------------- /modules/iam-policy/README.md: -------------------------------------------------------------------------------- 1 | # iam-policy 2 | 3 | This module creates following resources. 4 | 5 | - `aws_iam_policy` 6 | 7 | 8 | 9 | ## Requirements 10 | 11 | | Name | Version | 12 | |------|---------| 13 | | [terraform](#requirement\_terraform) | >= 1.12 | 14 | | [aws](#requirement\_aws) | >= 6.12 | 15 | 16 | ## Providers 17 | 18 | | Name | Version | 19 | |------|---------| 20 | | [aws](#provider\_aws) | 6.12.0 | 21 | 22 | ## Modules 23 | 24 | | Name | Source | Version | 25 | |------|--------|---------| 26 | | [resource\_group](#module\_resource\_group) | tedilabs/misc/aws//modules/resource-group | ~> 0.12.0 | 27 | 28 | ## Resources 29 | 30 | | Name | Type | 31 | |------|------| 32 | | [aws_iam_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | 33 | 34 | ## Inputs 35 | 36 | | Name | Description | Type | Default | Required | 37 | |------|-------------|------|---------|:--------:| 38 | | [name](#input\_name) | (Required) The name of the IAM policy. | `string` | n/a | yes | 39 | | [policy](#input\_policy) | (Required) The policy document. This is a JSON formatted string. | `string` | n/a | yes | 40 | | [description](#input\_description) | (Optional) The description of the IAM policy. | `string` | `"Managed by Terraform."` | no | 41 | | [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no | 42 | | [path](#input\_path) | (Optional) The path in which to create the policy. Defaults to `/`. | `string` | `"/"` | no | 43 | | [resource\_group](#input\_resource\_group) | (Optional) A configurations of Resource Group for this module. `resource_group` as defined below.
(Optional) `enabled` - Whether to create Resource Group to find and group AWS resources which are created by this module. Defaults to `true`.
(Optional) `name` - The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. If not provided, a name will be generated using the module name and instance name.
(Optional) `description` - The description of Resource Group. Defaults to `Managed by Terraform.`. |
object({
enabled = optional(bool, true)
name = optional(string, "")
description = optional(string, "Managed by Terraform.")
})
| `{}` | no | 44 | | [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | 45 | 46 | ## Outputs 47 | 48 | | Name | Description | 49 | |------|-------------| 50 | | [arn](#output\_arn) | The ARN assigned by AWS for this IAM policy. | 51 | | [description](#output\_description) | The description of the IAM policy. | 52 | | [id](#output\_id) | The ID of this IAM policy. | 53 | | [name](#output\_name) | The URL of the IAM policy. | 54 | | [path](#output\_path) | The path of the IAM policy. | 55 | | [resource\_group](#output\_resource\_group) | The resource group created to manage resources in this module. | 56 | 57 | -------------------------------------------------------------------------------- /.github/workflows/terraform.integration.yaml: -------------------------------------------------------------------------------- 1 | name: Integration (Terraform) 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: {} 8 | 9 | concurrency: 10 | group: terraform-integration-${{ github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | changed: 15 | name: Filter Changed Files and Directories 16 | runs-on: ubuntu-latest 17 | 18 | outputs: 19 | changed: ${{ steps.set-outputs.outputs.changed }} 20 | modified: ${{ steps.set-outputs.outputs.modified }} 21 | changed_files: ${{ steps.set-outputs.outputs.changed_files }} 22 | modified_files: ${{ steps.set-outputs.outputs.modified_files }} 23 | changed_directories: ${{ steps.set-outputs.outputs.changed_directories }} 24 | modified_directories: ${{ steps.set-outputs.outputs.modified_directories }} 25 | 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@v4 29 | with: 30 | fetch-depth: 0 31 | 32 | - name: Get Changed Files 33 | id: changed-files 34 | uses: tj-actions/changed-files@v44 35 | with: 36 | files: | 37 | modules/** 38 | examples/** 39 | json: true 40 | 41 | - name: Get Changed Directories 42 | id: changed-directories 43 | uses: tj-actions/changed-files@v44 44 | with: 45 | files: | 46 | modules/** 47 | examples/** 48 | dir_names: "true" 49 | dir_names_max_depth: 2 50 | json: true 51 | 52 | - name: Set outputs 53 | id: set-outputs 54 | run: | 55 | echo "changed=${{ steps.changed-directories.outputs.any_changed }}" >> $GITHUB_OUTPUT 56 | echo "modified=${{ steps.changed-directories.outputs.any_modified }}" >> $GITHUB_OUTPUT 57 | 58 | echo "changed_files=${{ steps.changed-files.outputs.all_changed_files }}" >> $GITHUB_OUTPUT 59 | echo "modified_files=${{ steps.changed-files.outputs.all_modified_files }}" >> $GITHUB_OUTPUT 60 | 61 | echo "changed_directories=${{ steps.changed-directories.outputs.all_changed_files }}" >> $GITHUB_OUTPUT 62 | echo "modified_directories=${{ steps.changed-directories.outputs.all_modified_files }}" >> $GITHUB_OUTPUT 63 | 64 | 65 | terraform: 66 | name: Lint (terraform) 67 | needs: 68 | - changed 69 | if: ${{ needs.changed.outputs.modified == 'true' }} 70 | uses: tedilabs/.github/.github/workflows/terraform.terraform.yaml@main 71 | 72 | strategy: 73 | matrix: 74 | path: ${{ fromJson(needs.changed.outputs.modified_directories) }} 75 | 76 | with: 77 | terraform_target_dir: ${{ matrix.path }} 78 | terraform_version: latest 79 | terraform_host: app.terraform.io 80 | secrets: 81 | gh_token: ${{ secrets.GITHUB_TOKEN }} 82 | token: ${{ secrets.GITHUB_TOKEN }} 83 | terraform_token: ${{ secrets.TERRAFORM_TOKEN }} 84 | 85 | 86 | tflint: 87 | name: Lint (tflint) 88 | needs: 89 | - changed 90 | if: ${{ needs.changed.outputs.modified == 'true' }} 91 | uses: tedilabs/.github/.github/workflows/terraform.tflint.yaml@main 92 | 93 | strategy: 94 | matrix: 95 | path: ${{ fromJson(needs.changed.outputs.modified_directories) }} 96 | 97 | with: 98 | tflint_version: latest 99 | tflint_config_file: .tflint.hcl 100 | tflint_target_dir: ${{ matrix.path }} 101 | tflint_recursive_enabled: false 102 | tflint_terraform_init_enabled: true 103 | terraform_version: latest 104 | terraform_host: app.terraform.io 105 | secrets: 106 | gh_token: ${{ secrets.GITHUB_TOKEN }} 107 | token: ${{ secrets.GITHUB_TOKEN }} 108 | terraform_token: ${{ secrets.TERRAFORM_TOKEN }} 109 | -------------------------------------------------------------------------------- /modules/iam-service-linked-role/README.md: -------------------------------------------------------------------------------- 1 | # iam-service-linked-role 2 | 3 | This module creates following resources. 4 | 5 | - `aws_iam_service_linked_role` 6 | 7 | 8 | ## Requirements 9 | 10 | | Name | Version | 11 | |------|---------| 12 | | [terraform](#requirement\_terraform) | >= 1.12 | 13 | | [aws](#requirement\_aws) | >= 6.12 | 14 | 15 | ## Providers 16 | 17 | | Name | Version | 18 | |------|---------| 19 | | [aws](#provider\_aws) | 6.12.0 | 20 | 21 | ## Modules 22 | 23 | | Name | Source | Version | 24 | |------|--------|---------| 25 | | [resource\_group](#module\_resource\_group) | tedilabs/misc/aws//modules/resource-group | ~> 0.12.0 | 26 | 27 | ## Resources 28 | 29 | | Name | Type | 30 | |------|------| 31 | | [aws_iam_service_linked_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_service_linked_role) | resource | 32 | 33 | ## Inputs 34 | 35 | | Name | Description | Type | Default | Required | 36 | |------|-------------|------|---------|:--------:| 37 | | [aws\_service](#input\_aws\_service) | (Required) The AWS service principal to which this role is attached. For example: `elasticbeanstalk.amazonaws.com`. | `string` | n/a | yes | 38 | | [custom\_suffix](#input\_custom\_suffix) | (Optional) Additional string appended to the role name. Not all AWS services support custom suffixes. | `string` | `""` | no | 39 | | [description](#input\_description) | (Optional) The description of the role. Defaults to `Managed by Terraform.`. | `string` | `"Managed by Terraform."` | no | 40 | | [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no | 41 | | [resource\_group](#input\_resource\_group) | (Optional) A configurations of Resource Group for this module. `resource_group` as defined below.
(Optional) `enabled` - Whether to create Resource Group to find and group AWS resources which are created by this module. Defaults to `true`.
(Optional) `name` - The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. If not provided, a name will be generated using the module name and instance name.
(Optional) `description` - The description of Resource Group. Defaults to `Managed by Terraform.`. |
object({
enabled = optional(bool, true)
name = optional(string, "")
description = optional(string, "Managed by Terraform.")
})
| `{}` | no | 42 | | [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | 43 | 44 | ## Outputs 45 | 46 | | Name | Description | 47 | |------|-------------| 48 | | [arn](#output\_arn) | The ARN assigned by AWS for this role. | 49 | | [aws\_service](#output\_aws\_service) | The AWS service principal to which this role is attached. | 50 | | [created\_at](#output\_created\_at) | The creation date of the IAM role. | 51 | | [description](#output\_description) | The description of the role. | 52 | | [name](#output\_name) | IAM Role name. | 53 | | [path](#output\_path) | The path of the role. | 54 | | [resource\_group](#output\_resource\_group) | The resource group created to manage resources in this module. | 55 | | [unique\_id](#output\_unique\_id) | The unique ID assigned by AWS. | 56 | 57 | -------------------------------------------------------------------------------- /modules/iam-role/trusted-service-policies.tf: -------------------------------------------------------------------------------- 1 | data "aws_iam_policy_document" "trusted_service_policies" { 2 | for_each = { 3 | for idx, policy in var.trusted_service_policies : 4 | idx => policy 5 | } 6 | 7 | statement { 8 | sid = "TrustedServices${each.key}" 9 | effect = "Allow" 10 | actions = ["sts:AssumeRole"] 11 | 12 | principals { 13 | type = "Service" 14 | identifiers = each.value.services 15 | } 16 | 17 | dynamic "condition" { 18 | for_each = var.conditions 19 | 20 | content { 21 | variable = condition.value.key 22 | test = condition.value.condition 23 | values = condition.value.values 24 | } 25 | } 26 | 27 | dynamic "condition" { 28 | for_each = each.value.conditions 29 | 30 | content { 31 | variable = condition.value.key 32 | test = condition.value.condition 33 | values = condition.value.values 34 | } 35 | } 36 | 37 | dynamic "condition" { 38 | for_each = each.value.effective_date != null ? ["go"] : [] 39 | 40 | content { 41 | test = "DateGreaterThan" 42 | variable = "aws:CurrentTime" 43 | values = [each.value.effective_date] 44 | } 45 | } 46 | 47 | dynamic "condition" { 48 | for_each = each.value.expiration_date != null ? ["go"] : [] 49 | 50 | content { 51 | test = "DateLessThan" 52 | variable = "aws:CurrentTime" 53 | values = [each.value.expiration_date] 54 | } 55 | } 56 | 57 | dynamic "condition" { 58 | for_each = length(each.value.source_ip_whitelist) > 0 ? ["go"] : [] 59 | 60 | content { 61 | test = "IpAddress" 62 | variable = "aws:SourceIp" 63 | values = each.value.source_ip_whitelist 64 | } 65 | } 66 | 67 | dynamic "condition" { 68 | for_each = length(each.value.source_ip_blacklist) > 0 ? ["go"] : [] 69 | 70 | content { 71 | test = "NotIpAddress" 72 | variable = "aws:SourceIp" 73 | values = each.value.source_ip_blacklist 74 | } 75 | } 76 | } 77 | 78 | dynamic "statement" { 79 | for_each = var.trusted_session_tagging.enabled ? ["go"] : [] 80 | 81 | content { 82 | sid = "TrustedTagSessionForServices${each.key}" 83 | effect = "Allow" 84 | actions = ["sts:TagSession"] 85 | 86 | principals { 87 | type = "Service" 88 | identifiers = each.value.services 89 | } 90 | 91 | dynamic "condition" { 92 | for_each = var.trusted_session_tagging.allowed_tags 93 | 94 | content { 95 | test = "StringLike" 96 | variable = "aws:RequestTag/${condition.key}" 97 | values = condition.value.values 98 | } 99 | } 100 | 101 | dynamic "condition" { 102 | for_each = length(var.trusted_session_tagging.allowed_transitive_tag_keys) > 0 ? ["go"] : [] 103 | 104 | content { 105 | test = "ForAllValues:StringLike" 106 | variable = "sts:TransitiveTagKeys" 107 | values = var.trusted_session_tagging.allowed_transitive_tag_keys 108 | } 109 | } 110 | } 111 | } 112 | 113 | dynamic "statement" { 114 | for_each = var.trusted_source_identity.enabled ? ["go"] : [] 115 | 116 | content { 117 | sid = "TrustedSourceIdentityForServices${each.key}" 118 | effect = "Allow" 119 | actions = ["sts:SetSourceIdentity"] 120 | 121 | principals { 122 | type = "Service" 123 | identifiers = each.value.services 124 | } 125 | 126 | dynamic "condition" { 127 | for_each = length(var.trusted_source_identity.allowed_identities) > 0 ? ["go"] : [] 128 | 129 | content { 130 | test = "StringLike" 131 | variable = "sts:SourceIdentity" 132 | values = var.trusted_source_identity.allowed_identities 133 | } 134 | } 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /modules/iam-role/trusted-saml-provider-policies.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | saml_provider_arn_prefix = "arn:${local.partition}:iam::${local.account_id}:saml-provider/" 3 | } 4 | 5 | data "aws_iam_policy_document" "trusted_saml_provider_policies" { 6 | for_each = { 7 | for idx, policy in var.trusted_saml_provider_policies : 8 | idx => policy 9 | } 10 | 11 | statement { 12 | sid = "TrustedSAML${each.key}" 13 | effect = "Allow" 14 | actions = ["sts:AssumeRoleWithSAML"] 15 | 16 | principals { 17 | type = "Federated" 18 | identifiers = ["${local.saml_provider_arn_prefix}${each.value.name}"] 19 | } 20 | 21 | dynamic "condition" { 22 | for_each = var.conditions 23 | 24 | content { 25 | variable = condition.value.key 26 | test = condition.value.condition 27 | values = condition.value.values 28 | } 29 | } 30 | 31 | dynamic "condition" { 32 | for_each = each.value.conditions 33 | 34 | content { 35 | variable = "saml:${condition.value.key}" 36 | test = condition.value.condition 37 | values = condition.value.values 38 | } 39 | } 40 | 41 | dynamic "condition" { 42 | for_each = each.value.effective_date != null ? ["go"] : [] 43 | 44 | content { 45 | test = "DateGreaterThan" 46 | variable = "aws:CurrentTime" 47 | values = [each.value.effective_date] 48 | } 49 | } 50 | 51 | dynamic "condition" { 52 | for_each = each.value.expiration_date != null ? ["go"] : [] 53 | 54 | content { 55 | test = "DateLessThan" 56 | variable = "aws:CurrentTime" 57 | values = [each.value.expiration_date] 58 | } 59 | } 60 | 61 | dynamic "condition" { 62 | for_each = length(each.value.source_ip_whitelist) > 0 ? ["go"] : [] 63 | 64 | content { 65 | test = "IpAddress" 66 | variable = "aws:SourceIp" 67 | values = each.value.source_ip_whitelist 68 | } 69 | } 70 | 71 | dynamic "condition" { 72 | for_each = length(each.value.source_ip_blacklist) > 0 ? ["go"] : [] 73 | 74 | content { 75 | test = "NotIpAddress" 76 | variable = "aws:SourceIp" 77 | values = each.value.source_ip_blacklist 78 | } 79 | } 80 | } 81 | 82 | dynamic "statement" { 83 | for_each = var.trusted_session_tagging.enabled ? ["go"] : [] 84 | 85 | content { 86 | sid = "TrustedTagSessionSamlProvider${each.key}" 87 | effect = "Allow" 88 | actions = ["sts:TagSession"] 89 | 90 | principals { 91 | type = "Federated" 92 | identifiers = ["${local.saml_provider_arn_prefix}${each.value.name}"] 93 | } 94 | 95 | dynamic "condition" { 96 | for_each = var.trusted_session_tagging.allowed_tags 97 | 98 | content { 99 | test = "StringLike" 100 | variable = "aws:RequestTag/${condition.key}" 101 | values = condition.value.values 102 | } 103 | } 104 | 105 | dynamic "condition" { 106 | for_each = length(var.trusted_session_tagging.allowed_transitive_tag_keys) > 0 ? ["go"] : [] 107 | 108 | content { 109 | test = "ForAllValues:StringLike" 110 | variable = "sts:TransitiveTagKeys" 111 | values = var.trusted_session_tagging.allowed_transitive_tag_keys 112 | } 113 | } 114 | } 115 | } 116 | 117 | dynamic "statement" { 118 | for_each = var.trusted_source_identity.enabled ? ["go"] : [] 119 | 120 | content { 121 | sid = "TrustedSourceIdentityForSamlProvider${each.key}" 122 | effect = "Allow" 123 | actions = ["sts:SetSourceIdentity"] 124 | 125 | principals { 126 | type = "Federated" 127 | identifiers = ["${local.saml_provider_arn_prefix}${each.value.name}"] 128 | } 129 | 130 | dynamic "condition" { 131 | for_each = length(var.trusted_source_identity.allowed_identities) > 0 ? ["go"] : [] 132 | 133 | content { 134 | test = "StringLike" 135 | variable = "sts:SourceIdentity" 136 | values = var.trusted_source_identity.allowed_identities 137 | } 138 | } 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /examples/iam-saml-identity-providers/saml-idp-metadata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | MIID7TCCAtWgAwIBAgIJANn3qP9lF7M3MA0GCSqGSIb3DQEBCwUAMIGMMQswCQYDVQQGEwJVQTEXMBUGA1UE 8 | CAwOS2hhcmtpdiBSZWdpb24xEDAOBgNVBAcMB0toYXJrb3YxDzANBgNVBAoMBk9yYWNsZTEYMBYGA1UEAwwPc3RzeWJvdi12bTEudWEzMScw 9 | JQYJKoZIhvcNAQkBFhhzZXJnaWkudHN5Ym92QG9yYWNsZS5jb20wHhcNMTUxMjI1MTIyMjU5WhcNMjUxMjI0MTIyMjU5WjCBjDELMAkGA1UE 10 | BhMCVUExFzAVBgNVBAgMDktoYXJraXYgUmVnaW9uMRAwDgYDVQQHDAdLaGFya292MQ8wDQYDVQQKDAZPcmFjbGUxGDAWBgNVBAMMD3N0c3lib 11 | 3Ytdm0xLnVhMzEnMCUGCSqGSIb3DQEJARYYc2VyZ2lpLnRzeWJvdkBvcmFjbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCA 12 | QEAw4OFwuUNjn6xxb/OuAnmQA6mCWPY2hKMoOz0cAajUHjNZZMwGnuEeUyPtEcULfz2MYo1yKQLxVj3pY0HTIQAzpY8o+xCqJFQmdMiakb 13 | PFHlh4z/qqiS5jHng6JCeUpCIxeiTG9JXVwF1ErBEZbwZYjVxa6S+0grVkS3YxuH4uTyqxskuGnHK/AviTHLBrLfSrbFKYuQUrXyy6X22wpzo 14 | bQ3Z+4bhEE8SXQtVbQdy7K0MKWYopNhX05SMTv7yMfUGp8EkGNyJ5Km8AuQt6ZCbVao6cHL2hSujQiN6aMjKbdzHeA1QEicppnnoG/Zefyi/ 15 | okWdlLAaLjcpYrjUSWQJZQIDAQABo1AwTjAdBgNVHQ4EFgQUIKa0zeXmAJsCuNhJjhU0o7KiQgYwHwYDVR0jBBgwFoAUIKa0zeXmAJsCuNhJj 16 | hU0o7KiQgYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAJawU5WRXqkW4emm+djpJAxZ0076qPgEsaaog6ng4MLAlU7RmfIY/ 17 | l0VhXQegvhIBfG4OfduuzGaqd9y4IsQZFJ0yuotl96iEVcqg7hJ1LEY6UT6u6dZyGj1a9I6IlwJm/9CXFZHuVqGJkMfQZ4gaunE4c5gjbQA5/ 18 | +PEJwPorKn48w8bojymV8hriqzrmaP8eQNuZUJsJdnKENOE5/asGyj+R2YfP6bmlOX3q0ozLcyJbXeZ6IvDFdRiDH5wO4JqW/ujvdvC553y 19 | CO3xxsorB4xCupuHu/c7vkzNpaKjYdmGRkqhEqBcCqYSxdwIFc1xhOwYPWKJzgn7pGQsT7yNJg== 20 | 21 | 22 | 23 | 24 | 25 | 26 | MIID7TCCAtWgAwIBAgIJANn3qP9lF7M3MA0GCSqGSIb3DQEBCwUAMIGMMQswCQYDVQQGEwJVQTEXMBUGA1 27 | UECAwOS2hhcmtpdiBSZWdpb24xEDAOBgNVBAcMB0toYXJrb3YxDzANBgNVBAoMBk9yYWNsZTEYMBYGA1UEAwwPc3RzeWJvdi12bTEud 28 | WEzMScwJQYJKoZIhvcNAQkBFhhzZXJnaWkudHN5Ym92QG9yYWNsZS5jb20wHhcNMTUxMjI1MTIyMjU5WhcNMjUxMjI0MTIyMjU5WjCB 29 | jDELMAkGA1UEBhMCVUExFzAVBgNVBAgMDktoYXJraXYgUmVnaW9uMRAwDgYDVQQHDAdLaGFya292MQ8wDQYDVQQKDAZPcmFjbGUxGDA 30 | WBgNVBAMMD3N0c3lib3Ytdm0xLnVhMzEnMCUGCSqGSIb3DQEJARYYc2VyZ2lpLnRzeWJvdkBvcmFjbGUuY29tMIIBIjANBgkqhkiG9w0B 31 | AQEFAAOCAQ8AMIIBCgKCAQEAw4OFwuUNjn6xxb/OuAnmQA6mCWPY2hKMoOz0cAajUHjNZZMwGnuEeUyPtEcULfz2MYo1yKQLxVj3pY0HT 32 | IQAzpY8o+xCqJFQmdMiakbPFHlh4z/qqiS5jHng6JCeUpCIxeiTG9JXVwF1ErBEZbwZYjVxa6S+0grVkS3YxuH4uTyqxskuGnHK/ 33 | AviTHLBrLfSrbFKYuQUrXyy6X22wpzobQ3Z+4bhEE8SXQtVbQdy7K0MKWYopNhX05SMTv7yMfUGp8EkGNyJ5Km8AuQt6ZCbVao6cHL2h 34 | SujQiN6aMjKbdzHeA1QEicppnnoG/Zefyi/okWdlLAaLjcpYrjUSWQJZQIDAQABo1AwTjAdBgNVHQ4EFgQUIKa0zeXmAJsCuNhJjhU0o 35 | 7KiQgYwHwYDVR0jBBgwFoAUIKa0zeXmAJsCuNhJjhU0o7KiQgYwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAJawU5WRXq 36 | kW4emm+djpJAxZ0076qPgEsaaog6ng4MLAlU7RmfIY/l0VhXQegvhIBfG4OfduuzGaqd9y4IsQZFJ0yuotl96iEVcqg7hJ1LEY6UT6u6d 37 | ZyGj1a9I6IlwJm/9CXFZHuVqGJkMfQZ4gaunE4c5gjbQA5/+PEJwPorKn48w8bojymV8hriqzrmaP8eQNuZUJsJdnKENOE5/ 38 | asGyj+R2YfP6bmlOX3q0ozLcyJbXeZ6IvDFdRiDH5wO4JqW/ujvdvC553yCO3xxsorB4xCupuHu/c7vkzNpaKjYdmGRkqhEqBcCqYSxd 39 | wIFc1xhOwYPWKJzgn7pGQsT7yNJg== 40 | 41 | 42 | 43 | 44 | urn:oasis:names:tc:SAML:2.0:nameid-format:transient 45 | 46 | 47 | 48 | Administrator 49 | name@emailprovider.com 50 | 51 | 52 | -------------------------------------------------------------------------------- /modules/iam-role/trusted-iam-entity-policies.tf: -------------------------------------------------------------------------------- 1 | data "aws_iam_policy_document" "trusted_iam_entity_policies" { 2 | for_each = { 3 | for idx, policy in var.trusted_iam_entity_policies : 4 | idx => policy 5 | } 6 | 7 | statement { 8 | sid = "TrustedIamEntities${each.key}" 9 | effect = "Allow" 10 | actions = ["sts:AssumeRole"] 11 | 12 | principals { 13 | type = "AWS" 14 | identifiers = each.value.iam_entities 15 | } 16 | 17 | dynamic "condition" { 18 | for_each = var.conditions 19 | 20 | content { 21 | variable = condition.value.key 22 | test = condition.value.condition 23 | values = condition.value.values 24 | } 25 | } 26 | 27 | dynamic "condition" { 28 | for_each = each.value.conditions 29 | 30 | content { 31 | variable = condition.value.key 32 | test = condition.value.condition 33 | values = condition.value.values 34 | } 35 | } 36 | 37 | dynamic "condition" { 38 | for_each = each.value.mfa.required ? ["go"] : [] 39 | 40 | content { 41 | test = "Bool" 42 | variable = "aws:MultiFactorAuthPresent" 43 | values = [tostring(each.value.mfa.required)] 44 | } 45 | } 46 | 47 | dynamic "condition" { 48 | for_each = each.value.mfa.required ? ["go"] : [] 49 | 50 | content { 51 | test = "NumericLessThan" 52 | variable = "aws:MultiFactorAuthAge" 53 | values = [each.value.mfa.ttl] 54 | } 55 | } 56 | 57 | dynamic "condition" { 58 | for_each = each.value.effective_date != null ? ["go"] : [] 59 | 60 | content { 61 | test = "DateGreaterThan" 62 | variable = "aws:CurrentTime" 63 | values = [each.value.effective_date] 64 | } 65 | } 66 | 67 | dynamic "condition" { 68 | for_each = each.value.expiration_date != null ? ["go"] : [] 69 | 70 | content { 71 | test = "DateLessThan" 72 | variable = "aws:CurrentTime" 73 | values = [each.value.expiration_date] 74 | } 75 | } 76 | 77 | dynamic "condition" { 78 | for_each = length(each.value.source_ip_whitelist) > 0 ? ["go"] : [] 79 | 80 | content { 81 | test = "IpAddress" 82 | variable = "aws:SourceIp" 83 | values = each.value.source_ip_whitelist 84 | } 85 | } 86 | 87 | dynamic "condition" { 88 | for_each = length(each.value.source_ip_blacklist) > 0 ? ["go"] : [] 89 | 90 | content { 91 | test = "NotIpAddress" 92 | variable = "aws:SourceIp" 93 | values = each.value.source_ip_blacklist 94 | } 95 | } 96 | } 97 | 98 | dynamic "statement" { 99 | for_each = var.trusted_session_tagging.enabled ? ["go"] : [] 100 | 101 | content { 102 | sid = "TrustedTagSessionForIamEntities${each.key}" 103 | effect = "Allow" 104 | actions = ["sts:TagSession"] 105 | 106 | principals { 107 | type = "AWS" 108 | identifiers = each.value.iam_entities 109 | } 110 | 111 | dynamic "condition" { 112 | for_each = var.trusted_session_tagging.allowed_tags 113 | 114 | content { 115 | test = "StringLike" 116 | variable = "aws:RequestTag/${condition.key}" 117 | values = condition.value.values 118 | } 119 | } 120 | 121 | dynamic "condition" { 122 | for_each = length(var.trusted_session_tagging.allowed_transitive_tag_keys) > 0 ? ["go"] : [] 123 | 124 | content { 125 | test = "ForAllValues:StringLike" 126 | variable = "sts:TransitiveTagKeys" 127 | values = var.trusted_session_tagging.allowed_transitive_tag_keys 128 | } 129 | } 130 | } 131 | } 132 | 133 | dynamic "statement" { 134 | for_each = var.trusted_source_identity.enabled ? ["go"] : [] 135 | 136 | content { 137 | sid = "TrustedSourceIdentityForIamEntities${each.key}" 138 | effect = "Allow" 139 | actions = ["sts:SetSourceIdentity"] 140 | 141 | principals { 142 | type = "AWS" 143 | identifiers = each.value.iam_entities 144 | } 145 | 146 | dynamic "condition" { 147 | for_each = length(var.trusted_source_identity.allowed_identities) > 0 ? ["go"] : [] 148 | 149 | content { 150 | test = "StringLike" 151 | variable = "sts:SourceIdentity" 152 | values = var.trusted_source_identity.allowed_identities 153 | } 154 | } 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /modules/iam-oidc-identity-provider/README.md: -------------------------------------------------------------------------------- 1 | # iam-oidc-identity-provider 2 | 3 | This module creates following resources. 4 | 5 | - `aws_iam_openid_connect_provider` 6 | 7 | 8 | 9 | ## Requirements 10 | 11 | | Name | Version | 12 | |------|---------| 13 | | [terraform](#requirement\_terraform) | >= 1.12 | 14 | | [aws](#requirement\_aws) | >= 6.12 | 15 | | [tls](#requirement\_tls) | >= 4.1 | 16 | 17 | ## Providers 18 | 19 | | Name | Version | 20 | |------|---------| 21 | | [aws](#provider\_aws) | 6.12.0 | 22 | | [tls](#provider\_tls) | 4.1.0 | 23 | 24 | ## Modules 25 | 26 | | Name | Source | Version | 27 | |------|--------|---------| 28 | | [resource\_group](#module\_resource\_group) | tedilabs/misc/aws//modules/resource-group | ~> 0.12.0 | 29 | 30 | ## Resources 31 | 32 | | Name | Type | 33 | |------|------| 34 | | [aws_iam_openid_connect_provider.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_openid_connect_provider) | resource | 35 | | [tls_certificate.this](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/data-sources/certificate) | data source | 36 | 37 | ## Inputs 38 | 39 | | Name | Description | Type | Default | Required | 40 | |------|-------------|------|---------|:--------:| 41 | | [url](#input\_url) | (Required) The secure OpenID Connect URL for authentication requests. Correspond to the `iss` claim. Maximum 255 characters. URL must begin with `https://`. | `string` | n/a | yes | 42 | | [audiences](#input\_audiences) | (Optional) A list of audiences (also known as client IDs) for the IAM OIDC provider. When a mobile or web app registers with an OpenID Connect provider, they establish a value that identifies the application. This is the value that's sent as the `client_id` parameter on OAuth requests. Defaults to STS service(`sts.amazonaws.com`) if not values are provided. | `set(string)` |
[
"sts.amazonaws.com"
]
| no | 43 | | [auto\_thumbprint\_enabled](#input\_auto\_thumbprint\_enabled) | (Optional) Whether to automatically calculate thumbprint of the server certificate. | `bool` | `true` | no | 44 | | [module\_tags\_enabled](#input\_module\_tags\_enabled) | (Optional) Whether to create AWS Resource Tags for the module informations. | `bool` | `true` | no | 45 | | [resource\_group](#input\_resource\_group) | (Optional) A configurations of Resource Group for this module. `resource_group` as defined below.
(Optional) `enabled` - Whether to create Resource Group to find and group AWS resources which are created by this module. Defaults to `true`.
(Optional) `name` - The name of Resource Group. A Resource Group name can have a maximum of 127 characters, including letters, numbers, hyphens, dots, and underscores. The name cannot start with `AWS` or `aws`. If not provided, a name will be generated using the module name and instance name.
(Optional) `description` - The description of Resource Group. Defaults to `Managed by Terraform.`. |
object({
enabled = optional(bool, true)
name = optional(string, "")
description = optional(string, "Managed by Terraform.")
})
| `{}` | no | 46 | | [tags](#input\_tags) | (Optional) A map of tags to add to all resources. | `map(string)` | `{}` | no | 47 | | [thumbprints](#input\_thumbprints) | (Optional) A list of server certificate thumbprints for the OpenID Connect (OIDC) identity provider's server certificate(s). | `list(string)` | `[]` | no | 48 | 49 | ## Outputs 50 | 51 | | Name | Description | 52 | |------|-------------| 53 | | [arn](#output\_arn) | The ARN assigned by AWS for this provider. | 54 | | [audiences](#output\_audiences) | A list of audiences (also known as client IDs) for the IAM OIDC provider. | 55 | | [id](#output\_id) | The ID of this provider. | 56 | | [resource\_group](#output\_resource\_group) | The resource group created to manage resources in this module. | 57 | | [thumbprints](#output\_thumbprints) | A list of server certificate thumbprints for the OpenID Connect (OIDC) identity provider's server certificate(s). | 58 | | [url](#output\_url) | The URL of the identity provider. | 59 | | [urn](#output\_urn) | The URN of the identity provider. | 60 | 61 | -------------------------------------------------------------------------------- /modules/iam-role/trusted-oidc-provider-policies.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | oidc_provider_common_urls = [ 3 | "accounts.google.com", 4 | "cognito-identity.amazonaws.com", 5 | "graph.facebook.com", 6 | "www.amazon.com", 7 | ] 8 | oidc_provider_arn_prefix = "arn:${local.partition}:iam::${local.account_id}:oidc-provider/" 9 | } 10 | 11 | data "aws_iam_policy_document" "trusted_oidc_provider_policies" { 12 | for_each = { 13 | for idx, policy in var.trusted_oidc_provider_policies : 14 | idx => policy 15 | } 16 | 17 | statement { 18 | sid = "TrustedOIDC${each.key}" 19 | effect = "Allow" 20 | actions = ["sts:AssumeRoleWithWebIdentity"] 21 | 22 | principals { 23 | type = "Federated" 24 | identifiers = [ 25 | contains(local.oidc_provider_common_urls, each.value.url) 26 | ? each.value.url 27 | : "${local.oidc_provider_arn_prefix}${each.value.url}" 28 | ] 29 | } 30 | 31 | dynamic "condition" { 32 | for_each = var.conditions 33 | 34 | content { 35 | variable = condition.value.key 36 | test = condition.value.condition 37 | values = condition.value.values 38 | } 39 | } 40 | 41 | dynamic "condition" { 42 | for_each = each.value.conditions 43 | 44 | content { 45 | variable = "${each.value.url}:${condition.value.key}" 46 | test = condition.value.condition 47 | values = condition.value.values 48 | } 49 | } 50 | 51 | dynamic "condition" { 52 | for_each = each.value.effective_date != null ? ["go"] : [] 53 | 54 | content { 55 | test = "DateGreaterThan" 56 | variable = "aws:CurrentTime" 57 | values = [each.value.effective_date] 58 | } 59 | } 60 | 61 | dynamic "condition" { 62 | for_each = each.value.expiration_date != null ? ["go"] : [] 63 | 64 | content { 65 | test = "DateLessThan" 66 | variable = "aws:CurrentTime" 67 | values = [each.value.expiration_date] 68 | } 69 | } 70 | 71 | dynamic "condition" { 72 | for_each = length(each.value.source_ip_whitelist) > 0 ? ["go"] : [] 73 | 74 | content { 75 | test = "IpAddress" 76 | variable = "aws:SourceIp" 77 | values = each.value.source_ip_whitelist 78 | } 79 | } 80 | 81 | dynamic "condition" { 82 | for_each = length(each.value.source_ip_blacklist) > 0 ? ["go"] : [] 83 | 84 | content { 85 | test = "NotIpAddress" 86 | variable = "aws:SourceIp" 87 | values = each.value.source_ip_blacklist 88 | } 89 | } 90 | } 91 | 92 | dynamic "statement" { 93 | for_each = var.trusted_session_tagging.enabled ? ["go"] : [] 94 | 95 | content { 96 | sid = "TrustedTagSessionForOidcProvider${each.key}" 97 | effect = "Allow" 98 | actions = ["sts:TagSession"] 99 | 100 | principals { 101 | type = "Federated" 102 | identifiers = [ 103 | contains(local.oidc_provider_common_urls, each.value.url) 104 | ? each.value.url 105 | : "${local.oidc_provider_arn_prefix}${each.value.url}" 106 | ] 107 | } 108 | 109 | dynamic "condition" { 110 | for_each = var.trusted_session_tagging.allowed_tags 111 | 112 | content { 113 | test = "StringLike" 114 | variable = "aws:RequestTag/${condition.key}" 115 | values = condition.value.values 116 | } 117 | } 118 | 119 | dynamic "condition" { 120 | for_each = length(var.trusted_session_tagging.allowed_transitive_tag_keys) > 0 ? ["go"] : [] 121 | 122 | content { 123 | test = "ForAllValues:StringLike" 124 | variable = "sts:TransitiveTagKeys" 125 | values = var.trusted_session_tagging.allowed_transitive_tag_keys 126 | } 127 | } 128 | } 129 | } 130 | 131 | dynamic "statement" { 132 | for_each = var.trusted_source_identity.enabled ? ["go"] : [] 133 | 134 | content { 135 | sid = "TrustedSourceIdentityForOidcProvider${each.key}" 136 | effect = "Allow" 137 | actions = ["sts:SetSourceIdentity"] 138 | 139 | principals { 140 | type = "Federated" 141 | identifiers = [ 142 | contains(local.oidc_provider_common_urls, each.value.url) 143 | ? each.value.url 144 | : "${local.oidc_provider_arn_prefix}${each.value.url}" 145 | ] 146 | } 147 | 148 | dynamic "condition" { 149 | for_each = length(var.trusted_source_identity.allowed_identities) > 0 ? ["go"] : [] 150 | 151 | content { 152 | test = "StringLike" 153 | variable = "sts:SourceIdentity" 154 | values = var.trusted_source_identity.allowed_identities 155 | } 156 | } 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /modules/iam-user/outputs.tf: -------------------------------------------------------------------------------- 1 | output "arn" { 2 | description = "The ARN of the IAM user." 3 | value = aws_iam_user.this.arn 4 | } 5 | 6 | output "id" { 7 | description = "The ID of the IAM user." 8 | value = aws_iam_user.this.id 9 | } 10 | 11 | output "unique_id" { 12 | description = "The unique ID assigned by AWS." 13 | value = aws_iam_user.this.unique_id 14 | } 15 | 16 | output "name" { 17 | description = "The user's name." 18 | value = aws_iam_user.this.name 19 | } 20 | 21 | output "path" { 22 | description = "The path of the IAM user." 23 | value = aws_iam_user.this.path 24 | } 25 | 26 | output "groups" { 27 | description = "A set of IAM Groups which the user is a member of." 28 | value = aws_iam_user_group_membership.this.groups 29 | } 30 | 31 | output "pgp_key" { 32 | description = "PGP key used to encrypt sensitive data for this user (if empty - secrets are not encrypted)." 33 | value = var.pgp_key 34 | } 35 | 36 | # NOTE: terraform output encrypted_password | base64 --decode | keybase pgp decrypt 37 | output "console_access" { 38 | description = "The information of the AWS console access and password for the user." 39 | value = { 40 | enabled = var.console_access.enabled 41 | encrypted_password = one(aws_iam_user_login_profile.this[*].encrypted_password) 42 | } 43 | } 44 | 45 | # NOTE: terraform output encrypted_secret_access_key | base64 --decode | keybase pgp decrypt 46 | output "access_keys" { 47 | description = "The list of IAM Access Keys for the user." 48 | value = [ 49 | for access_key in aws_iam_access_key.this : { 50 | id = access_key.id 51 | encrypted_secret_access_key = access_key.encrypted_secret 52 | encrypted_ses_smtp_password = access_key.encrypted_ses_smtp_password_v4 53 | secret_access_key = access_key.secret 54 | ses_smtp_password = access_key.ses_smtp_password_v4 55 | created_at = access_key.create_date 56 | enabled = access_key.status == "Active" 57 | } 58 | ] 59 | } 60 | 61 | output "ssh_keys" { 62 | description = "The list of SSH public keys for the user." 63 | value = [ 64 | for ssh_key in aws_iam_user_ssh_key.this : { 65 | id = ssh_key.ssh_public_key_id 66 | 67 | encoding = ssh_key.encoding 68 | public_key = ssh_key.public_key 69 | fingerprint = ssh_key.fingerprint 70 | enabled = ssh_key.status == "Active" 71 | } 72 | ] 73 | } 74 | 75 | output "service_credentials" { 76 | description = "The list of service specific credentials for the user." 77 | value = { 78 | for service, credential in aws_iam_service_specific_credential.this : 79 | service => { 80 | id = credential.service_specific_credential_id 81 | username = credential.service_user_name 82 | password = credential.service_password 83 | enabled = credential.status == "Active" 84 | } 85 | } 86 | } 87 | 88 | output "signing_certificates" { 89 | description = "The list of X.509 signing certificates for the user." 90 | value = [ 91 | for id, certificate in aws_iam_signing_certificate.this : { 92 | id = certificate.certificate_id 93 | enabled = certificate.status == "Active" 94 | } 95 | ] 96 | } 97 | 98 | output "assumable_roles" { 99 | description = "A set of ARNs of IAM roles which IAM user can assume." 100 | value = toset(var.assumable_roles) 101 | } 102 | 103 | output "exclusive_policy_management_enabled" { 104 | description = "Whether exclusive policy management is enabled for the IAM user." 105 | value = var.exclusive_policy_management_enabled 106 | } 107 | 108 | output "exclusive_inline_policy_management_enabled" { 109 | description = "Whether exclusive inline policy management is enabled for the IAM user." 110 | value = var.exclusive_inline_policy_management_enabled 111 | } 112 | 113 | output "policies" { 114 | description = "A set of ARNs of IAM policies which are atached to IAM user." 115 | value = (var.exclusive_policy_management_enabled 116 | ? toset(aws_iam_user_policy_attachments_exclusive.this[0].policy_arns) 117 | : toset(values(aws_iam_user_policy_attachment.managed)[*].policy_arn) 118 | ) 119 | } 120 | 121 | output "inline_policies" { 122 | description = "A set of names of inline IAM polices which are attached to IAM user." 123 | value = toset(keys(var.inline_policies)) 124 | } 125 | 126 | output "resource_group" { 127 | description = "The resource group created to manage resources in this module." 128 | value = merge( 129 | { 130 | enabled = var.resource_group.enabled && var.module_tags_enabled 131 | }, 132 | (var.resource_group.enabled && var.module_tags_enabled 133 | ? { 134 | arn = module.resource_group[0].arn 135 | name = module.resource_group[0].name 136 | } 137 | : {} 138 | ) 139 | ) 140 | } 141 | -------------------------------------------------------------------------------- /modules/iam-group/README.md: -------------------------------------------------------------------------------- 1 | # iam-group 2 | 3 | This module creates following resources. 4 | 5 | - `aws_iam_group` 6 | - `aws_iam_group_policies_exclusive` (optional) 7 | - `aws_iam_group_policy` (optional) 8 | - `aws_iam_group_policy_attachment` (optional) 9 | - `aws_iam_group_policy_attachments_exclusive` (optional) 10 | 11 | 12 | ## Requirements 13 | 14 | | Name | Version | 15 | |------|---------| 16 | | [terraform](#requirement\_terraform) | >= 1.12 | 17 | | [aws](#requirement\_aws) | >= 6.12 | 18 | 19 | ## Providers 20 | 21 | | Name | Version | 22 | |------|---------| 23 | | [aws](#provider\_aws) | 6.12.0 | 24 | 25 | ## Modules 26 | 27 | No modules. 28 | 29 | ## Resources 30 | 31 | | Name | Type | 32 | |------|------| 33 | | [aws_iam_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group) | resource | 34 | | [aws_iam_group_policies_exclusive.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group_policies_exclusive) | resource | 35 | | [aws_iam_group_policy.assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group_policy) | resource | 36 | | [aws_iam_group_policy.inline](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group_policy) | resource | 37 | | [aws_iam_group_policy_attachment.managed](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group_policy_attachment) | resource | 38 | | [aws_iam_group_policy_attachments_exclusive.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_group_policy_attachments_exclusive) | resource | 39 | | [aws_iam_policy_document.assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 40 | 41 | ## Inputs 42 | 43 | | Name | Description | Type | Default | Required | 44 | |------|-------------|------|---------|:--------:| 45 | | [name](#input\_name) | (Required) Desired name for the IAM group. | `string` | n/a | yes | 46 | | [assumable\_roles](#input\_assumable\_roles) | (Optional) A set of IAM roles ARNs which can be assumed by the group. | `set(string)` | `[]` | no | 47 | | [exclusive\_inline\_policy\_management\_enabled](#input\_exclusive\_inline\_policy\_management\_enabled) | (Optional) Whether to enable exclusive management for inline IAM policies of the IAM user. This includes removal of inline IAM policies which are not explicitly configured. Defaults to `false`. | `bool` | `false` | no | 48 | | [exclusive\_policy\_management\_enabled](#input\_exclusive\_policy\_management\_enabled) | (Optional) Whether to enable exclusive management for managed IAM policies of the IAM group. This includes removal of managed IAM policies which are not explicitly configured. Defaults to `false`. | `bool` | `false` | no | 49 | | [inline\_policies](#input\_inline\_policies) | (Optional) A map of inline IAM policies to attach to IAM group. The policy is a JSON document that you attach to an identity to specify what API actions you're allowing or denying for that identity, and under which conditions. Each key is the name of the policy, and the value is the policy document. | `map(string)` | `{}` | no | 50 | | [path](#input\_path) | (Optional) Desired path for the IAM group. Defaults to `/`. | `string` | `"/"` | no | 51 | | [policies](#input\_policies) | (Optional) A set of IAM policies ARNs to attach to IAM group. | `set(string)` | `[]` | no | 52 | 53 | ## Outputs 54 | 55 | | Name | Description | 56 | |------|-------------| 57 | | [arn](#output\_arn) | The ARN of the IAM group. | 58 | | [assumable\_roles](#output\_assumable\_roles) | A set of ARNs of IAM roles which members of IAM group can assume. | 59 | | [exclusive\_inline\_policy\_management\_enabled](#output\_exclusive\_inline\_policy\_management\_enabled) | Whether exclusive inline policy management is enabled for the IAM group. | 60 | | [exclusive\_policy\_management\_enabled](#output\_exclusive\_policy\_management\_enabled) | Whether exclusive policy management is enabled for the IAM group. | 61 | | [id](#output\_id) | The ID of the IAM group. | 62 | | [inline\_policies](#output\_inline\_policies) | A set of names of inline IAM polices which are attached to IAM group. | 63 | | [name](#output\_name) | The IAM group name. | 64 | | [path](#output\_path) | The path of the IAM group. | 65 | | [policies](#output\_policies) | A set of ARNs of IAM policies which are atached to IAM group. | 66 | | [unique\_id](#output\_unique\_id) | The unique ID assigned by AWS. | 67 | 68 | -------------------------------------------------------------------------------- /modules/region/outputs.tf: -------------------------------------------------------------------------------- 1 | output "region" { 2 | description = "The AWS region this module resources resides in." 3 | value = var.region 4 | } 5 | 6 | output "id" { 7 | description = "The ID of the current region." 8 | value = data.aws_region.this.id 9 | } 10 | 11 | output "code" { 12 | description = "The short code of the current region." 13 | value = local.region_codes[data.aws_region.this.id] 14 | } 15 | 16 | output "name" { 17 | description = "The name of the current region." 18 | value = data.aws_region.this.region 19 | } 20 | 21 | output "description" { 22 | description = "The description of the current region in this format: `Location (Region name)`" 23 | value = data.aws_region.this.description 24 | } 25 | 26 | output "cloudwdatch" { 27 | description = < 0 72 | index_arn = one(aws_resourceexplorer2_index.this[*].arn) 73 | index_type = one(aws_resourceexplorer2_index.this[*].type) 74 | views = { 75 | for name, view in aws_resourceexplorer2_view.this : 76 | name => { 77 | arn = view.arn 78 | name = view.name 79 | is_default = view.default_view 80 | scope = view.scope 81 | filter_queries = view.filters[*].filter_string 82 | additional_resource_attributes = view.included_property[*].name 83 | } 84 | } 85 | } 86 | } 87 | 88 | output "service_quotas" { 89 | description = < { 96 | quota_code = quota.quota_code 97 | quota_name = quota.quota_name 98 | default_value = quota.default_value 99 | value = quota.value 100 | } 101 | } 102 | } 103 | } 104 | 105 | output "ses" { 106 | description = < group.opt_in_status == "opted-in" 123 | } 124 | block_public_access_mode = (one(aws_vpc_block_public_access_options.this) != null 125 | ? { 126 | for k, v in local.block_public_access_mode : 127 | v => k 128 | }[aws_vpc_block_public_access_options.this[0].internet_gateway_block_mode] 129 | : "OFF" 130 | ) 131 | } 132 | } 133 | 134 | output "resource_group" { 135 | description = "The resource group created to manage resources in this module." 136 | value = merge( 137 | { 138 | enabled = var.resource_group.enabled && var.module_tags_enabled 139 | }, 140 | (var.resource_group.enabled && var.module_tags_enabled 141 | ? { 142 | arn = module.resource_group[0].arn 143 | name = module.resource_group[0].name 144 | } 145 | : {} 146 | ) 147 | ) 148 | } 149 | -------------------------------------------------------------------------------- /modules/account/outputs.tf: -------------------------------------------------------------------------------- 1 | output "id" { 2 | description = "The AWS Account ID." 3 | value = data.aws_caller_identity.this.account_id 4 | } 5 | 6 | output "name" { 7 | description = "Name of the AWS account. The account alias." 8 | value = aws_iam_account_alias.this.account_alias 9 | } 10 | 11 | output "signin_url" { 12 | description = "The URL to signin for the AWS account." 13 | value = "https://${var.name}.signin.aws.amazon.com/console" 14 | } 15 | 16 | output "password_policy" { 17 | description = "Password Policy for the AWS Account. `expire_passwords` indicates whether passwords in the account expire. Returns `true` if `max_password_age` contains a value greater than 0." 18 | value = aws_iam_account_password_policy.this 19 | } 20 | 21 | output "additional_regions" { 22 | description = "A map of additional regions enabled in the account." 23 | value = toset([ 24 | for region, enabled in var.additional_regions : 25 | region 26 | if enabled 27 | ]) 28 | } 29 | 30 | output "all_available_regions" { 31 | description = "A set of all available regions in the account." 32 | value = local.all_available_regions 33 | } 34 | 35 | output "primary_contact" { 36 | description = "The primary contact attached to an AWS Account." 37 | value = try({ 38 | name = aws_account_primary_contact.this[0].full_name 39 | company_name = aws_account_primary_contact.this[0].company_name 40 | country_code = aws_account_primary_contact.this[0].country_code 41 | state = aws_account_primary_contact.this[0].state_or_region 42 | city = aws_account_primary_contact.this[0].city 43 | district = aws_account_primary_contact.this[0].district_or_county 44 | address_line_1 = aws_account_primary_contact.this[0].address_line_1 45 | address_line_2 = aws_account_primary_contact.this[0].address_line_2 46 | address_line_3 = aws_account_primary_contact.this[0].address_line_3 47 | postal_code = aws_account_primary_contact.this[0].postal_code 48 | phone = aws_account_primary_contact.this[0].phone_number 49 | website_url = aws_account_primary_contact.this[0].website_url 50 | }, null) 51 | } 52 | 53 | output "billing_contact" { 54 | description = "The billing contact attached to an AWS Account." 55 | value = try({ 56 | name = aws_account_alternate_contact.billing[0].name 57 | title = aws_account_alternate_contact.billing[0].title 58 | email = aws_account_alternate_contact.billing[0].email_address 59 | phone = aws_account_alternate_contact.billing[0].phone_number 60 | }, null) 61 | } 62 | 63 | output "operation_contact" { 64 | description = "The operation contact attached to an AWS Account." 65 | value = try({ 66 | name = aws_account_alternate_contact.operation[0].name 67 | title = aws_account_alternate_contact.operation[0].title 68 | email = aws_account_alternate_contact.operation[0].email_address 69 | phone = aws_account_alternate_contact.operation[0].phone_number 70 | }, null) 71 | } 72 | 73 | output "security_contact" { 74 | description = "The security contact attached to an AWS Account." 75 | value = try({ 76 | name = aws_account_alternate_contact.security[0].name 77 | title = aws_account_alternate_contact.security[0].title 78 | email = aws_account_alternate_contact.security[0].email_address 79 | phone = aws_account_alternate_contact.security[0].phone_number 80 | }, null) 81 | } 82 | 83 | output "cost" { 84 | description = < k 111 | }[aws_costoptimizationhub_preferences.this[0].savings_estimation_mode], null) 112 | } 113 | } 114 | 115 | output "ec2" { 116 | description = < k 140 | }[aws_iam_security_token_service_preferences.this.global_endpoint_token_version] 141 | } 142 | } 143 | 144 | output "support_app" { 145 | description = < { 155 | name = configuration.channel_name 156 | workspace = configuration.team_id 157 | channel = configuration.channel_id 158 | 159 | channel_role = { 160 | arn = configuration.channel_role_arn 161 | } 162 | 163 | notification_case_severity = upper(configuration.notify_on_case_severity) 164 | notification_on_add_correspondence_to_case = configuration.notify_on_add_correspondence_to_case 165 | notification_on_create_or_reopen_case = configuration.notify_on_create_or_reopen_case 166 | notification_on_resolve_case = configuration.notify_on_resolve_case 167 | } 168 | } 169 | } 170 | } 171 | 172 | output "s3" { 173 | description = </` and the value is a desired value to request. 134 | (Optional) `code_translation_enabled` - Whether to use translated quota code for readability. Defaults to `false`. 135 | EOF 136 | type = object({ 137 | requests = optional(map(number), {}) 138 | code_translation_enabled = optional(bool, false) 139 | }) 140 | default = {} 141 | nullable = false 142 | 143 | validation { 144 | condition = alltrue([ 145 | for code, quota in var.service_quotas.requests : 146 | length(split("/", code)) == 2 147 | ]) 148 | error_message = "Require valid service quota codes. The format is `/`." 149 | } 150 | } 151 | 152 | variable "ses" { 153 | description = <