target_groups optional param is not optional : terraform - amazon-web-services

Error: Invalid index
on .terraform/modules/database-security-group/main.tf line 70, in resource "aws_security_group_rule" "ingress_rules":
70: to_port = var.rules[var.ingress_rules[count.index]][1]
|----------------
| count.index is 0
| var.ingress_rules is list of string with 1 element
| var.rules is map of list of string with 119 elements
The given key does not identify an element in this collection value
.
It's all Greek to me. We could use the help..
module "database-security-group" {
source = "terraform-aws-modules/security-group/aws"
name = "database-security"
description = "Security group for Database on database subnet."
vpc_id = module.vpc.vpc_id
ingress_cidr_blocks = ["0.0.0.0/0"]
ingress_rules = [ "http-3306-tcp"]
egress_rules = ["all-all"]
tags = {
Name = "Database"
Environment = "spoon"
}
}

I believe the intention of this particular module is that you select from its table of predefined rules when specifying ingress_rules and egress_rules.
At the time I write this I don't see a definition for a rule "http-3306-tcp", and so I think that's the cause of your error. If your intent was to allow TCP port 3306 for MySQL then it seems the rule key for that is "mysql-tcp".

Related

Is it possible to attach a security group to a load balancer ONLY IF the security group count is set to 1?

I'm working on a terraform module which creates a load balancer amongst other resources. I would like to attach a security group to the load balancer, but only if a a certain variable is equal to "all" OR "gw".
I currently have a count argument in place for the security group itself:
resource "aws_security_group" "akamai_sg" {
count = var.domain_name_suffix == "all" || var.domain_name_suffix == "gw" ? 1 : 0
name = "akamai-pl-sg"
description = "Manage access from Akamai to ${var.environment} alb"
vpc_id = var.vpc_id
tags = merge(var.common_tags, tomap({ "Name" = "akamai-pl-sg" }))
revoke_rules_on_delete = true
}
This means that the security group will only be created if the domain_name_suffix is set to "all" or "gw".
I would like this same functionality for attaching this security group to the ALB. However, I still want to create the ALB regardless of this variable, it is just the security group attachment which I want to be dependent.
Currently I have this configuration for my ALB:
resource "aws_lb" "internal" {
name = "${var.environment}-${var.domain_name_suffix}"
internal = true
load_balancer_type = "application"
security_groups = [aws_security_group.alb_sg.id, aws_security_group.akamai_sg.id]
subnets = var.alb_subnets
idle_timeout = var.alb_idle_timeout
tags = var.common_tags
drop_invalid_header_fields = true
dynamic "access_logs" {
for_each = var.alb_access_logging_enabled == true ? [var.alb_access_logging_enabled] : []
content {
bucket = var.alb_access_logging_bucket
enabled = true
prefix = "internal"
}
}
}
However, this results in an error:
"Error: Missing resource instance key
on .terraform/modules/comm_common/alb.tf line 5, in resource "aws_lb" "internal":
5: security_groups = [aws_security_group.alb_sg.id, aws_security_group.akamai_sg.id]
Because aws_security_group.alb_sg has "count" set, its attributes must be
accessed on specific instances.
For example, to correlate with indices of a referring resource, use:
aws_security_group.alb_sg[count.index]"
When I add in this [count.index] to the ALB security group reference, I still get the same error.
Could you try the same condition in security_groups field as well? but the value would be a list with two items if condition is true and with one item if condition is false?
resource "aws_lb" "internal" {
name = "${var.environment}-${var.domain_name_suffix}"
internal = true
load_balancer_type = "application"
security_groups = var.domain_name_suffix == "all" || var.domain_name_suffix == "gw" ? [aws_security_group.alb_sg.id, aws_security_group.akamai_sg.id] : [aws_security_group.alb_sg.id]
subnets = var.alb_subnets
idle_timeout = var.alb_idle_timeout
...
}

Terraform - Objects in wrong order

I want to get host IPs out of two subnets in AWS using Terraform/Terragrunt and the cidrhost function.
For some reason, the subnets are being injected into cidrhost in the wrong order.
The remote state it's checking looks like this:
"firewall_subnets_cidr_blocks": {["10.0.0.96/28", "10.0.0.112/28"]}
The first subnet is in 1a and the second in 1b
And this is my code:
resource "aws_network_interface" "private" {
count = length(var.firewall_subnets) > 0 ? length(var.firewall_subnets) : 0
subnet_id = element(var.firewall_subnets, count.index)
private_ip = cidrhost(tolist(var.firewall_subnets_cidr_blocks)[count.index], 10)
description = "Private Interface"
security_groups = [aws_security_group.sg_firewall_private.id]
source_dest_check = false
}
Whenever I run it, it puts the first subnet into the second - the 1b - interface and vice versa.
# aws_network_interface.private[0] will be created
+ resource "aws_network_interface" "private" {
+ description = "Private Interface"
+ private_ip = "10.0.0.122"
# aws_network_interface.private[1] will be created
+ resource "aws_network_interface" "private" {
+ description = "Private Interface"
+ private_ip = "10.0.0.106"
It only happens on this interface script; management and public are identical and behaving as expected.
TF 1.1.9
TG 0.37.3
Digging into the documentation I found this statement:
For a new network interface, the same primary IP address is consistently selected from a given set of addresses, regardless of the order provided.
This sorting happens before the list is passed to my functions resulting in the wrong IPs going to the wrong subnet/AZ.
To override this behaviour I've set private_ip_list_enabled to true.
Here's my working code:
resource "aws_network_interface" "private" {
count = length(var.private_subnets) > 0 ? length(var.private_subnets) : 0
subnet_id = element(var.private_subnets, count.index)
private_ip_list_enabled = true
private_ip_list = tolist([cidrhost(element(var.private_subnets_cidr_blocks, count.index), 10)])
description = "Private Interface"
source_dest_check = false
}
Victory is mine!

Error: Incorrect attribute value type module.network.private_subnets[0] is tuple with 3 elements

I am running into an issue while trying to upgrade from terraform 11 to terraform 12. I was previously using the following syntax to retrieve the 3rd element from a list of ids from a module. The module output is like so:
# Subnets
output "private_subnets" {
description = "List of IDs of private subnets"
value = ["${aws_subnet.private.*.id}"]
}
Previously, this worked with terraform 11
subnet_id = "${element(module.network.private_subnets,3)}"
I thought that I could use the index of 2 to get the same results, but I get the following error:
Error: Incorrect attribute value type
on terraformfile.tf line 65, in resource "aws_instance" "myinstance":
65: subnet_id = module.network.private_subnets[2]
|----------------
| module.network.private_subnets[2] is tuple with 3 elements
Any help with this would be greatly appreciated.
The currently used value:
value = ["${aws_subnet.private.*.id}"]
produces a list of lists. For example,
[
[
"subnet-0f5b759e80ffcf305",
"subnet-0500c8c2a40e5b381",
],
]
If you want to keep using this in that form, later, when you use element you have to do following:
subnet_id = element(module.network.private_subnets[0], 3)
Alternatively, redefine private_subnets to be:
value = aws_subnet.private.*.id
The answer from #Marcin holds good but I hit a very similar error in a slightly different situation and I took a different approach to resolve it.
Lets say you have a aws_security_group_rule rule and each of the data -sourced item in the list below i.e data.aws_subnet.app1_subnets.*.cidr_block and data.aws_subnet.app2_subnets.*.cidr_block are a tuple with lets say X elements - You might hit an error -
Error: Incorrect attribute value type
on stack.tf line 44, in resource "aws_security_group_rule" "ds_security_group_ingress_rule":
44: cidr_blocks = [
45: data.aws_subnet.app1_subnets.*.cidr_block,
46: data.aws_subnet.app2_subnets.*.cidr_block
47: ]
|----------------
| data.aws_subnet.app1_subnets.*.cidr_block is tuple with 3 elements
| data.aws_subnet.app2_subnets.*.cidr_block is tuple with 3 elements
Inappropriate value for attribute "cidr_blocks": element 0: string required.
I had to use the flatten function to resolve the issue which quite literally flattens the content..
Change the below:
cidr_blocks = [
data.aws_subnet.app1_subnets.*.cidr_block,
data.aws_subnet.app2_subnets.*.cidr_block
]
as
cidr_blocks = flatten([
data.aws_subnet.app1_subnets.*.cidr_block,
data.aws_subnet.app2_subnets.*.cidr_block
])
I had same issue , flatten HELPED !! .
the configuration file looked like this :
resource "aws_db_subnet_group" "for-database" {
name = "for-database"
description = "private subnets in different AZs"
subnet_ids = module.vpc.private_subnets
}
got this error :
│ Error: Unsupported attribute
│
│ 39: value = module.vpc.private_subnets.subnet_count
│ ├────────────────
│ │ module.vpc.private_subnets is tuple with 2 elements
│
│ This value does not have any attributes.
solution:
resource "aws_db_subnet_group" "for-database" {
name = "for-database"
description = "private subnets in different AZs"
subnet_ids = flatten([module.vpc.private_subnets])
}
it was quite difficult for me but i handled it via flatten function i took tuple and pass it to flatten function
/* Subnets group for DBs */
resource "aws_db_subnet_group" "default" {
name = "main"
subnet_ids = flatten([aws_subnet.PrivateSubnets.*.id])
tags = {
Name = "Private Subnet group"
}
}

Error: Invalid version constraint when using a module

I'm creating few resources like Alb, security group and I use modules from github :
module "efs_sg" {
source = "git::https://github.com/terraform-aws-modules/terraform-aws-security-group.git"
version = "3.2.0"
name = "${var.default_tags["app"]}-efs"
description = "Security group for FE "
vpc_id = data.terraform_remote_state.network.outputs.vpc_id
computed_ingress_with_source_security_group_id = [
{
from_port = 2049
to_port = 2049
protocol = "tcp"
description = "NFS"
source_security_group_id = "${module.asg_sg.this_security_group_id}"
}
]
number_of_computed_ingress_with_source_security_group_id = 1
tags = "${var.default_tags}"
}
when I do terraform apply/plan I get this error:
Error: Invalid version constraint
Cannot apply a version constraint to module "efs_sg" (at
terraform/dev/eu-west-1/sg.tf:107) because it has a non Registry
URL.
I'm using Terraform v0.12.12
How to fix this?
You can't use the version constraint with a git hosted module. The version constraint requires a Terraform Registry hosted module.
You can add a tag to the repo and use the ?ref query.
eg:
"git::https://github.com/terraform-aws-modules/terraform-aws-security-group.git?ref=3.2.0"
https://www.terraform.io/docs/modules/sources.html#selecting-a-revision

Terraform decouple Security Group dependency

This is tested with Terraform v0.12.9
I generally manage security groups and security group rules as separate resources, as in the below example:
resource "aws_security_group" "this" {
count = var.create ? 1 : 0
name_prefix = "${var.security_group_name}_"
vpc_id = var.vpc_id
lifecycle {
create_before_destroy = true
}
}
resource "aws_security_group_rule" "ingress_rules" {
count = var.create ? length(var.inbound_security_group_ids) : 0
security_group_id = aws_security_group.this[0].id
type = "ingress"
from_port = var.from_port
to_port = var.to_port
protocol = "tcp"
source_security_group_id = var.inbound_security_group_ids[count.index]
}
The implementation for this would look something like the below:
module "test_module" {
source = "../path/to/module/"
create = true
vpc_id = "vpc-xxxxxx"
security_group_name = "${var.service_name}-db"
from_port = 1234
to_port = 1234
inbound_security_group_ids = [
module.service.security_group_id_one,
module.service.security_group_id_two
]
}
Problem
I want this to work if the outputs from the module.service aren't created. In that scenario my expectation is that length(var.inbound_security_group_ids) should evaluate to 0 resulting in the security group rules not being created
What actually happens is that length(var.inbound_security_group_ids) evaluates to 2 when module.service isn't created. This is presumably because it is an array of two blank strings ["", ""]
According to the Terraform documentation I can handle this with the compact function, which removes empty strings from an array.
resource "aws_security_group_rule" "ingress_rules" {
count = var.create ? length(compact(var.inbound_security_group_ids)) : 0
security_group_id = aws_security_group.this[0].id
type = "ingress"
from_port = var.from_port
to_port = var.to_port
protocol = "tcp"
source_security_group_id = var.inbound_security_group_ids[count.index]
}
The problem with this, however, is that Terraform is unable to determine the plan because it doesn't know what var.inbound_security_group_ids evaluates to until apply-time. This is the error message (for context):
The "count" value depends on resource attributes that cannot be determined
until apply, so Terraform cannot predict how many instances will be created.
To work around this, use the -target argument to first apply only the
resources that the count depends on.
Question
Is it possible to decouple a security group like this so that it will still be created even when the source_security_group_id attribute has no values?
It's often easier to work with lists or sets that might be empty in Terraform than to work with individual values that might not be set, for a reason related to what you've observed: it separates whether the value is set from what the value actually is, so that the presence of the value can be known even if the value itself isn't known.
In this case, you might approach that by changing how you return the security group ids from your service module, so that each of those outputs is a list of security group ids rather than a single string that might be either a valid security group id or an empty string:
module "test_module" {
source = "../path/to/module/"
create = true
vpc_id = "vpc-xxxxxx"
security_group_name = "${var.service_name}-db"
from_port = 1234
to_port = 1234
inbound_security_group_ids = concat(
module.service.security_group_ids_one,
module.service.security_group_ids_two,
)
}
If either security_group_ids_one or security_group_ids_two is known to be an empty list then concat will ignore it entirely. If they are enabled then you can arrange for them to be a known list with one element whose value is unknown, and thus length(var.inbound_security_group_ids) will still have a known value in all cases.