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
-
Identity and Access Management
- Use IAM roles instead of users
- Implement least privilege
- Enable MFA
- Regular access reviews
-
Data Protection
- Encrypt data at rest
- Encrypt data in transit
- Implement backup policies
- Use KMS for key management
-
Network Security
- Use security groups
- Implement NACLs
- Enable VPC Flow Logs
- Use WAF for web applications
-
Monitoring and Logging
- Enable CloudTrail
- Configure CloudWatch
- Use AWS Config
- Enable GuardDuty
Compliance Frameworks
- 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
}
- 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"
}
}
- 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
- 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
}
}
- 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
- 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]
}
- 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