How to print command output in Ansible when there is a condition? - ansible

My ansible code looks something like the following. Problem is this only works when my inventory has dev,qa, perf and prod servers. For example, if my inventory has only dev servers then it fails. Is there any way to avoid this failure ?
I tried changing both cmd_dev_out and cmd_qa_out to cmd_out but that did not help either.
- name: Execute
hosts: all
tasks:
- name: Execute against dev
shell: some command
register: cmd_dev_out
when: ( servers are dev )
- debug: msg="{{ cmd_dev_out }}"
- name: Execute against qa
shell: some command
register: cmd_qa_out
when: ( servers are qa )
- debug: msg="{{ cmd_qa_out }}"
....... More conditions below .....

The best would be to use a block in here to logically group the execution of the command and the print of the output of the said command.
Because, if there is no command run, well, obviously you won't get anything as a result.
This is an example using block
- name: Execute
hosts: all
tasks:
- block:
- name: Execute against dev
shell: 'echo "dev"'
register: cmd_dev_out
- debug:
msg: "{{ cmd_dev_out }}"
when: "'dev' in inventory_hostname"
- block:
- name: Execute against qa
shell: 'echo "qa"'
register: cmd_qa_out
- debug:
msg: "{{ cmd_qa_out }}"
when: "'qa' in inventory_hostname"
Mind that this means that the condition when is now tied to the block and both the command and the debug will be skipped when the condition is false.
Example of recap:
PLAY [localhost] **************************************************
TASK [Execute against dev] ****************************************
skipping: [localhost]
TASK [debug] ******************************************************
skipping: [localhost]
TASK [Execute against qa] *****************************************
changed: [localhost]
TASK [debug] ******************************************************
ok: [localhost] =>
msg:
changed: true
cmd: echo "qa"
delta: '0:00:00.002641'
end: '2023-01-25 20:51:04.049532'
failed: false
msg: ''
rc: 0
start: '2023-01-25 20:51:04.046891'
stderr: ''
stderr_lines: []
stdout: qa
stdout_lines:
- qa
Another, but maybe less elegant solution would be to use the default Jinja filter on your command result in case the command was skipped.
- name: Execute
hosts: all
tasks:
- name: Execute against dev
shell: 'echo "dev"'
register: cmd_dev_out
when: "'dev' in inventory_hostname"
- debug:
msg: "{{ cmd_dev_out | default('cmd_dev was skipped') }}"
- name: Execute against qa
shell: 'echo "qa"'
register: cmd_qa_out
when: "'qa' in inventory_hostname"
- debug:
msg: "{{ cmd_qa_out | default('cmd_qa was skipped') }}"

Figured out another solution. Its given below (add the when condition to the debug statement) :
- name: Execute
hosts: all
tasks:
- name: Execute against dev
shell: some command
register: cmd_dev_out
when: ( servers are dev )
- debug: msg="{{ cmd_dev_out }}"
when: ( servers are dev )
- name: Execute against qa
shell: some command
register: cmd_qa_out
when: ( servers are qa )
- debug: msg="{{ cmd_qa_out }}"
when: ( servers are qa )
....... More conditions below .....

Related

Registering variables from a looped task and conditionals for skipped tasks

I'm trying to figure out a port test task in a playbook. I have two different sets of ports that need to be checked, but only checked if the server hostname has dev/test/stg/eng in it. Then everything else will be considered a production host and the production ports will be checked.
Here is my playbook:
- name: Filebeat Firewall port check | Dev
wait_for:
host: "{{ item.name }}"
port: "{{ item.port }}"
state: started
delay: 0
timeout: 5
loop: "{{ dev_ports }}"
register: devresults
when: ansible_facts['hostname'] | regex_search('dev|tst|test|eng')
- name: Filebeat Firewall port check | Prod
wait_for:
host: "{{ item.name }}"
port: "{{ item.port }}"
state: started
delay: 0
timeout: 5
loop: "{{ prod_ports }}"
when: devresults is skipped
The first task runs as expected when ran against a production server (is skipped) and a development server (ports are checked). However, the second task is skipped when ran against a production server, as well as a development server (this should happen).
Basically, if the first task is skipped the second task should run, and if the first ran the second task should be skipped, this isn't happening. I'm not sure what I'm missing, I'm thinking its the when statement in the second task, or if the loop is causing my issue, any help would be welcomed.
Regarding your question
Basically, if the first task is skipped the second task should run, and if the first ran the second task should be skipped, this isn't happening.
I've setup a short first example
---
- hosts: localhost
become: false
gather_facts: false
tasks:
- name: Create result set
shell:
cmd: echo 'I am not skipped.'
register: result
when: not ansible_check_mode
- name: Show result when not skipped
debug:
var: result
when: not result.skipped | default('false') | bool
- name: Show result when skipped
debug:
var: result
when: result.skipped | default('false') | bool
and found it working when running with ansible-playbook skip.yml and ansible-playbook skip.yml --check.
Depending on the task and used modules, the reason seems to be when a task becomes skipped the result set is
TASK [Show result when skipped] **************************
ok: [localhost] =>
result:
changed: false
skip_reason: Conditional result was False
skipped: true
whereby when not skipped the result set is
TASK [Show result when not skipped] *********************
ok: [localhost] =>
result:
changed: true
cmd: echo 'I am not skipped.'
delta: '0:00:00.123456'
end: '2022-02-16 20:00:00.123456'
failed: false
rc: 0
start: '2022-02-16 20:00:00.000000'
stderr: ''
stderr_lines: []
stdout: I am not skipped.
stdout_lines:
- I am not skipped.
and don't has the skipped flag. Therefore you may introduce filter to Provide default values and to Force the data type.
Regarding your objection
"the example doesn't really applies as it isn't doing any loops"
you may have a look into the result set of the following second example with loop and Extended loop variables.
---
- hosts: localhost
become: false
gather_facts: false
tasks:
- name: Create result set
shell:
cmd: echo 'I am not skipped.'
register: result
when: item is even
loop: "{{ [1,2] }}"
loop_control:
extended: true
label: "{{ item }}"
- name: Show result
debug:
var: result
which produce an output of
TASK [Create result set] *******
changed: [localhost] => (item=2)
TASK [Show result] ******************************
ok: [localhost] =>
result:
changed: true
msg: All items completed
results:
- ansible_loop:
allitems:
- 1
- 2
first: true
index: 1
index0: 0
last: false
length: 2
nextitem: 2
revindex: 2
revindex0: 1
ansible_loop_var: item
changed: false
item: 1
skip_reason: Conditional result was False
skipped: true
- ansible_loop:
allitems:
- 1
- 2
first: false
index: 2
index0: 1
last: true
length: 2
previtem: 1
revindex: 1
revindex0: 0
ansible_loop_var: item
changed: true
cmd: echo 'I am not skipped.'
delta: '0:00:00.123456'
end: '2022-02-16 22:15:00.123456'
failed: false
invocation:
module_args:
_raw_params: echo 'I am not skipped.'
_uses_shell: true
argv: null
chdir: null
creates: null
executable: null
removes: null
stdin: null
stdin_add_newline: true
strip_empty_ends: true
warn: true
item: 2
rc: 0
start: '2022-02-16 22:15:00.000000'
stderr: ''
stderr_lines: []
stdout: I am not skipped.
stdout_lines:
- I am not skipped.
As you can see it is a data structure containing a list of items and it does contain the information about the first skipped item. To access it you may enhance the second example with
- name: Show result when skipped
debug:
msg: "{{ item.item }}"
when: item.skipped | default('false') | bool
loop: "{{ result.results }}"
loop_control:
extended: true
label: "{{ item }}"
and have a look into the output. It also works according the examples of Conditions based on registered variables.
- name: Show result when skipped
debug:
msg: "{{ item.item }}"
when: item is skipped
loop: "{{ result.results }}"
Further Reading
Troubleshoot Ansible Playbooks
Ansible - Check variable type
Discovering the data type

Ansible when condition registered from csv

I'm using csv file as ingest data for my playbooks, but im having trouble with my when condition. it's either both task will skipped or both task will be ok, my objective is if ansible see the string in when condition it will skipped for the specific instance.
here is my playbook
- name: "Read ingest file from CSV return a list"
community.general.read_csv:
path: sample.csv
register: ingest
- name: debug ingest
debug:
msg: "{{ item.AWS_ACCOUNT }}"
with_items:
- "{{ ingest.list }}"
register: account
- name: debug account
debug:
msg: "{{ account.results | map(attribute='msg') }}"
register: accountlist
- name:
become: yes
become_user: awx
delegate_to: localhost
environment: "{{ proxy_env }}"
block:
- name: "Assume role"
community.aws.sts_assume_role:
role_arn: "{{ item.ROLE_ARN }}"
role_session_name: "pm"
with_items:
- "{{ ingest.list }}"
register: assumed_role
when: "'aws-account-rnd' not in account.results | map(attribute='msg')"
here is the content of sample.csv
HOSTNAME
ENVIRONMENT
AWS_ACCOUNT
ROLE_ARN
test1
dev
aws-account-rnd
arn:aws:iam::XXXX1
test2
uat
aws-account-uat
arn:aws:iam::XXXX2
my objective is to skipped all items in the csv file with aws-acount-rnd
Your condition does not mention item so it will have the same result for all loop items.
Nothing you've shown requires the weird abuse of debug + register that you're doing, and it is in fact getting in your way.
- name: Read CSV file
community.general.read_csv:
path: sample.csv
register: ingest
- name: Assume role
community.aws.sts_assume_role:
role_arn: "{{ item.ROLE_ARN }}"
role_session_name: pm
delegate_to: localhost
become: true
become_user: awx
environment: "{{ proxy_env }}"
loop: "{{ ingest.list }}"
when: item.AWS_ACCOUNT != 'aws-account-rnd'
register: assumed_role
If you'll always only care about one match you can also do this without a loop or condition at all:
- name: Assume role
community.aws.sts_assume_role:
role_arn: "{{ ingest.list | rejectattr('AWS_ACCOUNT', '==', 'aws-account-rnd') | map(attribute='ROLE_ARN') | first }}"
role_session_name: pm
delegate_to: localhost
become: true
become_user: awx
environment: "{{ proxy_env }}"
register: assumed_role
my objective is to skipped all items in the csv file with aws-acount-rnd
The multiple debug you have with register, seems to be a long-winded approach IMHO.
A simple task to debug the Role ARN, only if the account does not match aws-acount-rnd.
- name: show ROLE_ARN when account not equals aws-account-rnd
debug:
var: item['ROLE_ARN']
loop: "{{ ingest.list }}"
when: item['AWS_ACCOUNT'] != 'aws-account-rnd'
This results in:
TASK [show ROLE_ARN when account not equals aws-account-rnd] **********************************************************************************************************************
skipping: [localhost] => (item={'HOSTNAME': 'test1', 'ENVIRONMENT': 'dev', 'AWS_ACCOUNT': 'aws-account-rnd', 'ROLE_ARN': 'arn:aws:iam:XXXX1'})
ok: [localhost] => (item={'HOSTNAME': 'test2', 'ENVIRONMENT': 'uat', 'AWS_ACCOUNT': 'aws-account-uat', 'ROLE_ARN': 'arn:aws:iam:XXXX2'}) => {
"ansible_loop_var": "item",
"item": {
"AWS_ACCOUNT": "aws-account-uat",
"ENVIRONMENT": "uat",
"HOSTNAME": "test2",
"ROLE_ARN": "arn:aws:iam:XXXX2"
},
"item['ROLE_ARN']": "arn:aws:iam:XXXX2"
}
The same logic can be used to pass the item.ROLE_ARN to community.aws.sts_assume_role task.

Getting changed/failed hosts list from a previous task | Ansible

All,
Example: If i've got 20 hosts for a playbook and running them with Serial:10, below shell command runs on 10 hosts at a time. Once done handler task is called, wherein the task which creates dict (_dict) doesn't give a dictionary output thus the second task - Failed host - failed with mentioned error.
- name: Run some shell command
shell: "echo 2 > /abcd/abcd.txt"
when: random condition is satisfied
register: update2
ignore_errors: yes
notify: abc_handler
- handler:
- name: abcd_handler
set_fact:
_dict: "{{ dict(ansible_play_hosts|zip(
ansible_play_hosts|map('extract', hostvars, 'update2'))) }}"
run_once: true
- name: Find failed hosts
set_fact:
_failed: "{{ _dict|dict2items|json_query('[?value.failed].key') }}"
run_once: true
Handler First task output:
"changed: false"
"ansible_facts": {
"_dict": "{u'host1': {'stderr_lines': [], u'changed': True,...u'host2':.....u'host10'}"
2nd handler task gives the mentioned error when the dict2items is run for above values.
Thank you.
Q: "List of hosts where a certain task executed, changed something, or got failed."
A: For example, the command makes no changes at test_11 changes the file at test_12, and fails at test_13
- hosts: test_11,test_12,test_13
tasks:
- shell:
cmd: "echo 2 > /tmp/test/abcd.txt"
creates: /tmp/test/abcd.txt
register: update1
ignore_errors: true
TASK [shell] ***********************************************************
changed: [test_12]
fatal: [test_13]: FAILED! => changed=true
cmd: echo 2 > /tmp/test/abcd.txt
delta: '0:00:00.045992'
end: '2021-04-25 23:22:31.623804'
msg: non-zero return code
rc: 2
start: '2021-04-25 23:22:31.577812'
stderr: '/bin/sh: cannot create /tmp/test/abcd.txt: Permission denied'
stderr_lines: <omitted>
stdout: ''
stdout_lines: <omitted>
...ignoring
ok: [test_11]
Let's create a dictionary with the data first, e.g.
- set_fact:
_dict: "{{ dict(ansible_play_hosts|
zip(ansible_play_hosts|
map('extract', hostvars, 'update1'))) }}"
run_once: true
gives
_dict:
test_11:
changed: false
cmd: echo 2 > /tmp/test/abcd.txt
failed: false
rc: 0
stdout: skipped, since /tmp/test/abcd.txt exists
stdout_lines:
- skipped, since /tmp/test/abcd.txt exists
test_12:
changed: true
cmd: echo 2 > /tmp/test/abcd.txt
delta: '0:00:00.032474'
end: '2021-04-25 23:14:36.361510'
failed: false
rc: 0
start: '2021-04-25 23:14:36.329036'
stderr: ''
stderr_lines: []
stdout: ''
stdout_lines: []
test_13:
changed: true
cmd: echo 2 > /tmp/test/abcd.txt
delta: '0:00:00.054980'
end: '2021-04-25 23:14:35.565811'
failed: true
msg: non-zero return code
rc: 2
start: '2021-04-25 23:14:35.510831'
stderr: '/bin/sh: cannot create /tmp/test/abcd.txt: Permission denied'
stderr_lines:
- '/bin/sh: cannot create /tmp/test/abcd.txt: Permission denied'
stdout: ''
stdout_lines: []
Note that test_11 is reported ok not skipped despite the registered variable showing "stdout: skipped, since /tmp/test/abcd.txt exists".
The analysis is now trivial, e.g.
- set_fact:
_failed: "{{ _dict|dict2items|json_query('[?value.failed].key') }}"
run_once: true
gives the list of the failed hosts
_failed:
- test_13
and the next task
- set_fact:
_changed: "{{ (_dict|dict2items|json_query('[?value.changed].key'))|
difference(_failed) }}"
_ok: "{{ _dict|dict2items|json_query('[?value.changed == `false`].key') }}"
run_once: true
gives
_changed:
- test_12
_ok:
- test_11
Note that
The failed hosts need to be subtracted from the changed hosts because failed hosts are also reported as changed.
There will be no registered variable if a task is skipped.
Serial
Split the playbook into 2 plays if serial is used. e.g.
shell> cat playbook.yml
- hosts: all
serial: 10
tasks:
- shell:
cmd: "echo 2 > /tmp/test/abcd.txt"
creates: /tmp/test/abcd.txt
register: update1
ignore_errors: true
- hosts: all
tasks:
- set_fact:
_dict: "{{ dict(ansible_play_hosts|zip(
ansible_play_hosts|map('extract', hostvars, 'update1'))) }}"
run_once: true
It seems you would like to get the hosts on which the command task (shown in the question) failed or changed, and then target them for some other tasks.
There are two things required for this:
If the command task fails, playbook execution will stop and hence none of the following tasks will run. So we need to add ignore_errors flag to the task
add_host module to create a new group of hosts when the task failed or changed
So finally tasks like below should do the trick:
- hosts: some_group
serial: 1
- name: update file count
shell: "echo 2 > /home/ec2-user/abcd.txt"
when:
- count.stdout == "1"
register: update1
ignore_errors: true
- name: conditionally add the hosts from current play hosts to a new group
add_host:
groups:
- new_group
host: "{{ ansible_hostname }}"
when: >
cmd_stat is failed or
cmd_stat is changed
# Then have a play targeting the new group
- hosts: new_group
tasks:
# Tasks to be performed
Though the use of serial might make the whole playbook run longer if there are lot of hosts.

How to display values of two different task register variables in ansible

Below is my playbook,
---
- hosts: all
tasks:
- name: asyn task 1 use time command to see diff use when hostname to get faster output
command: sleep 15
async: 2
poll: 0
register: result1
- name: asyn task
command: sleep 2
register: result2
- name: showing result1
debug:
var: result1
var1: result2
- name: debugging output
debug: msg=this is the {‌{ result1 }} and {‌{ result2 }}
# with_items:
# - {‌{ result1 }}
# - {‌{ result2 }}
getting below error,
changed: [vishwa]
TASK [showing result1] **************************************************************************************************
fatal: [rudra]: FAILED! => {"changed": false, "msg": "'var1' is not a valid option in debug"}
fatal: [arya]: FAILED! => {"changed": false, "msg": "'var1' is not a valid option in debug"}
fatal: [vishwa]: FAILED! => {"changed": false, "msg": "'var1' is not a valid option in debug"}
to retry, use: --limit #/home/admin/ansibledemo/asynch.retry
First point, debug module doesn't have an option var1 and so the error from showing result1 task. You probably have figured that out and written debugging output task with msg option.
This brings to the second point, registering status of an asynchronous task. Since you are using poll: 0, the task would be executing in asynchronous mode so the result may not be immediately available to the registered variable. Use async_status to check the result as described here. Also, in your scenario you should use async value more than sleep period.
An example,
- name: Wait for asynchronous job to end
async_status:
jid: '{{ result1.ansible_job_id }}'
register: job_result
until: job_result.finished
retries: 5
Maybe below playbook file could help you to fulfill your requirement. If not then I have some other alternatives too.
---
- hosts: all
gather_facts: false
name: "[ Debug Senario Test ]"
become: true
become_method: sudo
tasks:
- name: "[[ Command Task 1 ]]"
command: "echo Command Task 1"
register: register1
- name: "[[ Command Task 2 ]]"
command: "echo Command Task 2"
register: register2
- name: "[[ Display Registers ]]"
command: "echo {{ item.stdout }}"
with_items:
- "{{ register1 }}"
- "{{register2}}"
register: register3
- debug:
msg: "{{ register3 }}"
debug does not have a parameter var1, or var. You can't just create a parameter for a module. The error message is clear.
If you would like to store the result1 or result2 then use set_fact module.
You are executing a task in async running in the background. Async task follows a fire and forget execution. That task will be triggered and in running state while the other is being triggered. Use 2 https://docs.ansible.com/ansible/latest/modules/async_status_module.html
for fetching the result of the async task.
Ref:https://docs.ansible.com/ansible/2.7/user_guide/playbooks_async.html

When condition is just skipping

I have an Ansible playbook as shown below. The only problem is every time I run it is just skipping/ignoring the task.
Can you please help me figure out what is the problem?
- name: Register Host to Dynamic Inventory
hosts: localhost
gather_facts: false
tasks:
- add_host:
name: "{{ myhost }}"
- name: Enabling the services
hosts: "{{ myhost }}"
gather_facts: true
tasks:
- name: Make the service available persistently after a reboot for SLES11
command: systemctl enable after.local
with_items: "{{ ansible_distribution_major_version }}"
when: ansible_distribution_major_version == 11
- name: Make the service available persistently after a reboot for SLES12
command: systemctl enable after.local
with_items: "{{ ansible_distributioni_major_version }}"
when: ansible_distribution_major_version == 12
TASK [add_host] ****************************************************************03:22:06
changed: [localhost]
PLAY [Enabling the services] ***************************************************03:22:06
TASK [Gathering Facts] *********************************************************03:22:06
ok: [hostname]
TASK [Make the service available persistently after a reboot for SLES11] ******03:22:10
skipping: [hostname] => (item=12)
TASK [Make the service available persistently after a reboot for SLES12] ******03:22:10
skipping: [hostname]
The tasks get skipped because ansible_distribution_major_version is a string and you compare it to an integer value.
You should either fix your conditions to:
when: ansible_distribution_major_version == "12"
Or cast the value:
when: ansible_distribution_major_version | int == 12
Having fixed that, the remaining code makes little sense and will produce a syntax error.

Resources