Serverless computing, a model in which the provider manages the server, lets developers focus on writing dedicated pieces of application logic. Serverless computing has been adopted by many development teams because it auto-scales. Auto-scaling relieves developers of allocation management tasks, so they do not need to worry about the allocation of server resources or being charged for resources they are not consuming. Another big benefit is that serverless computing fits well into both traditional and microservice architectures.

Almost every big cloud computing service provider has their own version of serverless offerings. A few examples are:

Prerequisites

To follow this tutorial, a few things are required:

  1. Node.js installed on your system (version >= 12)
  2. An Azure account
  3. A CircleCI account
  4. A GitHub account
  5. Azure CLI installed
  6. Azure functions core tools installed

With all these installed and set up, you can begin the tutorial.

Creating an Azure Service Principal account

To begin, you need to create an Azure Service principal account. This type of account is specifically designed by Azure for automated processes like continuous deployment pipelines. You will need to access your Azure account in the pipeline that will be built later on in this tutorial, so this is required.

After making sure you are logged in on your console, (run az login if you are not), use the Azure CLI to create a service principal account and give it a unique name. Run the command:

az ad sp create-for-rbac --name [ServicePrincipalName]

Replace the placeholder ServicePrincipalName with the name you want to use. For example:

az ad sp create-for-rbac --name MyServicePrincipal

The output of this command is a json string with keys for the name. These three (3) details are required for authentication in the pipeline script.

  • The name will be in the format http://[ServicePrincipalName]
  • Tenant id is tenant
  • password is the auto-generated password for the account

Creating the functions project

The next step is to create the Azure functions project locally using the Azure core tools CLI utility. Run the following command to create an Azure functions project based on JavaScript.

func init MySampleFunctionProject --javascript

This command creates a new project in a MySampleFunctionProject folder. The project will contain two files, host.json and local.settings.json. The files contain configurations for your global and local environments.

Go into the root of the newly created project. Enter:

cd MySampleFunctionProject

Adding a new function to your project

Next, you will be adding a new function to your project. The function we will use in this tutorial will be triggered by an HTTP call that will simply respond to the requesting client.

Add a function MyHttpExample to your project by running this command:

func new --name MyHttpExample --template "HTTP trigger" --authlevel "anonymous"

The function uses the HTTP trigger template that scaffolds a basic index.js entry point.

module.exports = async function (context, req) {
  context.log("JavaScript HTTP trigger function processed a request.");

  const name = req.query.name || (req.body && req.body.name);
  const responseMessage = name
    ? "Hello, " + name + ". This HTTP triggered function executed successfully."
    : "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.";

  context.res = {
    // status: 200, /* Defaults to 200 */
    body: responseMessage
  };
};

This code exports a function that receives a Node.js request object (req) and a function (context). It then checks that a name query string parameter is set in the request. If the parameter is set the code responds with the string Hello [name]. This HTTP triggered function executed successfully. in the response body. Otherwise, a default message is returned in the body of the response.

This function.json file is also created:

{
  "bindings": [
    {
      "authLevel": "Anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": ["get", "post"]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    }
  ]
}

This configures the function to respond to an HTTP trigger and allow both GET and POST requests. The output of the function is routed to the response stream of the HTTP request. The authLevel key is set to Anonymous allowing non-authenticated access to the function endpoint.

Now, start the function locally at the root of the project (not function) folder, by running:

func start

Note: If this does not run the first time or returns an error about a missing parameter, rerun the command. Make sure you are in the root of the project folder: MySampleFunctionProject.

The function will start up locally and display the endpoint in your CLI. Be patient, it might take a moment to display the localhost URL.

Azure Functions Core Tools
Core Tools Version:       3.0.3388 Commit hash: fb42a4e0b7fdc85fbd0bcfc8d743ff7d509122ae 
Function Runtime Version: 3.0.15371.0


Functions:

        MyHttpExample: [GET,POST] http://localhost:7071/api/MyHttpExample

For detailed output, run func with --verbose flag.
[2021-03-18T08:45:44.954Z] Worker process started and initialized.
[2021-03-18T08:45:49.427Z] Host lock lease acquired by instance ID '0000000000000000000000003EA6BE15'.

Visit this endpoint by passing a query parameter name to it in your browser.

Function Run - Browser

Creating a storage account

Azure functions require a storage account to maintain state and other information about your projects.

If you do not have a resource group created, create one with Azure CLI tool. Use the resource_group_name and region you prefer and run:

az group create --name <resource_group_name> --location <region>

For example, I used the following command for this tutorial:

az group create --name Demos-Group --location westeurope

Next, create a general-purpose storage account using your resource group. You will need a globally unique name for your storage_account_name. This name can contain only numbers and lowercase letters. Create a storage account using the command:

az storage account create --name <storage_account_name> --location <region> --resource-group <resource_group_name> --sku Standard_LRS

For example, I used the following command for this tutorial:

az storage account create --name <storage_account_name> --location westeurope --resource-group Demos-Group --sku Standard_LRS

Standard_LRS specifies a general-purpose account, which is supported by functions.

Creating an Azure function application

The function project you are currently working with exists only on your local machine. Now it is time to create the function on Azure. Create a new Azure function by running the following command:

az functionapp create --resource-group <resource_group_name> --consumption-plan-location <region> --runtime node --runtime-version 12 --functions-version 3 --name <app_name> --storage-account <storage_name>

You will need to replace the placeholders with the information you chose earlier. Replace resource_group_name with your resource group, region with the region you have been using so far (westeurope in this example), app_name with a globally unique name for your function, and storage_name with the name of the storage account (<storage_account_name>) that you just created in the last CLI command.

This command creates a function on Azure based on Node.js version 12. If you are using a different Node.js version (version 10 is also supported)switch the version using the --runtime-version parameter.

The app_name will be the default DNS domain for your function app, so it must be globally unique. An Application Insights resource will also be automatically created for this function, for monitoring.

Setting up the project on CircleCI

In this step, we will automate the deployment process. From the root of the project folder, push the project to GitHub.

Now, go to the Projects page on the CircleCI dashboard. Select the associated GitHub account to add the project.

Add Project - CircleCI

Click the Set Up Project button to begin setting up the project.

Add Config - CircleCI

On the setup page, click Use Existing Config to instruct CircleCI that you are adding a configuration file manually and not using the sample displayed. Next, you are prompted to either download a configuration file for the pipeline or to start building.

Build Prompt - CircleCI

Click Start Building. This build will fail because you have not set up the configuration file yet. We will complete this step later on in the tutorial.

You will need access to your Azure account from your deployment script, which is why we created the Azure Service Principal account at the start of the tutorial. We can use the azure-cli orb to log in and use Azure CLI in the deployment script. This orb requires a some environment variables to be set up on the project.

These are:

  • AZURE_SP, which is your service principal name (in the form http://[ServicePrincipalName])
  • AZURE_SP_PASSWORD is the password key in your service principal account creation response
  • AZURE_SP_TENANT is the tenant key in your service principal account creation response
  • FUNCTION_APP_NAME is the name of your Azure function app created using the Azure CLI

Note: FUNCTION_APP_NAME is not required by the azure-cli orb but it is good practice to set it up as an environment variable.

Go to Project Settings and then Environment Variables on your CircleCI project and click Add Environment Variable.

Add Environment Variable - CircleCI

Using the dialog, add the environment variables described earlier.

Environment Variables - CircleCI

Writing the deployment configuration

The final step to the process is to write the continuous deployment pipeline script that will deploy the function app and continuously deploy it anytime updates are pushed to the GitHub repository.

At the root of your project, create a folder named .circleci and a file named config.yml within it. Inside config.yml, enter this code:

jobs:
  deploy:
    working_directory: ~/repo
    executor: node/default
    steps:
      - checkout
      - azure-cli/install
      - azure-cli/login-with-service-principal
      - run:
          name: Install Azure functions tools
          command: sudo npm i -g azure-functions-core-tools@3 --unsafe-perm true
      - run:
          name: Deploy to Azure function App
          command: func azure functionapp publish $FUNCTION_APP_NAME --javascript

orbs:
  azure-cli: circleci/azure-cli@1.0.0
  node: circleci/node@4.1.0
version: 2.1
workflows:
  login-workflow:
    jobs:
      - deploy

In this file, a deploy job is defined. This file uses two orbs, the azure-cli orb and the node orb. node/default is used as the executor to provide a Node.js environment while azure-cli is used to install the Azure CLI and login with the service principal account. Next, azure-functions-core-tools is installed using npm. It is used to deploy the function app using the func azure functionapp publish command along with the function app name and the --javascript flag that defines the project as a Node.js/JavaScript project.

Commit your changes and push to the GitHub repository. You will have a successful deployment.

Deploy Successful - CircleCI

Click into the deploy job to see the details.

Deploy Details - CircleCI

The function endpoint is displayed in the Deploy to Azure function App step details. If part of the URL is masked, get the link from Azure. Load this endpoint in your browser with the name query parameter to test it.

Function Live - Browser

Awesome!

Conclusion

Serverless architectures provide a lot of scalability and cost-saving benefits. You are able to easily plug these serverless functions into your application process flows and have them triggered by services like storage services, mailing services, and pub/sub systems. These benefits make serverless functions a technology that can be useful in every application architecture.

In this tutorial, you have created and deployed an Azure function using CircleCI to set up a continuous deployment pipeline to deploy updates to the function seamlessly.

Happy coding!


Fikayo Adepoju is a Full-stack developer, technical writer, and tech content creator proficient in Web and Mobile technologies and DevOps with over 10 years experience developing scalable distributed applications. With over 40 articles written for CircleCI, Twilio, Auth0, and The New Stack blogs and also on his personal Medium page, he loves to share his knowledge to as many developers as would benefit from it. You can also check out his video courses on Udemy.