modules
**Scope**: Module selection patterns, builtin vs command/shell decisions, collection modules, and version-specific module changes **Version range**: ansible-core 2.14+ / Ansible Collections (community.general 7.0+) **Generated**: 2026-04-04 — verify against current Ansible
$ npx -y skills add notque/vexjoy-agent --agent claude-codeHow it fires
How this agent gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
Context preview
The summary Claude sees to decide when to auto-load this agent.
**Scope**: Module selection patterns, builtin vs command/shell decisions, collection modules, and version-specific module changes **Version range**: ansible-core 2.14+ / Ansible Collections (community.general 7.0+) **Generated**: 2026-04-04 — verify against current Ansible
Agent definition
modules.mdAnsible Modules Reference
> **Scope**: Module selection patterns, builtin vs command/shell decisions, collection modules, and version-specific module changes > **Version range**: ansible-core 2.14+ / Ansible Collections (community.general 7.0+) > **Generated**: 2026-04-04 — verify against current Ansible module documentation
---
Overview
The most common Ansible failure mode is using `command` or `shell` when an idempotent module exists. Module selection determines whether playbooks are idempotent, properly report changes, and work across environments. The hierarchy is: builtin module > collection module > command/shell with explicit `changed_when`.
---
Pattern Table: Module Selection by Task Type
| Task | Wrong Approach | Correct Module | Notes | |------|---------------|----------------|-------| | Install package (Debian) | `command: apt-get install nginx` | `ansible.builtin.apt` | Handles idempotency, state management | | Install package (RHEL) | `command: yum install nginx` | `ansible.builtin.dnf` | `dnf` preferred over `yum` on RHEL 8+ | | Install package (any OS) | `shell: {{pkg_mgr}} install` | `ansible.builtin.package` | Cross-platform, uses detected pkg manager | | Manage service | `command: systemctl start nginx` | `ansible.builtin.systemd` | Reports enabled/started/stopped state | | Copy file | `command: cp src dst` | `ansible.builtin.copy` | Detects changes by checksum | | Template | `command: sed 's/VAR/val/g' > /etc/conf` | `ansible.builtin.template` | Jinja2, detects changes, `--diff` support | | Create dir | `command: mkdir -p /path` | `ansible.builtin.file` | state=directory, handles permissions | | Download file | `command: wget url -O dest` | `ansible.builtin.get_url` | Checksum validation, change detection | | Run script once | `shell: /path/to/setup.sh` | `command` + `creates:` | `creates:` makes it idempotent | | Git checkout | `command: git clone` | `ansible.builtin.git` | Version-aware, detects changes | | Manage user | `command: useradd -m user` | `ansible.builtin.user` | Full lifecycle, idempotent | | Manage cron | `command: crontab -e` | `ansible.builtin.cron` | Named entries, idempotent |
---
Correct Patterns
Package Installation Across OS Families
# Use package module for cross-platform playbooks
- name: Install common packages
ansible.builtin.package:
name: "{{ item }}"
state: present
loop:
- nginx
- python3
- curl
# Use platform-specific modules when you need platform features
- name: Install nginx with apt options (Debian/Ubuntu)
ansible.builtin.apt:
name: nginx
state: present
install_recommends: false # apt-specific option
when: ansible_os_family == "Debian"
- name: Install nginx with dnf options (RHEL 8+)
ansible.builtin.dnf:
name: nginx
state: present
enablerepo: epel # dnf-specific option
when: ansible_distribution_major_version | int >= 8 and ansible_os_family == "RedHat"**Why**: `package` abstracts over OS differences. Use `apt`/`dnf` only when platform-specific options (repos, recommends, module streams) are needed.
---
Service Management with systemd
# Start and enable in one task
- name: Enable and start nginx
ansible.builtin.systemd:
name: nginx
state: started
enabled: true
daemon_reload: true # Required when unit files change
# Restart via handler (triggered by config change)
- name: Deploy nginx config
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
mode: '0644'
notify: Restart nginx
# handlers/main.yml
- name: Restart nginx
ansible.builtin.systemd:
name: nginx
state: restarted**Why**: Handlers ensure services restart only when the triggering resource actually changed. Using `state: restarted` directly in tasks causes restarts on every run.
---
Making `command` Idempotent with `creates`
# When no module exists, use command with creates/removes
- name: Run database migration
ansible.builtin.command:
cmd: /app/bin/migrate up
chdir: /app
creates: /app/.migrations-complete # Skip if this file exists
# After migration, create the sentinel file
- name: Mark migrations complete
ansible.builtin.file:
path: /app/.migrations-complete
state: touch
mode: '0644'
# For scripts that can be re-run safely
- name: Check if cluster is initialized
ansible.builtin.command: kubectl get nodes
register: cluster_check
changed_when: false
failed_when: false
- name: Initialize cluster
ansible.builtin.command: kubeadm init --config /etc/kubeadm/config.yml
when: cluster_check.rc != 0---
Pattern Catalog
Use Package Modules for Installation
**Detection**:
# Find shell/command used for package installation
grep -rn "command:\|shell:" playbooks/ roles/ \
| grep -E "apt-get|yum|dnf|pip install|npm install"
rg -t yaml '(command|shell):.*\b(apt-get|yum|dnf|pip)\b' roles/ playbooks/
**Signal**:
- name: Install nginx
shell: apt-get install -y nginx
- name: Update packages
command: yum update -y
**Why this matters**: `shell`/`command` always report `changed` regardless of whether nginx was already installed. Running twice installs twice (or errors). No change tracking. Breaks `--check` mode (would show false changes).
**Preferred action**:
- name: Install nginx
ansible.builtin.apt:
name: nginx
state: present
update_cache: true
cache_valid_time: 3600 # Only update cache if older than 1 hour---
Use the systemd Module for Service Management
**Detection**:
grep -rn "command:\|shell:" roles/ playbooks/ \
| grep "systemctl"
rg -t yaml '(command|shell):.*systemctl' roles/ playbooks/
**Signal**:
- name: Start nginx
command: systemctl start nginx
- name: Enable nginx on boot
shell: systemctl enable nginx
**Why this matters**: Doesn't report the service state as a change properly. Can't use `--check` mode (would actually run `s
Read more
Ansible Modules Reference
> **Scope**: Module selection patterns, builtin vs command/shell decisions, collection modules, and version-specific module changes > **Version range**: ansible-core 2.14+ / Ansible Collections (community.general 7.0+) > **Generated**: 2026-04-04 — verify against current Ansible module documentation
---
Overview
The most common Ansible failure mode is using `command` or `shell` when an idempotent module exists. Module selection determines whether playbooks are idempotent, properly report changes, and work across environments. The hierarchy is: builtin module > collection module > command/shell with explicit `changed_when`.
---
Pattern Table: Module Selection by Task Type
| Task | Wrong Approach | Correct Module | Notes | |------|---------------|----------------|-------| | Install package (Debian) | `command: apt-get install nginx` | `ansible.builtin.apt` | Handles idempotency, state management | | Install package (RHEL) | `command: yum install nginx` | `ansible.builtin.dnf` | `dnf` preferred over `yum` on RHEL 8+ | | Install package (any OS) | `shell: {{pkg_mgr}} install` | `ansible.builtin.package` | Cross-platform, uses detected pkg manager | | Manage service | `command: systemctl start nginx` | `ansible.builtin.systemd` | Reports enabled/started/stopped state | | Copy file | `command: cp src dst` | `ansible.builtin.copy` | Detects changes by checksum | | Template | `command: sed 's/VAR/val/g' > /etc/conf` | `ansible.builtin.template` | Jinja2, detects changes, `--diff` support | | Create dir | `command: mkdir -p /path` | `ansible.builtin.file` | state=directory, handles permissions | | Download file | `command: wget url -O dest` | `ansible.builtin.get_url` | Checksum validation, change detection | | Run script once | `shell: /path/to/setup.sh` | `command` + `creates:` | `creates:` makes it idempotent | | Git checkout | `command: git clone` | `ansible.builtin.git` | Version-aware, detects changes | | Manage user | `command: useradd -m user` | `ansible.builtin.user` | Full lifecycle, idempotent | | Manage cron | `command: crontab -e` | `ansible.builtin.cron` | Named entries, idempotent |
---
Correct Patterns
Package Installation Across OS Families
# Use package module for cross-platform playbooks
- name: Install common packages
ansible.builtin.package:
name: "{{ item }}"
state: present
loop:
- nginx
- python3
- curl
# Use platform-specific modules when you need platform features
- name: Install nginx with apt options (Debian/Ubuntu)
ansible.builtin.apt:
name: nginx
state: present
install_recommends: false # apt-specific option
when: ansible_os_family == "Debian"
- name: Install nginx with dnf options (RHEL 8+)
ansible.builtin.dnf:
name: nginx
state: present
enablerepo: epel # dnf-specific option
when: ansible_distribution_major_version | int >= 8 and ansible_os_family == "RedHat"**Why**: `package` abstracts over OS differences. Use `apt`/`dnf` only when platform-specific options (repos, recommends, module streams) are needed.
---
Service Management with systemd
# Start and enable in one task
- name: Enable and start nginx
ansible.builtin.systemd:
name: nginx
state: started
enabled: true
daemon_reload: true # Required when unit files change
# Restart via handler (triggered by config change)
- name: Deploy nginx config
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
mode: '0644'
notify: Restart nginx
# handlers/main.yml
- name: Restart nginx
ansible.builtin.systemd:
name: nginx
state: restarted**Why**: Handlers ensure services restart only when the triggering resource actually changed. Using `state: restarted` directly in tasks causes restarts on every run.
---
Making `command` Idempotent with `creates`
# When no module exists, use command with creates/removes
- name: Run database migration
ansible.builtin.command:
cmd: /app/bin/migrate up
chdir: /app
creates: /app/.migrations-complete # Skip if this file exists
# After migration, create the sentinel file
- name: Mark migrations complete
ansible.builtin.file:
path: /app/.migrations-complete
state: touch
mode: '0644'
# For scripts that can be re-run safely
- name: Check if cluster is initialized
ansible.builtin.command: kubectl get nodes
register: cluster_check
changed_when: false
failed_when: false
- name: Initialize cluster
ansible.builtin.command: kubeadm init --config /etc/kubeadm/config.yml
when: cluster_check.rc != 0---
Pattern Catalog
Use Package Modules for Installation
**Detection**:
# Find shell/command used for package installation grep -rn "command:\|shell:" playbooks/ roles/ \ | grep -E "apt-get|yum|dnf|pip install|npm install" rg -t yaml '(command|shell):.*\b(apt-get|yum|dnf|pip)\b' roles/ playbooks/
**Signal**:
- name: Install nginx shell: apt-get install -y nginx - name: Update packages command: yum update -y
**Why this matters**: `shell`/`command` always report `changed` regardless of whether nginx was already installed. Running twice installs twice (or errors). No change tracking. Breaks `--check` mode (would show false changes).
**Preferred action**:
- name: Install nginx
ansible.builtin.apt:
name: nginx
state: present
update_cache: true
cache_valid_time: 3600 # Only update cache if older than 1 hour---
Use the systemd Module for Service Management
**Detection**:
grep -rn "command:\|shell:" roles/ playbooks/ \ | grep "systemctl" rg -t yaml '(command|shell):.*systemctl' roles/ playbooks/
**Signal**:
- name: Start nginx command: systemctl start nginx - name: Enable nginx on boot shell: systemctl enable nginx
**Why this matters**: Doesn't report the service state as a change properly. Can't use `--check` mode (would actually run `s
Essays and writing behind this toolkit live at vexjoy.com. AI agents skip steps. "Looks correct" replaces running tests. "Trivial change" replaces verification.
Repo: notque/vexjoy-agent
Other agents on vexjoy-agent.
- ansible-automation-engineer
Ansible automation: playbooks, roles, collections, Molecule testing, Vault security.
Open agent - testing
**Scope**: Molecule test scenarios, ansible-lint rules, idempotency validation, and check-mode patterns **Version range**: Molecule 6.0+ / ansible-lint 6.0+ / ansible-core 2.14+ **Generated**: 2026-04-04 — verify against current Molecule and ansible-lint documentation
Open agent - base-instructions
Universal operational rules injected by /do at agent dispatch. Domain-specific rules live in each agent's .md file.
Open agent - communication-patterns
**Scope**: Failure modes in agent output style — over-reporting, self-congratulation, verbose narration, and hedging. Covers what to detect and how to fix each. **Version range**: all versions **Generated**: 2026-05-11
Open agent - combat-effects-upgrade
Zero-dependency combat visual upgrades: CSS particle replacement, Framer Motion combat juice, CSS 3D card transforms.
Open agent - css-3d-card-transforms
<!-- Loaded by combat-effects-upgrade when task involves card tilt, backface-visibility, CSS perspective, or Framer Motion + CSS 3D integration -->
Open agent

