Codepipelineのデプロイが完了したらどのバージョンをデプロイしたのかをCFn一撃通知してみる

梶原大使at福岡です。

Codepipelineのデプロイが完了したらCodepipelineがどのバージョンをデプロイしたのかSNS通知(メール)してみます。

さっそくですが、 つ CloudFormationテンプレート

ってことで、CFn一撃化して必要なのはメアドだけにしておきました。 テンプレートも載せていますのでカスタマイズはご自由に。

構成図はこんな感じです。

いるもの

AWSアカウント(各種権限)

各種設定

CloudWatch Events Rule

  • ソース: CodePipeline
  • イベントタイプ:CodePipelineの状態変更
  • 状態: 成功 or 失敗
  CodePipelineCwEventsRule: 
    Type: AWS::Events::Rule
    Properties: 
      EventPattern: 
        source:
          - aws.codepipeline
        detail-type:
          - CodePipeline Pipeline Execution State Change
        detail:
          state:
            - SUCCEEDED
            - FAILED
      State: ENABLED
      Targets: 
        - Id: "CodePipelineGetExecutionLambda"
          Arn: !GetAtt CodePipelineGetExecutionFunction.Arn

成功、失敗以外に欲しい状態があれば、stateに追加してください。

Lambdaでやっている事

  1. イベント通知からパイプライン名、実行IDを取得
  2. デプロイの詳細情報を取得(getPipelineExecution)
  3. デプロイの詳細情報をSNS通知

特に迷うところはないかと。

ちょっと注意

コンソールで設定した場合はよしなにやってくれるのですが CloudFormationの場合はEventsからLambdaを呼び出すので Lambda側に許可設定を実施します

  LambdaInvokePermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !GetAtt CodePipelineGetExecutionFunction.Arn
      Action: lambda:InvokeFunction
      Principal: events.amazonaws.com
      SourceArn: !GetAtt CodePipelineCwEventsRule.Arn

やってみる

CloudFormationの実行

最後のテンプレートをCloudFormationで実行してください。

CodepipeLineの実行

Codepipelineの実行が成功/失敗すると下記のようなメールが通知されます。 まんま、getPipelineExecutionの中身になりますが

見るべきところは

  • pipelineName:パイプライン名
  • status: デプロイ結果正常終了時はSucceeded, 失敗時はFailed
  • revisionId: githbuやCodecommitの場合はコミットのハッシュ値
  • revisionSummary: githubやCodecommitの場合はコミット時のコメント
  • revisionUrl: 何気にコミット時の差異がみれるので便利かも?  (前回のデプロイの差異とかではなくあくまでコミット時の差分です)

になります。

件名:CodePipeline Execution Succeeded

{
  "pipelineExecution": {
    "pipelineName": "temp-pipeline-hogehoge",
    "pipelineVersion": 1,
    "pipelineExecutionId": "XXXX-YYYY-ZZZ-1111-XXXXX",
    "status": "Succeeded",
    "artifactRevisions": [
      {
        "name": "SourceArtifact",
        "revisionId": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
        "revisionSummary": "update hoge\n",
        "revisionUrl": "https://ap-northeast-1.console.aws.amazon.com/codecommit/home#/repository/repo/commit/XXXXXXXXXXXXXXXXX"
      }
    ]
  }
}

まとめ

Pipeline Execution State Changeの通知だけでも失敗、成功は判別できますが どのリビジョンがデプロイされたか知りたかったので作ってみました。 どなたかの役に立てば幸いです。

テンプレート全体

AWSTemplateFormatVersion: '2010-09-09'
Metadata: 
  AWS::CloudFormation::Interface: 
    ParameterGroups: 
      - Label: 
          default: "SNS Settings"
        Parameters: 
          - EMailAddress
    ParameterLabels: 
      EMailAddress: 
        default: "E-Mail Address"
Parameters:
  EMailAddress:
    Type: String
    Default: hogefuga@exsample.com

Resources:

  EMailSNSTopic:
    Type: AWS::SNS::Topic
    Properties:
      Subscription:
      - Endpoint: !Ref EMailAddress
        Protocol: email

  CodePipelineCwEventsRule: 
    Type: AWS::Events::Rule
    Properties: 
      EventPattern: 
        source:
          - aws.codepipeline
        detail-type:
          - CodePipeline Pipeline Execution State Change
        detail:
          state:
            - SUCCEEDED
            - FAILED
      State: ENABLED
      Targets: 
        - Id: "CodePipelineGetExecutionLambda"
          Arn: !GetAtt CodePipelineGetExecutionFunction.Arn

  CodePipelineGetExecutionFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Environment:
        Variables:
          SNS_TOPIC_ARN: !Ref EMailSNSTopic
      Code:
        ZipFile: !Sub |
          var AWS = require('aws-sdk');
          var SNS_TOPIC_ARN = process.env.SNS_TOPIC_ARN;
          exports.handler = async(event) => {
              // console.log(JSON.stringify(event, null, 4));
              var codepipeline = new AWS.CodePipeline();
              var sns = new AWS.SNS();
              var params = {
                  pipelineExecutionId: event.detail['execution-id'],
                  pipelineName: event.detail.pipeline
              };
              var pipelineExecution = await codepipeline.getPipelineExecution(params).promise();
              // console.log(JSON.stringify(pipelineExecution, null, 4));
              params = {
                  Message: JSON.stringify(pipelineExecution, null, 4),
                  Subject: 'CodePipeline Execution ' + pipelineExecution.pipelineExecution.status,
                  TopicArn: SNS_TOPIC_ARN
              };
              var result = await sns.publish(params).promise();
              return result;
          };
      Runtime: nodejs8.10
      Timeout: 30

  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - lambda.amazonaws.com
          Action:
          - sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
        - arn:aws:iam::aws:policy/AWSCodePipelineReadOnlyAccess
      Path: "/"
      Policies:
        - PolicyName: LambdaExecutionRole-SnsPublishPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
            - Effect: Allow
              Action:
                - sns:Publish
              Resource: arn:aws:sns:*:*:*

  LambdaInvokePermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !GetAtt CodePipelineGetExecutionFunction.Arn
      Action: lambda:InvokeFunction
      Principal: events.amazonaws.com
      SourceArn: !GetAtt CodePipelineCwEventsRule.Arn