Solutions

Solutions are opinionated compositions of modules configured for deployment. They represent and group all the resources that compose a specific solution.

Characteristics

  • Compose modules — Aggregate multiple modules into a service definition
  • Enforce policies — Pin module versions, enforce security settings
  • Limited configurability — Only expose variables that truly vary across environments
  • Self-contained — A solution is the artifact that gets deployed

Solution Structure

solutions/my-app/
├── main.tf           # Module compositions
├── variables.tf      # Limited, intentional inputs
├── outputs.tf        # Service-level outputs
├── versions.tf       # Provider constraints
├── README.md         # Auto-generated docs
└── CHANGELOG.md      # Auto-generated changelog

Example: Application Solution (AWS)

This solution creates all AWS resources needed by an application — IAM roles, Kubernetes service accounts, and S3 buckets:

# solutions/my-app/main.tf

module "app_service_account" {
  source  = "terrareg.example.com/modules/eks-irsa-service-account/aws"
  version = "1.0.0"

  for_each = toset(var.eks_cluster_names)

  cluster_name       = each.value
  namespace          = var.app_namespace
  service_account    = "my-app"
  iam_role_arn       = module.app_irsa_infra.role_arn
}

module "app_irsa_infra" {
  source  = "terrareg.example.com/modules/eks-irsa-infra/aws"
  version = "1.0.0"

  business_region = var.business_region
  environment     = var.environment
  service_name    = "my-app"
  policy_arn      = aws_iam_policy.app_policy.arn
}

module "data_bucket" {
  source  = "terrareg.example.com/modules/s3-bucket/aws"
  version = "1.1.0"

  bucket_name = "${lower(var.business_region)}-${lower(var.environment)}-my-app-data"
  bucket_tags = {
    service = "my-app"
    team    = "platform"
  }
}

Example: Database Solution (AWS)

A database solution composes RDS modules with alarms and security groups:

# solutions/my-database/main.tf

module "main" {
  source  = "terrareg.example.com/modules/rds-postgres-database/aws"
  version = "1.3.0"

  instance_identifier = var.instance_identifier
  engine_version      = "16.6"  # Enforced — not configurable
  instance_class      = var.instance_class
  allocated_storage   = var.allocated_storage
  subnet_group_name   = var.subnet_group_name
  deletion_protection = true    # Enforced — always on
  multi_az            = var.multi_az
}

module "replica" {
  count   = var.create_replica ? 1 : 0
  source  = "terrareg.example.com/modules/rds-postgres-database/aws"
  version = "1.2.0"

  create_as_replica   = true
  instance_identifier = module.main.db_instance_name
  # ...
}

module "security_group" {
  source  = "terrareg.example.com/modules/security-group-static/aws"
  version = "1.0.0"
  # ...
}

module "main_db_alarms" {
  source  = "terrareg.example.com/modules/rds-alarms/aws"
  version = "1.3.0"

  db_instance_identifier = module.main.db_instance_name
  alarm_sns_topic_arn    = var.alarm_sns_topic_arn
}

Solutions should never be entirely configurable. Key settings like engine versions, deletion protection, and encryption must be enforced by the solution, not left to the consumer.

Available Solution Examples

Solution Description
Application IAM roles, K8s service accounts, S3 buckets
Database RDS instances, replicas, alarms, security groups
Cluster EKS cluster with addons, autoscaling, networking
Nexus Artifact repository with S3, EFS, DNS
Messaging Amazon MQ instances with alarms and dashboards