Using parameters and setting defaults

  • Try to parameterize as much as possible, making sure to cover settings that you expect users to customize. Some examples are CIDR blocks, FQDN names, host names, instance types, and storage volume sizes.
  • When setting defaults for instance types, make sure that the default is available in all (or the majority of) AWS Regions. To check availability, see the tables in the Amazon EC2 Pricing webpages.

Naming, labeling, and grouping

Using parameter types

  • Use AWS-specific parameter types as much as possible. This enables users to pick values from a dropdown list. See the AWS documentation for a list.

Using the DependsOn attribute

  • Make sure to use the DependsOn attribute appropriately across resources to control the order of resource creation. Also, be mindful of the situation where it is required. For more information around these special cases, see the AWS CloudFormation documentation.

Adding Quick Start portability parameters

  • Include the standard parameters for the Quick Start S3 bucket name and key prefix. Set the default value for the key prefix to quickstart-companyname-productname/, e.g., quickstart-microsoft-rdgateway/.

...
    "QSS3BucketName": {
        "AllowedPattern": "^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$",
        "ConstraintDescription": "Quick Start bucket name can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-).",
        "Default": "aws-quickstart",
        "Description": "S3 bucket name for the Quick Start assets. Quick Start bucket name can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-).",
        "Type": "String"
        },
    "QSS3KeyPrefix": {
        "AllowedPattern": "^[0-9a-zA-Z-/]*$",
        "ConstraintDescription": "Quick Start key prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/).",
        "Default": "quickstart-microsoft-rdgateway/",
        "Description": "S3 key prefix for the Quick Start assets. Quick Start key prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/).",
        "Type": "String"
...

  • Use the AWS CloudFormation Fn::Sub function to join the Quick Start S3 bucket name and key prefix values. For example:

   "Fn::Sub": "https://${QSS3BucketName}.s3.amazonaws.com/${QSS3KeyPrefix}scripts/some-script.sh"
   
  • Use an IAM role with a policy that allows GetObject to ${QSS3BucketName}/${QSS3KeyPrefix}*; for example:

   "BastionHostRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
         "Policies": [
            {
               "PolicyDocument": {
                  "Version": "2012-10-17",
                  "Statement": [
                     {
                        "Action": [
                           "s3:GetObject"
                        ],
                        "Resource": {
                           "Fn::Sub": "arn:aws:s3:::${QSS3BucketName}/${QSS3KeyPrefix}*"
                        },
                        "Effect": "Allow"
                     }
                  ]
               },
               "PolicyName": "aws-quick-start-s3-policy"
            }
         ],
         "Path": "/",
         "AssumeRolePolicyDocument": {
            "Statement": [
               {
                  "Action": [
                     "sts:AssumeRole"
                  ],
                  "Principal": {
                     "Service": [
                        "ec2.amazonaws.com"
                     ]
                  },
                  "Effect": "Allow"
               }
            ],
            "Version": "2012-10-17"
         }
      }
   },
   "BastionHostProfile": {
      "Type": "AWS::IAM::InstanceProfile",
      "Properties": {
         "Roles": [
            {
               "Ref": "BastionHostRole"
            }
         ],
         "Path": "/"
      }
   },

  • Associate the instance with the instance profile from the IAM role; for example, based on the previous code:

   "BastionLaunchConfiguration": {
      "Type": "AWS::AutoScaling::LaunchConfiguration", or "Type": "AWS::EC2::Instance",
...
      "Properties": {
...
         "IamInstanceProfile": {
            "Ref": "BastionHostProfile"
         },
...

  • Use AWS CloudFormation authentication with the IAM role, and use cfn-init to access resources; for example:

"Metadata": {
  "AWS::CloudFormation::Authentication": {
     "S3AccessCreds": {
        "type": "S3",
        "roleName": {
           "Ref": "BastionHostRole"
        },
        "buckets": [
           {
              "Ref": "QSS3BucketName"
           }
        ]
     }
  },
  "AWS::CloudFormation::Init": {
     "config": {
        "files": {
           "/tmp/bastion_bootstrap.sh": {
              "source": {
                 "Fn::Sub": "https://${QSS3BucketName}.s3.amazonaws.com/${QSS3KeyPrefix}scripts/bastion_bootstrap.sh"
              },
              "mode": "000550",
              "owner": "root",
              "group": "root",
              "authentication": "S3AccessCreds"
           }
        },
...
  "UserData": {
     "Fn::Base64": {
        "Fn::Join": [
           "",
           [
              "#!/bin/bash\n",
              "export PATH=$PATH:/usr/local/bin\n",
              "which pip &> /dev/null\n",
              "if [ $? -ne 0 ] ; then\n",
              "    echo \"PIP NOT INSTALLED\"\n",
              "    [ `which yum` ] && $(yum install -y epel-release; yum install -y python-pip) && echo \"PIP INSTALLED\"\n",
              "    [ `which apt-get` ] && apt-get -y update && apt-get -y install python-pip && echo \"PIP INSTALLED\"\n",
              "fi\n",
              "pip install --upgrade pip &> /dev/null\n",
              "pip install awscli --ignore-installed six &> /dev/null\n",
              "easy_install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz\n",
...
              "cfn-init -v --stack ",
              {
                 "Ref": "AWS::StackName"
              },
              " --resource BastionLaunchConfiguration --region ",
              {
                 "Ref": "AWS::Region"
              },
              "\n",
              "cfn-signal -e $? --stack ",
              {
                 "Ref": "AWS::StackName"
              },
              " --resource BastionAutoScalingGroup --region ",
              {
                 "Ref": "AWS::Region"
              },
              "\n"
           ]
        ]
     }
  }