├── main.tf ├── config-policies ├── efs-encrypted-check.tpl ├── access-keys-rotated.tpl ├── elb-logging-enabled.tpl ├── acm-certificate-expiration.tpl ├── ami-approved-tag.tpl ├── dynamodb_arn_encryption_list.tpl ├── ecs-no-environment-secrets.tpl ├── cloudwatch-log-retention.tpl ├── internet-gateway-authorized-vpc-only.tpl ├── cloudtrail-cloudwatch-logs-enabled.tpl ├── exclude-permission-boundary.tpl ├── s3_public_access_exclusion.tpl └── iam-password-policy.tpl ├── .terraform-docs.yml ├── examples ├── simple │ ├── outputs.tf │ ├── variables.tf │ └── main.tf ├── required-tags │ ├── outputs.tf │ ├── variables.tf │ └── main.tf ├── sns-topic │ ├── variables.tf │ └── main.tf └── encrypted-sns-topic │ ├── variables.tf │ └── main.tf ├── versions.tf ├── .gitignore ├── .markdownlintrc ├── .github └── workflows │ └── validate.yml ├── outputs.tf ├── .pre-commit-config.yaml ├── renovate.json ├── config-aggregator.tf ├── LICENSE ├── config-service.tf ├── iam.tf ├── variables.tf ├── README.md └── config-rules.tf /main.tf: -------------------------------------------------------------------------------- 1 | data "aws_partition" "current" {} 2 | -------------------------------------------------------------------------------- /config-policies/efs-encrypted-check.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "KmsKeyId": "${kms_key_id}" 3 | } 4 | -------------------------------------------------------------------------------- /.terraform-docs.yml: -------------------------------------------------------------------------------- 1 | settings: 2 | html: false 3 | anchor: false 4 | formatter: "markdown table" 5 | -------------------------------------------------------------------------------- /config-policies/access-keys-rotated.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "maxAccessKeyAge": "${access_key_max_age}" 3 | } 4 | -------------------------------------------------------------------------------- /config-policies/elb-logging-enabled.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "s3BucketNames": "${elb_logging_s3_buckets}" 3 | } 4 | -------------------------------------------------------------------------------- /config-policies/acm-certificate-expiration.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "daysToExpiration": "${acm_days_to_expiration}" 3 | } 4 | -------------------------------------------------------------------------------- /config-policies/ami-approved-tag.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "amisByTagKeyAndValue": "${ami_required_tag_key_value}" 3 | } 4 | -------------------------------------------------------------------------------- /config-policies/dynamodb_arn_encryption_list.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "kmsKeyArns": "${dynamodb_arn_encryption_list}" 3 | } 4 | -------------------------------------------------------------------------------- /config-policies/ecs-no-environment-secrets.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "secretKeys": "${ecs_no_environment_secrets}" 3 | } 4 | -------------------------------------------------------------------------------- /config-policies/cloudwatch-log-retention.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "MinRetentionTime": "${cw_loggroup_retention_period}" 3 | } 4 | -------------------------------------------------------------------------------- /config-policies/internet-gateway-authorized-vpc-only.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "AuthorizedVpcIds": "${authorized_vpc_ids}" 3 | } 4 | -------------------------------------------------------------------------------- /examples/simple/outputs.tf: -------------------------------------------------------------------------------- 1 | output "required_tags_rule_arn" { 2 | value = module.config.required_tags_rule_arn 3 | } 4 | -------------------------------------------------------------------------------- /examples/required-tags/outputs.tf: -------------------------------------------------------------------------------- 1 | output "required_tags_rule_arn" { 2 | value = module.config.required_tags_rule_arn 3 | } 4 | -------------------------------------------------------------------------------- /versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0" 3 | 4 | required_providers { 5 | aws = ">= 2.70" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .terraform 2 | terraform.tfstate 3 | terraform.tfstate.backup 4 | terraform.tfstate.*.backup 5 | 6 | # VSCode 7 | .vscode 8 | -------------------------------------------------------------------------------- /config-policies/cloudtrail-cloudwatch-logs-enabled.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "expectedDeliveryWindowAge": "${expected_delivery_window_age}" 3 | } 4 | -------------------------------------------------------------------------------- /config-policies/exclude-permission-boundary.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "excludePermissionBoundaryPolicy": "${exclude_permission_boundary}" 3 | } 4 | -------------------------------------------------------------------------------- /config-policies/s3_public_access_exclusion.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "excludedPublicBuckets": "${s3_bucket_public_access_prohibited_exclusion}" 3 | } 4 | -------------------------------------------------------------------------------- /.markdownlintrc: -------------------------------------------------------------------------------- 1 | { 2 | "default": true, 3 | "first-header-h1": false, 4 | "first-line-h1": false, 5 | "line_length": false, 6 | "no-multiple-blanks": false, 7 | "no-inline-html": false 8 | } 9 | -------------------------------------------------------------------------------- /examples/simple/variables.tf: -------------------------------------------------------------------------------- 1 | variable "config_name" { 2 | type = string 3 | } 4 | 5 | variable "config_logs_bucket" { 6 | type = string 7 | } 8 | 9 | variable "region" { 10 | type = string 11 | } 12 | -------------------------------------------------------------------------------- /examples/sns-topic/variables.tf: -------------------------------------------------------------------------------- 1 | variable "config_name" { 2 | type = string 3 | } 4 | 5 | variable "config_logs_bucket" { 6 | type = string 7 | } 8 | 9 | variable "region" { 10 | type = string 11 | } 12 | -------------------------------------------------------------------------------- /examples/required-tags/variables.tf: -------------------------------------------------------------------------------- 1 | variable "config_logs_bucket" { 2 | type = string 3 | } 4 | 5 | variable "region" { 6 | type = string 7 | } 8 | 9 | variable "config_name" { 10 | type = string 11 | } 12 | -------------------------------------------------------------------------------- /examples/encrypted-sns-topic/variables.tf: -------------------------------------------------------------------------------- 1 | variable "config_name" { 2 | type = string 3 | } 4 | 5 | variable "config_logs_bucket" { 6 | type = string 7 | } 8 | 9 | variable "region" { 10 | type = string 11 | } 12 | -------------------------------------------------------------------------------- /.github/workflows/validate.yml: -------------------------------------------------------------------------------- 1 | name: validate-tf 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | validate-tf: 13 | uses: trussworks/shared-actions/.github/workflows/validate-tf.yml@main 14 | -------------------------------------------------------------------------------- /config-policies/iam-password-policy.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "RequireUppercaseCharacters": "${password_require_uppercase}", 3 | "RequireLowercaseCharacters": "${password_require_lowercase}", 4 | "RequireSymbols": "${password_require_symbols}", 5 | "RequireNumbers": "${password_require_numbers}", 6 | "MinimumPasswordLength": "${password_min_length}", 7 | "PasswordReusePrevention": "${password_reuse_prevention}", 8 | "MaxPasswordAge": "${password_max_age}" 9 | } 10 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | output "required_tags_rule_arn" { 2 | description = "The ARN of the required-tags config rule." 3 | value = concat(aws_config_config_rule.required-tags.*.arn, [""])[0] 4 | } 5 | 6 | output "aws_config_role_arn" { 7 | description = "The ARN of the AWS config role." 8 | value = concat(aws_iam_role.main.*.arn, [""])[0] 9 | } 10 | 11 | output "aws_config_role_name" { 12 | description = "The name of the IAM role used by AWS config" 13 | value = concat(aws_iam_role.main.*.name, [""])[0] 14 | } 15 | -------------------------------------------------------------------------------- /examples/simple/main.tf: -------------------------------------------------------------------------------- 1 | # 2 | # AWS Config Logs Bucket 3 | # 4 | 5 | module "config_logs" { 6 | source = "trussworks/logs/aws" 7 | version = "~> 10" 8 | 9 | s3_bucket_name = var.config_logs_bucket 10 | allow_config = true 11 | config_logs_prefix = "config" 12 | force_destroy = true 13 | } 14 | 15 | module "config" { 16 | source = "../../" 17 | 18 | config_name = var.config_name 19 | config_logs_bucket = module.config_logs.aws_logs_bucket 20 | config_logs_prefix = "config" 21 | 22 | tags = { 23 | "Automation" = "Terraform" 24 | "Name" = var.config_name 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/required-tags/main.tf: -------------------------------------------------------------------------------- 1 | # 2 | # AWS Config Logs Bucket with Required Tags 3 | # 4 | 5 | module "config_logs" { 6 | source = "trussworks/logs/aws" 7 | version = "~> 10" 8 | 9 | s3_bucket_name = var.config_logs_bucket 10 | allow_config = true 11 | config_logs_prefix = "config" 12 | force_destroy = true 13 | } 14 | 15 | module "config" { 16 | source = "../../" 17 | 18 | config_name = var.config_name 19 | config_logs_bucket = module.config_logs.aws_logs_bucket 20 | config_logs_prefix = "config" 21 | 22 | check_required_tags = true 23 | required_tags_resource_types = ["S3::Bucket"] 24 | required_tags = { 25 | tag1Key = "Automation" 26 | tag1Value = "Terraform" 27 | tag2Key = "Environment" 28 | tag3Value = "Terratest" 29 | } 30 | 31 | tags = { 32 | "Automation" = "Terraform" 33 | "Name" = var.config_name 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.4.0 4 | hooks: 5 | - id: check-json 6 | - id: check-merge-conflict 7 | - id: check-yaml 8 | - id: detect-private-key 9 | - id: pretty-format-json 10 | args: 11 | - --autofix 12 | - id: trailing-whitespace 13 | - id: check-symlinks 14 | - id: end-of-file-fixer 15 | - id: mixed-line-ending 16 | 17 | - repo: https://github.com/executablebooks/mdformat 18 | rev: 0.7.16 19 | hooks: 20 | - id: mdformat 21 | additional_dependencies: 22 | - mdformat-gfm 23 | - mdformat-toc 24 | # mdformat fights with terraform_docs 25 | exclude: README.m(ark)?d(own)? 26 | 27 | - repo: https://github.com/igorshubovych/markdownlint-cli 28 | rev: v0.33.0 29 | hooks: 30 | - id: markdownlint 31 | 32 | - repo: https://github.com/detailyang/pre-commit-shell 33 | rev: 1.0.5 34 | hooks: 35 | - id: shell-lint 36 | 37 | - repo: https://github.com/antonbabenko/pre-commit-terraform 38 | rev: v1.77.1 39 | hooks: 40 | - id: terraform_fmt 41 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base", 4 | ":disableDependencyDashboard" 5 | ], 6 | "labels": [ 7 | "dependencies" 8 | ], 9 | "packageRules": [ 10 | { 11 | "automerge": true, 12 | "description": "Automerge all updates except major versions", 13 | "matchUpdateTypes": [ 14 | "patch", 15 | "pin", 16 | "digest", 17 | "minor" 18 | ] 19 | }, 20 | { 21 | "description": "Tag the waddlers Github Team for major updates", 22 | "matchUpdateTypes": [ 23 | "major" 24 | ], 25 | "reviewers": [ 26 | "team:waddlers" 27 | ] 28 | }, 29 | { 30 | "automerge": true, 31 | "description": "Group minor and patch updates into a single PR", 32 | "groupName": "dependencies", 33 | "managers": [ 34 | "terraform", 35 | "pre-commit", 36 | "dockerfile", 37 | "github-actions" 38 | ], 39 | "matchUpdateTypes": [ 40 | "minor", 41 | "patch" 42 | ] 43 | } 44 | ], 45 | "schedule": [ 46 | "every weekend" 47 | ], 48 | "separateMinorPatch": true, 49 | "timezone": "America/Los_Angeles" 50 | } 51 | -------------------------------------------------------------------------------- /examples/sns-topic/main.tf: -------------------------------------------------------------------------------- 1 | data "aws_partition" "current" {} 2 | 3 | # 4 | # AWS Config Logs Bucket 5 | # 6 | 7 | module "config_logs" { 8 | source = "trussworks/logs/aws" 9 | version = "~> 10" 10 | 11 | s3_bucket_name = var.config_logs_bucket 12 | allow_config = true 13 | config_logs_prefix = "config" 14 | force_destroy = true 15 | } 16 | 17 | # 18 | # SNS Topic 19 | # 20 | 21 | data "aws_iam_policy_document" "config" { 22 | statement { 23 | effect = "Allow" 24 | principals { 25 | type = "AWS" 26 | identifiers = [module.config.aws_config_role_arn] 27 | } 28 | actions = ["SNS:Publish"] 29 | resources = [aws_sns_topic.config.arn] 30 | } 31 | } 32 | 33 | resource "aws_sns_topic" "config" { 34 | name = var.config_name 35 | } 36 | 37 | resource "aws_sns_topic_policy" "config" { 38 | arn = aws_sns_topic.config.arn 39 | policy = data.aws_iam_policy_document.config.json 40 | } 41 | 42 | # 43 | # AWS Config 44 | # 45 | 46 | module "config" { 47 | source = "../../" 48 | 49 | config_name = var.config_name 50 | config_logs_bucket = module.config_logs.aws_logs_bucket 51 | config_logs_prefix = "config" 52 | config_sns_topic_arn = aws_sns_topic.config.arn 53 | 54 | tags = { 55 | "Automation" = "Terraform" 56 | "Name" = var.config_name 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /config-aggregator.tf: -------------------------------------------------------------------------------- 1 | # 2 | # IAM Role 3 | # 4 | 5 | data "aws_iam_policy_document" "aws_config_aggregator_role_policy" { 6 | statement { 7 | actions = ["sts:AssumeRole"] 8 | 9 | principals { 10 | type = "Service" 11 | identifiers = ["config.amazonaws.com"] 12 | } 13 | 14 | effect = "Allow" 15 | } 16 | } 17 | 18 | resource "aws_iam_role" "aggregator" { 19 | count = var.aggregate_organization ? 1 : 0 20 | name = "${var.config_name}-aggregator-role" 21 | assume_role_policy = data.aws_iam_policy_document.aws_config_aggregator_role_policy.json 22 | permissions_boundary = var.config_role_permissions_boundary 23 | tags = var.tags 24 | } 25 | 26 | resource "aws_iam_role_policy_attachment" "aggregator" { 27 | count = var.aggregate_organization ? 1 : 0 28 | role = aws_iam_role.aggregator[0].name 29 | policy_arn = format("arn:%s:iam::aws:policy/service-role/AWSConfigRoleForOrganizations", data.aws_partition.current.partition) 30 | } 31 | 32 | # 33 | # Configuration Aggregator 34 | # 35 | 36 | resource "aws_config_configuration_aggregator" "organization" { 37 | count = var.aggregate_organization ? 1 : 0 38 | depends_on = [aws_iam_role_policy_attachment.aggregator] 39 | name = var.config_aggregator_name 40 | 41 | organization_aggregation_source { 42 | all_regions = true 43 | role_arn = aws_iam_role.aggregator[0].arn 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, TrussWorks, Inc. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /examples/encrypted-sns-topic/main.tf: -------------------------------------------------------------------------------- 1 | data "aws_partition" "current" {} 2 | 3 | # 4 | # AWS Config Logs Bucket 5 | # 6 | 7 | module "config_logs" { 8 | source = "trussworks/logs/aws" 9 | version = "~> 10" 10 | 11 | s3_bucket_name = var.config_logs_bucket 12 | allow_config = true 13 | config_logs_prefix = "config" 14 | force_destroy = true 15 | } 16 | 17 | # 18 | # SNS Topic 19 | # 20 | 21 | data "aws_iam_policy_document" "config" { 22 | statement { 23 | effect = "Allow" 24 | principals { 25 | type = "AWS" 26 | identifiers = [module.config.aws_config_role_arn] 27 | } 28 | actions = ["SNS:Publish"] 29 | resources = [aws_sns_topic.config.arn] 30 | } 31 | } 32 | 33 | resource "aws_sns_topic" "config" { 34 | name = var.config_name 35 | kms_master_key_id = module.sns_key.key_arn 36 | } 37 | 38 | resource "aws_sns_topic_policy" "config" { 39 | arn = aws_sns_topic.config.arn 40 | policy = data.aws_iam_policy_document.config.json 41 | } 42 | 43 | # 44 | # KMS Key for SNS 45 | # 46 | module "sns_key" { 47 | source = "terraform-aws-modules/kms/aws" 48 | version = "~> 1.5.0" 49 | description = "Key for SNS usage" 50 | key_usage = "ENCRYPT_DECRYPT" 51 | 52 | # Policy 53 | key_users = [module.config.aws_config_role_arn] 54 | 55 | # Aliases 56 | aliases = ["theydo/sns"] 57 | } 58 | 59 | # 60 | # AWS Config 61 | # 62 | 63 | module "config" { 64 | source = "../../" 65 | 66 | config_name = var.config_name 67 | config_logs_bucket = module.config_logs.aws_logs_bucket 68 | config_logs_prefix = "config" 69 | config_sns_topic_arn = aws_sns_topic.config.arn 70 | sns_kms_key_id = module.sns_key.key_arn 71 | 72 | tags = { 73 | "Automation" = "Terraform" 74 | "Name" = var.config_name 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /config-service.tf: -------------------------------------------------------------------------------- 1 | # 2 | # AWS Config Service 3 | # 4 | 5 | resource "aws_config_configuration_recorder_status" "main" { 6 | count = var.enable_config_recorder ? 1 : 0 7 | 8 | name = var.config_name 9 | is_enabled = true 10 | depends_on = [aws_config_delivery_channel.main] 11 | } 12 | 13 | resource "aws_config_delivery_channel" "main" { 14 | count = var.enable_config_recorder ? 1 : 0 15 | 16 | name = var.config_name 17 | s3_bucket_name = var.config_logs_bucket 18 | s3_key_prefix = var.config_logs_prefix 19 | s3_kms_key_arn = var.config_logs_bucket_kms_key_arn 20 | sns_topic_arn = var.config_sns_topic_arn 21 | 22 | snapshot_delivery_properties { 23 | delivery_frequency = var.config_delivery_frequency 24 | } 25 | 26 | depends_on = [aws_config_configuration_recorder.main] 27 | } 28 | 29 | resource "aws_config_configuration_recorder" "main" { 30 | count = var.enable_config_recorder ? 1 : 0 31 | 32 | name = var.config_name 33 | role_arn = aws_iam_role.main[count.index].arn 34 | 35 | recording_group { 36 | all_supported = length(var.resource_types) == 0 ? true : false 37 | include_global_resource_types = length(var.resource_types) == 0 ? var.include_global_resource_types : null 38 | resource_types = length(var.resource_types) == 0 ? null : var.resource_types 39 | } 40 | 41 | recording_mode { 42 | recording_frequency = var.config_recording_frequency 43 | 44 | dynamic "recording_mode_override" { 45 | for_each = var.config_recording_frequency_overrides 46 | 47 | content { 48 | description = recording_mode_override.value.description 49 | resource_types = recording_mode_override.value.resource_types 50 | recording_frequency = recording_mode_override.value.recording_frequency 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /iam.tf: -------------------------------------------------------------------------------- 1 | # Get the access to the effective Account ID in which Terraform is working. 2 | data "aws_caller_identity" "current" { 3 | } 4 | 5 | # Allow the AWS Config role to deliver logs to configured S3 Bucket. 6 | # Derived from IAM Policy document found at https://docs.aws.amazon.com/config/latest/developerguide/s3-bucket-policy.html 7 | 8 | data "aws_iam_policy_document" "aws_config_policy" { 9 | statement { 10 | sid = "AWSConfigBucketPermissionsCheck" 11 | effect = "Allow" 12 | actions = [ 13 | "s3:GetBucketAcl", 14 | ] 15 | resources = [ 16 | format("arn:%s:s3:::%s", data.aws_partition.current.partition, var.config_logs_bucket) 17 | ] 18 | } 19 | 20 | dynamic "statement" { 21 | for_each = var.sns_kms_key_id != null ? [1] : [] 22 | content { 23 | sid = "AWSAllowKMSKeyUsage" 24 | effect = "Allow" 25 | actions = [ 26 | "kms:Decrypt", 27 | "kms:GenerateDataKey*" 28 | ] 29 | resources = [var.sns_kms_key_id] 30 | } 31 | } 32 | 33 | dynamic "statement" { 34 | for_each = var.sns_kms_key_id != null ? [1] : [] 35 | content { 36 | sid = "AWSAllowSNSPublish" 37 | effect = "Allow" 38 | actions = [ 39 | "sns:Publish" 40 | ] 41 | resources = [var.config_sns_topic_arn] 42 | } 43 | } 44 | 45 | statement { 46 | sid = "AWSConfigBucketExistenceCheck" 47 | effect = "Allow" 48 | actions = [ 49 | "s3:ListBucket" 50 | ] 51 | resources = [ 52 | format("arn:%s:s3:::%s", data.aws_partition.current.partition, var.config_logs_bucket) 53 | ] 54 | } 55 | statement { 56 | sid = "AWSConfigBucketDelivery" 57 | effect = "Allow" 58 | actions = ["s3:PutObject"] 59 | resources = [ 60 | format("arn:%s:s3:::%s%s%s/AWSLogs/%s/Config/*", 61 | data.aws_partition.current.partition, 62 | var.config_logs_bucket, 63 | var.config_logs_prefix == "" ? "" : "/", 64 | var.config_logs_prefix, 65 | var.enable_multi_account_logs ? "*" : data.aws_caller_identity.current.account_id 66 | ) 67 | ] 68 | condition { 69 | test = "StringLike" 70 | variable = "s3:x-amz-acl" 71 | values = ["bucket-owner-full-control"] 72 | } 73 | } 74 | } 75 | 76 | # Allow IAM policy to assume the role for AWS Config 77 | data "aws_iam_policy_document" "aws-config-role-policy" { 78 | statement { 79 | actions = ["sts:AssumeRole"] 80 | 81 | principals { 82 | type = "Service" 83 | identifiers = ["config.amazonaws.com"] 84 | } 85 | 86 | effect = "Allow" 87 | } 88 | } 89 | 90 | # 91 | # IAM 92 | # 93 | 94 | resource "aws_iam_role" "main" { 95 | count = var.enable_config_recorder ? 1 : 0 96 | name = "${var.config_name}-role" 97 | assume_role_policy = data.aws_iam_policy_document.aws-config-role-policy.json 98 | permissions_boundary = var.config_role_permissions_boundary 99 | tags = var.tags 100 | } 101 | 102 | resource "aws_iam_role_policy_attachment" "managed-policy" { 103 | count = var.enable_config_recorder ? 1 : 0 104 | 105 | role = aws_iam_role.main[count.index].name 106 | policy_arn = format("arn:%s:iam::aws:policy/service-role/AWS_ConfigRole", data.aws_partition.current.partition) 107 | } 108 | 109 | resource "aws_iam_policy" "aws-config-policy" { 110 | count = var.enable_config_recorder ? 1 : 0 111 | 112 | name = "${var.config_name}-policy" 113 | policy = data.aws_iam_policy_document.aws_config_policy.json 114 | } 115 | 116 | resource "aws_iam_role_policy_attachment" "aws-config-policy" { 117 | count = var.enable_config_recorder ? 1 : 0 118 | 119 | role = aws_iam_role.main[count.index].name 120 | policy_arn = aws_iam_policy.aws-config-policy[0].arn 121 | } 122 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "access_key_max_age" { 2 | description = "Maximum number of days without rotation." 3 | type = number 4 | default = 90 5 | } 6 | 7 | variable "acm_days_to_expiration" { 8 | description = "Specify the number of days before the rule flags the ACM Certificate as noncompliant." 9 | type = number 10 | default = 14 11 | } 12 | 13 | variable "aggregate_organization" { 14 | description = "Aggregate compliance data by organization" 15 | type = bool 16 | default = false 17 | } 18 | 19 | variable "ami_required_tag_key_value" { 20 | description = "Tag/s key and value which AMI has to have in order to be compliant: Example: key1:value1,key2:value2" 21 | type = string 22 | default = "" 23 | } 24 | 25 | variable "authorized_vpc_ids" { 26 | description = "Comma-separated list of the authorized VPC IDs with attached IGWs. If parameter is not provided all attached IGWs will be NON_COMPLIANT." 27 | type = string 28 | default = "example,CSV" 29 | } 30 | 31 | variable "check_access_keys_rotated" { 32 | description = "Enable access-keys-rotated rule" 33 | type = bool 34 | default = true 35 | } 36 | 37 | variable "check_acm_certificate_expiration_check" { 38 | description = "Enable acm-certificate-expiration-check rule" 39 | type = bool 40 | default = true 41 | } 42 | 43 | variable "check_approved_amis_by_tag" { 44 | description = "Enable approved-amis-by-tag rule" 45 | type = bool 46 | default = false 47 | } 48 | 49 | variable "check_cloud_trail_encryption" { 50 | description = "Enable cloud-trail-encryption-enabled rule" 51 | type = bool 52 | default = false 53 | } 54 | 55 | variable "check_cloud_trail_log_file_validation" { 56 | description = "Enable cloud-trail-log-file-validation-enabled rule" 57 | type = bool 58 | default = false 59 | } 60 | 61 | variable "check_cloudtrail_enabled" { 62 | description = "Enable cloudtrail-enabled rule" 63 | type = bool 64 | default = true 65 | } 66 | 67 | variable "check_cloudwatch_log_group_encrypted" { 68 | description = "Enable cloudwatch-log-group-encryption rule" 69 | type = bool 70 | default = true 71 | } 72 | 73 | variable "check_cmk_backing_key_rotated" { 74 | description = "Enable cmk_backing_key_rotation_enabled rule" 75 | type = bool 76 | default = true 77 | } 78 | 79 | variable "check_cw_loggroup_retention_period" { 80 | description = "Enable cloudwatch-log-group-retention-period-check rule" 81 | type = bool 82 | default = false 83 | } 84 | 85 | variable "check_db_instance_backup_enabled" { 86 | description = "Enable db-instance-backup-enabled rule" 87 | type = bool 88 | default = false 89 | } 90 | 91 | variable "check_dynamodb_table_encrypted_kms" { 92 | description = "Enable dynamodb-table-encrypted-kms rule" 93 | type = bool 94 | default = false 95 | } 96 | 97 | variable "check_dynamodb_table_encryption_enabled" { 98 | description = "Enable checkdynamodb-table-encryption-enabled rule" 99 | type = bool 100 | default = true 101 | } 102 | 103 | variable "check_ebs_optimized_instance" { 104 | description = "Enable ebs-optimized-instance-check rule" 105 | type = bool 106 | default = false 107 | } 108 | 109 | variable "check_ebs_snapshot_public_restorable" { 110 | description = "Enable ebs-snapshot-public-restorable rule" 111 | type = bool 112 | default = true 113 | } 114 | 115 | variable "check_ec2_encrypted_volumes" { 116 | description = "Enable ec2-encrypted-volumes rule" 117 | type = bool 118 | default = true 119 | } 120 | 121 | variable "check_ec2_imdsv2" { 122 | description = "Enable IMDSv2 rule" 123 | type = bool 124 | default = false 125 | } 126 | 127 | variable "check_ec2_volume_inuse_check" { 128 | description = "Enable ec2-volume-inuse-check rule" 129 | type = bool 130 | default = true 131 | } 132 | 133 | variable "check_ecr_private_image_scanning_enabled" { 134 | description = "Enable ecr-private-image-scanning-enabled rule" 135 | type = bool 136 | default = true 137 | } 138 | 139 | variable "check_ecr_private_lifecycle_policy_configured" { 140 | description = "Enable ecr-private-lifecycle-policy-configured rule" 141 | type = bool 142 | default = true 143 | } 144 | 145 | variable "check_ecs_awsvpc_networking_enabled" { 146 | description = "Enable ecs-awsvpc-networking-enabled rule" 147 | type = bool 148 | default = true 149 | } 150 | 151 | variable "check_ecs_containers_nonprivileged" { 152 | description = "Enable ecs-containers-nonprivileged rule" 153 | type = bool 154 | default = true 155 | } 156 | 157 | variable "check_ecs_containers_readonly_access" { 158 | description = "Enable ecs-containers-readonly-access rule" 159 | type = bool 160 | default = true 161 | } 162 | 163 | variable "check_ecs_no_environment_secrets" { 164 | description = "Enable ecs-no-environment-secrets rule" 165 | type = bool 166 | default = false 167 | } 168 | 169 | variable "check_eip_attached" { 170 | description = "Enable eip-attached rule" 171 | type = bool 172 | default = false 173 | } 174 | 175 | variable "check_elb_deletion_protection_enabled" { 176 | description = "Enable elb-deletion-protection-enabled rule" 177 | type = bool 178 | default = true 179 | } 180 | 181 | variable "check_elb_logging_enabled" { 182 | description = "Enable elb-logging-enabled rule" 183 | type = bool 184 | default = false 185 | } 186 | 187 | variable "check_guard_duty" { 188 | description = "Enable guardduty-enabled-centralized rule" 189 | type = bool 190 | default = false 191 | } 192 | 193 | variable "check_iam_group_has_users_check" { 194 | description = "Enable iam-group-has-users-check rule" 195 | type = bool 196 | default = true 197 | } 198 | 199 | variable "check_iam_password_policy" { 200 | description = "Enable iam-password-policy rule" 201 | type = bool 202 | default = true 203 | } 204 | 205 | variable "check_iam_policy_no_statements_with_admin_access" { 206 | description = "Enable iam-policy-no-statements-with-admin-access rule" 207 | type = bool 208 | default = true 209 | } 210 | 211 | variable "check_iam_policy_no_statements_with_full_access" { 212 | description = "Enable iam-policy-no-statements-with-full-access rule" 213 | type = bool 214 | default = true 215 | } 216 | 217 | variable "check_iam_root_access_key" { 218 | description = "Enable iam-root-access-key rule" 219 | type = bool 220 | default = true 221 | } 222 | 223 | variable "check_iam_user_no_policies_check" { 224 | description = "Enable iam-user-no-policies-check rule" 225 | type = bool 226 | default = true 227 | } 228 | 229 | variable "check_internet_gateway_authorized_vpc_only" { 230 | description = "Enable internet-gateway-authorized-vpc-only rule" 231 | type = bool 232 | default = false 233 | } 234 | 235 | variable "check_mfa_enabled_for_iam_console_access" { 236 | description = "Enable mfa-enabled-for-iam-console-access rule" 237 | type = bool 238 | default = true 239 | } 240 | 241 | variable "check_multi_region_cloud_trail" { 242 | description = "Enable multi-region-cloud-trail-enabled rule" 243 | type = bool 244 | default = false 245 | } 246 | 247 | variable "check_nacl_no_unrestricted_ssh_rdp" { 248 | description = "Enable nacl-no-unrestricted-ssh-rdp rule" 249 | type = bool 250 | default = true 251 | } 252 | 253 | variable "check_rds_cluster_deletion_protection_enabled" { 254 | description = "Enable rds-cluster-deletion-protection-enabled rule" 255 | type = bool 256 | default = true 257 | } 258 | 259 | variable "check_rds_public_access" { 260 | description = "Enable rds-instance-public-access-check rule" 261 | type = bool 262 | default = false 263 | } 264 | 265 | variable "check_rds_snapshot_encrypted" { 266 | description = "Enable rds-snapshot-encrypted rule" 267 | type = bool 268 | default = true 269 | } 270 | 271 | variable "check_rds_snapshots_public_prohibited" { 272 | description = "Enable rds-snapshots-public-prohibited rule" 273 | type = bool 274 | default = true 275 | } 276 | 277 | variable "check_rds_storage_encrypted" { 278 | description = "Enable rds-storage-encrypted rule" 279 | type = bool 280 | default = true 281 | } 282 | 283 | variable "check_required_tags" { 284 | description = "Enable required-tags rule" 285 | type = bool 286 | default = false 287 | } 288 | 289 | variable "check_restricted_common_ports" { 290 | description = "Enable restricted-common-ports-check" 291 | type = bool 292 | default = false 293 | } 294 | 295 | variable "check_restricted_ssh" { 296 | description = "Enable restricted-ssh rule" 297 | type = bool 298 | default = true 299 | } 300 | 301 | variable "check_root_account_mfa_enabled" { 302 | description = "Enable root-account-mfa-enabled rule" 303 | type = bool 304 | default = false 305 | } 306 | 307 | variable "check_s3_bucket_acl_prohibited" { 308 | description = "Enable s3-bucket-acl-prohibited rule" 309 | type = bool 310 | default = true 311 | } 312 | 313 | variable "check_s3_bucket_level_public_access_prohibited" { 314 | description = "Enable s3-bucket-level-public-access-prohibited rule" 315 | type = bool 316 | default = false 317 | } 318 | 319 | variable "check_s3_bucket_public_read_prohibited" { 320 | description = "Enable s3-bucket-public-read-prohibited rule" 321 | type = bool 322 | default = false 323 | } 324 | 325 | variable "check_s3_bucket_public_write_prohibited" { 326 | description = "Enable s3-bucket-public-write-prohibited rule" 327 | type = bool 328 | default = true 329 | } 330 | 331 | variable "check_s3_bucket_ssl_requests_only" { 332 | description = "Enable s3-bucket-ssl-requests-only rule" 333 | type = bool 334 | default = true 335 | } 336 | 337 | variable "check_vpc_default_security_group_closed" { 338 | description = "Enable vpc-default-security-group-closed rule" 339 | type = bool 340 | default = true 341 | } 342 | 343 | variable "check_vpc_sg_open_only_to_authorized_ports" { 344 | description = "Enable vpc-sg-open-only-to-authorized-ports rule" 345 | type = bool 346 | default = false 347 | } 348 | 349 | variable "cloud_trail_cloud_watch_logs_enabled" { 350 | description = "Enable cloud_trail_cloud_watch_logs_enabled rule" 351 | type = bool 352 | default = true 353 | } 354 | 355 | variable "config_aggregator_name" { 356 | description = "The name of the aggregator." 357 | type = string 358 | default = "organization" 359 | } 360 | 361 | variable "config_delivery_frequency" { 362 | description = "The frequency with which AWS Config delivers configuration snapshots." 363 | default = "Six_Hours" 364 | type = string 365 | } 366 | 367 | variable "config_logs_bucket" { 368 | description = "The S3 bucket for AWS Config logs. If you have set enable_config_recorder to false then this can be an empty string." 369 | type = string 370 | } 371 | 372 | variable "config_logs_bucket_kms_key_arn" { 373 | description = "The ARN of the AWS KMS key used to encrypt objects delivered by AWS Config. Must belong to the same Region as the destination S3 bucket." 374 | type = string 375 | default = null 376 | } 377 | 378 | variable "config_logs_prefix" { 379 | description = "The S3 prefix for AWS Config logs." 380 | type = string 381 | default = "config" 382 | } 383 | 384 | variable "config_max_execution_frequency" { 385 | description = "The maximum frequency with which AWS Config runs evaluations for a rule." 386 | type = string 387 | default = "TwentyFour_Hours" 388 | } 389 | 390 | variable "config_name" { 391 | description = "The name of the AWS Config instance." 392 | type = string 393 | default = "aws-config" 394 | } 395 | 396 | variable "config_recording_frequency" { 397 | description = "Default recording frequency for the AWS Config" 398 | type = string 399 | default = "CONTINUOUS" 400 | } 401 | 402 | variable "config_recording_frequency_overrides" { 403 | description = "Specific overrides of the recording frequency for the AWS Config" 404 | type = set(object({ 405 | description = optional(string, null) 406 | resource_types = list(string) 407 | recording_frequency = string 408 | })) 409 | default = [] 410 | } 411 | 412 | variable "config_role_permissions_boundary" { 413 | description = "The ARN of the permissions boundary to apply to IAM roles created for AWS Config" 414 | type = string 415 | default = null 416 | } 417 | 418 | variable "config_sns_topic_arn" { 419 | description = "An SNS topic to stream configuration changes and notifications to." 420 | type = string 421 | default = null 422 | } 423 | 424 | variable "cw_loggroup_retention_period" { 425 | description = "Retention period for cloudwatch logs in number of days" 426 | type = number 427 | default = 3653 428 | } 429 | 430 | variable "dynamodb_arn_encryption_list" { 431 | description = "Comma separated list of AWS KMS key ARNs allowed for encrypting Amazon DynamoDB Tables." 432 | type = string 433 | default = "example,CSV" 434 | } 435 | 436 | variable "ecs_no_environment_secrets" { 437 | description = "Comma-separated list of key names to search for in the environment variables of container definitions within Task Definitions. Extra spaces will be removed." 438 | type = string 439 | default = "example,CSV" 440 | } 441 | 442 | variable "elb_logging_s3_buckets" { 443 | description = "Comma-separated list of Amazon S3 bucket names for Amazon ELB to deliver the log files." 444 | type = string 445 | default = "example,CSV" 446 | } 447 | 448 | variable "enable_config_recorder" { 449 | description = "Enables configuring the AWS Config recorder resources in this module." 450 | type = bool 451 | default = true 452 | } 453 | 454 | variable "enable_efs_encrypted_check" { 455 | description = "Enable efs-encrypted-check rule" 456 | type = bool 457 | default = false 458 | } 459 | 460 | variable "enable_multi_account_logs" { 461 | description = "Enable sending of logs and snapshots from different Config accounts / regions into a single bucket" 462 | type = bool 463 | default = false 464 | } 465 | 466 | variable "exclude_permission_boundary" { 467 | description = "Boolean to exclude the evaluation of IAM policies used as permissions boundaries. If set to 'true', the rule will not include permissions boundaries in the evaluation. Otherwise, all IAM policies in scope are evaluated when set to 'false.'" 468 | type = bool 469 | default = false 470 | } 471 | 472 | variable "expected_delivery_window_age" { 473 | description = "Maximum age in hours of the most recent delivery to CloudWatch logs that satisfies compliance." 474 | type = number 475 | default = 12 476 | } 477 | 478 | variable "include_global_resource_types" { 479 | description = "Specifies whether AWS Config includes all supported types of global resources with the resources that it records." 480 | type = bool 481 | default = true 482 | } 483 | 484 | variable "kms_key_id" { 485 | description = "Amazon Resource Name (ARN) of the KMS key that is used to encrypt the EFS file system." 486 | type = string 487 | default = "example,CSV" 488 | } 489 | 490 | variable "password_max_age" { 491 | description = "Number of days before password expiration." 492 | type = number 493 | default = 90 494 | } 495 | 496 | variable "password_min_length" { 497 | description = "Password minimum length." 498 | type = number 499 | default = 14 500 | } 501 | 502 | variable "password_require_lowercase" { 503 | description = "Require at least one lowercase character in password." 504 | type = bool 505 | default = true 506 | } 507 | 508 | variable "password_require_numbers" { 509 | description = "Require at least one number in password." 510 | type = bool 511 | default = true 512 | } 513 | 514 | variable "password_require_symbols" { 515 | description = "Require at least one symbol in password." 516 | type = bool 517 | default = true 518 | } 519 | 520 | variable "password_require_uppercase" { 521 | description = "Require at least one uppercase character in password." 522 | type = bool 523 | default = true 524 | } 525 | 526 | variable "password_reuse_prevention" { 527 | description = "Number of passwords before allowing reuse." 528 | type = number 529 | default = 24 530 | } 531 | 532 | variable "required_tags" { 533 | description = "A map of required resource tags. Format is tagNKey, tagNValue, where N is int. Values are optional." 534 | type = map(string) 535 | default = {} 536 | } 537 | 538 | variable "required_tags_resource_types" { 539 | description = "Resource types to check for tags." 540 | type = list(string) 541 | default = [] 542 | } 543 | 544 | variable "resource_types" { 545 | description = "A list that specifies the types of AWS resources for which AWS Config records configuration changes (for example, AWS::EC2::Instance or AWS::CloudTrail::Trail). See relevant part of AWS Docs for available types." 546 | type = list(string) 547 | default = [] 548 | } 549 | 550 | variable "s3_bucket_public_access_prohibited_exclusion" { 551 | description = "Comma-separated list of known allowed public Amazon S3 bucket names." 552 | type = string 553 | default = "example,CSV" 554 | } 555 | 556 | variable "tags" { 557 | description = "Tags to apply to AWS Config resources" 558 | type = map(string) 559 | default = {} 560 | } 561 | 562 | variable "vpc_sg_authorized_ports" { 563 | description = "Object with values as Comma-separated list of ports authorized to be open to 0.0.0.0/0. Ranges are defined by dash. example, '443,1020-1025'" 564 | type = object({ 565 | authorizedTcpPorts = optional(string, null) 566 | authorizedUdpPorts = optional(string, null) 567 | }) 568 | default = {} 569 | } 570 | 571 | variable "sns_kms_key_id" { 572 | description = "The ARN of the KMS key used to encrypt the Amazon SNS topic." 573 | type = string 574 | default = null 575 | } 576 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS Config Terraform module 2 | 3 | Enables AWS Config and adds managed config rules with good defaults. 4 | 5 | ## Supported AWS Config Rules 6 | 7 | ### ACM 8 | 9 | - acm-certificate-expiration-check: Ensure ACM Certificates in your account are marked for expiration within the specified number of days. 10 | 11 | ### AMI 12 | 13 | - approved-amis-by-tag: Checks whether running instances are using specified AMIs. 14 | 15 | ### CloudTrail 16 | 17 | - cloudtrail-enabled: Ensure CloudTrail is enabled. 18 | - cloud-trail-encryption-enabled: Ensure CloudTrail is configured to use server side encryption (SSE) with AWS KMS or CMK encryption. 19 | - cloud-trail-log-file-validation-enabled: Checks whether AWS CloudTrail creates a signed digest file with logs. 20 | - multi-region-cloud-trail-enabled: Ensure that there is at least one multi-region AWS CloudTrail enabled. 21 | - cloud-trail-cloud-watch-logs-enabled: Checks whether AWS CloudTrail trails are configured to send logs to Amazon CloudWatch logs. 22 | 23 | ### CloudWatch Logs 24 | 25 | - cloudwatch-log-group-encrypted: Ensure that CloudWatch Logs are encrypted. 26 | - cw-loggroup-retention-period-check: Checks whether Amazon CloudWatch LogGroup retention period is set to specific number of days. 27 | 28 | ### DynamoDB 29 | 30 | - dynamodb-table-encryption-enabled: Checks if the Amazon DynamoDB tables are encrypted and checks their status. The rule is COMPLIANT if the status is enabled or enabling. [_Not supported in all regions_](https://docs.aws.amazon.com/config/latest/developerguide/dynamodb-table-encryption-enabled.html) 31 | - dynamodb-table-encrypted-kms: Checks if Amazon DynamoDB table is encrypted with AWS Key Management Service (KMS) 32 | 33 | ### EC2 34 | 35 | - ec2-encrypted-volumes: Evaluates whether EBS volumes that are in an attached state are encrypted. 36 | - ec2-volume-inuse-check: Checks whether EBS volumes are attached to EC2 instances. 37 | - ebs-snapshot-public-restorable-check: Checks whether Amazon Elastic Block Store snapshots are not publicly restorable. 38 | - ebs-optimized-instance: Checks if EBS optimization is enabled for your EC2 instances that can be EBS-optimized. 39 | 40 | ### ECR 41 | 42 | - ecr-private-image-scanning-enabled: Checks if a private Amazon Elastic Container Registry (ECR) repository has image scanning enabled. [_Not supported in all regions_](https://docs.aws.amazon.com/config/latest/developerguide/ecr-private-image-scanning-enabled.html) 43 | - ecr-private-lifecycle-policy-configured: Checks if a private Amazon Elastic Container Registry (ECR) repository has at least one lifecycle policy configured. [_Not supported in all regions_](https://docs.aws.amazon.com/config/latest/developerguide/ecr-private-lifecycle-policy-configured.html) 44 | 45 | ### ECS 46 | 47 | - ecs-awsvpc-networking-enabled: Checks if the networking mode for active ECSTaskDefinitions is set to ‘awsvpc’. [_Not supported in all regions_](https://docs.aws.amazon.com/config/latest/developerguide/ecs-awsvpc-networking-enabled.html) 48 | - ecs-containers-nonprivileged: Checks if the privileged parameter in the container definition of ECSTaskDefinitions is set to ‘true’. [_Not supported in all regions_](https://docs.aws.amazon.com/config/latest/developerguide/ecs-containers-nonprivileged.html) 49 | - ecs-containers-readonly-access: Checks if Amazon Elastic Container Service (Amazon ECS) Containers only have read-only access to its root filesystems. [_Not supported in all regions_](https://docs.aws.amazon.com/config/latest/developerguide/ecs-containers-readonly-access.html) 50 | - ecs-no-environment-secrets: Checks if secrets are passed as container environment variables. [_Not supported in all regions_](https://docs.aws.amazon.com/config/latest/developerguide/ecs-no-environment-secrets.html) 51 | 52 | ### EFS 53 | 54 | - efs-encrypted-check: Checks if Amazon Elastic File System is configured to encrypt file data using AWS Key Management Service. 55 | 56 | ### ELB 57 | 58 | - elb-logging-enabled: Checks if the Application Load Balancer and the Classic Load Balancer have logging enabled. 59 | - elb-deletion-protection-enabled: Checks if Elastic Load Balancing has deletion protection enabled. 60 | 61 | ### VPC 62 | 63 | - eip-attached: Checks whether all EIP addresses that are allocated to a VPC are attached to EC2 or in-use ENIs. 64 | - instances-in-vpc: Ensure all EC2 instances run in a VPC. 65 | - vpc-default-security-group-closed: Checks that the default security group of any Amazon Virtual Private Cloud (VPC) does not allow inbound or outbound traffic. 66 | - vpc-sg-open-only-to-authorized-ports: Checks whether any security groups with inbound 0.0.0.0/0 have TCP or UDP ports accessible. 67 | - restricted-common-ports: Checks if the security groups in use do not allow unrestricted incoming TCP traffic to the specified ports. 68 | 69 | ### GuardDuty 70 | 71 | - guardduty-enabled-centralized: Checks whether Amazon GuardDuty is enabled in your AWS account and region. 72 | 73 | ### IAM 74 | 75 | - iam-password-policy: Ensure the account password policy for IAM users meets the specified requirements. 76 | - iam-user-no-policies-check: Ensure that none of your IAM users have policies attached; IAM users must inherit permissions from IAM groups or roles. 77 | - iam-group-has-users-check: Checks whether IAM groups have at least one IAM user. 78 | - root-account-mfa-enabled: Ensure root AWS account has MFA enabled. 79 | - iam-root-access-key: Ensure root AWS account does not have Access Keys. 80 | - mfa_enabled_for_iam_console_access: Checks whether AWS Multi-Factor Authentication (MFA) is enabled for all AWS Identity and Access Management (IAM) users that use a console password. 81 | - iam-policy-no-statements-with-admin-access: Checks the IAM policies that you create for Allow statements that grant permissions to all actions on all resources. 82 | - iam-policy-no-statements-with-full-access: Checks if AWS Identity and Access Management (IAM) policies grant permissions to all actions on individual AWS resources. [_Not supported in all regions_](https://docs.aws.amazon.com/config/latest/developerguide/iam-policy-no-statements-with-full-access.html) 83 | 84 | ### Misc Security 85 | 86 | - restricted-ssh: Checks whether security groups that are in use disallow unrestricted incoming SSH traffic. 87 | - access_keys_rotated: Checks if the active access keys are rotated within the number of days specified in maxAccessKeyAge. 88 | - cmk_backing_key_rotation_enabled: Checks if automatic key rotation is enabled for every AWS Key Management Service customer managed symmetric encryption key. 89 | - nacl-no-unrestricted-ssh-rdp: Checks if default ports for SSH/RDP ingress traffic for network access control lists (NACLs) is unrestricted. [_Not supported in all regions_](https://docs.aws.amazon.com/config/latest/developerguide/nacl-no-unrestricted-ssh-rdp.html) 90 | - internet-gateway-authorized-vpc-only: Checks that Internet gateways (IGWs) are only attached to an authorized Amazon Virtual Private Cloud (VPCs). 91 | 92 | ### Tagging 93 | 94 | - required-tags: Checks if resources are deployed with configured tags. 95 | 96 | ### RDS 97 | 98 | - rds-instance-public-access-check: Checks whether the Amazon Relational Database Service (RDS) instances are not publicly accessible. 99 | - rds-snapshots-public-prohibited: Checks if Amazon Relational Database Service (Amazon RDS) snapshots are public. 100 | - rds-storage-encrypted: Checks whether storage encryption is enabled for your RDS DB instances. 101 | - rds-snapshot-encrypted: Checks whether Amazon Relational Database Service (Amazon RDS) DB snapshots are encrypted. 102 | - rds-cluster-deletion-protection-enabled: Checks if an Amazon Relational Database Service (Amazon RDS) cluster has deletion protection enabled. [_Not supported in all regions_](https://docs.aws.amazon.com/config/latest/developerguide/rds-cluster-deletion-protection-enabled.html) 103 | - db-instance-backup-enabled: Checks if RDS DB instances have backups enabled. 104 | 105 | ### S3 106 | 107 | - s3-bucket-public-write-prohibited: Checks that your S3 buckets do not allow public write access. 108 | - s3-bucket-public-read-prohibited: Checks if your Amazon S3 buckets do not allow public read access. 109 | - s3-bucket-ssl-requests-only: Checks whether S3 buckets have policies that require requests to use Secure Socket Layer (SSL). 110 | - s3-bucket-level-public-access-prohibited: Checks if Amazon Simple Storage Service (Amazon S3) buckets are publicly accessible. [_Not supported in all regions_](https://docs.aws.amazon.com/config/latest/developerguide/s3-bucket-level-public-access-prohibited.html) 111 | - s3-bucket-acl-prohibited: Checks if Amazon Simple Storage Service (Amazon S3) Buckets allow user permissions through access control lists (ACLs). [_Not supported in all regions_](https://docs.aws.amazon.com/config/latest/developerguide/s3-bucket-acl-prohibited.html) 112 | - s3-bucket-server-side-encryption-enabled: Checks if S3 bucket either has the S3 default encryption enabled or that S3 policy explicitly denies put-object requests without SSE that uses AES-256 or AWS KMS. 113 | 114 | ## Usage 115 | 116 | **Note: This module sets up AWS IAM Roles and Policies, which are globally namespaced. If you plan to have multiple instances of AWS Config, make sure they have unique values for `config_name`.** 117 | 118 | **Note: If you use this module in multiple regions, be sure to disable duplicate checks and global resource types.** 119 | 120 | ```hcl 121 | module "aws_config" { 122 | source = "trussworks/config/aws" 123 | 124 | config_name = "my-aws-config" 125 | config_logs_bucket = "my-aws-logs" 126 | } 127 | ``` 128 | 129 | 130 | ## Requirements 131 | 132 | | Name | Version | 133 | |------|---------| 134 | | terraform | >= 1.0 | 135 | | aws | >= 2.70 | 136 | 137 | ## Providers 138 | 139 | | Name | Version | 140 | |------|---------| 141 | | aws | >= 2.70 | 142 | 143 | ## Modules 144 | 145 | No modules. 146 | 147 | ## Resources 148 | 149 | | Name | Type | 150 | |------|------| 151 | | [aws_config_config_rule.access_keys_rotated](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 152 | | [aws_config_config_rule.acm-certificate-expiration-check](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 153 | | [aws_config_config_rule.approved-amis-by-tag](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 154 | | [aws_config_config_rule.cloud-trail-cloud-watch-logs-enabled](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 155 | | [aws_config_config_rule.cloud-trail-encryption-enabled](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 156 | | [aws_config_config_rule.cloud-trail-log-file-validation-enabled](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 157 | | [aws_config_config_rule.cloudtrail-enabled](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 158 | | [aws_config_config_rule.cloudwatch_log_group_encrypted](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 159 | | [aws_config_config_rule.cmk_backing_key_rotation_enabled](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 160 | | [aws_config_config_rule.cw-loggroup-retention-period-check](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 161 | | [aws_config_config_rule.db-instance-backup-enabled](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 162 | | [aws_config_config_rule.dynamodb-table-encrypted-kms](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 163 | | [aws_config_config_rule.dynamodb-table-encryption-enabled](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 164 | | [aws_config_config_rule.ebs-optimized-instance](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 165 | | [aws_config_config_rule.ebs_snapshot_public_restorable](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 166 | | [aws_config_config_rule.ec2-encrypted-volumes](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 167 | | [aws_config_config_rule.ec2-imdsv2-check](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 168 | | [aws_config_config_rule.ec2-volume-inuse-check](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 169 | | [aws_config_config_rule.ecr-private-image-scanning-enabled](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 170 | | [aws_config_config_rule.ecr-private-lifecycle-policy-configured](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 171 | | [aws_config_config_rule.ecs-awsvpc-networking-enabled](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 172 | | [aws_config_config_rule.ecs-containers-nonprivileged](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 173 | | [aws_config_config_rule.ecs-containers-readonly-access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 174 | | [aws_config_config_rule.ecs-no-environment-secrets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 175 | | [aws_config_config_rule.efs-encrypted-check](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 176 | | [aws_config_config_rule.eip_attached](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 177 | | [aws_config_config_rule.elb-deletion-protection-enabled](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 178 | | [aws_config_config_rule.elb-logging-enabled](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 179 | | [aws_config_config_rule.guardduty-enabled-centralized](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 180 | | [aws_config_config_rule.iam-group-has-users-check](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 181 | | [aws_config_config_rule.iam-password-policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 182 | | [aws_config_config_rule.iam-policy-no-statements-with-admin-access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 183 | | [aws_config_config_rule.iam-policy-no-statements-with-full-access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 184 | | [aws_config_config_rule.iam-user-no-policies-check](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 185 | | [aws_config_config_rule.iam_root_access_key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 186 | | [aws_config_config_rule.internet-gateway-authorized-vpc-only](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 187 | | [aws_config_config_rule.mfa_enabled_for_iam_console_access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 188 | | [aws_config_config_rule.multi-region-cloud-trail-enabled](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 189 | | [aws_config_config_rule.nacl-no-unrestricted-ssh-rdp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 190 | | [aws_config_config_rule.rds-cluster-deletion-protection-enabled](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 191 | | [aws_config_config_rule.rds-instance-public-access-check](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 192 | | [aws_config_config_rule.rds-snapshot-encrypted](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 193 | | [aws_config_config_rule.rds-snapshots-public-prohibited](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 194 | | [aws_config_config_rule.rds-storage-encrypted](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 195 | | [aws_config_config_rule.required-tags](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 196 | | [aws_config_config_rule.restricted-common-ports](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 197 | | [aws_config_config_rule.restricted_ssh](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 198 | | [aws_config_config_rule.root-account-mfa-enabled](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 199 | | [aws_config_config_rule.s3-bucket-acl-prohibited](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 200 | | [aws_config_config_rule.s3-bucket-level-public-access-prohibited](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 201 | | [aws_config_config_rule.s3-bucket-public-read-prohibited](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 202 | | [aws_config_config_rule.s3-bucket-public-write-prohibited](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 203 | | [aws_config_config_rule.s3_bucket_ssl_requests_only](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 204 | | [aws_config_config_rule.vpc-sg-open-only-to-authorized-ports](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 205 | | [aws_config_config_rule.vpc_default_security_group_closed](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_config_rule) | resource | 206 | | [aws_config_configuration_aggregator.organization](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_configuration_aggregator) | resource | 207 | | [aws_config_configuration_recorder.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_configuration_recorder) | resource | 208 | | [aws_config_configuration_recorder_status.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_configuration_recorder_status) | resource | 209 | | [aws_config_delivery_channel.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/config_delivery_channel) | resource | 210 | | [aws_iam_policy.aws-config-policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | 211 | | [aws_iam_role.aggregator](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | 212 | | [aws_iam_role.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | 213 | | [aws_iam_role_policy_attachment.aggregator](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | 214 | | [aws_iam_role_policy_attachment.aws-config-policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | 215 | | [aws_iam_role_policy_attachment.managed-policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | 216 | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | 217 | | [aws_iam_policy_document.aws-config-role-policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 218 | | [aws_iam_policy_document.aws_config_aggregator_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 219 | | [aws_iam_policy_document.aws_config_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 220 | | [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | 221 | 222 | ## Inputs 223 | 224 | | Name | Description | Type | Default | Required | 225 | |------|-------------|------|---------|:--------:| 226 | | access\_key\_max\_age | Maximum number of days without rotation. | `number` | `90` | no | 227 | | acm\_days\_to\_expiration | Specify the number of days before the rule flags the ACM Certificate as noncompliant. | `number` | `14` | no | 228 | | aggregate\_organization | Aggregate compliance data by organization | `bool` | `false` | no | 229 | | ami\_required\_tag\_key\_value | Tag/s key and value which AMI has to have in order to be compliant: Example: key1:value1,key2:value2 | `string` | `""` | no | 230 | | authorized\_vpc\_ids | Comma-separated list of the authorized VPC IDs with attached IGWs. If parameter is not provided all attached IGWs will be NON\_COMPLIANT. | `string` | `"example,CSV"` | no | 231 | | check\_access\_keys\_rotated | Enable access-keys-rotated rule | `bool` | `true` | no | 232 | | check\_acm\_certificate\_expiration\_check | Enable acm-certificate-expiration-check rule | `bool` | `true` | no | 233 | | check\_approved\_amis\_by\_tag | Enable approved-amis-by-tag rule | `bool` | `false` | no | 234 | | check\_cloud\_trail\_encryption | Enable cloud-trail-encryption-enabled rule | `bool` | `false` | no | 235 | | check\_cloud\_trail\_log\_file\_validation | Enable cloud-trail-log-file-validation-enabled rule | `bool` | `false` | no | 236 | | check\_cloudtrail\_enabled | Enable cloudtrail-enabled rule | `bool` | `true` | no | 237 | | check\_cloudwatch\_log\_group\_encrypted | Enable cloudwatch-log-group-encryption rule | `bool` | `true` | no | 238 | | check\_cmk\_backing\_key\_rotated | Enable cmk\_backing\_key\_rotation\_enabled rule | `bool` | `true` | no | 239 | | check\_cw\_loggroup\_retention\_period | Enable cloudwatch-log-group-retention-period-check rule | `bool` | `false` | no | 240 | | check\_db\_instance\_backup\_enabled | Enable db-instance-backup-enabled rule | `bool` | `false` | no | 241 | | check\_dynamodb\_table\_encrypted\_kms | Enable dynamodb-table-encrypted-kms rule | `bool` | `false` | no | 242 | | check\_dynamodb\_table\_encryption\_enabled | Enable checkdynamodb-table-encryption-enabled rule | `bool` | `true` | no | 243 | | check\_ebs\_optimized\_instance | Enable ebs-optimized-instance-check rule | `bool` | `false` | no | 244 | | check\_ebs\_snapshot\_public\_restorable | Enable ebs-snapshot-public-restorable rule | `bool` | `true` | no | 245 | | check\_ec2\_encrypted\_volumes | Enable ec2-encrypted-volumes rule | `bool` | `true` | no | 246 | | check\_ec2\_imdsv2 | Enable IMDSv2 rule | `bool` | `false` | no | 247 | | check\_ec2\_volume\_inuse\_check | Enable ec2-volume-inuse-check rule | `bool` | `true` | no | 248 | | check\_ecr\_private\_image\_scanning\_enabled | Enable ecr-private-image-scanning-enabled rule | `bool` | `true` | no | 249 | | check\_ecr\_private\_lifecycle\_policy\_configured | Enable ecr-private-lifecycle-policy-configured rule | `bool` | `true` | no | 250 | | check\_ecs\_awsvpc\_networking\_enabled | Enable ecs-awsvpc-networking-enabled rule | `bool` | `true` | no | 251 | | check\_ecs\_containers\_nonprivileged | Enable ecs-containers-nonprivileged rule | `bool` | `true` | no | 252 | | check\_ecs\_containers\_readonly\_access | Enable ecs-containers-readonly-access rule | `bool` | `true` | no | 253 | | check\_ecs\_no\_environment\_secrets | Enable ecs-no-environment-secrets rule | `bool` | `false` | no | 254 | | check\_eip\_attached | Enable eip-attached rule | `bool` | `false` | no | 255 | | check\_elb\_deletion\_protection\_enabled | Enable elb-deletion-protection-enabled rule | `bool` | `true` | no | 256 | | check\_elb\_logging\_enabled | Enable elb-logging-enabled rule | `bool` | `false` | no | 257 | | check\_guard\_duty | Enable guardduty-enabled-centralized rule | `bool` | `false` | no | 258 | | check\_iam\_group\_has\_users\_check | Enable iam-group-has-users-check rule | `bool` | `true` | no | 259 | | check\_iam\_password\_policy | Enable iam-password-policy rule | `bool` | `true` | no | 260 | | check\_iam\_policy\_no\_statements\_with\_admin\_access | Enable iam-policy-no-statements-with-admin-access rule | `bool` | `true` | no | 261 | | check\_iam\_policy\_no\_statements\_with\_full\_access | Enable iam-policy-no-statements-with-full-access rule | `bool` | `true` | no | 262 | | check\_iam\_root\_access\_key | Enable iam-root-access-key rule | `bool` | `true` | no | 263 | | check\_iam\_user\_no\_policies\_check | Enable iam-user-no-policies-check rule | `bool` | `true` | no | 264 | | check\_internet\_gateway\_authorized\_vpc\_only | Enable internet-gateway-authorized-vpc-only rule | `bool` | `false` | no | 265 | | check\_mfa\_enabled\_for\_iam\_console\_access | Enable mfa-enabled-for-iam-console-access rule | `bool` | `true` | no | 266 | | check\_multi\_region\_cloud\_trail | Enable multi-region-cloud-trail-enabled rule | `bool` | `false` | no | 267 | | check\_nacl\_no\_unrestricted\_ssh\_rdp | Enable nacl-no-unrestricted-ssh-rdp rule | `bool` | `true` | no | 268 | | check\_rds\_cluster\_deletion\_protection\_enabled | Enable rds-cluster-deletion-protection-enabled rule | `bool` | `true` | no | 269 | | check\_rds\_public\_access | Enable rds-instance-public-access-check rule | `bool` | `false` | no | 270 | | check\_rds\_snapshot\_encrypted | Enable rds-snapshot-encrypted rule | `bool` | `true` | no | 271 | | check\_rds\_snapshots\_public\_prohibited | Enable rds-snapshots-public-prohibited rule | `bool` | `true` | no | 272 | | check\_rds\_storage\_encrypted | Enable rds-storage-encrypted rule | `bool` | `true` | no | 273 | | check\_required\_tags | Enable required-tags rule | `bool` | `false` | no | 274 | | check\_restricted\_common\_ports | Enable restricted-common-ports-check | `bool` | `false` | no | 275 | | check\_restricted\_ssh | Enable restricted-ssh rule | `bool` | `true` | no | 276 | | check\_root\_account\_mfa\_enabled | Enable root-account-mfa-enabled rule | `bool` | `false` | no | 277 | | check\_s3\_bucket\_acl\_prohibited | Enable s3-bucket-acl-prohibited rule | `bool` | `true` | no | 278 | | check\_s3\_bucket\_level\_public\_access\_prohibited | Enable s3-bucket-level-public-access-prohibited rule | `bool` | `false` | no | 279 | | check\_s3\_bucket\_public\_read\_prohibited | Enable s3-bucket-public-read-prohibited rule | `bool` | `false` | no | 280 | | check\_s3\_bucket\_public\_write\_prohibited | Enable s3-bucket-public-write-prohibited rule | `bool` | `true` | no | 281 | | check\_s3\_bucket\_ssl\_requests\_only | Enable s3-bucket-ssl-requests-only rule | `bool` | `true` | no | 282 | | check\_vpc\_default\_security\_group\_closed | Enable vpc-default-security-group-closed rule | `bool` | `true` | no | 283 | | check\_vpc\_sg\_open\_only\_to\_authorized\_ports | Enable vpc-sg-open-only-to-authorized-ports rule | `bool` | `false` | no | 284 | | cloud\_trail\_cloud\_watch\_logs\_enabled | Enable cloud\_trail\_cloud\_watch\_logs\_enabled rule | `bool` | `true` | no | 285 | | config\_aggregator\_name | The name of the aggregator. | `string` | `"organization"` | no | 286 | | config\_delivery\_frequency | The frequency with which AWS Config delivers configuration snapshots. | `string` | `"Six_Hours"` | no | 287 | | config\_logs\_bucket | The S3 bucket for AWS Config logs. If you have set enable\_config\_recorder to false then this can be an empty string. | `string` | n/a | yes | 288 | | config\_logs\_bucket\_kms\_key\_arn | The ARN of the AWS KMS key used to encrypt objects delivered by AWS Config. Must belong to the same Region as the destination S3 bucket. | `string` | `null` | no | 289 | | config\_logs\_prefix | The S3 prefix for AWS Config logs. | `string` | `"config"` | no | 290 | | config\_max\_execution\_frequency | The maximum frequency with which AWS Config runs evaluations for a rule. | `string` | `"TwentyFour_Hours"` | no | 291 | | config\_name | The name of the AWS Config instance. | `string` | `"aws-config"` | no | 292 | | config\_recording\_frequency | Default recording frequency for the AWS Config | `string` | `"CONTINUOUS"` | no | 293 | | config\_recording\_frequency\_overrides | Specific overrides of the recording frequency for the AWS Config | ```set(object({ description = optional(string, null) resource_types = list(string) recording_frequency = string }))``` | `[]` | no | 294 | | config\_role\_permissions\_boundary | The ARN of the permissions boundary to apply to IAM roles created for AWS Config | `string` | `null` | no | 295 | | config\_sns\_topic\_arn | An SNS topic to stream configuration changes and notifications to. | `string` | `null` | no | 296 | | cw\_loggroup\_retention\_period | Retention period for cloudwatch logs in number of days | `number` | `3653` | no | 297 | | dynamodb\_arn\_encryption\_list | Comma separated list of AWS KMS key ARNs allowed for encrypting Amazon DynamoDB Tables. | `string` | `"example,CSV"` | no | 298 | | ecs\_no\_environment\_secrets | Comma-separated list of key names to search for in the environment variables of container definitions within Task Definitions. Extra spaces will be removed. | `string` | `"example,CSV"` | no | 299 | | elb\_logging\_s3\_buckets | Comma-separated list of Amazon S3 bucket names for Amazon ELB to deliver the log files. | `string` | `"example,CSV"` | no | 300 | | enable\_config\_recorder | Enables configuring the AWS Config recorder resources in this module. | `bool` | `true` | no | 301 | | enable\_efs\_encrypted\_check | Enable efs-encrypted-check rule | `bool` | `false` | no | 302 | | enable\_multi\_account\_logs | Enable sending of logs and snapshots from different Config accounts / regions into a single bucket | `bool` | `false` | no | 303 | | exclude\_permission\_boundary | Boolean to exclude the evaluation of IAM policies used as permissions boundaries. If set to 'true', the rule will not include permissions boundaries in the evaluation. Otherwise, all IAM policies in scope are evaluated when set to 'false.' | `bool` | `false` | no | 304 | | expected\_delivery\_window\_age | Maximum age in hours of the most recent delivery to CloudWatch logs that satisfies compliance. | `number` | `12` | no | 305 | | include\_global\_resource\_types | Specifies whether AWS Config includes all supported types of global resources with the resources that it records. | `bool` | `true` | no | 306 | | kms\_key\_id | Amazon Resource Name (ARN) of the KMS key that is used to encrypt the EFS file system. | `string` | `"example,CSV"` | no | 307 | | password\_max\_age | Number of days before password expiration. | `number` | `90` | no | 308 | | password\_min\_length | Password minimum length. | `number` | `14` | no | 309 | | password\_require\_lowercase | Require at least one lowercase character in password. | `bool` | `true` | no | 310 | | password\_require\_numbers | Require at least one number in password. | `bool` | `true` | no | 311 | | password\_require\_symbols | Require at least one symbol in password. | `bool` | `true` | no | 312 | | password\_require\_uppercase | Require at least one uppercase character in password. | `bool` | `true` | no | 313 | | password\_reuse\_prevention | Number of passwords before allowing reuse. | `number` | `24` | no | 314 | | required\_tags | A map of required resource tags. Format is tagNKey, tagNValue, where N is int. Values are optional. | `map(string)` | `{}` | no | 315 | | required\_tags\_resource\_types | Resource types to check for tags. | `list(string)` | `[]` | no | 316 | | resource\_types | A list that specifies the types of AWS resources for which AWS Config records configuration changes (for example, AWS::EC2::Instance or AWS::CloudTrail::Trail). See relevant part of AWS Docs for available types. | `list(string)` | `[]` | no | 317 | | s3\_bucket\_public\_access\_prohibited\_exclusion | Comma-separated list of known allowed public Amazon S3 bucket names. | `string` | `"example,CSV"` | no | 318 | | sns\_kms\_key\_id | The ARN of the KMS key used to encrypt the Amazon SNS topic. | `string` | `null` | no | 319 | | tags | Tags to apply to AWS Config resources | `map(string)` | `{}` | no | 320 | | vpc\_sg\_authorized\_ports | Object with values as Comma-separated list of ports authorized to be open to 0.0.0.0/0. Ranges are defined by dash. example, '443,1020-1025' | ```object({ authorizedTcpPorts = optional(string, null) authorizedUdpPorts = optional(string, null) })``` | `{}` | no | 321 | 322 | ## Outputs 323 | 324 | | Name | Description | 325 | |------|-------------| 326 | | aws\_config\_role\_arn | The ARN of the AWS config role. | 327 | | aws\_config\_role\_name | The name of the IAM role used by AWS config | 328 | | required\_tags\_rule\_arn | The ARN of the required-tags config rule. | 329 | 330 | 331 | ## Upgrade Paths 332 | 333 | ### Upgrading from 2.3.0 to 2.4.x 334 | 335 | Version 2.4.0 changed how AWS Config IAM polices would be attached to IAM roles. When applying the upgrade, you will likely see a race condition resulting in the following error 336 | 337 | ```text 338 | Error: Provider produced inconsistent result after apply 339 | ``` 340 | 341 | A second `terraform apply` should resolve the issue. 342 | 343 | ## Developer Setup 344 | 345 | Install dependencies (macOS) 346 | 347 | ```shell 348 | brew install pre-commit go terraform terraform-docs 349 | ``` 350 | -------------------------------------------------------------------------------- /config-rules.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | aws_config_iam_password_policy = templatefile("${path.module}/config-policies/iam-password-policy.tpl", 3 | { 4 | password_require_uppercase = var.password_require_uppercase ? "true" : "false" 5 | password_require_lowercase = var.password_require_lowercase ? "true" : "false" 6 | password_require_symbols = var.password_require_symbols ? "true" : "false" 7 | password_require_numbers = var.password_require_numbers ? "true" : "false" 8 | password_min_length = var.password_min_length 9 | password_reuse_prevention = var.password_reuse_prevention 10 | password_max_age = var.password_max_age 11 | } 12 | ) 13 | 14 | aws_config_acm_certificate_expiration = templatefile("${path.module}/config-policies/acm-certificate-expiration.tpl", 15 | { 16 | acm_days_to_expiration = var.acm_days_to_expiration 17 | } 18 | ) 19 | 20 | aws_config_ami_approved_tag = templatefile("${path.module}/config-policies/ami-approved-tag.tpl", 21 | { 22 | ami_required_tag_key_value = var.ami_required_tag_key_value 23 | } 24 | ) 25 | 26 | aws_config_cloudwatch_log_group_retention_period = templatefile("${path.module}/config-policies/cloudwatch-log-retention.tpl", 27 | { 28 | cw_loggroup_retention_period = var.cw_loggroup_retention_period 29 | } 30 | ) 31 | 32 | aws_config_dynamodb_arn_encryption_list = templatefile("${path.module}/config-policies/dynamodb_arn_encryption_list.tpl", 33 | { 34 | dynamodb_arn_encryption_list = var.dynamodb_arn_encryption_list 35 | } 36 | ) 37 | 38 | aws_config_access_key_max_age = templatefile("${path.module}/config-policies/access-keys-rotated.tpl", 39 | { 40 | access_key_max_age = var.access_key_max_age 41 | } 42 | ) 43 | 44 | aws_config_logs_delivery_window = templatefile("${path.module}/config-policies/cloudtrail-cloudwatch-logs-enabled.tpl", 45 | { 46 | expected_delivery_window_age = var.expected_delivery_window_age 47 | } 48 | ) 49 | 50 | aws_config_efs_encrypted_check = templatefile("${path.module}/config-policies/efs-encrypted-check.tpl", 51 | { 52 | kms_key_id = var.kms_key_id 53 | } 54 | ) 55 | 56 | aws_config_elb_logging_s3_buckets = templatefile("${path.module}/config-policies/elb-logging-enabled.tpl", 57 | { 58 | elb_logging_s3_buckets = var.elb_logging_s3_buckets 59 | } 60 | ) 61 | 62 | aws_config_exclude_permission_boundary = templatefile("${path.module}/config-policies/exclude-permission-boundary.tpl", 63 | { 64 | exclude_permission_boundary = var.exclude_permission_boundary 65 | } 66 | ) 67 | 68 | aws_config_authorized_vpc_ids = templatefile("${path.module}/config-policies/internet-gateway-authorized-vpc-only.tpl", 69 | { 70 | authorized_vpc_ids = var.authorized_vpc_ids 71 | } 72 | ) 73 | 74 | aws_config_ecs_no_environment_secrets = templatefile("${path.module}/config-policies/ecs-no-environment-secrets.tpl", 75 | { 76 | ecs_no_environment_secrets = var.ecs_no_environment_secrets 77 | } 78 | ) 79 | 80 | aws_config_s3_bucket_public_access_prohibited_exclusion = templatefile("${path.module}/config-policies/s3_public_access_exclusion.tpl", 81 | { 82 | s3_bucket_public_access_prohibited_exclusion = var.s3_bucket_public_access_prohibited_exclusion 83 | } 84 | ) 85 | 86 | aws_config_vpc_sg_authorized_ports = jsonencode({ for k, v in var.vpc_sg_authorized_ports : k => tostring(v) if v != null }) 87 | 88 | } 89 | 90 | 91 | # 92 | # AWS Config Rules 93 | # 94 | 95 | resource "aws_config_config_rule" "iam-password-policy" { 96 | count = var.check_iam_password_policy ? 1 : 0 97 | name = "iam-password-policy" 98 | description = "Ensure the account password policy for IAM users meets the specified requirements" 99 | input_parameters = local.aws_config_iam_password_policy 100 | 101 | source { 102 | owner = "AWS" 103 | source_identifier = "IAM_PASSWORD_POLICY" 104 | } 105 | 106 | maximum_execution_frequency = var.config_max_execution_frequency 107 | 108 | tags = var.tags 109 | 110 | depends_on = [aws_config_configuration_recorder.main] 111 | } 112 | 113 | resource "aws_config_config_rule" "cloudtrail-enabled" { 114 | count = var.check_cloudtrail_enabled ? 1 : 0 115 | name = "cloudtrail-enabled" 116 | description = "Ensure CloudTrail is enabled" 117 | 118 | source { 119 | owner = "AWS" 120 | source_identifier = "CLOUD_TRAIL_ENABLED" 121 | } 122 | 123 | maximum_execution_frequency = var.config_max_execution_frequency 124 | 125 | tags = var.tags 126 | 127 | depends_on = [aws_config_configuration_recorder.main] 128 | } 129 | 130 | resource "aws_config_config_rule" "multi-region-cloud-trail-enabled" { 131 | count = var.check_multi_region_cloud_trail ? 1 : 0 132 | name = "multi-region-cloud-trail-enabled" 133 | description = "Checks that there is at least one multi-region AWS CloudTrail. The rule is NON_COMPLIANT if the trails do not match inputs parameters." 134 | 135 | source { 136 | owner = "AWS" 137 | source_identifier = "MULTI_REGION_CLOUD_TRAIL_ENABLED" 138 | } 139 | 140 | maximum_execution_frequency = var.config_max_execution_frequency 141 | 142 | tags = var.tags 143 | 144 | depends_on = [aws_config_configuration_recorder.main] 145 | } 146 | 147 | resource "aws_config_config_rule" "cloud-trail-encryption-enabled" { 148 | count = var.check_cloud_trail_encryption ? 1 : 0 149 | name = "cloud-trail-encryption-enabled" 150 | description = "Checks whether AWS CloudTrail is configured to use the server side encryption (SSE) AWS Key Management Service (AWS KMS) customer master key (CMK) encryption. The rule is COMPLIANT if the KmsKeyId is defined." 151 | 152 | source { 153 | owner = "AWS" 154 | source_identifier = "CLOUD_TRAIL_ENCRYPTION_ENABLED" 155 | } 156 | 157 | maximum_execution_frequency = var.config_max_execution_frequency 158 | 159 | tags = var.tags 160 | 161 | depends_on = [aws_config_configuration_recorder.main] 162 | } 163 | 164 | resource "aws_config_config_rule" "cloud-trail-log-file-validation-enabled" { 165 | count = var.check_cloud_trail_log_file_validation ? 1 : 0 166 | name = "cloud-trail-log-file-validation-enabled" 167 | description = "Checks whether AWS CloudTrail creates a signed digest file with logs. AWS recommends that the file validation must be enabled on all trails. The rule is NON_COMPLIANT if the validation is not enabled." 168 | 169 | source { 170 | owner = "AWS" 171 | source_identifier = "CLOUD_TRAIL_LOG_FILE_VALIDATION_ENABLED" 172 | } 173 | 174 | maximum_execution_frequency = var.config_max_execution_frequency 175 | 176 | tags = var.tags 177 | 178 | depends_on = [aws_config_configuration_recorder.main] 179 | } 180 | 181 | resource "aws_config_config_rule" "root-account-mfa-enabled" { 182 | count = var.check_root_account_mfa_enabled ? 1 : 0 183 | name = "root-account-mfa-enabled" 184 | description = "Ensure root AWS account has MFA enabled" 185 | 186 | source { 187 | owner = "AWS" 188 | source_identifier = "ROOT_ACCOUNT_MFA_ENABLED" 189 | } 190 | 191 | maximum_execution_frequency = var.config_max_execution_frequency 192 | 193 | tags = var.tags 194 | 195 | depends_on = [aws_config_configuration_recorder.main] 196 | } 197 | 198 | resource "aws_config_config_rule" "acm-certificate-expiration-check" { 199 | count = var.check_acm_certificate_expiration_check ? 1 : 0 200 | name = "acm-certificate-expiration-check" 201 | description = "Ensures ACM Certificates in your account are marked for expiration within the specified number of days" 202 | input_parameters = local.aws_config_acm_certificate_expiration 203 | 204 | source { 205 | owner = "AWS" 206 | source_identifier = "ACM_CERTIFICATE_EXPIRATION_CHECK" 207 | } 208 | 209 | maximum_execution_frequency = var.config_max_execution_frequency 210 | 211 | tags = var.tags 212 | 213 | depends_on = [aws_config_configuration_recorder.main] 214 | } 215 | 216 | resource "aws_config_config_rule" "ec2-volume-inuse-check" { 217 | count = var.check_ec2_volume_inuse_check ? 1 : 0 218 | name = "ec2-volume-inuse-check" 219 | description = "Checks whether EBS volumes are attached to EC2 instances" 220 | 221 | source { 222 | owner = "AWS" 223 | source_identifier = "EC2_VOLUME_INUSE_CHECK" 224 | } 225 | 226 | tags = var.tags 227 | 228 | depends_on = [aws_config_configuration_recorder.main] 229 | } 230 | 231 | resource "aws_config_config_rule" "ec2-imdsv2-check" { 232 | count = var.check_ec2_imdsv2 ? 1 : 0 233 | name = "ec2-imdsv2-check" 234 | description = "Checks if EC2 instances metadata is configured with IMDSv2 or not" 235 | 236 | source { 237 | owner = "AWS" 238 | source_identifier = "EC2_IMDSV2_CHECK" 239 | } 240 | 241 | tags = var.tags 242 | 243 | depends_on = [aws_config_configuration_recorder.main] 244 | } 245 | 246 | resource "aws_config_config_rule" "ebs_snapshot_public_restorable" { 247 | count = var.check_ebs_snapshot_public_restorable ? 1 : 0 248 | name = "ebs-snapshot-public-restorable" 249 | description = "Checks whether Amazon Elastic Block Store snapshots are not publicly restorable" 250 | 251 | source { 252 | owner = "AWS" 253 | source_identifier = "EBS_SNAPSHOT_PUBLIC_RESTORABLE_CHECK" 254 | } 255 | 256 | tags = var.tags 257 | 258 | depends_on = [aws_config_configuration_recorder.main] 259 | } 260 | 261 | resource "aws_config_config_rule" "iam-user-no-policies-check" { 262 | count = var.check_iam_user_no_policies_check ? 1 : 0 263 | name = "iam-user-no-policies-check" 264 | description = "Ensure that none of your IAM users have policies attached. IAM users must inherit permissions from IAM groups or roles." 265 | 266 | source { 267 | owner = "AWS" 268 | source_identifier = "IAM_USER_NO_POLICIES_CHECK" 269 | } 270 | 271 | tags = var.tags 272 | 273 | depends_on = [aws_config_configuration_recorder.main] 274 | } 275 | 276 | resource "aws_config_config_rule" "iam-group-has-users-check" { 277 | count = var.check_iam_group_has_users_check ? 1 : 0 278 | name = "iam-group-has-users-check" 279 | description = "Checks whether IAM groups have at least one IAM user." 280 | 281 | source { 282 | owner = "AWS" 283 | source_identifier = "IAM_GROUP_HAS_USERS_CHECK" 284 | } 285 | 286 | tags = var.tags 287 | 288 | depends_on = [aws_config_configuration_recorder.main] 289 | } 290 | 291 | resource "aws_config_config_rule" "rds-storage-encrypted" { 292 | count = var.check_rds_storage_encrypted ? 1 : 0 293 | name = "rds-storage-encrypted" 294 | description = "Checks whether storage encryption is enabled for your RDS DB instances." 295 | 296 | source { 297 | owner = "AWS" 298 | source_identifier = "RDS_STORAGE_ENCRYPTED" 299 | } 300 | 301 | tags = var.tags 302 | 303 | depends_on = [aws_config_configuration_recorder.main] 304 | } 305 | 306 | resource "aws_config_config_rule" "rds-instance-public-access-check" { 307 | count = var.check_rds_public_access ? 1 : 0 308 | name = "rds-instance-public-access-check" 309 | description = "Checks whether the Amazon Relational Database Service (RDS) instances are not publicly accessible. The rule is non-compliant if the publiclyAccessible field is true in the instance configuration item." 310 | 311 | source { 312 | owner = "AWS" 313 | source_identifier = "RDS_INSTANCE_PUBLIC_ACCESS_CHECK" 314 | } 315 | 316 | tags = var.tags 317 | 318 | depends_on = [aws_config_configuration_recorder.main] 319 | } 320 | 321 | resource "aws_config_config_rule" "rds-snapshots-public-prohibited" { 322 | count = var.check_rds_snapshots_public_prohibited ? 1 : 0 323 | name = "rds-snapshots-public-prohibited" 324 | description = "Checks if Amazon Relational Database Service (Amazon RDS) snapshots are public." 325 | 326 | source { 327 | owner = "AWS" 328 | source_identifier = "RDS_SNAPSHOTS_PUBLIC_PROHIBITED" 329 | } 330 | 331 | tags = var.tags 332 | 333 | depends_on = [aws_config_configuration_recorder.main] 334 | } 335 | 336 | resource "aws_config_config_rule" "guardduty-enabled-centralized" { 337 | count = var.check_guard_duty ? 1 : 0 338 | name = "guardduty-enabled-centralized" 339 | description = "Checks whether Amazon GuardDuty is enabled in your AWS account and region." 340 | 341 | source { 342 | owner = "AWS" 343 | source_identifier = "GUARDDUTY_ENABLED_CENTRALIZED" 344 | } 345 | 346 | maximum_execution_frequency = var.config_max_execution_frequency 347 | 348 | tags = var.tags 349 | 350 | depends_on = [aws_config_configuration_recorder.main] 351 | } 352 | 353 | resource "aws_config_config_rule" "s3-bucket-public-write-prohibited" { 354 | count = var.check_s3_bucket_public_write_prohibited ? 1 : 0 355 | name = "s3-bucket-public-write-prohibited" 356 | description = "Checks that your S3 buckets do not allow public write access." 357 | 358 | source { 359 | owner = "AWS" 360 | source_identifier = "S3_BUCKET_PUBLIC_WRITE_PROHIBITED" 361 | } 362 | 363 | tags = var.tags 364 | 365 | depends_on = [aws_config_configuration_recorder.main] 366 | } 367 | 368 | resource "aws_config_config_rule" "eip_attached" { 369 | count = var.check_eip_attached ? 1 : 0 370 | name = "eip-attached" 371 | description = "Checks whether all Elastic IP addresses that are allocated to a VPC are attached to EC2 instances or in-use elastic network interfaces (ENIs)." 372 | 373 | source { 374 | owner = "AWS" 375 | source_identifier = "EIP_ATTACHED" 376 | } 377 | 378 | tags = var.tags 379 | 380 | depends_on = [aws_config_configuration_recorder.main] 381 | } 382 | 383 | resource "aws_config_config_rule" "required-tags" { 384 | count = var.check_required_tags ? 1 : 0 385 | name = "required-tags" 386 | description = "Checks if resources are deployed with configured tags." 387 | 388 | scope { 389 | compliance_resource_types = var.required_tags_resource_types 390 | } 391 | 392 | input_parameters = jsonencode(var.required_tags) 393 | 394 | source { 395 | owner = "AWS" 396 | source_identifier = "REQUIRED_TAGS" 397 | } 398 | 399 | tags = var.tags 400 | 401 | depends_on = [aws_config_configuration_recorder.main] 402 | } 403 | 404 | resource "aws_config_config_rule" "approved-amis-by-tag" { 405 | count = var.check_approved_amis_by_tag ? 1 : 0 406 | name = "approved-amis-by-tag" 407 | description = "Checks whether running instances are using specified AMIs. Running instances that dont have at least one of the specified tags are noncompliant" 408 | input_parameters = local.aws_config_ami_approved_tag 409 | 410 | source { 411 | owner = "AWS" 412 | source_identifier = "APPROVED_AMIS_BY_TAG" 413 | } 414 | 415 | tags = var.tags 416 | 417 | depends_on = [aws_config_configuration_recorder.main] 418 | } 419 | 420 | resource "aws_config_config_rule" "ec2-encrypted-volumes" { 421 | count = var.check_ec2_encrypted_volumes ? 1 : 0 422 | name = "ec2-volumes-must-be-encrypted" 423 | description = "Evaluates whether EBS volumes that are in an attached state are encrypted. Optionally, you can specify the ID of a KMS key to use to encrypt the volume." 424 | 425 | source { 426 | owner = "AWS" 427 | source_identifier = "ENCRYPTED_VOLUMES" 428 | } 429 | 430 | tags = var.tags 431 | 432 | depends_on = [aws_config_configuration_recorder.main] 433 | } 434 | 435 | resource "aws_config_config_rule" "cloudwatch_log_group_encrypted" { 436 | count = var.check_cloudwatch_log_group_encrypted ? 1 : 0 437 | 438 | name = "cloudwatch-log-group-encrypted" 439 | description = "Checks whether a log group in Amazon CloudWatch Logs is encrypted. The rule is NON_COMPLIANT if CloudWatch Logs has a log group without encryption enabled" 440 | 441 | source { 442 | owner = "AWS" 443 | source_identifier = "CLOUDWATCH_LOG_GROUP_ENCRYPTED" 444 | } 445 | 446 | tags = var.tags 447 | 448 | depends_on = [aws_config_configuration_recorder.main] 449 | } 450 | 451 | resource "aws_config_config_rule" "cw-loggroup-retention-period-check" { 452 | count = var.check_cw_loggroup_retention_period ? 1 : 0 453 | 454 | name = "cloudwatch-log-group-retention" 455 | description = "Checks whether Amazon CloudWatch LogGroup retention period is set to specific number of days. The rule is NON_COMPLIANT if the retention period is not set or is less than the configured retention period." 456 | 457 | input_parameters = local.aws_config_cloudwatch_log_group_retention_period 458 | 459 | source { 460 | owner = "AWS" 461 | source_identifier = "CW_LOGGROUP_RETENTION_PERIOD_CHECK" 462 | } 463 | 464 | tags = var.tags 465 | 466 | depends_on = [aws_config_configuration_recorder.main] 467 | } 468 | 469 | resource "aws_config_config_rule" "iam_root_access_key" { 470 | count = var.check_iam_root_access_key ? 1 : 0 471 | 472 | name = "iam-root-access-key" 473 | description = "Checks whether the root user access key is available. The rule is COMPLIANT if the user access key does not exist" 474 | 475 | source { 476 | owner = "AWS" 477 | source_identifier = "IAM_ROOT_ACCESS_KEY_CHECK" 478 | } 479 | 480 | tags = var.tags 481 | 482 | depends_on = [aws_config_configuration_recorder.main] 483 | } 484 | 485 | resource "aws_config_config_rule" "vpc_default_security_group_closed" { 486 | count = var.check_vpc_default_security_group_closed ? 1 : 0 487 | 488 | name = "vpc-default-security-group-closed" 489 | description = "Checks that the default security group of any Amazon Virtual Private Cloud (VPC) does not allow inbound or outbound traffic" 490 | 491 | source { 492 | owner = "AWS" 493 | source_identifier = "VPC_DEFAULT_SECURITY_GROUP_CLOSED" 494 | } 495 | 496 | tags = var.tags 497 | 498 | depends_on = [aws_config_configuration_recorder.main] 499 | } 500 | 501 | resource "aws_config_config_rule" "s3_bucket_ssl_requests_only" { 502 | count = var.check_s3_bucket_ssl_requests_only ? 1 : 0 503 | 504 | name = "s3-bucket-ssl-requests-only" 505 | description = "Checks whether S3 buckets have policies that require requests to use Secure Socket Layer (SSL)." 506 | 507 | source { 508 | owner = "AWS" 509 | source_identifier = "S3_BUCKET_SSL_REQUESTS_ONLY" 510 | } 511 | 512 | tags = var.tags 513 | 514 | depends_on = [aws_config_configuration_recorder.main] 515 | } 516 | 517 | resource "aws_config_config_rule" "mfa_enabled_for_iam_console_access" { 518 | count = var.check_mfa_enabled_for_iam_console_access ? 1 : 0 519 | 520 | name = "mfa-enabled-for-iam-console-access" 521 | description = "Checks whether AWS Multi-Factor Authentication (MFA) is enabled for all AWS Identity and Access Management (IAM) users that use a console password. The rule is compliant if MFA is enabled." 522 | 523 | source { 524 | owner = "AWS" 525 | source_identifier = "MFA_ENABLED_FOR_IAM_CONSOLE_ACCESS" 526 | } 527 | 528 | tags = var.tags 529 | 530 | depends_on = [aws_config_configuration_recorder.main] 531 | } 532 | 533 | resource "aws_config_config_rule" "restricted_ssh" { 534 | count = var.check_restricted_ssh ? 1 : 0 535 | 536 | name = "restricted-ssh" 537 | description = "Checks whether security groups that are in use disallow unrestricted incoming SSH traffic." 538 | 539 | source { 540 | owner = "AWS" 541 | source_identifier = "INCOMING_SSH_DISABLED" 542 | } 543 | 544 | tags = var.tags 545 | 546 | depends_on = [aws_config_configuration_recorder.main] 547 | } 548 | 549 | resource "aws_config_config_rule" "access_keys_rotated" { 550 | count = var.check_access_keys_rotated ? 1 : 0 551 | name = "access-keys-rotated" 552 | description = "Checks if the active access keys are rotated within the number of days specified in maxAccessKeyAge. The rule is NON_COMPLIANT if the access keys have not been rotated for more than maxAccessKeyAge number of days." 553 | input_parameters = local.aws_config_access_key_max_age 554 | 555 | source { 556 | owner = "AWS" 557 | source_identifier = "ACCESS_KEYS_ROTATED" 558 | } 559 | 560 | maximum_execution_frequency = var.config_max_execution_frequency 561 | 562 | tags = var.tags 563 | 564 | depends_on = [aws_config_configuration_recorder.main] 565 | } 566 | 567 | resource "aws_config_config_rule" "cmk_backing_key_rotation_enabled" { 568 | count = var.check_cmk_backing_key_rotated ? 1 : 0 569 | name = "cmk-backing-key-rotation-enabled" 570 | description = "Checks if automatic key rotation is enabled for every AWS Key Management Service customer managed symmetric encryption key. The rule is NON_COMPLIANT if automatic key rotation is not enabled for an AWS KMS customer managed symmetric encryption key." 571 | 572 | source { 573 | owner = "AWS" 574 | source_identifier = "CMK_BACKING_KEY_ROTATION_ENABLED" 575 | } 576 | 577 | maximum_execution_frequency = var.config_max_execution_frequency 578 | 579 | tags = var.tags 580 | 581 | depends_on = [aws_config_configuration_recorder.main] 582 | } 583 | 584 | resource "aws_config_config_rule" "cloud-trail-cloud-watch-logs-enabled" { 585 | count = var.cloud_trail_cloud_watch_logs_enabled ? 1 : 0 586 | name = "cloud-trail-cloud-watch-logs-enabled" 587 | description = "Checks whether AWS CloudTrail trails are configured to send logs to Amazon CloudWatch logs. The trail is non-compliant if the CloudWatchLogsLogGroupArn property of the trail is empty." 588 | input_parameters = local.aws_config_logs_delivery_window 589 | 590 | source { 591 | owner = "AWS" 592 | source_identifier = "CLOUD_TRAIL_CLOUD_WATCH_LOGS_ENABLED" 593 | } 594 | 595 | maximum_execution_frequency = var.config_max_execution_frequency 596 | 597 | tags = var.tags 598 | 599 | depends_on = [aws_config_configuration_recorder.main] 600 | } 601 | 602 | resource "aws_config_config_rule" "dynamodb-table-encryption-enabled" { 603 | count = var.check_dynamodb_table_encryption_enabled ? 1 : 0 604 | name = "dynamodb-table-encryption-enabled" 605 | description = "Checks if the Amazon DynamoDB tables are encrypted and checks their status. The rule is COMPLIANT if the status is enabled or enabling." 606 | 607 | source { 608 | owner = "AWS" 609 | source_identifier = "DYNAMODB_TABLE_ENCRYPTION_ENABLED" 610 | } 611 | 612 | tags = var.tags 613 | 614 | depends_on = [aws_config_configuration_recorder.main] 615 | } 616 | 617 | resource "aws_config_config_rule" "dynamodb-table-encrypted-kms" { 618 | count = var.check_dynamodb_table_encrypted_kms ? 1 : 0 619 | name = "dynamodb-table-encrypted-kms" 620 | description = "Checks if Amazon DynamoDB table is encrypted with AWS Key Management Service (KMS). NON_COMPLIANT if DynamoDB table is not encrypted with AWS KMS. Also NON_COMPLIANT if the encrypted AWS KMS key is not present in kmsKeyArns input parameter." 621 | input_parameters = local.aws_config_dynamodb_arn_encryption_list 622 | 623 | source { 624 | owner = "AWS" 625 | source_identifier = "DYNAMODB_TABLE_ENCRYPTED_KMS" 626 | } 627 | 628 | tags = var.tags 629 | 630 | depends_on = [aws_config_configuration_recorder.main] 631 | } 632 | 633 | resource "aws_config_config_rule" "ecr-private-image-scanning-enabled" { 634 | count = var.check_ecr_private_image_scanning_enabled ? 1 : 0 635 | name = "ecr-private-image-scanning-enabled" 636 | description = "Checks if a private Amazon Elastic Container Registry (ECR) repository has image scanning enabled. The rule is NON_COMPLIANT if image scanning is not enabled for the private ECR repository." 637 | 638 | source { 639 | owner = "AWS" 640 | source_identifier = "ECR_PRIVATE_IMAGE_SCANNING_ENABLED" 641 | } 642 | 643 | tags = var.tags 644 | 645 | depends_on = [aws_config_configuration_recorder.main] 646 | } 647 | 648 | resource "aws_config_config_rule" "ecr-private-lifecycle-policy-configured" { 649 | count = var.check_ecr_private_lifecycle_policy_configured ? 1 : 0 650 | name = "ecr-private-lifecycle-policy-configured" 651 | description = "Checks if a private Amazon Elastic Container Registry (ECR) repository has at least one lifecycle policy configured. The rule is NON_COMPLIANT if no lifecycle policy is configured for the ECR private repository." 652 | 653 | source { 654 | owner = "AWS" 655 | source_identifier = "ECR_PRIVATE_LIFECYCLE_POLICY_CONFIGURED" 656 | } 657 | 658 | tags = var.tags 659 | 660 | depends_on = [aws_config_configuration_recorder.main] 661 | } 662 | 663 | resource "aws_config_config_rule" "ecs-awsvpc-networking-enabled" { 664 | count = var.check_ecs_awsvpc_networking_enabled ? 1 : 0 665 | name = "ecs-awsvpc-networking-enabled" 666 | description = "Checks if the networking mode for active ECSTaskDefinitions is set to ‘awsvpc’. This rule is NON_COMPLIANT if active ECSTaskDefinitions is not set to ‘awsvpc’." 667 | 668 | source { 669 | owner = "AWS" 670 | source_identifier = "ECS_AWSVPC_NETWORKING_ENABLED" 671 | } 672 | 673 | tags = var.tags 674 | 675 | depends_on = [aws_config_configuration_recorder.main] 676 | } 677 | 678 | resource "aws_config_config_rule" "ecs-containers-nonprivileged" { 679 | count = var.check_ecs_containers_nonprivileged ? 1 : 0 680 | name = "ecs-containers-nonprivileged" 681 | description = "Checks if the privileged parameter in the container definition of ECSTaskDefinitions is set to ‘true’. The rule is NON_COMPLIANT if the privileged parameter is ‘true’." 682 | 683 | source { 684 | owner = "AWS" 685 | source_identifier = "ECS_CONTAINERS_NONPRIVILEGED" 686 | } 687 | 688 | tags = var.tags 689 | 690 | depends_on = [aws_config_configuration_recorder.main] 691 | } 692 | 693 | resource "aws_config_config_rule" "ecs-containers-readonly-access" { 694 | count = var.check_ecs_containers_readonly_access ? 1 : 0 695 | name = "ecs-containers-readonly-access" 696 | description = "Checks if Amazon Elastic Container Service (Amazon ECS) Containers only have read-only access to its root filesystems. The rule NON_COMPLIANT if readonlyRootFilesystem parameter in the container definition of ECSTaskDefinitions is set to ‘false’." 697 | 698 | source { 699 | owner = "AWS" 700 | source_identifier = "ECS_CONTAINERS_READONLY_ACCESS" 701 | } 702 | 703 | tags = var.tags 704 | 705 | depends_on = [aws_config_configuration_recorder.main] 706 | } 707 | 708 | resource "aws_config_config_rule" "ecs-no-environment-secrets" { 709 | count = var.check_ecs_no_environment_secrets ? 1 : 0 710 | name = "ecs-no-environment-secrets" 711 | description = "Checks if secrets are passed as container environment variables. Rule is NON_COMPLIANT if 1 or more environment variable key matches a key listed in the 'secretKeys' parameter (excluding env variables from other locations such as Amazon S3)." 712 | input_parameters = local.aws_config_ecs_no_environment_secrets 713 | 714 | source { 715 | owner = "AWS" 716 | source_identifier = "ECS_NO_ENVIRONMENT_SECRETS" 717 | } 718 | 719 | tags = var.tags 720 | 721 | depends_on = [aws_config_configuration_recorder.main] 722 | } 723 | 724 | resource "aws_config_config_rule" "efs-encrypted-check" { 725 | count = var.enable_efs_encrypted_check ? 1 : 0 726 | name = "efs-encrypted-check" 727 | description = "Checks if Amazon Elastic File System is configured to encrypt file data using AWS Key Management Service. NON_COMPLIANT if encrypted key set to false on DescribeFileSystems or KmsKeyId key on DescribeFileSystems does not match the KmsKeyId parameter" 728 | input_parameters = local.aws_config_efs_encrypted_check 729 | 730 | source { 731 | owner = "AWS" 732 | source_identifier = "EFS_ENCRYPTED_CHECK" 733 | } 734 | 735 | maximum_execution_frequency = var.config_max_execution_frequency 736 | 737 | tags = var.tags 738 | 739 | depends_on = [aws_config_configuration_recorder.main] 740 | } 741 | 742 | resource "aws_config_config_rule" "elb-deletion-protection-enabled" { 743 | count = var.check_elb_deletion_protection_enabled ? 1 : 0 744 | name = "elb-deletion-protection-enabled" 745 | description = "Checks if Elastic Load Balancing has deletion protection enabled. The rule is NON_COMPLIANT if deletion_protection.enabled is false." 746 | 747 | source { 748 | owner = "AWS" 749 | source_identifier = "ELB_DELETION_PROTECTION_ENABLED" 750 | } 751 | 752 | tags = var.tags 753 | 754 | depends_on = [aws_config_configuration_recorder.main] 755 | } 756 | 757 | resource "aws_config_config_rule" "elb-logging-enabled" { 758 | count = var.check_elb_logging_enabled ? 1 : 0 759 | name = "elb-logging-enabled" 760 | description = "Checks if the Application Load Balancer and the Classic Load Balancer have logging enabled. The rule is NON_COMPLIANT if the access_logs.s3.enabled is false or access_logs.S3.bucket is not equal to the s3BucketName that you provided." 761 | input_parameters = local.aws_config_elb_logging_s3_buckets 762 | 763 | source { 764 | owner = "AWS" 765 | source_identifier = "ELB_LOGGING_ENABLED" 766 | } 767 | 768 | tags = var.tags 769 | 770 | depends_on = [aws_config_configuration_recorder.main] 771 | } 772 | 773 | resource "aws_config_config_rule" "iam-policy-no-statements-with-admin-access" { 774 | count = var.check_iam_policy_no_statements_with_admin_access ? 1 : 0 775 | name = "iam-policy-no-statements-with-admin-access" 776 | description = "Checks the IAM policies that you create for Allow statements that grant permissions to all actions on all resources. The rule is NON_COMPLIANT if any policy statement includes \"Effect\": \"Allow\" with \"Action\": \"*\" over \"Resource\": \"*\"." 777 | 778 | source { 779 | owner = "AWS" 780 | source_identifier = "IAM_POLICY_NO_STATEMENTS_WITH_ADMIN_ACCESS" 781 | } 782 | 783 | tags = var.tags 784 | 785 | depends_on = [aws_config_configuration_recorder.main] 786 | } 787 | 788 | resource "aws_config_config_rule" "iam-policy-no-statements-with-full-access" { 789 | count = var.check_iam_policy_no_statements_with_full_access ? 1 : 0 790 | name = "iam-policy-no-statements-with-full-access" 791 | description = "Checks if AWS Identity and Access Management (IAM) policies grant permissions to all actions on individual AWS resources. The rule is NON_COMPLIANT if the managed IAM policy allows full access to at least 1 AWS service. " 792 | input_parameters = local.aws_config_exclude_permission_boundary 793 | 794 | source { 795 | owner = "AWS" 796 | source_identifier = "IAM_POLICY_NO_STATEMENTS_WITH_FULL_ACCESS" 797 | } 798 | 799 | tags = var.tags 800 | 801 | depends_on = [aws_config_configuration_recorder.main] 802 | } 803 | 804 | resource "aws_config_config_rule" "nacl-no-unrestricted-ssh-rdp" { 805 | count = var.check_nacl_no_unrestricted_ssh_rdp ? 1 : 0 806 | name = "nacl-no-unrestricted-ssh-rdp" 807 | description = "Checks if default ports for SSH/RDP ingress traffic for network access control lists (NACLs) is unrestricted. The rule is NON_COMPLIANT if a NACL inbound entry allows a source TCP or UDP CIDR block for ports 22 or 3389." 808 | 809 | source { 810 | owner = "AWS" 811 | source_identifier = "NACL_NO_UNRESTRICTED_SSH_RDP" 812 | } 813 | 814 | tags = var.tags 815 | 816 | depends_on = [aws_config_configuration_recorder.main] 817 | } 818 | 819 | resource "aws_config_config_rule" "internet-gateway-authorized-vpc-only" { 820 | count = var.check_internet_gateway_authorized_vpc_only ? 1 : 0 821 | name = "internet-gateway-authorized-vpc-only" 822 | description = "Checks that Internet gateways (IGWs) are only attached to an authorized Amazon Virtual Private Cloud (VPCs). The rule is NON_COMPLIANT if IGWs are not attached to an authorized VPC." 823 | input_parameters = local.aws_config_authorized_vpc_ids 824 | 825 | source { 826 | owner = "AWS" 827 | source_identifier = "INTERNET_GATEWAY_AUTHORIZED_VPC_ONLY" 828 | } 829 | 830 | tags = var.tags 831 | 832 | depends_on = [aws_config_configuration_recorder.main] 833 | } 834 | 835 | resource "aws_config_config_rule" "rds-snapshot-encrypted" { 836 | count = var.check_rds_snapshot_encrypted ? 1 : 0 837 | name = "rds-snapshot-encrypted" 838 | description = "Checks whether Amazon Relational Database Service (Amazon RDS) DB snapshots are encrypted. The rule is NON_COMPLIANT, if the Amazon RDS DB snapshots are not encrypted. " 839 | 840 | source { 841 | owner = "AWS" 842 | source_identifier = "RDS_SNAPSHOT_ENCRYPTED" 843 | } 844 | 845 | tags = var.tags 846 | 847 | depends_on = [aws_config_configuration_recorder.main] 848 | } 849 | 850 | resource "aws_config_config_rule" "rds-cluster-deletion-protection-enabled" { 851 | count = var.check_rds_cluster_deletion_protection_enabled ? 1 : 0 852 | name = "rds-cluster-deletion-protection-enabled" 853 | description = "Checks if an Amazon Relational Database Service (Amazon RDS) cluster has deletion protection enabled. The rule is NON_COMPLIANT if an Amazon RDS cluster does not have deletion protection enabled." 854 | 855 | source { 856 | owner = "AWS" 857 | source_identifier = "RDS_CLUSTER_DELETION_PROTECTION_ENABLED" 858 | } 859 | 860 | tags = var.tags 861 | 862 | depends_on = [aws_config_configuration_recorder.main] 863 | } 864 | 865 | resource "aws_config_config_rule" "db-instance-backup-enabled" { 866 | count = var.check_db_instance_backup_enabled ? 1 : 0 867 | name = "db-instance-backup-enabled" 868 | description = "Checks if RDS DB instances have backups enabled. Optionally, the rule checks the backup retention period and the backup window." 869 | 870 | source { 871 | owner = "AWS" 872 | source_identifier = "DB_INSTANCE_BACKUP_ENABLED" 873 | } 874 | 875 | tags = var.tags 876 | 877 | depends_on = [aws_config_configuration_recorder.main] 878 | } 879 | 880 | resource "aws_config_config_rule" "s3-bucket-level-public-access-prohibited" { 881 | count = var.check_s3_bucket_level_public_access_prohibited ? 1 : 0 882 | name = "s3-bucket-level-public-access-prohibited" 883 | description = "Checks if Amazon Simple Storage Service (Amazon S3) buckets are publicly accessible. This rule is NON_COMPLIANT if an Amazon S3 bucket is not listed in the excludedPublicBuckets parameter and bucket level settings are public. " 884 | input_parameters = local.aws_config_s3_bucket_public_access_prohibited_exclusion 885 | 886 | source { 887 | owner = "AWS" 888 | source_identifier = "S3_BUCKET_LEVEL_PUBLIC_ACCESS_PROHIBITED" 889 | } 890 | 891 | tags = var.tags 892 | 893 | depends_on = [aws_config_configuration_recorder.main] 894 | } 895 | 896 | resource "aws_config_config_rule" "s3-bucket-acl-prohibited" { 897 | count = var.check_s3_bucket_acl_prohibited ? 1 : 0 898 | name = "s3-bucket-acl-prohibited" 899 | description = "Checks if Amazon Simple Storage Service (Amazon S3) Buckets allow user permissions through access control lists (ACLs). The rule is NON_COMPLIANT if ACLs are configured for user access in Amazon S3 Buckets." 900 | 901 | source { 902 | owner = "AWS" 903 | source_identifier = "S3_BUCKET_ACL_PROHIBITED" 904 | } 905 | 906 | tags = var.tags 907 | 908 | depends_on = [aws_config_configuration_recorder.main] 909 | } 910 | 911 | resource "aws_config_config_rule" "vpc-sg-open-only-to-authorized-ports" { 912 | count = var.check_vpc_sg_open_only_to_authorized_ports ? 1 : 0 913 | name = "vpc-sg-open-only-to-authorized-ports" 914 | description = "Checks if security groups with inbound 0.0.0.0/0 have TCP or UDP ports accessible. NON_COMPLIANT if security group with inbound 0.0.0.0/0 has a port accessible which is not specified in rule parameters.(both Terraform inputs required if enabled)" 915 | input_parameters = local.aws_config_vpc_sg_authorized_ports 916 | 917 | source { 918 | owner = "AWS" 919 | source_identifier = "VPC_SG_OPEN_ONLY_TO_AUTHORIZED_PORTS" 920 | } 921 | 922 | tags = var.tags 923 | 924 | depends_on = [aws_config_configuration_recorder.main] 925 | } 926 | 927 | resource "aws_config_config_rule" "ebs-optimized-instance" { 928 | count = var.check_ebs_optimized_instance ? 1 : 0 929 | name = "ebs-optimized-instance" 930 | description = "Checks if EBS optimization is enabled for your EC2 instances that can be EBS-optimized." 931 | 932 | source { 933 | owner = "AWS" 934 | source_identifier = "EBS_OPTIMIZED_INSTANCE" 935 | } 936 | 937 | tags = var.tags 938 | 939 | depends_on = [aws_config_configuration_recorder.main] 940 | } 941 | 942 | resource "aws_config_config_rule" "s3-bucket-public-read-prohibited" { 943 | count = var.check_s3_bucket_public_read_prohibited ? 1 : 0 944 | name = "s3-bucket-public-read-prohibited" 945 | description = "Checks that your S3 buckets do not allow public read access." 946 | 947 | source { 948 | owner = "AWS" 949 | source_identifier = "S3_BUCKET_PUBLIC_READ_PROHIBITED" 950 | } 951 | 952 | tags = var.tags 953 | 954 | depends_on = [aws_config_configuration_recorder.main] 955 | } 956 | 957 | resource "aws_config_config_rule" "restricted-common-ports" { 958 | count = var.check_restricted_common_ports ? 1 : 0 959 | name = "restricted-common-ports" 960 | description = "Checks if the security groups in use do not allow unrestricted incoming TCP traffic to the specified ports." 961 | 962 | source { 963 | owner = "AWS" 964 | source_identifier = "RESTRICTED_INCOMING_TRAFFIC" 965 | } 966 | 967 | tags = var.tags 968 | 969 | depends_on = [aws_config_configuration_recorder.main] 970 | } 971 | --------------------------------------------------------------------------------