LoginSignup
5
6

More than 1 year has passed since last update.

[Terraform] CodeCommitで管理するモノレポの変更に応じてCodePipelineの実行を振り分ける

Posted at

はじめに

本記事では、CodeCommitで管理する単一リポジトリ(モノレポ)において、
変更されたディレクトリに応じて、実行するCodePipelineを振り分けるCIパイプラインをTerraformで構築する方法について記載しています。

Terraform で構築する全体構成図

codepipeline_Trgger_01.png

構成の概要

  1. Systems Managerの Parameter Storeには、リポジトリ内のディレクトリ名実行する CodePipeline名がJSON形式で格納されています。
  2. 開発者がCodeCommit のリポジトリにコードをプッシュすると EventBridgeがリポジトリの変更イベントを検知してLambda関数をトリガー実行します。
  3. Lambda関数は、Systems Managerのパラメータを読み取って、変更があったディレクトリに応じたCodePipeline を実行します。
  4. 変更があったディレクトリが複数の場合は、複数のCodePipelineが同時に実行されます。

Terraform のコードと構成

ディレクトリ構成

aws-tf-monorepo-codepipeline-trigger
├── main.tf
├── provider.tf
└── modules
    ├── codecommit
    │   ├── codecommit.tf
    │   └── variables.tf
    ├── codepipeline
    │   ├── buildspec.yml
    │   ├── codebuild.tf
    │   ├── codepipeline.tf
    │   ├── iam.tf
    │   ├── s3.tf
    │   └── variables.tf
    ├── eventbridge
    │   ├── event_rule.tf
    │   └── variables.tf
    ├── lambda
    │   ├── dist
    │   ├── iam.tf
    │   ├── lambda.tf
    │   ├── src
    │   │   └── codepipeline_trigger.py
    │   └── variables.tf
    └── ssm
        ├── ssm.tf
        └── variables.tf

main.tf

main.tf
locals {
  aws_region = "ap-northeast-1"

  repository_name = "monorepo"
  branch_name     = "main"

  ssm_parameter_name = "codepipeline-trigger"
  ssm_parameter_value = jsonencode({
    "project1" : "project1-codepipeline",
    "project2" : "project2-codepipeline"
  })

  event_rule_name = "monorepo-change-event"

  function_name = "codepipeline-trigger"

  project1_codepipeline_name = "project1-codepipeline"
  project2_codepipeline_name = "project2-codepipeline"
}

module "ssm" {
  source = "./modules/ssm"

  ssm_parameter_name  = local.ssm_parameter_name
  ssm_parameter_value = local.ssm_parameter_value
}

module "codecommit" {
  source = "./modules/codecommit"

  repository_name = local.repository_name
}

module "eventbridge" {
  source = "./modules/eventbridge"

  event_rule_name = local.event_rule_name
  function_name   = local.function_name
  repository_name = local.repository_name
  branch_name     = local.branch_name
}

module "lambda" {
  source = "./modules/lambda"

  function_name      = local.function_name
  ssm_parameter_name = local.ssm_parameter_name
}

module "project1_codepipeline" {
  source = "./modules/codepipeline"

  codepipeline_name = local.project1_codepipeline_name
  repository_name   = local.repository_name
  branch_name       = local.branch_name
}

module "project2_codepipeline" {
  source = "./modules/codepipeline"

  codepipeline_name = local.project2_codepipeline_name
  repository_name   = local.repository_name
  branch_name       = local.branch_name
}

CodePipeline を実行するLambda関数

codepipeline_trigger.py
import json
import boto3
import os

codepipeline = boto3.client('codepipeline')
ssm = boto3.client('ssm')

PARAMETER_NAME = os.environ['PARAMETER_NAME']
  
def lambda_handler(event, context):

    project_pipelines = load_project_pipelines_from_parameter_store(PARAMETER_NAME)

    commit_id = event['detail']['commitId']
    repository_name = event['detail']['repositoryName']
    changed_files = get_changed_files(repository_name, commit_id)

    for project_dir, pipeline_name in project_pipelines.items():
        if any(file.startswith(project_dir) for file in changed_files):
            start_pipeline(pipeline_name)

def load_project_pipelines_from_parameter_store(parameter_name):
    response = ssm.get_parameter(Name=parameter_name, WithDecryption=True)
    parameter_value = response['Parameter']['Value']
    project_pipelines = json.loads(parameter_value)
    return project_pipelines

def get_changed_files(repository_name, commit_id):
    codecommit = boto3.client('codecommit')
    response = codecommit.get_commit(
        repositoryName=repository_name,
        commitId=commit_id
    )

    commit = response['commit']
    parent_commit_id = commit['parents'][0]

    response = codecommit.get_differences(
        repositoryName=repository_name,
        afterCommitSpecifier=commit_id,
        beforeCommitSpecifier=parent_commit_id
    )

    changed_files = [difference['afterBlob']['path'] for difference in response['differences']]
    return changed_files

def start_pipeline(pipeline_name):
    response = codepipeline.start_pipeline_execution(name=pipeline_name)
    print(f"Started pipeline: {pipeline_name}, executionId: {response['pipelineExecutionId']}")

本構成で想定しているCodedCommit リポジトリ

monorepo
├── project1
│   └── code
└── project2
    └── code

使い方

Terraform の動作確認環境

$ terraform -version

Terraform v1.4.4
on darwin_amd64
+ provider registry.terraform.io/hashicorp/aws v4.62.0

変数の設定

main.tf 内の Local Values を設定します。

変数名 説明
aws_region リージョン
repository_name リポジトリ名
branch_name ブランチ名
ssm_parameter_name SSMのParameter Storeのパラメータ名
ssm_parameter_value リポジトリ内のディレクトリ名(Key)と実行する CodePipeline名(Value)をJSON形式で設定します
event_rule_name EventBridgeのイベントルール名
function_name Lambda関数名
project1_codepipeline_name CodePipeline名
project2_codepipeline_name CodePipeline名

Terraform の実行

$ export AWS_PROFILE= [ YOUR AWS ACCOUNT PROFILE NAME ]
$ terraform init

$ terraform plan
Plan: 39 to add, 0 to change, 0 to destroy.

$ terraform apply
Apply complete! Resources: 39 added, 0 changed, 0 destroyed.

動作確認

  1. リポジトリ内のproject1project2のディレクトリを変更します。
    01-codepipeline-trigger-exec.png

  2. project1project2のCodePipelineが実行されることを確認できました。
    02-codepipeline-trigger-exec.png

  3. project1のディレクトリのみ変更します。
    03-codepipeline-trigger-exec.png

  4. project1のCodePipelineが実行されることを確認できました。
    04-codepipeline-trigger-exec.png

  5. project2のディレクトリのみ変更します。
    05-codepipeline-trigger-exec.png

  6. project2のCodePipelineが実行されることを確認できました。
    06-codepipeline-trigger-exec.png

以上で、変更されたディレクトリに応じて、実行するCodePipelineが振り分けられていることを確認できました。

さいごに

こちらは、あくまでサンプルコードになります。参考になれば幸いです。

5
6
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
5
6