Importing user variables from multiple variable sources in Ansible - ansible

I want to know how I can import user account variables at multiple levels using loops/arrays and importing them all.
i.e. I have a generic module which has users defined in roles/common/tasks/main.yml as an array(or loop if more precise):
- name: add admin users on RHEL
user:
name: "{{ item.username }}"
comment: "{{ item.comment }}"
state: present
groups: wheel
shell: /bin/bash
password: "{{ item.password }}"
with_items: "{{ users }}"
when: ansible_facts["os_family"] == "RedHat"
tags: common
and the user attributes are all set as variables and the variables for these users are being picked up from roles/common/vars/main.yml
I also have user variables defined under group_vars/dev and group_vars/prd
And I also have specific users defined at host level under host_vars/server1
Now the problem is that Ansible is only picking up users from one level, whilst I want to pick up all the user variables from all levels, i.e. sysadmins, dev users & specific host users, and add them all in, not just from the most preferred variable source.
In Puppet this is achieved by using Hiera and hiera_hash which allows variable values to get collectively applied from all hiera levels.
How can the same be achieved with Ansible ?

Related

Ansible: Reference variable contents from variable name

I am trying to access the contents of a variable, from a variable name. I've used ansible vault to create some passwords, and I am trying to create database users, like so:
postgresql_user:
name: "{{ item }}"
db: "{{ item }}"
login_user: postgres_admin
login_password: '{{postgres_admin}}'
login_host: localhost
login_port: 5433
password: '{{ "{{item}}" }}'
loop:
- artifactory
- bitbucket
- confluence
- crowd
- jira
In this case, I have a database user, which is the variable in the loop. The password for that user, is stored inside the (vault) variable name of that user. In the password: field like I am trying to do a double reference. It doesn't work.
I've tried a few more things, but this is my first run with Ansible and I don't want to go down the wrong path. (I did not include the reference to the vault but it's at the top of the file, and it has been tested with a simpler tasks)
Thanks!
Use lookup plugin vars. For example
password: "{{ lookup('vars', item) }}"
With the introduction of the collections the documentation of the plugins is not available online anymore. See the documentation from the command-line
shell> ansible-doc -t lookup vars
To make the code both more robust and user-friendly, I'd propose to rename the variables with the passwords. For example "artifactory_passwd" etc. Then, in the lookup, create the name of the variable dynamically
password: "{{ lookup('vars', item ~ '_passwd') }}"

How to create one common playbook-wide writeable variable in ansible?

I'm writing a playbook to create many user accounts across many servers. At the end I want to get output with credentials sorted by username.
I used set_fact with run_once but it seems that defined variable is not playbook-wide.
main.yml
- name: Create users
import_tasks: creation_task.yml
creation_task.yml
- name: Init variable for creds
set_fact:
creds: []
delegate_to: localhost
run_once: true
- name: Create specific users
include: create.yml
with_items:
- input_data
- .......
- name: Print output creds
debug: var=creds
run_once: true
create.yml
- name: some actions that actually create users
....
- name: add creds to list
set_fact:
creds: "{{ creds + [ {'hostname': inventory_hostname,'username':item.name,'password':password.stdout} ]}}"
- name: add splitter to list
set_fact:
creds: "{{ creds + [ '-----------------------------------------------------' ]}}"
This is actually working but i get output sorted by server because (as I think) every host reports his version of "creds" variable.
I'd like to create one variable that will be visible and writeable across all nested plays. So output would be sorted by input data but not hostname. Is it possible?
I'd use the following syntax, to fetch a variable set from a specific host via hostvars:
- debug:
msg: "{{ groups['my_host_name']|map('extract',hostvars,'my_variable_name')|list|first }}"
when: groups['my_host_name']|map('extract',hostvars,'my_variable_name')|list|first|length > 0
Then, you can loop over your hostnames to create an array of values and sort them.
Though, printing all servers hostnames, users & plain text passwords in a text file seems to be a security risk.

Set variable if empty or not defined with ansible

In my ansible vars file, I have a variable that will sometimes be set and other times I want to dynamically set it. For example, I have an RPM that I want to install. I can manually store the location in a variable, or if I don't have a particular one in mind, I want to pull the latest from Jenkins. My question is, how can I check if the variable is not defined or empty, and if so, just use the default from Jenkins (already stored in a var)?
Here is what I have in mind:
...code which gets host_vars[jenkins_rpm]
- hosts: "{{ host }}"
tasks:
- name: Set Facts
set_fact:
jenkins_rpm: "{{ hostvars['localhost']['jenkins_rpm'] }}"
- name: If my_rpm is empty or not defined, just use the jenkins_rpm
set_fact: my_rpm=jenkins_rpm
when: !my_rpm | my_rpm == ""
There is default filter for that:
- set_fact:
my_rpm: "{{ my_rpm | default(jenkins_rpm) }}"

A special variable for ansible roles installed?

Is there a variable or a method allowing one to list all the roles applied to a group of ansible hosts?
For example:
- hosts: webservers
gather_facts: true
roles:
- nginx
- php-fpm
tasks:
- debug:
msg: {{ item }} installed
with_items: ansible_roles
or perhaps another way to achieve this?
It depends on what applied means in your question.
The variable role_names holds all roles of the current play, so it would be an array: [nginx, php-fpm].
- debug:
msg: {{ item }} installed
with_items: role_names
But these roles not necessarily have been applied to the hosts, if you mean by that they have been processed. There is no such thing that will update once a role has been run on a host.
If that is what you're looking for you could implement it yourself with a callback plugin. AFAIK there is no callback for starting/completing a role itself. But since the names of the roles are present in the task names you could simply use the playbook_on_task_start, extract the role name from the task name and store it in some way. I have not yet looked into callback plugins in Ansible 2 where the API changed, but I expect you have access to the global variable list and can manipulate it.

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