Firstly we need to create a key that Ansible needs to use ssh-keygen
ssh-keygen -t rsa -b 4096 -C "this is the ansible key no passphrase"
You will be prompted to specify a file to save the key, it is recommended not to use the default
we used the following: ~/.ssh/ansible
Finally you need to copy the public key to the remote server an example is the following piece of code
ssh-copy-id -i ~/.ssh/ansible.pub [email protected]
In order to fetch information from a node we need to execute the following commands
ansible $host-alias -m setup
ansible server1 -m setup
These variables will retrieve variables related to a specific node. All variables are prefixed with the ansible_
substring
In order to limit the facts gathered a common practice is to use filters
ansible $host-alias -m setup -a 'filter=${filter_arg}'
ansible server -m setup -a 'filter=ipv4'
some useful filters are:
ansible_mem*
ansible_eth*
ansible_os*
Inside the inventory we declare the host information
vmware:
hosts: # Define hosts under this group
server1:
ansible_host: 192.168.128.133
ansible_user: ubuntu
Inside this file we declare configuration information for the ansible playbook
[defaults]
inventory = inventory.yaml
private_key_file = ~/.ssh/ansible
How to execute a playbook
ansible-playbook test_playbook.yaml
If you need to manually provide the sudo password during playbook execution, you can use the --ask-become-pass flag. This will prompt you for the password at the start of the playbook run.
ansible-playbook test_playbook.yaml --ask-become-pass
---
- name: one test playbook
hosts: vmware
become: yes
tasks:
- name: execute a hello world in shell
shell: echo "Hello Malaka"
Ansible Vault is a feature that allows you to encrypt sensitive data, such as passwords or API keys, and keep them safe when managing infrastructure with Ansible. You can create, edit, view, and use encrypted files with ansible-vault. Here’s a step-by-step example of how to use Ansible Vault.
Firstly we need to create the file that will store the secrets
ansible-vault create secrets.yaml
Edit an ecrypter vault file
ansible-vault edit secrets.yaml
View the contents of a vault file
ansible-vault view secrets.yaml
Encrypt secret from existing YAML file
ansible-vault encrypt myfile.yml
ansible-playbook test_playbook.yaml --ask-vault-pass
ansible-playbook test_playbook.yml --vault-password-file ~/.vault_password.txt
---
- name: one test playbook
hosts: vmware
become: yes
vars_files:
- secrets.yaml
tasks:
- name: execute a hello world in shell
shell: echo "Hello Malaka"
- name: run docker ps -a command
shell: docker ps -a
- name: print secrets.yaml
debug:
msg: this is the password {{dbpass}} {{dbuser}}
When you want to install packages, remove them or update cache apt module is the prefered way to do so
- name: one test playbook
hosts: vmware
become: yes
vars_files:
- secrets.yaml
tasks:
- name: execute a hello world in shell
shell: echo "Hello Malaka"
- name: run docker ps -a command
shell: docker ps -a
- name: print secrets.yaml
debug:
msg: this is the password {{dbpass}} {{dbuser}}
- name: update cache
apt:
update_cache: yes
Install one package
- name: update cache
apt:
name: apache2
state: latest
Install more than one packages in one play
- name: update cache
apt:
name:
- apache2
- jq
state: latest
In order to execute a scenario with different handling on the hosts we need to update our inventory.yaml we added an extra VM called client and the updated inventory right now is listed below:
webservers:
hosts: # Define hosts under this group
server1:
ansible_host: 192.168.128.133
ansible_user: ubuntu
my_pack: apache2
the_pack: jq
clients:
hosts:
server2:
ansible_host: 192.168.128.131
ansible_user: ubuntu
When we want to perform tasks for all hosts we need to identify the playbook using the all
notation.
- hosts: all
become: yes
If we want to select a specific group we need to identify the group name in the playbook
hosts: webservers
- hosts: webservers
become: yes
Below we put the playbook we created for our case. As you can notice in the same file we can have more than one playbooks.
pre_tasks
notation is used to indicate the tasks need to be executed before everything else.
Like Init commands
- hosts: all
become: yes
pre_tasks:
- name: show your distributions
debug:
msg: "Distro: {{ansible_facts['distribution']}} Architecture: {{ansible_facts['architecture']}}"
- hosts: webservers
become: yes
tasks:
- name: Say hello
debug:
msg: "Hello from webserver {{ansible_facts['user_id']}}@{{ansible_facts['all_ipv4_addresses']}}"
- name: install jq
apt:
name: jq
state: latest
- hosts: clients
become: yes
tasks:
- name: Say hi
debug:
msg: "Hi from client {{ansible_facts['user_id']}}@{{ansible_facts['all_ipv4_addresses']}}"
- name: install apache2
apt:
name: apache2
state: latest
How you put tags in ansible, check the following playbook
- hosts: webservers
become: yes
tags: hello,jq
tasks:
- name: Say hello
debug:
msg: "Hello from webserver {{ansible_facts['user_id']}}@{{ansible_facts['all_ipv4_addresses']}}"
- name: install jq
apt:
name: jq
state: latest
ansible-playbook --list-tags site.yaml
ansible-playbook --tags centos --ask-become-pass site.yaml
ansible-playbook --tags "apache,jq" --ask-become-pass site.yaml
To accomplish this task we need to create a folder called files
inside the folder files we need to create a file called default.html
with some dump content.
The next think that we need to do is to add a play that copies that file to a server. In particular
you can find the play to be added in site.yaml
below
- name: copy default html file for site
tags: apache,apache2,httpd
copy:
src: files/default.html
dest: /var/www/html/index.html
owner: root
group: root
mode: 0644
The unarchive
module is the ansible builtin module responsible to unzip compressed files, supports local but also remote sources
Before you start using unarchive you need to have installed unzip
package in your remote servers.
An example play that uses the unarchive module is the following.
- name: install unzip
apt:
name: unzip
state: latest
- name: unarchive terraform
unarchive:
src: https://releases.hashicorp.com/terraform/1.10.2/terraform_1.10.2_linux_amd64.zip
dest: /usr/local/bin
remote_url: yes
mode: 0755
owner: root
group: root
The main idea of boostraping and setting the remote virtual machines before starting the real interaction with Ansible is to have a dedicated ansible playbook to make some actions / plays like creating users, use the authorized key module to provide access through ssh keys and provide sudo access
For that reason we will create a playbook called bootstrap.yaml
To create a user called simone we created the play below:
- name: create user simone
user:
name: simone
groups: root
Then we need to add the ssh key for user simone to do so we need to add the following play. In our example we have put ansible.pub to test the play
- name: add simone to authorized keys
authorized_key:
user: simone
key: "${simone public key}"
In order to evaluate that the play was applied we type the following command
ssh -i /.ssh/ansible [email protected]
To transform simone as a sudo user we need to perform the following actions
Firstly we need to add to files folder a file named sudoer_simone
which will follows the sudo format and syntax
simone ALL=(ALL) NOPASSWD: ALL
then we need to user the copy module as follows
- name: make simone a sudoer
copy:
src: files/sudoer_simone
dest: /etc/sudoers.d/simone
owner: root
group: root
mode: 0444
In order to improve the structure of our playbooks Ansible has introduced roles.
Ansible roles use a folder format where taskbooks (tasks) are stored inside under a
folder named with this pattern ${role-name}/tasks/main.yaml
.
Example given lets say that we have 3 different roles named base
, webservers
, clients
it is obligatory to create the following structure
- playbook.yaml
- base/
- tasks/
- main.yaml
- tasks/
- webservers/
- tasks/
- main.yaml
- tasks/
- clients/
- tasks/
- main.yaml
- tasks/
And the playbook should have the following syntax
---
- hosts: all
become: yes
tags: always
pre_tasks:
- name: show your distributions
debug:
msg: "Distro: {{ansible_facts['distribution']}} Architecture: {{ansible_facts['architecture']}}"
- hosts: all
become: yes
tags: base
vars_files:
- vault.yaml
roles:
- base
- hosts: webservers
become: yes
tags: hello,jq
roles:
- webservers
- hosts: clients
become: yes
tags: hi,apache
roles:
- clients
Finally we execute the playbook normally as we did in the previous cases
ansible-playbook playbook --ask-become-pass --ask-vault-pass
If a taskbook is managing files then inside the role folder we need to create
a directory named files
and put the origin files inside there. Example given
- clients/
- files/
- index.html
- tasks/
- main.yaml
- files/
Host variables help to generalize the playbooks and have more control.
It is very easy to use. Firstly you need to create a folder named host_vars
and to create yaml files using your server alias for instance server1.yaml
The folder structure needs to the following:
- host_vars/
- server1.yaml
- server2.yaml
- ${alias-no3}.yaml
An example variable file for server1 server is like below:
who_am_i: darth-vader
rnd_pack: htop
and for server2
who_am_i: anakin
rnd_pack: wget
And we are able very easily to generalize our roles, playbooks, taskbooks like following (base):
---
- name: say your name
debug:
msg: "I am {{ansible_facts['all_ipv4_addresses']}} {{base_description}}"
- name: whoAmI
debug:
msg: "I am {{who_am_i}}"
- name: install a package
apt:
name: "{{rnd_pack}}"
state: latest
Handlers are used in order to automate triggering events inside roles. To use a Hanlder inside a role you need to create a folder inside the role folder named handlers and create a yaml file called main.yaml and put the tasks that will act as handlers. We need to use notify directive to call a handler inside a task by using the handler name
In particular you need to following the folder structure below:
- playbook
- roles/
- ${role-name}/
- handlers/
- main.yaml
- tasks/
- main.yaml
- handlers/
- ${role-name}/
We have the following tasks:
---
- name: say your name
debug:
msg: "I am {{ansible_facts['all_ipv4_addresses']}} {{base_description}}"
- name: whoAmI
debug:
msg: "I am {{who_am_i}}"
- name: install a package
apt:
name: "{{rnd_pack}}"
state: latest
- name: change a file
lineinfile:
path: /home/ubuntu/test
regexp: '^ServerAdmin'
line: ServerAdmin [email protected]
notify: restart_apache2
If the task change a file
will change a file and take effect then it will notify
the task restart_apache2
that lies inside handlers/
and it will restart apache2 service
- name: restart_apache2
service:
name: apache2
state: restarted
Templates are very important especially in the configuration phase, where you need to setup multiple servers. In order to use template someone needs to be keen with the jinja2 format as well as to have already used host variables. E.g. to have a created the host_vars/ folder and for each host / server to have already created the jinja2 file (.j2) extension.
An example follows:
We have already the following structure:
- test-handlers.yaml
- host_vars/
- server1.yaml
- server2.yaml
- roles/
- base/
- handlers/
- main.yaml
- tasks/
- main.yaml
- templates/
- test-template.j2
- handlers/
- base/
The template is the following and uses the jinja2 syntax
My favourite color is {{fav_color}}
My name is {{who_am_i}}
{% if age > 17 %}
You are an adult and you can have a light saber.
My saber is {{light_saber}}
{% else %}
You are a yungster you should not keep a light saber
My saber is {{light_saber}}
{% endif %}
Your enemies are:
{% for enemy in enemies %}
- {{enemy}}
{% endfor %}
The variables mentioned above are already included in the host variables of each host and in particular are the following
# server1
who_am_i: darth-vader
rnd_pack: htop
fav_color: dark
age: 100
light_saber: Naberie
enemies:
- Chewbacca
- Commander Fox
- Cudon Prax
- Count Dooku
- Anakin
# server2
who_am_i: anakin
rnd_pack: wget
fav_color: blue
age: 17
light_saber: Skywalker
enemies:
- Count Dooku
- Darth Vader
- Chancellor
- General Grievous
- The Sith
and the playbook uses the template
package to point out as a source the template
and dest the output formated using the template. In particular the test.conf
will get its contents from the test-template.j2 and after the creation of the file
a handler will print a message that the file created successfully
- name: use the template to generate test.conf
template:
src: templates/test-template.j2
dest: /etc/test.conf
notify: template_used