Terraform not destroying resource after condition is false - amazon-web-services

I am creating vpc endpoints using terraform. My condition is if we provide subnet IDs in .tfvars file, it would create the endpoints or else it won't.
The code is working when endpoints are being created.
Issue:
Once the endpoint is created, if I remove the subnet Ids from .tfvars file it only removes the subnets from endpoints.
Expectation:
It should destroy the endpoint.
Below is my code:
resource.tf
resource "aws_vpc_endpoint" "CloudFormation" {
count = var.cf_subnet_ids != [] ? 1 : 0
vpc_id = var.vpc_id
service_name = data.aws_vpc_endpoint_service.cloudformation.service_name
vpc_endpoint_type = "Interface"
security_group_ids = var.security_group_ids
subnet_ids = var.cf_subnet_ids
private_dns_enabled = true
}
resource "aws_vpc_endpoint" "Monitoring" {
count = var.mntr_subnet_ids != [] ? 1 : 0
vpc_id = var.vpc_id
service_name = data.aws_vpc_endpoint_service.monitoring.service_name
vpc_endpoint_type = "Interface"
security_group_ids = var.security_group_ids
subnet_ids = var.mntr_subnet_ids
private_dns_enabled = true
}
module.tf
module "VPC1" {
source = "./endpoints"
count = var.vpc_endpoints[0].vpc_id != "" ? 1 : 0
vpc_id = var.vpc_endpoints[0].vpc_id
cf_subnet_ids = var.vpc_endpoints[0].endpointCloudFormationPerVPC
mntr_subnet_ids = var.vpc_endpoints[0].endpointMonitoringPerVPC
}
module "VPC2" {
source = "./endpoints"
count = var.vpc_endpoints[1].vpc_id != "" ? 1 : 0
vpc_id = var.vpc_endpoints[1].vpc_id
cf_subnet_ids = var.vpc_endpoints[1].endpointCloudFormationPerVPC
mntr_subnet_ids = var.vpc_endpoints[1].endpointMonitoringPerVPC
}
env.tfvars
vpc_endpoints = [
{
vpc_id = "vpc-0a7c8cb62ae12ecb0"
vpc_cidr = ["10.150.2.0/23"]
endpointCloudFormationPerVPC = ["subnet-0367288ea9b4a0656", "subnet-0779a62471a3ee5b6"]
endpointMonitoringPerVPC = []
},
{
vpc_id = "vpc-0b085d19c3c35617f"
vpc_cidr = ["10.150.0.0/23"]
endpointCloudFormationPerVPC = ["subnet-0367288ea9b4a0656", "subnet-0779a62471a3ee5b6"]
endpointMonitoringPerVPC = ["subnet-0fd8da6ec6672c759"]
}
]
Please help.
It creates 3 Endpoints. 2 CF and 1 monitoring with above script. If I modify the .tfvars to below, it should delete the monitoring endpoint. Instead it removes the subnet from endpoint as in it modifies the endpoint instead of destroying it.
vpc_endpoints = [
{
vpc_id = "vpc-0a7c8cb62ae12ecb0"
vpc_cidr = ["10.150.2.0/23"]
endpointCloudFormationPerVPC = ["subnet-0367288ea9b4a0656"]
endpointMonitoringPerVPC = []
},
{
vpc_id = "vpc-0b085d19c3c35617f"
vpc_cidr = ["10.150.0.0/23"]
endpointCloudFormationPerVPC = ["subnet-0367288ea9b4a0656"]
endpointMonitoringPerVPC = []
}

Currently in your declared module arguments, you are assigning the following value for cf_subnet_ids:
var.vpc_endpoints[0].endpointCloudFormationPerVPC
Given your vpc_endpoints variable as posted in the question, this argument value will be null after the list(object) is coalesced. Therefore, when we resolve this value in your config for the resource, it will be:
resource "aws_vpc_endpoint" "CloudFormation" {
count = null != [] ? 1 : 0
...
}
Since null does not equal the empty list constructor [], this will resolve to the first returned value in the ternary 1. This means the resource will still be managed with a count of 1, and your resource will not be deleted.
The easiest and best practices way to begin fixing your config and resolve this unintended behavior would be to convert the <= 0.11 meta-argument count into the >= 0.12 meta-argument for_each: documentation.

Related

In Terraform, how can I create an iterative list out of two aws_subnet objects?

New to Terraform. I have two aws_subnet objects which I want to associate with route tables. As I understand it, each AZ will need it's own route table. The easiest thing to do would be just declare two route tables, one for each subnet but would like to know if there is a better way to do it instead of just settling for things thrown together.
I have declared my subnets as a list in variables.tf:
variable "my_public_subnets" {
type = list
description = "public subnet within vpc cidr block"
default = ["10.1.2.0/24", "10.1.1.0/24"]
}
And have two public subnets in main.tf
resource "aws_subnet" "pub_1" {
vpc_id = aws_vpc.vpc.id
cidr_block = var.my_public_subnets[0]
availability_zone = "us-east-1a"
}
resource "aws_subnet" "pub_2" {
vpc_id = aws_vpc.vpc.id
cidr_block = var.my_public_subnets[1]
availability_zone = "us-east-1b"
}
Instead of:
resource "aws_route_table_association" "pub_ra_1" {
subnet_id = aws_subnet.pub_1.id
route_table_id = aws_route_table.bar.id
}
resource "aws_route_table_association" "pub2_ra_2" {
subnet_id = aws_subnet.pub_2.id
route_table_id = aws_route_table.bar.id
}
Is there way to do something like this? Create a list/array/map of those two subnets so I don't have to declare a aws_route_table_association for both of them? Maybe there's a better way to set this up in general?
locals {
my_pub_subnets = [aws_subnet.pub_1, aws_subnet.pub_2]
}
resource "aws_route_table_association" "pub_rt_a" {
for_each = locals.my_pub_subnets
subnet_id = each.value
route_table_id = aws_route_table.some_public_route_table.id
depends_on = [aws_subnet.pub_1]
}
Modules are how you create repeatable procedures in TF.
Something like:
locals{
subnets = {
public = "10.1.2.0/24",
private = "10.1.1.0/24"
}
module "subnets" {
source = "./modules/subnets"
for_each = subnets
name = each.key
cidr = each.value
}
for the AZ names, you could also use data.aws_availability_zones.available.names
I would guess that most of you want is really well done inside the VPC module.
You would have to import the VPC into your state to start, but this is how I do my subnets with it.
locals {
subnets = chunklist(cidrsubnets("10.2.8.0/24", 3, 3, 3, 3, 3, 3), 2)
public_subnets = local.subnets[1]
private_subnets = local.subnets[2]
}
data "aws_availability_zones" "available" {
}
resource "aws_eip" "nat" {
count = length(local.private_subnets)
vpc = true
}
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "3.14.0"
name = "foo"
cidr = "10.2.8.0/24"
azs = data.aws_availability_zones.available.names
private_subnets = local.private_subnets
public_subnets = local.public_subnets
enable_nat_gateway = true
single_nat_gateway = true
enable_dns_hostnames = true
reuse_nat_ips = true # <= Skip creation of EIPs for the NAT Gateways
external_nat_ip_ids = aws_eip.nat.*.id
public_subnet_tags = {
"Tier" = "Public"
}
private_subnet_tags = {
"Tier" = "Private"
}
}
output "public_subnets" {
value = module.vpc.public_subnets
}
output "public_subnets_cidr" {
value = module.vpc.public_subnets_cidr_blocks
}
output "private_subnets" {
value = module.vpc.private_subnets
}
output "private_subnets_cidr" {
value = module.vpc.private_subnets_cidr_blocks
}

Incorrect attribute value type. Inappropriate value for sttributes "subnets: set of string required."

I've passed subnet ids many times and haven't encountered this error before, I'm really not sure why or how to fix it.
I have my subnet resource block right here in my VPC module:
resource "aws_subnet" "subnet_1" {
vpc_id = aws_vpc.vpc_1.id
cidr_block = "10.0.0.0/24"
availability_zone = var.region_az
tags = {
Name = "pcp-subnet-1"
}
}
This is what the outputs file looks like for my subnet output:
output "subnet_1" {
value = aws_subnet.subnet_1.id
}
This is my root module calling the subnet id output
module "ecs" {
source = "./ecs"
service_subnets = module.vpc.subnet_1
pcp_service_sg = module.vpc.pcp_service_sg
}
In my ecs module I have the variable listed here in my variables file
variable "service_subnets" {
type = string
}
This is my ecs resource where the error is occurring
resource "aws_ecs_service" "ecs-service" {
name = "python-cloud-project"
cluster = aws_ecs_cluster.cluster.id
task_definition = aws_ecs_task_definition.pcp-ecs-task-definition.arn
launch_type = "FARGATE"
network_configuration {
subnets = var.service_subnets
security_groups = var.pcp_service_sg
assign_public_ip = true
}
desired_count = 1
}
network_configuration requires a list of subnets, you are passing only one subnet. What you can do is to create a list with one element:
module "ecs" {
source = "./ecs"
service_subnets = [module.vpc.subnet_1]
pcp_service_sg = module.vpc.pcp_service_sg
}

Terraform AWS VCPe Route Creation Endpoint Not Found

When I create a route to an endpoint I get the following error:
InvalidVpcEndpointId.NotFound: The vpcEndpoint ID 'vpce-044e0beXXXXXXXX' does not exist.
But further up in the output (and on the console) I can see the endpoint is created:
module.sec.aws_vpc_endpoint.s3[0]: Creation complete after 6s [id=vpce-044e0beXXXXXXXXX]
This is what I have to create the route:
resource "aws_route" "s3_route" {
count = length(var.s3_routes)
route_table_id = aws_route_table.main.id
destination_cidr_block = var.s3_routes[count.index]
vpc_endpoint_id = var.s3_endpoint_ID[0]
}
This module is an example of one that calls it:
module "sec-route-NATGW-ifw-a" {
source = "./route"
depends_on = [module.i-dmz, module.t-dmz, module.cde, module.tgw-core, module.tgw-ifw]
vpc_id = module.sec.vpc_id
subnet_association = [for s in range(0, length(module.sec.natgw_subnet_IDs)) : module.sec.natgw_subnet_IDs[s] if module.sec.natgw_subnet_AZs[s] == "${local.region}a"]
s3_endpoint_ID = module.sec.s3_endpoint_ID
s3_routes = local.s3_ips
}
And this is the output for the ID:
output "s3_endpoint_ID" {
value = aws_vpc_endpoint.s3[*].id
description = "ID for S3 Endpoint"
}
And the resource to create the endpoint:
resource "aws_vpc_endpoint" "s3" {
count = var.s3_servicename == "" ? 0 : 1
vpc_id = aws_vpc.SEC.id
service_name = var.s3_servicename
}
FYI VPCE ID has been changed from script.
The issue was the configuration on my route resource, I replaced it with the following:
resource "aws_vpc_endpoint_route_table_association" "s3" {
count = length(var.s3_endpoint_ID) == 0 ? 0 : 1
route_table_id = aws_route_table.main.id
vpc_endpoint_id = tostring(var.s3_endpoint_ID[0])
}
I don't think the tostring() is needed and I changed how count worked slightly, but adding the destination_cidr_block argument was the issue.

Terraform GCP: The resource was not found for an external ip address

I'm trying to create the base VPC network on google cloud platform using terraform on which I could then launch instances, GKE etc, however I end up with the following error message:
Error: Error creating RouterNat: googleapi: Error 404: The resource 'projects/lol/regions/mars-north1/addresses/39.144.42.123' was not found, notFound
This is the minimal terraform code without variables which leads to this state:
module "gcp_vpc" {
count = var.project_type == "aws" ? 0 : 1
source = "terraform-google-modules/network/google"
version = "3.0.1"
project_id = var.project_id
network_name = "${var.project_name}-lol-vpc"
routing_mode = "GLOBAL"
subnets = [
{
subnet_name = "${var.project_name}-lol-primary-subnet-${count.index}"
subnet_ip = "10.222.0.0/28"
subnet_region = var.gcp_region
subnet_private_access = "true"
subnet_flow_logs = "true"
}
]
secondary_ranges = {
"${var.project_name}-lol-secondary-subnets" = [
{
count = length(var.public_subnets)
range_name = "${var.project_name}-lol-subnet-alias-${count.index}"
ip_cidr_range = var.public_subnets[count.index]
},
{
count = length(var.private_subnets)
range_name = "${var.project_name}-lol-private-subnet-${count.index}"
ip_cidr_range = var.private_subnets[count.index]
},
]
}
}
module "cloud-nat" {
count = var.project_type == "aws" ? 0 : 1
source = "terraform-google-modules/cloud-nat/google"
version = "~> 1.3"
project_id = var.project_id
region = var.gcp_region
router = "${var.project_name}-lol-router"
create_router = true
network = module.gcp_vpc[0].network_name
# subnetworks = module.gcp_vpc[0].subnets_self_links
nat_ips = google_compute_address.nat.*.address
source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_PRIMARY_IP_RANGES"
min_ports_per_vm = "128"
icmp_idle_timeout_sec = "15"
tcp_established_idle_timeout_sec = "600"
tcp_transitory_idle_timeout_sec = "15"
udp_idle_timeout_sec = "15"
}
resource "google_compute_address" "nat" {
count = var.project_type == "aws" ? 0 : length(var.public_subnets)
name = "${var.project_name}-lol-eip-nat-${count.index}"
project = var.project_id
region = var.gcp_region
}
output "gcp_nat_ips" {
value = google_compute_address.nat.*.address
}
However, I can see the external ip address 39.144.42.123 on the GCP web console. Can someone please help with where should I be looking at to solve this issue? Is it terraform, the terraform provider or GCP?

Shuffle between subnets from two terraform maps as input for ec2 instance creation

I have two sets of CIDRs for each environment, i would like for terraform to shuffle between the two sets whenever a new instance is being created.
I have looked at the terraform random_shuffle provider and the merge function but these do not provide the solution to my problem.
resource "aws_subnet" "myapp" {
cidr_block = "${cidrsubnet(var.vpc_cidr[terraform.workspace], 5, count.index + 16 + 5)}"
}
variable "vpc_cidr" {
type = "map"
default = {
QA = "20.30.100.0/23"
TEST = "20.37.200.0/23"
PROD = "20.37.200.0/23"
DEV = "20.37.100.0/23"
}
}
locals {
"vpc_cidr_2" = {
QA = "10.30.182.0/23"
TEST = "10.37.238.0/23"
PROD = "<none>"
DEV = "<none>"
}
}
I would like cidr_block to be calculated based on either vpc_cidr or vpc_cidr2 and shuffle between each. Also it would need to check for and fall back to the other map if is found.
Note: vpc_cidr is a variable while vpc_cidr_2 is a local.
random_shuffle is the right resource you should work on, but you need change the idea to mix variable and locals together.
resource "aws_subnet" "myapp" {
vpc_id = "${aws_vpc.main.id}"
cidr_block = "${cidrsubnet(lookup(var.vpc_cidr[random_shuffle.vpc_cidr.result], terraform.workspace), 5, count.index + 16 + 5)}"
}
resource "random_shuffle" "vpc_cidr" {
input = ["vpc_cidr", "vpc_cidr_2"]
}
variable "vpc_cidr" {
type = "map"
default = {
vpc_cidr = {
"QA" = "20.30.100.0/23"
"TEST" = "20.37.200.0/23"
"PROD" = "20.37.200.0/23"
"DEV" = "20.37.100.0/23"
}
vpc_cidr_2 = {
"QA" = "10.30.182.0/23"
"TEST" = "10.37.238.0/23"
"PROD" = "<none>"
"DEV" = "<none>"
}
}
}