Re-use artifacts (playbooks, tasks, ...) in Ansible
Posted on April 15, 2021 by Adrian Wyssmann ‐ 4 min read
I recently struggled into an issue where I have certain tasks which I want to run for the bootstrapping, as well when applying the desired state. I don't want to have to repeat the same task in different playbooks, so I had a look into reusable of ansible elements
What does Ansible say about re-using artifacts
Re-using Ansible artifacts describes it pretty well
You can write a simple playbook in one very large file, and most users learn the one-file approach first. However, breaking tasks up into different files is an excellent way to organize complex sets of tasks and reuse them.
You can have different artifacts which can be re-used
- variables file contains only variables.
- task file contains only tasks.
- playbook contains at least one play, and may contain variables, tasks, and other content. You can re-use tightly focused playbooks, but you can only re-use them statically, not dynamically.
- role contains a set of related tasks, variables, defaults, handlers, and even modules or other plugins in a defined file-tree, see Ansible Roles
Known the different artifacts you also shall understand how you can use them. Ansible supports Including and importing. This means you can do various things:
- Import a task list: Imports a list of tasks to be added to the current playbook for subsequent execution.
- Load and execute a role: Dynamically loads and executes a specified role as a task, but has some constraints (check the docu)
- Import a role into a play: Much like the
roles:
keyword, this task loads a role, but it allows you to control when the role tasks run in between other tasks of the play. - Import a playbook: Includes a file with a list of plays to be executed, but cannot be used inside a play. If it includes other plays, it can only be included at the top level.
What is my issue
I heavily rely on roles when working with ansible, but still if you want to re-apply the same set of roles in different plays you may still need to use other artifacts. This is exactly the case for me, so when I bootstrap my servers, I want to apply the same roles - e.g. base-system
, papanito.cloudflared
, oefenweb.fail2ban
, etc. However I also want to apply the same roles as for my site.yml
.
As briefly mentioned in this post I have a bootstrap playbook which in does create the servers and applies the generic roles.
I have also a production.yml
which also does apply th same roles - with a slight difference that the ansible_host
and ansible_ssh_user
are different, as well as the hosts
.
This is necessary due to the way I have chosen to do my setup:
- add specific user for ansible, disable login for root
- install fail2ban
- make an ssh hardening
- setup a cloudflare argo tunnel so I can access the server via
xxx.example.com
and have additional protection (zero trust policy)
The issue is, whenever there is a new “generic” role to be applied I have to add it in more than one playbook - not a big deal if you only have few playbooks - but still I don’t like to have to repeat things which tend to get forgotten.
So what now?
For my use cases I created a playbook generic_roles.yml
which contains all roles I want to apply for all my nodes:
The hosts
is not fixed but excepts a target
variable. Then I call this playbook as follows in my bootstrap_hcloud_servers
The same playbook can be use in the site.yml
So my simplified site.yml
would look like this:
This way I can always run site.yml
to ensure the desired state is applied to all my nodes, but also I could run roles.ttrss.yml
alone in case I only need to apply the state to my rss
hosts.
That’s it, mission accomplished. You see Ansible has a lot of interesting concepts, which helps you to properly structure your project so that you don’t have to multiply the same “code” in various places.