Use ansible templating but rysnc to move files - templates

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

Related

Ansible AnsibleUndefinedVariable: 'XX is undefined

Can anyone please help me here, I have a playbook which has the following vars:
vars_files:
- "../vars/location.yml"
The location.yml has the following declared:
loc: "{{ inventory_hostname[0:3] }}"
location_file: "/root/ansible/vars/{{ loc }}.yml"
So from hostname location_file: is then prd.yml, this file contains the line:
txm_QA_access: false
Now if I run the playbook which copies a sudoers template with the following conditional specified in it:
{% if txm_QA_access %}
%Sudo-admin ALL= /bin/su - admin
%Team-QA ALL= /bin/su - admin
%Team-QA\ OffShore\ \(UK\) ALL= /bin/su - admin
{% endif %} `
When I run the playbook I get an error:
"msg": "AnsibleUndefinedVariable: `txm_QA_access` is undefined"
Where am I going wrong? Isn't it defined as a Boolean which is false so in theory it should skip adding those lines in the sudoers template?
Any help appreciated spent hours on this now.
I guess this is because the vars file prd.yml (most likely) is not getting loaded. And as #dechandler10 mentioned, you can use include_vars:, instead of vars_files:.
In your playbook you have already included location.yml, next you should include vars from prd.yml before the template task.
vars_files:
- '../vars/location.yml'
tasks:
# Your 'location_file' variable set to: /root/ansible/vars/prd.yml
- name: include variables
include_vars:
file: '{{ location_file }}'
Now that your prd.yml has been included, the variable txm_QA_access will be available for template.
However, Ansible already has built-in mechanism called group_vars and host_vars.
Instead of having to define variable paths with inventory_hostname[0:3], Ansible can directly load variables for hosts or groups with this method. See organizing host and group variables.
You didn't mention doing anything to include the variables in prd.yml, so I'm going to assume you haven't. You'll need to use include_vars to load the variables from that file.
If you are doing that, I recommend adding debug tasks immediately before your template task to sort out exactly which variable is causing the problem.

Trouble using list returned from ansible find module

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 }}"

Ansible. Find mac address and put in into variable

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] }}"

Ansible list variable reference

I have a list of variables, let's say
servers:
- server1
- server2
And I have folders on my hosts:
/tmp/server1
/tmp/server2
Now I would like to create files in those folders. I need to reference to all variables in the list separately. If I reference in the task to {{ servers }} it creates folder /tmp/[server1, server2]. When I do {{ server[0] }} it creates a file in server1 folder which is good, but I need to reference to all variables in the list separately not all at once. I think the answer will be something like {{ server[*] }}
Have a look at Loops in Ansible.
Your task will look like this:
- file:
path: "/tmp/{{ item }}/myfile"
state: touch
with_items: "{{ servers }}"
The above will create empty files (or update their datetime, just like touch command) /tmp/server1/myfile, /tmp/server2/myfile on all target servers.

Ansible loop over multiple dictionaries/lists

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