In this post I’ll demonstrate using variables in your Ansible Playbooks, then go on to secret management using Ansible Vault, before finally completing the post covering Ansible Roles. The complete list of posts in this series are:
- Ansible Part 1: Setup and Configure on Ubuntu and VirtualBox
- Ansible Part 2: Playbooks
- Ansible Part 3: Variables, Vault and Roles (This post)
All code in these posts can be found on GitHub - ubuntu-config/ansible and GitHub - ubuntu-config/ansible-roles
Variables and Debug
Ansible allows you to create local variables in your playbooks. We can use vars to create key/value pairs or you can use variable files and import them into the playbook. Variables use the same Jinger2 syntax I used to configure the load balancer in the previous post.
In the example below, I have defined a local variable called app_path
with a value of /var/www/html
. Then, anywhere there was “/var/www/html” hard-coded was replaced with "{{ app_path }}"
.
---
- hosts: webservers
become: true
vars:
app_path: "/var/www/html"
tasks:
- name: Copy app files
copy:
src: ../index.php
dest: "{{ app_path }}"
mode: 0755
notify: restart apache
- name: Delete index.html
ansible.builtin.file:
path: "{{ app_path }}/index.html"
state: absent
notify: restart apache
During the TASK [Gathering Facts] step, Ansible will create a variable for us called ansible_facts, which holds metadata about the host we are interacting with. This information, such as the hosts IP address, from the ansible_facts variable can be injected into future tasks.
Using the setup module, we can see the information gathered in the TASK [Gathering Facts] step.
ansible -m setup web1
Ansible allows you to use the debug module to see STDOUT information from the playbook, such as variables set and tasks that are ran. The example below shows a playbook which runs a command
to list the contents of a directory and uses register
to a dir_contents variable. The debug module then returns the captured information in the variable to STDOUT.
---
vars:
path_to_app: "/var/www/html"
tasks:
- name: see directory contents
command: ls -al {{ path_to_app }}
register: dir_contents
- name: debug directory contents
debug:
msg: "{{ dir_contents }}"
I have updated the app-setup.yaml
file to include the variables and debug module, which now looks as follows:
# app-setup.yaml
---
- hosts: webservers
become: true
vars:
app_path: "/var/www/html"
tasks:
- name: Copy app files
copy:
src: ../index.php
dest: "{{ app_path }}"
mode: 0755
notify: restart apache
- name: Delete index.html
ansible.builtin.file:
path: "{{ app_path }}/index.html"
state: absent
notify: restart apache
- name: gather directory contents
command: ls -al {{ app_path }}
register: dir_content
- name: show directory contents
debug:
msg: "{{ dir_content }}"
handlers:
- name: restart apache
service: name=apache2 state=restarted
Below you will see the command ls -al /var/www/html
returning the index.php file on both web1 and web2.
Ansible Vault
Ansible Vault allows us to keep sensitive information - such as passwords or variables - in encrypted files rather than in plan text in a playbook.
The vault is password protected with the default cipher of AES256. I created one as follows
ansible-vault create vault/secret-vars.yaml
Enter the password twice.
Next you will be able to fill add variables in your vault. Here as an example I added:
tenant_id: "000aaa-111bbb-222ccc-333ddd"
tenant_name: "examplename.com"
It is essentially VIM so exit by pressing Esc then:
:wq
To edit the vault again use
ansible-vault edit vault/secret-vars.yaml
Then you can reference this vault in a playbook by specifying vars_files
and referencing it in a task
as follows:
vim playbooks/vault-example.yaml
The debug module will allow you to return them as you would any other variable, as long as you enter the vault password when running the playbook.
# vault-example.yaml
---
- hosts: loadbalancers
vars_files:
- ../vault/secret-vars.yaml
tasks:
- name: show tenant_id var
debug:
msg: "{{ tenant_id }}"
- name: show tenant var
debug:
msg: "{{ tenant_name }}"
Run the playbook and specify --ask-vault-pass
ansible-playbook playbooks/vault-example.yaml --ask-vault-pass
Ansible Roles
Ansible Roles allow you to create a framework to separate out (make more modular) each part of the configuration. E.g. variables, tasks, and templates in their self contained directory. It will break-up the configuration into files which can be re-used and are easier to modify.
I created a new directory to host the roles.
mkdir ~/ansible-roles
Create the Role
We can create a role for the two web servers with the following command:
ansible-galaxy role init ansible-roles/webservers
I then copied the following three files I’ve been using to configure the webservers to the following directories:
ansible-roles/webservers/app-setup.yaml
ansible-roles/webservers/hosts-dev
ansible-roles/webservers/files/index.php
The directory structure looks as follows:
~/ansible-roles/webservers
├── app-setup.yaml
├── hosts-dev
└── webservers
├── defaults
│ └── main.yml
├── files
│ └── index.php
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── README.md
├── tasks
│ └── main.yml
├── templates
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml
9 directories, 12 files
Configure the Role
With the app-setup.yaml, hosts-dev and index.php in the correct place I can now configure the rest of the files in the webservers role.
Copy the vars
content from app-setup.yaml
into ansible-roles/webservers/vars/main.yaml
The vars/main.yaml
file will look as follows:
---
# vars file for ansible-roles/webservers
app_path: "/var/www/html"
Then, copy the handlers
section from app-setup.yaml
into ansible-roles/webservers/handlers/main.yaml
The handlers/main.yaml
file will look as follows:
---
# handlers file for ansible-roles/webservers
- name: restart apache
service: name=apache2 state=restarted
Then, copy the tasks
section from app-setup.yaml
into ansible-roles/webservers/tasks/main.yaml
The tasks/main.yaml
file will look as follows:
---
# tasks file for ansible-roles/webservers
- name: Copy app files
copy:
src: ../files/index.php
dest: "{{ app_path }}"
mode: 0755
notify: restart apache
- name: Delete index.html
ansible.builtin.file:
path: "{{ app_path }}/index.html"
state: absent
notify: restart apache
- name: gather directory contents
command: ls -al {{ app_path }}
register: dir_content
- name: show directory contents
debug:
msg: "{{ dir_content }}"
And finally, replace the content of the ansible-roles/webservers/app-setup.yaml
with the following:
# app-setup.yaml
---
- hosts: webservers
become: true
roles:
- webservers
Run the Role Playbook
Now it’s time to run the new playbook from the ~/ansible-roles
directory
ansible-playbook ansible-roles/app-setup.yaml -K
I haven’t captured the output as it is exactly the same as previous runs of app-setup.yaml
That concludes this series of posts on Ansible.