Files
Greg Hendrickson 6136cde9bb feat: Terraform Foundation - AWS Landing Zone
Enterprise-grade multi-tenant AWS cloud foundation.

Modules:
- GitHub OIDC for keyless CI/CD authentication
- IAM account settings and security baseline
- AWS Config Rules for compliance
- ABAC (Attribute-Based Access Control)
- SCPs (Service Control Policies)

Features:
- Multi-account architecture
- Cost optimization patterns
- Security best practices
- Comprehensive documentation

Tech: Terraform, AWS Organizations, IAM Identity Center
2026-02-02 02:57:23 +00:00

515 lines
17 KiB
HCL

################################################################################
# AWS Config Rules Module
#
# Compliance monitoring with managed rules:
# - CIS AWS Foundations Benchmark
# - PCI DSS
# - HIPAA
# - Custom rules
# - Auto-remediation (optional)
#
# Usage:
# module "config_rules" {
# source = "../modules/config-rules"
#
# enable_cis_benchmark = true
# enable_security_best_practices = true
#
# # Or pick individual rules
# rules = {
# s3-bucket-ssl = true
# ec2-imdsv2 = true
# }
# }
################################################################################
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0"
}
}
}
variable "enable_aws_config" {
type = bool
default = true
description = "Enable AWS Config (required for rules)"
}
variable "config_bucket" {
type = string
default = ""
description = "S3 bucket for Config snapshots (created if empty)"
}
variable "config_sns_topic_arn" {
type = string
default = ""
description = "SNS topic for Config notifications"
}
variable "delivery_frequency" {
type = string
default = "TwentyFour_Hours"
description = "Config snapshot delivery frequency"
}
# Compliance Packs
variable "enable_cis_benchmark" {
type = bool
default = false
description = "Enable CIS AWS Foundations Benchmark rules"
}
variable "enable_security_best_practices" {
type = bool
default = true
description = "Enable AWS Security Best Practices rules"
}
variable "enable_pci_dss" {
type = bool
default = false
description = "Enable PCI DSS compliance rules"
}
variable "enable_hipaa" {
type = bool
default = false
description = "Enable HIPAA compliance rules"
}
# Individual Rules (all optional)
variable "rules" {
type = object({
# S3 Security
s3_bucket_public_read_prohibited = optional(bool, true)
s3_bucket_public_write_prohibited = optional(bool, true)
s3_bucket_ssl_requests_only = optional(bool, true)
s3_bucket_logging_enabled = optional(bool, false)
s3_bucket_versioning_enabled = optional(bool, false)
s3_default_encryption_kms = optional(bool, false)
# EC2 Security
ec2_imdsv2_check = optional(bool, true)
ec2_instance_no_public_ip = optional(bool, false)
ec2_ebs_encryption_by_default = optional(bool, true)
ec2_security_group_attached_to_eni = optional(bool, false)
restricted_ssh = optional(bool, true)
restricted_rdp = optional(bool, true)
# IAM Security
iam_root_access_key_check = optional(bool, true)
iam_user_mfa_enabled = optional(bool, true)
iam_user_no_policies_check = optional(bool, true)
iam_password_policy = optional(bool, true)
access_keys_rotated = optional(bool, true)
access_keys_rotated_days = optional(number, 90)
# RDS Security
rds_instance_public_access_check = optional(bool, true)
rds_storage_encrypted = optional(bool, true)
rds_multi_az_support = optional(bool, false)
rds_snapshot_encrypted = optional(bool, true)
# Network Security
vpc_flow_logs_enabled = optional(bool, true)
vpc_default_security_group_closed = optional(bool, true)
# Encryption
kms_cmk_not_scheduled_for_deletion = optional(bool, true)
encrypted_volumes = optional(bool, true)
# Logging & Monitoring
cloudtrail_enabled = optional(bool, true)
cloudwatch_alarm_action_check = optional(bool, false)
cw_loggroup_retention_period_check = optional(bool, false)
guardduty_enabled_centralized = optional(bool, false)
# Lambda
lambda_function_public_access_prohibited = optional(bool, true)
lambda_inside_vpc = optional(bool, false)
})
default = {}
description = "Individual Config rules to enable"
}
variable "auto_remediation" {
type = bool
default = false
description = "Enable auto-remediation for supported rules"
}
variable "tags" {
type = map(string)
default = {}
}
################################################################################
# Data Sources
################################################################################
data "aws_caller_identity" "current" {}
data "aws_region" "current" {}
################################################################################
# S3 Bucket for Config
################################################################################
resource "aws_s3_bucket" "config" {
count = var.enable_aws_config && var.config_bucket == "" ? 1 : 0
bucket = "aws-config-${data.aws_caller_identity.current.account_id}-${data.aws_region.current.name}"
tags = merge(var.tags, { Name = "aws-config" })
}
resource "aws_s3_bucket_versioning" "config" {
count = var.enable_aws_config && var.config_bucket == "" ? 1 : 0
bucket = aws_s3_bucket.config[0].id
versioning_configuration { status = "Enabled" }
}
resource "aws_s3_bucket_server_side_encryption_configuration" "config" {
count = var.enable_aws_config && var.config_bucket == "" ? 1 : 0
bucket = aws_s3_bucket.config[0].id
rule {
apply_server_side_encryption_by_default { sse_algorithm = "AES256" }
}
}
resource "aws_s3_bucket_public_access_block" "config" {
count = var.enable_aws_config && var.config_bucket == "" ? 1 : 0
bucket = aws_s3_bucket.config[0].id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
locals {
config_bucket = var.config_bucket != "" ? var.config_bucket : (var.enable_aws_config ? aws_s3_bucket.config[0].id : "")
}
################################################################################
# IAM Role for Config
################################################################################
resource "aws_iam_role" "config" {
count = var.enable_aws_config ? 1 : 0
name = "AWSConfigRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = "sts:AssumeRole"
Principal = { Service = "config.amazonaws.com" }
}]
})
tags = merge(var.tags, { Name = "AWSConfigRole" })
}
resource "aws_iam_role_policy_attachment" "config" {
count = var.enable_aws_config ? 1 : 0
role = aws_iam_role.config[0].name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWS_ConfigRole"
}
resource "aws_iam_role_policy" "config_s3" {
count = var.enable_aws_config ? 1 : 0
name = "s3-delivery"
role = aws_iam_role.config[0].id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = ["s3:PutObject", "s3:PutObjectAcl"]
Resource = "arn:aws:s3:::${local.config_bucket}/*"
Condition = {
StringLike = { "s3:x-amz-acl" = "bucket-owner-full-control" }
}
},
{
Effect = "Allow"
Action = "s3:GetBucketAcl"
Resource = "arn:aws:s3:::${local.config_bucket}"
}
]
})
}
################################################################################
# AWS Config Recorder
################################################################################
resource "aws_config_configuration_recorder" "main" {
count = var.enable_aws_config ? 1 : 0
name = "default"
role_arn = aws_iam_role.config[0].arn
recording_group {
all_supported = true
include_global_resource_types = true
}
}
resource "aws_config_delivery_channel" "main" {
count = var.enable_aws_config ? 1 : 0
name = "default"
s3_bucket_name = local.config_bucket
sns_topic_arn = var.config_sns_topic_arn != "" ? var.config_sns_topic_arn : null
snapshot_delivery_properties {
delivery_frequency = var.delivery_frequency
}
depends_on = [aws_config_configuration_recorder.main]
}
resource "aws_config_configuration_recorder_status" "main" {
count = var.enable_aws_config ? 1 : 0
name = aws_config_configuration_recorder.main[0].name
is_enabled = true
depends_on = [aws_config_delivery_channel.main]
}
################################################################################
# Security Best Practices Rules
################################################################################
# S3 Rules
resource "aws_config_config_rule" "s3_bucket_public_read_prohibited" {
count = var.enable_aws_config && (var.rules.s3_bucket_public_read_prohibited || var.enable_security_best_practices) ? 1 : 0
name = "s3-bucket-public-read-prohibited"
source {
owner = "AWS"
source_identifier = "S3_BUCKET_PUBLIC_READ_PROHIBITED"
}
depends_on = [aws_config_configuration_recorder.main]
tags = var.tags
}
resource "aws_config_config_rule" "s3_bucket_public_write_prohibited" {
count = var.enable_aws_config && (var.rules.s3_bucket_public_write_prohibited || var.enable_security_best_practices) ? 1 : 0
name = "s3-bucket-public-write-prohibited"
source {
owner = "AWS"
source_identifier = "S3_BUCKET_PUBLIC_WRITE_PROHIBITED"
}
depends_on = [aws_config_configuration_recorder.main]
tags = var.tags
}
resource "aws_config_config_rule" "s3_bucket_ssl_requests_only" {
count = var.enable_aws_config && (var.rules.s3_bucket_ssl_requests_only || var.enable_security_best_practices) ? 1 : 0
name = "s3-bucket-ssl-requests-only"
source {
owner = "AWS"
source_identifier = "S3_BUCKET_SSL_REQUESTS_ONLY"
}
depends_on = [aws_config_configuration_recorder.main]
tags = var.tags
}
# EC2 Rules
resource "aws_config_config_rule" "ec2_imdsv2_check" {
count = var.enable_aws_config && (var.rules.ec2_imdsv2_check || var.enable_security_best_practices) ? 1 : 0
name = "ec2-imdsv2-check"
source {
owner = "AWS"
source_identifier = "EC2_IMDSV2_CHECK"
}
depends_on = [aws_config_configuration_recorder.main]
tags = var.tags
}
resource "aws_config_config_rule" "ebs_encryption_by_default" {
count = var.enable_aws_config && (var.rules.ec2_ebs_encryption_by_default || var.enable_security_best_practices) ? 1 : 0
name = "ec2-ebs-encryption-by-default-check"
source {
owner = "AWS"
source_identifier = "EC2_EBS_ENCRYPTION_BY_DEFAULT"
}
depends_on = [aws_config_configuration_recorder.main]
tags = var.tags
}
resource "aws_config_config_rule" "restricted_ssh" {
count = var.enable_aws_config && (var.rules.restricted_ssh || var.enable_security_best_practices) ? 1 : 0
name = "restricted-ssh"
source {
owner = "AWS"
source_identifier = "INCOMING_SSH_DISABLED"
}
depends_on = [aws_config_configuration_recorder.main]
tags = var.tags
}
# IAM Rules
resource "aws_config_config_rule" "iam_root_access_key_check" {
count = var.enable_aws_config && (var.rules.iam_root_access_key_check || var.enable_security_best_practices) ? 1 : 0
name = "iam-root-access-key-check"
source {
owner = "AWS"
source_identifier = "IAM_ROOT_ACCESS_KEY_CHECK"
}
depends_on = [aws_config_configuration_recorder.main]
tags = var.tags
}
resource "aws_config_config_rule" "iam_user_mfa_enabled" {
count = var.enable_aws_config && (var.rules.iam_user_mfa_enabled || var.enable_security_best_practices) ? 1 : 0
name = "iam-user-mfa-enabled"
source {
owner = "AWS"
source_identifier = "IAM_USER_MFA_ENABLED"
}
depends_on = [aws_config_configuration_recorder.main]
tags = var.tags
}
resource "aws_config_config_rule" "access_keys_rotated" {
count = var.enable_aws_config && (var.rules.access_keys_rotated || var.enable_security_best_practices) ? 1 : 0
name = "access-keys-rotated"
source {
owner = "AWS"
source_identifier = "ACCESS_KEYS_ROTATED"
}
input_parameters = jsonencode({
maxAccessKeyAge = var.rules.access_keys_rotated_days
})
depends_on = [aws_config_configuration_recorder.main]
tags = var.tags
}
# RDS Rules
resource "aws_config_config_rule" "rds_instance_public_access_check" {
count = var.enable_aws_config && (var.rules.rds_instance_public_access_check || var.enable_security_best_practices) ? 1 : 0
name = "rds-instance-public-access-check"
source {
owner = "AWS"
source_identifier = "RDS_INSTANCE_PUBLIC_ACCESS_CHECK"
}
depends_on = [aws_config_configuration_recorder.main]
tags = var.tags
}
resource "aws_config_config_rule" "rds_storage_encrypted" {
count = var.enable_aws_config && (var.rules.rds_storage_encrypted || var.enable_security_best_practices) ? 1 : 0
name = "rds-storage-encrypted"
source {
owner = "AWS"
source_identifier = "RDS_STORAGE_ENCRYPTED"
}
depends_on = [aws_config_configuration_recorder.main]
tags = var.tags
}
# Network Rules
resource "aws_config_config_rule" "vpc_flow_logs_enabled" {
count = var.enable_aws_config && (var.rules.vpc_flow_logs_enabled || var.enable_security_best_practices) ? 1 : 0
name = "vpc-flow-logs-enabled"
source {
owner = "AWS"
source_identifier = "VPC_FLOW_LOGS_ENABLED"
}
depends_on = [aws_config_configuration_recorder.main]
tags = var.tags
}
resource "aws_config_config_rule" "vpc_default_security_group_closed" {
count = var.enable_aws_config && (var.rules.vpc_default_security_group_closed || var.enable_security_best_practices) ? 1 : 0
name = "vpc-default-security-group-closed"
source {
owner = "AWS"
source_identifier = "VPC_DEFAULT_SECURITY_GROUP_CLOSED"
}
depends_on = [aws_config_configuration_recorder.main]
tags = var.tags
}
# CloudTrail Rule
resource "aws_config_config_rule" "cloudtrail_enabled" {
count = var.enable_aws_config && (var.rules.cloudtrail_enabled || var.enable_security_best_practices) ? 1 : 0
name = "cloudtrail-enabled"
source {
owner = "AWS"
source_identifier = "CLOUD_TRAIL_ENABLED"
}
depends_on = [aws_config_configuration_recorder.main]
tags = var.tags
}
# Encryption Rules
resource "aws_config_config_rule" "encrypted_volumes" {
count = var.enable_aws_config && (var.rules.encrypted_volumes || var.enable_security_best_practices) ? 1 : 0
name = "encrypted-volumes"
source {
owner = "AWS"
source_identifier = "ENCRYPTED_VOLUMES"
}
depends_on = [aws_config_configuration_recorder.main]
tags = var.tags
}
# Lambda Rules
resource "aws_config_config_rule" "lambda_function_public_access_prohibited" {
count = var.enable_aws_config && (var.rules.lambda_function_public_access_prohibited || var.enable_security_best_practices) ? 1 : 0
name = "lambda-function-public-access-prohibited"
source {
owner = "AWS"
source_identifier = "LAMBDA_FUNCTION_PUBLIC_ACCESS_PROHIBITED"
}
depends_on = [aws_config_configuration_recorder.main]
tags = var.tags
}
################################################################################
# Outputs
################################################################################
output "config_recorder_id" {
value = var.enable_aws_config ? aws_config_configuration_recorder.main[0].id : null
description = "Config recorder ID"
}
output "config_bucket" {
value = local.config_bucket
description = "S3 bucket for Config snapshots"
}
output "enabled_rules" {
value = var.enable_aws_config ? {
s3_public_read = var.rules.s3_bucket_public_read_prohibited || var.enable_security_best_practices
s3_public_write = var.rules.s3_bucket_public_write_prohibited || var.enable_security_best_practices
s3_ssl_only = var.rules.s3_bucket_ssl_requests_only || var.enable_security_best_practices
ec2_imdsv2 = var.rules.ec2_imdsv2_check || var.enable_security_best_practices
ebs_encryption = var.rules.ec2_ebs_encryption_by_default || var.enable_security_best_practices
restricted_ssh = var.rules.restricted_ssh || var.enable_security_best_practices
iam_root_key = var.rules.iam_root_access_key_check || var.enable_security_best_practices
iam_mfa = var.rules.iam_user_mfa_enabled || var.enable_security_best_practices
access_key_rotation = var.rules.access_keys_rotated || var.enable_security_best_practices
rds_public = var.rules.rds_instance_public_access_check || var.enable_security_best_practices
rds_encrypted = var.rules.rds_storage_encrypted || var.enable_security_best_practices
vpc_flow_logs = var.rules.vpc_flow_logs_enabled || var.enable_security_best_practices
cloudtrail = var.rules.cloudtrail_enabled || var.enable_security_best_practices
} : null
description = "List of enabled Config rules"
}
output "compliance_packs" {
value = {
cis_benchmark = var.enable_cis_benchmark
security_best = var.enable_security_best_practices
pci_dss = var.enable_pci_dss
hipaa = var.enable_hipaa
}
description = "Enabled compliance packs"
}