Ansible run task multiple times based on groups - ansible

How do you run a task on each group, not just once for the groups?
I was excepting the Run this on each host tasks would run once for each group_var value. Instead it seems to just be picking one and running it.
I plan on breaking these across multiple servers later but for now it should be able to run on one autoscale, and then easy break it up into multiple autoscale groups later as demand increases.
playbook.yml:
---
# Run with: ansible-playbook -i localhost, playbook.yml
- name: Register Groups
hosts: localhost
connection: local
tasks:
- name: Add the groups
add_host:
name: localhost
ansible_connection: local
groups: rest-api, msg-consumer
- name: Run this on each host
hosts:
- rest-api
- msg-consumer
tasks:
- name: Say type
debug: var=item
with_items: run_type
group_vars/rest-api:
---
run_type: web
group_vars/msg-consumer:
---
run_type: consumer
Output Ansible 1.8.2:
$ ansible-playbook -i localhost, playbook.yml
PLAY [Register Groups] ********************************************************
GATHERING FACTS ***************************************************************
ok: [localhost]
TASK: [Add the groups] ********************************************************
ok: [localhost]
PLAY [Run this on each host] **************************************************
GATHERING FACTS ***************************************************************
ok: [localhost]
TASK: [Say type] **************************************************************
ok: [localhost] => (item=consumer) => {
"item": "consumer"
}
PLAY RECAP ********************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0
Note: It may be something else. I thought I could also clutter my playbook but breaking up the tasks like like follows:
---
- name: Register Groups
hosts: localhost
connection: local
tasks:
- name: Add the groups
add_host:
name: localhost
ansible_connection: local
groups: rest-api, msg-consumer
- name: Run this on each host
hosts:
- msg-consumer
tasks:
- name: Say type
debug: var=item
with_items: run_type
- name: Run this on each host
hosts:
- rest-api
tasks:
- name: Say type
debug: var=item
with_items: run_type
But the output for the 2nd playbook is:
$ ansible-playbook -i localhost, playbook2.yml
PLAY [Register Groups] ********************************************************
GATHERING FACTS ***************************************************************
ok: [localhost]
TASK: [Add the groups] ********************************************************
ok: [localhost]
PLAY [Run this on each host] **************************************************
GATHERING FACTS ***************************************************************
ok: [localhost]
TASK: [Say type] **************************************************************
ok: [localhost] => (item=consumer) => {
"item": "consumer"
}
PLAY [Run this on each host] **************************************************
GATHERING FACTS ***************************************************************
ok: [localhost]
TASK: [Say type] **************************************************************
ok: [localhost] => (item=consumer) => {
"item": "consumer"
}
PLAY RECAP ********************************************************************
localhost : ok=6 changed=0 unreachable=0 failed=0
Edit: Yet Another attempt to access the data, it looks like group_vars isn't behaving like I expect. The following outputs consumer twice also.
-
# Run with: ansible-playbook -i localhost, playbook.yml
- name: Register Groups
hosts: localhost
connection: local
tasks:
- name: Add the groups
add_host:
name: localhost
ansible_connection: local
groups: rest-api, msg-consumer
- name: Run this on each host
hosts:
- msg-consumer
- rest-api
tasks:
- name: What's your run type
debug: var=hostvars[groups[item][0]]['run_type']
with_items: group_names

The easiest way to do this is to use aliases for the hostnames instead of the real hosts:
---
- name: Register Groups
hosts: localhost
connection: local
tasks:
- name: Add the rest-api alias for my app
add_host:
name: my-app-rest-api
ansible_ssh_host: 127.0.0.1
groups: rest-api
- name: Add the msg-consumer alias for my app
add_host:
name: my-app-msg-consumer
ansible_ssh_host: 127.0.0.1
groups: msg-consumer
- name: Test Run Types
hosts:
- msg-consumer
- rest-api
tasks:
- name: What's your run type
debug: msg="Run Type of {{ ansible_ssh_host }} is {{ run_type }}"
now you can use your group_vars again:
group_vars/rest-api:
---
run_type: web
group_vars/msg-consumer:
---
run_type: consumer
and the output will be:
PLAY [Register Groups] ********************************************************
TASK: [Add the rest-api alias for my app] *************************************
ok: [localhost]
TASK: [Add the msg-consumer alias for my app] *********************************
ok: [localhost]
PLAY [Test Run Types] *********************************************************
TASK: [What's your run type] **************************************************
ok: [my-app-msg-consumer] => {
"msg": "Run Type of 127.0.0.1 is consumer"
}
ok: [my-app-rest-api] => {
"msg": "Run Type of 127.0.0.1 is web"
}

For now this is the best I can come up with:
---
- name: Register Groups
hosts: localhost
connection: local
tasks:
- name: Add new host group
add_host:
name: 127.0.0.1
ansible_connection: local
groups: new-server
run_types:
- rest-api
- msg-consumer
- name: Add another new host group
add_host:
name: 127.0.0.2
ansible_connection: local
groups: new-server
run_types:
- nothing
- name: Test Run Types Server 1
hosts:
- new-server
tasks:
- name: What's your run type
debug: var=item
with_items: run_types
Note: The hosts must be different for this to work, otherwise it will override and use the last variable value used with add_host.

See my answer under Ansible run task once per database-name.
Basically, there is no run_once_per_group, and the closest method I'm aware of is a true run_once that loops over groups. To make matters more cluttered, there is no group_vars dictionary variable.
---
- hosts: all
tasks:
- name: "do this once per group"
delegate_to: localhost
debug:
msg: "do something on {{hostvars[groups[item.key].0]['somevar']}} for group named {{item}}"
run_once: yes
with_dict: groups
when: item.key not in ['all', 'ungrouped']

Related

Use dynamic variable name

I'm trying to get the value of ip_address from the following yaml that I'm including as variables on ansible:
common:
ntp:
- time.google.com
node1:
default_route: 10.128.0.1
dns:
- 10.128.0.2
hostname: ip-10-128-5-17
device_interface: ens5
cluster_interface: ens5
interfaces:
ens5:
ip_address: 10.128.5.17
nat_ip_address: 18.221.63.178
netmask: 255.255.240.0
version: 2
However the network interface (ens5 here) may be named something else, such as eth0. My ansible code is this:
- hosts: all
tasks:
- name: Read configuration from the yaml file
include_vars: "{{ config_yaml }}"
- name: Dump Interface Settings
vars:
msg: node1.interfaces.{{ cvp_device_interface }}.ip_address
debug:
msg: "{{ msg }}"
tags: debug_info
Running the code like this I can get the key's name:
TASK [Dump Interface Settings] *************************************************
│ ok: [18.221.63.178] => {
│ "msg": "node1.interfaces.ens5.ip_address"
│ }
But what I actually need is the value (i.e: something like {{ vars[msg] }}, which should expand into {{ node1.interfaces.ens5.ip_address }}). How can I accomplish this?
Use sqare brackets.
Example: a minimal playbook, which defines a variable called "device". This variable is used to return the active status of the device.
- hosts: localhost
connection: local
vars:
device: enx0050b60c19af
tasks:
- debug: var=device
- debug: var=hostvars.localhost.ansible_facts[device].active
Output:
$ ansible-playbook example.yaml
[WARNING]: provided hosts list is empty, only localhost is available. Note that
the implicit localhost does not match 'all'
PLAY [localhost] *******************************************************************
TASK [Gathering Facts] *************************************************************
ok: [localhost]
TASK [debug] ***********************************************************************
ok: [localhost] => {
"device": "enx0050b60c19af"
}
TASK [debug] ***********************************************************************
ok: [localhost] => {
"hostvars.localhost.ansible_facts[device].active": true
}
PLAY RECAP *************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0
see comment
- hosts: all
tasks:
- name: Read configuration from the yaml file
include_vars: "{{ config_yaml }}"
- name: Dump Interface Settings
debug:
msg: "{{ node1['interfaces'][cvp_device_interface]['ip_address'] }}"
debug:
msg: "{{ msg }}"
tags: debug_info

Ansible conditional task processing

I need some help on an ansible playbook. Using this sample playbook, I would like to modify this playbook in such a way that if the hostname of webservers1 equals "123.456.000", do not bother running the remaining parts of the playbook.
- name: test play 1
hosts: webservers1
serial: 2
gather_facts: False
tasks:
- name: first task
command: hostname
- name: test play 2
hosts: webservers2
serial: 2
gather_facts: False
tasks:
- name: first task
command: hostname
- name: second task
command: hostname
- name: test play 3
hosts: webservers3
serial: 2
gather_facts: False
tasks:
- name: first task
command: hostname
- name: second task
command: hostname
- name: test play 4
hosts: webservers4
serial: 2
gather_facts: False
tasks:
- name: first task
command: hostname
- name: second task
command: hostname
Q: "If the hostname of webservers1 equals "123.456.000", do not bother running the remaining parts of the playbook."
A: It's not possible to break a playbook from a play, e.g.
- hosts: localhost
tasks:
- meta: end_play
- hosts: localhost
tasks:
- debug:
msg: Start play 2
the playbook proceeds to the second play
PLAY [localhost] **************************************************************
PLAY [localhost] **************************************************************
TASK [debug] ******************************************************************
ok: [localhost] =>
msg: Start play 2
There is still an option to test a variable at the beginning of each play. For example, given the inventory
shell> cat hosts
[webservers1]
srv1 ansible_host=123.456.000
srv2 ansible_host=123.456.001
[webservers2]
srv3 ansible_host=123.456.002
srv4 ansible_host=123.456.003
The playbook below tests the condition in the first play and sets the variable. The next plays test this variable, e.g.
- hosts: all
tasks:
- set_fact:
_break: "{{ '123.456.000' in groups.webservers1|
map('extract', hostvars, 'ansible_host')|
list }}"
run_once: true
- hosts: webservers1
tasks:
- meta: end_play
when: _break|bool
- debug:
msg: Start webservers1
- hosts: webservers2
tasks:
- meta: end_play
when: _break|bool
- debug:
msg: Start webservers2
should break the next two plays
PLAY [all] ********************************************************************
TASK [set_fact] ***************************************************************
ok: [srv1]
PLAY [webservers1] ************************************************************
PLAY [webservers2] ************************************************************
PLAY RECAP ********************************************************************
srv1: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Multiple hosts prompt is working but there's a problem with execution

This is my code:
---
- hosts: localhost
gather_facts: no
vars_prompt:
- name: server
prompt: "What is the hostname/ip you want to execute at?"
private: no
tasks:
- add_host:
name: "{{ server }}"
groups: dynamic_hosts
with_items: "{{ server.split(',') }}"
#### Dynamic Host
- hosts: dynamic_hosts
gather_facts: no
tasks:
- name: "Running task id"
command: id
and this is the behaviour:
What is the hostname you want to execute at?: 10.0.0.2, 10.0.0.3
PLAY [localhost] *******************************************************************************************************************************************************************************************************************
TASK [add_host] ********************************************************************************************************************************************************************************************************************
changed: [localhost] => (item= 10.0.0.2)
changed: [localhost] => (item= 10.0.0.3)
PLAY [dynamic_hosts] ***************************************************************************************************************************************************************************************************************
TASK [Running task id] ********************************************************************************************************************************************
fatal: [10.0.0.2, 10.0.0.3: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: Could not resolve hostname 10.0.0.2, 10.0.0.3: Name or service not known\r\n", "unreachable": true}
to retry, use: --limit #/home/user/playbook.yaml
PLAY RECAP *************************************************************************************************************************************************************************************************************************
localhost : ok=1 changed=1 unreachable=0 failed=0
10.0.0.2, 10.0.0.3 : ok=0 changed=0 unreachable=1 failed=0
So the input of multiple hosts is working properly but when I try to call the group in hosts basically it tries to "ssh 10.0.0.2, 10.0.0.3" and naturally it fails.
What am I missing here?
What I want to do is prompt the user for the hosts he wants to execute at, input
and then just execute the tasks to each host. I do not want to make use of an inventory file.
Is it possible? Thank you in advance
Working code:
---
- hosts: localhost
gather_facts: no
vars_prompt:
- name: server
prompt: "What is the hostname/ip you want to execute at?"
private: no
tasks:
- add_host:
name: "{{ item }}"
groups: dynamic_hosts
with_items: "{{ server.split(',') }}"
#### Dynamic Host
- hosts: dynamic_hosts
gather_facts: no
tasks:
- name: "Running task id"
command: id
thank you

Ansible task to to get the localhost ip address and replace in the destination file with value

i am running below task and replacing whole content in the destination file with ip address
---
- hosts: localhost
connection: local
tasks:
- debug: var=ansible_all_ipv4_addresses
- debug: var=ansible_default_ipv4.address
- copy: content="{{ ansible_all_ipv4_addresses }}" dest=/root/curator.yml
i have variable in curator.yml, i want to update variable {{ ansible_default_ipv4.address }} with ip address.
---
client:
hosts:
- {{ ansible_default_ipv4.address }}
port: 9200
url_prefix:
use_ssl: False
ssl_no_validate: False
http_auth:
timeout: 30
master_only: False
logging:
loglevel: INFO
logfile:
logformat: default
blacklist: ['elasticsearch', 'urllib3']
When i execute above playbook task it's replacing whole information in curator.yml with ip address in the debug output
PLAY [localhost] ****************************************************************************************************************
TASK [Gathering Facts] **********************************************************************************************************
ok: [localhost]
TASK [debug] ********************************************************************************************************************
ok: [localhost] => {
"ansible_all_ipv4_addresses": [
"10.0.0.5"
]
}
TASK [debug] ********************************************************************************************************************
ok: [localhost] => {
"ansible_default_ipv4.address": "10.0.0.5"
}
TASK [copy] *********************************************************************************************************************
changed: [localhost]
PLAY RECAP **********************************************************************************************************************
localhost : ok=4 changed=1 unreachable=0 failed=0
i am also included below task but looks like it's not working
#- name: rewrite
# vars:
# ansible_default_ipv4.address: "{{ ansible_default_ipv4.address[0] }}"
# template:
# src: templates/curator.yml.j2
# dest: /root/curator.yml
There are issues with your copy and template tasks:
Copy Task - As you are using content parameter, it will "set the contents of a file directly to the specified value" copy_module
Template task - You can't define/update vars (or even set_fact) with .("dot") notation and you don't even need to, as the ansible_default_ipv4.address variable is already defined and the value is set.
This will work:
---
- name: Update ip
hosts: 127.0.0.1
connection: local
tasks:
- debug: var=ansible_all_ipv4_addresses
- debug: var=ansible_default_ipv4.address
- name: Template file with ip
template:
src: templates/curator.yml.j2
dest: /root/curator.yml
...

How to pass vars in nested Ansible playbook?

I have a nested ansible playbook (master) file and I want to call included playbook (slave) with their own JSON vars.
Master.yaml
- name: this is a play at the top level of a file
hosts: local
connection: local
tasks:
- debug: msg=hello
- include: slave_first.yaml
- include: slave_second.yaml
slave_first.yaml should make use of "vars/slave_first_vars.json" file and slave_second.yaml should make use of "vars/slave_second_vars.json" file.
When including playbooks you can only override variables with vars statement, like:
- include: slave_first.yaml
vars:
myvar: foo
- include: slave_second.yaml
vars:
myvar: foo
There are no other options for PlaybookInclude.
If you need to load variables from files, you have to use vars_files or include_vars inside your slave playbooks.
In your scenario, I'll use like this, master.yml:
- hosts: localhost
connection: local
tasks:
- include: slave_first.yml
vars:
VAR_FILE: "slave_first_vars"
- include: slave_second.yml
vars:
VAR_FILE: "slave_second_vars"
While slave_first.yml and slave_second.yml are like this, in my case both are same but you get an idea that how you can use them:
slave_first.yml:
---
- include_vars: "{{ VAR_FILE }}.yml"
- debug:
msg: "{{ DOMAIN_NAME }}"
slave_second.yml:
---
- include_vars: "{{ VAR_FILE }}.yml"
- debug:
msg: "{{ DOMAIN_NAME }}"
Now come to the different variable part:
slave_first_vars.yml: in your case it will be json
---
DOMAIN_NAME: "first.com"
slave_second_vars.yml:
---
DOMAIN_NAME: "second.com"
Then you can run and verify that if work as expected:
➤ansible-playbook -i localhost, master.yml
PLAY [localhost] **********************************************************************************
TASK [Gathering Facts] **********************************************************************************
ok: [localhost]
TASK [include_vars] **********************************************************************************
ok: [localhost]
TASK [debug] **********************************************************************************
ok: [localhost] => {
"changed": false,
"msg": "first.com"
}
TASK [include_vars] **********************************************************************************
ok: [localhost]
TASK [debug] **********************************************************************************
ok: [localhost] => {
"changed": false,
"msg": "second.com"
}
PLAY RECAP **********************************************************************************
localhost : ok=5 changed=0 unreachable=0 failed=0
Hope that might help you!

Resources