How get a value from a puppet resource - puppet

I have a problem with my puppet script.
I would like to get a value set in my resource file. I declare a resource like that
define checkxml(
$account = '',
$pwd = template('abc/abc.erb'),
){
if(empty($pwd)){
fail('pwd empty')
}
}
I call it via :
checkxml{"$agtaccount":
account => $agtaccount,
}
I want to get the value of $pwd. The $pwd will get is value by Template. If i try to show the value in my resource definition it's ok, I get the right value, so the Template works fine.
My problem is to get access this value after calling the ressource. I saw the getparam of stdlib but doesn't work for me.
getparam(Checkxml["$agtaccount"],"pwd")
If i try to get the account parameters instead of pwd it's ok. I think as i doesn't declare the pwd i can't get him back
How can i get him ?
Thanks for your help

Ugh, this looks dangerous. First off I'd recommend to steer clear of that function and the concept it embodies. It faces you with evaluation order dependencies, which can always lead to inconsistent manifest behavior.
As for the retrieval of the value itself - that will likely not work if the default is used. That's because on a catalog building level, there is not yet a value that is being bound to the parameter, if that makes any sense.
The resolution of final parameter values is rather involved, so there are lots of things that can go wrong with a manifest that relies on such introspective functionality.
I recommend to retrieve the desired value in a more central location (that depends on your manifest structure) and use it both when declaring the Checkxml["$agtaccount"] resource as well as its other uses (for which you are currently trying to extract it).

Related

Terraform aws_ssm_parameter null/empty with ignore_changes

I have a Terraform config that looks like this:
resource "random_string" "foo" {
length = 31
special = false
}
resource "aws_ssm_parameter" "bar" {
name = "baz"
type = "SecureString"
value = random_string.foo.result
lifecycle {
ignore_changes = [value]
}
}
The idea is that on the first terraform apply the bar resource will be stored in baz in SSM based on the value of foo, and then on subsequent calls to apply I'll be able to reference aws_ssm_parameter.bar.value, however what I see is that it works on the first run, stores the newly created random value, and then on subsequent runs aws_ssm_parameter.bar.value is empty.
If I create a aws_ssm_parameter data source that can pull the value correctly, but it doesn't work on the first apply when it doesn't exist yet. How can I modify this config so I can get the value stored in baz in SSM and work for creating the value in the same config?
(Sorry not enough karma to comment)
To fix the chicken-egg problem, you could add depends_on = [aws_ssm_parameter.bar] to a data resource, but this introduces some awkwardness (especially if you need to call destroy often in your workflow). It's not particularly recommended (see here).
It doesn't really make sense that it's returning empty, though, so I wonder if you've hit a different bug. Does the value actually get posted to SSM (i.e. can you see it when you run aws ssm get-paramter ...)?
Edit- I just tested your example code above with:
output "bar" {
value = aws_ssm_parameter.bar.value
}
and it seems to work fine. Maybe you need to update tf or plugins?
Oh, I forgot about this question, but turns out I did figure out the problem.
The issue was that I was creating the ssm parameter inside a module that was being used in another module. The problem was because I didn't output anything related to this parameter, so it seemed to get dropped from state by Terraform on subsequent replans after it was created. Exposing it as output on the module fixed the issue.

Conditionally ignore parameter in Terraform if its value is not provided?

I am using "google_sql_database_instance" resource to create a cloud instance with private IP only and taking the value for _private_network_ from variables file or command line argument or no value provided at all.
All I am trying to achieve is, create private IP instance only if private_network is provided and include _private_network_ parameter only if its value is provided. The problem here is this variable doesn't accept empty string ("") as it validates the bellow regular expression.
"projects/((?:(?:[-a-z0-9]{1,63}\\.)*(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?):)?(?:[0-9]{1,19}|(?:[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?)))/global/networks/((?:[a-z](?:[-a-z0-9]*[a-z0-9])?))$".
How would I make a module which is configurable (create private IP instance only if private_network is provided otherwise ignore the parameter completely) and does not compulsorily asks for the value of _private_network_ variable?
The old approach to this would be to pass an empty string ("") to the parameter and the provider would ignore it by convention.
Unfortunately this isn't done everywhere across all providers yet so if you are unable to pass an empty string due to validation on the parameter then you're stuck for now.
The options you have are to raise a pull request for the provider allowing you to pass an empty string, create a second module that differs only by whether it has a private network, or wait for Terraform 0.12 which will introduce the null value that will then make Terraform core ignore the parameter entirely.
The last option is the best one in my opinion but if you have an urgent need (currently no expected release date for 0.12 other than when it's ready) then I'd split the module into two for now to get you able to achieve this and then you can drop the private clone of the module and use the null value when 0.12 is released and you have upgraded to it.

How to refer to alternate resources in Terraform?

Terraform (as of today) has the somewhat disturbing limitation, that you cannot create a resource with an interpolated (calcuted) lifecycle attribute prevent_destroy.
Terraform: How to get a boolean from interpolation?
https://github.com/hashicorp/terraform/issues/3116
The work-around is pretty simple to code, just create 2 resources with "alternating" counts. When you have 1 "production" resource which does not allow destroying you have 0 "testing" resources which can be destroyed. Or the other way round. (See the answer to the stackoverflow question linked
above for details.)
However, this brings up a new question. When I want to refer to "the one of the alternate resources that exists for this execution", how do I do that?
In pseudo code something like
"${local.production ? "${aws_eip.commander_production.public_ip}" : "${aws_eip.commander_testing.public_ip}" }"
This pseudo code cannot work for a couple of reasons:
aws_eip.commander_production is no longer a single resource, it is a list, so you need the * syntax
one of the lists is always empty and Terraform easily complains that it cannot determine the type of an empty list. (I guess because the ternary operator requires that the alternates have the same type)
when you access into an empty list you will get an error (With C semantics the unused alternate would not be evaluated, but Terraform seems to work differently and I got errors when trying to code this)
To work around those I came up with the following hacky solution:
Extend the lists with a dummy element in the end and then refer to the
first element of the extended list. The code for this is pretty
horrible to type, but it seems to work
locals {
dummy = [ "foo" ]
}
output "0101: address" {
value = "${format("Public IP is %s", "${local.production ? "${element("${concat("${aws_eip.commander_production.*.public_ip}", "${local.dummy}")}", 0)}" : "${element("${concat("${aws_eip.commander_testing.*.public_ip}", "${local.dummy}")}", 0)}" }")}"
}
Question: What is a shorter / more elegant way to code this?
Note: I have found one answer myself, but feel welcome to contribute even better ones.
A bit shorter code is
output "0101: address" {
value = "${format("Public IP is %s", "${element("${concat("${aws_eip.commander_production.*.public_ip}", "${aws_eip.commander_testing.*.public_ip}")}", 0)}")}"
}
In plain text: Concatenate the lists and take the first element of the the result. One list has one element and the other one zero, so the result will be what we want, regardless whether the element is in the first or second list.

Any reason Origen::Parameter set contexts are hidden from the user?

Is there any reason the Origen::Parameters sets do not have a public method for retrieving all of the possible set ids? I do see a public method that works, though it isn't named like it is meant to be used publicly. Why is this not more visible?
[6] pry(#<PPEKit::Product>)> $dut.func._parameter_sets.ids
=> [:default,
:func_default,
:func_harvest_default,
EDIT
#Ginty, I tried your suggestion but it doesn't return the keys I am looking for. In the first sentence in the Parameter docs, the keys I am looking for are referred to 'parameter contexts'. The reason these would be useful would be to do something like this:
my_param_key = :my_param_key
if Origen.top_level.func.has_context? my_param_key
...
Specifically, I am creating parameter contexts from the information in my flow file and would like to verify that they exist before trying to access them. Essentially it is a handshake between my test flow and the test method parameters I am storing using unique (hopefully) parameter IDs/contexts.
thx
In your example, dut.func.params should return a hash-like object which contains all the parameter sets, so to get the IDs is just: dut.func.params.keys
EDIT
I see now that you want a collection containing the available contexts, but it doesn't seem like that is currently provided via an API.
I don't think there is any particular reason for that, probably hasn't been needed until now.
params.context returns the currently active context, I would recommend we add params.contexts and/or params.available_contexts to return an array of the available context names.
Origen now supports knowing the available parameter contexts.

When should I use resource or #property_hash in a Puppet provider?

When writing a Puppet provider, there are two ways to access properties of the resource: the resource variable, and the #property_hash variable. I'm trying to use a property foo in a setter, and started by using resource[:foo]. This works when doing
puppet apply
and it works when doing
puppet resource thing thingname
but if I try
puppet resource thing thingname foo=Foo
then resource[:foo] is unset. #property_hash[:foo] has the right value.
I can print out the value of foo right before calling new in self.instances, and it is correct in both cases.
This article shows resource being used all over the place. It's in a function called from flush, so I changed all my setters to work with flush, but still resource[:foo] isn't set.
I can use #property_hash[:foo], but a colleague found that that didn't work when creating a resource - not a problem in my case, as the resource is only managed not created - but I really need to understand this properly to avoid problems in the future. When should I use resource and when #property_hash? And why does resource work in that example but not for me?

Resources