[小ネタ] やっぱりCodepipelineはIaCよりCLIで作った方が良いな。。

## 初めに

小ネタになってしまうのですが、AWSですがCloudformationやTerraformなどIaC化しないで、CLIで作成した方が遥かに楽なリソースがあります。
今回は、私が体感した中では一番楽だったリソースCodepipelineのCLIにおける作成/削除について解説したいと思います。


## IaC化するまでの流れ
みなさまはテンプレート化するにあたり、おそらくですが
- 手動で作成したのち[former2](https://former2.com/)でIaCテンプレート化
- ChatGPTとかの生成AIに聞いて生成してみる。
のような流れではないかと思います。


ちなみに公式のExampleは[こちら](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-codepipeline-pipeline.html#aws-resource-codepipeline-pipeline--examples)になります。

IaC化が悪いと言うわけではないですが、**個人的な感想としては各種Stages, Action句でのリソースの定義やインデントが少し面倒そう**だなと感じました。


```
AWSTemplateFormatVersion: 2010-09-09
Description: CodePipeline sample
Parameters:
  CodePipelineServiceRole:
    Type: String
  ArtifactStoreS3Location:
    Type: String
  SourceActionName:
    Type: String
Resources:
  CodePipelineForIntegTest:
    Type: 'AWS::CodePipeline::Pipeline'
    Properties:
      RoleArn: !Ref CodePipelineServiceRole
      Stages:
        - Name: Source
          Actions:
            - Name: !Ref SourceActionName
              InputArtifacts: []
              ActionTypeId:
                Category: Source
                Owner: AWS
                Version: '1'
                Provider: CodeStarSourceConnection
              OutputArtifacts:
                - Name: SourceOutput
              Configuration:
                BranchName: main
                ConnectionArn: >-
                  arn:aws:codestar-connections:us-east-1:123456789123:connection/id
                FullRepositoryId: repo-owner/sample-project
              RunOrder: 1
        - Name: Beta
          Actions:
            - Name: BetaAction
              InputArtifacts:
                - Name: SourceOutput
              ActionTypeId:
                Category: Build
                Owner: AWS
                Provider: CodeBuild
                Version: '1'
              Configuration:
                ProjectName: Sample
              RunOrder: 1
      Triggers:
        - ProviderType: CodeStarSourceConnection
          GitConfiguration:
            Push:
              - Tags:
                  Excludes:
                    - beta-*
                  Includes:
                    - release-*
              - Branches:
                  Excludes:
                    - beta-*
                  Includes:
                    - release-*
                FilePaths:
                  Includes:
                    - projectA/**
                    - common/**/*.js
                  Excludes:
                    - '**/README.md'
                    - '**/LICENSE'
                    - '**/CONTRIBUTING.md'
            PullRequest:
              - Branches:
                  Excludes:
                    - stable-v1-*
                  Includes:
                    - stable-*
                    - release-*
                FilePaths:
                  Includes:
                    - projectA/**
                    - common/**/*.js
                  Excludes:
                    - '**/README.md'
                    - '**/LICENSE'
                    - '**/CONTRIBUTING.md'
                Events:
                  - CLOSED
            SourceActionName: !Ref SourceActionName
      PipelineType: V2
      ExecutionMode: PARALLEL
      ArtifactStore:
        Type: S3
        Location: !Ref ArtifactStoreS3Location

```


## CLI化の利点
CLI化の利点はズバリ「手動で作成 → CLIのテンプレート化」までの流れが簡単でかつ早いということです。
結論から言うと、手動で作成したパイプラインを`get-pipeline`で取得した後、軽く編集することで新規作成/更新が簡単にできます。

どれくらい用意なのか具体的な手順は以下の通りです。
まずはパイプラインの情報を取得します。

```sh
$ aws codepipeline get-pipeline \
    --name demo-appsPLTagging-001
```


以下のようにコマンドでパイプラインの情報をjson形式で取得することができます。


```json:出力結果
{
    "pipeline": {
        "name": "demo-appsPLTagging-001",
        "roleArn": "arn:aws:iam::123456789012:role/service-role/AWSCodePipelineServiceRole-<<REGION_NAME>>-demo-apps-00",
        "artifactStore": {
            "type": "S3",
            "location": "cicd-apne1-artifactstore-001"
        },
        "stages": [
            {
                "name": "Source",
                "actions": [
                    {
                        "name": "Source",
                        "actionTypeId": {
                            "category": "Source",
                            "owner": "AWS",
                            "provider": "S3",
                            "version": "1"
                        },
                        "runOrder": 1,
                        "configuration": {
                            "PollForSourceChanges": "false",
                            "S3Bucket": "cicd-apne1-artifactstore-001",
                            "S3ObjectKey": "demo-apps-tagging-001/tagging/SourceArtifact.zip"
                        },
                        "outputArtifacts": [
                            {
                                "name": "SourceArtifact"
                            }
                        ],
                        "inputArtifacts": [],
                        "region": "<<REGION_NAME>>",
                        "namespace": "SourceVariables"
                    }
                ]
            },
            {
                "name": "Build",
                "actions": [
                    {
                        "name": "Build",
                        "actionTypeId": {
                            "category": "Build",
                            "owner": "AWS",
                            "provider": "CodeBuild",
                            "version": "1"
                        },
                        "runOrder": 1,
                        "configuration": {
                            "BatchEnabled": "false",
                            "ProjectName": "demo-apps-plan-001"
                        },
                        "outputArtifacts": [
                            {
                                "name": "BuildArtifact"
                            }
                        ],
                        "inputArtifacts": [
                            {
                                "name": "SourceArtifact"
                            }
                        ],
                        "region": "<<REGION_NAME>>",
                        "namespace": "BuildVariables"
                    }
                ]
            },
            {
                "name": "ManualApprove",
                "actions": [
                    {
                        "name": "ManualApprove",
                        "actionTypeId": {
                            "category": "Approval",
                            "owner": "AWS",
                            "provider": "Manual",
                            "version": "1"
                        },
                        "runOrder": 1,
                        "configuration": {
                            "NotificationArn": "arn:aws:sns:<<REGION_NAME>>:123456789012:cicd-notify"
                        },
                        "outputArtifacts": [],
                        "inputArtifacts": [],
                        "region": "<<REGION_NAME>>"
                    }
                ]
            },
            {
                "name": "Apply",
                "actions": [
                    {
                        "name": "Apply",
                        "actionTypeId": {
                            "category": "Build",
                            "owner": "AWS",
                            "provider": "CodeBuild",
                            "version": "1"
                        },
                        "runOrder": 1,
                        "configuration": {
                            "ProjectName": "demo-apps-apply-001"
                        },
                        "outputArtifacts": [],
                        "inputArtifacts": [
                            {
                                "name": "SourceArtifact"
                            }
                        ],
                        "region": "<<REGION_NAME>>"
                    }
                ]
            }
        ],
        "version": 5,
        "executionMode": "SUPERSEDED",
        "pipelineType": "V1"
    },
    "metadata": {
        "pipelineArn": "arn:aws:codepipeline:<<REGION_NAME>>:123456789012:demo-appsPLTagging-001",
        "created": "2021-08-18T09:01:29.489000+00:00",
        "updated": "2021-08-18T10:31:26.857000+00:00"
    }
}
```

上記の内容をコピーして、保存する際に"metadata"部分を削除してte`pipeline.json`として保存します。
```json:出力結果
{
    "pipeline": {
        "name": "demo-appsPLTagging-001",
        "roleArn": "arn:aws:iam::123456789012:role/service-role/AWSCodePipelineServiceRole-<<REGION_NAME>>-demo-apps-00",
        "artifactStore": {
            "type": "S3",
            "location": "cicd-apne1-artifactstore-001"
        },
        "stages": [
            {
                "name": "Source",
                "actions": [
                    {
                        "name": "Source",
                        "actionTypeId": {
                            "category": "Source",
                            "owner": "AWS",
                            "provider": "S3",
                            "version": "1"
                        },
                        "runOrder": 1,
                        "configuration": {
                            "PollForSourceChanges": "false",
                            "S3Bucket": "cicd-apne1-artifactstore-001",
                            "S3ObjectKey": "demo-apps-tagging-001/tagging/SourceArtifact.zip"
                        },
                        "outputArtifacts": [
                            {
                                "name": "SourceArtifact"
                            }
                        ],
                        "inputArtifacts": [],
                        "region": "<<REGION_NAME>>",
                        "namespace": "SourceVariables"
                    }
                ]
            },
            {
                "name": "Build",
                "actions": [
                    {
                        "name": "Build",
                        "actionTypeId": {
                            "category": "Build",
                            "owner": "AWS",
                            "provider": "CodeBuild",
                            "version": "1"
                        },
                        "runOrder": 1,
                        "configuration": {
                            "BatchEnabled": "false",
                            "ProjectName": "demo-apps-plan-001"
                        },
                        "outputArtifacts": [
                            {
                                "name": "BuildArtifact"
                            }
                        ],
                        "inputArtifacts": [
                            {
                                "name": "SourceArtifact"
                            }
                        ],
                        "region": "<<REGION_NAME>>",
                        "namespace": "BuildVariables"
                    }
                ]
            },
            {
                "name": "ManualApprove",
                "actions": [
                    {
                        "name": "ManualApprove",
                        "actionTypeId": {
                            "category": "Approval",
                            "owner": "AWS",
                            "provider": "Manual",
                            "version": "1"
                        },
                        "runOrder": 1,
                        "configuration": {
                            "NotificationArn": "arn:aws:sns:<<REGION_NAME>>:123456789012:cicd-notify"
                        },
                        "outputArtifacts": [],
                        "inputArtifacts": [],
                        "region": "<<REGION_NAME>>"
                    }
                ]
            },
            {
                "name": "Apply",
                "actions": [
                    {
                        "name": "Apply",
                        "actionTypeId": {
                            "category": "Build",
                            "owner": "AWS",
                            "provider": "CodeBuild",
                            "version": "1"
                        },
                        "runOrder": 1,
                        "configuration": {
                            "ProjectName": "demo-apps-apply-001"
                        },
                        "outputArtifacts": [],
                        "inputArtifacts": [
                            {
                                "name": "SourceArtifact"
                            }
                        ],
                        "region": "<<REGION_NAME>>"
                    }
                ]
            }
        ],
        "version": 5,
        "executionMode": "SUPERSEDED",
        "pipelineType": "V1"
    }
}
```

整形して保存したのち、パラメータなどの編集したい部分があったら適宜変更いただければと思います。

その後、以下のコマンドを実行すれば簡単にCodePipeline を作成/編集することができます。

```sh 
# 新規作成
$ aws codepipeline create-pipeline \
    --pipeline demo-appsPLTagging-001 \
    --cli-input-json file://pipeline.json

# 更新
$ aws codepipeline update-pipeline \
    --pipeline demo-appsPLTagging-001 \
    --cli-input-json file://pipeline.json
```

別アカウントやリージョンへの複製は、`pipeline.json`ファイルのリソース名やリージョンなどを適宜変更すれば簡単にパイプラインを構築することができます。

## 終わりに
CodePipelineに関しては、CLIで作成した方が遥かに楽なリソースであることがわかりました。

近々、IaCやCLIで作成した方が楽なリソースをサマリ化 & テンプレート化したようなものも紹介しようかと思いました。