Setup a web server on AWS using Cloud Formation

In this post, we will write infrastructure as code, using Cloud Formation, to set up a web server on an EC2 instance on AWS.

We will be covering the following items:

  • Create an EC2 instance manually using AWS console
  • Automate the creation of EC2 instance with Cloud Formation
  • Add a name to the instance and create a tag
  • Connect to the EC2 instance through SSH
  • Set up a web server on the EC2 instance (manually then through Cloud Formation as a startup script)

As the goal of this post is mainly infrastructure, we will just create a web app that displays “Hello world!” when accessed from the browser.

Note — There might be a cost associated with creating/provisioning resources on AWS. Please make sure to check the pricing first, and delete the resources when done.

Below are a few definitions that would help us get started:

CloudFormation — AWS CloudFormation is a service that gives developers and businesses an easy way to create a collection of related AWS and third-party resources, and provision and manage them in an orderly and predictable fashion.

CloudFormation accepts a “Template” input that contains the definition of the resources that we want to create. Then it creates a stack based on the template provided.

  • A template is a JSON or YML file that contains a definition of the resource to create (e.g. EC2 instance, LoadBalancer, S3 bucket, etc.)
  • A stack implements and manages the group of resources outlined in the template, and handles the dependencies between these resources

This is the instance that will host our web server. We will start by creating it manually to get an understanding of what we’re doing, then use CloudFormation to automate this process afterward

  • Open AWS Console in your browser
  • Find EC2 service and select it (EC2 is a web service that provides resizable compute capacity in the cloud — in simpler terms, it is an AWS service that enables us to create virtual machines in the cloud)
  • Select Instances, then click the “Launch instances” button
  • Select the Amazon Machine Image (AMI) that we want to use to create that EC2 instance (AMIs are pre-built images that is used to create the virtual machine) — select one that is Free tier eligible. At the time of creating this post this is displayed as a tag under the AMI name as per the below screenshot — Select Amazon Linux 2 AMI (HVM), SSD Volume Type
  • The second step is to choose the Instance type (the type define the CPU , memory and other properties of the instance) — we will go with t2.micro, which is also Free tier eligible
  • At this point it is possible to click Launch instance or add more configuration

On your machine, create a new YAML file and call it `template.yml`:

The first key under “Resources” is the resource logical ID, which could be used as a reference in other resources (e.g. we would use the “EC2Instance” logical ID to add a security group to this instance). As for the “ImageId”, I copied it from the AMI page (screenshot above).

Navigate to CloudFormation in AWS Console, and click “Create Stack”. This will have an option to upload the template:

Give the stack a name (e.g. cf-ec2-example), leave everything else as default, and create the stack. In the “Events” tab notice what’s happening:

A new EC2 instance got created — check it out under “EC2” → “Instances”. It will show as follows:

To add a name to the instance we need to create a new Tag with “Name” as a key. Also, add an “Owner” tag

In the AWS console, under CloudFormation, select the previously created stack and click “Update”, then replace the template.

Notice the stack event:

Check the EC2 instance and verify that its name got updated, and an additional “Owner” key got added to the tags.

After creating the instance, we want to connect to it through ssh to install the webserver. This requires a key pair to be created in AWS, and the output pem file to be used when connecting. Do this by navigating to the AWS console → EC2 → Key Pair. Create a key pair, and give it a name. Once completed, a .pem file with the same name will be generated (the one I generated is called ec2-instances.pem) — For more info on how to generate a key pair have a look at

As we haven’t specified a key pair when we created our stack, we won’t be able to connect to the instance.

Update our CF template to include a key pair to connect to the instance. The template will look like that:

Notice the KeyName: ec2-instances added to the template. Update the stack, and once the EC2 instance is created, let’s connect to it:

In the EC2 console, select the instance, and copy the Public IPv4 address under the details tab. Open your terminal and run the following command:

ssh ec2-user@<copied IP address> -i ~/.ssh/ec2-instances.pem

Replace <copied IP address> with the Public IPv4 address we copied earlier. and replace ~/.ssh/ by the path under which you saved the pem file.

Let us now install a web server on the EC2 instance. As we already created the instance and ssh into it, installing the webserver requires only the execution of a few commands:

yum update -y # update existing packagesyum install -y httpd # install apache web serversystemctl start httpdsystemctl enable httpdecho "Hello World" > /var/www/html/index.html

After completing this, in the AWS console → EC2, select the instance and copy the Public IPv4 DNS from the Details tab. Paste that DNS in your browser and you will get a page that shows “Hello World”.

This is cool, but it feels like a lot of manual effort to ssh to the machine to install a web server. So having a script that runs on the startup of the instance to do this installation would be really useful. Let’s do this in CloudFormation.

We need to use UserData and run a shell script. The UserData has to be base64 encoded text.

In this CloudFormation template, we used the UserData key and passed to it the intrinsic function Fn::Base64, which returns the Base64 representation of the input string.

The input we’ve given is a !Sub or Fn::Sub an intrinsic function that substitutes variables in an input string with values that you specify. The Sub function returns the original string, substituting the values for all of the variables. In this case, there are no variables to substitute (we’re using this function to pass the bash script as a string to the Base64 function)

Now in AWS CloudFormation, update the stack. After completion navigate to EC2 and copy the Public IPv4 DNS from the Details tab and load it in the browser (A website with hello world would be displayed). Stop then start the EC2 instance, and load the new Public IPv4 DNS. The “Hello world” site will always show.

I hope this is helpful.

Software engineer (JS | REACT | Node | AWS | Test Automation)

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store