Hello! I am new to terraform and can’t seem to figure out how to use local variables within a resource.
In my mind, the variable availability_zone
defined within the scope of the resource aws_subnet
should be available for interpolation. Yet that isn’t proving to be the case thus far.
Can someone please point out what I am missing?
resource "aws_subnet" "public_subnets" {
vpc_id = aws_vpc.environment.id
count = length(var.public_subnets)
cidr_block = var.public_subnets[count.index]
availability_zone = element(var.availability_zones, count.index)
map_public_ip_on_launch = var.map_public_ip_on_launch
tags = {
Name = "${availability_zone}"
//Name = "${var.environment}-PublicSubnet-${availability_zone}"
}
}
Figured it out syntax should be Zone = "${var.availability_zones[count.index]}"
I still don’t grasp some of the logic behind the HCL though, guess I need more time/familarity
Hi @msuzoagu! Glad to hear that you figured out a solution. I thought it might be helpful to explain a bit more about what’s happening here.
availability_zone
is an attribute of the resource, not a variable. Terraform does not have the same concept of “scope” that you might expect from other languages. You can only refer to attributes of resources by specifying their full addresses, like aws_subnet.public_subnets.availability_zone
.
It’s also unusual to try to refer to a resource’s attributes from within itself. I believe that the only cases where this is explicitly supported are in the provisioner
and connection
blocks, which need access to attributes such as public_ip
to be useful.
So the solution you arrived at makes sense to me: accessing the input variable var.availability_zones[count.index]
directly.
One other note: it looks like you’re using the legacy interpolation syntax. As of Terraform 0.12 and later, there’s no need to write "${var.availability_zones[count.index]}"
, and instead you can write var.availability_zones[count.index]
.
I hope this makes sense! Let me know if anything is unclear.
1 Like
hi @alisdair, thanks for taking the time to explain; much appreciated!
Some follow-up questions:
-
I am using the old interpolation style because the final string output is a concatenation of a variable and a string.
tags = {
Name = "${var.environment}-PrivateSubnet-${var.availability_zones[count.index]}"
- Is there another way to do this?
-
I would like to return the last 2 elements (subnet ids) in here:
resource "aws_db_subnet_group" "db_subnets" {
count = length(aws_subnet.public_subnets)
subnet_ids = slice((list(aws_subnet.private_subnets[count.index].id)), 0, 1)
tags = {
Name = "${var.environment}"
}
}
but returns all 3 elements. Could you please explain why that is happening?
This is my workaround:
resource "aws_db_subnet_group" "db_subnets" {
count = length(aws_subnet.public_subnets) - 1
subnet_ids = list(aws_subnet.private_subnets[count.index].id)
tags = {
Name = "${var.environment}"
}
}
Thank you!
This is the idiomatic way to do this, looks good! I was referring to the use of redundant interpolation, i.e. just a single variable in a string: "${var.something}"
instead of just var.something
.
I’m not quite sure what you’re trying to do here. Some thoughts:
-
list(aws_subnet.private_subnets[count.index].id)
is constructing a single-element list using the ID of the corresponding private_subnets
resource
-
slice(x, 0, 1)
returns a single-element list with the first element of x
, but since your list already has one element this does nothing
-
count = length(aws_subnet.public_subnets) - 1
means you will create one less db_subnet_group
than you have public_subnets
, but since you then use aws_subnet.private_subnets[count.index]
, it will work against the start of the list
Without knowing more about why you have different numbers of subnets, I can’t be sure what the right approach is here. But with the information I have, it sounds like this might be a case where using for_each
would make more sense than count
. More on this in the Terraform documentation here.
If you can provide more information about what you’re trying to achieve, maybe I (or someone else) can suggest how to proceed.
Hello @alisdair! thanks for the feedback.
2 Points:
I am attempting to get this to work in a different way now:
My aim:
- I have a list of subnets for dbs only:
variable "db_subnets" {
type = list(string)
default = ["10.0.0.0/21", "10.0.64.0/21", "10.0.128.0/21"]
}
- I create the subnets that I want to be used for dbs:
resource "aws_subnet" "database" {
vpc_id = aws_vpc.environment.id
count = length(var.db_subnets)
cidr_block = var.db_subnets[count.index]
availability_zone = element(var.azs, count.index)
map_public_ip_on_launch = false
tags = {
Name = "${var.environment}-DBSubnet-${var.azs[count.index]}"
}
}
- I then use those subnets to populate the subnet_group resource:
resource "aws_db_subnet_group" "database-subnet-group" {
count = length(var.db_subnets)
name = "main"
description = "Database Subnet Group for ${var.azs[count.index]}"
subnet_ids = aws_subnet.database.*.id
}
Yet, running this results in the error:
Error: Error creating DB Subnet Group: DBSubnetGroupAlreadyExists: The DB subnet group 'db-subnet-group' already exists.
status code: 400, request id: f2afd41e-9bf5-4846-be48-ac2bddfdb829
on main.tf line 81, in resource "aws_db_subnet_group" "database-subnet-group":
81: resource "aws_db_subnet_group" "database-subnet-group" {
Error: Error creating DB Subnet Group: DBSubnetGroupAlreadyExists: The DB subnet group 'db-subnet-group' already exists.
status code: 400, request id: e33fbdbf-689c-4725-bad1-6d87dc3410ba
on main.tf line 81, in resource "aws_db_subnet_group" "database-subnet-group":
81: resource "aws_db_subnet_group" "database-subnet-group" {
I don’t understand what is going on.
- What is the difference between
aws_subnet
and aws_db_subnet_group
?
My assumption is that the former is for creating subnets while the latter is for populating/generating a list of (already created) subnet ids that should be used when creating dbs (not to create new subnets).
Ha!
It was name
argument that was the issue. Since I gave a static name, terraform would attempt to name each db_subnet_group the same static name and would throw an error because that name had already been used thus a db_subnet_group with that name was already existing.
Solution was to make name
argument dynamic so:
name = "db-subnet-group-${var.azs[count.index]}"