Home > Enterprise >  Terraform: Omit some variables when provisioning more than one resource of a module
Terraform: Omit some variables when provisioning more than one resource of a module

Time:11-05

So I created a module for creating a subnet in terraform:

# modules/azure/subnet.main.tf

resource "azurerm_subnet" "subnet" {
  name                 = var.subnet_name
  resource_group_name  = var.resource_group_name
  virtual_network_name = var.virtual_network_name
  address_prefixes     = var.subnet_address_prefixes
  enforce_private_link_endpoint_network_policies = var.enforce_private_link_endpoint_network_policies

  delegation {
    name = var.subnet_delegation_name

    service_delegation {
      name    = var.subnet_service_delegation_name
    }
  }
}

And then I referenced it in another file to create a 2 subnets:

# ../../../modules/azure/subnet/main.tf

locals {
  subnet_suffix = "dev-subnet"
  subnet_delegation_name = {
    public_1 = "app-service-delegation"
  }
  subnet_service_delegation_name = {
    public_1 = "Microsoft.Web/serverFarms"
  }
}

module "subnet_public_1" {
  source = "../../../modules/azure/subnet"

  subnet_name                                    = "${var.subnet_name}-public-1-${local.subnet_suffix}"
  resource_group_name                            = data.azurerm_resource_group.dev_resource_group.name
  virtual_network_name                           = data.azurerm_virtual_network.dev_virtual_network.name
  subnet_address_prefixes                        = var.subnet_address_prefixes.public_1
  enforce_private_link_endpoint_network_policies = var.enforce_private_link_endpoint_network_policies.public_1
  subnet_delegation_name                         = local.subnet_delegation_name.public_1
  subnet_service_delegation_name                 = local.subnet_service_delegation_name.public_1
  tag_environment                                = var.tag_environment
}

module "subnet_private_1" {
  source = "../../../modules/azure/subnet"

  subnet_name                                    = "${var.subnet_name}-private-1-${local.subnet_suffix}"
  resource_group_name                            = data.azurerm_resource_group.dev_resource_group.name
  virtual_network_name                           = data.azurerm_virtual_network.dev_virtual_network.name
  subnet_address_prefixes                        = var.subnet_address_prefixes.private_1
  enforce_private_link_endpoint_network_policies = var.enforce_private_link_endpoint_network_policies.private_1
  tag_environment                                = var.tag_environment
}

However I do not want the subnet_private_1 module to have the subnet_delegation_name and subnet_service_delegation_name variables. So I omitted them in the module block, but when I run terraform plan I get the error:

│ Error: Missing required argument
│ 
│   with module.subnet_private_1.azurerm_subnet.subnet,
│   on ../../../modules/azure/subnet/main.tf line 9, in resource "azurerm_subnet" "subnet":
│    9:     name = var.subnet_delegation_name
│ 
│ The argument "delegation.0.name" is required, but no definition was found.
╵
╷
│ Error: Missing required argument
│ 
│   with module.subnet_private_1.azurerm_subnet.subnet,
│   on ../../../modules/azure/subnet/main.tf line 12, in resource "azurerm_subnet" "subnet":
│   12:       name    = var.subnet_service_delegation_name
│ 
│ The argument "delegation.0.service_delegation.0.name" is required, but no definition was found.

I have also tried to add the variables and set them to null but it still didn't work fine:

# ../../../modules/azure/subnet/main.tf

locals {
  subnet_suffix = "dev-subnet"
}

module "subnet_public_1" {
  source = "../../../modules/azure/subnet"

  subnet_name                                    = "${var.subnet_name}-public-1-${local.subnet_suffix}"
  resource_group_name                            = data.azurerm_resource_group.dev_resource_group.name
  virtual_network_name                           = data.azurerm_virtual_network.dev_virtual_network.name
  subnet_address_prefixes                        = var.subnet_address_prefixes.public_1
  enforce_private_link_endpoint_network_policies = var.enforce_private_link_endpoint_network_policies.public_1
  subnet_delegation_name                         = local.subnet_delegation_name.public_1
  subnet_service_delegation_name                 = local.subnet_service_delegation_name.public_1
  tag_environment                                = var.tag_environment
}

module "subnet_private_1" {
  source = "../../../modules/azure/subnet"

  subnet_name                                    = "${var.subnet_name}-private-1-${local.subnet_suffix}"
  resource_group_name                            = data.azurerm_resource_group.dev_resource_group.name
  virtual_network_name                           = data.azurerm_virtual_network.dev_virtual_network.name
  subnet_address_prefixes                        = var.subnet_address_prefixes.private_1
  enforce_private_link_endpoint_network_policies = var.enforce_private_link_endpoint_network_policies.private_1
  subnet_delegation_name                         = var.subnet_delegation_name.private_1
  subnet_service_delegation_name                 = var.subnet_service_delegation_name.private_1
  tag_environment                                = var.tag_environment
}

This is my variables.tf file:

variable "subnet_name" {
  type        = string
  description = "The name of the subnet"
  default     = "mysubnet"
}

variable "resource_group_name" {
  type        = string
  description = "The name which should be used for this Resource Group."
  default     = "MyDevRG"
}

variable "virtual_network_name" {
  type        = string
  description = "The name of the virtual network"
  default     = "my-dev-vnet"
}

variable "subnet_address_prefixes" {
  type        = map(list(string))
  description = "The address prefixes to use for the subnet."
  default = {
    public_1  = ["10.1.1.0/24"],
    private_1 = ["10.1.2.0/24"]
  }
}

variable "enforce_private_link_endpoint_network_policies" {
  type        = map(bool)
  description = "Enable or Disable network policies for the private link endpoint on the subnet. Setting this to true will Disable the policy and setting this to false will Enable the policy. Default value is false"
  default = {
    public_1  = false,
    private_1 = true
  }
}

variable "subnet_delegation_name" {
  type        = map(string)
  description = "A name for this delegation"
  default = {
    public_1  = "app-service-delegation",
    private_1 = null
  }
}

variable "subnet_service_delegation_name" {
  type        = map(string)
  description = "The name of service to delegate to."
  default = {
    public_1  = "Microsoft.Web/serverFarms",
    private_1 = null
  }
}

variable "tag_environment" {
  type        = string
  description = "A mapping of tags which should be assigned to the resource."
  default     = "dev"
}

I got this error:

╷
│ Error: Missing required argument
│ 
│   with module.subnet_private_1.azurerm_subnet.subnet,
│   on ../../../modules/azure/subnet/main.tf line 9, in resource "azurerm_subnet" "subnet":
│    9:     name = var.subnet_delegation_name
│ 
│ The argument "delegation.0.name" is required, but no definition was found.
╵
╷
│ Error: Missing required argument
│ 
│   with module.subnet_private_1.azurerm_subnet.subnet,
│   on ../../../modules/azure/subnet/main.tf line 12, in resource "azurerm_subnet" "subnet":
│   12:       name    = var.subnet_service_delegation_name
│ 
│ The argument "delegation.0.service_delegation.0.name" is required, but no definition was found.

There is also an ongoing conversation here - null value is not treated per docs when passed to modules #24142 as to why null isn't working as per the documentation.

Any help as to how I can go about solving this will be highly appreciated.

CodePudding user response:

In your subnet module you have delegation settings block that is not optional, that's why you have to specify subnet_delegation_name and subnet_service_delegation_name variables when you call subnet module in subnet_private_1 and subnet_public_1.

To make delegation settings block optional you should use Terraform dynamic Blocks.

Here is an example:

subnet module with dynamic blocks:

# modules/azure/subnet.main.tf

resource "azurerm_subnet" "subnet" {
  name                 = var.subnet_name
  resource_group_name  = var.resource_group_name
  virtual_network_name = var.virtual_network_name
  address_prefixes     = var.subnet_address_prefixes
  enforce_private_link_endpoint_network_policies = var.enforce_private_link_endpoint_network_policies

  dynamic "delegation" {
    for_each = var.delegation_settings
    content {
      name = delegation.value["subnet_delegation_name"]
      service_delegation {
      name = delegation.value["subnet_service_delegation_name"]
      }
    }
  }
}

subnet module variables.tf:

variable "delegation_settings" {
    type = list(map(string))
    default = []
}

main.tf example:

terraform {
  required_version = "~> 1.0.8"

  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "2.81.0"
    }
  }
}

provider "azurerm" {
  features {}
}

data "azurerm_resource_group" "main" {
  name = var.resource_group_name
}

data "azurerm_virtual_network" "main" {
  name                = var.virtual_network_name
  resource_group_name = data.azurerm_resource_group.main.name
}

locals {
  subnet_suffix = "dev-subnet"
  
  delegation_settings = [{
    subnet_delegation_name         = "app-service-delegation"
    subnet_service_delegation_name = "Microsoft.Web/serverFarms"
  }]

}

module "subnet_public_1" {
  source = "../../../modules/azure/subnet"

  subnet_name                                    = "${var.subnet_name}-public-1-${local.subnet_suffix}"
  resource_group_name                            = data.azurerm_resource_group.main.name
  virtual_network_name                           = data.azurerm_virtual_network.main.name
  subnet_address_prefixes                        = var.subnet_address_prefixes.public_1
  enforce_private_link_endpoint_network_policies = var.enforce_private_link_endpoint_network_policies.public_1
  delegation_settings = [
    {
      subnet_delegation_name         = local.delegation_settings[0].subnet_delegation_name
      subnet_service_delegation_name = local.delegation_settings[0].subnet_service_delegation_name
    }
  ]
  tag_environment = var.tag_environment
}

module "subnet_private_1" {
  source = "../../../modules/azure/subnet"

  subnet_name                                    = "${var.subnet_name}-private-1-${local.subnet_suffix}"
  resource_group_name                            = data.azurerm_resource_group.main.name
  virtual_network_name                           = data.azurerm_virtual_network.main.name
  subnet_address_prefixes                        = var.subnet_address_prefixes.private_1
  enforce_private_link_endpoint_network_policies = var.enforce_private_link_endpoint_network_policies.private_1
  tag_environment                                = var.tag_environment
}

Note: I did not add the subnet_delegation_name and subnet_service_delegation_name variables to the variables.tf example file so that Terraform does not complain about omitting it in the subnet_private_1 module block. I only added them in my variables.tf module file.

  • Related