Search code examples
azureansiblejinja2

Looking for a way to reduce the number of VMs using Ansible by newest instances first


I am deploying any number of instances using ansible in Azure Cloud based on a instancesCount variable and iterating over the creation tasks for each resource (NIC, managed disk, VM, etc) using with_sequence: count={{ instancesCount }}. The instances, and their associated resources (managed disks, os disk, NICs) are created in the same name format:

{{ env }}-xxx-{{ vmName }}-{{ count }}:
say instancesCount is 3:
dev-xxx-myvm-1
dev-xxx-myvm-1-nic
dev-xxx-myvm--1-osDisk
dev-xxx-myvm-2
dev-xxx-myvm-2-nic
dev-xxx-myvm-2-osDisk
dev-xxx-myvm-3
dev-xxx-myvm-3-nic
dev-xxx-myvm-3-osDisk

I am looking for a way to scale down the number of instances when the variable instancesCount changes to a lower integer number, say from 3 to 1, by removing the newest instances first: dev-xxx-myvm-3, dev-xxx-myvm-2, and their associated resources, leaving just dev-xxx-myvm-1.

The logic I am thinking is first gather info from Azure using Ansible modules for each resource (azure_rm_virtualmachine_info, azure_rm_networkinterface_info, azure_rm_manageddisk_info, etc). Save each of that in a variable of its own, and using that, and instancesCount, remove those resources that exceed, but I am not sure how to proceed..

Using jinja2 I can easily get the vm names to be removed, but cant convert this code to Ansible:

{% set to_remove = existing_vms.vms | length - instancesCount | int %}

{% for item in existing_vms.vms[-to_remove:] %}
{{ item.name }}
{% endfor %}

Was able to convert successfully:

    - name: Print name of vms to be removed
      debug:
        var: item.name
      loop: "{{ existing_vms.vms[-(to_remove|int):] }}"

Yet, one last issue: the list is not sorted. Say we have 10 instances, the existing_vms.vms.*.name looks like this, as its pulled from Azure:

dev-xxx-myvm-1
dev-xxx-myvm-10
dev-xxx-myvm-2
dev-xxx-myvm-3
dev-xxx-myvm-4
dev-xxx-myvm-5
dev-xxx-myvm-6
dev-xxx-myvm-7
dev-xxx-myvm-8
dev-xxx-myvm-9

How would I be able to sort the list by the trailing number in the name, ascending, before passing it to the slice operator - [-(to_remove)|int:]?


Solution

  • Correct solution is to look for what VMs are already deployed using azure_rm_virtualmachine_info, save that in a variable and sanitize the variable, then pass it to azure_rm_virtualmachine while specifying remove_on_absent. There is no need to get managed disk, nic or any other resource info as this will remove dependent resources.

    1. get the deployment info:
    - name: "Getting deployment state"
      block:
      - name: "Getting deployment state"
        azure.azcollection.azure_rm_virtualmachine_info:
          resource_group: myRG
        register: existing_vms
      - name: Saving all VM names in vm_names
        ansible.builtin.set_fact:
          vm_names: "{{ vm_names | default([]) + [item.name] }}"
        loop: "{{ existing_vms.vms }}"
      - name: Sorting the VM names in vm_names_sorted
        ansible.builtin.set_fact:
          vm_names_sorted: "{{ vm_names | community.general.version_sort }}"
      ignore_errors: true
    

    This will sort the list properly:

    FROM:

    dev-xxx-myvm-1
    dev-xxx-myvm-10
    dev-xxx-myvm-2
    dev-xxx-myvm-3
    dev-xxx-myvm-4
    dev-xxx-myvm-5
    dev-xxx-myvm-6
    dev-xxx-myvm-7
    dev-xxx-myvm-8
    dev-xxx-myvm-9
    

    TO:

    dev-xxx-myvm-1
    dev-xxx-myvm-2
    dev-xxx-myvm-3
    dev-xxx-myvm-4
    dev-xxx-myvm-5
    dev-xxx-myvm-6
    dev-xxx-myvm-7
    dev-xxx-myvm-8
    dev-xxx-myvm-9
    dev-xxx-myvm-10
    
    1. Calculate how many we should remove:
    - name: "Setting scale down value if instancesCount < currrent deployment"
      ansible.builtin.set_fact: 
        to_remove: "{{ vm_names_sorted | length - instancesCount | int }}"
      when: vm_names_sorted is defined and vm_names_sorted | length | int > instancesCount | int and instancesCount | int != 0
    
    1. We can then pass the new list to azure_rm_virtualmachine to remove to_remove number of VMs, starting with the newest (dev-xxx-myvm-10, dev-xxx-myvm-9, etc):
    
    - name: "Scaling down deployment to {{ instancesCount }}"
      azure.azcollection.azure_rm_virtualmachine:
        resource_group: myRG
        name: "{{ item }}"
        remove_on_absent: ['network_interfaces', 'virtual_storage', 'public_ips']
        state: absent
      loop: "{{ vm_names_sorted[-(to_remove|int):] }}"
      when: to_remove is defined