diff --git a/apigw-geo-location-failover/README.md b/apigw-geo-location-failover/README.md new file mode 100644 index 000000000..6765d7234 --- /dev/null +++ b/apigw-geo-location-failover/README.md @@ -0,0 +1,129 @@ +# Amazon API Gateway GeoLocation REST API Disaster Recovery + +Customers often want to access the resources from the same country where user resides and this pattern will help them to achieve the same. This demo also demonstrates an Amazon API Gateway multi-region active-passive public API that proxies two independent multi-region active-passive Lambda function. + +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 for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +# Requirements + +* Create an AWS account if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* AWS CLI installed and configured +* Git Installed +* AWS Serverless Application Model (AWS SAM) installed + + +# Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` + + +2. Change directory to: + + ``` + cd apigw-geo-location-failover + ``` + +3. From the command line, use AWS SAM to deploy the AWS resources for the stack as specified in the template.yml file on the primary region: + + ``` + sam deploy -—guided —config-env primary + ``` + + - During the prompts: + + * **Stack Name:** Enter a stack name. + * **AWS Region:** Enter the desired secondary AWS Region. This stack has been tested with both us-east-1 and us-east-2. **Make sure to use a different region from the primary one but within the same country**. + * **PublicHostedZoneId:** You must have a public hosted zone in Route 53 with your domain name (i.e. mydomain.com). Enter the Hosted Zone Id for this hosted zone. + * **DomainName:** Enter your custom domain name (i.e. externalapi.mydomain.com). + * **CertificateArn** You must have an ACM certificate that covers your custom domain namespace (i.e. *.mydomain.com) on the region your are deploying this stack. Enter the ARN for this certificate here. **Make sure you are getting the certificate arn for the right region**. + * **FailoverDomainName:** Enter your failover domain name (i.e. failover.mydomain.com) + * **Priority:** Enter “SECONDARY” here + * **GeoLocationValue:** Enter the country code + * Allow SAM CLI to create IAM roles with the required permissions. + * Allow SAM CLI to create the LambdaRegionalApi Lambda function. + * **SAM configuration environment** Accept the **secondary** default value. + + Once you have run `sam deploy --guided --config-env primary` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy --config-env primary` in future to use these defaults. + + +4. From the command line, use AWS SAM to deploy the AWS resources for the stack as specified in the template.yml file on the secondary region: + + ``` + sam deploy —guided —config-env secondary + ``` + + - During the prompts: + + * **Stack Name:** Enter a stack name. + * **AWS Region:** Enter the desired secondary AWS Region. This stack has been tested with both us-east-1 and us-east-2. **Make sure to use a different region from the primary one but within the same country**. + * **PublicHostedZoneId:** You must have a public hosted zone in Route 53 with your domain name (i.e. mydomain.com). Enter the Hosted Zone Id for this hosted zone. + * **DomainName:** Enter your custom domain name (i.e. externalapi.mydomain.com). + * **CertificateArn** You must have an ACM certificate that covers your custom domain namespace (i.e. *.mydomain.com) on the region your are deploying this stack. Enter the ARN for this certificate here. **Make sure you are getting the certificate arn for the right region**. + * **FailoverDomainName:** Enter your failover domain name (i.e. failover.mydomain.com) + * **Priority:** Enter “SECONDARY” here + * **GeoLocationValue:** Enter the country code + * Allow SAM CLI to create IAM roles with the required permissions. + * Allow SAM CLI to create the LambdaRegionalApi Lambda function. + * **SAM configuration environment** Accept the **secondary** default value. + + Once you have run `sam deploy --guided --config-env secondary` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy --config-env secondary` in future to use these defaults. + +``` + Note the output from the SAM deployment process. These contain details which are used for testing. +``` + +# How it works + +1. You will deploy the same template in two separate regions: + +- When you deploy template in the Primary region, it will create a custom domain name , Geolocation route of custom domain name mapped to failover route, a Primary Record for failover routing mapped to API gateway domain name and Lambda function integrated with API gateway. +- When you deploy template in the Secondary region, it will create a custom domain name, a Primary Record for failover routing mapped to API Gateway domain name and Lambda function integrated with API gateway. + + + 2. If an issue with the primary region occurs, you can user Amazon Route53 ARC to route traffic to the secondary region. + + 3. This example demonstrates the failover only and does not encompass authentication and data for the multiple regions. + +Testing + +Once the stack is deployed, get the custom domain name output parameter. +Paste the URL in a browser, or in Postman, or using the curl command. +Eg: + +``` +curl https://externalapi.mydomain.com/ +``` + +You should see a response similar to when request made from the same country as GeoLocation: + +``` +{"message" : "Hello World! This is the regional API"} +``` + +You should see a response similar to when request made from the same country as GeoLocation: + +``` + Could not resolve host: https + Closing connection + curl: (6) Could not resolve host: https +``` + + + +# Cleanup + +1. Delete the stack on the primary region. + + ``` + sam delete --config-env primary + ``` + +2. Delete the stack on the secondary region. + + ``` + sam delete --config-env secondary + ``` diff --git a/apigw-geo-location-failover/example-pattern.json b/apigw-geo-location-failover/example-pattern.json new file mode 100644 index 000000000..7bb7fff47 --- /dev/null +++ b/apigw-geo-location-failover/example-pattern.json @@ -0,0 +1,45 @@ +{ + "title": "Amazon API Gateway GeoLocation REST API Disaster Recovery”, + "description": "The SAM template deploys a multi-region active-passive public API that proxies two independent multi-region active-passive Lambda function ”, + "language": "Python", + "level": "200", + "framework": "SAM", + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/apigw-geo-location-failover", + "templateURL": "serverless-patterns/apigw-geo-location-failover", + "projectFolder": "apigw-geo-location-failover", + "templateFile": "template.yaml" + } + }, + "deploy": { + "text": [ + "sam deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: sam delete." + ] + }, + "authors": [ + { + "name": "Manasvi Jain", + "image": "https://avatars.githubusercontent.com/u/56217984?v=4", + "bio": "Cloud Engineer at AWS", + "linkedin": "https://www.linkedin.com/in/manasvi-jain-36b9941a3/" + }, + { + "name": "Umang Aggarwal", + "image": "https://avatars.githubusercontent.com/Umang071", + "bio": "Cloud Engineer II @ AWS", + "linkedin": "https://www.linkedin.com/in/umangaggarwal" + } + ] +} + diff --git a/apigw-geo-location-failover/template.yaml b/apigw-geo-location-failover/template.yaml new file mode 100644 index 000000000..845e299ea --- /dev/null +++ b/apigw-geo-location-failover/template.yaml @@ -0,0 +1,128 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: Serverless patterns - Amazon API Gateway Custom Domain Name (uksb-1tthgi812) (tag:apigw-custom-domain-regional) + +Parameters: + + DomainName: + Type: String + + CertificateArn: + Type: String + + PublicHostedZoneId: + Type: String + + FailoverDomainName: + Type: String + + GeoLocationValue: + Type: String + + Priority: + Type: String + Description: PRIMARY or SECONDARY + AllowedValues: ["PRIMARY", "SECONDARY"] + +Conditions: + CreatePrimaryResources: !Equals + - !Ref Priority + - PRIMARY + +Resources: + #Lambda function + LambdaRegionalApi: + Type: AWS::Serverless::Function + Properties: + Handler: index.lambda_handler + InlineCode: | + import json + def lambda_handler(event, context): + return { + "statusCode": 200, + "body": json.dumps({ + "message" : "Hello World! This is the regional API"}) + } + Runtime: python3.9 + Events: + HelloWorld: + Type: Api + Properties: + Path: / + Method: get + RestApiId: + Ref: ApiRegional + + #REST Regional API + ApiRegional: + Type: AWS::Serverless::Api + Properties: + StageName: prod + EndpointConfiguration: REGIONAL + + CustomDomainRegional: + Type: AWS::ApiGatewayV2::DomainName + Properties: + DomainName: !Ref DomainName + DomainNameConfigurations: + - SecurityPolicy: TLS_1_2 + CertificateArn: !Ref CertificateArn + EndpointType: REGIONAL + + MappingRegional: + Type: AWS::ApiGatewayV2::ApiMapping + DependsOn: + - CustomDomainRegional + Properties: + DomainName: !Ref DomainName + ApiId: !Ref ApiRegional + Stage: !Ref ApiRegional.Stage + + HealthCheck: + Type: AWS::Route53::HealthCheck + Properties: + HealthCheckConfig: + EnableSNI: true + FullyQualifiedDomainName: !Sub ${ApiRegional}.execute-api.${AWS::Region}.amazonaws.com + Type: HTTPS + FailureThreshold: 3 ## Default is 3 + MeasureLatency: true ## Cannot be changed after creation + ResourcePath: /Prod + + FailoverRecord: + Type: AWS::Route53::RecordSet + DependsOn: + - CustomDomainRegional + Properties: + Failover: !Ref Priority + Name: !Ref FailoverDomainName + HostedZoneId: !Ref PublicHostedZoneId + HealthCheckId: !Ref HealthCheck + SetIdentifier: !Sub ${AWS::Region}-failover + AliasTarget: + DNSName: !GetAtt CustomDomainRegional.RegionalDomainName + HostedZoneId: !GetAtt CustomDomainRegional.RegionalHostedZoneId + Type: A + + GeoLocationRecord: + Condition: CreatePrimaryResources + Type: AWS::Route53::RecordSet + DependsOn: + - FailoverRecord + Properties: + GeoLocation: + CountryCode: !Ref GeoLocationValue + Name: !Ref DomainName + SetIdentifier: !Sub ${AWS::Region}-geolocation + HostedZoneId: !Ref PublicHostedZoneId + AliasTarget: + DNSName: !Ref FailoverDomainName + HostedZoneId: !Ref PublicHostedZoneId + Type: A + +Outputs: + + # Custom Domain Name endpoint to be used during tests + CustomDomainNameEndpoint: + Description: Custom Domain Name endpoint + Value: !Sub "https://${DomainName}"/