I’m currently trying to create several instances under Openstack with Terraform. Afterwards I want to attach more of them. But I’m not getting anywhere with the last point.
Does anyone have an idea how I can solve it?
variable "instances" {
description = "The Instances to be deployed"
type = map(object({
name = string
image = string
flavor = string
volume_size = number
security_groups = list(string)
network = string
keypair_name = string
floating_ip_pool = string
tags = list(string)
additional_disks = list(object({
disk = string
volume_size = number
}))
}))
}
instances = {
"Websrv01" = {
name = "websrv01"
image = "Ubuntu 22.04"
flavor = "SCS-2V-4-20s"
volume_size = 20
security_groups = ["default"]
network = "test-intern"
keypair_name = "ssh-pub"
floating_ip_pool = "public"
tags = ["general", "webserver"]
additional_disks = []
},
"dbsrv01" = {
name = "dbsrv01"
image = "Ubuntu 22.04"
flavor = "SCS-2V-4-20s"
volume_size = 20
security_groups = ["default"]
network = "test-intern"
keypair_name = "ssh-pub"
floating_ip_pool = "public"
tags = ["general", "dbserver"]
additional_disks = [
{ disk = "db_data", volume_size = 30 },
{ disk = "db_log", volume_size = 10 }
]
}
}
resource "openstack_compute_instance_v2" "instances" {
for_each = var.instances
name = each.value.name
flavor_id = data.openstack_compute_flavor_v2.flavor[each.key].id
key_pair = each.value.keypair_name
security_groups = each.value.security_groups
## Boot Disk
block_device {
uuid = data.openstack_images_image_v2.image[each.key].id
source_type = "image"
volume_size = each.value.volume_size
boot_index = 0
destination_type = "volume"
delete_on_termination = true
}
lifecycle {
prevent_destroy = true
network {
name = each.value.network
}
tags = each.value.tags
}
## Get floating IP if needed
resource "openstack_networking_floatingip_v2" "floating_ip" {
for_each = { for k, v in var.instances : k => v if v.floating_ip_pool != "" }
pool = each.value.floating_ip_pool
}
## Associate floating ip to Instances
resource "openstack_compute_floatingip_associate_v2" "associate" {
for_each = openstack_networking_floatingip_v2.floating_ip
instance_id = openstack_compute_instance_v2.instances[each.key].id
floating_ip = each.value.address
}
## Additional Disks
resource "openstack_blockstorage_volume_v3" "additional_disks" {
for_each = {
for instance, disks in var.instances : instance => disks.additional_disks
if length(disks.additional_disks) > 0
}
name = "${each.key}-${each.value[0].disk}"
size = each.value[0].volume_size
enable_online_resize = true
}
## Attach additional disks to instances
resource "openstack_compute_volume_attach_v2" "attach_additional_disks" {
for_each = {
for instance, disks in var.instances : instance => disks.additional_disks
if length(disks.additional_disks) > 0
}
instance_id = openstack_compute_instance_v2.instances[each.key].id
volume_id = openstack_blockstorage_volume_v3.additional_disks[each.key].id
}
Only the first hard disk is taken into account here.
Ich i try to loop truth the list, with [*] it fails.
regards
Eddi
with single addition Disk it works:
resource "openstack_blockstorage_volume_v3" "additional_disks" {
for_each = {
for instance, disks in var.instances : instance => disks.additional_disks
if length(disks.additional_disks) > 0
}
name = "${each.key}-${each.value[0].disk}"
size = each.value[0].volume_size
enable_online_resize = true
}
resource "openstack_compute_volume_attach_v2" "attach_additional_disks" {
for_each = {
for instance, disks in var.instances : instance => disks.additional_disks
if length(disks.additional_disks) > 0
}
instance_id = openstack_compute_instance_v2.instances[each.key].id
volume_id = openstack_blockstorage_volume_v3.additional_disks[each.key].id
}
2
Answers
I had already tried to solve it with ChatGPT. And he was already on the right track. But had not suggested a solution with locals. Had tried it in the for_each loop using flatten. I am not yet familiar with flatten and locals. Now I have changed the solution a bit, in a simpler notation, after ChatGPT had suggested to convert to a map(object).
This is what my solution looks like now:
I will also implement the tip with enable_online_resize.
Another question. I would like to use the code in several environments, so it will certainly make sense to create a module from it? Then I'll give it a try now.
And finally, I would like to generate a dynamic inventory in INI format for Ansible and group it with the tags. Are there any easy ways to do this?
This question is more or less similar to the one asked & answered here. Nevertheless, I want to make it easy for you.
If you want iterate over multiple objects in a json object & have appropriate correlation with parent objects, it is recommended to use
flatten
function & extract relevant fields of your interest for further iteration.Below
locals
var does exactly the same ::We construct a list of objects with
instance_key
forlookup
of instances & then associateddisk
keys & values to it.The above
local.additional_disks
could be used like below to provision the resources.And the
terraform plan
gives below output ::Another unasked advice, I see you hardcode
enable_online_resize
directly on the resource. This isn’t a good idea, pull it from theadditional_disks
attribute by hardcoding it totrue
.