Home > Blockchain >  Exclude list of numbers from Range function in Terraform
Exclude list of numbers from Range function in Terraform

Time:11-23

I am trying to create a number of VMs in Azure using the for_each loop. Let's say VMs 1-100. However, there will be requests to delete i.e. VM number 50.

I have 2 problems with this:

terraform_azure> terraform state list
module.edit_vms.azurerm_linux_virtual_machine.test_seat["test-vm-1"]

terraform state rm module.edit_vms.azurerm_linux_virtual_machine.test_seat["test-vm-1"]
zsh: no matches found: module.edit_vms.azurerm_linux_virtual_machine.test_seat[test-vm-1]
  1. When I try to delete the VM 50 from Terraform state I get the error above. I don't understand why am I seeing no matches found when the resource was listed under terraform state list command

  2. Is there a way to delete the VM number 50 and set the VM count from 1 -> 101 and to prevent terraform from re-creating VM number 50? If deleting the VM from state file is not a good practice it would be even better if I could specify in the code that I want it destroyed and not recreated on future apply.

This is my variables.tf

variable "edit_seat_spec" {
    description = "VM details"
    type = list
    default = [{
        resource_group_name = "my_rg"
        server_name = "test-vm"
        server_size = "Standard_B2s"
        location = "East US"
        vnet = "test_vnet"
        subnet_name = "default"
        username = "username"
        password = "password"
        vm_count = "4"
    }]
}

And main.tf

locals{
      edit_seat = [
        for edit in var.edit_seat_spec : [
          for i in range(1, edit.vm_count) : {
            name = "${edit.server_name}-${i}"
            resource_group = edit.resource_group_name
            vnet = edit.vnet
            nic = "${edit.server_name}-${i}-nic"
            location            = edit.location
            subnet_name = edit.subnet_name
            size                = edit.server_size
            admin_username      = edit.username
            admin_password = edit.password
            }
        ]
      ]
    }
    
locals {
    edit_vm = flatten(local.edit_seat)
  }

    resource "azurerm_linux_virtual_machine" "vm" {
    for_each = {for edit in local.edit_vm: edit.name => edit}
            name = each.value.name
            resource_group_name = each.value.resource_group
            location            = each.value.location
            size                = each.value.size
            admin_username      = each.value.admin_username
            admin_password = each.value.admin_password
            disable_password_authentication = false
            network_interface_ids = [azurerm_network_interface.edit_seat_nic[each.key].id]
    os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
    }
    
      source_image_reference {
        publisher = "Canonical"
        offer     = "UbuntuServer"
        sku       = "16.04-LTS"
        version   = "latest"
      }
    }

CodePudding user response:

You can filter elements in a for, with the help of if statement.

Also mind that, range won't include the upper limit:

The interpretation of limit depends on the direction of step: for a positive step, the sequence is complete when the next number is greater than or equal to limit.

So, if you want a range from 1 to 100, you will need a range(1, var.variable_that_contains_100 1).
And so, if you want to skip one element in the stepping from 1 to 100, you'll need your upper limit to be at 102.


Here is an example, requesting 5 elements, skipping 3, for brevity:

variable "vm_count" {
  default = 5 
}

output "test" {
  value = [for i in range(1, var.vm_count   2) : {
    name = "some-server-${i}"
  } if i != 3]
}

This yields:

Changes to Outputs:
    test = [
        {
            name = "some-server-1"
        },
        {
            name = "some-server-2"
        },
        {
            name = "some-server-4"
        },
        {
            name = "some-server-5"
        },
        {
            name = "some-server-6"
        },
    ]

And if you want to exclude a list, as the title of the question state it, you could use contains instead in the condition, then increase the upper limit based on the length of the exclusion list plus one.

variable "vm_count" {
  default = 5 
}

variable "exclusion" {
  default = [2,4,5]
}

output "test" {
  value = [for i in range(1, var.vm_count   length(var.exclusion)   1) : {
    name = "some-server-${i}"
  } if !contains(var.exclusion, i)]
}

This gives:

Changes to Outputs:
    test = [
        {
            name = "some-server-1"
        },
        {
            name = "some-server-3"
        },
        {
            name = "some-server-6"
        },
        {
            name = "some-server-7"
        },
        {
            name = "some-server-8"
        },
    ]

Of course, here, you start to have edge cases, where you will possibly have to much VMs if the exclusion list contains numbers that are outside of the range, but that all depends on the complexity of your use case.

  • Related