How to create attachments in transit gateway module terraform - amazon-web-services

I have created a transit gateway using the terraform tgw module as shown below.
module "transit-gateway" {
source = "terraform-aws-modules/transit-gateway/aws"
version = "1.4.0"
name = "tgw-nprod"
description = "My TGW shared with several other AWS accounts"
amazon_side_asn = 64532
enable_auto_accept_shared_attachments = true
vpc_attachments = {
vpc1 = {
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
dns_support = true
ipv6_support = false
transit_gateway_default_route_table_association = false
transit_gateway_default_route_table_propagation = false
}
}
ram_allow_external_principals = true
ram_principals = [1234567890, 0987654321]
tags = {
Purpose = "tgw-testing"
}
}
I have created vpc using the terraform vpc module.
When I run the above terraform Iam getting error "Error: error creating EC2 Transit Gateway VPC Attachment: DuplicateSubnetsInSameZone: Duplicate Subnets for same AZ"
I have 2 private subnet in ap-south-1 and 1 public in ap-south-1.

The AWS docs write that you can have your gateway in only one subnet per AZ:
You must select at least one subnet. You can select only one subnet per Availability Zone.
Your error msg suggests that your module.vpc.private_subnets are in same AZ. You have to redefine your VPC so that module.vpc.private_subnets are in two different AZs, or just use one subnet in your subnet_ids.
To use one subnet:
subnet_ids = [module.vpc.private_subnets[0]]

Related

How to remove the igw attached to the terraform vpc module for registry

Iam using the vpc module from terraform registry to create vpc.
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "2.77.0"
....
}
I want to remove the igw attached to the public subnet.
Any help would be appreciated.
The module provides create_igw option:
variable "create_igw" {
description = "Controls if an Internet Gateway is created for public subnets and the related routes that connect them."
type = bool
default = true
}
If you don't want igw, then set it to false:
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "2.77.0"
....
create_igw = false
}

Share RDS instance with another VPC, but no other resources?

I have created two VPCs using Terraform:
resource "aws_vpc" "alpha" {
cidr_block = "10.16.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "Alpha"
}
}
resource "aws_subnet" "alpha_private_a" {
vpc_id = aws_vpc.alpha.id
cidr_block = "10.16.192.0/24"
availability_zone = "${var.aws_region}a"
tags = {
Name = "Alpha Private A"
}
}
resource "aws_subnet" "alpha_private_b" {
vpc_id = aws_vpc.alpha.id
cidr_block = "10.16.224.0/24"
availability_zone = "${var.aws_region}b"
tags = {
Name = "Alpha Private B"
}
}
resource "aws_route_table" "alpha_private" {
vpc_id = aws_vpc.alpha.id
tags = {
Name = "Alpha Private"
}
}
resource "aws_route_table_association" "alpha_private_a" {
route_table_id = aws_route_table.alpha_private.id
subnet_id = aws_subnet.alpha_private_a.id
}
resource "aws_route_table_association" "alpha_private_b" {
route_table_id = aws_route_table.alpha_private.id
subnet_id = aws_subnet.alpha_private_b.id
}
# The same again for VPC "Bravo"
I also have an RDS in VPC "Alpha":
resource "aws_db_subnet_group" "alpha_rds" {
subnet_ids = [ aws_subnet.alpha_private_a.id, aws_subnet.alpha_private_b.id ]
tags = {
Name = "Alpha RDS"
}
}
resource "aws_db_instance" "alpha" {
identifier = "alpha"
allocated_storage = 20
max_allocated_storage = 1000
storage_type = "gp2"
engine = "postgres"
engine_version = "11.8"
publicly_accessible = false
db_subnet_group_name = aws_db_subnet_group.alpha_rds.name
performance_insights_enabled = true
vpc_security_group_ids = [ aws_security_group.alpha_rds.id ]
lifecycle {
prevent_destroy = true
}
}
Then I have an Elastic Beanstalk instance inside VPC "Bravo".
What I want to achieve:
alpha_rds is accessible to my Elastic Beanstalk instance inside Bravo VPC
Nothing else inside Alpha VPC is accessible to Bravo VPC
Nothing else inside Bravo VPC is accessible to Alpha VPC
I think VPC Peering is required for this?
How can I implement this in Terraform?
Related but not Terraform:
Access Private RDS DB From Another VPC
AWS Fargate connection to RDS in a different VPC
You should be able to set it up like this:
Create a VPC Peering Connection between Alpha and Bravo
In the Route table for Alpha add a route for the CIDR range of Bravo and set the destination to the peering connection (pcx-XXXXXX) to Bravo
In the Route table for Bravo add a route for the IP-address(es) of the Database and point it to the peering connection to Alpha
This setup guarantees, that resources in Bravo can only communicate to the Database in Alpha, every other packet to that VPC can't be routed.
The inverse is a little tougher - right now this setup should stop TCP connections from Alpha to Bravo being established, because there is no return path except for the database. UDP traffic could still go through, it's response will be dropped though, unless it comes from the database.
At this point you could set up Network Access Control lists in the Subnets in Bravo to Deny traffic from Alpha except for the database IPs. This depends on your level of paranoia or your requirements in terms of isolation - personally I wouldn't do it, but it's Friday afternoon and I'm in a lazy mood ;-).
Update
As Mark B correctly pointed out in the comments there is a risk, that the private IP addresses of your RDS cluster may change on failover if the underlying host can't be recovered.
To address these concerns, you could create separate subnets in Alpha for your database node(s) and substitute the database IPs in my description above with the CIDRs of these subnets. That should allow for slightly more flexibility and allows you to get around the NACL problem as well, because you can just edit the routing table of the new database subnet(s) and only add the Peering Connection there.

Service discovery using ECS Fargate

I have 2 services within ECS Fargate running.
I have set up service discovery with a private dns namespace as all my services are within a private subnet.
When I try and hit my config container from another I am getting the following error.
http://config.qcap-prod:50050/config: Get
"http://config.qcap-prod:50050/config": dial tcp: lookup
config.qcap-prod on 10.0.0.2:53: no such host
Below is my Terraform
resource "aws_service_discovery_service" "config" {
name = "config"
dns_config {
namespace_id = aws_service_discovery_private_dns_namespace.qcap_prod_sd.id
dns_records {
ttl = 10
type = "A"
}
}
health_check_custom_config {
failure_threshold = 1
}
}
Is there another step I need to do to allow me to hit my container from another within ECS using Fargate?
My terraform code for my namespace is:
resource "aws_service_discovery_private_dns_namespace" "qcap_prod_sd" {
name = "qcap.prod"
description = "Qcap prod service discovery"
vpc = module.vpc.vpc_id
}
The fix for this was to add
module "vpc" {
enable_dns_support = true
enable_dns_hostnames = true
}
In the module block within the vpc module to allow the DNS hostnames to be resolved within my VPC

Terraform get IPs of vpc endpoint subnets

I am trying to setup AWS SFTP transfer in vpc endpoint mode but there is one think I can't manage with.
The problem I have is how to get target IPs for NLB target group.
The only output I found:
output "vpc_endpoint_transferserver_network_interface_ids" {
description = "One or more network interfaces for the VPC Endpoint for transferserver"
value = flatten(aws_vpc_endpoint.transfer_server.*.network_interface_ids)
}
gives network interface ids which cannot be used as targets:
Outputs:
api_url = https://12345.execute-api.eu-west-1.amazonaws.com/prod
vpc_endpoint_transferserver_network_interface_ids = [
"eni-12345",
"eni-67890",
"eni-abcde",
]
I went through:
terraform get subnet integration ips from vpc endpoint subnets tab
and
Terraform how to get IP address of aws_lb
but none of them seems to be working. The latter says:
on modules/sftp/main.tf line 134, in data "aws_network_interface" "ifs":
134: count = "${length(local.nlb_interface_ids)}"
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.
You can create an Elastic IP
resource "aws_eip" "lb" {
instance = "${aws_instance.web.id}"
vpc = true
}
Then specify the Elastic IPs while creating Network LB
resource "aws_lb" "example" {
name = "example"
load_balancer_type = "network"
subnet_mapping {
subnet_id = "${aws_subnet.example1.id}"
allocation_id = "${aws_eip.example1.id}"
}
subnet_mapping {
subnet_id = "${aws_subnet.example2.id}"
allocation_id = "${aws_eip.example2.id}"
}
}

Terraform: programmatically generate multiple routing tables for multiple VPC peering connections

Using terraform-0.7.7. I'm following, more or less, the Best Practices repo, the AWS part:
https://github.com/hashicorp/best-practices/tree/master/terraform
I am trying to come up with a general template for building VPCs. The idea is to have a network module with several sub-modules (vpc, private_subnet, etc), and just plug into it different variables from terraform.tfvars files, in order to build different environments.
Let's say in the .tfvars file for one environment I have a list of availability zones, and another list with the IP blocks for the private subnets:
azs = "us-west-2a,us-west-2b,us-west-2c"
private_subnets = "10.XXX.1.0/24,10.XXX.2.0/24,10.XXX.3.0/24"
The network/private_subnets module will happily create subnets, route tables, and associations based on those lists:
resource "aws_subnet" "private" {
vpc_id = "${var.vpc_id}"
cidr_block = "${element(split(",", var.cidrs), count.index)}"
availability_zone = "${element(split(",", var.azs), count.index)}"
count = "${length(split(",", var.cidrs))}"
tags { Name = "${var.name}-${element(split(",", var.azs), count.index)}-private" }
lifecycle { create_before_destroy = true }
}
resource "aws_route_table" "private" {
vpc_id = "${var.vpc_id}"
count = "${length(split(",", var.cidrs))}"
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = "${element(split(",", var.nat_gateway_ids), count.index)}"
}
tags { Name = "${var.name}-${element(split(",", var.azs), count.index)}-private" }
lifecycle { create_before_destroy = true }
}
resource "aws_route_table_association" "private" {
count = "${length(split(",", var.cidrs))}"
subnet_id = "${element(aws_subnet.private.*.id, count.index)}"
route_table_id = "${element(aws_route_table.private.*.id, count.index)}"
lifecycle { create_before_destroy = true }
}
That works well. For each environment, I have different azs and private_subnets lists, and the VPC is created correctly. The NAT gateways are created prior to that in a different module. I have one NAT gateway, one private subnet, and one private routing table, per AZ.
But now I'm trying to create VPC peering connections in the same way.
peer_vpc_ids = "vpc-XXXXXXXX, vpc-YYYYYYYY"
peer_vpc_blocks = "10.XXX.0.0/16, 10.YYY.0.0/16"
So now I need to write Terraform code that goes into the private route tables and adds routes for each VPC peering connection.
The problem is, I need to iterate by two variables: the list of AZs, and the list of peering connections, and Terraform does not seem to allow that. AFAICT, you cannot do nested loops in Terraform.
Am I missing something? Is there a better way to solve this problem?
Of course I could write by hand some custom spaghetti code that would build the VPC no matter what, but the goal here is to keep the code composable and maintainable, and separate the logic from the attributes.
You can potentially achieve this with some math on the element interpolation method:
resource "aws_vpc_peering_connection" {
peer_vpc_ids = "${element(var.vpc_ids, count.index)}"
peer_vpc_blocks = "${element(var.vpc_blocks,floor(count.index / length(var.vpc_ids))}"
count = "${length(var.vpc_ids) * length(var.vpc_blocks)}"
}
Specifically the element(var.vpc_blocks,floor(count.index / length(var.vpc_ids)) call will make VPC blocks increment 1 for each of the var.vpc_ids values.