= Terraform Conditional Arguments and Dynamic Blocks = **Summary**: This wiki page shows how I used terraform's conditionals in arguments and dynamic blocks to make configurations more flexible by showing a few examples. \\ **Date**: 1 January 2026 \\ {{tag>terraform}} Conditionals and dynamic blocks are terraform features you usually don't need when you're just starting. Their power comes when you're starting to work on more complex terraform configurations or modules. Below I show a couple of examples where I used conditionals and dynamic blocks. For both features, you always need to consider if using them is really the best solution for your problem. I've often encountered that using for example default values and optional arguments can solve the same problem in a more straightforward way. == Conditionals == I usually use [[https://developer.hashicorp.com/terraform/language/expressions/conditionals |terraform conditionals]] to optionally include certain arguments in a resource or a module. === Conditional Resource Creation === My most used example of using conditionals is to conditionally create resources based on a boolean variable. In the example below, taken from [[https://github.com/getshifting/getshifting/blob/main/terraform/apps/postgresql/postgresql.tf |my PostgreSQL module]], I only create a Private DNS Zone if the variable `deploy_psql_pep` is set to true: module "avm-res-network-privatednszone" { count = var.deploy_psql_pep == true ? 1 : 0 source = "Azure/avm-res-network-privatednszone/azurerm" domain_name = var.privatezone parent_id = module.avm-res-resources-resourcegroup.resource_id tags = var.tags enable_telemetry = false } === Conditional Arguments === Another use case is to conditionally include certain arguments in a resource or a module. For example, in the code snippet below, I use a conditional argument to only include the `law_id` argument if the variable `deploy_psql_logs` is set to true. If it's false, the argument is set to null and will be ignored by terraform. law_id = var.deploy_psql_logs ? module.law.id : null This is another example where I use conditional arguments to only enable cross-region restore if the redundancy is set to GeoRedundant and the variable `cross_region_restore_enabled` is true: cross_region_restore_enabled = var.redundancy == "GeoRedundant" && var.cross_region_restore_enabled ? true : null === Conditional Argument Values === In the example below, I set the value to a certain argument based on the value of a variable: # Required for restore scenarios create_mode = var.create_mode # Set to null if empty string. Only required when using create_mode of Restore or Recovery recover_database_id = var.recover_database_id == "" ? null : var.recover_database_id recovery_point_id = var.recovery_point_id == "" ? null : var.recovery_point_id == Dynamic Blocks == [[https://developer.hashicorp.com/terraform/language/expressions/dynamic-blocks |Dynamic blocks]] are another way of making terraform configurations more flexible. They also have different use cases. === Repeat for each Object in a List === Below is an example where dynamic blocks are used to repeat a block for each object in a list. The variable {{{backup_vault_blobstorage_policies}}} has a list of {{{policy_retention_rules}}} objects. In the resource definition, a dynamic block is used to create a {{{retention_rule}}} block for each object in that list. # Variable backup_vault_blobstorage_policies = [ { policy_name = "bkpol-euw-shift" policy_repeating_time_intervals = ["R/2025-01-01T04:00:00Z/P1D"] policy_default_vault_retention_duration = "P5D" policy_data_store_type = "VaultStore" policy_retention_rules = [ { rule_name = "Yearly" rule_retention_duration = "P1Y" rule_priority = 1 absolute_criteria = "FirstOfYear" }, { rule_name = "Monthly" rule_retention_duration = "P1M" rule_priority = 5 absolute_criteria = "FirstOfMonth" }, { rule_name = "Weekly" rule_retention_duration = "P1W" rule_priority = 10 absolute_criteria = "FirstOfWeek" }, { rule_name = "Daily" rule_retention_duration = "P2D" rule_priority = 15 absolute_criteria = "FirstOfDay" } ] } ] # Resource resource "azurerm_data_protection_backup_policy_blob_storage" "bkpol" { for_each = { for policy in var.backup_vault_blobstorage_policies : policy.policy_name => policy } name = each.key vault_id = azurerm_data_protection_backup_vault.bvault.id backup_repeating_time_intervals = each.value.policy_repeating_time_intervals vault_default_retention_duration = each.value.policy_default_vault_retention_duration dynamic "retention_rule" { for_each = each.value.policy_retention_rules content { name = retention_rule.value.rule_name priority = retention_rule.value.rule_priority criteria { absolute_criteria = retention_rule.value.absolute_criteria } life_cycle { data_store_type = each.value.policy_data_store_type duration = retention_rule.value.rule_retention_duration } } } } === Conditionally add Dynamic Blocks to a Resource === In the example below, the variable only defines the {{{retention_daily}}} and {{{retention_weekly}}} objects. In the resources definition, because the {{{retention_monthly}}} and {{{retention_yearly}}} objects are not defined, these rules will not be created. # Variable recovery_vault_policies_vms = [ { policy_name = "rsvpol-euw-shift" backup_frequency = "Daily" timezone = "UTC" instant_restore_retention_days = 7 backup_time = "02:00" retention_daily = { count = 8 } retention_weekly = { count = 2 weekdays = ["Wednesday"] } instant_restore_resource_group_prefix = "rg-euw-shift-azurebackup-" } ] # Resource resource "azurerm_backup_policy_vm" "rsvpol_vm" { for_each = { for policy in var.recovery_vault_policies_vms: policy.policy_name => policy } name = each.key resource_group_name = var.resource_group_name policy_type = "V2" recovery_vault_name = azurerm_recovery_services_vault.rsvault.name timezone = each.value.timezone instant_restore_retention_days = each.value.instant_restore_retention_days backup { frequency = each.value.backup_frequency time = each.value.backup_time } dynamic "retention_daily" { for_each = each.value.retention_daily != null ? [each.value.retention_daily] : [] content { count = lookup(retention_daily.value, "count") } } dynamic "retention_weekly" { for_each = each.value.retention_weekly != null ? [each.value.retention_weekly] : [] content { count = lookup(retention_weekly.value, "count") weekdays = lookup(retention_weekly.value, "weekdays") } } dynamic "retention_monthly" { for_each = each.value.retention_monthly != null ? [each.value.retention_monthly] : [] content { count = lookup(retention_monthly.value, "count") weekdays = lookup(retention_monthly.value, "weekdays") weeks = lookup(retention_monthly.value, "weeks") } } dynamic "retention_yearly" { for_each = each.value.retention_yearly != null ? [each.value.retention_yearly] : [] content { count = lookup(retention_yearly.value, "count") weekdays = lookup(retention_yearly.value, "weekdays") weeks = lookup(retention_yearly.value, "weeks") months = lookup(retention_yearly.value, "months") } } instant_restore_resource_group { prefix = each.value.instant_restore_resource_group_prefix } } For completeness, as some of the blocks are not required, this is the variable definition: variable "recovery_vault_policies_vms" { description = "(Required) The list of Recovery Vault Policies. Note that the instant_restore_retention_days MUST be lower than the retention_daily count." type = list(object({ policy_name = string default_policy = optional(bool, false) timezone = string instant_restore_retention_days = number backup_frequency = string backup_time = string retention_daily = optional(object({ count = number }), null) retention_weekly = optional(object({ count = number weekdays = list(string) }), null) retention_monthly = optional(object({ count = number weekdays = list(string) weeks = list(string) }), null) retention_yearly = optional(object({ count = number weekdays = list(string) weeks = list(string) months = list(string) }), null) instant_restore_resource_group_prefix = string })) } === Conditionally add Dynamic Blocks to a Resource II === In the example below another use case is displayed for combining conditionals and dynamic blocks. In the example below, Entra ID permissions need to be assigned to a storage account fileshare. For this, the file share first needs to be configured and added to [[https://learn.microsoft.com/en-us/azure/storage/files/storage-files-identity-ad-ds-enable |Active Directory]]. For that purpose, a condition is added that is dependent on the variable {{{azure_files_authentication_enabled}}}. This variable can first be set to false, and once the share is added and the required information is known, the variable can be set to true to add the block to the resource. # Variables azure_files_authentication_enabled = true azure_storage_sid = "S-1-5-21-c07042a510-976c1cfc35-109e3ee4cb-59734" # Resource resource "azurerm_storage_account" "storage_account" { name = var.storage_account_name resource_group_name = var.resource_group_name location = var.location account_tier = var.account_tier account_replication_type = var.replication_type allow_nested_items_to_be_public = var.allow_nested_items_to_be_public tags = var.tags dynamic "azure_files_authentication" { for_each = var.azure_files_authentication_enabled ? [1] : [] content { directory_type = "AD" active_directory { domain_name = "getshifting.local" domain_guid = "446b774a-fd85-424d-947f-6cd03c26a57d" domain_sid = "S-1-5-21-c07042a510-67a1a4512-5974e28db3" forest_name = "getshifting.local" storage_sid = var.azure_storage_sid netbios_domain_name = "getshifting.local" } } } } //This wiki has been made possible by://