How can I access value of tuple? - amazon-web-services

I would like to know how can I access the value from the following output:
output "backend_tg_arn" {
value = var.backend_tg_needed ? aws_lb_target_group.backend_tg[*].arn : null
}
It is generated with the following code:
resource "aws_lb_target_group" "backend_tg" {
count = var.backend_tg_needed ? 1 : 0
name = "${var.client_code}-${var.environment}-backend-tg"
port = var.backend_port
protocol = "HTTP"
target_type = var.backend_target_type
vpc_id = aws_vpc.main_vpc.id
}
The output from above is passed to another module as variable:
backend_tg_arn = module.network.backend_tg_arn
and I'm trying to assign it to default action for alb listener:
default_action {
type = "forward"
target_group_arn = var.backend_tg_arn
}
}
Getting error:
var.backend_tg_arn is tuple with 1 element
Inappropriate value for attribute "target_group_arn": string required.
I am struggling how can I access this arn value... it should be done with some kind of loop but can't figure it out.

You are always setting a count of 1 so you can instead just output the first element if it's created:
output "backend_tg_arn" {
value = var.backend_tg_needed ? aws_lb_target_group.backend_tg[0].arn : null
}

Related

Terraform, How to output multiple private ips

I use "terraform-aws-modules/ec2-instance/aws" to provision two ec2 instances. How to output these two private IPs?
module "ec2_jekins_agent" {
source = "terraform-aws-modules/ec2-instance/aws"
version = "4.1.4"
for_each = toset(["one", "two"])
name = "jenkins-agent-${each.key}"
vpc_security_group_ids = [aws_security_group.sg_jenkins_agent.id]
subnet_id = data.terraform_remote_state.vpc.outputs.private_subnets[0]
}
The code below does not work.
output "jenkins_agent_private_ips" {
value = [module.ec2_jekins_agent.private_ip]
}
Error: Unsupported attribute
module.ec2_jekins_agent is object with 2 attributes
This object does not have an attribute named "private_ip".
Since you've used for_each, you can use values:
output "jenkins_agent_private_ips" {
value = values(module.ec2_jekins_agent)[*].private_ip
}

For loop in terraform

I am trying to get the value of function name from local.tf but I am not able to get it. I have terraform,tfvars in which I am giving the function name then it is passed to variable.tf. From varibale.tf I pass it to local.tf then to main.tf. I am not able to get the function name in main.tf. Any help would be appreciated.
terraform.tfvars
config = {
s3= {
//s3 configurations
}
s3_notifications = {
function_name = "test-lambda-mary"
}
}
variable.tf
variable "config" {
type = any
description = "S3 configuration block"
}
local.tf
function_name = {
for k, v in var.config :
k => lookup(v, "function_name", "")
}
module "all_notifications" {
source = "terraform-aws-modules/s3-bucket/aws//modules/notification"
for_each = var.config
bucket = module.s3_bucket[each.key].this_s3_bucket_id
lambda_notifications = {
lambda = {
function_name = local.function_name[each.key]
function_arn = "arn:aws:lambda:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:function:${local.function_name[each.key]}"
events = ["s3:ObjectCreated:*"]
}
}
}
error
"function_name" doesn't comply with restrictions ("^(arn:[\\w-]+:lambda:)?([a-z]{2}-(?:[a-z]+-){1,2}\\d{1}:)?(\\d{12}:)?(function:)?([a-zA-Z0-9-_]+)(:(\\$LATEST|[a-zA-Z0-9-_]+))?$"): ""
│
│ with module.all_notifications["s3"].aws_lambda_permission.allow["lambda"],
│ on .terraform/modules/all_notifications/modules/notification/main.tf line 63, in resource "aws_lambda_permission" "allow":
│ 63: function_name = each.value.function_name
If I put the function_name inside s3 braces it works absolutely fine but I need to have the fucntion name in s3_notification
That looks like a great hint. You're iterating over var.config which has 2 keys and only 1 of them has function_name defined. So when module is requested with s3 as a key, the function_value for that key will be empty string and AWS will fail the request as expected.
You can filter for_each = var.config to exclude such case, something like:
for_each = { for k, v in var.config: k => v if local.function_name[each.key] != ""}
Little nitpick: seems like the source of the module could be incorrectly written. Instead of terraform-aws-modules/s3-bucket/aws//modules/notification potentially it should be terraform-aws-modules/terraform-aws-s3-bucket//modules/notification. See https://github.com/terraform-aws-modules/terraform-aws-s3-bucket
A stab in the dark as I haven't used that module before.
But by looking at your error message:
"function_name" doesn't comply with restrictions ("^(arn:[\\w-]+:lambda:)?
it looks like for function_name you should pass Lambda's ARN, not name (contrary to what the variable name says).
BTW, is function_arn even a parameter here?
This error is coming since terraform module is expecting a valid and mandatory function name to create [aws_lambda_permission][1] resource using terraform-aws-modules/s3-bucket/aws//modules/notification module.
In your case you are looping the module on var.config which consist s3 and s3_notifications part and while first iteration its getting function name as null therefore its throwing this error.
"function_name" doesn't comply with restrictions ("^(arn:[\\w-]+:lambda:)?([a-z]{2}-(?:[a-z]+-){1,2}\\d{1}:)?(\\d{12}:)?(function:)?([a-zA-Z0-9-_]+)(:(\\$LATEST|[a-zA-Z0-9-_]+))?$"): ""
Better split the variable specific to s3_notification as list and iterate the module as below using count.
variable "s3_config" {
type = any
default = {
s3 = {
}
}
}
variable "s3_notify_lambda_func" {
type = list
default = ["test-lambda-mary","test"]. #N Numbers of functions
}
data "aws_region" "current" {}
data "aws_caller_identity" "current" {}
module "all_notifications" {
source = "terraform-aws-modules/s3-bucket/aws//modules/notification"
count = length(var.s3_notify_lambda_func) > 0 ? length(var.s3_notify_lambda_func) : 0
bucket = module.s3_bucket[count.index].this_s3_bucket_id
lambda_notifications = {
lambda = {
function_name = var.s3_notify_lambda_func[count.index]
function_arn = "arn:aws:lambda:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:function:${var.s3_notify_lambda_func[count.index]}"
events = ["s3:ObjectCreated:*"]
}
}
}

Terraform - don't create resource if data source does not exist

I'm using the following set up to iterate through my locals. Certain parameters should only be filled in if terraform can grab the data resource. If the data resource DOES NOT EXIST, then it is noted in the parameter and then the resource creation is skipped.
#Only get the data resource if it exists#################################
data "aws_ssm_parameter" "example_parameter" {
count = "${var.does_ssm_parameter_exist == true ? 1 : 0}"
name = "ssm_parameter"
}
#List of parameters for all config rules
locals {
config_rule_params = {
"access_keys_rotated" = {
"input_parameters" = "example"
},
"acm_certificate_expiration_check" = {
#ERROR! Get input parameters from data source if it exists#################################
"input_parameters" = "${var.does_ssm_parameter_exist == "true" ? "${data.aws_ssm_parameter.example_parameter[count.index].value}" : "DOES_NOT_EXIST"}"
}
}
#Only create config rule if input parameters exist
resource "aws_config_config_rule" "parameterised_config_rules" {
for_each = {
for rule, params in local.config_rule_params : rule => params
if params.input_parameters != "DOES_NOT_EXIST"
}
input_parameters = each.value.input_parameters
}
Unfortunately, it seems like I cannot use count.index in this way:
Error: Reference to "count" in non-counted context
"input_parameters" = "${var.does_ssm_parameter_exist == "true" ? "${data.aws_ssm_parameter.example_parameter[count.index].value}" : "DOES_NOT_EXIST"}"
The "count" object can be used only in "resource" and "data" blocks, and only when the "count" argument is set.
Your use of count.index in locals is incorrect. count can be used in resources and modules only, not locals. Thus you have to explicitly specify which parameter index do you want as follows:
"input_parameters" = "${var.does_ssm_parameter_exist == "true" ? "${data.aws_ssm_parameter.example_parameter[0].value}" : "DOES_NOT_EXIST"}"
Depending on the nature of your example_parameter you many need to have regular loop or use splat expression to get all its values.

Outputs from for_each loop for each resource

I am having a hard time figuring out how to make an output for each target group resource that this code creates.
I'd like to be able to reference each one individually in other modules. It sounds like for_each stores it as a map, so my question is how would I get the arn for targetgroup1 and targetgroup2?
Terraform normally refers to outputs by resource name, so I am struggling with that in this scenario and also how to refer to these individual arns.
Would I also need to work the outputs into the for_each or could I drop it into the output.tf file?
locals {
target_groups_beta = {
targetgroup1 = {
name = "example",
path = "/",
environment = "Beta"
}
targetgroup2 = {
name = "example2",
path = "/",
environment = "Beta"
}
}
}
resource "aws_lb_target_group" "target-group" {
for_each = local.target_groups_beta
name = "example-${each.value.name}-"
port = 80
protocol = "HTTP"
vpc_id = var.vpc-id
deregistration_delay = 5
tags = {
Environment = "${each.value.environment}"
}
health_check{
healthy_threshold = 2
unhealthy_threshold = 2
timeout = 10
interval = 15
path = each.value.path
}
}
I receive the following error when trying to do it in the output.tf file without a key value, but when I input one such as value = "${aws_lb_target_group.target-group[0].arn}" it says it's invalid. Error without key value below:
Error: Missing resource instance key
on modules\targetgroups\output.tf line 2, in output "tg_example_beta":
2: value = "${aws_lb_target_group.target-group.arn}"
Because aws_lb_target_group.target-group has "for_each" set, its attributes
must be accessed on specific instances.
For example, to correlate with indices of a referring resource, use:
aws_lb_target_group.target-group[each.key]
The aws_lb_target_group.target-group generated will be a map, with key values of targetgroup2 and targetgroup1.
Therefore, to get the individual target group details you can do:
output "target-group1-arn" {
value = aws_lb_target_group.target-group["targetgroup1"].arn
}
To return both as a map:
output "target-groups-arn-alternatice" {
value = {for k, v in aws_lb_target_group.target-group: k => v.arn}
}
target-groups-arn-alternatice = {
"targetgroup1" = "arn:aws:elasticloadbalancing:us-east-1:xxxx:targetgroup/example-example/285b26e15221b113"
"targetgroup2" = "arn:aws:elasticloadbalancing:us-east-1:xxxx:targetgroup/example-example2/075bd58359e4c4b2"
}
To return both as a list (order will be same as for keys function):
output "target-groups-arn" {
value = values(aws_lb_target_group.target-group)[*].arn
}
target-groups-arn = [
"arn:aws:elasticloadbalancing:us-east-1:xxxx:targetgroup/example-example/285b26e15221b113",
"arn:aws:elasticloadbalancing:us-east-1:xxxx:targetgroup/example-example2/075bd58359e4c4b2",
]

How to use for_each with iterator in dynamic block in Terraform?

I am trying to do this basically:
module "california" {
source = "./themodule"
# ...
}
module "oregon" {
source = "./themodule"
# ...
}
resource "aws_globalaccelerator_endpoint_group" "world" {
# ...
dynamic "endpoint_configuration" {
for_each = [
module.california.lb,
module.oregon.lb
]
iterator = lb
content {
endpoint_id = lb.arn
weight = 100
}
}
}
# themodule/main.tf
resource "aws_lb" "lb" {
# ...
}
output "lb" {
value = aws_lb.lb
}
I am outputting lb from a submodule in Terraform, and trying to use that in the parent module in a for_each array, with a custom iterator name. It is giving me this error:
This object does not have an attribute named "arn".
But it DOES have that attribute, it's an aws_lb. What am I doing wrong in the usage of this for_each and module setup, and how do I fix it? Thank you very much!
https://www.hashicorp.com/blog/terraform-0-12-rich-value-types/
If I change it to this it seems to work:
resource "aws_globalaccelerator_endpoint_group" "world" {
listener_arn = aws_globalaccelerator_listener.world.id
endpoint_configuration {
endpoint_id = module.california.lb.arn
weight = 100
}
}
From the docs:
The iterator object (setting in the example above) has two attributes:
key is the map key or list element index for the current element. If the for_each expression produces a set value then key is identical to value and should not be used.
value is the value of the current element.
Based on that, in the content, you should used lb.value["arn"], as per example. Thus, the following could be tried:
content {
endpoint_id = lb.value["arn"]
weight = 100
}