dynamic
Blocks with GCP ExamplesWhen working with Terraform to provision cloud infrastructure, you may encounter situations where you need to specify multiple environment variables, add multiple disks or IAM users, each requiring its own nested block structure. Writing each of these blocks separately can lead to repetitions and decreased code maintainability.
That's where dynamic blocks come in. Terraform's dynamic block feature allows you to programmatically generate nested blocks based on variable input. This can significantly reduce code duplication and make your infrastructure more modular and scalable.
In this tutorial, we'll illustrate how to use dynamic blocks in Terraform with a couple of examples. By the end of this tutorial, you'll understand how and when to use dynamic blocks to improve your own Terraform infrastructure-as-code projects.
If you want to follow along, ensure you have the following:
gcloud auth application-default login
, orWith that setup out of the way, let's dive into what Terraform dynamic blocks are and how you can use them to write cleaner and flexible infrastructure code.
dynamic
Block?
In Terraform, many resources include nested blocks that you define explicitly, for example, multiple env
variables inside a google_cloud_run_service
or multiple attached_disk
blocks
in a compute instance resource. These sub-blocks usually have the same structure and differ only in
their values.
Here's what multiple env
variable blocks might look like in a static configuration without
a dynamic block:
This approach works, but it becomes unwieldy if you want to add, remove, or modify the list of environment variables dynamically, especially if you want to drive these values from variables or data sources.
A dynamic block lets you programmatically generate multiple nested blocks of the same type, using input data
structured as a set or map. Instead of coding the same structure multiple times, you define it once with
dynamic
block which instructs Terraform to generate a nested block for every item included in the
input variable.
Here's the dynamic equivalent of the previous static example:
The dynamic "env"
block replaces two env
blocks.
This version is cleaner, scalable, driven entirely by input variable, and more suitable for automation
and reuse.
dynamic
Block
The dynamic
block has a specific structure and behaves differently from regular Terraform
configuration blocks. Here's a breakdown of its components:
block_name
env
, rule
, etc.).
for_each
(required)
<collection>
is the iterable data structure that defines sub-blocks' parameters.
It can be a list, map, or set.
iterator
(optional)
block_name
.block_name
conflicts with an attribute name, or for clarity in large blocks.Example with iterator
:
labels
(optional)
Now, let's review the google_cloud_run_service
configuration example with dynamic
block
in more details.
env
Blocks
This google_cloud_run_service
example configuration:
dynamic
block to configure environment variables for a Cloud Run serviceenv
parameters from an input variable (env_vars
)env_vars
is empty (i.e., no environment variables will be added)Warning:
This configuration provisions Google Cloud resources that may result in billing charges:
To avoid unexpected charges, remember to delete the service when no longer needed using
terraform destroy
.
Terraform Code:
Also create a file, for example env_vars.tfvars
, with the following content:
How This Works
The dynamic "env"
block tells Terraform to create multiple env { ... }
blocks (one
for each entry in var.env_vars
) within the container's specification.
for_each = var.env_vars
iterates over the items in the env_vars
variable (e.g.,
{ APP = "web", ENV = "prod" }
). For each key-value pair, a new env
block
will be created.
If var.env_vars
is empty ({}
), the dynamic block doesn't produce any env
blocks.
iterator = item
assigns the alias item
to the loop variable. Without this, the default
loop variable name would be env
, which could be confusing since we're creating env
blocks.
Using a dedicated iterator name (item
) clearly indicates that we're referring to the current key-value
pair in the iteration.
content { ... }
defines the content of each generated env
block.
name = item.key
- inside the content
block, item.key
accesses the key of the
current key-value pair from the env_vars
map. This key will be used as the name of the environment variable
within the container (e.g., "APP"
, "ENV"
).
value = item.value
- similarly, item.value
accesses the value associated with the current key
in the env_vars
map. This value will be assigned as the value of the environment variable within the
container (e.g., "web"
, "prod"
).
The output
block displays the URL and environment variables of the created Cloud Run service, while safely
handling the case when no environment variables are defined:
google_cloud_run_service.default.status[0].url
extracts the URL of the deployed Cloud Run
service.google_cloud_run_service.default.template[0].spec[0].containers[0].env
is the path to the
env
list for the first (and only) container in our Cloud Run resource.try( ... )
function prevents errors by safely falling back to []
if no
env
blocks are created (when env_vars
is empty).How to Apply This Configuration:
To create a Cloud Run instance without any environment variables simply run terraform apply
.
Since env_vars
is empty ({}
) by default, the dynamic block will not generate
any env
blocks.
If env
blocks are required, provide env_vars
values on the command line,
for example:
terraform apply -var='env_vars={APP="web",ENV="prod"}'
This overrides the default env_vars
value using the CLI input and Terraform will generate
env
blocks for each key-value pair.
Alternatively, supply env_vars
values in a .tfvars
file:
terraform apply -var-file="env_vars.tfvars"
The custom variable file is loaded using the -var-file
parameter. The default value from
main.tf
is overridden by the contents of env_vars.tfvars
and Terraform will create
env
blocks for each key-value pair.
The dynamic
block handles all these scenarios cleanly with no need for conditional logic.
Cleaning Up:
To delete the files, simply run terraform destroy
. Confirm when prompted, and Terraform will delete
all the created resources.
Summary:
As shown in this example, Terraform's dynamic
block combined with for_each
provides
a flexible way to define environment variables for a Google Cloud Run service. It allows your configuration
to adapt automatically based on variable input, without hardcoding individual env
blocks.
We also demonstrated how to use try( ... )
to safely access the parameters of the generated
resource through the structured indexing path (e.g., .template[0].spec[0].containers[0].env
).
Such code patterns are ideal for writing reusable, scalable infrastructure configurations that behave predictably regardless of input complexity.
Let's review a Terraform configuration example that provisions a Google Compute Engine (GCE) instance with two attached persistent disks using a dynamic block.
Warning:
This configuration creates Google Cloud resources that may incur charges:
Be sure to delete the resources (terraform destroy
) when they are no longer needed to avoid ongoing
charges.
Terraform Code:
How This Works
The attached_disks
variable defines a list of disk resources (excluding the boot disk),
each with the following parameters:
name
: the unique name of the disksize
: the size in GBtype
: the disk type (e.g., pd-standard
, pd-balanced
)You can modify this list to add or remove disks without changing the rest of the code.
The google_compute_disk
block uses for_each
to loop over the list of disks and create
corresponding named resources.
for disk in var.attached_disks : disk.name => disk
converts a list of disk objects (from the
attached_disks
variable) into a map where:
key
= disk.name
(disk names must be unique)value
= disk
- the corresponding full disk object (including name, size, and type)
from the list.
name = "${var.instance_name}-${each.value.name}"
forms the target disk names by adding the
instance name as a prefix.
The dynamic "attached_disk"
block uses for_each
to iterate over
google_compute_disk.extra_disks
and attach all the disks created earlier.
output "instance_and_attached_disks"
displays the instance name and the attached disk device
names (excluding the boot disk).
How to Apply This Configuration:
Initialize Terraform:
terraform init
Apply the configuration, providing your GCP project ID:
terraform apply -var="project_id=YOUR_PROJECT_ID"
Expected Result:
Cleaning Up:
To cleanup, simply run terraform destroy
. Confirm when prompted, and Terraform will delete
all the created resources.
Summary:
This example shows how to dynamically attach multiple disks to a Google Compute Engine instance using Terraform's
dynamic block and for_each
. The configuration allows you to change the number or type of attached
disks by simply modifying the input variable.
This approach simplifies the code and improves its maintainability, particularly when disk configuration is environment-specific or frequently updated.
In this tutorial, we explored two practical examples of using Terraform's dynamic blocks to programmatically generate nested resource blocks:
env_vars
), without hardcoding each variable.These examples demonstrate how dynamic blocks can help write cleaner, more modular, and DRY (Don't Repeat Yourself) Terraform code by iterating over variable input and rendering nested blocks accordingly.
By using dynamic blocks, you can significantly improve the maintainability and adaptability of your Terraform codebase, especially when dealing with variable infrastructure patterns across environments or deployments.
See Also:
Handling Terraform State in Multi-Environment Deployments
Understanding Terraform Variable Precedence
Terraform Value Types Tutorial
Terraform count
Explained with Practical Examples
Terraform for_each
Tutorial with Practical Examples
Working with External Data in Terraform
Handling Sensitive and Ephemeral Data in Terraform
Terraform Modules FAQ