The lookup
function gives you the ability to establish a default value for the hash itself, but not really for the keys and values inside the hash. Additionally, using the lookup
function as a class parameter value in Puppet will be superseded by automatic parameter bindings. In other words, your lookup
function in this context does nothing because Puppet is preferring automatic parameter bindings over it, and you are using those (this is definitely true for lookup
in conjunction with Hiera 5, which it appears you are using). I personally find this behavior annoying, but it is what it is. Edit: Never mind on that specific reasoning; the parameter is called $testhash
and not $hash
. If that parameter is coming from module data though, then the lookup
will still be ignored since Puppet only allows automatic parameter binding for module data.
I am surprised that the resource defaults are not working here for you. I have to believe that this is either unintended, or something was implemented wrong regarding them when you went that route.
Regardless, here is a guaranteed method for you. First, we implement per-expression default attributes: https://puppet.com/docs/puppet/5.3/lang_resources_advanced.html#per-expression-default-attributes
class test (
Hash $testhash = lookup('test::hash', "merge" => 'hash'}),
){
$testhash.each |String $key, Hash $value| {
user {
default:
ensure => present,
name => 'username',
password => 'userpassword',
groups => ['usergroups'],
managehome => false,
;
$key:
ensure => $value['ensure'],
name => $value['name'],
password => $value['password'],
groups => $value['groups'],
managehome => $value['managehome'],
;
}
}
}
One problem still remains here though, which is that you are establishing values for the attributes in the second block for all iterations. If those key-value pairs are undefined, Puppet will error instead of defaulting to another value. We need to instruct Puppet to only establish values for attributes if you have defined a value for them in the first place. Thankfully, we can do this with the *
attribute: https://puppet.com/docs/puppet/5.3/lang_resources_advanced.html#setting-attributes-from-a-hash
class test (
Hash $testhash = lookup('test::hash', "merge" => 'hash'}),
){
$testhash.each |String $key, Hash $value| {
user {
default:
ensure => present,
name => 'username',
password => 'userpassword',
groups => ['usergroups'],
managehome => false,
;
$key:
* => $value,
;
}
}
}
One recommendation here would be to make your lambda iterator variables $key, $value
a bit more transparently named. Another note is the caveat that for the *
attribute to work, your hash keys must match Puppet attribute names.
Updated question: In the situation where the hash has a key with a nil
value, you can set an empty hash as the default value for the key's value in the lambda iterator parameters. The empty hash will replace the undef
(Puppet nil
) for the $value
during that iteration. This will ensure that no attributes and values are included in the *
operator and that all the defaults will prevail.
class test (
Hash $testhash = lookup('test::hash', "merge" => 'hash'}),
){
$testhash.each |String $key, Hash $value = {}| {
user {
default:
ensure => present,
name => 'username',
password => 'userpassword',
groups => ['usergroups'],
managehome => false,
;
$key:
* => $value,
;
}
}
}