Certificate and NLB not required! I tried publishing ECS Express Mode without public subnets using API Gateway (VPC Link v2)

Certificate and NLB not required! I tried publishing ECS Express Mode without public subnets using API Gateway (VPC Link v2)

I verified a configuration to connect from API Gateway (REST/HTTP API) to an ECS Express Mode environment on a public subnetless VPC via VPC Link v2. This enables secure internet exposure without requiring an NLB. I've also included a CloudFormation template that you can try immediately.
2026.03.03

This page has been translated by machine translation. View original

Previously, I introduced a configuration that builds an ECS Express Mode environment in a VPC without public subnets and exposes it to the internet using CloudFront VPC Origin.

https://dev.classmethod.jp/articles/ecs-express-mode-public-subnet-less/

While CloudFront was used at that time, you can also expose environments without public subnets to the internet using API Gateway with VPC Link v2.

With the November 2025 update, REST API private integrations can now directly specify ALBs. VPC Link v2 is used behind the scenes, allowing both REST API and HTTP API to share the same VPC Link.

https://dev.classmethod.jp/articles/api-gateway-rest-apis-integration-load-balancer/

This time, I had the opportunity to connect to ECS Express Mode (Internal ALB) on a VPC without public subnets from both API Gateway REST API and HTTP API via VPC Link v2 and verify connectivity, so I'll introduce it.

The main differences from the traditional v1 are as follows:

VPC Link v1 VPC Link v2
API type REST API only Both REST API / HTTP API
Target NLB required ALB, NLB, CLB, private IP
Underlying mechanism PrivateLink (VPC endpoint service) ENI (created directly in subnet)
Resource type AWS::ApiGateway::VpcLink AWS::ApiGatewayV2::VpcLink
Pricing Charged ($0.01/hour + data processing fee) Free
Behavior when unused Maintained ENI automatically deleted after 60 days of inactivity

VPC Link v2 itself incurs no additional costs. The NLB that was previously required for v1 is also unnecessary.

Cost item v1 configuration (traditional) v2 configuration (current)
VPC Link $0.01/hour (about $7.3/month) Free
NLB About $16/month~ (fixed cost + LCU) Not required
API Gateway REST API: $3.50/1M req REST API: $3.50/1M req
HTTP API: $1.00/1M req
Total fixed costs About $23/month~ $0

Architecture

The current configuration is as follows:

Comparison with the previous version:

  • Previous: Client → CloudFront → VPC Origin → Internal ALB → ECS
  • Current: Client → API Gateway → VPC Link v2 → Internal ALB → ECS

CloudFormation Template Explanation

The configuration consists of 3 stacks, with inter-stack connectivity via Export/ImportValue.

cfn/
  01-vpc.yaml              # Stack 1: VPC without public subnets
  02-ecs-express.yaml      # Stack 2: ECS Express Mode (Nginx)
  03-apigw-vpclink.yaml    # Stack 3: REST API + HTTP API + VPC Link v2

Stack 1: VPC (01-vpc.yaml)

This is the same configuration without public subnets as in the previous article. It creates private subnets x3, Regional NAT Gateway, and Egress-Only IGW.

Stack 2: ECS Express Mode (02-ecs-express.yaml)

A Nginx container is deployed using AWS::ECS::ExpressGatewayService. This time, I used the Nginx image (public.ecr.aws/nginx/nginx:stable-alpine).

The following are exported in Outputs:

  • ServiceEndpoint: The HTTPS endpoint automatically provided by ECS Express Mode (ap-xxxxx.ecs.region.on.aws)
  • LoadBalancerArn: The ARN of the Internal ALB
  • ListenerArn: The ARN of the ALB listener (used for HTTP API integration)

In this case, one VPC Link v2 is shared by both REST API and HTTP API.

VpcLinkV2:
  Type: AWS::ApiGatewayV2::VpcLink
  Properties:
    Name: !Sub '${ProjectName}-vpclink'
    SecurityGroupIds:
      - !Ref VpcLinkSecurityGroup
    SubnetIds:
      Fn::Split:
        - ','
        - Fn::ImportValue: !Sub '${VPCStackName}-private-subnets'

For both HTTP API and REST API, the resource type is AWS::ApiGatewayV2::VpcLink.

REST API Integration Settings

RootMethod:
  Type: AWS::ApiGateway::Method
  Properties:
    RestApiId: !Ref RestApi
    ResourceId: !GetAtt RestApi.RootResourceId
    HttpMethod: ANY
    AuthorizationType: NONE
    Integration:
      Type: HTTP_PROXY
      IntegrationHttpMethod: ANY
      ConnectionType: VPC_LINK
      ConnectionId: !Ref VpcLinkV2
      IntegrationTarget:
        Fn::ImportValue: !Sub '${ProjectName}-alb-arn'
      Uri: !Sub
        - 'https://${Endpoint}/'
        - Endpoint:
            Fn::ImportValue: !Sub '${ProjectName}-service-endpoint'
      RequestParameters:
        integration.request.header.Host: !Sub
          - "'${Endpoint}'"
          - Endpoint:
              Fn::ImportValue: !Sub '${ProjectName}-service-endpoint'

There are three key points:

  1. Directly specify the ALB ARN in IntegrationTarget
  2. Specify VPC Link v2 with ConnectionType: VPC_LINK + ConnectionId
  3. Override the Host header with ServiceEndpoint using integration.request.header.Host

Why Host Header Override is Necessary

By default, API Gateway sends its own hostname (xxx.execute-api.region.amazonaws.com) as the Host header to the backend. ECS Express Mode's ALB distributes services via host-based routing, so if the Host header doesn't match the ServiceEndpoint, it won't match the routing rule and will return a 404.

The syntax for overriding the Host header differs between REST API and HTTP API.

# REST API — Override using RequestParameters
RequestParameters:
  integration.request.header.Host: "'ap-xxxxx.ecs.region.on.aws'"

# HTTP API — Override using overwrite:header.Host
RequestParameters:
  overwrite:header.Host: ap-xxxxx.ecs.region.on.aws

Note that since the Host header is overwritten with .on.aws, the backend application cannot obtain the original domain (API Gateway URL) that the client accessed from the Host header. If the original domain is needed for generating redirect URLs, etc., consider using the X-Forwarded-Host header.

HTTP API Integration Settings

HttpApiIntegration:
  Type: AWS::ApiGatewayV2::Integration
  Properties:
    ApiId: !Ref HttpApi
    IntegrationType: HTTP_PROXY
    IntegrationMethod: ANY
    ConnectionType: VPC_LINK
    ConnectionId: !Ref VpcLinkV2
    IntegrationUri:
      Fn::ImportValue: !Sub '${ProjectName}-listener-arn'
    RequestParameters:
      overwrite:header.Host:
        Fn::ImportValue: !Sub '${ProjectName}-service-endpoint'
    TlsConfig:
      ServerNameToVerify:
        Fn::ImportValue: !Sub '${ProjectName}-service-endpoint'
    PayloadFormatVersion: '1.0'

For HTTP API, the ALB listener ARN is specified in IntegrationUri. The hostname for TLS verification is also aligned with ServiceEndpoint using TlsConfig.ServerNameToVerify. PayloadFormatVersion is set to 1.0. While the default for HTTP API is 2.0, 1.0 is required for HTTP proxy integration via VPC Link. There's only one route, $default, with a stage linked by AutoDeploy: true.

About Security Groups

A SG for VPC Link v2 was created. The ALB SG for ECS Express Mode is automatically managed by AWS, and in this verification, connectivity was established without additional configuration.

Connectivity Verification

The verification region used was ap-northeast-3 (Osaka). After deployment (procedure listed in the reference information at the end), I obtained the API Gateway endpoints and verified with curl.

aws cloudformation describe-stacks \
  --stack-name apigw-vpclink-test-apigw \
  --query 'Stacks[0].Outputs' --output table \
  --region ap-northeast-3

REST API is accessed via the /test stage, while HTTP API is accessed via the $default stage (no path).

$ curl -s https://f5s2s3u41l.execute-api.ap-northeast-3.amazonaws.com/test
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
...
Endpoint URL Status
REST API (root) https://f5s2s3u41l.execute-api.ap-northeast-3.amazonaws.com/test 200 OK
HTTP API (root) https://4j83booagl.execute-api.ap-northeast-3.amazonaws.com 200 OK

Both REST API and HTTP API returned the Nginx Welcome page.

Observations and Considerations

ECS Express Mode's ALB automatically provides HTTPS endpoints and certificates, meeting VPC Link v2's HTTPS requirements. While a normal ALB + ECS configuration requires managing certificates yourself, this can be omitted with ECS Express Mode.

Choosing Between CloudFront and API Gateway

While ECS Express Mode is commonly paired with CloudFront VPC Origin, consider using VPC Link v2 when API Gateway's authentication/authorization or API management features are needed.

  • HTTP API: JWT authentication, Lambda authorizer, automatic CORS handling
  • REST API: In addition to the above, API keys + usage plans, direct WAF integration, request transformation
Want to expose ECS Express Mode
  ├─ Need API key management/usage plans/request transformation?
  │    └─ Yes → REST API + VPC Link v2
  ├─ Need JWT auth/Lambda authorizer?
  │    └─ Yes → HTTP API + VPC Link v2 (lower cost)
  ├─ Need WAF/cache/geographic restrictions?
  │    └─ Yes → CloudFront VPC Origin (previous configuration)
  └─ None of the above needed
       └─ CloudFront VPC Origin (first choice)

Summary

I confirmed that both API Gateway REST API and HTTP API can connect to an ECS Express Mode environment on a VPC without public subnets via VPC Link v2. If you're adopting ECS Express Mode for workloads that need API Gateway features like authentication/authorization, please try VPC Link v2.

Reference Information:

The complete CloudFormation templates used in this article, along with deployment and removal procedures.

Deployment and Removal

Deployment (about 10 minutes)

REGION="ap-northeast-3"
PROFILE="your-profile"
PROJECT="apigw-vpclink-test"

# Stack 1: VPC (about 2 minutes)
aws cloudformation deploy \
  --template-file cfn/01-vpc.yaml \
  --stack-name ${PROJECT}-vpc \
  --region ${REGION} --profile ${PROFILE} \
  --no-fail-on-empty-changeset

# Stack 2: ECS Express Mode (about 5-7 minutes)
aws cloudformation deploy \
  --template-file cfn/02-ecs-express.yaml \
  --stack-name ${PROJECT}-ecs \
  --capabilities CAPABILITY_NAMED_IAM \
  --region ${REGION} --profile ${PROFILE} \
  --no-fail-on-empty-changeset

# Stack 3: API Gateway + VPC Link v2 (about 2-3 minutes)
aws cloudformation deploy \
  --template-file cfn/03-apigw-vpclink.yaml \
  --stack-name ${PROJECT}-apigw \
  --region ${REGION} --profile ${PROFILE} \
  --no-fail-on-empty-changeset

Removal (reverse order)

aws cloudformation delete-stack --stack-name ${PROJECT}-apigw --region ${REGION} --profile ${PROFILE}
aws cloudformation wait stack-delete-complete --stack-name ${PROJECT}-apigw --region ${REGION} --profile ${PROFILE}

aws cloudformation delete-stack --stack-name ${PROJECT}-ecs --region ${REGION} --profile ${PROFILE}
aws cloudformation wait stack-delete-complete --stack-name ${PROJECT}-ecs --region ${REGION} --profile ${PROFILE}

aws cloudformation delete-stack --stack-name ${PROJECT}-vpc --region ${REGION} --profile ${PROFILE}
aws cloudformation wait stack-delete-complete --stack-name ${PROJECT}-vpc --region ${REGION} --profile ${PROFILE}
Complete CloudFormation Templates

cfn/01-vpc.yaml

<details><summary>Click to expand</summary>

AWSTemplateFormatVersion: '2010-09-09'
Description: 'パブリックサブネットレスVPC with Regional NAT Gateway'

Parameters:
  ProjectName:
    Type: String
    Default: 'apigw-vpclink-test'

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: '192.168.0.0/18'
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: !Ref ProjectName

  IPv6CidrBlock:
    Type: AWS::EC2::VPCCidrBlock
    Properties:
      VpcId: !Ref VPC
      AmazonProvidedIpv6CidrBlock: true

  PrivateSubnet1:
    Type: AWS::EC2::Subnet
    DependsOn: IPv6CidrBlock
    Properties:
      VpcId: !Ref VPC
      CidrBlock: '192.168.0.0/20'
      AvailabilityZone: !Select [0, !GetAZs '']
      Ipv6CidrBlock: !Select [0, !Cidr [!Select [0, !GetAtt VPC.Ipv6CidrBlocks], 3, 64]]
      AssignIpv6AddressOnCreation: true
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-private-1a'

  PrivateSubnet2:
    Type: AWS::EC2::Subnet
    DependsOn: IPv6CidrBlock
    Properties:
      VpcId: !Ref VPC
      CidrBlock: '192.168.16.0/20'
      AvailabilityZone: !Select [1, !GetAZs '']
      Ipv6CidrBlock: !Select [1, !Cidr [!Select [0, !GetAtt VPC.Ipv6CidrBlocks], 3, 64]]
      AssignIpv6AddressOnCreation: true
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-private-1c'

  PrivateSubnet3:
    Type: AWS::EC2::Subnet
    DependsOn: IPv6CidrBlock
    Properties:
      VpcId: !Ref VPC
      CidrBlock: '192.168.32.0/20'
      AvailabilityZone: !Select [2, !GetAZs '']
      Ipv6CidrBlock: !Select [2, !Cidr [!Select [0, !GetAtt VPC.Ipv6CidrBlocks], 3, 64]]
      AssignIpv6AddressOnCreation: true
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-private-1d'

  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-igw'

  InternetGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

  EgressOnlyIGW:
    Type: AWS::EC2::EgressOnlyInternetGateway
    Properties:
      VpcId: !Ref VPC

  NATGatewayEIP:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-nat-eip'

  RegionalNATGateway:
    Type: AWS::EC2::NatGateway
    Properties:
      AvailabilityMode: regional
      ConnectivityType: public
      VpcId: !Ref VPC
      AvailabilityZoneAddresses:
        - AvailabilityZone: !Select [0, !GetAZs '']
          AllocationIds:
            - !GetAtt NATGatewayEIP.AllocationId
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-regional-nat-gw'

  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-private-rt'

  PrivateRouteIPv4:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      DestinationCidrBlock: '0.0.0.0/0'
      NatGatewayId: !Ref RegionalNATGateway

  PrivateRouteIPv6:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      DestinationIpv6CidrBlock: '::/0'
      EgressOnlyInternetGatewayId: !Ref EgressOnlyIGW

  PrivateSubnet1Association:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet1
      RouteTableId: !Ref PrivateRouteTable

  PrivateSubnet2Association:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet2
      RouteTableId: !Ref PrivateRouteTable

  PrivateSubnet3Association:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet3
      RouteTableId: !Ref PrivateRouteTable

Outputs:
  VPCId:
    Value: !Ref VPC
    Export:
      Name: !Sub '${AWS::StackName}-vpc-id'

  PrivateSubnets:
    Value: !Join [',', [!Ref PrivateSubnet1, !Ref PrivateSubnet2, !Ref PrivateSubnet3]]
    Export:
      Name: !Sub '${AWS::StackName}-private-subnets'

</details>

cfn/02-ecs-express.yaml

<details><summary>Click to expand</summary>

AWSTemplateFormatVersion: '2010-09-09'
Description: 'ECS Express Mode Service - Nginx public image'

Parameters:
  ProjectName:
    Type: String
    Default: 'apigw-vpclink-test'

  VPCStackName:
    Type: String
    Default: 'apigw-vpclink-test-vpc'

Resources:
  ECSLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub '/aws/ecs/default/${ProjectName}-service'
      RetentionInDays: 7

  ECSTaskExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub '${ProjectName}-task-execution-role'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ecs-tasks.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy

  ECSTaskRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub '${ProjectName}-task-role'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ecs-tasks.amazonaws.com
            Action: sts:AssumeRole

  ECSInfrastructureRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub '${ProjectName}-infrastructure-role'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ecs.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonECSInfrastructureRoleforExpressGatewayServices

  ExpressModeService:
    Type: AWS::ECS::ExpressGatewayService
    DependsOn: ECSLogGroup
    Properties:
      ServiceName: !Sub '${ProjectName}-service'
      Cluster: 'default'
      ExecutionRoleArn: !GetAtt ECSTaskExecutionRole.Arn
      InfrastructureRoleArn: !GetAtt ECSInfrastructureRole.Arn
      TaskRoleArn: !GetAtt ECSTaskRole.Arn
      PrimaryContainer:
        Image: 'public.ecr.aws/nginx/nginx:stable-alpine'
        ContainerPort: 80
        AwsLogsConfiguration:
          LogGroup: !Sub '/aws/ecs/default/${ProjectName}-service'
          LogStreamPrefix: 'ecs'
      NetworkConfiguration:
        Subnets:
          Fn::Split:
            - ','
            - Fn::ImportValue: !Sub '${VPCStackName}-private-subnets'
      Cpu: '256'
      Memory: '512'
      HealthCheckPath: '/'
      ScalingTarget:
        MinTaskCount: 1
        MaxTaskCount: 1
        AutoScalingMetric: 'AVERAGE_CPU'
        AutoScalingTargetValue: 70

Outputs:
  ServiceEndpoint:
    Value: !GetAtt ExpressModeService.Endpoint
    Export:
      Name: !Sub '${ProjectName}-service-endpoint'

  LoadBalancerArn:
    Value: !GetAtt ExpressModeService.ECSManagedResourceArns.IngressPath.LoadBalancerArn
    Export:
      Name: !Sub '${ProjectName}-alb-arn'

  ListenerArn:
    Value: !GetAtt ExpressModeService.ECSManagedResourceArns.IngressPath.ListenerArn
    Export:
      Name: !Sub '${ProjectName}-listener-arn'

</details>

cfn/03-apigw-vpclink.yaml

<details><summary>Click to expand</summary>

AWSTemplateFormatVersion: '2010-09-09'
Description: 'API Gateway REST API + HTTP API + VPC Link v2 → ECS Express Internal ALB'

Parameters:
  ProjectName:
    Type: String
    Default: 'apigw-vpclink-test'

  VPCStackName:
    Type: String
    Default: 'apigw-vpclink-test-vpc'

  ECSStackName:
    Type: String
    Default: 'apigw-vpclink-test-ecs'

Resources:
  # Security group for VPC Link v2
  VpcLinkSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: 'Security group for VPC Link v2'
      GroupName: !Sub '${ProjectName}-vpclink-sg'
      VpcId:
        Fn::ImportValue: !Sub '${VPCStackName}-vpc-id'
      Tags:
        - Key: Name
          Value: !Sub '${ProjectName}-vpclink-sg'

  # VPC Link v2
  VpcLinkV2:
    Type: AWS::ApiGatewayV2::VpcLink
    Properties:
      Name: !Sub '${ProjectName}-vpclink'
      SecurityGroupIds:
        - !Ref VpcLinkSecurityGroup
      SubnetIds:
        Fn::Split:
          - ','
          - Fn::ImportValue: !Sub '${VPCStackName}-private-subnets'

  # REST API
  RestApi:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Name: !Sub '${ProjectName}-api'
      Description: 'REST API with VPC Link v2 to ECS Express Internal ALB'
      EndpointConfiguration:
        Types:
          - REGIONAL

  # /{proxy+} resource
  ProxyResource:
    Type: AWS::ApiGateway::Resource
    Properties:
      RestApiId: !Ref RestApi
      ParentId: !GetAtt RestApi.RootResourceId
      PathPart: '{proxy+}'

  # Root ANY method
  RootMethod:
    Type: AWS::ApiGateway::Method
    Properties:
      RestApiId: !Ref RestApi
      ResourceId: !GetAtt RestApi.RootResourceId
      HttpMethod: ANY
      AuthorizationType: NONE
      Integration:
        Type: HTTP_PROXY
        IntegrationHttpMethod: ANY
        ConnectionType: VPC_LINK
        ConnectionId: !Ref VpcLinkV2
        IntegrationTarget:
          Fn::ImportValue: !Sub '${ProjectName}-alb-arn'
        Uri: !Sub
          - 'https://${Endpoint}/'
          - Endpoint:
              Fn::ImportValue: !Sub '${ProjectName}-service-endpoint'
        RequestParameters:
          integration.request.header.Host: !Sub
            - "'${Endpoint}'"
            - Endpoint:
                Fn::ImportValue: !Sub '${ProjectName}-service-endpoint'

  # Proxy ANY method
  ProxyMethod:
    Type: AWS::ApiGateway::Method
    Properties:
      RestApiId: !Ref RestApi
      ResourceId: !Ref ProxyResource
      HttpMethod: ANY
      AuthorizationType: NONE
      RequestParameters:
        method.request.path.proxy: true
      Integration:
        Type: HTTP_PROXY
        IntegrationHttpMethod: ANY
        ConnectionType: VPC_LINK
        ConnectionId: !Ref VpcLinkV2
        IntegrationTarget:
          Fn::ImportValue: !Sub '${ProjectName}-alb-arn'
        Uri: !Sub
          - 'https://${Endpoint}/{proxy}'
          - Endpoint:
              Fn::ImportValue: !Sub '${ProjectName}-service-endpoint'
        RequestParameters:
          integration.request.header.Host: !Sub
            - "'${Endpoint}'"
            - Endpoint:
                Fn::ImportValue: !Sub '${ProjectName}-service-endpoint'
          integration.request.path.proxy: method.request.path.proxy

  # Deployment
  Deployment:
    Type: AWS::ApiGateway::Deployment
    DependsOn:
      - RootMethod
      - ProxyMethod
    Properties:
      RestApiId: !Ref RestApi

  # Stage
  Stage:
    Type: AWS::ApiGateway::Stage
    Properties:
      RestApiId: !Ref RestApi
      DeploymentId: !Ref Deployment
      StageName: 'test'

  # ===========================================
  # HTTP API (API Gateway v2)
  # ===========================================

  HttpApi:
    Type: AWS::ApiGatewayV2::Api
    Properties:
      Name: !Sub '${ProjectName}-http-api'
      Description: 'HTTP API with VPC Link v2 to ECS Express Internal ALB'
      ProtocolType: HTTP

  # VPC Link integration
  HttpApiIntegration:
    Type: AWS::ApiGatewayV2::Integration
    Properties:
      ApiId: !Ref HttpApi
      IntegrationType: HTTP_PROXY
      IntegrationMethod: ANY
      ConnectionType: VPC_LINK
      ConnectionId: !Ref VpcLinkV2
      IntegrationUri:
        Fn::ImportValue: !Sub '${ProjectName}-listener-arn'
      RequestParameters:
        overwrite:header.Host:
          Fn::ImportValue: !Sub '${ProjectName}-service-endpoint'
      TlsConfig:
        ServerNameToVerify:
          Fn::ImportValue: !Sub '${ProjectName}-service-endpoint'
      PayloadFormatVersion: '1.0'

  # Default route
  HttpApiRoute:
    Type: AWS::ApiGatewayV2::Route
    Properties:
      ApiId: !Ref HttpApi
      RouteKey: '$default'
      Target: !Sub 'integrations/${HttpApiIntegration}'

  # Stage (auto-deploy)
  HttpApiStage:
    Type: AWS::ApiGatewayV2::Stage
    Properties:
      ApiId: !Ref HttpApi
      StageName: '$default'
      AutoDeploy: true

Outputs:
  RestApiEndpoint:
    Description: 'REST API Invoke URL'
    Value: !Sub 'https://${RestApi}.execute-api.${AWS::Region}.amazonaws.com/test'

  HttpApiEndpoint:
    Description: 'HTTP API Invoke URL'
    Value: !Sub 'https://${HttpApi}.execute-api.${AWS::Region}.amazonaws.com'

  VpcLinkId:
    Value: !Ref VpcLinkV2

  RestApiId:
    Value: !Ref RestApi

  HttpApiId:
    Value: !Ref HttpApi

  SecurityGroupId:
    Value: !Ref VpcLinkSecurityGroup

Share this article

FacebookHatena blogX

Related articles