import_tasks vs include_tasks
Once your playbook grows past 50 lines, pull groups of tasks into
separate files.
| Statement | Resolved at | Conditionals | Loops |
|-------------------|--------------|------------------|-------|
| import_tasks | parse | applied to each | no |
| include_tasks | runtime | applied to the include | yes |
Rule of thumb: prefer import_* for static structure (you almost
always want it), reach for include_* when you need to iterate or
gate the entire group of tasks.
Create tasks/setup.yml:
---
- name: Ensure marker dir exists
file:
path: /etc/cac-structuring
state: directory
mode: '0755'
- name: Drop README
copy:
dest: /etc/cac-structuring/README
content: "Imported from tasks/setup.yml\n"
mode: '0644'
...
And tasks/cleanup.yml:
---
- name: Remove a (probably-missing) file
file:
path: /etc/cac-doesnt-exist
state: absent
...
Then the entry-point structure.yml:
---
- name: Composed playbook
hosts: linux
gather_facts: false
tasks:
- import_tasks: tasks/setup.yml
- include_tasks: tasks/cleanup.yml
...
Both forms point at a YAML file with a list of tasks. The difference
matters when you want to loop the include or wrap it in a
conditional โ include_tasks will resolve the loop/condition at
runtime per item, while import_tasks rewrites the playbook at
parse time and the loop/condition would apply to each imported task
individually.
Run it:
ansible-playbook structure.yml
Click Verify step once /etc/cac-structuring/README exists on
every host.
Hint
`import_tasks` resolves at parse time; `include_tasks` resolves at runtime and can be looped or gated.
Your first role
A role is the canonical reusable bundle: tasks + handlers +
templates + defaults + metadata in one directory.
Scaffold one with ansible-galaxy:
cd /root/ansible
ansible-galaxy init roles/webserver
tree roles/webserver
Edit roles/webserver/tasks/main.yml:
---
- name: Drop a placeholder index
copy:
dest: /etc/cac-webserver.html
content: "<h1>Served by {{ inventory_hostname }} โ role webserver</h1>\n"
mode: '0644'
...
Edit roles/webserver/defaults/main.yml:
---
webserver_owner: "ops@cloudaicampus.local"
...
Now consume the role from a play. Write site.yml:
---
- name: Apply the webserver role to every linux host
hosts: linux
gather_facts: false
roles:
- webserver
...
Run it:
ansible-playbook site.yml
The roles: keyword runs all of the role's tasks. Variables
from defaults/ are automatically merged into the play's scope, so
{{ webserver_owner }} would work inside any of the role's
templates.
Click Verify step once /etc/cac-webserver.html lands on every
host.
Hint
`ansible-galaxy init <name>` scaffolds the directory; `roles:` on a play consumes it.
Tags
tags: annotate tasks (or whole roles) so you can run a slice:
ansible-playbook site.yml --tags setup,deploy runs only those.
ansible-playbook site.yml --skip-tags slow,destructive excludes those.
--list-tags enumerates every tag in the playbook.
Add tags to your site.yml:
---
- name: Apply roles with tags
hosts: linux
gather_facts: false
tasks:
- name: Drop fast marker
file:
path: /etc/cac-tag-fast
state: touch
mode: '0644'
tags: [fast]
- name: Drop slow marker
command: sh -c 'sleep 1; touch /etc/cac-tag-slow'
tags: [slow]
changed_when: false
...
Now run only the fast tag:
ansible-playbook site.yml --tags fast
Only /etc/cac-tag-fast should appear; the slow task is skipped.
Then run only the slow tag:
ansible-playbook site.yml --tags slow
--list-tags is handy when a playbook gets bigger:
ansible-playbook site.yml --list-tags
Click Verify step once both markers exist on every host.
Hint
Add `tags: [setup]` to tasks; run `ansible-playbook ... --tags setup` to filter.
Tuning ansible.cfg
ansible.cfg is a layered configuration file. Order of precedence
(highest first):
ANSIBLE_* environment variables
./ansible.cfg in the current directory
~/.ansible.cfg
/etc/ansible/ansible.cfg
The two highest-impact knobs for big inventories:
forks = N โ how many hosts the controller talks to in
parallel (default 5).
strategy = free โ let each host run through the whole play
independently instead of synchronizing on each task (strategy = linear is the default).
Update /root/ansible/ansible.cfg:
[defaults]
inventory = hosts
host_key_checking = False
retry_files_enabled = False
forks = 10
strategy = free
Write bench.yml to feel the difference:
---
- name: Strategy benchmark
hosts: linux
gather_facts: false
tasks:
- name: Stagger the work
command: sh -c 'sleep {{ groups.linux.index(inventory_hostname) }}; true'
changed_when: false
- name: Mark complete
file:
path: /etc/cac-strategy.done
state: touch
mode: '0644'
...
Time it:
time ansible-playbook bench.yml
With strategy = free, hosts that finish their sleep quickly move
on to the file task without waiting for ubuntu3 (which sleeps 5s)
to finish. With strategy = linear (try toggling it back) the play
takes ~max(sleeps) per task.
Click Verify step once /etc/cac-strategy.done is present on every
host.
Hint
Increase `forks` for parallelism; set `strategy=free` so slow hosts don't block fast ones.