LoginSignup
8
2

More than 3 years have passed since last update.

codepipelineの承認をslack上で行う

Last updated at Posted at 2020-09-15

Codepipeline上で承認を行っていた

今回、社内から要望が上がってきたので備忘録として記載しておく。これまではCodepipelineで本番環境にDeployする前段階で以下のような「承認」を経てデプロイしていた。

ところがAWSのコンソールに入るまでに「二段階認証を行うのは面倒」ということでSlack上に「承認」を行うボタンを表示させ、そこをポチったら承認フローが完了するようにした。

ただし、slackのchannnelは「管理者」のみのプライベートにして無闇矢鱈に誰でもポチポチできるような仕組みは排除するようにした。

ScreenShot 2020-09-09 12.13.55.png

仕組みを考える

codepipleline上のApprovalから直接何かしらの手順で投げれれば良かったが投げれないため
① SNS通知させた。
②・③ その通知をlambdaのトリガーとしてslackに通知させる
④・⑤・⑥ slackで承認ボタンを押下した際にAPIGatewayにPOSTさせてlambdaを発動させえ最終的にCodepipelineの「承認」を実施する

slack承認.png

①のSNS通知から作る

まずは、SNSのトピックから作成した。ここは特に難しい部分はない。

ScreenShot 2020-09-09 12.38.01.png

次にCodepipelineの承認の設定。ここは作成したSNSトピックを指定するだけ。

ScreenShot 2020-09-09 13.24.01.png

Slackの設定を行う

最初に Slackアプリの設定でアプリの設定を行う。

新規のアプリの作成を行う。名前は適当にわかり易い名前で
ScreenShot 2020-09-09 16.52.51.png

このあたりをポチポチやっていく。どこのchannelにあてがうのか?とか設定があるが感覚でわかる。
ScreenShot 2020-09-09 16.56.16.png

次にTokenを取得する。

ScreenShot 2020-09-09 16.48.07.png

最後に赤枠の中の「Vertification Token」を取得
ScreenShot 2020-09-09 16.54.20.png

lambdaの作成

ようやく下準備ができたところでlambdaを作成する。

lambdaの環境変数を設定する。今回は、値の部分は暗号化をしていないが暗号化しておいたほうがベター。

ScreenShot 2020-09-15 11.24.05.png

自分のMACで実施した作業ログ(ライブラリの導入)

$ mkdir data
$ docker run -it -v $PWD/data:/data --rm python:3.7 /bin/bash
$ cd /data
$ pip3 install slackclient -t ./

スクリプト作成

今回はライブラリに記載のあった以下のapi_callを使おうかと思う。

import logging
import json
import os
import boto3
import traceback

from slack import WebClient
from slack.errors import SlackApiError


def lambda_handler(event, context):
    token = os.environ["SLACK_API_TOKEN"]
    channel_id = os.environ["channel_id"]

    client = WebClient(token=token)

    message = event["Records"][0]["Sns"]["Message"]
    data = json.loads(message)
    token = data["approval"]["token"]
    codepipeline_name = data["approval"]["pipelineName"]

    if codepipeline_name == "ml-age-api":
        msg_text = "```承認してもよろしいでしょうか?```"

    attachments_json = [
        {
            "fallback": "Upgrade your Slack client to use messages like these.",
            "color": "#258ab5",
            "attachment_type": "default",
            "callback_id": "the_greatest_war",
            "actions": [
                {
                    "name": "ok",
                    "text": "承認",
                    "value": token + ',' + codepipeline_name,
                    "style": "primary",
                    "type": "button",
                    "confirm": {
                        "title": "承認しますか?",
                        "text": "本当によろしいですか?",
                        "ok_text": "OK",
                        "dismiss_text": "Cancel"
                    }
                },
                {
                    "name": "cancel",
                    "text": "キャンセル",
                    "style": "danger",
                    "value": token + ',' + codepipeline_name,
                    "type": "button"
                }
            ]
        }
    ]

    try:
        response = client.chat_postMessage(
            channel=channel_id,
            text=msg_text,
            attachments=attachments_json
        )

        assert response["ok"]
    except Exception:
        print(traceback.format_exc())

ここで、ポイントとなる部分は下記の部分である。

"value": token + ',' + codepipeline_name,

「承認」をする際に必要な put_approval_result を見ていただくと分かる通り pipelineNametoken が必要になる。よってここで送ってしまえと思いこのようにした。

さらにここには理由があり複数のCodepipelineが動いているためlambdaをその都度作りたくなく出来れば使いまわしをしたかったためである。

承認用のlambdaの作成

上記は、slackにボタンを送信するためのlambdaで今回はAPIGatewayから「承認」または「却下」を受け取ってCodepipelineにわたすためのlambdaである。

# coding: utf-8
import json
import urllib.request
import boto3

stage_name = "Approval"
action_name = "Approval"

def lambda_handler(event, context):
    client = boto3.client("codepipeline")
    msg = event["body"]  # httpでpostされたpayloadの格納

    if not msg:
        return {
            'statusCode': 403,
            'headers': {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*'
            },
            'body': "Forbidden"
        }

    msg_decode = urllib.parse.unquote(msg)
    msg_replace = msg_decode.replace("payload=", '')
    new_msg = json.loads(msg_replace)

    token = new_msg["actions"][0]["value"]
    approval_data = token.split(',')
    pipeline_name = approval_data[1]
    token = approval_data[0]

    if new_msg["actions"][0]["name"] == "ok":
        client.put_approval_result(
            pipelineName=pipeline_name,
            stageName=stage_name,
            result={
                'summary': 'Approval',
                'status': 'Approved',
            },
            token=token,
            actionName=action_name
        )

        return {
            'statusCode': 200,
            'headers': {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*'
            },
            'body': "```承認されました```"
        }

    else:
        client.put_approval_result(
            pipelineName=pipeline_name,
            stageName=stage_name,
            result={
                'summary': 'Rejected',
                'status': 'Rejected',
            },
            token=token,
            actionName=action_name
        )

        return {
            'statusCode': 200,
            'headers': {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*'
            },
            'body': "```却下されました```"
        }

下記の2つはCodepipeline上のStageの名前とActionの名前

stage_name = "Approval"
action_name = "Approval"

この部分

ScreenShot 2020-09-15 15.01.42.png

次にこのlambdaに
「AWSCodePipelineApproverAccess」
を付与する

APIGatewayの設定

APIGatewayはPostメソッドで作成を行う。統合リクエストでLambda-Proxyを選択して先に作った。APIGatewayから受け取るLambdaに指定する。

ScreenShot 2020-09-15 11.18.26.png

あとは、APIをデプロイして終了。ここでデプロイしたURLを控える。

Slackの設定

httpsでAPIGatewayにPOSTするために以下の設定を行う。

ScreenShot 2020-09-15 11.14.13.png

実行してみる

ScreenShot 2020-09-15 15.05.58.png

こんな感じで出て「承認」と「キャンセル」をポチって確認終了。

投稿時のエラー

承認をslack投稿時に以下のようなエラーが出る場合がある

{ "ok": false, "error": "not_in_channel" }

この場合、slackの投稿したいchannelで

/invite @ボット名

とすればchannelにボットが追加されて投稿ができるようになる。

8
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
2