Skip to content

Terraform Conditions

Loading...

Conditions


Conditions in Terraform allow you to control the creation of resources based on the evaluation of an expression. This can be useful when you need to create resources conditionally based on the state of other resources or variables.


The syntax of a conditional expression is as follows:


1
condition ? true_val : false_val

Explanation:


  • condition: The expression that is evaluated to determine the result.
  • true_val: The value returned if the condition is true.
  • false_val: The value returned if the condition is false.

The syntax of a conditional multiple nested expressions is as follows:


1
condition1 ? condition2 ? true_val2 : false_val2 : false_val1

Explanation:


  • condition1: The first expression that is evaluated to determine the result.
  • condition2: The second expression that is evaluated if the first condition is true.
  • true_val2: The value returned if the second condition is true.
  • false_val2: The value returned if the second condition is false.
  • false_val1: The value returned if the first condition is false.

Best Practices


Use Conditions Sparingly


Conditions can make your code harder to read and maintain. Use them sparingly and only when necessary. If you find yourself using conditions frequently, consider refactoring your code to make it more readable.


Use Variables


Use variables to store the condition values. This makes it easier to change the condition values without modifying the code.


The following example shows how to use variables to store the condition values:


1
variable "instance_enable" {
2
type = bool
3
description = "(Required) Whether to create an instance. Set to false to prevent the instance from being created."
4
default = false
5
}
6
7
resource "aws_instance" "example" {
8
count = var.instance_enable ? 1 : 0
9
# ...
10
}

Explanation:


  • instance_enable: A variable that stores the condition value.
  • aws_instance.example: The resource that is created conditionally based on the value of the instance_enable variable.

Use Local Values


If you find yourself using conditions frequently in your code, consider using local values to simplify the code. Local values allow you to define a value that is calculated based on other values in your code.


The following example shows how to use local values to simplify the code:


1
variable "subnet_cidr_block" {
2
type = string
3
description = "(Optional) The IPv4 CIDR block for the subnet."
4
default = ""
5
}
6
7
locals {
8
subnet_cidr_block = var.subnet_cidr_block != "" ? var.subnet_cidr_block : null
9
}
10
11
resource "aws_subnet" "example" {
12
vpc_id = "vpc-12345678"
13
cidr_block = local.subnet_cidr_block
14
# ...
15
}

Explanation:


  • subnet_cidr_block: A variable that stores the CIDR block for the subnet.
  • subnet_cidr_block: A local value that calculates the value of the CIDR block based on the value of the subnet_cidr_block variable.
  • aws_subnet.example: The resource that is created conditionally based on the value of the subnet_cidr_block local value.

Use Conditions in Dynamic Block


You can use conditions in dynamic blocks to create resources conditionally. This can be useful when you need to create multiple resources based on the evaluation of an expression.


The following example shows how to use conditions in dynamic blocks:


variables.tf
1
variable "security_group_ingress" {
2
type = list(object({
3
from_port = number
4
to_port = number
5
protocol = string
6
cidr_blocks = optional(list(string))
7
ipv6_cidr_blocks = optional(list(string))
8
security_groups = optional(list(string))
9
prefix_list_ids = optional(list(string))
10
description = optional(string)
11
self = optional(bool)
12
}))
13
description = <<EOF
14
(Optional, VPC only) Configuration block for egress rules. Can be specified multiple times for each egress rule. Each egress block supports fields documented below. This argument is processed in attribute-as-blocks mode. This argument is processed in attribute-as-blocks mode.
15
16
The following arguments are required:
17
18
from_port - (Required) Start port (or ICMP type number if protocol is icmp or icmpv6).
19
to_port - (Required) End range port (or ICMP code if protocol is icmp).
20
protocol - (Required) Protocol. If you select a protocol of -1 (semantically equivalent to all, which is not a valid value here), you must specify a from_port and to_port equal to 0. The supported values are defined in the IpProtocol argument on the IpPermission API reference. This argument is normalized to a lowercase value to match the AWS API requirement when using with Terraform 0.12.x and above, please make sure that the value of the protocol is specified as lowercase when using with older version of Terraform to avoid an issue during upgrade.
21
cidr_blocks - (Optional) List of CIDR blocks.
22
description - (Optional) Description of this ingress rule.
23
ipv6_cidr_blocks - (Optional) List of IPv6 CIDR blocks.
24
prefix_list_ids - (Optional) List of Prefix List IDs.
25
security_groups - (Optional) List of security groups. A group name can be used relative to the default VPC. Otherwise, group ID.
26
self - (Optional) Whether the security group itself will be added as a source to this ingress rule.
27
EOF
28
default = [{
29
cidr_blocks = [""]
30
from_port = 0
31
to_port = 0
32
protocol = "-1"
33
}]
34
}
35
variable "security_group_egress" {
36
type = list(object({
37
from_port = number
38
to_port = number
39
protocol = string
40
cidr_blocks = optional(list(string))
41
ipv6_cidr_blocks = optional(list(string))
42
security_groups = optional(list(string))
43
prefix_list_ids = optional(list(string))
44
description = optional(string)
45
self = optional(bool)
46
}))
47
description = <<EOF
48
(Optional) Configuration block for ingress rules. Can be specified multiple times for each ingress rule. Each ingress block supports fields documented below. This argument is processed in attribute-as-blocks mode. This argument is processed in attribute-as-blocks mode.
49
50
The following arguments are required:
51
52
from_port - (Required) Start port (or ICMP type number if protocol is icmp)
53
to_port - (Required) End range port (or ICMP code if protocol is icmp).
54
protocol - (Required) Protocol. If you select a protocol of -1 (semantically equivalent to all, which is not a valid value here), you must specify a from_port and to_port equal to 0. The supported values are defined in the IpProtocol argument in the IpPermission API reference. This argument is normalized to a lowercase value to match the AWS API requirement when using Terraform 0.12.x and above. Please make sure that the value of the protocol is specified as lowercase when used with older version of Terraform to avoid issues during upgrade.
55
cidr_blocks - (Optional) List of CIDR blocks.
56
description - (Optional) Description of this egress rule.
57
ipv6_cidr_blocks - (Optional) List of IPv6 CIDR blocks.
58
prefix_list_ids - (Optional) List of Prefix List IDs.
59
security_groups - (Optional) List of security groups. A group name can be used relative to the default VPC. Otherwise, group ID.
60
self - (Optional) Whether the security group itself will be added as a source to this egress rule.
61
EOF
62
default = null
63
}

Explanation:


  • security_group_ingress: A variable that stores the configuration block for ingress rules.
  • security_group_egress: A variable that stores the configuration block for egress rules.

security-group.tf
1
resource "aws_security_group" "this" {
2
count = var.security_group_enable ? 1 : 0
3
4
...
5
6
dynamic "ingress" {
7
iterator = ingress
8
for_each = can(length(var.security_group_ingress)) ? var.security_group_ingress : []
9
10
content {
11
cidr_blocks = lookup(ingress.value, "cidr_blocks", null)
12
ipv6_cidr_blocks = lookup(ingress.value, "ipv6_cidr_blocks", null)
13
prefix_list_ids = lookup(ingress.value, "prefix_list_ids", null)
14
security_groups = lookup(ingress.value, "security_groups", null)
15
description = lookup(ingress.value, "description", null)
16
from_port = lookup(ingress.value, "from_port", null)
17
to_port = lookup(ingress.value, "to_port", null)
18
protocol = lookup(ingress.value, "protocol", null)
19
self = lookup(ingress.value, "self", null)
20
}
21
}
22
23
dynamic "egress" {
24
iterator = egress
25
for_each = can(length(var.security_group_egress)) ? var.security_group_egress : []
26
27
content {
28
cidr_blocks = lookup(egress.value, "cidr_blocks", null)
29
ipv6_cidr_blocks = lookup(egress.value, "ipv6_cidr_blocks", null)
30
prefix_list_ids = lookup(egress.value, "prefix_list_ids", null)
31
security_groups = lookup(egress.value, "security_groups", null)
32
description = lookup(egress.value, "description", null)
33
from_port = lookup(egress.value, "from_port", null)
34
to_port = lookup(egress.value, "to_port", null)
35
protocol = lookup(egress.value, "protocol", null)
36
self = lookup(egress.value, "self", null)
37
}
38
}
39
40
...
41
}

Explanation:


  • aws_security_group.this: The resource that is created conditionally based on the value of the security_group_enable variable.
  • dynamic "ingress": A dynamic block that creates ingress rules conditionally based on the value of the security_group_ingress variable.
  • dynamic "egress": A dynamic block that creates egress rules conditionally based on the value of the security_group_egress variable.
  • can() function: A function that checks if the length of the list is greater than zero.
  • length() function: A function that returns the length of the list.
  • lookup() function: A function that retrieves the value of the specified key from the map. If the key does not exist, it returns the default value.