Reputation: 345
I have created a module corresponding to the Azure Firewall Network Rule Collection. The module looks like this:
resource "azurerm_firewall_network_rule_collection" "fwnrc" {
name = "fwnrc-${var.name}"
resource_group_name = var.resource_group_name
azure_firewall_name = var.azure_firewall_name
priority = var.priority
action = var.action
dynamic "rule" {
for_each = var.rule != null ? [true] : []
content {
name = var.rule.name
description = var.rule.description
source_addresses = var.rule.source_addresses
source_ip_groups = var.rule.source_ip_groups
destination_addresses = var.rule.destination_addresses
destination_ip_groups = var.rule.destination_ip_groups
destination_fqdns = var.rule.destination_fqdns
destination_ports = var.rule.destination_ports
protocols = var.rule.protocols
}
}
}
The section of interest right now is the dynamic "rule"
, which has a corresponding variable defined like this:
variable "rule" {
type = object({
name = string
description = string
source_addresses = list(string)
source_ip_groups = list(string)
destination_addresses = list(string)
destination_ip_groups = list(string)
destination_fqdns = list(string)
destination_ports = list(string)
protocols = list(string)
})
}
I know that it is possible to make the rule
variable "Optional" by setting its default value to null
. I want to go one step deeper and make the sub-variables* optional/required. For instance, in the resource documentation it is written that one must specify either *_addresses
or *_ip_groups
. The docs also says destination_fqdns
is optional.
* Is there an actual name for these?
Since the rule
variable is required by my module I get an error if I do not give explicit values to all sub-variables. My solution for now is to do the following:
module "firewall_network_rule_collection" {
source = "/path/to/module"
name = "fwrc"
azure_firewall_name = "afw"
resource_group_name = "rg"
priority = 110
action = "Allow"
rule = {
description = "rule"
name = "rule"
source_addresses = ["*"]
source_ip_groups = null
destination_ports = ["*"]
destination_addresses = [
"AzureContainerRegistry",
"MicrosoftContainerRegistry",
"AzureActiveDirectory"
]
destination_fqdns = null
destination_ip_groups = null
protocols = ["Any"]
}
}
Note the null
values. Can I get rid of these somehow?
--
I am using the following provider settings:
terraform {
required_version = ">=1.0.11"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">=2.90.0"
}
}
}
Upvotes: 1
Views: 3317
Reputation: 4176
Without the experimental optional()
, you can remove the type
constraint and communicate the module user that a map
with the specified attributes is expected.
Then in the code you have to check if an attribute is set or not. Simplified example:
variable "rule" {
# Type can't be specified, as even `map(any)` would enforce all entries to same type
type = any
# Some validation might be added for required attributes,
# or just let the error flow though from code
}
# ...
content {
# Fails if `name` is not specified
name = var.rule.name
# Default to `null`
description = lookup(var.rule, "description", null)
# ...
}
With the lookup()
you can also add another default value to an attribute.
In case there are non-null default values or the defaults are constructed with other attributes, it might be cleaner to fill in the defaults using a local variable (similar to what is needed with the current optional()
proposal).
For example:
locals {
rule = merge(
{
description = var.rule.name
destination_fqdns = ["foo.example.com]
source_addresses = null
# ...
},
var.rule
)
}
Then you can always refer to the attributes with just the dot notation.
Upvotes: 0
Reputation: 345
The experimental "optional" object type is currently available if one opts in.
Add the following to your module:
terraform {
experiments = [module_variable_optional_attrs]
}
This allows the following:
variable "rule" {
type = object({
name = string
description = optional(string)
source_addresses = optional(list(string))
source_ip_groups = optional(list(string))
destination_addresses = optional(list(string))
destination_ip_groups = optional(list(string))
destination_fqdns = optional(list(string))
destination_ports = optional(list(string))
protocols = optional(list(string))
})
}
Hopefully this feature makes it to a fully supported part of the next release.
Upvotes: 3