High availability

  • Your architecture should include at least two Availability Zones to provide high availability.
  • Consider adding load balancers and Auto Scaling to automatically rebalance your nodes across Availability Zones.
  • For additional details about HA/DR, see the Architecting for the Cloud whitepaper.

Security

  • Place your product instances in private subnets, and route private subnet traffic through NAT gateways that are configured in public subnets. We recommend this configuration unless your architecture requires otherwise (e.g., your application provides networking services or manages other servers, and therefore requires direct inbound connection from the Internet).

  • Always use security groups and IAM roles to limit access to resources. Provision minimal access by applying the principle of least privilege (PoLP).

  • For bastion host access, provide a CIDR block parameter and apply it as an ingress rule to the bastion security group. In other security groups, add the bastion security group for connectivity within the VPC for administration, if needed. For example, a web server security group could include an ingress rule for the bastion host security group for SSH TCP port 22.

  • Consider integrating Amazon Inspector into the deployment. This should be an opt-in feature, because Amazon Inspector installs agents on each instance, and not all users will want that.

  • Consider supporting multiple ENIs for private channels between nodes.

  • Do not hardcode passwords. Instead, request them as parameters and include the NoEcho property.

  • Do not pass sensitive data in EC2 instance user data or other clear text bootstrapping mechanisms. Secrets can be written to a secured file and cleaned up at stack completion.

  • Do not hardcode security groups to 0.0.0.0/0 for ingress rules.

For additional recommendations, see the AWS Security Best Practices whitepaper.

Using AWS managed services

  • Use AWS managed services such as Amazon RDS and AWS Directory Service whenever possible.
  • Use Auto Scaling groups to automatically adjust resource capacity.
  • Use load balancers (preferably ALB/ELB) for traffic distribution and to maintain application availability.

Linux

  • Enable the -e flag at the top of all scripts (except user data) as follows:

    #!/bin/bash –e

    This will cause the script to exit with a non-zero exit code.

  • Use $? to check the status of your user data scripts.

  • Modularize bootstrap stages:
    • Pre-install script examples:
      • Enables epel repos
      • Checks for curl, wget unzip
      • Installs rpms or debs
      • Setup python setup tools (pip/easy_install)
      • Creates logical volumes and mount points
      • Enables service on boot (using systemctl or chkconfig)
    • App environment script examples:
      • Configures application writes (/etc/someapp.conf)
      • Creates MYSQL schema
      • Updates pip modules
    • App install:
      • Install core application
      • Execute installation with needed assets (license file or location)
  • Check status when switching install stage (pre-install/app-env/app-install). Non-zero status is a failed execution. For example:

    cat some.sh | bash -e -o pipefail
    if [ $STATUS -eq 0 ]
    then
        echo "Script [PASS]"
        # Continue to next step
    else
        echo "Script [FAILED]" >&2
        # Signal your resources
        exit 1
    fi
    
  • Define script names to new bootstrap version so they can be tested in the same location without overwriting existing assets:

    LOCATION={ "Ref": "QuickStartS3URL" }/{ "Ref": "QSS3BucketName" }/{ "Ref": "QSS3KeyPrefix" }
    wget or curl -O ${LOCATION}/scripts/${FILENAME}
    chmod 755 ${FILENAME}
    
  • Use -x flag so all script output is logged to cloud_init-output.log.

Windows

  • Make use of the common scripts in the GitHub aws-quickstart/quickstart-microsoft-utilities repo by adding it as a submodule.
  • If you’re using PowerShell scripts, follow these guidelines:
    • Wrap your scripts with a try/catch block that sends the exception to Write-AWSQuickStartException from the AWS Quick Start PowerShell module.
    • Include $ErrorActionPreference = "Stop" at the top of the scripts to ensure that exceptions are caught.
    • Include a Start-Transcript C:\cfn\log\<ScriptName>.txt -Append to capture all of the logging from the script being executed.
    • Use the Write-AWSQuickStartException cmdlet to signal to AWS CloudFormation, and also to write an Event Viewer error event with details about the exception.
    • Wrap all PowerShell scripts with try/catch blocks to signal exceptions and abort stack:

      param(
         [Parameter(Mandatory=$true)]
          [string]$Parameter1
      )
      try {
          $ErrorActionPreference = "Stop"
          #DO STUFF
      }
      catch {
          $_ | Write-AWSQuickStartException
      }
      
  • Use the AWS Quick Start PowerShell module to manage signaling and exceptions.
  • Handle reboots by using the waitAfterCompletion property of a command in the commands section of a cfn-init config set; for example:

    "b-rename-computer": {
        "command": {
            "Fn::Join": [
                "",
                [
                    "powershell.exe -Command \"C:\\cfn\\scripts\\Rename-Computer.ps1 -Restart -NewName '",
                    {
                        "Ref": "WSFCNode1NetBIOSName"
                    },
                    "'\""
                ]
            ]
        },
        "waitAfterCompletion": "forever"
    }
    
  • Divide the cfn-init config sets into stages of bootstrapping; for example: prep, install or setup, configure, finalize.

For all platforms

  • Derive AWS CloudFormation paths by joining Quick Start inputs. For example, a bucket name and a key prefix from a parameter can be passed into a script and the script can join them to create a path. Do not hardcode script paths.

Template formatting

You can author your AWS CloudFormation template in JSON or YAML format. For details, see the AWS CloudFormation documentation.

In either format, use LF for (UNIX style) line endings for cross-platform compatibility.

JSON:

  • Use 4-space tabs for indentation.
  • If your favorite editor supports JSON, make sure that the .template extensions are treated as JSON files. This allows for easy formatting. Additionally, you can use your favorite linter for formatting and validation.

YAML:

  • Use 2-space tabs for indentation.
  • Wrap strings with single quotes as needed.