AWS CloudFormation を使って CI/CD パイプラインを作成する

AWS

はじめに

本記事では CodeCommit、CodeBuild、CodePipeline を使用した CI/CD パイプラインの構築を行います。
なお、今回テンプレートの作成及びデプロイには rain を使用しました。 rain はCloudFormation の CLI ツールで、非常に使い勝手が良いため興味のある方は調べてみてください。

対象者

この記事は下記のような人を対象にしています。

  • CodeCommit、CodeBuild、CodePipeline を使用した CI/CD パイプラインを構築したい人
  • ネストされたスタックを利用してリソースを構築したい人

手順

S3 の作成

CodeBuild、CodePipeline のアーティファクトを保存するための S3 が必要になるので、事前に作成します。
S3 作成には以下のテンプレートを使用します。

AWSTemplateFormatVersion: "2010-09-09"

Description: Template generated by rain

Resources:
  CodePipelineBucket:
    Type: AWS::S3::Bucket
    Properties: 
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      BucketName: !Sub codepipeline-${AWS::Region}-${AWS::AccountId}
      PublicAccessBlockConfiguration: 
        BlockPublicAcls: True
        BlockPublicPolicy: True
        IgnorePublicAcls: True
        RestrictPublicBuckets: True

  CodeBuildOutputBucket:
    Type: AWS::S3::Bucket
    Properties: 
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      BucketName: !Sub codebuild-output-${AWS::Region}-${AWS::AccountId}
      PublicAccessBlockConfiguration: 
        BlockPublicAcls: True
        BlockPublicPolicy: True
        IgnorePublicAcls: True
        RestrictPublicBuckets: True

Outputs:
  CodePipelineBucketName:
    Value: !Ref CodePipelineBucket
    Export:
      Name: CodePipelineBucketName
  CodeBuildOutputBucketName:
    Value: !Ref CodeBuildOutputBucket
    Export:
      Name: CodeBuildOutputBucketName

CloudFormation テンプレート作成

ネストされたスタックを利用して、各リソースを作成します。ネストされたスタックの詳細については公式ドキュメントを参照ください。また CloudFormation テンプレートの各パラメータについては公式ドキュメントを参照ください。

下記のスタック構成で作成します。

各 CloudFormation テンプレートには以下のものを使用しました。
子スタック用のテンプレートファイルは S3 に保存し、URL を Parameters のデフォルト値に設定しておくと便利ですね。

AWSTemplateFormatVersion: "2010-09-09"
Description: Provision CodeCommit, CodeBuild, CodePipeline

Parameters: 
  TemplateCodeCommit:
    Description: CodeCommit template URL
    Type: String
    Default: {Your CodeCommit template URL}

  TemplateCodeBuild:
    Description: CodeBuild template URL
    Type: String
    Default: {Your CodeBuild template URL}

  TemplateCodePipeline:
    Description: CodePipeline template URL 
    Type: String
    Default: {Your CodePipeline template URL}

  CommonName:
    Description: Name commonly used by resources
    Type: String


Resources:
  # CodeCommitの作成
  CodeCommit:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: !Ref TemplateCodeCommit
      Parameters:
        CommonNameParameter: !Sub ${CommonName}

  # CodeBuildの作成
  CodeBuild:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: !Ref TemplateCodeBuild
      Parameters: 
        CommonNameParameter: !Sub ${CommonName}
    DependsOn: CodeCommit

  # CodePipelineの作成
  CodePipeline:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: !Ref TemplateCodePipeline
      Parameters: 
        CommonNameParameter: !Sub ${CommonName}
    DependsOn: CodeBuild
AWSTemplateFormatVersion: "2010-09-09"

Description: Template generated by rain

Parameters:
  CommonNameParameter:
    Type: String

Resources:
  MyRepository:
    Type: AWS::CodeCommit::Repository
    Properties:
      RepositoryDescription: repository for my codes
      RepositoryName: !Ref CommonNameParameter
Outputs:
  MyRepositoryArn:
    Value: !GetAtt MyRepository.Arn

  MyRepositoryCloneUrlHttp:
    Value: !GetAtt MyRepository.CloneUrlHttp
    Export:
      Name: !Sub ${CommonNameParameter}RepositoryCloneUrlHttp

  MyRepositoryName:
    Value: !GetAtt MyRepository.Name
    Export:
      Name: !Sub ${CommonNameParameter}RepositoryName
AWSTemplateFormatVersion: "2010-09-09"

Description: Template generated by rain

Parameters:
  CommonNameParameter:
    Type: String

Resources:
  MyPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      Path: /
      ManagedPolicyName: !Sub ${CommonNameParameter}_codebuild_policy
      PolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          Effect: Allow
          Action: 
            - "logs:CreateLogGroup"
            - "logs:CreateLogStream"
            - "logs:PutLogEvents"
            - "s3:PutObject"
            - "s3:GetObject"
            - "s3:GetObjectVersion"
            - "s3:GetBucketAcl"
            - "s3:GetBucketLocation"
            - "codecommit:GitPull"
            - "codebuild:CreateReportGroup"
            - "codebuild:CreateReport"
            - "codebuild:UpdateReport"
            - "codebuild:BatchPutTestCases"
            - "codebuild:BatchPutCodeCoverages"
          Resource: "*"
  MyRole:
    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/AWSLambda_FullAccess
        - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser
        - !Ref MyPolicy
      RoleName: !Sub ${CommonNameParameter}_codebuild_role

  MyProject:
    Type: AWS::CodeBuild::Project
    Properties:
      Artifacts:
        EncryptionDisabled: false 
        Location: !ImportValue CodeBuildOutputBucketName
        Name: !Sub CommonNameParameter
        NamespaceType: NONE
        OverrideArtifactName: false
        Packaging: NONE
        Path: ""
        Type: S3
      BadgeEnabled: false 
      Cache: 
        Type: NO_CACHE
      ConcurrentBuildLimit: 1 
      EncryptionKey: !Sub arn:aws:kms:${AWS::Region}:${AWS::AccountId}:alias/aws/s3
      Environment:
        ComputeType: BUILD_GENERAL1_SMALL
        Image: aws/codebuild/amazonlinux2-x86_64-standard:3.0-21.04.23
        ImagePullCredentialsType: CODEBUILD 
        PrivilegedMode: false 
        Type: LINUX_CONTAINER
      LogsConfig: 
        CloudWatchLogs: 
          Status: ENABLED
        S3Logs: 
          EncryptionDisabled: false 
          Status: DISABLED
      Name: !Sub ${CommonNameParameter}
      QueuedTimeoutInMinutes: 480
      ServiceRole: !GetAtt MyRole.Arn
      Source:
        GitCloneDepth: 1 
        GitSubmodulesConfig: 
          FetchSubmodules: false
        InsecureSsl: false 
        Location: 
          !ImportValue 
            Fn::Sub: ${CommonNameParameter}RepositoryCloneUrlHttp
        Type: CODECOMMIT
      TimeoutInMinutes: 60

Outputs:
  MyProjectArn:
    Value: !GetAtt MyProject.Arn
  MyProjectName:
    Value: !Ref MyProject
    Export:
      Name: !Sub ${CommonNameParameter}ProjectName

CodeBuild 用ロールに付与するポリシーの権限は用途に応じて追加・削除してください。

AWSTemplateFormatVersion: "2010-09-09"

Description: Template generated by rain

Parameters:
  CommonNameParameter:
    Type: String

Resources:
  MyPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      Path: /
      ManagedPolicyName: CodePipelineBasePolicy
      PolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          Effect: Allow
          Action: 
            - "iam:PassRole"
            - "codecommit:CancelUploadArchive"
            - "codecommit:GetBranch"
            - "codecommit:GetCommit"
            - "codecommit:GetRepository"
            - "codecommit:GetUploadArchiveStatus"
            - "codecommit:UploadArchive"
            - "codedeploy:CreateDeployment"
            - "codedeploy:GetApplication"
            - "codedeploy:GetApplicationRevision"
            - "codedeploy:GetDeployment"
            - "codedeploy:GetDeploymentConfig"
            - "codedeploy:RegisterApplicationRevision"
            - "codestar-connections:UseConnection"
            - "cloudwatch:*"
            - "s3:*"
            - "cloudformation:*"
            - "ecs:*"
            - "lambda:InvokeFunction"
            - "lambda:ListFunctions"
            - "codebuild:BatchGetBuilds"
            - "codebuild:StartBuild"
            - "codebuild:BatchGetBuildBatches"
            - "codebuild:StartBuildBatch"
            - "ecr:DescribeImages"
          Resource: "*"
  MyRole:
    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/AWSLambda_FullAccess
        - !Ref MyPolicy
      RoleName: !Sub ${CommonNameParameter}_codepipeline_role

  MyPipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      ArtifactStore: 
        Location: !ImportValue CodeBuildOutputBucketName
        Type: S3
      Name: !Sub ${CommonNameParameter}
      RestartExecutionOnUpdate: false 
      RoleArn: !GetAtt MyRole.Arn
      Stages:
        - Name: Source
          Actions:
            - Name: source
              ActionTypeId:
                Category: Source
                Owner: AWS
                Provider: CodeCommit
                Version: 1
              Configuration: 
                RepositoryName: 
                  !ImportValue 
                    Fn::Sub: ${CommonNameParameter}RepositoryName
                BranchName: master
                PollForSourceChanges: true
                OutputArtifactFormat: CODE_ZIP # Dockerコマンドを使用する場合は"CODEBUILD_CLONE_REF"
              OutputArtifacts:
                - Name: SourceArtifact
        - Name: Build
          Actions:
            - Name: build
              ActionTypeId:
                Category: Build
                Owner: AWS
                Provider: CodeBuild
                Version: 1
              Configuration:
                ProjectName: 
                  !ImportValue
                    Fn::Sub: ${CommonNameParameter}ProjectName
              RunOrder: 1
              InputArtifacts:
                - Name: SourceArtifact
              OutputArtifacts:
                - Name: BuildArtifact

Outputs:
  MyPipelineVersion:
    Value: !GetAtt MyPipeline.Version

CodePipeline 用ロールに付与するポリシーの権限は用途に応じて追加・削除してください。

スタックの作成

code.yml からスタックを作成します。スタックの作成が成功すると、CodeCommit、CodeBuild、CodePipeline が作成できました。

おわりに

本記事では CodeCommit、CodeBuild、CodePipeline を使用した CI/CD パイプラインを、ネストされたスタックを用いて構築を行いました。この記事がどなたかの参考になれば幸いです。

参考

コメント