Terraform list of objects default values - amazon-web-services

I'm getting an error when I try to set default values for an object inside a list:
variable "routes" {
type = list(object({
destination_cidr_block = string
blackhole = bool})
default = {
blackhole = "false"
destination_cidr_block = ""
})
description = "a list of objects containing the cidr blocks of the dest and whether the cidr is a blackhole or not."
default = null
}
When I run this, I get the below error:
Error: Missing argument separator
on variables.tf line 21, in variable "routes":
18: type = list(object({
19: destination_cidr_block = string
20: blackhole = bool})
21: default = {
A comma is required to separate each function argument from the next.
Line 21 "default" is underlined in the error.
Setting defaults in this way works fine when it's just an object by itself. I don't know why it complains when the variable is a list of objects.

You may want to have it like this:
variable "routes" {
type = list(object({
destination_cidr_block = string
blackhole = bool
}))
default = [{
blackhole = "false"
destination_cidr_block = ""
}]
description = "a list of objects containing the cidr blocks of the dest and whether the cidr is a blackhole or not."
}
You cannot really have a default inside a type.

I requested an object type default feature and they are now releasing Optional attributes for object type constraints into the next build of Terraform!

Related

In Terraform is it possible to store multiple values in a single ssm_parameters?

I'm wanting a single ssm_parameter to have multiple values.
values = dog, cat, turtle
I've tried doing
variable "dog" {
default = dog
}
variable "cat" {
default = cat
}
variable "turtle" {
default = turtle
}
resource "aws_ssm_parameter" "animals" {
for_each {
"/default/animals" = "${var.dog}"
"/default/animals" = "${var.cat}"
"/default/animals" = "${var.turtle}"
name = each.key
type = "string"
value = each.value
}
When I check on in the parameter store the ssm path name "default/animals" is only populated by 1 value and not 3 values.
You are approaching this in a wrong way. To make this work, you have to adjust the variable:
variable "animals" {
type = list(string)
description = "List of values for animals."
default = ["dog", "cat", "turtle"]
}
Then, in the second step, you would have to adjust the resource:
resource "aws_ssm_parameter" "animals" {
for_each = toset(var.animals)
name = "/default/animals/${each.value}"
type = "String"
value = each.value
}
Make sure you understand first how for_each [1] works and as well how to convert a list to a set using toset built-in function [2].
[1] https://developer.hashicorp.com/terraform/language/meta-arguments/for_each
[2] https://developer.hashicorp.com/terraform/language/functions/toset

How can I store a three element tuple in AWS SSM parameter with Terraform?

I'm creating private subnets with Terraform:
resource "aws_subnet" "private" {
count = length(data.aws_availability_zones.available.names)
vpc_id = aws_vpc.main_vpc.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index + 10)
availability_zone = element(data.aws_availability_zones.available.names, count.index)
map_public_ip_on_launch = false
tags = {
Name = "${var.client_code}-${var.environment}-private-${element(data.aws_availability_zones.available.names, count.index)}"
}
}
Later I'm trying to create SSM parameter with:
resource "aws_ssm_parameter" "private_subnets_ids" {
name = "/${var.client_code}-${var.environment}/backend/SUBNET_IDS"
type = "StringList"
value = aws_subnet.private.*.id
}
As subnets resource is making three subnets, it raises the following error:
4: value = aws_subnet.private.*.id
|----------------
| aws_subnet.private is tuple with 3 elements
Inappropriate value for attribute "value": string required.
How should I pass this three element tuple to the StringList type parameter?
The value parameter for the aws_ssm_parameter resource needs to be a string type regardless of the type specified. In fact, AWS always expects parameters to be of a string type as seen in the API docs and mentioned in this answer and the StringList type is essentially metadata for the client to expect it to be a string that contains other strings concatenated together by a comma character.
To convert your tuple type from aws_subnet.private.*.id into a list you can join it with the join function like this:
resource "aws_ssm_parameter" "private_subnets_ids" {
name = "/${var.client_code}-${var.environment}/backend/SUBNET_IDS"
type = "StringList"
value = join(",", aws_subnet.private.*.id)
}
Here is full example:
resource "aws_ssm_parameter" "SubnetIDs" {
name = "SubnetIDs"
description = "SubnetIDs"
type = "StringList"
value = join(", ", aws_subnet.private-subnet.*.id)
}

The parameter CacheSubnetGroupName must be provided and must not be blank

I use the module, https://github.com/cloudposse/terraform-aws-elasticache-redis to provision elasticache redis. Below are the errors when I run terraform apply. I have no clue of these errors.
Terraform version: v0.13.5
module.redis.aws_elasticache_parameter_group.default[0]: Creating...
module.redis.aws_elasticache_subnet_group.default[0]: Creating...
Error: Error creating CacheSubnetGroup: InvalidParameterValue: The parameter CacheSubnetGroupName must be provided and must not be blank.
status code: 400, request id: a1ab57b1-fa23-491c-aa7b-a2d3804014c9
Error: Error creating Cache Parameter Group: InvalidParameterValue: The parameter CacheParameterGroupName must be provided and must not be blank.
status code: 400, request id: 9abc80b6-bd3b-46fd-8b9e-9bf14d1913eb
redis.tf:
module "redis" {
source = "git::https://github.com/cloudposse/terraform-aws-elasticache-redis.git?ref=tags/0.25.0"
availability_zones = var.azs
vpc_id = module.vpc.vpc_id
allowed_security_groups = [data.aws_security_group.default.id]
subnets = module.vpc.private_subnets
cluster_size = var.redis_cluster_size #number_cache_clusters
instance_type = var.redis_instance_type
apply_immediately = true
automatic_failover_enabled = false
engine_version = var.engine_version
family = var.family
replication_group_id = var.replication_group_id
elasticache_subnet_group_name = var.elasticache_subnet_group_name
#enabled = true
enabled = var.enabled
#at-rest encryption is to increase data security by encrypting on-disk data.
at_rest_encryption_enabled = var.at_rest_encryption_enabled
#in-transit encryption protects data when it is moving from one location to another.
transit_encryption_enabled = var.transit_encryption_enabled
cloudwatch_metric_alarms_enabled = var.cloudwatch_metric_alarms_enabled
parameter = [
{
#Keyspace notifications send events for every operation affecting the Redis data space.
name = "notify-keyspace-events"
value = "lK"
}
]
context = module.this.context
}
context.tf:
module "this" {
source = "git::https://github.com/cloudposse/terraform-null-label.git?ref=tags/0.19.2"
enabled = var.enabled
namespace = var.namespace
environment = var.environment
stage = var.stage
name = var.name
delimiter = var.delimiter
attributes = var.attributes
tags = var.tags
additional_tag_map = var.additional_tag_map
label_order = var.label_order
regex_replace_chars = var.regex_replace_chars
id_length_limit = var.id_length_limit
context = var.context
}
variable "context" {
type = object({
enabled = bool
namespace = string
environment = string
stage = string
name = string
delimiter = string
attributes = list(string)
tags = map(string)
additional_tag_map = map(string)
regex_replace_chars = string
label_order = list(string)
id_length_limit = number
})
default = {
enabled = true
namespace = null
environment = null
stage = null
name = null
delimiter = null
attributes = []
tags = {}
additional_tag_map = {}
regex_replace_chars = null
label_order = []
id_length_limit = null
}
description = <<-EOT
Single object for setting entire context at once.
See description of individual variables for details.
Leave string and numeric variables as `null` to use default value.
Individual variable settings (non-null) override settings in context object,
except for attributes, tags, and additional_tag_map, which are merged.
EOT
}
variable "enabled" {
type = bool
default = true
description = "Set to false to prevent the module from creating any resources"
}
variable "namespace" {
type = string
default = null
description = "Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp'"
}
variable "environment" {
type = string
default = null
description = "Environment, e.g. 'uw2', 'us-west-2', OR 'prod', 'staging', 'dev', 'UAT'"
}
variable "stage" {
type = string
default = null
description = "Stage, e.g. 'prod', 'staging', 'dev', OR 'source', 'build', 'test', 'deploy', 'release'"
}
variable "name" {
type = string
default = null
description = "Solution name, e.g. 'app' or 'jenkins'"
}
variable "delimiter" {
type = string
default = null
description = <<-EOT
Delimiter to be used between `namespace`, `environment`, `stage`, `name` and `attributes`.
Defaults to `-` (hyphen). Set to `""` to use no delimiter at all.
EOT
}
variable "attributes" {
type = list(string)
default = []
description = "Additional attributes (e.g. `1`)"
}
variable "tags" {
type = map(string)
default = {}
description = "Additional tags (e.g. `map('BusinessUnit','XYZ')`"
}
variable "additional_tag_map" {
type = map(string)
default = {}
description = "Additional tags for appending to tags_as_list_of_maps. Not added to `tags`."
}
variable "label_order" {
type = list(string)
default = null
description = <<-EOT
The naming order of the id output and Name tag.
Defaults to ["namespace", "environment", "stage", "name", "attributes"].
You can omit any of the 5 elements, but at least one must be present.
EOT
}
variable "regex_replace_chars" {
type = string
default = null
description = <<-EOT
Regex to replace chars with empty string in `namespace`, `environment`, `stage` and `name`.
If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits.
EOT
}
variable "id_length_limit" {
type = number
default = null
description = <<-EOT
Limit `id` to this many characters.
Set to `0` for unlimited length.
Set to `null` for default, which is `0`.
Does not affect `id_full`.
EOT
}
Open up the context.tf and set variable "enabled" to true if you want the module to create resources for you, including the subnet group.
Otherwise, you have to create all per-requsite resources yourselves, which includes elasticache_subnet_group_name.
I found the problem. The default values of namespace/environment/stage/name in context.tf are null. That causes the problem. Set these values solved the problem.
You need to provide module input which can be found on the link you provided.
For instance :
Error: Error creating CacheSubnetGroup: InvalidParameterValue: The parameter CacheSubnetGroupName must be provided and must not be blank. status code: 400, request id: a1ab57b1-fa23-491c-aa7b-a2d3804014c9
In this case, you need to set elasticache_subnet_group_name, and so on:

Terraform Error: Incorrect attribute value type for subnet_ids vpc modules

I am getting the Error: Incorrect attribute value type, when I run terraform plan in version 12.24.
Error: Incorrect attribute value type
on .terraform/modules/app/main.tf line 134, in resource "aws_db_subnet_group" "db_subnet_group":
134: subnet_ids = var.subnets
|----------------
| var.subnets is list of tuple with 1 element
Inappropriate value for attribute "subnet_ids": incorrect set element type:
string required.
Code in tf file:
resource "aws_db_subnet_group" "db_subnet_group" {
count = "${var.create_subnet_group ? 1 : 0}"
name_prefix = "${var.name}-"
description = "Database subnet group for ${var.name}"
subnet_ids = var.subnets
The variables.tf file:
variable "subnets" {
description = "Subnets for RDS Instances"
type = "list"
}
How do I fix this?
The error message says that you have list of tuple with 1 element, which would mean that the var.subnets is something in the form of:
variable "subnets" {
description = "Subnets for RDS Instances"
type = "list"
default = [["subnet-070db0eee8c5f3bb1", "subnet-01e76559b44d06aa3"]]
}
Therefore, to use the inner list (i.e. tuple) you have to do:
resource "aws_db_subnet_group" "db_subnet_group" {
# other attributes not shown
subnet_ids = var.subnets[0]
}

Terraform data structures: object and list of object issue

I'm trying to use terraform to create some aws resource.
Here is my issue:
I'm creating some variables, so I can access them from the resources.
Here is the variables.tf file content:
variables.tf
variable "zones" {
type = "list"
default = ["us-east-1a", "us-east-1b"]
}
variable "init" {
type = object({
vpc-id=list(string),
public-subnet=string,
aws_region=string,
ami=string,
vpc-sec-group= list(string)
})
param = {
vpc-id = ["vpc-1111111"]
public-subnet = "subnet-98e4567"
aws_region = "${element(var.zones,0)}"
ami = "ami-09d95fab7fff3776c",
vpc-sec-group = ["sg-d60bf3f5"]
}
}
variable "instances" {
type = list(object({ type=string, count=string, tags=map(string) }))
t2-micro ={
type = "t2.micro"
count = 4
tags = { Name = "Test T2"}
}
m4-large ={
type = "m4-large"
count = 2
tags = { Name = "Test M4"}
}
}
My plan is to use these variable to create some ec2 instances as so
Here is ec2.tf
ec2.tf
resource "aws_instance" "Test-T2" {
type = lookup(var.insts["t2-micro"],"type")
ami = lookup(var.init.ami["ami"],var.init.aws_region["aws_region"] )
count = lookup(var.insts["t2-micro"],"count")
tags = lookup(var.insts["t2-micro"],"tags")
key_name = aws_key_pair.terraform-demo.key_name
vpc_security_group_ids = toset(lookup(var.init, "vpc-sec-group"))
subnet_id = lookup(var.init.params["public-subnet"])
}
ISSUE
When I execute
terraform init
I get the following error:
Error: Unsupported argument
on variables.tf line 26, in variable "instances":
26: t2-micro ={
An argument named "t2-micro" is not expected here.
Error: Unsupported argument
on variables.tf line 32, in variable "instances":
32: m4-large ={
An argument named "m4-large" is not expected here.
Terraform has initialized, but configuration upgrades may be needed.
Terraform found syntax errors in the configuration that prevented full
initialization. If you've recently upgraded to Terraform v0.12, this may be
because your configuration uses syntax constructs that are no longer valid,
and so must be updated before full initialization is possible.
Could someone please help me to correct those errors?
More details and some actions I have taken
I have tried different ways of creating the variables to the best of my knowledge and following the Terraform documentation
to no avail.
I'm just emulating what would be a Python:
list of objects( list of dictionaries)
and
an object
Here is my version of terraform
terraform -v
Terraform v0.12.26
+ provider.aws v2.65.0
some more details
I'm using the latest version of Visual Studio Code 1.45.1 with the Terraform module HashiCop 1.4.0 for "Syntax highlighting, linting, formatting, and validation for Hashicorp's Terraform"
You need to separate your variable declarations and assignments. Those cannot co-exist within the same block.
Your declaration within the variables.tf would look like (with some fixes and cleanup):
variable "instances" {
type = list(object({
type = string # removed commas since you specified object type
count = number # fixed from string type
tags = map(string)
}))
}
Your variable assignment should be moved to a .tfvars file. Customarily this file would be named terraform.tfvars:
instances = [ # you specified a list so we add the proper syntax here
{ # you specified an object, so we remove the keys and retain the values
type = "t2.micro"
count = 4
tags = { "Name" = "Test T2"} # you specified map(string) so Name becomes string
},
{
type = "m4-large"
count = 2
tags = { "Name" = "Test M4"}
}
]
and that will be a valid input variable with proper assignment given your declaration. That will fix your error.