CodeBuild + Step FunctionsでCI/CDパイプラインを構築！

# はじめに
こんにちは！ **yu-Matsu**です。

　以前、セキュリティの観点から**GitHubやSaaSのCI/CDを利用できない**ため、**AWSのサービスのみ**でコードの管理とCI/CDを実現しなければならないという場面がありました。そこで色々と苦戦しつつも **CodeBuild + Step Functions**　でのCI/CDパイプラインを構築しましたので、その経験談を記事にしたいと思います。同じような状況になっている方の一助になれば幸いです。

# そもそもなんでCodeBuild + Step Functions？
　AWSにはCI/CDパイプラインを実現するサービスとして**CodePipeline**というサービスがあります。巷では**Code4兄弟**とばれるサービス群のうちの一つで、「Pipeline」の名の如く、 CodeBuildを含む弟たち（？）をまとめてCI/CDパイプラインを構築するためのお兄ちゃん的なサービスです。
 
 https://aws.amazon.com/jp/codepipeline/

　今回はなんでこの便利なCodePipelineを利用出来なかったのでしょうか。私が携わる開発の現場では、基本的にブランチ戦略として以下のような**Gitflow**が採用されています。

<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/82174a8d-d406-571e-4b9b-cdf47eb8f607.png' width='450'>

(画像の引用：https://blog.kinto-technologies.com/posts/2023-03-07-From-Git-flow-to-GitHub-flow-ja/)

featureブランチ名やreleaseブランチ名は、**feature/{開発中の機能名}** などのように、developブランチから切った際に**動的に**決まることになりますが、ここがミソです。CodePipelineの設定を進めると、「ソースステージを追加する」というステップがあり、ここでソースプロバイダーとブランチを指定することになるのですが、**feature/*** のように**ワイルドカードを使用することができません。**

<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/197869bb-7114-3504-f1bd-ab6fc0aa392a.png' width='450'>

これでは、featureブランチのpush時にテストやセキュリティチェックを走らせることが出来ませんし、releaseブランチへのマージ時にデプロイを実施することが出来ません。pipelineのV2タイプであれば**トリガーフィルター**が利用できるので解決できそうですが、現状**ソースプロバイダがCodeCommitの場合はトリガーフィルターを利用できない**ようです...

https://qiita.com/neruneruo/items/246e577bec3a6d74e246

ですので、今回はCodePiplineをうまく利用することが出来ないので、**CodeBuild + Step Functions**での構築を試してみることになりました。

# 出来上がったものがこちらになります
　色々と苦戦しながら構築したCI/CDパイプラインが以下になります。記事冒頭でも触れた通り、**CodeBuild + Step Functions**の組み合わせで構築しています！　今回は例としてTerraformを実行するパイプラインを載せています。
- 左側のフロー(**Terraform Plan PR**)： プルリクエストが作成される、もしくはプルリクエストに変更があった際に terraform plan を実行する。
- 真ん中のフロー(**Terraform Plan feature**)： ローカルからリモートへfeatureブランチのpushがあった際に terraform plan を実行する。
- 右側のフロー(**Terraform Plan/Apply**)：　developブランチ、mainブランチに向けてのプルリクエストがマージされたら、terraform plan/apply を実行し、リソースをデプロイする。　
　![スクリーンショット 2024-07-06 22.47.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/ad9c3b4e-54cb-b063-94b7-c9df4c8a7536.png)

また、全体のアーキテクチャは以下のようになっています。

![cicd.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/b9ce20ad-3976-bf17-7073-55783c6464d7.png)


## featureブランチの取り扱い
　まず前述のfeatureブランチ（ワイルドカード）の問題をどう解決したかについてですが、こちらは先駆者の方の以下の記事を参考にさせていただいています。

https://dev.classmethod.jp/articles/codebuild-trigger-branch-wildcard/

　簡単に解説します。普通にCodeBuildのプロジェクトを作成する場合、CodePipelineと同じようにソースプロバイダとリファレンスタイプを選択することになりますが、ここでリファレンスタイプを「ブランチ」で指定しても、既存のブランチが選択肢として出てくるのみで、「**feature/***」のようにワイルドカードで指定することが出来ません。

<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/8433c8ce-ea5c-ddcf-2b81-e0bd469acc13.png' width='450'>

そこで、プロジェクト作成の際にはソースプロバイダを指定せず、**Step Functionsから実行するタイミングでパラメータのオーバーライドを利用してソースプロバイダ、ブランチを指定する**ようにします。こうすることで、feature/*のような曖昧なブランチにも対応出来るようになります。

<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/0597bfcb-8acc-75ce-3890-e7f4cc4fe764.png' width='450'>

ソースプロバイダを指定しない場合は、buildspecコマンドをインラインで指定する必要があるため、適当なものを入れておきます。**buildspecコマンドもStep Functionsから実行する際にオーバーライドします。**

<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/4dc26b45-065e-b27a-c7d8-ae27807c143c.png' width='450'>


## パイプラインの解説
　まずStep Functionsの起動に関してですが、CodeCommit上のイベントを検知して起動するように、**Event Bridge**を設定します。今回はブランチのpushやマージと、プルリクエストの作成/変更をトリガーとしたいため、２つのイベントルールを作成しています。それぞれのイベントパターンは以下のようになっています。これらのイベントルールのターゲットは言わずもがな、Step Functionsになります。

```JSON:ブランチのpushやマージを検知するイベントパターン
{
  "source": ["aws.codecommit"],
  "detail-type": ["CodeCommit Repository State Change"],
  "resources": ["arn:aws:codecommit:us-east-1:xxxxxxxx:cicd-test"],
  "detail": {
    "event": ["referenceCreated", "referenceUpdated"],
    "referenceType": ["branch"]
  }
}
```

```JSON:プルリクエストの作成/変更を検知するイベントパターン
{
  "source": ["aws.codecommit"],
  "detail-type": ["CodeCommit Pull Request State Change"],
  "resources": ["arn:aws:codecommit:us-east-1:xxxxxxxxx:cicd-test"],
  "detail": {
    "event": ["pullRequestCreated", "pullRequestSourceBranchUpdated"]
  }
}
```

　Step Functionsが起動すると、まず「**PR or push**」というChoiceアクションで、**ブランチの作成/変更があった場合**と**プルリクエストの作成/変更があった場合**で分岐します。Step Functionsのコードは以下のようになっています。
<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/cda74127-ead6-0a28-ccbe-1a7be7132264.png' witdh='300'>
```:PR or push
"PR or push": {
  "Type": "Choice",
  "Choices": [
    {
      "Variable": "$.detail-type",
      "StringEquals": "CodeCommit Pull Request State Change",
      "Next": "Get Reference Name"
    },
    {
      "Variable": "$.detail-type",
      "StringEquals": "CodeCommit Repository State Change",
      "Next": "Plan feature or Deploy"
    }
  ]
},
```
Event Bridgeからの入力に「**detail-type**」というパラメータがあり、この中にイベントタイプが格納されていますので、これを基に分岐しています。

### プルリクエストの作成/変更があった場合のフローについて
　左側のフローを見ていきます。こちらは**プルリクエストの作成/変更があった場合のフロー**になりますが、CodeBuildのアクションが実行される前に、一つLambdaが挟まっています。

<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/5c6a8e40-43a9-2080-2042-cf373b216d37.png' width='300'>

プルリクエストの作成/変更があった場合にStep Functionsに渡される入力の中に、以下のような情報があるのですが、ここから対象のブランチ名を取り出すためのLambdaとなります。コードは以下のようなイメージです。(併せてリポジトリ名も取り出しています)

```
"sourceReference": "refs/heads/feature/test"
```
```Node.js
export const handler = async (event) => {
  const sourceReference = event.detail.sourceReference;
  const branchName = sourceReference.split('refs/heads/')[1];
  const repositoryName = event.detail.repositoryNames[0];
  
  return {
    'branchName': branchName,
    'repositoryName': repositoryName,
  };
};
```
```:Get Reference Name
"Get Reference Name": {
  "Type": "Task",
  "Resource": "arn:aws:states:::lambda:invoke",
  "OutputPath": "$.Payload",
  "Parameters": {
    "Payload.$": "$",
    "FunctionName": "arn:aws:lambda:us-east-1:xxxxxx:function:get-reference-name:$LATEST"
  },
  "Retry": [
    {
      "ErrorEquals": [
        "Lambda.ServiceException",
        "Lambda.AWSLambdaException",
        "Lambda.SdkClientException",
        "Lambda.TooManyRequestsException"
      ],
      "IntervalSeconds": 1,
      "MaxAttempts": 3,
      "BackoffRate": 2
    }
  ],
  "Next": "Terraform Plan PR"
},
```

 次にCodeBuildのアクションの定義になります。ここでは、先で説明した通り、実行パラメータとbuildspecコマンドをオーバーライドして実行します。
```:Terraform Plan PR
"Terraform Plan PR": {
  "Type": "Task",
  "Resource": "arn:aws:states:::codebuild:startBuild.sync",
  "Parameters": {
    "ProjectName": "cicd-test",
    "SourceTypeOverride": "CODECOMMIT",
    "SourceLocationOverride.$": "States.Format('https://git-codecommit.us-east-1.amazonaws.com/v1/repos/{}',  $.repositoryName)",
    "BuildspecOverride": "cicd/planall.yml",
    "SourceVersion.$": "$.branchName",
    "EnvironmentVariablesOverride": [
      {
        "Name": "branch",
        "Type": "PLAINTEXT",
        "Value.$": "$.branchName"
      },
      {
        "Name": "job",
        "Type": "PLAINTEXT",
        "Value": "plan"
      }
    ]
  },
  "Catch": [
    {
      "ErrorEquals": [
        "States.TaskFailed"
      ],
      "Next": "Nofity Faliure"
    }
  ],
  "Next": "Nofity Success"
},
```
注目すべき点は「Parameters」配下でパラメータをオーバーライドしている箇所です。
- **SourceTypeOverride**：　CodeBuildのプロジェクト作成時に選択しなかったソースプロバイダ（CODECOMMIT）を指定します。
- **SourceLocationOverride**：　CodeCommitのリポジトリのurlを指定します。動的に指定する理由はあまりありませんが、Lambdaからの返り値を利用しています。
- **BuildspecOverride**：　詳しくは後述しますが、buildspecコマンドを上書きします。
- **SourceVersion**：　対象のブランチ名を指定します。

```
"SourceTypeOverride": "CODECOMMIT"
"SourceLocationOverride.$": "States.Format('https://git-codecommit.us-east-1.amazonaws.com/v1/repos/{}',  $.repositoryName)",
"BuildspecOverride": "cicd/planall.yml",
"SourceVersion.$": "$.branchName", 
```
BuildspecOverrideですが、リポジトリのルートディレクトの直下に「**cicd**」というディレクトリを作成し、その下に上書きするbuildspecコマンドを配置しています。内容は以下のとおりです。

```yaml:planall.yml
version: 0.2

phases:
  pre_build:
    commands:
      - yum install -y git-all
      - git clone https://github.com/tfutils/tfenv.git ~/.tfenv
      - ln -s ~/.tfenv/bin/* /usr/local/bin
      - tfenv -v
      - tfenv install latest
      - tfenv use latest

  build:
    commands:
      - echo ${CODEBUILD_SOURCE_VERSION}
      - |
        echo develop
        cd ${CODEBUILD_SRC_DIR}/terraform/env/dev
        terraform init
        terraform validate
        terraform plan
      - |
        echo production
        cd ${CODEBUILD_SRC_DIR}/terraform/env/prod
        terraform init
        terraform validate
        terraform plan

  post_build:
    commands:
      - echo planall finished
```
詳細は省略しますが、各環境（開発環境/商用環境）ごとに terraform plan　を実行するようなbuildspecコマンドになっています。CODEBUILD_SOURCE_VERSION、CODEBUILD_SRC_DIRはCodeBuildを実行時に含まれている環境変数で、それぞれ**対象のブランチ名**、**ビルド対象のソースのルードディレクトリのパス**が格納されています。


### ブランチの作成/変更があった場合のフローについて
　左側のフローは、**ブランチの作成/変更があった場合のフロー**になります。こちらでは、CodeBuildのアクションに入る前に、**featureブランチのpush**か、**develop/mainブランチへのマージ**かを判定します。Step Functionsへの入力の「**detail.referenceName**」が対象のブランチ名のため、こちらを利用して判定しています。


<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/276f6306-2c9f-2bbd-f7ab-c6d6f1dd46a6.png' width='400'>


```:Plan feature or Deploy
"Plan feature or Deploy": {
  "Type": "Choice",
  "Choices": [
    {
      "Variable": "$.detail.referenceName",
      "StringMatches": "feature/*",
      "Next": "Terrafrom Plan feature"
    },
    {
      "Or": [
        {
          "Variable": "$.detail.referenceName",
          "StringMatches": "main"
        },
        {
          "Variable": "$.detail.referenceName",
          "StringMatches": "develop"
        }
      ],
      "Next": "Terraform Plan"
    }
  ]
},
```

　**featureブランチのpush**の場合、CodeBuildのアクション「**Terraform Plan feature**」が実行されます。
<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/7e1304e7-46f2-1cfd-b7b8-fbe947b757ec.png' width='400'>

```:Terrafrom Plan feature
"Terrafrom Plan feature": {
  "Type": "Task",
  "Resource": "arn:aws:states:::codebuild:startBuild.sync",
  "Parameters": {
    "ProjectName": "cicd-test",
    "SourceTypeOverride": "CODECOMMIT",
    "SourceLocationOverride.$": "States.Format('https://git-codecommit.us-east-1.amazonaws.com/v1/repos/{}',  $.detail.repositoryName)",
    "BuildspecOverride": "cicd/planall.yml",
    "SourceVersion.$": "$.detail.referenceName",
    "EnvironmentVariablesOverride": [
      {
        "Name": "branch",
        "Type": "PLAINTEXT",
        "Value.$": "$.detail.referenceName"
      },
      {
        "Name": "job",
        "Type": "PLAINTEXT",
        "Value": "plan"
      }
    ]
  },
  "Catch": [
    {
      "ErrorEquals": [
        "States.TaskFailed"
      ],
      "Next": "Nofity Faliure"
    }
  ],
  "Next": "Nofity Success"
},
```
ほとんど内容は**Terraform Plan PR**と一緒ですが、PRの作成/変更のイベントとは違い、get-reference-nameを使わずともStep Functionsへの入力から直接ブランチ名をとることが可能です。また、featureブランチのpush時のbuildspecコマンドはPRの作成/変更時と同じく「**planall.yml**」です。

　次に**develop/mainブランチへのマージ**の場合ですが、こちらは一番右端のフローが動きます。まずterraform plan を実行し、成功すれば後続のアクションで terraform apply を実行します。
 ![スクリーンショット 2024-07-07 16.40.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/d4c991e5-f13c-6019-84d2-0b2616f6b252.png)
それぞれのアクションのStep Functionsのコード定義は以下になります。

```:Terraform Plan
"Terraform Plan": {
  "Type": "Task",
  "Resource": "arn:aws:states:::codebuild:startBuild.sync",
  "Parameters": {
    "ProjectName": "cicd-test",
    "SourceTypeOverride": "CODECOMMIT",
    "SourceLocationOverride.$": "States.Format('https://git-codecommit.us-east-1.amazonaws.com/v1/repos/{}',  $.detail.repositoryName)",
    "BuildspecOverride": "cicd/plan.yml",
    "SourceVersion.$": "$.detail.referenceName",
    "EnvironmentVariablesOverride": [
      {
        "Name": "branch",
        "Type": "PLAINTEXT",
        "Value.$": "$.detail.referenceName"
      },
      {
        "Name": "job",
        "Type": "PLAINTEXT",
        "Value": "plan"
      }
    ]
  },
  "Next": "Terraform Apply",
  "Catch": [
    {
      "ErrorEquals": [
        "States.TaskFailed"
      ],
      "Next": "Nofity Faliure"
    }
  ]
},
```
```:Terraform Apply
"Terraform Apply": {
  "Type": "Task",
  "Resource": "arn:aws:states:::codebuild:startBuild.sync",
  "Parameters": {
    "ProjectName": "cicd-test",
    "SourceTypeOverride": "CODECOMMIT",
    "SourceLocationOverride.$": "$.Build.Source.Location",
    "BuildspecOverride": "cicd/apply.yml",
    "SourceVersion.$": "$.Build.SourceVersion",
    "EnvironmentVariablesOverride": [
      {
        "Name": "branch",
        "Type": "PLAINTEXT",
        "Value.$": "$.Build.SourceVersion"
      },
      {
        "Name": "job",
        "Type": "PLAINTEXT",
        "Value": "apply"
      }
    ]
  },
  "Catch": [
    {
      "ErrorEquals": [
        "States.TaskFailed"
      ],
      "Next": "Nofity Faliure"
    }
  ],
  "Next": "Nofity Success"
}
```
buildspecコマンドに関して、**Terraform Plan**の場合「**plan.yml**」、**Terraform Apply**の場合「**apply.yml**」で上書きします。

```yaml:plan/apply.yml
version: 0.2

phases:
  pre_build:
    commands:
      - yum install -y git-all
      - git clone https://github.com/tfutils/tfenv.git ~/.tfenv
      - ln -s ~/.tfenv/bin/* /usr/local/bin
      - tfenv -v
      - tfenv install latest
      - tfenv use latest

  build:
    commands:
      - echo ${CODEBUILD_SOURCE_VERSION}
      - |
        case ${CODEBUILD_SOURCE_VERSION} in
          "develop")
            Env="dev"
            ;;
          "main")
            Env="prod"
            ;;
          *)
            exit 1
            ;;
        esac
      - |
        echo ${CODEBUILD_SOURCE_VERSION}
        cd ${CODEBUILD_SRC_DIR}/terraform/env/${Env}
        terraform init
        terraform validate

        # planの場合はこちら
        terraform plan

        # applyの場合はこちら
        terraform apply -auto-approve

  post_build:
    commands:
      - echo plan finished
```

planとapplyで内容はほぼ同じで、最後に実行するterrafromコマンドが違うだけです。前述の通り、CODEBUILD_SOURCE_VERSIONにブランチ名が入っているので、その内容によってterraformコマンドを実行するディレクトリを分けています。

### CI/CDの結果の通知に関して
　Step Funcitonsで定義してきた各フローでは、最後に結果を通知するためのLambdaを実行するようにしています。成功の場合は**Notify Success**が、失敗の場合は**Nofity Failure** が実行されます。詳細は割愛しますが、CodeBuildアクションの結果（出力）がそのままLambdaにイベントとして渡されるので、その内容を加工してSlackやTeams、メール通知を行う処理を実装します。

![スクリーンショット 2024-07-07 18.44.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/60402aeb-435d-5480-8710-643c940fbc7a.png)


　以上で作成したCI/CDパイプラインの解説は以上になります。今回はTerraformのCI/CDを例にしていましたが、CodeBuildの各アクションで上書きしていたbuildspecコマンドを変更すれば、例えばフロントエンドのコードのCI/CDなどにも流用することとが出来ます。（テスト用のCodeBuildアクションなどを別途追加する必要はありそうですが...）


## 動作イメージ
　それではTerraformによる簡単なリソース作成を通して、CI/CDパイプラインの動作を確認していきたいと思います。リポジトリのディレクトリ構成は以下のようになっており、今回は開発環境(dev)と商用環境(prod)でS3バケットを作成します。

```:リポジトリのディレクトリ構成
terraform
├ env
│ ├ dev
│ │ ├ main.tf
│ │ ├ terraform.tfvars # 変数 env='dev'を定義
│ │ └ variables.tf
│ └ prod
│ 　 ├ main.tf
│ 　 ├ terraform.tfvars # 変数 env='prod'を定義
│ 　 └ variables.tf
└ module
　 └ common
　 　 └ s3.tf
```

```terraform:s3.tf
resource "aws_s3_bucket" "cicd_bucket" {
    bucket = "cicd-bucket-${var.env}-240707"
}
```

### featureブランチをpushした際の動作
　まずは「**feature/create_bucket**」というブランチを切り、上記のコードを作成しリモートにpushしてみます。作成したCI/CDパイプラインのうち、**Terraform Plan feature**が動くはずですが、どうでしょうか...

![スクリーンショット 2024-07-07 22.18.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/15889204-5018-2004-4fa2-566900063aab.png)

**いい感じに動いていそう！** 対象のブランチも問題なく「feature/create_bucket」になっています！ イベントログからCodeBuildのログを開くことができるので、確認してみます。

![スクリーンショット 2024-07-07 22.18.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/1c5b5bf2-5005-0e1b-884e-ecf92a5a8cc2.png)
![スクリーンショット 2024-07-07 22.20.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/1e6ef6fd-3959-5d43-96f5-8da573fefc98.png)
![スクリーンショット 2024-07-07 22.20.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/94c65e4f-f87e-9de8-3afa-5065411b6b57.png)

各環境の terraform plan がばっちり動いています！　
しばらく待っているとCI/CDパイプラインの実行が完了しました。これで、**featureブランチのような曖昧なブランチも取り扱えることを確認出来ました！！**

![スクリーンショット 2024-07-07 22.21.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/3f4ec139-f05d-a6f8-a8ca-877e12ecadc8.png)


### プルリクエストを作成した際の動作
　次にプルリクエストを作成した際の動作を見てみたいと思います。pushしたfeatureブランチからdevelopブランチへのマージのプルリクエストを作成します。
 ![スクリーンショット 2024-07-07 22.22.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/c0d6879f-fbdb-b270-0d11-4c538e98621b.png)

プルリクエスト作成後、**Terraform Plan PR**が動いていることが確認できました！ こちらも特に問題はなさそうです。

![スクリーンショット 2024-07-07 22.23.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/873bd6ca-d9d3-745e-c6f1-7cc20f3963fa.png)

しばらく待った後、terraform planが成功したことも確認できました。

![スクリーンショット 2024-07-07 22.24.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/5355e79c-427d-2f5a-7d81-51f6b62f61e2.png)


### デプロイの際の動作
　最後に、developブランチやmainブランチにマージした際に実行されるデプロイの動作を確認したいと思います。まず、先ほど作成したdevelopブランチへのプルリエストをマージします。この作業で開発環境リソースがデプロイされます。
 ![スクリーンショット 2024-07-07 22.25.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/d1535493-d653-577b-eb35-548518e836b9.png)

こちらも問題なく**Terraform Plan**の方が動きました！

![スクリーンショット 2024-07-07 22.25.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/a081de74-18a0-4fdd-042b-3c971380a33f.png)

CodeBuildのログを見ても、developブランチで terraform plan が動いていることがわかります。

![スクリーンショット 2024-07-07 22.26.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/55bc20c0-67f3-da2b-37be-43e450c67f84.png)

無事にplanが成功し、applyのアクションが動き始めました！

![スクリーンショット 2024-07-07 22.27.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/d493e038-cf89-9e8a-0caa-80a7c4b06b57.png)

しばらく経ってCodeBuildのログを確認してみると、applyが成功したことが確認できました！ Step Functionsの方も実行が完了しています。

![スクリーンショット 2024-07-07 22.28.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/1ae4c5b4-f512-b336-4bca-1801eff3390d.png)

![スクリーンショット 2024-07-07 22.29.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/78569100-46e8-566b-a216-84a69e7aefa8.png)

では実際にS3バケットが作成できているか確認してみます。S3のコンソールに移動し、「cicd-bucket」で検索をかけると...

<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/a9e2ae62-9152-a558-633b-e26dd4877f95.png' width='500'>

**バケットが作成出来ています！**　これで、ローカルのfeatureブランチでの編集内容をpush → developブランチへのプルリクエスト作成 → マージして開発環境リソースのデプロイ まで作成したCI/CDパイプラインで実行できることが確認できました！！

　念の為、mainブランチへのマージ（商用環境リソースのデプロイ）も実施してみます。
 
![スクリーンショット 2024-07-07 22.33.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/954c5c3f-0612-a2e4-c359-257f134e0006.png)

こちらもちゃんと**Terraform Plan**の方のフローが動いています。

![スクリーンショット 2024-07-07 22.34.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/a5ac3b03-c420-7aca-afa5-bd93c18d48db.png)

CodeBuildのログを眺めていると、applyに無事成功したようです！　Step Functionsの方も大丈夫でした。

![スクリーンショット 2024-07-07 22.36.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/ff74fa3a-56f9-5070-5726-e3659ff593c5.png)


![スクリーンショット 2024-07-07 22.36.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/44aa3f62-a21a-4fb0-5d85-b822fa211733.png)

S3のコンソールに移動し、「cicd-bucket-prod」で検索をかけると、商用環境リソースも作成出来ていました！

<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3026734/b5626d5f-93cc-8206-f9ca-3c8b2465484e.png' width='500'>


# まとめと感想
　今回は　**CodeBuild + Step Functions**　でのCI/CDパイプラインの構築に関しての経験談を記事にしてみました！ 普段はGitHub ActionsなどのSaaSのCI/CDを利用しているため、**Code4兄弟**（の一部）に触れる貴重な機会となりました。
　実際に構築してみて感じたことはやはり**GitHub Actionsのありがたみ**でした。Actionsでは、workflow定義のyamlファイルさえ作成すればCI/CDを実現できますが（Enterprise版だとホストランナーを立てる必要あり）、今回の場合はAWSの複数サービスを組み合わせてパイプラインを組み上げる必要があったため、かなり構築に時間を要しました。また、今回の記事で紹介したものはベースラインということもありまだまだ足りていないものもありますが、運用上以下の点も気になるところです。
 - パイプラインの実行ログを確認するにはStep FunctionsのイベントログからCodeBuildのログに飛ぶ必要があるので、過去の実行履歴を見ようとした際に煩雑になる
 - もしCI/CDパイプラインが動かなくなった際に、複数サービスを組み合わせているため原因の切り分けが多少複雑になる
 - CodeCommitのブランチ削除保護ができない（IAMユーザーの権限で縛る必要がある）
 - DR構成を考えるのが大変

正直まだまだ経験が不足しており、もっと上手く構築する方法はいっぱいあるはずなので、色々とカスタマイズして強化していきたいと思っています。

　本記事はこれで以上になります。長々と書いてしまいましたが、最後までご精読いただきましてありがとうございました！！