How to do multi-conditional loops with Ansible? - ansible

I have roles and users. I would like to loop over my roles for users that contains the state=present.
iam_roles:
- name: "developers-role"
assume_role_policy_document: "developers"
state: present
managed_policy:
- arn:aws:iam::XXXXXXXXXXX:policy/CustomAmazonS3ReadOnlyAccess
- name: "bigdata-role"
assume_role_policy_document: "bigdata"
state: present
managed_policy:
- arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess
- arn:aws:iam::XXXXXXXXXXX:policy/CustomAmazonRDSReadOnlyAccess
iam_users:
- name: test-user-1
state: present
groups: [developers]
password:
slack_name:
access_key_state: create
- name: test-user-2
state: present
groups: [developers]
password:
slack_name:
I'm trying filter and get only the users with the state=present and use it on my when clause, but no luck so far.
- name: Loop all the present users
debug: msg={{ item }}
when: "{{ item.state == 'present' }}"
with_items: "{{ iam_users }}"
tags: always
register: present_users
- set_fact:
iam_present_users: "{{ present_users.results }}"
tags: always
- name: Show only present users, ideally
debug: msg="{{ iam_present_users }}"
tags: always
- name: Manage AWS IAM Roles
iam_role:
name: "{{ item.name }}"
assume_role_policy_document: "{{ lookup('template', policies_path + '/assume-role/' + item.assume_role_policy_document + '.json') }}"
state: "{{ item.state }}"
managed_policy: "{{ item.managed_policy }}"
when: "{{ item.managed_policy is defined and iam_present_users is defined }}"
with_items: "{{ iam_roles }}"
tags: manage_roles

Your use of a debug statement to try to extract users seems odd. If you want to select objects from a list based on the value of an attribute, your best choice is probably the Jinja2 selectattr filter. For example, given this input:
iam_users:
- name: test-user-1
state: present
groups: [developers]
password:
slack_name:
access_key_state: create
- name: test-user-2
state: present
groups: [developers]
password:
slack_name:
- name: test-user-3
state: absent
groups: [developers]
password:
slack_name:
You could use this set_fact task:
- set_fact:
iam_present_users: "{{ iam_users|selectattr('state', 'equalto', 'present')|list }}"
Which would result in iam_present_users containing:
"iam_present_users": [
{
"access_key_state": "create",
"groups": [
"developers"
],
"name": "test-user-1",
"password": null,
"slack_name": null,
"state": "present"
},
{
"groups": [
"developers"
],
"name": "test-user-2",
"password": null,
"slack_name": null,
"state": "present"
}
]
See the jinja documentation for the stock list of filters, and the ansible documentation for a list of filters specific to ansible.

Related

Ansible loop one COMPLEX array of objects looking for case in-sensitive matches in the second COMPLEX array

I have two complex objects with the following structures:
ProgA_Users
{
"Resources": [
{
"emails": [
{
"type": "work",
"value": "Oscar.Warren#domain.local"
}
],
"id": "2939XYZ",
"userName": "Oscar.Warren#domain.local#SOMEREALM"
},
{
"emails": [
{
"type": "work",
"value": "Alexandra.Savage#domain.local"
}
],
"id": "2032XYZ",
"userName": "Alexandra.Savage#domain.local#SOMEREALM"
}
]
}
ProgB_Usrs
[
{
"DistinguishedName": "CN=Warren\\, Oscar J.,OU=Users,OU=Finance Division,OU=Departments,DC=domain,DC=local",
"Name": "Warren, Oscar J.",
"ObjectClass": "user",
"mail": "Oscar.Warren#domain.local"
},
{
"DistinguishedName": "CN=Bodden\\, John B.,OU=Users,OU=Finance Division,OU=Departments,DC=domain,DC=local",
"Name": "Bodden, John B.",
"ObjectClass": "user",
"mail": "John.Bodden#domain.local"
}
]
In an Ansible Playbook I need to loop through the list of ProgA_Users and find all ProgB_Users with no matching mail attribute.
I tried the following YAML:
- name: Process Users
debug:
msg: "{{ item.userName }}"
loop: "{{ ProgA_Users.Resources }}"
loop_control:
loop_var: item
when: "{{ ad_users.objects|selectattr('mail','equalto','{{ item.emails[0].value }}')|list|length == 0 }}"
The problem is that Ansible is running the debug task on ALL objects, even when there is a matching member within ProgB_User.
I want the debug task to execute each time Ansible comes across a member of ProgA_Users that is not in ProgB_Users. The two arrays have objects with different schemas, so I am matching {ProgA_User}.emails[0].value against {ProgB_User}.mail. The match should not be case-sensitive.
How can I achieve this outcome?
The task below
- name: Find all ProgB_Users with no matching mail attribute
debug:
msg: "{{ ProgB_Users|rejectattr('mail', 'in', emails) }}"
loop: "{{ ProgA_Users.Resources }}"
loop_control:
label: "{{ emails }}"
vars:
emails: "{{ item.emails|map(attribute='value')|list }}"
gives
TASK [Find all ProgB_Users with no matching mail attribute] ************
ok: [localhost] => (item=['Oscar.Warren#domain.local']) =>
msg:
- DistinguishedName: CN=Bodden,OU=Departments,DC=domain,DC=local
Name: Bodden, John B.
ObjectClass: user
mail: John.Bodden#domain.local
ok: [localhost] => (item=['Alexandra.Savage#domain.local']) =>
msg:
- DistinguishedName: CN=Warren,OU=Departments,DC=domain,DC=local
Name: Warren, Oscar J.
ObjectClass: user
mail: Oscar.Warren#domain.local
- DistinguishedName: CN=Bodden,OU=Departments,DC=domain,DC=local
Name: Bodden, John B.
ObjectClass: user
mail: John.Bodden#domain.local
Example of a complete playbook for testing
- hosts: localhost
vars:
ProgA_Users:
Resources:
- emails:
- type: work
value: Oscar.Warren#domain.local
id: 2939XYZ
userName: Oscar.Warren#domain.local#SOMEREALM
- emails:
- type: work
value: Alexandra.Savage#domain.local
id: 2032XYZ
userName: Alexandra.Savage#domain.local#SOMEREALM
ProgB_Users:
- DistinguishedName: CN=Warren,OU=Departments,DC=domain,DC=local
Name: Warren, Oscar J.
ObjectClass: user
mail: Oscar.Warren#domain.local
- DistinguishedName: CN=Bodden,OU=Departments,DC=domain,DC=local
Name: Bodden, John B.
ObjectClass: user
mail: John.Bodden#domain.local
tasks:
- name: Find all ProgB_Users with no matching mail attribute
debug:
msg: "{{ ProgB_Users|rejectattr('mail', 'in', emails) }}"
loop: "{{ ProgA_Users.Resources }}"
loop_control:
label: "{{ emails }}"
vars:
emails: "{{ item.emails|map(attribute='value')|list }}"

Register return values from tasks_from when looping over include_role

I'd like to build an array of ticket numbers that are created by the create_srq.yaml task in role servicenow. Is it possible to do this when looping over an include_role?
roles/request_signed_certificate/tasks/main.yaml
- name: Create SNOW Records for Certificate Request
include_role:
name: servicenow
tasks_from: create_srq.yaml
register: result
loop: "{{ spreadsheet }}"
loop_control:
loop_var: csr
vars:
short_description: CSR for {{ csr.certname }}-{{ csr.env }}
attachment: "{{ cert_path }}/{{ csr.certname }}-{{ csr.env }}.csr"
- name: debug
debug:
var: result
roles/servicenow/tasks/create_srq.yaml
- name: Create a SRQ
snow_record:
state: present
table: u_request
username: "{{ snow_username }}"
password: "{{ snow_password }}"
instance: "{{ servicenow_instance }}"
data:
short_description: "{{ short_description }}"
attachment: "{{ attachment }}"
register: srq
- name: Attach file to {{ srq.record.number }}
snow_record:
state: present
table: u_request
username: "{{ snow_username }}"
password: "{{ snow_password }}"
instance: "{{ servicenow_instance }}"
number: "{{ srq.record.number }}"
attachment: "{{ attachment }}"
When running the playbook:
---
- hosts: "{{ hosts_list }}"
connection: local
gather_facts: false
vars:
cert_path: "/tmp/certs"
cert_version: "2023"
pre_tasks:
- name: Create facts from csv
csv_to_facts:
src: "file.csv"
delegate_to: localhost
run_once: true
roles:
- role: request_signed_certificate
The result does not include the registered srq variable from create_srq.yaml:
TASK [request_signed_certificate : debug] **************************************************************
ok: [host.example.com] => {
"result": {
"changed": false,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "csr",
"csr": {
"certname": "example_one",
"common_name": "domain_one.example.com",
"dns1": "domain_two.example.com",
"env": "development",
},
"include_args": {
"name": "servicenow",
"tasks_from": "create_srq.yaml"
}
},
{
"ansible_loop_var": "csr",
"csr": {
"certname": "example_two",
"common_name": "domain_123.example.com",
"dns1": "domain_456.example.com",
"env": "test",
},
"include_args": {
"name": "servicenow",
"tasks_from": "create_srq.yaml"
}
}
]
}
}
I was able to do this by appending results to a list. While this works, it wasn't exactly what I was after as I would have preferred to install a reusable role to create the srqs.
Adding this bit to the bottom of create_srq.yaml gave me what I was looking for:
- name: output csv entry + ticket number
set_fact:
csv: "{{ csv | default({}) | combine ( { item.key : item.value } ) | combine( { 'ticket': srq.record.number } ) }}"
loop: "{{ csr | dict2items }}"
# Create list of dictionaries to track
# also, a list is required for the to_csv plugin
- name: create list of dicts
set_fact:
contents: "{{ contents | default([]) + [ csv ] }}"

ansible create a new dict and add to list

my playbook always show me just last value , looks like the script is overwrite.
From json file I need extract some value, create dictionary and put it to the list.
My json file .
{
"rade": [
{
"apiRawValues": {
"verificationStatus": "signature-verified"
},
"deviceReference": {
"name": "bigip02"
},
"port": "Ir_HTTP_HTTPs"
},
{
"apiRawValues": {
"verificationStatus": "signature-verified"
},
"deviceReference": {
"name": "bigip01"
},
"port": "Ir_HTTP_HTTPs"
}
]
}
and my playbook look like
---
- hosts: localhost
connection: local
gather_facts: false
vars:
cert1: {}
vars_files:
tasks:
- name : deploy json file AS3 to F5
set_fact:
json_file: "{{ lookup('file', 'parse2.json') }}"
- name: create dic and create list
set_fact:
cert1: "{{ cert1 | d({}) | combine({ 'device': item['deviceReference']['name']}, { 'port': item.port}, recursive=True) }}"
loop: "{{ json_file['rade'] }}"
- name: debug4
debug:
msg: "{{ cert1 }}"
the result is
ok: [localhost] => {
"msg": {
"device": "bigip01",
"port": "Ir_HTTP_HTTPs"
}
}
why it just show me last value ?
I need list of device and port.
thank you for help
The filter json_query makes it simpler e.g.
- debug:
msg: "{{ json_file.rade|
json_query('[].{device: deviceReference.name, port: port}') }}"
gives
msg:
- device: bigip02
port: Ir_HTTP_HTTPs
- device: bigip01
port: Ir_HTTP_HTTPs
If the names of the devices are unique you can create a dictionary, e.g.
- debug:
msg: "{{ dict(_keys|zip(_vals)) }}"
vars:
_keys: "{{ json_file.rade|map(attribute='deviceReference.name')|list }}"
_vals: "{{ json_file.rade|map(attribute='port')|list }}"
gives
msg:
bigip01: Ir_HTTP_HTTPs
bigip02: Ir_HTTP_HTTPs
I have found out solution
- name: create dic and create list
set_fact:
cert1: "{{ cert1 | default([]) + [{ 'device' : item['deviceReference']['name'], 'irule' : item.port }] }}"
loop: "{{ json_file['rade'] }}"
- name: debug4
debug:
msg: "{{ cert1 }}"

Is it possible to set an Ansible fact only if a variable has a value?

I have an Ansible playbook that I'm running from AWX. The playing uses the Infoblox nios module to retrieve information about Infoblox host registrations.
I'm using the set_fact module to to take the output of the query and then define a number of new facts to use elsewhere in the playbook.
The problem that I have is that the query can return a differing number of variables depending on the format of the registration and this breaks the playbook.
What I am trying to do is workout if I can set a new fact only if the specific variable is returned in the original query.
I've tried using "if defined" but that doesn't seem to work.
In example 1 the play "fetch host record" returns the following values. host, ipv4addr and mac as the host has a Mac Address in Infoblox
ok: [localhost] => {
"ansible_facts": {
"host": {
"ipv4addrs": [
{
"host": "myhost1.test.com",
"ipv4addr": "192.168.30.1",
"mac": "00:22:33:11:44:55"
}
],
"name": "myhost1.test.com",
"view": "Internal"
}
},
"changed": false
}
In example 2 the same play only returns host and ipv4addr as the host does not have a Mac Address registered.
ok: [localhost] => {
"ansible_facts": {
"host": {
"ipv4addrs": [
{
"host": "myhost2.test.com",
"ipv4addr": "192.168.30.2"
}
],
"name": "myhost2.test.com",
"view": "Internal"
}
},
"changed": false
}
My playbook contains the following and works only if the host contains a Mac Address as the fact host, doesn't contain a value for host.ipv4addrs[0].mac so it crashes out. I'd like to add some logic to only try and set niosmac if host.ipv4addrs[0].mac is defined.
tasks:
- name: fetch host record
set_fact:
host: "{{ lookup('nios', 'record:host', filter={niossearchcatagory: searchcriteria, 'view': 'Internal'}, provider=nios_provider) }}"
- name: Set niosip
set_fact:
niosip: "{{ host.ipv4addrs[0].ipv4addr }}"
nioshostname: "{{ host.name }}"
niosdhcp: "{{ host.ipv4addrs[0].configure_for_dhcp }}"
niosmac: "{{ host.ipv4addrs[0].mac }}"
Here's the version I attempted using is defined
tasks:
- name: fetch host record
set_fact:
host: "{{ lookup('nios', 'record:host', filter={niossearchcatagory: searchcriteria, 'view': 'Internal'}, provider=nios_provider) }}"
- name: Set niosip
set_fact:
niosip: "{{ host.ipv4addrs[0].ipv4addr }}"
nioshostname: "{{ host.name }}"
niosdhcp: "{{ host.ipv4addrs[0].configure_for_dhcp }}"
niosmac: "{{ host.ipv4addrs[0].mac }}"
when: host.ipv4addrs[0].mac is defined
Cheers
Spence
Sorry, I must have typed something wrong before as I've tried again and it now seems to work. Here's the correct code for clarification.
- name: Set niosip
set_fact:
niosip: "{{ host.ipv4addrs[0].ipv4addr }}"
nioshostname: "{{ host.name }}"
niosdhcp: "{{ host.ipv4addrs[0].configure_for_dhcp }}"
niosmac: "{{ host.ipv4addrs[0].mac }}"
when: host != [] and host.ipv4addrs[0].mac is defined
- name: Set niosip
set_fact:
niosip: "{{ host.ipv4addrs[0].ipv4addr }}"
nioshostname: "{{ host.name }}"
niosdhcp: "{{ host.ipv4addrs[0].configure_for_dhcp }}"
when: host != [] and host.ipv4addrs[0].mac is undefined

Ansible parse JSON output

I am trying to parse the Ansible output the print a value
- name: Creating a new instance
os_server:
state: present
auth:
auth_url: "{{ auth_url }}"
username: "{{ username }}"
password: "{{ password }}"
project_name: "{{ project_name }}"
name: "{{ item.hostname }}"
image: "{{ item.image }}"
nics: "{{ nics }}"
with_items: "{{ servers }}"
register: "os"
Output:
"server": {
"OS-DCF:diskConfig": "MANUAL",
"OS-EXT-AZ:availability_zone": "zoneA",
"OS-EXT-STS:power_state": 1,
"OS-EXT-STS:task_state": null,
"OS-EXT-STS:vm_state": "active",
"OS-SRV-USG:launched_at": "2018-04-01T18:53:16.000000",
"OS-SRV-USG:terminated_at": null,
"accessIPv4": "10.190.230.23",
"accessIPv6": "",
"addresses": {
"provider_corenet_bif_757": [
{
"OS-EXT-IPS-MAC:mac_addr": "fa:1:3:3:5e:6a",
"OS-EXT-IPS:type": "fixed",
"addr": "10.19.23.23",
"version": 4
}
],
"provider_nmnet_bif_912": [
{
"OS-EXT-IPS-MAC:mac_addr": "fa:1:3:39:b:57",
"OS-EXT-IPS:type": "fixed",
"addr": "10.25.13.64",
"version": 4
}
]
server.addresses.provider_nmnet_bif_912.addr
},
I want to parse addr "10.25.13.64".
I tried {{ item.server.addresses.provider_nmnet_bif_912.addr }} and {{os.server.addresses.provider_nmnet_bif_912.addr}} both didnot work.
Need Help!!!
Finally figured it out:
"{{ item.server.addresses.provider_nmnet_bif_912[0].addr }}"

Resources