Durable Functions: Part 1 – The Intro

No Code in this post. Here we establish the starting point

Event Driven Programming is a popular way to approach complex systems with a heavy emphasis on breaking applications apart and into smaller, more fundamental pieces. Done correctly, taking an event driven approach can make coding more fun and concise and allow for “specialization” over “generalization”. In doing so, we get closer to the purity of code that does only what it needs to do and nothing more, which should always be our aim as software developers.

In realizing this for cloud applications I have become convinced that, with few exceptions, serverless technologies must be employed as the glue for complex systems. The more they mature, the greater the flexibility they offer for the Architect. In truth, not using serverless can, and should be, viewed in most cases as an anti-pattern. I will note that I am referring explicitly to tooling such as AWS Lamba, Google Cloud Functions, and Azure Functions, I am not speaking to “codeless” solutions such as Azure Logic Apps or similar tools in other platforms – the purpose of such tools is mainly to allow less technical persons to build out solutions. Serverless technologies, such as those mentioned, remain in the domain of the Engineer/Developer.

Very often I find that engineers view serverless functions as more of a “one off” technology, good for that basic endpoint that can run in Consumption. As I have shown before, Azure Functions in particular are very mature and through the use of “bindings” can enable highly sophisticated scenarios without need for writing excessive amounts of boilerplate code. Further, offerings such as Durable Functions in Azure (Step Functions in AWS) enable serverless to go a step further and actually maintain a semblance of state between calls – thus enabling sophisticated multi-part workflows that feature a wide variety of inputters for workflow progression. I wanted to demonstrate this in this series.

Planning Phase

As with any application, planning is crucial and our File Approver application shall be no different. In fact, with event driven applications planning is especially crucial because while Event Driven systems offer a host of advantages they also require certain questions to be answered. Some common questions:

  • How can I ensure events get delivered to the components of my system?
  • How do I handle a failure in one component but success in another?
  • How can I be alerted if events start failing?
  • How can I ensure events that are sent during downtime are processed? And in the correct order?

Understandable, I hope, these questions are too big to answer as part of this post but, are questions I hope you, as an architect, are asking your team when you embark on this style of architecture.

For our application, we will adopt a focus on the “golden path”. That is, the path which assumes everything goes correctly. The following diagram shows our workflow:

Our flow is quite simple and straightforward

  • Our user uploads a file to an Azure Function that operates off an HttpTrigger
  • After receiving this file, the binary data is written to Azure Blob Storage and a related entry is made in Azure Table Storage
  • The creation of the blob triggers Durable Function Orchestration which will manage a workflow that aims to gather data about the file contents and ultimately allow users to download it
  • Our Durable workflow contains three steps, two of which will pause our workflow waiting for human actions (done via Http API calls). The other is a “pure function” that is only called as part of this workflow
  • Once all steps are complete the file is marked available for download. When requested the Download File function will return the gathered metadata for the file AND the generated SAS Token allowing persons to download the file for a period of 1hr

Of course, we could accomplish this same goal with a traditional approach but, that would leave us to write a far more sophisticated solution than I ended up with. For reference, here is the complete source code: https://github.com/jfarrell-examples/DurableFunctionExample

Azure Function Bindings

Bindings are a crucial components of efficient Azure Function design, at present I am not aware of a similar concept in AWS but, I do not discount its existence. Using bindings we can write FAR LESS code and make our functions easier to understand with more focus on the actual task instead of logic for connecting and reading from various data source. In addition, the triggers tie very nicely into the whole Event Driven paradigm. You can find a complete list of ALL triggers here:

https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-storage-blobNote: this is a direct link to the Blob storage triggers, see the left hand side for a complete list.

Throughout my code sample you will see references to bindings for Durable Functions, Blobs, Azure Table Storage, and Http. Understanding these bindings is, as I said, crucial to your sanity when developing Azure Functions.

Visual Studio Code with Azure Function Tools

I recommend Visual Studio Code when developing any modern application since its lighter and the extensions give you a tremendous amount of flexibility. This is not to say you cannot use Visual Studio, the same tools and support exist, I just find Visual Studio Code (with the right extensions) to be the superior product, YMMV.

Once you have Visual Studio Code you will want to install two separate things:

  • Azure Functions Extension VSCode
  • Azure Function Core Tools (here)

I really cannot say enough good things about Azure Function Core Tools. It has come a long way from version 1.x and the recent versions are superb. In fact, I was able to complete my ENTIRE example without ever deploying to Visual Studio, using breakpoints all along the way.

The extension for Visual Studio Code is also very helpful for both creating and deploying Azure Functions. Unlike traditional .NET Core applications, I do not recommend using the command line to create the project. Instead, open Visual Studio Code and access your Azure Tools. If you have the Functions extension installed, you will see a dedicated blade – expand it.

The first icon (looks like a folder) enables you to create a Function project through Code. I recommend this approach since it gets you started very easily. I have not ruled out the existence of templates that could be downloaded and use through dotnet new but this works well enough.

Keep in mind that a Function project is 1:1 with a Function app so, you will want to target an existing directory if you play to have more than one in your solution. Note that this is likely completely different in Visual Studio, I do not have any advice for that approach.

When you go through the creation process you will be asked to create a function. For now, you can create whatever you like, I will be diving into our first function in Part 2, as you create subsequent functions use the lightning icon next to the folder. Doing this is not required, it is perfectly acceptable to build your functions up but, using this gets the VSCode settings correct to enable debugging with the Core Tools so, I highly recommend it.

The arrow (third icon) is for deploying. Of course, we should never use this outside of testing since we would like a CI/CD process to test and deploy code efficiently – we wont be covering CI/CD for Azure Functions in this series but, we will certainly in a future series.

Conclusion

Ok so, now we understand a little about what Durable Functions are and how they play a role in Event Driven Programming. I also walked through the tools that are used when developing Azure Functions and how to use them.

Moving forward into Part 2, we will construct our File Upload portion of the pipeline and show how it starts our Durable Function workflow.

Once again the code is available here:
https://github.com/jfarrell-examples/DurableFunctionExample

Connect to Azure SQL with MSI

Quick what type of password cannot be cracked? The answer is, one that is not known to anyone. You cannot reveal what you do not know. This is why so many people use Password Managers, we create insanely long passwords that we cannot remember, nor do we need to, and use them – their length and complexity makes it very difficult to crack. Plus, by making it easy to create these kinds of passwords we can avoid the other problem where the same password is used everywhere.

If you were to guess which password you would LEAST like to be compromised I am willing to bet many of you would indicate the password your web app uses to communicate with its database. And yet, I have seen so many cases where passwords to database are stored in web.config and other settings files in plain text for any would be attacker to read and use at their leisure. So I figured tonight I would tackle one of the easiest and most common ways to secure a password.

Remember RBAC

If you have been following my blog you know that, particularly of late, I have been harping on security through RBAC (Role Based Access Control). In the cloud especially, it is vital that applications only have access to what they need to carry out their role, such is the emphasis of least privileged security.

In Microsoft Azure, as well as other cloud platforms, we can associate the ability to read and update a database with a particular role and grant our web application an identity that is a member of that role. In doing so, we alleviate ourselves from having to manage a password while still ensuring that the application can only access data relevance to its task and purpose.

Microsoft actually has a doc article that details the steps I am going to take quite well. I will be, more or less, running through it and will enumerate any gotchas Link: https://docs.microsoft.com/en-us/azure/app-service/app-service-web-tutorial-connect-msi

Creating your API

As I often do, I like to start from the default project template for a .NET Core Web API project. This means I have a basic API setup with the WeatherForecast related assets. The first goal will be to set this up as an EF Core driven application that auto creates its database and seeds with some data – effectively we are going to replace the GET call with a database driven select type operation.

To aid with this, and to remove myself from writing out how to build an API I am providing the source code here: https://github.com/jfarrell-examples/DatabaseMSI. From this point I will only call out certain pieces of this code and shall assume, moving forward, you have an API that you can call an endpoint and it will return data from the database.

Create a Database Admin

For the majority of these steps you will want to have the Azure CLI installed and configured for your Azure instance. You can download it here

Access Azure Active Directory from the Portal and create a new user. You can find this option off the main landing in the left navigation sidebar. Your use does not need to be anything special though, I recommend setting the password yourself.

Once the user is created, open a private window or tab and log in to https://portal.azure.com as that user. You do this to validate the account and reset the password, it shows as Expired otherwise. We are going to use this user as your Azure SQL Admin (yes, I assume you already created this).

The tutorial linked above provides a command line query to search for your newly created Azure AD User and get its corresponding objectId (userId for the uninitiated). I personally prefer just using the following command:

az ad user list –query “[].{Name: displayName,Id: objectId}” -o table

This will format things nicely and require you only to look for the display name you gave the user. You will want to save the objectId to a shell variable or paste it somewhere you can easily copy it.

az sql server ad-admin create –resource-group <your rg> –server-name <db-server-name> –display-name ADMIN –object-id <ad-user-objectId>

This command will install our user as an admin to the target SQL Server. Replace the values above as shown. You can use whatever you like for display-name.

Congrats you have now linked the AD User to SQL Server and given them admin rights. We wont connect as this user, but we need this user to carry out certain tasks.

Configure the WebAPI to Support MSI Login

As a note, the link above also details the steps for doing this with ASP .NET, I wont be showing that, I will be focusing only on ASP .NET Core.

We need to inform that which is managing our database connection, if anything (EF Core for me in this case) that we are going to use MSI authentication. As with most MSI related things, this will entail getting an access token from the identity authority within Azure.

Open the DbContext and add the following code as part of your constructor:

For this to work you will need to add the Microsoft.Azure.Services.AppAuthentication NuGet package but the rest of this can be pasted in as a good starting point.

I have also added a AppSettings Env which denotes the present environment. In this case, since I am showing an example I will only have Local and Cloud. In professional projects the set of allowable values is going to be higher. From a purpose standpoint, this allows the code to use a typical connection method (username and password) locally.

Remember, it is essential that, when developing systems that will access cloud resources, we ensure a solid way for developers to interact with those same resources (or a viable alternative) without having to change code or jump through hoops.

The final bit is to prepare our connection string for use in the Cloud with MSI. In .NET Core this change is absurdly simple, the tutorial shows this but you will want this connection string to be used in your cloud environments:

“Server=tcp:<server-name>.database.windows.net,1433;Database=<database-name>;”

With this in place, we can now return to the Cloud and finish our setup.

Complete the manged identity setup

The use of MSI is built upon the concept of identity in Azure. There are, fundamentally, two types: user defined and system assigned. The later is the most common as it allows Azure to manage the underlying authentication mechanics.

The enablement of this identity for your Azure Resources is easy enough from the portal but, it can also be done via the Azure CLI using the following command (available in the tutorial linked above):

az webapp identity assign –resource-group <rg-name> –name <app-name>

This will return you a JSON object showing the pertinent values for your Managed Identity – copy and paste them somewhere.

When you use a Managed Identity, by default, Azure will name the identity after the resource for which it applies, in my case this was app-weatherforecast. We need to configure the rights within Azure SQL for this identity – to do that, we need to enter the database.

There are a multitude of ways to do this but, I like how the tutorial approaches it using Cloud Shell. sqlcmd is a program you can download locally but, I always prefer to NOT add additional firewall rules to support external access. Cloud Shell allows me to handle these kind of operations within the safety of the Azure Firewall.

sqlcmd -S <server-name>.database.windows.net -d <db-name> -U <aad-user-name> -P “<aad-password>” -G -l 30

This command will get you to a SQL prompt within your SQL Server. I want to point out that the add-user-name is the domain username assigned to the user you created earlier in this post. You will need to include the “@mytenant.domain” suffix as part of the username. You are logging in as the ADMIN user you created earlier.

When your application logs into the SQL Server it will do as a user with the name from the identity given (as mentioned above). To support this we need to do a couple things:

  • We must create a user within our SQL Server database that represents this user
  • For the created user we must assigned the appropriate SQL roles, keeping in mind principle of least privileged access

From the referenced tutorial, you will want to execute the following SQL block:

CREATE USER [<identity-name>] FROM EXTERNAL PROVIDER;
GO
ALTER ROLE db_datareader ADD MEMBER [<identity-name>];
GO
ALTER ROLE db_datawriter ADD MEMBER [<identity-name>];
GO
ALTER ROLE db_ddladmin ADD MEMBER [<identity-name>];
GO

Remember, identity-name here is the name of the identity we created earlier, or the name of your Azure resource if using System Assigned identity.

So, your case may vary but, deeply consider what roles your application needs. If you application will only be access the database to read you can forgo adding the datawriter and ddladmin roles.

If the database is already set in stone and you wont need new tables created by an ORM than you likely will not need the ddladmin role. Always consider, carefully, the rights given to a user. Remember, our seminal aim as developers is to ensure that, in the event of a breach, we limit what the attacker can do – thus if they somehow spoof our MSI in this case, we would want them to be confined to ONLY this database. If we used a global admin, they would then have access to everything.

Congrats. That is it, you now have MSI authentication working for your application.

Closing Thoughts

Frankly, there are MANY ways to secure the credentials for critical systems like databases in applications, from encryption, to process restrictions, to MSI – all have their place and all address the radically important goal of limiting access.

The reason I like MSI over many of these options is two principle reasons:

  1. It integrates perfectly into Azure and takes advantage of existing features. I always prefer to let someone else do something for me if they are better at it, and Microsoft is better at identity management than I am. Further, since we can associate with roles inside Azure its easier to limit access to the database and other systems the corresponding application accesses
  2. It totally removes the need to store and manage a password. As you saw above, we never referenced a password at any point. This is important since an attacker cannot steal what is never made available.

Attackers are going to find our information, they are going to hack our systems, we can do what we can to prevent this but, it will happen. So, the least we can do is make their efforts useless or limit what they can steal. Keep passwords out of source code, use Key Vault, and leverage good automation pipelines to ensure sensitive values are never exposed or “kept in an Excel somewhere”. Ideally, the fewer people that know these passwords the better.

The database is, for many applications, the critical resources and using MSI can go a long way to protecting our data and ensuring proper access and limit blast radius for attacks.

Thanks.

Customizing the Auth flow with Auth0

Using Auth0 for applications is a great way to offload user management and role management to a third party provider which can aid in limiting the blast radius of a breach. While, I will not yet be getting into truly customizing the login experience (UI related) I do want to cover how we can take control of the process to better hide our values and control the overall experience.

You dont control Authentication

In previous posts I discussed the difference between Authentication and Authorization and I bring it up here as well. For applications, Authentication information is the information we least want in the hands of an attacker – being able to log into a system legitimately can make it very hard to track down what has been lost and what has been compromised. This is why authentication mechanisms leveraging OAuth or OpenId rely on performing the authentication OUTSIDE of your system.

By performing the authentication outside of your system and returning a token your site never even see’s the user credentials and you cannot expose what you do not have. Google Pay and other contactless payment providers operate on a similar principal – they grab the credit card number and information for the merchant and pass back a token with the status.

Understanding this principle is very important when designing systems of authentication. Authorization information is less important in terms of data loss but, highly important in terms of proper provisioning and management.

For the remainder of this we will be looking mainly at how to initiate authentication in the backend.

Preparation

For this example, I constructed a simple ASP .NET Core MVC Application and created an Index view with a simple Login button – this could have been a link.

The goal here is to have a way for the user to initiate the login process. This post will not cover how to customize this Login Screen (hoping to cover that in the future).

Let’s get started

Looking at the code sample above you can see that, submitting the Form goes to a controller called Home and an action called Login. This does not actually submit anything because, remember, Auth0 operates as a third party and we want our users to login and be authenticated there rather than on our site. Our site only cares about the tokens that indicate Auth0 verified the user and their access to the specific application.

Here is the skeleton code for the action that will receive this Post request:

This is where the fun begins.

Auth0 Authentication API

OAuth flows are nothing more than a back and forth of specific URLs which first authorize our application request to log in, then authorize the user based on credentials, after which a token is generated and a callback URL is invoked. We want to own this callback URL.

Install the following Nuget package: Auth0.AuthenticationApi – I am using version 7.0.9. Our first step is to construct the Authentication Url which will send us to to Auth0 Lock which handles authentication.

So a few things here:

  • The use of the Client Id indicates to Auth0 for which application we want to authenticate against
  • Response Type is code meaning, we are asking for an authorization code that we can use to get other tokens
  • Connection indicates what connection scope our login can use. Connection scopes indicate where user credential information is stored (you can have multiple). In this case I specify Username-Password-Authentication which will disallow social logins
  • The redirect URI indicates what URL the auth code is passed to. In our case, we want it passed back to us so this URL is for another controller/action combination on our backend. Be sure your Application Settings also specify this URL
  • Scope is the access rights the given User will have. By default we want them to be able to access their information
  • State is a random variable, you will often see it as a value called nonce. This is just a random value designed to make the request unique

After we specify these values and call Build and ToString to get a URL that we can Redirect to. This will bring up the Login Lock screen for Auth0 to allow our user to present their credentials.

Receive the callback

Our next bit is to define the endpoint that will receive the callback from Auth0 when the login is successful. Auth0 will send us a code in the query string that indicates the login was successful.

This is not atypical for application which use this – if you ever looked at the Angular sample it too provides for a route that handles the callback to receive the code. Once we get the code we can ask for a token. Here is the complete code:

Here we are asking for authorization to the application and it will come with two pieces of information that we want – Access Token and Id Token. The former is what you pass to other APIs that you want to access (your permissions are embedded in this token) and the Id Token represents you user with all of their information.

To aid in what these tokens look like (wont cover Refresh tokens here) I have created this simple custom C# class and Razor view:

Successfully logging in will eventually land you on this Razor page where you should values for everything but AuthCode (there is no set in the code snippet). But something might strike you as weird, why is the access_token so short. In fact, if you run it through jwt.io you may find it lacks any real information.

Let’s explain.

By default Tokens can only talk to Auth0

In previous posts I have discussed accessing APIs using Auth0 Access Tokens. Core to that is the definition of an audience. I deliberately left his code off when we built our Authentication Url as part of login. Without it, Auth0 will only grant access to the userinfo API hosted in Auth0. If we also want that token to be good for our other APIs we need to register them with Auth0 and indicate our UI app can access it.

Before discuss this further, lets update our Auth URL building code as such:

The difference here is Line 12 where we specify our audience. Note that we are NOT allowed to specify multiple audiences so, when designing a microservice API this can be tricky. I want to cover this more in-depth in a later post.

With this in place you will see two things if you run through the flow again:

  • Your access token is MUCH longer and will contain relevant information for accessing your API
  • The refresh token will be gone (I cannot speak to this yet)

This will now return to your call the Access Token you can use and store to access APIs. Congrats.

Why use this?

So when would an approach this like this be practical? I like it for applications that need more security. You see, with traditional SPA application approaches you wind up exposing your client Id and potentially other information that, while not what I could sensitive, is more than you may want to expose.

Using this approach all of the information remains in the backend and is facilitated outside of the users control or ability to snoop.

Conclusion

In this post, I showed how you can implement the OAuth flow yourself using Auth0 API. This is not an uncommon use case and can be very beneficial should your application require tighter control over the process.

Controlling Azure Key Vault Access

Security is the name of the game with cloud applications these days. Gone are the days of willy-nilly handling of passwords and secrets, finding your production database password in web.config files and storing keys on servers in the earnest hope they are never discovered by an attacker. These days, great care must be given to not only controlling access to sensitive values but, ensuring proper isolation in the event of breach to limit the blast radius. For this reason, teams often turn to products like Azure Key Vault as a way to securely keep data off services and machines so would be attackers must go through yet another hoop to get the data they want.

As I work with teams I still find a lot of confusion around this aspect, in particular since teams can often find themselves in the Circle of Life as I call it – where they are constantly trying to hide their values but then must secure the way they are hiding values, and so on and so forth. I want to talk about the principles of isolation and impressing upon teams that, rather than try to prevent a hack, prevent the hacker from getting everything.

Footnote: Security is a huge topic, especially when it comes to Cloud products. I wont be going into everything, or even 25% of everything. When it comes to building applications the best advice I can give is, try to think like an attacker

Key Vault

For most software development teams, Key Vault is the most common way to achieve the pattern of keeping sensitive values off services and in a secure location. Key Vault fully encrypts everything and has three offerings, each with different use cases:

  • Keys – The keys feature is used mostly for encryption and decryption. It operates on the principle “you cannot reveal what you do not know”. These are managed keys which means, you, as the developer and accessor, do not know the actual raw value. This is very useful for asymmetric encryption, such as what is used with JWT, where the secrecy of the private key is paramount
  • Secrets – For most developers, this will be the feature you interact with the most. This lets you store known values and retrieve them via an HTTP call to the Key Vault. The values are versioned and are soft-deleted when removed. This gives you an ideal place to keep passwords and other sensitive values without revealing them in source code
  • Certificates – This feature lets you store certificates that can be used later. I honestly have not used this feature too much

A word of advice with Secrets, do not store everything here. Not everything is sensitive, nor does it require an extra lookup from a separate machine. Pick and choose carefully what is secret and what is not.

As an extra boon, you can “link” a Key Vault to a Variable Group in Azure DevOps Library. This lets you use sensitive values in your pipeline. Its a great way to inject certain values into services. But, always remember, what you send to a service has to get stored somewhere

Role Based Access Control (RBAC)

If you have spent any large amount of time around Cloud in the last 4-5 years you have likely heard the term RBAC thrown around. Modern cloud architectures are built around the notion of roles and we create specific roles for specific use cases. This enable very tight control over what a user/application can do and, more importantly, what they cannot do. Using roles also lessens the possibility of an application breaking when a login is removed or a password change is forced.

In Microsoft Azure, RBAC is mainly used (from the context of application development) with Managed Service Identity (MSI). In Azure we can either create this identity manually or ask Azure to create it for us (you will often see an Identity option for most Azure resources, this indicates to Azure that is should created a “System Managed identity”. Alternatively, you can create “User Managed Identity”. Each of these would be referred to, by Azure, as a Service Principal. Its important though to understand the difference between these two, especially how they aid code running locally and code running in the cloud.

User Managed Identity vs System Assigned Identity

Without getting too deep, Identities in Azure are held in Azure Active Directory service, which every tenant receives. User Managed identities are created using App Registrations, by this the creator can pick information like the name and, most importantly, the secret or certificate that acts as the password.

For System Assigned Identities, these are found under Enterprise Applications and most of its data is, as you would expect, managed by Azure. This means the Secret is unknown to you. This is ideal for a production scenario where, the less you know the less you can expose. In contrast, the App Registration is ideal for development and cases where we want the user to specify their identity verbosely.

When to use?

Let’s return to something I said earlier – “we cannot prevent ourselves from being hacked, we can minimize the blast radius of a breach”, this often refereed to as “defense in depth (or layers)”. Simply put, dont give the attacker something that will work everywhere.

  • If they get a login to a development environment, ensure that login does not work in Dev.
  • If your staging database password gets posted on reedit, make sure its not the same one you use for production
  • If the attacker compromises a role, ensure that role has access to only what it needs for the environment that it needs
  • Segregate your environments so they dont talk to each other. If an attacker breaches the virtual machine holding the dev version of your website, they should not be able to access the production servers

There are many more, but hopefully you get the idea. Taking this approach you can understand that, exposing sensitive data in an environment like development is acceptable as long as the information does not grant would-be attackers access to critical data.

Let’s take this approach and develop a simple application that uses Key Vault to return a sensitive value.

Create the Identities for Key Vault Access

Assuming you know how to create a Key Vault in Azure you need to define Access Policies. Key Vault’s do not support the notion of “granular access” which means when you define a permission that permission applies to everything in that category.

If you enable GET access to Secrets in your Key Vault, the authorized party can retrieve ALL secrets from that Key Vault. Consider this when defining your infrastructure, it is not uncommon for teams segregate data for apps across multiple key vaults to control access

Before you can grant access you need to define the parties requesting access. Create an Azure App Service. Once created, find the Identity option, switch it On and hit Save. After a time Azure will come back with an ObjectId – this Id represents the new Identity created (and managed) by Azure – the display name will be the same as the name of the service (the Azure App Service in this case).

Next, access the Azure Active Directory for your tenant, select App Registrations. Create a new Registration with any name you like (this will be the username of the Service Principal/Identity). Once created, select Certificates & Secrets from the left hand menu. Create a Secret and copy the value somewhere (you wont be able to see it again once you leave) – also take note of the Application (client id).

Go back to the top level of Azure Active Directory and select Enterprise Registrations – you should be able to search for an find your Managed Identity by its name (remember the name of the Azure App Service). Select it and note its Application (client id).

Ok, we have our identities, now we just need to use them.

Code the app

I created a simple WebApi with the following command:

dotnet new webapi -o KeyVaultTest

Install the following Nuget packages:

  • Azure.Security.KeyVault.Secrets (v4.0.3)
  • Azure.Identity (v1.1.1)

Here is the code for my controller

Thanks to Azure.Identity there are a number of ways to get credentials for accessing Azure services (here). Here I am using two, but the usage depends on the context.

  • If the environment is Local (my local machine) I use the ClientSecretCredential where I expose the values from the App Registration earlier. As I have said, I do not care if these values leak as they will only ever get a person into my Dev Key Vault to read the secrets there. I will NOT use this anywhere else and other environments will not support this type of access
  • If the environment is Development, I am running on Azure and can use ManagedIdentityCredential where Azure will interrogate the service (or VM) for the assigned identity. This has the advantage of exposing NOTHING in the environment to a would-be attacker. They can only do what the machine can already do so, the damage would be so confined.

By taking this approach we enable tighter security in the cloud while still allowing developers to easily do their jobs locally.

I tend to favor the environment injection approach since it still enables the code to be built one time. Using something like Precompiler directives works but requires the code to be special built for each environment

Conclusion

Security is vitally important for applications. It is important to do what we can to remove sensitive values from code bases and configuration files. We can use tools like Key Vault to ensure these values are kept safe and are not exposed in the event of a breach. We can further remove values by using RBAC to give identities access to only what they need. This enables us to have fewer values in code and makes our application easier to manage overall

Amend to previous – Jwt token mapping

When I write entries on this site I do my best to fully research and solve issues I encounter in a meaningful way in line with maintainability and cleanliness. Occasionally, I am not as successful in this endeavor as I would like – as was the case with how I referenced the the User Id from Auth0 in my previous entry: here

In this post, I expressed frustration at the lack of mapping for, what I believe are, common values that are part of the Jwt standard which meant a certain amount of digging and kludgy code to extract the needed value. I was wrong.

This evening, while researching something else, I stumbled across the proper way to achieve what I was looking for. It turns out, you can get it to work with a simple definition in the Startup.cs class when you configure the JwtBearer – see below:

You can see on line 11 what I mean. By doing this, you can change the controller code to the following:

Now, instead of casting and digging into the claims to perform matching, we can let .NET Core do it for us and simply use the .Name property on the resolved identity.

Neat, eh. This makes the code much cleaner and more straightforward.

Using the Management API with .NET Core 3.1

As part of my continuing dig into Auth0 I wanted to see how I could access user information for a user represented by a JWT Access Token issued by Auth0. For the uninitiated, Auth0 login will yield, to front end applications, 2 tokens: id_token and access_token.

The id_token is provided from Auth0 to represent the user who logged into the registered application. The access token is provided as a means to make calls to supported audiences. We talked a little about this in the previous post (Authorization with Auth0 in .NET Core 3.1). Thus, following login, we would keep the id_token stored as a way to identify our user to the application and the access_token to enable access to allowed APIs.

However, if you run each of these tokens through jwt.io you will see a large difference. Notably, the access_token contains NO user information, with the exception of the user_id as the subject (sub). The reason for this is to keep the payload small and to reduce the attack surface. The truth is, there is often no reason for the API being called to know anything about the user outside of permissions and the User_Id – passing around name, email, and other information is most often going to be superfluous and thus needs to be avoided.

The question is though, if we do need the user’s information, how can we get it? That is what I want to explore in this entry.

How should we view Auth0?

First and foremost, I want to point out that Auth0 is more than just a way to validate and generate tokens. Honestly, it should almost be viewed as a UserService as a Service. It replaces the service that would traditionally handle user related responsibilities. It even has the capability to integrate with existing accounts and data store mechanisms. As I have continued to use it, I have found that using it for login and user management is a speed boost to development and alleviates my teams from having to build the necessary UI that goes with such a system.

How do we get the UserId from our Token in .NET Core?

Honestly, I was disappointed here – I would have hoped .NET Core was smart enough to map certain well known claims to ClaimsIdentity properties so you could easily user the this.User property that exists on ControllerBase. Maybe I missed something but, given the popularity of JWT and token style logins, I would this this would be more readily available. This is how I ended up pulling out the UserId:

((ClaimsIdentity)User.Identity).Claims.First(x => x.Type.EndsWith(“nameidentifier”)).Value;
Auth0 sub claim gets mapped to the nameidentifier claim (endswith is used since its a namespaced type). I find this rather messy – I would expect common claims to be mapped to properties automatically or with some configuration – I was not able to find either.

Getting the User Information

Auth0 offers a standard API to all users known as the Management API. This is registered to your account with its own ClientId and Secret. When an access_token is provided we can use it, with a tenant level endpoint, to get an access_token to the Management API. Using this we can information about users, the tenant, just about anything.

First, you need to contact your tenant level OAuth endpoint and acquire an access_token that can be used to access the Management API. This call is relatively simple and is shown below – I am using the standard HttpClient class from System.Net.Http.

The client_id and client_secret are from the M2M application created for your API (under Applications) – hint, if you do not see this, access the API in question (WeatherApi for me) and select the API Explorer, this will auto create the M2M application with the client secret and id that are needed for this exercise.

Selection_011

The end result of this call is a JSON payload that contains, among other things, an access_token – it is this token we can use to access the Management API.

The best way to access the Management API with .NET is to use the supplied NuGet packages from Auth0:

  • Auth0.Core
  • Auth0.ManagementApi

The rest is very easy, we simply new up an instance of ManagementApiClient and call the appropriate method. Here is the complete code sample:

Really pretty straightforward – again my only complaint was getting the UserId (sub) out of the claims in .NET, I would expect it to be easier.

Final Thoughts

Centrally, I am starting to see Auth0 more as a service for user management than a simple vendor. If you think about it, having user data in a separate service does make things more secure since if you DB is breached the passwords are safe as they are physically not there. Further, you can take advantage, to a point, of the scale offered by Auth0 and the ability for it to integrate with services like Salesforce, Azure AD, Facebook, and other user info sources.

I am personally looking forward to using Auth0 extensively for my next project.

Authorization with Auth0 in .NET Core 3.1

Auth0 (https://auth0.com) remains one of the leaders in handling authentication and user management for sites. While it may seem odd to some to offload such a critical aspect of your application to a third party, the truth is, its not as far fetched as you think. Consider the popularity of touch to pay systems like Samsung Pay, Google Pay, and Apple Pay. Each of these (along with others) use a one time token exchange to allow payment. This enables payment information to be collected elsewhere lessening the impact in the event of a breach.

For this article, I wont dive to deeply into the intricacies of Auth0, its a very wide platform with a lot of functionality, a lot of which I am not an expert on. For my goal here, I wanted to show how I could use a Google and Username/Password login to access a service requiring a token and FURTHER how I could surface the permissions defined in Auth0 to facilitate authorization within our API (which uses .NET Core 3.1).

Authorization vs Authentication: Know the Difference

One of the key things that need to be understood when using Auth0 (or any identity provider) is the difference between Authentication and Authorization, in this case how it relates to APIs.

When we talk about Authentication we talk about someone have access to a system in a general sense. Usually, for an API, this is distinguished by the request having a token (JWT or otherwise) that is not expired and valid for the system. If this is not passed, or that which is passed is invalid the API shall return a 401 Unauthorized.

When we talk about Authorization we talk about whether someone CAN call an API endpoint. This is delineated through claims that are registered with the token. Information in these claims can be roles, permissions, or other data. When the given token does NOT supply the appropriate claims or data to access an endpoint the API shall return a 403 Forbidden.

Now that we understand that, let’s move on.

Establish a check for Authentication

There are multiple ways to go about this each with varying degrees of appropriateness. In my previous blog entry I did this through Kong (https://jfarrell.net/2020/06/11/kong-jwt-and-auth0/) and I will show it here in .NET Core as well.  What is the difference? It comes down to what you are trying to do.

With Kong JWT plugin you can ideally require authentication across MANY APIs that are being fronted by the Kong Gateway. In general, its very common when you have microservices that requires a token to place them behind such a gateway that enforces the token being present – this way you do not need to configure and maintain the middleware for such an operation across multiple APIs that may be in different languages and even managed by disparate teams (or external teams).

.NET Core (as with other API frameworks) supports this for the API itself either via a custom connection or an identity provider, such as Auth0.

To enable this feature you need to add two NuGet packages:

  • System.IdentityModel.Tokens.Jwt
  • Microsoft.AspNetCore.Authentication.JwtBearer

The middleware is configured in the Startup.cs file via the IServiceCollection::UseAuthentication extension method. The following code does this and instructs the service to contact Auth0 to validates the tokens authenticity.

The first thing to notice here is the Auth0:Domain value which is the full URL of your Auth0 tenant (mine is farrellsoft). This domain informs the underlying mechanisms where to look for the OAuth endpoints.

The second thing is Auth0:Audience and this is more specific to the OAuth flow. The audience is, in this context, a validation parameter. That is to say, we want to validate that the token being received can access our API (which is the audience). This will map to some unique value that identifies your API. In my case, this is https://weatherforecast.com. I do not own that URL, it is used as a way to identify this API.

When we authenticate to our frontend application, we specify what audience we want. Assuming the frontend application has access to that audience, we will receive a token that we can pass with API calls.

oauth1

This is another reason to look into a managed identity provider even beyond security. In high volume systems, the User API is constantly under load and can be a bottleneck if not thought through properly. You can imagine an architecture with MANY microservices, each needing to validate the token and get user information relevant to their task. Netflix has entire talks about this principle – with particular emphasis on the contribution of such services to cascade failures.

The final step is to enforce the presence of this token is the Authorize attribute on either the controller or specific endpoint method. Once you add this, not passing a valid JWT token will cause the middleware to return a 401 Unauthorized, as desired.

(Note: I am using the default controller implementation that comes out of the box with a new WebApi in .NET Core 3.1)

If you were to now, contact https://localhost:5001/weatherforecast in Postman (assuming you have SSL verification turned off in the settings) you will get a 401 (assuming you do not pass a token).

To generate a token that is valid, the easiest way is to create an SPA application in your Auth0 tenant and deploy a Quickstart (I recommend the Angular variant). Remember to have the the Web Console -> Network tab open as you login to this sample application – you can extract your token from the token endpoint call.

An additional bit of information is the site jwt.io which, given a token, can you show you all of the information contained within. Do not worry, nothing sensitive is exposed by default, just ALWAYS be mindful of what claims and properties you add to the token since they can be viewed here.

Establish a check for Authorization

While the authentication piece is commonly tied to a backend validation mechanism, authorization is commonly not the case, at least with JWT token. The reason is, we do not want to incur additional round trips if we can, safely, store that data in the token and have it decrypted.

This is an important aspect of this process because Authorization is ALWAYS the responsibility of the specific backend. There are many ways to accomplish it, but for this we are going to use .NET Core Authorization Requirement framework. This will allow us to inspect the valid token and indicate if certain requirements have been fulfilled. Based on this, we can create a policy for the user identified. Based on this policy the endpoint can be invoked or it cannot.

For this to work with Auth0 we need to ensure we create permissions and roles in the portal, enable RBAC and indicate we want assigned permissions for a specific audience (API) to be returned in the token.

First, we will need to create permissions:
Selection_008

Here I have created two permissions for my WeatherApiread:weather and write:weather

Next, we need to assign these permissions to users. Note, this can also be done by assigning permissions to specific roles and assigning that role to that user. Roles are groups of permissions. This is all done in the Users and Roles section.
Selection_009

Here you can see the permissions we added can be assigned to this user.

Next, we need to enable RBAC and toggle on for Auth0 to send assigned permissions down with the user. This is done from within the API configuration under RBAC (Role Based Access Control) Settings.
Selection_010

Finally, we have to modify the way we get our token to be specific to our API (https://weatherforecast.com). That is the trick you see, the token is SPECIFIC to a particular API – this is what ensure that permissions with the same name do not enter into the same token.

Now, on this you may wonder, well how do I handle it with multiple services that have different names and permissions? To that I will say, there are ways but they get at the heart of what makes designing effective microservice architectures so challenging and are not appropriate to delve into in this entry.

Assuming you are using the Angular Quickstart you only need to ensure that, when the Login request is made, the audience is provided. As of the Quickstart I downloaded today (07/04/2020):

  • Open the file at <root>/src/app/auth/auth.service.ts
  • Insert a new line after Line 18 with the contents: audience: <your API identifier>

Refresh your session either by revoking the user’s access in the Auth0 portal (under Authorized Applications) or simply by logging out. Log back in and recopy the token. Head over to jwt.io and run the token through – if everything is good, you will now see a permissions block in the decoded response.

You can now user this token in Postman or whatever to access the API and implement Authorization.

Building Authorization in .NET Core 3.1 WebApi

Now that the access token is coming into our backend we can analyze it for the qualities needed to “check” an authorization. In .NET Core this is handled via implementations of IAuthorizationRequirement and AuthorizationHandler<T> which work together to check the token for properties and validate fulfillment of policies.

We will start with implementing IAuthorizationRequirement – this class represents a desired requirement that we want to fulfill. In general it contains bits of information that the related handler will use to determine whether the requirement is fulfilled. Here is a sample of this Handler and Requirement working together:

Here, the token is passed to our IsWeathermanAuthorizationHandler where it looks for the permission as indicated by the requirement. If it finds it, it marks it as fulfilled. You can see the potential here for more sophisticated logic aimed at validating a requirement as fulfilled.

The final piece is the definition of a policy. A policy is a composition of requirements that need to be fulfilled to grant the user with the specific policy (by default ALL must be fulfilled but overloads can enable other functionality). In our example, we have created the IsWeatherman policy as such:

Notice the AuthorizationHandler is added via its interface as a singleton. Obviously use the instantiation strategy that makes the most sense. For our IsWeatherman policy to be fulfilled the single requirement (IsWeathermanAuthorizationRequirement) must be marked successful. This then allows the use of this policy via the Authorize attribute:

Pretty neat eh? Now, you can simply decorate the controller class or action method and forgo any logic in the method. If the policy is not given to the user a 403 Forbidden will be returned.

What about multiple policies? As of right now, based on what I can find, this is NOT supported. But given the flexibility of this implementation, it would not be challenging to create composite policies and go about it that way.

To validate this functionality, our user from previously should have the permission and, as such, should be able to access your endpoint. To further verify, create a new user in Auth0 with no permissions, sign in, and use the token to access the endpoint, you should get a 403 Forbidden.

Congrats on getting this to work. If you have questions please leave a comment I will do my best to answer.

Kong JWT and Auth0

Continuing my experiments with Kong Gateway (https://konghq.com/) I decided to take on a more complex and more valuable challenge – create an API which used Auth0 (http://auth0.com) to drive JWT authentication. I have done this before in both Azure API Management and API Gateway so I figured it would, generally, be pretty straight forward: I was wrong.

What I have come to discover is, the Kong documentation is written very deliberately for their traditional approach leveraging the Admin API which has admins sending JSON payloads against the API to create objects or using their custom types in a YAML file that can be provided at startup to the Gateway. My aim was to approach this for a Cloud Native mindset and really leverage Kubernetes and appropriate CRDs to make this work.

To say the documentation does not cover this approach is a gross understatement. There are examples, trivial and over simplified ones, but nothing substantive. No spec examples, no literature on common scenarios. This made things impressively difficult. Were it not for the help of their team on Twitter and in the forums I cannot say for certain if I would have been able to figure this out. Thus, it is hard for me to recommend Kong to anyone who plans to run in Kubernetes since their existing methods hardly following the Cloud Native mindset.

But, without further adieu let’s talk about how I eventually got this to work.

Planning

Our aim will a simple API with one endpoint which will require a JWT token and the other will be anonymous. Kong operates as an Ingress controller (install: https://docs.konghq.com/2.0.x/kong-for-kubernetes/install/#helm-chart) and thus relies on the Ingress spec (https://kubernetes.io/docs/concepts/services-networking/ingress/) to route incoming requests to services which then routes to the underlying pods.

Along the way we will leverage a couple custom resources defined within Kong: KongConsumer and KongPlugin.

Authentication will be handled via Auth0 through my Farrellsoft tenant. This provides maximum security and flexibility and keeps any super sensitive details out of our definitions and therefore out of source control.

For this exercise I will make the following assumptions:

  • You have a working understanding of Auth0 and can navigate its portal
  • You have a Kubernetes cluster you have already installed Kong to (install link si above)
  • You have the ability to use Deployments and Services within Kubernetes

Let’s get started

Create a basic Ingress route

Ingress in Kubernetes is often the point that is publicly exposed by the cluster. It serves as the traffic cop, directing incoming requests to services based on matching criteria. Its presence negates the need to spin up greater numbers of load balancers and creating cost overruns. It is built as a plugable component and many vendors have created their own, Kong is such a vendor. Here is a basic route to our API:

Here we use default backend notation (that is a spec with no routes defined) to direct ALL traffic to the weather-service. If we run the following kubectl command we can see our public IP address four the Ingress controller:

kubectl get ing -n weather-api

Here is a shot of what mine looks like (you may need to run it a few times):

ing-get

For this post we will use the IP address to query our controller. You could create a CNAME or A Record for this entry if you wanted something easier to read

If we call our API endpoint in Postman we get back our random weather forecast data as expected. Great, now let’s add authentication

My Url was http://52.154.abc.def/weatherforecast

Let’s require a JWT token

One of the things I like with Kong is the plugin model. Its very similar to how policies are used in Azure API Management. Effectively it can give you a wealth of functionality without having to write custom code and make the separation of responsibility that much cleaner.

For Kong, we must first define a KongPlugin to enable the plugin for use by our Ingress:

Yes, I know. Its weird. You would think that we would configure the plugin using this definition, and you would be wrong. We will get to that later. For now, this is basically just activating the plugin for use. To use  it, we need to update our Ingress as such:

We added the plugins.konghq.com and indicated the name of our JWT plugin which we defined in the previous example.

To test this, make a request to /weatherforecast (or whatever you endpoint address is) and you should now get Unauthorized. Great, this means the plugin is active and working.

Not working? I have a whole section on debugging at the end.

Setup Authentication

Won’t lie this was the trickiest part because it took piecing together examples from bug reports, guessing and, eventually, members of the Kong support team to figure it out. So here we go.

Make sure you have an Auth0 account (mine is https://farrellsoft.auth0.com) and that you grab the public key. This bit of the docs will explain this: https://docs.konghq.com/hub/kong-inc/jwt/#using-the-jwt-plugin-with-auth0/. Be careful only focus on the bit about getting the .pem file and the call to openssl.

Once you perform that you should end up with your tenant specific public key in a file. Dont worry, this is the public key and is thus designed to be shared – Auth0 takes good care of the private key.

Create a secret that looks something like this:

Kong performs the authentication using a KongConsumer which effectively represents the user of an incoming request – in our case we would want all users to be seen as the same (even though the underlying app logic will be different).

Now, with this created all of the pieces should be in place to enable JWT verification. Let’s test it out.

The easiest way is to go to your Auth0 portal and select APIs. Select the API in the box (I believe one gets created if none exist). Once selected, you should see API Explorer as a tab option (follow Create and Authorize if prompted). With this selected you will see a JWT token presented. Copy it.

You will want to use this as a Bearer token. I recommend Postman’s Authorization tab

Selection_004

If everything works, you should get back data.

Debugging

There are a number of ways to debug and troubleshoot this approach. They range from token validation, entity checking, and log files. I used all three to get this working.

JWT Token Validation

JWT tokens are based on a known standard and they can be examined at jwt.io where you can paste a token and see all of the values therein. For our example, Kong JWT keys off the key in our Secret and attempts to match the iss value in the token to a known credential.

You can use jwt.io to inspect the token to ensure the iss is what you expect and what you defined as the key in the secret. BE CAREFUL, the trailing slashes count too.

As a side note, tools like jwt.io are why it can never be understated to be very careful what you put in a JWT token. The values can be seen, and rather easily. Always use values that would mean nothing to another use and only mean something to the consuming application.

Using the Admin API

When running in db-less mode (the default for Kubernetes and the recommended approach) you will not be able to use the API to create Kong resources, other then via the /config endpoint. You can however, perform GET calls against the API and validate that Kong is creating appropriate resources based on Kubernetes resources (I wont cover syncing and how resources get translated from K8s to Kong in this post).

I especially found it useful to query my consumers and their corresponding jwt credentials. I continuously got an error regarding No Creds for ISS which was due to the fact that for a long time I was simply not creating any jwt credentials. You can validate this by calling the Admin API at:

/consumers/<username>/jwt

This will show an empty list if things are not working properly.

For Kubernetes to get at the Kong Admin API the easiest way is port-forward. Given this is really only used for validating in db-less mode having it available to the world is not needed. Here is the kubectl command I used to port forward this:

kubectl port-forward service/kong-gateway-kong-admin -n kong-gate 8080:8444

Then you can do the following to get all consumers:

http://localhost:8080/consumers

Reading the Logs

I found out, much too late, that all syncing operations are logged in the Kong Ingress Controller Pod. This let me discover when I was missing kongCredType (its not mentioned anywhere in the docs) in my Secret.

Remember, when you create resources via kubectl Kong monitors this and creates the appropriate Kong types. This log file will give you a view of any syncing errors that might occur. Here is how I accessed mine:

kubectl logs pod/kong-gateway-kong-7f878d48-tglk2 -n kong-gate -c ingress-controller

Conclusion

So what is my final assessment of Kong. Honestly, I like it and it has some great ideas – I am not aware of any other Ingress providers leveraging a plugin model which undoubtedly gives Kong an incredible amount of potential.

That said, the docs for db-less Kubernetes need A LOT of work and are, by my estimation, incomplete. So, it would be hard to suggest a larger enterprise take this tool on expecting to lean on a support staff for help with an angle that is surely going to be very common.

So, what I would say is, if you are prepared to really have to think to get things to work or you are comfortable using the Kong YAML resources Kong is for you. If you are looking for an Ingress for you enterprise critical application, not yet I would say.

Playing with Kong Gateway

I recently took some time to play with Kong Gateway – which supports a Kubernetes compatible Ingress controller. I find that by playing with the various Ingress controllers I can get a better sense of their abilities and how they can relate to common use cases such as Strangler Pattern and Microservice management.

Setting Up

I am a big fan of Helm Package Manager and I used that to install Kong to my Kubernetes cluster. Kong lays the instructions out nicely: https://docs.konghq.com/2.0.x/kong-for-kubernetes/install/

Here is the sequence of commands I used – note I am using Helm 3

helm install kong-gateway kong/kong --namespace kong-gate
   --set ingressController.installCRDs=false

Running this command with the appropriate configurations will install the Kong components to your Kubernetes cluster in the kong-gate namespace.

Configuration

Kubernetes leverages components like Kong through Ingress resources that identify the path and what class of Ingress to use, this is where you indicate to use Kong. Here is the configuration for the Ingress I arrived at.

Ingress components use annotations to not only define which Ingress type to use but also what configurations can be applied to that Ingress route (as defined by the CRD). In this case, you can see three custom annotations with the konghq identifier. This link lays out the various annotations that are supported: https://github.com/Kong/kubernetes-ingress-controller/blob/master/docs/references/annotations.md

In this case, weather-service is a Kubernetes Service that references the pods that contain the source code. Next, we want to leverage the Kong plugin system to apply rate limiting to this Ingress route.

Plugins

One of the aspects that makes Kong better than the standard Nginx Ingress controller is the bevy of plugins supported which can make common tasks much easier. There is a fully catalog of them here: https://docs.konghq.com/hub/

This part was more difficult because there does not seem to be a lot of documentation around how this works. I ended up stumbling upon a GitHub issue that showed some source that helped me see how this works – here is the plugin configuration code that I arrived at:

For reference to this, here is the “home” screen for the plugin – https://docs.konghq.com/hub/kong-inc/rate-limiting/. From here you can get a sense of the configurations. Most of it is shown as sending CURL commands to the Kong Admin API. But it turns out you can follow the model pretty easily when defining your KongPlugin.

The key connector here is the name (weather-rate-limit) and its use in the annotations of the Ingress route (see above). This is how the Ingress knows which Plugin configuration to use. Also important is the plugin name value pair which defines the name of the plugin being configured. This is the same name as is listed in the Kong plugin catalog.

I used this with the default .NET Core Web API example that returns randomized Forecast data. I was able to successfully send 6 requests in sequence and get a Too Many Requests message on the sixth. My next challenge will be JWT token validation.

Thoughts

Ingress controllers like Kong, and Envoy, Traefik, and others are essential tools when dealing with Kubernetes. Not only can they make dealing with Microservices easier but they can also lend themselves to making the break up of a monolith through the Strangler Pattern easier.

Building an Real Time Event Processor – Part 4

I always believe in reflecting on one’s design and always trying to get a better understanding of the problem. In this sample, there are areas that are rather obvious for improvement.

Do you really need that queue?

In Part 1, I discussed a Timer job I had created which fed data into a Service Bus queue. This data is summarily dequeued in the next job and its data written off to Event Hub (one name at a time) and Blob storage (where the raw JSON can be stored). So I wondered if I actually needed to have that queue.

The answer is yes, here is why – the way the bindings work is it happens not when the method returns or data is written to the out parameter, it happens at the very start. Since our starter here is a Timer there is no incoming data to suggest what the id value of the outgoing blob will be.

So the queue really one serves the purpose to create an input with the id value embedded in it so the binding can work, which is why the blob return works for the Dequeue method but cannot work for the trigger.

What I can do is change the code so it looks like this:

As you an see, we moved the Event Hub code to the Timer function so the purpose of the Dequeue method is literally just to write the blob. I honestly dont like this even though it make more sense. I just dislike having an Azure function that is so simplistic that I feel like that functionality should just exist somewhere else.

What about storing Time Series data?

Initially I thought Redis might make a lot of sense since I cam easily expire old aggregate data – keep in mind, often the historical data for aggregates are not important as they can be derived from raw historical data sources. Further, storing excess data for systems that are high volume adds to cost. Work with your teams and leaders to determine how much historical aggregate data makes the most sense.

Azure does offer a platform know as Time Series Insights which is designed for MASSIVE scale, usually output from an IoT style platform where you are gathering telemetry data from all over the world. Such a thing would dwarf what we are after with this.

Storing data for these systems is always a challenge. In this situation the best solution is to write a Timer Azure function that deletes data from Cosmos to ensure ONLY the data for the time period that makes the most sense is kept. Again, this data can be derived again from historical data sources.

Concluding Thoughts

I was amazed at how easy this process was. If you really focus on Azure Functions being the glue in a distributed process it is amazing how much functionality you can achieved. I think I wrote around 40 lines of code in the backend, and I got so much.

When you start to work on real time or streaming data platforms it really is important to have conversations, discuss your data needs, and try new technologies. The simple truth is, you can pay for the top tier Azure SQL database and likely handle the volume but, your costs will be enormous. Understanding other options and patterns can help select a strategy that not only works but is cost effective.

I hope you have enjoyed this series. Good lucking with Azure and everything else.

Part 1

Part 2

Part 3