irtnog-aws-static-website
This AWS CloudFormation stack hosts a static website using Amazon S3 and Amazon CloudFront.
Theory of Operation
TODO
Prerequisites
When deploying the stack with a web browser, whether interactively through the CloudFormation console or from the command line via AWS CloudShell, you must log into the AWS Console with an IAM user or role that has the developer power user job function.
Alternatively, to deploy the stack from the command line on a PC or Mac:
-
Configure AWS API access. Again, your IAM user account from which you derive your credentials must have the developer power user job function.
-
The example shell commands require a contemporary version of GNU Bash. On Windows, use the Git Bash app included with Git. On macOS, use the Terminal app.
-
Install jq. On Windows, download the 64-bit version of jq 1.6, rename
jq-win64.exe
tojq.exe
, and copy it to a directory in the executable search path. On macOS, use MacPorts (preferred) or HomeBrew to install jq. -
Optionally, set (and export) the environment variable AWS_PROFILE to the AWS CLI named profile to use, e.g.,
corpdev
. -
Optionally, set (and export) the environment variable AWS_REGION to the AWS data center cluster to which the CloudFormation stack will be deployed, e.g.,
us-east-1
.
In the example commands shown below, these shell variables will be used as follows:
-
TEMPLATE_BUCKET is the S3 bucket hosting the CloudFormation templates, e.g.,
devops-library-${AWS_REGION}
,cf-templates-1234567890abc-${AWS_REGION}
. While you may deploy the stack from the release bucket,irtnog-aws-static-website
, we recommend deploying from an S3 bucket under your control. Refer to Release Engineering for more information. -
STACK_NAME is a globally unique ID for the CloudFormation stack, e.g.,
example-web-site
. -
HOSTNAME is the website's fully qualified domain name (FQDN), e.g.,
www.example.com
.
Certificate
Amazon Certificate Manager (ACM) must have a public certificate for your website in the us-east-1 region. You may import an existing certificate or request a new one. The first FQDN on the certificate must match the website's FQDN. For example, commands similar to the following will request a new certificate from ACM and publish a domain control validation (DCV) record in the matching Amazon Route 53 hosted zone:
eval $(
aws acm request-certificate \
--domain-name "${HOSTNAME}" \
--validation-method DNS \
--output json \
| jq -r '@sh "CERTARN=\(.CertificateArn)"'
)
CERTDCVRRNAME=""
until [ -n "${CERTDCVRRNAME}" ]
do
sleep 5
eval $(
aws acm describe-certificate \
--certificate-arn "${CERTARN}" \
--output json \
| jq -r '.Certificate.DomainValidationOptions[0].ResourceRecord
| @sh "CERTDCVRRNAME=\(.Name); CERTDCVRRTYPE=\(.Type); CERTDCVRRVALUE=\(.Value)"'
)
done
eval $(
aws route53 list-hosted-zones-by-name --output json \
| jq -r '.HostedZones[]|.Name as $name
| select("'${CERTDCVRRNAME}'"|test($name))
| @sh "ZONEID=\(.Id|split("/")[2])"'
)
aws route53 change-resource-record-sets \
--hosted-zone-id "${ZONEID}" \
--change-batch file:///dev/fd/0 <<EOF
{
"Changes": [
{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "${CERTDCVRRNAME}",
"Type": "${CERTDCVRRTYPE}",
"TTL": 300,
"ResourceRecords": [
{
"Value": "${CERTDCVRRVALUE}"
}
]
}
}
]
}
EOF
Deployment
Launch the CloudFormation stack in your AWS account using one of the following links:
Alternatively, launch the stack using commands similar to the following:
eval $(
aws acm list-certificates \
--region us-east-1 \
--output json \
| jq -r '.CertificateSummaryList[]|select(.DomainName == "'${HOSTNAME}'")
| @sh "CERTARN=\(.CertificateArn)"'
)
aws cloudformation create-stack \
--stack-name ${STACK_NAME} \
--template-url https://s3.amazonaws.com/${TEMPLATE_BUCKET}/irtnog-aws-static-website.yaml \
--parameters \
ParameterKey=TemplateBucket,ParameterValue=${TEMPLATE_BUCKET} \
ParameterKey=FullyQualifiedDomainName,ParameterValue=${HOSTNAME} \
ParameterKey=CertificateArn,ParameterValue=${CERTARN} \
;
To publish your website, create a CNAME record for the website's FQDN that points to the stack's DistributionFqdn
output value. Note that certain restrictions apply to CNAME records.
However, if your domain is hosted in Route 53, create an alias record instead. For example, commands similar to the following will search Route 53 for a suitable hosted zone and create an alias record in it, overwriting any existing alias record:
eval $(
aws route53 list-hosted-zones-by-name --output json \
| jq -r '.HostedZones[]|.Name as $name|select("'${HOSTNAME}'."|test($name))
| @sh "ZONEID=\(.Id|split("/")[2])"'
)
eval $(
aws cloudformation describe-stacks \
--stack-name ${STACK_NAME} \
--output json \
| jq -r '.Stacks[0].Outputs[]|select(.OutputKey == "DistributionFqdn")
| @sh "DISTNAME=\(.OutputValue)"'
)
aws route53 change-resource-record-sets \
--hosted-zone-id $ZONEID \
--change-batch file:///dev/fd/0 <<EOF
{
"Changes": [
{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "${HOSTNAME}.",
"Type": "A",
"AliasTarget": {
"DNSName": "${DISTNAME}",
"HostedZoneId": "Z2FDTNDATAQYW2",
"EvaluateTargetHealth": false
}
}
}
]
}
EOF
Maintenance
Upload the website content to the S3 bucket created by the stack.
Removal
TODO
Release Engineering
The releng.yaml CloudFormation template creates resources used to deploy the stack.
Those resources must be created in the us-east-1 region due to current limitations in the construction of TemplateURL
values.
The production release is hosted in the us-east-1 region in a bucket named irtnog-aws-static-website
, with the relevant resources created using commands similar to the following:
aws cloudformation deploy \
--template-file releng.yaml \
--stack-name ${RELENG_STACK_NAME} \
--region us-east-1
To publish a release to the S3 bucket created by the above:
eval $(
aws cloudformation describe-stacks \
--stack-name ${RELENG_STACK_NAME} \
--region us-east-1 \
--output json \
| jq -r '.Stacks[0].Outputs[]|select(.OutputKey == "BucketUrl")
| @sh "RELENG_BUCKET_URL=\(.OutputValue)"'
)
(cd templates; aws s3 sync . ${RELENG_BUCKET_URL}templates/ --region us-east-1)
aws s3 cp irtnog-aws-static-website.yaml ${RELENG_BUCKET_URL} --region us-east-1
Contributing
TODO