Loop through list variable in Ansable - ansible

I am making an ansible script where a variable file will hold a list of servers, where I will loop through and power them down. It appears that Ansible is changing my list to a string, but maybe I am wrong.
I've tried using "loop", "with_items", and having them on the same line. serverA and ServerB does indeed exist
File dir:
taskName/vars/ServerListA.yaml
taskName/tasks/main.yaml
taskName/tasks/shutdown.yaml
ServerListA.yaml:
---
evenServers:
- serverB
- serverA
main.yaml:
---
- import_tasks: shutdown.yaml
shutdown.yaml:
---
- name: get vars
include_vars:
file: ServerListA.yaml
name: evenServers
- name: shutdown guest
vmware_guest_powerstate:
hostname: virtualCenterName
state: powered-on
username: user
name: "{{ item }}"
password: pass
validate_certs: no
loop:
- "{{ evenServers }}"
output:
TASK [vCenter_Infra_HA_Test : shutdown guest]
********************************************************************************************************* [WARNING]: The value {'evenServers': ['serverA', 'serverB']} (type
dict) in a string field was converted to u"{'evenServers': ['serverA',
'serverB']}" (type string). If this does not look like what you
expect, quote the entire value to ensure it does not change.
failed: [localServer] (item={u'evenServers': [u'serverA',
u'serverB']}) => {"ansible_loop_var": "item", "changed": false,
"item": {"evenServers": ["serverA", "serverB"]}, "msg": "Unable to set
power state for non-existing virtual machine : '{'evenServers':
['serverA', 'serverB']}'"}

You are including vars inside a top level 'namespace' with the same name as the first var defined in your included file. Thus, after inclusion, you data looks like this:
evenServers:
evenServers:
- serverA
- serverB
Since ansible cannot loop over the first level hash (without any other instructions) it transforms it to a string representation to have something to use.
You should transfrom your tasks file like this:
---
- name: get vars
include_vars:
file: ServerListA.yaml
- name: shutdown guest
vmware_guest_powerstate:
hostname: virtualCenterName
state: powered-on
username: user
name: "{{ item }}"
password: pass
validate_certs: no
loop: "{{ evenServers }}"
I also suggest you fix the indentation in your var file
---
evenServers:
- serverB
- serverA

Related

How to use the lookup plugin to get the directory path and file in Ansible

I have a two playbooks where one creates SSH Keys and the other one creates a new user and deploys the public ssh key for the new user created.
My issue is I created a task that create a new directory with a timestamp to store the relevant data, I was able to get the path to a variable where I added it as a dummy host so that I can be able to call that path with all my plays but it seems like I am unable to use the same variable in lookup so that I can be able to deploy the ssh key. Kindly assist, below are the relevant tasks.
# Create the directory with timestamp
- name: Create Directory with timestamp to store data that was run multiple times that day
when: inventory_hostname in groups['local']
file:
path: "{{store_files_path}}/{{ansible_date_time.date}}/{{ansible_date_time.time}}"
state: directory
mode: "0755"
register: dir_path
# Add the directory path to dummy host called save so that I can call it from other plays
- name: Add dir path:"{{dir_path.path}}" as a 'save' host
when: inventory_hostname in groups['local']
add_host:
name: "save"
dir: "{{dir_path.path}}"
# Deploying SSH Key I tried this -->
- name: Deploy Public Key to the server
when: inventory_hostname in groups['Servers']
authorized_key:
user: "{{hostvars['new-user']['user']}}"
state: present
key: "{{dir_path.path}}/SSH-Key.pub"
# ...this -->
- name: Deploy Public Key to the server
when: inventory_hostname in groups['Servers']
authorized_key:
user: "{{hostvars['new-user']['user']}}"
state: present
key: "{{ lookup('file','{{dir_path.path}}/SSH-Key.pub') }}"
# .... and this -->
- name: Deploy Public Key to the server
when: inventory_hostname in groups['Servers']
authorized_key:
user: "{{hostvars['new-user']['user']}}"
state: present
key: "{{ lookup('file','{{hostvars['save']['dir']}}/SSH-Key.pub') }}"
None of them worked, what am I doing wrong?
If you put a Jinja expression into a string in a Jinja expression, then you indeed end up with a your variable not being interpreted.
A basic example of this is:
- hosts: all
gather_facts: no
tasks:
- debug:
msg: "{{ '{{ foo }}' }}"
vars:
foo: bar
Which gives:
ok: [localhost] => {
"msg": "{{ foo }}"
}
When
- hosts: all
gather_facts: no
tasks:
- debug:
msg: "{{ foo }}"
vars:
foo: bar
Gives thes expected:
ok: [localhost] => {
"msg": "bar"
}
So in order to achieve what you want here, you should use the concatenation operator of Jinja: ~, in order to let Jinja interpret your variable and concatenate it with the rest of your "hardcoded" string.
Effectively ending with the instruction:
key: "{{ lookup('file', hostvars['save']['dir'] ~ '/SSH-Key.pub') }}"

Using Ansible loop to create multiple users: Undefined error

I am using the following ansible code to create multiple unix user accounts
---
- hosts: test
become: true
tasks:
- name: more complex items to add several users
user:
name: "{{ item.name }}"
uid: "{{ item.uid }}"
groups: "{{ item.groups }}"
state: present
with_items: "{{ user_details }}"
I am storing the user information by using a separate a variable file as below
`cat /etc/ansible/vars.yml
---
user_details:
- { name: testuser1, uid: 1002, groups: "admin, logs" }
- { name: testuser2, uid: 1003, groups: logs: }`
To execute above playbook , I tried with both the commands below
sudo ansible-playbook /etc/ansible/userloop.yml -e /etc/ansible/vars.yml
sudo ansible-playbook /etc/ansible/userloop.yml
but both commands are failing with below error
fatal: [host-003]: FAILED! => {"msg": "'user_details' is undefined"}
fatal: [host-004]: FAILED! => {"msg": "'user_details' is undefined"}
How to resolve the issue ? I want to maintain a separate variable file to store the user information rather then putting them in the same playbook file .
You can also refer the multiple variable files in playbooks like below
- hosts: all
become: true
vars_files:
- /etc/ansible/vars.yml
tasks:
- name: more complex items to add several users
user:
name: "{{ item.name }}"
uid: "{{ item.uid }}"
groups: "{{ item.groups }}"
state: present
with_items: "{{ user_details }}"
The type of variables is in the column "Parameter" of the module user. Try the structure of the data below
user_details:
- {name: 'testuser1', uid: 1002, groups: ['admin', 'logs']}
- {name: 'testuser2', uid: 1003, groups: ['logs']}
You are missing # while passing the vars.yml. Hence, the ansible is not reading the file. Try the below command. It works for me.
sudo ansible-playbook /etc/ansible/userloop.yml -e #/etc/ansible/vars.yml

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 can I pass a parameter to a role in Ansible from inventory file?

I have a set of roles that are all unique and use a common role that is pulled in via a dependency to perform a bunch of the same actions that all these other roles need to have done. I need to be able to pass into a specific role a parameter to say in this case pull a docker image from a registry, in that other case save the image out and do some other things. Variables cause issues as they are explicit for a host and I might have several roles per host. How can I structure my ansible playbook to do this?
Example:
I have a common role that is pulled into other roles as a dependency:
---
- name: Included Playbook - What vars do I see
debug:
msg: "Name Pull: {{ imagename }}"
- name: Local Running
debug:
msg: "Local option selected"
when: localimage == "true"
- name: Not local
debug:
msg: "Not Local remote"
when: localimage == "false"
Then the primary role tasks\main.yml
---
- name: Included Playbook - What vars do I see from Primary
vars:
myname: "{{ imagemainname }}"
debug:
msg: "Name-primary: {{ myname }}"
and its meta\main.yml
---
dependencies:
- { role: image-pull, imagename: "{{ imagemainname }}" }
This is the same for a second role
---
- name: Included Playbook - What vars do I see from Second
vars:
myname: "{{ secondname }}"
debug:
msg: "Name-second: {{ myname }}"
and its meta\main.yml
---
dependencies:
- { role: image-pull, imagename: "{{ secondname }}" }
My main playbook calls both primary and second roles and the role specific vars works fine.
---
- name: Master
hosts: imagemaster
remote_user: root
vars:
imagemainname: "Top Dog"
roles:
- image-master
- name: Second
hosts: second
remote_user: root
vars:
imagemainname: "Second Dog"
roles:
- second
What doesn't work is when I want to state the do option a or b in the "pulled" role.
If my inventory file looks like this:
[imagemaster]
127.0.0.1
[imagemaster:vars]
localimage=false
[second]
127.0.0.1
[second:vars]
localimage=true
It doesn't work as whatever is the last entry for localimage is what all roles will use.
What can I do to pass in something from the inventory/host_vars/etc that means my playbook doesn't change for every iteration in this setup?
If you plan to apply primary and secondary role to the same host (as in your example (127.0.0.1), then you have no options:
Within any section, redefining a var will overwrite the previous instance. If multiple groups have the same variable, the last one loaded wins. If you define a variable twice in a play’s vars: section, the 2nd one wins.
(from the docs)
If you plan to apply them to different hosts, then test is properly, e.g.:
[imagemaster]
127.0.0.1
[imagemaster:vars]
localimage=false
[second]
127.0.1.1
[second:vars]
localimage=true
In this scenario, when role (primary/secondary) is applied to imagemaster group, then localimage=false; when applied to second group – localimage=true.

Getting variable values from url

i am new to ansible and i am trying to achieve the following,
This is the sample playbook, here i am creating a reg key called "test" under HKLM:\SOFTWARE\myapp\Desk\ and i am adding 2 entries with items called hello and hi and i am assigning single same value "world" to both hello and hi.
i want to get the value for hello and hi from some file which will be stored in a repository or some url ..the value will be different for hello and hi.. is this possible ?
---
- hosts: web
tasks:
- name: Create and add reg entries
win_regedit:
path: HKLM:\SOFTWARE\myapp\Desk\Test
entry: "{{ item }}"
data: world
type: string
state: present
with_items:
- hello
- hi
Update:
Some improvement here for a newbie, i am able to iterate through with_items with key value , but i want to have a file with key:value and i want the ansible to iterate through all the left side keys and create them and also fill them with the right side values..that is the entry in win_Regedit should have key and data should have values
---
- hosts: web
tasks:
- name: Create and add reg entries
win_regedit:
path: HKLM:\SOFTWARE\myapp\Desk\Test
entry: "{{ item.regkey }}"
data: "{{ item.regvalue}}"
type: string
state: present
with_items:
- regkey: hello
regvalue: world
- regkey: hi
regvalue: universe
To include variable from some file, you can use the include_vars module:
include_vars:
file: vars.yml
Edit What you want to do is probably something like below (did not try the win_regedit as I don't have any windows node):
Create a vars.yml:
---
var1: 'key1'
var2: 'key2'
d1: "{
'{{var1}}': 'val1',
'{{var2}}': 'val2'
}"
Then launch the following playbook (see the with_dict statement):
---
- hosts: localhost
remote_user: my_user
tasks:
- name: pull var_file
url: http://example.com/path/to/vars
dest: /path/to/vars.yml
mode: 0440
- name: include_vars
include_vars:
/path/to/vars.yml
- name: Create and add reg entries
win_regedit:
path: HKLM:\SOFTWARE\myapp\Desk\Test
entry: "{{ item.key }}"
data: "{{ item.value}}"
type: string
state: present
with_dict: "{{d1}}"
Kudos to this answer for the with_dict trick
Use uri module.
---
- hosts: localhost
remote_user: my_user
tasks:
- name: pull var_file
uri:
url: http://example.com/path/to/vars
method: GET
return_content: yes
register: vars_from_url
- name: Create and add reg entries
win_regedit:
path: HKLM:\SOFTWARE\myapp\Desk\Test
entry: "{{ item.key }}"
data: "{{ item.value}}"
type: string
state: present
with_dict: "{{ vars_from_url.content }}"

Resources