I found this to be helpful:
https://medium.com/opsops/anternative-way-to-handle-errors-in-ansible-245a066c340
In your task you want to register the task.
register: some_name
Then add ignore_errors: yes
Then use set_fact
to get each register attribute:
- set_fact:
success: '{{ not([e1, e2]|map(attribute="failed")|max) }}'
Then place this at the end of your block:
- name: Fail server build
command: >
bash scripts/test_file.sh
when: success == false
ignore_errors: yes
The block above would only be executed when success is false
. The key is using ignore_errors
and making a register. From the link I posted and from my testing the task attribute is registered if it fails or not.
Example output:
PLAY [localhost] ***********************************************************************************************
TASK [Gathering Facts] *****************************************************************************************
ok: [localhost]
TASK [Task 1 test] *********************************************************************************************
fatal: [localhost]: FAILED! => {"changed": true, "cmd": ["bash", "scripts/unknown_file.sh"], "delta": "0:00:00.004343", "end": "2021-10-20 14:20:59.320389", "msg": "non-zero return code", "rc": 127, "start": "2021-10-20 14:20:59.316046", "stderr": "bash: scripts/unknown_file.sh: No such file or directory", "stderr_lines": ["bash: scripts/unknown_file.sh: No such file or directory"], "stdout": "", "stdout_lines": []}
...ignoring
TASK [Task 2 test] *********************************************************************************************
changed: [localhost]
TASK [set_fact] ************************************************************************************************
ok: [localhost]
TASK [Fail server build] ***************************************************************************************
changed: [localhost]
TASK [debug] ***************************************************************************************************
ok: [localhost] => {
"success": false
}
PLAY RECAP *****************************************************************************************************
localhost : ok=6 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1
Ansible Error handling – In this lesson, you will learn the different ways of how to handle failures in Ansible tasks by using the ignore_errors, force_handlers, Ansible blocks, Ansible rescue, and Ansible always directives in a playbook.
Contents
- How Can You Handle Error In Ansible
- Specifying Task Failure Conditions
- Managing Changed Status
- Using Ansible Blocks
- Using Ansible Blocks With Rescue and Always Statement
How Can Error Handling Be Done In Ansible
Ansible plays and tasks are executed in the order they are defined in a playbook, and by default, if a task fails, the other tasks will not be executed in that order. However, this behavior can be changed with the use of a keyword called “ignore_errors: true“.
This keyword can be added to a play or a task as the case may be. If it is added to a play, it means that all the errors in the tasks associated to a play will be ignored. More so, if it is added to a task, it means all the errors in the task will be ignored.
Well, we learnt about handlers in one of our previous lessons, what about handlers? Yes, this is also applicable to handlers, handlers error can be handled by the keyword, “force_handlers:yes”.
If a task that is supposed to notify a handler fails, the handlers will not be executed. This behavior can also be changed by using the keyword, “force_handler: yes” .
As usual, let’s understand better with examples.
If we were to install the httpd package and the autofs package using a playbook, the name argument will consist of the “httpd” value and the “autofs” value.
Now, we are going to write a playbook, and we will intentionally make an error by making the name argument of autofs containing “autos“.
1. Create a playbook
[lisa@drdev1 ~]$ vim playbook7.yml
- name: Install basic package
hosts: hqdev1.tekneed.com
tasks:
- name: install autofs
yum:
name: autos
state: present
ignore_errors: true
- name: install httpd
yum:
name: httpd
state: present
2. Run the playbook
[lisa@drdev1 ~]$ ansible-playbook playbook7.yml
PLAY [Install basic package] ***********************************************************
........
This playbook we run should have resulted in an error or task failure, and the second task shouldn’t have run because the first task failed, but because we used the “ignore_errors” keyword, the task did not fail and the play run.
Also, remember that you can choose to use the “ignore_errors” keyword at a play level or at a task level. In our case, it was used at the task level.
Again, let’s see an example of how we can use the force_handlers keyword to forcefully run a task with handlers.
1. create a playbook
- name: restarting httpd using handlers
hosts: hqdev1.tekneed.com
force_handlers: yes
tasks:
- name: restart httpd
service:
name: httpd
state: restarted
notify: restart httpd
handlers:
- name: restart httpd
service:
name: httpd
state: restarted
The force_handler directive will always force handlers to run whether it is called or not. Note that the keyword can only be used at a play level
2. Run the playbook
[lisa@drdev1 ~]$ ansible-playbook playbook3.yml
PLAY [restarting httpd using handlers] ********************************
......
Because the force_handlers directive is set to yes, the handler will always run.
Specifying Task Failure conditions
Ansible may run a task/command successfully, however may be a failure due to the final result a user desires to get. In this sense, one can specify a condition for tasks to fail or in other words, you are at liberty to determine what a failure is.
Let’s see how this can be done by using the “failed_when” directive. The failed_when directive, from its word, simply means the task should fail when a condition is met or not met.
Let’s use the playbook below as an example,
[lisa@drdev1 ~]$ vim playbook5.yml
- name: Web page fetcher
hosts: hqdev1.tekneed.com
tasks:
- name: connect to website
uri:
url: https://tekneed.com
return_content: true
register: output
- name: verify content
debug:
msg: "verifying content"
failed_when:
- '"this content" not in output.content'
- '"other content" not in output.content'
This is what this playbook will do. The uri module will interact with the webserver and fetch the page, https://www.tekneed.com.
More so, With the true value for the return_content argument, the body of the response of https://tekneed.com will be returned as content, and output will be captured with the aid of the register directive.
For the second task, the content “verifying content” will be printed by the debug module with the aid of the msg argument, and with the “failed_when” directive, the task will fail when the string, “this content” and “other content” is not in the captured output.
Run the playbook
[lisa@drdev1 ~]$ ansible-playbook playbook5.yml
PLAY [Web page fetcher] ************************************************************
.......
Alternatively, the “fail” module can be used to specify when a task fails. This module can only be very useful if the “when” keyword is used to specify the exact failure condition.
Let’s use the same example we used above, but this time around use the “fail” module and “when” keyword
[lisa@drdev1 ~]$ vim playbook5.yml
- name: Web page fetcher
hosts: hqdev1.tekneed.com
tasks:
- name: connect to website
uri:
url: https://tekneed.com
return_content: true
register: output
- name: verify content
fail:
msg: "verifying content"
when:
- '"this content" not in output.content'
- '"other content" not in output.content'
Run the playbook
[lisa@drdev1 ~]$ ansible-playbook playbook5.yml
PLAY [Web page fetcher] ************************************************************
.......
Managing Changed Status
Managing a changed status can be useful in avoiding unexpected results while running a playbook. Some tasks may even report a changed status and nothing in the real sense has really changed. It could just be that information was retrieved.
In some cases, you may not want a task to result in a changed status. In this case, one needs to use the “changed_when: false” keyword. The playbook will only report “ok” or “failed” status, and will never report a changed status.
An example of such task can be seen below.
- name: copy in nginx conf
template: src=nginx.conf.j2
dest=/etc/nginx/nginx.conf
- name: validate nginx conf
command: nginx -t
changed_when: false
One can also specify a condition when a task should change, an example of such task is seen in the playbook below.
- name: Web page fetcher
hosts: hqdev1.tekneed.com
tasks:
- name: connect to website
uri:
url: https://tekneed.com
return_content: true
register: output
- name: verify content
fail:
msg: "verifying content"
changed_when: "'success' in output.stdout"
Using Ansible Blocks
Blocks are used to group tasks, specific tasks that are related, and can be very useful with a conditional statement.
If tasks are grouped conditionally, and the conditions is/are true, all the tasks will be executed. You should also know that block is a directive in Ansible and not a module, hence the block directive and the when directive will be at the same indentation level.
Let’s see how blocks can be used with examples.
create a playbook
[lisa@drdev1 ~]$ vim playbook8.yml
- name: setting up httpd
hosts: localhost
tasks:
- name: Install start and enable httpd
block:
- name: install httpd
yum:
name: httpd
state: present
- name: start and enable httpd
service:
name: httpd
state: started
enabled: true
when: ansible_distribution == "Red Hat"
This playbook will group only two tasks in a block(install and enable httpd). The first task name is “install httpd” while the second task name is “start and enable httpd“. These tasks will only execute if the condition is true, which is, the OS ansible will execute against is/are Red Hat.
Run the playbook
[lisa@drdev1 ~]$ ansible-playbook playbook8.yml
PLAY [setting up httpd] ********************************************************
.......
With this kind of playbook, if the condition fails, other tasks will not be executed. Let’s see how we can use block with rescue and always if we don’t want this type of condition.
Using Ansible block With rescue and always Statement
Apart from blocks being used to group different tasks, they can also be used specifically for error handling with the rescue keyword.
It works in a way that; if a task that is defined in a block fails, the tasks defined in the rescue section will be executed. This is also similar to the ignore_errors keyword.
More so, there is also an always section, this section will always run either the task fails or not. This is also similar to the ignore_errors keyword.
Let’s understand better with examples.
create a playbook.
[lisa@drdev1 ~]$ vim playbook9.yml
- name: setting up httpd
hosts: localhost
tasks:
- name: Install the latest httpd and restart
block:
- name: install httpd
yum:
name: htt
state: latest
rescue:
- name: restart httpd
service:
name: httpd
state: started
- name: Install autofs
yum:
name: autofs
always:
- name: restart autofs
service:
name: autofs
state: started
This playbook will group four tasks in a block(install the latest httpd and restart).
The first task in the block section will fail because the name of the package is incorrect. However, the second and third tasks will run because they are in the rescue section. More so, the fourth task will run because it is in the always section.
Note that you can have as many tasks you want in the block section, rescue section or always section
Run the playbook
[lisa@drdev1 ~]$ ansible-playbook playbook9.yml
PLAY [setting up httpd] *********************************************************
......
Class Activity
create a playbook that contains 1 play with tasks using block, always and rescue statements
If you like this article, you can support us by
1. sharing this article.
2. Buying the article writer a coffee (click here to buy a coffee)
3. Donating to push our project to the next level. (click here to donate)
If you need personal training, send an email to info@tekneed.com
Click To Watch Video On Ansible Error Handling
RHCE EX294 Exam Practice Question On Ansible Error Handling
Suggested: Managing Layered Storage With Stratis – Video
Your feedback is welcomed. If you love others, you will share with others
In ansible if anyone of the task fails then it will stop the the entire execution of playbook or role.
So to avoid this problem we use ingnore_errors=True
ignore_errors=True
Error Handling In ansible Playbooks
If you mention ignore_errors=true at the end of the task , if the task fails also still the playbook or role will run. It will execute the next task without taking care of task success or failure. If the task failure also it will go for next task and if the task success also it will go for execution next task.
So if you mention ignore_errors=true in any task ansible will go for execution of next task with out taking care of the task result.
Ex:
Playbook or role:
[root@localhost ~]# cat ignore_errors.yml --- - hosts: localhost tasks: - name: Stop httpd if already running service: name=httkd state=stopped ignore_errors: True - name: Stop httpd if already running service: name=httpd state=stopped
Output Log:
[root@localhost ~]# ansible-playbook ignore_errors.yml PLAY [localhost] *************************************************************** TASK [Gathering Facts] ********************************************************* ok: [localhost] TASK [Stop httpd if already running] ********************************* fatal: [localhost]: FAILED! => {"changed": false, "msg": "Could not find the requested service httkd: host"} ...ignoring TASK [Stop httpd if already running] ********************************* ok: [localhost] PLAY RECAP ********************************************************************* localhost : ok=3 changed=0 unreachable=0 failed=0
here you can see that first task is failed but still ansible is executing second task and the playbook is not failed.
so here you can see in the log first task it is ignoring errors. and its going for second task.
ignore_errors=False
by default ansible will consider task as ignore_errors=False so no need to mention this, but mention ignore_errors=True depends on your requirements.
- ansible ignore errors
- Error Handling In Playbooks
- Error Handling In ansible Playbooks
- ansible ignore errors true
- ignore errors false
- playbook ignore errors ansible
- Error Handling In ansible Playbooks
- ansible role ignore errors
- ignore_errors
I’m trying to ignore a particular error message in playbook output is having below error message.
fatal: [192.168.0.1]: FAILED! => {«changed»: false, «failed»: true, «msg»: «Sending passwords in plain text without SSL/TLS is extremely insecure.. Query == CHANGE MASTER TO [‘MASTER_HOST=%(master_host)s’, ‘MASTER_USER=%(master_user)s’, ‘MASTER_PASSWORD=%(master_password)s’, ‘MASTER_LOG_FILE=%(master_log_file)s’, ‘MASTER_LOG_POS=%(master_log_pos)s’]»}
> Task: > - name: Setup Replication become: true mysql_replication: > login_host: "{{ slave_ip }}" > login_user: "user" > login_password: "***" > mode: changemaster > master_host: "{{ master_ip }}" > master_log_file: mysql-bin.000001 > master_log_pos: 107 > master_user: "{{ mysql_replicator_user }}" > master_password: "{{ mysql_replicator_password }}"
Any luck? how to achieve this?
EDITED: Reply to Marco Answer —
well that is the problem here, I would not know what error I might get. But I’m sure that if err msg contains «Sending passwords in plain text without SSL» then ignore if not and anyother error then don’t ignore. To explain in simple «Throw exception if error msg doesn’t contain -> ‘null’ or SSL.»
asked Apr 3, 2017 at 10:56
mannojmannoj
2311 gold badge2 silver badges6 bronze badges
There are a couple of options regarding error handling in Ansible. You could use ignore_errors: yes
attribute on your task. If you don’t want to ignore all errors, you can specify what exactly constitutes an error using something like:
- name: task name
module: arguments ...
register: out
failed_when: 'error message' in out.stderr
If you want to add more complex failure checks, you can split error handling off into separate task like this:
- name: test
shell: echo error; exit 123
register: out
ignore_errors: yes
- fail: msg="{{ out.stdout }}"
when: "out.rc != 0 and 'error' not in out.stdout"
In this example first task fails with return code 123 and prints «error» on it’s standard output. This will be registered, but ignored. Second task analyzes output values and fails only if return code is different than zero AND standard output does NOT contain string «error».
You can read more details in Ansible documentation: https://docs.ansible.com/ansible/playbooks_error_handling.html
answered Apr 3, 2017 at 11:35
2
I really wish ignore_errors wasn’t simple yes/no check. That would’ve make our life much more simpler. Recently I discovered this brilliant trick. With this method it’s possible to ignore certain known outputs. In this example we’re looking for Sending passwords in plain text without SSL
text in registered variable. If we find the text, it’ll evaluate to 0. Then we’ll compare it to -1
which is return code of find function if string not found. Since 0 == -1
will evaluate to False
ultimately our condition will be failed_when: False
On the other hand if string is not found, expression will be -1 == -1
=> failed_when: True
Be careful with this approach as you’ll be overriding default fail behavior of the module. So it’s best to combine with multiple conditions like exit code.
- name: Risky task
command: echo "Sending passwords in plain text without SSL"
register: result
changed_when: false
failed_when:
# returns -1 if string not found
- result.stdout.find("Sending passwords in plain text without SSL") == -1
# best to combine additional checks like return code
- result.rc != 0
I guess you could’ve use something like result.stdout.find("Sending passwords in plain text without SSL") != 0
or '"Sending passwords in plain text without SSL" not in result.stdout'
, but double negation makes it kinda harder to understand.
answered Aug 23, 2022 at 9:19
Shinebayar GShinebayar G
1931 gold badge1 silver badge4 bronze badges
playbook.yml пытаемся установить не существующий пакет treeee и выводим echo
— — name: Error find and control hosts: all become: yes tasks: — name: Tasks number1 yum: name=treeee state=present — name: Tasks number2 shell: echo Hellow world! — name: Tasks number3 shell: echo Privet Man! ... |
playbook.yml ignore_errors игнорим ошибки и выполняем следующие таски
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
— — name: Error find and control hosts: all become: yes tasks: — name: Tasks number1 yum: name=treeee state=present ignore_errors: yes — name: Tasks number2 shell: echo Hellow world! — name: Tasks number3 shell: echo Privet Man! ... |
playbook.yml дебаг ignore_errors
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
— — name: Error find and control hosts: all become: yes tasks: — name: Tasks number1 yum: name=treeee state=present ignore_errors: yes — name: Tasks number2 shell: ls —la /var/ register: results — debug: var: results — name: Tasks number3 shell: echo Privet Man! ... |
playbook.yml failed_when
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
— — name: Test ansible hosts: all become: yes tasks: — name: Tasks number1 yum: name=treeee state=present ignore_errors: yes — name: Tasks number2 shell: echo Hellow world register: results failed_when: «‘world’ in results.stdout» — debug: var: results — name: Tasks number3 shell: echo Privet Man! ... |
playbook.yml failed_when result.rc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
— — name: Test ansible hosts: all become: yes tasks: — name: Tasks number1 yum: name=treeee state=present ignore_errors: yes — name: Tasks number2 shell: echo Hellow world register: results failed_when: result.rc == 0 # failed_when: «‘world’ in results.stdout» — debug: var: results — name: Tasks number3 shell: echo Privet Man! ... |
playbook.yml выполнится только там где есть file1.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
— — name: Test ansible hosts: all become: yes tasks: — name: Tasks number1 yum: name=treeee state=present ignore_errors: yes — name: Tasks number2 shell: cat /home/secret/file1.txt register: results — debug: var: results — name: Tasks number3 shell: echo Privet Man! ... |
playbook.yml any_errors_fatal
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
— — name: Test ansible hosts: all any_errors_fatal : true become: yes tasks: — name: Tasks number1 yum: name=treeee state=present ignore_errors: yes — name: Tasks number2 shell: cat /home/secret/file71.txt register: results — debug: var: results — name: Tasks number3 shell: echo Privet Man! ... |