Terraform - populate variable values from same script - terraform

I'm very green to terraform; infact this is part of my training.
I'm wondering; is there a way to get terraform to store a specific value (as variable) from the previous command within the same file.
Example:
resource "aws_vpc" "TestVPC"{
cidr_block = "192.168.0.0/16"
instance_tenancy = "default"
enable_dns_hostnames="True"
tags{
Name="TestVpc"
}
}
resource "aws_subnet" "TestSubnet"{
vpc_id = "${var.aws_vpc_id}" ##This is where I'd like to populate the aws_vpc_id from the VPC creation step above.
cidr_block = "192.168.0.0/24"
map_public_ip_on_launch="True"
availability_zone = "us-east-2a"
tags{
Name="TestSubnet"
}
}
Help is greatly appreciated.
Thanks.

You can use the output from the creation of the VPC, ${aws_vpc.TestVPC.id}
Like so:
resource "aws_vpc" "TestVPC" {
cidr_block = "192.168.0.0/16"
instance_tenancy = "default"
enable_dns_hostnames = "True"
tags {
Name = "TestVpc"
}
}
resource "aws_subnet" "TestSubnet" {
vpc_id = "${aws_vpc.TestVPC.id}"
cidr_block = "192.168.0.0/24"
map_public_ip_on_launch = "True"
availability_zone = "us-east-2a"
tags {
Name = "TestSubnet"
}
}

Related

Terraform how to access resource local name as a variable inside the resource block itself

resource "aws_subnet" "goodsubnet" {
vpc_id = "VPCID"
cidr_block = "x.x.x.x/x"
availability_zone = "xyz"
tags =
{
tagname1 = "$something"
}
}
I want the tag "tagname1" to dynamically have the value of the resource local name i.e "goodsubnet" Is there a variable I can use?
Thanks
That is not possible the way you want to do it. However, Terraform has a concept of variables, which you can use to assign values to arguments. That could be either a local value [1] or an input variable [2]. For example, an input variable definition:
variable "subnet_name_tag" {
type = string
description = "Tag name for a subnet."
}
Then, in your code you would do:
resource "aws_subnet" "goodsubnet" {
vpc_id = "VPCID"
cidr_block = "x.x.x.x/x"
availability_zone = "xyz"
tags =
{
tagname1 = var.subnet_name_tag
}
}
Alternatively, you could define a local value:
locals {
subnet_tag_name = "goodsubnet"
}
Followed by:
resource "aws_subnet" "goodsubnet" {
vpc_id = "VPCID"
cidr_block = "x.x.x.x/x"
availability_zone = "xyz"
tags =
{
tagname1 = local.subnet_name_tag
}
}
[1] https://developer.hashicorp.com/terraform/language/values/locals
[2] https://developer.hashicorp.com/terraform/language/values/variables

terraform do not get variable passed through modules

I want to provision a simple terraform vpc and subnet.
I created a module and declared all the variable required in the module variable.tf
and passed wrote all the needed values through terraform.tfvars
but when I run terraform apply I get a prompt to insert a value for avail_zone
main.tf
module "myapp-subnet" {
source = "./modules/subnet"
subnet_cidr_block = var.subnet_cidr_block
avail_zone = "us-east-1a"
env_prefix = "dev"
vpc_id = aws_vpc.myapp-vpc.id
default_route_table_id = aws_vpc.myapp-vpc.default_route_table_id
}
modules/subnet/main.tf
resource "aws_subnet" "myapp-subnet-1" {
vpc_id = var.vpc_id
cidr_block = var.subnet_cidr_block
availability_zone = var.avail_zone
tags = {
Name: "${var.env_prefix}-subnet-1"
}
}
resource "aws_internet_gateway" "myapp-igw"{
vpc_id = var.vpc_id
tags = {
Name: "${var.env_prefix}-igw"
}
}
# using existing route table
resource "aws_default_route_table" "default-rtb" {
default_route_table_id = var.default_route_table_id
route{
cidr_block = "0.0.0.0/10"
gateway_id = aws_internet_gateway.myapp-igw.id
}
tags = {
Name: "${var.env_prefix}-main-rtb"
}
}
module/subnet/variables.tf
variable vpc_cidr_blocks {}
variable subnet_cidr_block {}
variable avail_zone {}
variable env_prefix{}
variable vpc_id {}
variable default_route_table_id {}
terraform.tfvars
vpc_cidr_blocks = "xxxx"
subnet_cidr_block = "xxxx"
avail_zone = "xxxxxx"
env_prefix = "dev"
myip = "xxxxxxx"
instance_type="t2.micro"
public_key_location= "/home/xxxxxxxx/.ssh/id_rsa.pub"

terraform for_each index for NAT GW with multiple subnets

I need to setup multiple private subnets at AWS per account and I need to have only one NAT GW per Account and traffic routed to this. The problem I guess is that the values are a map without an index. As I remember with count you have an index which can be simply accessed subnet_id = aws_subnet.private[0].id But I can't change the current setup. I need to create an idnex out of this map.
I have a yaml file with this values:
aws:
- accounts: ciss-goesaws-test
private_subnets:
-
az: eu-central-1a
short: a
cidr: 10.44.4.96/27
-
az: eu-central-1b
short: b
cidr: 10.44.5.128/27
-
az: eu-central-1c
short: c
cidr: 10.44.6.160/27
I have the following terraform code. But this creates one NAT GW per subnet. I need to NAT GW to be created in only one of the subnets.
locals {
private = flatten([
for a in var.aws : [
for ps in a.private_subnets : {
accounts = a.accounts
az = ps.az
cidr = ps.cidr
short = ps.short
}
]
])
}
resource "aws_eip" "this" {
vpc = true
}
resource "aws_nat_gateway" "this" {
for_each = {
for cidr_block in local.private : cidr_block.cidr => cidr_block
}
allocation_id = aws_eip.this.id
subnet_id = aws_subnet.private[each.key].id
}
resource "aws_subnet" "private" {
for_each = {
for cidr_block in local.private : cidr_block.cidr => cidr_block
}
availability_zone = each.value.az
cidr_block = each.value.cidr
vpc_id = aws_vpc.this.id
tags = {
Name = "${each.value.accounts}-private-${each.value.short}"
}
}
resource "aws_route_table" "private" {
for_each = {
for cidr_block in local.private : cidr_block.cidr => cidr_block
}
vpc_id = aws_vpc.this.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.this[each.key].id
}
}
resource "aws_route_table_association" "private" {
for_each = {
for cidr_block in local.private : cidr_block.cidr => cidr_block
}
subnet_id = aws_subnet.private[each.key].id
route_table_id = aws_route_table.private[each.key].id
}
As the CIDRs are used as index for the subnet resource collection, you have to select one (maybe first) for the NAT GW.
For example:
locals {
nat_gw_subnet_cidr = var.aws[0].private_subnets[0].cidr
}
resource "aws_nat_gateway" "this" {
allocation_id = aws_eip.this.id
subnet_id = aws_subnet.private[local.nat_gw_subnet_cidr].id
}
# `aws_subnet` resource as in the question
# One route table should be enough, as all subnets share the same GW
resource "aws_route_table" "private" {
vpc_id = aws_vpc.this.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.this.id
}
# No route for the VPC?
}
resource "aws_route_table_association" "private" {
# Simpler to use the subnets as index
for_each = aws_subnet.private
subnet_id = each.value.id
route_table_id = aws_route_table.private.id
}

How to convert the created subnets into map data for later code use?

First I create 3 subnets, I want to convert them into map form for the lookup of the code behind.
such as:
[0 = "${aws_subnet.subnet-1.id}"
1 = "${aws_subnet.subnet-2.id}"
2 = "${aws_subnet.subnet -3.id}"]
resource "aws_vpc" "module_vpc" {
cidr_block = "10.0.0.0/16"
instance_tenancy = "default"
}
resource "aws_subnet" "subnet-1" {
vpc_id = "${aws_vpc.module_vpc.id}"
cidr_block = "10.0.1.0/24"
}
resource "aws_subnet" "subnet-2" {
vpc_id = "${aws_vpc.module_vpc.id}"
cidr_block = "10.0.2.0/24"
}
resource "aws_subnet" "subnet-3" {
vpc_id = "${aws_vpc.module_vpc.id}"
cidr_block = "10.0.3.0/24"
}
The following is the code using lookup
resource "aws_instance" "server" {
count = 3
subnet_id = "${lookup(var.subnets, count.index % 3)}"
}
What should I do? thank you all
What about an alternative way, which is more extensible based on list:
variable "subnet_cidrs" {
default = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
}
resource "aws_vpc" "module_vpc" {
cidr_block = "10.0.0.0/16"
instance_tenancy = "default"
}
resource "aws_subnet" "subnet" {
count = length(var.subnet_cidrs)
vpc_id = aws_vpc.module_vpc.id
cidr_block = element(var.subnet_cidrs, count.index)
}
resource "aws_instance" "server" {
count = 3
subnet_id = element(aws_subnet.subnet.*.id, count.index) # modulo is automatically applied
}
I assume terraform 0.12, as you haven't specified any other version.

Value of 'count' cannot be computed in Terraform

Here I'm trying to create one subnet per availability zone and then associate the route table with each of them.
locals {
aws_region = "${var.aws_regions[var.profile]}"
base_name = "${var.product}-${local.aws_region}"
aws_avzones = {
pro = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]
dev = ["eu-west-2a", "eu-west-2b", "eu-west-2c"]
}
}
# ---
# Create VPC
resource "aws_vpc" "default" {
cidr_block = "${var.vpc_cidr_block}"
tags = {
Name = "${local.base_name}-vpc"
}
}
# ---
# Create public subnets - each in a different AZ
resource "aws_subnet" "public" {
count = "${length(local.aws_avzones[var.profile])}"
vpc_id = "${aws_vpc.default.id}"
cidr_block = "${cidrsubnet(var.vpc_cidr_block, 8, count.index)}"
availability_zone = "${element(local.aws_avzones[var.profile], count.index)}"
map_public_ip_on_launch = 1
tags = {
"Name" = "Public subnet - ${element(local.aws_avzones[var.profile], count.index)}"
}
}
# ---
# Create Internet gateway for inbound-outbound connections
resource "aws_internet_gateway" "default" {
vpc_id = "${aws_vpc.default.id}"
tags = {
"Name" = "${local.base_name}-igw"
}
}
# ---
# Create Internet gateway routes table
resource "aws_route_table" "pub" {
vpc_id = "${aws_vpc.default.id}"
route {
cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.default.id}"
}
tags = {
Name = "${local.base_name}-rtb-igw"
}
}
# ---
# Associate public subnets with the public route table
resource "aws_route_table_association" "pub" {
count = "${length(aws_subnet.public.*.id)}"
subnet_id = "${element(aws_subnet.public.*.id, count.index)}"
route_table_id = "${aws_route_table.pub.id}"
}
Unfortunately terraform plan renders an error:
aws_route_table_association.pub: aws_route_table_association.pub: value of 'count' cannot be computed
Why it cannot be computed? Terraform did not complain about that when the infra. was all up and running, I discovered this error only after the destruction when attempting to to recreate the infra.
Currently my workaround is to comment out all the aws_route_table_association blocks, then terraform apply, uncomment and then finish the job. Obviously this is very far from ideal.
BTW, I also tried the explicit dependency declaration like so:
resource "aws_route_table_association" "pub" {
count = "${length(aws_subnet.public.*.id)}"
subnet_id = "${element(aws_subnet.public.*.id, count.index)}"
route_table_id = "${aws_route_table.pub.id}"
depends_on = ["aws_subnet.public"]
}
But it didn't help.
$ terraform --version
Terraform v0.11.11
+ provider.aws v1.52.0

Resources