Ansible nested loop with dynamic group name - ansible

My vars file :
applications:
- name: server_1
deploy: True
branch: master
group: frontend
- name: server_2
deploy: True
branch: master
group: backend
My task is
- name: deploy boxer
command: "curl http://example.com?hosts={{item.1}}"
with_subelements:
- applications
- "{{ groups[group] }}" # This is not working.
inventory.yml
[frontend]
host1.example.com
host2.example.com
[backend]
host11.example.com
host12.example.com
I want to iterate through applications, and loop through each hosts in a group (only one host at a time) to pass it to curl command.
I want this at the end.
curl http://example.com?hosts=host1.example.com
curl http://example.com?hosts=host2.example.com
curl http://example.com?hosts=host11.example.com
curl http://example.com?hosts=host12.example.com

Did you mean to use with_nested?
with_nested:
- "{{ applications }}"
- "{{ groups }}"
when: item.0.group == item.1

Related

Ansible loop list in dictionary and retain the key

I'm trying to create a series of firewalld rules using a variable imported from a yaml file. The yaml file creates a dictionary of service names and the associated ports are a list within each item. A segment of the yaml looks like this:
---
myservice:
description: My service desc
ports:
- 1234/tcp
- 1235/tcp
another:
description: Another service
ports:
- 2222/tcp
- 3333/tcp
The Ansible role I have so far is:
- name: Read services from file
include_vars:
file: "services.yml"
name: services
- name: Create firewalld services
command: >
firewall-cmd --permanent
--new-service={{ item.key }}
--set-description="{{ item.value.description }}"
register: addserv
failed_when: addserv.rc != 26 and addserv.rc != 0
changed_when: not "NAME_CONFLICT" in addserv.stderr
with_dict: "{{ services }}"
- name: Add ports to firewalld service
command: >
firewall-cmd --permanent
--service={{ item.key }} --add-port={{ item.value.ports }}
register: addport
changed_when: not "ALREADY_ENABLED" in addport.stderr
The first segment to create the firewalld service works fine but I'm stumped on the second part with how to loop over the list of ports while retaining the dictionary key. I've tried using subelements to extract the ports list and that works but I can't figure out how to retain the service name from the key.
Use subelements. For example
- debug:
msg: "{{ item.0.key }} - {{ item.0.value.description }} - {{ item.1 }}"
with_subelements:
- "{{ services|dict2items }}"
- value.ports
gives
"msg": "myservice - My service desc - 1234/tcp"
"msg": "myservice - My service desc - 1235/tcp"
"msg": "another - Another service - 2222/tcp"
"msg": "another - Another service - 3333/tcp"

How to set fact witch is visible on all hosts in Ansible role

I'm setting fact in a role:
- name: Check if manager already configured
shell: >
docker info | perl -ne 'print "$1" if /Swarm: (\w+)/'
register: swarm_status
- name: Init cluster
shell: >-
docker swarm init
--advertise-addr "{{ ansible_default_ipv4.address }}"
when: "'active' not in swarm_status.stdout_lines"
- name: Get worker token
shell: docker swarm join-token -q worker
register: worker_token_result
- set_fact:
worker_token: "{{ worker_token_result.stdout }}"
Then I want to access worker_token on another hosts. Here's my main playbook, the fact is defined in the swarm-master role
- hosts: swarm_cluster
become: yes
roles:
- docker
- hosts: swarm_cluster:&manager
become: yes
roles:
- swarm-master
- hosts: swarm_cluster:&node
become: yes
tasks:
- debug:
msg: "{{ worker_token }}"
I'm getting undefined variable. How to make it visible globally?
Of course it works perfectly if I run debug on the same host.
if your goal is just to access worker_token from on another host, you can use hostvars variable and iterate through the group where you've defined your variable like this:
- hosts: swarm_cluster:&node
tasks:
- debug:
msg: "{{ hostvars[item]['worker_token'] }}"
with_items: "{{ groups['manager'] }}"
If your goal is to define the variable globally, you can add a step to define a variable on all hosts like this:
- hosts: all
tasks:
- set_fact:
worker_token_global: "{{ hostvars[item]['worker_token'] }}"
with_items: "{{ groups['manager'] }}"
- hosts: swarm_cluster:&node
tasks:
- debug:
var: worker_token_global

How to pass results back from include_tasks

Started to learn ansible yesterday, so I believe I may risk XY problem here, but still…
The main yml:
- hosts: localhost
vars_files:
[ "users.yml" ]
tasks:
- name: manage instances
#include_tasks: create_instance.yml
include_tasks: inhabit_instance.yml
with_dict: "{{users}}"
register: res
- name: print
debug: msg="{{res.results}}"
inhabit_instance.yml:
- name: Get instance info for {{ item.key }}
ec2_instance_facts:
profile: henryaws
filters:
"tag:name": "{{item.key}}"
instance-state-name: running
register: ec2
- name: print
debug:
msg: "IP: {{ec2.instances.0.public_ip_address}}"
So that's that IP that I'd like to have on the top level. Haven't found anything right away about return values of the include block…
Well, I've found some way that suits me, maybe it's even canonical?
main.yml:
- hosts: localhost
vars_files:
[ "users.yml" ]
vars:
ec_results: {}
tasks:
- name: manage instances
#include_tasks: create_instance.yml
include_tasks: inhabit_instance.yml
with_dict: "{{users}}"
register: res
inhabit_instance.yml:
- name: Get instance info for {{ item.key }}
ec2_instance_facts:
profile: henryaws
filters:
"tag:name": "{{item.key}}"
instance-state-name: running
register: ec2
- name: update
set_fact:
ec_results: "{{ ec_results|combine({ item.key: ec2.instances.0.public_ip_address }) }}"
Thanks for you answer - this helped me to find an answer to my issue when my task was called in a loop - my solution was just to use lists so for your example above, the solution would be:
The main yml:
- hosts: localhost
vars_files:
[ "users.yml" ]
vars:
ec_results: []
tasks:
- name: manage instances
#include_tasks: create_instance.yml
include_tasks: inhabit_instance.yml
with_dict: "{{users}}"
- name: print
debug: msg="{{ec_results}}"
inhabit_instance.yml:
- name: Get instance info for {{ item.key }}
ec2_instance_facts:
profile: henryaws
filters:
"tag:name": "{{item.key}}"
instance-state-name: running
register: ec2
- name: Add IP to results
set_fact:
ec_results: "{{ ec_results + [ec2.instances.0.public_ip_address] }}"
In my code, include_tasks was in a loop so then ec_results contained a list of the results from each iteration of the loop

Return Variable from Included Ansible Playbook

I have seen how to register variables within tasks in an ansible playbook and then use those variables elsewhere in the same playbook, but can you register a variable in an included playbook and then access those variables back in the original playbook?
Here is what I am trying to accomplish:
This is my main playbook:
- include: sub-playbook.yml job_url="http://some-jenkins-job"
- hosts: localhost
roles:
- some_role
sub-playbook.yml:
---
- hosts: localhost
tasks:
- name: Collect info from Jenkins Job
script: whatever.py --url "{{ job_url }}"
register: jenkins_artifacts
I'd like to be able to access the jenkins_artifacts results back in main_playbook if possible. I know you can access it from other hosts in the same playbook like this: "{{ hostvars['localhost']['jenkins_artifacts'].stdout_lines }}"
Is it the same idea for sharing across playbooks?
I'm confused what this question is about. Just use the variable name jenkins_artifacts:
- include: sub-playbook.yml job_url="http://some-jenkins-job"
- hosts: localhost
debug:
var: jenkins_artifacts
This might seem complicated but I love doing this in my Playbooks:
rc defines the name of the variable which contains the return value
ar gives the arguments to the include tasks
master.yml:
- name: verify_os
include_tasks: "verify_os/main.yml"
vars:
verify_os:
rc: "isos_present"
ar:
image: "{{ os.ar.to_os }}"
verify_os/main.yml:
---
- name: check image on device
ios_command:
commands:
- "sh bootflash: | inc {{ verify_os.ar.image }}"
register: image_check
- name: check if available
shell: "printf '{{ image_check.stdout_lines[0][0] }}\n' | grep {{ verify_os.ar.image }} | wc -l"
register: image_available
delegate_to: localhost
- set_fact: { "{{ verify_os.rc }}": "{{ true if image_available.stdout == '1' else false }}" }
...
I can now use the isos_present variable anywhere in the master.yml to access the returned value.

Ansible recursive checks in playbooks

We need to go through this structure
Zone spec
https://gist.github.com/git001/9230f041aaa34d22ec82eb17d444550c
I was able to run the following snipplet but now I'm stucked at the error checking.
playbook
--
- hosts: all
gather_facts: no
vars_files:
- "../doc/application-zone-spec.yml"
roles:
- { role: ingress_add, customers: "{{ application_zone_spec }}" }
role
- name: check if router exists
shell: "oc get dc -n default {{ customers.zone_name }}-{{ item.type }}"
with_items: "{{ customers.ingress }}"
ignore_errors: True
register: check_router
- name: Print ingress hostnames
debug: var=check_router
- name: create new router
shell: "echo 'I will create a router'"
with_items: "{{ customers.ingress }}"
when: check_router.rc == 1
Output of a ansible run
https://gist.github.com/git001/dab97d7d12a53edfcf2a69647ad543b7
The problem is that I need to go through the ingress items and I need to map the error of the differnt types from the "check_router" register.
It would be nice to make something like.
Pseudo code.
Iterate through the "customers.ingress"
check in "check_router" if the rc is ! 0
execute command.
We use.
ansible-playbook --version
ansible-playbook 2.1.0.0
config file = /etc/ansible/ansible.cfg
configured module search path = Default w/o overrides
You can replace the second loop with:
- name: create new router
shell: "echo 'I will create a router with type {{ item.item }}'"
with_items: "{{ check_router.results }}"
when: item.rc == 1
This will iterate over every step of check_route loop and you can access original items via item.item.

Resources