Created
February 5, 2019 09:09
-
-
Save a-h/153bc018f2ce3d82cb69c196f5601ebd to your computer and use it in GitHub Desktop.
Apex redirect handling using AWS Network and Application Load Balancers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- | |
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