I have used implemented same structure as this post:
Override a single value in a map.
I am able to create a number of resources based on each type. However I would also like the ability to override the “instance_count” values from either command line, but preferably in a .tfvars file without having to specify all the other attributes as well. Any help would be appreciated !!
#main.tf
# A list of objects with one object per instance.
instances = flatten([
for image_key, image in var.images : [
for index in range(image.instance_count) : {
flavor = image.flavor
instance_index = index
image_key = image_key
instance_name = image.instance_name
}
]
])
}
resource "openstack_compute_instance_v2" "instance" {
for_each = {
# Generate a unique string identifier for each instance
for inst in local.instances : format("%s-%02d", inst.image_key, inst.instance_index + 1) => inst
}
image_name = each.value.image_name
flavor_id = each.value.flavor
name = each.key
security_groups = var.security_groups
availability_zone = each.value.availability_zones
key_pair = "foptst"
network {
name = var.network_name
}
}
#variables.tf
variable "images" {
type = map(object({
instance_name = string
flavor = string
instance_count = string
}))
default = {
"web" = {
instance_name = "web"
flavor = "1"
"instance_count" = 4
}
"worker" = {
instance_name = "wrk"
flavor = "2"
"instance_count" = 3
}
"db" = {
instance_name = "sql"
flavor = "3"
"instance_count" = 2
}
}
#terraform.tfvars
images = {
db = {
instance_name = "sql"
flavor = "1"
"instance_count" = 0
},
"web" = {
instance_name = "web"
flavor = "2"
"instance_count" = 0
},
"worker" = {
instance_name = "wrk"
flavor = "3"
"instance_count" = 1
}
}
Hi @dominic.viau,
From what you’ve described it seems like you’ve currently defined “number of instances” as a property of the image itself, which therefore means that it needs to be defined as part of the set of images.
When I think about the goal you described, I imagine “compute instance set” as something separate that has an image. So I might define that like this:
variable "instance_sets" {
type = map(object({
instance_count = number
image_key = string
# (availability_zones?)
}))
}
variable "images" {
type = map(object({
name = string
flavor_id = string
}))
}
To actually declare the instances, we can combine those two data structures to produce a collection with one element per compute instance:
locals {
instances = flatten([
for instset_key, instset in var.instance_sets : [
for index in range(instset.instance_count) : {
instset_key = instset_key
instance_index = index
image_key = instset.image_key
image = var.images[instset.image_key]
}
]
])
}
resource "openstack_compute_instance_v2" "instance" {
for_each = {
for inst in local.instances :
format("%s-%02d", inst.instset_key, inst.instance_index + 1) => inst
}
image_name = each.value.image.name
flavor_id = each.value.image.flavor_id
name = each.key
security_groups = var.security_groups
availability_zone = each.value.availability_zones
key_pair = "foptst"
network {
name = var.network_name
}
}
If you don’t anticipate ever needing to create more than one instance set for the same image then you could simplify this by removing image_key
from the instance_sets
type and using instset_key
also as the image_key
, as long as you make sure to use the same keys for the instance_sets
map as for the images
map.
I am not sure I completely understand the use of the “instset.image_key” as it does not seem to be used in resource creation. would it not be simple to simply seperate both maps and simply use instset_key and forget about instset.image_key.
Anyways I declare this in my variables.tf to structure on how would like to define my instances. Then I simply overwrite them using a .tfvars file with values I want.
So taking your example , here’s what I have
#######################
#variables.tf
#######################
locals {
instances = flatten([
for instset_key, instset in var.instance_sets : [
for index in range(instset.instance_count) : {
instset_key = instset_key
instance_index = index
image_key = instset.image_key
image = var.images[instset.image_key]
#have not implemented multiple disks,
# that would be the next step.
#os_disk_count = image.os_disk_count
}
]
])
}
variable "instance_sets" {
type = map(object({
image_key = string
instance_count = number
# Have not implemented multiple disks, set value for future use.
# It simply an empty value not being used
os_disk_count = number
}))
default = {
"vm1"= {
"image_key" = "db"
"instance_count" = 0
"os_disk_count" = 0
}
"vm2" = {
"image_key" = "worker"
"instance_count" = 0
"os_disk_count" = 0
}
"vm3" = {
"image_key" = "web"
"instance_count" = 1
"os_disk_count" = 0
}
}
}
variable "images" {
type = map(object({
instance_name = string
flavor_id = string
}))
default = {
"db" = {
image_name = "rhel8"
instance_name = "vm1"
flavor_id = "xlarge"
}
"worker" = {
image_name = "ubuntu"
instance_name = "vm2"
flavor_id = "medium"
}
"web" = {
image_name = "win2022"
instance_name = "vm3"
flavor_id = "large"
}
}
}
#######################
#end of variables.tf file
#######################
############################################
If I run the following below I get as instance name : vm3-01
############################################
#######################
***example 1 of resource.tf***
#######################
resource "openstack_compute_instance_v2" "instance" {
for_each = {
for inst in local.instances :
format("%s-%02d", inst.instset_key, inst.instance_index + 1) => inst
}
image_name = each.value.image.instance_name
flavor_id = each.value.image.flavor_id
name = each.key
security_groups = var.security_groups
availability_zone = each.value.availability_zones
key_pair = "foptst"
network {
name = var.network_name
}
}
**outputs**
~ vm_name = {
+ vm3-01 = "vm3-01"
}
############################################
If I run the following below I get as instance name : web
############################################
#######################
***example 2 of resource.tf***
#######################
resource "openstack_compute_instance_v2" "instance" {
for_each = {
for inst in local.instances :
format("%s-%02d", inst.instset_key, inst.instance_index + 1) => inst
}
image_name = each.value.image.instance_name
flavor_id = each.value.image.flavor_id
name = each.value.image_key
security_groups = var.security_groups
availability_zone = each.value.availability_zones
key_pair = "foptst"
network {
name = var.network_name
}
}
**outputs**
~ vm_name = {
+ vm3-01 = "web"
}