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.
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.
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.
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.
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.