Ansible AnsibleUndefinedVariable: 'XX is undefined - templates

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.

Related

Exclude a string from a word that's a variable with regex using lineinfile ansible module?

I have an ansible playbook that uses lineinfile module.
It uses a variable {{ inventory_hostname }} for the line: part of the task
All the hosts in the inventory have a different name but each hostname's name ends with this .super.vms (don't ask why, it just has to be like this) so it looks something like this:
[webservers]
server1.super.vms
server2.super.vms
database1.super.vms
...
Now I want to add to line: '{{ inventory_hostname }}' something so it appends the line without .super.vms so the file looks something like this:
server1
server2
database1
...
Or maybe create another variabale that lineinfile will use.
Nevermind, I found the inventory_hostname_short variable which excludes the last part for some reason.

Extracting part of {{ inventory_hostname }} in Ansible to use in a playbook

I'm currently writing a small Ansible playbook whose job is to put in an additional domain in the search list in /etc/resolv.conf.
The second domain to add to the search list must contain part of the hostname of the target hosts. I'm getting the hostname of each of the target hosts during playbook execution using the magic variable {{ inventory_hostname }}.
I then need to extract characters 4 - 6 from the {{ inventory_hostname }} (say 'xyz') such that the second domain to add to the search list is xyz.foo.bar. In bash, this would be obtained with something like:
SERVER=$('hostname':3:3)
env=${SERVER:3:3}
... and the variable 'env' would be equal to 'xyz'.
The playbook works as long as 'xyz' is manually defined.
I am aware that Ansible has regular expression filters which can help with something like this, however I could not figure out a regular expression which does what I need.
For completeness sake, I have tried something like this in ansible:
{{ inventory_hostname|3:3 }}
Any help would be greatly appreciated.
It's almost the same, you can use "{{ inventory_hostname[3:6] }}" to select characters 3 to 6.
For example this task
- debug:
msg: "{{ inventory_hostname[3:6] }}"
Will output
ok: [localhost] => {
"msg": "alh"
}

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

Use ansible templating but rysnc to move files

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

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.