Sebastiaan Luca
Sebastiaan Luca

Reputation: 493

Using with_items inside vars_files in an Ansible playbook

I'm currently making the transition from Puppet to Ansible and so far so good. Yet I want to automatize as much as possible.

I'm trying to use a with_items loop inside vars_files to load variable files based on a given list of items. Ansible complains about the syntax and I can't seem to find an example of a similar solution, only examples that use with_items inside tasks and roles.

For example:

vars_files:
  - ["vars/{{ item }}-{{ ansible_fqdn }}.yml", "vars/{{ item }}-{{ system_environment }}.yml", "vars/{{ item }}.yml"]
    with_items:
      - php
      - nginx

The goal here is to loop the second line for as long as there are items in with_items using an array to fallback on the next item if it can't find the given file (which works).

Not sure if this is at all possible, but I wanted to ask before taking another direction.

Upvotes: 6

Views: 11988

Answers (2)

Sebastiaan Luca
Sebastiaan Luca

Reputation: 493

Putting this in a separate answer to expand on the group and host variables solution I eventually came up with (cc @udondan).

Basically I group all my hosts in my inventory file under several sub and parent groups no matter what. Then I create files for group vars whenever applicable so it follows a certain order of precedence (first is highest and overrides all others, last applies to all hosts and can be overridden down the chain):

task vars > playbook vars > host_vars > web/database-local > local > web/database > all

That way I can define variables for all hosts to use (all), just web/database (mostly production values), all local servers (local group), all local web/database servers, et cetera, or per-host (the standard host_vars). Of course playbook and task vars override these further. All of this following the Ansible guidelines.

An example of a local inventory (replace default with your hostname or IP, add as many as you like per group, x-local can be omitted if this would be a production inventory):

[web-local]
default

[database-local]
default

[local:children]
web-local
database-local

[web:children]
web-local

[database:children]
database-local

Then my group_vars folder with directories for each inventory group and variables split into files to keep it structured (could just have one database-local.yaml file for the database-local group for instance instead of folders and split YAML-files):

group_vars/
    all/
        always_applied_variables.yaml
        swap.yaml
    web/
    database/
        database_only_variables.yaml
    database-production/
        production_database_variables.yaml
    production/
        random_production_only_variables.yaml
    local/
        users.yaml
    web-local/
    database-local/
        local_database_variables.yaml
host_vars/
    default/
        php.yaml
        mysql.yaml
        other_specific_host_variables.yaml

Hope this is somewhat clear. I'd be happy to answer any questions.

Upvotes: 0

udondan
udondan

Reputation: 59989

with_items, or in general all loops, are a feature of tasks. vars_files though is no task. So it won't work the way you tried it and the short answer would be: It is not possible.

I don't know of a clean way to solve your exact problem. A custom vars plugin might be an option. But vars plugin work on a global level while your vars seem to be used in a role.

A custom lookup plugin might be a solution if solving this on task level is an option for you. The lookup plugin takes your input, checks for presence of the files and returns an array of the files which need to be include. This then can be used with the include_vars module.

- include_vars: "{{ item }}"
  with_my_custom_plugin:
    - php
    - nginx

An ugly solution would be to combine the with_items loop with a with_first_found loop. Though, since you cannot directly nest loops, you need to work with an include.

- include: include_vars.yml
  with_items:
    - php
    - nginx

And inside include_vars.yml you then can use with_first_found with the include_vars module.

- include_vars: "{{ item }}"
  with_first_found:
    - vars/{{ item }}-{{ ansible_fqdn }}.yml
    - vars/{{ item }}-{{ system_environment }}.yml
    - vars/{{ item }}.yml

Upvotes: 5

Related Questions