Ansible: More on Playbooks
In my previous post in this serie, I talked about what ansible playbook is and how to get started. In this post, I am going to talk about few things that make working with playbook more fun. At first, ansible playbook sounds basic and connot do much beside pingging hosts, installing packages, copying files, and checking services (at least the way presented it previously). But in ansible can do way more than that using a debugger, variables, and more.
Ansible Debug Module
Here is a simple example of a playbook that displays debug messages. It does not peroform any particular task but showing you how debug messages can be used in a playbook.
---
- name: A playbook with example debug messages
hosts: servers
become: 'yes'
tasks:
- name: Simple message
ansible.builtin.debug:
msg: This is a simple message
- name: Showing a multi-line message
ansible.builtin.debug:
msg:
- This is the first message
- This is the second message
- name: Showing host facts
ansible.builtin.debug:
msg:
- The node's hostname is {{ inventory_hostname }}
ansible.builtin.debug has 3 parameters.
-
msg: The debug message we want to show -
var: The variable we want to debug and show in logs we the playbook is ran. It cannot be used simultanuously withmsg. -
verbosity: An integer that represents the debug level when the playbook is ran. It can have a value between1and5(-vto-vvvvv). The default value is 0, meaning no verbosity.
---
- name: A playbook with example debug messages
hosts: servers
become: 'yes'
tasks:
- name: Debug a variable
ansible.builtin.debug:
var: inventory_hostname
- name: Debug a variable with verbosity of 3
ansible.builtin.debug:
msg: This is a message with a verbosity of 3
verbosity: 3
When we run the playbook without the verbosity flag, the messages with verbosity will not be logged. So, if we want to show all messages, we should run:
ansible-playbook my-playbook.yml -vvv
-vvv designate the verbosity level 3.
Defining variables in a playbook
We can define variables to store data we want to use in multiple places in a playbook. We define variable in the following way:
more code...
vars:
var1: Hello world
var2: 15
var3: true
var4:
- Apples
- Green
- 1.5
more code...
more code...
vars:
grouped:
var5: Hi there
var6: 30
var7: false
more code...
Debugging multiple variables
more code...
tasks:
- name: Display multiple variables
ansible.builtin.debug:
msg: |
var1: {{ var1 }}
var2: {{ var2 }}
var3: {{ var3 }}
var4: {{ var4 }}
more code...
more code...
tasks:
- name: Display multiple variables
ansible.builtin.debug:
var: grouped
more code...
Storing Outputs with Registers
Most ansible modules run and return a success or failure outputs. But, sometimes we want the resulting output of a task for later use. We can use a register to store that output. Here is an example:
---
- name: This is a playbook showcasing the use of registers
become: 'yes'
hosts: servers
tasks:
- name: Using a register to store output
ansible.builtin.shell: ssh -V
register: ssh_version
- name: Showing the ssh version
ansible.builtin.debug:
var: ssh_version
We store the output in the veriable in the register key and then we can use var or msg from the debug module to show.
Storing Data with Set_Fact Module
set_fact is used to store data associated to a node. It takes key: value pairs to store the variables. The key is the name of the variable and value is its value. For example:
---
- name: This is a playbook showcasing the use of set_fact
become: 'yes'
hosts: servers
tasks:
- name: Using a register to store output
ansible.builtin.shell: ssh -V
register: ssh_version
- ansible.builtin.set_fact:
ssh_version_number: "{{ ssh_version.stderr }}"
- ansible.builtin.debug:
var: ssh_version_number
Are you wondering why I used stderr instead of stdout or stdout_lines? That is ssh -V normal behavior.
Reading Variables at Runtime
For data we cannot hard code in the playbook, we can pass them to the playbook at runtime using the vars_prompt module.
---
- name: This is a playbook showcasing the use of vars_prompt
become: 'yes'
hosts: localhost
vars_prompt:
- name: description
prompt: Please provide the description
private: no
tasks:
- ansible.builtin.debug:
var: description
Date, Time, and Timestamp
ansible_date_time
ansible_date_time is coming from the facts. The playbook needs to gather the facts of the nodes. Otherwise it will be undefined.
---
- name: This is a playbook showcasing ansible_date_time
become: 'yes'
hosts: localhost
gather_facts: true
tasks:
- ansible.builtin.debug:
msg: "Datetime data {{ ansible_date_time }}"
- ansible.builtin.debug:
msg: "Date {{ ansible_date_time.date }}"
- ansible.builtin.debug:
msg: "Time {{ ansible_date_time.time }}"
- ansible.builtin.debug:
msg: "Timestamp {{ ansible_date_time.iso8601 }}"
Conditional Statements
when
A task with when conditional statement will only execute if the statement is true. For example:
---
- name: This is a playbook showcasing the use of `when` conditional statement
become: 'yes'
hosts: localhost
gather_facts: true
tasks:
- ansible.builtin.debug:
msg: "Date {{ ansible_date_time.date }}"
when: ansible_date_time is defined
The debug task will only run if ansible_date_time is define.
failed_when
---
- name: This is a playbook showcasing the use of `failed_when` conditional statement
become: 'yes'
hosts: localhost
gather_facts: false
tasks:
- name: Check connection
command: ping -c 4 mywebapp.local
register: ping_result
failed_when: false # never fail
In the above example, the task never fails. But when failed_when is given a statement that evaluate to true or false, the task will be kipped if the result of that statement is evaluated to false. Otherwise it will be executed.
changed_when
When ansible runs on a host, it may change something on that host. Sometime we want to define ourself when to considere the system as changed. That's what changed_when is for.
---
- name: This is a playbook showcasing the use of `changed_when` conditional statement
become: 'yes'
hosts: localhost
gather_facts: false
tasks:
- name: Check connection
command: ping -c 4 mywebapp.local
register: ping_result
failed_when: false # never fail
changed_when: false # never change anything
Handlers
Handlers are use to manage task dependencies. When we want to run a task only after another one has completed with changed=true we a handler. In the example below, we are only enabling nginx service after nginx is installed successfully.
---
- name: This is a playbook showcasing the use of handlers
become: 'yes'
hosts: servers
gather_facts: true
tasks:
- name: Install nginx
ansible.builtin.dnf:
name: nginx
state: present
notify:
- Enable nginx service
handlers:
- name: Enable nginx service
ansible.builtin.service:
name: nginx
enabled: true
state: restarted
Ansible Vault
The vault is where we keep our secrets secret. When we have confidential information that we want to keep secure, we use ansible vault. It allows a seemless encryption and decryption of sensitive data with a smooth integration with other ansible features suc has ansible playbook.
Encrypt a variable
ansible-vault encrypt-string "secret token string" --name "api_key"
Encrypt a file
ansible-vault encrypt myfile.txt
Dencrypt a file
ansible-vault decrypt myfile.txt
View content of encrypted file
ansible-vault view myfile.txt
Edit content of an encrypted file
ansible-vault edit myfile.txt
Change encrypted file encryption key
ansible-vault rekey myfile.txt
---
- name: This is a playbook showcasing the use of handlers
become: 'yes'
hosts: servers
gather_facts: true
vars:
my_secret: !vault |
$ANSIBLE_VAULT;1.1;AES256
15396363646563646365353331396364333839346632333964353531386132323034353163346432
6365313938653033613538366132353631626430373032620a653030326634376663613964366164
33373965656433346466326266363438376330386561386563353764646237643061613337323733
3633383934636236620a353132306539343363326437316539633432363436653437333866353534
3738
tasks:
- ansible.builtin.debug:
var: my_secret # never print secrets
no_log: true
The playbook will not be executed until we provide the key to decrypt the encrypted variable.
Conclusion
I am going to stop here for now but will come back later in other posts to talk more in about ansible playbook. Stay worm, everyone.