2
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?

More than 1 year has passed since last update.

CI/CDハンズオンの環境をCloudFormationで作成

Posted at

はじめに

CI/CDの勉強を行うにあたって、以下のハンズオンを行いました。

ハンズオンではコンソール操作で作成していましたが、CFnで作成してみようと思いました。
ハンズオンは以下の二つがあります。

  • S3 をデプロイ先とした CI/CD 環境の構築【AWS CodeCommit, AWS CodePipeline を利用】
  • EC2 インスタンスをデプロイ先とした CI/CD 環境の構築【AWS CodeCommit, AWS CodeBuild, AWS CodeDeploy, AWS CodePipeline を利用】

今回CFnで作成した対象はS3 をデプロイ先とした CI/CD 環境の構築【AWS CodeCommit, AWS CodePipeline を利用】のみです。

結果として、見えないところで色々なリソースが作られていることがわかりました。

参考

構成図

思っていたの

公式では以下の図を用いていました。(上の方のパイプラインになります)

image.png

実際

今回作成したのは以下のようになります。
image.png

さらに

ハンズオン(コンソールの操作)で作成されるパイプラインは、以下の様になるようです。
image.png

やったこと

環境

コンソールからの操作のみになります。

事前準備

初期ソースの用意

  • 任意のS3にZip圧縮したソースをアップ
    • CodeCommit作成時に、ブランチ名とS3のオブジェクトを指定しておくと、そのファイルがコミットされた状態で作成されます。

対象のソース

index.html
<!DOCTYPE html>

<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>S3 Static Web Hosting</title>
</head>
<body>
  Hello, AWS World!!
</body>
</html>

バケットの用意

任意のバケットでよいです。

Zip圧縮したHTMLファイルのアップロード

HTMLファイルをZipで固めて、先のバケットにアップロードしておきます。

パイプラインの作成(CodeCommitの変更を直接検知)

以下のCFnを実行するだけです。

クリックで表示
AWSTemplateFormatVersion: 2010-09-09

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Parameters:
          - RepositoryName
          - TargetBrranch
          - MySourceCodeBucket
          - MyKey
          - AllowedIps
          - PipelineName
Parameters:
  RepositoryName:
    Type: String
    Default: "repo20220926"
  TargetBrranch:
    Type: String
    Default: "main"
  MySourceCodeBucket:
    Type: String
  MyKey:
    Type: String
    Default: "index.zip"
  AllowedIps:
    Description: IP list to allow access.
    Type: CommaDelimitedList
    Default: "1.1.1.1/32, 2.2.2.2/32"
  PipelineName:
    Type: String
    Default: "pipeline20220926"


Resources:
  TargetRepository:
    Type: AWS::CodeCommit::Repository
    Properties:
      RepositoryName: !Ref RepositoryName
      Code:
        BranchName: !Ref TargetBrranch
        S3:
          Bucket: !Ref MySourceCodeBucket
          Key: !Ref MyKey

  TargetBucket:
    Type: AWS::S3::Bucket
    Properties:
      WebsiteConfiguration:
        IndexDocument: index.html
  TargetBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref TargetBucket
      PolicyDocument:
        Statement:
          - Effect: Allow
            Principal: '*'
            Action:
              - 's3:GetObject'
            Resource: !Sub arn:aws:s3:::${TargetBucket}/*
            Condition:
              IpAddress:
                'aws:SourceIp': !Ref AllowedIps

  # CodePipeline resoure
  ## Bucket for Artifactstore
  ArtifactstoreBucket:
    Type: AWS::S3::Bucket
    Properties:
      PublicAccessBlockConfiguration:
        BlockPublicAcls: TRUE
        BlockPublicPolicy: TRUE
        IgnorePublicAcls: TRUE
        RestrictPublicBuckets: TRUE
  ## IAM ROLE FOR Codepipeline
  PipelineRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
         -  Effect: Allow
            Principal:
              Service:
               -  codepipeline.amazonaws.com
            Action:
             -  sts:AssumeRole
      Policies:
       -  PolicyName: !Sub AWSCodePipelineServiceRole-${AWS::Region}-${PipelineName}
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
             -  Effect: Allow
                Action:
                 - s3:*Object
                 - codecommit:UploadArchive
                 - codecommit:GetCommit
                 - codecommit:GetUploadArchiveStatus
                 - codecommit:GetRepository
                 - codecommit:GetBranch
                 - codecommit:CancelUploadArchive
                Resource:
                 - !GetAtt TargetRepository.Arn
                 - !Sub arn:aws:s3:::${TargetBucket}/*
                 - !Sub arn:aws:s3:::${ArtifactstoreBucket}/*
  ## Codepipeline
  ContentsPipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties: 
      RoleArn: !GetAtt PipelineRole.Arn
      ArtifactStore:
        Type: S3
        Location: !Ref ArtifactstoreBucket
      Stages:
       -  Name: Source
          Actions:
           -  Name: Source
              ActionTypeId:
                Category: Source
                Owner: AWS
                Version: 1
                Provider: CodeCommit
              Configuration: 
                RepositoryName: !Ref RepositoryName
                BranchName: !Ref TargetBrranch
                PollForSourceChanges: true
              RunOrder: 1
              OutputArtifacts:
                -  Name: SourceArtifact
       -  Name: Deploy
          Actions:
           -  Name: Deploy
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Version: 1
                Provider: S3
              InputArtifacts:
                -  Name: SourceArtifact
              Configuration: 
                BucketName: !Ref TargetBucket
                Extract: true
              RunOrder: 1

Outputs:
    TargetURL:
        Value: !Sub http://${TargetBucket}.s3-website-${AWS::Region}.amazonaws.com

パラメータとして以下を指定します。

  • 作成するCodeCommitのリポジトリ名
    • 対象のブランチ
  • 最初にPUSHする(Zip圧縮された)コードファイルが置いてあるバケット名
    • ファイルのパス(Key)
  • 静的ホスティングサイトとして許可するIP群
  • パイプラインの名称

解説

コードパイプラインのところに癖があったので説明します

パイプライン起動のトリガー

一つ目のステージのConfiguration
              Configuration: 
                RepositoryName: !Ref RepositoryName
                BranchName: !Ref TargetBrranch
                PollForSourceChanges: true

このPollForSourceChangesは以下の様に動作する模様です。

  • true:対象のブランチにPUSHしたら、パイプラインが起動
  • false:PUSHしても、起動しない
    • 別途、EventBridge等から起動させる場合に使う模様
      • ハンズオンにて作成される(=コンソールで作成する)環境は、falseになっており、EventBridgeから起動している

詳細は公式にあります。(Default settings for the PollForSourceChanges parameterの箇所です)

S3にデプロイする際に、展開する指定

二つ目のステージのConfiguration
              Configuration: 
                BucketName: !Ref TargetBucket
                Extract: true

このExtractはfalseだと圧縮されたままアップロードされるようです。詳細は公式に。

ページ確認

CloudFormationのOutputsにURLを出力するよう設定したので、CFnの出力タブにあるURLで確認できます。
image.png

編集

CodeCommitであれば、コンソールから編集できます。
リポジトリから、index.htmlを選ぶと、編集できます。

image.png

以下の様に変更すると、パイプラインが動き、ページに反映されます。
image.png
image.png

実行履歴を見ると、ソースの変更がトリガーになっていることがわかります。
image.png

パイプラインの作成(CodeCommitの変更をEventBridgeが検知)

上記を作成した際に、ハンズオンで作成した環境と異なることに気が付きました。
そのためそれらを比較したところ、EventBridgeがトリガーとなり動いており、そちらもCFnで作成してみました。

クリックで表示
AWSTemplateFormatVersion: 2010-09-09

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Parameters:
          - RepositoryName
          - TargetBrranch
          - MySourceCodeBucket
          - MyKey
          - AllowedIps
          - PipelineName
Parameters:
  RepositoryName:
    Type: String
    Default: "repo20220928"
  TargetBrranch:
    Type: String
    Default: "main"
  MySourceCodeBucket:
    Type: String
  MyKey:
    Type: String
    Default: "index.zip"
  AllowedIps:
    Description: IP list to allow access.
    Type: CommaDelimitedList
    Default: "1.1.1.1/32, 2.2.2.2/32"
  PipelineName:
    Type: String
    Default: "hoge20220928"


Resources:
  TargetRepository:
    Type: AWS::CodeCommit::Repository
    Properties:
      RepositoryName: !Ref RepositoryName
      Code:
        BranchName: !Ref TargetBrranch
        S3:
          Bucket: !Ref MySourceCodeBucket
          Key: !Ref MyKey

  TargetBucket:
    Type: AWS::S3::Bucket
    Properties:
      WebsiteConfiguration:
        IndexDocument: index.html
  TargetBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref TargetBucket
      PolicyDocument:
        Statement:
          - Effect: Allow
            Principal: '*'
            Action:
              - 's3:GetObject'
            Resource: !Sub arn:aws:s3:::${TargetBucket}/*
            Condition:
              IpAddress:
                'aws:SourceIp': !Ref AllowedIps

  # CodePipeline resoure
  ## Bucket for Artifactstore
  ArtifactstoreBucket:
    Type: AWS::S3::Bucket
    Properties:
      PublicAccessBlockConfiguration:
        BlockPublicAcls: TRUE
        BlockPublicPolicy: TRUE
        IgnorePublicAcls: TRUE
        RestrictPublicBuckets: TRUE
  ## IAM ROLE FOR Codepipeline
  PipelineRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
         -  Effect: Allow
            Principal:
              Service:
               -  codepipeline.amazonaws.com
            Action:
             -  sts:AssumeRole
      Policies:
       -  PolicyName: !Sub AWSCodePipelineServiceRole-${AWS::Region}-${PipelineName}
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
             -  Effect: Allow
                Action:
                 - s3:*Object
                 - codecommit:UploadArchive
                 - codecommit:GetCommit
                 - codecommit:GetUploadArchiveStatus
                 - codecommit:GetRepository
                 - codecommit:GetBranch
                 - codecommit:CancelUploadArchive
                Resource:
                 - !GetAtt TargetRepository.Arn
                 - !Sub arn:aws:s3:::${TargetBucket}/*
                 - !Sub arn:aws:s3:::${ArtifactstoreBucket}/*
  ## Codepipeline
  ContentsPipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties: 
      RoleArn: !GetAtt PipelineRole.Arn
      ArtifactStore:
        Type: S3
        Location: !Ref ArtifactstoreBucket
      Stages:
       -  Name: Source
          Actions:
           -  Name: Source
              ActionTypeId:
                Category: Source
                Owner: AWS
                Version: 1
                Provider: CodeCommit
              Configuration: 
                RepositoryName: !Ref RepositoryName
                BranchName: !Ref TargetBrranch
                PollForSourceChanges: false
              RunOrder: 1
              OutputArtifacts:
                -  Name: SourceArtifact
       -  Name: Deploy
          Actions:
           -  Name: Deploy
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Version: 1
                Provider: S3
              InputArtifacts:
                -  Name: SourceArtifact
              Configuration: 
                BucketName: !Ref TargetBucket
                Extract: true
              RunOrder: 1

  ## IAM ROLE FOR EventBridge
  EventBridgeRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
         -  Effect: Allow
            Principal:
              Service:
               -  events.amazonaws.com
            Action:
             -  sts:AssumeRole
      Policies:
       -  PolicyName: !Sub AWSCodePipelineServiceRole-${AWS::Region}-${PipelineName}
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
             -  Effect: Allow
                Action:
                 - codepipeline:StartPipelineExecution
                Resource:
                 - !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${ContentsPipeline}

  EventRuleCodeCommit:
    Type: AWS::Events::Rule
    Properties:
      EventBusName: default
      State: ENABLED
      EventPattern:
        source:
         -  aws.codecommit
        detail-type: 
         -  "CodeCommit Repository State Change"
        resources: 
         - !GetAtt TargetRepository.Arn
        detail:
          event:
            - "referenceCreated"
            - "referenceUpdated"
          referenceType:
            - "branch"
          referenceName:
            - !Ref TargetBrranch
      Targets: 
       -  Arn: !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${ContentsPipeline}
          Id: !Ref AWS::StackName
          RoleArn: !GetAtt EventBridgeRole.Arn

Outputs:
    TargetURL:
        Value: !Sub http://${TargetBucket}.s3-website-${AWS::Region}.amazonaws.com

パラメータは、先のものと一緒です。

違いは、パイプライン起動のトリガーが、CodePipeline内では無効化されている点です。

一つ目のステージのConfiguration
              Configuration: 
                RepositoryName: !Ref RepositoryName
                BranchName: !Ref TargetBrranch
-               PollForSourceChanges: true
+               PollForSourceChanges: false

EventBridgeについては、以下の公式等を参考にしました。

先程と同様に、コードを変更させると反映されます。
履歴にはCloudWatchEvent(EventBridgeの昔の名前?)で動いたことがわかります。
image.png

片づけ

両方ともCFnで作成したので、削除するのは簡単です。

  1. バケットを空にする。
    • 公開用バケット
    • Artifact用バケット
  2. 対象のスタックを削除する。

おわりに

最も簡単(と思われる)なCI/CDパイプライン環境をCFnから作成してみました。
CI/CDの超基礎を勉強しつつ、同時にコンソールがどれだけ良しなに構築しているのか、大変参考になりました。

実業務で使うには簡単すぎる構成なので、もう少し複雑な環境も勉強していけたらと考えています。

2
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
2
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?