March 20th, 2020

BlobStorageEventTriggersJavascriptCLI

Using Azure Function Apps to process Blob Storage event triggers

Today's post is a tutorial on how to use Azure App Functions to process blob event triggers from Azure Blob Storage using only command line tools and Javascript.

Introduction

Often times when receiving files in Azure Blob Storage it is necessary to perform some action on each file as they are uploaded. This can include things like renaming files based on filenames, moving files to other locations, extracting data to load into a database, compressing, or decrypting. While products like Microsoft Power Automate (formerly Microsoft Flow) can accomplish some of these simpler tasks, most of the time you will need the flexibility of writing custom code. A very powerful low-cost solution to this problem is Azure App Functions coupled with blob event triggers.

For this example, we are going to be assuming that each blob that is uploaded has been PGP encrypted with a password, and we are going to decrypt it and store it on another path in the same blob container.

To complete the steps outlined below you will need to install:

Let's get started...

Part 1. Create the Azure resources

First we are going to use the Azure CLI to create the necessary resources. Note that Storage Account and Function App names must be globally unique so you may need to change those values when you run this.

Create a resource group

This resource group will hold all of our resources. At the end of the tutorial, we can delete this resource group to delete all associated resources.

az group create --name AzureFunctionsTutorial --location westeurope

Create a storage account

Each Function App requires a Storage Account. We need to create that before we can create the Function App. We will also use a blob container in this same Storage Account to store the encrypted files which will trigger our decryption function.

az storage account create \
  --name azurefunctionsblobs \
  --kind StorageV2 \
  --location westeurope \
  --resource-group AzureFunctionsTutorial \
  --sku Standard_LRS

Create the Function App

Now create the Function App. Note that the Storage Account is the one created in the previous step and that the consumption plan location is in the same location as our resource group.

az functionapp create \
  --name AzureFunctionsTutorialBlog \
  --storage-account azurefunctionsblobs \
  --consumption-plan-location westeurope \
  --resource-group AzureFunctionsTutorial

Create a blob container

We need to create a blob container in the Storage Account we created previously. This will be the blob container we upload our encrypted files to.

az storage container create --name tutorial --account-name azurefunctionsblobs

Configure app settings

this setting is going to be used by the function code later on. We are specifying the password we will use to decrypt incoming files.

az functionapp config appsettings set \
  --name AzureFunctionsTutorialBlog \
  --resource-group AzureFunctionsTutorial \
  --settings "DecryptionPassword=secretpass"

Part 2

Once all the required Azure resources are created we can begin developing the project. Create a folder for this project. All steps below assume that you are executing the commands from that folder.

Create an application

In the folder where you want to keep your project files, use the Azure Function Tools CLI to initiate a Node application. The application folder will contain a package.json and host.json file, as well as a sub-folder for each function in the application.

func init --worker-runtime node

Configure extension bundle

In order to set up a blob trigger function without installing dotnet we need to specify an extension bundle in the host.json file.

{
    "version": "2.0",
    "extensionBundle": {
        "id": "Microsoft.Azure.Functions.ExtensionBundle",
        "version": "[1.*, 2.0.0)"
    }
}

Create the function

We can now initialize the blob triggered Javascript function. This will create a new folder called BlobDecrypter.

func new --language javascript --name BlobDecrypter --template "Azure Blob Storage trigger"

Configure trigger settings

The BlobDecrypter/function.json file specifies the trigger settings for that function.

In the configuration below, the path property for the blogTrigger binding myBlob indicates that we only want to trigger the function when blobs are uploaded on the path inbox/ in a container called tutorial. Since we are going to be writing the decrypted files back to the same container we need to restrict the trigger to a specific path to prevent the function from triggering again when we write our decrypted files back to the container.

We will use an output binding to write the decrypted files back to the same blob container.

The property connection specifies the connection string of the Storage Account to use. Since we are leaving it blank, it will default to the value of AzureWebJobsStorage, which is the Storage Account that the function is using.

{
  "bindings": [
    {
      "name": "myBlob",
      "type": "blobTrigger",
      "direction": "in",
      "path": "tutorial/inbox/{name}",
      "connection": ""
    },
    {
      "name": "myOutputBlob",
      "type": "blob",
      "direction": "out",
      "path": "tutorial/decrypted/{name}",
      "connection": "",
    }
  ]
}


Add the code

The code below will load a password from the Function App configuration and use it to try to decrypt each triggered blob. The decrypted blob is then uploaded to a blob container under the path decrypted/. The location of this blob container is also retrieved from the Function App configuration.

Add the function code to BlobDecrypter/index.js

const openpgp = require('openpgp');

module.exports = async function (context, blob) {
  const decryptionPassword = process.env.DecryptionPassword;

  const { data: decrypted } = await openpgp.decrypt({
      message: await openpgp.message.read(blob),
      passwords: [ decryptionPassword ],
      format: 'binary'
  });

  // Write the decrypted blob to the container using the output binding.
  context.bindings.myOutputBlob = decrypted;
  context.done();
};

Install dependencies

We need to have all dependencies in our node_modules folder so Azure Functions Tools can package it along with our code when we publish.

npm install --save openpgp

Publish the function

That's it! We're all done. Time to package and upload the function to the Function App.

func azure functionapp publish AzureFunctionsTutorialBlog

Part 3

It's time to see the function in action. In this next part of the tutorial, we will create an encrypted file and upload it to our blob container. After about a minute we should be able to download the decrypted file.

Create an encrypted file

Create a file called create-encrypted-file.js and paste in the code below. This will create a password encrypted file from a short text string.

const openpgp = require('openpgp');
const fs = require('fs');

(async () => {
  const { message } = await openpgp.encrypt({
    message: openpgp.message.fromText("This is a secret message."),
    passwords: ['secretpass'],
    armor: false
  });

  const encrypted = message.packets.write();

  fs.writeFileSync('secret.txt', Buffer.from(encrypted));

})();

Now run the code using Node. An encrypted file called secret.txt will be created.

node create-encrypted-file.js

Upload the encrypted file

Use the Azure CLI to upload the file secret.txt to the inbox folder path of the blob container which our function is receiving events from.

az storage blob upload \
  --container-name tutorial \
  --account-name azurefunctionsblobs \
  --file secret.txt \
  --name inbox/secret.txt

Download decrypted

Now wait about a minute or so for the trigger to get processed, then download the decrypted file. At this point you can open the file and verify that it contains the string we encrypted in the previous step.

az storage blob download \
  --container-name tutorial \
  --account-name azurefunctionsblobs \
  --file secret.txt.decrypted \
  --name decrypted/secret.txt

Cleanup

That's it. We are done. Let's delete our resource group to clean everything up.

az group delete --name AzureFunctionsTutorial

Summary

In this blog post, you saw how to use the command line tools provided by Azure to quickly deploy a javascript function to process Blob Storage events. I hope you have enjoyed this post. Thanks for reading. If you are a Azure Blob Storage user keep scrolling to find out how FileMage Gateway could help you.

FileMage Gateway is a FTP and SFTP server that streams transfers directly to Azure Blob Storage.

Try a free test drive today on Azure Marketplace!
try it now find out more