azureterraformterraform-provider-awsterraform-provider-azureterraform-provider-gcp

Arguments must be maps or objects, got "string"


Expected Behaviour

I'm trying to iterate through var.vm_settings but doesn't seem to cooperate. The error doesn't make sense because the required type for the input variable is "string"

Actual Behaviour

throws error (see Error output)

Error Output

Error: Error in function call

  on ..\main.tf line 4, in locals:
   4:   vm_settings = zipmap(keys(var.vm_settings), [for vm_setting in values(var.vm_settings) : merge(var.vm_defaults, vm_setting)])
    |----------------
    | var.vm_defaults is object with 12 attributes

Call to function "merge" failed: arguments must be maps or objects, got
"string".

Terraform (and AzureRM Provider) Version

Affected Resource(s)

Terraform Configuration Files

Main.tf

locals {
  vm_settings = zipmap(keys(var.vm_settings), [for vm_setting in values(var.vm_settings) : merge(var.vm_defaults, vm_setting)])
}

resource "azurerm_linux_virtual_machine" "oracle_db" {
  for_each = local.vm_settings

  name                            = each.key
  resource_group_name             = var.resource_group_name
  location                        = var.location
  size                            = each.value.size
  admin_username                  = each.value.admin_username
  admin_password                  = local.admin_password
  disable_password_authentication = each.value.disable_password_authentication
  priority                        = each.value.priority
  eviction_policy                 = each.value.eviction_policy
  zone                            = each.value.zone
  network_interface_ids           = [azurerm_network_interface.nic.id, ]

  admin_ssh_key {
    username   = each.value.admin_ssh_key.username
    public_key = each.value.admin_ssh_key.public_key
  }

  identity {
    type = each.value.identity.type
  }

  os_disk {
    caching              = each.value.os_disk.caching
    storage_account_type = each.value.os_disk.storage_account_type
  }

  source_image_reference {
    publisher = each.value.source_image_reference.publisher
    offer     = each.value.source_image_reference.offer
    sku       = each.value.source_image_reference.sku
    version   = each.value.source_image_reference.version
  }

  boot_diagnostics {
    storage_account_uri = each.value.boot_diagnostics.storage_account_uri
  }
}


Variables.tf

variable "vm_settings" {
  description = "Map of vm's to create (keys are vm names). Allowed values are the same as for vm_defaults."
  type        = any
  default     = {}
}

variable "vm_defaults" {
  type = object({
    name                            = string
    size                            = string
    admin_username                  = string
    admin_password                  = string
    disable_password_authentication = string
    priority                        = string
    eviction_policy                 = string
    identity = object({
      type = string
    })
    admin_ssh_key = object({
      username   = string
      public_key = string
    })
    os_disk = object({
      name                 = string
      caching              = string
      storage_account_type = string
    })
    source_image_reference = object({
      publisher = string
      offer     = string
      sku       = string
      version   = string
    })
    boot_diagnostics = object({
      storage_account_uri = string
    })
  })
  default = {
    name                            = null
    size                            = null
    admin_username                  = null
    admin_password                  = ""
    disable_password_authentication = null
    priority                        = null
    eviction_policy                 = null
    identity = {
      type = null
    }
    admin_ssh_key = {
      username   = ""
      public_key = null
    }
    os_disk = {
      name                 = null
      caching              = null
      storage_account_type = null
    }
    # Image used to create the virtual machines.
    source_image_reference = {
      publisher = ""
      offer     = ""
      sku       = ""
      version   = ""
    }
    boot_diagnostics = {
      storage_account_uri = ""
    }
  }
  description = <<EOT
virtual machine default settings (only applied to virtual machine settings managed within this module)
    vm_settings = {
    size                            = (Required) The SKU which should be used for this Virtual Machine, such as Standard_F2.
    admin_username                  = (Required) The username of the local administrator used for the Virtual Machine. Changing this forces a new resource to be created.ring
    admin_password                  = (Optional) The Password which should be used for the local-administrator on this Virtual Machine. Changing this forces a new resource to be created.
    disable_password_authentication = (Optional) Should Password Authentication be disabled on this Virtual Machine? Defaults to true. Changing this forces a new resource to be created.

    # Spot VM to save money
    priority        = (Optional) Specifies the priority of this Virtual Machine. Possible values are Regular and Spot. Defaults to Regular. Changing this forces a new resource to be created.
    eviction_policy = (Optional) Specifies what should happen when the Virtual Machine is evicted for price reasons when using a Spot instance. At this time the only supported value is Deallocate. Changing this forces a new resource to be created. This can only be configured when priority is set to Spot.

    # type of Managed Identity which should be assigned to the Linux Virtual Machine
    identity = {
      type = (Required) The type of Managed Identity which should be assigned to the Linux Virtual Machine. Possible values are SystemAssigned, UserAssigned and SystemAssigned, UserAssigned.
    }

    # SSH key
    admin_ssh_key = {
      username   = (Required) The Public Key which should be used for authentication, which needs to be at least 2048-bit and in ssh-rsa format. Changing this forces a new resource to be created.
      public_key = (Required) The Username for which this Public SSH Key should be configured. Changing this forces a new resource to be created.
    }

    # Internal OS disk
    os_disk = {
      name                 = (Required) The Type of Caching which should be used for the Internal OS Disk. Possible values are None, ReadOnly and ReadWrite.
      caching              = (Required) The Type of Caching which should be used for the Internal OS Disk. Possible values are None, ReadOnly and ReadWrite.
      storage_account_type = (Required) The Type of Storage Account which should back this the Internal OS Disk. Possible values are Standard_LRS, StandardSSD_LRS and Premium_LRS. Changing this forces a new resource to be created.
    }

    # Image used to create the virtual machines.
    source_image_reference = {
      publisher = (Optional) Specifies the publisher of the image used to create the virtual machines.
      offer     = (Optional) Specifies the offer of the image used to create the virtual machines.
      sku       = (Optional) Specifies the SKU of the image used to create the virtual machines.
      version   = (Optional) Specifies the version of the image used to create the virtual machines.
    }

    boot_diagnostics = {
      storage_account_uri = (Optional) The Primary/Secondary Endpoint for the Azure Storage Account which should be used to store Boot Diagnostics, including Console Output and Screenshots from the Hypervisor. Passing a null value will utilize a Managed Storage Account to store Boot Diagnostics.
    }
  }
EOT
}

Solution

  • Usually if you have a default map and you want to combine it with user provided map, the following is enough:

    merge(var.vm_defaults, var.vm_settings)
    

    The above will use values from var.vm_settings, and everything else will be from var.vm_defaults.

    So your local.vm_settings would be:

    locals {
      vm_settings = merge(var.vm_defaults, var.vm_settings)
    }