PHP stack template for AWS Cloudformation¶
This was a template I created with troposphere and launches a PHP stack on AWS via cloudformation.
It takes no parameters but depends on the following Exports from another cloudformation stack.
Require Exports
- ElasticIP
- A export named ElasticIP (Domain Stack)
- VPC
- A export named VPC (VPC Stack) to add security groups
- Subnet1
- A export named Subnet1 (VPC Stack) to add our instance into
Note
- I was testing this in the eu-west-2 (London) region, you may have to modify this to match your geographic region
- To build this template use pip install troposphere and then run the file via python php-stack.py
Important
This template depends on the following stacks.
Hint
Checkout the following log files if you’re having any issues.
- cat /var/log/cloud-init-output.log
- cd /var/lib/cloud/instances/**/
# -*- coding: utf-8 -*-
from __future__ import print_function
import troposphere.ec2 as ec2
import os
from troposphere import Base64, Join, ImportValue
from troposphere import Parameter, Ref, Tags, Template
from troposphere.ec2 import SecurityGroupRule, SecurityGroup
#
# Troposphere template - https://github.com/cloudtools/troposphere
#
#
# ┌───────────────────────────────────────────────────────────────────────────┐
# │ Template & Parameters │
# └───────────────────────────────────────────────────────────────────────────┘
#
template = Template()
template.add_description("PHP stack | anil.io")
def make_name(component_name):
return Join('', [component_name, ' - ', Ref('AWS::StackName')])
#
# Linux AMI ID - We build of a AWS AMI, create your own image and add it here
#
LinuxAmi = template.add_parameter(Parameter(
"LinuxAmi",
Type="AWS::EC2::Image::Id",
Description="The AMI ID for our Linux Web server instance. (default: Amazon Linux AMI 2017 - ami-489f8e2c)",
Default="ami-489f8e2c",
))
#
# ┌───────────────────────────────────────────────────────────────────────────┐
# │ SSH Key pair │
# │ 1) add keypair via AWS Console 2) reference it here │
# └───────────────────────────────────────────────────────────────────────────┘
#
keyname_param = template.add_parameter(Parameter(
"KeyPairName",
Description="Name of an existing EC2 KeyPair to enable SSH access to the instance",
Type="AWS::EC2::KeyPair::KeyName",
Default="nld_aws_london"
))
#
# ┌───────────────────────────────────────────────────────────────────────────┐
# │ Security Groups │
# │ When creating a Security Group, a default "Main" group is also created. │
# │ We can ignore the empty group, we provide our own. │
# └───────────────────────────────────────────────────────────────────────────┘
#
instanceSecurityGroup = template.add_resource(
SecurityGroup(
'InstanceSecurityGroup',
GroupDescription='Enable EC2 HTTP, HTTPS',
SecurityGroupIngress=[
# SSH
SecurityGroupRule(IpProtocol='tcp', FromPort='22', ToPort='22', CidrIp='0.0.0.0/0'),
# HTTP
SecurityGroupRule(IpProtocol='tcp', FromPort='80', ToPort='80', CidrIp='0.0.0.0/0'),
# HTTPS
SecurityGroupRule(IpProtocol='tcp', FromPort='443', ToPort='443', CidrIp='0.0.0.0/0'),
# Github - https://help.github.com/articles/what-ip-addresses-does-github-use-that-i-should-whitelist/
# SecurityGroupRule(IpProtocol='tcp', FromPort='9418', ToPort='9418', CidrIp='192.30.252.0/22'),
],
VpcId=ImportValue('VPC'),
Tags=Tags(Name=make_name('Linux Web Server Sec-Group'))
)
)
#
# ┌───────────────────────────────────────────────────────────────────────────┐
# │ EC2 Instance │
# └───────────────────────────────────────────────────────────────────────────┘
#
# We have 1 instance. Linux (Amazon Linux AMI) built from an AMI
# You should create and setup this instance manually and reference it here
#
# An Elastic IP (required to expose it publicly), must be launched within a
# Subnet with an IGW. To expose a server publicly we need to have one of:
#
# a) An elastic IP
# b) An Elastic Load Balancer serving traffic to our instance
#
# View UserData files and output here:
# cd /var/lib/cloud/instances/**/
# /var/log/cloud-init-output.log
#
user_data_v_host = """ cat << EOF > /etc/httpd/conf.d/virtualhosts.conf
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
<Directory "/var/www/html">
AuthType Basic
AuthName "Restricted Content"
AuthUserFile /var/www/html/.htpasswd
Require valid-user
</Directory>
</VirtualHost>
EOF"""
user_data_bashrc = """ cat << EOF >> /home/ec2-user/.bashrc
# User
export PATH=./bin:$PATH
alias l='ls -alh'
# .pm2
export USER=ec2-user
export HOME=/home/ec2-user
export PM2_HOME=/home/ec2-user/.pm2
EOF"""
ec2_instance = template.add_resource(ec2.Instance(
"Ec2Instance",
ImageId=Ref(LinuxAmi),
InstanceType="t2.small",
KeyName=Ref(keyname_param),
SecurityGroupIds=[Ref(instanceSecurityGroup)],
SubnetId=ImportValue('Subnet1'),
Tags=Tags(Name=make_name('Linux Web Server')),
UserData=Base64(Join('', [
"#!/bin/bash\n"
# Set the timezone - picked up on next reboot
"sed -i '/ZONE=\"UTC\"/c\ZONE=\"Europe/London\"' /etc/sysconfig/clock \n",
"ln -sf /usr/share/zoneinfo/Europe/London /etc/localtime\n",
"echo [UserData] Updating Packages\n",
"yum update -y\n",
# Update bashrc with exports
user_data_bashrc,
". /home/ec2-user/.bashrc\n",
"echo [UserData] Installing LAMP\n",
"yum install -y httpd24 php56 mysql56-server php56-mysqlnd php56-intl git\n",
# Set PHP timezone
"sed -i \'/date.timezone =.*/a date.timezone = Europe\/London\' /etc/php.ini\n",
"service httpd start\n",
"chkconfig httpd on\n",
# Add a www group and add ec2-user to that group.
"groupadd www\n",
"usermod -a -G www ec2-user\n",
"chown -R ec2-user:www /var/www\n",
"chmod 2775 /var/www\n",
"find /var/www -type d -exec chmod 2775 {} +\n",
"find /var/www -type f -exec chmod 0664 {} +\n",
"echo \"<?php phpinfo(); ?>\" > /var/www/html/phpinfo.php\n",
# Add .htpasswd with user:pass demo:demo
"echo 'demo:$apr1$SHoPBfcN$httsSshmBhTe2pTOybwl3.' > /var/www/html/.htpasswd\n"
"touch /etc/httpd/conf.d/virtualhosts.conf\n"
"mkdir /var/www/html/logs\n"
"APACHE_LOG_DIR=/var/www/html/logs\n",
user_data_v_host,
"\nservice httpd restart\n"
# # Install Composer
# "curl -sS https://getcomposer.org/installer | php\n",
# "mv composer.phar /usr/bin/composer\n",
# "chmod +x /usr/bin/composer\n",
# "mkdir -p /home/ec2-user/.composer\n",
# # Install Redis
# "sudo yum-config-manager --enable epel -y\n",
# "sudo yum install redis -y\n",
# "sudo yum-config-manager --disable epel -y\n",
# "sudo service redis start -y\n",
# # Install Node.js - v6.x - http://stackoverflow.com/a/35165401/711308
# "curl --silent --location https://rpm.nodesource.com/setup_6.x | bash -\n",
# "sudo yum -y install nodejs npm\n\n",
# Go to: http://dev.lakhman.com/phpinfo.php
]
))
))
#
# Associate our ElasticIP (from Domain-Stack) to our instance
#
eip_association = template.add_resource(ec2.EIPAssociation(
"EIPEC2Association",
EIP=ImportValue('ElasticIP'),
InstanceId=Ref(ec2_instance),
))
#
# ┌───────────────────────────────────────────────────────────────────────────┐
# │ JSON DUMP │
# └───────────────────────────────────────────────────────────────────────────┘
#
with open(os.path.realpath(__file__)[:-2] + 'json', 'w') as the_file:
print(template.to_json(), file=the_file)
print('Generated: ' + __file__)
{
"Description": "PHP stack | anil.io",
"Metadata": {
"AWS::CloudFormation::Interface": {
"ParameterGroups": [
{
"Label": {
"default": "Application Configuration"
},
"Parameters": [
"LinuxAmi"
]
},
{
"Label": {
"default": "Amazon EC2 Configuration"
},
"Parameters": [
"KeyPairName",
"SSHLocation"
]
}
],
"ParameterLabels": {
"LinuxAmi": {
"default": "Linux Server AMI ID"
}
}
}
},
"Parameters": {
"KeyPairName": {
"Default": "nld_aws_london",
"Description": "Name of an existing EC2 KeyPair to enable SSH access to the instance",
"Type": "AWS::EC2::KeyPair::KeyName"
},
"LinuxAmi": {
"Default": "ami-489f8e2c",
"Description": "The AMI ID for our Linux Web server instance. (default: Amazon Linux AMI 2017 - ami-489f8e2c)",
"Type": "AWS::EC2::Image::Id"
},
"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.",
"Default": "52.56.84.136/32",
"Description": " The IP address range that can be used to SSH to the EC2 instances",
"MaxLength": "18",
"MinLength": "9",
"Type": "String"
}
},
"Resources": {
"EIPAssociation": {
"Properties": {
"EIP": {
"Fn::ImportValue": "ElasticIP"
},
"InstanceId": {
"Ref": "Ec2Instance"
}
},
"Type": "AWS::EC2::EIPAssociation"
},
"Ec2Instance": {
"Properties": {
"ImageId": {
"Ref": "LinuxAmi"
},
"InstanceType": "t2.small",
"KeyName": {
"Ref": "KeyPairName"
},
"SecurityGroupIds": [
{
"Ref": "InstanceSecurityGroup"
}
],
"SubnetId": {
"Fn::ImportValue": "Subnet1"
},
"Tags": [
{
"Key": "Name",
"Value": {
"Ref": "AWS::StackName"
}
}
],
"UserData": {
"Fn::Base64": {
"Fn::Join": [
"",
[
"#!/bin/bash\nsed -i '/ZONE=\"UTC\"/c\\ZONE=\"Europe/London\"' /etc/sysconfig/clock \n",
"ln -sf /usr/share/zoneinfo/Europe/London /etc/localtime\n",
"echo [UserData] Updating Packages\n",
"yum update -y\n",
"echo [UserData] Installing LAMP\n",
"yum install -y httpd24 php56 mysql56-server php56-mysqlnd php56-intl git\n",
"sed -i '/date.timezone =.*/a date.timezone = Europe\\/London' /etc/php.ini\n",
"service httpd start\n",
"chkconfig httpd on\n",
"groupadd www\n",
"usermod -a -G www ec2-user\n",
"chown -R root:www /var/www\n",
"chmod 2775 /var/www\n",
"find /var/www -type d -exec chmod 2775 {} +\n",
"find /var/www -type f -exec chmod 0664 {} +\n",
"echo \"<?php phpinfo(); ?>\" > /var/www/html/phpinfo.php\n",
"echo 'demo:$apr1$SHoPBfcN$httsSshmBhTe2pTOybwl3.' > /var/www/html/.htpasswd\ntouch /etc/httpd/conf.d/virtualhosts.conf\nmkdir /var/www/html/logs\nAPACHE_LOG_DIR=/var/www/html/logs\n",
" cat << EOF > /etc/httpd/conf.d/virtualhosts.conf\n<VirtualHost *:80>\n ServerAdmin webmaster@localhost\n DocumentRoot /var/www/html\n ErrorLog ${APACHE_LOG_DIR}/error.log\n CustomLog ${APACHE_LOG_DIR}/access.log combined\n\n <Directory \"/var/www/html\">\n AuthType Basic\n AuthName \"Restricted Content\"\n AuthUserFile /var/www/html/.htpasswd\n Require valid-user\n </Directory>\n</VirtualHost>\nEOF",
"\nservice httpd restart\necho 'export PATH=./bin:$PATH' | tee -a /home/ec2-user/.bash_profile /home/ec2-user/.bashrc\n",
"echo \"alias l='ls -alh'\" | tee -a /home/ec2-user/.bash_profile /home/ec2-user/.bashrc\n"
]
]
}
}
},
"Type": "AWS::EC2::Instance"
},
"InstanceSecurityGroup": {
"Properties": {
"GroupDescription": "Enable EC2 HTTP, HTTPS",
"SecurityGroupIngress": [
{
"CidrIp": "0.0.0.0/0",
"FromPort": "22",
"IpProtocol": "tcp",
"ToPort": "22"
},
{
"CidrIp": "0.0.0.0/0",
"FromPort": "80",
"IpProtocol": "tcp",
"ToPort": "80"
},
{
"CidrIp": "0.0.0.0/0",
"FromPort": "443",
"IpProtocol": "tcp",
"ToPort": "443"
}
],
"Tags": [
{
"Key": "Application",
"Value": {
"Ref": "AWS::StackId"
}
},
{
"Key": "Name",
"Value": "Linux WebServer Security Group"
}
],
"VpcId": {
"Fn::ImportValue": "VPC"
}
},
"Type": "AWS::EC2::SecurityGroup"
}
}
}