Ansible: How to declare global variable within playbook? - ansible

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 }}"

Related

Use a variable for remote_user in ansible

I want to parameterize (use a variable) for remote_user in ansible.
This is the first part from the playbook:
- hosts: xxx
remote_user: "centos"
become: true
I will replace it with
- hosts: wazuh
remote_user: "{{ new_user }}"
become: true
But what is a good place to store the value of this variable? It seems group_vars/all mostly contain variables which are more app/env specific than ansible specific. Or should I put it in inventories/hosts as a var? What is the recommended location to store it?
You should actually store it in your inventory as ansible_user either for the all group (for all host), a specific group or a specific host. You can keep a remote_user in your play which will be used in case the ansible_user is not defined for some hosts in your inventory. If you remove it, you local user on the controller will be the default, unless you use the -u flag on the command line.
You can find a good explanation of the difference between ansible_user and remote_user and how the overide works in the documentation on variable precedence
Using a var as you wrote it in your above example can actually work. But since it must be expanded before the play actually starts and any action is taken on any host, the only place you can "store" it is in an extra_var on the command line.
To be a little more practical, here is what I suggest from your above example:
inventories/dev/hosts.yml
---
wazuh:
hosts:
host_a.wazuh.tld:
host.b.wazuh.tld:
inventories/dev/group_vars/wazuh.yml
---
# Vars for the wazuh group
ansible_user: centos
plabook.yml
---
- hosts: wazuh
tasks:
- name: Proove we connected with the given user
cmd: whoami
register: whoami_result
- name: Show actual result
debug:
var: whoami_result.stdout
Launching the playbook:
ansible-playbook -i inventories/dev playbook.yml

Ansible - How to add the client list in inventory file through environment variable

I have the following inventory file
[vm_group]
xyz001.hq.company.com
xyz002.hq.company.com
[vm_group:vars]
ansible_connection=winrm
ansible_winrm_transport=ntlm
ansible_port=5986
ansible_winrm_server_cert_validation=ignore
I have a environment variable called CLIENT_LIST
echo $CLIENT_LIST
xyz001.hq.company.com,xyz002.hq.company.com
How can I do something like this
[vm_group]
${CLIENT_LIST}
so that the inventory group can be set dynamically.
I tried doing it the way I have shown above but it is not picking up the environment variable values.
Do you have any other recommendation for dynamic list. Environment variable would be my first preference though.
There are two answers to your question: multiple plays, or a dynamic inventory plugin; there is no such thing as variable substitution in the static inventory files
For the multiple-plays version, one would use add_host::
- hosts: localhost
connection: local
tasks:
- add_host:
name: '{{ item }}'
groups:
- vm_group
with_items: '{{ lookup("env", "CLIENT_LIST").split(",") }}'
- hosts: vm_group
# and you're of to the races
For the dynamic inventory version, the world is your oyster: https://docs.ansible.com/ansible/2.9/plugins/inventory.html#plugin-list

Passing a variable to an included Ansible playbook

I'd like to pass a variable to an included Ansible playbook as follows:
---
- hosts: localhost
connection: local
vars:
my_group: foo
- include: site.yml hosts={{ my_group }}
Then, in site.yml...
---
- hosts: "{{ hosts }}"
...
Unfortunately, I get an error saying that my_group is undefined in site.yml. Ansible docs do say that:
Note that you cannot do variable substitution when including one playbook inside another.
Is this my case? Is there a way around it?
You can use this syntax, but my_group has to be defined at the global level. Now it's local to the first play - it's even clear from the indentation.
You can confirm this by running your playbook with --extra-vars my_group=foo.
But generally what you seem to want to achieve is done using in-memory inventory files and add_host module. Take this as an example:
- hosts: localhost
gather_facts: no
vars:
target_host: foo
some_other_variable: bar
tasks:
- add_host:
name: "{{ target_host }}"
groups: dynamically_created_hosts
some_other_variable: "{{ some_other_variable }}"
- include: site.yml
with site.yml:
---
- hosts: dynamically_created_hosts
tasks:
- debug:
var: some_other_variable
I added some_other_variable to answer the question from your comment "how do I make a variable globally available from inside a play". It's not global, but it's passed to another play as a "hostvar".
From what I see (and I can't explain why) in Ansible >=2.1.1.0, there must be an inventory file specified for the dynamic in-memory inventory to work. In older versions it worked with Ansible executed ad hoc, without an inventory file, but now you must run ansible-playbook with -i inventory_file or have an inventory file defined through ansible.cfg.

Registering Each Host Specific Value from Dictionary

We wanted to have a single playbook for all the deployments and the multiple hosts will be looped in. Ansible calls will be made from Jenkins pipeline by passing in the environments, for example dev6 and dev8
env1=dev6
env2=dev8
Pipeline Call:
ansible-playbook -i hosts --limit $env1:$env2 deploy_test.yml -e "env1={{$env1}} env2={{$env2}}"
I defined all the host specific variables (dev1,dev2......PERF8 etc.) in single file so it is easy to manage and maintain,
dev6:
- { deploy_domain: "Dev6Domain",
WL_Admin: "DEV6WLAdmin",
WL_Managed: "DEV6Managed" }
dev7:
- { deploy_domain: "Dev7Domain",
WL_Admin: "Dev7WLAdmin",
WL_Managed: "Dev7Managed" }
Playbook "Deploy_test.yml"
- hosts: all
vars_files:
- host_variables.yml
tasks:
- debug: msg='Target Domain is "{{ item[0].deploy_domain }}"'
with_nested:
- "{{ env1 }}"
- "{{ env2 }}"
The env1 and env2 values are being read from jenkins, no issues there
Problem-1: When the playbook runs on dev6 first, it takes dev8 values as well since it is defined under with_nested items.
Problem-2: How do I register the values specific to every environment?
for example, down the playbook when I say, mkdir /tmp/{{deploy_domain}, I need seperate values for dev6 and dev8.
Here is an example how you can read name-specific variable for every host:
hosts:
[dev6]
box1
[dev8]
box2
host_variables.yml:
dev6:
deploy_domain: "Dev6Domain"
WL_Admin: "DEV6WLAdmin"
WL_Managed: "DEV6Managed"
dev8:
deploy_domain: "Dev8Domain"
WL_Admin: "Dev8WLAdmin"
WL_Managed: "Dev8Managed"
I stripped out list level from original host_variables.yml, because it is not necessary in this case, there is always single element in the list.
deploy_test.yml:
- hosts: all
tasks:
- include_vars: host_variables.yml
- set_fact:
my_env: "{{ hostvars[inventory_hostname][group_names[0]] }}"
- debug: msg="My domain = {{ my_env.deploy_domain }}"
execution: ansible-playbook -i hosts --limit $env1:$env2 deploy_test.yml
This will execute deploy_test.yml for all hosts in groups set in env vars env1 and env2.
In the begining of playbook, we load everything from host_variables.yml as host facts.
And with set_fact extract variable named after current host's group name as my_env.
So box1 will have dev6 as my_env and box2 will have dev8.

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