エムオーテックス株式会社が運営するテックブログです。

AWS CodePipeline V2をYAMLテンプレートを使ってCloudFormationで実装する

AWS CodePipeline V2をYAMLテンプレートを使ってCloudFormationで実装する

はじめに

こんにちは、アプリケーションチームの野地です。
今回は、業務内でAWS CodePipeline V2に触れる機会があったので、CodePipeline V2を使用する際の管理コンソールでの使い方から、開発時に悩んだ「CloudFormationで実装する際のyamlでの記述方法」も合わせて紹介したいと思います。

CodePipeline V2とは

2023年の10月ごろにリリースされたCodePipelineタイプです。
V2で作成したCodePipelineをコンソールからリリースする場合、ユーザーが宣言した環境変数を使用することで、入力パラメータの値を動的に渡すことができます。

aws.amazon.com

CodePipeline V2の変更をリリース押下時の例

今回は以下のような構成で、CodePipeline V2を使用して任意のファイルをS3へアップロードするパイプラインを作成したいと思います。

今回作成するパイプラインの概要

使い方

管理コンソールでの設定

基本的には通常通りに管理コンソールからパイプラインを作成し、設定をしていきます。

  • パイプラインの設定を選択する

    以前はこのようにV1、V2を選択しましたが、注意書きにもあるように、現在は管理コンソールからはパイプラインV1の作成ができなくなっています。

    以前のパイプラインタイプの選択
    現在のパイプラインの設定画面

    下にスクロールすると、「変数」の設定が出てきます。ここで変数を設定することで、パイプラインのリリース時に入力パラメータの値を割り当てることができます。

  • ソースステージを設定する

    ソースプロバイダー設定では、リポジトリやブランチ名を指定します。

※今回使用している環境は、パイプライン実行のアカウントとソースリポジトリのアカウントが違うため、管理コンソールからプルダウンで指定することができません。その場合は、yamlテンプレートなどを使用したCloudFormationからの作成によって指定が可能です

  • ビルドステージを追加する

    BuildフェーズでPipelineリリース時に入力したパラメータを受け取るには、「環境変数-オプショナル」の項目で次のように値を入力します。(例として、環境変数名FILENAMEを用いる)

    • 環境変数名:FILENAME、変数値:#{variables.FILENAME}

「環境変数-オプショナル」の設定をCloudFormation用にyamlで記述する際に、書き方が見つからずに苦労しましたので、テンプレート例を載せておきます。
yamlの場合は、BuildアクションのConfigurationセクションにEnvironmentVariablesとしてnamevaluetypeを記述することで反映させることができます。

CloudFormation用のyamlテンプレートでの記述方法

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  EnvName:
    Type: String
Mappings:
  EnvMap:
    develop:
      BranchName: 'develop_V2'
      EncryptionKey: 'arn:aws:kms:ap-northeast-1:xxxxxx:key/xxxxxx'
Resources:
  FileUpdatePipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      PipelineType: V2        
      Variables:
        - Name: 'FILENAME'
          DefaultValue: 'DEFAULT'
          Description: '更新したいファイル名を入力してください。例:sqs.yamlを更新したい場合→sqsと入力'     
      Name: !Sub 'file-update-${EnvName}'
      ArtifactStore:
        Type: S3
        Location: !Sub 'artifacts-${EnvName}' 
        EncryptionKey:
          Id: !FindInMap [EnvMap, !Ref EnvName, EncryptionKey]
          Type: KMS
      RoleArn: !Sub arn:aws:iam::${AWS::AccountId}:role_name
      Stages:
        - Name: Source
          Actions:
            - Name: Source
              ActionTypeId:
                Category: Source
                Owner: AWS
                Provider: CodeCommit
                Version: 1
              RunOrder: 1
              Configuration:
                PollForSourceChanges: false
                RepositoryName: 'repository_name'
                BranchName: !FindInMap [EnvMap, !Ref EnvName, BranchName]
              InputArtifacts: []
              OutputArtifacts:
                - Name: SourceArtifact
              RoleArn: arn:aws:iam::${SourceAccountID}:role_name
        - Name: Deploy
          Actions:
            - Name: Approval
              ActionTypeId:
                Category: Approval
                Owner: AWS
                Provider: Manual
                Version: 1
              RunOrder: 1
            - Name: Build
              ActionTypeId:
                Category: Build
                Owner: AWS
                Provider: CodeBuild
                Version: 1
              RunOrder: 2
              Configuration:
                ProjectName: !Ref 'FileUpdateCodeBuild'
                EnvironmentVariables: !Join
                  - ''
                  - - '['
                    - '{"name":"FILENAME",
                         "value":"#{variables.FILENAME}",
                         "type":"PLAINTEXT"
                    - '"}]'
              InputArtifacts:
                - Name: SourceArtifact
              OutputArtifacts:
                - Name: BuildArtifact
  FileUpdateCodeBuild:
    Type: AWS::CodeBuild::Project
    Properties:
      Name: !Sub 'file-update-${EnvName}'
      BadgeEnabled: false
      ServiceRole: !Sub arn:aws:iam::${AWS::AccountId}:role_name
      TimeoutInMinutes: 60
      Artifacts:
        Type: CODEPIPELINE
      EncryptionKey: !FindInMap [EnvMap, !Ref EnvName, EncryptionKey]
      Source:
        Type: CODEPIPELINE
        BuildSpec: 'buildspec.yaml'
      Environment:
        Image: 'aws/codebuild/standard:7.0'
        Type: LINUX_CONTAINER
        ComputeType: BUILD_GENERAL1_MEDIUM
        PrivilegedMode: true
        EnvironmentVariables:
          - Name: ENV_NAME
            Value: !Ref 'EnvName'
            Type: PLAINTEXT

実際に使ってみる

今回の実装内容

冒頭でも説明しましたが、今回は「受け取った入力パラメータを使って、S3の任意のyamlテンプレートを変数で指定して更新する」ような、BuildフェーズをもつPipelineを作成したいと思います。

今回作成するパイプラインの概要

Buildフェーズで使用するbuildspec.yamlを次のように記述します。

  • buildspec.yamlの記述例
version: 0.2
phases:
  install:
    commands:
      - printf "Installing dependencies..."
      - pip install awscli jsonschema PyYAML
  pre_build:
    commands:
      - find $FILENAME.yaml
      - EXISTS_STATUS=$?
      - |
        if [ $EXISTS_STATUS -eq 0 ]; then
          printf "go to build phase"        
        fi
  build:
    commands:
      - |
        if [ $EXISTS_STATUS -eq 0 ]; then
          printf "Uploading $FILENAME to S3..."
          aws s3 cp $FILENAME.yaml s3:/Sample/$FILENAME.yaml
        else
          printf "Skip build phase due to file not exists"
        fi

実行するコマンド内で、$FILENAMEという記述を用いることで入力パラメータを受け取り、任意のファイルを指定できます。
(念の為、指定したファイルが存在するかどうかのチェックも行っています。)

変更のリリース

パイプラインの変更リリース時に、環境変数に入れたい値の入力を求められます。
今回は、S3に保存してある任意のyamlテンプレートを更新したいので、入力パラメータにそのファイル名を入力します。

例:sqs.yamlテンプレートを更新する
「リリースする」の押下後に、パイプラインが成功すると、パラメータ入力で指定したファイルが更新されます。
S3の更新結果例

おわりに

今回紹介したファイルのアップロード作業は、業務内で一部手動で実施していました。
ですが、今回紹介したCodePipeline V2を使用した実装をすることで、コマンド実行時のオペミス回避に寄与できました。
今回はファイルのアップロードをしましたが、そのほかにも、スタックのアップデートをしたければ、同様にコマンド内でテンプレートを指定してあげれば良いので、使い道が色々ありそうです。
実装時にyamlでの記述方法について述べている記事がなかったので、同じ課題を持つ人の助けになれば幸いです。