Best Practices for Security and Compliance with Terraform on AWS

Learn how to implement security best practices and maintain compliance when using Terraform to manage AWS infrastructure

Best Practices for Security and Compliance with Terraform on AWS

Security and compliance are critical aspects of infrastructure management. This guide demonstrates how to implement security best practices and maintain compliance when using Terraform to manage AWS infrastructure.

Video Tutorial

Learn more about implementing Security with Terraform in AWS in this comprehensive video tutorial:

Prerequisites

  • AWS CLI configured with appropriate permissions
  • Terraform installed (version 1.0.0 or later)
  • Basic understanding of AWS security services
  • Understanding of compliance frameworks (e.g., CIS, HIPAA, SOC2)

Project Structure

terraform-security/
├── modules/
│   ├── security/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   └── compliance/
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
├── policies/
│   ├── iam/
│   ├── s3/
│   └── security-groups/
└── compliance/
    ├── cis/
    ├── hipaa/
    └── soc2/

Security Configuration

Create modules/security/main.tf:

# AWS Config
resource "aws_config_configuration_recorder" "main" {
  name     = "${var.project_name}-config-recorder"
  role_arn = aws_iam_role.config_recorder.arn

  recording_group {
    all_supported = true
    include_global_resources = true
  }
}

# AWS Config Rules
resource "aws_config_config_rule" "encrypted_volumes" {
  name = "encrypted-volumes"

  source {
    owner             = "AWS"
    source_identifier = "ENCRYPTED_VOLUMES"
  }

  scope {
    compliance_resource_types = ["AWS::EC2::Volume"]
  }
}

resource "aws_config_config_rule" "root_mfa" {
  name = "root-account-mfa-enabled"

  source {
    owner             = "AWS"
    source_identifier = "ROOT_ACCOUNT_MFA_ENABLED"
  }
}

# GuardDuty
resource "aws_guardduty_detector" "main" {
  enable = true

  datasources {
    s3_logs {
      enable = true
    }
    kubernetes {
      audit_logs {
        enable = true
      }
    }
  }
}

# Security Hub
resource "aws_securityhub_account" "main" {
  enable_default_standards = true
}

# KMS Keys
resource "aws_kms_key" "ebs" {
  description             = "KMS key for EBS encryption"
  deletion_window_in_days = 7
  enable_key_rotation     = true

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "Enable IAM User Permissions"
        Effect = "Allow"
        Principal = {
          AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
        }
        Action   = "kms:*"
        Resource = "*"
      }
    ]
  })

  tags = {
    Name        = "${var.project_name}-ebs-key"
    Environment = var.environment
  }
}

# WAF Web ACL
resource "aws_wafv2_web_acl" "main" {
  name        = "${var.project_name}-web-acl"
  description = "Web ACL for application protection"
  scope       = "REGIONAL"

  default_action {
    allow {}
  }

  rule {
    name     = "AWSManagedRulesCommonRuleSet"
    priority = 1

    override_action {
      none {}
    }

    statement {
      managed_rule_group_statement {
        name        = "AWSManagedRulesCommonRuleSet"
        vendor_name = "AWS"
      }
    }

    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name               = "AWSManagedRulesCommonRuleSetMetric"
      sampled_requests_enabled  = true
    }
  }

  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name               = "WebACLMetric"
    sampled_requests_enabled  = true
  }
}

# IAM Password Policy
resource "aws_iam_account_password_policy" "strict" {
  minimum_password_length        = 14
  require_lowercase_characters   = true
  require_uppercase_characters   = true
  require_numbers               = true
  require_symbols               = true
  allow_users_to_change_password = true
  password_reuse_prevention     = 24
  max_password_age             = 90
}

# CloudTrail
resource "aws_cloudtrail" "main" {
  name                          = "${var.project_name}-trail"
  s3_bucket_name                = aws_s3_bucket.cloudtrail.id
  include_global_service_events = true
  is_multi_region_trail        = true
  enable_logging               = true
  kms_key_id                   = aws_kms_key.cloudtrail.arn

  event_selector {
    read_write_type           = "All"
    include_management_events = true

    data_resource {
      type   = "AWS::S3::Object"
      values = ["arn:aws:s3:::"]
    }
  }

  tags = {
    Name        = "${var.project_name}-trail"
    Environment = var.environment
  }
}

# VPC Flow Logs
resource "aws_flow_log" "main" {
  iam_role_arn    = aws_iam_role.flow_log.arn
  log_destination = aws_cloudwatch_log_group.flow_log.arn
  traffic_type    = "ALL"
  vpc_id          = var.vpc_id

  tags = {
    Name        = "${var.project_name}-flow-log"
    Environment = var.environment
  }
}

Compliance Configuration

Create modules/compliance/main.tf:

# CIS Compliance Rules
resource "aws_config_config_rule" "iam_root_access_key" {
  name = "iam-root-access-key-check"

  source {
    owner             = "AWS"
    source_identifier = "IAM_ROOT_ACCESS_KEY_CHECK"
  }
}

resource "aws_config_config_rule" "cloudtrail_enabled" {
  name = "cloudtrail-enabled"

  source {
    owner             = "AWS"
    source_identifier = "CLOUD_TRAIL_ENABLED"
  }
}

resource "aws_config_config_rule" "incoming_ssh_disabled" {
  name = "restricted-ssh"

  source {
    owner             = "AWS"
    source_identifier = "INCOMING_SSH_DISABLED"
  }
}

# Security Hub Standards
resource "aws_securityhub_standards_subscription" "cis" {
  standards_arn = "arn:aws:securityhub:${data.aws_region.current.name}::standards/cis-aws-foundations-benchmark/v1.2.0"
}

resource "aws_securityhub_standards_subscription" "pci" {
  standards_arn = "arn:aws:securityhub:${data.aws_region.current.name}::standards/pci-dss/v3.2.1"
}

# S3 Bucket Policies
resource "aws_s3_bucket_policy" "secure" {
  bucket = aws_s3_bucket.main.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid       = "EnforceHTTPS"
        Effect    = "Deny"
        Principal = "*"
        Action    = "s3:*"
        Resource = [
          aws_s3_bucket.main.arn,
          "${aws_s3_bucket.main.arn}/*"
        ]
        Condition = {
          Bool = {
            "aws:SecureTransport": "false"
          }
        }
      }
    ]
  })
}

# Security Group Rules
resource "aws_security_group_rule" "egress" {
  type              = "egress"
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.main.id
}

resource "aws_security_group_rule" "ingress_https" {
  type              = "ingress"
  from_port         = 443
  to_port           = 443
  protocol          = "tcp"
  cidr_blocks       = var.allowed_cidrs
  security_group_id = aws_security_group.main.id
}

Security Best Practices

  1. Identity and Access Management

    • Use IAM roles instead of users
    • Implement least privilege
    • Enable MFA
    • Regular access reviews
  2. Data Protection

    • Encrypt data at rest
    • Encrypt data in transit
    • Implement backup policies
    • Use KMS for key management
  3. Network Security

    • Use security groups
    • Implement NACLs
    • Enable VPC Flow Logs
    • Use WAF for web applications
  4. Monitoring and Logging

    • Enable CloudTrail
    • Configure CloudWatch
    • Use AWS Config
    • Enable GuardDuty

Compliance Frameworks

  1. CIS Benchmarks
resource "aws_config_conformance_pack" "cis" {
  name = "cis-conformance-pack"

  template_body = <<EOF
Parameters:
  MaxPasswordAge:
    Default: '90'
    Type: String
Resources:
  IAMPasswordPolicy:
    Type: AWS::Config::ConfigRule
    Properties:
      ConfigRuleName: iam-password-policy
      Source:
        Owner: AWS
        SourceIdentifier: IAM_PASSWORD_POLICY
      InputParameters:
        MaxPasswordAge: ${MaxPasswordAge}
EOF
}
  1. HIPAA Compliance
resource "aws_kms_key" "hipaa" {
  description             = "KMS key for HIPAA compliance"
  deletion_window_in_days = 7
  enable_key_rotation     = true
  policy                 = data.aws_iam_policy_document.hipaa_kms.json
  tags = {
    Compliance = "HIPAA"
  }
}
  1. SOC2 Compliance
resource "aws_cloudwatch_metric_alarm" "unauthorized_api_calls" {
  alarm_name          = "unauthorized-api-calls"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = "1"
  metric_name        = "UnauthorizedAttemptCount"
  namespace          = "CloudTrailMetrics"
  period             = "300"
  statistic          = "Sum"
  threshold          = "1"
  alarm_description  = "Monitoring unauthorized API calls"
  alarm_actions      = [aws_sns_topic.security_alerts.arn]
}

Automated Security Checks

  1. Terraform Compliance
resource "null_resource" "compliance_check" {
  provisioner "local-exec" {
    command = <<EOF
      terraform-compliance \
        -f git:https://github.com/your-org/compliance-policies.git \
        -p terraform.tfplan
    EOF
  }
}
  1. Checkov
# .github/workflows/security-scan.yml
name: Security Scan
on: [pull_request]
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Run Checkov
        uses: bridgecrewio/checkov-action@master
        with:
          directory: .
          framework: terraform

Incident Response

  1. CloudWatch Alarms
resource "aws_cloudwatch_metric_alarm" "root_login" {
  alarm_name          = "root-account-usage"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = "1"
  metric_name        = "RootAccountUsage"
  namespace          = "CloudTrailMetrics"
  period             = "300"
  statistic          = "Sum"
  threshold          = "0"
  alarm_description  = "Monitoring root account usage"
  alarm_actions      = [aws_sns_topic.security_alerts.arn]
}
  1. Lambda Response
resource "aws_lambda_function" "security_response" {
  filename         = "security_response.zip"
  function_name    = "security-incident-response"
  role            = aws_iam_role.lambda_security.arn
  handler         = "index.handler"
  runtime         = "nodejs18.x"

  environment {
    variables = {
      SNS_TOPIC_ARN = aws_sns_topic.security_alerts.arn
    }
  }
}

Conclusion

You’ve learned how to implement security and compliance best practices using Terraform on AWS. This setup provides:

  • Secure infrastructure deployment
  • Compliance with industry standards
  • Automated security checks
  • Incident response capabilities

Remember to:

  • Regularly review security configurations
  • Update compliance policies
  • Monitor security events
  • Document security procedures