Ansible conditionally loop through with_items? - ansible

Is it possible to loop through a list of items if a string is defined in a variable i will specify.
Essentially i want to have a list of variables defined and utilized the aws_s3 module to download the files only if they are defined when running the playbook
e.g
say i have the list "var1,var2"
and I have the following variables defined:
apps_location:
- { name: 'vars1', src: 'vars1.tgz', dest: '/tmp/vars1_file.tgz' }
- { name: 'vars2', src: 'vars2.tgz', dest: '/tmp/vars2_file.tgz' }
- { name: 'vars3', src: 'vars3.tgz', dest: '/tmp/vars3_file.tgz' }
Task:
- name: "Splunk Search Head | Download Splunk Apps from S3"
aws_s3:
bucket: "{{ resource_bucket_name }}"
object: "{{ item.src }}"
dest: "{{ item.dest }}"
mode: get
with_items: "{{ apps_location }}"
I want to run the command:
ansible-playbook -i inventory -e "var1,var2"
and download only var1 and var2 on that specific run.
I tried utilizing "lookups" but couldnt get the syntax right. Im not entirely sure if this best way of doing this, but i want to have a predefined list of file locations and only download the ones that i'm passing during runtime.
Note the only reason "name" exists in apps_location is to see if i can do a lookup and only install that one but i couldnt get the syntax right.
Define a variable containing a list of defined apps. I'm trying:
- name: "Set Fact"
set_fact:
dict: "{{ apps_location[item].dest }}"
with_items: "{{ my_vars|default([]) }}"
However whenever I output dict I only get the last value.
Any help would be appreciated :)

The extra-vars must be an assignment of a variable and value. For example
shell> ansible-playbook -i inventory -e "my_vars=['vars1','vars2']"
A more convenient structure of the data would be a dictionary for this purpose. For example
apps_location:
vars1:
src: 'vars1.tgz'
dest: '/tmp/vars1_file.tgz'
vars2:
src: 'vars2.tgz'
dest: '/tmp/vars2_file.tgz'
vars3:
src: 'vars3.tgz'
dest: '/tmp/vars3_file.tgz'
Then the loop might look like
- aws_s3:
bucket: "{{ resource_bucket_name }}"
object: "{{ apps_location[item].src }}"
dest: "{{ apps_location[item].dest }}"
mode: get
loop: "{{ my_vars|default([]) }}"
Q: "Define a variable containing a list of defined apps."
A: Try this
- set_fact:
my_list: "{{ my_list(default([]) +
[apps_location[item].dest] }}"
loop: "{{ my_vars|default([]) }}"
(not tested)

Related

Split filename with multiple variables

We have sets of config files that end in the environment extension (eg. app.properties.prod, app.properties.dev, db.prod, db.dev, and so on). I am passing Ansible a variable named environment=prod with the intention of pulling just the files ending in a .prod extension from a filerepo and then need to drop that suffix from the filename so it ends up as app.properties
Something like this will find the correct files:
(env = prod)
copy:
src: "{{item}}"
dest: /app/homedir
with_fileglob:
- /go/to/my/repo/*{{env}}
This copies the correct files to my /app/homedir
However, trying to drop the env file extension does not work
copy:
src: "{{dropsuffix}}"
dest: "{{dropsuffix.split('.{{env}}')[0] }}"
with_fileglob:
-/app/homedir/*.{{env}}
loop_control:
loop_var: dropsuffix
However removing the {{env}} and just adding the text 'prod' will work
dest: "{{dropsuffix.split('.prod')[0] }}"
I'm assuming there is some jinja formatting issue with the variable nested in there, I've tried various permutations and I'm stumped
It's possible to use regex_replace filter to remove the extension.
- copy:
src: "{{ item }}"
dest: "/app/homedir/{{ item|basename|regex_replace(regex, replace) }}"
loop: "{{ lookup('fileglob', '/go/to/my/repo/*.' ~ env, wantlist=True) }}"
vars:
regex: "{{ '^(.*)\\.' ~ env ~ '$' }}"
replace: "{{ '\\1' }}"

Pass dictionary to jinja2 template from task loop

I pass dictionary from play to task. I use loop to call another task from separate yml file again passing the dictionary. From there I call Jinja2 template and pass the dictionary again. I cannot access the dictionary values from Jinja2.
I tried to pass the dictionary to template with_items and with_dict. Still the same problem.
play:
- role: example
vars:
brands:
brand_1:
name: "brand1"
brand_2:
name: "brand2"
brand_3:
name: "brand_3"
task in role with loop:
- name: Loop through configuration files
include_tasks: generate_config_files.yml
loop: "{{ lookup('dict', brands) }}"
loop_control:
loop_var: outer_item
generate_config_files.yml
- name: Generate the configuration files
template:
src: "consumer.properties.j2"
dest: "{{ kafka_location }}/{{ item.key }}/consumer.properties"
owner: "{{ kafka_user }}"
group: "{{ kafka_group }}"
mode: 0644
with_dict: "{{ outer_item }}"
consumer.properties.j2
{% for item in outer_item %}
Name: "{{ item.name }}"
{% endfor %}
I expect to access the dictionary value in template and generate the same file with different values based on number of brands in dictionary. So if there are 3 brands I expect to generate 3 files with different Name: inside.
Unfortunately I am getting:
"msg": "AnsibleUndefinedVariable: 'str object' has no attribute 'name'"
Any ideas?
1) Indentation of vars: is wrong.
2) The single loop does the job.
3) Iteration in the template is not necessary.
4) Numeric mode must be quoted mode: '0644'.
The playbook below
- hosts: localhost
roles:
- role: example
vars:
kafka_user: admin
kafka_group: admin
kafka_location: /scratch
brands:
brand_1:
name: "brand1"
brand_2:
name: "brand2"
brand_3:
name: "brand_3"
with tasks
$ cat roles/example/tasks/main.yml
- include_tasks: generate_config_files.yml
, with the included task
$ cat roles/example/tasks/generate_config_files.yml
- name: Generate the configuration files
template:
src: "consumer.properties.j2"
dest: "{{ kafka_location }}/{{ item.key }}/consumer.properties"
owner: "{{ kafka_user }}"
group: "{{ kafka_group }}"
mode: '0644'
loop: "{{ brands|dict2items }}"
, and with the template
$ cat roles/example/templates/consumer.properties.j2
Name: "{{ item.value.name }}"
gives
$ tree /scratch/brand_*
/scratch/brand_1
└── consumer.properties
/scratch/brand_2
└── consumer.properties
/scratch/brand_3
└── consumer.properties
$ cat /scratch/brand_*/consumer.properties
Name: "brand1"
Name: "brand2"
Name: "brand_3"
Is this what you're looking for?

Iterate with Ansible with_dict over list of a dictionaries

I am stuck in iterating over the list of a dictionary. Sample vars.yml and the minimal playbook is bellow.
---
- hosts: localhost
connection: local
gather_facts: false
become: false
vars:
csvfile: "{{ lookup('file', 'vars/users.csv') }}"
tasks:
- name: Convert CSV to YAML
template:
src: "./users_csv.j2"
dest: "vars/users.yml"
run_once: true
- name: Include users from users.yml to users variable
include_vars:
file: vars/users.yml
name: users
- debug:
msg: "{{ users.value }}"
with_dict:
- "{{ users }}"
My Jinja2 template produces a list of dictionaries in YAML format as below:
--
users:
- username: Auser1
group: Admin
- username: Auser2
group: Admin
- username: Auser3
group: User
Anyhow, when I am iterating the dictionary, I am not able to get for example a username or group.
Most far I got is getting a fatal error message saying:
fatal: [localhost]: FAILED! => {"msg": "with_dict expects a dict"}
I know how to iterate over the list, but I don't have an idea why it fails here.
The users is not a dictionary, its a list variable of dictionaries.
if you want to parse this variable in a loop, you can use:
- debug:
msg: "username: {{ item.username }}, group: {{ item.group }}"
with_items:
- "{{ users.users }}"
hope it helps
UPDATE
i noticed now that when including the var file, you pass the name: users instruction as well. this cause all the variables of the file to be placed under the users variable. So to refer to the users list which is defined in the variable file, you need to use users.users.
updated the with_items to:
with_items:
- "{{ users.users }}"

How to render all ansible variables to yml file with ansible 2.3

I am storing all ansible variables to a yaml file (filtering out those that starting with 'ansible_') with this playbook:
- hosts: localhost
tasks:
- set_fact:
all_vars: "{{all_vars | default({}) |combine({item.key: item.value})}}"
when: "{{not item.key.startswith('ansible_')}}"
with_dict: "{{vars}}"
- copy:
content: "{{ all_vars }}"
dest: "/tmp/tmp.yml"
This is group_vars/all/defaults.yml
SOME_FACT1: "some-fact"
SOME_FACT2: "{{ SOME_FACT1 }}"
SOME_FACT3: "{{ SOME_FACT2 }}"
This works perfectly with ansible 2.2. But with ansible 2.3 (2.3.1.0) the variables are not rendered.
I get results like this:
... "SOME_FACT1": "some-fact", "SOME_FACT3": "{{ SOME_FACT2 }}", "SOME_FACT2": "{{ SOME_FACT1 }}" ...
How can i force ansible 2.3 to render the variables?
The problem seems, that ansible will not render vars and (I do not know why) all_vars. But any variable inside vars/all_vars is rendered properly when used directly.
So this works:
- hosts: localhost
tasks:
- set_fact:
all_vars: "{{all_vars | default([]) |union([item.key + ':{{' + item.key + '|to_json}}'])}}"
when: "{{not item.key.startswith('ansible_')}}"
with_dict: "{{vars}}"
- copy:
content: "{{ all_vars | join('\n') }}"
dest: "/tmp/tmp1.yml"
- template:
src: "/tmp/tmp1.yml"
dest: "/tmp/tmp.yml"
The idea is:
Create a file that lists all variables in the format
SOME_VAR: {{ SOME_VAR | to_json }}
...
Render that file using template.
Not very nice, but it works.

Ansible: Access facts set by set_fact

I need to be able to set variables using tasks in Ansible. I use set_fact for this, but cannot seem to access the fact I set with this. What is wrong with the code below:
- name: kludge1
set_fact: fake_y = "{{ [] }}"
- name: Loop
debug:
msg: "{{ item }}"
with_items: "{{ fake_y }}"
You have spaces before and after =...
- name: kludge1
set_fact: fake_y="{{ [] }}"
Avoid var= shortcut syntax. Use original YAML syntax instead, it gives less errors:
- name: kludge1
set_fact:
fake_y: "{{ [] }}"

Resources