サイトアイコン

toLog

CodePipelineの状態をCodeStar Notificationsで通知

  • 更新日:
  • 投稿日:
サムネイル

この記事は最終更新日から3年以上が経過しています。

はじめに

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 を指定
1NotificationRule:
2  Type: AWS::CodeStarNotifications::NotificationRule
3  Properties:
4    Name: sample-codepipeline-notification-rule
5    Resource: !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:SamplePipelineName
6    DetailType: FULL
7    EventTypeIds:
8      - codepipeline-pipeline-stage-execution-started
9      - codepipeline-pipeline-stage-execution-succeeded
10      - codepipeline-pipeline-stage-execution-resumed
11      - codepipeline-pipeline-stage-execution-canceled
12      - codepipeline-pipeline-stage-execution-failed
13    Targets:
14      - TargetType: SNS
15        TargetAddress: !Ref SNSTopic

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

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

SNS Topic Policy

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

1SNSTopicPolicy:
2  Type: AWS::SNS::TopicPolicy
3  Properties:
4    Topics:
5      - !Ref SNSTopic
6    PolicyDocument:
7    Id: !Ref SNSTopic
8    Version: 2008-10-17
9    Statement:
10      - Sid: "__default_statement_ID"
11        Effect: Allow
12        Principal:
13          AWS: "*"
14        Action:
15          - SNS:GetTopicAttributes
16          - SNS:SetTopicAttributes
17          - SNS:AddPermission
18          - SNS:RemovePermission
19          - SNS:DeleteTopic
20          - SNS:Subscribe
21          - SNS:ListSubscriptionsByTopic
22          - SNS:Publish
23          - SNS:Receive
24        Resource: !Ref SNSTopic
25        Condition:
26          StringEquals:
27            AWS:SourceOwner: !Ref AWS::AccountId
28      - Sid: "AWSCodeStarNotifications_publish"
29        Effect: Allow
30        Principal:
31          Service:
32            - codestar-notifications.amazonaws.com
33        Action: SNS:Publish
34        Resource: !Ref SNSTopic

CloudFormation 全体

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

1AWSTemplateFormatVersion: 2010-09-09
2
3Parameters:
4  SamplePipelineName:
5    Type: String
6    Default: "SamplePipeline"
7  LambdaArn:
8    Type: String
9
10Resources:
11  NotificationRule:
12    Type: AWS::CodeStarNotifications::NotificationRule
13    Properties:
14      Name: sample-codepipeline-notification-rule
15      Resource: !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${SamplePipelineName}
16      DetailType: FULL
17      EventTypeIds:
18        - codepipeline-pipeline-stage-execution-started
19        - codepipeline-pipeline-stage-execution-succeeded
20        - codepipeline-pipeline-stage-execution-resumed
21        - codepipeline-pipeline-stage-execution-canceled
22        - codepipeline-pipeline-stage-execution-failed
23      Targets:
24        - TargetType: SNS
25          TargetAddress: !Ref SNSTopic
26
27  SNSTopic:
28    Type: AWS::SNS::Topic
29    Properties:
30      TopicName: sample-sns-topic
31      Subscription:
32        - Endpoint: !Ref LambdaArn
33          Protocol: lambda
34
35  SNSTopicPolicy:
36    Type: AWS::SNS::TopicPolicy
37    Properties:
38      Topics:
39        - !Ref SNSTopic
40      PolicyDocument:
41        Id: !Ref SNSTopic
42        Version: 2008-10-17
43        Statement:
44          - Sid: "__default_statement_ID"
45            Effect: Allow
46            Principal:
47              AWS: "*"
48            Action:
49              - SNS:GetTopicAttributes
50              - SNS:SetTopicAttributes
51              - SNS:AddPermission
52              - SNS:RemovePermission
53              - SNS:DeleteTopic
54              - SNS:Subscribe
55              - SNS:ListSubscriptionsByTopic
56              - SNS:Publish
57              - SNS:Receive
58            Resource: !Ref SNSTopic
59            Condition:
60              StringEquals:
61                AWS:SourceOwner: !Ref AWS::AccountId
62          - Sid: "AWSCodeStarNotifications_publish"
63            Effect: Allow
64            Principal:
65              Service:
66                - codestar-notifications.amazonaws.com
67            Action: SNS:Publish
68            Resource: !Ref SNSTopic

Slack 通知用の Lambda

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

1import json
2import urllib.request
3
4WEBHOOK_URL = "https://hooks.slack.com/services/***/***/******"
5
6
7def lambda_handler(event, context):
8
9    try:
10
11        message = trim_event_message(event)
12
13        state_color = get_stage_state_color(message["detail"]["state"])
14        state_emoji = get_stage_state_emoji(message["detail"]["state"])
15
16        tldr_message = f'@channel\n *{message["detail"]["stage"]}* stage is *{message["detail"]["state"]}*.  {state_emoji}'
17
18        blocks = [
19            {
20                "type": "section",
21                "text": {
22                    "type": "mrkdwn",
23                    "text": tldr_message
24                },
25            },
26        ]
27
28        attachments = [
29            {
30                "color": state_color,
31                "blocks": [
32                    {
33                        "type": "section",
34                        "text": {
35                            "type": "mrkdwn",
36                            "text": message["detailType"]
37                        },
38                    },
39                    {
40                        "type": "section",
41                        "fields": [
42                            {
43                                "type": "mrkdwn",
44                                "text": message["time"]
45                            },
46                            {
47                                "type": "mrkdwn",
48                                "text": message["region"]
49                            },
50                        ],
51                    },
52                ],
53            }
54        ]
55
56        send_data = {
57            "text": tldr_message,
58            "blocks": blocks,
59            "attachments": attachments
60        }
61
62        send_text = "payload=" + json.dumps(send_data)
63
64        request = urllib.request.Request(
65            WEBHOOK_URL,
66            data=send_text.encode('utf-8'),
67            method="POST"
68        )
69
70        with urllib.request.urlopen(request) as response:
71            response_body = response.read().decode('utf-8')
72
73    except Exception as e:
74
75         print(e)
76         raise e
77
78
79    return {
80        "statusCode": 200,
81        "body": json.dumps({
82            "message": "success",
83        }),
84    }
85
86
87def trim_event_message(event):
88
89    # eventにNoneが含まれる可能性あり、
90    # そのため一旦全てをstringに変換
91    # そして、dictに再変換
92    event_dict = json.loads(json.dumps(event))
93
94    sns_message = event_dict["Records"][0]["Sns"]["Message"]
95
96    return json.loads(sns_message)
97
98
99def get_stage_state_color(state):
100
101    if state == "CANCELED":
102        # yellow
103        return "#DAA038"
104
105    if state == "FAILED":
106        # red
107        return "#CF0100"
108
109    if state == "STARTED":
110        # blue
111        return "#439FE0"
112
113    if state == "SUCCEEDED":
114        # green
115        return "#34A64F"
116
117    if state == "RESUMED":
118        # grey
119        return ""
120
121def get_stage_state_emoji(state):
122
123    if state == "CANCELED":
124        return ":double_vertical_bar:"
125
126    if state == "FAILED":
127        return ":ng:"
128
129    if state == "STARTED":
130        return ":arrow_forward:"
131
132    if state == "SUCCEEDED":
133        return ":ok:"
134
135    if state == "RESUMED":
136        return ":repeat_one:"

通知結果

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

image_pipeline_state_notified_slack

おわりに

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

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

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

参考文献


プロフィール画像

canji

とにかく私的にサービスを作りたい発作を起こしている。お腹はペコペコ。

  • toLog Tools icon
  • dots icon