Ansible for Proxmox VE · Module 02
--check --diff before you mean itBefore three Proxmox VE nodes can become a cluster, they need:
| Requirement | Why |
|---|---|
| Working package sources | the enterprise repo fails without a subscription |
| Name resolution for each other | corosync and pvecm resolve node names |
| Synchronised time | corosync membership is time-sensitive |
| The same base state | “it works on pve01” is not a plan |
Four requirements, four groups of tasks. That is the playbook.
playbooks/10-node-prep.yml
---
- name: Prepare the Proxmox VE nodes
hosts: pve # which machines (a group from the inventory)
gather_facts: true # collect facts first (distribution, IPs, ...)
tasks:
- name: Install the admin tools we want everywhere
ansible.builtin.apt:
name: "{{ pve_packages }}"
state: present
update_cache: true
cache_valid_time: 3600name: on every task. It is what you read in the output and in the logs.group_varspve{ pve_packages }gather_facts: true collects a few hundred variables per host before the first task:
Use them instead of hard-coding:
Your lab runs Proxmox VE 9 on Debian 13. We use the fact instead of hardcoding trixie.
- name: Disable the enterprise repositories
ansible.builtin.file:
path: "{{ item }}"
state: absent
loop:
- /etc/apt/sources.list.d/pve-enterprise.sources
- /etc/apt/sources.list.d/ceph.sources
notify: Update apt cache
- name: Enable the no-subscription repository
ansible.builtin.deb822_repository:
name: pve-no-subscription
types: [deb]
uris: http://download.proxmox.com/debian/pve
suites: "{{ ansible_distribution_release }}"
components: [pve-no-subscription]
signed_by: /usr/share/keyrings/proxmox-archive-keyring.gpg
notify: Update apt cacheWe need the cache before installing packages, so we force it:
Name resolution comes from the provisioning, together with DNS and /etc/hosts. The playbook confirms it instead of rewriting it:
groups['pve'] is the inventory, read at runtimedifference([inventory_hostname]) drops the node itself from the loop/etc/hosts?Because something else already owns it.
Automating a file that another system owns is how you break a platform quietly. Check the state you depend on; configure only what is yours.
- name: Install the admin tools
ansible.builtin.apt:
name: "{{ pve_packages }}"
state: present
- name: Check whether the clock is synchronised
ansible.builtin.command: timedatectl show -p NTPSynchronized --value
register: ntp_state
changed_when: false # this only reads, so never report a change
check_mode: false # ... so it is safe to run it in --check too
- name: Fail early if time is not in sync
ansible.builtin.assert:
that: ntp_state.stdout == 'yes'
fail_msg: "Time is not synchronised - corosync will be unhappy"changed=0 on the second run is your proof that the playbook is idempotent. One task fewer, too: the handler only runs when something notifies it.
--check is not perfect: a task that depends on an earlier change cannot know the future. It still catches most mistakes before they reach three nodes at once.
Write playbooks/10-node-prep.yml, run it in check mode, then for real, then again.
→ Exercise sheet: Preparing the nodes
handlers:
- name: Update apt cache
ansible.builtin.apt: {update_cache: true}
- name: Enable the no-subscription repository
ansible.builtin.deb822_repository:
name: pve-no-subscription
uris: http://download.proxmox.com/debian/pve
suites: "{{ ansible_distribution_release }}"
components: [pve-no-subscription]
signed_by: /usr/share/keyrings/proxmox-archive-keyring.gpg
notify: Update apt cachenotify must match the handler name exactly. A typo does not error — the handler just never runs.
--check skips command tasks, which would leave ntp_state undefined and fail the assert. That is what check_mode: false on the timedatectl task is for.
One task fewer. Which one?
The handler. It runs only when something notifies it.
changed_when: false — without it command reports changed every run, and the proof from task 3 is gonecheck_mode: false — without it --check skips the task, ntp_state is never registered, and the dry run fails on an undefined variablecredativ · Ansible for Proxmox VE