🏷️

CodePipeline(v2)でtagトリガーを使う

2024/05/31に公開
3

はじめに

AWS CodePipeline v2では、コードのプッシュに加えてさまざまなGitイベントをトリガーにパイプラインを開始できるようになりました。

ここでは、タグの作成をトリガーにパイプラインを開始する例をご紹介します。
マネジメントコンソールを使ってパイプラインを作成する方法は、公式ドキュメントにチュートリアルがありますので、今回はTerraformを使ってパイプラインを作成する方法について紹介します。

https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/tutorials-github-tags.html

事前準備

パイプラインを作成する前に、AWSアカウントをGitリポジトリに接続する必要があります。
また、パイプラインには最低2つのステージが必要ですので、ビルドやテストするためのステージも必要です。

Terraformの導入

あらかじめTerraformのインストールが必要です。

https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli

CodeConnectionsの作成

CodeConnectionsを使用してGitリポジトリに接続します。
(以前は CodeStar Connections と呼ばれていました)

https://dev.classmethod.jp/articles/aws-codestar-connections-has-renamed-to-aws-codeconnections/

GitリポジトリはGitHubを使っているものとします。
他のソースコードリポジトリでもほぼ同様だと思いますが、CodeCommitではtagトリガーが使えないようです。(2024/5現在)

以下を参考に、あらかじめマネジメントコンソールで、GitHubへの接続を作成しておいてください。

はじめてCodeConnectionsの接続を作成する際には、GitHubアカウントに「AWS Connector for GitHub 」アプリケーションをインストールする必要があります。

https://dev.classmethod.jp/articles/aws-codepipeline-github-connection-v2/

新しい接続を作成したら、ARNをメモしておきます。

後続ステージで実行するAction

CodePipelineには少なくとも2つ以上のステージが必要です。
ソースステージで取得したソースコードに対して、後続のステージで実行するビルドプロジェクトを用意します。

以下にプロジェクトの雛型を示します。適宜変更してご利用ください。

CodeBuildプロジェクトの雛型(Terraform)
resource "aws_codebuild_project" "sampleProject" {
    name          = "sampleProject"
    description   = "Codebuild Sample Project"
    build_timeout = "5"
    service_role  = aws_iam_role.CodeBuildIAMRole.arn

    artifacts {
        type = "CODEPIPELINE"
    }

    environment {
        compute_type                = "BUILD_GENERAL1_SMALL"
        image                       = "aws/codebuild/standard:4.0"
        type                        = "LINUX_CONTAINER"
        privileged_mode             = false
        image_pull_credentials_type = "CODEBUILD"
    }

    source {
        type = "CODEPIPELINE"
        buildspec = <<BUILDSPEC
version: 0.2

phases:
    build:
        commands:
            - echo "test"
BUILDSPEC
    }

}

resource "aws_iam_role" "CodeBuildIAMRole" {
    name = "sampleProject_CodeBuildRole"

    assume_role_policy = jsonencode({
        Version = "2012-10-17",
        Statement = [
            {
                Action = "sts:AssumeRole",
                Effect = "Allow",
                Principal = {
                    Service = "codebuild.amazonaws.com"
                }
            },
        ]
    })
}

resource "aws_iam_role_policy_attachment" "CodeBuildIAMRoleAttach" {
    role       = aws_iam_role.CodeBuildIAMRole.name
    policy_arn = "arn:aws:iam::aws:policy/PowerUserAccess"
}

パイプラインの作成

ここからは実際にパイプラインを作成していきます。

プロバイダーの設定

まずプロバイダーの設定を記述します。
Codepipeline V2の機能を利用するには、Versionを5以上に設定する必要があります。

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

triggerの設定

まずpipeline_typeに「V2」を指定し、起動トリガー(trigger)を設定します。ここでは、新しいタグが作成された場合にパイプラインが起動されるように記述します。

resource "aws_codepipeline" "CodePipeline" {
    name = "samplePipeline"
    
    pipeline_type = "V2"

    trigger {
        provider_type = "CodeStarSourceConnection"
        git_configuration {
        source_action_name = "Source"
        push {
            tags {
                includes = ["*"]
            }
        }
    }
    # (中略)
}

ステージの作成

次に、ソースステージを作成します。
action.configuration.ConnectionArnプロパティに、さきほど作成した接続のARNを記述します。

また、後続のステージにテストやビルドを行うCodeBuildプロジェクトなどを記述します。

resource "aws_codepipeline" "CodePipeline" {
    # (中略)
    
    stage {
        # ソースステージ
        name = "Source"
        action {
            name = "Source"
            category = "Source"
            owner = "AWS"
            configuration = {
                BranchName = "main"
                # CodeConnectionのARN
                ConnectionArn = "arn:aws:codeconnections:<region>:<account>:connection/xxxxxx..."
                DetectChanges = "false"
                FullRepositoryId = "user_name/repository_name"
                OutputArtifactFormat = "CODE_ZIP"
            }
            provider = "CodeStarSourceConnection"
            version = "1"
            output_artifacts = [
                "SourceArtifact"
            ]
            run_order = 1
        }
    }
    stage {
        # ビルドステージ
        name = "Build"
        action {
          name = "Build"
          category = "Build"
          owner = "AWS"
          configuration = {
              # CodeBuildプロジェクト名
              ProjectName = "CodeBuildProject"
          }
          input_artifacts = [
              "SourceArtifact"
          ]
          provider = "CodeBuild"
          version = "1"
          run_order = 1
        }               
    }
    # (中略)
}

バケット・ロールの作成

最後に、アーティファクトを保存するためのバケットやロールを作成します。

以下ではロールにAWS マネージドポリシーのPowerUserAccessをアタッチしていますが、「最小権限の原則」に則り見直すことを推奨します。

resource "aws_codepipeline" "CodePipeline" {
    # (中略)
    # アーティファクト保存先
    artifact_store {
        location = "${aws_s3_bucket.CodePipelineArtifactBacket.id}"
        type = "S3"
    }
    # 付与するロール
    role_arn = "${aws_iam_role.CodePipelineIAMRole.arn}"
}

# ロール
resource "aws_iam_role" "CodePipelineIAMRole" {
    name = "samplePipelineRole"

    assume_role_policy = jsonencode({
        Version = "2012-10-17",
        Statement = [
            {
                Action    = "sts:AssumeRole",
                Effect    = "Allow",
                Principal = {
                    Service = "codepipeline.amazonaws.com"
                }
            }
        ]
    })
}

# ロールにポリシーをアタッチする
resource "aws_iam_role_policy_attachment" "CodePipelinePolicyAttach" {
    role       = aws_iam_role.CodePipelineIAMRole.name
    policy_arn = "arn:aws:iam::aws:policy/PowerUserAccess"
}

# アーティファクト保存先のバケット
resource "aws_s3_bucket" "CodePipelineArtifactBacket" {
    bucket     = "pipeline-artifact-xxxxxx"
}

パイプライン作成

準備ができたらterraformでパイプラインを作成します。

% terraform init
% terraform plan
% terraform apply

これでパイプラインが作成できました。
対象のブランチに任意のタグを作成すると、パイプラインが実行されるはずです。

もし実行されない場合、CodeConnection接続を作成した際にGitHubにログインしたユーザに、対象のリポジトリに対するアクセス権があるかを再度確認してください。

作成したタグ名の取得

ビルドプロジェクトなどでタグ名を利用したい場合はどうすればいいでしょうか。

マネジメントコンソールからソースアクションの「出力」を参照すると、トリガーとなったタグの名前が出力されています。

アクションの出力を後続のステージで利用するには、ソースアクションに名前空間(namespace)を設定する必要があります。

また、後続のステージのアクションには、#{SourceVariables.TagName} のように名前空間と変数名を指定して値を取得し、環境変数にセットします。

resource "aws_codepipeline" "codepipeline" {
    name = "samplePipeline"
    # (中略)

    stage {
        name = "Source"
        action {
          name = "Source"
          category = "Source"
          # (中略)

          namespace = "SourceVariables"  # 名前空間を指定
        }
    }
    stage {
        name = "Build"
        action {
          name = "Build"
          category = "Build"
          owner = "AWS"
          configuration = {
              ProjectName = "CodeBuildProject"
              # 値を環境変数にセットする
              EnvironmentVariables = jsonencode([
                  {
                      name  = "TAGNAME"
                      type  = "PLAINTEXT"  
                      value = "#{SourceVariables.TagName}"
                  },
              ])
          }
          # (中略)
        }       
    }
}

これで後続のステージ内で、タグ名を利用することができます。

【課題】手動で実行した場合にエラーになる

これで完璧...といいたいところですが、ひとつ課題があります。

このパイプラインを「変更をリリース」ボタンから手動で実行した場合などは、トリガーとなるタグが存在しないため、ソースアクションの出力には「TagName」がありません。

そのため、後続のビルドアクションでこの値を参照した際に、値が評価できずアクション実行エラーになってしまいます。

以下は失敗した際のメッセージです。

An action in this pipeline failed because one or more variables could not be resolved: Action name=Build. This can result when a variable is referenced that does not exist. Validate the configuration for this action.

今のところこれを回避する方法は用意されていないようです。

もし回避方法をご存じであれば、Discussionにコメントいただければ幸いです。
(あまりスマートではないですが、思いついた解決方法をDiscussionに記載しましたので、よければご覧ください。)

まとめ

CodePipeline v2の新機能を利用し、タグの作成をトリガーにビルドパイプラインを起動する方法についてご紹介しました。

ここでは紹介していませんが、CodePipeline v2では、指定したファイルパスやプルリクエストのイベントをトリガーにパイプラインを起動することも可能です。

https://docs.aws.amazon.com/codepipeline/latest/userguide/pipelines-filter.html

これらの機能により、さまざまなGit のブランチ戦略やリポジトリ構成に柔軟に対応できるようになりました。

興味があればぜひ試してみてください。

Discussion

koty(19)koty(19)

大変参考になります。

【課題】手動で実行した場合にエラーになる
もし回避方法をご存じであれば、Discussionにコメントいただければ幸いです。

ご存知なくてコメントして恐縮ですが、私も同じことを悩んでいます。codepipelineがコケたときに手動で再実行したいですよね。今のところ、tagを消してから同じタグをpushし直すしかないかなと思っています。

festiva1300festiva1300

コメントありがとうございます。

色々試してみたのですが、Sourceステージの設定をOutputArtifactFormat = "CODEBUILD_CLONE_REF" にしておいて、CodeBuild内でタグの一覧を取得し、コミットID(CODEBUILD_RESOLVED_SOURCE_VERSION)と突合するなどすれば、手動での再実行でもうまくいきそうです。

        TAGS=$(git tag)
        for current in $TAGS; do
          TAG_COMMIT_ID=$(git show $current| grep '^commit' | awk '{print $2}')
          if [ $TAG_COMMIT_ID = $CODEBUILD_RESOLVED_SOURCE_VERSION ];then 
            TAGNAME=$current
          fi
        done
        echo $TAGNAME
koty(19)koty(19)

なるほど、、、力技。試してみます。ありがとうございます。