Production-grade AWS environments provisioned with Terraform and CloudFormation — enforcing IAM least-privilege, KMS encryption at rest and in transit, multi-tier VPC segmentation, and security group hardening. Zero configuration drift between environments.
┌───────────────────────────────────────────────────────────────────────┐
│ Secure AWS Infrastructure │
│ (us-east-1) │
│ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ VPC 10.0.0.0/16 │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────┐ │ │
│ │ │ PUBLIC TIER (ALB, NAT Gateways) │ │ │
│ │ │ 10.0.0/24 │ 10.0.1/24 │ 10.0.2/24 │ │ │
│ │ │ ▼ │ │ │
│ │ │ [ ALB — HTTPS only from internet ] │ │ │
│ │ └──────────────────────┬───────────────────────────────┘ │ │
│ │ │ port 8080 only │ │
│ │ ┌──────────────────────▼───────────────────────────────┐ │ │
│ │ │ PRIVATE APP TIER (EC2, EKS) │ │ │
│ │ │ 10.0.10/24 │ 10.0.11/24 │ 10.0.12/24 │ │ │
│ │ │ ▼ │ │ │
│ │ │ [ EC2 — SSM access, no SSH, KMS-encrypted EBS ] │ │ │
│ │ └──────────────────────┬───────────────────────────────┘ │ │
│ │ │ port 5432 only │ │
│ │ ┌──────────────────────▼───────────────────────────────┐ │ │
│ │ │ PRIVATE DATA TIER (RDS, ElastiCache) │ │ │
│ │ │ 10.0.20/24 │ 10.0.21/24 │ 10.0.22/24 │ │ │
│ │ │ NO internet route — fully isolated │ │ │
│ │ └──────────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────┐ ┌───────────┐ ┌─────────────┐ ┌──────────────┐ │
│ │ KMS Keys │ │ GuardDuty │ │ Security Hub │ │ CloudTrail │ │
│ │ (EC2/S3/ │ │(threat │ │ (CIS + AWS │ │ (multi-region│ │
│ │ RDS/Sec) │ │ detection)│ │ best prac.) │ │ all APIs) │ │
│ └───────────┘ └───────────┘ └─────────────┘ └──────────────┘ │
│ │
│ ┌───────────┐ ┌───────────┐ ┌─────────────────────────────────┐ │
│ │AWS Config │ │ VPC Flow │ │ Audit S3 Bucket │ │
│ │(15+ rules,│ │ Logs │ │ (KMS-encrypted, versioned, │ │
│ │ drift det)│ │(90-day │ │ 7yr retention, HTTPS-only) │ │
│ └───────────┘ │ retention)│ └─────────────────────────────────┘ │
│ └───────────┘ │
└───────────────────────────────────────────────────────────────────────┘
Layer 1 — Network
└── 3-tier VPC (public / private-app / private-data)
└── Security groups: SG-to-SG references only (no broad CIDRs)
└── NAT Gateways per AZ (no internet route in data tier)
└── VPC Flow Logs (ALL traffic, 90-day retention)
└── VPC Endpoints for S3 and SSM (no internet traversal)
Layer 2 — Identity & Access
└── IAM least-privilege roles per service
└── No SSH keys — SSM Session Manager only
└── Break-glass admin role: MFA required, 1-hour max session
└── Guardrail SCPs: deny root usage, deny region hopping
└── Service-linked conditions on all assume-role policies
Layer 3 — Encryption
└── KMS CMKs per service: EC2/EBS, S3, RDS, Secrets Manager
└── Automatic key rotation enabled on all CMKs
└── EBS encryption by default (account-level)
└── S3 SSE-KMS on all buckets, HTTPS-only bucket policy
└── KMS key policy: deny deletion without MFA
Layer 4 — Audit & Detection
└── CloudTrail: multi-region, all APIs, log file validation
└── CloudTrail Insights: ApiCallRate + ApiError anomalies
└── AWS Config: 15 managed rules, continuous recording
└── GuardDuty: EC2, S3, EKS, malware scanning
└── Security Hub: CIS benchmark + AWS best practices
Layer 5 — Compliance (drift prevention)
└── Terraform state with S3 + DynamoDB locking
└── AWS Config rules fire on any non-compliant change
└── Guardrail policy denies disabling CloudTrail
03-secure-aws-infrastructure/
├── terraform/
│ ├── main.tf # Module orchestration
│ ├── variables.tf # All inputs with validation
│ └── modules/
│ ├── kms/main.tf # 4 CMKs (EC2, S3, RDS, Secrets)
│ ├── vpc/main.tf # 3-tier VPC, flow logs, VPC endpoints
│ ├── iam/main.tf # Least-privilege roles, guardrails
│ ├── security/main.tf # SGs, CloudTrail, Config, GuardDuty
│ └── compute/main.tf # EC2 ASG (no SSH, SSM only)
├── cloudformation/
│ └── template.yaml # Full CFN equivalent
├── scripts/
│ └── security_audit.py # Automated security posture assessment
└── README.md
cd terraform
# Initialise
terraform init
# Deploy dev environment
terraform apply \
-var="environment=dev" \
-var="project_name=secure-infra"
# Deploy prod (stricter settings auto-applied)
terraform apply \
-var="environment=prod" \
-var="project_name=secure-infra"aws cloudformation create-stack \
--stack-name secure-infra-dev \
--template-body file://cloudformation/template.yaml \
--parameters \
ParameterKey=Environment,ParameterValue=dev \
ParameterKey=ProjectName,ParameterValue=secure-infra \
--capabilities CAPABILITY_NAMED_IAM \
--region us-east-1pip install boto3
# Audit your account
python scripts/security_audit.py --region us-east-1
# Save report to file
python scripts/security_audit.py --output security-report.json
# Exit code 0 = score >= 80, exit code 1 = below 80Sample output:
[ IAM ]
✅ Root MFA: enabled
✅ Password policy: strong
[ EC2 ]
⚠️ Default VPC: present (consider removing)
✅ EBS default encryption: enabled
[ S3 ]
✅ S3 public access blocks: 0 issues
✅ S3 encryption: 0 unencrypted
[ CloudTrail ]
✅ Multi-region trail: 1 found
✅ Log validation: 1 trails
══════════════════════════════
AUDIT COMPLETE — Score: 92/100
✅ PASS: 9 ❌ FAIL: 0 ⚠️ WARN: 1
Why separate KMS keys per service? A single compromised key doesn't expose all data. EC2, S3, RDS, and Secrets Manager each have their own CMK with independent rotation schedules and access policies.
Why SSM Session Manager instead of SSH? No inbound port 22, no key pairs to manage, no key rotation risk. All sessions are logged to CloudWatch and S3. Fully auditable.
Why a 3-tier VPC? The data tier has zero internet route — even if an EC2 instance is fully compromised, the attacker cannot exfiltrate data directly to the internet from the database tier.
Why GuardDuty + Security Hub + Config together? They serve different purposes. Config catches drift (resource changed from desired state). GuardDuty detects active threats (unusual API calls, compromised credentials). Security Hub aggregates both into a unified compliance score against CIS and AWS standards.
# Terraform
terraform destroy
# CloudFormation
aws cloudformation delete-stack --stack-name secure-infra-dev
# Note: KMS keys have 30-day deletion window — this is intentional- 01-self-healing-infrastructure — AIOps self-healing
- 02-observability-ml-alerting — Full observability stack
- 04-kubernetes-orchestration — EKS zero-downtime deployments
- 05-devsecops-pipeline — CI/CD with SonarQube and Trivy
Built by Thomas Asamba | github.com/thomasasamba-bot