Beginning Azure Functions
Beginning Azure Functions
Functions
Building Scalable and Serverless Apps
—
Second Edition
—
Rahul Sawhney
Kalyan Chanumolu
Beginning Azure
Functions
Building Scalable
and Serverless Apps
Second Edition
Rahul Sawhney
Kalyan Chanumolu
Beginning Azure Functions: Building Scalable and Serverless Apps
Rahul Sawhney Kalyan Chanumolu
Hyderabad, India Hyderabad, India
Introduction���������������������������������������������������������������������������������������xiii
iii
Table of Contents
iv
Table of Contents
Fan-Out/Fan-In����������������������������������������������������������������������������������������������83
Async HTTP APIs��������������������������������������������������������������������������������������������84
Monitoring�����������������������������������������������������������������������������������������������������86
Human Interaction�����������������������������������������������������������������������������������������87
Bindings for Durable Functions���������������������������������������������������������������������������89
Activity Triggers���������������������������������������������������������������������������������������������89
Orchestration Triggers�����������������������������������������������������������������������������������90
Orchestration Client���������������������������������������������������������������������������������������93
Performance and Scaling of Durable Functions��������������������������������������������94
Internal Queue Triggers���������������������������������������������������������������������������������95
Orchestrator Scale-Out����������������������������������������������������������������������������������96
Autoscaling���������������������������������������������������������������������������������������������������������97
Concurrency Throttling����������������������������������������������������������������������������������������98
Orchestrator Function Replay�����������������������������������������������������������������������������98
Performance Targets�������������������������������������������������������������������������������������������99
Creating Durable Functions Using the Azure Portal������������������������������������������100
Creating a Durable Function�����������������������������������������������������������������������������100
Disaster Recovery and Geodistribution of Durable Functions���������������������������110
Summary����������������������������������������������������������������������������������������������������������111
v
Table of Contents
vi
Table of Contents
Index�������������������������������������������������������������������������������������������������173
vii
About the Authors
Rahul Sawhney works as a software developer
with Microsoft, India. He has more than five
years of experience delivering cloud solutions
using technologies such as .NET Core, Azure
Functions, microservices, AngularJS, Web
API, Azure AD, Azure Storage, ARM templates,
Azure App Service, Azure Traffic Manager,
and more. He is a Microsoft Certified Azure
Developer and Architect. He loves learning
new technologies and is passionate about Microsoft technologies. In his
free time, he loves playing table tennis, watching movies, and reading
books.
ix
About the Technical
Reviewers
Carsten Thomsen is a back-end developer primarily but works with
smaller front-end bits as well. He has authored and reviewed several books
and created numerous Microsoft Learning courses, all related to software
development. He works as a freelancer/contractor in various European
countries; Azure, Visual Studio, Azure DevOps, and GitHub are some of
the tools he regularly uses. He is an exceptional troubleshooter and enjoys
working with architecture, research, analysis, development, testing, and
bug fixing. Carsten is a great communicator with mentoring and team-lead
skills and enjoys researching and presenting new material.
xi
Introduction
Get ready to create highly scalable apps and monitor functions in
production using Azure Functions 2.0!
We’ll start by taking you through the basics of serverless technology
and Azure Functions and then cover the different pricing plans of Azure
Functions. After that, we’ll dive into how to use Azure Functions as a
serverless API. We will then walk you through the Durable Functions
model, disaster recovery, and georeplication.
Moving on, we provide practical recipes for creating different functions
in Azure Functions using the Azure portal and Visual Studio Code. Finally,
we discuss DevOps strategy and how to deploy Azure Functions and get
Azure Functions production-ready.
By the end of this book, you will have all the skills needed to work with
Azure Functions, including working with Durable Functions and deploying
functions and making them production-ready by using telemetry and
authentication/authorization.
xiii
Introduction
Source Code
The source code for this book can be downloaded from github.com/
apress/beginning-azure-functions-2e.
Let’s get started!
xiv
CHAPTER 1
Introduction to Azure
Functions
The software industry is now in an era where apps and services
increasingly rely on the cloud. Applications are globally available and need
to scale at a moment’s notice. Deployments need to be seamless without
any perceivable downtime to the end customer. Periodic maintenance
windows for making infrastructure upgrades are a thing of the past.
To help developers be more productive and focus on developing
software rather than worrying about provisioning and maintaining servers,
and orchestrating deployments, cloud platforms such as Microsoft Azure,
Amazon Web Services, and Google Cloud Platform, provide serverless
compute offerings.
Azure Functions is one such product for serverless computing.
With Azure Functions, developers can write less code and manage less
infrastructure, and organizations can bring applications to market quickly
with fewer upfront costs. Before diving into Azure Functions, let’s discuss
serverless computing and what it means.
This chapter covers the following topics.
2
Chapter 1 Introduction to Azure Functions
ServerIess
Unit of ScaIe 002D Function
Infrastructure Abstraction
Containers
Unit of ScaIe 002D AppIication
VirtuaI Machines
Unit of ScaIe 002D Machine
PhysicaI Servers
Unit of ScaIe 002D Server
3
Chapter 1 Introduction to Azure Functions
4
Chapter 1 Introduction to Azure Functions
4.x The latest and recommended runtime version for functions in all
languages. This version supports running C# functions on .NET 6.0 and
above.
3.x This runtime version supports all languages. This version supports
running C# functions on .NET Core 3.1 and .NET 5.0.
2.x Currently, this version is in maintenance mode. All the enhancements
are provided in newer versions only. This version supports running C#
functions on .NET Core 2.1
1.x This version is in maintenance mode and should only be used for C#
apps that must use .NET Framework. It only supports development
locally on Windows, the Azure Stack Hub portal, and the Azure portal.
Going forward in this book, functions are created using version 4.x.
5
Chapter 1 Introduction to Azure Functions
Trigger Azure Functions can be triggered with There are two types of
many triggers, as discussed in the WebJobs: triggered and
upcoming chapters. Azure Functions continuous.
don’t run continuously.
Supported Azure Functions support the following Azure Web Jobs also
Languages languages: C#, Java, JavaScript, supports languages like
PowerShell, Python, and TypeScript. JavaScript, C#, and F#.
Deployment Azure Functions run using a classic\ It runs as a background
dynamic App Service plan. process in app services,
such as API apps, mobile
apps, and web apps.
Azure WebJobs runs under the Azure App Service model, whereas
Azure Functions has different pricing models that give you more control
over pricing. You’ll learn about pricing next.
6
Chapter 1 Introduction to Azure Functions
Trigger An Azure Function can have only Azure Logic Apps can have up
one trigger. to ten triggers.
Supported Azure Functions supports the The source code generated by
Languages following languages: C#, Java, the graphic designer can be
JavaScript, PowerShell, Python, and accessed as a JSON template.
TypeScript.
Development Azure Functions can be developed Logic Apps are authored using
on the Azure portal using Visual the Azure portal or Visual Studio
Studio or any supported IDE and using an extension and produce
have support for debugging. They a JSON template that can be
can be deployed using Visual checked into source control and
Studio, Azure Pipelines, or GitHub deployed using Azure DevOps,
Actions. Visual Studio, or FTP.
(continued)
7
Chapter 1 Introduction to Azure Functions
Logic Apps are especially useful when you coordinate simple actions
across multiple systems and services to automate business processes. They
provide rich capabilities for control flow operations such as conditions,
switches, loops, and scopes. You can also implement error and exception
handling in your workflows. Azure Functions are better suited for
workflows with complex business logic and data transformations, reuse of
components and libraries across other projects and scenarios that require
more control on security, reliability, and availability. Azure Functions and
Logic Apps can call each other, allowing you to design a solution that can
take advantage of both technologies as appropriate for your scenario.
• Consumption plan
• Premium plan
Let’s look at each plan in detail.
8
Chapter 1 Introduction to Azure Functions
Consumption Plan
Azure Functions’ Consumption plan allows you to pay only when your
code is executing. This helps you save significantly over the App Service
plan or when using a virtual machine. For example, if you have a weekly
newsletter for your website, instead of using WebJobs, you can use Azure
Functions and save a significant amount of money.
The metric for calculating price in Azure Functions is gigabytes/
seconds (GB/s). This metric calculates the memory usage and total
execution time for billing. It is billed based on per-second resource
executions and consumptions.
The Consumption plan grants one million requests and 400,000 GB/s
of resource consumption for free per month per subscription across all
Azure Functions apps in that subscription.
9
Chapter 1 Introduction to Azure Functions
Premium Plan
Azure Functions’ Premium plan is a dynamic scale hosting plan for
function apps. Azure Functions Premium plan offers the following
benefits.
10
Chapter 1 Introduction to Azure Functions
11
Chapter 1 Introduction to Azure Functions
On the other hand, in the Premium plan, there are two ways to solve
a cold start problem. First, always ready instances and pre-warmed
instances. The Premium plan lets you configure up to 20 instances as
always ready instances. When an event triggers a function app, the
execution is routed to an always ready instance. Additional instances are
warmed as a buffer as the function gets more traffic. These buffer instances
are then used during scale to prevent a cold start. These buffer instances
are called pre-warmed instances.
Summary
You should now have a basic understanding of serverless computing,
Azure Functions, and app pricing. Chapter 2 builds upon this chapter and
gets started with creating Azure Functions.
12
CHAPTER 2
Creating Azure
Functions
In this chapter, you will learn how to create an Azure function using the
Azure portal and Visual Studio Code. You will also gain insight into a
function’s file hierarchy, configuration, and settings.
In this chapter, you start using Azure Functions. The latest runtime
version is 4.0, which is used throughout the book’s examples. Over the
course of this chapter, we will cover the following topics:
Let’s examine the two ways you can create functions with Azure
Functions.
14
Chapter 2 Creating Azure Functions
You are asked to provide details such as the app name, resource group,
OS, hosting plan, and so on. Refer to Figure 2-2 to enter the settings.
15
Chapter 2 Creating Azure Functions
16
Chapter 2 Creating Azure Functions
Click the Review + Create button to get the summary of the function,
and then click the Create button, as shown in Figure 2-3.
17
Chapter 2 Creating Azure Functions
You can check the status of your function by clicking the bell icon at
the top, as shown in Figure 2-4.
18
Chapter 2 Creating Azure Functions
For this example, choose “Develop in portal”. However, you can use
VS Code or any other editor that you are comfortable with. Several trigger
templates can trigger your Azure Function. Select the HTTP trigger
template for this example and provide your function app’s details and the
authorization level, as shown in Figure 2-7.
19
Chapter 2 Creating Azure Functions
20
Chapter 2 Creating Azure Functions
Click the Create button, and your first HTTP trigger function is created.
It comes with basic boilerplate code. This code should be good enough
for you to test your function. Click the Test/Run button, as shown in
Figure 2-8.
21
Chapter 2 Creating Azure Functions
In the Test panel, select Key as the default (Function key), update the
body parameter as needed, and click Run, as shown in Figure 2-9.
22
Chapter 2 Creating Azure Functions
23
Chapter 2 Creating Azure Functions
24
Chapter 2 Creating Azure Functions
In the Kudu back end, you can now navigate to Tools ➤ Zip Push
Deploy to see the file hierarchy, as shown in Figure 2-14.
The host.json file is at the root of the function app folder and contains
configuration options that affect all functions in a function app instance.
In this file, you can specify the extension bundle version and the default
FunctionTimeout.
The function.json file is inside each function folder and defines the
function’s trigger, bindings, and other configuration settings. The runtime
uses this file to determine the events to monitor and how to pass data into
and return data from a function execution. For compiled languages like C#,
this file is generated automatically. However, for scripting languages like
JavaScript and PowerShell, you must author this file yourself.
25
Chapter 2 Creating Azure Functions
All the logging-level settings for Azure Functions reside under the
logging object, as shown in Figure 2-16.
26
Chapter 2 Creating Azure Functions
27
Chapter 2 Creating Azure Functions
Summary
In this chapter, you created your first function app and function. Also,
you learned about the Azure Functions file hierarchy, configuration,
and settings. In the next chapter, you’ll learn how functions are triggered
and how other Azure services can be declaratively connected to Azure
Functions.
28
CHAPTER 3
Understanding Azure
Functions Triggers
and Bindings
This chapter teaches you two main components of Azure Functions:
triggers and bindings.
This chapter covers the following topics.
30
Chapter 3 Understanding Azure Functions Triggers and Bindings
Azure Functions has the following supported bindings, and the list is
ever-growing.
• Blob Storage
• Cosmos DB
• Event Grid
• Event Hubs
• Queue Storage
• Table Storage
• Service Bus
• SendGrid
• SignalR
• Twilio
Bindings are optional in Azure Functions, and you can have multiple
input and output bindings. Azure Functions triggers and bindings are
configured in the functions.json file, and they help you avoid putting
hard-coded values in the code.
Note You can find all the supported bindings in Azure Functions by
visiting https://docs.microsoft.com/en-us/azure/azure-
functions/functions-triggers-bindings#supported-
bindings.
31
Chapter 3 Understanding Azure Functions Triggers and Bindings
{
"type": "blob",
"name": "blobOutput",
"direction": "out",
"path": "my-output-container/{DateTime}.txt"
}
32
Chapter 3 Understanding Azure Functions Triggers and Bindings
[Function("RecurringOrdersProcessor")]
public async Task Run([TimerTrigger("%ScheduleConfiguration%",
RunOnStartup = false, UseMonitor = false)] MyInfo myTimer)
{
• App settings
• Trigger metadata
• JSON payloads
• New GUID
Note You can find all the supported binding expressions by visiting
https://learn.microsoft.com/en-us/azure/azure-
functions/functions-bindings-expressions-patterns.
33
Chapter 3 Understanding Azure Functions Triggers and Bindings
34
Chapter 3 Understanding Azure Functions Triggers and Bindings
Note With any of the installation steps mentioned, if you install and
register the extension, a metadata file named extensions.json is
generated in the bin folder inside the function’s app root folder. The
runtime uses only the extensions registered in this file.
35
Chapter 3 Understanding Azure Functions Triggers and Bindings
Create a new project on the next dialog and select a folder to save the
project files.
Select the language in which you want to code your function. In this
example, C# was selected, as shown in Figure 3-3.
36
Chapter 3 Understanding Azure Functions Triggers and Bindings
The next screen asks you to sign in to Azure. Select “Sign in to Azure”
if you are not signed in. All the subscriptions associated with your
Azure account load. Select the subscription, as shown in Figure 3-9. The
subscription name in this example is VSEng.
You can select the Azure storage account that already exists or create a
new one. In this case, you select the existing one, as shown in Figure 3-10.
Once the storage account is set up, provide a name for the blob trigger.
This is the path that the trigger monitors. By default, it shows as samples-
workitems. For this function, you are setting it to function-v4-book, as
shown in Figure 3-11.
Add your project to a workspace in Visual Studio Code for easy access,
as shown in Figure 3-13.
39
Chapter 3 Understanding Azure Functions Triggers and Bindings
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Processing;
namespace AzureFunctionV4Book.Function
{
public class MyFirstBlobTriggerFuntionApp
{
private readonly ILogger _logger;
40
Chapter 3 Understanding Azure Functions Triggers and Bindings
public MyFirstBlobTriggerFuntionApp(ILoggerFactory
loggerFactory)
{
_logger = loggerFactory.CreateLogger<MyFirstBlobTri
ggerFuntionApp>();
}
[Function("MyFirstBlobTriggerFuntionApp")]
[BlobOutput("output-blob/{name}_resized", Connection
= "functionv4book_STORAGE")] // Output Container and
connection string of the storage account
public byte[] Run([BlobTrigger("image-blob/{name}",
Connection = "functionv4book_STORAGE")] byte[]
inputImage, string name)
{
_logger.LogInformation($"C# Blob trigger function
processed blob\n Name:{name} \n Size: {inputImage.
Length} Bytes");
IImageFormat format;
var width = 100;
var height = 200;
41
Chapter 3 Understanding Azure Functions Triggers and Bindings
}
}
}
42
Chapter 3 Understanding Azure Functions Triggers and Bindings
43
Chapter 3 Understanding Azure Functions Triggers and Bindings
44
Chapter 3 Understanding Azure Functions Triggers and Bindings
After selecting the app setting name, the function asks you to provide
the name of the blob that triggers this function. Provide the name of your
blob, as shown in Figure 3-21.
Your function is ready. Click the file icon to see all the function files, as
shown in Figure 3-23.
45
Chapter 3 Understanding Azure Functions Triggers and Bindings
To resize the image, you need to add a few extension packages. The
packages that you need to install are @azure/storage-blob, urijs,
stream, jimp, and async. You can install these packages using npm i
<package name>.
Once the packages are installed, copy and paste the following code.
46
Chapter 3 Understanding Azure Functions Triggers and Bindings
47
Chapter 3 Understanding Azure Functions Triggers and Bindings
});
});
}
],
function (err, result) {
if (err) {
callback(err, null);
} else {
callback(null, result);
}
}
);
};
function generateSasToken(container, blobName, permissions) {
var connString = process.env.AzureWebJobsStorage;
var blobService = azure.createBlobService(connString);
// Create a SAS token that expires in an hour
// Set start time to five minutes ago to avoid clock skew.
var startDate = new Date();
startDate.setMinutes(startDate.getMinutes() - 5);
var expiryDate = new Date(startDate);
expiryDate.setMinutes(startDate.getMinutes() + 60);
permissions = permissions || storage.BlobUtilities.
SharedAccessPermissions.READ;
var sharedAccessPolicy = {
AccessPolicy: {
Permissions: permissions,
Start: startDate,
Expiry: expiryDate
}
};
48
Chapter 3 Understanding Azure Functions Triggers and Bindings
Once you run the code and upload the image, you should see the
image getting resized.
{
"version": "2.0"
}
Summary
You should now understand the concept of Azure triggers and bindings,
and you have created a blob-triggered function. The next chapter looks at
creating serverless APIs using Azure Functions.
49
CHAPTER 4
52
Chapter 4 Serverless APIs Using Azure Functions
But, with the benefits, there are also trade-offs when using
microservices.
53
Chapter 4 Serverless APIs Using Azure Functions
• Product service
54
Chapter 4 Serverless APIs Using Azure Functions
You can expose each of these services as an API that your front-end
user interface can easily consume.
Now, let’s see how microservices help you scale the application with
a minimum cost. Let’s say you have a holiday season sale coming up, and
you estimate that you have double the amount of traffic on the website
during this time.
After some analysis, you find that the product/service and the payment
and checkout services have the most load, and there won’t be much
change in load on the customer and inventory management service.
With microservices, you can scale out only those two services (product,
payment, and checkout) and leave the other services as is. In contrast,
if you had a monolithic application, you would have to scale out the
complete application, which would greatly increase your operating costs.
Since now you know why you want to convert a monolithic application
to microservices, let’s examine how Azure Functions can help you achieve
that in a simpler and more cost-efficient way.
Azure Functions allows you to write and deploy small pieces of code.
With the help of Azure Functions, you can divide the microservices into
small parts and write a function that performs a specific task. For example,
the customer service microservice would have different activities, such as
Update Profile, View My Orders, Cashback Amount, Card Details, and so
on. You could write each one as a separate function, and on days when you
have a big sale, you can scale up the individual functions.
With Azure Functions, the infrastructure maintenance is taken care of
by the cloud service provider, and you won’t have to worry about scaling
up, upgrading the software, and so on. This reduces the team’s workload
and helps them concentrate on the business.
55
Chapter 4 Serverless APIs Using Azure Functions
56
Chapter 4 Serverless APIs Using Azure Functions
57
Chapter 4 Serverless APIs Using Azure Functions
58
Chapter 4 Serverless APIs Using Azure Functions
For the data source, you could choose None and create the tables from
scratch. In this case, let’s use the sample database AdventureWorksLT
as shown in Figure 4-4b, because it has tables pre-populated with
sample data.
It takes time for SQL Server and the database to be ready. Once it is
ready, it is under the resource group you selected, as shown in Figure 4-5.
59
Chapter 4 Serverless APIs Using Azure Functions
Once you have successfully logged in, you see the Query Editor. In
the left menu, click Tables. The tables are being created, as shown in
Figure 4-7.
60
Chapter 4 Serverless APIs Using Azure Functions
You can query the tables similar to the way it is done in SQL Server
Management Studio.
Your database has been created with some initial data. Let’s now create
an HTTP-triggered function for it.
61
Chapter 4 Serverless APIs Using Azure Functions
Select the language to be used for the Azure function. C# is used for
this example, as shown in Figure 4-9.
62
Chapter 4 Serverless APIs Using Azure Functions
63
Chapter 4 Serverless APIs Using Azure Functions
Set the access rights for this function. You see three options:
Anonymous, Function, and Admin. The access rights determine which
keys are required to invoke the function.
64
Chapter 4 Serverless APIs Using Azure Functions
Now everything is set up. You follow a code structure similar to what
you would follow in normal projects. Create two folders: Helper and
Models. In Models, create the CustomerModel.cs file, and in Helper, create
the SqlClientHelper.cs file. You use them for your function, as shown in
Figure 4-16.
65
Chapter 4 Serverless APIs Using Azure Functions
namespace Company.Function.Models
{
public class CustomerModel
{
public int CustomerID { get; set; }
public int NameStyle { get; set; }
public string Title { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
66
Chapter 4 Serverless APIs Using Azure Functions
Click the SqlClientHelper.cs file and paste the following code, which
calls the SQL Server database and gets customer details from the SalesLT.
Customer table.
using System;
using System.Data;
using System.Data.SqlClient;
using Company.Function.Models;
namespace Company.Function.Helper
{
public static class SqlClientHelper
{
public static CustomerModel GetData(int customerId)
{
var connection = Environment.GetEnvironmentVariable
("coonectionString");
CustomerModel customer = new CustomerModel();
using (SqlConnection conn = new
SqlConnection(connection))
{
var text = "SELECT CustomerID, NameStyle,
FirstName, MiddleName, LastName, CompanyName
FROM SalesLT.Customer where CustomerID=" +
customerId;
SqlCommand cmd = new SqlCommand(text, conn);
// cmd.Parameters.AddWithValue("@CustomerId",
customerId);
67
Chapter 4 Serverless APIs Using Azure Functions
conn.Open();
using (SqlDataReader reader = cmd.
ExecuteReader(CommandBehavior.SingleRow))
{
while (reader.Read() && reader.HasRows)
{
customer.CustomerID = Convert.
ToInt32(reader["CustomerID"].
ToString());
customer.FirstName =
reader["FirstName"].ToString();
customer.MiddleName =
reader["MiddleName"].ToString();
customer.LastName = reader["LastName"].
ToString();
customer.CompanyName =
reader["CompanyName"].ToString();
}
conn.Close();
}
}
return customer;
}
}
}
68
Chapter 4 Serverless APIs Using Azure Functions
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Company.Function.Helper;
namespace Company.Function
{
public static class HttpTriggerCSharp
{
[FunctionName("HttpTriggerCSharp")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous,
"get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function
processed a request.");
int customerId = Convert.ToInt32(req.
Query["customerId"]);
return (ActionResult) new
OkObjectResult(SqlClientHelper.
GetData(customerId));
}
}
}
69
Chapter 4 Serverless APIs Using Azure Functions
If you look at the code, you’ll see you have created a basic customer
profile function where you get customer details based on CustomerID.
Select Debug in the top menu and click Start Debugging. Once the
function is compiled, you see the local URL of the function, as shown in
Figure 4-17.
Copy this URL, append ?customerId=1 to it, and hit Enter. You should
see the output shown in Figure 4-18.
70
Chapter 4 Serverless APIs Using Azure Functions
Note For more information about OData, you can visit the official
page at https://www.odata.org/.
1. Azure-odata-sql
2. Async
3. Tedious
4. Tedious-connection-pool
To install the packages, open the terminal and type the following,
which installs the package for you.
71
Chapter 4 Serverless APIs Using Azure Functions
Once the packages are installed, create a file named functions.js and
paste the following code. It connects to the SQL Server database, runs the
query, and returns the data. In the following code, you first create the pool
of SQL connections using poolConfig. Then, you write the getSqlResults
method, which fetches the records from the table.
// TEDIOUS
var ConnectionPool = require('tedious-connection-pool');
var Connection = require('tedious').Connection;
var Request = require('tedious').Request;
var TYPES = require('tedious').TYPES;
// Pool Connection Config
var poolConfig = {
min: 1,
max: 10,
log: true
};
//Connection Config
var config = {
userName: process.env.databaseUser,
password: process.env.databasePassword,
server: process.env.databaseUrl,
options: {
database: process.env.databaseName,
encrypt: true,
requestTimeout: 0,
}
};
//create the pool
var pool = new ConnectionPool(poolConfig, config);
pool.on('error', function (err) {
console.error(err);
});
72
Chapter 4 Serverless APIs Using Azure Functions
73
Chapter 4 Serverless APIs Using Azure Functions
module.exports = {
getSqlResult: getSqlResult,
}
Go to the main index.js file and paste the following code. In the
following code, you are first configuring the table and schema used in
this OData API. Then you are fetching the pageSize, filters, selection,
ordering, and so on from the query parameters. Once you get all this, you
prepare the query and call azureOdata.format to convert this to a proper
SQL query.
74
Chapter 4 Serverless APIs Using Azure Functions
75
Chapter 4 Serverless APIs Using Azure Functions
You first prepared the query inside var query = {}. Now, you
convert this into a SQL-understandable query by calling azureOdata.
format(query, tableConfig). Once the query is converted to a SQL
query, you pass this to the function you wrote in functions.js, which runs
the SQL statements and return the data.
76
Chapter 4 Serverless APIs Using Azure Functions
Run Azure Functions by going to the top menu and clicking Debug ➤
Start Debugging. Once the function starts, you make an HTTP call.
http://localhost:7071/products?$filter=CustomerID eq 1&$select=
CustomerID,FirstName,LastName
If you look at this URL, you see two query parameters. One is $filter,
which is converted to a WHERE clause. The other is $select, which is
converted to a SELECT statement.
Once you run the previous query, you get the result shown in
Figure 4-19.
Summary
In this chapter, you learned the differences between monolithic and
microservices architectures. You also created serverless APIs using Azure
Functions. The next chapter looks at the Durable Functions extension and
how you can use durable functions for long-running tasks.
77
CHAPTER 5
Azure Durable
Functions
Over the course of this chapter, we will cover the following durable
function topics.
• Overview
• Bindings
Types of Functions
The Durable Functions extension allows the stateful orchestration
of functions. Each function is made up of a combination of different
functions. Each function plays a different role in orchestration, as shown in
Figure 5-1.
80
Chapter 5 Azure Durable Functions
Application Patterns
The Durable Functions extension caters to five application patterns.
• Function chaining
• Fan-out/fan-in
• Monitoring
• Human interaction
Function Chaining
Function chaining is a pattern where you execute functions in sequential
order. Also, you use function chaining when the output of one function
must be used as the input of another function.
Let’s see an example of e-commerce order processing. First, a
customer orders a product, and after that, internally, you process the order
and notify the dealer. Once the dealer confirms that the product is ready to
be shipped, you notify the delivery service to pick up the order and ship it.
Once the product is shipped, you notify the customer. This process can be
done using function chaining, as shown in Figure 5-2.
81
Chapter 5 Azure Durable Functions
The following code calls the function chaining pattern using C#.
82
Chapter 5 Azure Durable Functions
Fan-Out/Fan-In
The fan-out/fan-in pattern refers to executing multiple functions in
parallel and then waiting for them to execute. Usually, aggregation work is
done on the result returned by multiple functions.
With normal functions in Azure Functions, fanning out can be done by
publishing multiple messages to the queue. But the fanning is complicated
because you must track when the message is picked up and processed and
store the result. This is a difficult task in Azure Functions, but the Durable
Functions extension handles this pattern quite easily.
Let’s look at an example where you have replenished the stock and
want to notify all the customers who selected “Notify me once the product
is available,” as shown in Figure 5-3.
83
Chapter 5 Azure Durable Functions
you call the UpdateStatus function, which updates the notification status
corresponding to each user who opted for notification, as shown in the
following code.
84
Chapter 5 Azure Durable Functions
In Figure 5-4, the OrderFood function act as an HTTP API that is called
once the end user clicks Order Food. The OrderFood function checks for
the validity of the order and calls the OrderProcess function. This function
keeps on updating the status of the order.
You redirect the end user to the order-tracking page, which polls the
GetStatus function and shows the order status.
85
Chapter 5 Azure Durable Functions
Monitoring
The Monitoring pattern is used when you need polling until a condition is
met. A regular timer trigger (which lets you run a function on a specified
schedule) can be used for scenarios such as a cleanup job. But the problem
with this is that the time interval is static, so managing the lifetime of the
instances becomes complex.
On the other hand, the Durable Functions runtime comes with flexible
intervals and lifetime management of tasks. It allows you to create multiple
monitor processes from a single orchestration (see Figure 5-5).
86
Chapter 5 Azure Durable Functions
Human Interaction
Usually, you automate processes that require no human intervention
because people are not as highly available and responsive as cloud
services. But, in certain scenarios that require approval, human
intervention is required, so the automated processes must account for that.
Automated processes generally do this by using timers and
compensation logic. Let’s look at a “leave approval” workflow as an
example. In this case, an employee applies for leave. The notification goes
to the manager to approve it. Here you can have two scenarios. One is if
the manager does not approve it within 48 hours, the leave is automatically
approved. The other scenario is if the manager does not approve it within
48 hours, then it is escalated to the manager’s manager (see Figure 5-6).
87
Chapter 5 Azure Durable Functions
88
Chapter 5 Azure Durable Functions
else
{
await context.CallActivityAsync("EscalateEvent");
}
}
}
Activity Triggers
An activity trigger enables you to author functions that are called by
orchestrator functions. Activity functions are like any other normal
function. The only difference is that you have ActivityTrigger, which is
triggered from the orchestrator function.
Internally, the following activity trigger binding keeps polling a series
of queues in the default storage account of the function app. The queues
are internal implementations of the extension, so that’s why they are not
part of the orchestrator trigger binding.
The following JSON object in the bindings array defines the activity trigger.
{
"name": "Input parameter name",
"activity": "<Optional parameter. Name of the activity",
"type": "activityTrigger",
"direction": "in"
}
89
Chapter 5 Azure Durable Functions
[FunctionName("City_Travel")]
public static string Run([ActivityTrigger] string cityName,
TraceWriter log)
{
log.Info($"I am travelling to {cityName}.");
return $"I am travelling to {cityName}!";
}
Orchestration Triggers
As the name suggests, an orchestration trigger enables you to author
orchestrator functions. The trigger allows you to start new instances of
orchestrator functions and also allows you to resume existing instances
of orchestrator functions that are awaiting a task.
Behind the scenes, the following orchestrator trigger binding keeps
polling a series of queues in the default storage account of the function
app. The queues are internal implementations of the extension, so that’s
why they are not part of the orchestrator trigger binding.
90
Chapter 5 Azure Durable Functions
{
"name": "Input parameter name",
"orchestration": "Optional parameter - Name of
orchestration",
"type": "orchestrationTrigger",
"direction": "in"
}
91
Chapter 5 Azure Durable Functions
[FunctionName("Orchestrator_City")]
public static async Task<List<string>> Run(
[OrchestrationTrigger] DurableOrchestrationContext context)
{
var outputs = new List<string>();
outputs.Add(await context.CallActivityAsync<string>
("City_Travel", "Hyderabad"));
outputs.Add(await context.CallActivityAsync<string>
("City_Travel", "New York"));
outputs.Add(await context.CallActivityAsync<string>
("City_Travel", "Delhi"));
return outputs;
// returns
// "I am travelling to Hyderabad"
// "I am travelling to New York"
// "I am travelling to Delhi"
}
As you can see, the previous orchestrator function is calling the activity
function City_Travel and passing the city’s name to it. From how it is
written, it looks like the orchestrator function is calling the City_Travel
activity function directly, but it is actually sending a message to a work-
item queue. The activity function City_Travel polls the queue, and as
soon as it receives the message in the queue, it executes the logic.
Once the activity function completes the logic execution, it sends the
response message to the control queue that the orchestrator function is
polling. As the orchestrator function Orchestrator_City receives the
message via OrchestrationTrigger, it shows the response. This is the
behavior of the durable function.
92
Chapter 5 Azure Durable Functions
Once you start the durable function, it creates four control queues and
one work-items queue, as shown in Figure 5-7.
Orchestration Client
The orchestrator client is responsible for starting/stopping the orchestrator
function. It is also used to query the status, send events, and purge
instances of the history of the orchestrator function.
The orchestrator client binding allows you to write functions in Azure
Functions that interact with orchestrator functions.
The following JSON object in the bindings array defines the
orchestrator client trigger.
{
"name": "Name of Input Parameter",
"taskHub": "Optional Parameter. name of the task hub",
"connectionName": "Optional Parameter. Name of the connection
string in the app settings",
"type": "orchestrationClient",
"direction": "in"
}
93
Chapter 5 Azure Durable Functions
[FunctionName("OrchestrationClient_Start")]
public static async Task<HttpResponseMessage> HttpStart(
[HttpTrigger(AuthorizationLevel.Anonymous, "get",
post")]HttpRequestMessage req,[OrchestrationClient]
DurableOrchestrationClient starter, TraceWriter log)
{
string instanceId = await starter.StartNewAsync
("Orchestrator_City", null);
log.Info($"Running orchestration with
ID = '{instanceId}'.");
History Table
The history table contains the history of events for all the orchestration
instances running within a task hub. The name of the table is in the
format TaskHubNameHistory. The partition key of this table is derived
from the instance ID of the orchestration function. Since the instance
ID is generated randomly, it ensures the optimal distribution of internal
partitions in an Azure storage table. As the orchestrator function instances
run, new rows are added to this table.
94
Chapter 5 Azure Durable Functions
Instance Table
This table contains the statuses of all the orchestrations running within
a task hub. The orchestration function instance ID is the partition key
of this table, and the row key is a fixed constant. There is one row per
orchestration function instance.
This table is consistent with the content of the history table. This table
is used by the GetStatusAsync (.NET) API and the getStatus (JavaScript)
API. Also, it is used by the HTTP status query API.
Using a separate table to efficiently satisfy the instance query operation
is influenced by the command and query responsibility segregation (CQRS)
pattern.
95
Chapter 5 Azure Durable Functions
Now that you understand the underlying mechanism, let’s look at how
to scale durable functions.
Orchestrator Scale-Out
Stateless functions like activity functions can be scaled out easily by
adding more VMs. But stateful functions like orchestrator functions are
partitioned across one or more queues for them to scale out. By default, a
task hub can have at most 16 partitions; by default, the partition count is
four. The number of control queues is defined in the host.json file for a
function running on the 2.0 runtime, as follows.
{
"extensions": {
"durableTask": {
"partitionCount": 2
}
}
}
96
Chapter 5 Azure Durable Functions
As you can see in Figure 5-8, all instances compete for the work
from the work-item queue, but only two instances at a time can acquire
messages from the control queue, and each instance locks the single
control queue.
Autoscaling
Durable Functions supports autoscaling via the scale controller. The scale
controller monitors the rate of events and decides whether to scale in or
scale out. In Durable Functions, the scale controller monitors the latency
of each queue by issuing a peek command. If the message latencies are
higher than the threshold, the scale controller keeps adding the instances
until it reaches the partition count.
97
Chapter 5 Azure Durable Functions
Concurrency Throttling
Azure Functions allows you to run multiple functions concurrently within
a single app instance. The concurrency increases the parallel execution
and reduces the number of “cold starts.” But you should also be mindful
that high concurrency results in high per-VM memory usage.
Orchestrator and activity functions support concurrency, and their
limits can be set in host.json. The setting for an activity function is
maxConcurrentActivityFunctions and for an orchestrator function is
maxConcurrentOrchestratorFunctions.
{
"extensions": {
"durableTask": {
"maxConcurrentActivityFunctions": 20,
"maxConcurrentOrchestratorFunctions": 20
}
}
}
98
Chapter 5 Azure Durable Functions
{
"extensions": {
"durableTask": {
"extendedSessionsEnabled": true,
"extendedSessionIdleTimeoutInSeconds": 30
}
}
}
But there are always two sides of a coin. So, when enabling extended
session to increase throughput, there is also a downside.
• It can increase function app memory usage.
Performance Targets
If you plan to use durable functions in a production application, consider
the performance requirements early in the process because they define the
pattern you should use for your functions.
99
Chapter 5 Azure Durable Functions
100
Chapter 5 Azure Durable Functions
101
Chapter 5 Azure Durable Functions
102
Chapter 5 Azure Durable Functions
Expand the function’s app and click the + icon. Then, click the
“In-portal” environment and continue, as shown in Figure 5-12.
Click “More templates” and click “Finish and view templates and
continue, as shown in Figure 5-12,” as shown in Figure 5-13.
In the search field, type durable and select the “Durable Functions
HTTP starter” template, as shown in Figure 5-14.
104
Chapter 5 Azure Durable Functions
#r "Microsoft.Azure.WebJobs.Extensions.DurableTask"
#r "Microsoft.Azure.WebJobs.Extensions.Http"
#r "Newtonsoft.Json"
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
[FunctionName("OrchestrationClient_Start")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get",
"post")] HttpRequestMessage req,
[OrchestrationClient] DurableOrchestrationClient
starter, ILogger log)
105
Chapter 5 Azure Durable Functions
{
string instanceId = await starter.StartNewAsync
("Orchestrator_City", null);
log.LogInformation($"Running orchestration with
ID = '{instanceId}'.");
return starter.CreateCheckStatusResponse(req, instanceId);
}
Click the + icon again and type durable. Select “Durable Functions
orchestrator,” as shown in Figure 5-16.
#r "Microsoft.Azure.WebJobs.Extensions.DurableTask"
using System.Collections.Generic;
using System.Threading.Tasks;
[FunctionName("Orchestrator_City")]
public static async Task<List<string>> Run(
106
Chapter 5 Azure Durable Functions
return outputs;
// returns
// "I am travelling to Hyderabad"
// "I am travelling to New York"
// "I am travelling to Delhi"
}
Click the + icon again and type durable. Select “Durable Functions
activity,” as shown in Figure 5-17.
107
Chapter 5 Azure Durable Functions
#r "Microsoft.Azure.WebJobs.Extensions.DurableTask"
[FunctionName("City_Travel")]
public static string Run([ActivityTrigger] string cityName,
ILogger log)
{
log.LogInformation($"I am travelling to {cityName}.");
return $"I am travelling to {cityName}!";
}
Click the function name and then click “Platform features.” In the
Platform features tab, select App Service Editor, as shown in Figure 5-18.
108
Chapter 5 Azure Durable Functions
The App Service Editor opens in a new tab. Now, select the host.json
file and copy and paste the following code into it.
{
"version": "2.0",
"logging": {
"fileLoggingMode": "always",
"logLevel": {
"default": "Information",
"Host.Results": "Information",
"Function": "Information",
"Host.Aggregator": "Trace"
}
}
}
109
Chapter 5 Azure Durable Functions
110
Chapter 5 Azure Durable Functions
Summary
This chapter covered how durable functions work and the patterns you’ll
use with the Durable Functions extension. Also, in this chapter, you
created your first durable function running in Azure. You now understand
how to manage your functions in the event of a disaster.
The next chapter looks at deploying functions to Azure using a CI/CD
pipeline.
111
CHAPTER 6
Deploying Functions
to Azure
This chapter covers the following topics.
This chapter walks you through the ways to deploy functions to Azure.
By the end of this chapter, you should be able to deploy your functions in
two different ways.
• OneDrive
• GitHub
• Dropbox
• Bitbucket
114
Chapter 6 Deploying Functions to Azure
All the functions in the function app should have the same
language worker.
Now that your code repository is ready, let’s set up your function for
continuous deployment.
115
Chapter 6 Deploying Functions to Azure
116
Chapter 6 Deploying Functions to Azure
117
Chapter 6 Deploying Functions to Azure
Once you click the organization, a blade should open on the right side,
as shown in Figure 6-4. Select “Set up billing” in the menu.
118
Chapter 6 Deploying Functions to Azure
Once you get a notification that the subscription is linked, you are
good to go, as shown in Figure 6-6.
119
Chapter 6 Deploying Functions to Azure
120
Chapter 6 Deploying Functions to Azure
Specify a name for the slot and click Add, as shown in Figure 6-8.
Once the slot is created, navigate to the resource group where your
Azure function is deployed. You should see an identical App Service (Slot)
and the original function app, as shown in Figure 6-9.
121
Chapter 6 Deploying Functions to Azure
This new slot is now practically identical to the production app. You
can create up to five deployment slots to test various functionalities and
configurations before deploying to the production slot.
122
Chapter 6 Deploying Functions to Azure
Now you should see many options, such as Azure Repos, GitHub,
Bitbucket, and so on, from where you can set up CI/CD. For this example,
select Azure Repos and click Next.
Azure now presents you with a choice of selecting a build provider.
Azure App Service build service should be good for basic scenarios, but
Azure Pipelines lets you customize the build artifacts accordingly if you
need more control over the build process.
123
Chapter 6 Deploying Functions to Azure
124
Chapter 6 Deploying Functions to Azure
Check the summary and click Finish, and your continuous deployment
is now ready and set up, as shown in Figure 6-13.
125
Chapter 6 Deploying Functions to Azure
To check the release pipeline, click the Release Pipeline link, as shown
in Figure 6-14. You are taken to the release pipeline of the function in
Visual Studio Team Service (VSTS), as shown in Figure 6-15.
Azure has made configuring CI/CD intuitive and easy to set up with
just a few clicks.
126
Chapter 6 Deploying Functions to Azure
• A hosting plan
• A function app
{
"type": "Microsoft.Storage/storageAccounts",
"name": "[variables('storageAccountName')]",
"apiVersion": "2016-12-01",
"location": "[parameters('location')]",
"kind": "Storage",
"sku": {
"name": "[parameters('storageAccountType')]"
}
}
127
Chapter 6 Deploying Functions to Azure
"storageAccountType": {
"type": "string",
"defaultValue": "Standard_LRS",
"allowedValues": ["Standard_LRS", "Standard_GRS",
"Standard_RAGRS"],
"metadata": {
"description": "Storage Account type"
}
}
The Azure storage account is set up, so let’s look at setting up the
hosting plan. Here you have two hosting plans: the Consumption plan and
the App Service plan. Let’s first look at the Consumption plan.
128
Chapter 6 Deploying Functions to Azure
{
"type": "Microsoft.Web/serverfarms",
"apiVersion": "2015-04-01",
"name": "[variables('hostingPlanName')]",
"location": "[parameters('location')]",
"properties": {
"name": "[variables('hostingPlanName')]",
"computeMode": "Dynamic",
"sku": "Dynamic"
}
}
"properties": {
"serverFarmId": "[resourceId('Microsoft.Web/
serverfarms', variables('hostingPlanName'))]",
"siteConfig": {
"appSettings": [
{
"name": "WEBSITE_CONTENTAZUREFILE
CONNECTIONSTRING",
"value": "[concat('DefaultEndpointsProtocol=
https;AccountName=', variables('storageAccount
Name'), ';AccountKey=', listKeys(variables
('storageAccountid'),'2015-05-01-preview').key1)]"
},
{
"name": "WEBSITE_CONTENTSHARE",
129
Chapter 6 Deploying Functions to Azure
"value": "[toLower(variables('functionAppName'))]"
}
]
}
}
{
"$schema": "https://schema.management.azure.com/schemas/
2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"appName": {
"type": "string",
"metadata": {
"description": "Function App Name"
}
},
"storageAccountType": {
"type": "string",
"defaultValue": "Standard_LRS",
"allowedValues": ["Standard_LRS", "Standard_GRS",
"Standard_RAGRS"],
"metadata": {
"description": "Storage Account type"
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
130
Chapter 6 Deploying Functions to Azure
"metadata": {
"description": "Location for all resources."
}
},
"runtime": {
"type": "string",
"defaultValue": "node",
"allowedValues": ["node", "dotnet", "java"],
"metadata": {
"description": "The language worker runtime to load in
the function app."
}
}
},
"variables": {
"functionAppName": "[parameters('appName')]",
"hostingPlanName": "[parameters('appName')]",
"applicationInsightsName": "[parameters('appName')]",
"storageAccountName": "[concat(uniquestring(resourceGro
up().id), 'azfunctions')]",
"storageAccountid": "[concat(resourceGroup().id,'/
providers/','Microsoft.Storage/storageAccounts/', variables
('storageAccountName'))]",
"functionWorkerRuntime": "[parameters('runtime')]"
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"name": "[variables('storageAccountName')]",
"apiVersion": "2016-12-01",
"location": "[parameters('location')]",
131
Chapter 6 Deploying Functions to Azure
"kind": "Storage",
"sku": {
"name": "[parameters('storageAccountType')]"
}
},
{
"type": "Microsoft.Web/serverfarms",
"apiVersion": "2015-04-01",
"name": "[variables('hostingPlanName')]",
"location": "[parameters('location')]",
"properties": {
"name": "[variables('hostingPlanName')]",
"computeMode": "Dynamic",
"sku": "Dynamic"
}
},
{
"apiVersion": "2015-08-01",
"type": "Microsoft.Web/sites",
"name": "[variables('functionAppName')]",
"location": "[parameters('location')]",
"kind": "functionapp",
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', variables
('hostingPlanName'))]",
"[resourceId('Microsoft.Storage/storageAccounts',
variables('storageAccountName'))]"
],
"properties": {
"serverFarmId": "[resourceId('Microsoft.Web/
serverfarms', variables('hostingPlanName'))]",
132
Chapter 6 Deploying Functions to Azure
"siteConfig": {
"appSettings": [
{
"name": "AzureWebJobsDashboard",
"value": "[concat('DefaultEndpointsProtocol=
https;AccountName=', variables('storageAccount
Name'), ';AccountKey=', listKeys(variables
('storageAccountid'),'2015-05-01-
preview').key1)]"
},
{
"name": "AzureWebJobsStorage",
"value": "[concat('DefaultEndpointsProtocol=
https;AccountName=', variables('storageAccount
Name'), ';AccountKey=', listKeys(variables(
'storageAccountid'),'2015-05-01-preview').key1)]"
},
{
"name": "WEBSITE_CONTENTAZUREFILE
CONNECTIONSTRING",
"value": "[concat('DefaultEndpointsProtocol=
https;AccountName=', variables('storageAccount
Name'), ';AccountKey=', listKeys(variables
('storageAccountid'),'2015-05-01-
preview').key1)]"
},
{
"name": "WEBSITE_CONTENTSHARE",
"value": "[toLower(variables('functionAppName'))]"
},
133
Chapter 6 Deploying Functions to Azure
{
"name": "FUNCTIONS_EXTENSION_VERSION",
"value": "~2"
},
{
"name": "WEBSITE_NODE_DEFAULT_VERSION",
"value": "8.11.1"
},
{
"name": "APPINSIGHTS_INSTRUMENTATIONKEY",
"value": "[reference(resourceId('microsoft.
insights/components/', variables('application
InsightsName')), '2015-05-01').
InstrumentationKey]"
},
{
"name": "FUNCTIONS_WORKER_RUNTIME",
"value": "[variables('functionWorkerRuntime')]"
}
]
}
}
},
{
"apiVersion": "2018-05-01-preview",
"name": "[variables('applicationInsightsName')]",
"type": "microsoft.insights/components",
"location": "East US",
"tags": {
134
Chapter 6 Deploying Functions to Azure
{
"type": "Microsoft.Web/serverfarms",
"apiVersion": "2016-09-01",
"name": "[variables('hostingPlanName')]",
"location": "[parameters('location')]",
"properties": {
"name": "[variables('hostingPlanName')]",
"sku": "[parameters('sku')]",
"workerSize": "[parameters('workerSize')]",
"hostingEnvironment": "",
"numberOfWorkers": 1
}
}
135
Chapter 6 Deploying Functions to Azure
Here, workerSize is the size of the VM, which is small (0), medium
(1), or large (2). You can set up the worker size in the ARM template in the
parameters section, as follows.
"workerSize": {
"type": "string",
"allowedValues": [
"0",
"1",
"2"
],
"defaultValue": "0",
"metadata": {
"description": "The instance size of the hosting plan"
}
}
{
"$schema": "https://schema.management.azure.com/
schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"appName": {
"type": "string",
"metadata": {
"description": "Function App name"
}
},
"sku": {
"type": "string",
"allowedValues": [
136
Chapter 6 Deploying Functions to Azure
"Free",
"Shared",
"Basic",
"Standard"
],
"defaultValue": "Standard",
"metadata": {
"description": "The pricing tier for the hosting plan."
}
},
"workerSize": {
"type": "string",
"allowedValues": [
"0",
"1",
"2"
],
"defaultValue": "0",
"metadata": {
"description": "The instance size of the hosting plan"
}
},
"storageAccountType": {
"type": "string",
"defaultValue": "Standard_LRS",
"allowedValues": [
"Standard_LRS",
"Standard_GRS",
"Standard_RAGRS"
],
"metadata": {
137
Chapter 6 Deploying Functions to Azure
138
Chapter 6 Deploying Functions to Azure
"location": "[parameters('location')]",
"properties": {
"name": "[variables('hostingPlanName')]",
"sku": "[parameters('sku')]",
"workerSize": "[parameters('workerSize')]",
"hostingEnvironment": "",
"numberOfWorkers": 1
}
},
{
"apiVersion": "2016-08-01",
"type": "Microsoft.Web/sites",
"name": "[variables('functionAppName')]",
"location": "[parameters('location')]",
"kind": "functionapp",
"properties": {
"name": "[variables('functionAppName')]",
"serverFarmId": "[resourceId('Microsoft.Web/
serverfarms', variables('hostingPlanName'))]",
"hostingEnvironment": "",
"clientAffinityEnabled": false,
"siteConfig": {
"alwaysOn": true
}
},
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', variables
('hostingPlanName'))]",
"[resourceId('Microsoft.Storage/storageAccounts',
variables('storageAccountName'))]"
],
139
Chapter 6 Deploying Functions to Azure
"resources": [
{
"apiVersion": "2016-08-01",
"name": "appsettings",
"type": "config",
"dependsOn": [
"[resourceId('Microsoft.Web/sites', variables
('functionAppName'))]",
"[resourceId('Microsoft.Storage/storageAccounts',
variables('storageAccountName'))]"
],
"properties": {
"AzureWebJobsStorage": "[concat('DefaultEndpoints
Protocol=https;AccountName=',variables('storage
AccountName'),';AccountKey=',listkeys(resourceId
('Microsoft.Storage/storageAccounts', variables
('storageAccountName')), '2015-05-01-preview').
key1,';')]",
"AzureWebJobsDashboard": "[concat('DefaultEndpoints
Protocol=https;AccountName=',variables('storage
AccountName'),';AccountKey=',listkeys(resourceId
('Microsoft.Storage/storageAccounts', variables
('storageAccountName')), '2015-05-01-preview').
key1,';')]",
"FUNCTIONS_EXTENSION_VERSION": "~1"
}
}
]
}
]
}
140
Chapter 6 Deploying Functions to Azure
Once the function is deployed using the CI/CD pipeline and you have
set up the staging slot, the function is deployed to the staging slot. The
following steps take you to the staging slot.
Go to the Azure portal and click Function Apps in the menu, as shown
in Figure 6-16.
Select the function for which you created the CI/CD pipeline. A pipeline
was created for durable-func-new-book, as shown in Figure 6-17.
141
Chapter 6 Deploying Functions to Azure
In the slot, you see the URL of the staging slot and the Swap option, as
shown in Figure 6-20. Now, you can test your function in the staging slot.
Once you are satisfied that things are running fine, you can swap the slot.
142
Chapter 6 Deploying Functions to Azure
To swap the slot, click the Swap button, as highlighted in Figure 6-21.
When you click Swap, the vertical screen open with option to swap. Once
you are satisfied with the values, click Swap again.
143
Chapter 6 Deploying Functions to Azure
Summary
This chapter examined setting up a CI/CD for your Azure function using
Azure DevOps, enabling deployment slots for testing your functions before
making changes to your functions in production. You also learned about
deploying functions using ARM templates. In the next chapter, you look at
what’s required to make functions production-ready.
144
CHAPTER 7
Getting Functions
Production-Ready
This chapter explains logging, Application Insights, securing functions,
and using cross-origin site scripting, and by the end, your function will be
production-ready.
This chapter covers the following topics.
• Securing functions
The context passed in Figure 7-2 has a log function, which you can use
to log at different levels. The log function has similar levels, such as Trace,
Debug, Information, Warning, Error, Critical, and None.
The host and function logs are stored in a file share in the storage
account associated with the Azure function under /LogFiles/
Application/Functions.
146
Chapter 7 Getting Functions Production-Ready
147
Chapter 7 Getting Functions Production-Ready
In the Basics tab, provide details about the function app, as shown in
Figure 7-4. (This was covered in Chapter 2.)
148
Chapter 7 Getting Functions Production-Ready
149
Chapter 7 Getting Functions Production-Ready
150
Chapter 7 Getting Functions Production-Ready
Once you are on the Application Insights creation page, provide details
such as the name, application type, resource group, and location, as shown
in Figure 7-7. Click Create.
151
Chapter 7 Getting Functions Production-Ready
152
Chapter 7 Getting Functions Production-Ready
153
Chapter 7 Getting Functions Production-Ready
154
Chapter 7 Getting Functions Production-Ready
You can configure which log levels can go to Application Insights for
these categories in the host.json file.
{
"logging": {
"fileLoggingMode": "always",
"logLevel": {
"default": "Information",
"Host.Results": "Error",
"Function": "Error",
"Host.Aggregator": "Trace"
}
}
}
So, now you are done, and your function is ready to be monitored
properly in production. Let’s see it in action in Figure 7-10.
155
Chapter 7 Getting Functions Production-Ready
156
Chapter 7 Getting Functions Production-Ready
As you can see, anyone who has your URL can access the function.
Since this is a basic function that does not interact with your database, it’s
OK. But consider a function like the OData API function you created in
Chapter 4. Now, if your endpoint is not secured (i.e., it does not require any
authentication/authorization), you invite hackers to easily get your data.
To avoid this, you must ensure that only authenticated users can access
your function. Let’s enable authentication/authorization for your function
using Active Directory.
157
Chapter 7 Getting Functions Production-Ready
158
Chapter 7 Getting Functions Production-Ready
On the next page, you can create a New AD app. Choose what
accounts are allowed to log in and how unauthenticated requests should
be handled. This creates the AD app and enables authentication/
authorization for this function, as shown in Figure 7-15.
159
Chapter 7 Getting Functions Production-Ready
160
Chapter 7 Getting Functions Production-Ready
You have now secured your function, and only the users in your AD
tenant can access this function.
161
Chapter 7 Getting Functions Production-Ready
162
Chapter 7 Getting Functions Production-Ready
You have enabled CORS on Azure Functions, and your function can
now be accessed from the domains you configured in Allowed Origins. To
enable all the URLs, set the Allowed Origins to *.
Summary
In this chapter, you learned about making your Azure Functions
production-ready by using the built-in logging capabilities, monitoring
using Application Insights, and securing your functions by configuring
authentication and CORS. The next chapter discusses some best practices
for working with Azure Functions.
163
CHAPTER 8
166
Chapter 8 Best Practices for Working with Azure Functions
By Privilege
Connection strings and other credentials stored in application settings
give all functions in the function app the same set of permissions in the
associated resource. Consider minimizing the number of functions with
access to specific credentials by moving functions that don’t use those
credentials to a separate function app. You can always use techniques
such as function chaining to pass data between functions in different
function apps.
167
Chapter 8 Best Practices for Working with Azure Functions
168
Chapter 8 Best Practices for Working with Azure Functions
169
Chapter 8 Best Practices for Working with Azure Functions
the app. Next time the function is triggered, the function’s runtime needs
to start and load dependencies before responding to the event. The total
time elapsed from the event occurring until the function responds to the
event is called cold start time (usually up to 10 seconds).
Implement a retry pattern in the calling app to wait and retry for a
response if it receives a time-out on the first attempt.
You can also create a function to periodically poll and invoke the other
functions to keep them “warm” even if they are not doing any actual work.
When a function needs to run, all the code is eventually loaded into
memory, which takes longer with a larger application. So, if the function
has many dependencies, the cold start times are longer due to increased
time for I/O operations from Azure Files and the longer time needed to
load them into memory.
For functions that cannot afford this latency, choose a Premium or App
Service plan so that the function’s host is always running.
170
Chapter 8 Best Practices for Working with Azure Functions
Manage Costs
Azure Functions’ instances in the Consumption plan automatically scale to
zero when there are no triggers for the functions. However, you are billed
for Application Insights depending on the volume of events collected. The
monitoring costs can become substantial and exceed the cost of Azure
Functions. Configure the log level to exclude Informational events if they
are not necessary for production. Application Insights also allows you to
specify sampling settings to reduce traffic and storage costs.
While inbound or ingress traffic to Azure is free, outbound or egress
traffic from Azure is billed. So, networking costs will apply if your Azure
Functions serve data to the outside world. Depending on the traffic
and volume of data transferred, these costs can add to your overall
application costs.
The storage account associated with the function is also charged for
the storage used. Although these charges are insignificant, they get added
to your total Azure spend.
Make sure that you understand the pricing model and closely monitor
your usage and Azure bill.
Summary
With this, you have come to the end of the book. We hope this book helped
you start your journey with building serverless applications on Azure. You
will learn more as you dig into the concepts explained in this book and
start building your own functions. Happy learning!
171
Index
A APPLICATIONINSIGHTS_
CONNECTION_STRING
Activity function, 81, 89, 92, 96, 98
key, 147
Activity trigger, 89
Application performance
message visibility, 90
monitoring (APM), 169
queues, 89
App Service plan, 9, 10,
return values, 90
135–143, 166
threading, 90
App Service (Slot), 121, 122
Admin authorization level, 21
Async HTTP APIs pattern, 84–86
Anonymous, 21, 64, 91, 105
Authenticated users, 157
Application insights, 171
Authorization-level function, 20
Azure Monitor, 147
Autoscaling, 97, 98
built-in logging, 154
Azure App Service, 6, 123
categories and log levels, Azure build providers, 124
configuration, 154–156 Azure data storage services, 110
CORS, 161–163 Azure DevOps account
integration, 147–150 bill, setup, 118
logging details, 147 link, subscription, 119
manual connection, 150–153 Azure DevOps configuration, 125
securing azure functions, Azure functions, 154, 170
147, 156–161 advanced tools, 25
Application insights, Azure application-level extensions, 26
functions app service plan, 9, 10
connection, 147 vs. Azure Logic Apps, 7, 8
manual connection, 152 vs. Azure WebJobs, 5, 6
APPLICATIONINSIGHTS_ cold start problem, 166
CONNECTION_ consumption plan, 9
STRING, 153 core tools, 34
174
INDEX
175
INDEX
176
INDEX
H I
History table, 94 ILogger, 105, 146
host.json file, 114, 155 Instance table, 95
HTTP Function boilerplate Integrated development
code, 21 environment (IDE), 3
HTTPTrigger, 30 Internal queue triggers, 95, 96
HttpTriggerCSharp.cs file, 68 Inter-service dependencies, 167
HTTP triggered function, Inventory management service, 55
20, 156 Isolated functions, 56
C#
create function, 62 J, K, L
CustomerModel.cs file, 66
JavaScript, 43, 146
folders creation, 66–68
language selection, 62
local URL, 70, 71 M
namespace, 64 Messaging services, 168
naming function, 62, 63 Microservices architecture, 54
NET 6.0 Isolated LTS benefits, 52, 53
runtime version, 63 nano services, 52
output, 70 trade-offs, 53
SqlClient package, 64, 65 Microsoft.Extensions.Logging, 146
template selection, 63 Monitoring functions, 145, 156
OData API, 71–77 Monitoring pattern, 86, 87
query output, OData API, 77 Monolithic applications
SQL server creation azure function, 54–56
connect to database, 60 Monolithic architecture
database creation, 58–60 APIs, 51
SQL database, vs. microservice
selection, 57 architecture, 52–54
tables, 61
HTTP-triggered public-facing N
APIs, 167
Nano services, 52
Human interaction, 87–89
Networking costs, 171
177
INDEX
O Q
OData API function, 157 QueueTrigger, 30
Open data protocol, 71
Orchestration trigger
activity function, 92 R
basic code, 92 Register binding extensions, 32
control queues, 93 Robust functions development
instances, 90 asynchronous patterns, 170
JSON object, 91 delays, function cold starts,
logic execution, 92 169, 170
message visibility, 91 messaging services, 168
queues, 90, 93 monitoring tools
return values, 91 integration, 169
single threading, 91 statelessness design, 168
OrchestrationTrigger, 92 storage accounts, 169
Orchestrator_City, 92 Routing strategy, 110
Orchestrator client, 93
Orchestrator function, 79, 80, 98, 99
Orchestrator scale-out, 96, 97
S
OrderFood function, 85 ScheduleConfiguration, 33
OrderProcess, 86 Seamless integration, 4
OrderProcess function, 85 Securing Azure functions
AD app ID, creation, 160
Serverless applications, 171
P Serverless computing, 2, 3
Performance targets, 99 Service bus trigger, 30
Platform features, 142 Single Premium plan, 166
Polyglot programming, 53 Software industry, 1
Premium plan, 10 SqlClient package, 65
Pricing model, 171 SqlClientHelper.cs file, 67
Programming languages, 4 SQLClientHelper.GetData
Publish, 15 method, 68
178
INDEX
V
T, U Virtual machine (VM), 9
TaskHubNameHistory, 94 Visual Studio, 34
Timers and compensation logic, 87 Visual Studio Code, 34
TimerTrigger, 30 Visual Studio Team
Traffic Manager, 110 Service (VSTS), 126
Trigger
Blob, 30
definition, 29 W, X, Y, Z
EventHub, 30 Work-item queue, 96
179