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