Provisioning an Apache HTTP Server with AWS CloudFormation
A couple of quick code snippets to get you provisioning and on your way.
Join the DZone community and get the full member experience.
Join For FreeFrom the AWS CloudFormation homepage:
"CloudFormation allows you to use a simple text file to model and provision, in an automated and secure manner, all the resources needed for your applications across all regions and accounts. This file serves as the single source of truth for your cloud environment."
CloudFormation is a web service that falls under the Infrastructure-as-Code (IaC) category. IaC allows users to define their infrastructure in a text file using a declarative approach to model your infrastructure. The text file is called "template" and is written in either JSON or YAML notation.
You may also enjoy: Using CloudFormation to Set Up Scalable Apps
In this post, I am going to share two templates file written in YAML which, when executed, provision an EC2 instance and install an Apache HTTP server without you logging in to the EC2 terminal. This can be used for beginners to get started with CloudFormation and add more configurations.
FAQs
Why Not Have Them All In One File?
It is true that you can declare all the resources in a single file, but as a best practice, it is always recommended to modularize your IaC. This helps in individual testing, deployment, the controlled impact of change, and most importantly, sharing. As an example, if there is an issue with my lambda deployment or EC2 deployment, then it helps and saves time to rollback or update only these components and leave the VPC and database as they are.
Why YAML?
To be honest, I have worked a lot on JSON (still using it in my current project), but after reading people's experience and trying both myself, I personally think YAML is suited better for a template. You don't see double-quotes and curly braces in YAML, which makes it more readable, plus you can add a lot of comments. Anyway, if you are comfortable with JSON, then go for it because YAML needs some learning (mine is still in progress).
So, let's get started. These template files are also available on GitHub for your reference.
Step 1
Create a base stack that consists of a VPC, RouteTable, NACL, IGW, and a Subnet. Based on your region, please provide the appropriate parameter values in the CloudFormation dashboard.
AWSTemplateFormatVersion 2010-09-09
Description
Sample template to create a VPC with IGW and public IP enabled.
You will be billed for the AWS resources used if you create a stack from this template.
After deleting stack, remember to delete the associated S3 bucket.
Parameters
# CIDR of VPC
NetworkCIDR
Description CIDR of the new VPC
Type String
Default 10.0.0.0/16
# AZ Name where subnet will be created
AvailabilityZoneName
Description CIDR of the new VPC
Type AWS EC2 AvailabilityZone Name
Default ap-south-1a
# CIDR of new subnet within this VPC
SubnetCIDR
Description CIDR of the new subnet within this VPC
Type String
Default 10.0.1.0/24
Resources
# create VPC
myVPC
Type AWS EC2 VPC
Properties
CidrBlock !Ref NetworkCIDR
EnableDnsHostnames'true'
EnableDnsSupport'true'
InstanceTenancy default
Tags
Key Name
Value demo-vpc
Key Application
Value !Ref 'AWS::StackName'
# create Internet Gateway
myIGW
Type AWS EC2 InternetGateway
Properties
Tags
Key Name
Value demo-igw
Key Application
Value !Ref 'AWS::StackName'
# attaching the IGW to my VPC
vpcToIgw
Type AWS EC2 VPCGatewayAttachment
Properties
VpcId !Ref myVPC
InternetGatewayId !Ref myIGW
# create a custom route table for demo vpc
myRouteTable
Type AWS EC2 RouteTable
Properties
VpcId !Ref myVPC
Tags
Key Name
Value demo-public-route-table
Key Application
Value !Ref 'AWS::StackName'
# Add routes entries for public network through igw
myRoutes
Type AWS EC2 Route
Properties
RouteTableId !Ref myRouteTable
DestinationCidrBlock'0.0.0.0/0'
GatewayId !Ref myIGW
# NACL
myPublicNACL
Type'AWS::EC2::NetworkAcl'
Properties
VpcId !Ref myVPC
Tags
Key Name
Value demo-vpc-nacl
Key Application
Value !Ref 'AWS::StackName'
# Allow all Incoming TCP traffic
myNaclRulesForInboundTCP
Type'AWS::EC2::NetworkAclEntry'
Properties
NetworkAclId !Ref myPublicNACL
RuleNumber'100'
Protocol'6' # tcp
RuleAction allow
Egress'false' # this rule applies to ingress traffic to the subnet
CidrBlock 0.0.0.0/0 # any ip address
PortRange
From'0'
To'65535'
# Allow all Outgoing TCP traffic
myNaclRulesForOutboundTCP
Type'AWS::EC2::NetworkAclEntry'
Properties
NetworkAclId !Ref myPublicNACL
RuleNumber'100'
Protocol'6' # tcp
RuleAction allow
Egress'true' # this rule applies to egress traffic from the subnet
CidrBlock 0.0.0.0/0
PortRange
From'0' # client will be using ephemeral port, using 80 or 22 here will not work
To'65535'
# creating a public subnet
myPublicSubnet
Type AWS EC2 Subnet
Properties
VpcId !Ref myVPC
AvailabilityZone !Ref AvailabilityZoneName
CidrBlock !Ref SubnetCIDR
MapPublicIpOnLaunchtrue
Tags
Key Name
Value
!Join
''
'public-subnet-' -
!Ref AvailabilityZoneName
Key Application
Value !Ref 'AWS::StackName'
# asscoiate subnet with our route table else by default it is asscoiated with main route table
mySubnetRouteTableAssociation
Type'AWS::EC2::SubnetRouteTableAssociation'
Properties
SubnetId !Ref myPublicSubnet
RouteTableId !Ref myRouteTable
# associate subnet with NACL else by default it is asscoiated with main NACLs
mySubnetNaclAssociation
Type'AWS::EC2::SubnetNetworkAclAssociation'
Properties
SubnetId !Ref myPublicSubnet
NetworkAclId !Ref myPublicNACL
# output key resources ids and export the values for cross-stack referencing
Outputs
VpcID
Description ID of the newly created VPC
Value !Ref myVPC
Export
Name !Sub "${AWS::StackName}-VPCID" # the name for cross referencing
PublicSubnetID
Description SubnetId of the public subnet
Value !Ref myPublicSubnet
Export
Name !Sub "${AWS::StackName}-SUBNET"
Step 2
Create the second stack that consists of a Security Group, rules to allow HTTP and SSH, an EC2 instance, and a user-data to install the Apache HTTP server. This stack will reference the base stack. Please provide the correct image ID which is available in your region. The default value is from the Mumbai (ap-south) region.
AWSTemplateFormatVersion 2010-09-09
Description
Sample template to provision an EC2 Instance with public IP. Create a Security Group and associate with this EC2.
You will be billed for the AWS resources used if you create a stack from this template.
After deleting stack, remember to delete the associated S3 bucket.
# get the name of the base stack which is created first and has VPC details
Parameters
VPCStackName
Description Name of the base VPC stack
Type String
Default BaseStack
KeyPairName
Description Name of an existing EC2 KeyPair to enable SSH access to the instance
Type'AWS::EC2::KeyPair::KeyName' # standard type
ConstraintDescription must be the name of an existing EC2 KeyPair.
InstanceType
Description EC2 instance type
Type String
Default t2.micro
InstanceImageId
Description EC2 Image Id from this region
Type AWS EC2 Image Id
Default ami-0cb0e70f44e1a4bb5 # defaults for amazon linux in mumbai region
Resources
# create a security group
mySG
Type AWS EC2 SecurityGroup
Properties
GroupDescription Enable http(80) & ssh(22) access
GroupName WebServer-SG
VpcId
Fn::ImportValue !Sub "${VPCStackName}-VPCID" # note here we are not using AWS::StackName
SecurityGroupIngress
# allow http
IpProtocol tcp
FromPort'80'
ToPort'80'
CidrIp 0.0.0.0/0 # any IP
# allow ssh
IpProtocol tcp
FromPort'22'
ToPort'22'
CidrIp 0.0.0.0/0 # only for demo else use your IP or corporate gateway IP
Tags
Key Name
Value demo-sg
Key Application
Value
Ref"AWS::StackName"
# allow local traffic
SGBaseIngress
Type AWS EC2 SecurityGroupIngress
Properties
GroupId !Ref mySG
IpProtocol'-1'
FromPort'-1'
ToPort'-1'
SourceSecurityGroupId !Ref mySG
# EC2 instance which will have access for http and ssh
EC2Instance
Type'AWS::EC2::Instance'
Properties
InstanceType !Ref InstanceType
SubnetId
Fn::ImportValue !Sub "${VPCStackName}-SUBNET"
SecurityGroupIds
!Ref mySG
KeyName !Ref KeyPairName
ImageId !Ref InstanceImageId
UserData
Fn::Base64
#!/bin/bash -xe
yum update -y # good practice to update existing packages
yum install -y httpd # install web server
systemctl start httpd
systemctl enable httpd
echo "Hello World" > /var/www/html/index.html
Tags
Key Name
Value demo-ec2
Key Application
Value
Ref"AWS::StackName"
# output important values for easy viewing in cloudformation dashboard
Outputs
InstanceId
Description InstanceId of the first EC2 instance
Value !Ref EC2Instance
PublicDNS
Description Public DNS Name of the EC2 instance
Value !GetAtt
EC2Instance
PublicDnsName
PublicIP
Description Public IP address of the EC2 instance
Value !GetAtt
EC2Instance
PublicIp
After provisioning, you can check the "Output" section of the CloudFormation dashboard to get the public IP and point your browser to this IP. Don't forget to delete the stack and the S3 bucket to avoid any cost.
Further Reading
Provision a Free AWS EC2 Instance in 5 Minutes
How to Secure an Apache Web Server
Opinions expressed by DZone contributors are their own.
Comments