What is the best way to dynamically add variables as Ansible Facts? - ansible

I have an Ansible task where I navigate to a YAML variable file in GitHub, download the file, and add the variables as Ansible Facts where they're later used.
My YAML file looks like:
---
foo: bar
hello: world
I have a method where I loop over this file, and individually add the key/value pairs as the facts:
- name: Grab contents of variable file
win_shell: cat '{{ playbook_dir }}/DEV1.yml'
register: raw_config
- name: Add variables to workspace
vars:
config: "{{ raw_config.stdout | from_yaml }}"
set_fact:
"{{ item.key }}": "{{ item.value }}"
loop: "{{ config | dict2items }}"
This works but generates much larger log outputs that look like:
ok: [localhost] => (item={u'key': u'foo', u'value': u'bar'}) => {
"ansible_facts": {
"foo": "bar"
},
"ansible_loop_var": "item",
"changed": false,
"item": {
"key": "foo",
"value": "bar"
}
}
ok: [localhost] => (item={u'key': u'hello', u'value': u'world'}) => {
"ansible_facts": {
"hello": "world"
},
"ansible_loop_var": "item",
"changed": false,
"item": {
"key": "hello",
"value": "world"
}
}
I was wondering if it was possible to add the entire variable file as Ansible Facts instead of needing to loop through it. The way I tried was like:
- name: Grab contents of variable file
win_shell: cat '{{ playbook_dir }}/DEV1.yml'
register: raw_config
- name: Add variables to workspace
vars:
config: '{{ raw_config.stdout | from_yaml }}'
set_fact: '{{ config }}'
This almost works, but it looks like this:
ok: [msf1vpom04d.corp.tjxcorp.net] => {
"ansible_facts": {
"_raw_params": {
"foo": "bar",
"hello": "world"
…
Can I add the entire object as Ansible Facts without generating this _raw_params object?

... where I navigate to a YAML variable file in GitHub, download the file, and add the variables ... I was wondering if it was possible to add the entire variable file ...
There are several possibilities.
One option (annot.: like in Ansible Tower) can be to checkout, download, sync the variable file before executing the playbook. To do so
curl --silent --user "${ACCOUNT}:${PASSWORD}" -X GET "https://${REPOSITORY_URL}/raw/group_vars/test?at=refs%2Fheads%2Fmaster" -o group_vars/test && \
sshpass -p ${PASSWORD} ansible-playbook --user ${ACCOUNT} --ask-pass test.yml
... used a Bitbucket URL here for demonstration and test
The advantage of this approach is that there is no implementation or logic within the playbooks necessary at all. The only requirement is just to Organize host and group variables. Furthermore it is the there recommended approach
Keeping your inventory file and variables in a git repo (or other version control) is an excellent way to track changes to your inventory and host variables.
An other option would be to use include_vars module to
Loads YAML/JSON variables dynamically from a file or directory, recursively, during task runtime.
In respect to simplicity it is still recommended to sync before execute.
Further Q&A
... and as already mentioned within the comments
Getting variable values from URL

You can take advantage of the play level vars_files parameter and the fact that it will be loaded and expanded for each running task. We just need to have a fallback file when your file does not yet locally exist so that we don't get an error (during facts gathering for example).
Here is an example with a uri of mine containing default values for an ansible role.
First, create an empty.yml file which will be empty as its name suggest. (It's adjacent to my playbook for the example but you can put it wherever your want, just reflect this in your playbook accordingly)
Then the following playbook:
---
- hosts: localhost
gather_facts: false
vars:
external_vars_uri: https://raw.githubusercontent.com/ansible-ThoTeam/nexus3-oss/main/defaults/main.yml
external_vars_file: /tmp/external_vars.yml
vars_files:
- "{{ lookup('first_found', [external_vars_file, 'empty.yml']) }}"
tasks:
- name: make sure we have our external file
get_url:
url: "{{ external_vars_uri }}"
dest: "{{ external_vars_file }}"
# Note: we're only using localhost here so the below
# parameters are useless. But they will be necessary
# if you target other (groups of) hosts.
run_once: true
delegate_to: localhost
- name: debug a var we know is in the external file
debug:
var: nexus_repos_maven_proxy
Gives:
$ ansible-playbook play.yml
PLAY [localhost] **************************************************************************************************************************************************************************************************
TASK [make sure we have our external file] ************************************************************************************************************************************************************************
changed: [localhost]
TASK [debug a var we know is in the external file] ****************************************************************************************************************************************************************************
ok: [localhost] => {
"nexus_repos_maven_proxy": [
{
"layout_policy": "permissive",
"name": "central",
"remote_url": "https://repo1.maven.org/maven2/"
},
{
"name": "jboss",
"remote_url": "https://repository.jboss.org/nexus/content/groups/public-jboss/"
}
]
}
PLAY RECAP ********************************************************************************************************************************************************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Related

Yml must be stored as a dictionary/hash

I have a yaml file of usernames and their ssh keys stored like this:
---
- user: bob
name: bob McBob
ssh_keys:
- ssh-rsa ...
- user: fred
name: fred McFred
ssh_keys:
- ssh-rsa ...
I'm trying to grab the user and ssh_keys keys so i can use this file to setup the users on linux hosts
It seems Ansible does not like the format of this file though, as this simple task throws an error:
- name: Get SSH Keys
include_vars:
file: ../admins.yml
name: ssh_keys
TASK [network-utility-servers : Get SSH Keys] *******************************************************************************************************************************
task path: main.yml:1
fatal: [127.0.0.1]: FAILED! => {
"ansible_facts": {
"ssh_keys": {}
},
"ansible_included_var_files": [],
"changed": false,
"message": "admins.yml must be stored as a dictionary/hash"
}
Unfortunately I can't change the format of the admins.yml file as it is used in other tools and changing the format will break them.
Any suggestions on how I can work around this?
Looks like ansible wants the admins.yml file to look like this:
---
foo:
- user: bob
name bob mcbob
ssh_keys:
- ssh-rsa ..
As you found out, include_vars is expecting a file containing dict key(s) at the top level. But there are other ways to read yaml files in ansible.
If you cannot change the file, the simplest way is to read its content inside a variable using a file lookup and the from_yaml filter.
Here is an example playbook. For this test, your above example data was stored in admins.yml in the same folder as the playbook. Adapt accordingly.
---
- hosts: localhost
gather_facts: false
vars:
admins: "{{ lookup('file', 'admins.yml') | from_yaml }}"
tasks:
- name: Show admin users list
debug:
var: admins
Which gives:
PLAY [localhost] ***********************************************************************************************************************************************************************************************************************
TASK [Show admin users list] ***********************************************************************************************************************************************************************************************************
ok: [localhost] => {
"admins": [
{
"name": "bob McBob",
"ssh_keys": [
"ssh-rsa ..."
],
"user": "bob"
},
{
"name": "fred McFred",
"ssh_keys": [
"ssh-rsa ..."
],
"user": "fred"
}
]
}
PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
As you said, the given vars file has not a valid format. You could use a task to fix the file in the fashion you described, and then attempt to load it.
The lineinfile task could be of use to do the trick, as in below example:
---
- hosts: localhost
gather_facts: false
vars:
tasks:
- name: fix vars file
lineinfile:
path: "{{ playbook_dir }}/vars_file.yml"
insertafter: "^---$"
line: "ssh_keys:"
backup: yes
- name: Get SSH Keys
include_vars:
file: "{{ playbook_dir }}/vars_file.yml"
- debug: var=ssh_keys
If you are not supposed to edit that file, you could copy to a new file name (vars_file_fixed.yml), then apply the lineinfile and load it.
cheers

Ansible 2.8: Access a list object element

In Ansible 2.8, I need to deploy and config Bind 9 DNS on Ubuntu Server VMs. I have a:
DNS Ansible role to do the installation and config,
a list of variables per domain zone (as DNS record type, domain name, dns entry,...). Until here, it works, the issues appear when I try to make it accept the next requirement:
potentially, several domain zones to configure in the same call, threfore, I send to it a list with groups of variables (mentioned in 2).
For now, in the shell, I call it with 1 element list, using:
--extra-vars "{"dns_entry_conf":
[domain=example.gal ip=192.168.167.166
nameserver1=example.gal nameserver1_ip=192.168.167.164
dns_record1_type=A ...]}"
Inside the role, the roles/dns/tasks/configure.yml file receives the right value, but the file that follows, doesn't: it says "list object has no attribute", and I started debugging in the configure.yml file, but I am not sure how to access the list object item:
---
- debug:
msg: "{{dns_entry_conf}}"
- debug:
msg: "{{dns_entry_conf | json_query(\"domain\") }}"
The first line prints what it should, but the 2nd does not... How I can access the value
ASK [dns : debug] **********************************************************************************
task path: /etc/ansible/roles/dns/tasks/configure.yml:2
ok: [ubuntuServer16_test] => {
"msg": [
"domain=example.gal ip=192.168.167.166 nameserver1=example.gal nameserver1_ip=192.168.167.164
dns_record1_type=A ...
]
}
TASK [dns : debug] **********************************************************************************
task path: /etc/ansible/roles/dns/tasks/configure.yml:4
ok: [ubuntuServer16_test] => {
"msg": ""
}
In debug, tried with the msg's: "{{ dns_entry_conf.domain }}", "{{ dns_entry_conf.0 }}", "{{dns_entry_conf | json_query(\"domain\") }}", "{{ dns_entry_conf.list | json_query('[*].domain') }}", and others that were sintactically wrong, but it never outputs what I want.
Probably there are more wrong things (I am no Ansible expert), but, for now, just trying to debug and fix one by one, so, I just want to know how I can access the "dns_entry_conf.domain" item, please... some idea?
Option1:
with extra-vars as below:
--extra-vars '{"dns_entry_conf":{"domain":example,"ip":1.2.3.4}}'
Playbook:
- debug:
msg: "{{dns_entry_conf.domain}}"
Output:
ok: [localhost] => {
"msg": "example"
}
Option2:
with extra vars as below:
--extra-vars '{"dns_entry_conf":["domain":example,"ip":1.2.3.4]}'
In Playbook tey as below:
- debug:
msg: "{{dns_entry_conf[0].domain}}"
Output:
ok: [localhost] => {
"msg": "example"
}
Option 3:
Pass the variables in the playbook.
vars:
dns_entry_conf:
domain: example
ip: 1.2.34.4
tasks:
- debug:
msg: "{{dns_entry_conf.domain}}"
Output:
ok: [localhost] => {
"msg": "example"
}

How to loop through inventory and assign value in Ansible

I have a task in my Ansible playbook that I'm wanting iterate over each host in the group that I have and for each host I would like to assign a name from the hostname list that I've created in the vars folder.
I'm familiar with looping through inventory already by writing loop: "{{ groups['mygroup'] }}" and I have a list of hostnames I would like to assign each IP in 'mygroup' within the host file.
# In tasks file - roles/company/tasks/main.yml
- name: change hostname
win_hostname:
name: "{{ item }}"
loop: "{{ hostname }}"
register: res
# In the Inventory file
[company]
10.0.10.128
10.0.10.166
10.0.10.200
# In vars - roles/company/vars/main.yml
hostname:
- GL-WKS-18
- GL-WKS-19
- GL-WKS-20
# site.yml file located under /etc/ansible
- hosts: company
roles:
- common
- company #This is where the loop exists mentioned above.
# Command to run playbook
ansible-playbook -i hosts company.yml
I seem to have the individual pieces down or know about it, but how can I combine iterating over hosts from an inventory group and assign names that I have in an already created list (in roles vars folder) already?
UPDATE
the task mentioned above has been updated to reflect changes mentioned in answer:
- name: change hostname
win_hostname:
name: "{{ item.1 }}"
loop: {{ groups.company|zip(hostname)|list }}"
register: res
However the output I'm getting is incorrect, this should not run 9 times rather only three times, once per IP in the [company] group in the inventory. Also there are only three hostnames in a list that need to be assigned to each of the hosts in the inventory sheet.
changed: [10.0.10.128] => (item=[u'10.0.10.128', u'GL-WKS-18'])
changed: [10.0.10.166] => (item=[u'10.0.10.128', u'GL-WKS-18'])
changed: [10.0.10.200] => (item=[u'10.0.10.128', u'GL-WKS-18'])
changed: [10.0.10.128] => (item=[u'10.0.10.166', u'GL-WKS-19'])
changed: [10.0.10.166] => (item=[u'10.0.10.166', u'GL-WKS-19'])
changed: [10.0.10.200] => (item=[u'10.0.10.166', u'GL-WKS-19'])
ok: [10.0.10.128] => (item=[u'10.0.10.200', u'GL-WKS-20'])
ok: [10.0.10.166] => (item=[u'10.0.10.200', u'GL-WKS-20'])
ok: [10.0.10.200] => (item=[u'10.0.10.200', u'GL-WKS-20'])
Whenever I have a question about looping in Ansible I also go visit the Loops documentation. It sounds like you want to iterate over two lists in parallel, pairing an item from the list of hosts in your inventory with an item from the list of hostnames. In previous versions of Ansible that would suggest using the with_together loop, while with more recent versions of Ansible that suggests the zip filter (there's an example in the docs here).
To demonstrate this for your use case, I started with an inventory file that has three hosts:
[mygroup]
hostA ansible_host=localhost
hostB ansible_host=localhost
hostC ansible_host=localhost
And the following playbook:
---
- hosts: all
- hosts: localhost
gather_facts: false
vars:
hostnames:
- hostname01
- hostname02
- hostname03
tasks:
- name: change hostname
debug:
msg:
win_hostname:
name: "{{ item }}"
loop: "{{ groups.mygroup|zip(hostnames)|list }}"
Here I'm using a debug task instead of actually running the win_hostname task. The output of running:
ansible-playbook -i hosts playbook.yml
Looks like:
TASK [change hostname] ********************************************************************************************************************************
ok: [localhost] => (item=[u'hostA', u'hostname01']) => {
"msg": {
"win_hostname": {
"name": [
"hostA",
"hostname01"
]
}
}
}
ok: [localhost] => (item=[u'hostB', u'hostname02']) => {
"msg": {
"win_hostname": {
"name": [
"hostB",
"hostname02"
]
}
}
}
ok: [localhost] => (item=[u'hostC', u'hostname03']) => {
"msg": {
"win_hostname": {
"name": [
"hostC",
"hostname03"
]
}
}
}
As you can see, it's paired each host from the inventory with a hostname from the hostnames list.
Update
Based on the additional information you've provided, I think what you
actually want is this:
- name: change hostname
win_hostname:
name: "{{ hostnames[group.company.index(inventory_hostname) }}"
This will assign one value from hostname to each host in your
inventory. We're looking up the position of the current
inventory_hostname in your group, and then using that to index into
the hostnames list.

How do I register a variable and persist it between plays targeted on different nodes?

I have an Ansible playbook, where I would like a variable I register in a first play targeted on one node to be available in a second play, targeted on another node.
Here is the playbook I am using:
---
- hosts: localhost
gather_facts: no
tasks:
- command: echo "hello world"
register: foo
- hosts: main
gather_facts: no
tasks:
- debug:
msg: {{ foo.stdout }}
But, when I try to access the variable in the second play, targeted on main, I get this message:
The task includes an option with an undefined variable. The error was: 'foo' is undefined
How can I access foo, registered on localhost, from main?
The problem you're running into is that you're trying to reference facts/variables of one host from those of another host.
You need to keep in mind that in Ansible, the variable foo assigned to the host localhost is distinct from the variable foo assigned to the host main or any other host.
If you want to access one hosts facts/variables from another host then you need to explicitly reference it via the hostvars variable. There's a bit more of a discussion on this in this question.
Suppose you have a playbook like this:
- hosts: localhost
gather_facts: no
tasks:
- command: echo "hello world"
register: foo
- hosts: localhost
gather_facts: no
tasks:
- debug:
var: foo
This will work because you're referencing the host localhost and localhosts's instance of the variable foo in both plays.
The output of this playbook is something like this:
PLAY [localhost] **************************************************
TASK: [command] ***************************************************
changed: [localhost]
PLAY [localhost] **************************************************
TASK: [debug] *****************************************************
ok: [localhost] => {
"var": {
"foo": {
"changed": true,
"cmd": [
"echo",
"hello world"
],
"delta": "0:00:00.004585",
"end": "2015-11-24 20:49:27.462609",
"invocation": {
"module_args": "echo \"hello world\",
"module_complex_args": {},
"module_name": "command"
},
"rc": 0,
"start": "2015-11-24 20:49:27.458024",
"stderr": "",
"stdout": "hello world",
"stdout_lines": [
"hello world"
],
"warnings": []
}
}
}
If you modify this playbook slightly to run the first play on one host and the second play on a different host, you'll get the error that you encountered.
Solution
The solution is to use Ansible's built-in hostvars variable to have the second host explicitly reference the first hosts variable.
So modify the first example like this:
- hosts: localhost
gather_facts: no
tasks:
- command: echo "hello world"
register: foo
- hosts: main
gather_facts: no
tasks:
- debug:
var: foo
when: foo is defined
- debug:
var: hostvars['localhost']['foo']
## alternatively, you can use:
# var: hostvars.localhost.foo
when: hostvars['localhost']['foo'] is defined
The output of this playbook shows that the first task is skipped because foo is not defined by the host main.
But the second task succeeds because it's explicitly referencing localhosts's instance of the variable foo:
TASK: [debug] *************************************************
skipping: [main]
TASK: [debug] *************************************************
ok: [main] => {
"var": {
"hostvars['localhost']['foo']": {
"changed": true,
"cmd": [
"echo",
"hello world"
],
"delta": "0:00:00.005950",
"end": "2015-11-24 20:54:04.319147",
"invocation": {
"module_args": "echo \"hello world\"",
"module_complex_args": {},
"module_name": "command"
},
"rc": 0,
"start": "2015-11-24 20:54:04.313197",
"stderr": "",
"stdout": "hello world",
"stdout_lines": [
"hello world"
],
"warnings": []
}
}
}
So, in a nutshell, you want to modify the variable references in your main playbook to reference the localhost variables in this manner:
{{ hostvars['localhost']['foo'] }}
{# alternatively, you can use: #}
{{ hostvars.localhost.foo }}
Use a dummy host and its variables
For example, to pass a Kubernetes token and hash from the master to the workers.
On master
- name: "Cluster token"
shell: kubeadm token list | cut -d ' ' -f1 | sed -n '2p'
register: K8S_TOKEN
- name: "CA Hash"
shell: openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'
register: K8S_MASTER_CA_HASH
- name: "Add K8S Token and Hash to dummy host"
add_host:
name: "K8S_TOKEN_HOLDER"
token: "{{ K8S_TOKEN.stdout }}"
hash: "{{ K8S_MASTER_CA_HASH.stdout }}"
- name:
debug:
msg: "[Master] K8S_TOKEN_HOLDER K8S token is {{ hostvars['K8S_TOKEN_HOLDER']['token'] }}"
- name:
debug:
msg: "[Master] K8S_TOKEN_HOLDER K8S Hash is {{ hostvars['K8S_TOKEN_HOLDER']['hash'] }}"
On worker
- name:
debug:
msg: "[Worker] K8S_TOKEN_HOLDER K8S token is {{ hostvars['K8S_TOKEN_HOLDER']['token'] }}"
- name:
debug:
msg: "[Worker] K8S_TOKEN_HOLDER K8S Hash is {{ hostvars['K8S_TOKEN_HOLDER']['hash'] }}"
- name: "Kubeadmn join"
shell: >
kubeadm join --token={{ hostvars['K8S_TOKEN_HOLDER']['token'] }}
--discovery-token-ca-cert-hash sha256:{{ hostvars['K8S_TOKEN_HOLDER']['hash'] }}
{{K8S_MASTER_NODE_IP}}:{{K8S_API_SERCURE_PORT}}
I have had similar issues with even the same host, but across different plays. The thing to remember is that facts, not variables, are the persistent things across plays. Here is how I get around the problem.
#!/usr/local/bin/ansible-playbook --inventory=./inventories/ec2.py
---
- name: "TearDown Infrastructure !!!!!!!"
hosts: localhost
gather_facts: no
vars:
aws_state: absent
vars_prompt:
- name: "aws_region"
prompt: "Enter AWS Region:"
default: 'eu-west-2'
tasks:
- name: Make vars persistant
set_fact:
aws_region: "{{aws_region}}"
aws_state: "{{aws_state}}"
- name: "TearDown Infrastructure hosts !!!!!!!"
hosts: monitoring.ec2
connection: local
gather_facts: no
tasks:
- name: set the facts per host
set_fact:
aws_region: "{{hostvars['localhost']['aws_region']}}"
aws_state: "{{hostvars['localhost']['aws_state']}}"
- debug:
msg="state {{aws_state}} region {{aws_region}} id {{ ec2_id }} "
- name: last few bits
hosts: localhost
gather_facts: no
tasks:
- debug:
msg="state {{aws_state}} region {{aws_region}} "
results in
Enter AWS Region: [eu-west-2]:
PLAY [TearDown Infrastructure !!!!!!!] ***************************************************************************************************************************************************************************************************
TASK [Make vars persistant] **************************************************************************************************************************************************************************************************************
ok: [localhost]
PLAY [TearDown Infrastructure hosts !!!!!!!] *********************************************************************************************************************************************************************************************
TASK [set the facts per host] ************************************************************************************************************************************************************************************************************
ok: [XXXXXXXXXXXXXXXXX]
TASK [debug] *****************************************************************************************************************************************************************************************************************************
ok: [XXXXXXXXXXX] => {
"changed": false,
"msg": "state absent region eu-west-2 id i-0XXXXX1 "
}
PLAY [last few bits] *********************************************************************************************************************************************************************************************************************
TASK [debug] *****************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"changed": false,
"msg": "state absent region eu-west-2 "
}
PLAY RECAP *******************************************************************************************************************************************************************************************************************************
XXXXXXXXXXXXX : ok=2 changed=0 unreachable=0 failed=0
localhost : ok=2 changed=0 unreachable=0 failed=0
You can use an Ansible known behaviour. That is using group_vars folder to load some variables at your playbook. This is intended to be used together with inventory groups, but it is still a reference to the global variable declaration. If you put a file or folder in there with the same name as the group, you want some variable to be present, Ansible will make sure it happens!
As for example, let's create a file called all and put a timestamp variable there. Then, whenever you need, you can call that variable, which will be available to every host declared on any play inside your playbook.
I usually do this to update a timestamp once at the first play and use the value to write files and folders using the same timestamp.
I'm using lineinfile module to change the line starting with timestamp :
Check if it fits for your purpose.
On your group_vars/all
timestamp: t26032021165953
On the playbook, in the first play:
hosts: localhost
gather_facts: no
- name: Set timestamp on group_vars
lineinfile:
path: "{{ playbook_dir }}/group_vars/all"
insertafter: EOF
regexp: '^timestamp:'
line: "timestamp: t{{ lookup('pipe','date +%d%m%Y%H%M%S') }}"
state: present
On the playbook, in the second play:
hosts: any_hosts
gather_facts: no
tasks:
- name: Check if timestamp is there
debug:
msg: "{{ timestamp }}"

Ansible - read inventory hosts and variables to group_vars/all file

I have a dummy doubt that keeps me stuck for a long time. I have a very banal inventory file with hosts and variables:
[lb]
10.112.84.122
[tomcat]
10.112.84.124
[jboss5]
10.112.84.122
...
[tests:children]
lb
tomcat
jboss5
[default:children]
tests
[tests:vars]
data_base_user=NETWIN-4.3
data_base_password=NETWIN
data_base_encrypted_password=
data_base_host=10.112.69.48
data_base_port=1521
data_base_service=ssdenwdb
data_base_url=jdbc:oracle:thin:#10.112.69.48:1521/ssdenwdb
The problem is that I need to access all these hosts and variables, in the inventory file, from the group_vars/all file.
I've tried the following manners to access the host IP:
{{ lb }}
"{{ hostvars[lb] }}"
"{{ hostvars['lb'] }}"
{{ hostvars[lb] }}
To access a host variable I tried:
"{{ hostvars[tests].['data_base_host'] }}"
All of them are wrong!!! Can anyone help me find out the best practice to access hosts and variables, not from a playbook but from a variables file?
EDIT:
Ok. Let's clarify.
Problem: Use a host declared in the inventory file in a variable file, let's say: group_vars/all.
Example: I have a DB host with IP:10.112.83.37.
Inventory file:
[db]
10.112.83.37
In the group:vars/all file I want to use that IP to build a variable.
group_vars/all file:
data_base_url=jdbc:oracle:thin:#{{ db }}:1521/ssdenwdb
In a template I use the variable built in the group_vars/all file.
Template file:
oracle_url = {{ data_base_url }}
The problem is that the {{ db }} variable in the group_vars/all file is not replaced by the DB host IP. The user can only edit the inventory file.
- name: host
debug: msg="{{ item }}"
with_items:
- "{{ groups['tests'] }}"
This piece of code will give the message:
'10.112.84.122'
'10.112.84.124'
as groups['tests'] basically return a list of unique ip addresses ['10.112.84.122','10.112.84.124'] whereas groups['tomcat'][0] returns 10.112.84.124.
If you want to programmatically access the inventory entries to include them in a task for example. You can refer to it like this:
{{ hostvars.tomcat }}
This returns you a structure with all variables related with that host. If you want just an IP address (or hostname), you can refer to it like this:
{{ hostvars.jboss5.ansible_ssh_host }}
Here is a list of variables which you can refer to: click. Moreover, you can declare a variable and set it with for example result of some step in a playbook.
- name: Change owner and group of some file
file: path=/tmp/my-file owner=new-owner group=new-group
register: chown_result
Then if you play this step on tomcat, you can access it from jboss5 like this:
- name: Print out the result of chown
debug: msg="{{ hostvars.tomcat.chown_result }}"
Just in case if the problem is still there,
You can refer to ansible inventory through ‘hostvars’, ‘group_names’, and ‘groups’ ansible variables.
Example:
To be able to get ip addresses of all servers within group "mygroup", use the below construction:
- debug: msg="{{ hostvars[item]['ansible_eth0']['ipv4']['address'] }}"
with_items:
- "{{ groups['mygroup'] }}"
Considering your previous example:
inventory file:
[db]
10.112.83.37
group_vars/all
data_base_url=jdbc:oracle:thin:#{{ db }}:1521/ssdenwdb
template file:
oracle_url = {{ data_base_url }}
You might want to replace your group_vars/all with
data_base_url="jdbc:oracle:thin:#{{ groups['db'][0] }}:1521/ssdenwdb"
Yes the example by nixlike works very well.
Inventory:
[docker-host]
myhost1 user=barbara
myhost2 user=heather
playbook:
---
- hosts: localhost
connection: local
tasks:
- name: loop debug inventory hostnames
debug:
msg: "the docker host is {{ item }}"
with_inventory_hostnames: docker-host
- name: loop debug items
debug:
msg: "the docker host is {{ hostvars[item]['user'] }}"
with_items: "{{ groups['docker-host'] }}"
output:
ansible-playbook ansible/tests/vars-test-local.yml
PLAY [localhost]
TASK [setup]
******************************************************************* ok: [localhost]
TASK [loop debug inventory hostnames]
****************************************** ok: [localhost] => (item=myhost2) => {
"item": "myhost2",
"msg": "the docker host is myhost2" } ok: [localhost] => (item=myhost1) => {
"item": "myhost1",
"msg": "the docker host is myhost1" }
TASK [loop debug items]
******************************************************** ok: [localhost] => (item=myhost1) => {
"item": "myhost1",
"msg": "the docker host is barbara" } ok: [localhost] => (item=myhost2) => {
"item": "myhost2",
"msg": "the docker host is heather" }
PLAY RECAP
********************************************************************* localhost : ok=3 changed=0 unreachable=0
failed=0
thanks!
If you want to refer one host define under /etc/ansible/host in a task or role, the bellow link might help:
https://www.middlewareinventory.com/blog/ansible-get-ip-address/
If you want to have your vars in files under group_vars, just move them here. Vars can be in the inventory ([group:vars] section) but also (and foremost) in files under group_vars or hosts_vars.
For instance, with your example above, you can move your vars for group tests in the file group_vars/tests :
Inventory file :
[lb]
10.112.84.122
[tomcat]
10.112.84.124
[jboss5]
10.112.84.122
...
[tests:children]
lb
tomcat
jboss5
[default:children]
tests
group_vars/tests file :
data_base_user=NETWIN-4.3
data_base_password=NETWIN
data_base_encrypted_password=
data_base_host=10.112.69.48
data_base_port=1521
data_base_service=ssdenwdb
data_base_url=jdbc:oracle:thin:#10.112.69.48:1521/ssdenwdb

Resources