Table of Contents
On this lab we will convert our infrastructure into code by using CloudFormation to stand up the same stack from last week.
Resources:
- http://jsonlint.com/
- http://www.tutorialspoint.com/json/json_data_types.htm
- http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html
- http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html
Create a CloudFormation template.
CloudFormation makes use of JSON to define parameters, resources and outputs. The high-level CloudFormation data structure is an Object; in JSON, Object are associative-arrays, i.e., a JSON Object associates keys with values.
- Create a new plain-text JSON file with the following sections. These are the minimum requirements for defining a CloudFormation template.
AWSTemplateFormatVersion
Parameters
Resources
Outputs
- Name it
<STUDENT ID>_lab_1.json
and save it to your desktop.
It should look something like:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "...",
"Parameters": {
},
"Resources": {
},
"Outputs": {
}
}
Parameters in CloudFormation are also JSON arrays, these take a Type
and a Description
. Fill in the parameters section to your CloudFormation template.
- Fill in the parameters section with
StudentId
,KeyName
,SubnetId
,InstanceType
,AmiId
, andWebAppSecurityGroup
.
It should look something like:
"StudentId": {
"Type": "String",
"Description": "Your student id, e.g., student1"
},
"KeyName": {
"Type": "AWS::EC2::KeyPair::KeyName",
"Description": "Name of an existing EC2 KeyPair to enable SSH access to the instance"
},
"SubnetId": {
"Type": "AWS::EC2::Subnet::Id",
"Description": "A subnet ID where the app will run"
},
"VpcId": {
"Type": "AWS::EC2::VPC::Id",
"Description": "A VPC ID where the app will run"
},
"InstanceType": {
"Description": "WebServer EC2 instance type",
"Type": "String",
"Default": "m3.medium"
},
"AmiId": {
"Description": "The AMI (Amazon Machine Image) ID",
"Type": "AWS::EC2::Image::Id"
},
"WebAppSecurityGroup": {
"Description": "The Web application security group ID",
"Type": "AWS::EC2::SecurityGroup::Id"
}
Within the Resources
section define a resource named WebServerInstance
of type AWS::EC2::Instance
. AWS::EC2::Instance
requires Properties
, ImageId
, InstanceType
, and KeyName
sections. Note how parameters are passed from the Parameters
section to the definition of the resource.
It should look something like:
"WebServerInstance": {
"Type": "AWS::EC2::Instance",
"Properties": {
"NetworkInterfaces": [
{
"AssociatePublicIpAddress": "true",
"DeviceIndex": "0",
"GroupSet": [
{
"Ref": "WebAppSecurityGroup"
}
],
"SubnetId": {
"Ref": "SubnetId"
}
}
],
"ImageId": {
"Ref": "AmiId"
},
"InstanceType": {
"Ref": "InstanceType"
},
"KeyName": {
"Ref": "KeyName"
},
"Tags": [
{
"Key": "Name",
"Value": {
"Ref": "StudentId"
}
}
]
}
}
Add a UserData
section to the Properties
subsection of the WebServerInstance
resource. In the UserData section, embed the necessary commands to install your application.
It should look something like:
"UserData": {
"Fn::Base64": {
"Fn::Join": [
"",
[
"#!/bin/bash -xe\n",
"rpm -ivh http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-7.noarch.rpm\n",
"yum -y install git git-core zlib zlib-devel gcc-c++ patch readline readline-devel libyaml-devel libffi-devel openssl-devel make bzip2 autoconf automake libtool bison curl sqlite-devel\n",
"yum -y install nodejs mariadb mariadb-server mariadb-devel\n",
"systemctl enable mariadb.service\n",
"systemctl start mariadb.service\n",
"rpm -ivh https://s3-us-west-2.amazonaws.com/dso-public-bucket/ruby-2.3.1-1.el7.x86_64.rpm\n",
"cd /home/ec2-user\n",
"echo \"export GEM_HOME=~/.gem\" >> .bash_profile\n",
"echo \"export GEM_PATH=~/.gem\" >> .bash_profile\n",
"echo \"export RAILS_ENV=mysql\" >> .bash_profile\n",
"echo \"export PATH=~/.gem/bin:$PATH\" >> .bash_profile\n",
"su -l -c \"git clone https://github.com/OWASP/railsgoat.git\" ec2-user\n",
"su -l -c \"gem install bundler\" ec2-user\n",
"su -l -c \"cd railsgoat && bundle install && bundle exec rake db:setup\" ec2-user\n",
"su -l -c \"cd railsgoat && bundle exec rails server -b 0.0.0.0 -p 8080 &\" ec2-user\n",
"\n"
]
]
}
}
** The resulting template should look something like lab-1.json.
- Log into the DSO target account.
E.g.,
$ unset AWS_SESSION_TOKEN AWS_SECRET_ACCESS_KEY AWS_ACCESS_KEY_ID
$ assumer -a 717986480831 -r human/dso/TGT-dso-DeploymentAdmin \
-A 100352119871 -R dso/ctrl/my-app/CTL-my-app-DeploymentAdmin \
-o dso -g -u $AWS_USERNAME
- Take note of your instance's configuration you will need these fields:
- Subnet ID
- VPC ID
- AMI ID
- Security Group ID
- Key Pair (your key name)
-
Terminate your (hand-jammed) instance. You can do this by selecting it in the console, then selecting
Actions
>Instance State
>Terminate
. -
Using the resources at the top of this lab, verify your CloudFormation template by validating the JSON file.
-
On the AWS Console select
Services
>CloudFormation
. Then clickCreate Stack
, selectUpload a template to Amazon S3
, clickBrowse...
and select your CloudFormation template and clickOK
. ClickNext
. -
Enter your student ID under
Stack name
. Using the information collected from step 2, fill in the rest of the form fields. ClickNext
, clickNext
, clickCreate
. Wait until your stack deployment is successful (CREATE_COMPLETE
). If you get aROLLBACK_COMPLETE
error, look under theevents
tab in the CloudFormation console to determine what's going on. -
Go back to
EC2
, note the public IP address of your new instance, onceStatus Checks
pass, ssh into the instance. Keep an eye on/var/log/cloud-init.log
to see if any errors occur. You can do this withtail
, e.g.,tail -f /var/log/cloud-init.log
, to exittail
pressctrl+c
. -
Load
http://PUBLIC_IP_ADDRESS:8080
on your browser. Is your application up and running?
Challenge: Using your awesome skills and the resources above fill in the Outputs section to expose/print your application's URL. Destroy your stack, make the changes and reload it. Under the Outputs tab in the CloudFormation console, make sure that your application URL is displayed.