{ "AWSTemplateFormatVersion": "2010-09-09", "Description": "VPC knowhow template", "Parameters": { "KeyName": { "Description": "Name of an existing EC2 KeyPair to enable SSH access to the instances", "Type": "String", "MinLength": "1", "MaxLength": "64", "AllowedPattern": "[-_ a-zA-Z0-9]*", "ConstraintDescription": "can contain only alphanumeric characters, spaces, dashes and underscores." }, "SSHFrom" : { "Description" : "Lockdown SSH access to the bastion host (default can be accessed from anywhere)", "Type" : "String", "MinLength": "9", "MaxLength": "18", "Default" : "0.0.0.0/0", "AllowedPattern" : "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})", "ConstraintDescription" : "must be a valid CIDR range of the form x.x.x.x/x." }, "DBInstanceType" : { "Description" : "EC2 instance type for the Blue environment", "Default" : "db.t1.micro", "Type" : "String" }, "DBSnapshotName" : { "Default" : "", "Description" : "The name of a DB snapshot (optional)", "Type" : "String" }, "DBAllocatedStorage" : { "Default" : "5", "Description" : "DB instance disk size", "Type" : "Number" }, "DBUsername": { "Default": "admin", "Description" : "The database master account username", "Type": "String", "MinLength": "1", "MaxLength": "16", "AllowedPattern" : "[a-zA-Z][a-zA-Z0-9]*", "ConstraintDescription" : "must begin with a letter and contain only alphanumeric characters." }, "DBPassword" : { "Description" : "Password of RDS master password", "Type" : "String", "NoEcho": "true", "MinLength": "4" }, "DBName" : { "Default" : "", "Description" : "The name of a DB01 database", "Type" : "String" }, "WebInstanceType" : { "Description" : "EC2 instance type for the web server", "Default" : "t1.micro", "Type" : "String" }, "WebFleetSize" : { "Description" : "Number of EC2 instances to launch for the web server", "Default" : "2", "Type" : "Number", "MaxValue" : "100", "MinValue" : "1" }, "HostedZone" : { "Description" : "The DNS name of an existing Amazon Route 53 hosted zone", "Type" : "String" } }, "Conditions" : { "UseDBSnapshot" : { "Fn::Not" : [ { "Fn::Equals" : [ { "Ref" : "DBSnapshotName" }, "" ] } ] } }, "Mappings": { "AWSAmazonLinuxAMI": { "us-east-1": { "name":"Virginia", "201303": "ami-3275ee5b", "201309": "ami-35792c5c", "201403": "ami-2f726546" }, "us-west-2": { "name":"Oregon", "201303": "ami-ecbe2adc", "201309": "ami-d03ea1e0", "201403": "ami-b8f69f88" }, "us-west-1": { "name":"California", "201303": "ami-66d1fc23", "201309": "ami-687b4f2d", "201403": "ami-84f1cfc1" }, "eu-west-1": { "name":"Ireland", "201303": "ami-44939930", "201309": "ami-149f7863", "201403": "ami-a921dfde" }, "ap-southeast-1": { "name":"Singapole", "201303": "ami-aa9ed2f8", "201309": "ami-14f2b946", "201403": "ami-787c2c2a" }, "ap-southeast-2": { "name":"Sydney", "201303": "ami-363eaf0c", "201309": "ami-a148d59b", "201403": "ami-0bc85031" }, "ap-northeast-1": { "name":"Tokyo", "201303": "ami-173fbf16", "201309": "ami-3561fe34", "201403": "ami-a1bec3a0" }, "sa-east-1": { "name":"SaoPaulo", "201303": "ami-dd6bb0c0", "201309": "ami-9f6ec982", "201403": "ami-89de7c94" } }, "ELBLogger": { "us-east-1": { "AccountID": "127311923021" }, "us-west-2": { "AccountID": "797873946194" }, "us-west-1": { "AccountID": "027434742980" }, "eu-west-1": { "AccountID": "156460612806" }, "ap-southeast-1": { "AccountID": "114774131450" }, "ap-southeast-2": { "AccountID": "783225319266" }, "ap-northeast-1": { "AccountID": "582318560864" }, "sa-east-1": { "AccountID": "507241528517" }, "us-gov-west-1": { "AccountID": "048591011584" } }, "StackConfig" : { "VPC" : { "CIDR" : "10.0.0.0/16" }, "FrontendSubnet1" : { "CIDR" : "10.0.0.0/24" }, "FrontendSubnet2" : { "CIDR" : "10.0.1.0/24" }, "ApplicationSubnet1": { "CIDR" : "10.0.100.0/24" }, "ApplicationSubnet2": { "CIDR" : "10.0.101.0/24" }, "DatastoreSubnet1" : { "CIDR" : "10.0.200.0/24" }, "DatastoreSubnet2" : { "CIDR" : "10.0.201.0/24" }, "BastionServer" : { "InstanceType" : "t1.micro" } } }, "Resources": { "PowerUserRole" : { "Type" : "AWS::IAM::Role", "Properties" : { "AssumeRolePolicyDocument" : { "Statement": [ { "Effect": "Allow", "Principal": { "Service": [ "ec2.amazonaws.com" ] }, "Action": [ "sts:AssumeRole" ] } ] }, "Path" : "/", "Policies" :[ { "PolicyName" : "PowerUserPolicy", "PolicyDocument" : { "Statement": [ { "Sid": "PowerUserStmt", "Effect": "Allow", "NotAction": "iam:*", "Resource": "*" } ] } }] } }, "PowerUserProfile" : { "Type" : "AWS::IAM::InstanceProfile", "Properties" : { "Path": "/", "Roles" : [ { "Ref" : "PowerUserRole" } ] } }, "LogBucket" : { "Type" : "AWS::S3::Bucket", "DeletionPolicy" : "Retain" }, "LogBucketPolicy" : { "Type" : "AWS::S3::BucketPolicy", "Properties" : { "Bucket" : { "Ref" : "LogBucket" }, "PolicyDocument" : { "Id" : "LogBucketPolicy", "Statement" : [{ "Sid" : "WriteAccess", "Action" : [ "s3:PutObject" ], "Effect" : "Allow", "Resource" : { "Fn::Join" : [ "", [ "arn:aws:s3:::", { "Ref" : "LogBucket" } , "/AWSLogs/", { "Ref" : "AWS::AccountId" }, "/*" ]]}, "Principal" : { "AWS" : { "Fn::FindInMap" : [ "ELBLogger", { "Ref": "AWS::Region" }, "AccountID" ]} } }] } } }, "VPC" : { "Type" : "AWS::EC2::VPC", "Properties" : { "CidrBlock" : { "Fn::FindInMap" : [ "StackConfig", "VPC", "CIDR" ]}, "EnableDnsSupport" : "true", "EnableDnsHostnames" : "true", "InstanceTenancy" : "default", "Tags" : [ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId" } }, {"Key" : "Network", "Value" : "Public" } ] } }, "InternetGateway" : { "Type" : "AWS::EC2::InternetGateway", "Properties" : { "Tags" : [ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId" } }, {"Key" : "Network", "Value" : "Public" } ] } }, "AttachGateway" : { "Type" : "AWS::EC2::VPCGatewayAttachment", "Properties" : { "VpcId" : {"Ref" : "VPC"}, "InternetGatewayId" : {"Ref" : "InternetGateway"} } }, "PublicRouteTable" : { "Type" : "AWS::EC2::RouteTable", "DependsOn" : "AttachGateway", "Properties" : { "VpcId" : { "Ref" : "VPC" }, "Tags" : [ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId"} }, {"Key" : "Network", "Value" : "Public" } ] } }, "PrivateRouteTable" : { "Type" : "AWS::EC2::RouteTable", "DependsOn" : "AttachGateway", "Properties" : { "VpcId" : { "Ref" : "VPC" }, "Tags" : [ { "Key" : "Application", "Value" : { "Ref" : "AWS::StackId" } }, { "Key" : "Network", "Value" : "Private" } ] } }, "PublicRoute" : { "Type" : "AWS::EC2::Route", "DependsOn" : "AttachGateway", "Properties" : { "RouteTableId" : { "Ref" : "PublicRouteTable" }, "DestinationCidrBlock" : "0.0.0.0/0", "GatewayId" : { "Ref" : "InternetGateway" } } }, "FrontendSubnet1": { "Type": "AWS::EC2::Subnet", "DependsOn" : "AttachGateway", "Properties" : { "VpcId": { "Ref": "VPC" }, "AvailabilityZone" : { "Fn::Select" : [ "0", { "Fn::GetAZs" : { "Ref" : "AWS::Region" }}]}, "CidrBlock": { "Fn::FindInMap" : [ "StackConfig", "FrontendSubnet1", "CIDR" ]}, "Tags" : [ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId"} }, {"Key" : "Network", "Value" : "Public" } ] } }, "FrontendSubnet2": { "Type": "AWS::EC2::Subnet", "DependsOn" : "AttachGateway", "Properties": { "VpcId": { "Ref": "VPC" }, "AvailabilityZone" : { "Fn::Select" : [ "1", { "Fn::GetAZs" : { "Ref" : "AWS::Region" }}]}, "CidrBlock": { "Fn::FindInMap" : [ "StackConfig", "FrontendSubnet2", "CIDR" ]}, "Tags" : [ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId"} }, {"Key" : "Network", "Value" : "Public" } ] } }, "ApplicationSubnet1" : { "Type" : "AWS::EC2::Subnet", "DependsOn" : "AttachGateway", "Properties" : { "VpcId" : {"Ref" : "VPC"}, "CidrBlock" : { "Fn::FindInMap" : [ "StackConfig", "ApplicationSubnet1", "CIDR" ]}, "AvailabilityZone" : { "Fn::Select" : [ "0", { "Fn::GetAZs" : { "Ref" : "AWS::Region" }}]}, "Tags" : [ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId"} }, {"Key" : "Network", "Value" : "Public" } ] } }, "ApplicationSubnet2" : { "Type" : "AWS::EC2::Subnet", "DependsOn" : "AttachGateway", "Properties" : { "VpcId" : {"Ref" : "VPC"}, "CidrBlock" : { "Fn::FindInMap" : [ "StackConfig", "ApplicationSubnet2", "CIDR" ]}, "AvailabilityZone" : { "Fn::Select" : [ "1", { "Fn::GetAZs" : { "Ref" : "AWS::Region" }}]}, "Tags" : [ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId"} }, {"Key" : "Network", "Value" : "Public" } ] } }, "DatastoreSubnet1" : { "Type" : "AWS::EC2::Subnet", "DependsOn" : "AttachGateway", "Properties" : { "VpcId" : { "Ref" : "VPC" }, "CidrBlock" : { "Fn::FindInMap" : [ "StackConfig", "DatastoreSubnet1", "CIDR" ]}, "AvailabilityZone" : { "Fn::Select" : [ "0", { "Fn::GetAZs" : { "Ref" : "AWS::Region" }}]}, "Tags" : [ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId" } }, {"Key" : "Network", "Value" : "Private" } ] } }, "DatastoreSubnet2" : { "Type" : "AWS::EC2::Subnet", "DependsOn" : "AttachGateway", "Properties" : { "VpcId" : { "Ref" : "VPC" }, "CidrBlock" : { "Fn::FindInMap" : [ "StackConfig", "DatastoreSubnet2", "CIDR" ]}, "AvailabilityZone" : { "Fn::Select" : [ "1", { "Fn::GetAZs" : { "Ref" : "AWS::Region" }}]}, "Tags" : [ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId" } }, {"Key" : "Network", "Value" : "Private" } ] } }, "FrontendSubnet1RouteTableAssociation" : { "Type" : "AWS::EC2::SubnetRouteTableAssociation", "Properties" : { "SubnetId" : { "Ref" : "FrontendSubnet1" }, "RouteTableId" : { "Ref" : "PublicRouteTable" } } }, "FrontendSubnet2RouteTableAssociation" : { "Type" : "AWS::EC2::SubnetRouteTableAssociation", "Properties" : { "SubnetId" : { "Ref" : "FrontendSubnet2" }, "RouteTableId" : { "Ref" : "PublicRouteTable" } } }, "ApplicationSubnet1RouteTableAssociation" : { "Type" : "AWS::EC2::SubnetRouteTableAssociation", "Properties" : { "SubnetId" : { "Ref" : "ApplicationSubnet1" }, "RouteTableId" : { "Ref" : "PublicRouteTable" } } }, "ApplicationSubnet2RouteTableAssociation" : { "Type" : "AWS::EC2::SubnetRouteTableAssociation", "Properties" : { "SubnetId" : { "Ref" : "ApplicationSubnet2" }, "RouteTableId" : { "Ref" : "PublicRouteTable" } } }, "DatastoreSubnet1RouteTableAssociation" : { "Type" : "AWS::EC2::SubnetRouteTableAssociation", "Properties" : { "SubnetId" : { "Ref" : "DatastoreSubnet1" }, "RouteTableId" : { "Ref" : "PrivateRouteTable" } } }, "DatastoreSubnet2RouteTableAssociation" : { "Type" : "AWS::EC2::SubnetRouteTableAssociation", "Properties" : { "SubnetId" : { "Ref" : "DatastoreSubnet2" }, "RouteTableId" : { "Ref" : "PrivateRouteTable" } } }, "VPCDefaultSecurityGroup" : { "Type" : "AWS::EC2::SecurityGroup", "Properties" : { "VpcId" : { "Ref" : "VPC" }, "GroupDescription" : "Allow all communications in VPC", "SecurityGroupIngress" : [ { "IpProtocol" : "tcp", "FromPort" : "0", "ToPort" : "65535", "CidrIp" : { "Fn::FindInMap" : [ "StackConfig", "VPC", "CIDR" ]} }, { "IpProtocol" : "udp", "FromPort" : "0", "ToPort" : "65535", "CidrIp" : { "Fn::FindInMap" : [ "StackConfig", "VPC", "CIDR" ]} }, { "IpProtocol" : "icmp", "FromPort" : "-1", "ToPort" : "-1", "CidrIp" : { "Fn::FindInMap" : [ "StackConfig", "VPC", "CIDR" ]} } ] } }, "SSHSecurityGroup" : { "Type" : "AWS::EC2::SecurityGroup", "Properties" : { "VpcId" : { "Ref" : "VPC" }, "GroupDescription" : "Enable SSH access via port 22", "SecurityGroupIngress" : [ { "IpProtocol" : "tcp", "FromPort" : "22", "ToPort" : "22", "CidrIp" : { "Ref" : "SSHFrom" }} ] } }, "PublicWebSecurityGroup" : { "Type" : "AWS::EC2::SecurityGroup", "Properties" : { "VpcId" : { "Ref" : "VPC" }, "GroupDescription" : "Public Security Group with HTTP access on port 443 from the internet", "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" } ] } }, "ApplicationSecurityGroup" : { "Type" : "AWS::EC2::SecurityGroup", "Properties" : { "VpcId" : {"Ref" : "VPC"}, "GroupDescription" : "Marker security group for Application server." } }, "MySQLSecurityGroup" : { "Type" : "AWS::EC2::SecurityGroup", "Properties" : { "VpcId" : {"Ref" : "VPC"}, "GroupDescription" : "Marker security group for MySQL server." } }, "BastionWaitHandle" : { "Type" : "AWS::CloudFormation::WaitConditionHandle" }, "BastionWaitCondition" : { "Type" : "AWS::CloudFormation::WaitCondition", "DependsOn" : "BastionInstance", "Properties" : { "Handle" : { "Ref" : "BastionWaitHandle" }, "Timeout" : "900" } }, "BastionInstance": { "Type": "AWS::EC2::Instance", "Properties": { "InstanceType": { "Fn::FindInMap" : [ "StackConfig", "BastionServer", "InstanceType" ]}, "KeyName": { "Ref": "KeyName" }, "SubnetId": { "Ref" : "FrontendSubnet1" }, "ImageId": { "Fn::FindInMap": [ "AWSAmazonLinuxAMI", { "Ref": "AWS::Region" }, "201403" ]}, "IamInstanceProfile": { "Ref" : "PowerUserProfile" }, "SecurityGroupIds" : [ { "Ref" : "SSHSecurityGroup" }, { "Ref" : "VPCDefaultSecurityGroup" } ], "Tags": [ { "Key": "Name", "Value": "Bastion" } ], "UserData" : { "Fn::Base64" : { "Fn::Join" : [ "", [ "#! /bin/bash -v\n", "yum update -y\n", "# Helper function\n", "function error_exit\n", "{\n", " /opt/aws/bin/cfn-signal -e 1 -r \"$1\" '", { "Ref" : "BastionWaitHandle" }, "'\n", " exit 1\n", "}\n", "# Install packages\n", "/opt/aws/bin/cfn-init -s ", { "Ref" : "AWS::StackId" }, " -r BastionInstance ", " --region ", { "Ref" : "AWS::Region" }, " || error_exit 'Failed to run cfn-init'\n", "# All is well so signal success\n", "/opt/aws/bin/cfn-signal -e $? -r \"BastionInstance setup complete\" '", { "Ref" : "BastionWaitHandle" }, "'\n" ]]}} }, "Metadata" : { "AWS::CloudFormation::Init" : { "config" : { "packages" : { "yum" : { "mysql55" : [], "jq" : [], "python-magic" : [] } } } } } }, "BastionInstanceEIP": { "Type": "AWS::EC2::EIP", "DependsOn" : "AttachGateway", "Properties": { "Domain": "vpc", "InstanceId": { "Ref": "BastionInstance" } } }, "BastionDNSRecord" : { "Type" : "AWS::Route53::RecordSet", "Properties" : { "HostedZoneName" : { "Fn::Join" : [ "", [{"Ref" : "HostedZone"}, "." ]]}, "Comment" : "A record for the Bastion instance.", "Name" : { "Fn::Join" : [ "", ["bastion.", {"Ref" : "HostedZone"}, "." ]]}, "Type" : "A", "TTL" : "300", "ResourceRecords" : [ {"Ref" :"BastionInstanceEIP"} ] } }, "BastionLocalDNSRecord" : { "Type" : "AWS::Route53::RecordSet", "Properties" : { "HostedZoneName" : { "Fn::Join" : [ "", [{"Ref" : "HostedZone"}, "." ]]}, "Comment" : "A record for the private IP address of Bastion instance.", "Name" : { "Fn::Join" : [ "", ["bastion.local.", {"Ref" : "HostedZone"}, "." ]]}, "Type" : "A", "TTL" : "300", "ResourceRecords" : [ { "Fn::GetAtt" : [ "BastionInstance", "PrivateIp" ] } ] } }, "DBParamGroup" : { "Type": "AWS::RDS::DBParameterGroup", "Properties" : { "Description" : "Default parameter group for Portnoy", "Family" : "MySQL5.6", "Parameters" : { "character_set_database" : "utf8mb4", "character_set_client" : "utf8mb4", "character_set_connection" : "utf8mb4", "character_set_results" : "utf8mb4", "character_set_server" : "utf8mb4", "skip-character-set-client-handshake" : "TRUE" } } }, "DBSubnetGroup" : { "Type" : "AWS::RDS::DBSubnetGroup", "Properties" : { "DBSubnetGroupDescription" : "Database subnets for RDS", "SubnetIds" : [ { "Ref": "DatastoreSubnet1" }, { "Ref": "DatastoreSubnet2" } ] } }, "DBInstance" : { "Type" : "AWS::RDS::DBInstance", "DeletionPolicy" : "Snapshot", "Properties" : { "DBInstanceClass" : { "Ref" : "DBInstanceType" }, "AllocatedStorage" : { "Ref" : "DBAllocatedStorage" }, "Engine" : "MySQL", "MultiAZ" : "true", "EngineVersion" : "5.6.13", "MasterUsername" : {"Ref":"DBUsername"}, "MasterUserPassword" : {"Ref":"DBPassword"}, "BackupRetentionPeriod" : "35", "DBParameterGroupName" : {"Ref":"DBParamGroup"}, "DBSubnetGroupName" : {"Ref":"DBSubnetGroup"}, "DBSnapshotIdentifier" : { "Fn::If" : [ "UseDBSnapshot", { "Ref" : "DBSnapshotName" }, { "Ref" : "AWS::NoValue" } ]}, "PreferredBackupWindow": "19:00-19:30", "PreferredMaintenanceWindow": "sat:20:00-sat:20:30", "VPCSecurityGroups" : [ { "Ref" : "VPCDefaultSecurityGroup" }, { "Ref" : "MySQLSecurityGroup" } ] } }, "DatabaseDNSRecord" : { "Type" : "AWS::Route53::RecordSet", "Properties" : { "HostedZoneName" : { "Fn::Join" : [ "", [{"Ref" : "HostedZone"}, "." ]]}, "Comment" : "CNAME for the database.", "Name" : { "Fn::Join" : [ "", [ "db.local.", { "Ref" : "HostedZone" }, "." ]]}, "Type" : "CNAME", "TTL" : "300", "ResourceRecords" : [ { "Fn::GetAtt" : [ "DBInstance", "Endpoint.Address" ] } ] } }, "ApplicationFleet" : { "Type" : "AWS::AutoScaling::AutoScalingGroup", "UpdatePolicy" : { "AutoScalingRollingUpdate" : { "MaxBatchSize" : "1", "MinInstancesInService" : "1", "PauseTime" : "PT2M30S" } }, "Properties" : { "AvailabilityZones" : [ { "Fn::GetAtt" : [ "ApplicationSubnet1", "AvailabilityZone" ] }, { "Fn::GetAtt" : [ "ApplicationSubnet2", "AvailabilityZone" ] } ], "VPCZoneIdentifier" : [ { "Ref" : "ApplicationSubnet1" }, { "Ref" : "ApplicationSubnet2" } ], "LaunchConfigurationName" : { "Ref" : "ApplicationServerLaunchConfig" }, "MinSize" : { "Ref" : "WebFleetSize" }, "MaxSize" : { "Ref" : "WebFleetSize" }, "DesiredCapacity" : { "Ref" : "WebFleetSize" }, "LoadBalancerNames" : [ { "Ref" : "ElasticLoadBalancer" } ], "Tags" : [ { "Key" : "Name", "Value" : "Application", "PropagateAtLaunch" : "true" } ] } }, "ApplicationServerLaunchConfig" : { "Type" : "AWS::AutoScaling::LaunchConfiguration", "Properties" : { "InstanceType": { "Ref" : "WebInstanceType" }, "KeyName": { "Ref" : "KeyName" }, "ImageId": { "Fn::FindInMap": [ "AWSAmazonLinuxAMI", { "Ref": "AWS::Region" }, "201403" ]}, "SecurityGroups": [ { "Ref" : "VPCDefaultSecurityGroup" }, { "Ref" : "ApplicationSecurityGroup" } ], "AssociatePublicIpAddress" : "true", "IamInstanceProfile": { "Ref" : "PowerUserProfile" }, "InstanceMonitoring" : "false", "UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [ "#! /bin/bash -v\n", "yum update -y\n", "# Install packages\n", "/opt/aws/bin/cfn-init -s ", { "Ref" : "AWS::StackId" }, " -r ApplicationServerLaunchConfig ", " --region ", { "Ref" : "AWS::Region" }, " || error_exit 'Failed to run cfn-init'\n" ]]}} }, "Metadata" : { "AWS::CloudFormation::Init" : { "config" : { "packages" : { "yum" : { "httpd" : [], "mysql55" : [] } }, "files" : { "/var/www/html/index.html" : { "content" : "