Resources:
# 01 Create VPC
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: "10.0.0.0/16"
EnableDnsHostnames: true
InternetGateway:
Type: AWS::EC2::InternetGateway
GatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
# 2 Add Route Table
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
PublicRoute:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
RouteTableId: !Ref PublicRouteTable
## 3 Add Public Subnets
PublicSubnetA:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Join ["", [!Ref "AWS::Region", "a"]]
CidrBlock: "10.0.0.0/24"
PublicSubnetARouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetA
RouteTableId: !Ref PublicRouteTable
PublicSubnetB:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Join ["", [!Ref "AWS::Region", "b"]]
CidrBlock: "10.0.1.0/24"
PublicSubnetBRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetB
RouteTableId: !Ref PublicRouteTable
# 4 Add NAT
EIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
NAT:
DependsOn: GatewayAttachment
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !Sub "${EIP.AllocationId}"
SubnetId: !Ref PublicSubnetA
# 5 Add private Route Table
PrivateSubnetRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId:
Ref: VPC
PrivateRouteTableRouteNAT:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateSubnetRouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NAT
# 6 Add private subnets and associate route tables
PrivateSubnetA:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Join ["", [!Ref "AWS::Region", "a"]]
CidrBlock: "10.0.64.0/24"
PrivateSubnetARouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnetA
RouteTableId: !Ref PrivateSubnetRouteTable
PrivateSubnetB:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Join ["", [!Ref "AWS::Region", "b"]]
CidrBlock: "10.0.65.0/24"
PrivateSubnetBRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnetB
RouteTableId: !Ref PrivateSubnetRouteTable
# 7 add data route without any rules
DataSubnetRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
DataSubnetA:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Join ["", [!Ref "AWS::Region", "a"]]
CidrBlock: "10.0.128.0/24"
DataSubnetARouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref DataSubnetA
RouteTableId: !Ref DataSubnetRouteTable
DataSubnetB:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Join ["", [!Ref "AWS::Region", "b"]]
CidrBlock: "10.0.129.0/24"
DataSubnetBRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref DataSubnetB
RouteTableId: !Ref DataSubnetRouteTable
# 8 Security Groups
ALBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Security group for ALB"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
VpcId: !Ref VPC
WebSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Security group for Web Servers"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
SourceSecurityGroupId: !Ref ALBSecurityGroup
VpcId: !Ref VPC
EFSSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Security group for EFS"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 2049
ToPort: 2049
SourceSecurityGroupId: !Ref WebSecurityGroup
VpcId: !Ref VPC
ElasticacheSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Security group for ElastiCache"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 11211
ToPort: 11211
SourceSecurityGroupId: !Ref WebSecurityGroup
VpcId: !Ref VPC
DatabaseSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Security group for Amazon RDS cluster"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 3306
ToPort: 3306
SourceSecurityGroupId: !Ref WebSecurityGroup
VpcId: !Ref VPC
# 9 ALB
ALB:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Scheme: internet-facing
Subnets:
- !Ref PublicSubnetA
- !Ref PublicSubnetB
LoadBalancerAttributes:
- Key: idle_timeout.timeout_seconds
Value: "60"
SecurityGroups:
- !Ref ALBSecurityGroup
ALBListener:
Type : AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref ALBTargetGroup
LoadBalancerArn: !Ref ALB
Port: 80
Protocol: HTTP
ALBTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckIntervalSeconds: 30
HealthCheckPath: /wp-login.php
HealthCheckTimeoutSeconds: 5
Port: 80
Protocol: HTTP
UnhealthyThresholdCount: 5
VpcId: !Ref VPC
# 10 Add ElastiCache
ElastiCacheSubnetGroup:
Type: AWS::ElastiCache::SubnetGroup
Properties:
Description: ElastiCache Subnet Group for WordPress
SubnetIds:
- !Ref DataSubnetA
- !Ref DataSubnetB
ElastiCacheCluster:
Type: AWS::ElastiCache::CacheCluster
Properties:
CacheNodeType: 'cache.t2.micro'
CacheSubnetGroupName: !Ref ElastiCacheSubnetGroup
Engine: memcached
NumCacheNodes: 1
VpcSecurityGroupIds:
- !Ref ElasticacheSecurityGroup
# 11 Database Cluster
DataSubnetGroup:
Type: AWS::RDS::DBSubnetGroup
Properties:
DBSubnetGroupDescription: "RDS Database Subnet Group for WordPress"
SubnetIds:
- !Ref DataSubnetA
- !Ref DataSubnetB
DatabaseCluster:
Type: AWS::RDS::DBCluster
Properties:
MasterUsername: "wordpress"
MasterUserPassword: "supersecret"
DatabaseName: "wordpressdb"
Engine: aurora
Port: 3306
EngineMode: serverless
ScalingConfiguration:
AutoPause: false
MaxCapacity: 16
MinCapacity: 2
DBSubnetGroupName: !Ref DataSubnetGroup
VpcSecurityGroupIds:
- !Ref DatabaseSecurityGroup
# 12 EFS
EFS:
Type: AWS::EFS::FileSystem
Properties:
PerformanceMode: 'maxIO'
EFSMountTargetA:
Type: AWS::EFS::MountTarget
Properties:
FileSystemId: !Ref EFS
SecurityGroups:
- !Ref EFSSecurityGroup
SubnetId: !Ref DataSubnetA
EFSMountTargetB:
Type: AWS::EFS::MountTarget
Properties:
FileSystemId: !Ref EFS
SecurityGroups:
- !Ref EFSSecurityGroup
SubnetId: !Ref DataSubnetB
# 13 Instance Profile
WebInstanceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: logs
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
- logs:DescribeLogStreams
Resource:
- arn:aws:logs:*:*:*
WebInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref WebInstanceRole
## 14 AutoScaling
WebLaunchConfiguration:
Type: AWS::AutoScaling::LaunchConfiguration
Properties:
IamInstanceProfile: !Ref WebInstanceProfile
ImageId: "ami-1a962263"
InstanceMonitoring: true
InstanceType: "t3.xlarge"
SecurityGroups:
- !Ref WebSecurityGroup
UserData:
"Fn::Base64":
!Sub |
#!/bin/bash -xe
yum update -y
mkdir -p /var/www/wordpress
mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 ${EFS}.efs.${AWS::Region}.amazonaws.com:/ /var/www/wordpress
/opt/aws/bin/cfn-init --configsets deploy_webserver --verbose --stack ${AWS::StackName} --resource WebLaunchConfiguration --region ${AWS::Region}
/opt/aws/bin/cfn-signal --exit-code $? --stack ${AWS::StackName} --resource WebAutoScalingGroup --region ${AWS::Region}
Metadata:
AWS::CloudFormation::Init:
configSets:
deploy_webserver:
- install_webserver
- build_cacheclient
- build_wordpress
- build_opcache
- download_aws_ini
- install_aws_ini
- install_cacheclient
- install_wordpress
- install_opcache
- start_webserver
install_webserver:
packages:
yum:
awslogs: []
httpd24: []
mysql56: []
php70: []
php70-devel: []
php7-pear: []
php70-mysqlnd: []
files:
/tmp/create_site_conf.sh:
content:
!Join [
"",[
"#!/bin/bash -xe\n",
"if [ ! -f /etc/httpd/conf.d/wordpress.conf ]; then\n",
" touch /etc/httpd/conf.d/wordpress.conf\n",
" echo 'ServerName 127.0.0.1:80' >> /etc/httpd/conf.d/wordpress.conf\n",
" echo 'DocumentRoot /var/www/wordpress/wordpress' >> /etc/httpd/conf.d/wordpress.conf\n",
" echo '<Directory /var/www/wordpress/wordpress>' >> /etc/httpd/conf.d/wordpress.conf\n",
" echo ' Options Indexes FollowSymLinks' >> /etc/httpd/conf.d/wordpress.conf\n",
" echo ' AllowOverride All' >> /etc/httpd/conf.d/wordpress.conf\n",
" echo ' Require all granted' >> /etc/httpd/conf.d/wordpress.conf\n",
" echo '</Directory>' >> /etc/httpd/conf.d/wordpress.conf\n",
"fi\n"
]
]
mode: 000500
owner: root
group: root
commands:
create_site_conf:
command: ./create_site_conf.sh
cwd: /tmp
ignoreErrors: false
build_cacheclient:
packages:
yum:
gcc-c++: []
files:
/tmp/install_cacheclient.sh:
content: |
#!/bin/bash -xe
ln -s /usr/bin/pecl7 /usr/bin/pecl #just so pecl is available easily
pecl7 install igbinary
wget -P /tmp/ https://s3.amazonaws.com/aws-refarch/wordpress/latest/bits/AmazonElastiCacheClusterClient-2.0.1-PHP70-64bit.tar.gz
tar -xf '/tmp/AmazonElastiCacheClusterClient-2.0.1-PHP70-64bit.tar.gz'
cp '/tmp/artifact/amazon-elasticache-cluster-client.so' /usr/lib64/php/7.0/modules/
if [ ! -f /etc/php-7.0.d/50-memcached.ini ]; then
touch /etc/php-7.0.d/50-memcached.ini
fi
echo 'extension=igbinary.so;' >> /etc/php-7.0.d/50-memcached.ini
echo 'extension=/usr/lib64/php/7.0/modules/amazon-elasticache-cluster-client.so;' >> /etc/php-7.0.d/50-memcached.ini
mode: 000500
owner: root
group: root
build_opcache:
packages:
yum:
php70-opcache: []
files:
/tmp/install_opcache.sh:
content: |
#!/bin/bash -xe
# create hidden opcache directory locally & change owner to apache
if [ ! -d /var/www/.opcache ]; then
mkdir -p /var/www/.opcache
fi
# enable opcache in /etc/php-7.0.d/10-opcache.ini
sed -i 's/;opcache.file_cache=.*/opcache.file_cache=\/var\/www\/.opcache/' /etc/php-7.0.d/10-opcache.ini
sed -i 's/opcache.memory_consumption=.*/opcache.memory_consumption=512/' /etc/php-7.0.d/10-opcache.ini
# download opcache-instance.php to verify opcache status
if [ ! -f /var/www/wordpress/wordpress/opcache-instanceid.php ]; then
wget -P /var/www/wordpress/wordpress/ https://s3.amazonaws.com/aws-refarch/wordpress/latest/bits/opcache-instanceid.php
fi
mode: 000500
owner: root
group: root
build_wordpress:
files:
/tmp/install_wordpress.sh:
content:
!Join [
"",[
"#!/bin/bash -xe\n",
"\n",
"# install wp-cli\n",
"if [ ! -f /bin/wp/wp-cli.phar ]; then\n",
" curl -o /bin/wp https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar\n",
" chmod +x /bin/wp\n",
"fi\n",
"\n",
"# make site directory\n",
"if [ ! -d /var/www/wordpress/wordpress ]; then\n",
" mkdir -p /var/www/wordpress/wordpress\n",
"\n",
" cd /var/www/wordpress/wordpress\n",
" # install wordpress if not installed\n",
" # use public alb host name if wp domain name was empty\n",
" if ! $(wp core is-installed --allow-root); then\n",
" wp core download --version='latest' --locale='en_GB' --allow-root\n",
" wp core config --dbname='wordpressdb' --dbuser='wordpress' --dbpass='supersecret' --dbhost='", !GetAtt DatabaseCluster.Endpoint.Address, "' --dbprefix=wp_ --allow-root\n",
" wp core install --url='", !GetAtt ALB.DNSName, "' --title='Test' --admin_user='admin' --admin_password='instruqt' --admin_email='noreply@xebia.com' --skip-email --allow-root\n",
" wp plugin install w3-total-cache\n",
" sed -i \"/$table_prefix = 'wp_';/ a \\define('WP_HOME', 'http://' . \\$_SERVER['HTTP_HOST']); \" /var/www/wordpress/wordpress/wp-config.php\n",
" sed -i \"/$table_prefix = 'wp_';/ a \\define('WP_SITEURL', 'http://' . \\$_SERVER['HTTP_HOST']); \" /var/www/wordpress/wordpress/wp-config.php\n",
" # set permissions of wordpress site directories\n",
" chown -R apache:apache /var/www/wordpress/wordpress\n",
" chmod u+wrx /var/www/wordpress/wordpress/wp-content/*\n",
" if [ ! -f /var/www/wordpress/wordpress/opcache-instanceid.php ]; then\n",
" wget -P /var/www/wordpress/wordpress/ https://s3.amazonaws.com/aws-refarch/wordpress/latest/bits/opcache-instanceid.php\n",
" fi\n",
" fi\n",
" RESULT=$?\n",
" if [ $RESULT -eq 0 ]; then\n",
" touch /var/www/wordpress/wordpress/wordpress.initialized\n",
" else\n",
" touch /var/www/wordpress/wordpress/wordpress.failed\n",
" fi\n",
"fi\n"
]
]
mode: 000500
owner: root
group: root
download_aws_ini:
files:
/tmp/download_aws_ini.sh:
content:
!Join [
"",[
"#!/bin/bash -x\n",
"\n",
"wget -P /etc/php-7.0.d/ https://s3.amazonaws.com/aws-refarch/wordpress/latest/bits/20-aws.ini\n"
]
]
mode: 000500
owner: root
group: root
install_aws_ini:
commands:
install_aws_ini:
command: ./download_aws_ini.sh
cwd: /tmp
ignoreErrors: true
install_wordpress:
commands:
install_wordpress:
command: ./install_wordpress.sh
cwd: /tmp
ignoreErrors: false
install_cacheclient:
commands:
install_cacheclient:
command: ./install_cacheclient.sh
cwd: /tmp
ignoreErrors: false
install_opcache:
commands:
install_opcache:
command: ./install_opcache.sh
cwd: /tmp
ignoreErrors: false
start_webserver:
services:
sysvinit:
httpd:
enabled: true
ensureRunning: true
# 15 auto scaling group
WebAutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
DependsOn:
- EFSMountTargetA
- EFSMountTargetB
- NAT
Properties:
Cooldown: "60"
HealthCheckGracePeriod: 120
HealthCheckType: ELB
LaunchConfigurationName: !Ref WebLaunchConfiguration
MaxSize: "2"
MinSize: "1"
TargetGroupARNs:
- !Ref ALBTargetGroup
VPCZoneIdentifier:
- !Ref PrivateSubnetA
- !Ref PrivateSubnetB
CreationPolicy:
ResourceSignal:
Count: 1
Timeout: PT15M
Resources:
# 01 Create VPC
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: "10.0.0.0/16"
EnableDnsHostnames: true
InternetGateway:
Type: AWS::EC2::InternetGateway
GatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
# 2 Add Route Table
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
PublicRoute:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
RouteTableId: !Ref PublicRouteTable
## 3 Add Public Subnets
PublicSubnetA:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Join ["", [!Ref "AWS::Region", "a"]]
CidrBlock: "10.0.0.0/24"
PublicSubnetARouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetA
RouteTableId: !Ref PublicRouteTable
PublicSubnetB:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Join ["", [!Ref "AWS::Region", "b"]]
CidrBlock: "10.0.1.0/24"
PublicSubnetBRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetB
RouteTableId: !Ref PublicRouteTable
# 4 Add NAT
EIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
NAT:
DependsOn: GatewayAttachment
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !Sub "${EIP.AllocationId}"
SubnetId: !Ref PublicSubnetA
# 5 Add private Route Table
PrivateSubnetRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId:
Ref: VPC
PrivateRouteTableRouteNAT:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateSubnetRouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NAT
# 6 Add private subnets and associate route tables
PrivateSubnetA:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Join ["", [!Ref "AWS::Region", "a"]]
CidrBlock: "10.0.64.0/24"
PrivateSubnetARouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnetA
RouteTableId: !Ref PrivateSubnetRouteTable
PrivateSubnetB:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Join ["", [!Ref "AWS::Region", "b"]]
CidrBlock: "10.0.65.0/24"
PrivateSubnetBRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnetB
RouteTableId: !Ref PrivateSubnetRouteTable
# 7 add data route without any rules
DataSubnetRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
DataSubnetA:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Join ["", [!Ref "AWS::Region", "a"]]
CidrBlock: "10.0.128.0/24"
DataSubnetARouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref DataSubnetA
RouteTableId: !Ref DataSubnetRouteTable
DataSubnetB:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Join ["", [!Ref "AWS::Region", "b"]]
CidrBlock: "10.0.129.0/24"
DataSubnetBRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref DataSubnetB
RouteTableId: !Ref DataSubnetRouteTable
# 8 Security Groups
ALBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Security group for ALB"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
VpcId: !Ref VPC
WebSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Security group for Web Servers"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
SourceSecurityGroupId: !Ref ALBSecurityGroup
VpcId: !Ref VPC
EFSSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Security group for EFS"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 2049
ToPort: 2049
SourceSecurityGroupId: !Ref WebSecurityGroup
VpcId: !Ref VPC
ElasticacheSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Security group for ElastiCache"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 11211
ToPort: 11211
SourceSecurityGroupId: !Ref WebSecurityGroup
VpcId: !Ref VPC
DatabaseSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Security group for Amazon RDS cluster"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 3306
ToPort: 3306
SourceSecurityGroupId: !Ref WebSecurityGroup
VpcId: !Ref VPC
# 9 ALB
ALB:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Scheme: internet-facing
Subnets:
- !Ref PublicSubnetA
- !Ref PublicSubnetB
LoadBalancerAttributes:
- Key: idle_timeout.timeout_seconds
Value: "60"
SecurityGroups:
- !Ref ALBSecurityGroup
ALBListener:
Type : AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref ALBTargetGroup
LoadBalancerArn: !Ref ALB
Port: 80
Protocol: HTTP
ALBTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckIntervalSeconds: 30
HealthCheckPath: /wp-login.php
HealthCheckTimeoutSeconds: 5
Port: 80
Protocol: HTTP
UnhealthyThresholdCount: 5
VpcId: !Ref VPC
# 10 Add ElastiCache
ElastiCacheSubnetGroup:
Type: AWS::ElastiCache::SubnetGroup
Properties:
Description: ElastiCache Subnet Group for WordPress
SubnetIds:
- !Ref DataSubnetA
- !Ref DataSubnetB
ElastiCacheCluster:
Type: AWS::ElastiCache::CacheCluster
Properties:
CacheNodeType: 'cache.t2.micro'
CacheSubnetGroupName: !Ref ElastiCacheSubnetGroup
Engine: memcached
NumCacheNodes: 1
VpcSecurityGroupIds:
- !Ref ElasticacheSecurityGroup
# 11 Database Cluster
DataSubnetGroup:
Type: AWS::RDS::DBSubnetGroup
Properties:
DBSubnetGroupDescription: "RDS Database Subnet Group for WordPress"
SubnetIds:
- !Ref DataSubnetA
- !Ref DataSubnetB
DatabaseCluster:
Type: AWS::RDS::DBCluster
Properties:
MasterUsername: "wordpress"
MasterUserPassword: "supersecret"
DatabaseName: "wordpressdb"
Engine: aurora
Port: 3306
EngineMode: serverless
ScalingConfiguration:
AutoPause: false
MaxCapacity: 16
MinCapacity: 2
DBSubnetGroupName: !Ref DataSubnetGroup
VpcSecurityGroupIds:
- !Ref DatabaseSecurityGroup
# 12 EFS
EFS:
Type: AWS::EFS::FileSystem
Properties:
PerformanceMode: 'maxIO'
EFSMountTargetA:
Type: AWS::EFS::MountTarget
Properties:
FileSystemId: !Ref EFS
SecurityGroups:
- !Ref EFSSecurityGroup
SubnetId: !Ref DataSubnetA
EFSMountTargetB:
Type: AWS::EFS::MountTarget
Properties:
FileSystemId: !Ref EFS
SecurityGroups:
- !Ref EFSSecurityGroup
SubnetId: !Ref DataSubnetB
# 13 Instance Profile
WebInstanceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: logs
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
- logs:DescribeLogStreams
Resource:
- arn:aws:logs:*:*:*
WebInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref WebInstanceRole
## 14 AutoScaling
WebLaunchConfiguration:
Type: AWS::AutoScaling::LaunchConfiguration
Properties:
IamInstanceProfile: !Ref WebInstanceProfile
ImageId: "ami-1a962263"
InstanceMonitoring: true
InstanceType: "t3.xlarge"
SecurityGroups:
- !Ref WebSecurityGroup
UserData:
"Fn::Base64":
!Sub |
#!/bin/bash -xe
yum update -y
mkdir -p /var/www/wordpress
mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 ${EFS}.efs.${AWS::Region}.amazonaws.com:/ /var/www/wordpress
/opt/aws/bin/cfn-init --configsets deploy_webserver --verbose --stack ${AWS::StackName} --resource WebLaunchConfiguration --region ${AWS::Region}
/opt/aws/bin/cfn-signal --exit-code $? --stack ${AWS::StackName} --resource WebAutoScalingGroup --region ${AWS::Region}
Metadata:
AWS::CloudFormation::Init:
configSets:
deploy_webserver:
- install_webserver
- build_cacheclient
- build_wordpress
- build_opcache
- download_aws_ini
- install_aws_ini
- install_cacheclient
- install_wordpress
- install_opcache
- start_webserver
install_webserver:
packages:
yum:
awslogs: []
httpd24: []
mysql56: []
php70: []
php70-devel: []
php7-pear: []
php70-mysqlnd: []
files:
/tmp/create_site_conf.sh:
content:
!Join [
"",[
"#!/bin/bash -xe\n",
"if [ ! -f /etc/httpd/conf.d/wordpress.conf ]; then\n",
" touch /etc/httpd/conf.d/wordpress.conf\n",
" echo 'ServerName 127.0.0.1:80' >> /etc/httpd/conf.d/wordpress.conf\n",
" echo 'DocumentRoot /var/www/wordpress/wordpress' >> /etc/httpd/conf.d/wordpress.conf\n",
" echo '<Directory /var/www/wordpress/wordpress>' >> /etc/httpd/conf.d/wordpress.conf\n",
" echo ' Options Indexes FollowSymLinks' >> /etc/httpd/conf.d/wordpress.conf\n",
" echo ' AllowOverride All' >> /etc/httpd/conf.d/wordpress.conf\n",
" echo ' Require all granted' >> /etc/httpd/conf.d/wordpress.conf\n",
" echo '</Directory>' >> /etc/httpd/conf.d/wordpress.conf\n",
"fi\n"
]
]
mode: 000500
owner: root
group: root
commands:
create_site_conf:
command: ./create_site_conf.sh
cwd: /tmp
ignoreErrors: false
build_cacheclient:
packages:
yum:
gcc-c++: []
files:
/tmp/install_cacheclient.sh:
content: |
#!/bin/bash -xe
ln -s /usr/bin/pecl7 /usr/bin/pecl #just so pecl is available easily
pecl7 install igbinary
wget -P /tmp/ https://s3.amazonaws.com/aws-refarch/wordpress/latest/bits/AmazonElastiCacheClusterClient-2.0.1-PHP70-64bit.tar.gz
tar -xf '/tmp/AmazonElastiCacheClusterClient-2.0.1-PHP70-64bit.tar.gz'
cp '/tmp/artifact/amazon-elasticache-cluster-client.so' /usr/lib64/php/7.0/modules/
if [ ! -f /etc/php-7.0.d/50-memcached.ini ]; then
touch /etc/php-7.0.d/50-memcached.ini
fi
echo 'extension=igbinary.so;' >> /etc/php-7.0.d/50-memcached.ini
echo 'extension=/usr/lib64/php/7.0/modules/amazon-elasticache-cluster-client.so;' >> /etc/php-7.0.d/50-memcached.ini
mode: 000500
owner: root
group: root
build_opcache:
packages:
yum:
php70-opcache: []
files:
/tmp/install_opcache.sh:
content: |
#!/bin/bash -xe
# create hidden opcache directory locally & change owner to apache
if [ ! -d /var/www/.opcache ]; then
mkdir -p /var/www/.opcache
fi
# enable opcache in /etc/php-7.0.d/10-opcache.ini
sed -i 's/;opcache.file_cache=.*/opcache.file_cache=\/var\/www\/.opcache/' /etc/php-7.0.d/10-opcache.ini
sed -i 's/opcache.memory_consumption=.*/opcache.memory_consumption=512/' /etc/php-7.0.d/10-opcache.ini
# download opcache-instance.php to verify opcache status
if [ ! -f /var/www/wordpress/wordpress/opcache-instanceid.php ]; then
wget -P /var/www/wordpress/wordpress/ https://s3.amazonaws.com/aws-refarch/wordpress/latest/bits/opcache-instanceid.php
fi
mode: 000500
owner: root
group: root
build_wordpress:
files:
/tmp/install_wordpress.sh:
content:
!Join [
"",[
"#!/bin/bash -xe\n",
"\n",
"# install wp-cli\n",
"if [ ! -f /bin/wp/wp-cli.phar ]; then\n",
" curl -o /bin/wp https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar\n",
" chmod +x /bin/wp\n",
"fi\n",
"\n",
"# make site directory\n",
"if [ ! -d /var/www/wordpress/wordpress ]; then\n",
" mkdir -p /var/www/wordpress/wordpress\n",
"\n",
" cd /var/www/wordpress/wordpress\n",
" # install wordpress if not installed\n",
" # use public alb host name if wp domain name was empty\n",
" if ! $(wp core is-installed --allow-root); then\n",
" wp core download --version='latest' --locale='en_GB' --allow-root\n",
" wp core config --dbname='wordpressdb' --dbuser='wordpress' --dbpass='supersecret' --dbhost='", !GetAtt DatabaseCluster.Endpoint.Address, "' --dbprefix=wp_ --allow-root\n",
" wp core install --url='", !GetAtt ALB.DNSName, "' --title='Test' --admin_user='admin' --admin_password='instruqt' --admin_email='noreply@xebia.com' --skip-email --allow-root\n",
" wp plugin install w3-total-cache\n",
" sed -i \"/$table_prefix = 'wp_';/ a \\define('WP_HOME', 'http://' . \\$_SERVER['HTTP_HOST']); \" /var/www/wordpress/wordpress/wp-config.php\n",
" sed -i \"/$table_prefix = 'wp_';/ a \\define('WP_SITEURL', 'http://' . \\$_SERVER['HTTP_HOST']); \" /var/www/wordpress/wordpress/wp-config.php\n",
" # set permissions of wordpress site directories\n",
" chown -R apache:apache /var/www/wordpress/wordpress\n",
" chmod u+wrx /var/www/wordpress/wordpress/wp-content/*\n",
" if [ ! -f /var/www/wordpress/wordpress/opcache-instanceid.php ]; then\n",
" wget -P /var/www/wordpress/wordpress/ https://s3.amazonaws.com/aws-refarch/wordpress/latest/bits/opcache-instanceid.php\n",
" fi\n",
" fi\n",
" RESULT=$?\n",
" if [ $RESULT -eq 0 ]; then\n",
" touch /var/www/wordpress/wordpress/wordpress.initialized\n",
" else\n",
" touch /var/www/wordpress/wordpress/wordpress.failed\n",
" fi\n",
"fi\n"
]
]
mode: 000500
owner: root
group: root
download_aws_ini:
files:
/tmp/download_aws_ini.sh:
content:
!Join [
"",[
"#!/bin/bash -x\n",
"\n",
"wget -P /etc/php-7.0.d/ https://s3.amazonaws.com/aws-refarch/wordpress/latest/bits/20-aws.ini\n"
]
]
mode: 000500
owner: root
group: root
install_aws_ini:
commands:
install_aws_ini:
command: ./download_aws_ini.sh
cwd: /tmp
ignoreErrors: true
install_wordpress:
commands:
install_wordpress:
command: ./install_wordpress.sh
cwd: /tmp
ignoreErrors: false
install_cacheclient:
commands:
install_cacheclient:
command: ./install_cacheclient.sh
cwd: /tmp
ignoreErrors: false
install_opcache:
commands:
install_opcache:
command: ./install_opcache.sh
cwd: /tmp
ignoreErrors: false
start_webserver:
services:
sysvinit:
httpd:
enabled: true
ensureRunning: true
# 15 auto scaling group
WebAutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
DependsOn:
- EFSMountTargetA
- EFSMountTargetB
- NAT
Properties:
Cooldown: "60"
HealthCheckGracePeriod: 120
HealthCheckType: ELB
LaunchConfigurationName: !Ref WebLaunchConfiguration
MaxSize: "2"
MinSize: "1"
TargetGroupARNs:
- !Ref ALBTargetGroup
VPCZoneIdentifier:
- !Ref PrivateSubnetA
- !Ref PrivateSubnetB
CreationPolicy:
ResourceSignal:
Count: 1
Timeout: PT15M