CodePipeline で複数名の承認を必要とするパイプラインの実装方法
前回、本番環境向けの 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
などの権限は別途必要です。
{ "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" } ] }
{ "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のアカウントで、ApproveTwo
の Review
を実行してみます。
承認グループAでは、ApproveTwo
を承認できないことが確認できました。
「よし、出来た!」
と、思うかもしれませんがちょっと待ってください。もし、管理者など双方の承認アクションに対しても PutApprovalResult
権限をもったアカウトが Review をポチッ!ポチッ!と押したらどうでしょうか。
はい、ご覧のとおり承認出来てしまいます。これでは「複数人の承認が必要」という要件を満たすには心もとないですね。
ですので、次に Lambda を使って承認者の差異性を確認するプロセスを追加しましょう。
Lambda で承認者の差異性を確認
この問題に対処するには Lambda アクションを追加し、ApproveOne
と ApproveTwo
が異なるユーザによって実行されたこと(差異性)をチェックするプロセスを追加します。イメージは下記のとおりです。
管理者はいずれの承認プロセスも Review 出来てしまいますが、後続処理で Lambda がパイプラインイベントをチェックし、同一ユーザーにて実行されている場合はエラーを返してパイプラインを停止します。
Lambda のコードは先述のサイトを参考にしています。Lambda 関数は Python 3.7
で作成しました。
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 ステージ
- 検証環境のパイプラインは以下の 5 ステージですので、
ApproveCheck ステージの追加
パイプラインを編集し、Approve ステージの後段に ApproveCheck ステージを追加。Add action
で以下のように ApproveCheck
アクションを追加します。
それでは、動作確認してみましょう。
同一ユーザによる承認動作の確認
まずは管理者(同一ユーザー)で双方の Rveiew を実施してみます。結果は以下のとおりです。
Review は出来てしまうので、Approve ステージは通過してしまいますが、ApproveCheck にて同一ユーザーによる承認であることがチェックされ、パイプラインはエラーで停止していますね!
異なるユーザーによる承認動作の確認
次に、承認者A、承認者B の異なるユーザーで、それぞれ許可されている側の Review を実施してみます。結果は以下のとおりです。
今度は承認ユーザーが異なっているため、ApproveCheck ステージを通過することができましたね!
検証は以上です!
さいごに
今回は CodePipeline で 2 名以上の承認を必要とするパイプラインの実装方法をご紹介しました。manual approve
を 2 つ並べ、IAM ポリシーでそれぞれの権限をコントロールすることで、とりあえずは実装できるのですが、より厳格に同一ユーザーではないことを確認するためには Lambda を使って承認ユーザーをチェックするほうが良いでしょう。
以上!大阪オフィスの丸毛(@marumo1981)でした!