Spotlight
Guidance for Technical Leadership
A brief exploration of evidence-based approaches to Technical Leadership and Performance Evaluations.
You need to host a static site on AWS, so following CloudFormation-first development, you write a CloudFormation template that defines these resources:
Developing this template takes time, and every time you iterate on the CloudFront distribution, you have to wait 20 to 30 minutes for the distrubution to finish updating. This hurdle can be one of the biggest hits to your productivity.
Even when you have the template completed (or if you pulled a template from an example online), you still need to set up CI on your frontend code to upload the built site to the created S3 bucket. After that, you need to create a CloudFront cache invalidation to make sure your users are getting the right assets. You might even need to build some custom Lambda@Edge functions to handle redirects or rewrites, depending on how your website is structured. That will add another painful developer experience since you must wait 20-30 minutes every time that function is updated so the CloudFront distribution can finish updating.
There has to be another way, right? Right.
Not to be confused with other services under the AWS "Amplify" umbrella, Amplify Console provides a way to host your website with full CI/CD, almost entirely (and serverlessly) managed for you. So let's get a site up and running.
We'll follow CloudFormation-first development and start with a template. Amplify Console's most basic resource type is the AWS::Amplify::App
resource. The only required property here is Name
. While you should let CloudFormation name your resources in most cases, Amplify App names are not required to be unique. The most basic amplify app would look like this:
Resources:
AmplifyApp:
Type: AWS::Amplify::App
Properties:
Name: my-website
This CloudFormation will create an app that doesn't offer much in terms of CI; you'll have to upload your site whenever you want to deploy something manually. So let's fix that:
Parameters:
Repository:
Type: String
Description: GitHub Repository URL
OauthToken:
Type: String
Description: GitHub Repository URL
NoEcho: true
Branch:
Type: String
Description: The name of the branch to deploy off of
Resources:
AmplifyApp:
Type: AWS::Amplify::App
Properties:
Name: my-website
Repository: !Ref Repository
OauthToken: !Ref OauthToken
AccessToken: !Ref OauthToken
AmplifyBranch:
Type: AWS::Amplify::Branch
Properties:
BranchName: !Ref Branch
AppId: !GetAtt AmplifyApp.AppId
EnableAutoBuild: true
Here we've added a Repository
and an OauthToken
property to the amplify app. The token will be used to create a webhook and read-only deploy key for your third-party provider. For Github, the token will be a "Personal Access Token." For Bitbucket, unfortunately, CloudFormation is not yet well supported. As for the repository, this is expected to be the https url to the repository, without the trailing .git
.
We've also added an AWS::Amplify::Branch
resource. Amplify Console treats a git "branch" as a deployment "environment." For most connected apps, a branch is an environment, but for apps that are not connected, this terminology can be confusing; just remember branch == environment.
Now, whenever we push a commit to our repository, Amplify Console will pick that up and spin off a deployment.
What about a custom domain?
Parameters:
Repository:
Type: String
Description: GitHub Repository URL
OauthToken:
Type: String
Description: GitHub Repository URL
NoEcho: true
Branch:
Type: String
Description: The name of the branch to deploy off of
Domain:
Type: String
Description: Domain name to host application
Resources:
AmplifyApp:
Type: AWS::Amplify::App
Properties:
Name: my-website
Repository: !Ref Repository
OauthToken: !Ref OauthToken
AccessToken: !Ref OauthToken
AmplifyBranch:
Type: AWS::Amplify::Branch
Properties:
BranchName: !Ref Branch
AppId: !GetAtt AmplifyApp.AppId
EnableAutoBuild: true
AmplifyDomain:
Type: AWS::Amplify::Domain
Properties:
DomainName: !Ref Domain
AppId: !GetAtt AmplifyApp.AppId
SubDomainSettings:
- BranchName: !GetAtt AmplifyBranch.BranchName
Prefix: ''
- BranchName: !GetAtt AmplifyBranch.BranchName
Prefix: www
We added a Domain
parameter that feeds right into an AWS::Amplify::Domain
resource. This resource enables you to connect a custom domain to an amplify app. If you are using Route53 for DNS, and the hosted zone for the domain you want is in the same account, a record set will automatically be added for you. You can even configure prefixes, like the common www
prefix, as shown in the template.
This resource also provisions an ACM SSL certificate for you. It again will use DNS-verification with a Route53 hosted zone of the same domain automatically, as long as it's in the same account. Note that it may take a few minutes for the cert verification to complete.
And that's it; now you're making use of a fully-managed frontend CI/CD platform without needing to configure your own CloudFront distributions, ACM certificates, or Route53 records. That's not all, there's plenty of features Amplify Console has to offer.
In a recent project that was under a multi-account strategy, we had a dev, staging, and production account. We set up a process to deploy the amplify CloudFormation template in each account. The production account's app targeted the master
branch, the staging account's app targeted a test
branch, and the dev account's app targeted a develop
branch. We also configured feature branch deployments on the dev account's app, so we could have frontend devs work on features in parallel, without stepping on any other frontend dev's work. Since Amplify Console provides you with continuous deployment, this is our frontend CI/CD platform.
Amplify Console will automatically detect what framework you're using, whether it's React, Angular, Vue, or even site generators like VuePress, Jekyll, and Gatsby. However, if you have other build requirements, you can place a buildspec-like file in the root of your git repository called amplify.yml
. This file follows a buildspec-like format, and is documented here. The Amplify App can also contain the build steps if that's more desirable. Just include the buildspec under Properties
. You can also include an IAMServiceRole
property to give your build IAM permissions:
AmplifyRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: amplify.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: Amplify
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: amplify:*
Resource: '*'
AmplifyApp:
Type: AWS::Amplify::App
Properties:
Name: my-website
Repository: !Ref Repository
OauthToken: !Ref OauthToken
AccessToken: !Ref OauthToken
IAMServiceRole: !GetAtt AmplifyRole.Arn
BuildSpec: |
version: 1.0
frontend:
phases:
...
You can also add environment variables to the build so that you can inject things like an API url environment variable. For a react app, it would look something like this:
AmplifyApp:
Type: AWS::Amplify::App
Properties:
...
EnvironmentVariables:
- Name: REACT_APP_API_URL
Value: !Sub https://api.${Domain}
With Amplify Console, you can configure your app so that the branch that gets created under a specific wildcard match (like feature/*
) will automatically create a new branch/environment and deploy that branch to it. To configure this in CloudFormation:
AmplifyApp:
Type: AWS::Amplify::App
Properties:
AutoBranchCreationConfig:
AutoBranchCreationPatterns:
- feature/*
BasicAuthConfig:
EnableBasicAuth: True
Password: password
Username: admin
EnableAutoBranchCreation: True
EnableAutoBuild: True
Stage: PULL_REQUEST # PRODUCTION | BETA | DEVELOPMENT | EXPERIMENTAL | PULL_REQUEST
This feature is available for private GitHub repositories only. You must install the Amplify App first.
With Amplify Console, you can configure your app so that every pull request deploys to a unique environment, with a unique preview url. When the pull request is closed, the unique environment will be deleted with it. To set this up in CloudFormation, add the EnablePullRequestPreview
and PullRequestEnvironmentName
properties to your AWS::Amplify::Branch
resource.
AmplifyBranch:
Type: AWS::Amplify::Branch
Properties:
EnablePullRequestPreview: True
PullRequestEnvironmentName: prenv
Alternatively you can set this up in the AutoBranchCreationConfig
section of an AWS::Amplify::App
resource, which will apply to dynamically-created amplify branches:
AmplifyApp:
Type: AWS::Amplify::App
Properties:
AutoBranchCreationConfig:
EnablePullRequestPreview: True
PullRequestEnvironmentName: prenv
This feature is not supported in CloudFormation yet, but you can configure email notifications to receive updates on your app's deployment. You'll get emails when builds start, fail, and succeed, on a branch-by-branch basis.
In the Amplify console for your app, select "Email notifications" on the left, and from there, you can manage emails that should receive notifications per branch.
You can configure basic auth to password-protect your sites. This can be applied to the entire app, or just the auto-created environments (such as pull-request previews or environments created from the AutoBranchCreationConfig
).
With Amplify Console you can view access logs to your website. In the console, you can click on the "Access Logs" section and view access logs for each domain source under your app. This includes the date, time, host header, status, and useragent. You can query within a specific time range and even download the logs in csv format. The console also allows you to search through logs to find exactly what you need.
You can use redirects to reroute navigation from one url to another, which comes in handy when developing a Single Page App like a react app where you want navigation to be routed to /index.html
. The documentation has common rewrite and redirect rules. This can remove the need for most Lambda@Edge functions for far less operational cost.
There are four parts to a redirect:
Source
- the requested addressTarget
- the address that should serve the contentStatus
- the type of redirect (such as 200 rewrite or permanent redirect 301)Condition
- A two-letter country code, which enables you to segment your audience by regionTo set this up in CloudFormation:
AmplifyApp:
Type: AWS::Amplify::App
Properties:
...
CustomRules:
- Source: /documents
Target: /documents/us/
Condition: <US>
Status: '302'
Apps deployed through amplify get the benefit of instant cache invalidation. When a build completes, and your app is deployed, Amplify will instantly invalidate the cache serving the site.
There's a lot more that Amplify Console can do, including running Cypress tests and rendering screenshots of your app on various devices. It offers a ton of value in terms of letting AWS manage the heavy lifting, so you don't have to. If you've got a static site, take advantage of Amplify Console, and never worry about CloudFront, bucket permissions, or cache invalidation again!
A brief exploration of evidence-based approaches to Technical Leadership and Performance Evaluations.