Conditional to add_host module - ansible

I have an inventory that i need to add a conditional to.
My code:
- name: Create memory inventory
add_host:
name: "{{ item.0.key }}"
group: target_hosts
with_nested:
- "{{ lookup ('dict', hosts, wantlist=True) }}"
But I want something like:
- name: Create memory inventory
add_host:
name: "{{ item.0.key }}"
{% if item.0.value.OS_Choice[:3] == 'win' %}
group:
- target_hosts
- win
{% else %}
group:
- target_hosts
- linux
{% endif %}
with_nested:
- "{{ lookup ('dict', hosts, wantlist=True) }}"
With this configuration, Ansible errors:
The offending line appears to be:
{% if item.0.value.OS_Choice[:3] == 'win' %}
^ here
Any ideas on how I can implement this conditional?

You're mixing up Jinja2 with YAML. Here you go:
- name: Create memory inventory when win
add_host:
name: "{{ item.0.key }}"
with_nested:
- "{{ lookup ('dict', hosts, wantlist=True) }}"
when: item.0.value.OS_Choice[:3] == 'win'
vars:
group:
- target_hosts
- win
- name: Create memory inventory when not win
add_host:
name: "{{ item.0.key }}"
with_nested:
- "{{ lookup ('dict', hosts, wantlist=True) }}"
when: item.0.value.OS_Choice[:3] != 'win'
vars:
group:
- target_hosts
- linux
However, Ansible gather facts about the OS already. Perhaps you want to use those, instead of configuring something like this yourself.

Building up on Kevin's answer (and fixing some wrongly placed parameters)
You should definitely do this differently, like creating dynamic groups based on detected OS in facts. See:
the group_by module
the ansible_distribution* facts that you can explore as an example with
ansible localhost -m setup -a filter="ansible_distribution*"
Meanwhile, with your current logic, you can still do this in a single task:
- name: Create memory inventory
vars:
additional_group: >-
{{ (item.0.value.OS_Choice[:3] == 'win') | ternary('win', 'linux') }}
add_host:
name: "{{ item.0.key }}"
groups:
- target_hosts
- "{{ additional_group }}"
with_nested:
- "{{ lookup ('dict', hosts, wantlist=True) }}"

Related

Ansible - loop over multiple items in stdout_lines

I am performing a grep with multiple items.
---
- hosts: my_host
gather_facts: false
vars:
my_list:
- whatever
- something
tasks:
- name: grep for item in search path
shell: "grep -rIL {{ item }} /tmp"
register: the_grep
loop: "{{ my_list }}"
- debug:
msg: "{{ item.stdout_lines }}"
loop: "{{ the_grep.results }}"
Depending on the result, multiple files could match.
msg:
- /tmp/something.conf
- /tmp/folder/file.txt
Q: How would I configure Ansible to loop over the items in stdout_lines?
The use case I'm solving is to delete .ini sections based on the item, but in this case, Ansible doesn't loop over the stdout_lines.
- name: remove stanza from ini file
ini_file:
path: "{{ item.stdout_lines }}"
section: "{{ item.item }}"
mode: '0600'
state: absent
loop: "{{ the_grep.results }}"
when: item.stdout_lines | length > 0
It seems that this doesn't work, but configuring item.stdout_lines[0] gives the partially expected result, since Ansible will use only the first item in that list. But ofc, not the 2nd and so on.
Perhaps there's a prettier answer, but solved it by using with_nested and creating a json_query:
- name: remove stanza from ini file
ini_file:
path: "{{ item.0 }}"
section: "{{ item.1.item }}"
mode: '0600'
state: absent
with_nested:
- "{{ the_grep | json_query('results[].stdout_lines[]') }}"
- "{{ the_grep.results }}"

add_host - divide by os

I want to create playbook that going on the entire inventory file and divide the servers to 2 groups: Windows and Linux (add-host going only on the first server on the inv)
I tried this code:
- name: Linux Group
add_host:
name: "{{ item }}"
group: LinuxGroup
when: hostvars[" {{ item }} "]['ansible_system'] == 'Linux'
with_items: "{{ ansible_play_hosts_all }} "
This code suppose to create the linux group
and I tried with other conditions of when but non was a succsess. I would like to get your help please.
PS:
I changed the code to this:
tasks:
- name: Create linux group
add_host:
name: "{{ item }}"
group: LinuxGroup
when: hostvars[item].ansible_system == 'Linux'
with_items: "{{ ansible_play_hosts_all }} "
ignore_errors: yes
- name: ping to Linux
ping:
with_items: LinuxGroup
and when I run the code the windows servers are skipped in the "Create linux group" task, but I used the module debug to print to group's items and there are the windows servers.
The fact ansible_system is a key under the host, not a list of keys. Also, conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}, use hostvars[item] instead of hostvars[ "{{ item }}" ].
Make sure gather_facts is set to true.
Note that the add_host module does not update the inventory file but updates the in-memory inventory. I have included a debug task to print the groups from the in-memory inventory of ansible.
gather_facts: true
tasks:
- name: Linux Group
add_host:
name: "{{ item }}"
group: LinuxGroup
when: hostvars[item].ansible_system == 'Linux'
with_items: "{{ ansible_play_hosts_all }}"
- debug: msg="{{ groups }}"

Can we have 2 with_items in ansible in a single task

Below is the condition
- name: Find the image
slurp:
src: "{{ IMAGE }}"
register: slurp_results
- name: Upload image
shell: |
skopeo copy -docker-archive:{{ item }}.tar docker://{{ URL }}/TESTIMAGE
with_items: "{{ (slurp_results.content|b64decode).splitlines() }}"
The above code works.
But I would need "TESTIMAGE" also to be replaced as {{ item }} like below.
skopeo copy -docker-archive:{{ item }}.tar docker://{{ URL }}/{{ item }}
How to define 2 with_items in a single shell task with 2 different slurp results
I believe you can by using the subelements module. Here is a link. Try going by this example:
- name: Setup MySQL users, given the mysql hosts and privs subkey lists
mysql_user:
name: "{{ item.0.name }}"
password: "{{ item.0.mysql.password }}"
host: "{{ item.1 }}"
priv: "{{ item.0.mysql.privs | join('/') }}"
with_subelements:
- "{{ users }}"
- mysql.hosts
Users is referred to as item.0 and hosts as item.1 and so on.

Registering and using multiple variables in Ansible

I'm trying to pop VM instances, put them into different host groups (say webservers and devops/admin machines) and install what is needed on them in one single playbook.
I don't know what IP addresses, for instance, GCP will give these instances, and so i am trying to capture them in a variable for use later on in the playbook. I can capture them fine by using "register" but using them is proving tricky. For instance if I do.
- name: création des adresses statiques
gcp_compute_address:
name: "{{ item }}"
state: present
region: "{{ region }}"
project: "{{ gcp_project }}"
auth_kind: "{{ gcp_cred_kind }}"
service_account_file: "{{ gcp_cred_file }}"
scopes:
- https://www.googleapis.com/auth/compute
loop:
- adresse-1
- adresse-2
- adresse-3
- adresse-4
- adresse-5
register: address
The best way i have figured out to use these variables later on is:
network_interfaces:
- network: "{{ network.name }}"
access_configs:
- name: 'External NAT'
type: 'ONE_TO_ONE_NAT'
nat_ip:
- "{{ address.results[0].address }}"
- "{{ address.results[1].address }}"
- "{{ address.results[2].address }}"
Which fails miserably.
Please help ? How can I use the range of addresses I have created ?
I am going nuts over this
It is possible to add_host to the group webservers and proceed with the next play
- add_host:
name: "{{ item }}"
groups: webservers
loop: "{{ address.results|json_query('[*].address') }}"
- debug:
msg: "{{ groups['webservers'] }}"
- hosts: webservers
tasks:
- name: Configure cluster
...
The tasks below split the hosts into two groups
- set_fact:
my_hosts: "{{ address.results|json_query('[*].address') }}"
- add_host:
name: "{{ item }}"
groups: webservers1
loop: "{{ my_hosts[0:(my_hosts|length / 2)|int] }}"
- add_host:
name: "{{ item }}"
groups: webservers2
loop: "{{ my_hosts[(my_hosts|length / 2)|int:my_hosts|length] }}"
- debug:
msg: "{{ groups['webservers1'] }}"
- debug:
msg: "{{ groups['webservers2'] }}"
There is also GCE Dynamic Inventory and other 100+ gcp modules. You might want to start with Google Cloud Platform Guide.
FWIW, Ansible 2 Cloud Automation Cookbook covers leading providers incl. GCP.

Ansible nested loops, how to set inner loop variable based on outer variable

I'm having problems with a nested loop on ansible.
I'm using ansible 2.5.2 with the following config files:
file hosts:
[group1]
host1
host2
host3
[group2]
hostA
file host_vars/host{N} (where N is the number for each host on group1):
variable:
- { line: "keyB" , file: "keyc"}
- { line: "key2" , file: "key3"}
I need to execute on hostA one task for each line in host_vars/host{N}.
in pseudo code, need something like this:
- name: modify file
for host in groups['group1']:
for item in host['variables']:
lineinfile: "path={{ host }}/{{ item.file }} line={{ item.line }}"
using jinja2 loops does not work:
- name: Modify files
lineinfile: "{% for linea in hostvars[item]['variables'] %}
path={{ item }}/{{ linea.file }}
line={{ linea.line }}
{% endfor %}"
loop: "{{ groups['group1'] }}"
normal nested loops does not work either because the inner loop deppends on the hostname:
- name: Modify files
lineinfile: "path={{ item[0] }}/{{ item[1]['file'] }} line={{ item[1]['line'] }}"
with_nested:
- "{{ groups['group1'] }}"
- "{{ hostvars[item[0]]['variables'] }}"
There is another way to cicle nested loops?
I solved my problem using loop_control,
I add a new file: inner.yml
- name: Modify files
lineinfile: "path={{ outer_item }}/{{ item.file }} line={{ item.line }}"
loop: "{{ hostvars[outer_item]['variables'] }}"
And defined my task file as follows
- include_tasks: inner.yml
loop: "{{ groups['group1'] }}"
loop_control:
loop_var: outer_item
which solves my problem of using two nested variables on loops.

Resources