11

hiera.yaml

---
:hierarchy:
- node/%{host_fqdn}
  - site_config/%{host_site_name}
  - site_config/perf_%{host_performance_class}
  - site_config/%{host_type}_v%{host_type_version}
  - site/%{host_site_name}
  - environments/%{site_environment}
  - types/%{host_type}_v%{host_type_version}
  - hosts
  - sites
  - users
  - common
# options are native, deep, deeper
:merge_behavior: deeper

We currently have this hiera config. So the config gets merged in the following sequence common.yaml > users.yaml > sites.yaml > hosts.yaml > types/xxx_vxxx.yaml > etc. For the variable top hierarchies, it gets overwritten only if that file exists.

eg: common.yaml

server:
  instance_type: m3.medium

site_config/mysite.yaml

server:
  instance_type: m4.large

So for all other sites, the instance type will be m3.medium, but only for mysite it will be m4.large.

How can I achieve the same in Ansible?

MavWolverine
  • 846
  • 1
  • 9
  • 24
  • 2
    Ok wow: this is a super real question. I want to say this is not possible in intrinsic Ansible. The whole concept of prioritized dynamic data lookups the way Puppet does them is so tied in with the way Puppet operates as a software that I don't think it is a good fit for what Ansible is attempting to achieve. This sounds like a really cool new FOSS idea though. – Matthew Schuchard Sep 13 '16 at 18:11

3 Answers3

5

I think that @Xiong is right that you should go the variables way in Ansible.
You can set up flexible inventory with vars precedence from general to specific.

But you can try this snippet if it helps:

---
- hosts: loc-test
  tasks:
    - include_vars: hiera/{{ item }}
      with_items:
        - common.yml
        - "node/{{ ansible_fqdn }}/users.yml"
        - "node/{{ ansible_fqdn }}/sites.yml"
        - "node/{{ ansible_fqdn }}/types/{{ host_type }}_v{{ host_type_version }}.yml"
      failed_when: false

    - debug: var=server

This will try to load variables from files with structure similar to your question.
Nonexistent files are ignored (because of failed_when: false).
Files are loaded in order of this list (from top to bottom), overwriting previous values.

Gotchas:

  • all variables that you use in the list must be defined (e.g. host_type in this example can't be defined in common.yml), because list of items to iterate is templated before the whole loop is executed (see update for workaround).

  • Ansible overwrite(replace) dicts by default, I guess your use case expects merging behavior. This can be achieved with hash_behavior setting – but this is unusual for Ansible playbooks.

P.S. You may alter top-to-bottom-merge behavior by changing with_items to with_first_found and reverse the list (from specific to general). In this case Ansible will load variables from first file found.

Update: use variables from previous includes in file path.

You can split the loop into multiple tasks, so Ansible will evaluate each task's result before templating next file's include path.
Make hiera_inc.yml:

- include_vars: hiera/common.yml
  failed_when: false
- include_vars: hiera/node/{{ ansible_fqdn }}/users.yml
  failed_when: false
- include_vars: hiera/node/{{ ansible_fqdn }}/sites.yml
  failed_when: false
- include_vars: hiera/node/{{ ansible_fqdn }}/types/{{ host_type | default('none') }}_v{{ host_type_version | default('none') }}.yml
  failed_when: false

And in your main playbook:

- include: hiera_inc.yml

This looks a bit clumsy, but this way you can define host_type in common.yaml and it will be honored in the path templating for next tasks.

With Ansible 2.2 it will be possible to include_vars into named variable (not global host space), so you can include_vars into hiera_facts and use combine filter to merge them without altering global hash behavior.

Konstantin Suvorov
  • 65,183
  • 9
  • 162
  • 193
1

I'm not familiar with Puppet, so this may not be a direct mapping. But what I understand your question to be is "how do I use values in one shared location but override their definitions for different servers?". In Ansible, you do this with variables.

You can define variables directly in your inventory. You can define variables in host- and group-specific files. You can define variables at a playbook level. You can define variables at a role level. Heck, you can even define variables with command-line switches.

Between all of these places, you should be able to define overrides to suit your situation. You'll probably want to take a look at the documentation section on how to decide where to define a variable for more info.

Xiong Chiamiov
  • 13,076
  • 9
  • 63
  • 101
0

It seems a little more basic than Hiera, but somebody has created a basic ansible lookup plugin with similar syntax

https://github.com/sailthru/ansible-oss/tree/master/tools/echelon

James McGuigan
  • 7,542
  • 4
  • 26
  • 29