Making complex Terraform output optional? - amazon-web-services

I have an unenviable situation where I have to pass some unnecessary information into a module to make one thing (a Private Hosted R53 Zone), but not a bunch of others.
tl;dr, I'm trying to suppress outputs. If I didn't need to expose this output, my plan would behave as I expect. Outputs that produce strings or tuples were fine with me using the same condition to decide whether or not they would be evaluated, but the one below is still evaluating the attributes of the object:
output endpoint_dns_entries {
value = contains(local.de_dupe_phz_names, var.service["phz_name"]) ? {empty = [""]
emptyalso = [""]} : {
dns_names = aws_vpc_endpoint.endpoint.*.dns_entry[*].dns_name
hosted_zone_ids = aws_vpc_endpoint.endpoint.*.dns_entry[*].hosted_zone_id
}
}
yields errors:
Error: Unsupported attribute
on ../../endpoint-with-phz/outputs.tf line 12, in output "endpoint_dns_entries":
12: dns_names = aws_vpc_endpoint.endpoint.*.dns_entry[*].dns_name
This value does not have any attributes.
Error: Unsupported attribute
on ../../endpoint-with-phz/outputs.tf line 13, in output "endpoint_dns_entries":
13: hosted_zone_ids = aws_vpc_endpoint.endpoint.*.dns_entry[*].hosted_zone_id
This value does not have any attributes.
Any ideas on how to make this output optional?

Related

How to enforce double quotes on all template values in input transformer

I have a JSON input that I would like to transform to another JSON output. I defined my list of input JSONPaths, and trying to create a simple JSON output in the template like so:
{
"fax": \"<userFax>\"
}
This was one of the formats given in the example from AWS themselves:
{
"instance": \"<instance>\",
"state": [9, \"<state>\", true],
"Transformed": "Yes"
}
However, when I try to update the changes, I get the following error:
Invalid InputTemplate for target ... : [Source: (String)"{ "fax": \"null\" }"; line: 2, column: 13].
Basically, I'd like all incoming values in the input to be converted to strings as an output via the template. This is to prevent values like zip codes from being converted into an integer and having it's leading zero stripped away. But it's confusing that even following the simple example from AWS is failing.

terraform giving error: unsupported argument in module when running terraform plan?

I am getting the Error: Unsupported argument, when I run terraform plan in version 12.24.
Error: Unsupported argument
on .terraform/modules/app/main.tf line 261, in resource "aws_db_instance" "db_instance":
261: timeouts = {
An argument named "timeouts" is not expected here. Did you mean to define a
block of type "timeouts"?
This is the code in tf file:
timeouts = {
create = "${var.db_instance_create_timeout}"
update = "${var.db_instance_update_timeout}"
delete = "${var.db_instance_delete_timeout}"
}
I am not sure how to fix this error.
above error was fixed by removing "=" after timeouts.
I am also getting more errors, that need solutions:
Error: Unsupported argument
on .terraform/modules/rds/main.tf line 150, in resource "aws_db_parameter_group" "db_parameter_group":
150: parameter = concat(var.parameters, local.parameters[local.parameter_lookup])
An argument named "parameter" is not expected here. Did you mean to define a
block of type "parameter"?
Code in tf file:
parameter = concat(var.parameters, local.parameters[local.parameter_lookup])
how to fix this?
I am copying the solution that worked for me from github , credits to hashicorp member bflad :
In Terraform 0.12 (or higher), the configuration language parser is stricter about the distinction between arguments and configuration blocks. This error:
An argument named "XXX" is not expected here. Did you mean to
define a block of type "XXX"?
Generally means the = (equals sign) needs to be removed from an argument assignment so it parses correctly as a configuration block, e.g.
root_block_device {
This distinction in HCL syntax may seem trivial, but under the hood this stricter type checking allowed for consistency with JSON syntax. More information about this change can be found in the Terraform 0.12 Upgrade Guide. Speaking of which, in that guide it does point to the helpful terraform 0.12upgrade command, which should automatically fix issues like these across your Terraform configurations when upgrading from Terraform 0.11. 👍
Error
An argument named "secret_environment_variables" is not expected here.
Did you mean to define a block of type "secret_environment_variables"?
Problem
main.tf
resource "google_cloudfunctions_function" "this" {
secret_environment_variables = var.secret_environment_variables
}
variables.tf
variable "secret_environment_variables" {
type = any
default = {}
description = "Secret environment variables configuration."
}
Solution
resource "google_cloudfunctions_function" "this" {
secret_environment_variables {
key = var.secret_environment_variables_key
secret = var.secret_environment_variables_secret
version = var.secret_environment_variables_version
}
}
variable "secret_environment_variables_key" {
type = string
default = null
nullable = true
description = "Name of the environment variable."
}
variable "secret_environment_variables_secret" {
type = string
default = null
nullable = true
description = "ID of the secret in secret manager (not the full resource name)."
}
variable "secret_environment_variables_version" {
type = string
default = null
nullable = true
description = "Version of the secret (version number or the string `latest`). It is recommended to use a numeric version for secret environment variables as any updates to the secret value is not reflected until new clones start."
}

Unable to create dynamic terraform outputs for use in terraform_remote_state

I have the following code block for creating various IAM groups
resource "aws_iam_group" "environment-access" {
count = "${length(var.environments)}"
name = "access-${element(var.environments, count.index)}"
}
variable "environments" {
default = ["production", "non-production"]
type = "list"
}
I want to write the outputs of the IAM groups created in order to grab the ARN of each group to use as data via terraform_remote_state where it would look something like the following
Outputs:
access-production = arn:aws:iam::XXXXXXX:group/basepath/access-production
access-non-production = arn:aws:iam::XXXXXXX:group/basepath/access-non-production
I am having trouble creating the dynamic outputs as I am unsure how to dynamically create the output stanzas based on the the resource originally created as using the below code yields an error referencing unknown resource 'aws_iam_group.access-production' referenced.
output "access-production" {
value = "${aws_iam_group.access-production.arn}"
}
output "access-non-production" {
value = "${aws_iam_group.access-non-production.arn}"
}
An initial problem with this requirement is that it calls for having a single dynamic list of environments but multiple separate output values. In order to make this work, you'll need to either make the environment inputs separate values or produce a single output value describing the environments.
# Variant with a fixed set of environments (v0.11 syntax)
variable "production_environment_name" {
type = "string"
default = "production"
}
variable "non_production_environment_name" {
type = "string"
default = "non-production"
}
resource "aws_iam_group" "production_access" {
name = "access-${var.production_environment_name}"
}
resource "aws_iam_group" "non_production_access" {
name = "access-${var.non_production_environment_name}"
}
output "access_production" {
value = "aws_iam_group.production_access.arn"
}
output "access_non_production" {
value = "aws_iam_group.non_production_access.arn"
}
# Variant with dynamic set of environments (v0.11 syntax)
variable "environments" {
type = "list"
default = ["production", "non_production"]
}
resource "aws_iam_group" "access" {
count = "${length(var.environments)}"
name = "access-${var.environments[count.index]}"
}
output "access" {
value = "${aws_iam_group.access.*.arn}"
}
The key here is that the input variable and the output value must have the same form, so that we can make all of the necessary references between the objects. In the second example, the environment names are provided as a list, and the group ARNs are also provided as a list such that the indices correspond between the two.
You can also use a variant of the output "access" expression to combine the two with zipmap and get a map keyed by the environment names, which will probably be more convenient for the caller to use:
output "access" {
value = "${zipmap(var.environments, aws_iam_group.access.*.arn)}"
}
The new features in Terraform 0.12 allow tidying this up a bit. Here's an idiomatic Terraform 0.12 equivalent of the version that produces a map as a result:
# Variant with dynamic set of environments (v0.12 syntax)
variable "environments" {
type = set(string)
default = ["production", "non_production"]
}
resource "aws_iam_group" "access" {
for_each = var.environments
name = "access-${each.key}"
}
output "access" {
value = { for env, group in aws_iam_group.access : env => group.arn }
}
As well as having some slightly different syntax patterns, this 0.12 example has an additional practical advantage: Terraform will track those IAM groups with addresses like aws_iam_group.access["production"] and aws_iam_group.access["non_production"], so the positions of the environment names in the var.environments list are not important and it's possible to add and remove environments without potentially disturbing the groups from other environments due to the list element renumbering.
It achieves that by using resource for_each, which makes aws_iam_group.access appear as a map of objects where the environment names are keys, whereas count makes it a list of objects.

Jenkins declarative pipeline using regex 101

I'm new to declarative pipeline and exploring its features. I tried to use regex. The following example is the result of trial and error. Is there a nicer solution out there? I'm looking for a new shiny declarative pipeline way, if there is any.
What I do not get is, why do I have to use the script{}-Enviroment and why do I have to use a local variable (def matcherX)?
working example followed by first attemts:
Working Example:
pipeline {
environment {
dummyText = 'aaa123aaa'
transfare = ''
}
agent any
stages {
stage('regex') {
steps{
script{
def matcherX = dummyText =~ /(\d+)/
transfare = matcherX[0][0]
echo transfare // results in 123
}
}
}
}
}
Error: without a script{}
/*
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
WorkflowScript: 11: Expected a step # line 11, column 21.
def matcherX = dummyText =~ /(\d+)/
^
WorkflowScript: 12: Expected a step # line 12, column 21.
transfare = matcherX[0][0]
*/
Error: with script but with a global variable
//script{
// transfare = dummyText =~ /(\d+)/
/*
// No line displayed where error occured???
an exception which occurred:
in field org.jenkinsci.plugins.pipeline.modeldefinition.withscript.WithScriptScript.script
in object org.jenkinsci.plugins.pipeline.modeldefinition.agent.impl.LabelScript#192ebed
in field groovy.lang.Closure.delegate
in object org.jenkinsci.plugins.workflow.cps.CpsClosure2#31fd6bc8
in field groovy.lang.Closure.delegate
in object org.jenkinsci.plugins.workflow.cps.CpsClosure2#609aa9b5
in field org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.closures
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup#3e51c1c3
in object org.jenkinsci.plugins.workflow.cps.CpsThreadGroup#3e51c1c3
Caused: java.io.NotSerializableException: java.util.regex.Matcher
at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:860)
at org.jboss.marshalling.river.BlockMarshaller.doWriteObject(BlockMarshaller.java:65)
at org.jboss.marshalling.river.BlockMarshaller.writeObject(BlockMarshaller.java:56)
*/
The script block indicates departure from the pure declarative syntax. Ideally builds (just as any other process) could take advantage of a pure declarative syntax to avoid implicit dependencies between steps.
The global variable of a matcher type fails to serialize which is needed to pass it between the Jenkins master and the slave (I guess).

LLVM how to set Attributes::NoUnwind to Function?

I think this is very simple question, but I can't resolve it. Very sad.
So. When I do
llc.exe -march=cpp test.bc
I get interesting test.cpp with this piece of code:
AttrListPtr func__Z2f1i_PAL;
{
SmallVector<AttributeWithIndex, 4> Attrs;
AttributeWithIndex PAWI;
PAWI.Index = 4294967295U; PAWI.Attrs = Attribute::None | Attribute::NoUnwind;
Attrs.push_back(PAWI);
func__Z2f1i_PAL = AttrListPtr::get(Attrs.begin(), Attrs.end());
}
But when I want to write string like PAWI.Attrs = Attribute::None | Attribute::NoUnwind;
in my project, I got error IntelliSense: no operator "=" matches these operands operand types are: llvm::Attributes = int What I need to do?
All necessary headers included. [OS - Windows 7 x64, LLVM - 3.2]
I don't know why the cpp backend generates this code. In any case attribute handing was changed in 3.2 (and will change again in 3.3). The proper way to get an attribute in 3.2 should be:
Attributes::get(Context, Attributes::NoUnwind)
(you can always pass any ArrayRef here as the second argument, to initialize the attribute set with multiple values).
The simplest way to add an attribute to a function would be:
Function->addFnAttr(Attributes::NoUnwind)
And if you want an AttributeWithIndex:
AttributeWithIndex::get(Context, ID, Attributes::NoUnwind)
// OR:
AttributeWithIndex::get(ID, Attributes::get(Context, Attributes::NoUnwind))