Ansible Playbook for Creating Multiple VMs with Sequential Naming in vCenter

In this blog post, we will explore an Ansible playbook that creates multiple virtual machines (VMs) in a vCenter environment.

· 5 min read
Ansible Playbook for Creating Multiple VMs with Sequential Naming in vCenter
Photo by Rich Tervet / Unsplash

In a previous post I explained how to deploy a batch of Virtual Machines from a template :

Automating VM Deployment in VMware vSphere with Ansible
This post aimed to provide a clear understanding of how the Ansible playbook functions for creating multiple VMs in a VMware vSphere environment.

In this blog post, we will explore an Ansible playbook that creates multiple virtual machines (VMs) in a vCenter environment. We will also host in another file common parameters to keep the main playbook "clean". This playbook uses sequential naming for the VMs and allows you to specify various parameters such as the number of VMs to create, VM names, templates, and more.

playbook diagram

Playbook Overview

The playbook is divided into several sections:

  1. Play definition: The play targets localhost, gathers no facts, and includes variables from an external YAML file called vars_multiple.yml.
  2. Create a folder for VMs in vCenter: This task uses the community.vmware.vcenter_folder module to create a new folder under the specified datacenter in vCenter, where the VMs will be created.
  3. List all VMs in the vCenter: This task retrieves information about all existing VMs in the vCenter environment using the community.vmware.vmware_vm_info module.
  4. Filter VMs based on prefix and determine the maximum number: Using a set_fact task, this section extracts the maximum number from the names of any existing VMs that match the specified prefix, in order to create new VM names with sequential numbering.
  5. Create VMs starting from the next available number: This section creates multiple VMs with custom specifications using the community.vmware.vmware_guest module and sequential naming based on the start_number.

Prerequisites

Before running this playbook, ensure that you have:

  1. Ansible installed and configured on your system.
  2. Access to a vCenter server with valid credentials.
  3. A proper inventory file or configuration for connecting to the vCenter server via an Ansible module.

To install Ansible you can read the instructions here :

Installing Ansible on Ubuntu
Quick post to run through Ansible installation on an Ubuntu OS.

Playbook Walkthrough

Let's walk through each section of this playbook:

1. Play definition

- name: Create multiple VMs with sequential naming
  hosts: localhost
  gather_facts: false
  vars_files:
    - vars_multiple.yml

This play is defined to run locally, does not gather facts about the system, and includes a vars_files directive that references a file called vars_multiple.yml. This YAML file contains variables required for creating VMs in vCenter.

2. Create a folder for VMs in vCenter

- name: Create a folder for VMs in vCenter
  community.vmware.vcenter_folder:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    validate_certs: "{{ vcenter_validate_certs }}"
    datacenter: "{{ vcenter_datacenter }}"
    folder_name: "{{ vcenter_destination_folder }}"
    folder_type: vm
    state: present

This task uses the community.vmware.vcenter_folder module to create a new folder under the specified datacenter in vCenter, where the VMs will be created. Replace variables like vcenter_hostname, vcenter_username, etc., with valid values according to your environment.

3. List all VMs in the vCenter

- name: List all VMs in the vCenter
  community.vmware.vmware_vm_info:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    validate_certs: "{{ vcenter_validate_certs }}"
  register: all_vms_info

This task retrieves information about all existing VMs in the vCenter environment using the community.vmware.vmware_vm_info module and registers the output as a variable called all_vms_info.

4. Filter VMs based on prefix and determine the maximum number

- name: Filter VMs based on prefix and determine the maximum number
  set_fact:
    max_existing_vm_number: "{{ (all_vms_info.virtual_machines | selectattr('guest_name', 'match', vm_name_prefix ~ '\\d+$') | map(attribute='guest_name') | map('regex_search', '\\d+$') | map('int') | list + [0]) | max }}"

Using a set_fact task, this section extracts the maximum number from the names of any existing VMs that match the specified prefix. This allows for sequential naming when creating new VMs.

5. Create VMs starting from the next available number

- name: Create VMs starting from the next available number
  community.vmware.vmware_guest:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    validate_certs: "{{ vcenter_validate_certs }}"
    name: "{{ vm_name }}_{{ item + start_number }}"
    datacenter: "{{ vcenter_datacenter }}"
    folder: "/{{ vcenter_destination_folder }}"
    template: "{{ templates }}"
    resource_pool: "{{ resource_pool }}"
    # Additional parameters for VM configuration
  loop: "{{ range(num_vms) }}"

This section creates multiple VMs with custom specifications using the community.vmware.vmware_guest module and sequential naming based on the start_number. Replace variables like templates, resource_pool, etc., with valid values according to your environment.

Running the Playbook

To run this playbook, save it as a YAML file (e.g., create-vms.yml) and execute the following command:

ansible-playbook create-vms.yml

Here is the full playbook :

---
- name: Create multiple VMs with sequential naming
  hosts: localhost
  gather_facts: false

  vars_files:
    - vars_multiple.yml

  tasks:
    - name: Create a folder for VMs in vCenter
      community.vmware.vcenter_folder:
        hostname: "{{ vcenter_hostname }}"
        username: "{{ vcenter_username }}"
        password: "{{ vcenter_password }}"
        validate_certs: "{{ vcenter_validate_certs }}"
        datacenter: "{{ vcenter_datacenter }}"
        folder_name: "{{ vcenter_destination_folder }}"
        folder_type: vm
        state: present

    - name: List all VMs in the vCenter
      community.vmware.vmware_vm_info:
        hostname: "{{ vcenter_hostname }}"
        username: "{{ vcenter_username }}"
        password: "{{ vcenter_password }}"
        validate_certs: "{{ vcenter_validate_certs }}"
      register: all_vms_info

    - name: Filter VMs based on prefix and determine the maximum number
      set_fact:
        max_existing_vm_number: "{{ (all_vms_info.virtual_machines | selectattr('guest_name', 'match', vm_name_prefix ~ '\\d+$') | map(attribute='guest_name') | map('regex_search', '\\d+$') | map('int') | list + [0]) | max }}"

    - name: Increment max_existing_vm_number safely
      set_fact:
        start_number: "{{ (max_existing_vm_number | int) + 1 }}"

    - name: Create VMs starting from the next available number
      community.vmware.vmware_guest:
        hostname: "{{ vcenter_hostname }}"
        username: "{{ vcenter_username }}"
        password: "{{ vcenter_password }}"
        validate_certs: "{{ vcenter_validate_certs }}"
        datacenter: "{{ vcenter_datacenter }}"
        folder: "{{ vcenter_destination_folder }}"
        template: "{{ vm_template }}"
        name: "{{ vm_name_prefix }}{{ '%d' | format(item + start_number | int) }}"
        state: "{{ vm_state }}"
        guest_id: "{{ vm_guest_id }}"
        cluster: "{{ vcenter_cluster }}"
        customization:
          hostname: "{{ vm_name_prefix }}{{ '%d' | format(item + start_number | int) }}"
        hardware:
          memory_mb: "{{ vm_hw_ram_mb }}"
          num_cpus: "{{ vm_hw_cpu_n }}"
          scsi: "{{ vm_hw_scsi }}"
          hotadd_cpu: "{{ hot_add_cpu | default('True') }}"
          hotadd_memory: "{{ hot_add_memory | default('True') }}"
        networks:
          - name: "{{ vm_net_name }}"
            device_type: "{{ vm_net_type }}"
            type: dhcp
      loop: "{{ range(0, vms_count) | list }}"
      loop_control:
        index_var: item

Content of the file vars_multiple.yml :

---
vcenter_hostname: <your vCenter hostname or IP address>
vcenter_username: <your vCenter username>
vcenter_password: <your vCenter password>
vcenter_validate_certs: True  # Set to False if you don't have SSL certificates
vcenter_datacenter: <your datacenter name>
vm_template: "Template VM"  # Replace with the actual template VM name
vms_count: 5  # Number of VMs to create
vm_name_prefix: "VM-"  # Prefix for VM names
vm_net_name: "VM Network"  # Name of the network to attach the VMs to
vm_hw_ram_mb: 2048  # Memory size in MB for new VMs
vm_hw_cpu_n: 2  # Number of CPUs for new VMs
vcenter_destination_folder: "VMs"  # Folder where the created VMs will be placed in vCenter
vm_state: present  # Initial state of the created VMs (absent or present)
vm_guest_id: <your Guest ID>  # Replace with the actual Guest ID for new VMs
vcenter_cluster: "Cluster"  # Cluster where the created VMs will be placed in vCenter
hot_add_cpu: True  # Whether to add CPU during VM creation if not specified in hardware configuration
hot_add_memory: False  # Whether to add memory during VM creation if not specified in hardware configuration

Note: Make sure to replace the placeholder values with actual values from your environment before running the Ansible playbook.

As with any automation, testing in a non-production environment is crucial before rolling out to production, ensuring that the playbook performs as expected in your specific environment.