Table of Contents

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

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 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 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

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 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: