Bitbucket PipelinesとAWS CodePipelineを連携する！

# 背景

とある案件でCI/CD パイプラインをAWS CodePipelineで実装することになったのだが、以下の諸々の制約を解消する連携方法を模索することになった。

- ソース管理にはBitbucketを使用する
- 作成するパイプラインでは複数のAWSサービス(CodeDeployやSSM Automation、CloudFormation）と連携が必要
- 一つのBitbucketリポジトリから起動したいパイプラインの種類が複数ある

関連する各種サービスの仕様は以下の通り。

- CodePiplineのソースとしてはBitbucketはサポートされていない
- CodePipelineのソースとしてS3はサポートされている
- CodeBuildはBitbucketと連携可能

ということで、当初はこんな感じの構成を考えていた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/a9069713-2a63-ffaf-b8fd-1d7ee6ecff4c.png)

ところが、後になって以下の制約が判明！

- セキュリティ上Bitbucketは特定のIPアドレスの範囲からしかアクセスできない

AWS CodeBuildとしては以下の仕様があるのでIPアドレスの範囲に追加してもらうことを交渉してみる。

- CodeBuildはVPC内で実行することも可能

しかし、お客様がCodeBuildからもBitbucketにアクセスできるようにすることに乗り気でなかったのでBitbucketのパイプライン機能に白羽の矢を立てることになる。
いろいろ調べたところBitbucket Pipelinesの機能で色々と出来ることが判明し、一気に問題は解決の方向へ！

- Bitbucket Pipelinesで特定のブランチへの更新やタグ付けに反応してパイプラインを起動可能
- Bitbucket Pipelinesの[他サービスとの連携機能(Pipesと呼ぶ)](https://support.atlassian.com/bitbucket-cloud/docs/use-pipes-in-bitbucket-pipelines/)には[S3へのデプロイ用のPipe](https://bitbucket.org/product/features/pipelines/integrations?p=atlassian/aws-s3-deploy)もあるのでS3経由でCodePiplineと連携が簡単にできる

これらを踏まえ以下の流れでBitbucketをソースとしたCodePiplineの起動が出来るようになった。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/682f9c96-4c0f-9e26-f451-a8030361c8c7.png)

# 実装の手順
ここでは、実際にBitbucket PipelinesとCodePiplineを構成し連携するための手順を記載する。

## CodePiplineの構成
### 連携用S3バケットの作成
- S3のコンソールから**バケットを作成する**をクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/eb79d5b7-0edb-6bff-eb49-e21053f200e7.png)

- 任意の名前を入力して**作成**をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/5a825591-9514-b89f-9e5f-63a19e34165a.png)

- 作成したバケットを開く
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/fb10fda4-bb82-9f4f-a41d-ce27ab7bed8f.png)

- **プロパティ**を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/8e645efb-9174-fe09-b149-8eda25aced6a.png)

- **バージョニング**を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/43865f25-76e1-6bde-df2a-c4d33a1652d7.png)

- **バージョニングを有効化**を選択して**保存**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/d6c84683-459b-01a6-0aa8-b844823d4539.png)

- 有効化されたことを確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/2bac0ee7-2d41-2fc8-5ddf-d8b7b7dfe215.png)

※CodePipeのソースステージで指定するバケットはバージョニングを有効化している必要がある

### CodePipelineの作成 
- CodePiplineのコンソールから**パイプラインを作成する**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/8526be1b-8976-8dff-5abf-f4ec515d4e0e.png)

- 任意のパイプライン名を入力し、サービスロールで**新しいサービスロール**を選択して**次に**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/8e10714e-eda9-1cb9-2b90-8dd484aad6b2.png)

- ソースステージのプロバイダーに**AWS S3**を選択し、バケットに事前に作成したバケット名を、オブジェクトキーに**source.zip**を入力し**次に**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/6c3fa8da-a621-5689-1747-f0fa70aa9c43.png)

- ビルドステージのプロバイダーで**AWS CodeBuild**を選択し、**プロジェクトを作成する**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/6d6379f8-efdf-3085-c4a6-7eb5cbb085fe.png)

- 別のウィンドウが開くので、以下の項目を入力して**CodePipelineに進む**
※デフォルトからの変更点のみ

| 項目名 | 値 |
|:--|:--|
| プロジェクト名 | 任意の名前 |
| オペレーティングシステム | Amazon Linux 2 |
| ランタイム | Standard |
| イメージ | aws/codebuild/amazonlinux2-x86_64-standard:3.0 |
| ビルド仕様 | ビルドコマンドの挿入 |

※ビルドコマンドはエディターを開くことで直接編集可能
内容は以下を設定

```
version: 0.2
phases:
  install:
    runtime-versions:
      python: 3.7
    commands:
      - ls -R
```
中身はPython 3.7のランタイムをインストールしたコンテナ上でls コマンドを実行するのみ

![screencapture-ap-northeast-1-console-aws-amazon-codesuite-codebuild-project-new-2020-06-25-14_00_56.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/57e09552-9290-2c68-925a-26530c565d22.jpeg)

- 元の画面のビルドプロジェクト名に入力した値が反映されたことを確認の上**次に**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/2817a612-35a0-1c73-9b38-5a47a43c0d7d.png)

- デプロイステージは今回はスキップするので**導入段階をスキップ**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/053b9785-92a8-a7cf-a47d-5bddcf58a643.png)

- 表示されるダイアログでも**スキップ**をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/1cd4c2fb-04c5-9afe-7cef-d333f0f160af.png)

- CodePipeline全体の構成を確認し**パイプラインを作成する**
![screencapture-ap-northeast-1-console-aws-amazon-codesuite-codepipeline-pipeline-new-2020-06-25-14_06_00.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/c72ed7a5-4bc5-c859-00d6-21b20d73a56d.jpeg)

- AWS CodePipelineは作成時に必ず起動するが、この段階ではS3にsource.zipが存在していないため必ずエラーになる
![screencapture-ap-northeast-1-console-aws-amazon-codesuite-codepipeline-pipelines-Bitbucket-integration-view-2020-06-25-14_07_21.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/36f2abc4-26b7-c9d1-af77-d9930d5c6947.jpeg)


### Bitbucket Pipelinesで使用するIAMユーザーの作成
- IAMのコンソールから**ユーザー**メニューを選択し**ユーザーを追加**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/a2f0bb33-9cb6-ef89-cc1d-4261bd9cbb51.png)

- 任意のユーザー名を入力し、**プログラムによるアクセス**を選択し、**次のステップ**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/2d9b43ad-da35-d90b-177f-5a38f5ea6931.png)

- このまま**次のステップ**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/8270deca-1eba-5115-15ff-b669b36fd225.png)

- このまま**次のステップ**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/08510388-804d-d443-1d45-53675463aefb.png)

- **このユーザーにはアクセス権限がありません**と表示されるが、そのまま**ユーザーの作成**
    - ユーザー作成中にはインラインポリシーの作成が出来ないため一度作成を完了している
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/3712924e-6b6f-1b80-7f30-e66d09d5fc7c.png)

- 自動的にアクセスキーIDとシークレットアクセスキーが発行されるのでコピーするかcsvをダウンロードしておく
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/46e44e9e-bd2a-7550-74db-59f5c5a11e9a.png)

- 作成されたユーザーを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/c9296cdb-637f-9f4d-25a8-88e0a1d6f5b1.png)

- **インラインポリシーの追加**を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/3ba8c9e6-8fbe-94be-2088-ed32c5dc9f17.png)

- **JSON**タブを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/eb7f0020-2fc0-2fea-2a70-f4aef5bc1698.png)

- 以下のJSONのResource内のS3のARNのバケット名を先ほど作成したものに変更してからコピペしたら**ポリシーの確認**をクリック
※以下の太字部分を変更
arn:aws:s3:::**bitbucket-sourcecode**とarn:aws:s3:::**bitbucket-sourcecode**/*

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::bitbucket-sourcecode",
                "arn:aws:s3:::bitbucket-sourcecode/*"
            ]
        }
    ]
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/0d90f675-5a5f-8704-1d1e-a0ef25ed1a91.png)

- 任意のポリシー名を入力し**ポリシーの作成**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/8db00dfe-a3e5-e73f-1919-677620f3341e.png)


## Bitbucket Pipelinesの構成
### Bitbucket リポジトリの作成
- Bitbucketにアクセスし**リポジトリ**メニューからリポジトリを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/2b4039f9-35f9-da77-6d3b-a03982418365.png)

- 任意のプロジェクト名とリポジトリ名を入力し**リポジトリの作成**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/34071c33-7ecf-2acb-112c-b8c0681f58d7.png)

### Pipelinesの有効化
- **Pipelines**メニューを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/869b4377-7ccc-b155-6236-a924d2c9e4df.png)

- 下部に今回のPipelineで使用する言語テンプレートの選択箇所があるので今回は**Python**を選択
![screencapture-bitbucket-org-strada501gmail-codepipeline-integration-addon-pipelines-home-2020-06-25-14_52_15.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/53c9f087-0c2e-5140-1883-e71109b6febd.jpeg)

- 言語を選択した時点で自動的にエディターが開き、Pipelineの構成ファイルの初期テンプレートを設定してくれる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/519c2747-ca78-ea20-7b22-7b9c2285d079.png)


- 初期の状態だと起動トリガー（ブランチやタグ）を指定していない状態になっている

```
image: python:3.7.3

pipelines:
  default:
    - step:
        caches:
          - pip
        script: # Modify the commands below to build your repository.
          - pip install -r requirements.txt
```

- 起動トリガーを**master**ブランチに変更があった場合のみにするため以下のように編集する

```
image: python:3.7.3

pipelines:
  branches:
    master:
      - step:
          caches:
            - pip
          script: # Modify the commands below to build your repository.
            - pip install -r requirements.txt
```

- さらにエディターの右ペインに使用できるPipeが表示されるので、検索用のテキストエリアに**s3**を入力し、**AWS S3 Deploy**Pipeを表示する
- Pipe名の横のコピーアイコンをクリックするとSnippetがコピーできる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/279bec42-f479-8d6b-f202-d8ea0967435a.png)

- コピーされるSnippetは以下の通り

```
- pipe: atlassian/aws-s3-deploy:0.4.4
  variables:
    AWS_ACCESS_KEY_ID: '<string>' # Optional if already defined in the context.
    AWS_SECRET_ACCESS_KEY: '<string>' # Optional if already defined in the context.
    AWS_DEFAULT_REGION: '<string>' # Optional if already defined in the context.
    S3_BUCKET: '<string>'
    LOCAL_PATH: '<string>'
    # CONTENT_ENCODING: '<string>' # Optional.
    # ACL: '<string>' # Optional.
    # STORAGE_CLASS: '<string>' # Optional.
    # CACHE_CONTROL: '<string>' # Optional.
    # EXPIRES: '<timestamp>' # Optional.
    # DELETE_FLAG: '<boolean>' # Optional.
    # EXTRA_ARGS: '<string>' # Optional.
    # DEBUG: '<boolean>' # Optional.

```

- このSnippetも参考にしつつ設定した最終的な実装は以下の通り
- この内容で**Commit file**をクリックする

```
# This is a sample build configuration for Python.
# Check our guides at https://confluence.atlassian.com/x/x4UWN for more examples.
# Only use spaces to indent your .yml configuration.
# -----
# You can specify a custom docker image from Docker Hub as your build environment.
image: python:3.7.3

pipelines:
  branches:
    master:
      - step:
          caches:
            - pip
          script: # Modify the commands below to build your repository.
            - apt update && apt install -y zip
            - zip -r source.zip * -x "./.git/*"
            - mkdir to_s3
            - cp source.zip ./to_s3
            - pipe: atlassian/aws-s3-deploy:0.4.4
              variables:
                AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY_ID # Optional if already defined in the context.
                AWS_SECRET_ACCESS_KEY: $AWS_SECRET_ACCESS_KEY # Optional if already defined in the context.
                AWS_DEFAULT_REGION: $AWS_DEFAULT_REGION # Optional if already defined in the context.
                S3_BUCKET: 'bitbucket-sourcecode'
                LOCAL_PATH: './to_s3'
39c4-4b67ef4143a0.png)
```

- 処理は以下の通り
    - zipコマンドのインストール
    - .gitディレクトリ以外の内容をsource.zipに圧縮
    - to_s3ディレクトリにsource.zipをコピー
    - aws-s3-deployのパイプを以下の変数で実行
        - AWS_ACCESS_KEY_ID:環境変数AWS_ACCESS_KEY_IDから取得
        - AWS_SECRET_ACCESS_KEY:環境変数AWS_SECRET_ACCESS_KEYから取得
        - AWS_DEFAULT_REGION:環境変数AWS_DEFAULT_REGIONから取得
        - S3_BUCKET:前の手順で作成したS3バケット名
        - LOCAL_PATH: source.zipをコピーしたto_s3ディレクトリを指定

- 設定した内容がbitbucket-pipelines.ymlとしてmasterブランチに登録される
- masterブランチへの変更があったとみなされすぐに初回のPipelineが起動するが環境変数の設定をしていないため初回は以下のように失敗する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/bc70ff1a-cba1-2775-e8fc-c5bd1e8c8b9a.png)


### 環境変数へAWS認証情報を設定
- **Repository settings**メニューを選択し、**PIPELINES**セクションの**Repository variables**を選択
![screencapture-bitbucket-org-strada501gmail-codepipeline-integration-admin-2020-06-25-14_58_04.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/f7e977e7-53a6-f615-0391-5245d4df2c8d.jpeg)

- 以下の環境変数を作成する
    - securedをチェックすると値をmaskして保管できるのでシークレットアクセスキーはmaskすると良い

| 変数名 | 値 |
|:------|:---|
|AWS_ACCESS_KEY_ID| IAMユーザーの作成時にコピーした値 |
|AWS_SECRET_ACCESS_KEY| IAMユーザーの作成時にコピーした値 |
|AWS_DEFAULT_REGION| ap-northeast-1 |


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/14210b88-f876-a807-90b6-22b1ad661c77.png)

## 実行
### masterブランチへの変更
- Bitbucketのリポジトリの**ソース**メニューを選択し、右上のリポジトリのドロップダウンメニューから**ファイルを追加**を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/c146b3e1-4246-6790-95a6-d7c51006eea4.png)

- リポジトリ直下に任意のファイルを作成し**コミット**する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/cad8f364-0179-441a-67ae-9c0bfc397db9.png)
- コミットのコメントの内容を確認し**コミット**する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/16edd902-0a57-c478-59dc-57b594912fc7.png)
- masterへの変更によりPipelineが起動しているはずなので**Pipelines**メニューを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/24e7c2a6-406d-401d-6f65-ca995039fb19.png)
- \#2として今コミットした内容でパイプラインが起動しているのでStatusの**In Progress**を選択し、Pipelineを開く
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/8e67255e-d7f3-d9e5-2d02-1570de593281.png)
- S3へのアップロードが完了するとPipelineのステータスがSuccessになるのでそれまで待機
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/0c9d0dd9-848a-9afd-d8dc-a61d827961d4.png)
- Bitbucket Pipelineが完了するとAWS CodePipelineが起動しているはずなのでCodePipelineのコンソールを開くと初回は失敗していたSourceステージをクリアして先に進んだことが分かる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/55a1bfbe-4406-4e8b-79c5-2b19bbc94fa5.png)
- Buildステージの**詳細**をクリックするとCodeBuildのログが参照できる
- buildspecで仕込んだlsコマンドでbitbucketのファイルが連携できていることが確認できた
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/d0466abe-6287-a2f7-988f-840056ac228f.png)




# 所感
- Good
    - 環境に依存する設定ファイルの配布などをBitbucket側で吸収しておくことで、AWS CodePipelineは環境に依存する処理が不要になった
    - パイプラインのトリガーをbitbucket-pipelines.yamlのみで管理できるため見通しが良い
    - CodeBuildでBitbucketのファイルを取得する構成にする場合はパイプラインの数分CodeBuildのプロジェクトが必要なので、作成するリソースを少なくすることが出来た
- Bad
    - 時々BitbucketのIncidentによりWebhookが停止してしまい、Bitbucket Pipelineが起動しないことあるため運が悪いとデプロイが出来ない時間が発生する
    - 契約しているプランごとに使用できる時間に以下の制約がある
        - Premiumでも制約あり
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5259/8399bd4f-f1f1-06f3-a356-0bd2c7e8fd7f.png)