[PYTHON] [AWS] Addressing an issue where Lambda called from CodePipeline is still in progress

problem

** The indicator is spinning all the time **

スクリーンショット 2019-11-08 23.47.25.png (Stay for 10 minutes) I'm crazy! !! !!

I stumbled when I wanted to call Lambda to clear the cache of CloudFront after the deployment to S3 was completed, so I summarized it.

Cause

** Because codepipeline: PutJobSuccessResult (*) is not returned **

  • In case of failure, codepipeline: PutJobFailedResult

What is PutJobSuccessResult?

PutJobSuccessResult Required to report the success of the job returned by the job worker to the pipeline. Used only for custom actions. Quoted from https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/permissions-reference.html

images.png

In summary

CodePipeline only receives the results of ** custom actions (such as Lambda) that it calls in a fixed format **, PutJobSuccessResult and PutJobFailedResult. ** Even if I return it in any other format, it will be ignored, so the indicator is spinning around ** I haven't received any results from Lambda for a long time.

When PutJobSuccessResult is returned

スクリーンショット 2019-11-08 23.56.48.png

When PutJobFailedResult is returned

スクリーンショット 2019-11-09 0.09.46.png スクリーンショット 2019-11-09 0.09.58.png

I did it. </ font>

Correspondence

** OK by writing code that returns PutJobSuccessResult and PutJobFailedResult on Lambda. ** **

How to actually return PutJobSuccessResult, PutJobFailedResult in Lambda by language.

Python

Lambda runtime_Python3.7


import json
import boto3

def lambda_handler(event, context):
    codepipeline = boto3.client('codepipeline')
    
    #Results on CodePipeline(success)return it
    codepipeline.put_job_success_result(jobId = event['CodePipeline.job']['id'])
    
    #Results on CodePipeline(Failure)return it
    # codepipeline.put_job_failure_result(
    #     jobId = event['CodePipeline.job']['id'],
    #     failureDetails={
    #         'type': 'JobFailed',
    #         'message': 'Failed test.'
    #     }
    # )
    
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

Ruby

Lambda runtime_Ruby2.5


require 'json'
require 'aws-sdk'

def lambda_handler(event:, context:)
    codepipeline = Aws::CodePipeline::Client.new()
    
    #Results on CodePipeline(success)return it
    codepipeline.put_job_success_result({job_id: event["CodePipeline.job"]['id']})
    
    #Results on CodePipeline(Failure)return it
    # codepipeline.put_job_failure_result({
    #    job_id: event["CodePipeline.job"]['id'], 
    #    failure_details: {
    #        type: "JobFailed", # accepts {JobFailed, ConfigurationError, PermissionError, RevisionOutOfSync, RevisionUnavailable, SystemUnavailable}
    #        message: "Failed test."
    #    }
    # })
    
    { statusCode: 200, body: JSON.generate('Hello from Lambda!') }
end

Node.js

javascript:Lambda runtime_Node.js10.x


var AWS = require('aws-sdk');

exports.handler = function(event, context) {
    var codepipeline = new AWS.CodePipeline();
    
    //Results on CodePipeline(success)return it
    codepipeline.putJobSuccessResult({jobId: event["CodePipeline.job"].id}, function(err, data) {
        if(err) {
            context.fail(err);
        } else {
            context.succeed('Success test.');
        }
    });
    
    //Results on CodePipeline(Failure)return it
    // var params = {
    //     jobId: event["CodePipeline.job"].id,
    //     failureDetails: {
    //         message: JSON.stringify('Failed test.'),
    //         type: 'JobFailed',
    //     }
    // };
    // codepipeline.putJobFailureResult(params, function(err, data) {
    //     context.fail('Failed test.');
    // });
    
    const response = {
        statusCode: 200,
        body: JSON.stringify('Hello from Lambda!'),
    };
    return response;
};

I don't usually write Node.js, so I don't know if this is the minimum code required. </ Font>

Even though I copied the code, it's still spinning!

First, let's take a look at Lambda's CloudWatch log.

AccessDeniedException

As a result of running Lambda, CloudWatch Log shows something like this.

{
   "errorType": "AccessDeniedException",
   "errorMessage": "User: arn:aws:sts::1111111111111:assumed-role/codetest-role-xxxxxxxxx/codetest is not authorized to perform: codepipeline:PutJobSuccessResult",
    ...etc
}

You also have permission to return results from Lambda to CodePipeline ...

You need to add the following IAM policy to the IAM role assigned to Lambda.

IAM policy


{
  "Version": "2012-10-17", 
  "Statement": [
    {
      "Action": [ 
        "logs:*"
      ],
      "Effect": "Allow", 
      "Resource": "arn:aws:logs:*:*:*"
    },
    {
      "Action": [
        "codepipeline:PutJobSuccessResult",
        "codepipeline:PutJobFailureResult"
        ],
        "Effect": "Allow",
        "Resource": "*"
     }
  ]
} 

Please let me know if you make any mistakes.