AWSTemplateFormatVersion: 2010-09-09 Description: VPC stack for Swift build stack Parameters: ImageId: Type: AWS::EC2::Image::Id Default: ami-06d51e91cea0dac8d Resources: WebAppVPC: Type: AWS::EC2::VPC Properties: CidrBlock: 10.0.0.0/16 EnableDnsSupport: 'true' EnableDnsHostnames: 'true' Tags: - Key: Name Value: !Sub "${AWS::StackName}-VPC" WebAppVPCIGW: Type: AWS::EC2::InternetGateway WebAppVPCIGWAttach: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref WebAppVPC InternetGatewayId: !Ref WebAppVPCIGW PublicRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref WebAppVPC Tags: - Key: Name Value: Public Route Table PublicEgressRoute: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PublicRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref WebAppVPCIGW WebAppPublicSubnetA: Type: AWS::EC2::Subnet Properties: VpcId: Ref: WebAppVPC CidrBlock: 10.0.1.0/24 AvailabilityZone: Fn::Select: - 0 - Fn::GetAZs: "" Tags: - Key: Name Value: Public Subnet A WebAppPublicSubnetAAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref WebAppPublicSubnetA RouteTableId: !Ref PublicRouteTable WebAppPublicSubnetB: Type: AWS::EC2::Subnet Properties: VpcId: Ref: WebAppVPC CidrBlock: 10.0.2.0/24 AvailabilityZone: Fn::Select: - 1 - Fn::GetAZs: "" Tags: - Key: Name Value: Public Subnet B WebAppPublicSubnetBAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref WebAppPublicSubnetB RouteTableId: !Ref PublicRouteTable WebAppNAT: Type: AWS::EC2::NatGateway Properties: AllocationId: !GetAtt WebAppNATEIP.AllocationId SubnetId: !Ref WebAppPublicSubnetA WebAppNATEIP: DependsOn: WebAppVPCIGWAttach Type: AWS::EC2::EIP Properties: Domain: vpc PrivateRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref WebAppVPC Tags: - Key: Name Value: Private Route Table PrivateEgressRoute: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PrivateRouteTable DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref WebAppNAT WebAppPrivateSubnetA: Type: AWS::EC2::Subnet Properties: VpcId: Ref: WebAppVPC CidrBlock: 10.0.3.0/24 AvailabilityZone: Fn::Select: - 0 - Fn::GetAZs: "" Tags: - Key: Name Value: Private Subnet A WebAppPrivateSubnetAAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref WebAppPrivateSubnetA RouteTableId: !Ref PrivateRouteTable WebAppPrivateSubnetB: Type: AWS::EC2::Subnet Properties: VpcId: Ref: WebAppVPC CidrBlock: 10.0.4.0/24 AvailabilityZone: Fn::Select: - 1 - Fn::GetAZs: "" Tags: - Key: Name Value: Private Subnet B WebAppPrivateSubnetBAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref WebAppPrivateSubnetB RouteTableId: !Ref PrivateRouteTable WebAppLaunchRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Action: ['sts:AssumeRole'] Effect: Allow Principal: Service: [ec2.amazonaws.com] Version: '2012-10-17' Path: / Policies: - PolicyName: !Sub "${AWS::StackName}-WebAppLaunch" PolicyDocument: Version: '2012-10-17' Statement: - Action: - 'ssmmessages:CreateControlChannel' - 'ssmmessages:CreateDataChannel' - 'ssmmessages:OpenControlChannel' - 'ssmmessages:OpenDataChannel' - 'ssm:UpdateInstanceInformation' Effect: Allow Resource: '*' - Action: - 'cloudformation:DescribeStackResource' - 'cloudformation:SignalResource' Effect: Allow Resource: !Sub "arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}/*" - Action: - 'logs:CreateLogStream' - 'logs:PutLogEvents' - 'logs:DescribeLogGroups' - 'logs:DescribeLogStreams' Effect: Allow Resource: '*' - Action: - 's3:Get*' - 's3:List*' Effect: Allow Resource: '*' - Action: - 's3:GetEncryptionConfiguration' Effect: Allow Resource: '*' WebAppLaunchProfile: Type: AWS::IAM::InstanceProfile Properties: InstanceProfileName: !Sub "${AWS::StackName}-InstanceProfile" Roles: - !Ref WebAppLaunchRole WebAppLaunchTemplate: Type: AWS::EC2::LaunchTemplate Metadata: AWS::CloudFormation::Init: config: files: /etc/systemd/system/swiftapp.service: content: | [Unit] Description=Swift demo service After=network.target StartLimitIntervalSec=0 [Service] Type=simple Restart=always RestartSec=1 User=vapor WorkingDirectory=/opt/app ExecStart=/opt/app/Run --env production --hostname 0.0.0.0 [Install] WantedBy=multi-user.target Properties: LaunchTemplateName: !Sub ${AWS::StackName}-launch-template LaunchTemplateData: CreditSpecification: CpuCredits: Unlimited ImageId: !Ref ImageId InstanceType: t3.small Monitoring: Enabled: true SecurityGroupIds: - !Ref WebAppSecurityGroup IamInstanceProfile: Arn: !GetAtt WebAppLaunchProfile.Arn UserData: Fn::Base64: !Sub | #!/bin/bash -xe apt-get -y update apt-get install -y python-pip awscli ruby libatomic1 clang pkg-config libicu-dev libpython2.7 libxml2-dev wget git libssl-dev uuid-dev libsqlite3-dev libpq-dev libmysqlclient-dev libbson-dev libmongoc-dev libcurl4-openssl-dev pip install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz echo Setting up AWS Metadata... /usr/local/bin/cfn-init -v --stack ${AWS::StackName} --resource WebAppLaunchTemplate --region ${AWS::Region} systemctl daemon-reload echo Installing Swift... cd $(mktemp -d) wget https://swift.org/builds/swift-5.1.2-release/ubuntu1804/swift-5.1.2-RELEASE/swift-5.1.2-RELEASE-ubuntu18.04.tar.gz gunzip < swift-5.1.2-RELEASE-ubuntu18.04.tar.gz | tar -C / -xv --strip-components 1 useradd -r vapor echo Installing CodeDeploy... cd $(mktemp -d) aws s3 cp s3://aws-codedeploy-${AWS::Region}/latest/install . --region ${AWS::Region} chmod +x ./install ./install auto service codedeploy-agent start WebAppASG: Type: AWS::AutoScaling::AutoScalingGroup Properties: AutoScalingGroupName: !Sub "${AWS::StackName}-WebAppASG" MinSize: 2 MaxSize: 3 DesiredCapacity: 2 HealthCheckGracePeriod: 300 TargetGroupARNs: - !Ref EC2TargetGroup LaunchTemplate: LaunchTemplateId: !Ref WebAppLaunchTemplate Version: !GetAtt WebAppLaunchTemplate.LatestVersionNumber VPCZoneIdentifier: - !Ref WebAppPrivateSubnetA - !Ref WebAppPrivateSubnetB Tags: - Key: Name Value: !Sub "${AWS::StackName}-WebApp" PropagateAtLaunch: true WebAppSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: WebApp Instance Security Group GroupDescription: 'WebApp SecurityGroup' VpcId: !Ref WebAppVPC SecurityGroupIngress: - IpProtocol: tcp FromPort: 8080 ToPort: 8080 SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup LoadBalancerSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: WebApp Load Balancer Security Group GroupDescription: 'WebApp SecurityGroup' VpcId: !Ref WebAppVPC SecurityGroupIngress: - IpProtocol: "tcp" FromPort: 80 ToPort: 80 CidrIp: "0.0.0.0/0" EC2TargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: HealthCheckEnabled: true Name: SwiftEC2Targets Protocol: HTTP Port: 8080 VpcId: !Ref WebAppVPC EC2LoadBalancer: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: SecurityGroups: - !Ref LoadBalancerSecurityGroup Subnets: - !Ref WebAppPublicSubnetA - !Ref WebAppPublicSubnetB EC2LBListener: Type: AWS::ElasticLoadBalancingV2::Listener Properties: DefaultActions: - Type: forward TargetGroupArn: !Ref EC2TargetGroup LoadBalancerArn: !Ref EC2LoadBalancer Port: 80 Protocol: HTTP Outputs: WebAppASG: Value: !Ref WebAppASG PublicSubnetA: Value: !Ref WebAppPublicSubnetA PublicSubnetB: Value: !Ref WebAppPublicSubnetB PrivateSubnetA: Value: !Ref WebAppPrivateSubnetA PrivateSubnetB: Value: !Ref WebAppPrivateSubnetB VPC: Value: !Ref WebAppVPC Ec2LbUrl: Value: !Sub "http://${EC2LoadBalancer.DNSName}/"