Skip to main content
Back to Articles
SecurityJanuary 25, 20269 min read

Compliance-Ready Infrastructure on AWS Guide

Build AWS infrastructure that meets SOC 2, HIPAA, and GDPR compliance requirements with automated controls, audit logging, and security guardrails.

Compliance as Architecture

Compliance is often treated as an afterthought — a checklist exercise before an audit. This approach leads to expensive retroactive fixes, manual evidence collection, and infrastructure that barely passes requirements. A better approach builds compliance controls directly into the infrastructure architecture from day one.

This guide covers practical patterns for building AWS infrastructure that satisfies SOC 2, HIPAA, and GDPR requirements through automated controls and continuous monitoring. These patterns form the core of our security and compliance services.

Foundational AWS Account Structure

AWS Organizations with SCPs

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyRootUser",
      "Effect": "Deny",
      "Action": "*",
      "Resource": "*",
      "Condition": {
        "StringLike": {
          "aws:PrincipalArn": "arn:aws:iam::*:root"
        }
      }
    },
    {
      "Sid": "RequireIMDSv2",
      "Effect": "Deny",
      "Action": "ec2:RunInstances",
      "Resource": "arn:aws:ec2:*:*:instance/*",
      "Condition": {
        "StringNotEquals": {
          "ec2:MetadataHttpTokens": "required"
        }
      }
    },
    {
      "Sid": "DenyUnencryptedS3",
      "Effect": "Deny",
      "Action": "s3:PutObject",
      "Resource": "*",
      "Condition": {
        "StringNotEquals": {
          "s3:x-amz-server-side-encryption": "aws:kms"
        }
      }
    },
    {
      "Sid": "RestrictRegions",
      "Effect": "Deny",
      "NotAction": [
        "iam:*",
        "organizations:*",
        "sts:*",
        "support:*",
        "cloudfront:*"
      ],
      "Resource": "*",
      "Condition": {
        "StringNotEquals": {
          "aws:RequestedRegion": ["us-east-1", "eu-west-1"]
        }
      }
    }
  ]
}

Service Control Policies enforce guardrails across all accounts in the organization. These policies prevent root user access, require IMDSv2 (preventing SSRF-based credential theft), enforce S3 encryption, and restrict deployment to approved regions.

Encryption Everywhere

KMS Key Management

resource "aws_kms_key" "main" {
  description             = "Primary encryption key for production data"
  deletion_window_in_days = 30
  enable_key_rotation     = true
  multi_region            = true

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "KeyAdministration"
        Effect = "Allow"
        Principal = {
          AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/SecurityAdmin"
        }
        Action = [
          "kms:Create*",
          "kms:Describe*",
          "kms:Enable*",
          "kms:List*",
          "kms:Put*",
          "kms:Update*",
          "kms:Revoke*",
          "kms:Disable*",
          "kms:Get*",
          "kms:Delete*",
          "kms:ScheduleKeyDeletion",
          "kms:CancelKeyDeletion"
        ]
        Resource = "*"
      },
      {
        Sid    = "KeyUsage"
        Effect = "Allow"
        Principal = {
          AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/ApplicationRole"
        }
        Action = [
          "kms:Decrypt",
          "kms:DescribeKey",
          "kms:Encrypt",
          "kms:GenerateDataKey*"
        ]
        Resource = "*"
      }
    ]
  })
}

Data at Rest Encryption

# RDS encryption
resource "aws_db_instance" "production" {
  storage_encrypted = true
  kms_key_id       = aws_kms_key.main.arn
}

# EBS encryption (default for all volumes)
resource "aws_ebs_encryption_by_default" "enabled" {
  enabled = true
}

resource "aws_ebs_default_kms_key" "main" {
  key_arn = aws_kms_key.main.arn
}

# S3 bucket encryption
resource "aws_s3_bucket_server_side_encryption_configuration" "main" {
  bucket = aws_s3_bucket.data.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm     = "aws:kms"
      kms_master_key_id = aws_kms_key.main.id
    }
    bucket_key_enabled = true
  }
}

Audit Logging

CloudTrail Configuration

resource "aws_cloudtrail" "organization" {
  name                          = "organization-trail"
  s3_bucket_name                = aws_s3_bucket.audit_logs.id
  is_organization_trail         = true
  is_multi_region_trail         = true
  enable_log_file_validation    = true
  include_global_service_events = true
  kms_key_id                    = aws_kms_key.audit.arn

  cloud_watch_logs_group_arn = "${aws_cloudwatch_log_group.cloudtrail.arn}:*"
  cloud_watch_logs_role_arn  = aws_iam_role.cloudtrail_cloudwatch.arn

  event_selector {
    read_write_type           = "All"
    include_management_events = true

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

  insight_selectors {
    insight_type = "ApiCallRateInsight"
  }

  insight_selectors {
    insight_type = "ApiErrorRateInsight"
  }
}

# Immutable log storage
resource "aws_s3_bucket_object_lock_configuration" "audit_logs" {
  bucket = aws_s3_bucket.audit_logs.id

  rule {
    default_retention {
      mode = "COMPLIANCE"
      days = 365
    }
  }
}

S3 Object Lock in Compliance mode ensures audit logs cannot be deleted or modified, even by the root account. This satisfies SOC 2 and HIPAA requirements for tamper-proof audit trails.

Access Management

Least Privilege IAM Policies

resource "aws_iam_policy" "application" {
  name = "application-policy"

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "ReadOwnBucket"
        Effect = "Allow"
        Action = [
          "s3:GetObject",
          "s3:PutObject",
          "s3:ListBucket"
        ]
        Resource = [
          aws_s3_bucket.app_data.arn,
          "${aws_s3_bucket.app_data.arn}/*"
        ]
      },
      {
        Sid    = "ReadSecrets"
        Effect = "Allow"
        Action = [
          "secretsmanager:GetSecretValue"
        ]
        Resource = "arn:aws:secretsmanager:us-east-1:*:secret:production/*"
      },
      {
        Sid    = "DecryptWithKMS"
        Effect = "Allow"
        Action = [
          "kms:Decrypt",
          "kms:GenerateDataKey"
        ]
        Resource = aws_kms_key.main.arn
      }
    ]
  })
}

MFA Enforcement

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyAllExceptMFASetup",
      "Effect": "Deny",
      "NotAction": [
        "iam:CreateVirtualMFADevice",
        "iam:EnableMFADevice",
        "iam:GetUser",
        "iam:ListMFADevices",
        "iam:ListVirtualMFADevices",
        "iam:ResyncMFADevice",
        "sts:GetSessionToken"
      ],
      "Resource": "*",
      "Condition": {
        "BoolIfExists": {
          "aws:MultiFactorAuthPresent": "false"
        }
      }
    }
  ]
}

Automated Compliance Monitoring

AWS Config Rules

resource "aws_config_config_rule" "encrypted_volumes" {
  name = "encrypted-volumes"
  source {
    owner             = "AWS"
    source_identifier = "ENCRYPTED_VOLUMES"
  }
}

resource "aws_config_config_rule" "s3_encryption" {
  name = "s3-bucket-server-side-encryption-enabled"
  source {
    owner             = "AWS"
    source_identifier = "S3_BUCKET_SERVER_SIDE_ENCRYPTION_ENABLED"
  }
}

resource "aws_config_config_rule" "rds_encrypted" {
  name = "rds-storage-encrypted"
  source {
    owner             = "AWS"
    source_identifier = "RDS_STORAGE_ENCRYPTED"
  }
}

resource "aws_config_config_rule" "mfa_enabled" {
  name = "iam-user-mfa-enabled"
  source {
    owner             = "AWS"
    source_identifier = "IAM_USER_MFA_ENABLED"
  }
}

resource "aws_config_config_rule" "root_mfa" {
  name = "root-account-mfa-enabled"
  source {
    owner             = "AWS"
    source_identifier = "ROOT_ACCOUNT_MFA_ENABLED"
  }
}

Security Hub Integration

Enable AWS Security Hub with compliance standards:

resource "aws_securityhub_account" "main" {}

resource "aws_securityhub_standards_subscription" "cis" {
  depends_on    = [aws_securityhub_account.main]
  standards_arn = "arn:aws:securityhub:::ruleset/cis-aws-foundations-benchmark/v/1.4.0"
}

resource "aws_securityhub_standards_subscription" "pci" {
  depends_on    = [aws_securityhub_account.main]
  standards_arn = "arn:aws:securityhub:::standards/pci-dss/v/3.2.1"
}

GDPR-Specific Controls

Data Classification and Retention

resource "aws_s3_bucket_lifecycle_configuration" "pii_data" {
  bucket = aws_s3_bucket.pii.id

  rule {
    id     = "delete-after-retention"
    status = "Enabled"

    expiration {
      days = 730  # 2-year retention for PII
    }

    transition {
      days          = 90
      storage_class = "GLACIER"
    }
  }
}

Right to Erasure Implementation

Build automated data deletion pipelines triggered by customer requests, covering all data stores — RDS, S3, Elasticsearch, and backups. Log every deletion action for audit purposes.

Compliance Evidence Automation

Automate evidence collection for audits:

#!/bin/bash
# Quarterly compliance evidence collection
REPORT_DATE=$(date +%Y-Q$(( ($(date +%-m) - 1) / 3 + 1 )))

# IAM access review
aws iam generate-credential-report
aws iam get-credential-report --output text --query Content | base64 -d > "iam-report-${REPORT_DATE}.csv"

# Config compliance summary
aws configservice get-compliance-summary-by-config-rule > "config-compliance-${REPORT_DATE}.json"

# Security Hub findings
aws securityhub get-findings --filters '{"ComplianceStatus":[{"Value":"FAILED","Comparison":"EQUALS"}]}' > "securityhub-findings-${REPORT_DATE}.json"

# CloudTrail digest validation
aws cloudtrail validate-logs --trail-arn arn:aws:cloudtrail:us-east-1:123456789:trail/organization-trail --start-time $(date -d '-90 days' +%Y-%m-%dT%H:%M:%SZ)

Best Practices

  • Treat compliance controls as code — version control all policies and configurations
  • Automate evidence collection to reduce audit preparation from weeks to hours
  • Use AWS Organizations with SCPs to enforce guardrails across all accounts
  • Enable CloudTrail with log file validation and immutable storage
  • Rotate encryption keys automatically with KMS key rotation
  • Conduct quarterly access reviews and revoke unused permissions
  • Integrate compliance monitoring into your infrastructure management workflows

Building compliance into your architecture from the start costs a fraction of retrofitting it later. The automated controls and continuous monitoring described here satisfy auditors while reducing operational burden on engineering teams.

Need help with this?

Our team handles this kind of work daily. Let us take care of your infrastructure.