I want to follow best practices for ansible and use roles that match the features.
How to organize roles that have same commons but different usage? Let me explain this on an example of installing docker swarm.
Docker swarm may be installed as a _master_ or as a _node_. Currently I have the swarm role. It has main.yml with tasks for both master and a node. Only by variables I determine if tasks is for a master, or for a node.
I dont like this. Instead of having variables, I would like to use hosts lists and say?
serverA is swarm_master
serverB, serverC... is swarm_node
This means I need two different roles, right? One for swarm_master and one for swarm_node. How I am going to share the same installation of swarm between these two roles?
You can use:
Hosts:
[swarm-master]
serverA
[swarm-nodes]
serverB
serverC
Playbook:
---
- hosts: swarm-master, swarm-nodes
roles:
- role: swarm
is_master: "{{ 'swarm-master' in group_names }}"
I think it's easier to understand the playbook, when you assign role parameters/variables when applying role.
It will be hard to guess for others that your role somewhere inside checks some variables.
To logically structure you tasks inside role, you may use conditional includes.
./roles/swarm/task/main.yml:
- include: docker_common.yml
- include: swarm_master.yml
when: is_master
- include: swarm_node.yml
when: not is_master
Related
I am setting up Ansible as a newbie. I would like to group a few tasks under an Nginx role.
See my folder structure
The only way I could get this to work is to use include statements in my playbooks... but that means I needed to start changing everything to relative paths since Ansible stopped being able to find things.
My playbook currently:
- name:
Install Nginx, Node, and Verdeccia
hosts:
all
remote_user:
root
tasks:
- include: ../roles/nginx/tasks/install.yml
- include: ../roles/nginx/tasks/create_node_config.yml
vars:
hostname: daz.com
How can I reference the sub task in the playbook to be something like
tasks:
- nginx.install
and still be within best practices.
Your use of roles so far is not in line with the norm or the idea of Ansible. From a purely technical point of view, it is quite possible that you have multiple task bundles in yml files that you include in the playbook.
If you want to include a specific task file from a role, you should better do this via the module include_role with the parameter tasks_from.
However, the workflow with roles usually looks different.
in the folder tasks should always be a file main.yml, this is called automatically, if simply the role is included, no matter over which way.
in this main.yml you can then add further control logic to include your yml files as required.
As it looks to me, for example, you always need to install, but depending on the use case you want to have different configurations.
create in your nginx role a file defaults/main.yml.
---
config_flavor: none
This initializes the config_flavor variable with the string none.
create in your nginx role a file tasks/main.yml.
---
- name: include installation process
include_tasks: install.yml
- name: configure node
include_tasks: create_node_config.yml
when: config_flavor == "node"
- name: configure symfony
include_tasks: create_symfony_config.yml
when: config_flavor == "symfony"
- name: configure wordpress
include_tasks: create_wordpress_config.yml
when: config_flavor == "wordpress"
The main.yml will be included by default when the role is applied.
This is where the installation is done first, then the proper configuration is done.
Which configuration should be done is defined by the variable config_flavor. In the listed example the values are node, symfony and wordpress. In all other cases the installation will be done, but no configuration (so in the default case with none).
include your role in the playbook as follows
---
- name: Install Nginx, Node, and Verdeccia
hosts: all
remote_user: root
vars:
hostname: daz.com
roles:
- role: nginx
config_flavor: node
At this point you can set the value for config_flavor. If you set wordpress instead, the tasks create_wordpress_config.yml will be included automatically, using the logic from tasks/main.yml.
You can find more about roles in the Ansible Docs.
There are many more possibilities and ways, you will get to know them in the course of your learning. Basically I would recommend you to read a lot in the Ansible docs and to use them as a reference book.
Please refer to bellow document. you can use "include_role" with "tasks_from" attribute. you don't need to provide full path of task files, Just file names.
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/include_role_module.html
Example:
- name: Run tasks/other.yaml instead of 'main'
ansible.builtin.include_role:
name: myrole
tasks_from: other
I'm trying to skip a task in a playbook based on the group in my inventory file.
Inventory:
[test]
testserver1
[qa]
qaserver1
[prod]
prodserver1
prodserver2
I'm pretty sure I need to add the when clause to my task, but not sure how to reference the inventory file.
- name: Add AD group to local admin
ansible.windows.win_group_membership:
name: Administrators
members:
- "{{ local_admin_ad_group }}"
state: present
when: inventory is not prod
Basically, I want run the task above on all the servers except for the ones in the group called prod.
You can have all your hosts in one environment, but I suggest to use different environment files for dev, staging, qa and prod. If you separate it by condition, it can happen quite fast, that you mess up some condition or forget to add it to a new task altogether and accidentally run a task on a prod host, that should not run there.
If you still want to have all your hosts in the same inventory, you can either separate them using different plays (you can have multiple in the same playbook) and then using hosts to specify where they should run.
For example:
- name: play one
hosts:
- qa
- test
tasks:
<all your tasks for qa and test>
- name: play two
hosts: prod
tasks:
<all your tasks for prod>
If you want to do it on a per-task level, you can use the group_names variable.
For example:
- name: Add AD group to local admin
ansible.windows.win_group_membership:
name: Administrators
members:
- "{{ local_admin_ad_group }}"
state: present
when: '"prod" not in group_names'
In that case you need to be really careful if you change things, so your conditions are still the way they are supposed to be.
The current case is this:
I have a playbook which provisions a bunch of servers and installs apps to these servers.
One of these apps already has it's own ansible playbook which I wanted to use. Now my problem arises from this playbook, as it's limited to hosts: [prod] and the host groups I have in the upper-level playbook are different.
I know I could just use add_host to add the needed hosts to a prod group, but that is a solution which I don't like.
So my question is: Is there a way to add the current hosts to a new host group in the include statement?
Something like - include: foo.yml prod={{ ansible_host_group }}
Or can I somehow include only the tasks from a playbook?
No, there's no direct way to do this.
Now my problem arises from this playbook, as it's limited to
hosts: [prod]
You can setup host's more flexible via extra vars:
- name: add role fail2ban
hosts: '{{ target }}'
remote_user: root
roles:
- fail2ban
Run it:
ansible-playbook testplaybook.yml --extra-vars "target=10.0.190.123"
ansible-playbook testplaybook.yml --extra-vars "target=webservers"
Is this workaround suitable for you?
I have 4 servers: production web, production db, staging web+db, development web+db (actually there are many dev servers, but they are used on localhosts using vagrant).
Config vars are different for these playbooks.
Actually I can use 4 playbooks, which include other yml files (nginx.yml, php.yml, mysql.yml), and I don't have to use roles.
Is it correct?
How should I organize my ansible files?
Ansible is flexile enough to work either way.
You can proceed with two aproaches
Define roles (i.e. production web, production db, staging web+db, development web+db) and under roles have tasks (nginx, php, mysql)
Define each module as role (i.e. nginx role, php role, mysql role) and then specify which roles are included in which playbook.
First way is better for cases that every php setup is different for every environment.
Second way is better for cases that all your php setups are the same, and it would be cumbersome to maintain 4 similar setup/config; you would write them once, and call them by their local variables.
Example of second one
./ansible-playbook roles/role1/tasks/production-web.yml -i hosts.yml
- name: playbook name
hosts: host1
sudo: yes
vars:
- php_version: 5.6.16
vars_files:
- "vars/local_vars"
pre_tasks:
- include: system-preparation.yml
- include: user-setup.yml
tasks:
- debug: msg="Installing and configuring PHP"
- include: php-setup.yml
- include: php-configuration.yml
post_tasks:
- include: health-check.yml
handlers:
Here is my play book:
- name: Install MySQL with replication
hosts: mysql-master:mysql-slave
user: root
sudo: false
roles:
- common
- admin-users
- generic-directories
- { role: iptables, tags: [ 'mysql-iptables'] }
- mysql
I have ip tables tasks for different ports, I want to run the task depending on the group of servers.
I have tagged the iptables task based on the group.
When i ran the play book instead of playing the tagged task, its run through all the tasks defined in iptables role.
Please let me know if am doing anything wrong here.
In practices roles should not contains code/configuration used only by you. Try to develop roles like if you're going to publish them, doing this you will create more generic/usefull roles
With an iptable role, what you want in the end is to open port/change firewall configuration.
The role should contains tasks that allow configuration in the playbook:
---
- name: iptables | Open ports
command: 'open port {{item.protocol}} {{item.port}}
with_items: 'iptable_conf'
tags:
- iptables
then you playbook
- name: Install MySQL with replication
hosts: mysql-master:mysql-slave
user: root
sudo: false
vars:
- iptables_conf:
- {protocol: tcp, port: 3307}
- {protocol: tcp, port: 3306}
roles:
- common
- admin-users
- generic-directories
- iptables
- mysql
Hope you like opinionated software all the way: https://github.com/ansible/ansible/issues/3283 .
If I'm reading that correctly, you're experiencing a feature, with everything in that role being tagged for you and your CLI tag specification subsequently matching all those tasks. I hate this feature. It's stupid. "Application" vs. "selection" w/r/t to tags is an obvious first question when you're initially exposed to Ansible tags. It should have a more flexible answer, or at least a nod in the docs.
I would suggest a different way of organizing the versatile iptables role. First, consider not having the role if it's wrapping a very sparse amount of tasks. I would recommend for roles to have meaning to you, and not be module-adapters. So maybe the sql role can handle sql-specific rules in a separate tasks file.
Otherwise, a role parameter which can then be used to load variables dynamically (e.g. the list of firewall rules). Here's what that would look like, stubbed out:
Playbook:
---
- hosts: loc
roles:
- { role: does-too-much, focus_on: 'specific-kind' }
Role tasks/main.yml:
---
- include_vars: "{{ focus_on }}.yml"
- debug:
msg: "ok - {{ item }}"
with_items: stuff
Variables vars/specific-kind.yml:
---
stuff:
- b
- c