Looping tasks in Ansible - loops

I have the following tasks in Ansible:
- name: Getting Job Info
uri:
url: "https://{{ hostname }}/job/JobService"
method: POST
user: "{{ username }}"
password: "{{ password }}"
body: "{{ lookup( 'template' , 'jobInfo.xml.j2' ) }}"
status_code: 200
validate_certs: false
headers:
soapaction: "getJobInfoVO"
return_content: true
register: job_status_soap
- name: Converting Job Info response to JSON
set_fact:
job_status_json: "{{ job_status_soap.content | xml2json }}"
- name: Setting Job Status
set_fact:
job_status: "{{ job_status_json['soapenv:Envelope']['soapenv:Body']['multiRef'][0]['status']['#text'] }}"
- debug: msg="{{ job_status }}"
As you can see, it makes a web service call, converts the SOAP response to JSON and then extracts the relevant value (an integer) and sets it as a fact. I need to repeat this block of code until the aforementioned integer value is equal to certain integer.
Any ideas on how I might approach this task?

This would have been easy to implement using until to execute ansible do-until loop if the response was in JSON format.
But here's a working example with xml response.
Pre-requisites: (Make sure you have the python dependencies installed on your ansible host)
1) Using custom xml_to_json filter.
2) Using json_query filter
P.S. Executing following play will not trigger retry as the until contion is already met.
If you want to test if retry is working then change the until condition or the value of tag in the request header.
---
- name: Retry url until xml content equals certain value
hosts: 127.0.0.1
connection: local
become_user: root
become: yes
tasks:
- name: Check job status
uri:
url: "https://httpbin.org/anything"
method: GET
timeout: 10
validate_certs: no
headers:
content: "<envelope><body><request><status>2</status></request></body></envelope>"
register: get_search_job_status_response
until: get_search_job_status_response.json.headers.Content|from_xml|json_query('envelope.body.request.status')|int == 2
retries: 2
delay: 2
...

Related

Ansible loop using ansible_hostnames

I'm trying to update a configuration file for a NiFi deployment, the inital deployment configuration needs to include the nodes to allow HTTPS connections to be established between them.
I have an ansible tasks that makes the required structural changes to the configuration files, but I can't seem to get the right details inserted.
- name: Add each host to the authorizers.xml
lineinfile:
path: /opt/nifi/conf/authorizers.xml
line: "<property name=\"Node Identity {{ item }}\">CN={{ item }}, OU=NiFi</property>"
insertafter: <!--accessPolicyProvider Node Identities-->
loop: "{{ query('inventory_hostnames', 'nifi') }}"
This puts the ip addresses for the hosts, and I need to get the ansible_hostname for each node instead.
I've played around with ansible_play_batch and loop: "{{ groups['nifi'] }}" but I'm getting the result, outputting the ip addresses instead of the short hostnames each time.
The short hostnames are not stored in my ansible configuration anywhere, they are (if I understand correctly) determined at run time via the gathering facts process. I'd really like to not have to put the node names into a list variable.
Q: "Get the ansible_hostname for each node"
A: Given the inventory
shell> cat hosts
[nifi]
10.1.0.51
10.1.0.52
The playbook below
- hosts: nifi
tasks:
- debug:
var: ansible_hostname
gives (abridged)
ok: [10.1.0.51] =>
ansible_hostname: test_01
ok: [10.1.0.52] =>
ansible_hostname: test_02
It's possible to iterate the hosts in the group and get ansible_hostname from the hostvars. For example, delegate_to localhost and run_once
- debug:
msg: "{{ hostvars[item].ansible_hostname }}"
loop: "{{ groups.nifi }}"
delegate_to: localhost
run_once: true
gives
ok: [10.1.0.51 -> localhost] => (item=10.1.0.51) =>
msg: test_01
ok: [10.1.0.51 -> localhost] => (item=10.1.0.52) =>
msg: test_02

How to perform Ansible task based on passing a when condition while iterating over a with_item debug list?

I am trying to perform a API POST based on a when condition over a with_item list of users. The when condition is gathered from a API GET, then stored as a debug variable.
Problem: It seems the debug variable is not iterating properly, and API POST tasks follow the first result only. This results in either trying to perform a POST on all items in with_items list, or skipping them all.
I dont understand if each "request_ad_user" is creating a new variable, and how they are iterated over the "when" condition. What am I missing?
Here is my code:
- name: Add a users to univention AD server.
hosts: localhost
tasks:
- name: Include user to add as variable
include_vars:
file: users.yaml
name: users
- name: Check if AD users exist (object DN)
uri:
url: https://10.10.10.10/univention/udm/users/user/uid%3D{{item.username}}%2Ccn%3Dusers%2Cdc%3Dcybertax%2Cdc%3Dcso%2Cdc%3Dcom
user: admin
password: "{{users.adminpw}}"
validate_certs: no
return_content: yes
status_code: 200,404
method: GET
timeout: 10
with_items:
- "{{users.user}}"
register: request_ad_user
- name: debug univention user object DN request
debug:
var: request_ad_user
- name: Add AD user accounts
uri:
url: https://10.10.10.10/univention/udm/users/user/
user: admindh
password: "{{users.vcenterPassword}}"
validate_certs: no
return_content: yes
status_code: 201
method: POST
body: "{\"uuid\": \"string\", \"uri\": \"https://10.104.8.110/univention/udm/users/user/uid={{item.username}},dc=cybertax,dc=cso,dc=com\", \"options\": {\"pki\": false}, \"policies\": {\"pol
body_format: json
when:
- request_ad_user.results[0].status == 404
with_items:
- "{{users.user}}"
Try adding a loop index, and use that index in the when clause:
- name: Add AD user accounts
uri:
url: https://10.10.10.10/univention/udm/users/user/
user: admindh
password: "{{users.vcenterPassword}}"
validate_certs: no
return_content: yes
status_code: 201
method: POST
body: "{\"uuid\": \"string\", \"uri\": \"https://10.104.8.110/univention/udm/users/user/uid={{item.username}},dc=cybertax,dc=cso,dc=com\", \"options\": {\"pki\": false}, \"policies\": {\"pol
body_format: json
when:
- request_ad_user.results[ndx].status == 404
with_items:
- "{{users.user}}"
loop_control:
index_var: ndx

ansible register with loops

I want to create user and gather their info in local file but with loop register is now working as expected.
I thought it was an indentation problem but no luck.
My playbook
---
- hosts: localhost
tasks:
- name: Clearing Local file
local_action: shell echo "Zone,docode,doname,testuser Output" > user.csv
- hosts: app
tasks:
- name: Creating user Testuser
become: yes
user:
name: "{{ item }}"
state: present
shell: "/bin/bash"
password: "$6$mysecretsalt$qyctTVhRMS1ZSnCuzQNAM8Y7V/yqSEnyRbal0IYXSqSEVKkXF8ZmXBZoRIaN/PvzE/msq8iOJO830OOCG89va/"
update_password: always
groups: santosh
shell: id "{{item}}"
ragister: userout
loop:
- newuser1
- newuser2
- newuser3
- debug:
var=userout
which gives the following error when executed
ERROR! conflicting action statements: shell, user
The error appears to have been in '/home/santosh/ans-home/playbooks/Create_User_and_Gather_output.yml': line 12, column 7, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- name: Creating user Testuser
^ here
You cannot call several modules in one task, you need to separate each call in its own task, as reported by the error message.
I understand why you tried it: to take advantage of the same loop for several task. Unfortunately this is not possible. You could move your set of tasks to a separate file and include it in a loop if you really have to loop over a significant amount of tasks. This is not really required in your situation because:
you only have two tasks
you can compact writing of your loop by using a declared var for reuse
and most essentially because you don't need your second task
In fact, the user module will return the uid of the user it created or that is existing in its result. You just have to register the result of calling the user module.
Just try the following for your second play:
- name: Resgister application users
hosts: app
vars:
user_list:
- newuser1
- newuser2
- newuser3
tasks:
- name: Create the users if they don't exist
user:
name: "{{ item }}"
state: present
shell: "/bin/bash"
password: "$6$mysecretsalt$qyctTVhRMS1ZSnCuzQNAM8Y7V/yqSEnyRbal0IYXSqSEVKkXF8ZmXBZoRIaN/PvzE/msq8iOJO830OOCG89va/"
update_password: always
groups: santosh
register: create_users
loop: "{{ user_list }}"
- name: Show ids of users
debug:
msg: "The uid of user {{ item.name }} is: {{ item.uid }}"
loop: "{{ create_users.results }}"
And as a side note: for your first play, do yourself a favor and stop using the old local_action syntax in favor of delegate_to: localhost for a task. It is not even required in your case as your are already targeting your play to localhost only.

Ansible update variables for module execution during loop

I need to use ansible to do the following job:
1. send a Http Post reuqest to a WebApi
2. check wheter the response meet requirement, otherwise extract some data from the response
3. send another Http Post request with body filled with the data got in step 2
4. loop step 1 to step 3 until the response meet requirement
My code is as the following, but it did not work. The finished_res seems to be undefined forever inside uri module. what I mean is that the variable finished_res inside the uri module seems to never change because I keep receiving the same request body in server. However, the finished_res outside the loop works, I could see the debug message.
- hosts: all
remote_user: ubuntu
gather_facts: no
vars:
default_job_list: "xxx,yyy,zzz,aaa,bbb"
tasks:
- name: test is jobs finish
uri:
url: "{{ API_URL }}"
method: POST
body: "{% if finished_res is defined %}{{ finished_res.json.remainingJobs }}{% else %}{{ default_job_list }}{% endif %}"
return_content: yes
register: finished_res
changed_when: True
until: finished_res is defined and finished_res.json is defined and finished_res.json.status is defined and finished_res.json.status != "FALSE"
- set_fact:
RES: "{{ finished_res }}"
- debug: msg="{{ RES }}"
Could anybody help me ? Thanks in advance !
At last, I solved the problem in another way : create a new ansible module

Ansible looping over nested variables

I have a set of variables which define FQDNs.
domains:
- erp: erp.mycompany.com
- crm: crm.mycompany.com
- git: git.mycompany.com
Indeed, I both need to loop over them and access them namely (in a template file). So accessing them like domains.erpworks like a charm. But I can't get ansible to loop over these.
Obviously, if I do:
- name: Print domains
debug:
msg: test {{ item }}
with_items:
- "{{ domains }}"
It prints both the key and the value… And if I do:
- name: Print domains
debug:
msg: test {{ domains[{{ item }}] }}
with_items:
- "{{ domain }}"
But that doesn't work. I also tried the hashes form as mentionned in the docs, but didn't get any luck either…
Finally, I had to use a dict.
It didn't work the first time because unlike with_items, which has the items going each on their own line, with_dict is a one liner without - before the element to loop through.
domains:
erp:
address: erp.mycompany.com
crm:
address: crm.mycompany.com
git:
address: git.mycompany.com
# used by letsencrypt
webserverType: apache2
withCerts: true
tasks:
- name: Print phone records
debug:
msg: "{{ item.value.address }}"
with_dict: "{{ domains }}"
# I can still access a given domain by its name when needed like so:
{{ domains.erp.address }}
Looks like you figured out your issue. Your original attempt uses a list of dictionaries that do not contain the same keys, making it difficult to access the values uniformly across each list item.
Your second solution creates a dictionary where the keys refer to other dictionaries.
Another solution than what you posted if you still wanted to use a list:
- hosts: localhost
vars:
domains:
- name: erp
address: erp.mycompany.com
- name: crm
address: crm.mycompany.com
- name: git
address: git.mycompany.com
tasks:
- name: Print phone records
debug:
msg: "{{ item.address }}"
with_items: "{{ domains }}"
To me this approach is simpler but your second approach works as well.

Resources