Skip to content

Instantly share code, notes, and snippets.

@a-h
Created February 5, 2019 09:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save a-h/153bc018f2ce3d82cb69c196f5601ebd to your computer and use it in GitHub Desktop.
Save a-h/153bc018f2ce3d82cb69c196f5601ebd to your computer and use it in GitHub Desktop.
Apex redirect handling using AWS Network and Application Load Balancers
---
AWSTemplateFormatVersion: '2010-09-09'
Description: Sets up the required resources for the website at example.com
Parameters:
DomainName:
Type: String
Description: The website domain name.
Default: example.co.uk
RedirectTo:
Type: String
Description: The Application Load Balancer redirect, typically from example.com to the www.example.com CloudFront distribution. Not used in dev.
Default: www.example.co.uk
ALBCertificateArn:
Type: String
Description: ARN of the SSL certificate used for the Application Load Balancer redirect (must be in the local region).
CloudFrontCertificateArn:
Type: String
Description: ARN of the SSL certificate used for the CloudFront distribution (must be in us-east-1).
WebsiteCloudFrontViewerRequestLambdaFunctionARN:
Type: String
Description: ARN of the Lambda@Edge function that does rewriting of URLs (must be in us-east-1). See lambda_at_edge.js
Stage:
Type: String
Description: Deployment stage
Default: prod
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.1.0.0/16
Tags:
- Key: Name
Value: !Ref DomainName
InternetGateway:
Type: AWS::EC2::InternetGateway
DependsOn: VPC
AttachGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
PublicSubnetA:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.1.10.0/24
AvailabilityZone: !Select [ 0, !GetAZs ]
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-public-a
PublicSubnetB:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.1.20.0/24
AvailabilityZone: !Select [ 1, !GetAZs ]
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-public-b
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: Public
PublicRouteToInternet:
Type: AWS::EC2::Route
DependsOn: AttachGateway
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PublicSubnetARouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetA
RouteTableId: !Ref PublicRouteTable
PublicSubnetBRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetB
RouteTableId: !Ref PublicRouteTable
AllowAllWebSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow all Web traffic on ports 80 and 443
VpcId:
Ref: VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
NetworkLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Sub ${AWS::StackName}-nlb
SubnetMappings:
- AllocationId: !GetAtt
- NetworkLoadBalancerIP1
- AllocationId
SubnetId: !Ref PublicSubnetA
- AllocationId: !GetAtt
- NetworkLoadBalancerIP2
- AllocationId
SubnetId: !Ref PublicSubnetB
Type: network
NetworkLoadBalancerIP1:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
NetworkLoadBalancerIP2:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
NetworkLoadBalancerListener80:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref NetworkLoadBalancerTargetGroup80
LoadBalancerArn: !Ref NetworkLoadBalancer
Port: 80
Protocol: TCP
NetworkLoadBalancerListener443:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref NetworkLoadBalancerTargetGroup443
LoadBalancerArn: !Ref NetworkLoadBalancer
Port: 443
Protocol: TCP
NetworkLoadBalancerTargetGroup80:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Port: 80
Protocol: TCP
# Targets are specified by a Lambda which regularly gets the IP addresses
# of the ApplicationLoadBalancer.
TargetType: ip
VpcId: !Ref VPC
NetworkLoadBalancerTargetGroup443:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Port: 443
Protocol: TCP
# Targets are specified by a Lambda which regularly gets the IP addresses
# of the ApplicationLoadBalancer.
TargetType: ip
VpcId: !Ref VPC
ApplicationLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Sub ${AWS::StackName}-alb-int
Scheme: internal
SecurityGroups:
- Ref: AllowAllWebSG
Subnets:
- !Ref PublicSubnetA
- !Ref PublicSubnetB
Type: application
ApplicationLoadBalancerListener80:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- Type: redirect
RedirectConfig:
Host: !Ref RedirectTo
Protocol: HTTPS
Port: 443
StatusCode: HTTP_302
LoadBalancerArn: !Ref ApplicationLoadBalancer
Port: 80
Protocol: HTTP
ApplicationLoadBalancerListener443:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- Type: redirect
RedirectConfig:
Host: !Ref RedirectTo
Protocol: HTTPS
Port: 443
StatusCode: HTTP_302
LoadBalancerArn: !Ref ApplicationLoadBalancer
Port: 443
Protocol: HTTPS
Certificates:
- CertificateArn: !Ref ALBCertificateArn
WebsiteBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref 'DomainName'
WebsiteCloudFrontOriginAccessIdentity:
Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment: !Sub 'CloudFront OAI for ${DomainName}'
WebsiteBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref WebsiteBucket
PolicyDocument:
Statement:
-
Action:
- s3:GetObject
Effect: Allow
Resource: !Join [ "", [ "arn:aws:s3:::", !Ref WebsiteBucket, "/*" ] ]
Principal:
CanonicalUser: !GetAtt WebsiteCloudFrontOriginAccessIdentity.S3CanonicalUserId
WebsiteCloudfront:
Type: AWS::CloudFront::Distribution
DependsOn:
- WebsiteBucket
Properties:
DistributionConfig:
Comment: !Ref 'DomainName'
Origins:
- DomainName: !GetAtt WebsiteBucket.DomainName
Id: website-s3-bucket
S3OriginConfig:
OriginAccessIdentity:
!Join [ "", [ "origin-access-identity/cloudfront/", !Ref WebsiteCloudFrontOriginAccessIdentity ] ]
Aliases:
- !Ref 'DomainName'
- !Ref 'RedirectTo'
DefaultCacheBehavior:
ViewerProtocolPolicy: redirect-to-https
TargetOriginId: website-s3-bucket
Compress: true
ForwardedValues:
QueryString: true
LambdaFunctionAssociations:
- EventType: viewer-request
LambdaFunctionARN: !Ref WebsiteCloudFrontViewerRequestLambdaFunctionARN
ViewerCertificate:
AcmCertificateArn: !Ref CloudFrontCertificateArn
MinimumProtocolVersion: TLSv1.2_2018
SslSupportMethod: sni-only
Enabled: true
HttpVersion: http2
DefaultRootObject: index.html
IPV6Enabled: true
CustomErrorResponses:
- ErrorCode: 403
ResponseCode: 404
ResponsePagePath: '/error/index.html'
PriceClass: PriceClass_100
Tags:
-
Key: Name
Value: !Ref 'DomainName'
-
Key: Environment
Value: !Ref 'Stage'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment