Compare commits

..

4 Commits

Author SHA1 Message Date
patrick 4f09e886c2 feat(proxmox_lxc_provision): support custom bridge, interface name, and VLAN tag
The bridge (vmbr0) and interface name (eth0) were previously hardcoded
in the netif string, and there was no way to set an 802.1Q VLAN tag.
Expose lxc_bridge, lxc_iface_name, and lxc_vlan_tag (optional) so
containers can be attached to non-default bridges or tagged into a VLAN
without forking the role.

Also drop the misleading 'lxc_ipv6 | default(omit)' filter — default(omit)
does not produce omission inside a string context, and lxc_ipv6 always
has a default of 'auto' in defaults/main.yml.
2026-06-27 21:23:14 -04:00
patrick a0f9b06da9 refactor(system_setup): rename bare 'password' var to user_password
The bare 'password' variable in user.yml could silently collide with any
same-named variable elsewhere in scope. Rename to user_password to
namespace it alongside username.

BREAKING CHANGE: callers passing 'password' to this role must rename it
to user_password.
2026-06-27 21:11:25 -04:00
patrick 51c1c5b611 refactor(proxmox_lxc_provision): rename password vars and parameterize user name
Rename lxc_password to lxc_root_password for consistency with the new
lxc_user_password (replaces the previously bare 'password' variable in
post-clone.yml, which silently collided with any same-named caller var).
Add lxc_user_name (default: admin) so the non-root account managed in
post-clone.yml is no longer hardcoded. Apply default(omit) to the root
password in create.yml so it is genuinely optional as documented.

BREAKING CHANGE: callers passing lxc_password or a bare 'password' var
must rename to lxc_root_password and lxc_user_password respectively.
2026-06-27 21:11:13 -04:00
patrick 27924cf55c docs(proxmox_lxc_provision): document missing task files and optional variables
Add post-clone.yml and edit-config.yml to the task list, and document
the previously undocumented optional variables (lxc_unprivileged,
lxc_mounts, lxc_onboot, lxc_startup, lxc_timezone, lxc_nvidia_gpu_mount,
gpu_device_id, uvm_device_id, lxc_id_mappings).
2026-06-27 21:09:27 -04:00
7 changed files with 37 additions and 17 deletions
+17 -1
View File
@@ -14,6 +14,8 @@ It also includes tasks which may be used individually:
- `update.yml`: Updates an existing LXC container
- `wait.yml`: Waits for SSH to be available on the container
- `check-exists.yml`: Checks if an LXC exists by `lxc_vmid` or `lxc_hostname`
- `post-clone.yml`: Post-clone configuration (passwords, SSH host key regeneration). Runs against the new container; depends on the `system_setup` role.
- `edit-config.yml`: Edits `/etc/pve/lxc/<vmid>.conf` directly to add ID mappings and NVIDIA GPU passthrough entries. Must be delegated to the Proxmox host.
## Requirements
@@ -60,7 +62,9 @@ It also includes tasks which may be used individually:
| `lxc_storage` | Target storage for the container | `local-zfs` |
| `lxc_size` | Disk size in GB | `16` |
| `lxc_disk` | The target storage and storage size | `local-zfs:16` |
| `lxc_password` | The password for the root account | - |
| `lxc_root_password` | Password for the root account. On creates from `lxc_template` it is set via the Proxmox API; on clones it is applied inside the container by `post-clone.yml`. | - |
| `lxc_user_name` | Name of an additional non-root user to manage in `post-clone.yml` (clone path only). | `admin` |
| `lxc_user_password` | Password for `lxc_user_name`. Only applied on the clone path via `post-clone.yml`. The user must already exist in the source template. | - |
| `lxc_cores` | The number of CPU cores | `4` |
| `lxc_memory` | Memory size in MB | `2048` |
| `lxc_swap` | Swap memory size in MB | `2048` |
@@ -68,10 +72,22 @@ It also includes tasks which may be used individually:
| `lxc_ipv6` | The IPv6 address | `auto` |
| `lxc_gateway` | The default gateway | `10.0.0.1` |
| `lxc_nameserver` | DNS nameserver | `10.0.0.7` |
| `lxc_bridge` | Linux bridge on the Proxmox host to attach the container to | `vmbr0` |
| `lxc_iface_name` | Interface name inside the container | `eth0` |
| `lxc_vlan_tag` | 802.1Q VLAN tag (integer 1-4094). Omitted from netif when unset. | - |
| `lxc_pubkey_file` | Path to SSH public key file | `~/.ssh/id_ed25519.pub` |
| `lxc_features` | List of container features | `["nesting=1"]` |
| `lxc_tags` | Tags for the container | `["ansible-managed"]` |
| `lxc_start` | Start container after creation | `true` |
| `lxc_unprivileged` | Create as an unprivileged container | `true` |
| `lxc_mounts` | Dict of additional bind mounts (e.g. `{ mp0: "/srv/data,mp=/data" }`) | - |
| `lxc_onboot` | Start container on Proxmox host boot | `false` |
| `lxc_startup` | Startup order string passed to Proxmox (e.g. `order=1,up=30`) | - |
| `lxc_timezone` | Timezone inside the container (e.g. `Europe/Berlin`) | - |
| `lxc_nvidia_gpu_mount` | Add NVIDIA GPU passthrough entries via `edit-config.yml` | `false` |
| `gpu_device_id` | Major device number for `/dev/nvidia*` (required when `lxc_nvidia_gpu_mount` is true) | - |
| `uvm_device_id` | Major device number for `/dev/nvidia-uvm*` (required when `lxc_nvidia_gpu_mount` is true) | - |
| `lxc_id_mappings` | Multi-line `lxc.idmap:` block written into the container config by `edit-config.yml` | - |
## Example Playbook
@@ -17,6 +17,9 @@ lxc_ipv4: dhcp
lxc_ipv6: auto
lxc_nameserver: 10.0.0.7
lxc_gateway: 10.0.0.1
lxc_bridge: vmbr0
lxc_iface_name: eth0
# lxc_vlan_tag: unset by default; set to an integer (1-4094) to add an 802.1Q tag
lxc_pubkey_file: "~/.ssh/id_ed25519.pub"
lxc_unprivileged: true
lxc_features:
@@ -25,3 +28,4 @@ lxc_nvidia_gpu_mount: false
lxc_tags: ["ansible-managed"]
lxc_clone_type: full
lxc_start: true
lxc_user_name: admin
+3 -3
View File
@@ -3,15 +3,15 @@
community.proxmox.proxmox:
vmid: "{{ lxc_vmid | default(omit) }}"
hostname: "{{ lxc_hostname }}"
password: "{{ lxc_password }}"
password: "{{ lxc_root_password | default(omit) }}"
ostemplate: "{{ lxc_template }}"
cores: "{{ lxc_cores }}"
memory: "{{ lxc_memory }}"
swap: "{{ lxc_swap }}"
disk: "{{ lxc_disk }}"
mounts: "{{ lxc_mounts | default(omit) }}"
netif: >
{"net0": "name=eth0,gw={{ lxc_gateway }},ip={{ lxc_ipv4 }},ip6={{ lxc_ipv6 | default(omit) }},bridge=vmbr0"}
netif: >-
{"net0": "name={{ lxc_iface_name }},bridge={{ lxc_bridge }},ip={{ lxc_ipv4 }},gw={{ lxc_gateway }},ip6={{ lxc_ipv6 }}{% if lxc_vlan_tag is defined %},tag={{ lxc_vlan_tag }}{% endif %}"}
pubkey: "{{ lookup('file', lxc_pubkey_file) | default(omit) }}"
onboot: "{{ lxc_onboot | default(false) }}"
startup: "{{ lxc_startup | default(omit) }}"
@@ -2,16 +2,16 @@
- name: Change root password
ansible.builtin.user:
name: root
password: "{{ lxc_password | password_hash('sha512') }}"
password: "{{ lxc_root_password | password_hash('sha512') }}"
update_password: always
when: lxc_password is defined
when: lxc_root_password is defined
- name: Change admin password
- name: Change user password
ansible.builtin.user:
name: admin
password: "{{ password | password_hash('sha512') }}"
name: "{{ lxc_user_name }}"
password: "{{ lxc_user_password | password_hash('sha512') }}"
update_password: always
when: password is defined
when: lxc_user_password is defined
- name: Regenerate SSH host keys
ansible.builtin.include_role:
+2 -2
View File
@@ -3,12 +3,12 @@
community.proxmox.proxmox:
vmid: "{{ lxc_vmid }}"
hostname: "{{ lxc_hostname }}"
password: "{{ lxc_password | default(omit) }}"
password: "{{ lxc_root_password | default(omit) }}"
cores: "{{ lxc_cores }}"
memory: "{{ lxc_memory }}"
swap: "{{ lxc_swap }}"
disk: "{{ lxc_disk }}"
netif: '{"net0": "name=eth0,gw={{ lxc_gateway }},ip={{ lxc_ipv4 }},ip6={{ lxc_ipv6 | default(omit) }},bridge=vmbr0"}'
netif: '{"net0": "name={{ lxc_iface_name }},bridge={{ lxc_bridge }},ip={{ lxc_ipv4 }},gw={{ lxc_gateway }},ip6={{ lxc_ipv6 }}{% if lxc_vlan_tag is defined %},tag={{ lxc_vlan_tag }}{% endif %}"}'
pubkey: "{{ lookup('file', lxc_pubkey_file) | default(omit) }}"
onboot: "{{ lxc_onboot | default(false) }}"
startup: "{{ lxc_startup | default(omit) }}"
+4 -4
View File
@@ -16,7 +16,7 @@ Performs initial system configuration including user creation, SSH hardening, an
| Variable | Default | Description |
|----------|---------|-------------|
| `username` | `admin` | Username for the admin account |
| `password` | *required* | Password for the admin user |
| `user_password` | *required* | Password for the admin user |
| `shell` | `/bin/bash` | Default shell for the user |
| `passwordless_sudo` | `true` | Allow sudo without password |
| `ssh_pubkey_file` | *required* | Path to SSH public key file |
@@ -28,7 +28,7 @@ Performs initial system configuration including user creation, SSH hardening, an
You must provide these variables when using this role:
```yaml
password: "your_secure_password"
user_password: "your_secure_password"
ssh_pubkey_file: "/path/to/your/public/key.pub"
```
@@ -40,7 +40,7 @@ ssh_pubkey_file: "/path/to/your/public/key.pub"
include_role:
name: system_setup
vars:
password: "{{ admin_password }}"
user_password: "{{ admin_password }}"
ssh_pubkey_file: "~/.ssh/id_rsa.pub"
```
@@ -51,7 +51,7 @@ ssh_pubkey_file: "/path/to/your/public/key.pub"
name: system_setup
vars:
username: myuser
password: "{{ user_password }}"
user_password: "{{ vault_myuser_password }}"
shell: /bin/zsh
ssh_pubkey_file: "keys/mykey.pub"
passwordless_sudo: false
+1 -1
View File
@@ -2,7 +2,7 @@
- name: "Create a new user {{ username }}"
user:
name: "{{ username }}"
password: "{{ password | password_hash('sha512') }}"
password: "{{ user_password | password_hash('sha512') }}"
groups:
- sudo
shell: "{{ shell }}"