0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS CloudFormationでStorybookのCI/CD環境構築

Last updated at Posted at 2025-03-06

概要

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接続を設定する必要があります:

  1. AWS Management Consoleで「Developer Tools」→「Settings」→「Connections」を開く
  2. 「Create connection」をクリック
  3. プロバイダーとして「GitHub」を選択し、接続名を入力
  4. 「Connect to GitHub」をクリックし、認証を完了
  5. 作成された接続の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をスムーズに共有し、コンポーネントライブラリの品質向上に役立ててください。

以上、お疲れさまでした!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?