Complex hiera lookup not working - puppet

I have the following definition in a yaml file:
keepalived:
cluster_name: "cluster.example.lan"
cluster_ip: "192.168.1.10"
cluster_nic: "eth0"
haproxy:
bind_address: %{hiera('keepalived::cluster_ip')}
And as a result in bind_address I've got an empty string.
If I use %{hiera('keepalived')} I've got the whole hash printed, but I need only cluster_ip from this hash. How can I lookup the cluster_ip ?

I think it is not possible:
Hiera can only interpolate variables whose values are strings. (Numbers from Puppet are also passed as strings and can be used safely.) You cannot interpolate variables whose values are booleans, numbers not from Puppet, arrays, hashes, resource references, or an explicit undef value.
Additionally, Hiera cannot interpolate an individual element of any array or hash, even if that element’s value is a string.
You can always define cluster_ip as a variable:
common::cluster_ip: "192.168.1.10"
and than use it:
keepalived:
cluster_name: "cluster.example.lan"
cluster_ip: "%{hiera('common::cluster_ip')}"
cluster_nic: "eth0"
haproxy:
bind_address: "%{hiera('common::cluster_ip')}"

Hiera uses the . in a string interpolation to look up sub-elements in an array or hash. Change your hiera code to look like this:
keepalived:
cluster_name: "cluster.example.lan"
cluster_ip: "192.168.1.10"
cluster_nic: "eth0"
haproxy:
bind_address: %{hiera('keepalived.cluster_ip')}
For an array, you use the array index (0 based) instead of a hash key.
See interpolating hash or array elements

Related

How to terraform output from state file with or without jq?

I need to read some data from the state file, to be specific, I need to import a DNS record's IP address into my terraform configuration. I have a resource aws_route53_record.dns_record that I imported from the state file.
This is the output definition:
output "dns_record_ip" {
value = aws_route53_record.dns_record.*.records[0]
}
And this is the actual output:
dns_record_ip = toset([
"10.10.10.100",
])
When I try to extract only the IP as a string like this:
terraform output -json dns_record_ip | jq -r '.[0]'
I indeed get a string:
10.10.10.100
Is there a way to achieve this by modifying the output definition so I don't have to use jq and command line since I need to use the output value in my terraform configuration? EDIT: Needed an output value so it can be used by another module.
How can I modify this:
output "dns_record_ip" {
value = aws_route53_record.dns_record.*.records[0]
}
so the value of dns_record_ip is actually 10.10.10.100?
ps: edited to avoid confusion
If you know that there will only be at most one instance of aws_route53_record.dns_record -- for example, if you have a count expression whose value can only be zero or one -- then you can define the output to be the single element from that list using the one function, like this:
output "dns_record_ip" {
value = one(one(aws_route53_record.dns_record[*].records))
}
This is a slightly more confusing use of one than normal because you seem to have two different one-element collections here: the aws_route53_record.dns_record resource as a whole has one instance, and then its records attribute is a one-element set. Using one twice like this should therefore first peel out the one set of records and then the one record from that set.
You should then be able to get this raw string, without any need for jq, using:
terraform output -raw dns_record_ip
If there is a possibility of this result containing more than one element in some cases then there isn't really any alternative to returning a collection, and so you would need to use jq in that case because JSON is the only supported machine-readable format for an output value that cannot convert to a string.

what is lookup used for in terraform?

I am trying to understand what exactly the function "lookup" does in terraform code? does it find maps or lists? just confused how to use this lookup correctly.
The lookup documentation has the following to say about it:
lookup retrieves the value of a single element from a map, given its key. If the given key does not exist, the given default value is returned instead.
The normal way to look up a value from a map by key is to use the index syntax, like example["foo"], but that operation will return an error if there is no element with the key foo in the map example.
lookup is therefore similar to the index syntax except for the additional behavior of choosing a fallback value to use instead of an error if the key doesn't exist. lookup(example, "foo", "default") is the same as example["foo"] except that it will produce "default" rather than an error if there is no element with the key "foo".
More recent Terraform versions also have the try function which can serve as a perhaps easier to understand alternative to lookup, because it still uses the normal index syntax as part of the expression: try(example["foo"], "default") is similar to lookup(example, "foo", "default"), and will produce the same result as long as example is a map of strings.

Set max length on string variable

I am using Ansible to create an Azure storage account which must have a max name size of 24 characters. I am looking at the Jinja truncate() method but the parameter passed to this method removes that number of characters rather than limiting the string to that number of characters.
Is there a different way of implementing a max length of string variable?
Do I need to combine truncate and length filters from Jinja?
You could use Python's slicing notation for this.
Slice objects are also generated when extended indexing syntax is used. For example: a[start:stop:step] or a[start:stop, i].
More in the documentation: https://docs.python.org/3/library/functions.html?highlight=slice#slice
Also a good read: https://python-reference.readthedocs.io/en/latest/docs/brackets/slicing.html
Given:
- debug:
msg: "{{ str[:24] }}"
vars:
str: abcdefghijklmnopqrstuvwxyz0123456789
This should give you:
abcdefghijklmnopqrstuvwx

Map Hiera value to another value

How can I achieve something like this in Hiera?
service::enabled: true
plugin:
sensu:
ensure: (if service::enabled: 'present' else 'absent')
I know I can do this with puppet but would like to avoid that.
If you really, really (see below why you don't want to do this) want to have conditional logic in your data, then you could use my tahu::ppyaml() hiera backend which allows you to have puppet logic embedded in the yaml data, or write your own specific backend function. The tahu module requires Puppet 6. For versions of puppet before Hiera 5 you need to write a hiera 3 backend to achieve something similar. With Hiera 5 a backend function is very simple and can even be written in the puppet language.
You can find the tahu::ppayaml function here:
https://github.com/hlindberg/tahu/blob/master/lib/puppet/functions/tahu/ppyaml_key.rb
Your data would then look like this:
service::enabled: true
plugin:
sensu:
ensure: "if $service::enabled { 'present'} else {'absent'}"
Since the ppyaml backend treats every string as puppet language you need to quote all literal strings in the data file read by ppyaml; for example "'foo'" or '"foo"'.
You can however break out the key with a conditional into a separate file and use and alias in your main data file. Like this:
service::enabled: true
plugin:
sensu:
ensure: '%{alias("sensu::ensure")}'
And then, either using the tahu::ppyaml to bind only dynamic keys:
sensu::ensure: if $service::enabled { 'present'} else {'absent'}"
and adding that to your hiera.yaml referencing tahu::ppyaml as a backend.
It would work the same way if you write your own backend.
If any of this is recommended is a different question as it is questionable to have conditional logic in the data that depends on a variable being set or not in a manifest as you will get one value if you do the lookup before the inclusion of sensu and a different value after - and you are probably looking up the hash for the very purpose of declaring sensu.
Unfortunately, Hiera doesn't have expressions that can do anything as sophisticated as conditional logic.
There are some aliasing and lookup functions, so you may be able to pass the value service::enabled through unmodified, but that is it. The functions in Hiera are documented at https://puppet.com/docs/puppet/latest/hiera_merging.html.

Lookup same keys in multiple hiera files in puppet using lookup command

I am trying to read a key present in multiple hiera files and merge the values.
My hiera file contains:
hierarchy:
- name: "Per-env secrets"
paths:
- "puppet/hiera/dict-1.yaml"
- "puppet/hiera/dict-2.yaml"
and My puppet script contains:
lookup(dictionaries,Hash).each |String $keyDico, Hash $valueDico| {
notify{"The value of dictionary is: ${keyDico}": }
The key 'dictionaries' is present in both dict-1.yaml and dict-2.yaml. However, It always reads and prints the Key from the first matched hiera file.
I tried changing 'Hash' in lookup function's argument to 'Unique' or 'Deep'. But it didn't work.
Getting error:
Error: Evaluation Error: Resource type not found: Deep and Error: Evaluation Error: Resource type not found: Unique
Is there any way to achieve this?
Thanks in advance.
If you look at the docs for specifying merge behaviours (ref), you can see that you need to specify the optional third argument to lookup, and you are getting that error because "unique" is being interpreted as the data type.
Try either:
lookup(dictionaries, Hash, 'unique')
or
lookup(dictionaries, Hash, {'strategy' => 'unique'})
according to whichever you find more readable.

Resources