pass list(strings) as a terraform variable - amazon-web-services

While defining my module, I do this:
module "activate_alarms" {
source = "../_modules/aws/.."
config = module.config
alarm_arns = ["arn:aws:cloudwatch:eu-central-123:test-alarm"]
}
variable "alarm_arns" {
type = list(string)
}
resource "aws_lambda_function" "processing_lambda" {
filename = data.archive_file.lambda_zip.output_path
function_name = local.processing_lambda_name
handler = "enable_disable_alarms.lambda_handler"
source_code_hash = data.archive_file.lambda_zip.output_base64sha256
role = aws_iam_role.lambda_role.arn
runtime = "python3.9"
tags = var.config.tags
environment {
variables = {
alarm_arns = local.alarm_arns
}
}
}
However, I get an error:
Error: Incorrect attribute value type
│
│ on ../_modules/aws/enable_disable_alarms/processing_lambda.tf line 20, in resource "aws_lambda_function" "processing_lambda":
│ 20: variables = {
│ 23: alarm_arns = local.alarm_arns
│ 30: }
│ ├────────────────
│ │ local.alarm_arns is a list of string, known only after apply
│
│ Inappropriate value for attribute "variables": element "alarm_arns": string required.
What's the correct way to pass a list of strings?
Edit:
locals {
config = var.config
alarm_arns = "${var.alarm_arns}"
}

You can't pass list(string) directly as environment into lambda. You have to make it into string first, e.g.:
environment {
variables = {
alarm_arns = jsonencode(var.alarm_arns)
}
}
Then in your lambda you would have to convert it back to list of strings according to your programming language.

Related

Add tag to launch template for ec2 nodes

I am trying to add tags to a launch template so that the ec2 nodes are tagged and named..
When I add the hardcoded tags inside the module it works, but the idea is to have dynamic tags and be able to merge with the local ones.
module
resource "aws_autoscaling_group" "ecs_asg" {
name = var.name_asg
max_size = var.max_size
min_size = var.min_size
.
.
.
service_linked_role_arn = var.service_linked_role_arn
tags = var.asg_tags
launch_template {
id = aws_launch_template.launch_template.id
version = "$Latest"
}
}
variables.tf
variable "asg_tags" {
type = map(string)
default = {}
}
main.tf
name_asg = "leo-nombre-asg"
max_size = var.max_size
min_size = var.min_size
.
.
.
asg_tags = merge(
local.tags,
{
propagate_at_launch=true,
},
)
locals.tf
locals {
tags = {
"Accountable" = "business"
"Deploy" = "terraform"
"Role" = "services"
}
}
terraform validate
│ Error: Incorrect attribute value type
│
│ on modules\ecs\main.tf line 38, in resource "aws_autoscaling_group" "ecs_asg":
│ 38: tags = var.asg_tags
│ ├────────────────
│ │ var.asg_tags is a map of string
│
│ Inappropriate value for attribute "tags": set of map of string required.
The two fixes necessary here are both for the type in the asg_tags parameter argument value:
asg_tags = [merge(local.tags, { "propagate_at_launch" = "true" })]
Here we use the list/set constructor to cast the type set(map(string)). Terraform will coerce to set instead of list with the constructor as long as the type is specified to be set instead. Since we need to fix the type declaration anyway to be compatible with the resource attribute schema, this is convenient to do:
variable "asg_tags" {
type = set(map(string))
default = {}
}

Terraform loop over list of objects in dynamic block issue

I am trying to create a storage bucket in GCP using Terraform. Please see the below implementation and the .tfvars snippet foe the same
implementation logic
`
resource "google_storage_bucket" "cloud_storage" {
for_each = {for gcs in var.storage_buckets : gcs.name => gcs}
name = each.value.name
location = lookup(each.value, "location", "AUSTRALIA-SOUTHEAST1")
project = data.google_project.existing_projects[each.value.project].project_id
force_destroy = lookup(each.value, "force_destroy", false)
storage_class = lookup(each.value, "storage_class", "STANDARD")
labels = merge(
lookup(each.value, "labels", {}),
{
managed_by = "terraform"
}
)
dynamic "versioning" {
for_each = [for version in [lookup(each.value, "versioning", null)] : version if version != null]
content {
enabled = lookup(versioning.value, "enabled", true)
}
}
dynamic "lifecycle_rule" {
for_each = [for rule in [lookup(each.value, "lifecycle_rule", toset([]))] : rule if length(rule) != 0]
content {
action {
type = lifecycle_rule.value.action.type
storage_class = lookup(lifecycle_rule.value.action, "storage_class", null)
}
condition {
# matches_suffix = lookup(lifecycle_rule.value["condition"], "matches_suffix", null)
age = lookup(lifecycle_rule.value.condition, "age", null)
}
}
}
uniform_bucket_level_access = lookup(each.value, "uniform_bucket_level_access", false)
depends_on = [
data.google_project.existing_projects
]
}
.tfvars snippet
storage_buckets = [
# this 1st bucket is only defined in DEV tf vars. reason: this bucket is a onetime creation for all DWH cloud artifacts under ecx-cicd-tools project.
{
name = "ecx-dwh-artefacts"
localtion = "AUSTRALIA-SOUTHEAST1"
force_destroy = false
project = "ecx-cicd-tools"
storage_class = "STANDARD"
versioning = {
enabled = false
}
labels = {
app = "alation"
project = "resetx"
team = "dwh"
}
uniform_bucket_level_access = false
folders = ["alation/","alation/packages/","alation/packages/archive/",
"alation/backups/","alation/backups/data/","alation/backups/data/DEV/","alation/backups/data/PROD/"]
lifecycle_rule = [
{
action = {
type = "Delete"
}
condition = {
age = "10"
}
},
]
}
,
{
name = "eclipx-dwh-dev"
localtion = "AUSTRALIA-SOUTHEAST1"
force_destroy = false
project = "eclipx-dwh-dev"
storage_class = "STANDARD"
versioning = {}
labels = {
app = "dataflow"
project = "resetx"
team = "dwh"
}
uniform_bucket_level_access = false
folders = ["Data/","Data/stagingCustomDataFlow/","Data/temp/","Data/templatesCustomDataFlow/"]
lifecycle_rule = []
}
]
`
Some have I am unable to make the dynamic block working in the bucket provision logic for the lifecycle_rule section, I am passing a list of objects from .tfvars as I need to be able to add many rules to the same bucket.
It looks like the foreach loop is not iterating over the list of objects in the lifecycle_rule of .tfvars
Below are the errors its throwing. Can someone please assist.
Error: Unsupported attribute
│
│ on storage.tf line 56, in resource "google_storage_bucket" "cloud_storage":
│ 56: type = lifecycle_rule.value.action.type
│ ├────────────────
│ │ lifecycle_rule.value is list of object with 1 element
│
│ Can't access attributes on a list of objects. Did you mean to access attribute "action" for a specific element of the list, or across all elements of the list?
╵
╷
│ Error: Unsupported attribute
│
│ on storage.tf line 57, in resource "google_storage_bucket" "cloud_storage":
│ 57: storage_class = lookup(lifecycle_rule.value.action, "storage_class", null)
│ ├────────────────
│ │ lifecycle_rule.value is list of object with 1 element
│
│ Can't access attributes on a list of objects. Did you mean to access attribute "action" for a specific element of the list, or across all elements of the list?
╵
╷
│ Error: Unsupported attribute
│
│ on storage.tf line 61, in resource "google_storage_bucket" "cloud_storage":
│ 61: age = lookup(lifecycle_rule.value.condition, "age", null)
│ ├────────────────
│ │ lifecycle_rule.value is list of object with 1 element
│
│ Can't access attributes on a list of objects. Did you mean to access attribute "condition" for a specific element of the list, or across all elements of the list?
Thank you.
I am expecting it that the dynamic block loop over lifecycle_rule
Your for_each is incorrect. It should be:
dynamic "lifecycle_rule" {
for_each = length(each.value["lifecycle_rule"]) != 0 ? each.value["lifecycle_rule"] : []
content {
action {
type = lifecycle_rule.value.action.type
storage_class = lookup(lifecycle_rule.value.action, "storage_class", null)
}
condition {
# matches_suffix = lookup(lifecycle_rule.value["condition"], "matches_suffix", null)
age = lookup(lifecycle_rule.value.condition, "age", null)
}
}

How to instantiate an object in locals?

I have a module which uses a variable of object type
variable "table" {
type = object({
name = string
read_capacity = number
write_capacity = number
arn = string
policy_name = string
})
}
I am not able to understand how to initialize this and pass to the module
locals {
table_dev_configs = {
arn = "arn:aws:dynamodb:us-east-2:123456789:table/my-table-dev"
name = "some_name"
policy_name = "some_policy"
read_capacity = 20
write_capacity = 20
}
}
module "my_table_dev" {
source = "../../modules/my_table"
table = local.table_dev_configs
}
I mean locals.table_dev_configs looks I am initializing a Map instead of an object.
../modules/my_table.tf
resource "aws_dynamodb_table" "my_table" {
name = var.table.name
read_capacity = var.table.read_capactity
write_capacity = var.table.write_capactity
hash_key = "id"
}
Doing it the Map way gives me this error:
│ Error: Unsupported attribute
│
read_capacity = var.table.read_capactity
│ ├────────────────
│ │ var.table is a object, known only after apply
│
│ This object does not have an attribute named "read_capactity".
╵
What is the right way to initialize an object?
Instead of
read_capacity = var.table.read_capactity
write_capacity = var.table.write_capactity
it should be
read_capacity = var.table.read_capacity
write_capacity = var.table.write_capacity

How to set default value for a field when referencing a value not in map of map?

I have a map of map as a local variable. I am using this
locals {
region_map = {
mumbai = {
is_second_execution = true
cg_ip_address = "ip.add.re.ss"
}
}
}
Now I am referencing it as
module "saopaulo" {
source = "./site-to-site-vpn-setup"
providers = { aws = aws.saopaulo }
is_second_execution = lookup(local.region_map, local.region_map["saopaulo"]["is_second_execution"], false)
cg_ip_address = lookup(local.region_map, local.region_map["saopaulo"]["cg_ip_address"], "")
}
but since I have not added saopaulo in the map, I get an error. I want to set the fields is_second_execution and cg_ip_address to default values without adding saopaulo in the map so how do I do that?
The error I get is -
Error: Unsupported attribute
│
│ on main.tf line 20, in module "saopaulo":
│ 20: is_second_execution = lookup(local.region_map.saopaulo, "is_second_execution", false)
│ ├────────────────
│ │ local.region_map is object with 1 attribute "mumbai"
│
│ This object does not have an attribute named "saopaulo".
There are few ways of doing that. But I think in your particular case, easiest could be to use try, instead of lookup:
module "saopaulo" {
source = "./site-to-site-vpn-setup"
providers = { aws = aws.saopaulo }
is_second_execution = try(local.region_map["saopaulo"]["is_second_execution"], false)
cg_ip_address = try(local.region_map["saopaulo"]["cg_ip_address"], "")
}
Basically, try returns the first value which does not error out.

Extracting list of subnets result in error - is tuple with 1 element

I have a module that creates a VPC with public and private subnets
module "vpc" {
count = var.vpc_enabled ? 1 : 0
source = "./vpc"
}
and as an output of that module I'm extracting the private subnets
output "private_subnets" {
value = aws_subnet.private.*.id
}
Then I want to use that subnets list as an input of another module:
module "eks" {
source = "./eks"
name = var.name
private_subnets = var.vpc_enabled ? module.vpc.private_subnets : var.private_subnets_id
}
basically what I'm trying to achieve is that the user can choose if he want to create a new VPC or use as an input a list of subnets of their existing VPC.
The problem that I've right now is that I'm getting the following error in terraform plan:
on main.tf line 32, in module "eks":
32: private_subnets = var.vpc_enabled ? module.vpc.private_subnets : var.private_subnets_id
|----------------
| module.vpc is tuple with 1 element
This value does not have any attributes.
Does anyone knows how to fix this?
You are defining your vpc module with count. Thus you need to refer to individual instances of the module, even if you have only 1.
private_subnets = var.vpc_enabled ? module.vpc[0].private_subnets : var.private_subnets_id
Just to add Marcin's answer
I had a similar issue when working with dynamic blocks and locals in Terraform.
I had a locals block like this:
locals {
subnet_suffix = "dev-subnet"
delegation_settings = [{
subnet_delegation_name = "app-service-delegation"
subnet_service_delegation_name = "Microsoft.Web/serverFarms"
}]
}
And I was referencing the attributes this way:
module "subnet_public_1" {
source = "../../../modules/azure/subnet"
subnet_name = "${var.subnet_name}-public-1-${local.subnet_suffix}"
resource_group_name = data.azurerm_resource_group.main.name
virtual_network_name = data.azurerm_virtual_network.main.name
subnet_address_prefixes = var.subnet_address_prefixes.public_1
enforce_private_link_endpoint_network_policies = var.enforce_private_link_endpoint_network_policies.public_1
delegation_settings = [
{
subnet_delegation_name = local.delegation_settings.subnet_delegation_name
subnet_service_delegation_name = local.delegation_settings.subnet_service_delegation_name
}
]
tag_environment = var.tag_environment
}
And when I run terraform plan I get the error below:
│ Error: Unsupported attribute
│
│ on main.tf line 68, in module "subnet_public_1":
│ 68: subnet_delegation_name = local.delegation_settings.subnet_delegation_name
│ ├────────────────
│ │ local.delegation_settings is tuple with 1 element
│
│ This value does not have any attributes.
╵
╷
│ Error: Unsupported attribute
│
│ on main.tf line 69, in module "subnet_public_1":
│ 69: subnet_service_delegation_name = local.delegation_settings.subnet_service_delegation_name
│ ├────────────────
│ │ local.delegation_settings is tuple with 1 element
│
│ This value does not have any attributes.
Here's how I solved it:
All I had to do was to add the index to the attributes, in this case it was 0:
module "subnet_public_1" {
source = "../../../modules/azure/subnet"
subnet_name = "${var.subnet_name}-public-1-${local.subnet_suffix}"
resource_group_name = data.azurerm_resource_group.main.name
virtual_network_name = data.azurerm_virtual_network.main.name
subnet_address_prefixes = var.subnet_address_prefixes.public_1
enforce_private_link_endpoint_network_policies = var.enforce_private_link_endpoint_network_policies.public_1
delegation_settings = [
{
subnet_delegation_name = local.delegation_settings[0].subnet_delegation_name
subnet_service_delegation_name = local.delegation_settings[0].subnet_service_delegation_name
}
]
tag_environment = var.tag_environment
}
That's all