AWS CodePipeline Cross-Account Lambda Invoke Explained

You have two AWS accounts:

Your goal is:

"CodePipeline in tools-account should run a Lambda function inside dev-account, wait for the Lambda to finish, and then either continue or stop the pipeline."

Since the Lambda is in another AWS account, CodePipeline cannot directly control it automatically. AWS solves this using:

The important part is:

The Lambda must explicitly tell CodePipeline whether the job succeeded or failed.

Without that callback, the pipeline does not properly know the final result.


Step 1 — Lambda Exists in Dev Account

Inside the dev-account, you create a Lambda function.

Example:

import boto3

codepipeline = boto3.client('codepipeline')

def lambda_handler(event, context):

    job_id = event['CodePipeline.job']['id']

    try:
        print("Running validation")

        # your deployment validation logic here

        codepipeline.put_job_success_result(
            jobId=job_id
        )

    except Exception as e:

        codepipeline.put_job_failure_result(
            jobId=job_id,
            failureDetails={
                'type': 'JobFailed',
                'message': str(e)
            }
        )

        raise

This Lambda can do things like:


Why put_job_success_result() Is Important

This is the key idea.

CodePipeline is not sitting inside the dev-account watching the Lambda directly.

Instead:

  1. Pipeline sends a job
  2. Lambda receives the job
  3. Lambda must call back and say:

That callback is:

codepipeline.put_job_success_result()

or

codepipeline.put_job_failure_result()

Without this callback:


Step 2 — Create Cross-Account Role in Dev Account

Now create an IAM Role inside the dev-account.

This role says:

"I trust the tools-account pipeline."

This is called a trust policy.

Example:

DevAccountCodePipelineRole:
  Type: AWS::IAM::Role
  Properties:
    RoleName: DevAccount-CodePipeline-InvokeRole

    AssumeRolePolicyDocument:
      Version: '2012-10-17'
      Statement:
        - Effect: Allow
          Principal:
            AWS: arn:aws:iam::<TOOLS_ACCOUNT_ID>:root
          Action: sts:AssumeRole

This allows tools-account to temporarily become this role.


Step 3 — Give the Role Permission to Invoke Lambda

Now attach permissions to that role:

Policies:
  - PolicyName: InvokeLambdaPolicy
    PolicyDocument:
      Version: '2012-10-17'
      Statement:
        - Effect: Allow
          Action:
            - lambda:InvokeFunction
          Resource:
            - arn:aws:lambda:us-east-1:<DEV_ACCOUNT_ID>:function:MyFunction

This says:

"This role can invoke the Lambda."

Step 4 — Allow Lambda to Talk Back to CodePipeline

The Lambda also needs permission to send success/failure results back to CodePipeline.

Example:

- Effect: Allow
  Action:
    - codepipeline:PutJobSuccessResult
    - codepipeline:PutJobFailureResult
  Resource: "*"

Without this:


Step 5 — Allow CodePipeline to Assume the Role

Now in the tools-account, the pipeline execution role needs permission to assume the dev-account role.

Example:

- Effect: Allow
  Action:
    - sts:AssumeRole
  Resource:
    - arn:aws:iam::<DEV_ACCOUNT_ID>:role/DevAccount-CodePipeline-InvokeRole

This is AWS security working both ways.

The dev-account must trust the tools-account, and the tools-account must also be allowed to assume the role.


Step 6 — Configure CodePipeline

Inside the pipeline stage:

- Name: InvokeValidationLambda
  Actions:
    - Name: RunValidation
      ActionTypeId:
        Category: Invoke
        Owner: AWS
        Provider: Lambda
        Version: '1'

      Configuration:
        FunctionName: MyFunction

      RoleArn: arn:aws:iam::<DEV_ACCOUNT_ID>:role/DevAccount-CodePipeline-InvokeRole

The important part is:

RoleArn:

This tells AWS:

"Before invoking Lambda, temporarily become this role in the dev-account."

What Actually Happens During Execution

When pipeline reaches this stage:

  1. CodePipeline starts the job
  2. It assumes the cross-account role
  3. That role invokes the Lambda
  4. Lambda receives a CodePipeline job event
  5. Lambda performs validation/checks
  6. Lambda explicitly reports:
  1. Pipeline either continues or terminates

If Lambda Succeeds

Example:

codepipeline.put_job_success_result(
    jobId=job_id
)

Pipeline stage becomes successful.

Deployment continues.


If Lambda Fails

Example:

codepipeline.put_job_failure_result(
    jobId=job_id,
    failureDetails={
        'type': 'JobFailed',
        'message': 'Validation failed'
    }
)

Pipeline immediately stops.

Deployment terminates.