{ "AWSTemplateFormatVersion": "2010-09-09", "Description": "QS(5004) Create an EC2 Instance with a local development environment for Swift pre-installed. Contains a Docker configuration to allow easy deployment to Amazon ECS.", "Mappings": { "RegionMap": { "us-east-1": { "BastionAMI": "ami-80861296", "ClusterAMI": "ami-55870742" }, "us-east-2": { "BastionAMI": "ami-618fab04", "ClusterAMI": "ami-44164c21" }, "us-west-1": { "BastionAMI": "ami-2afbde4a", "ClusterAMI": "ami-07713767" }, "us-west-2": { "BastionAMI": "ami-efd0428f", "ClusterAMI": "ami-241bd844" } } }, "Outputs": { "BastionIp": { "Description": "Public IP address of the newly created EC2 instance", "Value": { "Fn::GetAtt": [ "BastionInstance", "PublicIp" ] } }, "ClusterIp": { "Description": "Public IP address of the ECS cluster instance", "Value": { "Fn::GetAtt": [ "ClusterInstance", "PublicIp" ] } }, "DatabaseEndpoint": { "Description": "Public IP address of the newly created EC2 instance", "Value": { "Fn::GetAtt": [ "DBinstance", "Endpoint.Address" ] } }, "PushPreBuiltImage": { "Description": "Push the pre-built Docker image to the ECS Repository", "Value": { "Fn::Join": [ "", [ "docker push ", { "Fn::Join": [ "", [ { "Ref": "AWS::AccountId" }, ".dkr.ecr.", { "Ref": "AWS::Region" }, ".amazonaws.com/", { "Ref": "ECSRepository" } ] ] }, ":latest" ] ] } }, "RepositoryURL": { "Description": "ECS Repository clone URL", "Value": { "Fn::Join": [ "", [ { "Ref": "AWS::AccountId" }, ".dkr.ecr.", { "Ref": "AWS::Region" }, ".amazonaws.com/", { "Ref": "ECSRepository" } ] ] } }, "SSHToBastion": { "Description": "1. Unix command to SSH into the bastion instance", "Value": { "Fn::Join": [ "", [ "ssh -i ", { "Ref": "KeyPairPath" }, " ubuntu@", { "Fn::GetAtt": [ "BastionInstance", "PublicIp" ] } ] ] } }, "TagPreBuiltImage": { "Description": "Tag the pre-built Docker image", "Value": { "Fn::Join": [ "", [ "docker tag swift-on-ecs-prebuilt:latest ", { "Fn::Join": [ "", [ { "Ref": "AWS::AccountId" }, ".dkr.ecr.", { "Ref": "AWS::Region" }, ".amazonaws.com/", { "Ref": "ECSRepository" } ] ] }, ":latest" ] ] } } }, "Parameters": { "ECRRepository": { "AllowedPattern": "[a-z]*", "ConstraintDescription": "Must be all lower case.", "Description": "The name of ECR repository in all lower case", "MinLength": 1, "Type": "String" }, "InstanceType": { "AllowedValues": [ "t1.micro", "t2.nano", "t2.micro", "t2.small", "t2.medium", "t2.large", "m1.small", "m1.medium", "m1.large", "m1.xlarge", "m2.xlarge", "m2.2xlarge", "m2.4xlarge", "m3.medium", "m3.large", "m3.xlarge", "m3.2xlarge", "m4.large", "m4.xlarge", "m4.2xlarge", "m4.4xlarge", "m4.10xlarge", "c1.medium", "c1.xlarge", "c3.large", "c3.xlarge", "c3.2xlarge", "c3.4xlarge", "c3.8xlarge", "c4.large", "c4.xlarge", "c4.2xlarge", "c4.4xlarge", "c4.8xlarge", "g2.2xlarge", "g2.8xlarge", "r3.large", "r3.xlarge", "r3.2xlarge", "r3.4xlarge", "r3.8xlarge", "i2.xlarge", "i2.2xlarge", "i2.4xlarge", "i2.8xlarge", "d2.xlarge", "d2.2xlarge", "d2.4xlarge", "d2.8xlarge", "hi1.4xlarge", "hs1.8xlarge", "cr1.8xlarge", "cc2.8xlarge", "cg1.4xlarge" ], "ConstraintDescription": "must be a valid EC2 instance type.", "Default": "t2.small", "Description": "WebServer EC2 instance type", "Type": "String" }, "KeyName": { "ConstraintDescription": "Must be the name of an existing EC2 KeyPair.", "Description": "Name of an existing EC2 KeyPair to enable SSH access to the instance", "MinLength": 1, "Type": "AWS::EC2::KeyPair::KeyName" }, "KeyPairPath": { "Description": "Path to the local .pem file named in KeyName", "MinLength": 1, "Type": "String" }, "SSHLocation": { "AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})", "ConstraintDescription": "must be a valid IP CIDR range of the form x.x.x.x/x.", "Description": "The IP address range that can be used to SSH to the EC2 instances Note a value of 0.0.0.0/0 will allow access from anywhere", "MaxLength": "18", "MinLength": "9", "Type": "String" } }, "Resources": { "BastionInstance": { "Metadata": { "AWS::CloudFormation::Init": { "config": { "commands": { "01_docker_on_start": { "command": "systemctl enable docker" }, "02_add_ubuntu_to_docker": { "command": "sudo usermod -aG docker ubuntu" } }, "packages": { "apt": { "docker-ce": [], "git": [] } } } } }, "Properties": { "IamInstanceProfile": { "Ref": "BastionInstanceProfile" }, "ImageId": { "Fn::FindInMap": [ "RegionMap", { "Ref": "AWS::Region" }, "BastionAMI" ] }, "InstanceType": { "Ref": "InstanceType" }, "KeyName": { "Ref": "KeyName" }, "SecurityGroupIds": [ { "Fn::GetAtt": [ "BastionInstanceSecurityGroup", "GroupId" ] } ], "SubnetId": { "Ref": "Subnet" }, "Tags": [ { "Key": "Name", "Value": { "Fn::Join": [ "", [ { "Ref": "AWS::StackName" }, "-bastion" ] ] } } ], "UserData": { "Fn::Base64": { "Fn::Join": [ "", [ "#!/bin/bash -x\n", "apt-get -y update\n", "apt-get -y install python-setuptools python-pip\n", "easy_install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz\n", "pip install awscli \n", "curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - \n", "add-apt-repository \"deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable\" \n", "apt-get -y update \n", "/usr/local/bin/cfn-init", " --verbose ", " --stack ", { "Ref": "AWS::StackName" }, " --resource BastionInstance", " --region ", { "Ref": "AWS::Region" }, "\n", "/usr/local/bin/cfn-signal -e $? ", " '", { "Ref": "BastionWaitHandle" }, "'" ] ] } } }, "Type": "AWS::EC2::Instance" }, "BastionInstanceProfile": { "Properties": { "Roles": [ { "Ref": "BastionInstanceRole" } ] }, "Type": "AWS::IAM::InstanceProfile" }, "BastionInstanceRole": { "Properties": { "AssumeRolePolicyDocument": { "Statement": [ { "Action": [ "sts:AssumeRole" ], "Effect": "Allow", "Principal": { "Service": [ "ec2.amazonaws.com" ] } } ], "Version": "2012-10-17" }, "Policies": [ { "PolicyDocument": { "Statement": [ { "Action": [ "codecommit:*" ], "Effect": "Allow", "Resource": "*" } ], "Version": "2012-10-17" }, "PolicyName": "codecommit" }, { "PolicyDocument": { "Statement": [ { "Action": [ "ecr:*" ], "Effect": "Allow", "Resource": "*" } ], "Version": "2012-10-17" }, "PolicyName": "ecr" } ] }, "Type": "AWS::IAM::Role" }, "BastionInstanceSecurityGroup": { "Properties": { "GroupDescription": "Enable SSH access via port 22", "SecurityGroupIngress": [ { "CidrIp": { "Ref": "SSHLocation" }, "FromPort": "22", "IpProtocol": "tcp", "ToPort": "22" } ], "VpcId": { "Ref": "VPC" } }, "Type": "AWS::EC2::SecurityGroup" }, "BastionWaitHandle": { "Type": "AWS::CloudFormation::WaitConditionHandle" }, "ClusterInstance": { "Properties": { "IamInstanceProfile": { "Ref": "ClusterInstanceProfile" }, "ImageId": { "Fn::FindInMap": [ "RegionMap", { "Ref": "AWS::Region" }, "ClusterAMI" ] }, "InstanceType": { "Ref": "InstanceType" }, "KeyName": { "Ref": "KeyName" }, "SecurityGroupIds": [ { "Fn::GetAtt": [ "ClusterInstanceSecurityGroup", "GroupId" ] } ], "SubnetId": { "Ref": "Subnet" }, "Tags": [ { "Key": "Name", "Value": { "Fn::Join": [ "", [ { "Ref": "AWS::StackName" }, "-cluster" ] ] } } ], "UserData": { "Fn::Base64": { "Fn::Join": [ "\n", [ "#!/bin/bash", { "Fn::Join": [ "", [ "echo ECS_CLUSTER=", { "Ref": "ECSCluster" }, " >> /etc/ecs/ecs.config" ] ] } ] ] } } }, "Type": "AWS::EC2::Instance" }, "ClusterInstanceProfile": { "Properties": { "Roles": [ { "Ref": "ClusterInstanceRole" } ] }, "Type": "AWS::IAM::InstanceProfile" }, "ClusterInstanceRole": { "Properties": { "AssumeRolePolicyDocument": { "Statement": [ { "Action": [ "sts:AssumeRole" ], "Effect": "Allow", "Principal": { "Service": [ "ec2.amazonaws.com" ] } } ], "Version": "2012-10-17" }, "ManagedPolicyArns": [ "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role" ], "Policies": [ { "PolicyDocument": { "Statement": [ { "Action": "ecr:*", "Effect": "Allow", "Resource": "*" } ], "Version": "2012-10-17" }, "PolicyName": "ECRAccess" } ] }, "Type": "AWS::IAM::Role" }, "ClusterInstanceSecurityGroup": { "Properties": { "GroupDescription": "Enable SSH access via port 22", "SecurityGroupIngress": [ { "CidrIp": { "Ref": "SSHLocation" }, "FromPort": "22", "IpProtocol": "tcp", "ToPort": "22" }, { "CidrIp": "0.0.0.0/0", "FromPort": "80", "IpProtocol": "tcp", "ToPort": "80" } ], "VpcId": { "Ref": "VPC" } }, "Type": "AWS::EC2::SecurityGroup" }, "DBEC2SecurityGroup": { "Properties": { "GroupDescription": "Open database for access from cluster instance", "SecurityGroupIngress": [ { "FromPort": "3306", "IpProtocol": "tcp", "SourceSecurityGroupId": { "Fn::GetAtt": [ "ClusterInstanceSecurityGroup", "GroupId" ] }, "ToPort": "3306" } ], "VpcId": { "Ref": "VPC" } }, "Type": "AWS::EC2::SecurityGroup" }, "DBinstance": { "DeletionPolicy": "Delete", "Properties": { "AllocatedStorage": "5", "BackupRetentionPeriod": "0", "DBInstanceClass": "db.t2.micro", "DBName": "testDB", "DBSubnetGroupName": { "Ref": "myDBSubnetGroup" }, "Engine": "MySQL", "MasterUserPassword": "password", "MasterUsername": "admin", "MultiAZ": false, "VPCSecurityGroups": [ { "Ref": "DBEC2SecurityGroup" } ] }, "Type": "AWS::RDS::DBInstance" }, "ECSCluster": { "Type": "AWS::ECS::Cluster" }, "ECSRepository": { "Properties": { "RepositoryName": { "Ref": "ECRRepository" } }, "Type": "AWS::ECR::Repository" }, "InternetGateway": { "Properties": { "Tags": [ { "Key": "foo", "Value": "bar" } ] }, "Type": "AWS::EC2::InternetGateway" }, "InternetGatewayAttachment": { "DependsOn": "InternetGateway", "Properties": { "InternetGatewayId": { "Ref": "InternetGateway" }, "VpcId": { "Ref": "VPC" } }, "Type": "AWS::EC2::VPCGatewayAttachment" }, "Route": { "DependsOn": "InternetGateway", "Properties": { "DestinationCidrBlock": "0.0.0.0/0", "GatewayId": { "Ref": "InternetGateway" }, "RouteTableId": { "Ref": "RouteTableIGW" } }, "Type": "AWS::EC2::Route" }, "RouteTableIGW": { "Properties": { "VpcId": { "Ref": "VPC" } }, "Type": "AWS::EC2::RouteTable" }, "Subnet": { "Properties": { "AvailabilityZone": { "Fn::Select": [ "0", { "Fn::GetAZs": "" } ] }, "CidrBlock": "10.0.0.0/24", "MapPublicIpOnLaunch": "true", "Tags": [ { "Key": "Application", "Value": { "Ref": "AWS::StackId" } } ], "VpcId": { "Ref": "VPC" } }, "Type": "AWS::EC2::Subnet" }, "Subnet2": { "Properties": { "AvailabilityZone": { "Fn::Select": [ "1", { "Fn::GetAZs": "" } ] }, "CidrBlock": "10.0.1.0/24", "MapPublicIpOnLaunch": "true", "Tags": [ { "Key": "Application", "Value": { "Ref": "AWS::StackId" } } ], "VpcId": { "Ref": "VPC" } }, "Type": "AWS::EC2::Subnet" }, "SubnetRouteTableAssociation": { "DependsOn": "InternetGateway", "Properties": { "RouteTableId": { "Ref": "RouteTableIGW" }, "SubnetId": { "Ref": "Subnet" } }, "Type": "AWS::EC2::SubnetRouteTableAssociation" }, "VPC": { "Properties": { "CidrBlock": "10.0.0.0/16", "EnableDnsHostnames": "true", "EnableDnsSupport": "true", "InstanceTenancy": "default" }, "Type": "AWS::EC2::VPC" }, "myDBSubnetGroup": { "Properties": { "DBSubnetGroupDescription": "description", "SubnetIds": [ { "Ref": "Subnet" }, { "Ref": "Subnet2" } ] }, "Type": "AWS::RDS::DBSubnetGroup" } } }