CodePipeline で複数名の承認を必要とするパイプラインの実装方法

CodePipelineで二名以上の承認を必要とするパイプラインの実装方法をご紹介
2020.03.02

前回、本番環境向けの CI/CD パイプラインとして承認プロセスを設けたパターンをご紹介しました。

環境によってはこれで十分かもしれませんが、より厳格に運用されている環境においては「2 名以上の承認が必要」といった要件もあるのではないでしょうか。今回は CodePipeline で 2 名以上の承認を必要とするパイプラインの実装方法をご紹介したいと思います。

イメージ

こちらの実装を行うにあたり、以下のサイトを参考にさせていただきました。

やってみる

今回の構成においてポイントとなるのは以下 2 点の実装です。

  • 承認者 A (承認グループ A) が承認プロセス B の承認を行えないこと(その逆も然り)
  • 承認者が同一でないこと

それでは実装していきましょう。

承認プロセスA/Bの作成

パイプラインに Approve ステージを作成し、2 つのアクション(承認プロセス)を追加します。今回は以下のように、2つの manual approve アクションを追加しました。

  • ApproveOne
  • ApproveTwo

IAM ポリシーの作成

次に先述の承認処理を行うための IAM ポリシーを以下のように 2 つ作成します。ポイントは、双方の PutApprovalResult を行えないように一方に Deny をいれています。今回はその他に PowerUserAccess ポリシーをアタッチしたロールで検証していますが、最小権限で運用される場合は公式ガイドを参考に、GetPipeline,ListPipelines などの権限は別途必要です。

承認グループAのポリシー

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "codepipeline:PutApprovalResult",
            "Resource": "arn:aws:codepipeline:[region]:[account]:*/*/ApproveOne",
            "Effect": "Allow"
        },
        {
            "Action": "codepipeline:PutApprovalResult",
            "Resource": "arn:aws:codepipeline:[region]:[account]:*/*/ApproveTwo",
            "Effect": "Deny"
        }
    ]
}

承認グループBのポリシー

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "codepipeline:PutApprovalResult",
            "Resource": "arn:aws:codepipeline:[region]:[account]:*/*/ApproveOne",
            "Effect": "Deny"
        },
        {
            "Action": "codepipeline:PutApprovalResult",
            "Resource": "arn:aws:codepipeline:[region]:[account]:*/*/ApproveTwo",
            "Effect": "Allow"
        }
    ]
}

*/*/ApproveOne の部分ですが、最初のアスタリスクは「パイプライン名」、2つ目のアスタリスクは「ステージ名」になります。ワイルドカードで定義したくない場合にはそれぞれ任意の値を埋めてください。

これらのポリシーを IAM ロールやグループなどにアタッチし、ApproveOne を承認できるメンバー、ApproveTwo を承認できるメンバーを分けます。

承認権限の確認

承認権限が正しく動作していることを確認します。期待する動作は以下のイメージです。

それでは承認グループAのアカウントで、ApproveTwoReview を実行してみます。

承認グループAでは、ApproveTwo を承認できないことが確認できました。

「よし、出来た!」

と、思うかもしれませんがちょっと待ってください。もし、管理者など双方の承認アクションに対しても PutApprovalResult 権限をもったアカウトが Review をポチッ!ポチッ!と押したらどうでしょうか。

はい、ご覧のとおり承認出来てしまいます。これでは「複数人の承認が必要」という要件を満たすには心もとないですね。

ですので、次に Lambda を使って承認者の差異性を確認するプロセスを追加しましょう。

Lambda で承認者の差異性を確認

この問題に対処するには Lambda アクションを追加し、ApproveOneApproveTwo が異なるユーザによって実行されたこと(差異性)をチェックするプロセスを追加します。イメージは下記のとおりです。

管理者はいずれの承認プロセスも Review 出来てしまいますが、後続処理で Lambda がパイプラインイベントをチェックし、同一ユーザーにて実行されている場合はエラーを返してパイプラインを停止します。

Lambda のコードは先述のサイトを参考にしています。Lambda 関数は Python 3.7 で作成しました。

ApproveChecker

from __future__ import print_function
import boto3
import traceback

code_pipeline = boto3.client('codepipeline')

def put_job_success(job, message):
    code_pipeline.put_job_success_result(jobId=job)
  
def put_job_failure(job, message):
    code_pipeline.put_job_failure_result(jobId=job, failureDetails={'message': message, 'type': 'JobFailed'})

def lambda_handler(event, context):
    try:
        job_id = event['CodePipeline.job']['id']
        state = code_pipeline.get_pipeline_state(name='YOUR_PIPELINE_NAME_HERE')
        stage = state['stageStates'][2]
        first_approver = stage['actionStates'][0]['latestExecution']['lastUpdatedBy']
        second_approver = stage['actionStates'][1]['latestExecution']['lastUpdatedBy']

        if first_approver == second_approver:
            put_job_failure(job_id, "ERROR: This pipeline does not permit the same approver ({}) to provide two approvals!".format(first_approver))
        else:
            put_job_success(job_id, "Two different approvers signed off on this change: {} , {}".format(first_approver, second_approver))


    except Exception as e:
        # If any other exceptions which we didn't expect are raised
        # then fail the job and log the exception message.
        traceback.print_exc()
        put_job_failure(job_id, 'Function exception: ' + str(e))
        
    return
  • 16行目の YOUR_PIPELINE_NAME_HERE はご利用のパイプライン名に置き換え
  • 17行目の ['stageStates'][2] はご利用のパイプラインのステージ数で指定を変える
    • 検証環境のパイプラインは以下の 5 ステージですので、[2] は 3 番目の Approve ステージ指定になります
      • Source ステージ
      • Build ステージ
      • Approve ステージ
      • ApproveCheck ステージ
      • Deploy ステージ

ApproveCheck ステージの追加

パイプラインを編集し、Approve ステージの後段に ApproveCheck ステージを追加。Add action で以下のように ApproveCheck アクションを追加します。

それでは、動作確認してみましょう。

同一ユーザによる承認動作の確認

まずは管理者(同一ユーザー)で双方の Rveiew を実施してみます。結果は以下のとおりです。

Review は出来てしまうので、Approve ステージは通過してしまいますが、ApproveCheck にて同一ユーザーによる承認であることがチェックされ、パイプラインはエラーで停止していますね!

異なるユーザーによる承認動作の確認

次に、承認者A、承認者B の異なるユーザーで、それぞれ許可されている側の Review を実施してみます。結果は以下のとおりです。

今度は承認ユーザーが異なっているため、ApproveCheck ステージを通過することができましたね!

検証は以上です!

さいごに

今回は CodePipeline で 2 名以上の承認を必要とするパイプラインの実装方法をご紹介しました。manual approve を 2 つ並べ、IAM ポリシーでそれぞれの権限をコントロールすることで、とりあえずは実装できるのですが、より厳格に同一ユーザーではないことを確認するためには Lambda を使って承認ユーザーをチェックするほうが良いでしょう。

以上!大阪オフィスの丸毛(@marumo1981)でした!

リファレンス