AWS Inter-Region PrivateLink using Terraform

AWS Inter-Region PrivateLink using Terraform

Photo by Taylor Vick on Unsplash

AWS PrivateLink provides a secure and reliable way to connect VPCs within the same region, but it doesn’t directly support connections between VPCs in different regions. To address this limitation, inter-Region VPC peering offers a viable solution.

Inter-Region VPC peering enables private connectivity between VPCs in different AWS regions ( have a look into [this previous article](https://awsmorocco.com/aws-multi-region-vpc-peering-using- terraform-a0b8aabf084b) for a deep dive into AWS VPC peering).

How it works:

1 — The Service Provider VPC hangs out in region A (let’s say us-east-1). Now, the Service Consumer VPC wants a local interface VPC endpoint, but in a different spot, region B (like us-west-2).

2 — If you’re not keen on directly connecting the Consumer VPC and the main Service Provider VPC (not the best idea, they say), you can birth another VPC in the same region as the consumer (us-west-2). Connect this new kid (let’s call it Outpost VPC) in the same region as the customer to the main Service Provider VPC.

3 — Time to play matchmaker! Enable PrivateLink between the Outpost VPC (us- west-2) and the Consumer VPC (also us-west-2). Create a VPC endpoint Service in the Outpost VPC with the NLB that gets its targets from the Service Provider VPC (reachable through the peering).

This whole shebang makes even more sense when the Consumer VPC and the Service Provider VPCs are in different accounts or when a direct connection between them is a no-go for security or compliance reasons.

Cross-account inter- region PrivateLink

Let’s Set it Up:

1 — Create VPCs to emulate the Service provider, Service Provider Outpost and the Consumer VPC:

    module "vpc_service_provider" {  
      source = "terraform-aws-modules/vpc/aws"  
      name   = "awsmorocco_service_provider_vpc"  
      cidr   = "10.10.0.0/16"  
      
      azs             = ["us-east-1a"]  
      private_subnets = ["10.10.1.0/24"]  
      public_subnets  = ["10.10.2.0/24"]  
      
      enable_nat_gateway = true  
      single_nat_gateway = true  
      
      enable_dns_hostnames = true  
      enable_dns_support   = true  
      
      providers = {  
        aws = aws.service_provider  
      }  
    }  
      
    module "vpc_service_provider_outpost" {  
      source = "terraform-aws-modules/vpc/aws"  
      name   = "awsmorocco_service_provider_outpost"  
      cidr   = "10.11.0.0/16"  
      
      azs             = ["us-west-2a"]  
      private_subnets = ["10.11.1.0/24"]  
      public_subnets  = ["10.11.2.0/24"]  
      
      enable_nat_gateway = true  
      single_nat_gateway = true  
      
      enable_dns_hostnames = true  
      enable_dns_support   = true  
      
      providers = {  
        aws = aws.service_provider_outpost  
      }  
    }  
      
    module "vpc_service_consumer" {  
      source = "terraform-aws-modules/vpc/aws"  
      name   = "awsmorocco_service_consumer"  
      cidr   = "10.2.0.0/16"  
      
      azs                = ["us-west-2a"]  
      private_subnets    = ["10.12.1.0/24"]  
      public_subnets     = ["10.12.2.0/24"]  
      enable_nat_gateway = true  
      single_nat_gateway = true  
      
      enable_dns_hostnames = true  
      enable_dns_support   = true  
      
      providers = {  
        aws = aws.consumer  
      }  
    }

The providers configuration looks like this:

    
    provider "aws" {  
      alias  = "consumer"  
      region = "us-east-1"  
    }  
      
    provider "aws" {  
      alias  = "service_provider"  
      region = "us-west-2"  
    }  
      
    provider "aws" {  
      alias  = "service_provider_outpost"  
      region = "us-west-2"  
    }

2 — Once the VPCs are created, enable the peering between the Service provider VPC and the Outpost PVC:

    resource "aws_vpc_peering_connection" "this" {  
      vpc_id      = module.vpc_service_provider_outpost.vpc_id  
      peer_vpc_id = module.vpc_service_provider.vpc_id  
      peer_region = "us-east-1"  
      auto_accept = false  
      
      provider = aws.service_provider_outpost  
    }  
      
    resource "aws_vpc_peering_connection_accepter" "this" {  
      provider                  = aws.service_provider  
      vpc_peering_connection_id = aws_vpc_peering_connection.this.id  
      auto_accept               = true  
    }  
      
    resource "aws_vpc_peering_connection_options" "this" {  
      vpc_peering_connection_id = aws_vpc_peering_connection.this.id  
      accepter {  
        allow_remote_vpc_dns_resolution = true  
      }  
      
      provider = aws.service_provider  
    }  
      
    locals {  
      requester_route_tables_ids = concat(module.vpc_service_provider_outpost.public_route_table_ids, module.vpc_service_provider_outpost.private_route_table_ids)  
      accepter_route_tables_ids  = concat(module.vpc_service_provider.public_route_table_ids, module.vpc_service_provider.private_route_table_ids)  
    }  
      
    resource "aws_route" "requester" {  
      count                     = length(local.requester_route_tables_ids)  
      route_table_id            = local.requester_route_tables_ids[count.index]  
      destination_cidr_block    = module.vpc_service_provider.vpc_cidr_block  
      vpc_peering_connection_id = aws_vpc_peering_connection.this.id  
      
      provider = aws.service_provider_outpost  
    }  
      
    resource "aws_route" "accepter" {  
      count                     = length(local.accepter_route_tables_ids)  
      route_table_id            = local.accepter_route_tables_ids[count.index]  
      destination_cidr_block    = module.vpc_service_provider_outpost.vpc_cidr_block  
      vpc_peering_connection_id = aws_vpc_peering_connection.this.id  
      
      provider = aws.service_provider  
    }

3 — Once the VPCs are created and the peering is established, you can proceed and enable/create the PrivateLink setup between the Service Consumer VPC and the Service Provider VPC:

    
    data "aws_caller_identity" "current" {}  
      
    resource "aws_lb" "nlb" {  
      name               = "service-provider-nlb"  
      internal           = true  
      load_balancer_type = "network"  
      subnets            = module.vpc_service_provider.private_subnets  
      
      enable_deletion_protection = false  
      
      provider = aws.service_provider_outpost  
    }  
      
    resource "aws_vpc_endpoint_service" "this" {  
      acceptance_required        = false  
      network_load_balancer_arns = [aws_lb.nlb.arn]  
      
      provider = aws.service_provider_outpost  
    }  
      
    resource "aws_vpc_endpoint_service_allowed_principal" "this" {  
      vpc_endpoint_service_id = aws_vpc_endpoint_service.this.id  
      principal_arn           = data.aws_caller_identity.current.arn  
      
      provider = aws.service_provider_outpost  
    }  
      
    resource "aws_vpc_endpoint" "this" {  
      service_name      = aws_vpc_endpoint_service.this.service_name  
      subnet_ids        = module.vpc_service_consumer.private_subnets  
      vpc_endpoint_type = "Interface"  
      vpc_id            = module.vpc_service_consumer.vpc_id  
      
      provider = aws.consumer  
    }

4 — Finally, deploy EC2 instances within the Service Provider VPCs, each running a straightforward Flask API. Register these instances as targets in the Network Load Balancer (NLB), as elaborated in a prior article explaining the intricacies of PrivateLink .

Additionally, instantiate a public instance within the Consumer VPC and attempt to access the service — specifically, the Flask application — exposed by the deployed instances, following the guidelines outlined in the aforementioned article .

The Benefits of this setup:

  • Security : Traffic remains private within AWS’s private fiber network
  • Scalability : Connect VPCs across different AWS regions
  • Agility : Provide local access without immediate service deployment.

The Limitations to Consider:

  • Latency : Increased latency due to inter-region communication
  • Costs : Data transfer costs for inter-Region VPC peering

Conclusion

In conclusion, the implementation of Inter-Region VPC peering, coupled with AWS PrivateLink, offers a robust and flexible solution for SaaS providers expanding their services across diverse regions. This architectural approach ensures secure and private connectivity within AWS’s infrastructure, allowing for scalability and local access without immediate service deployment.

Read More:


AWS Inter-Region PrivateLink using Terraform was originally published in AWSMorocco on Medium, where people are continuing the conversation by highlighting and responding to this story.

Disclaimer for Awsmorocco.com

The content, views, and opinions expressed on this blog, awsmorocco.com, are solely those of the authors and contributors and not those of Amazon Web Services (AWS) or its affiliates. This blog is independent and not officially endorsed by, associated with, or sponsored by Amazon Web Services or any of its affiliates.

All trademarks, service marks, trade names, trade dress, product names, and logos appearing on the blog are the property of their respective owners, including in some instances Amazon.com, Inc. or its affiliates. Amazon Web Services®, AWS®, and any related logos are trademarks or registered trademarks of Amazon.com, Inc. or its affiliates.

awsmorocco.com aims to provide informative and insightful commentary, news, and updates about Amazon Web Services and related technologies, tailored for the Moroccan community. However, readers should be aware that this content is not a substitute for direct, professional advice from AWS or a certified AWS professional.

We make every effort to provide timely and accurate information but make no claims, promises, or guarantees about the accuracy, completeness, or adequacy of the information contained in or linked to from this blog.

For official information, please refer to the official Amazon Web Services website or contact AWS directly.

Related Posts

Do Pods Really Get Evicted Due to CPU Pressure?

Do Pods Really Get Evicted Due to CPU Pressure?

As Kubernetes administrators and developers, we’ve all heard the notion that pods can get evicted due to high CPU pressure on a node.

Read More
All you need to know about Terraform provisioners and why you should avoid them.

All you need to know about Terraform provisioners and why you should avoid them.

Photo by Danist Soh on Unsplash As defined in the Terraform documentation, provisioners can be used to model specific actions on the local machine running the Terraform Core or on a remote machine to prepare servers or other infrastructure objects.

Read More