VPC Endpoints on-demand with CloudFormation

VPC Endpoints on-demand with CloudFormation

service

By Andrzej Komarnicki
Cloud DevOps @ smallfries.digital
Date: 2024-07-01

In modern cloud architectures, it's essential to secure communication between your resources within a Virtual Private Cloud (VPC) and other AWS services. VPC Endpoints provide a secure and scalable way to connect your VPC to AWS services without traversing the public internet, reducing the risk of data exposure and improving network performance.

This blog post will walk you through a CloudFormation template that creates multiple VPC Endpoints for popular AWS services like Amazon S3, DynamoDB, CloudWatch Logs, and AWS Systems Manager (SSM). We'll also cover the necessary security groups and policies to control access to these endpoints.

The CloudFormation Template

The provided YAML template creates the following resources:

  • Gateway VPC Endpoints: For Amazon S3 and DynamoDB, allowing private connectivity from your VPC to these services.

  • Interface VPC Endpoints: For CloudWatch Monitoring, CloudWatch Logs, SSM, and SSM Messages, enabling private access to these services from your VPC.

  • Security Groups: A default security group for the interface endpoints, and a separate security group for EC2 Instance Connect.

Here's a breakdown of the key sections in the template:

Parameters:
  VpcId:
    Type: String
    Description: VPC ID
  VpcCIDR:
    Type: String
    Description: VPC CIDR to associate with VPC Endpoint security group
  SubnetIds:
    Type: List<String>
    Description: Comma-separated list of subnet IDs
  RouteTableIds:
    Type: List<String>
    Description: Comma-separated list of route table IDs

The template accepts four parameters: the VPC ID, the VPC CIDR block (optional), a list of subnet IDs, and a list of route table IDs. These parameters allow you to customize the deployment to your specific VPC and network configuration.

Resources:
  VPCEndpointSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: VPC Endpoint Security Group
      VpcId: !Ref VpcId

This section creates a security group for the interface VPC Endpoints. You can modify the security group rules to control inbound and outbound traffic to these endpoints.

S3Endpoint:
  Type: AWS::EC2::VPCEndpoint
  Properties:
    PolicyDocument:
      Version: 2012-10-17
      Statement:
        - Effect: Allow
          Principal: "*"
          Action:
            - s3:*
          Resource:
            - "*"
    RouteTableIds: !Ref RouteTableIds
    ServiceName: !Sub "com.amazonaws.${AWS::Region}.s3"
    VpcId: !Ref VpcId
    VpcEndpointType: Gateway
    PrivateDnsEnabled: false

This resource creates a Gateway VPC Endpoint for Amazon S3. Note the PolicyDocument property, which allows all actions (s3:*) on all S3 resources (*). You can modify this policy to restrict access as needed.

The template includes similar resources for creating VPC Endpoints for DynamoDB, CloudWatch Monitoring, CloudWatch Logs, SSM, and SSM Messages.

Deploying the Template

To deploy this CloudFormation template, you'll need to provide the required parameters (VPC ID, subnet IDs, and route table IDs) specific to your AWS account and VPC configuration. You can deploy the template using the AWS Management Console, AWS CLI, or AWS CloudFormation APIs.

Once deployed, your VPC will have secure, private connectivity to the specified AWS services, without the need for internet gateways, NAT gateways, or public IP addresses.

Conclusion

VPC Endpoints are a powerful tool for securing communication between your VPC resources and AWS services. By using CloudFormation templates like the one provided, you can streamline the deployment of VPC Endpoints across multiple accounts and environments, ensuring consistent and secure network configurations.

Remember to review and customize the security group rules and endpoint policies to align with your organization's security requirements and access controls. Additionally, consider automating the deployment process using CI/CD pipelines or infrastructure-as-code tools for efficient and repeatable infrastructure provisioning.

Original Complete Template:
AWSTemplateFormatVersion: 2010-09-09
Description: Create Gateway VPC Endpoints for S3 and DynamoDB. Interface VPC Endpoints for CloudWatch Monitoring/Logs, EC2 Instance Connect and SSM with a default SG.
Parameters:
  VpcId:
    Type: String
    Description: VPC ID
  VpcCIDR:
    Type: String
    Description: VPC CIDR to associate with VPC Endpoint security group (if not provided 0.0.0.0/0 will be used instead)
  SubnetIds:
    Type: List<String>
    Description: Comma separated list of subnet IDs
  RouteTableIds:
    Type: List<String>
    Description: Comma separated list of route table IDs
Conditions:
  HasVpcCIDR: !Not
    - !Equals
      - ""
      - !Ref VpcCIDR
Resources:
  VPCEndpointSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: VPC Endpoint Security Group
      VpcId: !Ref VpcId
  VPCEndpointSecurityGroupIngress:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: !Ref VPCEndpointSecurityGroup
      FromPort: 443
      ToPort: 443
      IpProtocol: tcp
      SourceSecurityGroupId: !Ref VPCEndpointSecurityGroup
  VPCEndpointSecurityGroupIngress2:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: !Ref VPCEndpointSecurityGroup
      FromPort: 443
      ToPort: 443
      IpProtocol: tcp
      CidrIp: !If
        - HasVpcCIDR
        - !Ref VpcCIDR
        - "0.0.0.0/0"
  S3Endpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal: "*"
            Action:
              - s3:*
            Resource:
              - "*"
      RouteTableIds: !Ref RouteTableIds
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.s3"
      VpcId: !Ref VpcId
      VpcEndpointType: Gateway
      PrivateDnsEnabled: false
  DynamoDbEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal: "*"
            Action:
              - dynamodb:*
            Resource:
              - "*"
      RouteTableIds: !Ref RouteTableIds
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.dynamodb"
      VpcId: !Ref VpcId
      VpcEndpointType: Gateway
      PrivateDnsEnabled: false
  CloudWatchEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal: "*"
            Action:
              - logs:*
            Resource:
              - "*"
      SubnetIds: !Ref SubnetIds
      SecurityGroupIds: !Split
        - ","
        - !Join
          - ","
          - - !GetAtt VPCEndpointSecurityGroup.GroupId
      VpcId: !Ref VpcId
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.logs"
      VpcEndpointType: Interface
      PrivateDnsEnabled: false
  MonitoringEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal: "*"
            Action:
              - cloudwatch:*
            Resource:
              - "*"
      SubnetIds: !Ref SubnetIds
      SecurityGroupIds: !Split
        - ","
        - !Join
          - ","
          - - !GetAtt VPCEndpointSecurityGroup.GroupId
      VpcId: !Ref VpcId
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.monitoring"
      VpcEndpointType: Interface
      PrivateDnsEnabled: false
  SsmEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal: "*"
            Action:
              - ssm:*
            Resource:
              - "*"
      SubnetIds: !Ref SubnetIds
      SecurityGroupIds: !Split
        - ","
        - !Join
          - ","
          - - !GetAtt VPCEndpointSecurityGroup.GroupId
      VpcId: !Ref VpcId
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssm"
      VpcEndpointType: Interface
      PrivateDnsEnabled: false
  SsmMessagesEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal: "*"
            Action:
              - ssmmessages:*
            Resource:
              - "*"
      SubnetIds: !Ref SubnetIds
      SecurityGroupIds: !Split
        - ","
        - !Join
          - ","
          - - !GetAtt VPCEndpointSecurityGroup.GroupId
      VpcId: !Ref VpcId
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssmmessages"
      VpcEndpointType: Interface
      PrivateDnsEnabled: false
  VPCEndpointEC2ICSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: VPC Endpoint Security Group for EC2 Instance Connect
      VpcId: !Ref VpcId
  VPCEndpointEC2ICSecurityGroupEgress:
    Type: AWS::EC2::SecurityGroupEgress
    Properties:
      GroupId: !Ref VPCEndpointEC2ICSecurityGroup
      FromPort: 22
      ToPort: 22
      IpProtocol: tcp
      DestinationSecurityGroupId: !Ref VPCEndpointEC2ICSecurityGroup
  VPCEndpointEC2ICSecurityGroupEgress2:
    Type: AWS::EC2::SecurityGroupEgress
    Properties:
      GroupId: !Ref VPCEndpointEC2ICSecurityGroup
      FromPort: 22
      ToPort: 22
      IpProtocol: tcp
      CidrIp: !If
        - HasVpcCIDR
        - !Ref VpcCIDR
        - "0.0.0.0/0"
  EC2InstanceConnectEndpoint:
    Type: AWS::EC2::InstanceConnectEndpoint
    UpdateReplacePolicy: "Delete"
    Properties:
      SubnetId: !Select
        - 0
        - !Ref SubnetIds
      SecurityGroupIds: !Split
        - ","
        - !Join
          - ","
          - - !GetAtt VPCEndpointEC2ICSecurityGroup.GroupId
      PreserveClientIp: false