Storybook is a static website and is therefore a perfect fit to be hosted on a S3 bucket.
I will go through how to setup the hosting and configuration of the site which will use the following ingredients:
- AWS S3 to store all the assets
- CDK with TypeScript to define the infrastructure as code
- IP access list to restrict access to the site
- Route53 to enable the use of a custom domain name
- AWS CLI, tool manage AWS services
Begin with installing the AWS CLI
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install**
I will presume you have an application with Storybook already added to it. Lets make a new directory in that project for the infrastructure code.
mkdir storybook-hosting && cd $_
# Install CDK
npm install -g aws-cdk
# Create a Typescript CDK application without GIT repo
npx cdk init app --language typescript --generate-only
# Install the dependencies
npm ci
# Open the IDE
code .
Start with modifying the storybook-hosting application, bin/storybook-hosting.ts
.
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { StorybookHostingStack } from '../lib/storybook-hosting-stack';
const app = new cdk.App();
new StorybookHostingStack(app, 'StorybookHostingStack', {
description: 'The infrastructure for hosting Storybook with IP restriction.',
env: { account: '<replace with correct account>', region: '<replace with correct region>' },
});
Now open the lib/storybook-hosting-stack.ts
file. We will start with adding a new method to the class where we will make it possible to add IP addresses that should have access to the site.
createIpAccessList(): string[] {
// Making it as an object so we can use the properties
// as nice descriptions of the ip addresses
const ipAddresses = {
myNetwork: '10.10.10.10/32',
myFriendsNetwork: '192.168.1.1/31',
}
return [...Object.values(ipAddresses)]
}
With that in place we can move on and import some libraries that we will need before configuring the S3 bucket.
import {
aws_iam as iam,
aws_route53 as route53,
aws_s3 as s3,
Stack,
StackProps,
} from 'aws-cdk-lib'
import { Construct } from 'constructs'
The imports will make more since when we have added our next method for setting up the S3 static website.
// The method takes two parameters, the domain name for the
// site, and the IP access list.
setupS3StaticWebsiteHosting(
storybookDomainName: string,
ipAccessList: string[]
): s3.Bucket {
// Create the bucket which will have the same name as
// the domain for the site
const bucket = new s3.Bucket(this, storybookDomainName, {
bucketName: storybookDomainName,
websiteIndexDocument: 'index.html',
websiteErrorDocument: 'error.html',
})
// Create an IAM policy for a accessing the objects
// in the bucket
const ipAccessListPolicy = new iam.PolicyStatement({
actions: ['s3:GetObject'],
effect: iam.Effect.ALLOW,
resources: [bucket.arnForObjects('*')],
principals: [new iam.AnyPrincipal()],
})
// Here we add a condition that only the ip addresses
// in ipAccessList will be allowed to access the bucket
ipAccessListPolicy.addCondition('IpAddress', {
'aws:SourceIp': ipAccessList,
})
bucket.addToResourcePolicy(ipAccessListPolicy)
// Add permission to the GitHub Pipeline role to deploy new assets to the bucket
// Only necessary if you use GitHub actions to deploy the story book solution
// to the S3 bucket
const gitHubPipelineRole = new iam.ArnPrincipal(
'arn:aws:iam::<use your account number>:role/github-pipeline-oidc-<pipeline role>'
)
bucket.policy?.document.addStatements(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [gitHubPipelineRole],
actions: ['s3:GetObject', 's3:PutObject', 's3:DeleteObject'],
resources: [bucket.arnForObjects('*')],
})
)
bucket.policy?.document.addStatements(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
principals: [gitHubPipelineRole],
actions: ['s3:ListBucket'],
resources: [bucket.bucketArn],
})
)
return bucket
}
I have tried to comment the code above as much as possible. The last two policies are only interesting if you want to setup a GitHub action workflow for a CI/CD solution. Finally we need a method for configuring the Route53 service so that we can use a domain name for the site.
configureDNS(bucket: s3.Bucket) {
const domainName = 'example.com'
const publicHostedZone = route53.PublicHostedZone.fromLookup(
this,
'example-com-hosted-zone',
{
domainName: domainName,
}
)
const _ = new route53.CnameRecord(this, 'CName', {
recordName: 'storybook',
zone: publicHostedZone,
domainName: bucket.bucketWebsiteDomainName,
})
}
This code expect you to already have registered a domain name through Route53, and in this case, example.com
. The cname record will use storybook
as a subdomain for the site, which means that the complete address will be, storybook.example.com
.
To tie all this together there has to be some changes in the constructor of the stack.
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props)
const storybookDomainName = 'storybook.example.com'
const ipAccessList = this.createIpAccessList()
const bucket = this.setupS3StaticWebsiteHosting(
storybookDomainName,
ipAccessList
)
this.configureDNS(bucket)
}
The CDK application is complete now and we can deploy it to AWS. That can be done from the terminal like this. However make sure that your AWS credentials are initialized correctly for the AWS CLI.
npx cdk deploy StorybookHostingStack
When the deployment has finished the infrastructure is all done for the hosting part of the site and we have to push some content to the S3 bucket.
To do that we will need to add Storybook Deployer to our web project.
npm i @storybook/storybook-deployer --save-dev
This simple tool will enable the possibility to deploy storybook to a S3 bucket. After the dependency has been installed we can add a new script entry in the package.json
file. This script will handle the deployment for us.
"deploy:storybook": "mkdir -p ../build && NODE_OPTIONS=--openssl-legacy-provider storybook-to-aws-s3 -o ../build/storybook --aws-profile=NONE --bucket-path=storybook.example.com/",
Use the help to see what options you have with the tool.
storybook-to-aws-s3 --help
The last step is to actually deploy some content to the site by running the script.
npm run deploy:storybook
Now everything should be in place and there is restricted access to the storybook site at storybook.example.com
.
One thing to note here is that we donβt have any TLS certificate for the site so you will need to use http
and not https
when accessing the site.
This is a quite simple and fast setup for making your story book more accessible for your team (and cheap).
Happy surfing!
Top comments (0)