skip to Main Content

I want to deploy a azurerm_linux_function_app and depending on the access restrictions I will need a restriction from a VNET and other times I will need an IP address restriction.
The code is:

resource "azurerm_linux_function_app" "function_app" {
  name                = var.function_app_name
  resource_group_name = var.resource_group_name
  location            = "northeurope"
    
  storage_account_name       = var.storage_account_name
  service_plan_id            = var.service_plan_id
    
  identity {
    type = "SystemAssigned"
  }

  site_config {
    dynamic "ip_restriction" {
      for_each = var.enable_restriction == true ? [1] : []
        content {
          virtual_network_subnet_id = var.subnet_id
          ip_address                = var.ip_address
        }
    }

    application_stack {
      python_version = "3.8"
    }
  }
}

In the block called "ip_restriction" is where I want to have both variables, but sometimes I will want the VNET id and other times the IP address.

2

Answers


  1. Could you have 2 dynamic blocks for each of the cases? Only 1 of these variables will be set, is that right?

     resource "azurerm_linux_function_app" "function_app" {
      name                = var.function_app_name
      resource_group_name = var.resource_group_name
      location            = "northeurope"
    
      storage_account_name       = var.storage_account_name
      service_plan_id            = var.service_plan_id
    
      identity {
        type = "SystemAssigned"
      }
      site_config {
        dynamic "ip_restriction"{
          for_each = var.enable_restriction == true && var.subnet_id != "" ? [1] : []
          content {
            virtual_network_subnet_id = var.subnet_id
          }
        }
        dynamic "ip_restriction"{
          for_each = var.enable_restriction == true && var.ip_address != "" ? [1] : []
          content {
            ip_address                = var.ip_address
          }
        }
        application_stack {
          python_version = "3.8"
        }
      }
    }
    
    Login or Signup to reply.
  2. For arguments inside resource blocks and the blocks nested inside them like ip_restriction here, assigning the value null to the argument is exactly equivalent to omitting it entirely: from the provider’s perspective it cannot tell the difference because the Terraform language automatically sends null to represent "not set" when it sends the configuration to the provider.

    Therefore if you make sure both of your variables will be null when you want them to be unset then you’ll get the right behavior essentially "for free". null is the correct way to represent the absense of a value in Terraform; an empty string is not suitable to represent "unset" because an empty string is a valid value for some arguments.

    For example:

    variable "subnet_id" {
      type    = string
      default = null
    }
    
    variable "ip_address" {
      type    = string
      default = null
    }
    
    resource "azurerm_linux_function_app" "function_app" {
      # ...
    
      site_config {
        dynamic "ip_restriction" {
          for_each = var.enable_restriction == true ? [1] : []
          content {
            # If either variable is null then the provider will
            # treat it as if unset.
            virtual_network_subnet_id = var.subnet_id
            ip_address                = var.ip_address
          }
        }
    
        # ...
      }
    }
    

    If you only use the subnet ID and the IP address as part of the IP restriction and don’t need them in any other context, an idiomatic way to model this would be to make ip_restriction be a single input variable of an object type and use its presence to decide whether to include the ip_restriction block:

    variable "ip_restriction" {
      type = object({
        subnet_id  = optional(string)
        ip_address = optional(string)
      })
      default = null
    }
    
    resource "azurerm_linux_function_app" "function_app" {
      # ...
    
      site_config {
        dynamic "ip_restriction" {
          for_each = var.ip_restriction[*]
          content {
            virtual_network_subnet_id = ip_restriction.value.subnet_id
            ip_address                = ip_restriction.value.ip_address
          }
        }
    
        # ...
      }
    }
    

    This structure tends to make it clearer how these different values are all related to one another, and avoids the nonsensical situation of setting either subnet_id or ip_address when the ip_restriction block isn’t enabled at all: it isn’t possible for the entire object to be null while the attributes inside are non-null.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search