Presented by: Adam Holden
Presented to: Carrum Health
"We needed to move from a flat, brittle IAC implementation to something that could support our growth trajectory."
# Example of our inherited main.tf (simplified)
provider "aws" {
region = "us-west-2"
}
# Network resources
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
# ... dozens of parameters
}
# Compute resources
resource "aws_instance" "app_servers" {
count = 10
ami = "ami-12345678"
instance_type = "t3.medium"
# ... hundreds of lines of configuration
}
# Database resources
resource "aws_db_instance" "database" {
allocated_storage = 20
engine = "postgres"
# ... more configuration
}
# Dozens more resources for load balancing, security, etc.
infrastructure/
├── _envcommon/
│ ├── networking.hcl
│ ├── compute.hcl
│ └── database.hcl
├── prod/
│ ├── terragrunt.hcl
│ ├── us-west-2/
│ │ ├── networking/
│ │ ├── compute/
│ │ └── database/
│ └── us-east-1/
├── staging/
└── dev/
# Root terragrunt.hcl:
locals {
environment = path_relative_to_include()
# Parse account and region from the directory structure
account_vars = read_terragrunt_config(find_in_parent_folders("account.hcl"))
region_vars = read_terragrunt_config(find_in_parent_folders("region.hcl"))
env_vars = read_terragrunt_config(find_in_parent_folders("env.hcl"))
# Extract commonly used variables
aws_region = local.region_vars.locals.aws_region
account_id = local.account_vars.locals.aws_account_id
environment = local.env_vars.locals.environment
}
# Generate provider configuration for all child modules
generate "provider" {
path = "provider.tf"
if_exists = "overwrite"
contents = <
modules/
└── networking/
├── main.tf
├── variables.tf
├── outputs.tf
└── README.md
# Common settings for the networking module
terraform {
source = "${get_parent_terragrunt_dir()}/modules//networking"
}
# Dependencies
dependencies {
paths = []
}
# Common input variables
inputs = {
vpc_name = "carrum-vpc"
# These can be overridden in environment-specific configs
enable_vpc_flow_logs = true
vpc_flow_logs_retention_days = 30
}
# Include the common configuration
include {
path = "${find_in_parent_folders()}"
}
# Include the envcommon configuration
include "envcommon" {
path = "${dirname(find_in_parent_folders())}/_envcommon/networking.hcl"
}
# Override inputs
inputs = {
vpc_cidr = "10.0.0.0/16"
subnet_cidrs = {
public_a = "10.0.0.0/24"
public_b = "10.0.1.0/24"
private_a = "10.0.2.0/24"
private_b = "10.0.3.0/24"
}
enable_nat_gateway = true
}
The timeline depends on the needs of Carrum Health. The following is a general timeline that can be adjusted based on the needs of the business.
Options: Few large modules vs. Many small modules
Choice: Middle ground - domain-focused modules
Rationale: Balance between reusability and management complexity
Options: Single state vs. State per environment vs. State per component
Choice: Hybrid approach - state per environment per component
Rationale: Optimal balance of isolation and dependency management
Options: Big bang cutover vs. Incremental migration
Choice: Incremental with parallel infrastructure
Rationale: Minimize risk and business disruption
Risk | Mitigation |
---|---|
State file corruption | Implemented versioning and locking with DynamoDB |
Knowledge gaps in team | Regular training sessions and pair programming |
Disruption to production | Blue/green approach for critical infrastructure |
Dependency tracking | Created visualization tools for module dependencies |
Cost management | Implemented tagging strategy and regular cost reviews |
Resistance to change | Early stakeholder involvement and demos |
Thank you for your attention!
I'm happy to answer any questions about: