Thanks for all the help,
I was interested in finding out how to set up a vars file with the following structure and how to iterate multiple deeply nested items in a dictionary. The end goal is to have a task performed on hosts that match a multiple or single roles or are used by a particular team, or teams.
1) Is this structure correct? I have seen so many different examples, I'm not sure anymore...
2) Is it possible in ansible to perhaps perform a task on multiple hosts by matching a single role but matching multiple teams?
3) How can I iterate through the dictionary and get an individual item from within a subelement that has multiple items, like "teams: sales" or "teams: dev" ? For example, I'd like to perform the task on host1 and matching operation and dev and sales teams files I have available for them. For example, copy a file to a team directory on that one host. Host2 has different teams and therefore the same task could be used but deliver the file to only those team directories on that single host.
4) The same desire would apply to the role of each host... being able to have multiple roles for a single host but matching on the role value (either both roles or a single role).
Anyhow, I've looked at a lot of things and I haven't quite found something that fits this but I feel it is possible -- sorta??
### hostmap.yml
---
host_team_map:
host_list:
host1:
comment: "host description"
application_role: [ 'role1', 'role2' ] <----------- multiple subitems
teams: [ 'operations', 'dev', 'sales' ] <----------- multiple subitems
host2:
comment: "host description"
application_role: [ 'role3' ]
teams: [ 'operations', 'dev' ]
host3:
comment: "host description"
application_role: [ 'role3', 'role4' ]
teams: [ 'operations' ]
Thanks again for any help!!
So, if I understand well, you want to apply the list of roles for an host, and from this role, only a part of tasks based on the teams elements.
If it is the case, then you may have, for each role, a task file with the name of the teams and use the include_role module: https://docs.ansible.com/ansible/latest/modules/include_role_module.html
Here is an example of what you may do:
1) As #larsks suggest, you should put necessary vars in the inventory (thus, having application_role an teams specific for "current" host)
For example, in host_vars:
### host1.yml
---
application_role:
- role1
- role2
- ...
teams:
- dev
- operation
- ...
2) Role directory structure example:
roles/
role1/
tasks/
operation.yml
dev.yml
...
role2/
tasks/
dev.yml
...
3) And the task in the playbook(s):
- name: Apply every role, but only the teams part
include_role:
name: "{{ item.0 }}"
tasks_from: "{{ item.1 }}"
loop: "{{ application_role|product(teams)|list }}"
Related
When I run my Ansible playbook, I define my hosts which looks to a group in my inventory.
$ ansible-playbook -i inv/hosts conf.yml
conf.yml:
- name: Configure QA Nodes
hosts: conf_qa
inv/hosts:
[conf_qa]
confqa1
# confqa2
[conf_prod]
prod1
# prod2
prod3
Is there a way in my Roles (or other elements of the Playbook) where I can back out which group_name (or equivalent) is being used?
I know I could set a variable in group_vars/conf_qa.yml such as qa: true and then reference it later in my Roles
roles/init/tasks/main.yml:
- name: Do this when dealing with the conf_qa group
when: qa == true
But using group_vars/conf_qa.yml seems like an extra intermediary step when I was hoping to reference the host groups more directly. Is there a better way?
You can add the following condition, this is from a playbook I created and I can confirm that it works.
It only runs the task in the servers that belong to that group, the rest of them will appear as "skipped"
- name: create api folder
file:
path: /var/log/api
state: directory
when: inventory_hostname in groups['switch']
I'm new at using ansible for server management and I need some help managing users and group's membership definition according to host and hosts-group, with a minimum of duplication and a maximum of scalability.
(25 users/20 groups over 50 hosts, with different "sudo" and "groups membership" at the end).
The idea is to have:
"groups_vars" files defining the users (list or hash) to create on each host of the host group.
"host_vars" files defining users for a specific host. (At the end, I will need nested groups, more than specific host_vars files)
I need these \*_vars files contents to be merged and not to be replaced (I understand how "vars precedence" work) because I want to avoid user declaration duplication.
To achieve this, I used hash syntax in \*_vars files and set hash_behaviour=merge in /etc/ansible/ansible.cfg.
Here are my files:
My inventory:
all:
children:
type_a:
hosts:
vm1:
vm2:
My debugging playbook :
- hosts: type_a
tasks:
- name: Debugging
debug:
msg: "{{ users }}"
group_vars/type_a.yaml :
users:
user1:
name: user1
user2:
name: user2
host_vars/vm1.yaml
users:
user3_vm1_specific:
name: user3_vm1_specific
At the end, I need the 3 users on the "vm1" and only "user1" and "user2" on "vm2" and then I will use the vars for the user creation.
Using the merge option (that will be deprecated in newer version of ansible) is working, but doesn't seem to be a best practice.
I searched here on StackOverflow and on other web sites, and most of the time the solutions are:
to duplicate the user definition
(more than 8 properties for each user and too many hostsgroup: unacceptable.)
to use an other name for the second user list, then to assemble both using {{ user_list1 + user_list2 }}.
Not very scalable if we want to add many nested groups. You will need to add custom named list each time. It also, makes duplicates if "host_vars" and "group_vars" have the same user defined: it does not merge the content, but declares it twice with a different content each time.
My first solution is working, but using a near-deprecated option.
So what are the best practices in managing vars in this kind of situation ? (already have read the ansible documentation about vars but it didn't really helped me).
Also, maybe ansible tower or foreman could solve this problem?
A very simple, easily maintainable, and flexible solution is to put users into one dictionary. For example
shell> cat group_vars/all/users.yml
users:
groups:
type_a:
- user1
- user2
type_b:
- user3
- user4
hosts:
vm1:
- user3_vm1_specific
vm2:
- user4_vm2_specific
Then the task below extracts all group's lists, add the host's list, makes the items unique, and sort it
shell> cat playbook.yml
- hosts: type_a
tasks:
- set_fact:
my_users: "{{ (group_names|
map('extract', users.groups)|list +
users.hosts[inventory_hostname]|default([]))|
flatten|unique|sort }}"
- debug:
var: my_users
gives
shell> ansible-playbook playbook.yml
PLAY [type_a] ****
TASK [debug] ****
ok: [vm1] =>
my_users:
- user1
- user2
- user3_vm1_specific
ok: [vm2] =>
my_users:
- user1
- user2
- user4_vm2_specific
Notes:
Lists are simpler in this use-case. Dictionaries are also possible, but the code would be more complex.
A host can be a member of more groups.
Single dictionary is easily maintainable, potentially created from an external DB (e.g. Forman Manage Users).
The task selecting the list is simple and under the admin's control.
I'm working on putting together a playbook that will deploy local facts scripts to various groups in my Ansible inventory, and I would to be able to utilize the group name being worked on as a variable in the tasks themselves. Assume for this example that I have the traditional Ansible roles directory structure on my Ansible machine, and I have subdirectories under the "files" directory called "apache", "web", and "db". I'll now illustrate by example, ...
---
- hosts: apache:web:db
tasks:
- name: Set facts for facts directories
set_fact:
facts_dir_local: "files/{{ group_name }}"
facts_dir_remote: "/etc/ansible/facts.d"
- name: Deploy local facts
copy:
src: "{{ item }}"
dest: "{{ facts_dir_remote }}"
owner: ansible
group: ansible
mode: 0750
with_fileglob:
- "{{ facts_dir_local }}/*.fact"
The goal is to have {{ group_name }} above take on the value of "apache" for the hosts in the apache group, "web" for the hosts in the web group, and "db" for the hosts in the db group. This way I don't have to copy and paste this task and assign custom variables for each group. Any suggestions for how to accomplish this would be greatly appreciated.
While there is no group_name variable in ansible, there is a group_names (plural). That is because any host may be part of one or more groups.
It is described in the official documentation as
group_names
List of groups the current host is part of
In the most common case each host would only be part of one group and so you could simply say group_names[0] to get what you want.
TLDR;
Use group_names[0].
You can use group variables to achieve this, either specified in the inventory file or in separate group_vars/<group> files - see https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html
Note though that these group variables are squashed into host variables when you run the playbook. Hosts that are listed in multiple groups will end up with variables based on a precedence order
I would like to be able to reference the direct parent of an ansible host as a variable.
Take the following example inventory:
[resourcegroup1]
host1
host2
[resourcegroup2]
host3
host4
[application:children]
[resourcegroup1]
[database:children]
[resourcegroup2]
[environoments:children]
[application]
[database]
[enivronoments]
dev
staging
prod
I would like to run a looping task in a play which can reference a hosts parent. Example:
tasks:
- name: Start Datanbase Servers
with_items: "{{ groups['database'] }}"
azure_rm_virtualmachine:
name: "{{ item }}"
resource_group: "{{ item.parent }}"
started: yes
allocated: yes
{{ item }} would iterate through the values of "host3" and "host4" while I am looking for what I could put in place of {{ item.parent }} that would be the hosts direct parent, in this case: "resourcegroup2".
Is there any way to reference the hierarchy of an inventory?
After doing some quick research, it appears Ansible doesn't have that sort of functionality builtin.
According to the tickets opened in Ansible Github repo, it sounds like people were interested in this feature, but was never developed. Probably because of the battle between usefulness & complexity.
Ansible Tix
Ansible Tix
Here are some snippets from the above links:
It would be nice to have a way of recreating inventory group hierarchy inside i.e. Jinja2 template.
#kinvaris We have dynamic inventories for this. You could just write a small dynamic inventory script, and have it translate. I don't see why this is a necessary addition, it only complicates the tools even more.
If you just want a list of groups associated with a host, but don't care about hierarchical structure:
'{{hostvars["host1"]}}'
You'll get an list like this:
"groups": {
"all": [
"host"
],
"child": [
"host1"
],
"parent": [
"host1"
],
"ungrouped": []
},
Is there a way to have multiple variable files append to an array?
The end goal is to have each file append an AWS security group for the launch configuration without the need to copy those groups into each file
ex:
group_vars/all.yml
group_vars/php.yml
group_vars/web.yml
all.yml:
aws_security_groups:
- sg-ssh
php.yml
aws_security_groups:
- sg-mysql
web.yml
aws_security_groups:
- sg-http
Debugging aws_security_groups produces:
TASK [Debugging] ***************************************************************
ok: [XXX.XXX.XXX.XXX] => {
"aws_security_groups": [
"sg-mysql"
]
}
We have API servers and front-end servers which have different scaling polices. I would like the web servers to have the groups: sg-web and sg-ssh with the api servers to have: sg-web, sg-ssh and sg-mysql
AFAIK there is no way to merge list variables during definition time in Ansible.
As a workaround, you can make a dict in your all.yml:
aws_security_groups:
all:
- sg-ssh
- sg-all
php:
- sg-mysql
web:
- sg-http
Or define each subkey (all, php, web, etc.) in corresponding group vars file and use merge hash_behavior (but changing hash_behavior is not generally recommended).
And then use this magic to get a list of security groups:
- debug: msg="{{ (group_names + ['all']) | intersect(aws_security_groups.keys()) | map('extract', aws_security_groups) | sum(start=[]) }}"
This will make plain list of all security groups depending on groups for current host.