Skip to content

Instantly share code, notes, and snippets.

@thoughtful-solutions
Last active November 10, 2020 08:05
Show Gist options
  • Select an option

  • Save thoughtful-solutions/cb0f22f47cce0b5354fb419a7228c7cc to your computer and use it in GitHub Desktop.

Select an option

Save thoughtful-solutions/cb0f22f47cce0b5354fb419a7228c7cc to your computer and use it in GitHub Desktop.
FaaS-on-Azure

FaaS-on-Azure

Setting up the Desktop Environment

On Windows

iwr -useb get.scoop.sh | iex

Then

scoop install nodejs-lts
scoop install azure-cli
scoop install azure-functions-core-tools
scoop install openssl
scoop install git
scoop install gow

On macOS

Install HomeBrew

brew update && brew install azure-cli
brew tap azure/functions
brew install azure-functions-core-tools@3

Setting up Azure and Login

   az login
   az account list-locations -o table  
   az account list --query "[?name=='Microsoft Partner Network'].tenantId" -o tsv
   az account set --subscription "Microsoft Partner Network"

Configure Resources for the Functions

  • Storage account name must be [a-z][0-9]{3..24}
  • az functionapp --name the must be globally unique
  • func azure functionapp publish must be in the directory in which the code exists (LocalFunctionProj in this example)
  az login
  az account set --subscription "Microsoft Partner Network"
  az storage account list -o table
  az group create --name mtiexample000rg --location westeurope
  az storage account create --name mtiexample000sa --resource-group mtiexample000rg --location westeurope --kind StorageV2 --sku Standard_LRS
  az storage container create --auth-mode login --account-name mtiexample000sa --name mtiexample000sc
  az functionapp plan create --name mtiexample000sp --resource-group  mtiexample000rg --location westeurope --sku B1
  az functionapp create --runtime node --runtime-version 10 --functions-version 2 --resource-group mtiexample000rg --storage-account mtiexample000sa --name mtiexample000sp --name mti-example --plan  mtiexample000sp

Check we can build functions

    func init mti --javascript
    cd mti
    func new --name mti-example --template "HTTP trigger"

The func new command also creates sub-directory mti-example

  • index.json (This is the implementation of the function)
  • function.json (this contains the binding of the function to services and triggers)

Confirming local behaviour

Inside the mti-example directory execute

   func start

This will confirm that your code is correctly configure

  curl -l http://localhost:7071/api/mti-example?name=test

This should return as before but also commit the output to a message queue

Having demonstrated this working you can Ctrl-C the window with the func Start executing and then you can publish the function to Azure

Confirming remote behaviour

the --force on the publish appears to be required.. It appears to be an issue with runtime environment versioning

func azure functionapp publish mti-example --force --javascript 

This will produce output like

Getting site publishing info...
Creating archive for current directory...
Uploading 1.31 KB [###############################################################################]
Upload completed successfully.
Deployment completed successfully.
Syncing triggers...
Functions in mti-example:
    mti-example - [httpTrigger]
        Invoke url: https://mti-example.azurewebsites.net/api/mti-example?
        code=Aj0/VfBBRLteG6VkL8nCaMkzRK8mrBUVjxsoBTdwdqCV34m9bm7nEw==

Remember that the code= parameter is the key to access the function so its unique to deployment

Curl for remote behaviour

curl -l "https://mti-example.azurewebsites.net/api/mti-example?\
code=Aj0/VfBBRLteG6VkL8nCaMkzRK8mrBUVjxsoBTdwdqCV34m9bm7nEw==&name=fred"

Produces

Hello, fred. This HTTP triggered function executed successfully.

and

curl -l "https://mti-example.azurewebsites.net/api/mti-example?  \
code=Aj0/VfBBRLteG6VkL8nCaMkzRK8mrBUVjxsoBTdwdqCV34m9bm7nEw=="

Produces

This HTTP triggered function executed successfully. 
Pass a name in the query string or in the request body for a personalized response.

Curl for JSON remote behaviour

It is also possible to directly post JSON files

curl -X POST -H "Content-Type: application/json"  \
   -d "{\"name\": \"edmund\"}"  \
   http://localhost:7071/api/mti-example 

Remeber that the quotes matter.

List Azure Functions deployed and keys

az functionapp list -o table
az functionapp show --resource-group mtiexample000rg --name mti-example -o table
func azure functionapp list-functions mti-example --show-keys

View Azure Logging for the function

  func azure functionapp logstream mti-example --browser  

Deployment configuration

  func azure functionapp fetch-app-settings mti-example

Outward bindings on function.json

in the mti-example\function.json file change it to look like this

{
  "bindings": [
    {
      "authLevel": "anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    },
    {
      "name": "outputBlob",
      "direction": "out",
      "type": "blob",
      "path": "mtiexample000sc/{rand-guid}",
      "connection": "AzureWebJobsStorage"
    }
  ]
}
  • outputBlob is the JavaScript Function into which data is written
  • mtiexample000sc is the azure Storage Blob Container into which the data is stored. {rand-guid} is a guid to create the filename
  • AzureWebJobsStorage is the connection string

Outward bindings on index.js

in the mti-example\index.js file change it to look like this

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.bindings.outputBlob = req.body

    context.res = {
        // status: 200, /* Defaults to 200 */
        body: responseMessage
    };
}
  • the req.body gets placed into context.bindings.outputBlob

Publish the changes

func azure functionapp publish mti-example

A new URL will be given to use the service in Azure.

Verify behaviour

 func azure functionapp list-functions mti-example --show-keys

Gives us the URL to access the function

  az storage container list --account-name mtiexample000sa -o table

Gives us a list of the Storage Containers

We can test the function with

curl -X POST -H "Content-Type: application/json"  -d "{\"name\": \"edmund\"}" https://mti-example.azurewebsites.net/api/mti-example

Show Blob storage contents

    az storage blob list --account-name mtiexample000sa --container-name mtiexample000sc --auth-mode login  -o table

Provides a list of the blobs in the container

   az storage blob download --account-name mtiexample000sa --container-name mtiexample000sc --auth-mode login -n 53c89099-589d-4626-99c2-4d24961110c2 -f t.out

downloads a blob and places it in a file t.out

Destroying what is built

az group list -o table
az group delete --resource-group mtiexample000rg

Appendix

Stuff learnt along the way

This is not in any sensible order

CosmosDB

  • Resource group is used to agregate resources
  • Storage Account provisions a particularly availability of Storage
  • CosmosDB Account is the principal which looks after the CosmosDB service
  • SQLDB is created by the CosmosDB Account, in a particularly ResourceGroup
  • SQL Container is use to partition the SQLDB accross multiple instances of the CosmosDB service (for scale). The partition key must be reflected as a column in the database
  az cosmosdb create --resource-group mtiexample000rg --name "mtiexample000cdba" --enable-free-tier
  az cosmosdb sql database create -a mtiexample000cdba -g mtiexample000rg -n mtiexample000db
  az cosmosdb sql container create  -a mtiexample000cdba -g mtiexample000rg -d mtiexample000db \
      --name  mtiexample000dbc  -p "/name"

You can confirm the connection strings

   az cosmosdb keys list -n mtiexample000cdba -g mtiexample000rg
   az cosmosdb keys list -n mtiexample000cdba -g mtiexample000rg  --type connection-strings

You can confirm the database

   az cosmosdb sql database  show -a mtiexample000cdba -g mtiexample000rg -n mtiexample000db
   az cosmosdb sql container show -a mtiexample000cdba -g mtiexample000rg -d mtiexample000db -n mtiexample000dbc

Azure Storage Queues

Examine the local.settings.json file key AzureWebJobsStorage contains the storage account connection string. Put this into an environmental variable as you'll use this lots

set AZURE_STORAGE_CONNECTION_STRING= \
"DefaultEndpointsProtocol=https;EndpointSuffix=core.windows.net;AccountName=mtiexample000sa;\
 AccountKey=XH34KYJIQJINA5wFWN1AyAqhkM9jVy5IkS33CbKYv29p9XUNYvQVek52OTwNQWLGBVWvnR+FmYgnTn1l0LwIEg=="
 
 az storage queue list

Should work and show you have no queues currently created for that storage connection string.

az storage queue create --name outqueue

Checking Storage Queue

az storage message get --queue-name outqueue -o tsv --query [].{Message:content}

returns the parameter base64 encoded

az storage message get --queue-name outqueue -o tsv --query [].{Message:content} | openssl base64 -d
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment