Alexa for CI/CD: Introduction to serverless with AWS Lambda and Bitrise API - Part 1
Tags: tutorial, serverless, aws, cloud
Published: 2022-04-12 17:16:06

In this tutorial, we will share some hands-on experience on how to use the AWS Lambda, learn to design and build a Serverless function to trigger Bitrise builds with Bitrise API via the custom Lambda function.

In the early days of software development, anyone seeking to develop a web, mobile, or backend application had to own the hardware required to run a server, which is an expensive process.

Then, when **cloud computing** came, it became possible to lease server space or a number of servers remotely. The developers and companies who rent these fixed units of server space generally overbuy to ensure that a spike in traffic or activity won't exceed their monthly limits and break their applications. Because of this, a lot of the server space that gets paid for can be wasted.

As a result of this, **serverless** computing allows developers to purchase backend services on a **pay-per-use** basis, which means that **only** what they use must be paid for.

## What is serverless?

In a **serverless environment**, a cloud provider (AWS, Azure, or Google Cloud) dynamically allocates resources to run code. The cost is only incurred when the code is actually executed. The code that is usually executed is called **stateless containers**, which can be triggered by a variety of events, for instance: database events, call API endpoint, and cron jobs.

**Serverless** computing refers to the ability to run these **functions** without maintaining your own servers. 

The term "serverless" **doesn't refer to the absence of servers**, but rather to the fact that the servers, operating systems, network layer, and rest of the infrastructure have already been configured so you can focus on writing the application code.

## What is AWS Lambda?

> [AWS Lambda](https://aws.amazon.com/lambda/) is a compute service that lets you run code without provisioning or managing servers. Lambda runs your code on a high-availability compute infrastructure and performs all of the administration of the compute resources, including server and operating system maintenance, capacity provisioning and automatic scaling, code monitoring and logging. 
— AWS Lambda Documentation.


**You don't have to worry about any infrastructure with AWS Lambda since it's a fully managed service.**

For instance, You can develop backends using AWS Lambda and [Amazon API Gateway](https://aws.amazon.com/de/api-gateway/) to authenticate and process API requests.


![Lambda](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9osqelr9hunycabtem8l.png) 
Image source https://aws.amazon.com/de/lambda/

The Lambda functions can perform a variety of tasks, from serving web pages to processing streams of data to calling APIs and integrating with other AWS services such as **CloudWatch, API Gateway, S3, SNS**, and more. 

## How does AWS Lambda work?

When a function is created, Lambda packages it into a new **container** and then executes that container on a **multi-tenant cluster of machines managed by AWS**. Before the functions start running, each function’s container is allocated its necessary **RAM** and **CPU** **capacity**. Once the functions finish running, the RAM allocated at the beginning is multiplied by the amount of time the function spent running. The customers then get charged based on the **allocated memory and the amount of run time** the function took to complete.

## Supported languages and runtimes

AWS Lambda supports a number of the most popular languages and runtimes such as Nodejs, Python, Ruby, Java, Go, and C# This is the [full list](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html) of what’s supported.

## AWS Lambda pricing

With [AWS Free Tier](https://aws.amazon.com/lambda/pricing/?loc=ft#Free_Tier), you can run **1 million requests for free**. Also if you’d like to estimate your AWS Lambda architecture cost you can use the AWS [Pricing Calculator](https://calculator.aws/#/createCalculator/Lambda). More info about Lambda pricing can be found in the [pricing](https://aws.amazon.com/lambda/pricing/) section. 

## Getting started with AWS Lambda

You can create, invoke, and manage the Lambda functions using any of the following interfaces:

- AWS Management Console 
- AWS Command Line Interface (AWS CLI)
- AWS SDKs
- AWS CloudFormation

In this tutorial, I will use the [AWS Management Console](https://aws.amazon.com/console/) to create our Lambda function using NodeJS. 

**Before we get started you should have the following prerequisites:**

- [AWS Free Tier account](https://portal.aws.amazon.com/billing/signup?refid=em_127222&redirect_url=https%3A%2F%2Faws.amazon.com%2Fregistration-confirmation#/start/email)
- IDE such as [VS Code ](https://code.visualstudio.com/download)or [IntelliJ IDEA](https://www.jetbrains.com/idea/)
- NodeJS
- [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) and configure it with your AWS Account

**Now let’s start with our first Lambda function:**

- Log in to the **AWS Management Console** with your AWS account
- Choose **AWS Lambda** from the **Compute** Services
- Click on **Create function**
- Choose **Author from scratch** option because we will build our own function and not use a template or a blueprint

![1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0i6yx6oihl324sxko3k6.png)

- Add the **function name** i.e “**HelloBitrise**” select the Runtime to be **Node.js 14.x** and leave the **Architecture** with the default option.

![2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nw7b79jux1c7fa7g4jya.png)

- In the **Permissions** section, we will select **Create a new role with basic Lambda permissions** and click **Create function**


![3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8dk8352xgj5yptl24pe2.png)

Congratulations 🎉 you just created your first Lambda function and you have the Function overview and the code source with an online editor.

![4](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5q81vucivanbli4rsgo1.png)


![5](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pvzycttct09x3t0if2ns.png)

Now you can do everything you want with your function such as change the code, test the function, monitor the traffic using CloudWatch, configure the Environment Variables and deploy the function. 

In the **Function Overview**, you will find two options: 

**Add trigger:** to specify which service will invoke the function. In our case it will be the Alexa Skills Kit. 
**Add destination:** to send invocation records to a destination when your function is invoked asynchronously, or if your function’s processes record from a stream such as an SNS topic.


![6](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hl184ztt9d65llo9q2fs.png)


![7](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/01g986u1e6cy0y9w5g5v.png)


![8](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8qcn4fgrankb8g8t69bx.png)

Let’s now learn how to test our function. 

- Click on **Test** and **Create new event** with an event name (**hello**) and the template will be **hello-world** because there are different templates but we will use it in part 3 when integrating the function with our Alexa Skills kit. 
- Click the Save button and click the **Test** button again to trigger the test event

![9](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mnsgglyxwpclcpker323.png)

And the execution results will be displayed in a new tab in the online editor. 

![10](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2tpwiq7wf0m5n36yaxd0.png)

After you’ve explored the Lambda function, and know how to trigger and test it, it’s time to know how to implement our function to trigger Bitrise builds. 

The next step in this tutorial requires that you have a Bitrise account and have an API Key to access it. To know more about Bitrise API and how to get your API Key you can read our [article](https://blog.bitrise.io/post/bitrise-api-build-custom-integrations).

## AWS Lambda and Bitrise API

To implement a NodeJS function to trigger a Bitrise build we need to install different node modules because we will create the project locally and then upload it to Lambda to include the **node_modules** folder as a part of our service. 

Let’s get started!

- Open your terminal and create a new folder:

```
mkdir LambdaBitrise
cd LambdaBitrise
```

- Create a nodejs project with the following command: 
`npm init
`

- Leave the default setting in case you don’t need to change anything, you just need to hit return [Enter] key on all the prompts 

![11](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0jsyzdj1p6c2vhkqwnc5.png)

The **package.json** file that will be generated

Open the project in [VS Code](https://code.visualstudio.com/) to be able to manage files within the project. 
Now we need to implement a function that triggers a [new build](https://devcenter.bitrise.io/en/api/triggering-and-aborting-builds.html#) endpoint from the Bitrise API, to do that: 

- Inside the project and from the terminal install the request module

`npm install request 
`

- In VS Code create a new file with the name **index.js** at the root of the project 

![12](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uvaa0y0favepbf16ynrs.png)

And add the following code:

```
var request = require('request');
var options = {
  'method': 'POST',
  'url': 'https://api.bitrise.io/v0.1/apps/{YOUR_APP_SLUG}/builds',
  'headers': {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
    'Authorization': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    "build_params": {
      "branch": "chapter-3",
      "workflow_id": "primary"
    },
    "hook_info": {
      "type": "bitrise"
    }
  })
‍
};
‍
exports.handler =  async function(event, response) {
  console.log(response.body);
  return "done..."
}
‍
request(options, function (error,response) {
  if (error) throw new Error(error);
  console.log(response.body);
});
```

- You can test the function by running the following command:
 
`node index.js
`
And if it is successful run, you will find the output like the following: 

```

![13](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8q98gyfjk6geg7phlje3.png)
```

In the **package.json** file add the above command in the test configuration to be able to test it again.
We need to zip the project to be able to upload it to the AWS Lambda function by the following command (Don’t worry about the API Key and App Slug because we can use Lambda Environment variables to replace the hardcoded values) 

`zip -r LambdaBitrise.zip index.js node_modules
`
Now it’s time to upload the zipped folder to Lambda by the following steps: 

- Click on Upload from the list and select zip file (you can also upload the file to the S3 bucket and load it from there)


![14](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9ja3b8yudau4wmu071jn.png)


![15](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oe1cr1ucr7cwwgv4bqfj.png)

The folder will be uploaded and extracted successfully and displayed in the online editor you will notice the **node_modules** folder as well. 

![16](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v6rm3213sre0pbfjp4z8.png)

- Click on the **Configuration** tab to add the Environment variables: 
- APP_SLUG
- API_KEY

![16](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u3lpkjl09puz1rk75jd2.png)

Now you can replace the **app_slug** and **API_Key** with the Lambda Environment variables:
- ${process.env.APP_SLUG}
- ${process.env.Api_KEY}

```
var request = require('request');
‍
let app_slug = process.env.APP_SLUG
let api_key = process.env.API_KEY
‍
var options = {
  'method': 'POST',
  'url': 'https://api.bitrise.io/v0.1/apps/'+app_slug+'/builds',
  'headers': {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
    'Authorization': api_key
  },
  body: JSON.stringify({
   'build_params': {
      'branch': 'chapter-3',
      'workflow_id': 'primary'
    },
    'hook_info': {
      'type': 'bitrise'
    }
  })
‍
};
‍
exports.handler =  async function(event, response) {
  console.log(response.body);
  return "done..."
}
‍
request(options, function (error,response) {
  if (error) throw new Error(error);
  console.log(response.body);
});
```

Click the **Deploy** button to save the changes and run Test to make sure that everything is working properly. 

Now to be able to test our Lambda function we need to add a trigger, which will be discussed in Part 2 to invoke the function from your terminal you can use the following command but remembers you need to have the AWS CLI: 

`aws lambda invoke --function-name HelloBitrise  --cli-binary-format raw-in-base64-out  output.txt
`
And the output should be like the following: 

```
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}
```
And this is the output when you run the test in the Lambda online editor:

![16](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/26vowkbandki9hbu6ekl.png)

Now you can check your [Bitrise dashboard](https://app.bitrise.io/dashboard) to make sure that a new build is already triggered, then you can abort it to save your credits.

Congratulations, you did it! You created your first serverless function using AWS Lambda to trigger a new Bitrise build using Bitrise API.

## References

- [AWS Lambda Documentation](https://docs.aws.amazon.com/lambda/index.html)
- [Using AWS Lambda environment variables](Using AWS Lambda environment variables)
- [Bitrise API: Build custom integrations with your preferred tools and processes](https://blog.bitrise.io/post/bitrise-api-build-custom-integrations)

Thank you and happy building 🤖