Continuous deployment for Azure functions
Fullstack Developer and Tech Author
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:
- Serverless by AWS
- Firebase Cloud Functions by Google,
- Azure Functions In this tutorial, you will learn and demonstrate how to continuously deploy an Azure function.
Prerequisites
To follow this tutorial, a few things are required:
- Node.js installed on your system (version >= 12)
- An Azure account
- A CircleCI account
- A GitHub account
- Azure CLI installed
- 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.
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.
Click the Set Up Project button to begin setting up the project.
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.
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.
Using the dialog, add the environment variables described earlier.
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.
Click into the deploy
job to see the details.
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.
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!