Create a Code Pipeline for a Lambda Function

In my cloud computing class we have a project that requires us to set up a code pipeline for various lambda functions in AWS. This is an extremely complicated process, especially when the lambda function is complicated. So I decided to create a bare bones lambda function and see if I can get that to work.

Here are the steps I took...

Create a Bucket

The CodePipeline will put the build files (the 'artifact') into a folder in this bucket (the folder will get created during the build process).

Create a Lambda Functon

In the Lambda dashboard create a very simple function:

I did not even change the starter code for the function. Just leave it as it.

Click the Download button and download both the code .zip file and the SAM file. This downloaded a testFunction.zip file that had these contents in it:

- template.yml
- src
  - lambda_function.py

Extract the zip file and add a file (in the testFunction folder) named buildspec.yml. Then put this code into the file (and make the changes that I put in the comments):

version: 0.2

phases:
  install:
    runtime-versions:
      python: 3.12
    commands:
      - pip install aws-sam-cli
  build:
    commands:
      - sam build --template template.yml
      - sam package --s3-bucket code-bucket-05-04-2025 --s3-prefix testFunction --output-template-file packaged.yaml
      # For the above command, replace 'code-bucket-05-04-2025' with your actual S3 bucket name
      # The --s3-prefix option is a folder that will get created in the bucket which stores your artifact
      # The --output-template-file option specifies the name of the output file that will be created after packaging
  post_build:
    commands:
      - sam deploy --template-file packaged.yaml --stack-name testFunctionStack --capabilities CAPABILITY_IAM CAPABILITY_AUTO_EXPAND
      # For the above command, the template file is the one that will be created in the build phase
      # The stack name is the name of the CloudFormation stack that will be created during deployment
      # The --capabilities option is required to allow the stack to create IAM roles and policies
      # The --capabilities CAPABILITY_AUTO_EXPAND option is used to allow the stack to create nested stacks (not sure I need this)
artifacts:
  type: zip
  files:
    - src/packaged.yaml
  # The files section specifies the files that will be included in the output artifact
  # In this case, it includes the packaged.yaml file that was created in the build phase

Create a Git repository

Create a Git repository in the testFunction folder and push it to GitHub. Make it a private repository.

Create a Project in CodeBuild

  1. Set the Project Name to testFunction-project
  2. Set the Project Type to Default Project
  3. Set the Source Provider to GitHub
    • Note that in a previous project I already established a connection to my GitHub account
    • So I didn't have to go through the steps of connecting
  4. Select Repository in My GitHub Account and select the repository you created earlier
  5. In the Environment section:
    • Set the Compute to Lambda
    • The OS defaulted to Amazon Linux (the only option)
    • Set the Runtime to Python
    • Set the Image to the standard one for Python3.12
    • Select New Service Role
    • The name of the role defaulted to codebuild-testFunction-project-service-role
    • Note that later in the process I had to add a lot of permissions to the role in order to get everything to work!
  6. In the Buildspec section:
    • Select Use a buildspec file
    • Leave the default Buildspec Name as buildspec.yml
  7. In the Artifacts section:
    • Set the Type to S3
    • Set the bucket to the name of the bucket you created in the first step (my bucket name is code-bucket-05-04-2025)
    • For the Name, enter testFunction
      • This will be the name of the folder, inside the bucket, that the artifact is put into
      • The folder will get created during the build process
      • Note that it should match the '--s3-prefix' in the buildspec.yml file
  8. Click Create Build Project

A note about the service role that was created during the CodeBuild project setup I looked at the role in IAM (codebuild-testFunction-project-service-role): A policy was created for the role, and it's called CodeBuildBasePolicy-testFunction-project-us-east-1. Here is that policy file:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Resource": [
                "arn:aws:logs:us-east-1:668656059730:log-group:/aws/codebuild/testFunction-project",
                "arn:aws:logs:us-east-1:668656059730:log-group:/aws/codebuild/testFunction-project:*"
            ],
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ]
        },
        {
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::codepipeline-us-east-1-*"
            ],
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:GetObjectVersion",
                "s3:GetBucketAcl",
                "s3:GetBucketLocation"
            ]
        },
        {
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::code-bucket-05-04-2025",
                "arn:aws:s3:::code-bucket-05-04-2025/*"
            ],
            "Action": [
                "s3:PutObject",
                "s3:GetBucketAcl",
                "s3:GetBucketLocation"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "codebuild:CreateReportGroup",
                "codebuild:CreateReport",
                "codebuild:UpdateReport",
                "codebuild:BatchPutTestCases",
                "codebuild:BatchPutCodeCoverages"
            ],
            "Resource": [
                "arn:aws:codebuild:us-east-1:668656059730:report-group/testFunction-project-*"
            ]
        }
    ]
}

We'll be using this role in the pipeline, and I found out through a lot of pain, and trial and error, that this role needs additional permissions in order do all the things that happen in the pipeline.

I attached these managed policies to the role:

I'm guessing that it's not recommended to attach these policies, but at least I got things to work. I'll try to refine the permissions later.

I also added these 'Statements' to the policy file that is displayed above:

{
  "Effect": "Allow",
  "Action": [
    "iam:PassRole",
    "iam:CreateRole",
    "iam:AttachRolePolicy",
    "iam:PutRolePolicy",
    "iam:TagRole",
    "iam:GetRole"
  ],
  "Resource": "*"
},
{
  "Effect": "Allow",
  "Action": [
    "tag:TagResources",
    "tag:GetResources",
    "tag:UntagResources"
  ],
  "Resource": "*"
}

Again, I'm sure there's a more secure way to set these up, but at least it works!

Hopefully this will save you the time and troubleshooting I had to go through in order to get things to work. After setting up the CodePipeline (below), I'll try to explain how I went about solving all these permission problems.

Create a CodePipeline

  1. In the CodePipeline dashboard Create a pipeline
  2. Select Build a Custom Pipeline instead of using a template
  3. In the Choose Pipeline Settings screen:
    • I named test-function-pipeline
    • For Service Role, choose New Service Role
    • Enter test-function-pipeline-role for the name
  4. In the Add Source Stage (note that if you have already connected your AWS account to GitHub, then these steps may be different for you):
    • For the Source Provider, select GitHub (via GitHub App)
    • Click Connect to GitHub
    • For the Connection Name, enter testFunction-connection
    • Click Connect to GitHub
    • Click Install a New App
    • Choose Only Select Repositories and then set it to the repository that you created
    • Click Install and Authorize
    • For the Repository Name, select the repository you created
    • For Default Branch, select your main/master branch
  5. In the Add Build Stage:
    • Select Other Build Providers and then selected AWS CodeBuild
    • For Project Name select testFunction-project
  6. Skip the Test Stage
  7. In the Add Deploy Stage
    • For Deploy Provider choose AWS CloudFormation
    • The Input Artifact should be set to BuildArtifact
    • For the Action Mode, choose Create or update a stack
    • For Stack Name, enter testFunctionStack (this is the name of the stack we put in the buildspec.yml file)
    • For the Template
      • I set the Artifact Name to BuildArtifact
      • I set the File Name to src/packaged.yaml (this is the template file name we specified in the buildspec.yml file)
    • For Capabilities
      • I added CAPABILITY_IAM and CAPABILITY_AUTO_EXPAND (although I don't think we need the second one)
    • For Role Name, chose codebuild-testFunction-project-service-role, this is the role we created for the CodeBuild Project
    • For Output File Name, leave it blank

Try It Out

Make a small change to the lambda function code, then commit and push the change. It should start the codepipeline.

To observe the process:

  1. Go to the CodeBuild dashboard and select the testFunction-project
  2. In the Build History tab, you should see a build run in progress, click on the link for the build run.
  3. Click on the Tail Logs button

If the build fails, you might see an error in the log like this:

Error: Failed to create/update the stack: testFunctionStack, An error occurred (AccessDenied) when calling the DescribeStacks operation: 
User: arn:aws:sts::668656059730:assumed-role/codebuild-testFunction-project-service-role/AWSCodeBuild-9c62d82a-5214-4b5a-9cf6-909521c9fc50
is not authorized to perform: cloudformation:DescribeStacks on resource: arn:aws:cloudformation:us-east-1:668656059730:stack/testFunctionStack/* 
because no identity-based policy allows the cloudformation:DescribeStacks action

After seeing this error, I added the cloudformation:DescribeStacks permission to the codebuild-testFunction-project-service-role.

I continued to rerun the pipeline (by committing and pushing), only to find another permission was missing. And this process continued for many hours. That's how I was able to figure out how the configure the the role.

Here are some very important things to know if you are going to troubleshoot this process:

Also note that the stack that gets created includes a copy of the original lambda that you created. So I suppose that once you get everything set up, then you could delete the testFunction lambda.

Hopefully this helps!