diff --git a/private-apigw-custom-domain/README.md b/private-apigw-custom-domain/README.md
new file mode 100644
index 000000000..2978b0663
--- /dev/null
+++ b/private-apigw-custom-domain/README.md
@@ -0,0 +1,61 @@
+# Private Amazon API Gateway with private custom domain name
+
+The AWS SAM template deploys a private Amazon API Gateway with a private custom domain name mapped to deployed stage. This template also create a Amazon Route53 A-Alias record in a private hosted zone to map the private custom domain name (e.g. `private.mydomain.com`) to the target VPC Endpoint DNS name. (e.g. `vpce-abcdefgh123456789-abcd1234.execute-api.us-east-1.vpce.amazonaws.com`).
+
+Learn more about this pattern at [Serverless Land Patterns](https://serverlessland.com/patterns/private-apigw-custom-domain)
+
+## Requirements
+
+* An [AWS account](https://signin.aws.amazon.com/signup?request_type=register) with an IAM user or role that has sufficient permissions to make the necessary AWS service calls and manage AWS resources.
+* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured.
+* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git).
+* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed.
+* An [execute-api VPC Endpoint](https://docs.aws.amazon.com/vpc/latest/privatelink/interface-endpoints.html).
+* A Route 53 [Private Hosted Zone](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/hosted-zones-private.html). (*You can also use Public Hosted Zone but it is recommnded to use Private Hosted Zone to make sure that the Domain Name is only resolvable from within the VPC*)
+* An SSL/TLS certificate in [AWS Certificate Manager](https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-specify-certificate-for-custom-domain-name.html#how-to-specify-certificate-for-custom-domain-name-setup).
+
+## Deployment Instructions
+
+1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository:
+ ```bash
+ git clone https://github.com/aws-samples/serverless-patterns
+ ```
+2. Change directory to the pattern directory:
+ ```bash
+ cd serverless-patterns/private-apigw-custom-domain
+ ```
+3. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yml file:
+ ```bash
+ sam deploy --guided
+ ```
+4. During the prompts:
+ - Enter **stack name** and desired **AWS Region**.
+ - Enter **DNS Name** of the **execute-api** VPC endpoint for the VpcEndpointDNSName parameter. (e.g. vpce-abcdefgh123456789-abcd1234.execute-api.us-east-1.vpce.amazonaws.com)
+ - Enter **Hosted Zone ID** of the **execute-api** VPC endpoint for the VPCEndpointHostedZoneID parameter. (This can be found along with the DNS Name of the VPC endpoing on the AWS Console.)
+ - Enter **Private Custom Domain Name** (e.g. private.mydomain.com) for the CustomDomainName parameter.
+ - Enter **ACM Certificate ARN** from the same region as Private Amazon API Gateway for the CertificateArn parameter. The certificate must cover the Private Custom Domain name entered in the previous step.
+ - Enter **Private Hosted Zone ID** that has the domain name you would like to use for the parameter PrivateHostedZoneId.
+ - Allow SAM to create roles with the required permissions if needed.
+
+ Once you have run guided mode once, you can use `sam deploy` in future to use these defaults.
+
+1. Note the outputs from the SAM deployment process. This contain the curl command to test the Private Custom Domain Name.
+
+## Testing
+
+The stack will output the **Private Custom Domain Name**. You can use `curl` to send a HTTP request to the Private Custom Domain endpoint to test the correct mapping to your API.
+
+```bash
+curl https://{PrivateCustomDomainName}/
+```
+
+## Cleanup
+
+1. Delete the stack
+ ```bash
+ sam delete
+ ```
+1. Confirm the stack has been deleted
+ ```bash
+ aws cloudformation list-stacks --query "StackSummaries[?contains(StackName,'STACK_NAME')].StackStatus"
+ ```
diff --git a/private-apigw-custom-domain/example-pattern.json b/private-apigw-custom-domain/example-pattern.json
new file mode 100644
index 000000000..39f557a55
--- /dev/null
+++ b/private-apigw-custom-domain/example-pattern.json
@@ -0,0 +1,58 @@
+{
+ "title": "Private Amazon API Gateway with private custom domain name",
+ "description": "Create a Private API Gateway with a private custom domain name, configure access based on VPC endpoint, and set up DNS routing using Amazon Route 53 private hosted zone.",
+ "language": "",
+ "level": "200",
+ "framework": "SAM",
+ "introBox": {
+ "headline": "How it works",
+ "text": [
+ "This AWS SAM template demonstrates how to create a private Amazon API Gateway with a private custom domain mame, configure secure access based on a specific VPC endpoint, and route traffic through Route 53 in a private hosted zone.",
+ "Private custom domain name is only accessible from a VPC endpoint, which is mapped to a stage in private Amazon API Gateway. A custom domain name is configured with an SSL/TLS certificate to provide secure access, and an associated Route 53 A-Alias record ensures that traffic is routed to the API.",
+ "As prerequisites for this pattern, you must have:"
+ "* A DNS name of execute-api VPC endpoint",
+ "* A custom domain name that you would like to create (e.g. private.mydomain.com)",
+ "* A valid certificate in ACM (Amazon Certificate Manager) in the same Region as Private Amazon API Gateway, that covers the namespace of the domain you would like to use (i.e. *.mydomain.com).",
+ "* A Route 53 Private Hosted Zone ID that has the domain name you would like to use (e.g. mydomain.com).",
+ ]
+ },
+ "gitHub": {
+ "template": {
+ "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/private-apigw-custom-domain",
+ "templateURL": "serverless-patterns/private-apigw-custom-domain",
+ "projectFolder": "private-apigw-custom-domain",
+ "templateFile": "template.yaml"
+ }
+ },
+ "resources": {
+ "bullets": [
+ {
+ "text": "Custom domain names for private APIs in API Gateway",
+ "link": "https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-private-custom-domains.html"
+ }
+ ]
+ },
+ "deploy": {
+ "text": [
+ "Deploy the stack: sam deploy --guided
+ ]
+ },
+ "testing": {
+ "text": [
+ "See the GitHub repo for detailed testing instructions."
+ ]
+ },
+ "cleanup": {
+ "text": [
+ "Delete the stack: sam delete
."
+ ]
+ },
+ "authors": [
+ {
+ "name": "Usama Ali Khan",
+ "image": "https://media.licdn.com/dms/image/v2/D4E03AQHcLMpZ1LV9UQ/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1685892371158?e=1737590400&v=beta&t=RaPZkIgm7m3thW4PyKSQNn_w9fMbYBeu5PPrQ6K4vBU",
+ "bio": "Usama is a Technical Account Manager at Amazon Web Services.",
+ "linkedin": "usama-ali-khan"
+ }
+ ]
+}
diff --git a/private-apigw-custom-domain/pattern.json b/private-apigw-custom-domain/pattern.json
new file mode 100644
index 000000000..3d82ecb86
--- /dev/null
+++ b/private-apigw-custom-domain/pattern.json
@@ -0,0 +1,95 @@
+{
+ "title": "Private Amazon API Gateway with private custom domain name",
+ "description": "Create a Private API Gateway with a custom domain. Configure access via VPC endpoint and set up DNS routing with Amazon Route 53 private hosted zone.",
+ "language": "",
+ "level": "200",
+ "framework": "SAM",
+ "introBox": {
+ "headline": "How it works",
+ "text": [
+ "This AWS SAM template demonstrates how to create a private Amazon API Gateway with a private custom domain mame, configure secure access based on a specific VPC endpoint, and route traffic through Route 53 in a private hosted zone.",
+ "Private custom domain name is only accessible from a VPC endpoint, which is mapped to a stage in private Amazon API Gateway. A custom domain name is configured with an SSL/TLS certificate to provide secure access, and an associated Route 53 A-Alias record ensures that traffic is routed to the API.",
+ "As prerequisites for this pattern, you must have:",
+ "* A DNS name of execute-api VPC endpoint",
+ "* A custom domain name that you would like to create (e.g. private.mydomain.com)",
+ "* A valid certificate in ACM (Amazon Certificate Manager) in the same Region as Private Amazon API Gateway, that covers the namespace of the domain you would like to use (i.e. *.mydomain.com).",
+ "* A Route 53 Private Hosted Zone ID that has the domain name you would like to use (e.g. mydomain.com)."
+ ]
+ },
+ "gitHub": {
+ "template": {
+ "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/private-apigw-custom-domain",
+ "templateURL": "serverless-patterns/private-apigw-custom-domain",
+ "projectFolder": "private-apigw-custom-domain",
+ "templateFile": "template.yaml"
+ }
+ },
+ "resources": {
+ "bullets": [
+ {
+ "text": "Custom domain names for private APIs in API Gateway",
+ "link": "https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-private-custom-domains.html"
+ }
+ ]
+ },
+ "deploy": {
+ "text": ["Deploy the stack: sam deploy --guided
"]
+ },
+ "testing": {
+ "text": ["See the GitHub repo for detailed testing instructions."]
+ },
+ "cleanup": {
+ "text": ["Delete the stack: sam delete
."]
+ },
+ "authors": [
+ {
+ "name": "Usama Ali Khan",
+ "image": "https://media.licdn.com/dms/image/v2/D4E03AQHcLMpZ1LV9UQ/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1685892371158?e=1737590400&v=beta&t=RaPZkIgm7m3thW4PyKSQNn_w9fMbYBeu5PPrQ6K4vBU",
+ "bio": "Usama is a Technical Account Manager at Amazon Web Services.",
+ "linkedin": "usama-ali-khan"
+ }
+ ],
+ "patternArch": {
+ "icon1": {
+ "x": 20,
+ "y": 50,
+ "service": "route53",
+ "label": "Amazon Route 53"
+ },
+ "icon2": {
+ "x": 50,
+ "y": 50,
+ "service": "apigw",
+ "label": "Amazon API Gateway"
+ },
+ "icon3": {
+ "x": 80,
+ "y": 50,
+ "service": "vpc-endpoint",
+ "label": "VPC Endpoint"
+ },
+ "icon4": {
+ "x": 20,
+ "y": 58,
+ "service": "",
+ "label": "private hosted zone"
+ },
+ "icon5": {
+ "x": 50,
+ "y": 58,
+ "service": "",
+ "label": "(private)"
+ },
+
+ "line1": {
+ "from": "icon1",
+ "to": "icon2",
+ "label": ""
+ },
+ "line2": {
+ "from": "icon2",
+ "to": "icon3",
+ "label": ""
+ }
+ }
+}
diff --git a/private-apigw-custom-domain/template.yaml b/private-apigw-custom-domain/template.yaml
new file mode 100644
index 000000000..750a3b794
--- /dev/null
+++ b/private-apigw-custom-domain/template.yaml
@@ -0,0 +1,112 @@
+AWSTemplateFormatVersion: '2010-09-09'
+Transform: AWS::Serverless-2016-10-31
+Description: >
+ Serverless patterns - Amazon Private API Gateway endpoint with Private Custom Domain Name
+
+Parameters:
+ VpcEndpointDNSName:
+ Type: String
+ VPCEndpointHostedZoneID:
+ Type: String
+ CustomDomainName:
+ Type: String
+ CertificateArn:
+ Type: String
+ PrivateHostedZoneId:
+ Type: String
+
+Resources:
+ PrivateApi:
+ Type: AWS::Serverless::Api
+ Properties:
+ EndpointConfiguration: PRIVATE
+ StageName: Prod
+ AlwaysDeploy: true
+ DefinitionBody:
+ openapi: "3.0.1"
+ info:
+ version: "1.0"
+ title: !Sub "PrivateApi-${AWS::StackName}"
+ paths:
+ /:
+ get:
+ responses:
+ "200":
+ description: "200 response"
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Empty"
+ x-amazon-apigateway-integration:
+ type: "mock"
+ responses:
+ default:
+ statusCode: "200"
+ responseTemplates:
+ application/json: '{\"message\":\"Hello from Amazon Private API Gateway\"\}'
+ requestTemplates:
+ application/json: "{\"statusCode\": 200}"
+ passthroughBehavior: "when_no_match"
+ x-amazon-apigateway-policy:
+ Version: "2012-10-17"
+ Statement:
+ - Effect: "Allow"
+ Principal: "*"
+ Action: "execute-api:Invoke"
+ Resource: "execute-api:/*"
+ Condition:
+ StringEquals:
+ aws:sourceVpce: !Join ["-", [!Select [0, !Split ["-", !Select [0, !Split [".", !Ref VpcEndpointDNSName]]]], !Select [1, !Split ["-", !Select [0, !Split [".", !Ref VpcEndpointDNSName]]]]]]
+
+ PrivateDomainName:
+ Type: AWS::ApiGateway::DomainNameV2
+ Properties:
+ DomainName: !Ref CustomDomainName
+ CertificateArn: !Ref CertificateArn
+ EndpointConfiguration:
+ Types:
+ - PRIVATE
+ SecurityPolicy: TLS_1_2
+ Policy:
+ Statement:
+ - Action: 'execute-api:Invoke'
+ Effect: Allow
+ Principal: '*'
+ Resource: 'execute-api:/*'
+ - Action: 'execute-api:Invoke'
+ Condition:
+ StringNotEquals:
+ aws:SourceVpce : !Join ["-", [!Select [0, !Split ["-", !Select [0, !Split [".", !Ref VpcEndpointDNSName]]]], !Select [1, !Split ["-", !Select [0, !Split [".", !Ref VpcEndpointDNSName]]]]]]
+ Effect: Deny
+ Principal: '*'
+ Resource: 'execute-api:/*'
+ Version: 2012-10-17
+
+ BasePathMapping:
+ Type: AWS::ApiGateway::BasePathMappingV2
+ Properties:
+ RestApiId: !Ref PrivateApi
+ DomainNameArn: !GetAtt PrivateDomainName.DomainNameArn
+ Stage: !Ref PrivateApi.Stage
+
+ DomainNameAccessAssociation:
+ Type: AWS::ApiGateway::DomainNameAccessAssociation
+ Properties:
+ DomainNameArn: !GetAtt PrivateDomainName.DomainNameArn
+ AccessAssociationSource: !Join ["-", [!Select [0, !Split ["-", !Select [0, !Split [".", !Ref VpcEndpointDNSName]]]], !Select [1, !Split ["-", !Select [0, !Split [".", !Ref VpcEndpointDNSName]]]]]]
+ AccessAssociationSourceType: VPCE
+
+ R53Alias:
+ Type: AWS::Route53::RecordSet
+ Properties:
+ Name: !Ref CustomDomainName
+ HostedZoneId: !Ref PrivateHostedZoneId
+ Type: A
+ AliasTarget:
+ DNSName: !Ref VpcEndpointDNSName
+ HostedZoneId: !Ref VPCEndpointHostedZoneID
+
+Outputs:
+ CURLCommand:
+ Description: "Curl Command to test"
+ Value: !Sub "curl https://${CustomDomainName}/"
\ No newline at end of file