DEV Community

se-piyush
se-piyush

Posted on

Creating and Linking VPC Endpoint of Type Interface using Terraform and AWS management console

In my previous posts, Setting Up VPC and Lambda Function with Terraform and Setting Up a VPC for Your App Using AWS Management Console, I talked about setting up VPC with Lambda and how my Lambda function interacts with DynamoDB and the external internet.

To connect to DynamoDB, we used a VPC Endpoint of type Gateway, which is easier to manage and doesn’t come with additional costs if the data transfer is within the VPC.

However, Gateway endpoints only support S3 and DynamoDB resources. All other resources (including DynamoDB and S3) require an Interface endpoint.

The Interface type VPC Endpoint requires a Security Group and some changes in your Lambda function code, specifically the DynamoDB configuration, unlike the Gateway type Endpoint, which doesn't require them.

This means that Interface Endpoints can provide more granular restrictions.

In this post, I’ll explain how to create and link a VPC endpoint of type Interface using both the AWS Management Console and Terraform.

Steps to Create a VPC Endpoint of Type Interface

  1. Create Security Groups for Lambda and DynamoDB:

    • For the DynamoDB Security Group, specify an inbound rule that accepts incoming requests from the Lambda Security Group.
    • For the Lambda Security Group, specify an outbound rule that permits sending requests to DynamoDB.
  2. Create the VPC Endpoint:

    • Choose the Interface type service, select the private subnet ID, and link your DynamoDB Security Group to it.
    • After creating the VPC Endpoint, retrieve the DNS name from the description, and use it in your Lambda code to connect to DynamoDB.

Creating a VPC Endpoint of type Interface Using Terraform

resource "aws_vpc" "main" {
  cidr_block = "<YOUR_CIDR_BLOCK>"
}

# Create private subnets
resource "aws_subnet" "private" {
  count                   = <NUMBER OF PRIVATE SUBNETS>
  vpc_id                  = aws_vpc.main.id
  cidr_block              = cidrsubnet(aws_vpc.main.cidr_block, 8, count.index * 2 + 1)
  availability_zone       = element(data.aws_availability_zones.available.names, count.index)
  map_public_ip_on_launch = false
}

resource "aws_security_group" "dynamodb_vpc_endpoint_sg" {
  name        = "dynamo-sg"
  description = "Security group for Lambda"
  vpc_id      = aws_vpc.main.id

  ingress {
    from_port       = 0
    to_port         = 0
    protocol        = "-1"
    security_groups = [aws_security_group.lambda.id]
  }
}

resource "aws_security_group" "lambda" {
  name        = "lambda-sg"
  description = "Security group for Lambda"
  vpc_id      = aws_vpc.main.id

  egress {
    from_port   = 443
    to_port     = 443
    protocol    = "TCP"
    cidr_blocks = [aws_vpc.main.cidr_block]
  }
}

resource "aws_vpc_endpoint" "dynamodb" {
  vpc_id             = aws_vpc.main.id
  service_name       = "com.amazonaws.${var.region}.dynamodb"
  vpc_endpoint_type  = "Interface"
  subnet_ids         = aws_subnet.private[*].id
  security_group_ids = [aws_security_group.dynamodb_vpc_endpoint_sg.id]
}

# Use the endpoint as an environment variable in your Lambda function
output "dynamodb_vpc_endpoint_dns" {
  value = aws_vpc_endpoint.dynamodb.dns_entry[0].dns_name
}
Enter fullscreen mode Exit fullscreen mode
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const client = new DynamoDBClient({
    endpoint: `https://<THE VPC ENDPOINT DNS>`,
});
const dynamoDbClient = DynamoDBDocumentClient.from(client);
Enter fullscreen mode Exit fullscreen mode

Creating a VPC Endpoint of Type Interface Using AWS Management Console

  1. Create the DynamoDB Security Group with the appropriate inbound rule (In this example, I’ve created an outbound rule to the Lambda Security Group).
    DynamoDB SG

  2. Create the Lambda Security Group with the appropriate outbound rule (Here, I’ve added my VPC CIDR as an outbound rule).
    Lambda SG

  3. Create a VPC Endpoint of type Interface with the intended VPC and private subnet, then link your DynamoDB Security Group to it. After creation, get the DNS name from the description and use it in your Lambda function to connect to DynamoDB.
    Create VPCE
    VPCE Details
    VPCE Created
    VPCE DNS

You can use this endpoint like this in your Node.js Lambda function:

const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const client = new DynamoDBClient({
    endpoint: `https://<THE VPC ENDPOINT DNS>`,
});
const dynamoDbClient = DynamoDBDocumentClient.from(client);
Enter fullscreen mode Exit fullscreen mode

Conclusion

In this post, we explored how to create and link a VPC Endpoint of type Interface using both the AWS Management Console and Terraform. By leveraging Interface endpoints, you can secure your Lambda function's communication with DynamoDB and other AWS services, while maintaining fine-grained control over traffic and security.

We contrasted the Interface endpoint with the Gateway endpoint, noting the additional configuration required, such as setting up security groups and modifying Lambda function code. However, this additional complexity provides enhanced security and control, making Interface endpoints a powerful tool for managing internal communication within your AWS environment.

With the provided Terraform code and AWS Management Console steps, you should now be able to integrate Interface endpoints into your architecture, ensuring secure and efficient access to DynamoDB from your Lambda functions.

Top comments (0)