Terraform variables defaults and description for nested values - amazon-web-services

Is there a option to set a defaults and description for nested variables in terraform?
Example:
variable "example_page_rule"{
type = list(object({
level = string,
tags = string,
prefix = string
}))
}
This can be achieved ?
variable "example_page_rule"{
type = list(object({
level = string, default = "1", description = "debug level 0-7"
tags = string, default = {}, description = "tags"
prefix = string, default = "abc", description = "some description"
}))
}

You could set default values for your map with new, experimental feature defaults of TF. Its different syntax, but same outcome. There are no descriptions though.
If you don't want to use the experiential feature, you would have to develop custom code for handling default values, like any other TF code that does it these days. One example of this is here.

Related

Nested structured search is Sourcegraph?

I would like to search Java annotations with another annotations inside. I don't know how many nested levels there are or I don't want to specify it. Finally, I would like to search for examples of #ApiImplicitParams with param type body and with #Example annotations inside.
But first I am trying to match anything nested.
First I searched for
#ApiImplicitParams(...)
and it found me somethind. The very first result is
#ApiImplicitParams({ #ApiImplicitParam(name = "foo", value = "List of strings", paramType = "body", dataType = "Foo") })
and has #ApiImplicitParam inside. Let's try to match it.
I tried
#ApiImplicitParams({#ApiImplicitParam(...) ...})
but it didn't find that case with one nesting and didn't find any cases with multiple #ApiImplicitParams inside.
How to accomplish?
Finally, I would like to search for examples of #ApiImplicitParams with param type body and with #Example annotations inside.
I believe the following query will meet your requirements here.
#ApiImplicitParams({...#ApiImplicitParam(...paramType = "body"...)...}) lang:Java
Some examples of matches --
#ResponseBody
#ApiImplicitParams({ #ApiImplicitParam(name = "foo", value = "List of strings", paramType = "body", dataType = "Foo") })
public Foo create(#RequestBody final Foo foo) {
nickname = "setLoggingSettings")
#ApiImplicitParams({
#ApiImplicitParam(
name = "Logging Config",
value = "Logging config to be updated",
required = true,
dataType = "com.yugabyte.yw.forms.PlatformLoggingConfig",
paramType = "body")
})
public Result setLoggingSettings() throws JoranException {
A note on this --
I don't know how many nested levels there are or I don't want to specify it.
The query provided above assumes one nested block, I'm afraid right now Sourcegraph doesn't have a good way to express an arbitrary level of nesting.
Hope that helps!

Is there an equivalent to omit in Terraform?

I'm working on my Terraform code, and I would like to use the same code whether or a variable is set or equal "".
But if I pass the variable with "" in the aws_vpc module, there is an exception raised, saying a valid cidr is required.
If it were Ansible, I'd use the | omit filter which omit the parameter and don't pass it to the module if the variable is not set.
There is the code.
module "server_vpc" {
source = "terraform-aws-modules/vpc/aws"
name = "servers_vpc"
cidr = var.main_cidr
secondary_cidr = [var.secondary_cidr]
azs = var.azs
public_subnets = local.public_subnets
private_subnets = var.private_subnets
enable_nat_gateway = true
create_igw = true
}
On this code, if the variable is equal to "" (default), then public_subnets equals [var.main_cidr], if the variable is set, public_subnets equals [var.main_cidr, var.secondary_cidr].
But the part that didn't work like I'd like is the line with secondary_cidr = [var.secondary_cidr].
When the variable contains a valid cidr, it works, but when the variable is not set and equals to the default "", Terraform raise an error from the aws vpc module explaining that a valid cidr is needed.
So I would like a way to create this vpc with the module, but not set the parameter secondary_cidr if the variable equals "".
I hope I was clear :)
According to the documentation for this specific module secondary_cidr is not a required argument. In that case you don't have to provide an empty string. You either leave out secondary_cidr entirely or you assign null to it. Since secondary_cidr expects an array, most likely an empty array [] will work as well.

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.

Select where tag end in a or b in Terraform data lookup

I have 3 subnets. They are named:
test-subnet-az-a test-subnet-az-b test-subnet-az-c
I have a datasource like so:
data "aws_subnet_ids" "test" {
vpc_id = "${module.vpc.id}"
tags = {
Name = "test-subnet-az-*"
}
}
This will return a list including all 3 subnets.
How do I return just the first 2, or those ending in a or b?
Terraform data sources are generally constrained by the capabilities of whatever underlying system they are querying, so the filtering supported by aws_subnet_ids is the same filtering supported by the underlying API, and so reviewing that API (EC2's DescribeSubnets) may show some variants you could try.
With that said, if you can use the data source in a way that is close enough to reduce the resultset down to a manageable size (which you seem to have achieved here) then you can filter the rest of the way using a for expression within the Terraform language itself:
data "aws_subnet_ids" "too_many" {
vpc_id = "${module.vpc.id}"
tags = {
Name = "test-subnet-az-*"
}
}
locals {
want_suffixes = toset(["a", "b"])
subnet_ids = toset([
for s in data.aws_subnet_ids.too_many.ids : s
if contains(local.want_suffixes, substr(s, length(s)-1, 1))
])
}
You can place any condition expression you like after if in that for expression to apply additional filters to the result, and then use local.subnet_ids elsewhere in the configuration to access that reduced set.
I used toset here to preserve the fact that aws_subnet_ids returns a set of strings value rather than a list of strings, but that's not particularly important unless you intend to use the result with a Terraform feature that requires a set, such as the for_each argument within resource and data blocks (which is not yet released as I write this, but should be released soon.)