Invalid value for "inputMap" parameter: lookup() requires a map as the first argument

Hello I’m getting this error - Invalid value for “inputMap” parameter: lookup() requires a map as the first argument

I’m building new resources for the azure windows app app services. If I leave the site_config empty it works fine, however if I put any values in the site_config it will error out.

The current module looks like this:

terraform {
  required_providers {
    azurerm = {
      source = "hashicorp/azurerm"
      version = "3.3.0"
    }
  }
}

module "win_app_service_module" {
  depends_on                      = [
    module.app_plan_module
    ]
  source              = "../../win_appservice"
  win_app_services    = {
    app_service1      = {
    name                = "az_app_name"
    resource_group_name = "az_rg_name"
    service_plan_id       = module.app_plan_module.asp_details.id
    location            = "centralus"
    tags                = {
      product           = "dev"
    }
    site_config         = {
      minimum_tls_version = "1.2"
      }
    }
  }
}
variable win_app_services {
    default     = ""
    description = "description"
}

resource "azurerm_windows_web_app" "main" {
  for_each                    = var.win_app_services

  name                        = each.value.name
  location                    = each.value.location
  resource_group_name         = upper(each.value.resource_group_name)
  service_plan_id             = each.value.service_plan_id
  app_settings                = lookup(each.value, "app_settings", null)
  client_affinity_enabled     = lookup(each.value, "client_affinity_enabled", null)
  client_certificate_enabled  = lookup(each.value, "client_certificate_enabled", null)
  client_certificate_mode     = lookup(each.value, "client_certificate_mode", null)
  enabled                     = lookup(each.value, "enabled", null)
  https_only                  = lookup(each.value, "https_only", null)
  tags                        = merge(each.value["tags"], var.additional_tags)

  dynamic "connection_string" {
    for_each = lookup(each.value, "connection_string", {})
    content {
        name  = lookup(connection_string.value, "name", null)
        type  = lookup(connection_string.value, "type", null)
        value = lookup(connection_string.value, "value", null)
    }
  }
dynamic "site_config" {
    for_each = lookup(each.value, "site_config", {})
    content {
scm_type                                        = lookup(site_config.value, "scm_type", null)
        always_on                                       = lookup(site_config.value, "always_on", null)
        api_management_api_id                           = lookup(site_config.value, "api_management_api_id", null)
        app_command_line                                = lookup(site_config.value, "app_command_line", null)
        auto_heal_enabled                               = lookup(site_config.value, "auto_heal_enabled", null)
        container_registry_managed_identity_client_id   = lookup(site_config.value, "container_registry_managed_identity_client_id", null)
        container_registry_use_managed_identity         = lookup(site_config.value, "container_registry_use_managed_identity", null)  
        local_mysql_enabled                             = lookup(site_config.value, "local_mysql_enabled", null)
        default_documents                               = lookup(site_config.value, "default_documents", null) == null ? [] : [lookup(site_config.value, "default_documents", null)]
        ftps_state                                      = lookup(site_config.value, "ftps_state", null)
        health_check_path                               = lookup(site_config.value, "health_check_path", null)
        health_check_eviction_time_in_min               = lookup(site_config.value, "health_check_eviction_time_in_min", null)
        http2_enabled                                   = lookup(site_config.value, "http2_enabled", null)
        minimum_tls_version                             = lookup(site_config.value, "minimum_tls_version", null)
        load_balancing_mode                             = lookup(site_config.value, "load_balancing_mode", null)
        managed_pipeline_mode                           = lookup(site_config.value, "managed_pipeline_mode", null)
        remote_debugging_version                        = lookup(site_config.value, "remote_debugging_version", null)
        scm_minimum_tls_version                         = lookup(site_config.value, "scm_minimum_tls_version", null)
        scm_use_main_ip_restriction                     = lookup(site_config.value, "scm_use_main_ip_restriction", null)
        use_32_bit_worker                               = lookup(site_config.value, "use_32_bit_worker", null)
        websockets_enabled                              = lookup(site_config.value, "websockets_enabled", null)
      }
  }

The error goes through each argument and claims site_config.value is 1.2, however it should just be tls version. This worked in older versions and I’m not sure what I’m missing.

│ Error: Invalid function argument
│ 
│   on .terraform\modules\win_app_service_module\win_appservice\main.tf line 226, in resource "azurerm_windows_web_app" "main":
│  226:         scm_type                                        = lookup(site_config.value, "scm_type", null)
│     ├────────────────
│     │ site_config.value is "1.2"
│ 
│ Invalid value for "inputMap" parameter: lookup() requires a map as the
│ first argument.
╵
╷
│ Error: Invalid function argument
│ 
│   on .terraform\modules\win_app_service_module\win_appservice\main.tf line 227, in resource "azurerm_windows_web_app" "main":
│  227:         always_on                                       = lookup(site_config.value, "always_on", null)
│     ├────────────────
│     │ site_config.value is "1.2"
│ 
│ Invalid value for "inputMap" parameter: lookup() requires a map as the
│ first argument.

Hi @robertlelliott,

It looks like you only have a single site_config object per elements of var.win_app_services, and so the dynamic block is iterating over the attributes of that single map, where site_config.key would be "minimum_tls_version" and site_config.value is "1.2" as shown in the error message.

You may not need a dynamic block here at all, if it would be acceptable to always define a site_config block but to leave minimum_tls_version unset when not needed:

  site_config {
    minimum_tls_version = try(each.value.site_config.minimum_tls_version, null)
   # ... and similar for other arguments
  }

If you do need to dynamically choose between zero or one site_config blocks based on whether that attribute is set then you will need a slightly different expression in the dynamic block’s for_each argument:

  for_each = try(each.value.site_config, null)[*]

There are two parts to this expression:

  • The try function handles the situation where site_config isn’t set at all, returning null instead in that case. try is essentially a more modern version of lookup that can work with the normal attribute access syntax.
  • The [*] operator converts that single value that might be null into a list of either zero or one elements, returning an empty list only if the value was null. That means the dynamic block will produce a site_config block only if you have a non-null site_config attribute in each.value.

@apparentlymart , yes I don’t need a dynamic block for what I’m doing but thanks for showing me how to do it if I ever need it. What you showed above made it work, thank you!