AWSTemplateFormatVersion: '2010-09-09' Description: >- This template deploys services in Ireland. It deploys 1 VPC, with a pair of public and private subnets spread across two Availability Zones. It deploys an internet gateway, with a default route on the public subnets. It deploys a NAT gateway, and default routes for them in the private subnets. An application load balancer, a DynamoDB global table and an AWS Lambda Function. **WARNING** You will be billed for the AWS resources used if you create a stack from this template. Parameters: EnvironmentName: Description: An environment name that is prefixed to resource names Type: String VpcCIDR: Description: Please enter the IP range (CIDR notation) for this VPC Type: String Default: 10.20.0.0/16 PublicSubnet1CIDR: Description: Please enter the IP range (CIDR notation) for the public subnet in the first Availability Zone Type: String Default: 10.20.10.0/24 PublicSubnet2CIDR: Description: Please enter the IP range (CIDR notation) for the public subnet in the second Availability Zone Type: String Default: 10.20.11.0/24 PrivateSubnet1CIDR: Description: Please enter the IP range (CIDR notation) for the private subnet in the first Availability Zone Type: String Default: 10.20.20.0/24 PrivateSubnet2CIDR: Description: Please enter the IP range (CIDR notation) for the private subnet in the second Availability Zone Type: String Default: 10.20.21.0/24 CreateIGW: Description: Variable to choose to create the IGW or not Type: String Default: true AllowedValues: [true,false] CreateNat: Description: Variable to choose to create the IGW or not Type: String Default: true AllowedValues: [true,false] Conditions: ShouldCreateIGW: !Equals [true, !Ref CreateIGW] ShouldCreateNat: !Equals [true, !Ref CreateNat] Resources: #Creation of the VPC in the first region VPC: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref VpcCIDR EnableDnsSupport: true EnableDnsHostnames: true Tags: - Key: Name Value: !Ref EnvironmentName InternetGateway: Type: AWS::EC2::InternetGateway Condition: ShouldCreateIGW Properties: Tags: - Key: Name Value: !Ref EnvironmentName InternetGatewayAttachment: Type: AWS::EC2::VPCGatewayAttachment Condition: ShouldCreateIGW Properties: InternetGatewayId: !Ref InternetGateway VpcId: !Ref VPC PublicSubnet1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: !Select [ 0, !GetAZs '' ] CidrBlock: !Ref PublicSubnet1CIDR MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub ${EnvironmentName} Public Subnet (AZ1) PublicSubnet2: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: !Select [ 1, !GetAZs '' ] CidrBlock: !Ref PublicSubnet2CIDR MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub ${EnvironmentName} Public Subnet (AZ2) PrivateSubnet1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: !Select [ 0, !GetAZs '' ] CidrBlock: !Ref PrivateSubnet1CIDR MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Sub ${EnvironmentName} Private Subnet (AZ1) PrivateSubnet2: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: !Select [ 1, !GetAZs '' ] CidrBlock: !Ref PrivateSubnet2CIDR MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Sub ${EnvironmentName} Private Subnet (AZ2) #Creation of the NAT Gateway in the first region NatGateway1EIP: Type: AWS::EC2::EIP Condition: ShouldCreateNat DependsOn: InternetGatewayAttachment Properties: Domain: vpc NatGateway1: Type: AWS::EC2::NatGateway Condition: ShouldCreateNat Properties: AllocationId: !GetAtt NatGateway1EIP.AllocationId SubnetId: !Ref PublicSubnet1 # Routes and Route Tables configuration in the first region's VPC PublicRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub ${EnvironmentName} Public Routes DefaultPublicRoute: Type: AWS::EC2::Route Condition: ShouldCreateIGW DependsOn: InternetGatewayAttachment Properties: RouteTableId: !Ref PublicRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref InternetGateway PublicSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PublicRouteTable SubnetId: !Ref PublicSubnet1 PublicSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PublicRouteTable SubnetId: !Ref PublicSubnet2 PrivateRouteTable1: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub ${EnvironmentName} Private Routes DefaultPrivateRoute1: Condition: ShouldCreateNat Type: AWS::EC2::Route DependsOn: NatGateway1 Properties: RouteTableId: !Ref PrivateRouteTable1 DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NatGateway1 PrivateSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PrivateRouteTable1 SubnetId: !Ref PrivateSubnet1 PrivateSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PrivateRouteTable1 SubnetId: !Ref PrivateSubnet2 #DynamoDB Global Table creation and a custom resource to add an item to a global table InitLambdaRole: Type: AWS::IAM::Role DependsOn: - DynamoDBGlobalTable Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: "/" Policies: - PolicyName: dynamodbAccessRole PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - logs:DescribeLogStreams - logs:CreateLogStream - logs:PutLogEvents Resource: "*" - Effect: Allow Action: - dynamodb:CreateTable - dynamodb:ListGlobalTables - dynamodb:PutItem - dynamodb:DescribeTable - dynamodb:ListTables - dynamodb:DescribeGlobalTable - dynamodb:GetItem - dynamodb:CreateTableReplica - dynamodb:Scan - dynamodb:Query - dynamodb:UpdateItem Resource: !GetAtt DynamoDBGlobalTable.Arn InitFunction: Type: AWS::Lambda::Function Properties: Runtime: nodejs12.x Role: !GetAtt InitLambdaRole.Arn Handler: index.handler Code: ZipFile: | const AWS = require("aws-sdk"); const response = require("cfn-response"); const docClient = new AWS.DynamoDB.DocumentClient(); exports.handler = function(event, context) { console.log(JSON.stringify(event,null,2)); var params = { TableName: event.ResourceProperties.DynamoTableName, Item:{ "id": "abc123", "Color": "Blue" } }; docClient.put(params, function(err, data) { if (err) { response.send(event, context, "FAILED", {}); } else { response.send(event, context, "SUCCESS", {}); } }); }; DynamoDBGlobalTable: Type: "AWS::DynamoDB::GlobalTable" Properties: AttributeDefinitions: - AttributeName: "id" AttributeType: "S" BillingMode: "PAY_PER_REQUEST" TableName: "GlobalTable" KeySchema: - AttributeName: "id" KeyType: "HASH" StreamSpecification: StreamViewType: "NEW_AND_OLD_IMAGES" Replicas: - Region: "eu-west-1" - Region: "us-east-1" InitializeDynamoDB: Type: AWS::CloudFormation::CustomResource DependsOn: DynamoDBGlobalTable Properties: ServiceToken: Fn::GetAtt: [ InitFunction , "Arn" ] DynamoTableName: Ref: DynamoDBGlobalTable #Lambda to read from DynamoDB Global Table ReadDynamoLambdaRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: "/" ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole - arn:aws:iam::aws:policy/AmazonEC2FullAccess - arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess ReadDynamoLambdaFunction: Type: AWS::Lambda::Function DependsOn: - ReadDynamoLambdaRole - ReadDynamolambdaSG Properties: Description: Lambda to read data from DynamoDB table FunctionName: lambda-read-dynamoDB Handler: index.handler Role: !GetAtt ReadDynamoLambdaRole.Arn Runtime: "nodejs12.x" Timeout: 30 TracingConfig: Mode: Active VpcConfig: SubnetIds: - !Ref PrivateSubnet1 - !Ref PrivateSubnet2 SecurityGroupIds: - !Ref ReadDynamolambdaSG Code: ZipFile: | const AWS = require('aws-sdk'); const dynamodb = new AWS.DynamoDB({apiVersion: '2012-08-10'}); exports.handler = (event, context, callback) => { var params = { Key: { "id": { S: "abc123" } }, TableName: "GlobalTable" }; dynamodb.getItem(params, function(err, data) { if (err) { console.log(err, err.stack); callback(null, { statusCode: '500', body: err }); } else { callback(null, { statusCode: 200, isBased64Encoded: false, body: "

Global Accelerator as disaster Recovery

This is the item of your Amazon DynamoDB Table: " + JSON.stringify(data) + "and the data was retrieved in a serverless fashion

From the Ireland Region

", headers: { "Content-Type": "text/html; charset=utf-8" } }); } }); }; ReadDynamolambdaSG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Allow http to client host VpcId: !Ref VPC SecurityGroupEgress: - IpProtocol: "-1" CidrIp: 0.0.0.0/0 #ALB creation LambdaInvokePermission: Type: AWS::Lambda::Permission DependsOn: ReadDynamoLambdaFunction Properties: FunctionName: !Ref ReadDynamoLambdaFunction Action: 'lambda:InvokeFunction' Principal: elasticloadbalancing.amazonaws.com ALBListener: Type: "AWS::ElasticLoadBalancingV2::Listener" DependsOn: - ApplicationLoadBalancer - ApplicationLoadBalancerTargetGroup Properties: LoadBalancerArn: !Ref ApplicationLoadBalancer Port: 80 Protocol: "HTTP" DefaultActions: - TargetGroupArn: !Ref ApplicationLoadBalancerTargetGroup Type: "forward" ApplicationLoadBalancer: Type: "AWS::ElasticLoadBalancingV2::LoadBalancer" DependsOn: - PrivateSubnet1 - PrivateSubnet2 - ALBSecurityGroup Properties: Name: "private-alb-to-lambda" Scheme: "internal" Type: "application" Subnets: - !Ref PrivateSubnet1 - !Ref PrivateSubnet2 SecurityGroups: - !Ref ALBSecurityGroup IpAddressType: "ipv4" LoadBalancerAttributes: - Key: "access_logs.s3.enabled" Value: "false" - Key: "idle_timeout.timeout_seconds" Value: "60" - Key: "deletion_protection.enabled" Value: "false" - Key: "routing.http2.enabled" Value: "true" - Key: "routing.http.drop_invalid_header_fields.enabled" Value: "false" - Key: "routing.http.desync_mitigation_mode" Value: "defensive" - Key: "waf.fail_open.enabled" Value: "false" - Key: "routing.http.x_amzn_tls_version_and_cipher_suite.enabled" Value: "false" ALBSecurityGroup: Type: "AWS::EC2::SecurityGroup" Properties: GroupDescription: "alb-access" GroupName: "alb-access" VpcId: !Ref VPC SecurityGroupIngress: - CidrIp: "0.0.0.0/0" FromPort: 80 IpProtocol: "tcp" ToPort: 80 SecurityGroupEgress: - CidrIp: "0.0.0.0/0" IpProtocol: "-1" ApplicationLoadBalancerTargetGroup: Type: "AWS::ElasticLoadBalancingV2::TargetGroup" DependsOn: ReadDynamoLambdaFunction Properties: HealthCheckIntervalSeconds: 35 HealthCheckPath: "/" HealthCheckTimeoutSeconds: 30 UnhealthyThresholdCount: 2 TargetType: "lambda" Matcher: HttpCode: "200" HealthyThresholdCount: 5 Name: "lambda-TG-private" HealthCheckEnabled: false TargetGroupAttributes: - Key: "lambda.multi_value_headers.enabled" Value: "false" Targets: - Id: !GetAtt ReadDynamoLambdaFunction.Arn AvailabilityZone: "all" Outputs: VPC: Description: A reference to the created VPC Value: !Ref VPC PublicSubnets: Description: A list of the public subnets Value: !Join [ ",", [ !Ref PublicSubnet1, !Ref PublicSubnet2 ]] PrivateSubnets: Description: A list of the private subnets Value: !Join [ ",", [ !Ref PrivateSubnet1, !Ref PrivateSubnet2 ]] LoadBalancer: Description: The arn of the load balancer in Ireland region Value: !Ref ApplicationLoadBalancer