Terraform does not work input and environment variables - list

I have a glue job in aws.
I made a loop.
In variables.tf
variable "list_of_jobs" {
type = list(string)
default = ["myjob1","myjob2","myjob3"]
}
In glue.tf
resource "aws_glue_job" "this" {
for_each = toset(var.list_of_jobs)
name = each.value
role_arn = var.role_arn
command {
name = "pythonshell"
python_version = 3
script_location = "s3://mybucket/${each.value}/run.py"
}
}
In main.tf
variable "region" {}
variable "list_of_jobs" {}
module "my_glue" {
source = "../terraform-glue"
region = var.region
list_of_jobs = var.list_of_jobs
}
This loop works fine, and I have 3 glue jobs after execution of terraform apply.
The problem, when I am trying to make:
export TF_VAR_list_of_jobs='["myjob1","myjob2","myjob3"]'
In this case, when I am making terraform apply, I am receiving this:
Error: Invalid function argument
on ../terraform-glue/glue.tf line 2, in resource "aws_glue_job" "this":
2: for_each = toset(var.list_of_jobs)
|----------------
| var.list_of_jobsis "[\"myjob1\",\"myjob2\",\"myjob3\"]"
Invalid value for "v" parameter: cannot convert string to set of any single
type.
Input variables, does not work too. Only variable from variables.tf. Could You help me please ? I am trying to resolve this during all night.

It does not work because you need to provide type constrain for your complex variable if you want to pass it though env variables:
variable "list_of_jobs" {
type = list(string)
default = ["myjob1","myjob2","myjob3"]
}

Related

How to get IP addresses from several variables in Terraform

I tried to do so
private_ip_ws0 = ["10.0.10.3"]
private_ip_ws1 = ["10.0.20.3"]
private_ip_db0 = ["10.0.11.3"]
private_ip_db1 = ["10.0.21.3"]
resource "aws_network_interface" "ws_interface" {
count = 2
subnet_id = aws_subnet.ws_net_public[count.index].id
private_ips = "${var.private_ip_ws}[count.index]"[0]
tags = {
Name = "${var.environment}${count.index}-Interface"
}
}
But I get error:
Error: Reference to undeclared input variable
│
│ on vpc.tf line 55, in resource "aws_network_interface" "ws_interface":
│ 55: private_ips = "${var.private_ip_ws}[count.index]"[0]
│
│ An input variable with the name "private_ip_ws" has not been declared. Did you mean "private_ip_ws1"?
How to write code correctly?
And why in one place Terraform allows you to write like this
cidr_block = "${var.public_cidr}${count.index+1}0.0/24"
but in another similar expression it doesn't allow to write:
private_ips = "${var.private_ip_ws}[count.index]"[0]
Why?
You can't reference a variable name via a variable like that. That just isn't a feature supported by Terraform at all. The correct way to do this would be to convert your variables into arrays, but if you want to keep the variables the same you will need to wrap them in a local array before using them:
private_ip_ws0 = ["10.0.10.3"]
private_ip_ws1 = ["10.0.20.3"]
private_ip_db0 = ["10.0.11.3"]
private_ip_db1 = ["10.0.21.3"]
locals {
private_ip_ws = [private_ip_ws0, private_ip_ws1]
}
resource "aws_network_interface" "ws_interface" {
count = 2
subnet_id = aws_subnet.ws_net_public[count.index].id
private_ips = local.private_ip_ws[count.index]
tags = {
Name = "${var.environment}${count.index}-Interface"
}
}
And why in one place Terraform allows you to write like this
cidr_block = "${var.public_cidr}${count.index+1}0.0/24"
but in another similar expression it doesn't allow to write:
private_ips = "${var.private_ip_ws}[count.index]"[0]
Because in the first example you are just doing basic string interpolation, which is perfectly valid Terraform syntax.
In the second example you are building a dynamic variable name and then trying to reference the value inside that variable, which is completely invalid Terraform syntax.

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
}

Reference variable in a for loop in terraform

I have a list of accounts declared under variable "acct". I need to create as many ARNs as the accounts below using this list. How to do it? The below code gives me attribute error:
A reference to a resource type must be followed by at least one attribute access, specifying the resource name.
variable "acct"{
type = list(string)
default = ["111111111111","22222222222",.....]
}
data "aws_arn" "SrcArn" {
account = [for acc in var.acct : acc ]
arn = ["arn:aws:logs:us-east-1:${account}:*"]
}
I need to create a list of arns which then can be used further down in the code. Can these then be referenced below like this:
condition {
test = "ArnLike"
values = data.aws_arn.SrcArn
variable = "aws:SourceArn"
}
If you want to use aws_arn, you can use to this.
variable "acct"{
type = list(string)
default = ["111111111111","22222222222",.....]
}
data "aws_arn" "SrcArn" {
count = var.acct
arn = ["arn:aws:logs:us-east-1:${var.acct[count.index]}:*"]
}
And if you wanna make arn_list,
locals {
# only use to variable.
# not use to data "aws_arn"
arn_list_only_use_to_variable = [for account in var.acct: "arn:aws:logs:us-east-1:${account}:*"]
# use to data "aws_arn"
src_arn = data.aws_arn.SrcArn
arn_list_2_use_to_data_aws_arn = [for account in src_arn: account]
}
...
condition {
test = "ArnLike"
values = local.arn_list_only_use_to_variable
variable = "aws:SourceArn"
}
...

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.