Skip to main content

Ansible Vault, Modules & Roles

Shenzhen, China

Ansible Vault

Store secrets inside an encrypted vault file instead of adding them in clear text inside your playbooks:

ansible-vault create vault.yml

This command will create the vault file and ask you for a password. Once the password is assigned you can start adding secrets to the vault in YAML formatting - e.g. password: secretpassword. Exit the text editor by pressing escape followed by the three keys :wq. Once the file is closed it is encrypted and becomes unreadable:

cat vault.yml

$ANSIBLE_VAULT;1.1;AES256
34376565356336313537633532346238633365396666616564346638393133323033343830646635
3066336664366461323832333939303834303435313732650a306430343532343163666131666236
64383239376532386435303431353862373734363635653030663835383933633638353765646361
3335373030663461350a633662316437323239636663353136623231636533653038303465633531
64366139303963393534613639626430663639366165663731353736666363313134

To read the content of a vault file type use the view command and type in the password you used when creating the vault file:

ansible-vault view vault.yml

To use a playbook that contains a reference to an encrypted secret, you have to execute it with the following flag:

ansible-playbook my-playbook.yml --ask-vault-pass

Ansible Modules

In the previous step I already used a couple of modules like:

  • Apt Module
  • Service Module
  • Copy Module
  • User Module
  • Command Module

Setup Module

You can execute the Setup module ad hoc by running the following command on your host:

ansible 192.168.2.111 -m setup

It will return an extensive list with system hard- and software information from your host system.

File Module

ansible-doc file

Examples:

- name: Change file ownership, group and permissions
file:
path: /etc/foo.conf
owner: foo
group: foo
mode: '0644'

- name: Give insecure permissions to an existing file
file:
path: /work
owner: root
group: root
mode: '1777'

- name: Create a symbolic link
file:
src: /file/to/link/to
dest: /path/to/symlink
owner: foo
group: foo
state: link

- name: Create two hard links
file:
src: '/tmp/{{ item.src }}'
dest: '{{ item.dest }}'
state: hard
loop:
- { src: x, dest: y }
- { src: z, dest: k }

- name: Create a directory if it does not exist
file:
path: /etc/some_directory
state: directory
mode: '0755'

- name: Recursively change ownership of a directory
file:
path: /etc/foo
state: directory
recurse: yes
owner: foo
group: foo

- name: Remove file (delete file)
file:
path: /etc/foo.txt
state: absent

Shell Module

Execute shell commands on your host system through Ansible playbooks or ad hoc commands:

ansible 192.168.2.* -m shell -a "tail /var/log/nginx/access.log | grep 192.168.2.21"

Ansible Galaxy

Ansible Galaxy was built as a repository for roles, ansible-galaxy exists to aid in installing and creating them.

Directory Structure of an Ansible Project

We can use the init command to initialize a role:

ansible-galaxy init test

A role’s directory structure consists of defaults, vars, files, handlers, meta, tasks, and templates:

/opt/ansible/
└── roles
└── test
├── .travis.yml
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── README.md
├── tasks
│ └── main.yml
├── templates
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml

defaults

Within defaults, there is a main.yml file with the default variables used by a role. For example you can set user names, passwords or version numbers:

---
nginx_default_release: '1.18.0-6ubuntu2'

nginx_user: 'nginx'

vars

vars and defaults hold variables - but variables in vars have a higher priority. Variables in defaults have the lowest priority of any variables available.

files

files is where you put files that need to be added to the machine being provisioned, without modification. Most of the time, files in files are referenced by copy tasks.

handlers

handlers usually contain targets for notify directives, and are almost always associated with services.

meta

Meta data of an Ansible role consists of attributes such as author, supported platforms, and dependencies.

tasks

tasks hold a series of Ansible plays to install, configure, and run software.

templates

templates is similar to files except that templates support modification. Modifications are achieved through the Jinja2 templating language.

Downloading a Role

We can download ready to use roles for common problems from Ansible Galaxy:

ansible-galaxy install geerlingguy.docker
mv /root/.ansible/roles/geerlingguy.docker /opt/ansible/roles/docker

This will install the following:

 /opt/ansible/roles/docker
├── defaults
│ └── main.yml
├── handlers
│ └── main.yml
└── tasks
├── docker-compose.yml
├── docker-users.yml
├── main.yml
├── setup-Debian.yml
└── setup-RedHat.yml

Defaults

cat docker/defaults/main.yml
---
# Edition can be one of: 'ce' (Community Edition) or 'ee' (Enterprise Edition).
docker_edition: 'ce'
docker_package: 'docker-{{ docker_edition }}'
docker_package_state: present

# Service options.
docker_service_state: started
docker_service_enabled: true
docker_restart_handler_state: restarted

# Docker Compose options.
docker_install_compose: true
docker_compose_version: '1.26.0'
docker_compose_path: /usr/local/bin/docker-compose

# Used only for Debian/Ubuntu. Switch 'stable' to 'edge' if needed.
docker_apt_release_channel: stable
docker_apt_arch: amd64
docker_apt_repository: 'deb [arch={{ docker_apt_arch }}] https://download.docker.com/linux/{{ ansible_distribution | lower }} {{ ansible_distribution_release }} {{ docker_apt_release_channel }}'
docker_apt_ignore_key_error: true
docker_apt_gpg_key: https://download.docker.com/linux/{{ ansible_distribution | lower }}/gpg

# Used only for RedHat/CentOS/Fedora.
docker_yum_repo_url: https://download.docker.com/linux/{{ (ansible_distribution == "Fedora") | ternary("fedora","centos") }}/docker-{{ docker_edition }}.repo
docker_yum_repo_enable_edge: '0'
docker_yum_repo_enable_test: '0'
docker_yum_gpg_key: https://download.docker.com/linux/centos/gpg

# A list of users who will be added to the docker group.
docker_users: []

Handlers

cat docker/handlers/main.yml
---
- name: restart docker
service: 'name=docker state={{ docker_restart_handler_state }}'

Tasks

cat docker/tasks/main.yml
---
# Uninstall old Docker Versions, Install dependencies, Add Docker Repository to APT/YUM
- include_tasks: setup-RedHat.yml
when: ansible_os_family == 'RedHat'

- include_tasks: setup-Debian.yml
when: ansible_os_family == 'Debian'

- name: Install Docker.
package:
name: '{{ docker_package }}'
state: '{{ docker_package_state }}'
notify: restart docker

- name: Ensure Docker is started and enabled at boot.
service:
name: docker
state: '{{ docker_service_state }}'
enabled: '{{ docker_service_enabled }}'

- name: Ensure handlers are notified now to avoid firewall conflicts.
meta: flush_handlers

- include_tasks: docker-compose.yml
when: docker_install_compose | bool

- include_tasks: docker-users.yml
when: docker_users | length > 0

Troubleshooting

Ansible is using the wrong Python interpreter

An exception occurred during task execution. To see the full traceback, use -vvv. The error was: ImportError: No module named pkg_resources
failed: [test_server] (item={'name': 'setuptools'}) => {"ansible_loop_var": "item", "changed": false, "item": {"name": "setuptools"}, "msg": "Failed to import the required Python library (setuptools) on Tomcat's Python /usr/bin/python. Please read module documentation and install in the appropriate location. If the required library is installed, but Ansible is using the wrong Python interpreter, please consult the documentation on ansible_python_interpreter"}

Try re-running the command but add the interpreter explicitly:

ansible-playbook install_docker.yml -e 'ansible_python_interpreter=/usr/bin/python3'

When this solves your issue, add the information to your inventory:

# Example of setting a group of hosts to use Python3
[py3-hosts]
debian10
centos7

[py3-hosts:vars]
ansible_python_interpreter=/usr/bin/python3