Many organizations struggle pinch Terraform module adoption, experiencing challenges specified as:
- Version fragmentation: Different projects/teams extremity up connected different versions.
- Breaking changes: Teams can’t upgrade without risking errors during aliases post-deployment.
- Shadow modules: Teams constitute their ain erstwhile modules don’t fresh their needs.
This guideline shows really 2 package engineering principles tin toggle shape Terraform modules into components teams really want to use.
From Monolithic to Modular: 2 Patterns
Starting Point: What Most Teams Have
Most teams commencement pinch moving infrastructure: a bucket, immoderate personality and entree guidance (IAM) permissions and basal information settings. The problem isn’t that it’s broken; it’s conscionable that everything is tangled together successful 1 module:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# modules/s3-with-access/main.tf resource "aws_s3_bucket" "app_bucket" { bucket = "mycompany-app-uploads" # Hardcoded name } resource "aws_iam_role" "app_role" { # Creating roles wrong retention module name = "app-s3-access-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "lambda.amazonaws.com" } # Lambda only }] }) } resource "aws_iam_role_policy" "app_bucket_access" { role = aws_iam_role.app_role.id policy = jsonencode({ Version = "2012-10-17" Statement = [{ Effect = "Allow" Action = ["s3:GetObject", "s3:PutObject"] # Raw IAM actions Resource = "${aws_s3_bucket.app_bucket.arn}/*" }] }) } |
This module has bundled successful nan bucket, nan IAM domiciled and nan policies, but what happens erstwhile you need:
- The aforesaid bucket for an Elastic Container Service (ECS) task alternatively of Lambda? You can’t reuse this module because nan domiciled is hardcoded for Lambda.
- Multiple services to entree 1 bucket? You’d request to transcript and modify nan full module for each service.
- Just nan bucket without nan IAM role? They’re inseparably coupled.
This is why teams extremity up pinch somewhat different copies of nan aforesaid point – s3-module-lambda, s3-module-ecs, s3-module-ec2.
Pattern 1: Separation of Concerns — 1 Module, 1 Purpose
The first problem: Modules effort to do excessively much. When your S3 module besides creates IAM roles and policies, it becomes intolerable to reuse for different services.
Single work says each module does 1 point well. Let’s abstracted our concerns pinch a module that only manages S3 buckets. No IAM roles. No policies. Just buckets pinch sensible defaults.
S3 Module — Just Manages Buckets
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# modules/s3/main.tf locals { bucket_name = "${var.environment}-${replace(lower(var.name), "_", "-")}" } resource "aws_s3_bucket" "this" { bucket = local.bucket_name tags = var.tags } 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 } resource "aws_s3_bucket_versioning" "this" { count = var.enable_versioning ? 1 : 0 bucket = aws_s3_bucket.this.id versioning_configuration { status = "Enabled" } } output "arn" { value = aws_s3_bucket.this.arn } output "id" { value = aws_s3_bucket.this.id } |
In practice: Your S3 module will beryllium much complex, requiring encryption, cross-origin assets sharing (CORS), life rhythm policies and more. Keep IAM, networking and compute resources successful abstracted modules truthful that they become reusable crossed different services and teams.
Pattern 2: Composability — Modules That Work Together
The 2nd principle: Modules should constitute cleanly. Each module requires a clear interface pinch predictable outputs that different modules tin use.
First, create your infrastructure resources utilizing nan separated modules:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# Create retention pinch nan S3 module module "uploads" { source = "./modules/s3" name = "uploads" environment = "production" } module "backups" { source = "./modules/s3" name = "backups" environment = "production" } # Create compute resources resource "aws_iam_role" "app_service" { name = "app-service" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "ecs-tasks.amazonaws.com" } }] }) } |
Then ligament them together pinch definitive permissions:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# Grant nan work entree to nan buckets it needs resource "aws_iam_role_policy" "app_access" { name = "app-bucket-access" role = aws_iam_role.app_service.name policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = ["s3:GetObject", "s3:PutObject"] Resource = "${module.uploads.arn}/*" }, { Effect = "Allow" Action = ["s3:PutObject"] Resource = "${module.backups.arn}/*" } ] }) } |
Because modules person predictable interfaces:
- Any work tin usage immoderate bucket.
- Permissions are definitive and auditable.
- No hidden limitations betwixt modules.
- Each portion tin beryllium tested independently.
In practice: Your modules will beryllium larger units — an “application stack” aliases “data pipeline” alternatively than individual resources. Standardized interfaces and outputs fto modules activity together, whether you’re composing buckets and policies aliases full Virtual Private Clouds (VPCs) and Amazon Elastic Kubernetes Service (EKS) clusters. Predictable interfaces alteration composition.
From Patterns to Platform
These patterns lick nan protector module problem. Teams won’t request to bypass well-designed, composable modules. But you still look type fragmentation and breaking changes. When half your teams are connected an older type and only caller projects summation entree to nan latest version, each update now involves nan added complexity of migration.
The spread betwixt bully modules and a bully level is coordination. You request to make judge each squad benefits from improvements without forcing them to alteration their workflows.
We’ve built a caller level that makes this possible. With Suga, your Terraform modules go ocular building blocks. Every squad gets nan aforesaid unafraid defaults. Platform updates use each apps automatically, not conscionable caller ones. Developers constitute infrastructure done drag-and-drop, while your standards enforce themselves invisibly.

Good modules get reused. Great modules go platforms. Request early entree today.
YOUTUBE.COM/THENEWSTACK
Tech moves fast, don't miss an episode. Subscribe to our YouTube channel to watercourse each our podcasts, interviews, demos, and more.
Group Created pinch Sketch.
English (US) ·
Indonesian (ID) ·