How to reference variable in files folder under roles - ansible

In one of my ansible roles, I have created a variable via the "defaults/main.yml" file. I am able to reference this variable just fine in "tasks/main.yml" file. However, the variable does not appear to work in "files/some_file.txt"
Is this expected ?

Yes that's expected. tasks/main.yml is being parsed by ansible and will replace variables as you've seen.
Generally files/some_file.txt should contain static files or scripts that should be used with the copy module. As you've discovered it will not be parsed beyond that.
If you want to use variables in a file you should use the template module. Create a templates directory and copy your files there e.g template/some_file.txt. Note that it's common to rename the file with a .j2 extension to indicate that it is a jinja template e.g some_file.j2 but this is not required.
- name: Create fact
set_fact:
my_variable: 123456
- name: Create file from template
template:
src: some_file.j2
dest: "/tmp/some_file.txt"
mode: 0755
some_file.j2 might contain:
This file contains this sentence and the number {{ my_variable }}
After the template task run /tmp/some_file.txt will looks like:
This file contains this sentence and the number 123456

Use template. For example, the role
shell> cat roles/role-17/defaults/main.yml
var1: value1
shell> cat roles/role-17/files/some_file.txt
[{{ var1 }}]
shell> cat roles/role-17/tasks/main.yml
- debug:
msg: task/main.yml [{{ var1 }}]
- debug:
msg: files/some_file.txt {{ lookup('template', 'files/some_file.txt') }}
and the playbook
shell> cat test-17.yml
- hosts: localhost
roles:
- role-17
give
"msg": "task/main.yml [value1]"
"msg": "files/some_file.txt [value1]\n"

Related

Copy json file from windows to linux in seperate awx jobs

I created a Worflow job in awx containing 2 jobs:
Job 1 is using the credentials of the windows server where we get the json file from. It reads the content and put it in a variable using set_stats
Job2 is using the credential of the server where to upload the json file. It reads the content of the variable set in the job 1 in the set_stats task and creates a json file with the content.
First job:
- name: get content
win_shell: 'type {{ file_dir }}{{ file_name }}'
register: content
- name: write content
debug:
msg: "{{ content.stdout_lines }} "
register: result
- set_fact:
this_local: "{{ content.stdout_lines }}"
- set_stats:
data:
test_stat: "{{ this_local }}"
- name: set hostname in a variable
set_stats:
data:
current_hostname: "{{ ansible_hostname }}"
per_host: no
Second job
- name: convert to json and copy the file to destination control node.
copy:
content: "{{ test_stat | to_json }}"
dest: "/tmp/{{ current_hostname }}.json"
How can I get the current_hostname, so that the the created json file is named <original_hostname>.json? In my case its concatenating the two hosts which I passed in the first job.
In my case its concatenating the two hosts which I passed in the first job
... which is precisely what you asked for since you used per_host: no as parameter to set_stats to gather the current_hostname stat globally for all host and that aggregate: yes is the default.
Anyhow, this is not exactly the intended use of set_stats and you are making this overly complicated IMO.
You don't need two jobs. In this particular case, you can delegate the write task to a linux host in the middle of a play dedicated to windows hosts (and one awx job can use several credentials).
Here is a pseudo untested playbook to give you the idea. You'll want to read the slurp module documentation which I used to replace your shell task to read the file (which is a bad practice).
Assuming your inventory looks something like:
---
windows_hosts:
hosts:
win1:
win2:
linux_hosts:
hosts:
json_file_target_server:
The playbook would look like:
- name: Gather jsons from win and write to linux target
hosts: windows_hosts
tasks:
- name: Get file content
slurp:
src: "{{ file_dir }}{{ file_name }}"
register: json_file
- name: Push json content to target linux
copy:
content: "{{ json_file.content | b64decode | to_json }}"
dest: "/tmp/{{ inventory_hostname }}.json"
delegate_to: json_file_target_server

Use value of CLI variable as the name of a host_vars variable?

Is there a way to use the value of one Ansible variable as the name of another variable so I can extract a value from its list?
host_vars:
this:
does: walk
says: hi
that:
does: run
says: hello
On the CLI when I run the playbook, I add -e="thing=this".
In the playbook, I've tried all manner of things to expand the variable thing to its value this, then use this to extract the value of does in the host_vars file.
Using the variable name directly obviously works:
- name: Check what the thing does
debug:
msg: "{{ this['does'] }}"
But the following do not:
{{ thing['does'] }}
{{ {{ thing }}['does'] }}
Those, plus several other iterations I've tried all either throw an error or print out the literal string.
You need the vars lookup plugin to address variables indirectly. See
shell> ansible-doc -t lookup vars
For example,
- debug:
msg: "{{ lookup('vars', thing).does }}"
should give (abridged)
shell> ansible-playbook pb.yml -e "thing=this"
...
msg: walk
Example of a complete playbook for testing
- hosts: localhost
vars:
this:
does: walk
says: hi
that:
does: run
says: hello
tasks:
- debug:
msg: "{{ lookup('vars', thing).does }}"
You can simplify the code further by putting all declarations into the vars. For example, into the group_vars/all
shell> cat group_vars/all/thing.yml
this:
does: walk
says: hi
that:
does: run
says: hello
_thing: "{{ lookup('vars', thing) }}"
Then, the simplified playbook below gives the same results
- hosts: localhost
tasks:
- debug:
var: _thing.does

How to get the current file name in ansible?

I am having an ansible script. It calls other scripts using include module.
I need to get the current file name in a variable.
For ex:
- include: Run-Config889.yml
Inside Run-Config889.yml, i need to get the file name Run-Config889.yml in a variable.
Whether any built-in variable is there to find the current file name? if so what it is?
You can get the current playbook file name with :
---
- hosts: 127.0.0.1
connection: local
gather_facts: no
vars:
playbook_absoluteName: "{{ (lookup('file', '/proc/self/cmdline') | regex_replace('\u0000',' ')).split() | select('match','^.*[.]ya?ml$') | list | first }}"
playbook_baseName: "{{ playbook_absoluteName | basename }}"
tasks:
- debug:
var: item
loop:
- "{{ playbook_absoluteName }}"
- "{{ playbook_baseName }}"
...
This was inspired by this blog article, with minor/cosmetics improvements.
Don't know about any built-in variable but there are 2 options I can think of.
Option 1
You can pass variables to included files like so:
main.yml
---
- include: Run-Config889.yml file_name_variable="Run-Config889.yml"
Option 2
Set the variable in any of the vars files used by the play. All defined variables in a play are inherited by subsequent include statements.
sample-vars.yml
---
file_name_variable: "Run-Config889.yml"
Using the variable.
Run-Config889.yml
---
- name: Display variable
debug:
msg: File name {{ file_name_variable }}

Ansible items in separate files

Is it possible to have few .yml files and then read them as separate items for task?
Example:
- name: write templates
template: src=template.j2 dest=/some/path
with_items: ./configs/*.yml
I have found pretty elegant solution:
---
- hosts: localhost
vars:
my_items: "{{ lookup('fileglob', './configs/*.yml', wantlist=True) }}"
tasks:
- name: write templates
template: src=template.j2 dest=/some/path/{{ (item | from_yaml).name }}
with_file: "{{ my_items }}"
And then in template you have to add {% set item = (item | from_yaml) %} at the beginning.
Well, yes and no. You can loop over files and even use their content as variables. But the template module does not take parameters. There is an ugly workaround by using an include statement. Includes do take parameters and if the template task is inside the included file it will have access to them.
Something like this should work:
- include: other_file.yml parameters={{ lookup('file', item) | from_yaml }}
with_fileglob: ./configs/*.yml
And in other_file.yml then the template task:
- name: write template
template: src=template.j2 dest=/some/path
The ugly part here, beside the additional include, is that the include statement only takes parameters in the format of key=value. that's what you see in above task as parameters=.... parameters here has no special meaning, it just is the name of the variable with which the content of the file will be available inside the include.
So if your vars files have a variable foo defined, you would be able to access it in the template as {{ parameters.foo }}.

Ansible: Set variable to file content

I'm using the ec2 module with ansible-playbook I want to set a variable to the contents of a file. Here's how I'm currently doing it.
Var with the filename
shell task to cat the file
use the result of the cat to pass to the ec2 module.
Example contents of my playbook.
vars:
amazon_linux_ami: "ami-fb8e9292"
user_data_file: "base-ami-userdata.sh"
tasks:
- name: user_data_contents
shell: cat {{ user_data_file }}
register: user_data_action
- name: launch ec2-instance
local_action:
...
user_data: "{{ user_data_action.stdout }}"
I assume there's a much easier way to do this, but I couldn't find it while searching Ansible docs.
You can use lookups in Ansible in order to get the contents of a file, e.g.
user_data: "{{ lookup('file', user_data_file) }}"
Caveat: This lookup will work with local files, not remote files.
Here's a complete example from the docs:
- hosts: all
vars:
contents: "{{ lookup('file', '/etc/foo.txt') }}"
tasks:
- debug: msg="the value of foo.txt is {{ contents }}"
You can use the slurp module to fetch a file from the remote host: (Thanks to #mlissner for suggesting it)
vars:
amazon_linux_ami: "ami-fb8e9292"
user_data_file: "base-ami-userdata.sh"
tasks:
- name: Load data
slurp:
src: "{{ user_data_file }}"
register: slurped_user_data
- name: Decode data and store as fact # You can skip this if you want to use the right hand side directly...
set_fact:
user_data: "{{ slurped_user_data.content | b64decode }}"
You can use fetch module to copy files from remote hosts to local, and lookup module to read the content of fetched files.
lookup only works on localhost. If you want to retrieve variables from a variables file you made remotely use include_vars: {{ varfile }} . Contents of {{ varfile }} should be a dictionary of the form {"key":"value"}, you will find ansible gives you trouble if you include a space after the colon.

Resources