Hiera Variable Interpolation: Referencing Variables Across Hierarchy Levels in Puppet


2 views

When working with Puppet's Hiera, a common challenge arises when trying to reference variables defined in one hierarchy level from another level. The key limitation is that Hiera doesn't automatically make all variables available across the entire hierarchy during interpolation.


# hiera.yaml
:hierarchy:
  - "nodes/%{::trusted.certname}"
  - "env/%{env_name}"
  - "common"

Given this hierarchy structure, if we define env_name in a node-specific file:


# nodes/webserver01.yaml
env_name: "prod-stable"

We cannot directly reference it in common.yaml:


# common.yaml
server_name: "app-%{env_name}.%{::domain}"  # Won't work

Here are three approaches to solve this interpolation challenge:

1. Using Hiera's lookup() function


# common.yaml
server_name: "app-%{lookup('env_name')}.%{::domain}"

2. Restructuring hierarchy with explicit dependencies


# hiera.yaml
:hierarchy:
  - "nodes/%{::trusted.certname}"
  - "env/%{::location}"
  - "common/%{env_name}"
  - "common"

3. Using Puppet manifests for variable resolution


# site.pp
$env_name = lookup('env_name')
$server_name = "app-${env_name}.${facts['domain']}"

# Then just reference $server_name in Hiera
  • Document all variable dependencies between hierarchy levels
  • Consider using the "alias" pattern for frequently-referenced variables
  • Test interpolation with puppet lookup --explain
  • Avoid circular dependencies between hierarchy levels

For complex cases, you might implement a custom backend that handles cross-hierarchy references:


class MyHieraBackend
  def lookup(key, scope, order_override, resolution_type, context)
    # Custom logic to handle cross-hierarchy lookups
  end
end

When working with Puppet's Hiera, a common pain point emerges when trying to reference variables defined at different hierarchy levels. Consider this scenario:

# in environments/dev-unstable/data/common.yaml
env_name: "dev-unstable"

# in common.yaml we try to reference it
server_name: "service-%{env_name}.%{::domain}"

Hiera processes files sequentially from most specific to most general, but doesn't automatically make variables from specific levels available to more general files. This behavior is by design to prevent circular dependencies and maintain predictable variable precedence.

Here are several approaches to achieve cross-hierarchy references:

1. Using the lookup() Function

server_name: "service-%{lookup('env_name')}.%{::domain}"

2. Hierarchical Data Design

Restructure your hierarchy to put shared variables in a common file that gets included early:

# hiera.yaml hierarchy:
- "nodes/%{::trusted.certname}"
- "env/%{::environment}"
- "common"

3. Puppet-Managed Defaults

Set defaults in your Puppet manifests:

class profile::base {
  $env_name = lookup('env_name', {default_value => 'production'})
  $server_name = "service-${env_name}.${facts['domain']}"
}

For complex cases, consider writing a custom backend that handles cross-references:

class MyHieraBackend
  def lookup(key, scope, order_override, resolution_type, context)
    # Custom logic to check other hierarchy levels
  end
end
  • Keep variable dependencies flowing downward in the hierarchy
  • Document cross-file references clearly
  • Consider using explicit lookups rather than implicit interpolation
  • Test hierarchy changes thoroughly