Ansible loop with multiple register value - automation

would you please help me with this problem:
I have a playbook with multiple tasks and each tasks contains loop and register for the output of the task. Last task is going to use lineinfile to create a csv report based on the previous registers. something like below:
- name: information
module:
xxxx: xxxx
xxxx: xxxxx
loop:
- xxxx
- xxxx
register: task1_info
- name: information
module:
xxxx: xxxx
xxxx: xxxxx
loop:
- xxxx
- xxxx
register: task2_info
- name: information
lineinfile:
path: xxxx
line: "{{ item.tags.Name }}, {{ item.lastName }}"
loop:
- task1_info.results
- task2_info.results
if i use only one register at the end it is working, but not loop through all registers. the other option is to write a task after each register which I don't think reasonable!!

I understand your use case that you like to append one list to an other or to merge two lists.
To do so you could use an approach like
---
- hosts: localhost
become: false
gather_facts: false
vars:
LIST_1:
- 1
- 2
- 3
LIST_2:
- A
- B
- C
tasks:
- name: Info
debug:
msg: "{{ item }}"
loop: "{{ LIST_1 + LIST_2 }}"
loop_control:
extended: true
label: "{{ansible_loop.index0 }}"
resulting into an output of
TASK [Info] ******************
ok: [localhost] => (item=0) =>
msg: 1
ok: [localhost] => (item=1) =>
msg: 2
ok: [localhost] => (item=2) =>
msg: 3
ok: [localhost] => (item=3) =>
msg: A
ok: [localhost] => (item=4) =>
msg: B
ok: [localhost] => (item=5) =>
msg: C
Credits to
Append list variable to another list in Ansible
Further Q&A
Combine two lists in Ansible when one list could be empty
Ansible: Merge two lists based on an attribute

Related

how to print last incremented value in set_fact ansible? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 12 days ago.
Improve this question
Here is my ansible playbook. I'm inputting a file to my playbook and if it finds keywords like "open" or "allowed" in the file, I'm asking it to increment the variable dbsuccesscount by 1.
- name: testing
set_fact: dbsuccescount={{ dbsuccescount | int + 1 }}
when: item | regex_search('OPEN') and item | regex_search('ALLOWED')
with_items: "{{ file_check.stdout_lines }}"
tags: dbstatus_success
- debug:
var: dbsuccescount
Increment works successfully like below (last success number is 107).
snippet of the output
ok: [localhost] => (item=OPEN,ALLOWED) => {
"ansible_facts": {
"dbsuccescount": "107"
},
"ansible_loop_var": "item",
"changed": false,
"item": "OPEN,ALLOWED"
However I would like to print this last value which is 107 from dbsuccesscount. How can we print/read it?
I tried running the below which doesn't work
debug: msg="{{ dbsuccescount }} | last "
expected result:
I want my output to look like below
the dbsucesscount is 107 --> which is the last incremented value
I think your code is correct as written. Here's a complete example:
- hosts: localhost
gather_facts: false
vars:
file_check:
stdout_lines:
- OPEN ALLOWED
- junk
- junk
- OPEN ALLOWED
- junk
- OPEN ALLOWED
tasks:
- name: testing
set_fact:
dbsuccesscount: "{{ dbsuccesscount | default(0) | int + 1 }}"
when: item | regex_search('OPEN') and item | regex_search('ALLOWED')
with_items: "{{ file_check.stdout_lines }}"
tags: dbstatus_success
- debug:
var: dbsuccesscount
Running this produces:
PLAY [localhost] ***************************************************************
TASK [testing] *****************************************************************
ok: [localhost] => (item=OPEN ALLOWED)
skipping: [localhost] => (item=junk)
skipping: [localhost] => (item=junk)
ok: [localhost] => (item=OPEN ALLOWED)
skipping: [localhost] => (item=junk)
ok: [localhost] => (item=OPEN ALLOWED)
TASK [debug] *******************************************************************
ok: [localhost] => {
"dbsuccesscount": "3"
}
PLAY RECAP *********************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
There are three matching lines, so the debug task is printing exactly what we expect.
Of course, you don't need a with_items loop to count the number of matching lines; you can use a filter instead:
- name: testing
set_fact:
dbsuccescount: "{{ file_check.stdout_lines | select('match', 'OPEN|ALLOWED') | length }}"
- debug:
var: dbsuccescount
This produces the same output.

Is there a way in Ansible to retrieve a variable located on a different host, to get it on the localhost

I have the following playbook:
- name: Some action on the workers
hosts: workers
gather_facts: false
run_once: true
tasks:
- name: Set var for server information
set_fact:
server_info:
"name": "winserver"
"os": "Microsoft Windows Server 2019 Datacenter"
- name: Some action on the localhost
hosts: localhost
gather_facts: false
run_once: true
tasks:
- name: Show script stdout
debug:
msg:
- "{{ server_info }}"
The hosts is actually a group of servers put in a group named workers (for example server1, server2 and server3), where just one is chosen (arbitrary) to run this task. Now I need to retrieve the information from this variable on the localhost, but as I don't know on which server the first task runs, I cannot explicitly reference it by using:
"{{ hostvars['server2']['server_info'] }}"
Does someone know if there is a way to retrieve this variable on the localhost?
Q: "I don't know on which server the first task runs."
A: It's irrelevant which server the first task runs on. The variable server_info will be declared on all of them. For example, given the inventory
shell> cat hosts
[workers]
server1
server2
server3
The playbook
- hosts: workers
gather_facts: false
run_once: true
tasks:
- set_fact:
server_info: winserver
- debug:
var: hostvars[item]['server_info']
loop: "{{ ansible_play_hosts_all }}"
gives
TASK [debug] *********************************************************
ok: [server1] => (item=server1) =>
ansible_loop_var: item
hostvars[item]['server_info']: winserver
item: server1
ok: [server1] => (item=server2) =>
ansible_loop_var: item
hostvars[item]['server_info']: winserver
item: server2
ok: [server1] => (item=server3) =>
ansible_loop_var: item
hostvars[item]['server_info']: winserver
item: server
You can pick any host you like. For example,
- hosts: localhost
gather_facts: false
run_once: true
tasks:
- debug:
var: hostvars.server2.server_info
gives
TASK [debug] ************************************************************
ok: [localhost] =>
hostvars.server2.server_info: winserver
I just found the answer to this question myself: through the use of
delegate_to: localhost
delegate_facts: true
This way the variable gets stored on the localhost.
- name: Some action on the workers
hosts: workers
gather_facts: false
run_once: true
tasks:
- name: Set var for server information
set_fact:
server_info:
"name": "winserver"
"os": "Microsoft Windows Server 2019 Datacenter"
delegate_to: localhost
delegate_facts: true
- name: Some action on the localhost
hosts: localhost
gather_facts: false
run_once: true
tasks:
- name: Show script stdout
debug:
msg:
- "{{ server_info }}"

Remove first line in 'stdout_lines'

I'm just a beginner and write some small playbook.
For now I get some little error.
- name: Facts
ios_command:
commands: show interface status
register: ios_comm_result
In this playbook, for beginner I get the list of interfaces and then in next task I get list with first position including column name.
- name: get a list of ports to modify
set_fact:
newlist: "{{ (newlist | default([])) + [item.split()[0]] }}"
with_items: "{{ ios_comm_result.stdout_lines }}"
Output:
'Port'
'fa0/1'
'gi0/2'
....
etc
How can I delete first line? Or where can I read about this?
Based on your requirement and the given comments about Accessing list elements with Slice notation ([1:]) and Jinja Filters (| reject()) I've setup an short test
---
- hosts: localhost
become: false
gather_facts: false
vars:
STDOUT_LINES: ['Port', 'fa0/1', 'gi0/2', 'etc/3']
tasks:
# Slice notation
- name: Show 'stdout_lines' from the 2nd line to the end
debug:
msg: "{{ item }}"
loop: "{{ STDOUT_LINES[1:] }}"
# Jinja2 filter
- name: Show 'stdout_lines' and drop any lines which match
debug:
msg: "{{ item }}"
loop: "{{ STDOUT_LINES | reject('match', '^Port') | list }}"
# Conditionals
- name: Show 'stdout_lines' but not the first one
debug:
msg: "{{ item }}"
when: ansible_loop.index != 1
loop: "{{ STDOUT_LINES }}"
loop_control:
extended: true
label: "{{ ansible_loop.index0 }}"
with some value added about Extended loop variables and using conditionals in loops.
All three options show the expected result.

How to alert via email in Ansible

I have setup a mail task in ansible to send emails if yum update is marked as 'changed'.
Here is my current working code:
- name: Send mail alert if updated
community.general.mail:
to:
- 'recipient1'
cc:
- 'recipient2'
subject: Update Alert
body: 'Ansible Tower Updates have been applied on the following system: {{ ansible_hostname }}'
sender: "ansible.updates#domain.com"
delegate_to: localhost
when: yum_update.changed
This works great, however, every system that gets updated per host group sends a separate email. Last night for instance I had a group of 20 servers update and received 20 separate emails. I'm aware of why this happens, but my question is how would I script this to add all the systems to one email? Is that even possible or should I just alert that the group was updated and inform teams of what servers are in each group? (I'd prefer not to take the second option)
Edit 1:
I have added the code suggested and am now unable to receive any emails. Here's the error message:
"msg": "The conditional check '_changed|length > 0' failed. The error was: error while evaluating conditional (_changed|length > 0): {{ hostvars|dict2items| selectattr('value.yum_update.changed')| map(attribute='key')|list }}: 'ansible.vars.hostvars.HostVarsVars object' has no attribute 'yum_update'\n\nThe error appears to be in '/tmp/bwrap_1073_o8ibkgrl/awx_1073_0eojw5px/project/yum-update-ent_template_servers.yml': line 22, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Send mail alert if updated\n ^ here\n",
I am also attaching my entire playbook for reference:
---
- name: Update enterprise template servers
hosts: ent_template_servers
tasks:
- name: Update all packages
yum:
name: '*'
state: latest
register: yum_update
- name: Reboot if needed
import_tasks: /usr/share/ansible/tasks/reboot-if-needed-centos.yml
- name: Kernel Cleanup
import_tasks: /usr/share/ansible/tasks/kernel-cleanup.yml
- debug:
var: yum_update.changed
- name: Send mail alert if updated
community.general.mail:
to:
- 'email#domain.com'
subject: Update Alert
body: |-
Updates have been applied on the following system(s):
{{ _changed }}
sender: "ansible.updates#domain.com"
delegate_to: localhost
run_once: true
when: _changed|length > 0
vars:
_changed: "{{ hostvars|dict2items|
selectattr('yum_update.changed')|
map(attribute='key')|list }}"
...
Ansible version is: 2.9.27
Ansible Tower version is: 3.8.3
Thanks in advance!
For example, the mail task below
- debug:
var: yum_update.changed
- community.general.mail:
sender: ansible
to: root
subject: Update Alert
body: |-
Updates have been applied to the following system:
{{ _changed }}
delegate_to: localhost
run_once: true
when: _changed|length > 0
vars:
_changed: "{{ hostvars|dict2items|
selectattr('value.yum_update.changed')|
map(attribute='key')|list }}"
TASK [debug] ***************************************************************
ok: [host01] =>
yum_update.changed: true
ok: [host02] =>
yum_update.changed: false
ok: [host03] =>
yum_update.changed: true
TASK [community.general.mail] **********************************************
ok: [host01 -> localhost]
will send
From: ansible#domain.com
To: root#domain.com
Cc:
Subject: Update Alert
Date: Wed, 09 Feb 2022 16:55:47 +0100
X-Mailer: Ansible mail module
Updates have been applied to the following system:
['host01', 'host03']
Remove the condition below if you want to receive also empty lists
when: _changed|length > 0
Debug
'ansible.vars.hostvars.HostVarsVars object' has no attribute 'yum_update'
Q: "What I could try?"
A: Some of the hosts are missing the variables yum_update. You can test it
- debug:
msg: "{{ hostvars|dict2items|
selectattr('value.yum_update.changed')|
map(attribute='key')|list }}"
run_once: true
Either make sure that the variable is defined on all hosts or use json_query. This filter tolerates missing attributes, e.g.
- debug:
msg: "{{ hostvars|dict2items|
json_query('[?value.yum_update.changed].key') }}"
run_once: true
Q: "The 'debug' task prior to the 'mail' task gives me the same output. But it fails when the 'mail' task is executed."
A: Minimize the code and isolate the problem. For example, in the code below you can see
Variable yum_update.changed is missing on host03
The filter json_query ignores this
The filter selectattr fails
- debug:
var: yum_update.changed
- debug:
msg: "{{ hostvars|dict2items|
json_query('[?value.yum_update.changed].key') }}"
run_once: true
- debug:
msg: "{{ hostvars|dict2items|
selectattr('value.yum_update.changed')|
map(attribute='key')|list }}"
run_once: true
gives
TASK [debug] **************************************************
ok: [host01] =>
yum_update.changed: true
ok: [host02] =>
yum_update.changed: false
ok: [host03] =>
yum_update.changed: VARIABLE IS NOT DEFINED!
TASK [debug] **************************************************
ok: [host01] =>
msg:
- host01
TASK [debug] **************************************************
fatal: [host01]: FAILED! =>
msg: |-
The task includes an option with an undefined variable.
The error was: 'ansible.vars.hostvars.HostVarsVars object'
has no attribute 'yum_update'
Both filters give the same results if all variables are present
TASK [debug] **************************************************
ok: [host01] =>
yum_update.changed: true
ok: [host02] =>
yum_update.changed: false
ok: [host03] =>
yum_update.changed: true
TASK [debug] **************************************************
ok: [host01] =>
msg:
- host01
- host03
TASK [debug] **************************************************
ok: [host01] =>
msg:
- host01
- host03

Saving a random choice string to a usable variable in ansible

Hoping someone can help me figure out what I feel should be a simple nested string problem. I have the following tasks to randomly choose a string, save it to a variable and print it:
tasks:
- name: Debug Section 1
debug:
msg: "{{ item }}"
with_random_choice:
- MY_CHOICE1
- MY_CHOICE2
register: choice
- name: Set result to a fact
set_fact:
THE_CHOICE: "{{ choice.results }}"
- name: Debug Section 3
debug:
msg: "{{ THE_CHOICE }}"
The results return with:
PLAY [Testing variable] **********************************************************************************************************************************************************************************************************************
TASK [Debug Section 1] ***********************************************************************************************************************************************************************************************************************ok: [localhost] => (item=MY_CHOICE1) => {
"msg": "MY_CHOICE1"
}
TASK [Set result to a fact] ******************************************************************************************************************************************************************************************************************ok: [localhost]
TASK [Debug Section 3] ***********************************************************************************************************************************************************************************************************************ok: [localhost] => {
"msg": [
{
"ansible_loop_var": "item",
"changed": false,
"failed": false,
"item": "MY_CHOICE1",
"msg": "MY_CHOICE1"
}
]
}
PLAY RECAP ***********************************************************************************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
I would like the variable THE_CHOICE to just return the item, but I can't seem to get it to work. In the set_fact section I've tried the following:
THE_CHOICE: "{{ choice.results['item'] }}"
THE_CHOICE: "{{ choice.results.item }}"
THE_CHOICE: "{{ choice['results']['item'] }}"
All attempts result in something to the effect of this:
"The task includes an option with an undefined variable. The error was: 'list object' has no attribute 'item'
Could anybody provide some insight into what I'm missing?
It would be simpler to use the random filter (since Ansible 1.6):
vars:
choices:
- MY_CHOICE1
- MY_CHOICE2
tasks:
- name: Set fact random
set_fact:
THE_CHOICE: "{{ choices | random }}"
About your original playbook, choice.results is a list, containing one result for each item of the loop. In your case, it only contains one result, because the with_random_choice loop only iterates once. So, in order to access your item, you must select the first result of the list with [0]:
- name: Set result to a fact
set_fact:
THE_CHOICE: "{{ choice.results[0].item }}"