LogoSkills

Terraform Code Conventions

terraform/

Module Structure#

terraform/
├── main.tf              # Module composition and orchestration
├── variables.tf         # Map-based variables (per environment)
├── config.auto.tfvars   # Actual value configuration
├── outputs.tf           # Output variables
├── backend.tf           # S3 + DynamoDB remote state
├── moved.tf             # State migration
└── modules/
    ├── shared/          # Created once (VPC, ALB, IAM, SG)
    ├── environment/     # Per environment (EC2, RDS, Redis, S3, DNS)
    ├── message/         # Push notifications (SQS, SNS, Lambda)
    ├── pdf_processing/  # PDF processing (Step Functions, Lambda)
    └── batch_processing/ # Batch processing (SQS, Lambda)

Variable Design Patterns#

Map-Based Variables (Per-Environment Configuration)#

# CORRECT: Map for per-environment settings
variable  " instance_types "   {
  type = map(string)
  default = {
     " production "    =  " t4g.medium " 
     " staging "       =  " t4g.small " 
     " development "   =  " t4g.small " 
   }
}

# WRONG: Separate variables per environment
variable  " production_instance_type "   { ... }
variable  " staging_instance_type "   { ... }

Passing Variables to Modules (lookup pattern)#

# CORRECT: Safe access with lookup
deeplink_subdomain = lookup(var.deeplink_config,  " production " , null) != null ? var.deeplink_config[ " production " ][ " subdomain " ] :  " " 

 # WRONG: Direct access (errors if key is missing)
deeplink_subdomain = var.deeplink_config[ " production " ][ " subdomain " ]

Feature Toggle Pattern#

# Environment activation
locals {
  is_production_enabled  = var.enable_production_server
  is_staging_enabled     = var.enable_staging_server
  is_development_enabled = var.enable_development_server
}

# Module count condition
module  " production_env "   {
  count = local.is_production_enabled ? 1 : 0
  # ...
}

# Service + environment combination condition
module  " message_production "   {
  count = var.enable_push_notifications  & &   local.is_production_enabled ? 1 : 0
  # ...
}

Route53 Resource Patterns#

count Condition Rules#

Record Typecount Condition
Primary CNAME enable_route53 && enable_primary_dns && subdomain != ""
Secondary CNAME enable_route53 && secondary_domain != null && subdomain != ""
Top domain A enable_route53 && enable_primary_dns && enable_top_domain_alias && length(ips) > 0
Production-onlyAdditional local.is_production condition

Subdomain Naming#

# CORRECT: Production has no prefix, others have suffix
name = local.is_production ?  " console "   :  " console-${local.env_short_suffix} " 

 # env_short_suffix:  " stg "   or  " dev "

Lambda ZIP Path Rules#

Relative Paths Based on path.module#

# Reference Lambda ZIP within module
locals {
  lambda_base_path =  " ${path.module}/../../../lambda/python " 
   router_zip_path  =  " ${local.lambda_base_path}/pdf_router/build/pdf-router.zip " 
 }

Caution: path.module is relative to the module .tf file location:

  • modules/pdf_processing/../../../ = deploy/aws/
  • NOT the terraform/ execution directory

fileexists() Guard Pattern#

# CORRECT: Guard with fileexists()
locals {
  zip_exists = fileexists(local.zip_path)
}

resource  " aws_lambda_function "   " example "   {
  count            = local.zip_exists ? 1 : 0
  source_code_hash = local.zip_exists ? filebase64sha256(local.zip_path) : null
}

# WRONG: Direct call without guard (plan fails if file missing)
resource  " aws_lambda_function "   " example "   {
  source_code_hash = filebase64sha256(local.zip_path)
}

State Management#

Remote Backend#

backend  " s3 "   {
  bucket         =  " kobic-tfstate-bucket " 
   key            =  " terraform.tfstate " 
   region         =  " ap-northeast-2 " 
   dynamodb_table =  " kobic-tfstate-lock " 
   encrypt        = true
}

Workspace Usage#

# List workspaces
terraform workspace list

# Switch workspace
terraform workspace select production

This project manages environments with a workspace + enable_*_server flag combination. Within each workspace, feature flags conditionally create per-environment resources.

Tag Rules#

tags = {
  Project     = var.project_name
  Environment = var.environment
  ManagedBy   =  " terraform " 
 }