Reputation: 79
How do I get the value of a variable from the inventory if the variable could be either in the inventory file or group_vars
directory?
For example, region=place-a
could be in the inventory file or in a file in the group_vars
somewhere. I would like a command to be able to retrieve that value using ansible or something retrieve that value. like:
$ ansible -i /somewhere/production/web --get-value region
place-a
That would help me with deployments and knowing which region is being deployed to.
.
A longer explanation to clarify, my inventory structure looks like this:
/somewhere/production/web
/somewhere/production/group_vars/web
The contents with the variables of the inventory file, /somewhere/production/web
looks like this:
[web:children]
web1 ansible_ssh_host=10.0.0.1
web2 ansible_ssh_host=10.0.0.2
[web:vars]
region=place-a
I could get the value from the inventory file by simply parsing the file. like so:
$ awk -F "=" '/^region/ {print $2}' /somewhere/production/web
place-a
But that variable could be in the group_vars
file, too. For example:
$ cat /somewhere/production/group_vars/web
region: place-a
Or it could look like an array:
$ cat /somewhere/production/group_vars/web
region:
- place-a
I don't want to look for and parse all the possible files.
Does Ansible have a way to get the values? Kind of like how it does with --list-hosts
?
$ ansible web -i /somewhere/production/web --list-hosts
web1
web2
Upvotes: 5
Views: 10496
Reputation: 154
The best way I found so far is using the debug module w/ JSON output plugin. Obviously, this requires calling ansible. The advantage is that it's exactly what your playbooks sees when running, i.e. you get all your group_vars merged and "{{ jinja_expressions }}" replaced (ansible-inventory doesn't evaluate Jinja for you).
ANSIBLE_LOAD_CALLBACK_PLUGINS=true \
ANSIBLE_STDOUT_CALLBACK=json \
ansible \
--connection local \
--inventory=inventory \
-m debug \
-a var="hostvars" \
localhost \
| jq \
--arg host somehost \
--arg var somevar \
--raw-output ' \
.plays[0].tasks[0].hosts.localhost
| .hostvars[$host][$var]'
This is also very useful for exporting the full hostvars w/ all hosts to JSON.
Upvotes: 1
Reputation: 6547
NO there is no CLI option to get value of variables.
First of all, you must look into how variable precedence works in Ansible.
The exact order is defined in the documentation. Here is an summary excerpt:
Basically, anything that goes into “role defaults” (the defaults folder inside the role) is the most malleable and easily overridden. Anything in the vars directory of the role overrides previous versions of that variable in namespace. The idea here to follow is that the more explicit you get in scope, the more precedence it takes with command line -e extra vars always winning. Host and/or inventory variables can win over role defaults, but not explicit includes like the vars directory or an include_vars task.
With that cleared out, the only way to see what value is a variable taking is to use the debug
task as follows:
- name: debug a variable
debug:
var: region
This will print out the value of the variable region
.
My suggestion would be to maintain a single value type, either a string
or a list
to prevent confusion across tasks
in different playbooks
.
You could use something like this:
- name: deploy to regions
<task_type>:
<task_options>: <task_option_value>
// use the region variable as you wish
region: {{ item }}
...
with_items: "{{ regions.split(',') }}"
In this case you could have a variable regions=us-west,us-east
comma-separated. The with_items
statement would split it over the ,
and repeat the task for all the regions.
Upvotes: 1
Reputation: 611
The CLI version of this is important for people who are trying to connect ansible to other systems. Building on the usage of the copy
module elsewhere, if you have a POSIX mktemp
and a copy of jq locally, then here's a bash one-liner that does the trick from the CLI:
export TMP=`mktemp` && ansible localhost -c local -i inventory.yml -m copy -a "content={{hostvars['SOME_HOSTNAME']}} dest=${TMP}" >/dev/null && cat ${TMP}|jq -r .SOME_VAR_NAME && rm ${TMP}
Breaking it down
# create a tempfile
export TMP=`mktemp`
# quietly use Ansible in local only mode, loading inventory.yml
# digging up the already-merged global/host/group vars
# for SOME_HOSTNAME, copying them to our tempfile from before
ansible localhost --connection local \
--inventory inventory.yml --module-name copy \
--args "content={{hostvars['SOME_HOSTNAME']}} dest=${TMP}" \
> /dev/null
# CLI-embedded ansible is a bit cumbersome. After all the data
# is exported to JSON, we can use `jq` to get a lot more flexibility
# out of queries/filters on this data. Here we just want a single
# value though, so we parse out SOME_VAR_NAME from all the host variables
# we saved before
cat ${TMP}|jq -r .SOME_VAR_NAME
rm ${TMP}
Upvotes: 1
Reputation: 23
Another more easy solution get a variable through cli from ansible could be this:
export tmp_file=/tmp/ansible.$RANDOM
ansible -i <inventory> localhost -m copy -a "content={{ $VARIABLE_NAME }} dest=$tmp_file"
export VARIBALE_VALUE=$(cat $tmp_file)
rm -f $tmp_file
Looks quite ugly but is really helpful.
Upvotes: 0