CodePipelineの状態をCodeStar Notificationsで通知

当記事は、半年以上前に投稿されたものです。そのため、古い技術や情報をもとに書かれている可能性があります。参照する際は十分に注意していただければです。

はじめに

CodePipeline で CI/CD を組んでいると各ステージの進捗状況や結果を CloudWatch と SNS を使って Slack 等に通知されている方もいるかと思いますが、これを CodeStar Notifications と言うサービスの通知機能を使えば簡単に代替することができます。

CloudWatch は イベント 100 万件あたり 1.00USD 掛かりますが、Notifications は無料になります。

まぁ、個人利用で 100 万イベントなんて私にはあり得ないので、イベントだろうが Notifications だろうが関係ありませんが。

今回は CodeStar Notifications + SNS + Lambda を使って CodePipeline の状態通知を Slack に送信する仕組みを作ってみます。

イメージ

今までは CloudWatch Events だったところが CodeStar Notifications に変わっています。

image_codepipeline_state_notification_mini

手順

Notifications と SNS を CloudFormation で作成

CodeStar Notifications

Code 兄弟(Commit、Build、Deploy、 Pipeline)のイベント通知ルールを作成する CloudFormation 例が下記になります。各 Code 兄弟の通知イベントは EventTypeIds から指定できます。各イベントの一覧はこちらの公式から確認できます。今回は CodePipeline の各ステージの実行イベントを通知しています。

  • Resource で対象となる Code 兄弟の ARN を指定
  • EventTypeIds で通知したいイベントを指定
  • Targets で Publish する SNS の ARN を指定
NotificationRule:
  Type: AWS::CodeStarNotifications::NotificationRule
  Properties:
    Name: sample-codepipeline-notification-rule
    Resource: !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:SamplePipelineName
    DetailType: FULL
    EventTypeIds:
      - codepipeline-pipeline-stage-execution-started
      - codepipeline-pipeline-stage-execution-succeeded
      - codepipeline-pipeline-stage-execution-resumed
      - codepipeline-pipeline-stage-execution-canceled
      - codepipeline-pipeline-stage-execution-failed
    Targets:
      - TargetType: SNS
        TargetAddress: !Ref SNSTopic

また、地味にハマるのが TargetType の書き方でした。公式の YAML の例が下記になるのですが、SNS, と ” , ” 区切りで SNS のトピックを指定していますが、” , ” があると問答無用で CloudFormation のデプロイが止まりました。CloudFormation あるあるでエラー内容がよく分からないためハマりましたが、”, ” を消すことでエラーが除かれました。

Targets:
  - TargetType: SNS,
    TargetAddress: 'Fn::Sub': 'arn:aws:sns:us-east-2:123456789012:MyNotificationTopic'

SNS Topic Policy

CodeStar Notifications が SNS トピックを発行できるように codestar-notifications.amazonaws.comPrincipal に追加して上げます。

SNSTopicPolicy:
  Type: AWS::SNS::TopicPolicy
  Properties:
    Topics:
      - !Ref SNSTopic
    PolicyDocument:
    Id: !Ref SNSTopic
    Version: 2008-10-17
    Statement:
      - Sid: "__default_statement_ID"
        Effect: Allow
        Principal:
          AWS: "*"
        Action:
          - SNS:GetTopicAttributes
          - SNS:SetTopicAttributes
          - SNS:AddPermission
          - SNS:RemovePermission
          - SNS:DeleteTopic
          - SNS:Subscribe
          - SNS:ListSubscriptionsByTopic
          - SNS:Publish
          - SNS:Receive
        Resource: !Ref SNSTopic
        Condition:
          StringEquals:
            AWS:SourceOwner: !Ref AWS::AccountId
      - Sid: "AWSCodeStarNotifications_publish"
        Effect: Allow
        Principal:
          Service:
            - codestar-notifications.amazonaws.com
        Action: SNS:Publish
        Resource: !Ref SNSTopic

CloudFormation 全体

あくまでサンプル程度です。Lambda や CodePipeline の ARN を指定する必要があるので注意下さい。

AWSTemplateFormatVersion: 2010-09-09

Parameters:
  SamplePipelineName:
    Type: String
    Default: "SamplePipeline"
  LambdaArn:
    Type: String

Resources:
  NotificationRule:
    Type: AWS::CodeStarNotifications::NotificationRule
    Properties:
      Name: sample-codepipeline-notification-rule
      Resource: !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${SamplePipelineName}
      DetailType: FULL
      EventTypeIds:
        - codepipeline-pipeline-stage-execution-started
        - codepipeline-pipeline-stage-execution-succeeded
        - codepipeline-pipeline-stage-execution-resumed
        - codepipeline-pipeline-stage-execution-canceled
        - codepipeline-pipeline-stage-execution-failed
      Targets:
        - TargetType: SNS
          TargetAddress: !Ref SNSTopic

  SNSTopic:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: sample-sns-topic
      Subscription:
        - Endpoint: !Ref LambdaArn
          Protocol: lambda

  SNSTopicPolicy:
    Type: AWS::SNS::TopicPolicy
    Properties:
      Topics:
        - !Ref SNSTopic
      PolicyDocument:
        Id: !Ref SNSTopic
        Version: 2008-10-17
        Statement:
          - Sid: "__default_statement_ID"
            Effect: Allow
            Principal:
              AWS: "*"
            Action:
              - SNS:GetTopicAttributes
              - SNS:SetTopicAttributes
              - SNS:AddPermission
              - SNS:RemovePermission
              - SNS:DeleteTopic
              - SNS:Subscribe
              - SNS:ListSubscriptionsByTopic
              - SNS:Publish
              - SNS:Receive
            Resource: !Ref SNSTopic
            Condition:
              StringEquals:
                AWS:SourceOwner: !Ref AWS::AccountId
          - Sid: "AWSCodeStarNotifications_publish"
            Effect: Allow
            Principal:
              Service:
                - codestar-notifications.amazonaws.com
            Action: SNS:Publish
            Resource: !Ref SNSTopic

Slack 通知用の Lambda

Slack API の Incoming Webhooks を使って Python で通知する Lambda になります。通知内容を分かりやすくするためステージの状態に応じて色や emoji を変更しています。

import json
import urllib.request

WEBHOOK_URL = "https://hooks.slack.com/services/***/***/******"


def lambda_handler(event, context):

    try:

        message = trim_event_message(event)

        state_color = get_stage_state_color(message["detail"]["state"])
        state_emoji = get_stage_state_emoji(message["detail"]["state"])

        tldr_message = f'@channel\n *{message["detail"]["stage"]}* stage is *{message["detail"]["state"]}*.  {state_emoji}'

        blocks = [
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": tldr_message
                },
            },
        ]

        attachments = [
            {
                "color": state_color,
                "blocks": [
                    {
                        "type": "section",
                        "text": {
                            "type": "mrkdwn",
                            "text": message["detailType"]
                        },
                    },
                    {
                        "type": "section",
                        "fields": [
                            {
                                "type": "mrkdwn",
                                "text": message["time"]
                            },
                            {
                                "type": "mrkdwn",
                                "text": message["region"]
                            },
                        ],
                    },
                ],
            }
        ]

        send_data = {
            "text": tldr_message,
            "blocks": blocks,
            "attachments": attachments
        }

        send_text = "payload=" + json.dumps(send_data)

        request = urllib.request.Request(
            WEBHOOK_URL,
            data=send_text.encode('utf-8'),
            method="POST"
        )

        with urllib.request.urlopen(request) as response:
            response_body = response.read().decode('utf-8')

    except Exception as e:

         print(e)
         raise e


    return {
        "statusCode": 200,
        "body": json.dumps({
            "message": "success",
        }),
    }


def trim_event_message(event):

    # eventにNoneが含まれる可能性あり、
    # そのため一旦全てをstringに変換
    # そして、dictに再変換
    event_dict = json.loads(json.dumps(event))

    sns_message = event_dict["Records"][0]["Sns"]["Message"]

    return json.loads(sns_message)


def get_stage_state_color(state):

    if state == "CANCELED":
        # yellow
        return "#DAA038"

    if state == "FAILED":
        # red
        return "#CF0100"

    if state == "STARTED":
        # blue
        return "#439FE0"

    if state == "SUCCEEDED":
        # green
        return "#34A64F"

    if state == "RESUMED":
        # grey
        return ""

def get_stage_state_emoji(state):

    if state == "CANCELED":
        return ":double_vertical_bar:"

    if state == "FAILED":
        return "<img class="emoji-icon" alt="emoji-ng" data-icon="emoji-ng" style="display: inline; margin: 0; margin-top: 0; position: relative; top: 5px; width: 20px" src="data:image/png;base64, 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" title="emoji-ng" />"

    if state == "STARTED":
        return "<img class="emoji-icon" alt="emoji-arrow_forward" data-icon="emoji-arrow_forward" style="display: inline; margin: 0; margin-top: 0; position: relative; top: 5px; width: 20px" src="data:image/png;base64, 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" title="emoji-arrow_forward" />"

    if state == "SUCCEEDED":
        return "<img class="emoji-icon" alt="emoji-ok" data-icon="emoji-ok" style="display: inline; margin: 0; margin-top: 0; position: relative; top: 5px; width: 20px" src="data:image/png;base64, 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" title="emoji-ok" />"

    if state == "RESUMED":
        return "<img class="emoji-icon" alt="emoji-repeat_one" data-icon="emoji-repeat_one" style="display: inline; margin: 0; margin-top: 0; position: relative; top: 5px; width: 20px" src="data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QA/wD/AP+gvaeTAAAAB3RJTUUH5AUOEQEFHasH/gAAB41JREFUWMOll1uMXVUdxn9r7XWuc791Lu3Q1lKKLYW2ClLDTVMwKSkvGl+MwqsPJiRqYkyIGBLDgw8EYnxFTSCSeMVCQEQkUmKv2DRth2mBmV5mOrfOOWfObe+9/n8f9j4HhrYG2p2sZGdf1vftb33/b/234VPHvu8/TWNlGazDBA5jLTdyqAjqY5CYfGcvL//6J6vum9bJN773BBrF2GKBW/f+gON/+EWHddks1hrQ64Q3IKISh+Ht3/xp9fQrzyG1OibjeO23T31M4MHHfoZ6T/euXVTee+9m6zL7jA12EwRr7A1KICqC93Mq/l2Jo5e7duw4Uz56FBME/P35n2P2PPYkKjGV2Rnbf9OGvS6bf6LY2b1r3ciQGxvqoVjIYo25TnClVg+5OF/i/Ox8XFspH43DxlNL0x+90jUyKsY6XLNRp3dwCJfN35cpdD4zOjq66et3fZF1w/3EosQiN+QBZy3OGs5fWnJvHjx118zMzDN9N21cyebzby0vzOOKnV3Uy+XufFfP4wODazbt2b2dQiHPxIVlojgBvwEHAJBxljW9nezZvZ1X3vabFub84/Vy6Wixs6vsXDaHDez2TKF4z+aNa8EETM9VrjrRZz00faelnQ8903MVhrrzbN64llKldI+Pm9tt4N5xgctgDJvzuUJ3sVBgoVwn9srnXXZVpRF5ci5ICueK+zBfrlMsFMjnCt1RvbrZBu4dZwOHosXAuSCMPMZ49Bqi/z9OxhrWDnbx4WyJrIWsC654JvKGMPIEzgUmCIo2cLjW1KJKM4oJgmAVAQPEPvm62Ms1/RAEhge/vIGtNw2w/+BZmrUmxZxb9bzB0IxiRFuLBE7Fg/eoKFHsiX3cfklVKdUiAudYO9hFX1f+mioEgaWvM89IX5HOQoY/vzPJYrlGdyHTls4AUZxg4T1qDE5VURRVj/cxURygCiLKSjPmjltGuX/7OoZ7i2TdZ8ukzWM9PPrQNv5yYJILl5YJrAUUYwzex6j6FFNxiE/z2hPFMZmUQC3yPLBzIw99aQOZ4POHYWfOMdqbZ3I6JOsCAmtSBWLUJ5gYjxNVVAUvQrUekgkczdhz6/o17Nm5vg2+WK4xNVuiEUZXN6Ex3Lp+iL7OPDOLK7x28AyHJmYYHuymXKmRsQZjDNV6iBdBVRC1OBVBVVERqrU6gTVkM467t46RyyROPj9f5k//OsmFhTIiV7dhYA1dxRyzixX2H5hg8sJl7t+1ib1f2cTLB97n8MlpUKhW63wS06kKKh5VIY49l0tVxoZ6WDvYDUC1EfLmkTOcODuDtYZsJpFTV/FQJLAcOjnN1Oxllsp1Ht59Cw/fvYUgMNy5ZZR/HztLvREiKZZKy4QpG5HkokexNonP988t8MKrR5k6t0RvbwdqDIuXqxirdHXkMKm9FSCKOXzyHCKaLGet0Q6z4b4OirmASjXGqCRYLQWkxUY8ogIoYRgyMTXH8387RG8uz4+/+wCjIz2IKhcvlXjp9fc4ce4SXR3ZlgBoWrYAYSScubBA5D0563CBJessIh6DfoyXKJAAa6qAsZbF0grP7z9IV0eBH37nATo7chw5fQ5RZfdtGxgb7uHp373J2fNzZJxdtRzeC9WVkI3DfeQySc6t1JtU6w1QSeUXRAUrnuTttBISLyj1RpNLi2UeuW8bvd0FXj9wiudefIuJqTkABnqKPHLvNlAlrDSJlxvtkW0K++7awr57trZJnfpwlqXSSkIg9UAL17UyIFFAAI+I0t/TwcbRPgB2376Bj2YWkU/0Bl8Y62dNbwff+toOtowPJSqoks1mGBzoxKV7waXFCm/85zRhGCXmTXHUe5S2CQURn64RqCjiPWEUA9DX08FAXwfllUabQDOMAGX9ugHG1w5ctTQvLpR44dWDTE7NJtuzkKx9uxIEJ5p6IHWnTQ21XKlyfPI848OJCtYrwScy4NjpaTLOknOOSrXe9oEXYblS5/RHs7x1eIKz5+dR1fYe0sZSAfVJDpCyQQRNayf2MfvfPs7YYA93bBln77234UXxXjhyaorXDpyg0Yh47sU32uXYIlCpNliu1Ihi3+4n29RbWCqoGlzrQluBwKTRChfnlvjV7//BV3fczObxYVSVialZ3v3vWZZKVYyB0lTtylhOo9nAFb2FSKsSFGMUJ6KoalNFRFWsilk10fxSmb/+8yiZ1FRR5NvZj8I1tylNofXTlwUVERVpJDngPeL9B+LjFe9979X6HlFohvEqYqrX16p67xEfr4j3H1oMzovH+/i499FhH4d7jDGJ/p+ev8VLr7NLTljj4xDvo8Oxj48HBmyzVqV/ZHzRR9GzcdiY9T5KTaKrh6RDr28kP0gRcdiY9VH0bP/I+GKzViVYv/Vu6vUqK/XGB1ln54FtBh3Q9HO1FZ83MEQ84kPisDEZNetPllfKf4wbdQlSo3Lfoz/CN2NKl5dsf9+anc5lvm2D4E5j7IAxxtzIj4mqqqosiveH4jh6aeny3LGevn4Jco63f/PLpCuunijTvbOH7rBHcoXikcXZmeMdXR2D1roCxhiu9+/MJgxE4nq1Ul0YGBmNusMesp0ZysdKAPwPLmYKfJJmwqQAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTktMDQtMDZUMjA6MjA6NDMrMDA6MDBiSxACAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE5LTA0LTA2VDIwOjIwOjQzKzAwOjAwExaovgAAAABJRU5ErkJggg==" title="emoji-repeat_one" />"

通知結果

もろもろをデプロイすると下記のような結果が Slack に返ってくると思います。工夫する点は多々あると思いますが、個人利用であればこれで十分かなと思います。

image_pipeline_state_notified_slack

おわりに

あると便利なこれらの通知を作りまくってると意外に費用が掛かったりするので、今回は無料の Notifications を使ってみました。

前々から思ってましたが Slack って便利ですね。

APIも用意されていて、AWS Chat と連携すれば本当に簡単にですが Slack 上で管理画面が作れるのではと思ったり思わなかったり、ちょとした夢が膨らんでいます。

参考文献


Canji

クラウド周りをちょこまかしたい注意散漫人間。個人開発を楽しんでいたあの頃。