Puppet Pattern: the site_location fact

Often you will need location-specific settings on your servers. Especially when you subscribe to the “almost everything should be managed” school - likely your /etc/resolv.conf will depend on where a server (or VM) is.

My recommendation: have a fact called site_location and use that in your hiera hierarchy.

Example for your hiera.yaml:

---
:hierarchy:
  - defaults
  - "nodes/%{trusted.certname}"
  - "environment/%{server_facts.environment}"
  - "location/%{::site_location}"
  - global

An example location yaml, in my case location/nlay-fle.yaml:

---
nullmailer::smarthost: 10.40.0.10
dnsclient::nameservers:
  - 10.40.0.10
  - 10.40.0.11

If you have nicely short site names, I would use those as the site_location. In a well-mannered environment, you would have that name as part of the machines fqdn. Or, for VMs, you can probably retrieve that using your hypervisor API from the VM host.

Or, if you are a bit more ghetto:

For VMs: have the VM host write the location name into a file inside the VM. (Previously, I have used /etc/facter/facts.d for that.)

For physical machines, derive the location off the configured IP addresses. Like this:

# modules/site/lib/facter/site_location.rb
require 'puppet'
Facter.add("site_location") do
  lookup_table = {
    '10.40.' => 'nlay-fle',
    '10.41.' => 'site-b',
    '10.42.' => 'site-c',
    '10.43.' => 'site-d',
    '10.44.' => 'site-e',
  }
  setcode do
    if Facter::Util::Resolution.which('ip')
      ips = Facter::Util::Resolution.exec('ip addr ls | awk \'/inet / {print $2}\'').split
      lookup_table.select { |prefix, location| ips.any? { |ip| ip.start_with?(prefix) } }.values.first
    end
  end
end