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.
AWSTemplateFormatVersionParametersResourcesOutputs
- Name it
<STUDENT ID>_lab_1.jsonand 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_COMPLETEerror, look under theeventstab in the CloudFormation console to determine what's going on. -
Go back to
EC2, note the public IP address of your new instance, onceStatus Checkspass, ssh into the instance. Keep an eye on/var/log/cloud-init.logto see if any errors occur. You can do this withtail, e.g.,tail -f /var/log/cloud-init.log, to exittailpressctrl+c. -
Load
http://PUBLIC_IP_ADDRESS:8080on 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.