ansible - using array of Variables and loop through it - loops

I have to run steps like this around 20-25 times. How can I do with for loop (with_items).
I can have the the parameters URL1, Location1, pkg1.comamd1, $pkg1.command2 are pre defined and I can have them defined in the ansible play book. the Pkg1 value will be passed from the jenkins script
- get_url:
url: "$URL1"
dest: $Location1
when: $Pkg1 != 'NONE'
- Name : run the commands
Shell: sh $pkg1.comamd1; sh $pkg1.command2
when: Pkg1 != 'NONE'
how can I create an array of variables and do it with_items
VarDetails {Pkg1, URL1, Location1, comamd1a, $command1b
Pkg2, URL2, Location2, comamd2a, $command2b
Pkg3, URL3, Location3, comamd3a, $command3b
....................
....................
}

I haven't tested but it must work with below reference example while using list of items.
- name: more complex items to add several users
user:
name: "{{ item.name }}"
uid: "{{ item.uid }}"
groups: "{{ item.groups }}"
state: present
with_items:
- { name: testuser1, uid: 1002, groups: "wheel, staff" }
- { name: testuser2, uid: 1003, groups: staff }
dont forget to add item before your variables by changing
url: "$URL1"
dest: $Location1
to
url: "item.url"
dest: "item.location"
and while referencing in with_items.. use your variable $URL1, $URL2

Related

Construct a Distinguished Name with Ansible

In my ansible playbook I have a variable defining the OU DN I'd like to create.
my_ou: "OU=win10,OU=workstations,OU=mycompany"
domain_root: "DC=example,DC=com"
I'd like to convert my_ou to a list and create each OU. The problem is that the loop does not know the path for each OU because it is an aggregation of the previous elements in the loop.
If I split my_ou on ',' and reverse the loop, I can print each OU in order of creation. I can set the name by using regex_replace. However, I don't know how to construct the path.
For example:
- name: create required OU's
win_domain_ou:
name: "{{ my_ou | regex_replace('^OU=') }}"
path: ???? should be all items to the right of the current {{ item }},{{ domain_root }}
state: present
loop: "{{ my_ou.split(',') | reverse | list }}"
So, to be clear, the path for OU=win10 should be OU=workstations,OU=mycompany,DC=example,DC=com
The OU=workstations path should be OU=mycompany,DC=example,DC=com
And so forth.
Thanks!
Declare the list of elements
elements: "{{ my_ou.split(',') + domain_root.split(',') }}"
gives
elements:
- OU=win10
- OU=workstations
- OU=mycompany
- DC=example
- DC=com
and use extended loop variables. For example,
- debug:
msg: "item: {{ item }} path: {{ elements[idx|int:]|join(',') }}"
loop: "{{ my_ou.split(',')|reverse }}"
loop_control:
extended: true
vars:
idx: "{{ my_ou.split(',')|length - ansible_loop.index0 }}"
gives (abridged)
msg: 'item: OU=mycompany path: DC=example,DC=com'
msg: 'item: OU=workstations path: OU=mycompany,DC=example,DC=com'
msg: 'item: OU=win10 path: OU=workstations,OU=mycompany,DC=example,DC=com'
Example of a complete playbook for testing
- hosts: localhost
vars:
my_ou: "OU=win10,OU=workstations,OU=mycompany"
domain_root: "DC=example,DC=com"
elements: "{{ my_ou.split(',') + domain_root.split(',') }}"
tasks:
- debug:
msg: "item: {{ item }} path: {{ elements[idx|int:]|join(',') }}"
loop: "{{ my_ou.split(',')|reverse }}"
loop_control:
extended: true
vars:
idx: "{{ my_ou.split(',')|length - ansible_loop.index0 }}"

Best way to conditionally override an item value in an Ansible loop

I'm attempting to adapt an existing play that was built for automating account creation and deletion in a small environment without centralized authentication. Since then the scope has expanded and we've added bastion servers where it's not appropriate for all accounts to be created.
Because user IDs are hard coded (not desirable - I know) I'd like to avoid having a separate play for the bastions as that means keeping track of UIDs in two places. My workaround was to add an onbastion boolean to each row, then use that in create-account.yml to flip the state to absent if applicable. I originally tried changing the value of item.state directly but ran into syntax errors that I couldn't figure out. So I added a set_fact task to set the value of a new variable conditionally, but the playbook now fails with a message that "The task includes an option with an undefined variable. The error was: 'desired_item_state' is undefined".
What am I missing, and is there a better way to tackle this? Thanks in advance!
Relevant Snippets
project/roles/create-accounts/default/main.yml
users:
- { name: "user_1", id: 15001, state: "present", groups: ["project"], onbastion: true }
- { name: "user_2", id: 15002, state: "present", groups: ["project"], onbastion: true }
- { name: "fired_user_3", id: 15003, state: "absent", groups: [], onbastion: false}
- { name: "retired_user_4", id: 15004, state: "absent", groups: [], onbastion: false}
- { name: "osr1", id: 15009, state: "present", groups: ["project"], onbastion: false }
- { name: "osr2", id: 15010, state: "present", groups: ["project"], onbastion: false }
- { name: "osr3", id: 15011, state: "present", groups: ["project"], onbastion: false }
- { name: "project_svc", id: 15018, state: "present", groups: ["project"], onbastion: false }
- { name: "jenkins", id: 15019, state: "present", groups: ["project"], onbastion: false }
project/roles/create-accounts/tasks/main.yml
- name: Set up user accounts
ansible.builtin.include_tasks: create-account.yml
loop: "{{ users }}"
project/roles/create-accounts/tasks/create-account.yml
---
- name: Update item state if on bastion
set_fact:
desired_item_state: "{{ 'absent' if ('bastion' in group_names and item.onbastion == false) else item.state }}"
- name: Create/update {{ item.name }} user ID
become: true
ansible.builtin.user:
name: "{{ item.name }}"
state: "{{ desired_item_state }}"
uid: "{{ item.id }}"
groups: "{{ item.groups }}"
append: yes
update_password: on_create
password: "!"
- name: Create/update {{ item.name }} group ID
become: true
ansible.builtin.group:
name: "{{ item.name }}"
state: "{{ desired_item_state }}"
gid: "{{ item.id }}"
- name: Update ownership of /home/{{ item.name }}
become: true
ansible.builtin.file:
path: /home/{{ item.name }}
state: directory
recurse: yes
owner: "{{ item.name }}"
group: "{{ item.name }}"
when: desired_item_state == 'present'
Output
TASK [create-accounts : Create/update osr1 user ID] ***********************
fatal: [project-subsystem-bastion2]: FAILED! =>
msg: |-
The task includes an option with an undefined variable. The error was: 'desired_item_state' is undefined
The error appears to be in '/project/ansible-e86f3754/plays/roles/create-accounts/tasks/create-account.yml': line 13, column 3, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- name: Create/update {{ item.name }} user ID
^ here
We could be wrong, but this one looks like it might be an issue with
missing quotes. Always quote template expression brackets when they
start a value. For instance:
with_items:
- {{ foo }}
Should be written as:
with_items:
- "{{ foo }}"
See comments. flowerysong was unable to reproduce my issue, and the only thing I'd left out were tags on the tasks. Lesson learned: sanitize code before posting but don't leave anything out.

ansible and sub loops

Hope I have framed the title correctly. I am trying to loop over the first play using with_sequence and then use the output of the first play as the input of another, but the issue is the second play also uses a sequence. I can't get my around how I could achieve that.
create root directory
file:
path: dir{{ item }}
with_sequence: 1-6
register: outdirs
mount using above directory
mount:
state: mounted
src: xxxx{{ item }}
path: *outdirs.dir1*
fstype: nfs
with_sequence: 21-26
Thanks
Use the results from first task to loop on the next one and add your offset:
- name: Create root directory
file:
path: "dir{{ item }}"
state: directory
with_sequence: 1-6
register: outdirs
- name: Mount root dir
vars:
nfs_offset: 20
nfs_number: "{{ item.item | int + nfs_offset | int }}"
mount:
state: mounted
src: "xxxx{{ nfs_number }}"
path: "{{ item.path }}"
fstype: nfs
loop: "{{ outdirs.results }}"

Ansible: How to loop through 2 var dicts only when hostname corresponds

Im trying to loop to a set of variables depending on the hostname. I cant get the conditional right.
Say i have 2 set of vars:
databases_eu:
- name: database_eu_1
port: 5432
login: {{ vault_user_database_eu_1 }}
password: {{ vault_pw_database_eu_1 }}
- name: database_eu_2
port: 5433
login: {{ vault_user_database_eu_2 }}
password: {{ vault_pw_database_eu_2 }}
databases_us:
- name: database_us_1
port: 5432
login: {{ vault_user_database_us_1 }}
password: {{ vault_pw_database_us_1 }}
- name: database_us_2
port: 5433
login: {{ vault_user_database_us_2 }}
password: {{ vault_pw_database_us_2 }}
And i want to loop through it depending on the hostname, how would i do that?
I thought something like below, but im missing the when statement:
- name: copy configs
ansible.builtin.template:
src: config.yml.j2
dest: /opt/db/configs/{{ item.name }}.yml
owner: root
group: root
mode: '0644'
loop:
- "{{ databases_eu | inventory_hostname == 'eu.db.example.com' }}"
- "{{ databases_us | inventory_hostname == 'us.db.example.com' }}"
Use the lookup plugin vars to get the value of a variable. See
shell> ansible-doc -t lookup vars
There are many options on how to reference the data. For example, given the inventory
shell> cat hosts
eu.db.example.com
us.db.example.com
Create a dictionary of data assigned to the hosts, e.g.
databases_host:
eu.db.example.com: databases_eu
us.db.example.com: databases_us
Then the playbook
- hosts: all
tasks:
- debug:
msg: "Configure {{ item.name }}"
loop: "{{ lookup('vars', databases_host[inventory_hostname]) }}"
loop_control:
label: "{{ item.name }}"
gives
ok: [eu.db.example.com] => (item=database_eu_1) =>
msg: Configure database_eu_1
ok: [us.db.example.com] => (item=database_us_1) =>
msg: Configure database_us_1
ok: [us.db.example.com] => (item=database_us_2) =>
msg: Configure database_us_2
ok: [eu.db.example.com] => (item=database_eu_2) =>
msg: Configure database_eu_2
The next option is to put the name of the dictionaries to a host-specific variable, e.g.
shell> cat hosts
eu.db.example.com my_db=databases_eu
us.db.example.com my_db=databases_us
Then the playbook below gives the same result
- hosts: all
tasks:
- debug:
msg: "Configure {{ item.name }}"
loop: "{{ lookup('vars', my_db) }}"
loop_control:
label: "{{ item.name }}"
The next option is to create the name of the dictionary dynamically, e.g.
- hosts: all
tasks:
- debug:
msg: "Configure {{ item.name }}"
loop: "{{ lookup('vars', my_db) }}"
loop_control:
label: "{{ item.name }}"
vars:
my_db: "{{ 'databases_' ~ inventory_hostname.split('.').0 }}"
Thanks. I solved by creating a group_vars dir. Thanks for the input.

How to read a complex include variables in Ansible

I have a variable file that has multiple variables "name" & "path" listing under an IP address like below.
10.0.0.12
- name: exe_folder
path: /tmp/exe
- name: log_folder
path: /tmp/log
- name: src_folder
path: /tmp/src
10.0.0.13
- name: test_folder
path: /tmp/exe1
- name: out_folder
path: /tmp/log1
- name: com_folder
path: /tmp/src1
etc ....
I can loop on the name and path successfully in my playbook as below.
- name: Load repository
include_vars:
file="{{ playbook_dir }}/vars/list.yml"
name=user1
- debug:
msg: "{{ item.name + ':' + item.path }}"
loop: "{{ user1[inventory_hostname] }}"
- set_fact:
allinonecmd: "{{ allinonecmd | default('') + 'ls -ltr ' + item.path + ' '}}"
loop: "{{ user1[inventory_hostname] }}"
My requirement is to have a new variable "mycode" under each IP however just have a single mention of it and I should be able to print it in the loop like above playbook does.
Thus i need my variable file to have mycode specified onetime for each IP. I'm not sure what changes I need to make to my variable file and playbook to accomodate this requirement.
10.0.0.12
- name: exe_folder
path: /tmp/exe
mycode: "56.12"
- name: log_folder
path: /tmp/log
- name: src_folder
path: /tmp/src
10.0.0.13
- name: test_folder
path: /tmp/exe1
mycode: "76.88"
- name: out_folder
path: /tmp/log1
- name: com_folder
path: /tmp/src1
etc ....
The playbook fails after I make the above changes and try to print the mycode variable.
- debug:
msg: "{{ item.name + ':' + item.path + item.mycode }}"
loop: "{{ user1[inventory_hostname] }}"
Error Output:
fatal: [10.0.0.12]: FAILED! => {
"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'mycode'
Note: I do not wish to specify mycode multiple times under an IP as it looks like a dirty solution.
I think that for this use case, you can use default() filter, so if this element is not on the list, you can recall a default one or omit it.
vars:
my_default_value: "56.12"
- debug:
msg: "{{ item.name + ':' + item.path + (item.mycode | default(my_default_value) }}" loop: "{{ user1[inventory_hostname] }}"
Even you can make a combination with default(value) and default(omit) to fit your needs.

Resources