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.
Related Articles
How to Detect and Respond to a Compromised Linux Server
A practical incident response guide for Linux servers: identifying signs of compromise, initial triage, evidence preservation, containment, rootkit detection, and writing an incident report.
SecurityAWS WAF Configuration for Web Application Security
Deploy and configure AWS WAF with managed rule groups, custom rules, rate limiting, and bot control to protect web applications from common threats.
SecurityKubernetes Network Policies for Microservices
Implement zero-trust networking in Kubernetes with network policies that control pod-to-pod traffic, namespace isolation, and egress filtering.