terraform create simple loop of variable that can be used later - amazon-web-services

I am new to terraform development, trying to create simple loop of variable that can be used later, some thing like below:
This perfectly worked for me and creates two subnet as expected.
variable "availability_zones" {
description = "Available Availability Zones"
type = "list"
default = [ "us-east-1a", "us-east-1b" ]
}
variable "public_subnet_cidr" {
description = "CIDR for Public Subnets"
type = "list"
default = [ "10.240.32.0/26", "10.240.32.64/26" ]
# Define Public Subnet
resource "aws_subnet" "public-subnets" {
count = 2
vpc_id = "${aws_vpc.default.id}"
cidr_block = "${element(var.public_subnet_cidr, count.index)}"
availability_zone = "${element(var.availability_zones, count.index)}"
tags {
Name = "${element(var.availability_zones, count.index)}_${element(var.public_subnet_cidr, count.index)}"
}
}
But while trying to associate those subnets to default route, I am not able to figure out how to fetch individual subnet id from those subnets created earlier. And ended up with below code. Is there a way to fetch subnet.id of individual subnets?
# Assign Default Public Route Table to Public Subnet
resource "aws_route_table_association" "default_public_route" {
subnet_id = "${aws_subnet.public-subnets.id}" <<-- This is the line I am trying to figure out
route_table_id = "${aws_route_table.default_public_route_table.id}"
}
Thanks in advance.
Sam

You're close on how to use it. Here's a walk through that can help you.
resource "aws_route_table_association" "default_public_route" {
count = 2
subnet_id = "${element(aws_subnet.public-subnets.*.id, count.index)}"
route_table_id = "${aws_route_table.default_public_route_table.id}"
}

Related

terraform iterate to associate aws resources

I try to create nat gateway on terraform, on each public subnets that I created.
I create the public subntes like that:
resource "aws_subnet" "public_subnet" {
count = length(var.vpc.public_subnets)
vpc_id = aws_vpc.vpc.id
availability_zone = var.vpc.public_subnets[count.index].availability_zone
cidr_block = var.vpc.public_subnets[count.index].cidr_block
tags = var.vpc.public_subnets[count.index].tags
}
I create all elastic ip like that:
resource "aws_eip" "eip" {
for_each = { for eip in var.vpc.eip : eip.name => eip }
vpc = true
tags = each.value.tags
}
And finally I have a resource block to create 3 nat gateways. Each nat gateway have to use a subnet and an eip:
resource "aws_nat_gateway" "ngw" {
count = length(var.vpc.public_subnets)
allocation_id = element(aws_eip.eip.*.allocation_id, count.index)
subnet_id = element(aws_subnet.public_subnet.*.id, count.index)
}
results ==> This object does not have an attribute named "allocation_id"
How should I iterate over 2 resources to create the nat gateay for each pair of subnet/eip ?
thanks.
Since you are using for_each for eip it will be a map, not a list. Thus to access its values you can use values:
allocation_id = element(values(aws_eip.eip)[*].allocation_id, count.index)

how to add multiple aws routes to multiple aws route tables

To avoid creating multiple aws_route resources. I am trying to add multiple destination_cidr_block from a map to multiple route tables. I think I am in the right direction, however, it is only adding a few cidr blocks from the list to each route table and not all cidr blocks to each route table.
variable "destination_cidr_block" {
type = map
default = {
destination_cidr_block = {
private = [
"0.0.0.0/0",
"0.0.0.0/0",
"0.0.0.0/0",
"0.0.0.0/0",]
private1 = [
"0.0.0.0/0",
"0.0.0.0/0"]
resource "aws_route" "private" {
count = local.length_of_rt_cnt
route_table_id = element(module.private.private_route_tables_id, count.index)
destination_cidr_block = element(var.destination_cidr_block["private1"], count.index)
gateway_id = module.vpc.id
}
How can I get all routes from private1 to the the multiple route tables that are already created from this module.
I apologize, everything has been destroyed and I did not saved the error. I know accomplishing multiple route tables with on destination cidr block which would look like this.
resource "aws_route" "private" {
count = local.length_of_rt_cnt
route_table_id = element(module.private.private_route_tables_id, count.index)
destination_cidr_block = 0.0.0.0/0
gateway_id = module.vpc.id
}
In this case 4 route tables will receive this cidr block. Now the question is how can I add a list of cidr block to a list of route tables

Terraform error creating subnet dependency

I'm trying to get a documentdb cluster up and running and have it running from within a private subnet I have created.
Running the config below without the depends_on i get the following error message as the subnet hasn't been created:
Error: error creating DocDB cluster: DBSubnetGroupNotFoundFault: DB subnet group 'subnet-0b97a3f5bf6db758f' does not exist.
status code: 404, request id: 59b75d23-50a4-42f9-99a3-367af58e6e16
Added the depends on setup to wait for the subnet to be created but are running into an issue.
cluster_identifier = "my-docdb-cluster"
engine = "docdb"
master_username = "myusername"
master_password = "mypassword"
backup_retention_period = 5
preferred_backup_window = "07:00-09:00"
skip_final_snapshot = true
apply_immediately = true
db_subnet_group_name = aws_subnet.eu-west-3a-private
depends_on = [aws_subnet.eu-west-3a-private]
}
On running terraform apply I an getting an error on the config:
Error: error creating DocDB cluster: DBSubnetGroupNotFoundFault: DB subnet group 'subnet-0b97a3f5bf6db758f' does not exist.
status code: 404, request id: 8b992d86-eb7f-427e-8f69-d05cc13d5b2d
on main.tf line 230, in resource "aws_docdb_cluster" "docdb":
230: resource "aws_docdb_cluster" "docdb"
A DB subnet group is a logical resource in itself that tells AWS where it may schedule a database instance in a VPC. It is not referring to the subnets directly which is what you're trying to do there.
To create a DB subnet group you should use the aws_db_subnet_group resource. You then refer to it by name directly when creating database instances or clusters.
A basic example would look like this:
resource "aws_vpc" "example" {
cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "eu-west-3a" {
vpc_id = aws_vpc.example.id
availability_zone = "a"
cidr_block = "10.0.1.0/24"
tags = {
AZ = "a"
}
}
resource "aws_subnet" "eu-west-3b" {
vpc_id = aws_vpc.example.id
availability_zone = "b"
cidr_block = "10.0.2.0/24"
tags = {
AZ = "b"
}
}
resource "aws_db_subnet_group" "example" {
name = "main"
subnet_ids = [
aws_subnet.eu-west-3a.id,
aws_subnet.eu-west-3b.id
]
tags = {
Name = "My DB subnet group"
}
}
resource "aws_db_instance" "example" {
allocated_storage = 20
storage_type = "gp2"
engine = "mysql"
engine_version = "5.7"
instance_class = "db.t2.micro"
name = "mydb"
username = "foo"
password = "foobarbaz"
parameter_group_name = "default.mysql5.7"
db_subnet_group_name = aws_db_subnet_group.example.name
}
The same thing applies to Elasticache subnet groups which use the aws_elasticache_subnet_group resource.
It's also worth noting that adding depends_on to a resource that already references the dependent resource via interpolation does nothing. The depends_on meta parameter is for resources that don't expose a parameter that would provide this dependency information directly only.
It seems value in parameter is wrong. db_subnet_group_name created somewhere else gives the output id/arn. So u need to use id value. although depends_on clause looks okie.
db_subnet_group_name = aws_db_subnet_group.eu-west-3a-private.id
So that would be correct/You can try to use arn in place of id.
Thanks,
Ashish

Terraform - Creating resources in one transaction / setting rollback policies

I'm using Terraform with AWS as a provider.
In one of my networks I accidentally configured wrong values which led to
failure in resources creation.
So the situation was that some parts of the resources were up and running,
but I would prefer that the all process was executed as one transaction.
I'm familiar with the output the Terraform gives in such cases:
Terraform does not automatically rollback in the face of errors.
Instead, your Terraform state file has been partially updated with any
resources that successfully completed. Please address the error above
and apply again to incrementally change your infrastructure.
My question is: Is there still a way to setup a rollback policy in cases that some resources where created and some failed?
Below is a simple example to reproduce the problem.
In the local variable 'az_list' just the change value from 'names' to 'zone_ids':
az_list = "${data.aws_availability_zones.available.zone_ids}"
And a VPC will be created with some default security groups and Route tables but without subnets.
resources.tf:
provider "aws" {
region = "${var.region}"
}
### Local data ###
data "aws_availability_zones" "available" {}
locals {
#In order to reproduce an error: Change 'names' to 'zone_ids'
az_list = "${data.aws_availability_zones.available.names}"
}
### Vpc ###
resource "aws_vpc" "base_vpc" {
cidr_block = "${var.cidr}"
instance_tenancy = "default"
enable_dns_hostnames = "false"
enable_dns_support = "true"
}
### Subnets ###
resource "aws_subnet" "private" {
vpc_id = "${aws_vpc.base_vpc.id}"
cidr_block = "${cidrsubnet( var.cidr, 8, count.index + 1 + length(local.az_list) )}"
availability_zone = "${element(local.az_list, count.index)}"
count = 2
}
resource "aws_subnet" "public" {
vpc_id = "${aws_vpc.base_vpc.id}"
cidr_block = "${cidrsubnet(var.cidr, 8, count.index + 1)}"
availability_zone = "${element(local.az_list, count.index)}"
count = 2
map_public_ip_on_launch = true
}
variables.tf:
variable "region" {
description = "Name of region"
default = "ap-south-1"
}
variable "cidr" {
description = "The CIDR block for the VPC"
default = "10.0.0.0/16"
}

How do I attach an elastic IP upon creation of a network load balancer with Terraform?

I'm attempting to associate my elastic IP address to a newly created network balancer using Terraform. I see no option in the aws_lb documentation for adding an elastic IP like one is able to do in the AWS console. The difficulty is that you have to associate the elastic IP upon creation of NLB.
EDIT: They now have made an explicit example on their documentation!
The aws_lb resource has a subnet_mapping block which allows you to specify an Elastic IP per subnet that the network load balancer exists in.
An absolutely minimal example looks something like this:
resource "aws_eip" "lb" {
vpc = true
}
resource "aws_lb" "network" {
name = "test-lb-tf"
load_balancer_type = "network"
subnet_mapping {
subnet_id = "${var.subnet_id}"
allocation_id = "${aws_eip.lb.id}"
}
}
Obviously you'll probably want to run the load balancer in multiple subnets so you'd probably use something like this:
variable "vpc" {}
data "aws_vpc" "selected" {
tags {
Name = "${var.vpc}"
}
}
data "aws_subnet_ids" "public" {
vpc_id = "${data.aws_vpc.selected.id}"
tags {
Tier = "public"
}
}
resource "aws_eip" "lb" {
count = "${length(data.aws_subnet_ids.public)}"
vpc = true
}
resource "aws_lb" "network" {
name = "test-lb-tf"
internal = false
load_balancer_type = "network"
subnet_mapping {
subnet_id = "${data.aws_subnet_ids.public.ids[0]}"
allocation_id = "${aws_eip.lb.id[0]}"
}
subnet_mapping {
subnet_id = "${data.aws_subnet_ids.public.ids[1]}"
allocation_id = "${aws_eip.lb.id[1]}"
}
subnet_mapping {
subnet_id = "${data.aws_subnet_ids.public.ids[2]}"
allocation_id = "${aws_eip.lb.id[2]}"
}
}
The above assumes you have tagged your VPC with a Name tag and your subnets with a Tier tag that in this case uses public as the value for any external facing subnets. It then creates an elastic IP address for each of the public subnets a network load balancer in each of the public subnets, attaching an elastic IP for each of them.
The above answer is correct, however it can now be simplified using dynamic blocks available in Terraform 0.12. This has the advantage of working in vpcs with more or less subnets.
resource "aws_lb" "network" {
name = "test-lb-tf"
internal = false
load_balancer_type = "network"
dynamic "subnet_mapping" {
for_each = data.aws.subnet_ids.public_ids
content {
subnet_id = subnet_mapping.value
allocation_id = aws_eip.lb.id[subnet_mapping.key].allocation_id
}
}
}
Here's my implementation base on the answer above:
resource "aws_eip" "nlb" {
for_each = toset(aws_subnet.public.*.id)
vpc = true
tags = {
"Name" = "my-app-nlb-eip"
}
}
resource "aws_lb" "nlb" {
name = "my-app-nlb"
internal = false
load_balancer_type = "network"
enable_deletion_protection = false
enable_cross_zone_load_balancing = true
dynamic "subnet_mapping" {
for_each = toset(aws_subnet.public.*.id)
content {
subnet_id = subnet_mapping.value
allocation_id = aws_eip.nlb[subnet_mapping.key].allocation_id
}
}
tags = {
Environment = "development"
}
}