概要
Storybookはコンポーネントのカタログやドキュメント化に重宝するツールですが、チーム内で共有するには適切なホスティング環境が必要です。この記事では、AWS CloudFormationを使って、GitHubからの自動デプロイ、Basic認証付きのStorybookホスティング環境を構築する方法を紹介します。
完成イメージ
以下のような環境を自動構築します:
メリット
- GitHubへのプッシュで自動デプロイ
- CloudFront Functionによる簡易認証
- CloudFrontによる高速コンテンツ配信
- CloudFormationでインフラをコード化
構築手順
1. CloudFormationテンプレートの作成
まず、storybook-cicd.yml
というファイル名でCloudFormationテンプレートを作成します:
AWSTemplateFormatVersion: "2010-09-09"
Description: "Storybook CI/CD Pipeline with Basic Authentication"
Parameters:
GitHubBranch:
Type: String
Default: main
Description: デプロイするブランチ
BasicAuthToken:
Type: String
Description: "Base64でエンコードされたユーザー名:パスワード"
NoEcho: true
RepositoryName:
Type: String
Description: "GitHubリポジトリ名(organization/repository形式)"
GithubConnectionArn:
Type: String
Description: "AWS CodeStar GitHub接続のARN"
Resources:
# S3バケット - Storybook用デプロイ先
storybookBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "${AWS::StackName}-storybook"
AccessControl: Private
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
LifecycleConfiguration:
Rules:
- Id: expire-noncurrent-versions
Status: Enabled
NoncurrentVersionExpiration:
NoncurrentDays: 30
- Id: delete-incomplete-mpu-7days
AbortIncompleteMultipartUpload:
DaysAfterInitiation: 7
Prefix: ""
Status: Enabled
VersioningConfiguration:
Status: Enabled
# S3バケット - パイプラインアーティファクト用
pipelineBucket:
Type: AWS::S3::Bucket
Properties:
AccessControl: Private
BucketName: !Sub "${AWS::StackName}-pipeline"
LifecycleConfiguration:
Rules:
- Id: expire-noncurrent-versions
Status: Enabled
NoncurrentVersionExpiration:
NoncurrentDays: 30
- Id: delete-incomplete-mpu-7days
AbortIncompleteMultipartUpload:
DaysAfterInitiation: 7
Prefix: ""
Status: Enabled
VersioningConfiguration:
Status: Enabled
# CloudFront Function for Basic Auth
basicAuthFunction:
Type: AWS::CloudFront::Function
Properties:
Name: !Sub "${AWS::StackName}-basic-auth"
AutoPublish: true
FunctionConfig:
Comment: "Basic Authentication for Storybook"
Runtime: cloudfront-js-2.0
FunctionCode: !Sub |
function handler(event) {
var request = event.request;
var headers = request.headers;
// echo -n username:password | base64
var authString = "Basic ${BasicAuthToken}";
if (
typeof headers.authorization === "undefined" ||
headers.authorization.value !== authString
) {
return {
statusCode: 401,
statusDescription: "Unauthorized",
headers: { "www-authenticate": { value: "Basic" } }
};
}
return request;
}
# CloudFront OriginAccessControl
storybookOAC:
Type: AWS::CloudFront::OriginAccessControl
Properties:
OriginAccessControlConfig:
Description: Origin Access Control for Storybook
Name: !Sub ${AWS::StackName}-storybook-oac
OriginAccessControlOriginType: s3
SigningBehavior: always
SigningProtocol: sigv4
# CloudFront Distribution
storybookDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Comment: !Sub "${AWS::StackName} Storybook Distribution with Basic Auth"
DefaultCacheBehavior:
AllowedMethods:
- GET
- HEAD
- OPTIONS
CachedMethods:
- GET
- HEAD
- OPTIONS
Compress: true
DefaultTTL: 86400 # 1日
ForwardedValues:
Cookies:
Forward: none
QueryString: false
MaxTTL: 31536000 # 1年
MinTTL: 0
TargetOriginId: S3Origin
ViewerProtocolPolicy: redirect-to-https
FunctionAssociations:
- EventType: viewer-request
FunctionARN: !GetAtt basicAuthFunction.FunctionARN
DefaultRootObject: index.html
Enabled: true
HttpVersion: http2
Origins:
- DomainName: !GetAtt storybookBucket.RegionalDomainName
Id: S3Origin
OriginAccessControlId: !GetAtt storybookOAC.Id
S3OriginConfig: {}
PriceClass: PriceClass_100
ViewerCertificate:
CloudFrontDefaultCertificate: true
# S3バケットポリシー - CloudFrontからのアクセス許可
storybookBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref storybookBucket
PolicyDocument:
Statement:
- Sid: AllowCloudFrontServicePrincipal
Effect: Allow
Principal:
Service: cloudfront.amazonaws.com
Action: s3:GetObject
Resource: !Sub ${storybookBucket.Arn}/*
Condition:
StringEquals:
AWS:SourceArn: !Sub arn:aws:cloudfront::${AWS::AccountId}:distribution/${storybookDistribution}
# CodeBuildプロジェクト
codeBuildProject:
Type: AWS::CodeBuild::Project
Properties:
Artifacts:
Type: CODEPIPELINE
Environment:
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/amazonlinux2-x86_64-standard:5.0
PrivilegedMode: true
Type: LINUX_CONTAINER
EnvironmentVariables:
- Name: DEPLOYMENT_BUCKET
Type: PLAINTEXT
Value: !Ref storybookBucket
- Name: DISTRIBUTION_ID
Type: PLAINTEXT
Value: !Ref storybookDistribution
ServiceRole: !GetAtt codeBuildServiceRole.Arn
Source:
BuildSpec: |
version: 0.2
phases:
install:
runtime-versions:
nodejs: 22
commands:
- npm install
build:
commands:
- npm run build-storybook
post_build:
commands:
- aws s3 sync storybook-static s3://$DEPLOYMENT_BUCKET/ --exact-timestamps --delete
- aws cloudfront create-invalidation --distribution-id $DISTRIBUTION_ID --paths "/*"
artifacts:
files:
- '**/*'
base-directory: storybook-static
Type: CODEPIPELINE
# CodePipeline
codePipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
ArtifactStore:
Location: !Ref pipelineBucket
Type: S3
RoleArn: !GetAtt codePipelineServiceRole.Arn
Stages:
- Name: Source
Actions:
- Name: Source
ActionTypeId:
Category: Source
Owner: AWS
Provider: CodeStarSourceConnection
Version: '1'
Configuration:
ConnectionArn: !Ref GithubConnectionArn
DetectChanges: true
FullRepositoryId: !Ref RepositoryName
BranchName: !Ref GitHubBranch
OutputArtifactFormat: CODEBUILD_CLONE_REF
OutputArtifacts:
- Name: SourceCode
Region: !Ref AWS::Region
RunOrder: 1
- Name: Build
Actions:
- Name: BuildAndDeploy
ActionTypeId:
Category: Build
Owner: AWS
Provider: CodeBuild
Version: '1'
Configuration:
ProjectName: !Ref codeBuildProject
InputArtifacts:
- Name: SourceCode
OutputArtifacts:
- Name: BuildOutput
Region: !Ref AWS::Region
RunOrder: 1
OnFailure:
Result: ROLLBACK
# IAMロール - CodeBuild用
codeBuildServiceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: codebuild.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonS3FullAccess
- arn:aws:iam::aws:policy/CloudFrontFullAccess
# IAMロール - CodePipeline用
codePipelineServiceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: codepipeline.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AWSCodeBuildAdminAccess
- arn:aws:iam::aws:policy/AmazonS3FullAccess
- arn:aws:iam::aws:policy/AWSCodeStarFullAccess
Outputs:
StorybookURL:
Description: "Storybook URL"
Value: !Sub "https://${storybookDistribution.DomainName}"
2. テンプレートのデプロイ
AWS CloudFormationでスタックを作成します:
# AWS CLIを使う場合
aws cloudformation create-stack \
--stack-name storybook-cicd \
--template-body file://storybook-cicd.yml \
--capabilities CAPABILITY_NAMED_IAM \
--parameters \
ParameterKey=GitHubBranch,ParameterValue=main \
ParameterKey=RepositoryName,ParameterValue=your-org/your-repo \
ParameterKey=GithubConnectionArn,ParameterValue=arn:aws:codestar-connections:region:account:connection/xxxx \
ParameterKey=BasicAuthToken,ParameterValue=YOUR_BASE64_TOKEN
または、AWS Management Consoleからも作成できます。
3. GitHub接続の設定
CodePipelineがGitHubリポジトリからコードを取得するには、AWS CodeStar接続を設定する必要があります:
- AWS Management Consoleで「Developer Tools」→「Settings」→「Connections」を開く
- 「Create connection」をクリック
- プロバイダーとして「GitHub」を選択し、接続名を入力
- 「Connect to GitHub」をクリックし、認証を完了
- 作成された接続のARNをテンプレートの
GithubConnectionArn
パラメータに設定
4. Basic認証の設定
Basic認証のトークンは以下のコマンドで生成できます:
echo -n "username:password" | base64
生成されたトークンをテンプレートのBasicAuthToken
パラメータに設定します。
アクセス方法
スタックが作成されると、CloudFrontのURLが出力されます。このURLにアクセスすると、Basic認証が要求されます。設定したユーザー名とパスワードを入力することでStorybookにアクセスできます。
カスタマイズポイント
- 独自ドメインの設定: ACM証明書を作成し、CloudFrontに関連付けることで独自ドメインが使用できます
- 認証情報の管理: AWS Systems Manager Parameter Storeで認証情報を管理することも検討できます
- ビルドカスタマイズ: buildspecを修正して、Storybookのビルド前にテストを実行するなどの拡張が可能です
まとめ
今回紹介したCloudFormationテンプレートを使えば、GitHubへのプッシュから自動デプロイまでのCI/CD環境と、Basic認証付きのStorybookホスティング環境を簡単に構築できます。CloudFront Functionsを活用することで、サーバーレスなBasic認証を実現しました。
また、CloudFormationを使用することで、環境の再現性や構成管理が容易になります。開発チームのStorybookをスムーズに共有し、コンポーネントライブラリの品質向上に役立ててください。
以上、お疲れさまでした!