Skip to content

Breakdown of Resources section of CloudFormation Template

Chris Churas edited this page Oct 19, 2018 · 24 revisions

Overview

The Resources section has lots of subsections but each resource has the following format:

        "<NAME>": {
            "Type": { },
            "Properties": { }
        }

Here is a resource to spin up EC2 instance along with some needed fields under the Properties subsection:

        "<INSTANCE NAME>": {
            "Type": "AWS::EC2::Instance",
            "Properties": {
                "InstanceType": {
                },
                "SecurityGroups": [
                ],
                "KeyName": {
                },
                "ImageId": {
                },
                "BlockDeviceMappings" : [
                ],
                "Tags" : [ 
                ],
                "UserData"    : {  
                }
            }
       }
  }

In above example <INSTANCE NAME> would be given a name (such as GPUInstance for CDeep3M)

In addition, a security group resource is also defined as is a wait condition and wait handle so the CloudFormation template knows when the EC2 instance is ready. An example of this is here:

"InstanceSecurityGroup": {
            "Type": "AWS::EC2::SecurityGroup",
            "Properties": { }
        },
        "WaitHandle" : {
            "Type" : "AWS::CloudFormation::WaitConditionHandle",
            "Properties" : { }
        },
        "WaitCondition" : {
            "Type" : "AWS::CloudFormation::WaitCondition",
            "DependsOn" : "<INSTANCE NAME>",
            "Properties" : { }
        }
}

The rest of this page describes these resources in more detail.


"Type": { }

Under the <NAME> section and defines type of resource. For CDeep3M the big resource is AWS::EC2::Instance which is an EC2 instance.

"Properties": { }

This section also under the <NAME> section which other attributes for that resource can be defined.

"Properties": { "Type": "AWS::EC2::Instance", "InstanceType": { } }

For EC2 resource types this defines type of instance. In case of CDeep3M the user gets to choose via the GPUInstanceType parameter and their selection is set with the following code:

"InstanceType": {
                    "Ref": "GPUInstanceType"
                }

"Properties": { "Type": "AWS::EC2::Instance", "SecurityGroups": { } }

For EC2 resource types this defines security or firewall rules of instance. For CDeep3M another resource defines the firewall rules named InstanceSecurityGroup hence the Ref below.

"SecurityGroups": [
                    {
                        "Ref": "InstanceSecurityGroup"
                    }
                ],

"Properties": { "Type": "AWS::EC2::Instance", "KeyName": { } }

Sets the [ssh] key for the instance. For CDeep3M a reference to the user selection is passed in via "Ref": "KeyName"

"KeyName": {
                    "Ref": "KeyName"
                },

"Properties": { "Type": "AWS::EC2::Instance", "ImageId": { } }

Sets the AMI image to use for the EC2 instance. For CDeep3M the code below extracts the AMI for the region the instance is running in.

"ImageId": {
                    "Fn::FindInMap": [ "RegionMap",
                        { "Ref": "AWS::Region" }, "AMI" ]
                    
                },

"Properties": { "Type": "AWS::EC2::Instance", "BlockDeviceMappings": { } }

Sets the disk for the EC2 instance. The CDeep3M code below creates a gp2 EBS disk with size set by user parameter GPUDiskSize

"BlockDeviceMappings" : [
                    {
                      "DeviceName" : "/dev/sda1",
                      "Ebs" : { 
                                 "VolumeSize" : { "Ref": "GPUDiskSize" },
                                 "VolumeType" : "gp2"
                              }
                    }
                ],

"Properties": { "Type": "AWS::EC2::Instance", "Tags": { } }

Lets one set tags on EC2 which can be used to identify things. For CDeep3M a tag named Name is set to the name of the stack.

"Tags" : [ 
                    {
                      "Key" : "Name", "Value" : { "Ref": "AWS::StackName" } 
                    }
                ],

"Properties": { "Type": "AWS::EC2::Instance", "UserData": { } }

For EC2 resource types this defines a post installation script that is run on the EC2 instance after it is booted up. The value must be base64 encoded which is done by the AWS function "Fn::Base64"

For CDeep3M, this section contains commands to install various packages, such as octave, as well as install CDeep3M.

First few lines from actual UserData for CDeep3M:

"UserData"    : { "Fn::Base64" : { "Fn::Join" : [ "", [
                     "#!/bin/bash\n\n",
                     "echo 'Installing octave' `date` ' ::::'`date +%s`\n",
                     "apt-get update\n",
                     "ecode=100\n",
                     "while [ $ecode -ne 0 ]\n",
                     "do\n",
                     "  apt-get -y install octave octave-image octave-pkg-dev git python-pip unzip parallel python-opencv python-mpi4py libtiff-dev libjpeg62\n",
                     "  ecode=$?\n",
                     "  echo 'Sleeping 1 second'\n",
                     "  sleep 1\n",
                     "done\n\n",

The Fn::Base64 performs a base64 encoding of the string and the Fn::Join joins the lines (delimited by commas) into one string. This first section installs some needed packages. The while loop is there cause the Unattended upgrades sometimes starts and logic was needed to retry.

Cell Image Library Data Download

The DatasetURL parameter if set with a URL causes the post installation script defined in UserData section to create a download script /home/ubuntu/datasetdownload.sh with a wget call to download the URL passed in to /home/ubuntu/.datasetdownload. When the download is complete the resulting data is moved into the /home/ubuntu directory. After creating the script the UserData section will use the screen command to create a new screen virtual terminal and invoke the /home/ubuntu/datasetdownload.sh script which downloads the data. In addition, the /etc/motd file is modified to let users know data is being downloaded.

The section that performs the above actions:

                     "export DATASETURL=",
                     { "Ref" : "DatasetURL" },
                     "\n",
                     "if [ -n \"$DATASETURL\" ] ; then\n",
                     "  cd /home/ubuntu\n",
                     "  echo -e '#!/bin/bash\nmkdir -p /home/ubuntu/.datasetdownload\n' > /home/ubuntu/datasetdownload.sh\n",
                     "  echo -e 'dataset='",
                     { "Ref" : "DatasetURL" },
                     " >> /home/ubuntu/datasetdownload.sh\n",
                     "  echo -e '\npushd /home/ubuntu/.datasetdownload\nwget --timeout=30 \"$dataset\"\n' >> /home/ubuntu/datasetdownload.sh\n",
                     "  echo -e 'if [ $? == 0 ] ; then\n  mv * /home/ubuntu/.\n' >> /home/ubuntu/datasetdownload.sh\n",
                     "  echo -e '  sudo wall \"Download of $dataset complete\"\nelse\n' >> /home/ubuntu/datasetdownload.sh\n",
                     "  echo -e ' sudo wall \"Download of $dataset failed\"\nfi\n' >> /home/ubuntu/datasetdownload.sh\n",
                     "  chmod a+x /home/ubuntu/datasetdownload.sh\n",
                     "  chown ubuntu.ubuntu /home/ubuntu/datasetdownload.sh\n",
                     "  sudo -u ubuntu screen -S downloader -d -m nice -n 19 /home/ubuntu/datasetdownload.sh\n",
                     "  export DATASETFILE=`echo \"$DATASETURL\" | sed 's/.*\\///'`\n",
                     "  echo 'NOTE: Downloading '$DATASETFILE' file in background to ~/.datasetdownload, file will appear in /home/ubuntu when done' >> /etc/motd\n",
                     "  echo '      Advanced users can see progress via this screen resume command: screen -r downloader' >> /etc/motd\n",
                     "  echo '      (To exit the screen type Ctrl-a d) If no screen is available, download has completed or failed' >> /etc/motd\n",
                     "fi\n",

Telling CloudFormation script in UserData has completed

This section uses cfn-signal to inform CloudFormation that the script in UserData has completed. The { "Ref" : "WaitHandle" } gets converted to a URL by CloudFormation and the --exit-code lets the caller denote success with a zero 0 or failure with a one 1 In the logic below the if statement simply does a single retry if the first attempt fails. For robustness this could be converted to a while loop, but a single retry so far has been sufficient.

"echo 'Signaling completion' `date` ' ::::'`date +%s`\n",
                     "/usr/local/bin/cfn-signal --exit-code 0 -i gpu1 '", { "Ref" : "WaitHandle" }, "'\n",
                     "if [ $? != 0 ] ; then\n",
                     "  sleep 10;/usr/local/bin/cfn-signal --exit-code 0 -i gpu1 '", { "Ref" : "WaitHandle" }, "'\n",
                     "fi\n",
Clone this wiki locally