terraform flatten loop for 3 times - terraform

Has any one tried to get flatten for 3 loops working? I keep getting an error when i try the 3rd one:
I am trying to loop through a list within a data resource - data.instances.sg.ids
Variable example:
alarms = [
{
instances = data.instances.ids ## a list of instance ids
config = [
metric_name = "disk_free"
threshold = "GreaterThan"
]
}
]
locals {
configs = flatten([
for config_key, config_list in var.alarms : [
for instance in config_list.instances : {
for config in config_list.configs : {
instance_id = instance
metric_name = config.name
threshold = config.threshold
}
}
]
])
}
how can i properly loop through and flatten this list with data instances list.
Thanks

Not sure what exactly you want to construct, but I think it should be:
locals {
configs = flatten([
for config_key, config_list in var.alarms : [
for instance in config_list.instances : [
for config in config_list.configs :
{
instance_id = instance
metric_name = config.name
threshold = config.threshold
}
]
]
])
}

Related

did I do something wrong? or I found a bug?

this is to prove that there might be a bug in setintersection() or keys() in terraform v1.2.1
that it treats local and var differently.
I hope someone can help to confirm, or point out if I did something wrong?
# this file is to prove that there might be a bug in setintersection() or keys() in terraform v1.2.1
# that it treats local and var differently.
# I hope someone can help to confirm, or point out if I did something wrong?
# local.regions is to serve as the setintersection control set of strings
# as the keys(s_v) has non-region-related keys
locals {
regions = ["region-1", "region-2"]
}
# local.local_obj has the exact same data structure as of var.var_obj
locals {
local_obj = {
collection_local = {
subnet_local = {
other_keys = "somthing"
region-1 = "cidr-for-region-1-for-local-obj"
# region-2 = "cidr-for-region-2-for-local-obj"
}
}
}
}
# var.var_obj is a map(map(object())) data structure, outter map is the collection of subnets,
# inner map is the subnets within a collection, object is the subnet config
variable "var_obj" {
type = map(
map (
object ({
other_keys = "whatever"
region-1 = optional(string)
region-2 = optional(string)
})
)
)
default = {
collection_var = {
subnet_var = {
other_keys = "something_else"
region-1 = "cidr-for-region-1-for-var-obj"
# region-2 = "cidr-for-region-2-for-var-obj"
}
}
}
}
# local.transform-local and local.transform-var are two transforming locals to suite subnet resource needs
locals {
transform-local = flatten([
for c_k,c_v in local.local_obj : [
for s_k, s_v in c_v : [
for rg in setintersection(local.regions, keys(s_v)) : {
name = "${rg}-${s_k}"
region = rg
ip_cidr_range = s_v[rg]
}
]
]
])
transform-var = flatten([
for c_k,c_v in var.var_obj : [
for s_k, s_v in c_v : [
for rg in setintersection(local.regions, keys(s_v)) : {
name = "${rg}-${s_k}"
region = rg
ip_cidr_range = s_v[rg]
}
]
]
])
}
# output.contrast is to show the different behavior of setintersection() and keys() on local and var
# I think it is a bug?
# as commenting out the region-2 in local and var has different behavior,
# local behaves corretly, not producing region-2 record at all,
# but var transformer produced a region-2 record without ip_cidr_range, which will break the resource code execution
output "contrast" {
value = {
transform-local = local.transform-local
transform-var = local.transform-var
}
}
if uncommenting the region-2 line, setintersection() and keys() behave exactly the same on local and var:
output:
contrast : {
transform-local : [
{
name : "region-1-subnet_local"
ip_cidr_range : "cidr-for-region-1-for-local-obj"
region : "region-1"
}
{
name : "region-2-subnet_local"
ip_cidr_range : "cidr-for-region-2-for-local-obj"
region : "region-2"
}
]
transform-var : [
{
name : "region-1-subnet_var"
ip_cidr_range : "cidr-for-region-1-for-var-obj"
region : "region-1"
}
{
name : "region-2-subnet_var"
ip_cidr_range : "cidr-for-region-2-for-var-obj"
region : "region-2"
}
]
}
but if I comment out the region-2 line (as shown in code above), transform-var output is not desirable as it produces something that will cause malfunctioning
output:
contrast : {
transform-local : [
{
name : "region-1-subnet_local"
ip_cidr_range : "cidr-for-region-1-for-local-obj"
region : "region-1"
}
]
transform-var : [
{
name : "region-1-subnet_var"
ip_cidr_range : "cidr-for-region-1-for-var-obj"
region : "region-1"
}
{
name : "region-2-subnet_var"
region : "region-2"
}
]
}

Convert Terraform map keys into a list

I have a Terraform map of accounts like below.
account_map = {
111111111111 = "DEV"
222222222222 = "DEV"
333333333333 = "STG"
333333333333 = "PROD"
}
How can I create a list of Dev accounts IDs as below from the above map?. I tired to use keys function but not sure how to search the DEV value inside the keys function.
dev_accounts = ["111111111111", "222222222222"]
For situations like this without an intrinsic function, and no possibility of a custom function, then you basically must use a for expression:
local {
dev_accounts = [ for number, account in var.account_map : number if account == "DEV" ] # ["111111111111", "222222222222"]
}
There is a built-in Terraform keys function.
> keys({a=1, c=2, d=3})
[
"a",
"c",
"d",
]
So, in your case:
locals {
account_map = {
111111111111 = "DEV"
222222222222 = "DEV"
333333333333 = "STG"
333333333333 = "PROD"
}
dev_accounts = keys(local.account_map)
}
output "dev_accounts" {
value = local.dev_accounts
}
Results to:
% terraform plan
Changes to Outputs:
+ dev_accounts = [
+ "111111111111",
+ "222222222222",
+ "333333333333",
]
...
Terraform docs: https://developer.hashicorp.com/terraform/language/functions/keys

terraform combine 2 objects with 2 attributes for aws instance_ids

Following hashicorp doc to leverage for_each to provision multiple instance using local var map.
I am unable to get the instance_ids into a single lists for output:
output "instance_ids" {
description = "IDs of EC2 instances"
value = { for p in sort(keys(var.project)) : p => module.ec2_instances[p].instance_ids }
}
This is the output:
instance_ids = {
"client-webapp" = [
"i-0e11fcc341e6ce292",
"i-0b7ddd178c0590116",
"i-0c570628d3997874b",
"i-0a1642d7cc173f329",
]
"internal-webapp" = [
"i-0e65c8569f2d2c6f5",
"i-0c62e911e9446c53b",
]
}
Looking to get both objects lists of instance_ids into single list. Any good recommendation? Attempt to use merge, flatten, concat fail with various errors.
The var context for ids in above output loops thru the KEYs 'client-webapp' & 'internal-webapp'
variable "project" {
description = "Map of project names to configuration."
type = map
default = {
client-webapp = {
public_subnets_per_vpc = 2,
private_subnets_per_vpc = 2,
instances_per_subnet = 2,
instance_type = "t2.micro",
environment = "dev"
},
internal-webapp = {
public_subnets_per_vpc = 1,
private_subnets_per_vpc = 1,
instances_per_subnet = 2,
instance_type = "t2.nano",
environment = "test"
}
}
}
Any suggestions?
You can concatenate both lists together.
locals {
instance_ids = {
"client-webapp" = [
"i-0e11fcc341e6ce292",
"i-0b7ddd178c0590116",
"i-0c570628d3997874b",
"i-0a1642d7cc173f329",
]
"internal-webapp" = [
"i-0e65c8569f2d2c6f5",
"i-0c62e911e9446c53b",
]
}
new-list = concat(local.instance_ids["client-webapp"], local.instance_ids["internal-webapp"])
}
output "new-list" {
description = "my new list"
value = local.new-list
}
Here is the output
Changes to Outputs:
+ new-list = [
+ "i-0e11fcc341e6ce292",
+ "i-0b7ddd178c0590116",
+ "i-0c570628d3997874b",
+ "i-0a1642d7cc173f329",
+ "i-0e65c8569f2d2c6f5",
+ "i-0c62e911e9446c53b",
]
Instead of creating a map with the p =>, can you just return the array? And flatten.
Something like...
output "instance_ids" {
description = "IDs of EC2 instances"
value = flatten({ for p in sort(keys(var.project)) : module.ec2_instances[p].instance_ids })
}

How to get Subnet list from VPC with terraform

I've tried to get all subnet ids to add aws batch with terraform with following code:
data "aws_subnet_ids" "test_subnet_ids" {
vpc_id = "default"
}
data "aws_subnet" "test_subnet" {
count = "${length(data.aws_subnet_ids.test_subnet_ids.ids)}"
id = "${tolist(data.aws_subnet_ids.test_subnet_ids.ids)[count.index]}"
}
output "subnet_cidr_blocks" {
value = ["${data.aws_subnet.test_subnet.*.id}"]
}
Fortunately, it was working fine when I've tested like that. But when I tried to integrate with batch terraform like:
resource "aws_batch_compute_environment" "test-qr-processor" {
compute_environment_name = "test-qr-processor-test"
compute_resources {
instance_role = "${aws_iam_instance_profile.test-ec2-role.arn}"
instance_type = [
"optimal"
]
max_vcpus = 256
min_vcpus = 0
security_group_ids = [
"${aws_security_group.test-processor-batch.id}"
]
subnets = ["${data.aws_subnet.test_subnet.*.id}"]
type = "EC2"
}
service_role = "${aws_iam_role.test-batch-service-role.arn}"
type = "MANAGED"
depends_on = [ "aws_iam_role_policy_attachment.test-batch-service-role" ]
}
I've encountered following error message,
Error: Incorrect attribute value type
on terraform.tf line 142, in resource
"aws_batch_compute_environment" "test-processor": 142: subnets =
["${data.aws_subnet.test_subnet.*.id}"]
Inappropriate value for attribute "subnets": element 0: string
required.
Please let me know why, thanks.
"${data.aws_subnet.test_subnet.*.id}" is already string array type.
you should input value without [ ]
write code like :
subnets = "${data.aws_subnet.test_subnet.*.id}"
See :
Here's A document about Resource: aws_batch_compute_environment

Outputting null_data_source output as plain maps instead of array of maps in terraform

Terraform v0.11.11
+ provider.null v2.0.0
variable "vpc1" {
type = "list"
default = [
"name1:ip1:az1:dedicated",
"name2:ip2:az2:dedicated",
]
}
variable vpc2 {
type = "list"
default = [
"foo:10.1:east:yes",
"bar:10.2:west:no",
]
}
data "null_data_source" "values" {
count = "${length(var.vpc1)}"
inputs = "${
map (
"${element(split(":",var.vpc1[count.index]),0)}",
"${element(split(":", var.vpc2[count.index]),0)}"
)
}"
}
output "mappings" {
value = "${data.null_data_source.values.*.outputs}"
}
How to get the desired output , What am i missing
You need function of flatten()
output "mappings" {
value = "${flatten(data.null_data_source.values.*.outputs)}"
}
And there is easy way to get what you need with function concat()
variable "vpc1" {
type = "list"
default = [
"name1:ip1:az1:dedicated",
"name2:ip2:az2:dedicated",
]
}
variable vpc2 {
type = "list"
default = [
"foo:10.1:east:yes",
"bar:10.2:west:no",
]
}
output "mappings" {
value = "${flatten(concat(var.vpc1, var.vpc2))}"
}
Here is the output
$ terraform apply
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
mappings = [
name1:ip1:az1:dedicated,
name2:ip2:az2:dedicated,
foo:10.1:east:yes,
bar:10.2:west:no
]
reference:
https://www.terraform.io/docs/configuration/interpolation.html#flatten-list-of-lists-
https://www.terraform.io/docs/configuration/interpolation.html#concat-list1-list2-

Resources