Ansible Vault, Modules & Roles
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