How to Use Terraform for HIPAA Compliance: Requirements, Safeguards, and Examples

Product Pricing
Ready to get started? Book a demo with our team
Talk to an expert

How to Use Terraform for HIPAA Compliance: Requirements, Safeguards, and Examples

Kevin Henry

HIPAA

March 16, 2026

6 minutes read
Share this article
How to Use Terraform for HIPAA Compliance: Requirements, Safeguards, and Examples

Terraform helps you codify HIPAA-aligned technical safeguards so your cloud resources are consistent, reviewable, and auditable. This guide maps core requirements to practical configurations and examples you can adapt for HIPAA infrastructure automation across environments.

The patterns below emphasize least privilege, CloudTrail logging, cryptographic integrity checks, AWS KMS encryption, Multi-Factor Authentication, and encrypted Terraform backends. Examples target AWS but the principles apply to any supported provider.

Implement Access Control Measures

Access control for ePHI hinges on least privilege, network segmentation, and clear separation of duties. Use IAM roles and policies to scope actions to specific cloud resources, restrict access paths, and require TLS for data access.

Terraform pattern: least-privilege ePHI access

The example below grants read-only access to a designated ePHI bucket, only over TLS, and only for objects tagged as ePHI. This enforces ePHI access control with clear, testable intent.

resource "aws_iam_policy" "ephi_readonly" {
  name   = "ephi-readonly"
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid      = "ListBucketOverTLS"
        Effect   = "Allow"
        Action   = ["s3:ListBucket"]
        Resource = "arn:aws:s3:::ephi-bucket"
        Condition = {
          Bool = { "aws:SecureTransport" = "true" }
        }
      },
      {
        Sid      = "GetObjectsTaggedEPHIOverTLS"
        Effect   = "Allow"
        Action   = ["s3:GetObject"]
        Resource = "arn:aws:s3:::ephi-bucket/*"
        Condition = {
          Bool = { "aws:SecureTransport" = "true" }
          StringEquals = { "s3:ExistingObjectTag/classification" = "ephi" }
        }
      }
    ]
  })
}

Implementation tips

  • Scope IAM actions to ARNs, not wildcards; prefer explicit resource lists or tags.
  • Use security groups and subnets to segment admin planes from data planes.
  • Continuously test policy impact with automated access reviews and drift detection.

Enable Audit Controls

HIPAA expects you to record access and administrative activity. Centralize CloudTrail logging across all regions, enable log file validation, and stream to CloudWatch for alerting and retention.

Terraform pattern: multi-Region CloudTrail with encryption

resource "aws_s3_bucket" "ct_logs" {
  bucket = "hipaa-ct-logs-123456"
}

resource "aws_s3_bucket_versioning" "ct" {
  bucket = aws_s3_bucket.ct_logs.id
  versioning_configuration { status = "Enabled" }
}

resource "aws_kms_key" "ct" {
  description         = "KMS for CloudTrail logs"
  enable_key_rotation = true
}

resource "aws_s3_bucket_server_side_encryption_configuration" "ct" {
  bucket = aws_s3_bucket.ct_logs.id
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm     = "aws:kms"
      kms_master_key_id = aws_kms_key.ct.arn
    }
  }
}

resource "aws_cloudwatch_log_group" "trail" {
  name              = "/hipaa/cloudtrail"
  retention_in_days = 365
}

resource "aws_iam_role" "ct_to_cw" {
  name = "cloudtrail-to-cloudwatch"
  assume_role_policy = jsonencode({
    Version = "2012-10-17",
    Statement = [{
      Effect    = "Allow",
      Principal = { Service = "cloudtrail.amazonaws.com" },
      Action    = "sts:AssumeRole"
    }]
  })
}

resource "aws_iam_role_policy" "ct_to_cw" {
  role = aws_iam_role.ct_to_cw.id
  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [{
      Effect   = "Allow",
      Action   = ["logs:CreateLogStream", "logs:PutLogEvents"],
      Resource = "${aws_cloudwatch_log_group.trail.arn}:*"
    }]
  })
}

resource "aws_cloudtrail" "org" {
  name                          = "org-trail"
  s3_bucket_name                = aws_s3_bucket.ct_logs.id
  is_multi_region_trail         = true
  include_global_service_events = true
  enable_log_file_validation    = true
  cloud_watch_logs_group_arn    = aws_cloudwatch_log_group.trail.arn
  cloud_watch_logs_role_arn     = aws_iam_role.ct_to_cw.arn
}
  • Enable data events for S3 and Lambda where ePHI may flow to capture object-level activity.
  • Set retention aligned to policy; use immutable storage where required.
  • Alert on high-risk events (KMS key changes, policy updates, failed Console logins).

Ensure Data Integrity

Data integrity safeguards prove that ePHI was not altered or destroyed improperly. Use versioning, immutability, and cryptographic integrity checks to detect and prevent tampering.

Ready to simplify HIPAA compliance?

Join thousands of organizations that trust Accountable to manage their compliance needs.

Terraform pattern: versioning and object lock

resource "aws_s3_bucket" "ephi" {
  bucket                = "ephi-data-123456"
  object_lock_enabled   = true
}

resource "aws_s3_bucket_versioning" "ephi" {
  bucket = aws_s3_bucket.ephi.id
  versioning_configuration { status = "Enabled" }
}

resource "aws_s3_bucket_object_lock_configuration" "ephi_lock" {
  bucket = aws_s3_bucket.ephi.id
  rule {
    default_retention {
      mode = "COMPLIANCE"
      days = 30
    }
  }
}
  • Turn on CloudTrail log file validation to verify log integrity end-to-end.
  • Use checksums and signed artifacts for deployments; validate hashes in CI/CD.
  • Continuously monitor resource drift; fail builds when unauthorized changes appear.

Apply Encryption Methods

Encrypt ePHI at rest with AWS KMS encryption and enforce TLS in transit. Prefer customer-managed KMS keys with rotation and tightly scoped key policies.

Terraform pattern: KMS + default encryption

resource "aws_kms_key" "ephi" {
  description         = "KMS CMK for ePHI"
  enable_key_rotation = true
}

resource "aws_s3_bucket_server_side_encryption_configuration" "ephi" {
  bucket = aws_s3_bucket.ephi.id
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm     = "aws:kms"
      kms_master_key_id = aws_kms_key.ephi.arn
    }
  }
}

resource "aws_ebs_encryption_by_default" "on" {
  enabled = true
}

resource "aws_db_instance" "ephi_pg" {
  identifier              = "ephi-db"
  engine                  = "postgres"
  instance_class          = "db.m6g.large"
  allocated_storage       = 100
  storage_encrypted       = true
  kms_key_id              = aws_kms_key.ephi.arn
  publicly_accessible     = false
  multi_az                = true
  backup_retention_period = 7
  deletion_protection     = true
}

Enforce TLS for apps

resource "aws_lb_listener" "https" {
  load_balancer_arn = aws_lb.app.arn
  port              = 443
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-TLS13-1-2-2021-06"
  certificate_arn   = aws_acm_certificate.app.arn
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.app.arn
  }
}

Configure Authentication

Use strong identity controls for administrators and workloads. Require Multi-Factor Authentication for privileged roles and restrict session duration. Where possible, federate with an enterprise IdP.

Terraform pattern: require MFA to assume role

resource "aws_iam_role" "ephi_operator" {
  name = "ephi-operator"
  assume_role_policy = jsonencode({
    Version = "2012-10-17",
    Statement = [{
      Effect = "Allow",
      Action = "sts:AssumeRole",
      Principal = {
        AWS = "arn:aws:iam::123456789012:role/Enterprise-SSO-Admins"
      },
      Condition = {
        Bool = { "aws:MultiFactorAuthPresent" = "true" }
      }
    }]
  })
}

resource "aws_iam_role_policy_attachment" "ephi_access" {
  role       = aws_iam_role.ephi_operator.name
  policy_arn = aws_iam_policy.ephi_readonly.arn
}
  • Use short session lifetimes and require justification tags for elevated access.
  • Rotate access keys for non-interactive principals; prefer role assumption over long-lived keys.
  • Audit role assumptions in CloudTrail and alert on anomalies.

Manage Terraform State Securely

Terraform state can contain resource metadata and, in some cases, sensitive values. Store it in encrypted Terraform backends, lock it during changes, and limit who can read it.

Terraform pattern: S3 + DynamoDB with KMS

resource "aws_kms_key" "state" {
  description         = "KMS for Terraform state"
  enable_key_rotation = true
}

resource "aws_s3_bucket" "tf_state" {
  bucket = "hipaa-tf-state-123456"
}

resource "aws_s3_bucket_versioning" "tf_state" {
  bucket = aws_s3_bucket.tf_state.id
  versioning_configuration { status = "Enabled" }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "tf_state" {
  bucket = aws_s3_bucket.tf_state.id
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm     = "aws:kms"
      kms_master_key_id = aws_kms_key.state.arn
    }
  }
}

resource "aws_s3_bucket_public_access_block" "tf_state" {
  bucket                  = aws_s3_bucket.tf_state.id
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

resource "aws_dynamodb_table" "tf_lock" {
  name         = "tf-state-lock"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"
  attribute { name = "LockID"; type = "S" }
  server_side_encryption { enabled = true }
}
# backend configuration (in your root module)
terraform {
  backend "s3" {
    bucket         = "hipaa-tf-state-123456"
    key            = "prod/global/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "tf-state-lock"
    encrypt        = true
  }
}
  • Restrict bucket access to CI/CD roles; deny public access and cross-account reads by default.
  • Avoid placing secrets in state; fetch at apply time from a secret manager.
  • Use workspaces or separate keys per environment to reduce blast radius.

Use HIPAA Compliance Modules

Package repeatable guardrails into modules so teams can launch compliant foundations quickly. A baseline module can provision CloudTrail logging, KMS keys, VPC controls, and opinionated defaults for encryption and retention.

Example: opinionated baseline module

# modules/hipaa-baseline/main.tf
module "kms_ephi" {
  source = "../kms-key"
  alias  = "ephi"
}

module "cloudtrail" {
  source           = "../cloudtrail"
  kms_key_arn      = module.kms_ephi.arn
  retention_days   = 365
  multi_region     = true
  log_validation   = true
}

module "network" {
  source = "../vpc-hardened"
  # private subnets, restricted SGs, flow logs, etc.
}
Share this article

Ready to simplify HIPAA compliance?

Join thousands of organizations that trust Accountable to manage their compliance needs.

Related Articles