High availability

  • For high availability, your architecture should include at least two Availability Zones.
  • Consider adding load balancers and AWS Auto Scaling to automatically rebalance your nodes across zones.
  • For details about high availability and disaster recovery, refer to the AWS Well-Architected Framework.

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 a 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 customers want that.

  • Consider supporting multiple elastic network interfaces for private channels between nodes.

  • Do not prompt for new passwords. Instead, generate the passwords as part of your build, and store them securely in AWS Secrets Manager. Provide a link to the password in the Output section of your CloudFormation stack.

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

  • Do not pass sensitive data in EC2-instance customer data or other clear text bootstrapping mechanisms. If you write secrets to a secured file, you must delete the secured file after completion.

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

  • Do not create IAM resources with custom names, such as CAPABILITY_NAMED_IAM. Custom names increase the chances of a collision if resources are created concurrently in an account. If you must name your resources, consider appending a random prefix or suffix to the name to avoid name collisions.

  • Scrub logs for passwords, and avoid explicit output (bash set -x).

For more recommendations, refer to Security Pillar - AWS Well-Architected Framework.

Portability

Using AWS managed services

  • Use AWS managed services, such as Amazon Relational Database Service (Amazon RDS) and AWS Directory Service, whenever possible.
  • Use Auto Scaling groups to automatically adjust resource capacity.
  • Use load balancers (preferably Application Load Balancing or Elastic Load Balancing) for traffic distribution and to maintain application availability.

Using custom resources

If you’re implementing Lambda-backed custom resources in your CloudFormation stack, review the best practices discussed in the AWS Support Knowledge Center.

Linux

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

    #!/bin/bash –e

    This causes the script to exit with a nonzero exit code.

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

  • Modularize bootstrap stages:
    • Pre-install script examples:
      • Enables EPEL repositories
      • Checks for curl, wget unzip
      • Installs rpms or debs
      • Set up 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). Nonzero status is a failed execution. 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

  • Utilize common scripts from the GitHub aws-quickstart/quickstart-microsoft-utilities repository by adding them as submodules.
  • 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 Partner Solution 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 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 Partner Solution 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 configuration set. 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 configuration sets into bootstrapping stages. Example: prep, install or setup, configure, and finalize.

For all platforms

Derive AWS CloudFormation paths by joining Partner Solution inputs. For example, an S3 bucket name and key prefix from a parameter can be passed to 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 YAML format. For details, refer to AWS CloudFormation template formats.

For cross-platform compatibility in either format, use List File (LF) for UNIX-style line endings.

YAML:

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

GitHub branches

When creating your Partner Solution repository, we create the following branches:

  • main
  • doc-edits
  • gh-pages

When your Partner Solution repository is provisioned, you fork it into your personal or organization account. You can create any branches you like in your fork. When you’re ready to commit your source code to the Partner Solution repository, do so to the main branch using a pull request (PR).

The documentation for your Partner Solution is kept in an orphan branch named doc-edits. This orphan branch shares no history with the main branch. Do not mix content between these two branches.

The doc-edits branch contains boilerplate content templates (.adoc files) for you to use when documenting your Partner Solution. For instructions, refer to Build your AWS Partner Solution documentation. When you’re ready to commit your documentation to the Partner Solution repository, create a pull request (PR) to the doc-edits branch.

When the documentation is edited and approved, we publish it to the gh-pages branch.