I am adding routes to route table using module. Below is my code. It runs successfully but routes don't get added.
module.tf: (This checks if the publicRoute & privateRoute has more than one item, it will add that many routes to route table)
resource "aws_route" "public_routes" {
count = length(var.ExtraRoutes.publicRoute) > 1 ? length(var.ExtraRoutes.publicRoute) : 0
route_table_id = aws_route_table.VPCPublicSubnetRouteTable[0].id
destination_cidr_block = length(regexall("^[0-9].*.[0-9].*",var.ExtraRoutes.publicRoute[count.index].destination)) != 0 ? var.ExtraRoutes.publicRoute[count.index].destination : null
gateway_id = length(regexall("^igw-.*",var.ExtraRoutes.publicRoute[count.index].target)) != 0 ? var.ExtraRoutes.publicRoute[count.index].target : null
}
resource "aws_route" "private_routes" {
count = length(var.ExtraRoutes.privateRoute) > 1 ? length(var.ExtraRoutes.privateRoute) : 0
route_table_id = aws_route_table.VPCPrivateSubnetRouteTable[0].id
destination_cidr_block = length(regexall("^[0-9].*.[0-9].*",var.ExtraRoutes.privateRoute[count.index].destination)) != 0 ? var.ExtraRoutes.privateRoute[count.index].destination : null
gateway_id = length(regexall("^igw-.*",var.ExtraRoutes.privateRoute[count.index].target)) != 0 ? var.ExtraRoutes.privateRoute[count.index].target : null
}
module_var.tf (I am keeping it only a map)
variable "ExtraRoutes" {
type = map
default = {
publicRoute = []
privateRoute = []
}
}
main.tf (As I need the first item in ExtraRoutes for something else I want from count.index + 1)
module "ExtraVPCs" {
source = "./modules/VPC"
count = length(var.ExtraRoutes)
ExtraRoutes = {
publicRoute = var.ExtraRoutes[count.index + 1].publicRoute
privateRoute = var.ExtraRoutes[count.index + 1].privateRoute
}
}
main_var.tf
variable "ExtraRoutes" {
type = list(object({
publicRoute = list(object({
destination = string
target = string
})
)
privateRoute = list(object({
destination = string
target = string
}))
}))
}
init.tfvars (There are 2 items in ExtraRoutes. It should add the 2nd item in Route table but it's not working as expected.
ExtraRoutes = [
{
publicRoute = [
{
destination = "10.0.0.0/32"
target = "igw-092aba6c187183f48"
}
]
privateRoute = [
{
destination = "10.0.0.0/32"
target = "igw-092aba6c187183f48"
}
]
},
{
publicRoute = [
{
destination = "10.0.0.0/32"
target = "igw-0acf4f7ac1e7eba47"
}
]
privateRoute = [
{
destination = "10.0.0.0/32"
target = "igw-0acf4f7ac1e7eba47"
}
]
}
]
You check the length of a list using >0, not >1:
count = length(var.ExtraRoutes.publicRoute) > 0 ? length(var.ExtraRoutes.publicRoute) : 0
TF counts items from 0. When you use >1, in your case you end up with count = 0.
Related
I need to create TF CloudWatch Metrices only of the env is QAT and PROD. Currently using TF modules to create those in all env.
module "aws_cloudwatch_log_metric_filter" {
source = "https://github.com/modules.git//aws-cloudwatch-log-metric-filter"
log_group_name = "/aws/lambda/${var.lambda_name}"
pattern = "{$.message = \"---------------- Message ----------------\"}"
}
locals {
base_tags = {
environment = var.environment
}
}
Main resource where I am calling module.
resource "aws_cloudwatch_log_metric_filter" "log_metric" {
count = var.count
name = "Metric"
pattern = var.pattern
log_group_name = var.log_group_name
metric_transformation {
name = "name"
namespace = "namespace"
value = "1"
default_value = "0"
}
}
You could do this in the module:
resource "aws_cloudwatch_log_metric_filter" "log_metric" {
count = var.environment == "QAT" || var.environment == "PROD" ? 1 : 0
name = "Metric"
pattern = var.pattern
log_group_name = "${var.environment}-${var.log_group_name}"
metric_transformation {
name = "name"
namespace = "namespace"
value = "1"
default_value = "0"
}
}
The log_group_name = "${var.environment}-${var.log_group_name}" will ensure you don't get issues with the same log group name.
A simple but good solution :
locals {
cw_env = ["QUA", "PROD"]
}
resource "aws_cloudwatch_log_metric_filter" "log_metric" {
count = contains(local.cw_env, var.env) ? 1 : 0
name = "Metric"
pattern = var.pattern
log_group_name = var.log_group_name
metric_transformation {
name = "name"
namespace = "namespace"
value = "1"
default_value = "0"
}
}
Note you can also use it at the above level :
module "aws_cloudwatch_log_metric_filter" {
source = "https://github.com/modules.git//aws-cloudwatch-log-metric-filter"
count = contains(local.cw_env, local.base_tags.environment) ? 1 : 0
log_group_name = "/aws/lambda/${var.lambda_name}"
pattern = "{$.message = \"---------------- Message ----------------\"}"
}
locals {
base_tags = {
environment = var.environment
}
cw_env = ["QUA", "PROD"]
}
Im trying to add retention policy but I want to enable it conditionally, as you can see from the code
buckets.tf
locals {
team_buckets = {
arc = { app_id = "20390", num_buckets = 2, retention_period = null }
ana = { app_id = "25402", num_buckets = 2, retention_period = 631139040 }
cha = { app_id = "20391", num_buckets = 2, retention_period = 631139040 } #20 year
}
}
module "team_bucket" {
source = "../../../../modules/gcs_bucket"
for_each = {
for bucket in flatten([
for product_name, bucket_info in local.team_buckets : [
for i in range(bucket_info.num_buckets) : {
name = format("%s-%02d", product_name, i + 1)
team = "ei_${product_name}"
app_id = bucket_info.app_id
retention_period = bucket_info.retention_period
}
]
]) : bucket.name => bucket
}
project_id = var.project
name = "teambucket-${each.value.name}"
app_id = each.value.app_id
team = each.value.team
retention_period = each.value.retention_period
}
root module is defined as follows
main.tf
resource "google_storage_bucket" "bucket" {
project = var.project_id
name = "${var.project_id}-${var.name}"
location = var.location
labels = {
app_id = var.app_id
ei_team = var.team
cost_center = var.cost_center
}
uniform_bucket_level_access = var.uniform_bucket_level_access
dynamic "retention_policy" {
for_each = var.retention_policy == null ? [] : [var.retention_period]
content {
retention_period = var.retention_period
}
}
}
but I can't seem to make the code pick up the value,
for example as you see below the value doesn't get implemented
~ resource "google_storage_bucket" "bucket" {
id = "teambucket-cha-02"
name = "teambucket-cha-02"
# (11 unchanged attributes hidden)
- retention_policy {
- is_locked = false -> null
- retention_period = 3155760000 -> null
}
}
variables.tf for retention policy is as follows
variable "retention_policy" {
description = "Configuation of the bucket's data retention policy for how long objects in the bucket should be retained"
type = any
default = null
}
variable "retention_period" {
default = null
}
Your var.retention_policy is always null, as its default value. You are not changing the default value at all. Probably you wanted the following:
for_each = var.retention_period == null ? [] : [var.retention_period]
instead of
for_each = var.retention_policy == null ? [] : [var.retention_period]
I'm trying to take each value from the list in the map and iterate over it with the description in a map for a prefix list but can't work out how.
Variable:
users = {
"user1" = {
description = ""
secret_key_value = {
username = "user1"
home_directory = "/user1/"
}
allowlist = ["200.0.0.1/32"]
},
"user2" = {
description = ""
secret_key_value = {
username = "user2"
home_directory = "/user2/"
}
allowlist = ["200.0.0.5/32", "200.0.0.10/32"]
}
Resource:
resource "aws_ec2_managed_prefix_list" "sftp" {
count = local.prefix_list_enabled ? 1 : 0
name = "User Whitelist"
address_family = "IPv4"
max_entries = 10
dynamic "entry" {
for_each = {
for k, v in var.users : k => v
if v.allowlist != "" || v.description != ""
}
content {
cidr = entry.value.allowlist
description = entry.value.description
}
}
}
With the above, I'm getting "Inappropriate value for attribute "cidr": string required.". I need to break up the list values in the allowlist variable key and iterate through them with the description. Does anyone know how I can achieve this?
You have to flatten your users:
locals {
users_flat = merge([
for k,v in var.users: {
for cidr in v.allowlist:
"${k}-${cidr}" => {
description = v.description
secret_key_value = v.secret_key_value
"cidr" = cidr
}
}
]...)
}
then
dynamic "entry" {
for_each = local.users_flat
content {
cidr = entry.value.cidr
description = entry.value.description
}
}
I am trying to pick up both elements in my query_string tuple. However, only the last element is being picked up. Below, is my test/main.tf file:
terraform {
required_version = ">= 0.13.0"
required_providers {
aws = ">= 3.58"
}
}
provider "aws" {
region = "us-east-1"
}
module "test_module" {
source = "../"
lb = [
{
name = "lb-name"
enable_deletion_protection = true
internal = false
load_balancer_type = "application"
lb_listener = [
{
port = "8080"
protocol = "HTTP"
ssl_policy = "ELBSecurityPolicy-2016-08"
default_action = {
type = "redirect"
order = 1
redirect = {
status_code = "HTTP_302"
}
}
lb_listener_rule = [
{
action = {
type = "fixed-response"
fixed_response = {
content_type = "text/plain"
message_body = "This is a message body."
}
}
condition = {
query_string = [
{
key = "health"
value = "check"
},
{
value = "bar"
}
]
}
}
]
}
]
}
]
}
Here is my lb.tf file:
variable "lb" {
description = <<DESCRIPTION
The AWS Elastic Load Balancing v2 module.
DESCRIPTION
type = any
default = []
}
locals {
lb_listener_flat_list = flatten([
for lb in var.lb : [
for lb_listener in lookup(lb, "lb_listener", []) : {
lb_name = lb.name
lb_listener_load_balancer_arn = aws_lb.lb[lb.name].arn
lb_listener_port = lookup(lb, "load_balancer_type", "application") != "gateway" ? lookup(lb_listener, "port", null) : null
lb_listener_protocol = lookup(lb, "load_balancer_type", "application") != "gateway" || lookup(lb, "ip_address_type", null) != "dualstack" ? lookup(lb_listener, "protocol", null) : null
lb_listener_ssl_policy = lookup(lb_listener, "protocol", null) != "HTTPS" || lookup(lb_listener, "protocol", null) != "TLS" ? lb_listener.ssl_policy : lookup(lb_listener, "ssl_policy", null)
lb_listener_default_action_type = lb_listener.default_action.type
lb_listener_default_action_order = lookup(lb_listener.default_action, "order", null)
lb_listener_default_action_redirect = lookup(lb_listener.default_action, "redirect", {}) != {} ? {
lb_listener_default_action_redirect_status_code = lb_listener.default_action.redirect.status_code
} : null
}
]
])
lb_listener_rule_flat_list = flatten([
for lb in var.lb : [
for lb_listener in lookup(lb, "lb_listener", []) : [
for lb_listener_rule in lookup(lb_listener, "lb_listener_rule", []) : [
# I've recently added the line below in an attempte to loop through the list of maps for query_string
for index in range(length(lookup(lb_listener_rule.condition, "query_string", []))) : {
lb_name = lb.name
lb_listener_rule_index = index
lb_listener_rule_listener_arn = aws_lb_listener.lb_listener[lb.name].arn
lb_listener_rule_action_type = lb_listener_rule.action.type
lb_listener_rule_action_fixed_response = lookup(lb_listener_rule.action, "fixed_response", {}) != {} ? {
lb_listener_rule_action_fixed_response_content_type = lb_listener_rule.action.fixed_response.content_type
lb_listener_rule_action_fixed_response_message_body = lookup(lb_listener_rule.action.fixed_response, "message_body", null)
} : null
lb_listener_rule_condition_query_string = lookup(lb_listener_rule.condition, "query_string", {}) != {} ? {
lb_listener_rule_condition_query_string_key = lookup(lb_listener_rule.condition.query_string[index], "key", null)
lb_listener_rule_condition_query_string_value = lb_listener_rule.condition.query_string[index].value
} : null
}
]
]
]
])
lb_map = { for lb in var.lb : lb.name => lb }
lb_listener_map = { for lb_listener in local.lb_listener_flat_list : lb_listener.lb_name => lb_listener }
lb_listener_rule_map = { for lb_listener_rule in local.lb_listener_rule_flat_list : "${lb_listener_rule.lb_name}-[${lb_listener_rule.lb_listener_rule_index}]" => lb_listener_rule }
}
resource "aws_lb_listener" "lb_listener" {
for_each = local.lb_listener_map
load_balancer_arn = each.value.lb_listener_load_balancer_arn
port = each.value.lb_listener_port
protocol = each.value.lb_listener_protocol
ssl_policy = each.value.lb_listener_ssl_policy
default_action {
type = each.value.lb_listener_default_action_type
order = each.value.lb_listener_default_action_order
dynamic "redirect" {
for_each = each.value.lb_listener_default_action_redirect != null ? [{}] : []
content {
status_code = each.value.lb_listener_default_action_redirect.lb_listener_default_action_redirect_status_code
}
}
}
}
resource "aws_lb_listener_rule" "lb_listener_rule" {
for_each = local.lb_listener_rule_map
listener_arn = each.value.lb_listener_rule_listener_arn
action {
type = each.value.lb_listener_rule_action_type
dynamic "fixed_response" {
for_each = each.value.lb_listener_rule_action_fixed_response != null ? [{}] : []
content {
content_type = each.value.lb_listener_rule_action_fixed_response.lb_listener_rule_action_fixed_response_content_type
message_body = each.value.lb_listener_rule_action_fixed_response.lb_listener_rule_action_fixed_response_message_body
}
}
}
condition {
dynamic "query_string" {
for_each = each.value.lb_listener_rule_condition_query_string != null ? [{}] : []
content {
key = each.value.lb_listener_rule_condition_query_string.lb_listener_rule_condition_query_string_key
value = each.value.lb_listener_rule_condition_query_string.lb_listener_rule_condition_query_string_value
}
}
}
}
resource "aws_lb" "lb" {
for_each = local.lb_map
name = lookup(each.value, "name", null)
enable_deletion_protection = lookup(each.value, "enable_deletion_protection", null)
internal = lookup(each.value, "internal", null)
ip_address_type = lookup(each.value, "ip_address_type", null)
load_balancer_type = lookup(each.value, "load_balancer_type", null)
}
Here is how the condition block looks when I run a terraform plan:
condition {
query_string {
value = "bar"
}
}
Here is how I would like the condition block to look:
condition {
query_string {
key = "health"
value = "check"
}
query_string {
value = "bar"
}
}
Your code, as complex as it is creates the two query_string correctly:
The code, in the form posted in the question has number of issues and it will not work. But then as you wrote that "My original files are very long. ", I guess that your real code actually works. Thus I only focused on its verification, rather then fixing all possible errors as I don't know what is your factual code.
I am trying to create routes in transit gateway route table. Below is the code block.
locals {
vpc_attachments_with_routes = chunklist(flatten([
for k, v in var.vpc_attachments : setproduct([{ key = k }], v["tgw_route"]) if length(lookup(v, "tgw_route", {})) > 0
]), 2)
}
resource "aws_ec2_transit_gateway_route_table" "route" {
count = var.create_tgw ? 1 : 0
transit_gateway_id = aws_ec2_transit_gateway.this[0].id
}
resource "aws_ec2_transit_gateway_route" "this" {
count = length(local.vpc_attachments_with_routes)
destination_cidr_block = local.vpc_attachments_with_routes[count.index][1]["destination_cidr_block"]
blackhole = lookup(local.vpc_attachments_with_routes[count.index][1], "blackhole", null)
transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.route[count.index].id
transit_gateway_attachment_id = tobool(lookup(local.vpc_attachments_with_routes[count.index][1], "blackhole", false)) == false ? aws_ec2_transit_gateway_vpc_attachment.this[local.vpc_attachments_with_routes[count.index][0]["key"]].id : null
depends_on = [
aws_ec2_transit_gateway_route_table.route,
]
}
Error:
Error: Invalid index\n\n on ../modules/tgw/main.tf line 85, in resource "aws_ec2_transit_gateway_route" "this":\n 85: transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.route[count.index].id\n |----------------\n | aws_ec2_transit_gateway_route_table.route is tuple with 1 element\n | count.index is 1\n\nThe given key does not identify an element in this collection value.\n\n",
You will have only 0 or 1 aws_ec2_transit_gateway_route_table.route, depending on the value of create_tgw. So it should be:
resource "aws_ec2_transit_gateway_route" "this" {
count = length(local.vpc_attachments_with_routes)
destination_cidr_block = local.vpc_attachments_with_routes[count.index][1]["destination_cidr_block"]
blackhole = lookup(local.vpc_attachments_with_routes[count.index][1], "blackhole", null)
transit_gateway_route_table_id = var.create_tgw ? aws_ec2_transit_gateway_route_table.route[0].id : null
transit_gateway_attachment_id = tobool(lookup(local.vpc_attachments_with_routes[count.index][1], "blackhole", false)) == false ? aws_ec2_transit_gateway_vpc_attachment.this[local.vpc_attachments_with_routes[count.index][0]["key"]].id : null
}