Files
howtos/topics/ansible-roles.md
user 80f82dcde7 docs: add ansible howto collection
Four reference files covering:
- ansible.md — core commands, ansible.cfg, key settings
- ansible-inventory.md — static/dynamic inventory, directory layout
- ansible-variables.md — 22-level precedence, scoping, merge behavior
- ansible-roles.md — structure, defaults vs vars, dependencies
2026-02-21 20:33:34 +01:00

212 lines
5.7 KiB
Markdown

# Ansible Roles
> Reusable, self-contained units of automation with a standard directory structure.
## Role Directory Structure
```
roles/
└── nginx/
├── defaults/
│ └── main.yml # Low-precedence defaults (user overrides these)
├── vars/
│ └── main.yml # High-precedence vars (internal to role)
├── tasks/
│ └── main.yml # Entry point for tasks
├── handlers/
│ └── main.yml # Handlers triggered by notify
├── templates/
│ └── nginx.conf.j2 # Jinja2 templates
├── files/
│ └── index.html # Static files for copy module
├── meta/
│ └── main.yml # Dependencies, galaxy metadata
└── README.md
```
All directories are optional — include only what the role needs.
### What Goes Where
| Directory | Purpose | Precedence |
|-------------|--------------------------------|-------------|
| `defaults/` | Default variable values | **Lowest** — meant to be overridden |
| `vars/` | Internal role variables | **High** — not easily overridden |
| `tasks/` | Main task list | Entry point: `tasks/main.yml` |
| `handlers/` | Restart/reload triggers | Run once at end of play |
| `templates/` | Jinja2 files (`.j2`) | Referenced as `template: nginx.conf.j2` |
| `files/` | Static files | Referenced as `copy: src=index.html` |
| `meta/` | Role deps, platform info | Processed before role tasks run |
## Creating a Role
```bash
# Scaffold with ansible-galaxy
ansible-galaxy role init roles/nginx
# Minimal manual structure
mkdir -p roles/nginx/{tasks,defaults,templates,handlers,meta}
```
## Using Roles
### In a Playbook
```yaml
# site.yml
- hosts: webservers
roles:
- nginx # simple
- role: nginx # with params
vars:
nginx_listen_port: 8080
- role: nginx # conditional
when: install_nginx | bool
- role: nginx # tagged
tags: [web, nginx]
```
### With `include_role` / `import_role`
```yaml
tasks:
# Static import — processed at parse time
- import_role:
name: nginx
# Dynamic include — processed at runtime (supports loops/conditionals)
- include_role:
name: "{{ item }}"
loop:
- nginx
- certbot
```
| | `import_role` | `include_role` |
|---|---|---|
| When parsed | Playbook load | Runtime |
| Tags/when | Applied to all tasks inside | Applied to include itself |
| Loops | Not supported | Supported |
| Handlers | Visible globally | Scoped to include |
## Role Dependencies
```yaml
# roles/nginx/meta/main.yml
dependencies:
- role: common
- role: firewall
vars:
firewall_allowed_ports:
- 80
- 443
```
Dependencies run **before** the role's own tasks. Duplicate dependencies are skipped unless `allow_duplicates: true` is set.
## Example Role: nginx
```yaml
# roles/nginx/defaults/main.yml
nginx_listen_port: 80
nginx_worker_processes: auto
nginx_server_name: "_"
nginx_root: /var/www/html
nginx_access_log: /var/log/nginx/access.log
```
```yaml
# roles/nginx/tasks/main.yml
- name: Install nginx
apt:
name: nginx
state: present
update_cache: true
- name: Deploy configuration
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: "0644"
notify: Reload nginx
- name: Enable and start nginx
service:
name: nginx
state: started
enabled: true
```
```yaml
# roles/nginx/handlers/main.yml
- name: Reload nginx
service:
name: nginx
state: reloaded
```
## Splitting Tasks
```yaml
# roles/nginx/tasks/main.yml
- import_tasks: install.yml
- import_tasks: configure.yml
- import_tasks: service.yml
# Conditional platform tasks
- import_tasks: debian.yml
when: ansible_os_family == "Debian"
- import_tasks: redhat.yml
when: ansible_os_family == "RedHat"
```
## Project Layout with Roles
```
ansible-project/
├── ansible.cfg
├── site.yml # Master playbook
├── webservers.yml # Play for web tier
├── dbservers.yml # Play for db tier
├── inventory/
│ ├── hosts.yml
│ ├── group_vars/
│ └── host_vars/
├── roles/
│ ├── common/ # Shared baseline
│ ├── nginx/
│ ├── postgresql/
│ └── requirements.yml # Galaxy dependencies
└── collections/
└── requirements.yml # Collection dependencies
```
```yaml
# roles/requirements.yml
- src: geerlingguy.docker
version: "6.1.0"
- src: geerlingguy.certbot
```
```bash
ansible-galaxy install -r roles/requirements.yml -p roles/
```
## Gotchas
- `defaults/` is for users to override; `vars/` is for role internals — mixing them up breaks precedence
- Handlers run at end of play, not after each task — use `meta: flush_handlers` if needed mid-play
- Role names must be valid Python identifiers (no hyphens) when used as variable namespaces
- `import_role` makes role tasks visible to `--list-tasks`; `include_role` does not
- Dependencies declared in `meta/` run every time the role is referenced unless deduplicated
- Template paths are relative to `templates/` — use `nginx.conf.j2`, not `templates/nginx.conf.j2`
- File paths in `copy` are relative to `files/` — use `index.html`, not `files/index.html`
## See Also
- `ansible` — core commands and ansible.cfg
- `ansible-variables` — how defaults/ vs vars/ fits into precedence
- `ansible-inventory` — inventory structure for role-consuming projects