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 setupThese 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: ubuntuInside this file we declare configuration information for the ansible playbook
[defaults]
inventory = inventory.yaml
private_key_file = ~/.ssh/ansibleHow 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.yamlEdit an ecrypter vault file
ansible-vault edit secrets.yamlView the contents of a vault file
ansible-vault view secrets.yamlEncrypt 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: yesInstall one package
- name: update cache
apt:
name: apache2
state: latestInstall more than one packages in one play
- name: update cache
apt:
name:
- apache2
- jq
state: latestIn 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: ubuntuWhen we want to perform tasks for all hosts we need to identify the playbook using the all notation.
- hosts: all
become: yesIf we want to select a specific group we need to identify the group name in the playbook
hosts: webservers
- hosts: webservers
become: yesBelow 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: latestHow 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: latestansible-playbook --list-tags site.yamlansible-playbook --tags centos --ask-become-pass site.yamlansible-playbook --tags "apache,jq" --ask-become-pass site.yamlTo 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: 0644The 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: rootThe 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: rootThen 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: 0444In 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:
- clientsFinally we execute the playbook normally as we did in the previous cases
ansible-playbook playbook --ask-become-pass --ask-vault-passIf 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: htopand for server2
who_am_i: anakin
rnd_pack: wgetAnd 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: latestHandlers 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_apache2If 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: restartedTemplates 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 Sithand 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