Reputation: 392
I am trying to create 3 VM's in Azure using Terraform count and each VM requires multiple disks. Is there any way to accomplish this ? I tried creating map of disk name and sizes but got an error that I can not use count and for_each together?
resource "azurerm_managed_disk" "this" {
for_each = var.disks
count = each.value > 0 ? var.node_count : 0
name = format("%s-%02d-datadisk", each.key, count.index + 1)
location = var.location
resource_group_name = var.resource_group_name
storage_account_type = "Premium_LRS"
create_option = "Empty"
disk_size_gb = each.value
tags = {
environment = "staging"
}
}
resource "azurerm_virtual_machine_data_disk_attachment" "this" {
for_each = var.disks
count = each.value > 0 ? var.node_count : 0
managed_disk_id = azurerm_managed_disk.this.*.id[count.index]
virtual_machine_id = azurerm_virtual_machine.this.*.id[count.index]
lun = "10"
caching = "None"
}
vm.tf
variable "disks" {
description = "Map of disk name and respective disk size"
type = map(string)
default = {
"binlog_disk" = "30"
"innodb_disk" = "20"
"data_disk" = "100"
"tmp_disk" = "10"
"backup_disk" = "150"
}
}
Upvotes: 2
Views: 5535
Reputation: 74209
The key requirement for resource for_each
is that you construct a map that has one element for each instance you want to create.
In this case, you want one instance for each (instance, disk) pair, which is a good use-case for the setproduct
function. In this case, because one of your resources is using count
, we'll combine it with the range
function to produce a suitable sequence of integers to identify the instances:
locals {
instance_disks = {
for pair in setproduct(range(length(azurerm_virtual_machine.this)), keys(var.disks)) : "${pair[0]}:${pair[1]}" => {
vm_index = pair[0]
disk_key = pair[1]
disk_size = var.disks[pair[1]]
}
}
}
The above will produce a map with one element per pair of virtual machine index and disk, with elements like this:
{
"0:binlog_disk" = {
vm_index = 0
disk_key = "binlog_disk"
disk_size = 30
},
"1:binlog_disk" = {
vm_index = 1
disk_key = "binlog_disk"
disk_size = 30
},
etc...
}
This map meets the resource for_each
requirement, so we can use it as the for_each
expression in the azurerm_managed_disk
and azurerm_virtual_machine_data_disk_attachment
resources:
resource "azurerm_managed_disk" "this" {
for_each = local.instance_disks
name = format("%s-%02d-datadisk", each.value.disk_key, each.value.instance_index + 1)
location = var.location
resource_group_name = var.resource_group_name
storage_account_type = "Premium_LRS"
create_option = "Empty"
disk_size_gb = each.value.disk_size
tags = {
environment = "staging"
}
}
resource "azurerm_virtual_machine_data_disk_attachment" "this" {
for_each = local.instance_disks
managed_disk_id = azurerm_managed_disk.this[each.key].id
virtual_machine_id = azurerm_virtual_machine.this[each.value.instance_index].id
lun = "10"
caching = "None"
}
The addresses of the instances of these resources will use the same keys as the map, giving addresses like this:
azurerm_managed_disk.this["0:binlog_disk"]
azurerm_managed_disk.this["1:binlog_disk"]
azurerm_managed_disk.this["0:innodb_disk"]
azurerm_managed_disk.this["1:innodb_disk"]
It's important to think about the instance keys you're using for a resource because they decide how Terraform will understand changes to the underlying map. In this case in particular, because we're identifying the virtual machines by their positions in the count
sequence, reducing the number of virtual machines by one would be understood by Terraform as a request to destroy all of the attachments which have the highest index. Removing the innodb_disk
entry from var.disks
would be understood by Terraform as a request to destroy all of the attachments whose addresses end with :innodb_disk
.
Upvotes: 6
Reputation: 222582
This is possible with v0.12
and above. You can do something like this,
variable "disks" {
default = [
{
unit_number = 0
size = "80"
thin_provisioned = true
},
]
}
dynamic "disk" {
for_each = [for s in var.disks: {
label = s.unit_number == "0" ? "${var.name}.vmdk" : "${var.name}_${s.unit_number}.vmdk"
unit_number = s.unit_number
size = s.size
thin_provisioned = contains(keys(s),"thin_provisioned") ? s.thin_provisioned : "true"
}]
content {
label = disk.value.label
unit_number = disk.value.unit_number
size = disk.value.size
thin_provisioned = disk.value.thin_provisioned
}
}
Upvotes: 0