count parameter

Used for conditional resources.

Syntax of Terraform conditional expression:

<CONDITION> ? <TRUE_VAL> : <FALSE_VAL>

Example how this and count can be utilized to conditionally create a resource:

resource "aws_autoscaling_schedule" "scale_in_at_night" {
  count = var.enable_autoscaling ? 1 : 0
 
  scheduled_action_name  = "${var.cluster_name}-scale-in-at-night"
  min_size               = 2
  max_size               = 10
  desired_capacity       = 2
  recurrence             = "0 17 * * *"
  autoscaling_group_name = aws_autoscaling_group.example.name
}

Example of if-else:

resource "aws_iam_user_policy_attachment" "neo_cloudwatch_full_access" {
  count = var.give_neo_cloudwatch_full_access ? 1 : 0
 
  user       = aws_iam_user.example[0].name
  policy_arn = aws_iam_policy.cloudwatch_full_access.arn
}
 
resource "aws_iam_user_policy_attachment" "neo_cloudwatch_read_only" {
  count = var.give_neo_cloudwatch_full_access ? 0 : 1
 
  user       = aws_iam_user.example[0].name
  policy_arn = aws_iam_policy.cloudwatch_read_only.arn
}

data sources can be referenced like so:

resource "aws_instance" "example_2" {
  count             = length(data.aws_availability_zones.all.names)
  availability_zone = data.aws_availability_zones.all.names[count.index]
  ami               = "ami-0fb653ca2d3203ac1"
  instance_type     = "t2.micro"
}
 
data "aws_availability_zones" "all" {}

How to access attributes of conditionally created resources

A safer approach is to take advantage of the concat and one functions. The concat function takes two or more lists as inputs and combines them into a single list. The one function takes a list as input and if the list has 0 elements, it returns null; if the list has 1 element, it returns that element; and if the list has more than 1 element, it shows an error. Putting these two together, and combining them with a splat expression, you get the following:

output "neo_cloudwatch_policy_arn" {
  value = one(concat(
aws_iam_user_policy_attachment.neo_cloudwatch_full_access[*].policy_arn,
aws_iam_user_policy_attachment.neo_cloudwatch_read_only[*].policy_arn
  ))
}

Limitations

You cannot reference any resource outputs in count.

resource "random_integer" "num_instances" {
  min = 1
  max = 3
}
 
resource "aws_instance" "example_3" {
  count         = random_integer.num_instances.result
  ami           = "ami-0fb653ca2d3203ac1"
  instance_type = "t2.micro"
}

will produce:

Error: Invalid count argument

  on main.tf line 30, in resource "aws_instance" "example_3":
  30:   count         = random_integer.num_instances.result

The "count" value depends on resource attributes that cannot be determined
until apply, so Terraform cannot predict how many instances will be created.
To work around this, use the -target argument to first apply only the
resources that the count depends on.

Terraform requires that it can compute count and for_each during the plan phase, before any resources are created or modified. This means that count and for_each can reference hardcoded values, variables, data sources, and even lists of resources (so long as the length of the list can be determined during plan), but not computed resource outputs.

for_each and for expressions

Used for conditional resources and inline blocks within a resource.

  dynamic "tag" {
    for_each = {
      for key, value in var.custom_tags:
      key => upper(value)
      if key != "Name"
    }
 
    content {
      key                 = tag.key
      value               = tag.value
      propagate_at_launch = true
    }
  }

Limitations

You cannot reference any resource outputs in for_each, same as in [[Terraform conditionals#count parameter#Limitations|count]].

if string directive

Used for conditionals within a string

Syntax:

%{ if <CONDITION> }<TRUEVAL>%{ endif }
%{ if <CONDITION> }<TRUEVAL>%{ else }<FALSEVAL>%{ endif }

where CONDITION is any expression that evaluates to a boolean and TRUEVAL is the expression to render if CONDITION evaluates to true, and FALSEVAL is the expression to render if CONDITION evaluates to false.

Example:

output "for_directive_index_if_else_strip" {
  value = <<EOF
%{~ for i, name in var.names ~}
${name}%{ if i < length(var.names) - 1 }, %{ else }.%{ endif }
%{~ endfor ~}
EOF
}
 
# for_directive_index_if_else_strip = "neo, trinity, morpheus."