From 585d7ffbcf46ca5829760afa41e628a5d7aa5780 Mon Sep 17 00:00:00 2001 From: Luigi Napoleone Capasso Date: Fri, 13 Dec 2024 10:43:23 +0100 Subject: [PATCH 1/7] added templates and readme.md file --- .../README.md | 91 +++++++++++++++ .../template1.yaml | 106 ++++++++++++++++++ .../template2.yaml | 51 +++++++++ 3 files changed, 248 insertions(+) create mode 100644 private-custom-domain-name-cross-account/README.md create mode 100644 private-custom-domain-name-cross-account/template1.yaml create mode 100644 private-custom-domain-name-cross-account/template2.yaml diff --git a/private-custom-domain-name-cross-account/README.md b/private-custom-domain-name-cross-account/README.md new file mode 100644 index 000000000..c9dde1140 --- /dev/null +++ b/private-custom-domain-name-cross-account/README.md @@ -0,0 +1,91 @@ +Invoking a Private Custom Domain Name cross-account + +## Architecture Overview + +This architecture enables the invocation of a Private Custom Domain Name deployed for a Private API Gateway cross-account. The solution leverages [Amazon Private API Gateway](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-private-apis.html), [Execute-API VPC Endpoint](https://docs.aws.amazon.com/vpc/latest/privatelink/create-interface-endpoint.html), and the Private Custom Domain N [Private Custom Domain name](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-private-custom-domains.html). + +Learn more about this pattern at [Serverless Land Patterns](https://serverlessland.com/patterns/private-custom-domain-name-cross-account). + +You can update the template to add AWS resources through the same deployment process that updates your application code. + +Important: This application uses various AWS Services and there are costs associated with these services after the Free Tier Usage - please see the [AWS Pricing Page](https://aws.amazon.com/pricing/) for more details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +### Requirements + +- Two [AWS accounts](https://signin.aws.amazon.com/signup?request_type=register). IAM users or roles with sufficient permissions to make the necessary AWS service calls and manage AWS resources. +- [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) installed and configured. +- [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html) (AWS SAM) installed. +- Setup .aws/credentials [named profiles](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html) namely **accountA** and **accountB** so you can run CLI and AWS SAM commands against them. +- An [Amazon Execute-API VPC Endpoint](https://docs.aws.amazon.com/vpc/latest/privatelink/create-interface-endpoint.html) +- A [Public ACM Certificate issued](https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-request-public.html) + +### Deployment Instructions + +**Note**: Please make sure to follow the below steps in order to make sure the deployment is successful. + +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-pattern + ``` +2. Change directory to the pattern directory: + ```bash + cd aws-samples/serverless-patterns/private-custom-domain-name-cross-account + ``` + +#### AccountA + +1. In account A, where you would like to create **Private API Gateway**, along with its Private Custom Domain Name, navigate to the `accountA` directory from the main directory and deploy using *(if you are in a different directory, then run `cd ..` before entering the below command)*: + ```bash + cd accountA + + sam deploy --guided --profile accountA + +2. During the prompts: + - Enter **stack name** and desired **AWS Region**. + - Enter **the AccountB VPC Endpoint**. + - Enter **the AWS ACM Certificate ARN**. + - Enter the **Custom Domain Name** which is covered by the aforementioned certificate. + - Enter the **AccountB ID**. This will be used to share the Custom Domain Name resource with. + - Allow SAM CLI to create IAM roles with the required permissions. +3. Note the outputs from the SAM deployment process. This contains both the `Custom Domain Name` and `Custom Domain Name ARN`, which will be used as inputs for the second account's stack deployment. + + +#### AccountB +1. In account B, where you would like to create the **Custom Domain Name Access Association** and the **Private Hosted Zone**, navigate to the `accountB` directory from the main directory and deploy using *(if you are in a different directory, then run `cd ..` before entering the below command)*: + ```bash + cd accountB + + sam deploy --guided --profile accountB + ``` +2. During the prompts: + - Enter **stack name** and desired **AWS Region**. + - Enter the **VPC ID** where the Hosted Zone will be created + - Enter the **VPC Endpoint DNS Name**. It will be used to create the Alias record in the Private Hosted Zone + - Enter the **Custom Domain Name** created in the first template + - Enter the **Custom Domain Name ARN** created in the first template + - Enter the **VPC Endpoint Hosted Zone ID** + - Allow SAM CLI to create IAM roles with the required permissions. + +# How it works + +This pattern utilizes two AWS Sccounts and their respective templates. + +1. **Account A** : Hosts the Private API Gateway and the Private Custom Domain Name: + - **Amazon API Gateway (Private)**: Receives requests from the Account B via the Execute VPC Endpoint deployed in Account B + - **Custom Domain Name** which is hit by the client in Account B to invoke the API Gateway + - The **Custom Domain Name** is then shared with Account B via AWS Resource Access Manager (AWS RAM) + +2. **Account B** : Hosts the Private Hosted Zone, the Execute API VPC Endpoint and creates the Domain Name Access association. A client in the VPC where the VPC Endpoint is deployed can then send requests to the Private API Gateway in Account A using the Custom Domain Name + + +## Cleanup +To avoid incurring future charges, it's important to delete the resources in the correcct order. Follow these steps to clean up the resources created by the four templates *(Make sure to navigate to the directory containing the template before running the below commands)*: + +1. Delete Account A template + ```bash + sam delete --stack-name STACK_NAME --profile PROFILE_NAME + ``` +2. Delete Account B template + ```bash + sam delete --stack-name STACK_NAME_ACCOUNT_B --profile accountB + ``` \ No newline at end of file diff --git a/private-custom-domain-name-cross-account/template1.yaml b/private-custom-domain-name-cross-account/template1.yaml new file mode 100644 index 000000000..4f10de583 --- /dev/null +++ b/private-custom-domain-name-cross-account/template1.yaml @@ -0,0 +1,106 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + Serverless patterns - Amazon Private API Gateway endpoint with Private Custom Domain Name + +Parameters: + VpcEndpoint: + Type: String + CustomDomainName: + Type: String + CertificateArn: + Type: String + TargetAccountID: + 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: !Ref VpcEndpoint + + 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 : !Ref VpcEndpoint + 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 + + ResourceShare: + Type: "AWS::RAM::ResourceShare" + Properties: + Name: "Private Domain Name" + ResourceArns: + - !GetAtt PrivateDomainName.DomainNameArn + Principals: + - !Ref TargetAccountID + +Outputs: + CustomDomainName: + Description: "Custom Domain Name" + Value: !Ref CustomDomainName + + CustomDomainNameArn: + Description: "Custom Domain Name Arn" + Value: !GetAtt PrivateDomainName.DomainNameArn diff --git a/private-custom-domain-name-cross-account/template2.yaml b/private-custom-domain-name-cross-account/template2.yaml new file mode 100644 index 000000000..c7adf63d1 --- /dev/null +++ b/private-custom-domain-name-cross-account/template2.yaml @@ -0,0 +1,51 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + todo + +Parameters: + VPC: + Type: AWS::EC2::VPC::Id + Description: VPC where the hosted zone will be created + + VpcEndpointDNSName: + Type: String + Description: DNS name of the VPC endpoint + + CustomDomainName: + Type: String + Description: Domain name for the private hosted zone + + CustomDomainNameARN: + Type: String + Description: Domain name ARN of the Private API Gateway + + VpcEndpointHostedZoneId: + Type: String + Description: Hosted Zone ID of the VPC endpoint + +Resources: + PrivateHostedZone: + Type: AWS::Route53::HostedZone + Properties: + Name: !Ref CustomDomainName + VPCs: + - VPCId: !Ref VPC + VPCRegion: !Ref AWS::Region + + DNSRecord: + Type: AWS::Route53::RecordSet + Properties: + Name: !Ref CustomDomainName + HostedZoneId: !Ref PrivateHostedZone + Type: A + AliasTarget: + DNSName: !Ref VpcEndpointDNSName + HostedZoneId: !Ref VpcEndpointHostedZoneId + + DomainNameAccessAssociation: + Type: AWS::ApiGateway::DomainNameAccessAssociation + Properties: + DomainNameArn: !Ref CustomDomainNameARN + AccessAssociationSource: !Join ["-", [!Select [0, !Split ["-", !Select [0, !Split [".", !Ref VpcEndpointDNSName]]]], !Select [1, !Split ["-", !Select [0, !Split [".", !Ref VpcEndpointDNSName]]]]]] + AccessAssociationSourceType: VPCE \ No newline at end of file From 99779822386e2312348ff93ed584ec48623cafe3 Mon Sep 17 00:00:00 2001 From: Luigi Napoleone Capasso Date: Fri, 13 Dec 2024 10:55:07 +0100 Subject: [PATCH 2/7] edited the README.md file --- private-custom-domain-name-cross-account/README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/private-custom-domain-name-cross-account/README.md b/private-custom-domain-name-cross-account/README.md index c9dde1140..1c406b82d 100644 --- a/private-custom-domain-name-cross-account/README.md +++ b/private-custom-domain-name-cross-account/README.md @@ -49,6 +49,18 @@ Important: This application uses various AWS Services and there are costs associ - Allow SAM CLI to create IAM roles with the required permissions. 3. Note the outputs from the SAM deployment process. This contains both the `Custom Domain Name` and `Custom Domain Name ARN`, which will be used as inputs for the second account's stack deployment. +#### Accept the Resource Share Invitation +Upon deploying the first template, you can get the invitation ARN from the AWS CLI by running the following command: + ``` + aws ram get-resource-share-invitations --profile + ``` + +Copy the invitation ARN, and paste it in the following command: + ``` + aws ram accept-resource-share-invitation \ + --resource-share-invitation-arn arn:aws:ram:region:account-id:resource-share-invitation/invitation-id \ + --profile + ``` #### AccountB 1. In account B, where you would like to create the **Custom Domain Name Access Association** and the **Private Hosted Zone**, navigate to the `accountB` directory from the main directory and deploy using *(if you are in a different directory, then run `cd ..` before entering the below command)*: From a560a94f6ae1ac6bb68b40f4ed14d2a9e7296f0c Mon Sep 17 00:00:00 2001 From: Luigi Napoleone Capasso Date: Thu, 2 Jan 2025 09:39:34 +0100 Subject: [PATCH 3/7] edtited the README.md file and added the example-pattern.json file --- .../README.md | 4 +- .../{ => accountA}/template1.yaml | 0 .../{ => accountB}/template2.yaml | 0 .../example-pattern.json | 57 +++++++++++++++++++ 4 files changed, 59 insertions(+), 2 deletions(-) rename private-custom-domain-name-cross-account/{ => accountA}/template1.yaml (100%) rename private-custom-domain-name-cross-account/{ => accountB}/template2.yaml (100%) create mode 100644 private-custom-domain-name-cross-account/example-pattern.json diff --git a/private-custom-domain-name-cross-account/README.md b/private-custom-domain-name-cross-account/README.md index 1c406b82d..d7c976d87 100644 --- a/private-custom-domain-name-cross-account/README.md +++ b/private-custom-domain-name-cross-account/README.md @@ -25,11 +25,11 @@ Important: This application uses various AWS Services and there are costs associ 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-pattern + git clone https://github.com/aws-samples/serverless-patterns ``` 2. Change directory to the pattern directory: ```bash - cd aws-samples/serverless-patterns/private-custom-domain-name-cross-account + cd serverless-patterns/private-custom-domain-name-cross-account ``` #### AccountA diff --git a/private-custom-domain-name-cross-account/template1.yaml b/private-custom-domain-name-cross-account/accountA/template1.yaml similarity index 100% rename from private-custom-domain-name-cross-account/template1.yaml rename to private-custom-domain-name-cross-account/accountA/template1.yaml diff --git a/private-custom-domain-name-cross-account/template2.yaml b/private-custom-domain-name-cross-account/accountB/template2.yaml similarity index 100% rename from private-custom-domain-name-cross-account/template2.yaml rename to private-custom-domain-name-cross-account/accountB/template2.yaml diff --git a/private-custom-domain-name-cross-account/example-pattern.json b/private-custom-domain-name-cross-account/example-pattern.json new file mode 100644 index 000000000..40e79d05a --- /dev/null +++ b/private-custom-domain-name-cross-account/example-pattern.json @@ -0,0 +1,57 @@ +{ + "title": "Cross Account Private Domain Name Access", + "description": "Create ", + "level": "300", + "framework": "AWS SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This patterns enables the invocation of a Private Custom Domain Name deployed for a Private API Gateway cross-account." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/", + "templateURL": "serverless-patterns/private-custom-domain-name-cross-account", + "projectFolder": "private-custom-domain-name-cross-account", + "templateFile": "private-custom-domain-name-cross-account/accountA/template1.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "Amazon Private API Gateway", + "link": "https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-private-apis.html" + }, + { + "text": "Execute-API VPC Endpoint", + "link": "https://docs.aws.amazon.com/vpc/latest/privatelink/create-interface-endpoint.html" + }, + { + "text": "Private Custom Domain name", + "link": "https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-private-custom-domains.html" + } + ] + }, + "deploy": { + "text": [ + "sam deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: sam delete." + ] + }, + "authors": [ + { + "name": "Luigi Napoleone Capasso", + "bio": "Luigi Napoleone Capasso is a Technical Account Manager at Amazon Web Services." + } + ] + } \ No newline at end of file From ea65d433509087afe5feb33f48ca510ba2ac80b0 Mon Sep 17 00:00:00 2001 From: Luigi Napoleone Capasso Date: Wed, 8 Jan 2025 15:13:02 +0100 Subject: [PATCH 4/7] edtited the example-pattern.json file --- private-custom-domain-name-cross-account/example-pattern.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/private-custom-domain-name-cross-account/example-pattern.json b/private-custom-domain-name-cross-account/example-pattern.json index 40e79d05a..895ddfa07 100644 --- a/private-custom-domain-name-cross-account/example-pattern.json +++ b/private-custom-domain-name-cross-account/example-pattern.json @@ -2,7 +2,7 @@ "title": "Cross Account Private Domain Name Access", "description": "Create ", "level": "300", - "framework": "AWS SAM", + "framework": "SAM", "introBox": { "headline": "How it works", "text": [ @@ -11,7 +11,7 @@ }, "gitHub": { "template": { - "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/", + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/private-custom-domain-name-cross-account", "templateURL": "serverless-patterns/private-custom-domain-name-cross-account", "projectFolder": "private-custom-domain-name-cross-account", "templateFile": "private-custom-domain-name-cross-account/accountA/template1.yaml" From 1933b2f661a9915665bff79c43e31871d0d7bef9 Mon Sep 17 00:00:00 2001 From: Luigi Napoleone Capasso Date: Wed, 8 Jan 2025 15:22:01 +0100 Subject: [PATCH 5/7] edtited the example-pattern.json file --- private-custom-domain-name-cross-account/example-pattern.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/private-custom-domain-name-cross-account/example-pattern.json b/private-custom-domain-name-cross-account/example-pattern.json index 895ddfa07..6b8152703 100644 --- a/private-custom-domain-name-cross-account/example-pattern.json +++ b/private-custom-domain-name-cross-account/example-pattern.json @@ -14,7 +14,7 @@ "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/private-custom-domain-name-cross-account", "templateURL": "serverless-patterns/private-custom-domain-name-cross-account", "projectFolder": "private-custom-domain-name-cross-account", - "templateFile": "private-custom-domain-name-cross-account/accountA/template1.yaml" + "templateFile": "accountA/template1.yaml" } }, "resources": { From 3b6621e75732fd1e25d3595c8d925c3713cb8e34 Mon Sep 17 00:00:00 2001 From: Luigi Napoleone Capasso Date: Wed, 8 Jan 2025 15:22:25 +0100 Subject: [PATCH 6/7] edtited the example-pattern.json file --- private-custom-domain-name-cross-account/example-pattern.json | 1 + 1 file changed, 1 insertion(+) diff --git a/private-custom-domain-name-cross-account/example-pattern.json b/private-custom-domain-name-cross-account/example-pattern.json index 6b8152703..b819cbcb3 100644 --- a/private-custom-domain-name-cross-account/example-pattern.json +++ b/private-custom-domain-name-cross-account/example-pattern.json @@ -1,6 +1,7 @@ { "title": "Cross Account Private Domain Name Access", "description": "Create ", + "language": "Python", "level": "300", "framework": "SAM", "introBox": { From 25f619f4b86f40cd826ac4158fe248d15d8ae1ab Mon Sep 17 00:00:00 2001 From: Luigi Napoleone Capasso Date: Fri, 17 Jan 2025 09:34:47 +0100 Subject: [PATCH 7/7] edited the example-pattern-json file --- private-custom-domain-name-cross-account/example-pattern.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/private-custom-domain-name-cross-account/example-pattern.json b/private-custom-domain-name-cross-account/example-pattern.json index b819cbcb3..db9ce1222 100644 --- a/private-custom-domain-name-cross-account/example-pattern.json +++ b/private-custom-domain-name-cross-account/example-pattern.json @@ -1,13 +1,13 @@ { "title": "Cross Account Private Domain Name Access", - "description": "Create ", + "description": "Setting up Cross-Account Private API Gateway with Custom Domain", "language": "Python", "level": "300", "framework": "SAM", "introBox": { "headline": "How it works", "text": [ - "This patterns enables the invocation of a Private Custom Domain Name deployed for a Private API Gateway cross-account." + "This pattern demonstrates how to implement cross-account access to a Private API Gateway using Custom Domain Names. Learn how to configure, deploy, and invoke private APIs across AWS accounts" ] }, "gitHub": {