In my playbook, I'd like to loop over two dictionaries (or one dictionary and one list). One is a list (or dictionary) of Domains, the other one is a dictionary that includes the aws-regions with the corresponding server-IPs to use for the DNS-Entries for latency based routing. I want to set for each domain one DNS-record for each aws-region.
- name: set DNS records for Domains
route53:
zone: "{{ item[0].key }}"
record: "{{ item[0].key }}"
value: "{{ item[1].value.server_ip }}"
region: "{{ item[1].key }}"
identifier: "{{ item[1].key }}"
with_nested:
- "{{ domain_dict }}"
- "{{ aws_dict }}"
With two lists, the example works fine. How do I get it to work using at least one dictionary?
domain_dict: (could be a list as well)
domain_dict:
mytest1.example:
mytest2.example:
mytest3.example:
aws_dict:
aws_dict:
us-east-1:
# some other region-related stuff like ami-id,...
server_ip: 1.2.3.4
us-west-1:
# some other region-related stuff
server_ip: 1.2.3.5
us-west-2:
# some other region-related stuff
server_ip: 1.2.3.6
#all other aws-regions
A custom lookup_plugin is your best bet. Otherwise it'll be an ugly sequence of set_fact.
PS:
While you ordinarily shouldn’t have to, should you wish to write your own ways to loop over arbitrary datastructures, you can read Developing Plugins for some starter information. Each of the above features are implemented as plugins in ansible, so there are many implementations to reference
Related
I'm struggling with syntax to build and add to list dynamically. Would like to do it in an ansible line without going to a jinja2 template. I've been messing around while but the below is where I started. The error is in the line to set the fact.
- name: Assign data to the correct list based on the meta value.
set_fact:
"{{ next_reference.list_name }}": "{{ next_reference.list_name|default([]) + [ input_data ] }}"
loop: "{{ reference_list }}"
loop_control:
loop_var: next_reference
when: input_data.meta == next_reference.meta
reference_list:
- meta: "test1"
list_name: "test1_list"
- meta: "test2"
list_name: "test2_list"
- meta: "test3"
list_name: "test3_list"
input_data_list:
- meta: "anp"
value1: "one"
value2: "two"
Thanks for any assistance may have to go the jinja2 route in the meantime.
Without Jinja, it's not possible to indirectly declare or modify variables in Ansible. Instead, an appropriate data structure might serve the purpose, e.g.
t:
t1: "{{ test1_list|default([]) }} + {{ input_data }}"
t2: "{{ test2_list|default([]) }} + {{ input_data }}"
t3: "{{ test3_list|default([]) }} + {{ input_data }}"
Thanks for the reply. This issue was a reminder that jinja2 and ansible are simple tools not programming languages so the key is to think simplistically.
If you want to change data in a list of dictionaries. The best way to do it is trawl through the current list and pull out the data from the dictionary you want to create a new dictionary put the new data in it or amend it. Stick this new dictionary in a list.
Use rejectattr to remove the dictionary from the current list. Use a simple jinja2 template to recombine the new and old list.
I need to extract some text from a variable in ansible.
- name: Find modules
shell: find "{{ role_path }}/files/{{ flavor_modules }}/modules.d/" -name "*.yml"
register: modules_list
- debug:
msg: "aca {{ item }}"
with_items: "{{ modules_list }}"
So modules list returns something like this: /etc/ansible/roles/filebeat/files/something/modules.d/haproxy.yml
I need to register or create a new variable that looks like this: haproxy.yml
I been trying with some regex expressions with no luck.
I want to create a file in each user home directory. Based on the debug output it looks like it is returning a dict containing a list called "files" that has all the info I need including user and group to set on the newly created file. However, it doesn't behave the way I expected and based on everything I am reading I must be misunderstanding how to properly access the variable. Here is what I am doing:
- name: Get Directory Listing Using Find
find:
file_type: directory
paths: /home
register: find_user_dirs
- name: Create Test File in Each Directory
file:
status: touch
path: "{{ item.path }}/test.file"
owner: root
group: root
with_items: "{{ find_user_dirs.files }}"
The documentation briefly mentions dicts vs lists, but there seems to be more to it. I need help to get pointed in the right direction for understanding how to properly use this variable.
Ok, I feel stupid. Probably because I was working on this at 4am, but still a stupid simple oversight on my part. I had a typo in there with "status" instead of "state".
After some rest and debugging with the information provided by Rost, which was helpful for other applications by the way, I paid closer attention and found my error. Now this actually works as intended:
- name: Get Directory Listing Using Find
find:
file_type: directory
paths: /home
register: find_user_dirs
- name: Create Test File in Each Directory
file:
state: touch
path: "{{ item.path }}/test.file"
owner: "{{ item.uid }}"
group: "{{ item.gid }}"
with_items: "{{ find_user_dirs.files }}"
The classic rookie mistake - seeing what you think you see instead of what you ACTUALLY see. Sorry to anyone that wasted any time on this.
Well, for me such behavior is quite unexpected too.
But I found the workaround, this works:
- name: Create Test File in Each Directory
file:
status: touch
path: "{{ item }}/test.file"
owner: root
group: root
with_items: "{{ find_user_dirs | map(attribute='path') | list }}"
I have quite a few files (Nginx configs) that are candidates for templating but I want to move them using rysnc/synchronize modules.
Is there a way to achieve this?
Right now I do this
- name: Copy configuration
synchronize:
src: "{{ nginx_path }}/"
dest: /etc/nginx/
rsync_path: "sudo rsync"
rsync_opts:
- "--no-motd"
- "--exclude=.git"
- "--exclude=modules"
- "--delete"
notify:
- Reload Nginx
The templating engine is combined with the move/copy action and therefore I can’t use it to apply the templates and keep it in the source itself and then use rsync to move it.
Edit:
Another way to rephrase this would be:
Is there a way to apply templates, and keep the applied output it in the source machine itself?
Not in a single task no. However, the following playbook stub achieves what, I believe, you desire:
---
- hosts: localhost
gather_facts: no
tasks:
- name: "1. Create temporary directory"
tempfile:
state: directory
register: temp_file_path
- name: "2. Template source files to temp directory"
template:
src: "{{ item }}"
dest: "{{ temp_file_path.path }}/{{ item | basename | regex_replace('.j2$', '') }}"
loop: "{{ query('fileglob', 'source/*.j2') }}"
delegate_to: localhost
- name: "3. Sync these to the destination"
synchronize:
src: "{{ temp_file_path.path }}/"
dest: "dest"
delete: yes
- name: "4. Delete the temporary directory (optional)"
file:
path: "{{ temp_file_path.path }}"
state: absent
Explanation:
For testing, this playbook was written to target localhost and connect using the local method, I developed it to simply look for all .j2 files in ./source/*.j2 and rsync the created files to ./dest/ on my workstation. I ran it using ansible-playbook -i localhost, playbook_name.yml --connection=local
Task 1. We are going to template out the source files to the local host first (using the delegate_to: localhost option on the template task). You can either create a specific directory to do this, or use Ansible's tempfile module to create one somewhere (typically) under /tmp.
Task 2. Use the template module to convert your jinja templates (with arbitrary filenames ending in .j2) found in "./source/" to output files written to the directory created in task 1 above.
Task 3. Use the synchronize module to rsync these to the destination server (for testing I used ./dest on the same box).
Task 4. Delete the temporary directory created in task 1 above.
There is with_filetree plugin that not only allows you to copy a bunch of templates, it also allows you to recreate the folder structure of your source on your destination:
- name: Template complete tree
template:
src: '{{ item.src }}'
dest: /web/{{ item.path }}
force: yes
with_community.general.filetree: web/
when: item.state == 'file'
For more details take a look at the original pull request: https://github.com/ansible/ansible/pull/14332
On the latest versions, it seems to be under community general
https://docs.ansible.com/ansible/latest/collections/community/general/filetree_lookup.html
tasks:
- name: Find mac-address
ios_command:
provider: "{{ cli }}"
commands:
- show mac add | i d83b
register: sh_mac_res
sh_mac_res is variable contain string like this:
"22 18a9.0530.d83b DYNAMIC Fa0/17"
How can I put 18a9.0530.d83b into a variable?
set_facts:
mac_address: {{ sh_mac_res.stdout }}.........????
What should I use? regex_search or something else?
Use Python split() function and select the second element of the resulting list:
- set_fact:
mac_address: "{{ sh_mac_res.stdout.split()[1] }}"