ansible-patterns
Skill0 starsChanged 5 months agoMITView on GitHub
Ansible repo layout, FQCN, idempotency, mixed Windows+Linux patterns, no-command/shell rule, role variable naming, inventory-as-directory, group_vars directories. Triggers on: ansible, playbook, role, inventory, winrm, psrp, idempotent, FQCN.
- Installs packages
In this repository24 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- ansible-testingSkill
- change-documentationSkill
- ci-pipeline-debuggingSkill
- drift-detectionSkill
- gitlab-cicd-pipelineSkill
- hsa-infrastructureSkill
- iac-sast-scanningSkill
- iac-tooling-selectionSkill
- incident-responseSkill
- instinct-promotionSkill
- instinct-rollbackSkill
- knowledge-curationSkill
- multi-env-promotionSkill
- octopus-releaseSkill
- pci-cp-complianceSkill
- pci-dss-complianceSkill
- pci-pin-awarenessSkill
- perso-change-controlSkill
- perso-complianceSkill
- pre-commit-and-secret-scanningSkill
- rollback-and-runbooksSkill
- secrets-vaultSkill
- supply-chain-and-sbomSkill
The file
We split this skill into the rules it gives an agent
8 rules. About 0 are common, 2 are distinctive, and 6 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 73
When genuinely unavoidable, use ansible.builtin.command (not shell) with the sanctioned idempotency guard — creates:/removes: for state, or …
- Very specific: line 96
Use ansible.windows, community.windows, microsoft.ad collections for Windows.
- Very specific: line 157
Every module call must use a Fully Qualified Collection Name.
- Distinctive: line 177
Before marking a task idempotent, verify:
- Very specific: line 180
If using command/shell: creates: or removes: or changed_when: false is present
- Very specific: line 211
Use ansible.windows.* and community.windows.* FQCNs.
- Distinctive: line 237
When writing OS hardening roles, reference CIS Benchmark Level 1/2.
- Very specific: line 258
Every new role ships with a Molecule scenario under molecule/default/:
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Ansible Patterns Skill
When to Use
Load this skill when authoring or reviewing any Ansible playbook, role, collection, or
inventory file. Applies to mixed Windows/Linux estates, new roles, updates to existing
playbooks, and any MR touching inventories/, roles/, playbooks/, or *.yml files
under the Ansible repos.
How It Works
Repo Layout (Red Hat CoP canonical)
repo/
ansible.cfg # committed; pins inventory, roles_path, callbacks_enabled
requirements.yml # pinned exact versions for all collections and roles
requirements.lock.yml # machine-generated transitive-version record
inventories/
dev/
hosts.yml
group_vars/
all/ # directory, not a single file — auto-loads every YAML inside
main.yml
vault.yml # ansible-vault encrypted secrets
linux/main.yml
windows/main.yml
host_vars/
test/ staging/ prod/ # one directory per environment — isolates blast radius
playbooks/
site.yml
linux/*.yml
windows/*.yml
roles/ # local roles, name-prefixed vars
collections/requirements.yml # or galaxy_requirements.ymlSource: Red Hat CoP "Good Practices for Ansible"; ansible-iac-gitops.md §1; modular-ansible-repos.md §6.
Key Decisions
- Inventory as a directory per environment — prevents wrong-env blast radius. One dir
per env (
dev/,staging/,prod/). Invoke with-i inventories/prod. Setinventory = ./inventories/devinansible.cfgas a safe default so workstation runs cannot accidentally target prod. (DigitalOcean multi-stage guide; multi-env-versioning.md §1.2) group_vars/<group>/as directories — every YAML file inside is auto-loaded; reduces merge conflicts; separatesvault.yml(encrypted secrets) from plain vars. Role-scoped vars go ingroup_vars/<group>/<role>.yml. (Red Hat CoP; ansible-iac-gitops.md §1)- Variables live in inventory, never in plays — Red Hat CoP: "Avoid playbook and play
variables as well as
include_vars. Opt for inventory variables instead." Keeps a clean code/data boundary and makes variable precedence predictable. (ansible-iac-gitops.md §1) - Role variable naming — role vars must start with the role name (
nginx_port, notport); internal vars use__double_underscore. (Red Hat CoP; modular-ansible-repos.md §4) - FQCN everywhere — use
ansible.builtin.copy,ansible.windows.win_service, etc. Claude Code generates FQCN ~80% of the time naturally; lint enforces the rest. (ansible-iac-gitops.md §6 — prior-art finding) Never
commandorshellwhere a module exists — breaks idempotency and--check --diffdrift detection; caught byansible-lint+ Molecule idempotence gate. When genuinely unavoidable, useansible.builtin.command(notshell) with the sanctioned idempotency guard —creates:/removes:for state, orchanged_when:for read-only queries — and a comment explaining why no module covers it:# Sanctioned escape hatch: no module covers this, guarded for idempotency - name: Initialize the widget store (only once) ansible.builtin.command: widgetctl init args: creates: /var/lib/widget/.initialized # makes it idempotent(ansible-iac-gitops.md §§1,6; ansible-testing skill)
- Validate role inputs with
meta/argument_specs.yml— declare each role variable's type, required-ness, and description. This is a production-profile expectation and a strong correctness control for AI-authored roles (the role fails fast on a bad/missing arg instead of misbehaving downstream). - Mixed Windows + Linux by structure, not conditionals — separate plays per OS group
or separate playbook files.
group_vars/windows/setsansible_connection: winrm(orpsrp) and WinRM transport;group_vars/linux/uses SSH defaults. Do NOT rely onwhen: ansible_os_family == "Windows"guards alone — the model "sometimes forgets them." Useansible.windows,community.windows,microsoft.adcollections for Windows. Control node must be Linux; WinRM/Kerberos (domain-joined) or WinRM/NTLM+TLS (non-domain) drives both targets from one runner. (ansible-iac-gitops.md §1 Windows section)
# group_vars/windows/main.yml
# Choose the connection per your AD/transport posture — do not assume a default.
# Kerberos over WinRM-HTTPS (5986) remains the PCI-defensible choice for AD-joined
# hosts; psrp is a valid alternative. ansible.windows/SSH-on-Windows has matured in
# ansible-core 2.18+ but is still estate-dependent.
ansible_connection: winrm # or psrp — per transport posture
ansible_winrm_transport: kerberos # AD-joined preferred; NTLM+TLS for non-domain
ansible_port: 5986TODO: Expand with specific Kerberos setup checklist (krb5.conf, SPN registration) once the AD domain name is known from ingested network diagram.
ansible.cfg (committed audit artifact)
Pin: inventory, roles_path, collections_path, interpreter_python = auto_silent,
stdout_callback = yaml, callbacks_enabled = ara (ARA for run tracking),
host_key_checking = True, forks = 20. A committed config is itself a
change-controlled artifact.
(ansible-iac-gitops.md §1; DESIGN.md §13)
Trust Boundary
- Propose via MR; never run
ansible-playbookdirectly against prod. (SPEC.md §2) - Agent edits code and vars — never vault keys, never plaintext secrets.
- Windows SSH is experimental (ansible-core 2.18+, Windows Server 2022+ only) — do not use in production until it matures. (gitlab-octopus-cicd.md §5.1)
Examples
# Correct: FQCN, idempotent module, no command/shell
- name: Ensure nginx is installed (Linux)
ansible.builtin.package:
name: nginx
state: present
# Correct: Windows module via FQCN
- name: Ensure IIS feature is present (Windows)
ansible.windows.win_feature:
name: Web-Server
state: present
# Wrong: breaks idempotency
- name: Install nginx
ansible.builtin.command: apt install -y nginx # NEVER — use package moduleTODO: Add org-specific role-prefix convention once naming standards are confirmed from ingested runbooks. TODO: Add dynamic-inventory plugin examples once CMDB integration is known.
Deep Reference
FQCN Enforcement (Ansible Galaxy + Collections)
Every module call must use a Fully Qualified Collection Name. Short names
(copy:, service:) break when collections are installed and the resolver
picks the wrong one. There are no exceptions.
# CORRECT
- ansible.builtin.copy:
- ansible.builtin.service:
- community.general.ufw:
- community.hashi_vault.hashi_vault_secret:
- ansible.windows.win_service:
# WRONG — never acceptable
- copy:
- service:
- win_service:Idempotency Checklist
Before marking a task idempotent, verify:
- The module itself is idempotent (most ansible.builtin.* are;
command/shellare not) - If using
command/shell:creates:orremoves:orchanged_when: falseis present - Running the play twice produces no changes on the second run (
--check --diffshows nothing) - Any
notify:handlers are also idempotent when run multiple times
OS-Targeting by Structure (not when:)
# CORRECT structure — separate plays per OS in site.yml:
- hosts: linux_servers
roles: [base-linux]
- hosts: windows_servers
roles: [base-windows]
# WRONG — mixing OS logic in a shared role:
- name: Install service
ansible.builtin.service:
name: myservice
state: started
when: ansible_os_family != 'Windows' # fragile; breaks on new OS familiesVariable Precedence (know it, don't fight it)
Order from weakest to strongest (last wins): role defaults → inventory group_vars → inventory host_vars → playbook vars → extra-vars (-e). Use role defaults for safe defaults; never duplicate a var at multiple precedence levels with different intent.
Windows Targets
Use ansible.windows.* and community.windows.* FQCNs. WinRM connection requires:
ansible_connection: winrmansible_winrm_transport: kerberosorntlm(neverbasicin production)ansible_winrm_server_cert_validation: validate(neverignorein production)
no_log Mandatory Patterns
# Any task that touches secret values must suppress output:
- name: Retrieve DB password
community.hashi_vault.hashi_vault_secret:
url: "{{ vault_addr }}"
path: "secret/db/password"
register: db_secret
no_log: true # MANDATORY — vault response contains the secret value
- name: Create DB user
community.mysql.mysql_user:
name: app
password: "{{ db_secret.secret.value }}"
no_log: true # MANDATORY — password is in task argsCIS Benchmark Hardening Patterns (Linux)
When writing OS hardening roles, reference CIS Benchmark Level 1/2. Key patterns:
# Disable unused filesystems (CIS 1.1.x)
- name: Disable cramfs
ansible.builtin.lineinfile:
path: /etc/modprobe.d/cramfs.conf
line: "install cramfs /bin/false"
create: true
mode: '0644'
# Ensure rsyslog is running (CIS 4.2.x)
- name: Ensure rsyslog is enabled
ansible.builtin.systemd:
name: rsyslog
state: started
enabled: trueMolecule Scenario Structure
Every new role ships with a Molecule scenario under molecule/default/:
roles/<name>/
molecule/
default/
molecule.yml # driver: podman; platforms: [{name: instance, image: ...}]
converge.yml # apply the role
verify.yml # assert expected state
prepare.yml # pre-role setup (optional)The idempotence test runs converge.yml twice and asserts no changes on the second run.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

