Claude
Skills
Sign in
Back

ansible-proxmox

Included with Lifetime
$97 forever

Expert Proxmox VE automation using the community.proxmox Ansible collection for VM provisioning, template management, and cluster operations with minimal CLI usage.

General

What this skill does


# Ansible Proxmox Integration

Expert Proxmox automation using community.proxmox collection with minimal CLI usage.

## Module Selection

### Prefer Native Modules

Use `community.proxmox` modules when available:

| Operation | Use Module | NOT CLI |
|-----------|------------|---------|
| Create VM | `community.proxmox.proxmox_kvm` | `qm create` |
| Clone VM | `community.proxmox.proxmox_kvm` | `qm clone` |
| Manage users | `community.proxmox.proxmox_user` | `pveum user` |
| Manage groups | `community.proxmox.proxmox_group` | `pveum group` |
| Manage pools | `community.proxmox.proxmox_pool` | `pveum pool` |
| Manage ACLs | `community.proxmox.proxmox_acl` | `pveum acl` |
| Storage | `community.proxmox.proxmox_storage` | `pvesm` |

### When CLI is Required

Some operations lack native modules:

| Operation | Requires CLI | Reason |
|-----------|--------------|--------|
| Cluster create | `pvecm create` | No module exists |
| Cluster join | `pvecm add` | No module exists |
| CEPH init | `pveceph init` | Complex workflow |
| CEPH OSD | `pveceph osd create` | Complex workflow |

## Native Module Examples

### Create VM from Template

```yaml
- name: Create VM from template
  community.proxmox.proxmox_kvm:
    api_host: "{{ proxmox_api_host }}"
    api_user: "{{ proxmox_api_user }}"
    api_token_id: "{{ proxmox_token_id }}"
    api_token_secret: "{{ proxmox_token_secret }}"
    node: "{{ proxmox_node }}"
    vmid: "{{ vm_id }}"
    name: "{{ vm_name }}"
    clone: "{{ template_name }}"
    full: true
    storage: local-lvm
    memory: "{{ vm_memory | default(4096) }}"
    cores: "{{ vm_cores | default(2) }}"
    state: present
  delegate_to: localhost
```

### Manage Proxmox User

```yaml
- name: Create Terraform user
  community.proxmox.proxmox_user:
    api_host: "{{ proxmox_api_host }}"
    api_user: "root@pam"
    api_password: "{{ proxmox_root_password }}"
    userid: "terraform@pve"
    comment: "Terraform automation user"
    groups:
      - automation
    state: present
  no_log: true
  delegate_to: localhost
```

### Configure ACLs

```yaml
- name: Grant terraform permissions
  community.proxmox.proxmox_acl:
    api_host: "{{ proxmox_api_host }}"
    api_user: "{{ proxmox_api_user }}"
    api_token_id: "{{ proxmox_token_id }}"
    api_token_secret: "{{ proxmox_token_secret }}"
    path: "/"
    users:
      - terraform@pve
    roles:
      - Administrator
    state: present
  delegate_to: localhost
```

## CLI with Idempotency

When CLI is required, add proper idempotency controls:

### Cluster Formation

```yaml
- name: Check existing cluster status
  ansible.builtin.command: pvecm status
  register: cluster_status
  failed_when: false
  changed_when: false

- name: Set cluster facts
  ansible.builtin.set_fact:
    is_cluster_member: "{{ cluster_status.rc == 0 }}"
    in_target_cluster: "{{ cluster_name in cluster_status.stdout }}"

- name: Create cluster on primary node
  ansible.builtin.command: pvecm create {{ cluster_name }}
  when:
    - inventory_hostname == groups['proxmox'][0]
    - not in_target_cluster
  register: cluster_create
  changed_when: cluster_create.rc == 0

- name: Join cluster on secondary nodes
  ansible.builtin.command: pvecm add {{ hostvars[groups['proxmox'][0]].ansible_host }}
  when:
    - inventory_hostname != groups['proxmox'][0]
    - not is_cluster_member
  register: cluster_join
  changed_when: cluster_join.rc == 0
```

### API Token Creation

```yaml
- name: Create API token
  ansible.builtin.command: >
    pveum user token add {{ username }}@pam {{ token_name }}
    --privsep 0
  register: token_result
  changed_when: "'already exists' not in token_result.stderr"
  failed_when:
    - token_result.rc != 0
    - "'already exists' not in token_result.stderr"
  no_log: true
```

## Authentication Patterns

### API Token (Recommended)

```yaml
# Store token in variables or Infisical
proxmox_api_host: "192.168.1.10"
proxmox_api_user: "terraform@pve"
proxmox_token_id: "automation"
proxmox_token_secret: "{{ lookup('infisical', 'PROXMOX_TOKEN_SECRET') }}"

- name: Create VM
  community.proxmox.proxmox_kvm:
    api_host: "{{ proxmox_api_host }}"
    api_user: "{{ proxmox_api_user }}"
    api_token_id: "{{ proxmox_token_id }}"
    api_token_secret: "{{ proxmox_token_secret }}"
    # ... rest of config
```

### Root Password (Local Operations)

For operations on Proxmox nodes themselves:

```yaml
- name: Retrieve root password
  ansible.builtin.include_tasks: tasks/infisical-secret-lookup.yml
  vars:
    secret_name: 'PROXMOX_ROOT_PASSWORD'
    secret_var_name: 'proxmox_root_password'

- name: Configure via API
  community.proxmox.proxmox_user:
    api_host: "{{ inventory_hostname }}"
    api_user: "root@pam"
    api_password: "{{ proxmox_root_password }}"
    # ... config
  no_log: true
```

## Cluster Operations

### Idempotent Cluster Status Check

```yaml
- name: Get cluster status
  ansible.builtin.command: pvecm status
  register: cluster_status
  changed_when: false
  failed_when: false

- name: Get cluster nodes
  ansible.builtin.command: pvecm nodes
  register: cluster_nodes
  changed_when: false
  failed_when: false
  when: cluster_status.rc == 0

- name: Set cluster facts
  ansible.builtin.set_fact:
    cluster_exists: "{{ cluster_status.rc == 0 }}"
    cluster_node_count: "{{ cluster_nodes.stdout_lines | length | default(0) }}"
    is_quorate: "{{ 'Quorate: Yes' in cluster_status.stdout | default('') }}"
```

### Verify Cluster Health

```yaml
- name: Verify cluster quorum
  ansible.builtin.command: pvecm status
  register: cluster_health
  changed_when: false
  failed_when: "'Quorate: Yes' not in cluster_health.stdout"

- name: Verify expected node count
  ansible.builtin.command: pvecm nodes
  register: nodes_check
  changed_when: false
  failed_when: nodes_check.stdout_lines | length != groups['proxmox'] | length
```

## CEPH Integration

### Initialize CEPH

```yaml
- name: Check if CEPH is initialized
  ansible.builtin.command: pveceph status
  register: ceph_status
  changed_when: false
  failed_when: false

- name: Initialize CEPH
  ansible.builtin.command: >
    pveceph init --network {{ ceph_network }}
  when:
    - inventory_hostname == groups['proxmox'][0]
    - ceph_status.rc != 0
  register: ceph_init
  changed_when: ceph_init.rc == 0
```

### Create OSD

```yaml
- name: Check if OSD exists on device
  ansible.builtin.command: >
    pveceph osd list
  register: osd_list
  changed_when: false

- name: Create OSD
  ansible.builtin.command: >
    pveceph osd create {{ item }}
  loop: "{{ ceph_osd_devices }}"
  when: item not in osd_list.stdout
  register: osd_create
  changed_when: osd_create.rc == 0
```

## Network Configuration

Use `community.general.interfaces_file` for network config:

```yaml
- name: Configure VLAN-aware bridge
  community.general.interfaces_file:
    iface: vmbr1
    option: bridge-vlan-aware
    value: "yes"
    backup: true
    state: present
  notify: reload network

- name: Set bridge ports
  community.general.interfaces_file:
    iface: vmbr1
    option: bridge-ports
    value: "bond0"
    backup: true
    state: present
  notify: reload network
```

## Anti-Patterns

### Using CLI When Module Exists

```yaml
# BAD - Module exists for this
- name: Create user
  ansible.builtin.command: pveum user add terraform@pve

# GOOD
- name: Create user
  community.proxmox.proxmox_user:
    api_host: "{{ proxmox_api_host }}"
    api_user: "root@pam"
    api_password: "{{ password }}"
    userid: "terraform@pve"
    state: present
```

### Missing Idempotency on CLI

```yaml
# BAD - Will fail on second run
- name: Create cluster
  ansible.builtin.command: pvecm create MyCluster

# GOOD
- name: Check cluster status
  ansible.builtin.command: pvecm status
  register: cluster_check
  changed_when: false
  failed_when: false

- name: Create cluster
  ansible.builtin.command: pvecm create MyCluster
  when: cluster_check.rc != 0
```

### Running on Wrong Host

```yaml
# BAD - API calls from

Related in General