In Ansible, how to combine variables from separate files into one array?
You can not do that. Variables will always override variables with the same name. The only thing you could do with this exact setup is to write your own vars plugin which reads those files and merges them into an array.
If you are open to change the structure of your apps definition you can use a hash and set your hash_behavior=merge
. In each vars file then you'd have a definition like:
apps:
app1:
git_repo: https://github.com/philgyford/app1.git
apps:
app2:
git_repo: https://github.com/philgyford/app2.git
When Ansible loads both files it will merge it automatically together into:
apps:
app1:
git_repo: https://github.com/philgyford/app1.git
app2:
git_repo: https://github.com/philgyford/app2.git</pre>
But be advised that hash_behavior=merge
fundamentally changes the default behavior of Ansible on a global level. Make sure all your roles do not have issues with this setting. The documentation mentions:
We generally recommend not using this setting unless you think you have an absolute need for it
If you still use Ansible 1 you could use one of my old plugins instead: include_vars_merged. Basically this adds the behavior of hash_behavior=merge
to only a single task.
I have not yet looked into migrating this to Ansible 2 though and currently it looks like I won't have the need for it any longer.
Well, you cannot directly build an array, but you can achieve the same effort with a dict.
Suppose you want to construct an array:
[{
name: 'bob',
age: 30
}, {
name: 'alice',
age: 35
}]
You can put each element in a file like:
bob.yml
bob:
name: bob
age: 30
alice.yml
alice:
name: alice
age: 35
Place these files in the same dir (e.g. user
), then use include_vars
to load the whole dir:
- name: Include vars
include_vars:
name: users
dir: user
This will give you a dict users
:
users:
alice:
name: alice
age: 35
bob:
name: bob
age: 30
User the dict2items
filter in ansible, you get the array you want
Since Ansible 2.2
, the include_vars
(link) module has been expanded quite a bit.
It's now possible to do something like:
- include_vars:
name: 'apps'
dir: '../vars'
extensions:
- 'yaml'
- 'yml'
name
is the key there. From the module page:
The name of a variable into which assign the included vars. If omitted (null) they will be made top level vars.
This allows you to convert:
vars/
app1.yml
app2.yml
...
app1.yml:
name: app1
git_repo: https://github.com/philgyford/app1.git
# ...
app2.yml:
name: app2
git_repo: https://github.com/philgyford/app2.git
# ...
Into...
apps:
- name: app1
git_repo: https://github.com/philgyford/app1.git
# ...
- name: app2
git_repo: https://github.com/philgyford/app2.git
# ...
Use attribute name and put the included variables into the dictionaries. Fit the name of the dictionaries to your needs. For example
- name: Load var files
include_vars:
file: "{{ item }}"
name: "incl_vars_{{ item|basename|splitext|first }}"
with_fileglob:
- vars/*.yml
Then, use the lookup plugin varnames (New in version 2.8.), find all dictionaries, and iterate the list. In the loop use the lookup plugin vars (New in version 2.5.) and create the list apps. For example
- set_fact:
apps: "{{ apps|default([]) + [lookup('vars', item)] }}"
loop: "{{ query('varnames', '^incl_vars_(.*)$') }}"
gives
apps:
- git_repo: https://github.com/philgyford/app2.git
name: app2
- git_repo: https://github.com/philgyford/app1.git
name: app1
You can convert the list to a dictionary if you want to
- set_fact:
apps: "{{ dict(_keys|zip(apps)) }}"
vars:
_keys: "{{ apps|map(attribute='name')|list }}"
gives
apps:
app1:
git_repo: https://github.com/philgyford/app1.git
name: app1
app2:
git_repo: https://github.com/philgyford/app2.git
name: app2