Skip to content

Dynamic Block

Dynamic Block


Dynamic blocks are used to define multiple instances of the same block type within a single resource. They are used to simplify the configuration of resources that can have multiple instances of the same block type.


The syntax of a dynamic block is as follows:


codepipeline.tf
1
#---------------------------------------------------
2
# AWS codepipeline
3
#---------------------------------------------------
4
5
resource "aws_codepipeline" "this" {
141 collapsed lines
6
count = var.codepipeline_enable ? 1 : 0
7
8
name = var.codepipeline_name
9
role_arn = var.codepipeline_role_arn
10
pipeline_type = var.codepipeline_pipeline_type
11
execution_mode = var.codepipeline_execution_mode
12
13
dynamic "artifact_store" {
14
iterator = artifact_store
15
for_each = can(length(var.codepipeline_artifact_store)) ? var.codepipeline_artifact_store : []
16
17
content {
18
type = lookup(artifact_store.value, "type", null)
19
location = lookup(artifact_store.value, "location", null)
20
region = lookup(artifact_store.value, "region", null)
21
22
dynamic "encryption_key" {
23
iterator = encryption_key
24
for_each = can(length(lookup(artifact_store.value, "encryption_key", []))) ? [lookup(artifact_store.value, "encryption_key", [])] : []
25
26
content {
27
id = lookup(encryption_key.value, "id", null)
28
type = lookup(encryption_key.value, "type", null)
29
}
30
}
31
}
32
}
33
34
dynamic "stage" {
35
iterator = stage
36
for_each = can(length(var.codepipeline_stage)) ? var.codepipeline_stage : []
37
38
content {
39
name = lookup(stage.value, "name", null)
40
41
dynamic "action" {
42
iterator = action
43
for_each = can(length(lookup(stage.value, "action", []))) ? lookup(stage.value, "action", []) : []
44
45
content {
46
name = lookup(action.value, "name", null)
47
category = lookup(action.value, "category", null)
48
owner = lookup(action.value, "owner", null)
49
provider = lookup(action.value, "provider", null)
50
version = lookup(action.value, "version", null)
51
configuration = lookup(action.value, "configuration", null)
52
input_artifacts = lookup(action.value, "input_artifacts", null)
53
output_artifacts = lookup(action.value, "output_artifacts", null)
54
role_arn = lookup(action.value, "role_arn", null)
55
run_order = lookup(action.value, "run_order", null)
56
region = lookup(action.value, "region", null)
57
namespace = lookup(action.value, "namespace", null)
58
}
59
}
60
}
61
}
62
63
dynamic "trigger" {
64
iterator = trigger
65
for_each = can(length(var.codepipeline_trigger)) ? var.codepipeline_trigger : []
66
67
content {
68
provider_type = lookup(trigger.value, "provider_type", null)
69
70
dynamic "git_configuration" {
71
iterator = git_configuration
72
for_each = can(length(lookup(trigger.value, "git_configuration", []))) ? [lookup(trigger.value, "git_configuration", [])] : []
73
74
content {
75
source_action_name = lookup(git_configuration.value, "source_action_name", null)
76
77
dynamic "pull_request" {
78
iterator = pull_request
79
for_each = can(length(lookup(git_configuration.value, "pull_request", []))) ? [lookup(git_configuration.value, "pull_request", [])] : []
80
81
content {
82
events = lookup(pull_request.value, "events", null)
83
84
dynamic "branches" {
85
iterator = branches
86
for_each = can(length(lookup(pull_request.value, "branches", []))) ? [lookup(pull_request.value, "branches", [])] : []
87
88
content {
89
includes = lookup(branches.value, "includes", null)
90
excludes = lookup(branches.value, "excludes", null)
91
}
92
}
93
94
dynamic "file_paths" {
95
iterator = file_paths
96
for_each = can(length(lookup(pull_request.value, "file_paths", []))) ? [lookup(pull_request.value, "file_paths", [])] : []
97
98
content {
99
includes = lookup(file_paths.value, "includes", null)
100
excludes = lookup(file_paths.value, "excludes", null)
101
}
102
}
103
}
104
}
105
106
dynamic "push" {
107
iterator = push
108
for_each = can(length(lookup(git_configuration.value, "push", []))) ? [lookup(git_configuration.value, "push", [])] : []
109
110
content {
111
dynamic "branches" {
112
iterator = branches
113
for_each = can(length(lookup(push.value, "branches", []))) ? [lookup(push.value, "branches", [])] : []
114
115
content {
116
includes = lookup(branches.value, "includes", null)
117
excludes = lookup(branches.value, "excludes", null)
118
}
119
}
120
121
dynamic "file_paths" {
122
iterator = file_paths
123
for_each = can(length(lookup(push.value, "file_paths", []))) ? [lookup(push.value, "file_paths", [])] : []
124
125
content {
126
includes = lookup(file_paths.value, "includes", null)
127
excludes = lookup(file_paths.value, "excludes", null)
128
}
129
}
130
131
dynamic "tags" {
132
iterator = tags
133
for_each = can(length(lookup(push.value, "tags", []))) ? lookup(push.value, "tags", []) : []
134
135
content {
136
includes = lookup(tags.value, "includes", null)
137
excludes = lookup(tags.value, "excludes", null)
138
}
139
}
140
}
141
}
142
}
143
}
144
}
145
}
146
147
dynamic "variable" {
148
iterator = variable
149
for_each = can(length(var.codepipeline_variable)) ? var.codepipeline_variable : []
150
151
content {
152
name = lookup(variable.value, "name", null)
153
default_value = lookup(variable.value, "default_value", null)
154
description = lookup(variable.value, "description", null)
155
}
156
}
157
11 collapsed lines
158
tags = merge(
159
local.codepipeline_stack_tags,
160
{
161
Name = var.codepipeline_name
162
}
163
)
164
165
lifecycle {
166
create_before_destroy = true
167
ignore_changes = []
168
}
169
}

Explanation:


  • The dynamic block is used to create multiple instances of the variable block within the aws_codepipeline resource.
  • The iterator argument is used to reference the current instance of the block.
  • The for_each argument is used to iterate over the list of variables defined in the var.codepipeline_variable variable.
  • The content block is used to define the attributes of the variable block.
  • The can() function is used to check if the var.codepipeline_variable variable is defined and not null.
  • The lookup() function is used to access the value of the attribute in the variable block.

The dynamic block is used to create multiple instances of the variable block within the aws_codepipeline resource. The iterator argument is used to reference the current instance of the block, and the for_each argument is used to iterate over the list of variables defined in the var.codepipeline_variable variable.


The syntax of a nested dynamic block is as follows:


Nested Dynamic Block

codepipeline.tf
1
#---------------------------------------------------
2
# AWS codepipeline
3
#---------------------------------------------------
4
5
resource "aws_codepipeline" "this" {
6
...
7
dynamic "stage" {
8
iterator = stage
9
for_each = can(length(var.codepipeline_stage)) ? var.codepipeline_stage : []
10
11
content {
12
name = lookup(stage.value, "name", null)
13
14
dynamic "action" {
15
iterator = action
16
for_each = can(length(lookup(stage.value, "action", []))) ? lookup(stage.value, "action", []) : []
17
18
content {
19
name = lookup(action.value, "name", null)
20
category = lookup(action.value, "category", null)
21
owner = lookup(action.value, "owner", null)
22
provider = lookup(action.value, "provider", null)
23
version = lookup(action.value, "version", null)
24
configuration = lookup(action.value, "configuration", null)
25
input_artifacts = lookup(action.value, "input_artifacts", null)
26
output_artifacts = lookup(action.value, "output_artifacts", null)
27
role_arn = lookup(action.value, "role_arn", null)
28
run_order = lookup(action.value, "run_order", null)
29
region = lookup(action.value, "region", null)
30
namespace = lookup(action.value, "namespace", null)
31
}
32
}
33
}
34
}
35
36
...
37
}

Explanation:


  • AWS CodePipeline Resource:

    • The aws_codepipeline resource block defines the configuration for an AWS CodePipeline. It allows you to create and manage CI/CD pipelines in AWS.
  • Dynamic Stage Block:

    • The dynamic block is used to dynamically generate multiple instances of the stage block within the aws_codepipeline resource. This is useful for pipelines with multiple stages, each having its own configuration.
    • iterator (stage): References the current instance of the stage block being created.
    • for_each: Iterates over the list of stages defined in the var.codepipeline_stage variable. If the list is empty or null, the block is skipped.
    • content: Defines the attributes of the stage block.
  • Stage Attributes:

    • name: The name of the stage, retrieved using the lookup() function from the stage block. If not found, it returns null.
  • Nested Dynamic Action Block:

    • A nested dynamic block within the stage block is used to create multiple instances of the action block within each stage.
    • iterator (action): References the current instance of the action block being created.
    • for_each: Iterates over the list of actions defined in the action attribute of the stage block. If the list is empty or null, the block is skipped.
    • content: Defines the attributes of the action block.
  • Action Attributes:

    • name: The name of the action, retrieved using the lookup() function from the action block. If not found, it returns null.
    • category: The category of the action (e.g., Source, Build, Deploy).
    • owner: The owner of the action (e.g., AWS).
    • provider: The service provider for the action (e.g., CodeBuild).
    • version: The version of the action.
    • configuration: A map of configuration settings for the action.
    • input_artifacts: A list of input artifacts for the action.
    • output_artifacts: A list of output artifacts for the action.
    • role_arn: The ARN of the IAM role assumed by the action.
    • run_order: The order in which the action runs within the stage.
    • region: The AWS region where the action is executed.
    • namespace: The namespace for variables generated by the action.
  • Helper Functions:

    • lookup(): Retrieves the value of the specified attribute from the block. If the attribute is not found, it returns a default value (e.g., null).
    • can(): Checks if the lookup() function returns a non-null value, ensuring that the for_each iteration only occurs if there are valid values.
    • length(): Returns the length of the list being iterated over, used in conjunction with can() to validate non-empty lists.

By using the dynamic block, the configuration becomes more flexible and scalable, allowing for easy management of complex pipelines with multiple stages and actions. This approach leverages Terraform’s powerful interpolation and iteration features to simplify the creation of intricate infrastructure setups.


Single Object Dynamic Block

In some cases, you may need to use a dynamic block where the block is not an array of objects but a single object, and it has a dynamic block inside it, or both the dynamic block and the block itself are single objects. In this scenario, you can use the for_each argument with a single object to create a single instance of the block.


codepipeline.tf
1
#---------------------------------------------------
2
# AWS CodePipeline Configuration
3
#---------------------------------------------------
4
5
resource "aws_codepipeline" "this" {
6
# Definition of the AWS CodePipeline resource
7
8
dynamic "artifact_store" {
9
iterator = artifact_store
10
for_each = can(length(var.codepipeline_artifact_store)) ? var.codepipeline_artifact_store : []
11
12
content {
13
type = lookup(artifact_store.value, "type", null)
14
location = lookup(artifact_store.value, "location", null)
15
region = lookup(artifact_store.value, "region", null)
16
17
dynamic "encryption_key" {
18
iterator = encryption_key
19
for_each = can(length(keys(lookup(artifact_store.value, "encryption_key", {})))) ? [lookup(artifact_store.value, "encryption_key", {})] : []
20
21
content {
22
id = lookup(encryption_key.value, "id", null)
23
type = lookup(encryption_key.value, "type", null)
24
}
25
}
26
}
27
}
28
...
29
}

Explanation:


  • AWS CodePipeline Resource:

    • The aws_codepipeline resource block defines the configuration for an AWS CodePipeline. It allows you to create and manage CI/CD pipelines in AWS.
  • Dynamic Artifact Store Block:

    • The dynamic block is used to dynamically generate multiple instances of the artifact_store block within the aws_codepipeline resource. This is useful for pipelines with multiple artifact stores, each having its own configuration.
    • iterator (artifact_store): References the current instance of the artifact_store block being created.
    • for_each: Iterates over the list of artifact stores defined in the var.codepipeline_artifact_store variable. If the list is empty or null, the block is skipped.
    • content: Defines the attributes of the artifact_store block.
  • Artifact Store Attributes:

    • type: The type of the artifact store (e.g., S3), retrieved using the lookup() function from the artifact_store block. If not found, it returns null.
    • location: The location of the artifact store (e.g., S3 bucket name).
    • region: The AWS region where the artifact store is located.
  • Nested Dynamic Encryption Key Block:

    • A nested dynamic block within the artifact_store block is used to create an instance of the encryption_key block if an encryption key is specified.
    • iterator (encryption_key): References the current instance of the encryption_key block being created.
    • for_each: Iterates over a single-item list containing the encryption_key object if it exists. If the object is empty or null, the block is skipped.
    • content: Defines the attributes of the encryption_key block.
  • Encryption Key Attributes:

    • id: The ID of the encryption key, retrieved using the lookup() function from the encryption_key block. If not found, it returns null.
    • type: The type of the encryption key (e.g., KMS).

  • Helper Functions:
    • lookup():
      • Retrieves the value of the specified attribute from the block.
      • If the attribute is not found, it returns a default value (e.g., null).
    • can():
      • Checks if the lookup() function returns a non-null value.
      • Ensures that the for_each iteration only occurs if there are valid values.
      • Example: can(length(var.codepipeline_stage)) ensures the for_each loop only runs if the list is not empty.
    • length():
      • Returns the length of the list being iterated over.
      • Used in conjunction with can() to validate non-empty lists.
      • Example: length(var.codepipeline_stage) returns the number of stages in the pipeline.
    • keys():
      • Returns a list of keys from a map.
      • Useful for checking the existence of keys in a nested object.
      • Example: keys(lookup(artifact_store.value, "encryption_key", {})) checks if the encryption_key map has any keys.

  • Usage of Helper Functions:
    • can() and length():
      • Ensure that dynamic blocks are only created if the corresponding lists are non-empty.
      • Prevents unnecessary block creation and potential errors in the configuration.
    • lookup():
      • Provides a safe way to access attributes within dynamic blocks.
      • Allows setting default values if the attribute does not exist.
    • keys():
      • Validates the existence of keys within nested objects.
      • Ensures that the for_each loop only runs if the nested object is not empty.

By using these helper functions, the configuration becomes more robust and flexible, allowing for dynamic generation of resources based on variable inputs. This approach simplifies the management of complex infrastructure setups and enhances the scalability of the configuration.