user3358549
user3358549

Reputation: 79

Ansible: How to get a value of a variable from the inventory or group_vars file?

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

Answers (4)

Timur
Timur

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

nyumerics
nyumerics

Reputation: 6547

Short Answer

NO there is no CLI option to get value of variables.

Long Answer

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

mvr
mvr

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

user1945826
user1945826

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

Related Questions