How to access group variables from inventory file inside ansible yaml - ansible

I have an inventory file with one group like below:
[example1]
H1 ip1 username1
H2 ip2 username2
H3 ip3 username3
and I have defined group variable like below to make that variable common to all hosts in that group:
[example1:vars]
temp='H2'
I am trying to access this variable inside my ansible yml under hosts field like below:
---
- name: temp playbook to practice hostvars
hosts: "{{ hostvars['example1']['temp'] }}"
tasks:
.....
.....
....
But while executing this playbook, I am getting the hostvars undefined error like below:
"ERROR! The field 'hosts' has an invalid value, which includes an undefined variable. The error was: 'hostvars' is undefined"
My ansible file and inventory file in same folder, could anyone help me to rectify what I am missing to access the variable from inventory file?

Please, don't do it like that. It's bad way. Use groups with 'children' property for host grouping.
Nevertheless, here is a fix for your problem. You've tried to access hostvars for a group. There is no host 'example1' in your inventory. And hostvars have variables for host, as you can see.
Following code will do the trick:
- hosts: localhost
gather_facts: no
tasks:
- debug:
- hosts: '{{ hostvars[groups.example1[0]].temp }}'
tasks:
...
I use groups.example1 to get a list of hosts in a group example1, then I take first member of that group ([0]) then I peek into its variables through hostvars.
The strange task with debug at the begin need to help Ansible to initialize hostvars. Without it, it will fail with undefined variable error.
But, again, don't do it like that. It's really, really mess up way to reinvent groups.

I know it's an old question, but just found the solution for this problem. Just put this before using hostvars in the next task or role and somehow you will be able to use the hostvars variables into hosts: definition.
- name: Do nothing
hosts: all
gather_facts: no
become: no
it seems that there is no need for run_once: yes (maybe only for some performance improvment, if any)
I can't say I understand what's under the hood, but it seems that ansible (2.9, 2.10) need something bogus in order to use hostvars variable in the next tasks/roles.

Related

How to read a specific line from the file and register it as a variable and pass it to another role in ansible

I am creating a azure image using ansible roles, below is the sample of my play book:
- hosts: localhost
gather_facts: yes
roles:
- azure_vmcreator
- hosts: azure_vms
gather_facts: no
roles:
- vm_software
- download_Artifact
- hosts: localhost
gather_facts: yes
roles:
- azure_imagecreator
The idea is to download a f.exe file in download_Artifact role and install it in the vm, on installation f.exe will create a product.id file. I want to read the second line of that file and register it as a variable say image_prefix, so that i can use image_prefix variable in azure_imagecreator role and create a image adding it in the prefix.
I am new to ansible can any one help me out with this ?
Accessing variables set on another host in a playbook isn't very straightforward. The general workflow would look like this:
In download_Artifact use the set_fact module to save the line in question as a host_var. ( set_fact always stores variables as host vars).
You can use with_file to get the contents of a file into a variable and then use jinja to get the second line of the file:
- name: Print Second line of a File
debug:
msg: "{{ item.split("\n")[1] }}"
with_file:
- "/filepath"
What the jinja2 snippet does, is splitting the file on newlines, converting it to an array of the lines, and then accessing the second line (Array indices start with 0).
Instead of printing the contents, you would need to use the set_fact module to save that string into a variable.
Then, In your third playbook, pass the hostvar you've set as an argument to the role. Use can use hostvars.<hostname>.<varname> to access the hostvars of another host.
If you don't know the hostname beforehand, you can use ansibles special variable groups to get the name during playbook execution. The call would then look somewhat like this (haven't tested this though and assuming your role accepts a variable called image_prefix):
- hosts: localhost
gather_facts: yes
roles:
- name: azure_imagecreator
azure_imagecreator:
image_prefix: hostvars[groups.azure_vms[0]].image_prefix

How to use variables between different roles in ansible

my playbook structure looks like:
- hosts: all
name: all
roles:
- roles1
- roles2
In tasks of roles1, I define such a variable
---
# tasks for roles1
- name: Get the zookeeper image tag # rel3.0
run_once: true
shell: echo '{{item.split(":")[-1]}}' # Here can get the string rel3.0 normally
with_items: "{{ret.stdout.split('\n')}}"
when: "'zookeeper' in item"
register: zk_tag
ret.stdout:
Loaded image: test/old/kafka:latest
Loaded image: test/new/mysql:v5.7
Loaded image: test/old/zookeeper:rel3.0
In tasks of roles2, I want to use the zk_tag variable
- name: Test if the variable zk_tag can be used in roles2
debug: var={{ zk_tag.stdout }}
Error :
The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'stdout'
I think I encountered the following 2 problems:
When registering a variable with register, when condition is added, this variable cannot be used in all groups. How to solve this problem? How to make this variable available to all groups?
is my title, How to use variables between different roles in ansible?
You're most likely starting a new playbook for a new host. Meaning all previous collected vars are lost.
What you can do is pass a var to another host with the add_host module.
- name: Pass variable from this play to the other host in the same play
add_host:
name: hostname2
var_in_play_2: "{{ var_in_play_1 }}"
--- EDIT ---
It's a bit unclear. Why do you use the when statement in the first place if you want every host in the play for it to be available?
You might want to use the group_vars/all.yml file to place vars in.
Also, using add_host should be the way to go as how I read it. Can you post your playbook, and the outcome of your playbook on a site, e.g. pastebin?
If there is any chance the var is not defined because of a when condition, you should use a default value to force the var to be defined when using it. While you are at it, use the debug module for your tests rather than echoing something in a shell
- name: Debug my var
debug:
msg: "{{ docker_exists | default(false) }}"

Ansible: How to declare global variable within playbook?

How can I declare global variable within Ansible playbook. I have searched in google and found the below solution, but its not working as expected.
- hosts: all
vars:
prod-servers:
- x.x.x.x
- x.x.x.x
- hosts: "{{prod-servers}}"
tasks:
- name: ping
action: ping
When I'm trying the above code, it says variable prod-servers is undefined.
You cannot define a variable accessible on a playbook level (global variable) from within a play.
Variable Scopes
Ansible has 3 main scopes:
Global: this is set by config, environment variables and the command line
Play: each play and contained structures, vars entries (vars; vars_files; vars_prompt), role defaults and vars.
Host: variables directly associated to a host, like inventory, include_vars, facts or registered task outputs
Anything you declare inside a play can thus only be either a play variable, or a (host) fact.
To define a variable, which you can use in the hosts declaration:
run ansible-playbook with --extra-vars option and pass the value in the argument;
or to achieve the same functionality (decide which hosts to run a play on, from within a preceding play):
define an in-memory inventory and run the subsequent play against that inventory.
what you seem to want is an inventory (http://docs.ansible.com/ansible/latest/intro_inventory.html), it looks like you have an static list of IP's that may be prod servers (or dev, or whatever), therefore you can create an static inventory.
In your second play you want to use the list of IP's as hosts to run the tasks, that's not what Ansible expects. After the "hosts" keyword in a play declaration, Ansible expects a group name from the inventory.
If, on the opossite, your prod servers change from time to time, you may need to create a dynamic inventory. You can have a look at examples in https://github.com/ansible/ansible/tree/devel/contrib/inventory (for instance, there are examples of dynamic inventory based on EC2 from Amazon or vsphere)
regards
well, this can be done using
set_fact.
I don't know the best practice for this but this works for me
Here's my playbook example
- hosts: all
gather_facts: false
tasks:
- set_fact: host='hostname'
- hosts: host-name1
gather_facts: false
tasks:
- name: CheckHostName
shell: "{{ host }}"
register: output
- debug: msg="{{ output }}"
- hosts: host-name2
gather_facts: false
tasks:
- name: CheckHostName
shell: "{{ host }}"
register: output
- debug: msg="{{ output }}"

Ansible: Include playbook according to inventory variable

I am trying to set up Ansible to be able to run a playbook according to what inventory group the host is in. For example, in the inventory, we have:
[group1]
host1.sub.domain.tld ansible_host=10.0.0.2
...
[group1:vars]
whatsmyplaybook=build-server.yml
Then we want to make a simple playbook that will more or less redirect to the playbook that is in the inventory:
---
- name: Load Playbook from inventory
include: "{{hostvars[server].whatsmyplaybook}}"
Where the "server" variable would be the host's FQDN, passed in from the command line:
ansible-playbook whatsmyplaybook.yml -e "server=host1.sub.domain.tld"
Our reasoning for this would be to have a server bootstrap itself from a fresh installation (PXE boot), where it will only really know its FQDN, then have a firstboot script SSH to our Ansible host and kick off the above command. However, when we do this, we get the below error:
ERROR! 'hostvars' is undefined
This suggests that the inventory is not parsed until a host list is provided, which sucks a lot. Is there another way to accomplish this?
A bit strange workflow, honestly.
Your setup doesn't work, because most of variables are not defined during playbook parse time.
You may be more lucky with defining single playbook with different plays for different groups (no need to set group var, just use correct host pattern (group name in my example)) and execute it limiting to specific host:
site.yml:
---
- hosts: group1
tasks:
- include: build-web-server-tasks.yml
- hosts: group2
tasks:
- include: build-db-server-tasks2.yml
command to provision specific server:
ansible-playbook -l host1.sub.domain.tld site.yml
You can develop your own dynamic inventory file so that all machines which needs to be bootstrapped will automatically added into your inventory and group respectively with out an manual entry in to the inventory file.
For developing dynamic inventory you can follow the below link:
http://docs.ansible.com/ansible/latest/dev_guide/developing_inventory.html
You can include multiple playbooks targeted to different groups as follows.
---
- hosts: all
tasks:
- include: build-web-server-tasks.yml
where: inventory_hostname in groups['group1']
- include: build-db-server-tasks2.yml
where: inventory_hostname in groups['group2']
inventory_hostname is the name of the hostname as configured in Ansible’s inventory host file. This can be useful for when you don’t want to rely on the discovered hostname ansible_hostname or for other mysterious reasons. If you have a long FQDN, inventory_hostname_short also contains the part up to the first period, without the rest of the domain.

How can I persist an ansible variable across ansible roles?

I've registered a variable in a play.
---
- hosts: 127.0.0.1
gather_facts: no
connection: local
sudo: no
vars_files:
- vars.yml
tasks:
- name: build load balancer
os_load_balancer: net=mc_net ext_net=vlan3320 name=load_balancer protocol=HTTPS port=80
register: my_lb
I can access that variable fine, until I make the request inside a role.
For example, in a separate role in the same run, I want to access that registered variable:
- debug: var=my_lb
I get the following output:
{'msg': "AnsibleUndefinedVariable: One or more undefined variables: 'my_lb' is undefined", 'failed': True}
How can I access variables registered in a separate role, within the same play?
Edit for clarity of how things piece together:
Top Play
-includes:
- Sub play 1
- registers variable foo
- Sub play 2
-includes:
- sub play A
- role 1
- role 2
- role 3
- references variable foo in template
- Sub play B
- Sub play 3
NB: This was written referring to Ansible 1.X. I suspect variable parsing and scoping has changed considerably with Ansible 2.0, so bear that in mind. I'll try to update the answer if I get time (or maybe someone else will answer for v2.0!)
There are two options here. The simplest is to define the variables in your top-level playbook, then propagate them down into your various roles. You can either set these as simple vars, or use pre_tasks to lookup / calculate values dynamically, e.g.
vars:
foo_static: "value_bar"
pre_tasks:
- set_fact:
foo_ncpu: "{{ ansible_processor_vcpus }}"
roles:
- {role: role1, role1_var1: "{{foo_static}}", role1_var2: "{{foo_ncpu}}" }
- {role: role2, role2_var1: "{{foo_static}}", role2_var2: "{{foo_ncpu}}" }
The second option requires that you add a task to whichever role you need to extract a variable from (though since all ansible roles are open-source, that should be easy enough). The trick is to use set_fact to export a variable as a 'host_fact', e.g.
- name: Export role1_varfoo to a 'host-fact' type variable
set_fact:
role1_varfoo: "{{ role1_varfoo }}"
Which can then be accessed later like so:
vars:
role2_varfoo: "{{ hostvars['myhost']['role1_varfoo']}}"
As described in this bit of the ansible docs.
Note that if you always want to lookup the hostvars of the current machine you're running commands on (without knowing what it's actually called in the ansible hosts file), you can use the variable inventory_hostname like so:
vars:
role2_varfoo: "{{ hostvars[inventory_hostname]['role1_varfoo']}}"
(Note lack of quote-marks, since it's a variable, not a string-literal.)
Slightly awkward, but those combinations have met all my needs so far.
Try moving your variable declarations into a pre_task block. Variables set here should be available within and following roles.
https://docs.ansible.com/playbooks_roles.html#roles
e.g.
pre_tasks:
- name: build load balancer
os_load_balancer: net=mc_net ext_net=vlan3320 name=load_balancer protocol=HTTPS port=80
register: my_lb
roles:
- { role: some_role }
Update: To access the variable using the hostvars syntax use the appropriate host GROUP variable rather than the host that executed the set_fact:
hostvars[inventory_hostname]['variable']
To broaden the accepted question.
1.-You can define ANOTHER role where you register the variable and then set it there then refer that variable from multiple roles. AS LONG as the roles are in the same play.
Docu here:
http://docs.ansible.com/ansible/playbooks_variables.html#variable-examples
Generally speaking, variables set in one role are available to others. This means if you have a roles/common/vars/main.yml you can set variables in there and make use of them in other roles and elsewhere in your playbook
Edit: Clarification, this applies for REGISTERED and set variables in Ansible 2.x in my experience.
2.-As far as using hostvars goes, I tried it myself and failed with this error:
fatal: [localhost]: FAILED! => {"failed": true, "msg": "'dict object' has no attribute 'ec2_instance_id'"}
I my case I was doing the following.
- hosts: localhost
gather_facts: yes
roles:
- { role: role_1 }
post_tasks:
- name: Check instance variables within localhost
debug: var={{ hostvars['localhost']['ec2_instance_id'] }}
On role 1 I had:
- name: register instance_id
set_fact: ec2_instance_id="{{ item.id }}"
with_items: "{{ ec2_instance.instances }}"
Although according to this old thread, the hostvar approach should work:
https://github.com/ansible/ansible/issues/1934
How abt passing it as parameter to role. I'm assuming such (register) variables aren't passed implicitly (like hotvars are).
- { role: my_role, my_lb: "{{my_lb}}" }

Resources