--- - name: Ensure multipass is installed and create a VM with cloud-init hosts: localhost gather_facts: yes vars_prompt: - name: vm_name prompt: "What is the VM name (don't use spaces)?" private: no default: testvm vars: vm_image: 24.04 vm_disk: 15G vm_memory: 4G vm_cpus: 1 ssh_key_name: "{{ vm_name }}_ssh_key" tasks: - name: Check if VM already exists command: multipass list --format json register: vm_list_json - name: Parse VM list set_fact: vm_exists: "{{ vm_name in ((vm_list_json.stdout | from_json).list | map(attribute='name') | list) }}" - name: Output message if VM already exists debug: msg: "VM with name '{{ vm_name }}' already exists. No action will be taken." when: vm_exists - name: Gather OS facts setup: filter: "ansible_distribution" when: not vm_exists - name: Detect OS set_fact: os_type: "{{ ansible_facts['distribution'] }}" - name: Check if Homebrew is installed (macOS) command: brew --version register: homebrew_check ignore_errors: yes when: os_type == "MacOSX" and not vm_exists - name: Install Homebrew (macOS) shell: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" when: os_type == "MacOSX" and homebrew_check.failed and not vm_exists - name: Check if multipass is installed shell: "command -v multipass || echo 'not found'" register: multipass_check changed_when: false - name: Install multipass using Homebrew (macOS) homebrew: name: multipass state: present when: - os_type == "MacOSX" - multipass_check.stdout == "not found" - not vm_exists - name: Install multipass using snap (Ubuntu) snap: name: multipass state: present when: - os_type == "Ubuntu" - multipass_check.stdout == "not found" - not vm_exists - name: Generate SSH key pair command: ssh-keygen -t rsa -b 2048 -f ./{{ ssh_key_name }} -N "" -C "ubuntu" args: creates: ./{{ ssh_key_name }} when: not vm_exists - name: Read the public SSH key command: cat ./{{ ssh_key_name }}.pub register: ssh_public_key when: not vm_exists - name: Create cloud-init.yaml file copy: dest: ./cloud-init.yaml content: | ## cloud-init users: - name: ubuntu ssh-authorized-keys: - {{ ssh_public_key.stdout }} sudo: ['ALL=(ALL) NOPASSWD:ALL'] groups: sudo shell: /bin/bash package_update: true packages: - python3-pip - python3 - python-is-python3 - python3-venv hostname: {{ vm_name }} fqdn: {{ vm_name }}.local final_message: "The instance is now up for $UPTIME seconds" when: not vm_exists - name: Verify cloud-init.yaml file exists stat: path: ./cloud-init.yaml register: cloud_init_file when: not vm_exists - name: Fail if cloud-init.yaml does not exist fail: msg: "cloud-init.yaml file does not exist" when: - not vm_exists - cloud_init_file is defined - not cloud_init_file.stat.exists | default(false) - name: Launch VM with multipass command: multipass launch {{ vm_image }} --name {{ vm_name }} --disk {{ vm_disk }} -m {{ vm_memory }} --cloud-init ./cloud-init.yaml register: vm_launch when: not vm_exists - name: Get VM info command: multipass info {{ vm_name }} --format json register: vm_info_json when: not vm_exists - name: Parse VM IP address set_fact: vm_ip: "{{ (vm_info_json.stdout | from_json).info[vm_name].ipv4[0] }}" when: not vm_exists - name: Generate inventory file copy: dest: ./inventory.yml content: | all: hosts: {{ vm_name }}: ansible_host: {{ vm_ip }} ansible_python_interpreter: /usr/bin/python3 vars: ansible_user: ubuntu ansible_ssh_private_key_file: ./{{ ssh_key_name }} when: not vm_exists - name: Add VM hostname to local /etc/hosts become: yes lineinfile: path: /etc/hosts line: "{{ vm_ip }} {{ vm_name }}.local" state: present when: not vm_exists - name: Wait for VM to be ready wait_for: host: "{{ vm_ip }}" port: 22 delay: 10 timeout: 300 when: not vm_exists