Terraform Notes

Contents

1. if-else-elseif

https://stackoverflow.com/a/69586941/17858112

Conditional statements are common in development, but Terraform seems to support only if-else.

If you want to add other conditions, as with elseif, you can implement them as shown below.

terraform
conditioning_a != "condition_a" ? "value1" : (conditioning_b == "condition_b" ? "value_b" : (conditioning_c == "condition_c" ? "value_c" : (conditioning_d == "condition_d" ? "value_d" : "value2")))

2. concat(), lookup()

Pipelines often deploy develop and staging immediately without review, while requiring review before production deployment. I used the concat function to construct the CodePipeline stages.

python
locals {
  common_stages = [
    {
      name             = "Source"
      category         = "Source"
      ...
      ...
      }
    },
    {
      name             = "Build"
      category         = "Build"
      ...
      ...
    }
  ]

  production_only_stage = [
    {
      name             = "Review"
      category         = "Approval"
      ...
      ...
    }
  ]

  deploy_stage = [
    {
      name             = "Deploy"
      category         = "Deploy"
      ...
      ...
    }
  ]

  stages = concat(
    local.common_stages,
    (var.is_production ? local.production_only_stage : []),
    local.deploy_stage
  )
}

Configure the pipeline stages in local values, then use a dynamic block to define the stages when declaring the resource. When adding a Review stage in CodePipeline, the namespace attribute is null, so I used the lookup function to handle that.

python
resource "aws_codepipeline" "pipeline" {
  name     = var.pipeline_name
  role_arn = var.pipeline_role_arn

  dynamic "stage" {
    for_each = local.stages

    content {
      name = "${stage.value["name"]}"

      action {
        name             = stage.value["name"]
        category         = stage.value["category"]
        owner            = stage.value["owner"]
        provider         = stage.value["provider"]
        version          = stage.value["version"]
        input_artifacts  = stage.value["input_artifacts"]
        output_artifacts = stage.value["output_artifacts"]
        namespace        = lookup(stage.value, "namespace", null)
        configuration    = stage.value["configuration"] 
      }
    }
  }
}

3. flatten()

I create computing resources, queues, and other resources for AWS Batch, and share an IAM policy that allows EventBridge rules to trigger jobs.

Updating the policy every time I created a batch job was tedious, so I implemented it as follows.

python
module "batch_develop_dongdorrong" {
...
}

module "batch_staging_dongdorrong" {
...
}

module "batch_production_dongdorrong" {
...
}

locals {
    modules = {
        develop    = module.batch_develop_dongdorrong
        staging    = module.batch_staging_dongdorrong
        production = module.batch_production_dongdorrong
    }

    job_definitions = {
        for name, mod in local.modules : name => regex("^(.+):[^:]+$", mod.job_definition_arn)[0]
    }
}

resource "aws_iam_policy" "batch_trigger" {
    name = "batch_dongdorrong_trigger"

    policy = jsonencode({
        Version = "2012-10-17"
        Statement = [
          {
            Sid    = "BatchSubmitJobStatement"
            Action = [
              "batch:SubmitJob",
            ]
            Effect   = "Allow"
            Resource = flatten([
              for name, mod in local.modules : [
                mod.job_queue_arn,
                local.job_definitions[name],
                format("%s:*", local.job_definitions[name])
              ]
            ])
          },
        ]
      })
}

Because EventBridge needs access to the Batch job definitions, I handled this with a regular expression in local values. I configured job_definitions and job_queue_arn to reference the module outputs.

With this setup, when you create a new Batch configuration, you only need to update the modules local value to run Batch jobs flexibly.

Diagram

100%

Drag to pan; pinch or use the wheel to zoom. Keyboard: arrow keys pan, + − zoom, 0 fits, Esc closes. The same content is in “Read the diagram as text” below the figure.