Reputation: 549
I use terraform to build my infrastructure in the cloud (I'm new to terraform).
I also create some firewall rules with terraform that I need to assign to multiple servers. For that I created a field called firewall in my model.
Variables.tf
variable "hosts" {
type = map(object({
name = string
serverType = string
serverImage = string
serverLocation = string
serverKeepDisk = bool
serverBackup = bool
ip = string
volume = bool
volumeName = string
volumeSize = number
volumeFormat = string
volumeAutomount = bool
volumeDeleteProtection = bool
floating = bool
firewall = list(string) <--- this thing here!
}))
}
Terraform.tfvars
"myServer01" = {
name = "s01"
serverType = "cx11"
serverImage = "ubuntu-20.04"
serverLocation = "fsn1"
serverKeepDisk = false
serverBackup = false
ip = "192.168.0.12"
volume = true
volumeName = "data"
volumeSize = 100
volumeFormat = "ext4"
volumeAutomount = false
volumeDeleteProtection = false
floating = false
firewall = [hcloud_firewall.basic.id, hcloud_firewall.ssh.id, hcloud_firewall.webserver.id] <-- I define here variables of the firewalls I need to assign to this specific server
},
main.tf
resource "hcloud_server" "default" {
for_each = var.hosts
name = each.value.name
server_type = each.value.serverType
image = each.value.serverImage
location = each.value.serverLocation
user_data = file("userdata.yml")
keep_disk = each.value.serverKeepDisk
backups = each.value.serverBackup
ssh_keys = [hcloud_ssh_key.default.id]
firewall_ids = each.value.firewall
Error:
│ Error: Variables not allowed
│
│ on terraform.tfvars line 71:
│ 71: firewall = [hcloud_firewall.basic.id, hcloud_firewall.ssh.id, hcloud_firewall.webserver.id]
│
│ Variables may not be used here.
How can I define multiple firewall rules by ID/ as variable in Terraform?
Upvotes: 0
Views: 2329
Reputation: 74259
Definitions for root module input variables effectively exist outside of the root module, in the same sense that when you pass values to variables of a child module you define them in the context of the caller, and so they don't have direct access to anything defined in the called module.
Because there's no module outside of the root module to evaluate expressions in, root module input variables must always have entirely constant values. There is no way for root module variable definitions to directly refer to objects declared inside the root module.
However, you can get a similar effect to what you intended here by adding a level of indirection: rather than having the hosts
variable directly include the firewall ids, it could instead include some symbolic names that have meaning only in this configuration, each of which refers to one of your declared firewalls.
variable "hosts" {
type = map(object({
name = string
serverType = string
serverImage = string
serverLocation = string
serverKeepDisk = bool
serverBackup = bool
ip = string
volume = bool
volumeName = string
volumeSize = number
volumeFormat = string
volumeAutomount = bool
volumeDeleteProtection = bool
floating = bool
firewall_names = set(string)
}))
}
locals {
firewall_ids = {
basic = hcloud_firewall.basic.id
ssh = hcloud_firewall.ssh.id
webserver = hcloud_firewall.webserver.id
}
}
resource "hcloud_server" "default" {
for_each = var.hosts
name = each.value.name
server_type = each.value.serverType
image = each.value.serverImage
location = each.value.serverLocation
user_data = file("userdata.yml")
keep_disk = each.value.serverKeepDisk
backups = each.value.serverBackup
ssh_keys = [hcloud_ssh_key.default.id]
firewall_ids = [
for name in each.value.firewall_names : local.firewall_ids[name]
]
}
In your terraform.tfvars
file you can then specify these indirectly using the symbolic names, instead of by referring directly to the objects inside the module:
hosts = {
"myServer01" = {
name = "s01"
serverType = "cx11"
serverImage = "ubuntu-20.04"
serverLocation = "fsn1"
serverKeepDisk = false
serverBackup = false
ip = "192.168.0.12"
volume = true
volumeName = "data"
volumeSize = 100
volumeFormat = "ext4"
volumeAutomount = false
volumeDeleteProtection = false
floating = false
firewall_names = ["basic", "ssh", "webserver"]
}
}
Please note that idiomatic Terraform style is to use attribute names that are all lowercase with underscores separating words, like server_type
instead of serverType
. Terraform will of course accept both, but using the conventional style may make your module less surprising to people with prior Terraform experience.
Upvotes: 0
Reputation: 238249
You can't create dynamic variables. So its not possible to do what you want to do. Instead, you should create a local
variable and use that instead:
locals {
firewall = [hcloud_firewall.basic.id, hcloud_firewall.ssh.id, hcloud_firewall.webserver.id]
}
Then you use local.firewall
in place of var.firewall
in your code.
Upvotes: 1