Claude
Skills
Sign in
Back

ansible

Included with Lifetime
$97 forever

Ansible automation conventions, patterns, and toolchain: playbook design, roles, inventory, vault, collections, execution environments, Event-Driven Ansible, testing, and performance tuning. Invoke whenever task involves any interaction with Ansible — writing playbooks, creating roles, managing inventory, reviewing automation code, debugging runs, upgrading ansible-core, or working with AAP.

Design

What this skill does


# Ansible

Idempotency is the highest Ansible virtue. Every task must describe desired state, not a sequence of commands.

## References

Extended examples, patterns, and rationale for the rules below live in `${CLAUDE_SKILL_DIR}/references/`.

- **playbook-patterns** — [`${CLAUDE_SKILL_DIR}/references/playbook-patterns.md`]: Play execution order, static vs
  dynamic reuse comparison table, batched execution with `serial`, verification flags, standard directory layouts
- **role-structure** — [`${CLAUDE_SKILL_DIR}/references/role-structure.md`]: Extended directory tree, three ways to use
  roles (play-level, import_role, include_role) with examples, platform-specific task splitting, argument_specs example,
  dependency mechanics, deduplication rules
- **inventory-management** — [`${CLAUDE_SKILL_DIR}/references/inventory-management.md`]: YAML/INI examples, group
  hierarchy, environment separation, AWS/Azure/GCP/NetBox/Terraform plugins, multi-cloud chaining, caching
- **vault-and-security** — [`${CLAUDE_SKILL_DIR}/references/vault-and-security.md`]: File vs variable encryption,
  password sources, ansible-sign, GPG verification, hardening roles, compliance scanning, security smells
- **variables-and-templating** — [`${CLAUDE_SKILL_DIR}/references/variables-and-templating.md`]: Full 22-level
  precedence list, magic variables, YAML quoting gotcha, registered variables
- **error-handling** — [`${CLAUDE_SKILL_DIR}/references/error-handling.md`]: Block execution flow, rescue variables,
  failed_when/changed_when, any_errors_fatal
- **handlers-and-delegation** — [`${CLAUDE_SKILL_DIR}/references/handlers-and-delegation.md`]: Handler execution order,
  flushing, delegate_to, delegate_facts, fire-and-forget async
- **testing-and-performance** — [`${CLAUDE_SKILL_DIR}/references/testing-and-performance.md`]: Molecule lifecycle,
  driver comparison, CI matrix testing, strategy plugins, SSH pipelining, fact caching, serial batching
- **execution-environments-and-collections** —
  [`${CLAUDE_SKILL_DIR}/references/execution-environments-and-collections.md`]: EE vs local installs, version 3 schema,
  FQCN migration, collection certification, Galaxy publishing, automation mesh, AAP 2.5/2.6 platform architecture
- **event-driven-ansible** — [`${CLAUDE_SKILL_DIR}/references/event-driven-ansible.md`]: Rulebook structure, event
  sources, conditions, actions, Event Streams, decision environments, Kafka vs webhooks, performance tuning,
  troubleshooting
- **porting-guide** — [`${CLAUDE_SKILL_DIR}/references/porting-guide.md`]: ansible-core 2.17/2.18 breaking changes,
  Python version requirements, removed modules, AAP 2.5/2.6 deprecations, upgrade strategy

## Playbook Design

### Naming and Clarity

- **Always name plays, tasks, and blocks.** Unnamed tasks produce opaque output that makes debugging impossible.
- **Always specify `state:` explicitly.** Different modules have different defaults. `state: present` / `state: absent`
  makes intent visible.
- **Always use FQCN** (Fully Qualified Collection Names): `ansible.builtin.copy`, not `copy`. Prevents ambiguity when
  multiple collections are installed.

### Idempotency

- Prefer declarative modules (`ansible.builtin.template`, `ansible.builtin.service`, `ansible.builtin.user`) over
  imperative ones (`ansible.builtin.command`, `ansible.builtin.shell`)
- When `command`/`shell` is unavoidable, add `creates:`, `removes:`, or `changed_when:` to make it idempotent
- Move complex logic into custom modules or filter plugins — Ansible is a desired state engine, not a scripting language
- Test idempotency: run twice, second run must report zero changes

### Static vs Dynamic Reuse

- `import_tasks` / `import_role` -- static, parsed at load time. Tags propagate to all imported tasks. Cannot loop. Use
  when structure is fixed.
- `include_tasks` / `include_role` -- dynamic, evaluated at runtime. Tags apply only to the include statement. Can loop
  and use `when`. Use when inclusion is conditional.

Default to `import_*` for predictability.

### Project Layout

```
inventories/
  production/
    hosts
    group_vars/
    host_vars/
  staging/
    hosts
    group_vars/
    host_vars/
site.yml                  # imports tier playbooks
webservers.yml
dbservers.yml
roles/
  common/
  webserver/
  database/
```

`site.yml` imports tier playbooks. Each tier playbook maps host groups to roles.

## Roles

A role manages one service or component — not an entire stack. Keep provisioning separate from configuration and
application deployment. Roles are not programming constructs: avoid deep inheritance hierarchies, tight coupling, or
hard dependencies on external variables.

### Structure

```
roles/my_role/
  tasks/main.yml          # entry point
  handlers/main.yml       # auto-imported into play scope
  templates/*.j2          # Jinja2 templates
  files/                  # static files
  defaults/main.yml       # low-precedence (user-configurable)
  vars/main.yml           # high-precedence (internal constants)
  meta/main.yml           # dependencies
  meta/argument_specs.yml # argument validation (2.11+)
```

### defaults/ vs vars/

- `defaults/` -- easily overridden. Use for knobs users should change (ports, paths, feature flags).
- `vars/` -- hard to override. Use for internal constants the role needs to function.

### Naming

- Role names: lowercase, hyphens: `nginx-proxy`, `ssl-certs`
- Prefix all role variables with the role name: `nginx_port`, `nginx_worker_count`
- Prefix handler names with role name: `nginx : Restart nginx`

### Argument Validation

Define expected parameters in `meta/argument_specs.yml`. Validation runs before role tasks execute.

### Dependencies

Defined in `meta/main.yml`. Run before the role. Deduplicated per play unless parameters differ or
`allow_duplicates: true` is set.

## Inventory

### Format

Prefer YAML over INI. INI `:vars` sections treat all values as strings, causing type confusion.

### Grouping Strategy

Group along three dimensions:

- **What** (function): `webservers`, `dbservers`, `monitoring`
- **Where** (location): `dc1`, `dc2`, `us_east`
- **When** (environment): `production`, `staging`, `development`

### Environment Separation

Split large inventories by function or region — a single static file with 5,000+ hosts takes 15-30 seconds to load. Keep
production and staging in separate inventory files or directories. Never mix environments in a single inventory --
developers using a mixed inventory need access to all vault passwords.

### Dynamic Inventory

Use inventory plugins (not scripts) for cloud providers:

- **AWS:** `amazon.aws.aws_ec2` -- groups from tags, instance types, regions
- **Azure:** `azure.azcollection.azure_rm` -- conditional groups, keyed groups
- **GCP:** `google.cloud.gcp_compute` -- zones, machine types, labels
- **NetBox:** `netbox.netbox.nb_inventory` -- single source of truth for hybrid environments, automatic group updates
  from tags/custom fields
- **Terraform:** `cloud.terraform.terraform_state` -- parse state files as inventory

Mix static and dynamic sources in the same inventory directory.

### Constructed Inventory

Build groups dynamically from host metadata using Jinja2 logic. Chain multiple cloud inventories into a single
constructed inventory for cross-cloud targeting. Successor to Smart Inventories in AAP.

## Variables and Precedence

### The 22-Level Precedence Rule

Role `defaults/` is lowest. Extra vars (`-e`) always win. Most common layers:

- **Overridable defaults** → Role `defaults/main.yml`
- **Environment-wide values** → `group_vars/all.yml`
- **Group-specific values** → `group_vars/<group>.yml`
- **Host-specific values** → `host_vars/<host>.yml`
- **Force a value in a role** → Role `vars/main.yml`
- **Override everything at runtime** → `--extra-vars`

Define each variable in ONE place.

### YAML Quoting

Values starting with `{{ }}` must be quoted:

```yaml
app_path: "{{ base_path }}/app"    # correct
app_path: {{ base_path }}/app      

Related in Design