Skip to main content
INS // Insights

Agile Cloud Sub Delivery for Government IT

Updated May 2026 · 7 min read

Infrastructure as Code (IaC) with Terraform is the standard approach for federal cloud deployments — it provides auditability, repeatability, and configuration drift detection critical for ATO maintenance. But uncontrolled Terraform module proliferation creates technical debt, security inconsistency, and compliance gaps.

A disciplined Terraform module reuse strategy solves these problems by encoding security baselines and compliance requirements into shareable, versioned modules.

Why Module Reuse Matters in Federal IaC

Every federal cloud deployment must implement the same core security patterns: logging enabled, encryption at rest, encryption in transit, tagging for asset inventory, and access controls. Without reusable modules, each team reimplements these patterns from scratch — inconsistently.

A compliant Terraform module encapsulates:

  • Security defaults (encryption on by default, logging on by default)
  • STIG-required configurations enforced at module level
  • Required tagging for asset inventory (AU control family support)
  • Output values needed by other modules (ARNs, resource IDs)

Module Structure for Federal Cloud

Organize modules with versioning, documentation, and testing built in:

modules/
  aws-s3-compliant/
    main.tf
    variables.tf
    outputs.tf
    versions.tf
    README.md
    examples/
      basic/
        main.tf
      with-replication/
        main.tf
    tests/
      s3_compliance_test.go   # Go-based Terratest

Example: Compliant S3 Module

A reusable S3 module that enforces FedRAMP/FISMA requirements by default:

# modules/aws-s3-compliant/main.tf

resource "aws_s3_bucket" "this" {
  bucket = var.bucket_name

  tags = merge(var.tags, {
    Environment  = var.environment
    DataClass    = var.data_classification
    Owner        = var.owner
    CostCenter   = var.cost_center
    Compliance   = "FedRAMP-${var.impact_level}"
  })
}

# Block all public access - enforced, no override
resource "aws_s3_bucket_public_access_block" "this" {
  bucket = aws_s3_bucket.this.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

# Encryption - CMK required for FedRAMP High/Moderate
resource "aws_s3_bucket_server_side_encryption_configuration" "this" {
  bucket = aws_s3_bucket.this.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm     = var.kms_key_id != null ? "aws:kms" : "AES256"
      kms_master_key_id = var.kms_key_id
    }
    bucket_key_enabled = true
  }
}

# Versioning - required for many data integrity controls
resource "aws_s3_bucket_versioning" "this" {
  bucket = aws_s3_bucket.this.id

  versioning_configuration {
    status = var.enable_versioning ? "Enabled" : "Suspended"
  }
}

# Logging to central audit bucket
resource "aws_s3_bucket_logging" "this" {
  count = var.logging_bucket != null ? 1 : 0

  bucket        = aws_s3_bucket.this.id
  target_bucket = var.logging_bucket
  target_prefix = "s3-access-logs/${var.bucket_name}/"
}

# Lifecycle policy for cost management
resource "aws_s3_bucket_lifecycle_configuration" "this" {
  count = length(var.lifecycle_rules) > 0 ? 1 : 0

  bucket = aws_s3_bucket.this.id

  dynamic "rule" {
    for_each = var.lifecycle_rules
    content {
      id     = rule.value.id
      status = rule.value.enabled ? "Enabled" : "Disabled"

      transition {
        days          = rule.value.transition_days
        storage_class = rule.value.storage_class
      }
    }
  }
}
# modules/aws-s3-compliant/variables.tf

variable "bucket_name" {
  type        = string
  description = "Globally unique S3 bucket name"
}

variable "data_classification" {
  type        = string
  description = "Data classification level for tagging"
  validation {
    condition = contains(
      ["UNCLASSIFIED", "CUI", "SECRET"],
      var.data_classification
    )
    error_message = "data_classification must be UNCLASSIFIED, CUI, or SECRET"
  }
}

variable "impact_level" {
  type        = string
  description = "FedRAMP impact level"
  default     = "Moderate"
  validation {
    condition     = contains(["Low", "Moderate", "High"], var.impact_level)
    error_message = "impact_level must be Low, Moderate, or High"
  }
}

variable "kms_key_id" {
  type        = string
  description = "KMS CMK ARN for encryption. Required for FedRAMP High."
  default     = null
}

Module Versioning and Registry Strategy

Use a private Terraform module registry (Terraform Cloud, GitLab, or self-hosted):

# Reference versioned module from private registry
module "audit_logs" {
  source  = "registry.internal.gov/mission-platform/aws-s3-compliant/aws"
  version = "~> 2.1"  # Pin to minor version, allow patches

  bucket_name         = "mission-audit-logs-${var.environment}"
  data_classification = "CUI"
  kms_key_id          = aws_kms_key.audit.arn
  logging_bucket      = null  # Audit log bucket doesn't self-log
  enable_versioning   = true
  
  tags = {
    Purpose = "AuditLogs"
  }
}

Semantic versioning for federal modules:

  • Patch (x.x.1): Bug fixes, no behavior change
  • Minor (x.1.x): New optional features, backwards compatible
  • Major (1.x.x): Breaking changes, compliance-impacting changes — require review

Testing Modules with Terratest

// modules/aws-s3-compliant/tests/s3_compliance_test.go
package test

import (
    "testing"
    "github.com/gruntwork-io/terratest/modules/aws"
    "github.com/gruntwork-io/terratest/modules/terraform"
    "github.com/stretchr/testify/assert"
)

func TestS3ComplianceModule(t *testing.T) {
    t.Parallel()

    opts := &terraform.Options{
        TerraformDir: "../examples/basic",
        Vars: map[string]interface{}{
            "bucket_name":          "test-compliance-bucket",
            "data_classification":  "UNCLASSIFIED",
            "environment":          "test",
        },
    }

    defer terraform.Destroy(t, opts)
    terraform.InitAndApply(t, opts)

    bucketID := terraform.Output(t, opts, "bucket_id")

    // Verify public access block
    publicAccessBlock := aws.GetS3BucketPublicAccessBlock(t, "us-gov-east-1", bucketID)
    assert.True(t, *publicAccessBlock.BlockPublicAcls)
    assert.True(t, *publicAccessBlock.RestrictPublicBuckets)

    // Verify encryption enabled
    encryption := aws.GetS3BucketServerSideEncryptionConfig(t, "us-gov-east-1", bucketID)
    assert.NotNil(t, encryption)
}

See Rutagon's FedRAMP ConMon automation and NIST 800-53 cloud automation for the compliance context these modules support.

Explore Rutagon's cloud engineering capabilities.

FAQ

Should federal Terraform modules be stored in a public registry like the Terraform Registry?

No — federal cloud modules often contain agency-specific configurations, internal IP ranges, approved service lists, and compliance patterns that should not be public. Use a private module registry (Terraform Enterprise, GitLab's Terraform module registry, or an S3-backed solution with a Terraform-compatible API) that enforces authenticated access.

How do you handle Terraform state management for federal cloud environments?

Use remote state in an S3 backend with DynamoDB state locking, encrypted at rest with a CMK, versioned, and with access logging enabled. Never store Terraform state in source control. For multi-account architectures, use separate state buckets per environment with cross-account assume-role patterns for state access.

What is the right cadence for updating module versions in federal environments?

Balance currency (patching known vulnerabilities) against stability (avoiding unplanned changes). A reasonable approach: patch versions apply automatically, minor versions are reviewed quarterly, major versions go through a formal change review. For compliance-critical modules (IAM, networking), all version upgrades require a Terraform plan review before apply.

How do you enforce module usage in a federal program with multiple teams?

OPA/Sentinel policies in Terraform Enterprise can enforce that required modules are used (e.g., all S3 buckets must use the compliant-s3 module rather than the aws_s3_bucket resource directly). Git repository templates that include module references as a starting point also guide teams toward the approved patterns.

What's the compliance benefit of defining Terraform modules with validation blocks?

Terraform validation blocks enforce correct values at plan time, before resources are created. In federal environments, this means catching configuration errors (wrong data classification, missing KMS key for High impact) before they reach a real environment. Combined with automated testing, validation blocks are a low-effort, high-value compliance mechanism.

Ready to discuss your project?

We deliver production-grade software for government, defense, and commercial clients. Let's talk about what you need.

Initiate Contact