Roles Directory structure and Ansible Galaxy

Roles

While it’s possible to write a playbook in one very large file eventually you’ll want to reuse files and start to organize things.
In above example, we have seen in our playbook we have variables, tasks, handlers and templates. This can slowly grow and will become eventually difficult to read and manage.
Roles is a directory structure where we distribute the content of our main playbook into proper directory structure.

Roles directory structure:

├──roles
│└──apache
│├──handlers
││└──main.yml
│├──tasks
││└──main.yml
│├──templates
││├──httpd.j2
││└──index.j2
│└──vars
│└──main.yml
This is what they are all for:
1. Files: This directory contains regular files that need to be transferred to the hosts you are configuring for this role. This may also include script files to run.
2. Handlers: All handlers that were in your playbook previously can now be added into this directory.
3. Meta: This directory can contain files that establish role dependencies. You can list roles that must be applied before the current role can work correctly.
4. Templates: You can place all files that use variables to substitute information during creation in this directory.
5. Tasks: This directory contains all of the tasks that would normally be in a playbook. These can reference files and templates contained in their respective directories without using a path.
6. Vars: Variables for the roles can be specified in this directory and used in your configuration files.
Instead of having all our code in one playbook mashed up, we can distribute it into different directory structure. For example, all the tasks from our playbook will got into roles/apache/tasks/main.yml file, likewise vars go into vars/main.yml.
We can create the roles directory structure with ansible-galaxy command.
$ mkdir roles
$ cd roles/
imran@DevOps:.../roles$ ansible-galaxy init apache
- apache was created successfully

imran@DevOps:.../roles$ tree
. └──apache
├──defaults
│└──main.yml
├──handlers
│└──main.yml
├──meta
│└──main.yml
├──README.md
├──tasks
│└──main.yml
├──tests
│├──inventory
│└──test.yml
└──vars
└──main.yml
$ cd ..
Our main playbook will just call the role by its name and will not have any tasks.
$ cat webservers.yml --- - hosts: webservers sudo: yes gather_facts: no roles: - apache
$ cat roles/apache/tasks/main.yml
---
- name: Ensure Apache installed
yum: name=httpd state=present
- name: Creates directoy
file: path=/var/www/html/ansible state=directory

- name: Ensure libselinux-python installed
yum: name=libselinux-python state=present

- name: Ensure Apache is running
service: name=httpd enabled=yes state=started

- name: Deploy configuration File
template: src=templates/httpd.j2 dest=/etc/httpd/conf/httpd.conf
notify:
- Restart Apache
- name: Copy Site Files
template: src=templates/index.j2 dest={{doc_root}}/index.html mode=0644
- name: Stop IPTABLES Now!!
service: name=iptables state=stopped
$ cat roles/apache/handlers/main.yml
---
- name: Restart Apache
service: name=httpd state=restarted
$ cat roles/apache/vars/main.yml
http_port: 80
doc_dir: /ansible/
doc_root: /var/www/html/ansible/
max_clients: 5
ansible_python_interpreter: python
username: devops
$ ls roles/apache/templates/
httpd.j2 index.j2

It would be a good idea to first write a mashed-up playbook then we can start copying the content of the main playbook to the roles directory structure. As the complexity grow we can then manage with the roles directory structure.

Executing Playbook

We execute the main playbook which in turn will read the roles directory structure and execute all the tasks & handlers for us.
$ ansible-playbook webservers.yml

Overriding variables of roles.
We have few variables defined in our apache roles in roles/apache/vars/main.yml file.
When we execute our playbook it uses all these defined variables but we can override these variables without changing the content of roles/apache/vars/main.yml file.
---
- hosts: webservers
sudo: yes
gather_facts: no
roles:
- {role:apache, http_port:8090, max_clients:250}
As shown above we are passing a dictionary now {} instead of the role name. Dictionary has key=value pairs for role name and variables that we want to override.

Ansible Galaxy

So far, we have got the idea of what ansible roles are and how to create them. There are so many predefined roles in Ansible Galaxy website which we can download and use freely. For example, if we want to setup mysql service, we can create mysql role from scratch or we can use some existing role from ansible galaxy.
https://galaxy.ansible.com/list#/roles?page=1&page_size=10
We can search and find a relevant role for our work. Role gets reviewed and gets star marks. Click on the role to read about it in detail. Check the supported platforms and ansible version which it supports.  
README section talks about how to use this role with examples.

Download the ansible galaxy role

Role by default gets downloaded into /etc/ansible/roles directory. Once its downloaded you can start using this role from your playbook. Refer to the example given in the README section of the role.
$ sudo ansible-galaxy install bennojoy.mysql
- downloading role 'mysql', owned by bennojoy
- downloading role from https://github.com/bennojoy/mysql/archive/master.tar.gz
- extracting bennojoy.mysql to /etc/ansible/roles/bennojoy.mysql
- bennojoy.mysql (master) was installed successfully
Ansible Vault-Managing secrets with Ansible vault
The vault feature can encrypt any structured data file used by Ansible. This can include “group_vars/” or “host_vars/” inventory variables, variables loaded by “include_vars” or “vars_files”, or variable files passed on the ansible-playbook command line with “-e @file.yml” or “-e @file.json”. Role variables and defaults are also included! We can store our passwords/secrets encrypted in the ansible vault.
We will create vault in group_vars/all directory.
mkdir -p group_vars/all
cd group_vars/all
export EDITOR=vim
ansible-vault create vault
Give a vault password and put variables that you want to encrypt, below mentioned is the example of storing tomcat password.
---
vault_tomcat_pass : <-Enter password for tomcat here>
Refer to vault_tomcat_pass in group_vars/all/vars file
cd group_vars/all/
vi vars
---
tomcatuser: tomcat
tomcatpass: “{{vault_tomcat_pass}}”
If you execute your playbook now where you are using tomcatpass, you should get some error like below. For example, I am using {{tomcatpass}} variable in my tomcat.yml playbook.
# ansible-playbook tomcat.yml
ERROR! Decryption failed
ERROR! A vault password must be specified
You can give --ask-vault-pass option while executing playbook which will ask you the vault password.
# ansible-playbook tomcat.yml --ask-vault-pass
You can also use a file where you specify vault password.
Lets say “vaultpass” is our vault password.
# echo "vaultpass"> ~/.vault_pass.txt
# chmod 0600 ~/.vault_pass.txt
Sample Execution.
imran@DevOps:.../ans$ tree
. ├── group_vars
│└── all
│├── vars
│└── vault
└── test.yml
2 directories, 3 files
imran@DevOps:.../ans$ cat group_vars/all/vars
--- tomcatpassword: "{{tomcatpass}}"
imran@DevOps:.../ans$ cat test.yml
--- - hosts: localhost
tasks: - name: Print tomcat pass
debug: msg="{{tomcatpassword}}"
imran@DevOps:.../ans$ ansible-playbook test.yml --ask-vault-pass
Vault password:
[WARNING]: provided hosts list is empty, only localhost is available
PLAY ***********************************************************************
TASK [setup] *****************************************************************
ok: [localhost]
TASK [Print tomcat pass] *******************************************************
ok: [localhost] => {
"msg": "tomcat"
}
PLAY RECAP ****************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0

Editing Vault
To edit an encrypted file in place, use the ansible-vault edit command. This command will decrypt the file to a temporary file and allow you to edit the file, saving it back when done and removing the temporary file:
ansible-vault edit vault 

Comments