Ansible: Create a conditional from the output of previous task - ansible

I want to run a task only if at the previous task it contains a specific output.
E.g. I am checking if the status is "running". If yes, then run a command
My playbook:
- name: 'Backup'
hosts: localhost
tasks:
-
name: 'CHeck_Status'
shell: tool ha status |grep -i Status
register: service_status
-
debug:
var: service_status.stdout_lines
-
name: 'Run ls'
shell: ls -ltr
when: service_status.stdout_lines == "Status*running"
register: output
-
debug:
var: output
The output of the command is
PLAY [Backup] ******************************************************************************************************************************************************
TASK [Gathering Facts] *****************************************************************************************************************************************************
ok: [localhost]
TASK [CHeck_Status] *****************************************************************************************************************************************************
changed: [localhos]
TASK [debug] ***************************************************************************************************************************************************************
ok: [localhost] => {
"service_status.stdout_lines": [
"Status: running",
"Status: running"
]
}
TASK [Run ls] **************************************************************************************************************************************************************
skipping: [localhost]
TASK [debug] ***************************************************************************************************************************************************************
ok: [localhost] => {
"output": {
"changed": false,
"skip_reason": "Conditional result was False",
"skipped": true
}
}
PLAY RECAP *****************************************************************************************************************************************************************
localhost : ok=4 changed=1 unreachable=0 failed=0
it always fails. Any help on how can I do the conditional for when?
Also if I try this conditional I get the below error:
The offending line appears to be:
shell: ls -ltr
when: service_status.stdout_lines == "'Status: running'"
^ here
Thank you

Test the item 'Status: running' is in the list, e.g.
- name: Run ls
shell: ls -ltr
register: output
when: "'Status: running' in service_status.stdout_lines"

Related

How to skip a template copy in Ansible if the file has a pattern?

Trying to only copy an Nginx config file if the destination file does not have a string in it.
I thought this would work:
- name: Copy nginx config file
template:
src: templates/nginx.conf
dest: /etc/nginx/sites-enabled/default
validate: grep -l 'managed by Certbot' %s
But this task fails if "managed by Certbot" isn't in the file and stops the playbook run.
How can I just skip the template copy if the destination file already has that pattern? Maybe there's a better way to get the same result?
Inspired from this other answer
You can check for the presence of a content in a file using the lineinfile module in check mode. Then you can use the result as a condition to your template task. The default in the condition is to cope with the case when the file does not exists and the found attribute is not in the registered result.
---
- name: Check for presence of "managed by Certbot" in file
lineinfile:
path: /etc/nginx/sites-enabled/default
regexp: ".*# managed by Certbot.*"
state: absent
check_mode: yes
changed_when: false
register: certbot_managed
- name: Copy nginx config file when not certbot managed
template:
src: templates/nginx.conf
dest: /etc/nginx/sites-enabled/default
when: certbot_managed.found | default(0) == 0
You could use the failed_when condition and base it on the fail message that validate generate — failed to validate — to act upon:
- name: Copy nginx config file
template:
src: templates/nginx.conf
dest: /etc/nginx/sites-enabled/default
validate: grep -l 'managed by Certbot' %s
failed_when:
- copy_config_file.failed
- copy_config_file.msg != 'failed to validate'
register: copy_config_file
Note: in when and *_when, having a list of conditions is like doing list.0 and list.1 and ...
Given the playbook:
- hosts: all
gather_facts: no
tasks:
- copy:
dest: templates/nginx.conf
content: "{{ content | default('some random content') }}"
- copy:
dest: /etc/nginx/sites-enabled/default
content: "blank"
- template:
src: templates/nginx.conf
dest: /etc/nginx/sites-enabled/default
validate: grep -l 'managed by Certbot' %s
failed_when:
- copy_config_file.failed
- copy_config_file.msg != 'failed to validate'
register: copy_config_file
- shell: cat templates/nginx.conf
register: template_content
failed_when: false
- shell: cat /etc/nginx/sites-enabled/default
register: file_content
failed_when: false
- debug:
var: template_content.stdout
- debug:
var: file_content.stdout
When run via:
ansible-playbook play.yml
It gives:
PLAY [all] *******************************************************************************************************
TASK [copy] ******************************************************************************************************
changed: [localhost]
TASK [copy] ******************************************************************************************************
changed: [localhost]
TASK [template] **************************************************************************************************
ok: [localhost]
TASK [shell] *****************************************************************************************************
changed: [localhost]
TASK [shell] *****************************************************************************************************
changed: [localhost]
TASK [debug] *****************************************************************************************************
ok: [localhost] => {
"template_content.stdout": "some random content"
}
TASK [debug] *****************************************************************************************************
ok: [localhost] => {
"file_content.stdout": "blank"
}
PLAY RECAP *******************************************************************************************************
localhost : ok=7 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Now, when run with
ansible-playbook play.yml -e "content='managed by Certbot\nsome other content'"
With an extra parameter to modify the content of the template, it gives:
PLAY [all] *******************************************************************************************************
TASK [copy] ******************************************************************************************************
ok: [localhost]
TASK [copy] ******************************************************************************************************
changed: [localhost]
TASK [template] **************************************************************************************************
changed: [localhost]
TASK [shell] *****************************************************************************************************
changed: [localhost]
TASK [shell] *****************************************************************************************************
changed: [localhost]
TASK [debug] *****************************************************************************************************
ok: [localhost] => {
"template_content.stdout": "managed by Certbot\nsome other content"
}
TASK [debug] *****************************************************************************************************
ok: [localhost] => {
"file_content.stdout": "managed by Certbot\nsome other content"
}
PLAY RECAP *******************************************************************************************************
localhost : ok=7 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

How to check few keywords in a string and assert if not present in Ansible tasks

I have a string like this
"3000 Native active Po121, Po123"
I need to check if 3000 is present and active is present in this string. If not assert
I wanted to use when command and set_fact. Check if the variable present and assert. (which I haven't finished). Right now I am just printing a message. This is not the good to way to do it. If I can assert directly when 3000 and active not present, that would be great.
Also another question about when, if it matches first condition, it prints the debug message. It should match both right as its an and?
var:
vlan_output: "3000 Native active Po121, Po123"
item={vlan_id: 3000, state: present}
I tried like this
- name: Validate vlan for delete
debug: msg="VLAN FAILED "
when: item.state == "present" and "item.vlan_id not in vlan_output"
We can directly use the when condition if "3000" and "active" is present in the output
I believe the vlan_id would be a registered variable so the value can be accessed using vlan_id.stdout.
The below play works with when and assert module of ansible
for assertion +ve:
command -->
ansible-playbook tmp.yml --extra-vars "vlan_output='3000 active'"
playbook -->
---
- hosts: localhost
tasks:
- debug:
msg: "Strings Matched"
when: vlan_output | search("3000") and vlan_output | search("active")
- debug:
var: vlan_output
- assert:
that:
- "'3000' in vlan_output"
- "'active' in vlan_output"
output -->
ok: [localhost] => {
"msg": "Strings Matched"
}
TASK [debug] *****************************************************************************************************************************
ok: [localhost] => {
"vlan_output": "3000 active"
}
TASK [assert] ****************************************************************************************************************************
ok: [localhost] => {
"changed": false,
"msg": "All assertions passed"
}
PLAY RECAP *******************************************************************************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0
for assertion -ve:
command -->
ansible-playbook tmp.yml --extra-vars "vlan_output='is'"
playbook -->
---
- hosts: localhost
tasks:
- debug:
msg: "Strings Matched"
when: vlan_output is not search("3000") and vlan_output is not search("active")
- debug:
var: vlan_output
- assert:
that:
- "'3000' not in vlan_output"
- "'active' not in vlan_output"
output -->
PLAY [localhost] *******************************************************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************************************************************
ok: [localhost]
TASK [debug] ***********************************************************************************************************************************************************
ok: [localhost] => {
"msg": "Strings Matched"
}
TASK [debug] ***********************************************************************************************************************************************************
ok: [localhost] => {
"vlan_output": "is"
}
TASK [assert] **********************************************************************************************************************************************************
ok: [localhost] => {
"changed": false,
"msg": "All assertions passed"
}
PLAY RECAP *************************************************************************************************************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0
There's a couple of issues here
Firstly, you should not quote "item.vlan_id not in vlan_output" - this is a string and will always evaluate to True.
Secondly, the not in test requires the operands to be type string (currently vlan_id is an integer).
You should see the behaviour you are looking for with these changes:
vars:
vlan_output: "3000 Native active Po121, Po123"
item:
vlan_id: "3000"
state: present
tasks:
- debug: msg="VLAN FAILED"
when: item.state == "present" and item.vlan_id not in vlan_output

How do I handle rollback in case of failure using handlers in Ansible?

I wrote below yml file which will install the SSM and cloudwatch agent but I want to rollback the installation in case of any failures during the installation. I tried use FAIL but not working..Please advise..
---
# tasks file for SSMAgnetInstall
- name: status check
command: systemctl status amazon-ssm-agent
register: s_status
- debug:
msg: "{{ s_status }}"
- name: Get CPU architecture
command: getconf LONG_BIT
register: cpu_arch
changed_when: False
check_mode: no
when: s_status.stdout == ""
ignore_errors: true
- name: Install rpm file for Redhat Family (Amazon Linux, RHEL, and CentOS) 32/64-bit
yum:
name: "https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_386/amazon-ssm-agent.rpm"
state: present
when: s_status.stdout == ""
become: yes
ignore_errors: true
- name: cloud status check
command: systemctl status amazon-cloudwatch-agent
register: cld_status
become: yes
- debug:
msg: "{{ cld_status }}"
- name: Register to cloud watch service
become: yes
become_user: root
service:
name: amazon-ssm-agent
enabled: yes
state: started
- name: copy the output to a local file
copy:
content: "{{ myshell_output.stdout }}"
dest: "/home/ansible/rama/output.txt"
delegate_to: localhost
You should have a look at the the documentation on blocks, more specifically the error handling part. This is the general idea with an oversimplified example, you will have to adapt to your specific case.
The test.yml playbook
---
- hosts: localhost
gather_facts: false
tasks:
- block:
- name: I am a task that can fail
debug:
msg: "I {{ gen_fail | default(false) | bool | ternary('failed', 'succeeded') }}"
failed_when: gen_fail | default(false) | bool
- name: I am a task that will never fail
debug:
msg: I succeeded
rescue:
- name: I am a task in a block played when a failure happens
debug:
msg: rescue task
always:
- name: I am a task always played whatever happens
debug:
msg: always task
Played normally (no fail)
$ ansible-playbook test.yml
PLAY [localhost] ************************************************************************
TASK [I am a task that can fail] ********************************************************
ok: [localhost] => {
"msg": "I succeeded"
}
TASK [I am a task that will never fail] *************************************************
ok: [localhost] => {
"msg": "I succeeded"
}
TASK [I am a task always played whatever happens] ***************************************
ok: [localhost] => {
"msg": "always task"
}
PLAY RECAP ******************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Played forcing a fail
$ ansible-playbook test.yml -e gen_fail=true
PLAY [localhost] ************************************************************************
TASK [I am a task that can fail] ********************************************************
fatal: [localhost]: FAILED! => {
"msg": "I failed"
}
TASK [I am a task in a block played when a failure happens] *****************************
ok: [localhost] => {
"msg": "rescue task"
}
TASK [I am a task always played whatever happens] ***************************************
ok: [localhost] => {
"msg": "always task"
}
PLAY RECAP ******************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=1 ignored=0

Registered fact does not work in "when" condition in Ansible

I have my playbook as below:
---
- hosts: myser
tasks:
- name: Checking.
win_command: mycommand
register: win_command_result
- set_fact:
myvar={{win_command_result.stdout | regex_search('\\d+')}}
register: myvar_result
- debug:
var: myvar_result.ansible_facts.ple
- name: Checking Condition
win_command: ipconfig
register: ipconfig
when: myvar_result.ansible_facts.ple < 5000
- debug:
var: ipconfig
And below is output.
I am getting two different values per server but the task Checking Condition gets skipped. Based on the value, for one server it should skip and for another it should execute.
PLAY [myser]
*******************************************************
TASK [Gathering Facts]
**************************************************
ok: [ser1]
ok: [ser2]
TASK [Checking]
****************************
changed: [ser1]
changed: [ser2]
TASK [set_fact]
*********************************************************
ok: [ser1]
ok: [ser2]
TASK [debug]
************************************************************
ok: [ser1] => {
"myvar_result.ansible_facts.ple": "232"
}
ok: [ser2] => {
"myvar_result.ansible_facts.ple": "378416"
}
TASK [Checking Condition]
**********************************************
skipping: [ser1]
skipping: [ser2]
TASK [debug]
************************************************************
ok: [ser1] => {
"ipconfig": {
"changed": false,
"skip_reason": "Conditional result was False",
"skipped": true
}
}
ok: [ser2] => {
"ipconfig": {
"changed": false,
"skip_reason": "Conditional result was False",
"skipped": true
}
}
PLAY RECAP
**************************************************************
ser2 : ok=5 changed=1 unreachable=0 failed=0
ser1 : ok=5 changed=1 unreachable=0 failed=0
I want to use myvar_result.ansible_facts.ple in when condition. So idea here is if myvar_result.ansible_facts.ple crosses value of 5000 then execute "checking name"
Am I missing something here? How to get it work?
It is working properly, but you are comparing a string with an integer.
As you can see in your output:
"myvar_result.ansible_facts.ple": "232"
"myvar_result.ansible_facts.ple": "378416"
your values are strings (as is any command result passed in stdout, as well as the output of the regex_search filter).
Cast them to integer before doing the comparison in the conditional:
- name: Checking Condition
win_command: ipconfig
register: ipconfig
when: myvar_result.ansible_facts.ple|int < 5000

ansible: how to associate two remote hosts with eachother, and share hostvars between them

Background information:
I need to dynamically set a variable on a set of hosts (web1) and then check the same on a different set of hosts. Once they match, I can perform further actions.
Code
My hosts file looks like this:
[web1]
web1.ttv.mydomain.com
[web1:vars]
primary_count=0
[web2]
web2.ttv.mydomain.com
[web2:vars]
secondary_count=0
[web]
web1
web2
And this is the playbook:
- hosts: web1
tasks:
- name: query primary servers
shell: psql -U widget widget -c 'SELECT COUNT(*) FROM test' -t
register: result
- set_fact: primary_count={{result.stdout}}
- hosts: web
tasks:
- name: retrieve variable from previous play
shell: echo hello
- debug: var=primary_count
This playbook produces the following results:
TASK [setup] *******************************************************************
ok: [web1.ttv.mydomain.com]
TASK [query primary servers] ****************************************************
changed: [web1.ttv.mydomain.com]
TASK [debug] *******************************************************************
ok: [web1.ttv.mydomain.com] => {
"primary_count": 0
}
TASK [set_fact] ****************************************************************
ok: [web1.ttv.mydomain.com]
PLAY ***************************************************************************
TASK [setup] *******************************************************************
ok: [web1.ttv.mydomain.com]
ok: [web2.ttv.mydomain.com]
TASK [retrieve variable from previous play] ************************************
changed: [web1.ttv.mydomain.com]
changed: [web2.ttv.mydomain.com]
TASK [debug] *******************************************************************
ok: [web2.ttv.mydomain.com] => {
"primary_count": "VARIABLE IS NOT DEFINED!"
}
ok: [web1.ttv.mydomain.com] => {
"primary_count": " 2"
}
Problem
Now I need a way to do the following in the second play:
run the same select statement on web2.ttv.mydomain.com
save the value to secondary_count variable
check if secondary_count matches the value of the "primary_count" on web1.mydomain.com. (Notice how right now, since I'm looping through more than just the web1 servers in play 2, I get an error about the "primary_count" not being defined on web2 servers.)
when the values match then restart various services on secondary
Questions:
How do I evaluate the "primary_count" variable on the web1 host with the matching web2 host name on? In the future my hosts file will look like this:
[web1]
web1.ttv.mydomain.com
web1.ttx.mydomain.com
[web2]
web2.ttv.mydomain.com
web2.ttx.mydomain.com
[web]
web1
web2
So I need to write some sort of an eval statement that does this:
(pseudocode)
while looping through ***ALL*** web servers
if primary_count on web1.ttv.mydomain.com matches secondary_count on web2.ttx.mydomain.com then
restart service x on web2.ttx.mydomain.com
else
wait a few seconds and repeat
end
end loop
I think the solution lies with my hosts / inventory file. Somehow I need this playbook to run on all web1 servers and all web2 servers... but I also need a way to associate web1.ttv with just web2.ttv and web1.ttx with just web2.ttx and so on.
I'm just learning ansible as I go along, so if this approach is entirely wrong, please let me know!
Thanks.
EDIT 1
On doing some research about group_vars, it looks like group_vars doesn't really help me because I still have the same problem. While looping through all web servers (play 2), the variables I set on web1 servers in play 1 are not visible from web2 servers.
EDIT 2:
- hosts: web1
tasks:
- name: query primary servers
shell: psql -U widget widget -c 'SELECT COUNT(*) FROM widget' -t
register: result
- local_action: shell echo {{ result.stdout }} > varacrossplay.txt
That fails on the local_action line with this error:
fatal: [web1.ttv.mydomain.com -> localhost]: FAILED! => {"changed": true, "cmd": "echo 2 > varacrossplay.txt", "delta": "0:00:00.001641", "end":
": "echo 2 > varacrossplay.txt", "_uses_shell": true, "chdir": null, "creates": null, "executable": null, "removes": null, "warn": true}, "mod
1: cannot create varacrossplay.txt: Permission denied", "stdout": "", "stdout_lines": [], "warnings": []}
Try with this example playbook:
[jenkins#batman ansible]$ cat testplaybook.yml
- hosts: web1
tasks:
- name: query primary servers
shell: echo "TEST"
register: result
- local_action: shell echo {{ result.stdout }} > varacrossplay.txt
- hosts: web
tasks:
- local_action: shell cat varacrossplay.txt
register: result
- set_fact: other_fact="{{ result.stdout }}"
- debug: var=other_fact
With my servers all works fine xD
[jenkins#batman ansible]$ ansible-playbook -i inventory testplaybook.yml
PLAY ***************************************************************************
TASK [setup] *******************************************************************
ok: [10.0.0.100]
TASK [query primary servers] ***************************************************
changed: [10.0.0.100]
TASK [command] *****************************************************************
changed: [10.0.0.100 -> localhost]
PLAY ***************************************************************************
TASK [setup] *******************************************************************
ok: [10.0.0.2]
ok: [10.0.0.1]
TASK [command] *****************************************************************
changed: [10.0.0.1 -> localhost]
changed: [10.0.0.2 -> localhost]
TASK [set_fact] ****************************************************************
ok: [10.0.0.1]
ok: [10.0.0.2]
TASK [debug] *******************************************************************
ok: [10.0.0.2] => {
"other_fact": "TEST"
}
ok: [10.0.0.1] => {
"other_fact": "TEST"
}
PLAY RECAP *********************************************************************
10.0.0.100 : ok=3 changed=2 unreachable=0 failed=0
10.0.0.1 : ok=4 changed=1 unreachable=0 failed=0
10.0.0.2 : ok=4 changed=1 unreachable=0 failed=0

Resources