CDKPipelineを用いてS3バケットをソースとしたパイプラインを構築する

# 完成物

![Pipeline drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480176/e8e8f5e9-6513-bc28-a64c-426e85f1616e.png)

下記の記事で左側のOIDC認証のフローを作成したので、今回は右側のパイプラン部分の備忘録となります。

https://qiita.com/hosomatu/items/7f32416048bd3a0e1b99


# CDKPipelineとはざっくり

私の主観を含んだ感じで言うと
**「CodePipelineとCodeBuildなどのパターンを用意してくれたL3のようなコンストラクトライブラリ」**
です

CodePipeline用のモジュールが
aws-cdk-lib.aws_codepipelineという立ち位置にあるモジュールなのに対して

https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_codepipeline-readme.html


CDKPipelineは
aws-cdk-lib.pipelinesという立ち位置にあるモジュールです。

https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.pipelines-readme.html

こちらはCDKPipelineの方のドキュメントなのですが、
> Give the CDK Pipelines way of doing things a shot first: you might find it does everything you need. If you need more control, or if you need v2 support from aws-codepipeline, we recommend you drop down to using the aws-codepipeline construct library directly.

> まずは CDK Pipelines の方法を試してみてください。必要なことはすべてこれで実行できることがわかるかもしれません。 
より詳細な制御が必要な場合、または aws-codepipeline からの v2 サポートが必要な場合は、ドロップダウンして aws-codepipeline コンストラクト ライブラリを直接使用することをお勧めします。

CDK開発においては構築するパターンと同じL3コンストラクトが用意されていたら使うことが推奨されています。CDKPipelineもパイプライン構築についてその立ち位置にありそうなので、「CodePipelineとCodeBuildのパターンを用意してくれたL3のようなコンストラクトライブラリ」と私は捉えています。

こちらの記事がCodePipelineとCDKPipelineの違いについて参考にさせていただいた記事です。とてもわかりやすかった

https://zenn.dev/siroken3/scraps/769002c03b31d6

https://qiita.com/tanaka_ryotaro/items/d172b6aef7cb81464552

### CDKPipelineで用意されているパターン
ざっくり日本語で書くと
**「CodePipeline、CodeBuilt、CodeDeployを簡単に設定＋パイプライン自身をCDK管理して自動アップデート」** 
にようなパターンを高抽象度化で実装できます。

パイプラインが走ったときに、指定したステージ（スタックと環境を定義した概念。後述）のデプロイと、パイプラインを定義したスタックを自身を更新してくれます。


パイプライン自身をCDK管理して自動アップデートする機構を簡単に実装できることはCDKPipelineを使用するメリットだと言えると思います。

ここからさらに詳細な要件がある場合はaws-cdk-lib.aws_codepipelineを使用して、より詳細にCodePipeline、CodeBuildなどを実装すればよさそうです。

# S3をソースとしたパイプラインの実際のコード

最終的にはこんな感じになりました

``` TypeScript
// zip化されたソースコードを格納するS3バケット。パイプラインのソースステージで利用する。
    const bucket = new cdk.aws_s3.Bucket(this, 'SourceCodeBucket', {
      bucketName: 's3-bucket-name',
      versioned: true,
      blockPublicAccess: cdk.aws_s3.BlockPublicAccess.BLOCK_ALL,
      encryption: cdk.aws_s3.BucketEncryption.S3_MANAGED,
      removalPolicy: cdk.RemovalPolicy.RETAIN,
    });

    // CodePipeline、CodeBuildの作成。S3バケットをソースとしたパイプラインの構築
    const pipeline = new CodePipeline(this, 'Pipeline', {
      pipelineName: 'cdkPipeline-name',
      synth: new ShellStep('Synth', {
        input: CodePipelineSource.s3(bucket, 'source-code-name.zip'),
        commands: [
          'npm ci',
          'npm run build',
          'npx cdk synth'
        ],
      }),
    });

    // CodeDeploy。デプロイステージ追加
    pipeline.addStage(deployStage);
```

これだけで、
![Pipeline drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480176/8ff45f9e-2e50-2bcd-8f9d-f08fb22535a9.png)

このリソースを実装することができます。高抽象度化ありがたい。

diffを取るとわかるのですが、Roleとポリシーもいい感じにしてくれています。
![diff.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480176/da911bc7-5772-b19b-a062-8b66712f4957.png)

デプロイするとパイプラインがきちんと起動しました
![pipeline.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480176/64ca2499-a6d8-3ddf-e115-9ce53577be63.png)




<br>


S3へのトリガーの設定についても
ここの部分で自動的に変更を検知して発火するようにしてくれています。

```TypeScript
input: CodePipelineSource.s3(bucket, 'source-code-name.zip')
```
https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.pipelines-readme.html

![s3source.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480176/1bf0c79d-3262-8b16-6205-738cfa92990d.png)


<br>


## CDKにおけるステージと、Pipelineにおけるステージの概念

CodeDeployについてはこの部分で初めてリソースが追加されます。
```TypeScript
pipeline.addStage(deployStage);
```

この**addStage**と、**deployStage**はのStage同士は異なる概念なので注意が必要です。

### addStage
パイプラインにおけるステージの概念。ソフトウェアのビルド、テスト、デプロイの各フェーズを表すもの。

<br>

### deployStage
※名前自体はただの変数名
CDKにおけるステージの概念で、デプロイ単位でスタックをグループ化するためのもの。
一つ以上のスタックをグループ化するためのコンテナ。異なるデプロイメント環境を模倣するために使用されることが多く、例えば「開発ステージ」「テストステージ」「本番ステージ」といった形で分けることができる。各ステージは独立しており、ステージごとに異なる設定やリソースの構成を持つことができる。

<br>

CDKPipelineにてスタックをデプロイするには、スタックではなく、「CDKのステージ」として指定しないといけないようだったので別ファイルにてステージを定義して、インスタンス化したものをimportして使用しました。

```TypeScript
// パイプラインで使用するためにステージを作成
export class DeployStage extends cdk.Stage {
  constructor(scope: Construct, id: string, props: cdk.StageProps) {
    super(scope, id, props);

    new CdkStackName(this, 'CdkStackName', {
      env: { region },
      config: DevParameter, //この辺は個人で設定したパラメータ
    });
  }
}

const deployStage = new DeployStage(app, 'DeployStage', { //ステージをインスタンス化
  env: { region: region }
});
```

**スタックをステージに含めると、スタックの内容が全てデプロイされ直し**になって面倒だったので、やはりパイプラインは最初に構築するべきだなと改めて認識しました。


ちなみに主導承認のフェーズを追加したい場合はこのように記述すると追加することができました

```TypeScript
// 主導承認が必要なデプロイステージの追加
    pipeline.addStage(deployStage, {
      pre: [new ManualApprovalStep('Approval')],
    });
```

コンソールではこのように表示されます

![pipesyounin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480176/d2820b1a-18bc-b503-8f53-ea984f00bb6b.png)

![syounin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480176/95784bf3-8343-5b3f-a896-0542552f877f.png)


以上のような工程でS3をソースとしたパイプラインを簡単に構築することができました。


## 参考にした記事まとめ

公式

https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.pipelines-readme.html

https://zenn.dev/siroken3/scraps/769002c03b31d6

https://qiita.com/tanaka_ryotaro/items/d172b6aef7cb81464552