Terraform error A value of type string cannot be used as the collection in a 'for' expression - string

I have created a module to add an AWS.APIGateway route and authoriser.
The module is called depending on how many variables are in the tfsvars file.
This variable is passed through to the module to create the Route name as extprovider.
I need to take this variable and append a suffix to it so that i can then add multiple clientids (audiences) to the authoriser.
Error: Iteration over non-iterable value
on ../modules/apiresource/locals.tf line 3, in locals:
3: extprovider_new = flatten([ for e in var.extprovider : tolist(["${e}odd","${e}even"])])
├────────────────
│ var.extprovider is "foo"
A value of type string cannot be used as the collection in a 'for' expression.
Error: Iteration over non-iterable value
on ../modules/apiresource/locals.tf line 3, in locals:
3: extprovider_new = flatten([ for e in var.extprovider : tolist(["${e}odd","${e}even"])])
├────────────────
│ var.extprovider is "bar"
A value of type string cannot be used as the collection in a 'for' expression.
My code is;
locals.tf
locals {
extprovider_new = flatten([ for e in var.extprovider : tolist(["${e}odd","${e}even"])])
}
variables.tf
variable extprovider {}

You are passing a string variable to module but in module you want to use it as list.
You can pass variable like
module "module_name" {
...
extprovider = ["foo"]
...
}
and it should work
or
in locals.tf
locals {
extprovider_new = ["${var.extprovider}odd", "${var.extprovider}even"]
}
you can use string variable and convert it to list which you need.

Related

Can I use for_each meta-argument with file function?

I am creating a bunch of random strings using resource_string resource block.
length is a required argument for this resource, and my goal is to read all the values for this variable from a file, using the file function.
Is there a way to do it?
Here is my code, along with the error:
resource "random_string" "any_string" {
for_each = toset(file("string_number_file.txt"))
length = each.key
}
cat string_number_file.txt
"10","12","13"
Goal is to create three random_strings, with above lengths.
Here is the error with above code:
Error: Invalid function argument
│
│ on main.tf line 9, in resource "random_string" "any_string":
│ 9: for_each = toset(file("string_number_file.txt"))
│
│ Invalid value for "v" parameter: cannot convert string to set of any single type.
Thanks in advance!
In that case you can convert your file to json, and then use that:
resource "random_string" "any_string" {
for_each = toset(jsondecode(format("[%s]",file("string_number_file.txt"))))
length = each.key
}

Load variable values from the given .tfvars file got an error to many arguments

I run terraform plan commnad with apllo.tfvars file
terraform plan -var-file=apllo.tfvars
│ Error: Too many command line arguments
│ To specify a working directory for the plan, use the global -chdir flag.
my variable.tf
variable "user" {
type = string
}
# number variable
variable "age" {
type = number
}
apllo.tfvars
user = "AWSUSER"
age = 222
output.tf
output "name" {
value = "hello ${var.user}"
}
output "age" {
value = "age ${var.age}"
}
If you are using Powershell for running Terraform, try specifying the .tfvar file using single or double quotes, such as:
terraform plan -var-file="apollo.tfvar"

How to use a condition count statement in Terraform

Given a module like so
module us-west-2 {
count = "${local.environments[terraform.workspace] == "logging" ? true : false}"
source = "./modules/flow_log"
providers = {
aws = aws.us-west-2
}
log_destination = module.nf_cis_benchmark.aws_s3_bucket_vpc_flow_log
log_destination_type = "s3"
traffic_type = "REJECT"
depends_on = [ module.nf_cis_benchmark.raws_s3_bucket_vpc_flow_log_arn ]
aws_vpc_ids = data.aws_vpcs.us-west-2.ids
}
How can we conditionally create this module based on the return value from local.environments[terraform.workspace]?
Expected:
When the user runs terraform apply the resources are conditionally created based on the selected workspace.
Actual:
330: count = length("${local.environments[terraform.workspace] == "logging" ? true : false}")
│ ├────────────────
│ │ local.environments is object with 9 attributes
│ │ terraform.workspace is "nf-logging"
│
│ Call to function "length" failed: argument must be a string, a collection type, or a structural type.
Your error message has a length() call, but your posted code does not. Please post the actual code that is generating the error when you post a question like this.
I have no idea why you are trying to wrap the count expression in double quotes, or why you are trying to return true or false, and then take the string length of those strings to create a count value. Are you using a really old version of Terraform? I think what you are attempting to do would actually look like this, if you are using Terraform 0.12 or later:
count = local.environments[terraform.workspace] == "logging" ? 1 : 0
There are several issues with the count meta-argument in the question, including the use of it originally, but to answer the question's intent, you could could conditionally manage a module like:
module "us-west-2" {
for_each = local.environments[terraform.workspace] == "logging" ? toset(["this"]) : []
...
}
which will manage one declaration of the module (consequentially from the size one list iterated) when the local equals the string logging, and zero declarations (from the size zero list iterated) otherwise.

How to create string output with splat operator in terraform

I am creating several count - based ELBs with terraform.
e.g.
resource "aws_elb" "webserver_example" {
count = var.create_webserver
name = var.name
subnets = data.aws_subnet_ids.default.ids
security_groups = [aws_security_group.elb[count.index].id]
}
I therefore want to be able to get as outputs their http endpoints.
These outputs I assume shoul be strings, and their should somehow incorporate each elb's dns name.
However the following approach using splat, does not work
output "url" {
value = "http://${aws_elb.webserver_example.*.dns_name}:${var.elb_port}"
}
│ Error: Invalid template interpolation value
│
│ on outputs.tf line 2, in output "url":
│ 2: value = "http://${aws_elb.webserver_example.*.dns_name}:${var.elb_port}"
│ ├────────────────
│ │ aws_elb.webserver_example is empty tuple
│
│ Cannot include the given value in a string template: string required.
╵
Is there a way to print multiple count-based strings?
From what I was able to infer from just the code you provided, your var.create_webserver will have different count values (e.g. >= 0). The answer to your specific question is in this code block:
output "url" {
value = [
for dns_name in aws_elb.webserver_example.*.dns_name :
format("http://%s:%s", dns_name, var.elb_port)
]
}
However, be sure you introduce some way to make the names of your Security Groups and ELBs different, because that will be your next error. For example, name = "${var.name}-${count.index}".
Once you get to that point, you will have output that looks like this:
Outputs:
url = [
"http://so-0-2118247212.us-east-1.elb.amazonaws.com:443",
"http://so-1-1137510015.us-east-1.elb.amazonaws.com:443",
]

Unable to properly reference a value from a list within a map

I'm trying to reference a value within a list from a map but can't seem to get terraform to recognize that its a string.
Below is my module that I'm working on along with the variable defined.
resource "aws_transfer_user" "aws_transfer_users" {
for_each = var.transfer_users_and_keys
server_id = aws_transfer_server.aws_transfer_service.id
user_name = each.key
role = aws_iam_role.aws_transfer_role.arn
home_directory = format("/%s/%s",var.transfer_users_and_keys[each.value[1]],var.transfer_users_and_keys[each.key])
tags = {
Name = each.key
Project = var.product_name
Terraform = true
}
}
variable "transfer_users_and_keys" {
type = map(list(string))
}
For some reason when I call the value from the list it gives me the following error:
on main.tf line 38, in resource "aws_transfer_user" "aws_transfer_users":
38: home_directory = format("/%s/%s",var.transfer_users_and_keys[each.value[1]],var.tran
sfer_users_and_keys[each.key])
|----------------
| each.value[1] is "bucket-dev-client"
| var.transfer_users_and_keys is map of list of string with 2 elements
The given key does not identify an element in this collection value.
Here is my variable that I'm creating:
transfer_users_and_keys = {
format("key-%s",local.environment) = ["value.pub",tostring(local.sftp_bucket[0])]
format("key-%s02",local.environment) = ["value02.pub",local.sftp_bucket]
}
sftp_bucket = [format("bucket-%s-client",local.environment)]
The goal here is to build out the home_directory based on the 2nd value in the "transfer_users_and_keys" variable (tostring(local.sftp_bucket[0])).
When using for_each, you don't need to keep referencing the variable and indexing it. Change:
home_directory = format("/%s/%s",var.transfer_users_and_keys[each.value[1]],var.transfer_users_and_keys[each.key])
to simply
home_directory = format("/%s/%s", each.value[1], each.key)

Resources