How to create infrastructure with Serverless Framework (Azure)

NP
4 min readMar 21, 2021

The serverless framework’s azure provider is great. The azure function serverless plugin allows for creation of Azure functions with ease. They also provide all the bindings you need to kick start your function with a new event from a service bus, a new drop in blob storage or even to process a new event on an EventHub topic.

But what if your function needs to store its processed data on another cloud resource? say a blob storage or service bus?

The good news is the azure functions plugin does provide a way for you to create those resources as well. Albeit it’s more of a hidden feature and something that isn’t well documented. I am doing to cover this scenario here with hopes that it will help someone out there.

Disclaimer: This article assumes that you have a good understanding what serverless framework is (and does) and you also have a basic understanding of development for azure and ARM templates.

The documentation for the plugin, specifies that you can provide a path to an ARM template. The field to provide in your severless.yml is armTemplate . Here is an example:

service: connect-node-listener-azureframeworkVersion: '2'provider:
name: azure
region: EAST US
runtime: nodejs12
prefix: "sample" # prefix of generated resource name
subscriptionId: xxxx-xxxx-xxxxx-xxxxx
armTemplate:
file: template.json
environment:
SVC_BUS_QUEUE_NAME: connect-queue
plugins:
- serverless-azure-functions
package:
exclude:
- local.settings.json
- .vscode/**
functions:
connect:
handler: handler.connectNotificationMessage
events:
- http: true
methods:
- GET
authLevel: anonymous # can also be `function` or `admin`

template.jsonis a file that contains the ARM definition. In my case here, it includes the definition for creating a service bus queue. The queue is a location where my function will store its results on.

{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"namespaces_Connect_Events_name": {
"defaultValue": "Connect-Events-DS",
"type": "String"
}
},
"variables": {},
"resources": [
{
"type": "Microsoft.ServiceBus/namespaces",
"apiVersion": "2018-01-01-preview",
"name": "[parameters('namespaces_Connect_Events_name')]",
"location": "East US",
"sku": {
"name": "Basic",
"tier": "Basic"
},
"properties": {
"zoneRedundant": false
}
},
{
"type": "Microsoft.ServiceBus/namespaces/AuthorizationRules",
"apiVersion": "2017-04-01",
"name": "[concat(parameters('namespaces_Connect_Events_name'), '/RootManageSharedAccessKey')]",
"location": "East US",
"dependsOn": [
"[resourceId('Microsoft.ServiceBus/namespaces', parameters('namespaces_Connect_Events_name'))]"
],
"properties": {
"rights": [
"Listen",
"Manage",
"Send"
]
}
},
{
"type": "Microsoft.ServiceBus/namespaces/queues",
"apiVersion": "2018-01-01-preview",
"name": "[concat(parameters('namespaces_Connect_Events_name'), '/connect-queue')]",
"location": "East US",
"dependsOn": [
"[resourceId('Microsoft.ServiceBus/namespaces', parameters('namespaces_Connect_Events_name'))]"
],
"properties": {
"lockDuration": "PT30S",
"maxSizeInMegabytes": 1024,
"requiresDuplicateDetection": false,
"requiresSession": false,
"defaultMessageTimeToLive": "P14D",
"deadLetteringOnMessageExpiration": false,
"enableBatchedOperations": true,
"duplicateDetectionHistoryTimeWindow": "PT10M",
"maxDeliveryCount": 10,
"status": "Active",
"enablePartitioning": false,
"enableExpress": false
}
},
{
"type": "Microsoft.ServiceBus/namespaces/queues/authorizationRules",
"apiVersion": "2018-01-01-preview",
"name": "[concat(parameters('namespaces_Connect_Events_name'), '/connect-queue/sendandlisten')]",
"location": "East US",
"dependsOn": [
"[resourceId('Microsoft.ServiceBus/namespaces/queues', parameters('namespaces_Connect_Events_name'), 'connect-queue')]",
"[resourceId('Microsoft.ServiceBus/namespaces', parameters('namespaces_Connect_Events_name'))]"
],
"properties": {
"rights": [
"Listen",
"Send"
]
}
}
],
"outputs": {
"hostname": {
"type": "string",
"value": "[listKeys(resourceId('Microsoft.ServiceBus/namespaces/AuthorizationRules',parameters('namespaces_Connect_Events_name'),'RootManageSharedAccessKey'),'2015-08-01').primaryConnectionString]"
}
}
}

Now if you run sls deploy you’ll see that serverless will create the Azure function resources as well as the service bus we specified in our ARM template:

It really is that simple and it’s a pity that this feature isn’t documented at all.

The fun, however, ends here. As of right now, I couldn’t find any ways to reference the resources that were created via my ARM templates in my serverless.ymlfile. This is wildly important since as I mentioned earlier, my Azure function will be storing data on this Service Bus queue and I need to know it’s connection string in my function’s application settings.

This is a missing feature and I have submitted a feature request to the plugin owners. I would love to be the one implementing it, only if weekends had 4 days in them.

For now, I went down the route of using a simple workaround. I created a simple bash script that grabs the connection str from service bus and updates the azure function application settings with it:

#!/bin/bashecho "Be sure to run 'az login' first"echo " "echo "What is your resource group name?"read rgecho "What is your FunctionApp name?"read functionAppaz deployment group create --name sdDeployment --resource-group $rg --template-file template.jsonconnectionstr=$(az servicebus namespace authorization-rule keys list --resource-group $rg --namespace-name Connect-Events-DS --name RootManageSharedAccessKey --query primaryConnectionString --output tsv)az functionapp config appsettings set --name $functionApp --resource-group $rg --settings "SVC_BUS_CONNECTION_STRING=$connectionstr"

Then I bundled the script in a command for npm to deploy and configure.

Please keep in mind that such workaround only works with small deployments like mine. For a bigger and more complex scenario, always go down the route of Terraform scripts. They’re very versatile and easy to use. They can in fact, even do the same deployments that serverless does quite easily (as there are many sample scripts online for azure functions you can already use).

Ultimately, I still feel this would be an interesting feature to add to Serverless.

I hope you found this article useful. Good luck.

--

--

NP

A tech enthusiast who resides in a jungle.