From 5487e6598d35cdf80406c3ea780eec9a41b89b49 Mon Sep 17 00:00:00 2001 From: usama-khan98 Date: Mon, 18 Nov 2024 15:15:58 +0000 Subject: [PATCH 01/12] added files --- multi-account-private-apigw/README.md | 189 ++++++++++++++++ .../accountA/template.yaml | 123 +++++++++++ .../accountB/template.yaml | 178 +++++++++++++++ .../accountC/src/lambda_function.py | 108 +++++++++ .../accountC/template.yaml | 89 ++++++++ .../centralAccount/src/cfnresponse.py | 54 +++++ .../centralAccount/src/lambda_function.py | 33 +++ .../centralAccount/template.yaml | 205 ++++++++++++++++++ .../example-pattern.json | 84 +++++++ .../images/architecture.png | Bin 0 -> 124583 bytes multi-account-private-apigw/template.yaml | 16 ++ multi-account-private-apigw/vpc/template.yaml | 183 ++++++++++++++++ 12 files changed, 1262 insertions(+) create mode 100644 multi-account-private-apigw/README.md create mode 100644 multi-account-private-apigw/accountA/template.yaml create mode 100644 multi-account-private-apigw/accountB/template.yaml create mode 100644 multi-account-private-apigw/accountC/src/lambda_function.py create mode 100644 multi-account-private-apigw/accountC/template.yaml create mode 100644 multi-account-private-apigw/centralAccount/src/cfnresponse.py create mode 100644 multi-account-private-apigw/centralAccount/src/lambda_function.py create mode 100644 multi-account-private-apigw/centralAccount/template.yaml create mode 100644 multi-account-private-apigw/example-pattern.json create mode 100644 multi-account-private-apigw/images/architecture.png create mode 100644 multi-account-private-apigw/template.yaml create mode 100644 multi-account-private-apigw/vpc/template.yaml diff --git a/multi-account-private-apigw/README.md b/multi-account-private-apigw/README.md new file mode 100644 index 000000000..cac14b356 --- /dev/null +++ b/multi-account-private-apigw/README.md @@ -0,0 +1,189 @@ +# Enabling East/West Communication in Multi-Account AWS Architectures with Private API Gateway + +This architecture enables secure, centralized API communications across multiple AWS accounts, facilitating private east/west communication between services. 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),[VPC links](https://aws.amazon.com/blogs/compute/understanding-vpc-links-in-amazon-api-gateway-private-integrations/), and [Network Load Balancer (NLB)](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/introduction.html) to establish a centralized API management model. + +Learn more about this pattern at [Serverless Land Patterns](https://serverlessland.com/patterns/multi-account-private-apigw). + +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 + +- Four [AWS accounts](https://signin.aws.amazon.com/signup?request_type=register). IAM users with sufficient permissions to make 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 **centralAccount**, **accountA**, **accountB** and **accountC** so you can run CLI and AWS SAM commands against them. +- An [Amazon VPC](https://docs.aws.amazon.com/vpc/latest/userguide/what-is-amazon-vpc.html) with 1 public and 2 private subnets in each account. + +### 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/multi-account-private-apigw + ``` + +#### VPC (Optional) +1. If you do not have VPCs in your accounts, navigate to the `vpc` directory using *(if you are in a different directory, then run `cd ..` before entering the below command)*: + ```bash + cd vpc + ``` +2. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yaml file using **profiles** to specify the accounts: + ```bash + sam deploy --guided --profile PROFILE_NAME + ``` +3. During the prompts: + - Enter **stack name** and desired **AWS Region**. + - Enter **custom CIDR Ranges** for `VpcCidr`, `PublicSubnetCidr` and `PrivateSubnet1Cidr` or use the **default CIDR ranges** by simply pressing `Enter` key. + - Allow SAM CLI to create IAM roles with the required permissions. + +Once you have run `sam deploy --guided --profile PROFILE_NAME` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy --profile PROFILE_NAME` in future to use these defaults. + +4. Note the outputs from the SAM deployment process. These contain the `VPCId`,`PublicSubnetId`,`PrivateSubnet1Id` and `PrivateSubnet2Id`. These will be used as inputs for other stack deployments. + +#### AccountA +1. In account A, where you would like to create **EC2** and **VPC Endpoint**, 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 **Instance type** either `t2.micro` or `t2.small` + - Enter **unique [AMI Id](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/finding-an-ami.html)** from chosen region. + - Enter **Allowed IP** from where you can SSH into the EC2 Instance. If left empty, the default CIDR range will be 0.0.0.0/0 + - Enter **current account's VPC ID** where EC2 Instance and VPC Endpoint will be launched. + - Enter **Public Subnet ID**. + - Enter **1st and 2nd Private Subnet IDs**. + - Allow SAM CLI to create IAM roles with the required permissions. +3. Note the outputs from the SAM deployment process. This contains the `VPC Endpoint ID`, which will be used as inputs for central account's stack deployment. `EC2KeyPairName` to download key material, that you can use to SSH into EC2 Instance. +4. Follow the instructions to [store the key material from AWS System Manager parameter](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/create-key-pairs.html#create-key-pair-cloudformation) into your local machine. + +#### AccountB + +1. In account B, where you would like to create **private API Gateway** with **ECS Fargate** integration, 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 **current account's VPC ID** where NLB and ECS Fargate will be created. + - Enter **1st and 2nd Private Subnet IDs**. + - Enter **Central Account's VPC ID**. This will be used in the Private API's resource policy. + - Allow SAM CLI to create IAM roles with the required permissions. +3. Note the outputs from the SAM deployment process. This contains the `API Gateway Invoke URL`, which will be used as inputs for central account's stack deployment. + +#### AccountC +1. In account C, where you would like to create **private API Gateway** with **Lambda** integration, navigate to the `accountC` 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 accountC + + sam deploy --guided --profile accountC + ``` +2. During the prompts: + - Enter **stack name** and desired **AWS Region**. + - Enter **Central Account's VPC ID**. This will be used in the Private API's resource policy. + - Enter **Bedrock Region** where you have access to **[Amazon Titan Image Generator v2](https://docs.aws.amazon.com/bedrock/latest/userguide/titan-image-models.html)** model. + - Allow SAM CLI to create IAM roles with the required permissions. +3. Note the outputs from the SAM deployment process. This contains the `API Gateway's Invoke URL`, which will be used as inputs for central account's stack deployment. + +#### Central Account +1. In Central Account, where you would like to create **private API Gateway**, navigate to the `centralAccount` directory from the main directory and deploy using (if you are in different directory, then run `cd ..` before entering the below command): + ```bash + cd centralAccount + + sam deploy --guided --profile CentralAccount + ``` +2. During the prompts: + - Enter **stack name** and desired **AWS Region**. + - Enter **current account's VPC ID** where NLB and VPC Endpoint will be created. + - Enter **1st and 2nd Private Subnet IDs**. + - Enter **Account A's VpcEndpoint ID**. This will be used in the Private Api's resource policy. + - Enter **Account B's Api Gateway URL**. e.g. `https://abcdefghij.execute-api.eu-west-1.amazonaws.com/Prod/` + - Enter **Account C's Api Gateway URL**. e.g. `https://abcdefghij.execute-api.eu-west-1.amazonaws.com/Prod/` + - Allow SAM CLI to create IAM roles with the required permissions. +3. Note the outputs from the SAM deployment process. This contains `two API Gateway's Invoke URLs`, which will be used to test from Account A. + +## How it works + +![Architecture Diagram](./images/architecture.png) + +This pattern utilizes four accounts and their respective templates. + +1. **Account A** : Acts as a client environment for testing east/west communication with the central API account. This template contains: + + - **EC2 Instance**: Serves as an API client to initiate test requests. + - **VPC Endpoint(execute-api)**: The VPC Endpoint is configured to securely route requests from the EC2 instance to the central account's Private API, ensuring traffic remains within the AWS network. + +2. **Central API Account** : Hosts the central components required to manage and route API requests securely across multiple AWS accounts. This template contains: + + - **Amazon API Gateway (Private)**: A private API Gateway serves as the entry point for API requests. + - **VPC Link(Private Link)**: Connects the API Gateway to an NLB within the Central Account's VPC, ensuring secure, private connectivity. + - **Network Load Balancer (NLB)**: Routes incoming traffic from the VPC link to Elastic Network Interfaces (ENIs), forwarding requests to the target VPC Endpoint. + - **VPC Endpoint**: The endpoint for routing/resolving incoming API requests and provides connectivity to downstream Private API Gateways in other AWS accounts (e.g., Account B and Account C). + +3. **Account B** : Hosts a service that provides a simple HTTP response from an NGINX server running on ECS Fargate. This template contains: + + - **Amazon API Gateway (Private)**: Receives requests from the Central API Account and forwards them as per configured paths and integration. + - **VPC Link**: Connects the API Gateway to an internal NLB within Account B. + - **Network Load Balancer (NLB)**: Routes traffic from the VPC link to the ECS Fargate service. + - **Elastic Container Service (ECS) Fargate**: A containerized NGINX application on ECS Fargate returns a basic HTTP response. This verifies the connectivity and functionality of the architecture. + +4. **Account C** : Hosts a Lambda function that interacts with Amazon Bedrock, generating images based on input prompts provided by the client in Account A. This template contains: + + - **Amazon API Gateway (Private)**: Receives requests from the Central API Account and forwards them as per configured paths and integration. + - **AWS Lambda**: The Lambda function processes requests from the API Gateway and invokes an Amazon Bedrock (Amazon Titan Image Generator v2) to generate images based on the input prompt, which then returns the generated image to the client in Account A. + - **Amazon Bedrock (Amazon Titan Image Generator v2)**: Bedrock generates an image using Amazon Titan Image Generator v2 based on the input. + +5. **VPC (Optional)**: Creates a VPC with CIDR Range `10.1.0.0/16` with 1 Public subnet and 2 Private subnets. This template contains: + - **1 Public Subnet**: The subnet has a direct route to an [internet gateway](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html). Resources in a public subnet can access the public internet. + - **2 Private subnets**: Resources in a private subnet use a [NAT gateway](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat.html) to access the public internet. + +## Testing +1. Once you have deployed all the Stacks, [connect to your EC2 instance using SSH](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-to-linux-instance.html) or [using EC2 Instance Connect](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-linux-inst-eic.html) in **Account A**. + +2. After connecting to the EC2 instance, run the following `curl` command from the outputs to test the **/text** path (you can add `-v` flag for verbose response): + ```bash + curl --location 'https://abcdefghij.execute-api.eu-west-1.amazonaws.com/Prod/text' + ``` +3. For **/image** path, use the following curl command (*you can update the prompt and image name as needed*): + ```bash + curl --location --request POST 'https://abcdefghij.execute-api.eu-west-1.amazonaws.com/Prod/image' \ + --data 'A bustling futuristic city at night with neon signs, towering skyscrapers, flying vehicles, and busy street life, in the rain. Detailed and atmospheric.' --output image.jpg + ``` + + +## 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 correct directory before running the below commands)*: + +1. Delete Account B template + ```bash + sam delete --stack-name STACK_NAME_ACCOUNT_B --profile accountB + ``` +2. Delete Account C template + ```bash + sam delete --stack-name STACK_NAME_ACCOUNT_C --profile accountC + ``` +3. Delete Central Account template + ```bash + sam delete --stack-name STACK_NAME_CENTRAL_ACCOUNT --profile centralAccount + ``` +4. Delete Account A template + ```bash + sam delete --stack-name STACK_NAME_ACCOUNT_A --profile accountA + ``` + +---- +Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 \ No newline at end of file diff --git a/multi-account-private-apigw/accountA/template.yaml b/multi-account-private-apigw/accountA/template.yaml new file mode 100644 index 000000000..ae3cb0c7d --- /dev/null +++ b/multi-account-private-apigw/accountA/template.yaml @@ -0,0 +1,123 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + accountA + Sample SAM Template for accountA containing EC2 Instance and execute-api VPC Endpoint + +Parameters: + ########### Parameters for EC2 Instance and Network ########### + InstanceType: + Description: Enter t2.micro or t2.small. Default is t2.micro. + Type: String + AllowedValues: + - t2.micro + - t2.small + Default: t2.micro + ConstraintDescription: Must be either t2.micro or t2.small. + AmiID: + Description: Please provide a valid AMI id to launch EC2 Instance. + Type: AWS::EC2::Image::Id + AllowedIP: + Description: CIDR block to allow SSH access (e.g., 203.0.113.0/24). + Type: String + Default: 0.0.0.0/0 + VPCId: + Description: Please provide a VPC to deploy the solution into. + Type: AWS::EC2::VPC::Id + PublicSubnet: + Description: Please provide the public subnet id to launch EC2 Instance + Type: AWS::EC2::Subnet::Id + PrivateSubnet1: + Description: Please provide the first private subnet id to create Interface VPC Endpoint. + Type: AWS::EC2::Subnet::Id + PrivateSubnet2: + Description: Please provide the second private subnet id to create Interface VPC Endpoint. + Type: AWS::EC2::Subnet::Id + +Resources: + ########### EC2 Client Instance ########### + ClientInstance: + Type: AWS::EC2::Instance + Properties: + ImageId: !Ref AmiID + InstanceType: !Ref InstanceType + SubnetId: !Ref PublicSubnet + KeyName: !Ref NewKeyPair + SecurityGroupIds: + - !GetAtt ClientEC2SecurityGroup.GroupId + Tags: + - Key: Name + Value: !Join ['-', [!Ref InstanceType, "Client"]] + - Key: InstanceType + Value: !Ref InstanceType + + ########### EC2 Key Pair ########### + NewKeyPair: + Type: AWS::EC2::KeyPair + Properties: + KeyName: !Sub ${AWS::StackName}-${AWS::Region}-MyKeyPair + + ########### API Gateway VPC Endpoint ########### + ApiGatewayVpcEndpoint: + Type: AWS::EC2::VPCEndpoint + Properties: + ServiceName: !Sub "com.amazonaws.${AWS::Region}.execute-api" + VpcId: !Ref VPCId + SubnetIds: + - !Ref PrivateSubnet1 + - !Ref PrivateSubnet2 + VpcEndpointType: Interface + PrivateDnsEnabled: true + SecurityGroupIds: + - !Ref VpcEndpointSG + + ########### Security Group for Client EC2 ########### + ClientEC2SecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Allow SSH inbound traffic + VpcId: !Ref VPCId + SecurityGroupIngress: + - IpProtocol: tcp + FromPort: 22 + ToPort: 22 + CidrIp: !Ref AllowedIP + + ########### Security Group for VPC Endpoint ########### + VpcEndpointSG: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Security Group for NLB and VPC endpoint + VpcId: !Ref VPCId + + ########### Self-Allow Rule for VPC Endpoint Security Group ########### + VpcEndpointSGingressSelfAllow: + Type: AWS::EC2::SecurityGroupIngress + Properties: + GroupId: !Ref VpcEndpointSG + IpProtocol: tcp + FromPort: 0 + ToPort: 65535 + SourceSecurityGroupId: !GetAtt VpcEndpointSG.GroupId + + ########### Allow Client EC2 to Access VPC Endpoint ########### + VpcEndpointSGingressEC2Client: + Type: AWS::EC2::SecurityGroupIngress + Properties: + GroupId: !Ref VpcEndpointSG + IpProtocol: tcp + FromPort: 0 + ToPort: 65535 + SourceSecurityGroupId: !GetAtt ClientEC2SecurityGroup.GroupId + +Outputs: + ########### Outputs ########### + VPCEndpointID: + Value: !Ref ApiGatewayVpcEndpoint + Description: The ID of the VPC endpoint for API Gateway + EC2KeyPairName: + Value: !Ref NewKeyPair + Description: Name of Newly created EC2 Key Pair, that you can use to SSH into the EC2 Instance + EC2PublicIP: + Value: !GetAtt ClientInstance.PublicIp + Description: Public IP address of the EC2 instance. diff --git a/multi-account-private-apigw/accountB/template.yaml b/multi-account-private-apigw/accountB/template.yaml new file mode 100644 index 000000000..9261b6208 --- /dev/null +++ b/multi-account-private-apigw/accountB/template.yaml @@ -0,0 +1,178 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + accountB + Sample SAM Template for accountB containing Private API Gateway, VPC Link, Network Load Balancer, and ECS Cluster with Fargate. + +Parameters: + ########### Parameters ########### + VPCId: + Type: AWS::EC2::VPC::Id + Description: The VPC ID where the ECS service will be deployed. + PrivateSubnet1: + Type: AWS::EC2::Subnet::Id + Description: The first private subnet ID within the VPC. + PrivateSubnet2: + Type: AWS::EC2::Subnet::Id + Description: The second private subnet ID within the VPC. + CentralAccountVpcID: + Type: String + Description: The ID of the VPC from the Central Account + +Resources: + ########### Private API Gateway ########### + PrivateApi: + Type: AWS::Serverless::Api + Properties: + EndpointConfiguration: PRIVATE + StageName: Prod + AlwaysDeploy: true + DefinitionBody: + openapi: "3.0.1" + info: + title: "PrivateApi-${AWS::StackName}" + version: "1.0" + paths: + /: + get: + responses: + "200": + description: "200 response" + x-amazon-apigateway-integration: + connectionId: !Ref ApiGatewayVpcLink + httpMethod: GET + uri: !Sub http://${NLB.DNSName}/ + connectionType: VPC_LINK + passthroughBehavior: when_no_match + type: http_proxy + x-amazon-apigateway-policy: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Principal: "*" + Action: "execute-api:Invoke" + Resource: "execute-api:/*" + Condition: + StringEquals: + aws:sourceVpc: !Ref CentralAccountVpcID + + ########### API Gateway VPC Link ########### + ApiGatewayVpcLink: + Type: AWS::ApiGateway::VpcLink + Properties: + Name: VPCLinkRestNlbInternal + TargetArns: + - !Ref NLB + + ########### Network Load Balancer (NLB) ########### + NLB: + Type: AWS::ElasticLoadBalancingV2::LoadBalancer + Properties: + Name: PrivateNLB + Scheme: internal + Subnets: + - Ref: PrivateSubnet1 + - Ref: PrivateSubnet2 + Type: network + EnforceSecurityGroupInboundRulesOnPrivateLinkTraffic: "off" + SecurityGroups: + - !Ref NLBSecurityGroup + + ########### NLB Listener ########### + NLBListener: + Type: AWS::ElasticLoadBalancingV2::Listener + Properties: + DefaultActions: + - Type: forward + TargetGroupArn: !Ref NLBTG + LoadBalancerArn: !Ref NLB + Port: 80 + Protocol: TCP + + ########### NLB Target Group ########### + NLBTG: + Type: AWS::ElasticLoadBalancingV2::TargetGroup + Properties: + Name: FargateNLBTargetGroup + Port: 80 + Protocol: TCP + VpcId: !Ref VPCId + TargetType: ip + HealthCheckProtocol: HTTP + HealthCheckPort: '80' + HealthCheckPath: '/' + Matcher: + HttpCode: 200 + + ########### ECS Cluster ########### + ECSCluster: + Type: AWS::ECS::Cluster + Properties: + ClusterName: FargateCluster + + ########### ECS Fargate Task Definition ########### + ECSFargateTaskDefinition: + Type: AWS::ECS::TaskDefinition + Properties: + Family: FargateTask + Cpu: 256 + Memory: 512 + NetworkMode: awsvpc + RequiresCompatibilities: + - FARGATE + ContainerDefinitions: + - Name: nginx + Image: public.ecr.aws/nginx/nginx:latest + Essential: true + PortMappings: + - ContainerPort: 80 + Protocol: tcp + + ########### ECS Fargate Service ########### + ECSFargateService: + Type: AWS::ECS::Service + DependsOn: NLBListener + Properties: + Cluster: !Ref ECSCluster + TaskDefinition: !Ref ECSFargateTaskDefinition + DesiredCount: 1 + LaunchType: FARGATE + NetworkConfiguration: + AwsvpcConfiguration: + AssignPublicIp: DISABLED + Subnets: + - Ref: PrivateSubnet1 + - Ref: PrivateSubnet2 + SecurityGroups: + - !Ref ECSSecurityGroup + LoadBalancers: + - ContainerName: nginx + ContainerPort: 80 + TargetGroupArn: !Ref NLBTG + + ########### ECS Security Group ########### + ECSSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Security group for ECS Fargate Service + VpcId: !Ref VPCId + SecurityGroupIngress: + - IpProtocol: tcp + FromPort: 80 + ToPort: 80 + SourceSecurityGroupId: !Ref NLBSecurityGroup + + ########### NLB Security Group ########### + NLBSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Security group for the NLB + VpcId: !Ref VPCId + +Outputs: + ########### Outputs ########### + ApiUrl: + Description: "API Gateway Invoke URL" + Value: !Sub "https://${PrivateApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/" + + diff --git a/multi-account-private-apigw/accountC/src/lambda_function.py b/multi-account-private-apigw/accountC/src/lambda_function.py new file mode 100644 index 000000000..f20a23434 --- /dev/null +++ b/multi-account-private-apigw/accountC/src/lambda_function.py @@ -0,0 +1,108 @@ +import json +import boto3 +import os +import logging +from botocore.exceptions import ClientError +import base64 + +logger = logging.getLogger() +logger.setLevel("INFO") + +def lambda_handler(event, context): + try: + + body = event.get('body') + + if not body: + return { + 'statusCode': 400, + "headers": { + "Content-Type": "application/json", + }, + "isBase64Encoded": False, + 'body': json.dumps({"message": "Empty body!! Please pass a prompt in the request body."}) + } + try: + prompt = base64.b64decode(body).decode('utf-8') + logger.info("Body was Base64-encoded. Decoded successfully.") + except (base64.binascii.Error, UnicodeDecodeError): + prompt = body + logger.info("Body is a plain string. No decoding needed.") + + image_base64 = generate_image(prompt=prompt) + + return { + 'statusCode': 200, + "headers": { + "Content-Type": "image/jpeg", + }, + "isBase64Encoded": True, + 'body': image_base64 + } + except Exception as e: + logger.error("Failed to generate image: %s", e) + return { + "statusCode": 500, + "headers": { + "Content-Type": "application/json", + }, + "isBase64Encoded": False, + "body": json.dumps({"message": "Oops! Something went wrong. Please try again later."}) + } + +def generate_image(prompt): + """ + Generates an image using Amazon Titan Image Generator G1 model. + + Args: + prompt (str): The text prompt for generating the image. + + Returns: + str: Base64-encoded image data. + + Raises: + ValueError: If the image generation fails. + """ + region= os.environ.get('region') + bedrock = boto3.client(service_name='bedrock-runtime', region_name=region) + + model_id = 'amazon.titan-image-generator-v2:0' + body = json.dumps({ + "taskType": "TEXT_IMAGE", + "textToImageParams": { + "text": prompt + }, + "imageGenerationConfig": { + "numberOfImages": 1, + "height": 1024, + "width": 1024, + "cfgScale": 8.0, + "seed": 0 + } + }) + + try: + logger.info("Generating image with model %s", model_id) + response = bedrock.invoke_model( + body=body, + modelId=model_id, + accept="application/json", + contentType="application/json" + ) + response_body = json.loads(response['body'].read()) + + # Check for error in response + if "error" in response_body: + raise ValueError(f"Image generation failed: {response_body['error']}") + + base64_image = response_body["images"][0] + logger.info("Image generated successfully with model %s", model_id) + + return base64_image # Base64 string ready for direct return + + except ClientError as e: + logger.error("AWS ClientError: %s", e) + raise ValueError("Error invoking image generation model") + except ValueError as e: + logger.error("Image generation error: %s", e) + raise diff --git a/multi-account-private-apigw/accountC/template.yaml b/multi-account-private-apigw/accountC/template.yaml new file mode 100644 index 000000000..bccbf800d --- /dev/null +++ b/multi-account-private-apigw/accountC/template.yaml @@ -0,0 +1,89 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + accountC + SAM Template for accountC containing Private API Gateway, with lambda integration. + +Parameters: + ########### Parameters ########### + CentralAccountVpcID: + Type: String + Description: The ID of the VPC Endpoint you want to use from the Central Account + BedrockRegion: + Type: String + Default: us-east-1 + Description: Region where Amazon Titan Image Generator G1 model is enabled. + +Resources: + ########### Private API Gateway ########### + 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}" + x-amazon-apigateway-binary-media-types: + - "*/*" + paths: + /: + post: + responses: + "200": + description: "200 response" + x-amazon-apigateway-integration: + httpMethod: "POST" + uri: + Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaFunction.Arn}/invocations + passthroughBehavior: "when_no_match" + type: "aws_proxy" + x-amazon-apigateway-policy: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Principal: "*" + Action: "execute-api:Invoke" + Resource: "execute-api:/*" + Condition: + StringEquals: + aws:sourceVpc: !Ref CentralAccountVpcID + + ########### Lambda Function ########### + LambdaFunction: + Type: AWS::Serverless::Function + Properties: + FunctionName: !Sub Bedrock-Lambda-${AWS::StackName} + CodeUri: src/ + Handler: lambda_function.lambda_handler + Runtime: python3.12 + Timeout: 29 + MemorySize: 256 + Architectures: + - arm64 + Environment: + Variables: + region: !Ref BedrockRegion + Events: + APIRoot: + Type: Api + Properties: + Path: / + Method: ANY + RestApiId: !Ref PrivateApi + Policies: + - Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - bedrock:InvokeModel + Resource: !Sub arn:aws:bedrock:${BedrockRegion}::foundation-model/amazon.titan-image-generator-v2:0 + +Outputs: + ########### Outputs ########### + ApiUrl: + Description: "API Gateway Invoke URL" + Value: !Sub https://${PrivateApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/ diff --git a/multi-account-private-apigw/centralAccount/src/cfnresponse.py b/multi-account-private-apigw/centralAccount/src/cfnresponse.py new file mode 100644 index 000000000..cd9e00e98 --- /dev/null +++ b/multi-account-private-apigw/centralAccount/src/cfnresponse.py @@ -0,0 +1,54 @@ +# Copyright 2016 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. +# This file is licensed to you under the AWS Customer Agreement (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at http://aws.amazon.com/agreement/ . +# This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. +# See the License for the specific language governing permissions and limitations under the License. + +from __future__ import print_function +import urllib3 +import json +import re + +SUCCESS = "SUCCESS" +FAILED = "FAILED" + +http = urllib3.PoolManager() + + +def send(event, context, responseStatus, responseData, physicalResourceId=None, noEcho=False, reason=None): + responseUrl = event['ResponseURL'] + + responseBody = { + 'Status' : responseStatus, + 'Reason' : reason or "See the details in CloudWatch Log Stream: {}".format(context.log_stream_name), + 'PhysicalResourceId' : physicalResourceId or context.log_stream_name, + 'StackId' : event['StackId'], + 'RequestId' : event['RequestId'], + 'LogicalResourceId' : event['LogicalResourceId'], + 'NoEcho' : noEcho, + 'Data' : responseData + } + + json_responseBody = json.dumps(responseBody) + + print("Response body:") + print(json_responseBody) + + headers = { + 'content-type' : '', + 'content-length' : str(len(json_responseBody)) + } + + try: + response = http.request('PUT', responseUrl, headers=headers, body=json_responseBody) + print("Status code:", response.status) + + + except Exception as e: + + print("send(..) failed executing http.request(..):", mask_credentials_and_signature(e)) + +def mask_credentials_and_signature(message): + message = re.sub(r'X-Amz-Credential=[^&\s]+', 'X-Amz-Credential=*****', message, flags=re.IGNORECASE) + return re.sub(r'X-Amz-Signature=[^&\s]+', 'X-Amz-Signature=*****', message, flags=re.IGNORECASE) diff --git a/multi-account-private-apigw/centralAccount/src/lambda_function.py b/multi-account-private-apigw/centralAccount/src/lambda_function.py new file mode 100644 index 000000000..a423a8d31 --- /dev/null +++ b/multi-account-private-apigw/centralAccount/src/lambda_function.py @@ -0,0 +1,33 @@ +import cfnresponse +import json +import boto3 + +def lambda_handler(event, context): + print('REQUEST RECEIVED:\n' + json.dumps(event)) + responseData = {} + + # Handle Delete requests + if event['RequestType'] == 'Delete': + cfnresponse.send(event, context, cfnresponse.SUCCESS, {}) + return + + # Handle Create or Update requests + if event['RequestType'] == 'Create' or event['RequestType'] == 'Update': + try: + ec2 = boto3.resource('ec2') + enis = event['ResourceProperties']['NetworkInterfaceIds'] + + # Fetch private IPs of the network interfaces + for index, eni in enumerate(enis): + network_interface = ec2.NetworkInterface(eni) + responseData[f'IP{index}'] = network_interface.private_ip_address + print(responseData) + + # Send success response back to CloudFormation + cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData) + + except Exception as e: + # Send failure response to CloudFormation with the error message + responseData = {'error': str(e)} + cfnresponse.send(event, context, cfnresponse.FAILED, responseData) + return diff --git a/multi-account-private-apigw/centralAccount/template.yaml b/multi-account-private-apigw/centralAccount/template.yaml new file mode 100644 index 000000000..c909dd110 --- /dev/null +++ b/multi-account-private-apigw/centralAccount/template.yaml @@ -0,0 +1,205 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + centralAccount + Sample SAM Template for central Account containing Private API Gateway, VPC link, Network Load Balancer and execute-api VPC Endpoint. + +Parameters: + VPCId: + Description: Please provide a VPC to deploy the solution into. + Type: AWS::EC2::VPC::Id + PrivateSubnet1: + Description: Please provide the first private subnet id with outbound connectivity within the VPC you selected above. + Type: AWS::EC2::Subnet::Id + PrivateSubnet2: + Description: Please provide the second private subnet id with outbound connectivity within the VPC you selected above. + Type: AWS::EC2::Subnet::Id + AccountAVpcEndpoint: + Type: String + Description: The ID of the VPC Endpoint you want to use from Account A + AccountBApiGwURL: + Type: String + Description: The API GW URL from Account B including path + AccountCApiGwURL: + Type: String + Description: The API GW URL from Account B including path + +Resources: + ########### API Gateway REST API ############### + MyApi: + Type: AWS::Serverless::Api + Properties: + EndpointConfiguration: PRIVATE + StageName: Prod + AlwaysDeploy: true + DefinitionBody: + openapi: "3.0.1" + info: + title: !Sub "PrivateApi-${AWS::StackName}" + version: "1.0" + x-amazon-apigateway-binary-media-types: + - "*/*" + servers: + - x-amazon-apigateway-endpoint-configuration: + vpcEndpointIds: + - !Ref AccountAVpcEndpoint + paths: + /text: + get: + responses: + "200": + description: "200 response" + x-amazon-apigateway-integration: + connectionId: !Ref ApiGatewayVpcLink + httpMethod: "GET" + uri: !Ref AccountBApiGwURL + connectionType: "VPC_LINK" + passthroughBehavior: "when_no_templates" + type: "http" + responses: + default: + statusCode: "200" + /image: + post: + responses: + "200": + description: "200 response" + x-amazon-apigateway-integration: + connectionId: !Ref ApiGatewayVpcLink + httpMethod: "POST" + uri: !Ref AccountCApiGwURL + connectionType: "VPC_LINK" + passthroughBehavior: "when_no_templates" + type: "http" + responses: + default: + statusCode: "200" + x-amazon-apigateway-policy: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Principal: "*" + Action: "execute-api:Invoke" + Resource: "execute-api:/*" + Condition: + StringEquals: + aws:sourceVpce: + - !Ref AccountAVpcEndpoint + + ########### API Gateway VPC Link ############### + ApiGatewayVpcLink: + Type: AWS::ApiGateway::VpcLink + Properties: + Name: VPCLinkRestNlbInternal + TargetArns: + - !Ref MyNLB + + ########### Network Load Balancer (NLB) ############### + MyNLB: + Type: AWS::ElasticLoadBalancingV2::LoadBalancer + Properties: + Type: network + Subnets: + - !Ref PrivateSubnet1 + - !Ref PrivateSubnet2 + SecurityGroups: + - !Ref NLBVpcEndpointSG + EnforceSecurityGroupInboundRulesOnPrivateLinkTraffic: "off" + + ########### NLB Target Group ############### + NLBTargetGroup: + Type: AWS::ElasticLoadBalancingV2::TargetGroup + Properties: + VpcId: !Ref VPCId + Protocol: TCP + Port: 443 + TargetType: ip + Targets: + - Id: !GetAtt GetPrivateIPs.IP0 + - Id: !GetAtt GetPrivateIPs.IP1 + HealthCheckProtocol: TCP + + ########### NLB Listener ############### + NLBListener: + Type: AWS::ElasticLoadBalancingV2::Listener + Properties: + LoadBalancerArn: !Ref MyNLB + Port: 443 + Protocol: TCP + DefaultActions: + - Type: forward + TargetGroupArn: !Ref NLBTargetGroup + + ########### Execute API VPC Endpoint ############### + ExecuteApiVpcEndpoint: + DependsOn: NLBVpcEndpointSG + Type: AWS::EC2::VPCEndpoint + Properties: + ServiceName: !Sub com.amazonaws.${AWS::Region}.execute-api + VpcId: !Ref VPCId + SubnetIds: + - !Ref PrivateSubnet1 + - !Ref PrivateSubnet2 + VpcEndpointType: Interface + PrivateDnsEnabled: true + SecurityGroupIds: + - !Ref NLBVpcEndpointSG + + ########### Security Group for NLB and VPC Endpoint ############### + NLBVpcEndpointSG: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Security Group for NLB and VPC endpoint + VpcId: !Ref VPCId + SecurityGroupEgress: + - IpProtocol: -1 + CidrIp: 0.0.0.0/0 + + ########### Security Group Ingress Rule ############### + NLBVpcEndpointSGingress: + Type: AWS::EC2::SecurityGroupIngress + Properties: + GroupId: + Ref: NLBVpcEndpointSG + IpProtocol: tcp + FromPort: '0' + ToPort: '65535' + SourceSecurityGroupId: + Ref: NLBVpcEndpointSG + + ########### Lambda Function to Fetch VPC Endpoint IPs ############### + FetchVPCEndpointIPsFunction: + Type: AWS::Serverless::Function + Properties: + Handler: index.lambda_handler + Runtime: python3.12 + Timeout: 10 + Architectures: + - arm64 + CodeUri: src/ + Handler: lambda_function.lambda_handler + Policies: + - AWSLambdaExecute + - Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - ec2:DescribeNetworkInterfaces + Resource: "*" + + ########### Custom Resource to Fetch Private IPs ############### + GetPrivateIPs: + DependsOn: + - ExecuteApiVpcEndpoint + Type: Custom::NLBTargets + Properties: + ServiceToken: !GetAtt FetchVPCEndpointIPsFunction.Arn + NetworkInterfaceIds: !GetAtt ExecuteApiVpcEndpoint.NetworkInterfaceIds + +Outputs: + ApiUrlForECS: + Description: "API Gateway Invoke URL with GET method for ECS Fargate integration" + Value: !Sub "https://${MyApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/${MyApi.Stage}/text" + ApiUrlForLambda: + Description: "API Gateway Invoke URL with POST method for Lambda function integration" + Value: !Sub "https://${MyApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/${MyApi.Stage}/image" diff --git a/multi-account-private-apigw/example-pattern.json b/multi-account-private-apigw/example-pattern.json new file mode 100644 index 000000000..1a2ca05bb --- /dev/null +++ b/multi-account-private-apigw/example-pattern.json @@ -0,0 +1,84 @@ +{ + "title": "Enabling East/West Communication in Multi-Account AWS Architectures with Private API Gateway", + "description": "Create Private REST API Gateway in multiple accounts and integrate with the central account", + "language": "Python", + "level": "300", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This sample project demonstrates how to enable secure, centralized API communications across multiple AWS accounts using a private Amazon API Gateway. It facilitates east/west communication between services while keeping traffic within the AWS network.", + "The architecture utilizes key AWS services such as Amazon API Gateway (Private), VPC links, Network Load Balancers (NLBs), and Execute-API VPC Endpoints. These services work together to securely route requests between multiple AWS accounts and their respective private APIs.", + "This pattern deploys four separate AWS accounts: a client account with an EC2 instance and VPC Endpoint for testing, a central account hosting the main API Gateway and routing components, an account with an ECS Fargate service behind a private API Gateway, and another account with a Lambda function integrated with Amazon Bedrock for image generation. Each account contains its own AWS resources to ensure proper communication and isolation." + ] + } + , + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/sfn-athena-cdk-python", + "templateURL": "serverless-patterns/multi-account-private-apigw", + "projectFolder": "multi-account-private-apigw", + "templateFile": "multi-account-private-apigw/centralAccount/template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "Amazon API Gateway (Private)", + "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": "VPC Links", + "link": "https://docs.aws.amazon.com/vpc/latest/userguide/endpoint-services-overview.html" + }, + { + "text": "Network Load Balancer (NLB)", + "link": "https://docs.aws.amazon.com/elasticloadbalancing/latest/network/introduction.html" + }, + { + "text": "Amazon ECS Fargate", + "link": "https://docs.aws.amazon.com/AmazonECS/latest/developerguide/AWS_Fargate.html" + }, + { + "text": "AWS Lambda", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/welcome.html" + }, + { + "text": "Amazon Bedrock", + "link": "https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html" + }, + { + "text": "Amazon EC2", + "link": "https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/concepts.html" + } + ] + } +, + "deploy": { + "text": [ + "sam deploy --guided --profile PROFILE_NAME" + ] + }, + "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": "https://www.linkedin.com/in/usama-ali-khan/" + } + ] +} diff --git a/multi-account-private-apigw/images/architecture.png b/multi-account-private-apigw/images/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..55e512238751644372d87bfaafb03f207e114e4c GIT binary patch literal 124583 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 Zt1&8r + AWS SAM template for creating a VPC with 1 Public subnet and 2 Private subnets, with optional user-defined CIDR ranges + +Parameters: + # ########### Parameter for VPC CIDR ########### + VpcCidr: + Type: String + Default: 10.1.0.0/16 + Description: CIDR block for the VPC (e.g., 10.1.0.0/16) + AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$' + + # ########### Parameter for Public Subnet CIDR ########### + PublicSubnetCidr: + Type: String + Default: 10.1.10.0/24 + Description: CIDR block for the Public Subnet (e.g., 10.1.10.0/24) + AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$' + + # ########### Parameter for Private Subnet 1 CIDR ########### + PrivateSubnet1Cidr: + Type: String + Default: 10.1.20.0/24 + Description: CIDR block for the first Private Subnet (e.g., 10.1.20.0/24) + AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$' + + # ########### Parameter for Private Subnet 2 CIDR ########### + PrivateSubnet2Cidr: + Type: String + Default: 10.1.30.0/24 + Description: CIDR block for the second Private Subnet (e.g., 10.1.30.0/24) + AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$' + +Resources: + # ########### VPC ########### + VPC: + Type: AWS::EC2::VPC + Properties: + CidrBlock: !Ref VpcCidr + EnableDnsSupport: true + EnableDnsHostnames: true + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-VPC + + # ########### Public Subnet ########### + PublicSubnet: + Type: AWS::EC2::Subnet + Properties: + VpcId: !Ref VPC + CidrBlock: !Ref PublicSubnetCidr + MapPublicIpOnLaunch: true + AvailabilityZone: !Select [ 0, !GetAZs ] + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-Public + + # ########### Private Subnet 1 ########### + PrivateSubnet1: + Type: AWS::EC2::Subnet + Properties: + VpcId: !Ref VPC + CidrBlock: !Ref PrivateSubnet1Cidr + AvailabilityZone: !Select [ 0, !GetAZs ] + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-Private-A + + # ########### Private Subnet 2 ########### + PrivateSubnet2: + Type: AWS::EC2::Subnet + Properties: + VpcId: !Ref VPC + CidrBlock: !Ref PrivateSubnet2Cidr + AvailabilityZone: !Select [ 1, !GetAZs ] + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-Private-B + + # ########### Internet Gateway ########### + InternetGateway: + Type: AWS::EC2::InternetGateway + Properties: + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-IGW + + # ########### Attach Gateway to VPC ########### + AttachGateway: + Type: AWS::EC2::VPCGatewayAttachment + Properties: + VpcId: !Ref VPC + InternetGatewayId: !Ref InternetGateway + + # ########### NAT Gateway Elastic IP ########### + NatGatewayEIP: + Type: AWS::EC2::EIP + Properties: + Domain: vpc + + # ########### NAT Gateway ########### + NatGateway: + Type: AWS::EC2::NatGateway + Properties: + AllocationId: !GetAtt NatGatewayEIP.AllocationId + SubnetId: !Ref PublicSubnet + + # ########### Public Route Table ########### + PublicRouteTable: + Type: AWS::EC2::RouteTable + Properties: + VpcId: !Ref VPC + Tags: + - Key: Name + Value: PublicRT + + # ########### Public Route ########### + PublicRoute: + Type: AWS::EC2::Route + Properties: + RouteTableId: !Ref PublicRouteTable + DestinationCidrBlock: 0.0.0.0/0 + GatewayId: !Ref InternetGateway + + # ########### Associate Public Subnet with Route Table ########### + PublicSubnetRouteTableAssociation: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + SubnetId: !Ref PublicSubnet + RouteTableId: !Ref PublicRouteTable + + # ########### Private Route ########### + PrivateRoute: + Type: AWS::EC2::Route + Properties: + RouteTableId: !Ref PrivateRouteTable + DestinationCidrBlock: 0.0.0.0/0 + NatGatewayId: !Ref NatGateway + + # ########### Private Route Table ########### + PrivateRouteTable: + Type: AWS::EC2::RouteTable + Properties: + VpcId: !Ref VPC + Tags: + - Key: Name + Value: PrivateRT-A + + # ########### Associate Private Subnet 1 with Route Table ########### + PrivateSubnet1RouteTableAssociation: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + SubnetId: !Ref PrivateSubnet1 + RouteTableId: !Ref PrivateRouteTable + + # ########### Associate Private Subnet 2 with Route Table ########### + PrivateSubnet2RouteTableAssociation: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + SubnetId: !Ref PrivateSubnet2 + RouteTableId: !Ref PrivateRouteTable + +Outputs: + # ########### VPC Output ########### + VPCId: + Description: VPC ID + Value: !Ref VPC + + # ########### Public Subnet Output ########### + PublicSubnetId: + Description: Public Subnet ID + Value: !Ref PublicSubnet + + # ########### Private Subnet 1 Output ########### + PrivateSubnet1Id: + Description: Private Subnet 1 ID + Value: !Ref PrivateSubnet1 + + # ########### Private Subnet 2 Output ########### + PrivateSubnet2Id: + Description: Private Subnet 2 ID + Value: !Ref PrivateSubnet2 From 0b4565ef717b50605251be0fda3f4f4f74c7690f Mon Sep 17 00:00:00 2001 From: usama-khan98 Date: Fri, 22 Nov 2024 16:59:03 +0000 Subject: [PATCH 02/12] simplified pattern, removed accountA and bedrock integration --- multi-account-private-apigw/README.md | 92 +++---- .../accountA/.gitignore | 244 ++++++++++++++++++ .../accountA/template.yaml | 236 ++++++++++------- .../accountB/.gitignore | 244 ++++++++++++++++++ .../accountB/template.yaml | 195 ++++---------- .../accountC/src/lambda_function.py | 108 -------- .../accountC/template.yaml | 89 ------- .../centralAccount/.gitignore | 1 + .../centralAccount/template.yaml | 88 +++++-- .../example-pattern.json | 84 ------ .../images/architecture.png | Bin 124583 -> 81318 bytes multi-account-private-apigw/template.yaml | 16 -- multi-account-private-apigw/vpc/.gitignore | 244 ++++++++++++++++++ multi-account-private-apigw/vpc/template.yaml | 2 +- 14 files changed, 1030 insertions(+), 613 deletions(-) create mode 100644 multi-account-private-apigw/accountA/.gitignore create mode 100644 multi-account-private-apigw/accountB/.gitignore delete mode 100644 multi-account-private-apigw/accountC/src/lambda_function.py delete mode 100644 multi-account-private-apigw/accountC/template.yaml create mode 100644 multi-account-private-apigw/centralAccount/.gitignore delete mode 100644 multi-account-private-apigw/example-pattern.json delete mode 100644 multi-account-private-apigw/template.yaml create mode 100644 multi-account-private-apigw/vpc/.gitignore diff --git a/multi-account-private-apigw/README.md b/multi-account-private-apigw/README.md index cac14b356..c3750252f 100644 --- a/multi-account-private-apigw/README.md +++ b/multi-account-private-apigw/README.md @@ -1,4 +1,8 @@ -# Enabling East/West Communication in Multi-Account AWS Architectures with Private API Gateway +# Enabling East/West Communication in Multi-Account AWS Architectures with Private API Gateway. + +![Architecture Diagram](./images/architecture.png) + +## Architecture Overview This architecture enables secure, centralized API communications across multiple AWS accounts, facilitating private east/west communication between services. 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),[VPC links](https://aws.amazon.com/blogs/compute/understanding-vpc-links-in-amazon-api-gateway-private-integrations/), and [Network Load Balancer (NLB)](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/introduction.html) to establish a centralized API management model. @@ -10,10 +14,10 @@ Important: This application uses various AWS Services and there are costs associ ### Requirements -- Four [AWS accounts](https://signin.aws.amazon.com/signup?request_type=register). IAM users with sufficient permissions to make necessary AWS service calls and manage AWS resources. +- Three [AWS accounts](https://signin.aws.amazon.com/signup?request_type=register). IAM users with sufficient permissions to make 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 **centralAccount**, **accountA**, **accountB** and **accountC** so you can run CLI and AWS SAM commands against them. +- Setup .aws/credentials [named profiles](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html) namely **centralAccount**, **accountA** and **accountB** so you can run CLI and AWS SAM commands against them. - An [Amazon VPC](https://docs.aws.amazon.com/vpc/latest/userguide/what-is-amazon-vpc.html) with 1 public and 2 private subnets in each account. ### Deployment Instructions @@ -48,32 +52,13 @@ Once you have run `sam deploy --guided --profile PROFILE_NAME` mode once and sav 4. Note the outputs from the SAM deployment process. These contain the `VPCId`,`PublicSubnetId`,`PrivateSubnet1Id` and `PrivateSubnet2Id`. These will be used as inputs for other stack deployments. #### AccountA -1. In account A, where you would like to create **EC2** and **VPC Endpoint**, 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)*: + +1. In account A, where you would like to create **private API Gateway** with **ECS Fargate** integration, 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 **Instance type** either `t2.micro` or `t2.small` - - Enter **unique [AMI Id](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/finding-an-ami.html)** from chosen region. - - Enter **Allowed IP** from where you can SSH into the EC2 Instance. If left empty, the default CIDR range will be 0.0.0.0/0 - - Enter **current account's VPC ID** where EC2 Instance and VPC Endpoint will be launched. - - Enter **Public Subnet ID**. - - Enter **1st and 2nd Private Subnet IDs**. - - Allow SAM CLI to create IAM roles with the required permissions. -3. Note the outputs from the SAM deployment process. This contains the `VPC Endpoint ID`, which will be used as inputs for central account's stack deployment. `EC2KeyPairName` to download key material, that you can use to SSH into EC2 Instance. -4. Follow the instructions to [store the key material from AWS System Manager parameter](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/create-key-pairs.html#create-key-pair-cloudformation) into your local machine. - -#### AccountB - -1. In account B, where you would like to create **private API Gateway** with **ECS Fargate** integration, 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 **current account's VPC ID** where NLB and ECS Fargate will be created. @@ -82,22 +67,21 @@ Once you have run `sam deploy --guided --profile PROFILE_NAME` mode once and sav - Allow SAM CLI to create IAM roles with the required permissions. 3. Note the outputs from the SAM deployment process. This contains the `API Gateway Invoke URL`, which will be used as inputs for central account's stack deployment. -#### AccountC -1. In account C, where you would like to create **private API Gateway** with **Lambda** integration, navigate to the `accountC` directory from the main directory and deploy using *(if you are in a different directory, then run `cd ..` before entering the below command)*: +#### AccountB +1. In account B, where you would like to create **private API Gateway** with **Lambda** integration, 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 accountC + cd accountB - sam deploy --guided --profile accountC + sam deploy --guided --profile accountB ``` 2. During the prompts: - Enter **stack name** and desired **AWS Region**. - Enter **Central Account's VPC ID**. This will be used in the Private API's resource policy. - - Enter **Bedrock Region** where you have access to **[Amazon Titan Image Generator v2](https://docs.aws.amazon.com/bedrock/latest/userguide/titan-image-models.html)** model. - Allow SAM CLI to create IAM roles with the required permissions. 3. Note the outputs from the SAM deployment process. This contains the `API Gateway's Invoke URL`, which will be used as inputs for central account's stack deployment. #### Central Account -1. In Central Account, where you would like to create **private API Gateway**, navigate to the `centralAccount` directory from the main directory and deploy using (if you are in different directory, then run `cd ..` before entering the below command): +1. In Central Account, where you would like to create central **private API Gateway**, navigate to the `centralAccount` directory from the main directory and deploy using (if you are in different directory, then run `cd ..` before entering the below command): ```bash cd centralAccount @@ -105,51 +89,48 @@ Once you have run `sam deploy --guided --profile PROFILE_NAME` mode once and sav ``` 2. During the prompts: - Enter **stack name** and desired **AWS Region**. + - Enter **Instance type** either `t2.micro` or `t2.small` + - Enter **unique [AMI Id](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/finding-an-ami.html)** from chosen region. + - Enter **Allowed IP** from where you can SSH into the EC2 Instance. If left empty, the default CIDR range will be **0.0.0.0/0** - Enter **current account's VPC ID** where NLB and VPC Endpoint will be created. + - Enter **Public Subnet ID**. - Enter **1st and 2nd Private Subnet IDs**. - - Enter **Account A's VpcEndpoint ID**. This will be used in the Private Api's resource policy. + - Enter **Account A's Api Gateway URL**. e.g. `https://abcdefghij.execute-api.eu-west-1.amazonaws.com/Prod/` - Enter **Account B's Api Gateway URL**. e.g. `https://abcdefghij.execute-api.eu-west-1.amazonaws.com/Prod/` - - Enter **Account C's Api Gateway URL**. e.g. `https://abcdefghij.execute-api.eu-west-1.amazonaws.com/Prod/` - Allow SAM CLI to create IAM roles with the required permissions. -3. Note the outputs from the SAM deployment process. This contains `two API Gateway's Invoke URLs`, which will be used to test from Account A. +3. Note the outputs from the SAM deployment process. This contains `two API Gateway's Invoke URLs`, `EC2KeyPairName` to download key material and `EC2 Public IP` address. +4. Follow the instructions to [store the key material from AWS System Manager parameter](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/create-key-pairs.html#create-key-pair-cloudformation) into your local machine. ## How it works -![Architecture Diagram](./images/architecture.png) - This pattern utilizes four accounts and their respective templates. -1. **Account A** : Acts as a client environment for testing east/west communication with the central API account. This template contains: - - - **EC2 Instance**: Serves as an API client to initiate test requests. - - **VPC Endpoint(execute-api)**: The VPC Endpoint is configured to securely route requests from the EC2 instance to the central account's Private API, ensuring traffic remains within the AWS network. - 2. **Central API Account** : Hosts the central components required to manage and route API requests securely across multiple AWS accounts. This template contains: + - **EC2 Instance**: Serves as an API client to initiate test requests. - **Amazon API Gateway (Private)**: A private API Gateway serves as the entry point for API requests. - **VPC Link(Private Link)**: Connects the API Gateway to an NLB within the Central Account's VPC, ensuring secure, private connectivity. - **Network Load Balancer (NLB)**: Routes incoming traffic from the VPC link to Elastic Network Interfaces (ENIs), forwarding requests to the target VPC Endpoint. - - **VPC Endpoint**: The endpoint for routing/resolving incoming API requests and provides connectivity to downstream Private API Gateways in other AWS accounts (e.g., Account B and Account C). + - **VPC Endpoint**: The endpoint for routing/resolving incoming API requests and provides connectivity to downstream Private API Gateways in other AWS accounts (e.g., Account A and Account B). -3. **Account B** : Hosts a service that provides a simple HTTP response from an NGINX server running on ECS Fargate. This template contains: +3. **Account A** : Hosts a service that provides a simple HTTP response from an NGINX server running on ECS Fargate. This template contains: - **Amazon API Gateway (Private)**: Receives requests from the Central API Account and forwards them as per configured paths and integration. - - **VPC Link**: Connects the API Gateway to an internal NLB within Account B. + - **VPC Link**: Connects the API Gateway to an internal NLB within Account A. - **Network Load Balancer (NLB)**: Routes traffic from the VPC link to the ECS Fargate service. - **Elastic Container Service (ECS) Fargate**: A containerized NGINX application on ECS Fargate returns a basic HTTP response. This verifies the connectivity and functionality of the architecture. -4. **Account C** : Hosts a Lambda function that interacts with Amazon Bedrock, generating images based on input prompts provided by the client in Account A. This template contains: +4. **Account B** : Hosts a Lambda function that return a simple text response to the client. This template contains: - **Amazon API Gateway (Private)**: Receives requests from the Central API Account and forwards them as per configured paths and integration. - - **AWS Lambda**: The Lambda function processes requests from the API Gateway and invokes an Amazon Bedrock (Amazon Titan Image Generator v2) to generate images based on the input prompt, which then returns the generated image to the client in Account A. - - **Amazon Bedrock (Amazon Titan Image Generator v2)**: Bedrock generates an image using Amazon Titan Image Generator v2 based on the input. + - **AWS Lambda**: The Lambda function processes requests from the API Gateway and returns a simple text response to the client. 5. **VPC (Optional)**: Creates a VPC with CIDR Range `10.1.0.0/16` with 1 Public subnet and 2 Private subnets. This template contains: - **1 Public Subnet**: The subnet has a direct route to an [internet gateway](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html). Resources in a public subnet can access the public internet. - **2 Private subnets**: Resources in a private subnet use a [NAT gateway](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat.html) to access the public internet. ## Testing -1. Once you have deployed all the Stacks, [connect to your EC2 instance using SSH](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-to-linux-instance.html) or [using EC2 Instance Connect](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-linux-inst-eic.html) in **Account A**. +1. Once you have deployed all the Stacks, [connect to your EC2 instance using SSH](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-to-linux-instance.html) or [using EC2 Instance Connect](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-linux-inst-eic.html) in **Central Account**. 2. After connecting to the EC2 instance, run the following `curl` command from the outputs to test the **/text** path (you can add `-v` flag for verbose response): ```bash @@ -166,24 +147,15 @@ This pattern utilizes four accounts and their respective templates. 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 correct directory before running the below commands)*: -1. Delete Account B template +1. Delete Account A template ```bash - sam delete --stack-name STACK_NAME_ACCOUNT_B --profile accountB + sam delete --stack-name STACK_NAME_ACCOUNT_A --profile accountA ``` -2. Delete Account C template +2. Delete Account B template ```bash - sam delete --stack-name STACK_NAME_ACCOUNT_C --profile accountC + sam delete --stack-name STACK_NAME_ACCOUNT_B --profile accountB ``` 3. Delete Central Account template ```bash sam delete --stack-name STACK_NAME_CENTRAL_ACCOUNT --profile centralAccount ``` -4. Delete Account A template - ```bash - sam delete --stack-name STACK_NAME_ACCOUNT_A --profile accountA - ``` - ----- -Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. - -SPDX-License-Identifier: MIT-0 \ No newline at end of file diff --git a/multi-account-private-apigw/accountA/.gitignore b/multi-account-private-apigw/accountA/.gitignore new file mode 100644 index 000000000..4808264db --- /dev/null +++ b/multi-account-private-apigw/accountA/.gitignore @@ -0,0 +1,244 @@ + +# Created by https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### OSX ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### PyCharm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# CMake +cmake-build-debug/ + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Ruby plugin and RubyMine +/.rakeTasks + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +### PyCharm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +.idea/sonarlint + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +.pytest_cache/ +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule.* + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Build folder + +*/build/* + +# End of https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode \ No newline at end of file diff --git a/multi-account-private-apigw/accountA/template.yaml b/multi-account-private-apigw/accountA/template.yaml index ae3cb0c7d..d6576932c 100644 --- a/multi-account-private-apigw/accountA/template.yaml +++ b/multi-account-private-apigw/accountA/template.yaml @@ -1,123 +1,177 @@ AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: > - accountA - Sample SAM Template for accountA containing EC2 Instance and execute-api VPC Endpoint + SAM Template for accountA containing Private API Gateway, VPC Link, Network Load Balancer, and ECS Cluster with Fargate. Parameters: - ########### Parameters for EC2 Instance and Network ########### - InstanceType: - Description: Enter t2.micro or t2.small. Default is t2.micro. - Type: String - AllowedValues: - - t2.micro - - t2.small - Default: t2.micro - ConstraintDescription: Must be either t2.micro or t2.small. - AmiID: - Description: Please provide a valid AMI id to launch EC2 Instance. - Type: AWS::EC2::Image::Id - AllowedIP: - Description: CIDR block to allow SSH access (e.g., 203.0.113.0/24). - Type: String - Default: 0.0.0.0/0 + ########### Parameters ########### VPCId: - Description: Please provide a VPC to deploy the solution into. Type: AWS::EC2::VPC::Id - PublicSubnet: - Description: Please provide the public subnet id to launch EC2 Instance - Type: AWS::EC2::Subnet::Id + Description: The VPC ID where the ECS service will be deployed. PrivateSubnet1: - Description: Please provide the first private subnet id to create Interface VPC Endpoint. Type: AWS::EC2::Subnet::Id + Description: The first private subnet ID within the VPC. PrivateSubnet2: - Description: Please provide the second private subnet id to create Interface VPC Endpoint. Type: AWS::EC2::Subnet::Id + Description: The second private subnet ID within the VPC. + CentralAccountVpcID: + Type: String + Description: The ID of the VPC from the Central Account Resources: - ########### EC2 Client Instance ########### - ClientInstance: - Type: AWS::EC2::Instance + ########### Private API Gateway ########### + PrivateApi: + Type: AWS::Serverless::Api + Properties: + EndpointConfiguration: PRIVATE + StageName: Prod + AlwaysDeploy: true + DefinitionBody: + openapi: "3.0.1" + info: + title: !Sub "PrivateApi-${AWS::StackName}" + version: "1.0" + paths: + /: + get: + responses: + "200": + description: "200 response" + x-amazon-apigateway-integration: + connectionId: !Ref ApiGatewayVpcLink + httpMethod: GET + uri: !Sub http://${NLB.DNSName}/ + connectionType: VPC_LINK + passthroughBehavior: when_no_match + type: http_proxy + x-amazon-apigateway-policy: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Principal: "*" + Action: "execute-api:Invoke" + Resource: "execute-api:/*" + Condition: + StringEquals: + aws:sourceVpc: !Ref CentralAccountVpcID + + ########### API Gateway VPC Link ########### + ApiGatewayVpcLink: + Type: AWS::ApiGateway::VpcLink + Properties: + Name: !Sub VPCLinkRestNlbInternal-${AWS::StackName} + TargetArns: + - !Ref NLB + + ########### Network Load Balancer (NLB) ########### + NLB: + Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: - ImageId: !Ref AmiID - InstanceType: !Ref InstanceType - SubnetId: !Ref PublicSubnet - KeyName: !Ref NewKeyPair - SecurityGroupIds: - - !GetAtt ClientEC2SecurityGroup.GroupId - Tags: - - Key: Name - Value: !Join ['-', [!Ref InstanceType, "Client"]] - - Key: InstanceType - Value: !Ref InstanceType + Name: !Sub PrivateNLB-${AWS::StackName} + Scheme: internal + Subnets: + - Ref: PrivateSubnet1 + - Ref: PrivateSubnet2 + Type: network + EnforceSecurityGroupInboundRulesOnPrivateLinkTraffic: "off" + SecurityGroups: + - !Ref NLBSecurityGroup - ########### EC2 Key Pair ########### - NewKeyPair: - Type: AWS::EC2::KeyPair + ########### NLB Listener ########### + NLBListener: + Type: AWS::ElasticLoadBalancingV2::Listener Properties: - KeyName: !Sub ${AWS::StackName}-${AWS::Region}-MyKeyPair + DefaultActions: + - Type: forward + TargetGroupArn: !Ref NLBTG + LoadBalancerArn: !Ref NLB + Port: 80 + Protocol: TCP - ########### API Gateway VPC Endpoint ########### - ApiGatewayVpcEndpoint: - Type: AWS::EC2::VPCEndpoint + ########### NLB Target Group ########### + NLBTG: + Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: - ServiceName: !Sub "com.amazonaws.${AWS::Region}.execute-api" + Name: !Sub FargateNLB-TG-${AWS::StackName} + Port: 80 + Protocol: TCP VpcId: !Ref VPCId - SubnetIds: - - !Ref PrivateSubnet1 - - !Ref PrivateSubnet2 - VpcEndpointType: Interface - PrivateDnsEnabled: true - SecurityGroupIds: - - !Ref VpcEndpointSG + TargetType: ip + HealthCheckProtocol: HTTP + HealthCheckPort: '80' + HealthCheckPath: '/' + Matcher: + HttpCode: 200 + + ########### ECS Cluster ########### + ECSCluster: + Type: AWS::ECS::Cluster + Properties: + ClusterName: !Sub FargateCluster-${AWS::StackName} - ########### Security Group for Client EC2 ########### - ClientEC2SecurityGroup: + ########### ECS Fargate Task Definition ########### + ECSFargateTaskDefinition: + Type: AWS::ECS::TaskDefinition + Properties: + Family: FargateTask + Cpu: 256 + Memory: 512 + NetworkMode: awsvpc + RequiresCompatibilities: + - FARGATE + ContainerDefinitions: + - Name: nginx + Image: public.ecr.aws/nginx/nginx:latest + Essential: true + PortMappings: + - ContainerPort: 80 + Protocol: tcp + + ########### ECS Fargate Service ########### + ECSFargateService: + Type: AWS::ECS::Service + DependsOn: NLBListener + Properties: + Cluster: !Ref ECSCluster + TaskDefinition: !Ref ECSFargateTaskDefinition + DesiredCount: 1 + LaunchType: FARGATE + NetworkConfiguration: + AwsvpcConfiguration: + AssignPublicIp: DISABLED + Subnets: + - Ref: PrivateSubnet1 + - Ref: PrivateSubnet2 + SecurityGroups: + - !Ref ECSSecurityGroup + LoadBalancers: + - ContainerName: nginx + ContainerPort: 80 + TargetGroupArn: !Ref NLBTG + + ########### ECS Security Group ########### + ECSSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: - GroupDescription: Allow SSH inbound traffic + GroupDescription: Security group for ECS Fargate Service VpcId: !Ref VPCId SecurityGroupIngress: - IpProtocol: tcp - FromPort: 22 - ToPort: 22 - CidrIp: !Ref AllowedIP + FromPort: 80 + ToPort: 80 + SourceSecurityGroupId: !Ref NLBSecurityGroup - ########### Security Group for VPC Endpoint ########### - VpcEndpointSG: + ########### NLB Security Group ########### + NLBSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: - GroupDescription: Security Group for NLB and VPC endpoint + GroupDescription: Security group for the NLB VpcId: !Ref VPCId - ########### Self-Allow Rule for VPC Endpoint Security Group ########### - VpcEndpointSGingressSelfAllow: - Type: AWS::EC2::SecurityGroupIngress - Properties: - GroupId: !Ref VpcEndpointSG - IpProtocol: tcp - FromPort: 0 - ToPort: 65535 - SourceSecurityGroupId: !GetAtt VpcEndpointSG.GroupId - - ########### Allow Client EC2 to Access VPC Endpoint ########### - VpcEndpointSGingressEC2Client: - Type: AWS::EC2::SecurityGroupIngress - Properties: - GroupId: !Ref VpcEndpointSG - IpProtocol: tcp - FromPort: 0 - ToPort: 65535 - SourceSecurityGroupId: !GetAtt ClientEC2SecurityGroup.GroupId - Outputs: ########### Outputs ########### - VPCEndpointID: - Value: !Ref ApiGatewayVpcEndpoint - Description: The ID of the VPC endpoint for API Gateway - EC2KeyPairName: - Value: !Ref NewKeyPair - Description: Name of Newly created EC2 Key Pair, that you can use to SSH into the EC2 Instance - EC2PublicIP: - Value: !GetAtt ClientInstance.PublicIp - Description: Public IP address of the EC2 instance. + ApiUrl: + Description: "API Gateway Invoke URL" + Value: !Sub "https://${PrivateApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/" + + diff --git a/multi-account-private-apigw/accountB/.gitignore b/multi-account-private-apigw/accountB/.gitignore new file mode 100644 index 000000000..4808264db --- /dev/null +++ b/multi-account-private-apigw/accountB/.gitignore @@ -0,0 +1,244 @@ + +# Created by https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### OSX ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### PyCharm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# CMake +cmake-build-debug/ + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Ruby plugin and RubyMine +/.rakeTasks + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +### PyCharm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +.idea/sonarlint + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +.pytest_cache/ +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule.* + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Build folder + +*/build/* + +# End of https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode \ No newline at end of file diff --git a/multi-account-private-apigw/accountB/template.yaml b/multi-account-private-apigw/accountB/template.yaml index 9261b6208..402c1fb67 100644 --- a/multi-account-private-apigw/accountB/template.yaml +++ b/multi-account-private-apigw/accountB/template.yaml @@ -1,24 +1,14 @@ AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 -Description: > - accountB - Sample SAM Template for accountB containing Private API Gateway, VPC Link, Network Load Balancer, and ECS Cluster with Fargate. +Description: | + SAM Template for accountB containing Private API Gateway, with lambda integration. Parameters: ########### Parameters ########### - VPCId: - Type: AWS::EC2::VPC::Id - Description: The VPC ID where the ECS service will be deployed. - PrivateSubnet1: - Type: AWS::EC2::Subnet::Id - Description: The first private subnet ID within the VPC. - PrivateSubnet2: - Type: AWS::EC2::Subnet::Id - Description: The second private subnet ID within the VPC. CentralAccountVpcID: Type: String - Description: The ID of the VPC from the Central Account - + Description: The ID of the VPC Endpoint you want to use from the Central Account + Resources: ########### Private API Gateway ########### PrivateApi: @@ -28,151 +18,58 @@ Resources: StageName: Prod AlwaysDeploy: true DefinitionBody: - openapi: "3.0.1" + openapi: 3.0.1 info: - title: "PrivateApi-${AWS::StackName}" - version: "1.0" + version: '1.0' + title: !Sub PrivateApi-${AWS::StackName} + x-amazon-apigateway-binary-media-types: + - '*/*' paths: /: get: responses: - "200": - description: "200 response" + '200': + description: 200 response x-amazon-apigateway-integration: - connectionId: !Ref ApiGatewayVpcLink - httpMethod: GET - uri: !Sub http://${NLB.DNSName}/ - connectionType: VPC_LINK + httpMethod: POST + uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaFunction.Arn}/invocations passthroughBehavior: when_no_match - type: http_proxy + type: aws_proxy x-amazon-apigateway-policy: - Version: "2012-10-17" + Version: '2012-10-17' Statement: - - Effect: "Allow" - Principal: "*" - Action: "execute-api:Invoke" - Resource: "execute-api:/*" - Condition: - StringEquals: - aws:sourceVpc: !Ref CentralAccountVpcID - - ########### API Gateway VPC Link ########### - ApiGatewayVpcLink: - Type: AWS::ApiGateway::VpcLink - Properties: - Name: VPCLinkRestNlbInternal - TargetArns: - - !Ref NLB - - ########### Network Load Balancer (NLB) ########### - NLB: - Type: AWS::ElasticLoadBalancingV2::LoadBalancer - Properties: - Name: PrivateNLB - Scheme: internal - Subnets: - - Ref: PrivateSubnet1 - - Ref: PrivateSubnet2 - Type: network - EnforceSecurityGroupInboundRulesOnPrivateLinkTraffic: "off" - SecurityGroups: - - !Ref NLBSecurityGroup - - ########### NLB Listener ########### - NLBListener: - Type: AWS::ElasticLoadBalancingV2::Listener - Properties: - DefaultActions: - - Type: forward - TargetGroupArn: !Ref NLBTG - LoadBalancerArn: !Ref NLB - Port: 80 - Protocol: TCP - - ########### NLB Target Group ########### - NLBTG: - Type: AWS::ElasticLoadBalancingV2::TargetGroup - Properties: - Name: FargateNLBTargetGroup - Port: 80 - Protocol: TCP - VpcId: !Ref VPCId - TargetType: ip - HealthCheckProtocol: HTTP - HealthCheckPort: '80' - HealthCheckPath: '/' - Matcher: - HttpCode: 200 - - ########### ECS Cluster ########### - ECSCluster: - Type: AWS::ECS::Cluster + - Effect: Allow + Principal: '*' + Action: execute-api:Invoke + Resource: execute-api:/* + Condition: + StringEquals: + aws:sourceVpc: !Ref CentralAccountVpcID + + ########### Lambda Function ########### + LambdaFunction: + Type: AWS::Serverless::Function Properties: - ClusterName: FargateCluster - - ########### ECS Fargate Task Definition ########### - ECSFargateTaskDefinition: - Type: AWS::ECS::TaskDefinition - Properties: - Family: FargateTask - Cpu: 256 - Memory: 512 - NetworkMode: awsvpc - RequiresCompatibilities: - - FARGATE - ContainerDefinitions: - - Name: nginx - Image: public.ecr.aws/nginx/nginx:latest - Essential: true - PortMappings: - - ContainerPort: 80 - Protocol: tcp - - ########### ECS Fargate Service ########### - ECSFargateService: - Type: AWS::ECS::Service - DependsOn: NLBListener - Properties: - Cluster: !Ref ECSCluster - TaskDefinition: !Ref ECSFargateTaskDefinition - DesiredCount: 1 - LaunchType: FARGATE - NetworkConfiguration: - AwsvpcConfiguration: - AssignPublicIp: DISABLED - Subnets: - - Ref: PrivateSubnet1 - - Ref: PrivateSubnet2 - SecurityGroups: - - !Ref ECSSecurityGroup - LoadBalancers: - - ContainerName: nginx - ContainerPort: 80 - TargetGroupArn: !Ref NLBTG - - ########### ECS Security Group ########### - ECSSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - GroupDescription: Security group for ECS Fargate Service - VpcId: !Ref VPCId - SecurityGroupIngress: - - IpProtocol: tcp - FromPort: 80 - ToPort: 80 - SourceSecurityGroupId: !Ref NLBSecurityGroup - - ########### NLB Security Group ########### - NLBSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - GroupDescription: Security group for the NLB - VpcId: !Ref VPCId + FunctionName: !Sub Bedrock-Lambda-${AWS::StackName} + Handler: index.lambda_handler + Runtime: python3.13 + InlineCode: | + import json + def lambda_handler(event, context): + return { + "statusCode": 200, + "body": json.dumps({ + "message" : "Hello from Lambda!"}) + } + Events: + APIRoot: + Type: Api + Properties: + Path: / + Method: ANY + RestApiId: !Ref PrivateApi Outputs: - ########### Outputs ########### ApiUrl: - Description: "API Gateway Invoke URL" - Value: !Sub "https://${PrivateApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/" - - + Description: API Gateway Invoke URL + Value: !Sub https://${PrivateApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/ \ No newline at end of file diff --git a/multi-account-private-apigw/accountC/src/lambda_function.py b/multi-account-private-apigw/accountC/src/lambda_function.py deleted file mode 100644 index f20a23434..000000000 --- a/multi-account-private-apigw/accountC/src/lambda_function.py +++ /dev/null @@ -1,108 +0,0 @@ -import json -import boto3 -import os -import logging -from botocore.exceptions import ClientError -import base64 - -logger = logging.getLogger() -logger.setLevel("INFO") - -def lambda_handler(event, context): - try: - - body = event.get('body') - - if not body: - return { - 'statusCode': 400, - "headers": { - "Content-Type": "application/json", - }, - "isBase64Encoded": False, - 'body': json.dumps({"message": "Empty body!! Please pass a prompt in the request body."}) - } - try: - prompt = base64.b64decode(body).decode('utf-8') - logger.info("Body was Base64-encoded. Decoded successfully.") - except (base64.binascii.Error, UnicodeDecodeError): - prompt = body - logger.info("Body is a plain string. No decoding needed.") - - image_base64 = generate_image(prompt=prompt) - - return { - 'statusCode': 200, - "headers": { - "Content-Type": "image/jpeg", - }, - "isBase64Encoded": True, - 'body': image_base64 - } - except Exception as e: - logger.error("Failed to generate image: %s", e) - return { - "statusCode": 500, - "headers": { - "Content-Type": "application/json", - }, - "isBase64Encoded": False, - "body": json.dumps({"message": "Oops! Something went wrong. Please try again later."}) - } - -def generate_image(prompt): - """ - Generates an image using Amazon Titan Image Generator G1 model. - - Args: - prompt (str): The text prompt for generating the image. - - Returns: - str: Base64-encoded image data. - - Raises: - ValueError: If the image generation fails. - """ - region= os.environ.get('region') - bedrock = boto3.client(service_name='bedrock-runtime', region_name=region) - - model_id = 'amazon.titan-image-generator-v2:0' - body = json.dumps({ - "taskType": "TEXT_IMAGE", - "textToImageParams": { - "text": prompt - }, - "imageGenerationConfig": { - "numberOfImages": 1, - "height": 1024, - "width": 1024, - "cfgScale": 8.0, - "seed": 0 - } - }) - - try: - logger.info("Generating image with model %s", model_id) - response = bedrock.invoke_model( - body=body, - modelId=model_id, - accept="application/json", - contentType="application/json" - ) - response_body = json.loads(response['body'].read()) - - # Check for error in response - if "error" in response_body: - raise ValueError(f"Image generation failed: {response_body['error']}") - - base64_image = response_body["images"][0] - logger.info("Image generated successfully with model %s", model_id) - - return base64_image # Base64 string ready for direct return - - except ClientError as e: - logger.error("AWS ClientError: %s", e) - raise ValueError("Error invoking image generation model") - except ValueError as e: - logger.error("Image generation error: %s", e) - raise diff --git a/multi-account-private-apigw/accountC/template.yaml b/multi-account-private-apigw/accountC/template.yaml deleted file mode 100644 index bccbf800d..000000000 --- a/multi-account-private-apigw/accountC/template.yaml +++ /dev/null @@ -1,89 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: > - accountC - SAM Template for accountC containing Private API Gateway, with lambda integration. - -Parameters: - ########### Parameters ########### - CentralAccountVpcID: - Type: String - Description: The ID of the VPC Endpoint you want to use from the Central Account - BedrockRegion: - Type: String - Default: us-east-1 - Description: Region where Amazon Titan Image Generator G1 model is enabled. - -Resources: - ########### Private API Gateway ########### - 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}" - x-amazon-apigateway-binary-media-types: - - "*/*" - paths: - /: - post: - responses: - "200": - description: "200 response" - x-amazon-apigateway-integration: - httpMethod: "POST" - uri: - Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaFunction.Arn}/invocations - passthroughBehavior: "when_no_match" - type: "aws_proxy" - x-amazon-apigateway-policy: - Version: "2012-10-17" - Statement: - - Effect: "Allow" - Principal: "*" - Action: "execute-api:Invoke" - Resource: "execute-api:/*" - Condition: - StringEquals: - aws:sourceVpc: !Ref CentralAccountVpcID - - ########### Lambda Function ########### - LambdaFunction: - Type: AWS::Serverless::Function - Properties: - FunctionName: !Sub Bedrock-Lambda-${AWS::StackName} - CodeUri: src/ - Handler: lambda_function.lambda_handler - Runtime: python3.12 - Timeout: 29 - MemorySize: 256 - Architectures: - - arm64 - Environment: - Variables: - region: !Ref BedrockRegion - Events: - APIRoot: - Type: Api - Properties: - Path: / - Method: ANY - RestApiId: !Ref PrivateApi - Policies: - - Version: '2012-10-17' - Statement: - - Effect: Allow - Action: - - bedrock:InvokeModel - Resource: !Sub arn:aws:bedrock:${BedrockRegion}::foundation-model/amazon.titan-image-generator-v2:0 - -Outputs: - ########### Outputs ########### - ApiUrl: - Description: "API Gateway Invoke URL" - Value: !Sub https://${PrivateApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/ diff --git a/multi-account-private-apigw/centralAccount/.gitignore b/multi-account-private-apigw/centralAccount/.gitignore new file mode 100644 index 000000000..3c3629e64 --- /dev/null +++ b/multi-account-private-apigw/centralAccount/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/multi-account-private-apigw/centralAccount/template.yaml b/multi-account-private-apigw/centralAccount/template.yaml index c909dd110..f7a5b6a10 100644 --- a/multi-account-private-apigw/centralAccount/template.yaml +++ b/multi-account-private-apigw/centralAccount/template.yaml @@ -1,22 +1,36 @@ AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: > - centralAccount - Sample SAM Template for central Account containing Private API Gateway, VPC link, Network Load Balancer and execute-api VPC Endpoint. + SAM Template for central Account containing Private API Gateway, VPC link, Network Load Balancer and execute-api VPC Endpoint. Parameters: + InstanceType: + Description: Enter t2.micro or t2.small. Default is t2.micro. + Type: String + AllowedValues: + - t2.micro + - t2.small + Default: t2.micro + ConstraintDescription: Must be either t2.micro or t2.small. + AmiID: + Description: Please provide a valid AMI id to launch EC2 Instance. + Type: AWS::EC2::Image::Id + AllowedIP: + Description: CIDR block to allow SSH access (e.g., 203.0.113.0/24). + Type: String + Default: 0.0.0.0/0 VPCId: Description: Please provide a VPC to deploy the solution into. Type: AWS::EC2::VPC::Id + PublicSubnet: + Description: Please provide the public subnet id to launch EC2 Instance + Type: AWS::EC2::Subnet::Id PrivateSubnet1: Description: Please provide the first private subnet id with outbound connectivity within the VPC you selected above. Type: AWS::EC2::Subnet::Id PrivateSubnet2: Description: Please provide the second private subnet id with outbound connectivity within the VPC you selected above. Type: AWS::EC2::Subnet::Id - AccountAVpcEndpoint: - Type: String - Description: The ID of the VPC Endpoint you want to use from Account A AccountBApiGwURL: Type: String Description: The API GW URL from Account B including path @@ -25,8 +39,43 @@ Parameters: Description: The API GW URL from Account B including path Resources: + ########### EC2 Client Instance ########### + ClientInstance: + Type: AWS::EC2::Instance + Properties: + ImageId: !Ref AmiID + InstanceType: !Ref InstanceType + SubnetId: !Ref PublicSubnet + KeyName: !Ref NewKeyPair + SecurityGroupIds: + - !GetAtt ClientEC2SecurityGroup.GroupId + Tags: + - Key: Name + Value: !Join ['-', [!Ref InstanceType, "Client"]] + - Key: InstanceType + Value: !Ref InstanceType + + ########### EC2 Key Pair ########### + NewKeyPair: + Type: AWS::EC2::KeyPair + Properties: + KeyName: !Sub ${AWS::StackName}-${AWS::Region}-MyKeyPair + + ########### Security Group for Client EC2 ########### + ClientEC2SecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Allow SSH inbound traffic + VpcId: !Ref VPCId + SecurityGroupIngress: + - IpProtocol: tcp + FromPort: 22 + ToPort: 22 + CidrIp: !Ref AllowedIP + ########### API Gateway REST API ############### MyApi: + DependsOn: ApiGatewayVpcLink Type: AWS::Serverless::Api Properties: EndpointConfiguration: PRIVATE @@ -42,9 +91,9 @@ Resources: servers: - x-amazon-apigateway-endpoint-configuration: vpcEndpointIds: - - !Ref AccountAVpcEndpoint + - !Ref ExecuteApiVpcEndpoint paths: - /text: + /fargate: get: responses: "200": @@ -59,14 +108,14 @@ Resources: responses: default: statusCode: "200" - /image: - post: + /lambda: + get: responses: "200": description: "200 response" x-amazon-apigateway-integration: connectionId: !Ref ApiGatewayVpcLink - httpMethod: "POST" + httpMethod: "GET" uri: !Ref AccountCApiGwURL connectionType: "VPC_LINK" passthroughBehavior: "when_no_templates" @@ -84,7 +133,7 @@ Resources: Condition: StringEquals: aws:sourceVpce: - - !Ref AccountAVpcEndpoint + - !Ref ExecuteApiVpcEndpoint ########### API Gateway VPC Link ############### ApiGatewayVpcLink: @@ -151,6 +200,9 @@ Resources: Properties: GroupDescription: Security Group for NLB and VPC endpoint VpcId: !Ref VPCId + SecurityGroupIngress: + - IpProtocol: -1 + SourceSecurityGroupId: !GetAtt ClientEC2SecurityGroup.GroupId SecurityGroupEgress: - IpProtocol: -1 CidrIp: 0.0.0.0/0 @@ -198,8 +250,14 @@ Resources: Outputs: ApiUrlForECS: - Description: "API Gateway Invoke URL with GET method for ECS Fargate integration" - Value: !Sub "https://${MyApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/${MyApi.Stage}/text" + Description: API Gateway Invoke URL with GET method for ECS Fargate integration + Value: !Sub https://${MyApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/${MyApi.Stage}/fargate ApiUrlForLambda: - Description: "API Gateway Invoke URL with POST method for Lambda function integration" - Value: !Sub "https://${MyApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/${MyApi.Stage}/image" + Description: API Gateway Invoke URL with GET method for Lambda function integration + Value: !Sub https://${MyApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/${MyApi.Stage}/lambda + EC2KeyPairName: + Value: !Ref NewKeyPair + Description: Name of Newly created EC2 Key Pair, that you can use to SSH into the EC2 Instance + EC2PublicIP: + Value: !GetAtt ClientInstance.PublicIp + Description: Public IP address of the EC2 instance. \ No newline at end of file diff --git a/multi-account-private-apigw/example-pattern.json b/multi-account-private-apigw/example-pattern.json deleted file mode 100644 index 1a2ca05bb..000000000 --- a/multi-account-private-apigw/example-pattern.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "title": "Enabling East/West Communication in Multi-Account AWS Architectures with Private API Gateway", - "description": "Create Private REST API Gateway in multiple accounts and integrate with the central account", - "language": "Python", - "level": "300", - "framework": "SAM", - "introBox": { - "headline": "How it works", - "text": [ - "This sample project demonstrates how to enable secure, centralized API communications across multiple AWS accounts using a private Amazon API Gateway. It facilitates east/west communication between services while keeping traffic within the AWS network.", - "The architecture utilizes key AWS services such as Amazon API Gateway (Private), VPC links, Network Load Balancers (NLBs), and Execute-API VPC Endpoints. These services work together to securely route requests between multiple AWS accounts and their respective private APIs.", - "This pattern deploys four separate AWS accounts: a client account with an EC2 instance and VPC Endpoint for testing, a central account hosting the main API Gateway and routing components, an account with an ECS Fargate service behind a private API Gateway, and another account with a Lambda function integrated with Amazon Bedrock for image generation. Each account contains its own AWS resources to ensure proper communication and isolation." - ] - } - , - "gitHub": { - "template": { - "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/sfn-athena-cdk-python", - "templateURL": "serverless-patterns/multi-account-private-apigw", - "projectFolder": "multi-account-private-apigw", - "templateFile": "multi-account-private-apigw/centralAccount/template.yaml" - } - }, - "resources": { - "bullets": [ - { - "text": "Amazon API Gateway (Private)", - "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": "VPC Links", - "link": "https://docs.aws.amazon.com/vpc/latest/userguide/endpoint-services-overview.html" - }, - { - "text": "Network Load Balancer (NLB)", - "link": "https://docs.aws.amazon.com/elasticloadbalancing/latest/network/introduction.html" - }, - { - "text": "Amazon ECS Fargate", - "link": "https://docs.aws.amazon.com/AmazonECS/latest/developerguide/AWS_Fargate.html" - }, - { - "text": "AWS Lambda", - "link": "https://docs.aws.amazon.com/lambda/latest/dg/welcome.html" - }, - { - "text": "Amazon Bedrock", - "link": "https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html" - }, - { - "text": "Amazon EC2", - "link": "https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/concepts.html" - } - ] - } -, - "deploy": { - "text": [ - "sam deploy --guided --profile PROFILE_NAME" - ] - }, - "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": "https://www.linkedin.com/in/usama-ali-khan/" - } - ] -} diff --git a/multi-account-private-apigw/images/architecture.png b/multi-account-private-apigw/images/architecture.png index 55e512238751644372d87bfaafb03f207e114e4c..6df7f6ff4ed71b12ace66233aca7090812229b77 100644 GIT binary patch literal 81318 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 Zt1&8r Date: Fri, 22 Nov 2024 17:14:27 +0000 Subject: [PATCH 03/12] added example-pattern.json file --- .../example-pattern.json | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 multi-account-private-apigw/example-pattern.json diff --git a/multi-account-private-apigw/example-pattern.json b/multi-account-private-apigw/example-pattern.json new file mode 100644 index 000000000..bda1b30e9 --- /dev/null +++ b/multi-account-private-apigw/example-pattern.json @@ -0,0 +1,80 @@ +{ + "title": "Enabling East/West Communication in Multi-Account AWS Architectures with Private API Gateway", + "description": "Create Private REST API Gateway in multiple accounts and integrate with the central account", + "language": "Python", + "level": "300", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This sample project demonstrates how to enable secure, centralized API communications across multiple AWS accounts using a private Amazon API Gateway. It facilitates east/west communication between services while keeping traffic within the AWS network.", + "The architecture utilizes key AWS services such as Amazon API Gateway (Private), VPC links, Network Load Balancers (NLBs), and Execute-API VPC Endpoints. These services work together to securely route requests between multiple AWS accounts and their respective private APIs.", + "This pattern deploys three separate AWS accounts: a central account hosting the main API Gateway and routing components, an account with an ECS Fargate service behind a private API Gateway, and another account with a Lambda function integration. Each account contains its own AWS resources to ensure proper communication and isolation." + ] + } + , + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/sfn-athena-cdk-python", + "templateURL": "serverless-patterns/multi-account-private-apigw", + "projectFolder": "multi-account-private-apigw", + "templateFile": "multi-account-private-apigw/centralAccount/template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "Amazon API Gateway (Private)", + "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": "VPC Links", + "link": "https://docs.aws.amazon.com/vpc/latest/userguide/endpoint-services-overview.html" + }, + { + "text": "Network Load Balancer (NLB)", + "link": "https://docs.aws.amazon.com/elasticloadbalancing/latest/network/introduction.html" + }, + { + "text": "Amazon ECS Fargate", + "link": "https://docs.aws.amazon.com/AmazonECS/latest/developerguide/AWS_Fargate.html" + }, + { + "text": "AWS Lambda", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/welcome.html" + }, + { + "text": "Amazon EC2", + "link": "https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/concepts.html" + } + ] + } +, + "deploy": { + "text": [ + "sam deploy --guided --profile PROFILE_NAME" + ] + }, + "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": "https://www.linkedin.com/in/usama-ali-khan/" + } + ] +} \ No newline at end of file From 1902eb7a5de3b769fb876a3ea27a37cf37e23577 Mon Sep 17 00:00:00 2001 From: usama-khan98 Date: Fri, 22 Nov 2024 17:45:01 +0000 Subject: [PATCH 04/12] updated lambda function name in AccountB --- multi-account-private-apigw/accountB/template.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multi-account-private-apigw/accountB/template.yaml b/multi-account-private-apigw/accountB/template.yaml index 402c1fb67..f4d251257 100644 --- a/multi-account-private-apigw/accountB/template.yaml +++ b/multi-account-private-apigw/accountB/template.yaml @@ -50,7 +50,7 @@ Resources: LambdaFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub Bedrock-Lambda-${AWS::StackName} + FunctionName: !Sub Lambda-${AWS::StackName} Handler: index.lambda_handler Runtime: python3.13 InlineCode: | From 52cf934b2dc06ba38d58a5c9ad0d22b101f54735 Mon Sep 17 00:00:00 2001 From: usama-khan98 Date: Wed, 27 Nov 2024 16:55:13 +0000 Subject: [PATCH 05/12] clean up --- multi-account-private-apigw/README.md | 20 +- .../accountA/.gitignore | 244 ---------------- .../accountA/template.yaml | 177 ------------ .../accountB/.gitignore | 244 ---------------- .../accountB/template.yaml | 75 ----- .../centralAccount/.gitignore | 1 - .../centralAccount/src/cfnresponse.py | 54 ---- .../centralAccount/src/lambda_function.py | 33 --- .../centralAccount/template.yaml | 263 ------------------ .../example-pattern.json | 6 +- .../images/architecture.png | Bin 81318 -> 0 bytes multi-account-private-apigw/vpc/.gitignore | 244 ---------------- multi-account-private-apigw/vpc/template.yaml | 183 ------------ 13 files changed, 11 insertions(+), 1533 deletions(-) delete mode 100644 multi-account-private-apigw/accountA/.gitignore delete mode 100644 multi-account-private-apigw/accountA/template.yaml delete mode 100644 multi-account-private-apigw/accountB/.gitignore delete mode 100644 multi-account-private-apigw/accountB/template.yaml delete mode 100644 multi-account-private-apigw/centralAccount/.gitignore delete mode 100644 multi-account-private-apigw/centralAccount/src/cfnresponse.py delete mode 100644 multi-account-private-apigw/centralAccount/src/lambda_function.py delete mode 100644 multi-account-private-apigw/centralAccount/template.yaml delete mode 100644 multi-account-private-apigw/images/architecture.png delete mode 100644 multi-account-private-apigw/vpc/.gitignore delete mode 100644 multi-account-private-apigw/vpc/template.yaml diff --git a/multi-account-private-apigw/README.md b/multi-account-private-apigw/README.md index c3750252f..f7ab6e077 100644 --- a/multi-account-private-apigw/README.md +++ b/multi-account-private-apigw/README.md @@ -90,7 +90,7 @@ Once you have run `sam deploy --guided --profile PROFILE_NAME` mode once and sav 2. During the prompts: - Enter **stack name** and desired **AWS Region**. - Enter **Instance type** either `t2.micro` or `t2.small` - - Enter **unique [AMI Id](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/finding-an-ami.html)** from chosen region. + - Enter **unique [Amazon Linux 2023 AMI Id](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/finding-an-ami.html)** from AMI Catalog in the chosen region. - Enter **Allowed IP** from where you can SSH into the EC2 Instance. If left empty, the default CIDR range will be **0.0.0.0/0** - Enter **current account's VPC ID** where NLB and VPC Endpoint will be created. - Enter **Public Subnet ID**. @@ -103,7 +103,7 @@ Once you have run `sam deploy --guided --profile PROFILE_NAME` mode once and sav ## How it works -This pattern utilizes four accounts and their respective templates. +This pattern utilizes three accounts and their respective templates. 2. **Central API Account** : Hosts the central components required to manage and route API requests securely across multiple AWS accounts. This template contains: @@ -132,24 +132,20 @@ This pattern utilizes four accounts and their respective templates. ## Testing 1. Once you have deployed all the Stacks, [connect to your EC2 instance using SSH](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-to-linux-instance.html) or [using EC2 Instance Connect](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-linux-inst-eic.html) in **Central Account**. -2. After connecting to the EC2 instance, run the following `curl` command from the outputs to test the **/text** path (you can add `-v` flag for verbose response): +2. After connecting to the EC2 instance, run the following `curl` command to test the **/fargate** and **/lambda** path (*replace the URL with your own API GW URL*): ```bash - curl --location 'https://abcdefghij.execute-api.eu-west-1.amazonaws.com/Prod/text' - ``` -3. For **/image** path, use the following curl command (*you can update the prompt and image name as needed*): - ```bash - curl --location --request POST 'https://abcdefghij.execute-api.eu-west-1.amazonaws.com/Prod/image' \ - --data 'A bustling futuristic city at night with neon signs, towering skyscrapers, flying vehicles, and busy street life, in the rain. Detailed and atmospheric.' --output image.jpg + curl --location 'https://abcdefghij.execute-api.eu-west-1.amazonaws.com/Prod/fargate' + + curl --location 'https://abcdefghij.execute-api.eu-west-1.amazonaws.com/Prod/lambda' ``` - ## 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 correct directory before running the below commands)*: +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_ACCOUNT_A --profile accountA + sam delete --stack-name STACK_NAME --profile PROFILE_NAME ``` 2. Delete Account B template ```bash diff --git a/multi-account-private-apigw/accountA/.gitignore b/multi-account-private-apigw/accountA/.gitignore deleted file mode 100644 index 4808264db..000000000 --- a/multi-account-private-apigw/accountA/.gitignore +++ /dev/null @@ -1,244 +0,0 @@ - -# Created by https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode - -### Linux ### -*~ - -# temporary files which can be created if a process still has a handle open of a deleted file -.fuse_hidden* - -# KDE directory preferences -.directory - -# Linux trash folder which might appear on any partition or disk -.Trash-* - -# .nfs files are created when an open file is removed but is still being accessed -.nfs* - -### OSX ### -*.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -### PyCharm ### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff: -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/dictionaries - -# Sensitive or high-churn files: -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.xml -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml - -# Gradle: -.idea/**/gradle.xml -.idea/**/libraries - -# CMake -cmake-build-debug/ - -# Mongo Explorer plugin: -.idea/**/mongoSettings.xml - -## File-based project format: -*.iws - -## Plugin-specific files: - -# IntelliJ -/out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# Ruby plugin and RubyMine -/.rakeTasks - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -### PyCharm Patch ### -# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 - -# *.iml -# modules.xml -# .idea/misc.xml -# *.ipr - -# Sonarlint plugin -.idea/sonarlint - -### Python ### -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -.pytest_cache/ -nosetests.xml -coverage.xml -*.cover -.hypothesis/ - -# Translations -*.mo -*.pot - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule.* - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ - -### VisualStudioCode ### -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -.history - -### Windows ### -# Windows thumbnail cache files -Thumbs.db -ehthumbs.db -ehthumbs_vista.db - -# Folder config file -Desktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msm -*.msp - -# Windows shortcuts -*.lnk - -# Build folder - -*/build/* - -# End of https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode \ No newline at end of file diff --git a/multi-account-private-apigw/accountA/template.yaml b/multi-account-private-apigw/accountA/template.yaml deleted file mode 100644 index d6576932c..000000000 --- a/multi-account-private-apigw/accountA/template.yaml +++ /dev/null @@ -1,177 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: > - SAM Template for accountA containing Private API Gateway, VPC Link, Network Load Balancer, and ECS Cluster with Fargate. - -Parameters: - ########### Parameters ########### - VPCId: - Type: AWS::EC2::VPC::Id - Description: The VPC ID where the ECS service will be deployed. - PrivateSubnet1: - Type: AWS::EC2::Subnet::Id - Description: The first private subnet ID within the VPC. - PrivateSubnet2: - Type: AWS::EC2::Subnet::Id - Description: The second private subnet ID within the VPC. - CentralAccountVpcID: - Type: String - Description: The ID of the VPC from the Central Account - -Resources: - ########### Private API Gateway ########### - PrivateApi: - Type: AWS::Serverless::Api - Properties: - EndpointConfiguration: PRIVATE - StageName: Prod - AlwaysDeploy: true - DefinitionBody: - openapi: "3.0.1" - info: - title: !Sub "PrivateApi-${AWS::StackName}" - version: "1.0" - paths: - /: - get: - responses: - "200": - description: "200 response" - x-amazon-apigateway-integration: - connectionId: !Ref ApiGatewayVpcLink - httpMethod: GET - uri: !Sub http://${NLB.DNSName}/ - connectionType: VPC_LINK - passthroughBehavior: when_no_match - type: http_proxy - x-amazon-apigateway-policy: - Version: "2012-10-17" - Statement: - - Effect: "Allow" - Principal: "*" - Action: "execute-api:Invoke" - Resource: "execute-api:/*" - Condition: - StringEquals: - aws:sourceVpc: !Ref CentralAccountVpcID - - ########### API Gateway VPC Link ########### - ApiGatewayVpcLink: - Type: AWS::ApiGateway::VpcLink - Properties: - Name: !Sub VPCLinkRestNlbInternal-${AWS::StackName} - TargetArns: - - !Ref NLB - - ########### Network Load Balancer (NLB) ########### - NLB: - Type: AWS::ElasticLoadBalancingV2::LoadBalancer - Properties: - Name: !Sub PrivateNLB-${AWS::StackName} - Scheme: internal - Subnets: - - Ref: PrivateSubnet1 - - Ref: PrivateSubnet2 - Type: network - EnforceSecurityGroupInboundRulesOnPrivateLinkTraffic: "off" - SecurityGroups: - - !Ref NLBSecurityGroup - - ########### NLB Listener ########### - NLBListener: - Type: AWS::ElasticLoadBalancingV2::Listener - Properties: - DefaultActions: - - Type: forward - TargetGroupArn: !Ref NLBTG - LoadBalancerArn: !Ref NLB - Port: 80 - Protocol: TCP - - ########### NLB Target Group ########### - NLBTG: - Type: AWS::ElasticLoadBalancingV2::TargetGroup - Properties: - Name: !Sub FargateNLB-TG-${AWS::StackName} - Port: 80 - Protocol: TCP - VpcId: !Ref VPCId - TargetType: ip - HealthCheckProtocol: HTTP - HealthCheckPort: '80' - HealthCheckPath: '/' - Matcher: - HttpCode: 200 - - ########### ECS Cluster ########### - ECSCluster: - Type: AWS::ECS::Cluster - Properties: - ClusterName: !Sub FargateCluster-${AWS::StackName} - - ########### ECS Fargate Task Definition ########### - ECSFargateTaskDefinition: - Type: AWS::ECS::TaskDefinition - Properties: - Family: FargateTask - Cpu: 256 - Memory: 512 - NetworkMode: awsvpc - RequiresCompatibilities: - - FARGATE - ContainerDefinitions: - - Name: nginx - Image: public.ecr.aws/nginx/nginx:latest - Essential: true - PortMappings: - - ContainerPort: 80 - Protocol: tcp - - ########### ECS Fargate Service ########### - ECSFargateService: - Type: AWS::ECS::Service - DependsOn: NLBListener - Properties: - Cluster: !Ref ECSCluster - TaskDefinition: !Ref ECSFargateTaskDefinition - DesiredCount: 1 - LaunchType: FARGATE - NetworkConfiguration: - AwsvpcConfiguration: - AssignPublicIp: DISABLED - Subnets: - - Ref: PrivateSubnet1 - - Ref: PrivateSubnet2 - SecurityGroups: - - !Ref ECSSecurityGroup - LoadBalancers: - - ContainerName: nginx - ContainerPort: 80 - TargetGroupArn: !Ref NLBTG - - ########### ECS Security Group ########### - ECSSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - GroupDescription: Security group for ECS Fargate Service - VpcId: !Ref VPCId - SecurityGroupIngress: - - IpProtocol: tcp - FromPort: 80 - ToPort: 80 - SourceSecurityGroupId: !Ref NLBSecurityGroup - - ########### NLB Security Group ########### - NLBSecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - GroupDescription: Security group for the NLB - VpcId: !Ref VPCId - -Outputs: - ########### Outputs ########### - ApiUrl: - Description: "API Gateway Invoke URL" - Value: !Sub "https://${PrivateApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/" - - diff --git a/multi-account-private-apigw/accountB/.gitignore b/multi-account-private-apigw/accountB/.gitignore deleted file mode 100644 index 4808264db..000000000 --- a/multi-account-private-apigw/accountB/.gitignore +++ /dev/null @@ -1,244 +0,0 @@ - -# Created by https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode - -### Linux ### -*~ - -# temporary files which can be created if a process still has a handle open of a deleted file -.fuse_hidden* - -# KDE directory preferences -.directory - -# Linux trash folder which might appear on any partition or disk -.Trash-* - -# .nfs files are created when an open file is removed but is still being accessed -.nfs* - -### OSX ### -*.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -### PyCharm ### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff: -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/dictionaries - -# Sensitive or high-churn files: -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.xml -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml - -# Gradle: -.idea/**/gradle.xml -.idea/**/libraries - -# CMake -cmake-build-debug/ - -# Mongo Explorer plugin: -.idea/**/mongoSettings.xml - -## File-based project format: -*.iws - -## Plugin-specific files: - -# IntelliJ -/out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# Ruby plugin and RubyMine -/.rakeTasks - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -### PyCharm Patch ### -# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 - -# *.iml -# modules.xml -# .idea/misc.xml -# *.ipr - -# Sonarlint plugin -.idea/sonarlint - -### Python ### -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -.pytest_cache/ -nosetests.xml -coverage.xml -*.cover -.hypothesis/ - -# Translations -*.mo -*.pot - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule.* - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ - -### VisualStudioCode ### -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -.history - -### Windows ### -# Windows thumbnail cache files -Thumbs.db -ehthumbs.db -ehthumbs_vista.db - -# Folder config file -Desktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msm -*.msp - -# Windows shortcuts -*.lnk - -# Build folder - -*/build/* - -# End of https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode \ No newline at end of file diff --git a/multi-account-private-apigw/accountB/template.yaml b/multi-account-private-apigw/accountB/template.yaml deleted file mode 100644 index f4d251257..000000000 --- a/multi-account-private-apigw/accountB/template.yaml +++ /dev/null @@ -1,75 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: | - SAM Template for accountB containing Private API Gateway, with lambda integration. - -Parameters: - ########### Parameters ########### - CentralAccountVpcID: - Type: String - Description: The ID of the VPC Endpoint you want to use from the Central Account - -Resources: - ########### Private API Gateway ########### - 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} - x-amazon-apigateway-binary-media-types: - - '*/*' - paths: - /: - get: - responses: - '200': - description: 200 response - x-amazon-apigateway-integration: - httpMethod: POST - uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaFunction.Arn}/invocations - passthroughBehavior: when_no_match - type: aws_proxy - x-amazon-apigateway-policy: - Version: '2012-10-17' - Statement: - - Effect: Allow - Principal: '*' - Action: execute-api:Invoke - Resource: execute-api:/* - Condition: - StringEquals: - aws:sourceVpc: !Ref CentralAccountVpcID - - ########### Lambda Function ########### - LambdaFunction: - Type: AWS::Serverless::Function - Properties: - FunctionName: !Sub Lambda-${AWS::StackName} - Handler: index.lambda_handler - Runtime: python3.13 - InlineCode: | - import json - def lambda_handler(event, context): - return { - "statusCode": 200, - "body": json.dumps({ - "message" : "Hello from Lambda!"}) - } - Events: - APIRoot: - Type: Api - Properties: - Path: / - Method: ANY - RestApiId: !Ref PrivateApi - -Outputs: - ApiUrl: - Description: API Gateway Invoke URL - Value: !Sub https://${PrivateApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/ \ No newline at end of file diff --git a/multi-account-private-apigw/centralAccount/.gitignore b/multi-account-private-apigw/centralAccount/.gitignore deleted file mode 100644 index 3c3629e64..000000000 --- a/multi-account-private-apigw/centralAccount/.gitignore +++ /dev/null @@ -1 +0,0 @@ -node_modules diff --git a/multi-account-private-apigw/centralAccount/src/cfnresponse.py b/multi-account-private-apigw/centralAccount/src/cfnresponse.py deleted file mode 100644 index cd9e00e98..000000000 --- a/multi-account-private-apigw/centralAccount/src/cfnresponse.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright 2016 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. -# This file is licensed to you under the AWS Customer Agreement (the "License"). -# You may not use this file except in compliance with the License. -# A copy of the License is located at http://aws.amazon.com/agreement/ . -# This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. -# See the License for the specific language governing permissions and limitations under the License. - -from __future__ import print_function -import urllib3 -import json -import re - -SUCCESS = "SUCCESS" -FAILED = "FAILED" - -http = urllib3.PoolManager() - - -def send(event, context, responseStatus, responseData, physicalResourceId=None, noEcho=False, reason=None): - responseUrl = event['ResponseURL'] - - responseBody = { - 'Status' : responseStatus, - 'Reason' : reason or "See the details in CloudWatch Log Stream: {}".format(context.log_stream_name), - 'PhysicalResourceId' : physicalResourceId or context.log_stream_name, - 'StackId' : event['StackId'], - 'RequestId' : event['RequestId'], - 'LogicalResourceId' : event['LogicalResourceId'], - 'NoEcho' : noEcho, - 'Data' : responseData - } - - json_responseBody = json.dumps(responseBody) - - print("Response body:") - print(json_responseBody) - - headers = { - 'content-type' : '', - 'content-length' : str(len(json_responseBody)) - } - - try: - response = http.request('PUT', responseUrl, headers=headers, body=json_responseBody) - print("Status code:", response.status) - - - except Exception as e: - - print("send(..) failed executing http.request(..):", mask_credentials_and_signature(e)) - -def mask_credentials_and_signature(message): - message = re.sub(r'X-Amz-Credential=[^&\s]+', 'X-Amz-Credential=*****', message, flags=re.IGNORECASE) - return re.sub(r'X-Amz-Signature=[^&\s]+', 'X-Amz-Signature=*****', message, flags=re.IGNORECASE) diff --git a/multi-account-private-apigw/centralAccount/src/lambda_function.py b/multi-account-private-apigw/centralAccount/src/lambda_function.py deleted file mode 100644 index a423a8d31..000000000 --- a/multi-account-private-apigw/centralAccount/src/lambda_function.py +++ /dev/null @@ -1,33 +0,0 @@ -import cfnresponse -import json -import boto3 - -def lambda_handler(event, context): - print('REQUEST RECEIVED:\n' + json.dumps(event)) - responseData = {} - - # Handle Delete requests - if event['RequestType'] == 'Delete': - cfnresponse.send(event, context, cfnresponse.SUCCESS, {}) - return - - # Handle Create or Update requests - if event['RequestType'] == 'Create' or event['RequestType'] == 'Update': - try: - ec2 = boto3.resource('ec2') - enis = event['ResourceProperties']['NetworkInterfaceIds'] - - # Fetch private IPs of the network interfaces - for index, eni in enumerate(enis): - network_interface = ec2.NetworkInterface(eni) - responseData[f'IP{index}'] = network_interface.private_ip_address - print(responseData) - - # Send success response back to CloudFormation - cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData) - - except Exception as e: - # Send failure response to CloudFormation with the error message - responseData = {'error': str(e)} - cfnresponse.send(event, context, cfnresponse.FAILED, responseData) - return diff --git a/multi-account-private-apigw/centralAccount/template.yaml b/multi-account-private-apigw/centralAccount/template.yaml deleted file mode 100644 index f7a5b6a10..000000000 --- a/multi-account-private-apigw/centralAccount/template.yaml +++ /dev/null @@ -1,263 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09' -Transform: AWS::Serverless-2016-10-31 -Description: > - SAM Template for central Account containing Private API Gateway, VPC link, Network Load Balancer and execute-api VPC Endpoint. - -Parameters: - InstanceType: - Description: Enter t2.micro or t2.small. Default is t2.micro. - Type: String - AllowedValues: - - t2.micro - - t2.small - Default: t2.micro - ConstraintDescription: Must be either t2.micro or t2.small. - AmiID: - Description: Please provide a valid AMI id to launch EC2 Instance. - Type: AWS::EC2::Image::Id - AllowedIP: - Description: CIDR block to allow SSH access (e.g., 203.0.113.0/24). - Type: String - Default: 0.0.0.0/0 - VPCId: - Description: Please provide a VPC to deploy the solution into. - Type: AWS::EC2::VPC::Id - PublicSubnet: - Description: Please provide the public subnet id to launch EC2 Instance - Type: AWS::EC2::Subnet::Id - PrivateSubnet1: - Description: Please provide the first private subnet id with outbound connectivity within the VPC you selected above. - Type: AWS::EC2::Subnet::Id - PrivateSubnet2: - Description: Please provide the second private subnet id with outbound connectivity within the VPC you selected above. - Type: AWS::EC2::Subnet::Id - AccountBApiGwURL: - Type: String - Description: The API GW URL from Account B including path - AccountCApiGwURL: - Type: String - Description: The API GW URL from Account B including path - -Resources: - ########### EC2 Client Instance ########### - ClientInstance: - Type: AWS::EC2::Instance - Properties: - ImageId: !Ref AmiID - InstanceType: !Ref InstanceType - SubnetId: !Ref PublicSubnet - KeyName: !Ref NewKeyPair - SecurityGroupIds: - - !GetAtt ClientEC2SecurityGroup.GroupId - Tags: - - Key: Name - Value: !Join ['-', [!Ref InstanceType, "Client"]] - - Key: InstanceType - Value: !Ref InstanceType - - ########### EC2 Key Pair ########### - NewKeyPair: - Type: AWS::EC2::KeyPair - Properties: - KeyName: !Sub ${AWS::StackName}-${AWS::Region}-MyKeyPair - - ########### Security Group for Client EC2 ########### - ClientEC2SecurityGroup: - Type: AWS::EC2::SecurityGroup - Properties: - GroupDescription: Allow SSH inbound traffic - VpcId: !Ref VPCId - SecurityGroupIngress: - - IpProtocol: tcp - FromPort: 22 - ToPort: 22 - CidrIp: !Ref AllowedIP - - ########### API Gateway REST API ############### - MyApi: - DependsOn: ApiGatewayVpcLink - Type: AWS::Serverless::Api - Properties: - EndpointConfiguration: PRIVATE - StageName: Prod - AlwaysDeploy: true - DefinitionBody: - openapi: "3.0.1" - info: - title: !Sub "PrivateApi-${AWS::StackName}" - version: "1.0" - x-amazon-apigateway-binary-media-types: - - "*/*" - servers: - - x-amazon-apigateway-endpoint-configuration: - vpcEndpointIds: - - !Ref ExecuteApiVpcEndpoint - paths: - /fargate: - get: - responses: - "200": - description: "200 response" - x-amazon-apigateway-integration: - connectionId: !Ref ApiGatewayVpcLink - httpMethod: "GET" - uri: !Ref AccountBApiGwURL - connectionType: "VPC_LINK" - passthroughBehavior: "when_no_templates" - type: "http" - responses: - default: - statusCode: "200" - /lambda: - get: - responses: - "200": - description: "200 response" - x-amazon-apigateway-integration: - connectionId: !Ref ApiGatewayVpcLink - httpMethod: "GET" - uri: !Ref AccountCApiGwURL - connectionType: "VPC_LINK" - passthroughBehavior: "when_no_templates" - type: "http" - responses: - default: - statusCode: "200" - x-amazon-apigateway-policy: - Version: "2012-10-17" - Statement: - - Effect: "Allow" - Principal: "*" - Action: "execute-api:Invoke" - Resource: "execute-api:/*" - Condition: - StringEquals: - aws:sourceVpce: - - !Ref ExecuteApiVpcEndpoint - - ########### API Gateway VPC Link ############### - ApiGatewayVpcLink: - Type: AWS::ApiGateway::VpcLink - Properties: - Name: VPCLinkRestNlbInternal - TargetArns: - - !Ref MyNLB - - ########### Network Load Balancer (NLB) ############### - MyNLB: - Type: AWS::ElasticLoadBalancingV2::LoadBalancer - Properties: - Type: network - Subnets: - - !Ref PrivateSubnet1 - - !Ref PrivateSubnet2 - SecurityGroups: - - !Ref NLBVpcEndpointSG - EnforceSecurityGroupInboundRulesOnPrivateLinkTraffic: "off" - - ########### NLB Target Group ############### - NLBTargetGroup: - Type: AWS::ElasticLoadBalancingV2::TargetGroup - Properties: - VpcId: !Ref VPCId - Protocol: TCP - Port: 443 - TargetType: ip - Targets: - - Id: !GetAtt GetPrivateIPs.IP0 - - Id: !GetAtt GetPrivateIPs.IP1 - HealthCheckProtocol: TCP - - ########### NLB Listener ############### - NLBListener: - Type: AWS::ElasticLoadBalancingV2::Listener - Properties: - LoadBalancerArn: !Ref MyNLB - Port: 443 - Protocol: TCP - DefaultActions: - - Type: forward - TargetGroupArn: !Ref NLBTargetGroup - - ########### Execute API VPC Endpoint ############### - ExecuteApiVpcEndpoint: - DependsOn: NLBVpcEndpointSG - Type: AWS::EC2::VPCEndpoint - Properties: - ServiceName: !Sub com.amazonaws.${AWS::Region}.execute-api - VpcId: !Ref VPCId - SubnetIds: - - !Ref PrivateSubnet1 - - !Ref PrivateSubnet2 - VpcEndpointType: Interface - PrivateDnsEnabled: true - SecurityGroupIds: - - !Ref NLBVpcEndpointSG - - ########### Security Group for NLB and VPC Endpoint ############### - NLBVpcEndpointSG: - Type: AWS::EC2::SecurityGroup - Properties: - GroupDescription: Security Group for NLB and VPC endpoint - VpcId: !Ref VPCId - SecurityGroupIngress: - - IpProtocol: -1 - SourceSecurityGroupId: !GetAtt ClientEC2SecurityGroup.GroupId - SecurityGroupEgress: - - IpProtocol: -1 - CidrIp: 0.0.0.0/0 - - ########### Security Group Ingress Rule ############### - NLBVpcEndpointSGingress: - Type: AWS::EC2::SecurityGroupIngress - Properties: - GroupId: - Ref: NLBVpcEndpointSG - IpProtocol: tcp - FromPort: '0' - ToPort: '65535' - SourceSecurityGroupId: - Ref: NLBVpcEndpointSG - - ########### Lambda Function to Fetch VPC Endpoint IPs ############### - FetchVPCEndpointIPsFunction: - Type: AWS::Serverless::Function - Properties: - Handler: index.lambda_handler - Runtime: python3.12 - Timeout: 10 - Architectures: - - arm64 - CodeUri: src/ - Handler: lambda_function.lambda_handler - Policies: - - AWSLambdaExecute - - Version: '2012-10-17' - Statement: - - Effect: Allow - Action: - - ec2:DescribeNetworkInterfaces - Resource: "*" - - ########### Custom Resource to Fetch Private IPs ############### - GetPrivateIPs: - DependsOn: - - ExecuteApiVpcEndpoint - Type: Custom::NLBTargets - Properties: - ServiceToken: !GetAtt FetchVPCEndpointIPsFunction.Arn - NetworkInterfaceIds: !GetAtt ExecuteApiVpcEndpoint.NetworkInterfaceIds - -Outputs: - ApiUrlForECS: - Description: API Gateway Invoke URL with GET method for ECS Fargate integration - Value: !Sub https://${MyApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/${MyApi.Stage}/fargate - ApiUrlForLambda: - Description: API Gateway Invoke URL with GET method for Lambda function integration - Value: !Sub https://${MyApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/${MyApi.Stage}/lambda - EC2KeyPairName: - Value: !Ref NewKeyPair - Description: Name of Newly created EC2 Key Pair, that you can use to SSH into the EC2 Instance - EC2PublicIP: - Value: !GetAtt ClientInstance.PublicIp - Description: Public IP address of the EC2 instance. \ No newline at end of file diff --git a/multi-account-private-apigw/example-pattern.json b/multi-account-private-apigw/example-pattern.json index bda1b30e9..e3d948a25 100644 --- a/multi-account-private-apigw/example-pattern.json +++ b/multi-account-private-apigw/example-pattern.json @@ -1,5 +1,5 @@ { - "title": "Enabling East/West Communication in Multi-Account AWS Architectures with Private API Gateway", + "title": "East-West Communication in Multi-Account Setup with Private API Gateway", "description": "Create Private REST API Gateway in multiple accounts and integrate with the central account", "language": "Python", "level": "300", @@ -18,7 +18,7 @@ "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/sfn-athena-cdk-python", "templateURL": "serverless-patterns/multi-account-private-apigw", "projectFolder": "multi-account-private-apigw", - "templateFile": "multi-account-private-apigw/centralAccount/template.yaml" + "templateFile": "centralAccount/template.yaml" } }, "resources": { @@ -74,7 +74,7 @@ "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": "https://www.linkedin.com/in/usama-ali-khan/" + "linkedin": "usama-ali-khan" } ] } \ No newline at end of file diff --git a/multi-account-private-apigw/images/architecture.png b/multi-account-private-apigw/images/architecture.png deleted file mode 100644 index 6df7f6ff4ed71b12ace66233aca7090812229b77..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 81318 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 - AWS SAM template for creating a VPC with 1 Public subnet and 2 Private subnets, with optional user-defined CIDR ranges - -Parameters: - # ########### Parameter for VPC CIDR ########### - VpcCidr: - Type: String - Default: 10.1.0.0/16 - Description: CIDR block for the VPC (e.g., 10.1.0.0/16) - AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$' - - # ########### Parameter for Public Subnet CIDR ########### - PublicSubnetCidr: - Type: String - Default: 10.1.10.0/24 - Description: CIDR block for the Public Subnet (e.g., 10.1.10.0/24) - AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$' - - # ########### Parameter for Private Subnet 1 CIDR ########### - PrivateSubnet1Cidr: - Type: String - Default: 10.1.20.0/24 - Description: CIDR block for the first Private Subnet (e.g., 10.1.20.0/24) - AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$' - - # ########### Parameter for Private Subnet 2 CIDR ########### - PrivateSubnet2Cidr: - Type: String - Default: 10.1.30.0/24 - Description: CIDR block for the second Private Subnet (e.g., 10.1.30.0/24) - AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$' - -Resources: - # ########### VPC ########### - VPC: - Type: AWS::EC2::VPC - Properties: - CidrBlock: !Ref VpcCidr - EnableDnsSupport: true - EnableDnsHostnames: true - Tags: - - Key: Name - Value: !Sub ${AWS::StackName} - - # ########### Public Subnet ########### - PublicSubnet: - Type: AWS::EC2::Subnet - Properties: - VpcId: !Ref VPC - CidrBlock: !Ref PublicSubnetCidr - MapPublicIpOnLaunch: true - AvailabilityZone: !Select [ 0, !GetAZs ] - Tags: - - Key: Name - Value: !Sub ${AWS::StackName}-Public - - # ########### Private Subnet 1 ########### - PrivateSubnet1: - Type: AWS::EC2::Subnet - Properties: - VpcId: !Ref VPC - CidrBlock: !Ref PrivateSubnet1Cidr - AvailabilityZone: !Select [ 0, !GetAZs ] - Tags: - - Key: Name - Value: !Sub ${AWS::StackName}-Private-A - - # ########### Private Subnet 2 ########### - PrivateSubnet2: - Type: AWS::EC2::Subnet - Properties: - VpcId: !Ref VPC - CidrBlock: !Ref PrivateSubnet2Cidr - AvailabilityZone: !Select [ 1, !GetAZs ] - Tags: - - Key: Name - Value: !Sub ${AWS::StackName}-Private-B - - # ########### Internet Gateway ########### - InternetGateway: - Type: AWS::EC2::InternetGateway - Properties: - Tags: - - Key: Name - Value: !Sub ${AWS::StackName}-IGW - - # ########### Attach Gateway to VPC ########### - AttachGateway: - Type: AWS::EC2::VPCGatewayAttachment - Properties: - VpcId: !Ref VPC - InternetGatewayId: !Ref InternetGateway - - # ########### NAT Gateway Elastic IP ########### - NatGatewayEIP: - Type: AWS::EC2::EIP - Properties: - Domain: vpc - - # ########### NAT Gateway ########### - NatGateway: - Type: AWS::EC2::NatGateway - Properties: - AllocationId: !GetAtt NatGatewayEIP.AllocationId - SubnetId: !Ref PublicSubnet - - # ########### Public Route Table ########### - PublicRouteTable: - Type: AWS::EC2::RouteTable - Properties: - VpcId: !Ref VPC - Tags: - - Key: Name - Value: PublicRT - - # ########### Public Route ########### - PublicRoute: - Type: AWS::EC2::Route - Properties: - RouteTableId: !Ref PublicRouteTable - DestinationCidrBlock: 0.0.0.0/0 - GatewayId: !Ref InternetGateway - - # ########### Associate Public Subnet with Route Table ########### - PublicSubnetRouteTableAssociation: - Type: AWS::EC2::SubnetRouteTableAssociation - Properties: - SubnetId: !Ref PublicSubnet - RouteTableId: !Ref PublicRouteTable - - # ########### Private Route ########### - PrivateRoute: - Type: AWS::EC2::Route - Properties: - RouteTableId: !Ref PrivateRouteTable - DestinationCidrBlock: 0.0.0.0/0 - NatGatewayId: !Ref NatGateway - - # ########### Private Route Table ########### - PrivateRouteTable: - Type: AWS::EC2::RouteTable - Properties: - VpcId: !Ref VPC - Tags: - - Key: Name - Value: PrivateRT-A - - # ########### Associate Private Subnet 1 with Route Table ########### - PrivateSubnet1RouteTableAssociation: - Type: AWS::EC2::SubnetRouteTableAssociation - Properties: - SubnetId: !Ref PrivateSubnet1 - RouteTableId: !Ref PrivateRouteTable - - # ########### Associate Private Subnet 2 with Route Table ########### - PrivateSubnet2RouteTableAssociation: - Type: AWS::EC2::SubnetRouteTableAssociation - Properties: - SubnetId: !Ref PrivateSubnet2 - RouteTableId: !Ref PrivateRouteTable - -Outputs: - # ########### VPC Output ########### - VPCId: - Description: VPC ID - Value: !Ref VPC - - # ########### Public Subnet Output ########### - PublicSubnetId: - Description: Public Subnet ID - Value: !Ref PublicSubnet - - # ########### Private Subnet 1 Output ########### - PrivateSubnet1Id: - Description: Private Subnet 1 ID - Value: !Ref PrivateSubnet1 - - # ########### Private Subnet 2 Output ########### - PrivateSubnet2Id: - Description: Private Subnet 2 ID - Value: !Ref PrivateSubnet2 From 2df33345517400992782b46a9f43916119ccdb9e Mon Sep 17 00:00:00 2001 From: usama-khan98 Date: Wed, 27 Nov 2024 16:59:42 +0000 Subject: [PATCH 06/12] fixed ReadMe file, central account template input, removed license paragraph from the code and fixed the title --- .../accountA/template.yaml | 177 ++++++++++++ .../accountB/template.yaml | 75 +++++ .../centralAccount/src/cfnresponse.py | 47 ++++ .../centralAccount/src/lambda_function.py | 33 +++ .../centralAccount/template.yaml | 263 ++++++++++++++++++ .../images/architecture.png | Bin 0 -> 81318 bytes multi-account-private-apigw/vpc/template.yaml | 183 ++++++++++++ 7 files changed, 778 insertions(+) create mode 100644 multi-account-private-apigw/accountA/template.yaml create mode 100644 multi-account-private-apigw/accountB/template.yaml create mode 100644 multi-account-private-apigw/centralAccount/src/cfnresponse.py create mode 100644 multi-account-private-apigw/centralAccount/src/lambda_function.py create mode 100644 multi-account-private-apigw/centralAccount/template.yaml create mode 100644 multi-account-private-apigw/images/architecture.png create mode 100644 multi-account-private-apigw/vpc/template.yaml diff --git a/multi-account-private-apigw/accountA/template.yaml b/multi-account-private-apigw/accountA/template.yaml new file mode 100644 index 000000000..d6576932c --- /dev/null +++ b/multi-account-private-apigw/accountA/template.yaml @@ -0,0 +1,177 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + SAM Template for accountA containing Private API Gateway, VPC Link, Network Load Balancer, and ECS Cluster with Fargate. + +Parameters: + ########### Parameters ########### + VPCId: + Type: AWS::EC2::VPC::Id + Description: The VPC ID where the ECS service will be deployed. + PrivateSubnet1: + Type: AWS::EC2::Subnet::Id + Description: The first private subnet ID within the VPC. + PrivateSubnet2: + Type: AWS::EC2::Subnet::Id + Description: The second private subnet ID within the VPC. + CentralAccountVpcID: + Type: String + Description: The ID of the VPC from the Central Account + +Resources: + ########### Private API Gateway ########### + PrivateApi: + Type: AWS::Serverless::Api + Properties: + EndpointConfiguration: PRIVATE + StageName: Prod + AlwaysDeploy: true + DefinitionBody: + openapi: "3.0.1" + info: + title: !Sub "PrivateApi-${AWS::StackName}" + version: "1.0" + paths: + /: + get: + responses: + "200": + description: "200 response" + x-amazon-apigateway-integration: + connectionId: !Ref ApiGatewayVpcLink + httpMethod: GET + uri: !Sub http://${NLB.DNSName}/ + connectionType: VPC_LINK + passthroughBehavior: when_no_match + type: http_proxy + x-amazon-apigateway-policy: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Principal: "*" + Action: "execute-api:Invoke" + Resource: "execute-api:/*" + Condition: + StringEquals: + aws:sourceVpc: !Ref CentralAccountVpcID + + ########### API Gateway VPC Link ########### + ApiGatewayVpcLink: + Type: AWS::ApiGateway::VpcLink + Properties: + Name: !Sub VPCLinkRestNlbInternal-${AWS::StackName} + TargetArns: + - !Ref NLB + + ########### Network Load Balancer (NLB) ########### + NLB: + Type: AWS::ElasticLoadBalancingV2::LoadBalancer + Properties: + Name: !Sub PrivateNLB-${AWS::StackName} + Scheme: internal + Subnets: + - Ref: PrivateSubnet1 + - Ref: PrivateSubnet2 + Type: network + EnforceSecurityGroupInboundRulesOnPrivateLinkTraffic: "off" + SecurityGroups: + - !Ref NLBSecurityGroup + + ########### NLB Listener ########### + NLBListener: + Type: AWS::ElasticLoadBalancingV2::Listener + Properties: + DefaultActions: + - Type: forward + TargetGroupArn: !Ref NLBTG + LoadBalancerArn: !Ref NLB + Port: 80 + Protocol: TCP + + ########### NLB Target Group ########### + NLBTG: + Type: AWS::ElasticLoadBalancingV2::TargetGroup + Properties: + Name: !Sub FargateNLB-TG-${AWS::StackName} + Port: 80 + Protocol: TCP + VpcId: !Ref VPCId + TargetType: ip + HealthCheckProtocol: HTTP + HealthCheckPort: '80' + HealthCheckPath: '/' + Matcher: + HttpCode: 200 + + ########### ECS Cluster ########### + ECSCluster: + Type: AWS::ECS::Cluster + Properties: + ClusterName: !Sub FargateCluster-${AWS::StackName} + + ########### ECS Fargate Task Definition ########### + ECSFargateTaskDefinition: + Type: AWS::ECS::TaskDefinition + Properties: + Family: FargateTask + Cpu: 256 + Memory: 512 + NetworkMode: awsvpc + RequiresCompatibilities: + - FARGATE + ContainerDefinitions: + - Name: nginx + Image: public.ecr.aws/nginx/nginx:latest + Essential: true + PortMappings: + - ContainerPort: 80 + Protocol: tcp + + ########### ECS Fargate Service ########### + ECSFargateService: + Type: AWS::ECS::Service + DependsOn: NLBListener + Properties: + Cluster: !Ref ECSCluster + TaskDefinition: !Ref ECSFargateTaskDefinition + DesiredCount: 1 + LaunchType: FARGATE + NetworkConfiguration: + AwsvpcConfiguration: + AssignPublicIp: DISABLED + Subnets: + - Ref: PrivateSubnet1 + - Ref: PrivateSubnet2 + SecurityGroups: + - !Ref ECSSecurityGroup + LoadBalancers: + - ContainerName: nginx + ContainerPort: 80 + TargetGroupArn: !Ref NLBTG + + ########### ECS Security Group ########### + ECSSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Security group for ECS Fargate Service + VpcId: !Ref VPCId + SecurityGroupIngress: + - IpProtocol: tcp + FromPort: 80 + ToPort: 80 + SourceSecurityGroupId: !Ref NLBSecurityGroup + + ########### NLB Security Group ########### + NLBSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Security group for the NLB + VpcId: !Ref VPCId + +Outputs: + ########### Outputs ########### + ApiUrl: + Description: "API Gateway Invoke URL" + Value: !Sub "https://${PrivateApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/" + + diff --git a/multi-account-private-apigw/accountB/template.yaml b/multi-account-private-apigw/accountB/template.yaml new file mode 100644 index 000000000..f4d251257 --- /dev/null +++ b/multi-account-private-apigw/accountB/template.yaml @@ -0,0 +1,75 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: | + SAM Template for accountB containing Private API Gateway, with lambda integration. + +Parameters: + ########### Parameters ########### + CentralAccountVpcID: + Type: String + Description: The ID of the VPC Endpoint you want to use from the Central Account + +Resources: + ########### Private API Gateway ########### + 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} + x-amazon-apigateway-binary-media-types: + - '*/*' + paths: + /: + get: + responses: + '200': + description: 200 response + x-amazon-apigateway-integration: + httpMethod: POST + uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaFunction.Arn}/invocations + passthroughBehavior: when_no_match + type: aws_proxy + x-amazon-apigateway-policy: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: '*' + Action: execute-api:Invoke + Resource: execute-api:/* + Condition: + StringEquals: + aws:sourceVpc: !Ref CentralAccountVpcID + + ########### Lambda Function ########### + LambdaFunction: + Type: AWS::Serverless::Function + Properties: + FunctionName: !Sub Lambda-${AWS::StackName} + Handler: index.lambda_handler + Runtime: python3.13 + InlineCode: | + import json + def lambda_handler(event, context): + return { + "statusCode": 200, + "body": json.dumps({ + "message" : "Hello from Lambda!"}) + } + Events: + APIRoot: + Type: Api + Properties: + Path: / + Method: ANY + RestApiId: !Ref PrivateApi + +Outputs: + ApiUrl: + Description: API Gateway Invoke URL + Value: !Sub https://${PrivateApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/ \ No newline at end of file diff --git a/multi-account-private-apigw/centralAccount/src/cfnresponse.py b/multi-account-private-apigw/centralAccount/src/cfnresponse.py new file mode 100644 index 000000000..7531416a0 --- /dev/null +++ b/multi-account-private-apigw/centralAccount/src/cfnresponse.py @@ -0,0 +1,47 @@ +from __future__ import print_function +import urllib3 +import json +import re + +SUCCESS = "SUCCESS" +FAILED = "FAILED" + +http = urllib3.PoolManager() + + +def send(event, context, responseStatus, responseData, physicalResourceId=None, noEcho=False, reason=None): + responseUrl = event['ResponseURL'] + + responseBody = { + 'Status' : responseStatus, + 'Reason' : reason or "See the details in CloudWatch Log Stream: {}".format(context.log_stream_name), + 'PhysicalResourceId' : physicalResourceId or context.log_stream_name, + 'StackId' : event['StackId'], + 'RequestId' : event['RequestId'], + 'LogicalResourceId' : event['LogicalResourceId'], + 'NoEcho' : noEcho, + 'Data' : responseData + } + + json_responseBody = json.dumps(responseBody) + + print("Response body:") + print(json_responseBody) + + headers = { + 'content-type' : '', + 'content-length' : str(len(json_responseBody)) + } + + try: + response = http.request('PUT', responseUrl, headers=headers, body=json_responseBody) + print("Status code:", response.status) + + + except Exception as e: + + print("send(..) failed executing http.request(..):", mask_credentials_and_signature(e)) + +def mask_credentials_and_signature(message): + message = re.sub(r'X-Amz-Credential=[^&\s]+', 'X-Amz-Credential=*****', message, flags=re.IGNORECASE) + return re.sub(r'X-Amz-Signature=[^&\s]+', 'X-Amz-Signature=*****', message, flags=re.IGNORECASE) diff --git a/multi-account-private-apigw/centralAccount/src/lambda_function.py b/multi-account-private-apigw/centralAccount/src/lambda_function.py new file mode 100644 index 000000000..a423a8d31 --- /dev/null +++ b/multi-account-private-apigw/centralAccount/src/lambda_function.py @@ -0,0 +1,33 @@ +import cfnresponse +import json +import boto3 + +def lambda_handler(event, context): + print('REQUEST RECEIVED:\n' + json.dumps(event)) + responseData = {} + + # Handle Delete requests + if event['RequestType'] == 'Delete': + cfnresponse.send(event, context, cfnresponse.SUCCESS, {}) + return + + # Handle Create or Update requests + if event['RequestType'] == 'Create' or event['RequestType'] == 'Update': + try: + ec2 = boto3.resource('ec2') + enis = event['ResourceProperties']['NetworkInterfaceIds'] + + # Fetch private IPs of the network interfaces + for index, eni in enumerate(enis): + network_interface = ec2.NetworkInterface(eni) + responseData[f'IP{index}'] = network_interface.private_ip_address + print(responseData) + + # Send success response back to CloudFormation + cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData) + + except Exception as e: + # Send failure response to CloudFormation with the error message + responseData = {'error': str(e)} + cfnresponse.send(event, context, cfnresponse.FAILED, responseData) + return diff --git a/multi-account-private-apigw/centralAccount/template.yaml b/multi-account-private-apigw/centralAccount/template.yaml new file mode 100644 index 000000000..4bd359454 --- /dev/null +++ b/multi-account-private-apigw/centralAccount/template.yaml @@ -0,0 +1,263 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + SAM Template for central Account containing Private API Gateway, VPC link, Network Load Balancer and execute-api VPC Endpoint. + +Parameters: + InstanceType: + Description: Enter t2.micro or t2.small. Default is t2.micro. + Type: String + AllowedValues: + - t2.micro + - t2.small + Default: t2.micro + ConstraintDescription: Must be either t2.micro or t2.small. + AmiID: + Description: Please provide a valid AMI id to launch EC2 Instance. + Type: AWS::EC2::Image::Id + AllowedIP: + Description: CIDR block to allow SSH access (e.g., 203.0.113.0/24). + Type: String + Default: 0.0.0.0/0 + VPCId: + Description: Please provide a VPC to deploy the solution into. + Type: AWS::EC2::VPC::Id + PublicSubnet: + Description: Please provide the public subnet id to launch EC2 Instance + Type: AWS::EC2::Subnet::Id + PrivateSubnet1: + Description: Please provide the first private subnet id with outbound connectivity within the VPC you selected above. + Type: AWS::EC2::Subnet::Id + PrivateSubnet2: + Description: Please provide the second private subnet id with outbound connectivity within the VPC you selected above. + Type: AWS::EC2::Subnet::Id + ApiGwURLAccountA: + Type: String + Description: The API GW URL from Account A including path + ApiGwURLAccountB: + Type: String + Description: The API GW URL from Account B including path + +Resources: + ########### EC2 Client Instance ########### + ClientInstance: + Type: AWS::EC2::Instance + Properties: + ImageId: !Ref AmiID + InstanceType: !Ref InstanceType + SubnetId: !Ref PublicSubnet + KeyName: !Ref NewKeyPair + SecurityGroupIds: + - !GetAtt ClientEC2SecurityGroup.GroupId + Tags: + - Key: Name + Value: !Join ['-', [!Ref InstanceType, "Client"]] + - Key: InstanceType + Value: !Ref InstanceType + + ########### EC2 Key Pair ########### + NewKeyPair: + Type: AWS::EC2::KeyPair + Properties: + KeyName: !Sub ${AWS::StackName}-${AWS::Region}-MyKeyPair + + ########### Security Group for Client EC2 ########### + ClientEC2SecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Allow SSH inbound traffic + VpcId: !Ref VPCId + SecurityGroupIngress: + - IpProtocol: tcp + FromPort: 22 + ToPort: 22 + CidrIp: !Ref AllowedIP + + ########### API Gateway REST API ############### + MyApi: + DependsOn: ApiGatewayVpcLink + Type: AWS::Serverless::Api + Properties: + EndpointConfiguration: PRIVATE + StageName: Prod + AlwaysDeploy: true + DefinitionBody: + openapi: "3.0.1" + info: + title: !Sub "PrivateApi-${AWS::StackName}" + version: "1.0" + x-amazon-apigateway-binary-media-types: + - "*/*" + servers: + - x-amazon-apigateway-endpoint-configuration: + vpcEndpointIds: + - !Ref ExecuteApiVpcEndpoint + paths: + /fargate: + get: + responses: + "200": + description: "200 response" + x-amazon-apigateway-integration: + connectionId: !Ref ApiGatewayVpcLink + httpMethod: "GET" + uri: !Ref ApiGwURLAccountA + connectionType: "VPC_LINK" + passthroughBehavior: "when_no_templates" + type: "http" + responses: + default: + statusCode: "200" + /lambda: + get: + responses: + "200": + description: "200 response" + x-amazon-apigateway-integration: + connectionId: !Ref ApiGatewayVpcLink + httpMethod: "GET" + uri: !Ref ApiGwURLAccountB + connectionType: "VPC_LINK" + passthroughBehavior: "when_no_templates" + type: "http" + responses: + default: + statusCode: "200" + x-amazon-apigateway-policy: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Principal: "*" + Action: "execute-api:Invoke" + Resource: "execute-api:/*" + Condition: + StringEquals: + aws:sourceVpce: + - !Ref ExecuteApiVpcEndpoint + + ########### API Gateway VPC Link ############### + ApiGatewayVpcLink: + Type: AWS::ApiGateway::VpcLink + Properties: + Name: VPCLinkRestNlbInternal + TargetArns: + - !Ref MyNLB + + ########### Network Load Balancer (NLB) ############### + MyNLB: + Type: AWS::ElasticLoadBalancingV2::LoadBalancer + Properties: + Type: network + Subnets: + - !Ref PrivateSubnet1 + - !Ref PrivateSubnet2 + SecurityGroups: + - !Ref NLBVpcEndpointSG + EnforceSecurityGroupInboundRulesOnPrivateLinkTraffic: "off" + + ########### NLB Target Group ############### + NLBTargetGroup: + Type: AWS::ElasticLoadBalancingV2::TargetGroup + Properties: + VpcId: !Ref VPCId + Protocol: TCP + Port: 443 + TargetType: ip + Targets: + - Id: !GetAtt GetPrivateIPs.IP0 + - Id: !GetAtt GetPrivateIPs.IP1 + HealthCheckProtocol: TCP + + ########### NLB Listener ############### + NLBListener: + Type: AWS::ElasticLoadBalancingV2::Listener + Properties: + LoadBalancerArn: !Ref MyNLB + Port: 443 + Protocol: TCP + DefaultActions: + - Type: forward + TargetGroupArn: !Ref NLBTargetGroup + + ########### Execute API VPC Endpoint ############### + ExecuteApiVpcEndpoint: + DependsOn: NLBVpcEndpointSG + Type: AWS::EC2::VPCEndpoint + Properties: + ServiceName: !Sub com.amazonaws.${AWS::Region}.execute-api + VpcId: !Ref VPCId + SubnetIds: + - !Ref PrivateSubnet1 + - !Ref PrivateSubnet2 + VpcEndpointType: Interface + PrivateDnsEnabled: true + SecurityGroupIds: + - !Ref NLBVpcEndpointSG + + ########### Security Group for NLB and VPC Endpoint ############### + NLBVpcEndpointSG: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Security Group for NLB and VPC endpoint + VpcId: !Ref VPCId + SecurityGroupIngress: + - IpProtocol: -1 + SourceSecurityGroupId: !GetAtt ClientEC2SecurityGroup.GroupId + SecurityGroupEgress: + - IpProtocol: -1 + CidrIp: 0.0.0.0/0 + + ########### Security Group Ingress Rule ############### + NLBVpcEndpointSGingress: + Type: AWS::EC2::SecurityGroupIngress + Properties: + GroupId: + Ref: NLBVpcEndpointSG + IpProtocol: tcp + FromPort: '0' + ToPort: '65535' + SourceSecurityGroupId: + Ref: NLBVpcEndpointSG + + ########### Lambda Function to Fetch VPC Endpoint IPs ############### + FetchVPCEndpointIPsFunction: + Type: AWS::Serverless::Function + Properties: + Handler: index.lambda_handler + Runtime: python3.12 + Timeout: 10 + Architectures: + - arm64 + CodeUri: src/ + Handler: lambda_function.lambda_handler + Policies: + - AWSLambdaExecute + - Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - ec2:DescribeNetworkInterfaces + Resource: "*" + + ########### Custom Resource to Fetch Private IPs ############### + GetPrivateIPs: + DependsOn: + - ExecuteApiVpcEndpoint + Type: Custom::NLBTargets + Properties: + ServiceToken: !GetAtt FetchVPCEndpointIPsFunction.Arn + NetworkInterfaceIds: !GetAtt ExecuteApiVpcEndpoint.NetworkInterfaceIds + +Outputs: + ApiUrlForECS: + Description: API Gateway Invoke URL with GET method for ECS Fargate integration + Value: !Sub https://${MyApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/${MyApi.Stage}/fargate + ApiUrlForLambda: + Description: API Gateway Invoke URL with GET method for Lambda function integration + Value: !Sub https://${MyApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/${MyApi.Stage}/lambda + EC2KeyPairName: + Value: !Ref NewKeyPair + Description: Name of Newly created EC2 Key Pair, that you can use to SSH into the EC2 Instance + EC2PublicIP: + Value: !GetAtt ClientInstance.PublicIp + Description: Public IP address of the EC2 instance. \ No newline at end of file diff --git a/multi-account-private-apigw/images/architecture.png b/multi-account-private-apigw/images/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..6df7f6ff4ed71b12ace66233aca7090812229b77 GIT binary patch literal 81318 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 + AWS SAM template for creating a VPC with 1 Public subnet and 2 Private subnets, with optional user-defined CIDR ranges + +Parameters: + # ########### Parameter for VPC CIDR ########### + VpcCidr: + Type: String + Default: 10.1.0.0/16 + Description: CIDR block for the VPC (e.g., 10.1.0.0/16) + AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$' + + # ########### Parameter for Public Subnet CIDR ########### + PublicSubnetCidr: + Type: String + Default: 10.1.10.0/24 + Description: CIDR block for the Public Subnet (e.g., 10.1.10.0/24) + AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$' + + # ########### Parameter for Private Subnet 1 CIDR ########### + PrivateSubnet1Cidr: + Type: String + Default: 10.1.20.0/24 + Description: CIDR block for the first Private Subnet (e.g., 10.1.20.0/24) + AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$' + + # ########### Parameter for Private Subnet 2 CIDR ########### + PrivateSubnet2Cidr: + Type: String + Default: 10.1.30.0/24 + Description: CIDR block for the second Private Subnet (e.g., 10.1.30.0/24) + AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$' + +Resources: + # ########### VPC ########### + VPC: + Type: AWS::EC2::VPC + Properties: + CidrBlock: !Ref VpcCidr + EnableDnsSupport: true + EnableDnsHostnames: true + Tags: + - Key: Name + Value: !Sub ${AWS::StackName} + + # ########### Public Subnet ########### + PublicSubnet: + Type: AWS::EC2::Subnet + Properties: + VpcId: !Ref VPC + CidrBlock: !Ref PublicSubnetCidr + MapPublicIpOnLaunch: true + AvailabilityZone: !Select [ 0, !GetAZs ] + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-Public + + # ########### Private Subnet 1 ########### + PrivateSubnet1: + Type: AWS::EC2::Subnet + Properties: + VpcId: !Ref VPC + CidrBlock: !Ref PrivateSubnet1Cidr + AvailabilityZone: !Select [ 0, !GetAZs ] + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-Private-A + + # ########### Private Subnet 2 ########### + PrivateSubnet2: + Type: AWS::EC2::Subnet + Properties: + VpcId: !Ref VPC + CidrBlock: !Ref PrivateSubnet2Cidr + AvailabilityZone: !Select [ 1, !GetAZs ] + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-Private-B + + # ########### Internet Gateway ########### + InternetGateway: + Type: AWS::EC2::InternetGateway + Properties: + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-IGW + + # ########### Attach Gateway to VPC ########### + AttachGateway: + Type: AWS::EC2::VPCGatewayAttachment + Properties: + VpcId: !Ref VPC + InternetGatewayId: !Ref InternetGateway + + # ########### NAT Gateway Elastic IP ########### + NatGatewayEIP: + Type: AWS::EC2::EIP + Properties: + Domain: vpc + + # ########### NAT Gateway ########### + NatGateway: + Type: AWS::EC2::NatGateway + Properties: + AllocationId: !GetAtt NatGatewayEIP.AllocationId + SubnetId: !Ref PublicSubnet + + # ########### Public Route Table ########### + PublicRouteTable: + Type: AWS::EC2::RouteTable + Properties: + VpcId: !Ref VPC + Tags: + - Key: Name + Value: PublicRT + + # ########### Public Route ########### + PublicRoute: + Type: AWS::EC2::Route + Properties: + RouteTableId: !Ref PublicRouteTable + DestinationCidrBlock: 0.0.0.0/0 + GatewayId: !Ref InternetGateway + + # ########### Associate Public Subnet with Route Table ########### + PublicSubnetRouteTableAssociation: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + SubnetId: !Ref PublicSubnet + RouteTableId: !Ref PublicRouteTable + + # ########### Private Route ########### + PrivateRoute: + Type: AWS::EC2::Route + Properties: + RouteTableId: !Ref PrivateRouteTable + DestinationCidrBlock: 0.0.0.0/0 + NatGatewayId: !Ref NatGateway + + # ########### Private Route Table ########### + PrivateRouteTable: + Type: AWS::EC2::RouteTable + Properties: + VpcId: !Ref VPC + Tags: + - Key: Name + Value: PrivateRT-A + + # ########### Associate Private Subnet 1 with Route Table ########### + PrivateSubnet1RouteTableAssociation: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + SubnetId: !Ref PrivateSubnet1 + RouteTableId: !Ref PrivateRouteTable + + # ########### Associate Private Subnet 2 with Route Table ########### + PrivateSubnet2RouteTableAssociation: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + SubnetId: !Ref PrivateSubnet2 + RouteTableId: !Ref PrivateRouteTable + +Outputs: + # ########### VPC Output ########### + VPCId: + Description: VPC ID + Value: !Ref VPC + + # ########### Public Subnet Output ########### + PublicSubnetId: + Description: Public Subnet ID + Value: !Ref PublicSubnet + + # ########### Private Subnet 1 Output ########### + PrivateSubnet1Id: + Description: Private Subnet 1 ID + Value: !Ref PrivateSubnet1 + + # ########### Private Subnet 2 Output ########### + PrivateSubnet2Id: + Description: Private Subnet 2 ID + Value: !Ref PrivateSubnet2 From ac5599359f38da8745837cce778b774a24444724 Mon Sep 17 00:00:00 2001 From: usama-khan98 Date: Wed, 27 Nov 2024 19:01:46 +0000 Subject: [PATCH 07/12] added name for central account NLB and configured scheme to be internal --- multi-account-private-apigw/centralAccount/template.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/multi-account-private-apigw/centralAccount/template.yaml b/multi-account-private-apigw/centralAccount/template.yaml index 4bd359454..7140189ba 100644 --- a/multi-account-private-apigw/centralAccount/template.yaml +++ b/multi-account-private-apigw/centralAccount/template.yaml @@ -147,6 +147,8 @@ Resources: MyNLB: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: + Name: !Sub PrivateNLB-${AWS::StackName} + Scheme: internal Type: network Subnets: - !Ref PrivateSubnet1 From 21a80b52d2b9b68113e4b0b9e4731a2b8da9b6c0 Mon Sep 17 00:00:00 2001 From: Usama ali khan <56304192+usama-khan98@users.noreply.github.com> Date: Thu, 28 Nov 2024 11:56:53 +0000 Subject: [PATCH 08/12] Update multi-account-private-apigw/README.md Co-authored-by: Ben <9841563+bfreiberg@users.noreply.github.com> --- multi-account-private-apigw/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multi-account-private-apigw/README.md b/multi-account-private-apigw/README.md index f7ab6e077..09a8b59d3 100644 --- a/multi-account-private-apigw/README.md +++ b/multi-account-private-apigw/README.md @@ -1,4 +1,4 @@ -# Enabling East/West Communication in Multi-Account AWS Architectures with Private API Gateway. +# Enabling East/West Communication in Multi-Account AWS Architectures with Amazon Private API Gateway. ![Architecture Diagram](./images/architecture.png) From ecd967de4bb779e354e097685f9b27725c602681 Mon Sep 17 00:00:00 2001 From: Usama ali khan <56304192+usama-khan98@users.noreply.github.com> Date: Thu, 28 Nov 2024 11:57:35 +0000 Subject: [PATCH 09/12] Update multi-account-private-apigw/README.md Co-authored-by: Ben <9841563+bfreiberg@users.noreply.github.com> --- multi-account-private-apigw/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multi-account-private-apigw/README.md b/multi-account-private-apigw/README.md index 09a8b59d3..e26c35ff8 100644 --- a/multi-account-private-apigw/README.md +++ b/multi-account-private-apigw/README.md @@ -14,7 +14,7 @@ Important: This application uses various AWS Services and there are costs associ ### Requirements -- Three [AWS accounts](https://signin.aws.amazon.com/signup?request_type=register). IAM users with sufficient permissions to make necessary AWS service calls and manage AWS resources. +- Three [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 **centralAccount**, **accountA** and **accountB** so you can run CLI and AWS SAM commands against them. From 400ad0835d1c7e0084fc7eb8e85ddef20f7ad38e Mon Sep 17 00:00:00 2001 From: Usama ali khan <56304192+usama-khan98@users.noreply.github.com> Date: Thu, 28 Nov 2024 11:57:59 +0000 Subject: [PATCH 10/12] Update multi-account-private-apigw/accountB/template.yaml Co-authored-by: Ben <9841563+bfreiberg@users.noreply.github.com> --- multi-account-private-apigw/accountB/template.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multi-account-private-apigw/accountB/template.yaml b/multi-account-private-apigw/accountB/template.yaml index f4d251257..3425302ee 100644 --- a/multi-account-private-apigw/accountB/template.yaml +++ b/multi-account-private-apigw/accountB/template.yaml @@ -1,7 +1,7 @@ AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: | - SAM Template for accountB containing Private API Gateway, with lambda integration. + SAM Template for accountB containing Private API Gateway, with Lambda integration. Parameters: ########### Parameters ########### From 85de61dab4e298571855055f047f905f2dbc1c4a Mon Sep 17 00:00:00 2001 From: Ben <9841563+bfreiberg@users.noreply.github.com> Date: Thu, 28 Nov 2024 12:59:22 +0100 Subject: [PATCH 11/12] Add final pattern file --- .../multi-account-private-apigw.json | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 multi-account-private-apigw/multi-account-private-apigw.json diff --git a/multi-account-private-apigw/multi-account-private-apigw.json b/multi-account-private-apigw/multi-account-private-apigw.json new file mode 100644 index 000000000..86bdfc951 --- /dev/null +++ b/multi-account-private-apigw/multi-account-private-apigw.json @@ -0,0 +1,152 @@ +{ + "title": "East-West Communication in Multi-Account Setup with Private API Gateway", + "description": "Create Private REST API Gateway in multiple accounts and integrate with the central account", + "language": "Python", + "level": "300", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This sample project demonstrates how to enable secure, centralized API communications across multiple AWS accounts using a private Amazon API Gateway. It facilitates east/west communication between services while keeping traffic within the AWS network.", + "The architecture utilizes key AWS services such as Amazon API Gateway (Private), VPC links, Network Load Balancers (NLBs), and Execute-API VPC Endpoints. These services work together to securely route requests between multiple AWS accounts and their respective private APIs.", + "This pattern deploys three separate AWS accounts: a central account hosting the main API Gateway and routing components, an account with an ECS Fargate service behind a private API Gateway, and another account with a Lambda function integration. Each account contains its own AWS resources to ensure proper communication and isolation." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/sfn-athena-cdk-python", + "templateURL": "serverless-patterns/multi-account-private-apigw", + "projectFolder": "multi-account-private-apigw", + "templateFile": "centralAccount/template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "Amazon API Gateway (Private)", + "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": "VPC Links", + "link": "https://docs.aws.amazon.com/vpc/latest/userguide/endpoint-services-overview.html" + }, + { + "text": "Network Load Balancer (NLB)", + "link": "https://docs.aws.amazon.com/elasticloadbalancing/latest/network/introduction.html" + }, + { + "text": "Amazon ECS Fargate", + "link": "https://docs.aws.amazon.com/AmazonECS/latest/developerguide/AWS_Fargate.html" + }, + { + "text": "AWS Lambda", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/welcome.html" + }, + { + "text": "Amazon EC2", + "link": "https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/concepts.html" + } + ] + }, + "deploy": { + "text": [ + "sam deploy --guided --profile PROFILE_NAME" + ] + }, + "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": 15, + "y": 50, + "service": "apigw", + "label": "Central API Gateway" + }, + "icon2": { + "x": 35, + "y": 50, + "service": "vpc-endpoint", + "label": "VPC Link" + }, + "icon3": { + "x": 55, + "y": 25, + "service": "apigw", + "label": "API Gateway" + }, + "icon4": { + "x": 80, + "y": 25, + "service": "lambda", + "label": "Lambda" + }, + "icon5": { + "x": 55, + "y": 65, + "service": "apigw", + "label": "API Gateway" + }, + "icon6": { + "x": 72, + "y": 65, + "service": "vpc-endpoint", + "label": "VPC Link" + }, + "icon7": { + "x": 90, + "y": 65, + "service": "fargate", + "label": "Fargate" + }, + "line1": { + "from": "icon1", + "to": "icon2", + "label": "" + }, + "line2": { + "from": "icon2", + "to": "icon3", + "label": "" + }, + "line3": { + "from": "icon3", + "to": "icon4", + "label": "" + }, + "line4": { + "from": "icon2", + "to": "icon5", + "label": "" + }, + "line5": { + "from": "icon5", + "to": "icon6", + "label": "" + }, + "line6": { + "from": "icon6", + "to": "icon7", + "label": "" + } + } +} From 0064c302f8d89078da405ec94f780ab0d3a753d0 Mon Sep 17 00:00:00 2001 From: Usama ali khan <56304192+usama-khan98@users.noreply.github.com> Date: Wed, 4 Dec 2024 20:58:13 +0000 Subject: [PATCH 12/12] fixed repoURL in example-pattern.json file to point to the correct URL --- multi-account-private-apigw/example-pattern.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/multi-account-private-apigw/example-pattern.json b/multi-account-private-apigw/example-pattern.json index e3d948a25..6d783af6e 100644 --- a/multi-account-private-apigw/example-pattern.json +++ b/multi-account-private-apigw/example-pattern.json @@ -15,7 +15,7 @@ , "gitHub": { "template": { - "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/sfn-athena-cdk-python", + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/multi-account-private-apigw", "templateURL": "serverless-patterns/multi-account-private-apigw", "projectFolder": "multi-account-private-apigw", "templateFile": "centralAccount/template.yaml" @@ -77,4 +77,4 @@ "linkedin": "usama-ali-khan" } ] -} \ No newline at end of file +}