HIPAA-Compliant AWS Architecture: Reference Design for Healthcare Applications
Comprehensive guide to building HIPAA-compliant applications on AWS. Learn the reference architecture, required services, encryption strategies, and Terraform implementation for healthcare workloads.
Healthcare organizations moving to the cloud face a critical question: How do we build on AWS while maintaining HIPAA compliance?
The answer isn't as simple as "use these services and check these boxes." HIPAA compliance on AWS requires a carefully architected solution that addresses encryption, access controls, audit logging, disaster recovery, and Business Associate Agreements—all while maintaining the performance and scalability benefits of cloud infrastructure.
This comprehensive guide provides a reference architecture for HIPAA-compliant healthcare applications on AWS, complete with service selection guidance, security configurations, and production-ready Terraform code you can adapt for your organization.
HIPAA Compliance Fundamentals on AWS
The Shared Responsibility Model
AWS operates under a shared responsibility model for HIPAA compliance:
AWS Responsibilities (Security OF the Cloud):
- Physical security of data centers
- Hardware and network infrastructure
- Hypervisor and virtualization layer security
- Managed service security (RDS, S3, etc.)
- HIPAA-eligible service compliance certifications
Your Responsibilities (Security IN the Cloud):
- Data encryption (at rest and in transit)
- Access controls and identity management
- Network security (VPCs, security groups, NACLs)
- Application-level security
- Audit logging and monitoring
- Incident response procedures
- Business Associate Agreement (BAA) with AWS
Critical Point: AWS provides HIPAA-eligible services, but YOU must configure them correctly to achieve HIPAA compliance.
AWS Business Associate Agreement (BAA)
Before storing, processing, or transmitting PHI on AWS, you must execute a Business Associate Agreement with AWS. This is a legal requirement under HIPAA.
How to Sign AWS BAA:
- Log into AWS Artifact (via AWS Console → Compliance → Artifact)
- Review and accept the AWS BAA
- Download executed BAA for your compliance records
- Ensure all accounts storing PHI have signed BAAs
HIPAA-Eligible AWS Services (as of 2026):
- Compute: EC2, ECS, EKS, Lambda, Batch
- Storage: S3, EBS, EFS, FSx
- Database: RDS (PostgreSQL, MySQL, Oracle, SQL Server), DynamoDB, Aurora, DocumentDB, Neptune, ElastiCache (Redis), MemoryDB for Redis
- Networking: VPC, CloudFront, Route 53, Direct Connect, PrivateLink
- Security: KMS, Secrets Manager, Certificate Manager, CloudHSM, WAF, Shield
- Analytics: Athena, EMR, Kinesis, Glue, Redshift
- Machine Learning: SageMaker
- Monitoring: CloudWatch, CloudTrail, Config, GuardDuty, Security Hub
- Management: Systems Manager, CloudFormation
NOT HIPAA-Eligible (do not use for PHI):
- Lightsail, Elastic Beanstalk (limited support), WorkSpaces, AppStream, Comprehend Medical (unless BAA coverage confirmed), most AI/ML services without explicit BAA coverage
HIPAA Security Rule Requirements
Your AWS architecture must implement technical safeguards from HIPAA Security Rule:
| HIPAA Requirement | AWS Implementation |
|---|---|
| Access Control (§164.312(a)(1)) | IAM policies, MFA, role-based access, least privilege |
| Audit Controls (§164.312(b)) | CloudTrail, VPC Flow Logs, S3 access logs, application logs |
| Integrity (§164.312(c)(1)) | S3 versioning, RDS snapshots, checksums, object lock |
| Person/Entity Authentication (§164.312(d)) | IAM, Cognito, SSO integration, MFA enforcement |
| Transmission Security (§164.312(e)(1)) | TLS 1.2+, VPN, Direct Connect, PrivateLink |
| Encryption (§164.312(a)(2)(iv)) | KMS encryption at rest, TLS in transit, field-level encryption |
Reference Architecture: HIPAA-Compliant Healthcare Application
High-Level Architecture Diagram
┌─────────────────────────────────────────────────────────────────────────┐
│ INTERNET │
└────────────────────────────────┬────────────────────────────────────────┘
│
▼
┌────────────────────────┐
│ Amazon Route 53 │
│ (DNS + Health Checks)│
└────────────┬───────────┘
│
▼
┌────────────────────────┐
│ AWS WAF │
│ (Web Application │
│ Firewall) │
└────────────┬───────────┘
│
▼
┌────────────────────────────────────────────────────────────────────────┐
│ AWS ACCOUNT (PRODUCTION) │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ Amazon CloudFront │ │
│ │ (CDN with TLS 1.2+, Field-level encryption) │ │
│ └────────────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ VPC (10.0.0.0/16) │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────────────────┐ │ │
│ │ │ PUBLIC SUBNETS (10.0.1.0/24, 10.0.2.0/24) │ │ │
│ │ │ - NAT Gateways (for private subnet internet access) │ │ │
│ │ │ - Application Load Balancer │ │ │
│ │ │ - Bastion Host (locked down, session logging) │ │ │
│ │ └────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────────────────┐ │ │
│ │ │ PRIVATE SUBNETS - APPLICATION TIER │ │ │
│ │ │ (10.0.10.0/24, 10.0.11.0/24) │ │ │
│ │ │ │ │ │
│ │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │
│ │ │ │ ECS Fargate │ │ ECS Fargate │ │ │ │
│ │ │ │ Healthcare API │ │ Healthcare API │ │ │ │
│ │ │ │ (Container) │ │ (Container) │ │ │ │
│ │ │ └──────────────────┘ └──────────────────┘ │ │ │
│ │ │ ↓ ↓ │ │ │
│ │ │ ┌─────────────────────────────────────────────┐ │ │ │
│ │ │ │ ElastiCache Redis (Encrypted) │ │ │ │
│ │ │ │ - Session storage (no PHI) │ │ │ │
│ │ │ │ - Rate limiting cache │ │ │ │
│ │ │ └─────────────────────────────────────────────┘ │ │ │
│ │ └────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────────────────┐ │ │
│ │ │ PRIVATE SUBNETS - DATABASE TIER │ │ │
│ │ │ (10.0.20.0/24, 10.0.21.0/24) │ │ │
│ │ │ │ │ │
│ │ │ ┌─────────────────────────────────────────────┐ │ │ │
│ │ │ │ Amazon RDS PostgreSQL (Multi-AZ) │ │ │ │
│ │ │ │ - Encrypted with KMS │ │ │ │
│ │ │ │ - Automated backups (7-year retention) │ │ │ │
│ │ │ │ - Read replicas in separate AZ │ │ │ │
│ │ │ └─────────────────────────────────────────────┘ │ │ │
│ │ └────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────────────────┐ │ │
│ │ │ SECURITY & MONITORING │ │ │
│ │ │ - VPC Flow Logs → CloudWatch Logs │ │ │
│ │ │ - GuardDuty (threat detection) │ │ │
│ │ │ - Security Hub (compliance dashboards) │ │ │
│ │ │ - Config (resource compliance monitoring) │ │ │
│ │ └────────────────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ SUPPORTING SERVICES │ │
│ │ - S3 Buckets (PHI storage, encrypted, versioned, lifecycle) │ │
│ │ - KMS (encryption keys with automatic rotation) │ │
│ │ - Secrets Manager (database credentials, API keys) │ │
│ │ - CloudTrail (all API calls logged, immutable) │ │
│ │ - CloudWatch Alarms (security + performance monitoring) │ │
│ │ - SNS Topics (alert notifications to security team) │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ DISASTER RECOVERY (DR) - SEPARATE REGION │ │
│ │ - RDS cross-region automated backups │ │
│ │ - S3 cross-region replication (PHI buckets) │ │
│ │ - Infrastructure as Code (Terraform) for rapid rebuild │ │
│ └──────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────┘
Architecture Principles
1. Defense in Depth: Multiple layers of security (WAF → ALB → Security Groups → NACLs → IAM → Encryption)
2. Least Privilege Access: Every resource has minimum required permissions (IAM roles, security groups)
3. Encryption Everywhere: Data encrypted at rest (KMS) and in transit (TLS 1.2+)
4. Immutable Audit Trail: All actions logged to CloudTrail, logs stored in immutable S3 buckets
5. High Availability: Multi-AZ deployment for zero downtime during maintenance or failures
6. Disaster Recovery: Cross-region backups, RTO < 4 hours, RPO < 1 hour
Network Architecture: VPC Design
VPC Configuration
# terraform/network.tf
# VPC with DNS support (required for RDS, PrivateLink)
resource "aws_vpc" "healthcare" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "healthcare-vpc-${var.environment}"
HIPAA = "true"
Environment = var.environment
}
}
# Internet Gateway (for public subnets)
resource "aws_internet_gateway" "healthcare" {
vpc_id = aws_vpc.healthcare.id
tags = {
Name = "healthcare-igw-${var.environment}"
}
}
# Public Subnets (for ALB, NAT Gateway, Bastion)
resource "aws_subnet" "public" {
count = 2
vpc_id = aws_vpc.healthcare.id
cidr_block = "10.0.${count.index + 1}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
tags = {
Name = "healthcare-public-subnet-${count.index + 1}-${var.environment}"
Tier = "Public"
}
}
# NAT Gateways (one per AZ for high availability)
resource "aws_eip" "nat" {
count = 2
domain = "vpc"
tags = {
Name = "healthcare-nat-eip-${count.index + 1}-${var.environment}"
}
}
resource "aws_nat_gateway" "healthcare" {
count = 2
allocation_id = aws_eip.nat[count.index].id
subnet_id = aws_subnet.public[count.index].id
tags = {
Name = "healthcare-nat-gw-${count.index + 1}-${var.environment}"
}
depends_on = [aws_internet_gateway.healthcare]
}
# Private Subnets - Application Tier
resource "aws_subnet" "private_app" {
count = 2
vpc_id = aws_vpc.healthcare.id
cidr_block = "10.0.${count.index + 10}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = {
Name = "healthcare-private-app-subnet-${count.index + 1}-${var.environment}"
Tier = "Application"
}
}
# Private Subnets - Database Tier (isolated from app tier)
resource "aws_subnet" "private_db" {
count = 2
vpc_id = aws_vpc.healthcare.id
cidr_block = "10.0.${count.index + 20}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = {
Name = "healthcare-private-db-subnet-${count.index + 1}-${var.environment}"
Tier = "Database"
HIPAA = "true"
DataClass = "PHI"
}
}
# Route Table - Public Subnets
resource "aws_route_table" "public" {
vpc_id = aws_vpc.healthcare.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.healthcare.id
}
tags = {
Name = "healthcare-public-rt-${var.environment}"
}
}
resource "aws_route_table_association" "public" {
count = 2
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
# Route Tables - Private App Subnets (route through NAT Gateway)
resource "aws_route_table" "private_app" {
count = 2
vpc_id = aws_vpc.healthcare.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.healthcare[count.index].id
}
tags = {
Name = "healthcare-private-app-rt-${count.index + 1}-${var.environment}"
}
}
resource "aws_route_table_association" "private_app" {
count = 2
subnet_id = aws_subnet.private_app[count.index].id
route_table_id = aws_route_table.private_app[count.index].id
}
# Route Tables - Private DB Subnets (NO internet access)
resource "aws_route_table" "private_db" {
vpc_id = aws_vpc.healthcare.id
# NO default route - database tier has no internet access
tags = {
Name = "healthcare-private-db-rt-${var.environment}"
}
}
resource "aws_route_table_association" "private_db" {
count = 2
subnet_id = aws_subnet.private_db[count.index].id
route_table_id = aws_route_table.private_db.id
}
# VPC Flow Logs (HIPAA audit requirement)
resource "aws_flow_log" "healthcare" {
iam_role_arn = aws_iam_role.vpc_flow_logs.arn
log_destination = aws_cloudwatch_log_group.vpc_flow_logs.arn
traffic_type = "ALL"
vpc_id = aws_vpc.healthcare.id
tags = {
Name = "healthcare-vpc-flow-logs-${var.environment}"
HIPAA = "true"
}
}
resource "aws_cloudwatch_log_group" "vpc_flow_logs" {
name = "/aws/vpc/healthcare-${var.environment}"
retention_in_days = 2555 # 7 years (HIPAA retention requirement)
kms_key_id = aws_kms_key.logs.arn
tags = {
HIPAA = "true"
}
}
# VPC Endpoints (avoid internet for AWS service access)
resource "aws_vpc_endpoint" "s3" {
vpc_id = aws_vpc.healthcare.id
service_name = "com.amazonaws.${var.aws_region}.s3"
route_table_ids = concat(
aws_route_table.private_app[*].id,
[aws_route_table.private_db.id]
)
tags = {
Name = "healthcare-s3-endpoint-${var.environment}"
}
}
resource "aws_vpc_endpoint" "secrets_manager" {
vpc_id = aws_vpc.healthcare.id
service_name = "com.amazonaws.${var.aws_region}.secretsmanager"
vpc_endpoint_type = "Interface"
subnet_ids = aws_subnet.private_app[*].id
security_group_ids = [aws_security_group.vpc_endpoints.id]
private_dns_enabled = true
tags = {
Name = "healthcare-secretsmanager-endpoint-${var.environment}"
}
}
Security Groups (Principle of Least Privilege)
# terraform/security-groups.tf
# ALB Security Group (public-facing)
resource "aws_security_group" "alb" {
name = "healthcare-alb-${var.environment}"
description = "Security group for Application Load Balancer"
vpc_id = aws_vpc.healthcare.id
# Inbound HTTPS only
ingress {
description = "HTTPS from internet"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# Outbound to ECS containers only
egress {
description = "To ECS containers"
from_port = 8080
to_port = 8080
protocol = "tcp"
security_groups = [aws_security_group.ecs_tasks.id]
}
tags = {
Name = "healthcare-alb-sg-${var.environment}"
}
}
# ECS Tasks Security Group
resource "aws_security_group" "ecs_tasks" {
name = "healthcare-ecs-tasks-${var.environment}"
description = "Security group for ECS tasks"
vpc_id = aws_vpc.healthcare.id
# Inbound from ALB only
ingress {
description = "From ALB"
from_port = 8080
to_port = 8080
protocol = "tcp"
security_groups = [aws_security_group.alb.id]
}
# Outbound to RDS
egress {
description = "To RDS"
from_port = 5432
to_port = 5432
protocol = "tcp"
security_groups = [aws_security_group.rds.id]
}
# Outbound to ElastiCache
egress {
description = "To ElastiCache Redis"
from_port = 6379
to_port = 6379
protocol = "tcp"
security_groups = [aws_security_group.elasticache.id]
}
# Outbound HTTPS for AWS API calls (via VPC endpoints)
egress {
description = "To AWS services via VPC endpoints"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = [aws_vpc.healthcare.cidr_block]
}
tags = {
Name = "healthcare-ecs-tasks-sg-${var.environment}"
}
}
# RDS Security Group
resource "aws_security_group" "rds" {
name = "healthcare-rds-${var.environment}"
description = "Security group for RDS PostgreSQL"
vpc_id = aws_vpc.healthcare.id
# Inbound from ECS tasks only
ingress {
description = "PostgreSQL from ECS tasks"
from_port = 5432
to_port = 5432
protocol = "tcp"
security_groups = [aws_security_group.ecs_tasks.id]
}
# NO outbound rules (database doesn't need to initiate connections)
tags = {
Name = "healthcare-rds-sg-${var.environment}"
HIPAA = "true"
DataClass = "PHI"
}
}
# ElastiCache Redis Security Group
resource "aws_security_group" "elasticache" {
name = "healthcare-elasticache-${var.environment}"
description = "Security group for ElastiCache Redis"
vpc_id = aws_vpc.healthcare.id
# Inbound from ECS tasks only
ingress {
description = "Redis from ECS tasks"
from_port = 6379
to_port = 6379
protocol = "tcp"
security_groups = [aws_security_group.ecs_tasks.id]
}
tags = {
Name = "healthcare-elasticache-sg-${var.environment}"
}
}
Data Storage: RDS PostgreSQL with Encryption
RDS Configuration for HIPAA Compliance
# terraform/rds.tf
# KMS Key for RDS Encryption
resource "aws_kms_key" "rds" {
description = "KMS key for RDS encryption - ${var.environment}"
deletion_window_in_days = 30
enable_key_rotation = true
tags = {
Name = "healthcare-rds-kms-${var.environment}"
HIPAA = "true"
DataClass = "PHI"
}
}
resource "aws_kms_alias" "rds" {
name = "alias/healthcare-rds-${var.environment}"
target_key_id = aws_kms_key.rds.key_id
}
# DB Subnet Group (must span multiple AZs)
resource "aws_db_subnet_group" "healthcare" {
name = "healthcare-db-subnet-group-${var.environment}"
subnet_ids = aws_subnet.private_db[*].id
tags = {
Name = "healthcare-db-subnet-group-${var.environment}"
HIPAA = "true"
}
}
# DB Parameter Group (enforce SSL connections)
resource "aws_db_parameter_group" "healthcare" {
name = "healthcare-pg14-${var.environment}"
family = "postgres14"
# Require SSL for all connections (HIPAA transmission security)
parameter {
name = "rds.force_ssl"
value = "1"
}
# Log all DDL statements (audit trail)
parameter {
name = "log_statement"
value = "ddl"
}
# Log all connections/disconnections
parameter {
name = "log_connections"
value = "1"
}
parameter {
name = "log_disconnections"
value = "1"
}
tags = {
HIPAA = "true"
}
}
# RDS PostgreSQL Instance (Multi-AZ for HA)
resource "aws_db_instance" "healthcare" {
identifier = "healthcare-db-${var.environment}"
engine = "postgres"
engine_version = "14.9"
instance_class = "db.r6g.xlarge" # Graviton2 for better price/performance
# Storage
allocated_storage = 100
max_allocated_storage = 1000 # Auto-scaling enabled
storage_type = "gp3"
storage_encrypted = true
kms_key_id = aws_kms_key.rds.arn
# Database
db_name = "healthcare"
username = "postgres"
password = random_password.db_master_password.result
port = 5432
# High Availability
multi_az = true # CRITICAL for production
db_subnet_group_name = aws_db_subnet_group.healthcare.name
vpc_security_group_ids = [aws_security_group.rds.id]
publicly_accessible = false
# Backups (HIPAA requires 7-year retention)
backup_retention_period = 35 # 35 days automated backups
backup_window = "03:00-04:00" # UTC
maintenance_window = "Mon:04:00-Mon:05:00"
# Additional backups via AWS Backup for 7-year retention
copy_tags_to_snapshot = true
skip_final_snapshot = false
final_snapshot_identifier = "healthcare-db-final-${var.environment}-${formatdate("YYYY-MM-DD-hhmm", timestamp())}"
# Monitoring
enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"]
monitoring_interval = 60
monitoring_role_arn = aws_iam_role.rds_monitoring.arn
performance_insights_enabled = true
performance_insights_kms_key_id = aws_kms_key.rds.arn
performance_insights_retention_period = 731 # 2 years
# Security
deletion_protection = true # Prevent accidental deletion
parameter_group_name = aws_db_parameter_group.healthcare.name
iam_database_authentication_enabled = true
tags = {
Name = "healthcare-db-${var.environment}"
HIPAA = "true"
DataClass = "PHI"
}
lifecycle {
prevent_destroy = true # Extra safety for production
}
}
# Store master password in Secrets Manager
resource "random_password" "db_master_password" {
length = 32
special = true
}
resource "aws_secretsmanager_secret" "db_master_password" {
name = "healthcare/db/master-password-${var.environment}"
recovery_window_in_days = 30
kms_key_id = aws_kms_key.secrets.arn
tags = {
HIPAA = "true"
}
}
resource "aws_secretsmanager_secret_version" "db_master_password" {
secret_id = aws_secretsmanager_secret.db_master_password.id
secret_string = jsonencode({
username = aws_db_instance.healthcare.username
password = random_password.db_master_password.result
engine = "postgres"
host = aws_db_instance.healthcare.address
port = aws_db_instance.healthcare.port
dbname = aws_db_instance.healthcare.db_name
})
}
# Read Replica (for reporting queries, reduces load on primary)
resource "aws_db_instance" "healthcare_read_replica" {
identifier = "healthcare-db-replica-${var.environment}"
replicate_source_db = aws_db_instance.healthcare.identifier
instance_class = "db.r6g.large"
publicly_accessible = false
skip_final_snapshot = true
vpc_security_group_ids = [aws_security_group.rds.id]
# Inherit encryption from source
storage_encrypted = true
tags = {
Name = "healthcare-db-replica-${var.environment}"
HIPAA = "true"
DataClass = "PHI"
Purpose = "ReadReplica"
}
}
# AWS Backup Plan (7-year retention for HIPAA)
resource "aws_backup_plan" "healthcare_db" {
name = "healthcare-db-backup-plan-${var.environment}"
# Daily backups retained for 7 years
rule {
rule_name = "daily-7year-retention"
target_vault_name = aws_backup_vault.healthcare.name
schedule = "cron(0 5 * * ? *)" # 5 AM UTC daily
lifecycle {
cold_storage_after = 90 # Move to Glacier after 90 days
delete_after = 2555 # 7 years in days
}
copy_action {
destination_vault_arn = aws_backup_vault.healthcare_dr.arn
lifecycle {
cold_storage_after = 90
delete_after = 2555
}
}
}
}
resource "aws_backup_selection" "healthcare_db" {
name = "healthcare-db-backup-selection-${var.environment}"
plan_id = aws_backup_plan.healthcare_db.id
iam_role_arn = aws_iam_role.backup.arn
resources = [
aws_db_instance.healthcare.arn
]
}
resource "aws_backup_vault" "healthcare" {
name = "healthcare-backup-vault-${var.environment}"
kms_key_arn = aws_kms_key.backup.arn
tags = {
HIPAA = "true"
}
}
# DR vault in separate region
resource "aws_backup_vault" "healthcare_dr" {
provider = aws.dr_region
name = "healthcare-backup-vault-dr-${var.environment}"
kms_key_arn = aws_kms_key.backup_dr.arn
tags = {
HIPAA = "true"
Purpose = "DisasterRecovery"
}
}
Application Layer: ECS Fargate
ECS Configuration
# terraform/ecs.tf
# ECS Cluster
resource "aws_ecs_cluster" "healthcare" {
name = "healthcare-cluster-${var.environment}"
setting {
name = "containerInsights"
value = "enabled" # Enhanced monitoring
}
configuration {
execute_command_configuration {
kms_key_id = aws_kms_key.ecs.arn
logging = "OVERRIDE"
log_configuration {
cloud_watch_log_group_name = aws_cloudwatch_log_group.ecs_exec.name
}
}
}
tags = {
Name = "healthcare-cluster-${var.environment}"
HIPAA = "true"
}
}
# ECS Task Definition
resource "aws_ecs_task_definition" "healthcare_api" {
family = "healthcare-api-${var.environment}"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = "1024"
memory = "2048"
execution_role_arn = aws_iam_role.ecs_execution.arn
task_role_arn = aws_iam_role.ecs_task.arn
container_definitions = jsonencode([
{
name = "healthcare-api"
image = "${aws_ecr_repository.healthcare_api.repository_url}:${var.app_version}"
essential = true
portMappings = [
{
containerPort = 8080
protocol = "tcp"
}
]
environment = [
{
name = "ENVIRONMENT"
value = var.environment
},
{
name = "AWS_REGION"
value = var.aws_region
}
]
secrets = [
{
name = "DATABASE_URL"
valueFrom = "${aws_secretsmanager_secret.db_master_password.arn}:engine::"
},
{
name = "REDIS_URL"
valueFrom = aws_secretsmanager_secret.redis_connection.arn
}
]
logConfiguration = {
logDriver = "awslogs"
options = {
"awslogs-group" = aws_cloudwatch_log_group.ecs_tasks.name
"awslogs-region" = var.aws_region
"awslogs-stream-prefix" = "healthcare-api"
}
}
healthCheck = {
command = ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"]
interval = 30
timeout = 5
retries = 3
startPeriod = 60
}
}
])
tags = {
Name = "healthcare-api-task-${var.environment}"
HIPAA = "true"
}
}
# ECS Service
resource "aws_ecs_service" "healthcare_api" {
name = "healthcare-api-${var.environment}"
cluster = aws_ecs_cluster.healthcare.id
task_definition = aws_ecs_task_definition.healthcare_api.arn
desired_count = 2 # Minimum 2 for HA
launch_type = "FARGATE"
network_configuration {
subnets = aws_subnet.private_app[*].id
security_groups = [aws_security_group.ecs_tasks.id]
assign_public_ip = false
}
load_balancer {
target_group_arn = aws_lb_target_group.healthcare_api.arn
container_name = "healthcare-api"
container_port = 8080
}
deployment_configuration {
maximum_percent = 200
minimum_healthy_percent = 100
deployment_circuit_breaker {
enable = true
rollback = true # Auto-rollback on failed deployment
}
}
enable_execute_command = true # For troubleshooting (sessions logged)
tags = {
Name = "healthcare-api-service-${var.environment}"
HIPAA = "true"
}
depends_on = [aws_lb_listener.healthcare_api_https]
}
Monitoring & Compliance
CloudWatch Dashboards
# terraform/monitoring.tf
resource "aws_cloudwatch_dashboard" "hipaa_compliance" {
dashboard_name = "HIPAA-Compliance-${var.environment}"
dashboard_body = jsonencode({
widgets = [
{
type = "metric"
properties = {
metrics = [
["AWS/RDS", "FreeStorageSpace", { stat = "Minimum", label = "RDS Storage" }],
["AWS/RDS", "DatabaseConnections", { stat = "Maximum" }],
["AWS/ApplicationELB", "TargetResponseTime", { stat = "Average" }],
["AWS/ApplicationELB", "HTTPCode_Target_5XX_Count", { stat = "Sum" }]
]
period = 300
region = var.aws_region
title = "Infrastructure Health"
}
},
{
type = "log"
properties = {
query = "SOURCE '/aws/vpc/healthcare-${var.environment}' | fields @timestamp, @message | filter action = 'REJECT' | stats count() by srcAddr"
region = var.aws_region
title = "Rejected Network Traffic (Potential Threats)"
}
},
{
type = "metric"
properties = {
metrics = [
["CWAgent", "unauthorized_api_calls", { stat = "Sum" }],
["GuardDuty", "findings", { stat = "Sum" }]
]
period = 3600
region = var.aws_region
title = "Security Alerts"
}
}
]
})
}
Disaster Recovery Strategy
Multi-Region Failover
RTO (Recovery Time Objective): 4 hours RPO (Recovery Point Objective): 1 hour
DR Strategy:
- Automated Backups: RDS automated backups + AWS Backup (cross-region replication)
- Infrastructure as Code: Entire stack deployable to DR region via Terraform in < 30 minutes
- S3 Cross-Region Replication: PHI data replicated to DR region continuously
- Route 53 Health Checks: Automatic DNS failover to DR region if primary unhealthy
# terraform/disaster-recovery.tf
# Route 53 Health Check (primary region)
resource "aws_route53_health_check" "primary" {
fqdn = "api.healthcare.example.com"
port = 443
type = "HTTPS"
resource_path = "/health"
failure_threshold = 3
request_interval = 30
tags = {
Name = "healthcare-primary-health-check"
}
}
# Route 53 Failover Record (primary)
resource "aws_route53_record" "primary" {
zone_id = aws_route53_zone.healthcare.zone_id
name = "api.healthcare.example.com"
type = "A"
set_identifier = "primary"
failover_routing_policy {
type = "PRIMARY"
}
health_check_id = aws_route53_health_check.primary.id
alias {
name = aws_lb.healthcare.dns_name
zone_id = aws_lb.healthcare.zone_id
evaluate_target_health = true
}
}
# Route 53 Failover Record (DR region)
resource "aws_route53_record" "dr" {
zone_id = aws_route53_zone.healthcare.zone_id
name = "api.healthcare.example.com"
type = "A"
set_identifier = "dr"
failover_routing_policy {
type = "SECONDARY"
}
alias {
name = aws_lb.healthcare_dr.dns_name
zone_id = aws_lb.healthcare_dr.zone_id
evaluate_target_health = true
}
}
Compliance Validation
Automated HIPAA Compliance Checks
# scripts/validate-hipaa-compliance.sh
#!/bin/bash
set -e
ENVIRONMENT=$1
REGION=${2:-us-east-1}
echo "🔍 Running HIPAA Compliance Validation for $ENVIRONMENT environment..."
# Check 1: All S3 buckets storing PHI are encrypted
echo "✓ Checking S3 encryption..."
aws s3api list-buckets --query "Buckets[*].Name" --output text | while read bucket; do
encryption=$(aws s3api get-bucket-encryption --bucket "$bucket" 2>/dev/null || echo "NONE")
if [[ "$encryption" == "NONE" ]]; then
echo "❌ FAIL: Bucket $bucket is not encrypted"
exit 1
fi
done
# Check 2: RDS instances are encrypted
echo "✓ Checking RDS encryption..."
aws rds describe-db-instances --region "$REGION" --query "DBInstances[*].[DBInstanceIdentifier,StorageEncrypted]" --output text | while read instance encrypted; do
if [[ "$encrypted" != "True" ]]; then
echo "❌ FAIL: RDS instance $instance is not encrypted"
exit 1
fi
done
# Check 3: CloudTrail is enabled and logging
echo "✓ Checking CloudTrail status..."
trail_status=$(aws cloudtrail get-trail-status --name healthcare-trail-$ENVIRONMENT --query "IsLogging" --output text)
if [[ "$trail_status" != "True" ]]; then
echo "❌ FAIL: CloudTrail is not logging"
exit 1
fi
# Check 4: VPC Flow Logs are enabled
echo "✓ Checking VPC Flow Logs..."
vpc_id=$(aws ec2 describe-vpcs --filters "Name=tag:Name,Values=healthcare-vpc-$ENVIRONMENT" --query "Vpcs[0].VpcId" --output text)
flow_logs=$(aws ec2 describe-flow-logs --filter "Name=resource-id,Values=$vpc_id" --query "FlowLogs" --output text)
if [[ -z "$flow_logs" ]]; then
echo "❌ FAIL: VPC Flow Logs not enabled for $vpc_id"
exit 1
fi
# Check 5: MFA enabled for root account
echo "✓ Checking root account MFA..."
mfa_enabled=$(aws iam get-account-summary --query "SummaryMap.AccountMFAEnabled" --output text)
if [[ "$mfa_enabled" != "1" ]]; then
echo "❌ FAIL: Root account MFA is not enabled"
exit 1
fi
echo "✅ All HIPAA compliance checks passed!"
Conclusion
Building a HIPAA-compliant AWS architecture requires careful attention to:
Security: Encryption at rest and in transit, network isolation, least privilege access Monitoring: Comprehensive logging (CloudTrail, VPC Flow Logs, application logs) Availability: Multi-AZ deployments, automated failover, disaster recovery Auditability: Immutable logs, 7-year retention, compliance dashboards
This reference architecture provides a solid foundation for healthcare applications on AWS. Key takeaways:
- Always sign AWS BAA before storing PHI
- Use only HIPAA-eligible services for PHI workloads
- Encrypt everything (KMS for at-rest, TLS 1.2+ for in-transit)
- Implement defense in depth (multiple security layers)
- Automate compliance validation (infrastructure as code + automated testing)
- Plan for disaster recovery (cross-region backups, tested failover procedures)
Implementation Roadmap
Phase 1 (Weeks 1-2): Foundation
- Set up AWS Organization, sign BAA
- Deploy VPC with public/private subnets
- Configure CloudTrail, VPC Flow Logs
- Set up KMS keys for encryption
Phase 2 (Weeks 3-4): Data Layer
- Deploy RDS PostgreSQL (Multi-AZ, encrypted)
- Configure automated backups + AWS Backup
- Set up Secrets Manager for credentials
- Deploy ElastiCache Redis
Phase 3 (Weeks 5-6): Application Layer
- Set up ECR for container images
- Deploy ECS Fargate cluster
- Configure ALB with TLS termination
- Deploy application containers
Phase 4 (Weeks 7-8): Security & Monitoring
- Enable GuardDuty, Security Hub, Config
- Set up CloudWatch dashboards and alarms
- Configure SNS alerts for security events
- Implement automated incident response
Phase 5 (Weeks 9-10): Disaster Recovery
- Set up cross-region backups
- Deploy DR infrastructure (separate region)
- Test failover procedures
- Document runbooks
Phase 6 (Week 11-12): Compliance Validation
- Run automated compliance checks
- Conduct penetration testing
- Perform HIPAA risk assessment
- Prepare for audit
Via Lucra: HIPAA-Compliant AWS Architecture Experts
Via Lucra specializes in designing and implementing HIPAA-compliant AWS architectures for healthcare organizations. Our services include:
- AWS Architecture Design: Custom reference architectures tailored to your workload
- Terraform Implementation: Production-ready infrastructure as code with security built-in
- HIPAA Compliance Automation: Automated validation, monitoring, and audit preparation
- Disaster Recovery Planning: Multi-region DR strategies with tested failover procedures
Contact us to learn how we've helped 6 healthcare organizations achieve HIPAA compliance on AWS while reducing infrastructure costs by 40% through optimized architecture design.
Last updated: February 2026. AWS services and HIPAA requirements evolve. Always verify current BAA coverage and compliance requirements with AWS and your compliance team.
Via Lucra LLC
Secure cloud and DevSecOps consultancy specializing in healthcare operations platforms for Medicaid, HCBS, and human services organizations.
Prêt à moderniser vos opérations ?
Discutons de la façon dont Via Lucra peut vous aider à créer des opérations de soins conformes et prêtes pour l'audit.
Planifier une consultation