Configuring CI/CD pipeline for AWS Lambda functions

How to automate AWS Lambda Function deployment

Posted by Bishal Sarker on 22/12/2024

Let's do something cool today! We are going to create an AWS Lambda function and deploy it using CI/CD pipeline. If you have no idea about what I'm referring to, no worries. Here are some basics before you can move on:


AWS Lambda is a serverless computing service provided by Amazon Web Services. The concept of “serverless” computing refers to not needing to maintain your own servers to run these functions. Read more,


CI/CD pipeline is a process that drives software development through a path of building, testing, and deploying code. Read more,


So, why use pipeline if we also can upload our to Lambda manually?

We can upload it manually, that's true. But that is only convenient when our code doesn't need to be modified frequently and that scenario is very rare in actual software production.


Let's begin with our code!

Okay! So firstly, let's create a simple node.js function which will be used for sending emails. This is our send-email.js file:


const nodemailer = require('nodemailer');


const sendEmail = () => {
    const smtp_config = {
        host: 'smtp_host',
        port: 'smtp_port',
        auth: {
            user: 'smtp_auth_username',
            pass: 'smtp_auth_password'
        }
    };
        
    const transporter = nodemailer.createTransport(smtp_config);

    const email_request = {
        from: '[email protected]',
        to: '[email protected]',
        subject: 'Test Email',
        body: 'Hi, this is a test email'
    };


    transporter.sendMail(email_request, (error, info) => {
        if (error) {
           console.log(error);
        } else {
           console.log("Email sent to: " + email_request.to);
        };
    });
};


module.exports = { sendEmail };


We will also need an index.js file with a handler function. It's a Lambda convention. While triggering, Lambda will look for an index file with a handler function. Here's is the file:

const { sendEmail } = require('./send-email.js');

exports.handler = (event) => {
    sendEmail();
};

Now, let's push all the codes in our repository. I'm using Bitbucket.


Configuring pipelines for the repository

After our code is pushed, we need to enable pipeline for our repository. This step will be different for other code repository hosting platforms (GitHub, Gitlab etc.). For Bitbucket you can: 

Go to Your repository Settings > Under the section of the pipeline, click on Settings > Click on the Enable Pipelines Switch to enable pipelines.

We also need to set up few repository variables that will be required running the pipeline:

Go to Your repository Settings > Repository variables

And set these variables as key-pair values:

  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY
  • AWS_DEFAULT_REGION

Okay, now we need to add bitbucket-pipelines.yml file which will be required by Bitbucket to trigger the pipeline. We need create that file and push it in our repository.

pipelines
   branches:          
        deployment:
            - step:
                name: Build and package
                script:
                  - apt-get install -y zip
                  - npm install
                  - mkdir artifacts
                  - cp -r ./* ./artifacts
                  - cd ./artifacts
                  - zip -r code.zip index.js send-email.js node_modules/
                  - cd ..
                  - cp -r ./artifacts/code.zip ./
                artifacts:
                  - code.zip
            - step:
                name: Update Lambda code
                script:
                  - pipe: atlassian/aws-lambda-deploy:0.2.1
                    variables:
                      AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
                      AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
                      AWS_DEFAULT_REGION: ${AWS_DEFAULT_REGION}
                      FUNCTION_NAME: 'sendEmailFunction'
                      COMMAND: 'update'
                      ZIP_FILE: 'code.zip'


If you want know more about these pipeline configuration you can check out this page: https://support.atlassian.com/bitbucket-cloud/docs/configure-bitbucket-pipelinesyml/

We have set a trigger on the deployment branch. If we push any update in that branch we will be doing these two things here:

  • In step #1, we are building our application, in our case we resolving our npm packages. When our files are ready we are combining all these files in a compressed zip folder.
  • In step #2, we are deploying our code in lambda. In this step pipeline will automatically authenticate by using those repository variables and upload our zipped code file in our function.

The branch name can be anything but make sure you mention that branch in bitbucket-pipelines.yml pipeline file.


Creating AWS Lambda Function

Now let's create a lambda function. Log in to your AWS Management Console and search for Lambda. Open your Lambda dashboard and click "Create function".


No alt text provided for this image


Let's create our function sendEmailFunction from scratch, select runtime Node.js 16.x (or your preferred version) and hit "Create function". Our function should be created and ready:

No alt text provided for this image


So, we are ready. Now, Let's create a branch called "deployment" in our repository. After creating/pushing our code to deployment branch you will see that a pipeline has been started like this:

Photo by Bitbucket


Click that running pipeline and you'll get a view like this:

No alt text provided for this image


Done! Our code is successfully deployed to our sendEmailFunction. From now, you don't need to worry about deploy your codes to Lambda function all by your own. All you have to do is to update your code and push it to your deployment branch. You can also maintain different environments with different respective branches.