Understanding Azure Durable Functions

In this post I would like to share a simple Azure Durable Function which will perform Simple Return Value along with some Timer Delays.

Function Replays

As you are aware Functions undergo multiple Replays which may require aligning your code as well. 

context.IsReplaying property says whether it is a replay.

The Code

The code ensures there is a 2 minute delay between sections.

namespace DurableFunctionTest
{
     public static class Function1
     {
         [FunctionName(“Function1_HttpStart”)]
         public static async Task<HttpResponseMessage> HttpStart(
             [HttpTrigger(AuthorizationLevel.Anonymous, “get”, “post”)]HttpRequestMessage req,
             [OrchestrationClient]DurableOrchestrationClient starter,
             ILogger log)
         {
             // Function input comes from the request content.
             string instanceId = await starter.StartNewAsync(“Function1”, null);

            log.LogInformation($”Started orchestration with ID = ‘{instanceId}’.”);

            return starter.CreateCheckStatusResponse(req, instanceId);
         }

        [FunctionName(“Function1”)]
         public static async Task<List<string>> RunOrchestrator(
             [OrchestrationTrigger] DurableOrchestrationContext context)
         {
             var outputs = new List<string>();

            outputs.Add(await context.CallActivityAsync<string>(“Function1_Hello”, “London”));

            return outputs;
         }

        [FunctionName(“Function1_Hello”)]
         public static string SayHello([ActivityTrigger] string name, ILogger log)
         {
             log.LogInformation($”step 1 {DateTime.Now.ToLongTimeString()}”);
             Thread.Sleep(2 * 60 * 1000); // 2min

            log.LogInformation($”step 2 {DateTime.Now.ToLongTimeString()}”);
            Thread.Sleep(2 * 60 * 1000); // 2min

            log.LogInformation($”step 3 {DateTime.Now.ToLongTimeString()}”);
             Thread.Sleep(2 * 60 * 1000); // 2min

            log.LogInformation($”step 4 {DateTime.Now.ToLongTimeString()}”);
             Thread.Sleep(2 * 60 * 1000); // 2min

            return $”Hello {name} !”;
         }
     }
}

The Output

2/10/2020 6:38:10 PM] 352ff6a310684f6eba4274bb81a54220: Function ‘Function1_Hello (Activity)’ started. IsReplay: False. Input: (40 bytes). State: Started. HubName: DurableFunctionsHub. AppName: . SlotName: . ExtensionVersion: 1.8.2. SequenceNumber: 9.
[2/10/2020 6:38:10 PM] Executing ‘Function1_Hello’ (Reason=”, Id=7f616c57-f299-4f9c-91ec-75feaa180893)
[2/10/2020 6:38:10 PM] step 1 1:38:10 PM
[2/10/2020 6:38:13 PM] Host lock lease acquired by instance ID ‘000000000000000000000000FE3C5631’.
[2/10/2020 6:40:08 PM] step 2 1:40:08 PM
[2/10/2020 6:40:10 PM] step 2 1:40:10 PM
[2/10/2020 6:40:10 PM] step 2 1:40:10 PM
[2/10/2020 6:42:08 PM] step 3 1:42:08 PM
[2/10/2020 6:42:10 PM] step 3 1:42:10 PM
[2/10/2020 6:42:10 PM] step 3 1:42:10 PM
[2/10/2020 6:44:08 PM] step 4 1:44:08 PM
[2/10/2020 6:44:10 PM] step 4 1:44:10 PM
[2/10/2020 6:44:10 PM] step 4 1:44:10 PM
[2/10/2020 6:46:08 PM] Executed ‘Function1_Hello’ (Succeeded, Id=02c78620-a573-4ffa-9f62-0aac2333ceaf)

The Inference

As you can see, although the Function replayed multiple times, but the restart was not loosing any time, it was accurate enough to replay from the past exit point.

Notes

Break your code into multiple chunks

Ensure transactions are protecting the Atomicity.

SQL Server–Compare Performance of INT, GUID, Sequential GUID

In this post we can compare the performance of ID as INT, GUID & Sequential GUID

Advantages of GUID Columns as Primary Key

  • Makes them Globally Unique
  • Can backup/restore/sync to another DB without breaking Primary Keys & Foreign Keys
  • No Performance Impact (proven here)

Note

Sequential GUID preferred over GUID to avoid pagination issues

PROOF – No Performance Issues on GUID

Create Table – INT Primary AutoIncrement

CREATE TABLE [dbo].[IDTest_INT](
     [ID] [int] IDENTITY(1,1) NOT NULL,
     [Name] [varchar](100) NOT NULL,
  CONSTRAINT [PK_IDTest_INT] PRIMARY KEY CLUSTERED
(
     [ID] ASC
)
)

Create Table – GUID Primary –

CREATE TABLE [dbo].[IDTest_GUID](
     [ID] uniqueidentifier DEFAULT NEWID(),
     [Name] [varchar](100) NOT NULL,
  CONSTRAINT [PK_IDTest_GUID] PRIMARY KEY CLUSTERED
(
     [ID] ASC
)
)

Create Table – GUID Primary – Sequential GUID

CREATE TABLE [dbo].[IDTest_SequentialGUID](
     [ID] uniqueidentifier DEFAULT NEWSEQUENTIALID(),
     [Name] [varchar](100) NOT NULL,
  CONSTRAINT [PK_IDTest_SequentialGUID] PRIMARY KEY CLUSTERED
(
     [ID] ASC
)
)

INSERT TEST – 1 LAKH RECORDS

INT 13 seconds

GUID 12 seconds

Sequential GUID  11 seconds

INFERENCE No Impact on Performance for Insert

Here are the queries for same:

declare @count int
select @count = 1
while  @count <= 100000
begin
     insert into IDTest_INT (name) values(‘Name’ + STR(@count))
     insert into IDTest_GUID (name) values(‘Name’ + STR(@count))
     insert into IDTest_SequentialGUID (name) values(‘Name’ + STR(@count))
     select @count = @count + 1
end

SELECT TEST – ON ID

INT 0seconds

GUID 0seconds

Sequential GUID  0seconds

Here are the queries for same:

  SELECT * FROM IDTest_INT WHERE ID = 1000

  SELECT * FROM IDTest_GUID WHERE ID = ‘1EDE341C-7692-4D7C-A99D-000C387337DE’

  SELECT * FROM IDTest_SequentialGUID WHERE ID = ’84a4d912-5448-ea11-8740-48f17ffd0966′

INFERENCE No Impact on Performance for Select

Azure Migrate, Azure Site Recovery, Azure Database Migration Service & Disaster Recovery

For migrating on-premise applications to Azure, following tools are available to help you:

  1. Azure Migrate
  2. Azure Site Recovery
  3. Azure Database Migration Service

Planning Phase

Before migrating to Azure, you will have to do a Planning on your existing applications, technologies, hosting methodologies & their corresponding equivalent in Azure.

Azure Migrate

Azure Migrate is a free service which will help in discover, assess & migrate on-premise systems to Azure.

Azure Migrate can do the Assessment with following steps:

  • Configure Appliance on Server
  • Run Agents on each VM
  • Create Assessment

Azure Site Recovery

Azure Site Recovery is a Disaster Recovery strategy which can also be used to Migrate On-Premises VM to Azure.

Database Migration Service

Database Migration Service helps to asses the Compatibility of source database against destination Azure database & Also migrate the data.

Disaster Recovery

Business Continuity and Disaster Recovery is Essential to prevent Revenue Loss & Maintaining SLAs of businesses.  Azure provides Disaster Recovery through Azure Site Recovery strategy.

For the same, one has to understand RTO & RPO during a disaster:

  • Recovery Time Objective (RTO) is maximum time business can survive until service restoration
  • Recovery Point Objective (RPO) is maximum data business can loose until service restoration

Azure Site Recovery is an important tool for Disaster Recovery & Business Continuity.  ASR will replicate workloads from primary site to secondary site during a disaster.  Replication can happen:

  • From VM to Azure
  • From Azure Region to another Azure region

Note

Failover is the process of switching service from primary to secondary during disaster. 

Failback is the process of switching service back to primary when it is recovered.

HADR – High Availability Disaster Recovery

BCDR – Business Continuity Disaster Recovery

Azure Active Directory–How to protect a Web Application without Code

In this post I would be demonstrating how to protect a web site without any code changes.

Advantages

  • High Protection Web Site will not serve any html, js, css files without Authentication  (MSAL protection causes few MSAL JS to be served to do authentication – this may be restricted by Information Security groups of Enterprise class customers)
  • Same Code can be deployed multiple places as the Authentication pieces being decoupled

Create Web Application

Create a web application of ASP.NET or Angular.

Publish to Azure

Publish to Azure so that an App Service is created.

Set the Authentication

image

Test the Application

Now you can test the application & the login prompt happens.

Note

In the background the App Service is adding Redirect URL

https://working-angular-webapi.azurewebsites.net/.auth/login/aad/callback

Client Side Applications

For client side applications, you can always refer the following URL to get the current ID Token for the Authenticated User.  It can also be used as Access Token as the Audience is same as Client ID.

https://working-angular-webapi.azurewebsites.net/.auth/me


Summary

In this post we have seen how to do No Code protection of web application using Azure Active Directory.

Azure AD Search using C#

In this post I would like to give a Sample to Search on Azure Active Directory using Filter.

Pre-Requisites

Following are the pre-requisites:

  • Create AD > App Registration & Client Credentials
  • Assign Directory.Read.All “application” permission & Provide Admin Consent

The Code

private async static void SerachAzureAD(string search)
        {
                string clientID = “YOUR-CLIENT-ID”;
                string clientSecret = “YOUR-CLIENT-SECRET”;
                string tenantID = “YOUR-AD-TENANT-ID”;

               string graphApiResource = “https://graph.microsoft.com”;
                Uri microsoftLogin = new Uri(“https://login.microsoftonline.com/”);

               string authority = new Uri(microsoftLogin, tenantID).AbsoluteUri;
                AuthenticationContext authenticationContext = new AuthenticationContext(authority);
                ClientCredential clientCredential = new ClientCredential(clientID, clientSecret);

               // Picks up the bearer token.
                AuthenticationResult authenticationResult = authenticationContext.AcquireTokenAsync(graphApiResource, clientCredential).Result;

               GraphServiceClient graphClient = new GraphServiceClient(new DelegateAuthenticationProvider(
                async (requestMessage) =>
                {
                    requestMessage.Headers.Authorization = new AuthenticationHeaderValue(“bearer”, authenticationResult.AccessToken);
                }));

               string filter = $”startswith(displayName, ‘{search}’) or startswith(givenName, ‘{search}’) or startswith(surname, ‘{search}’) or startswith(mail, ‘{search}’) or startswith(userPrincipalName, ‘{search}’)”;
                //$”$filter=displayName EQ {search}”;

               IGraphServiceUsersCollectionPage users = graphClient.Users.Request()
                    .Filter(filter)
                    .GetAsync().Result;

               Console.WriteLine(“Searching..”);

               while (users.Count > 0)
                {
                    foreach (var user in users.CurrentPage)
                    {
                        Console.WriteLine(user.DisplayName);
                    }

                   if (users.NextPageRequest != null)
                    {
                        users = await users.NextPageRequest
                            .GetAsync();
                    }
                    else
                    {
                        break;
                    }
                }

        }

Result

image

Summary

In this post we have seen how to do Azure Active Directory Search using C# & Search Filters.

Minimal Code to Protect Web API with Azure AD

In this post we can see the most minimal code to protect a Web API with Azure AD.

This protected Web API can be used for both:

  • Frontend Flow with ID Token
  • Backend Flow with Access Token

Create App Registration

Create App Registration with ID Token & Access Token enabled.

Note down the Client ID & Tenant ID

Create Web API Project

Create a .net core web application, add a ToDo controller into it with get & post methods.

Configuration add the following section into app.config

“AzureActiveDirectory”: {
   “Instance”: “https://login.microsoftonline.com/”,
   “TenantId”: “YOUR-TENANT-ID”,
   “ClientId”: “YOUR-CLIENT-ID”
}

Startup Code

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.AzureAD.UI;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Cors.Internal;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace WebAPI
{
     public class StartupTest
     {
         public StartupTest(IConfiguration configuration)
         {
             Configuration = configuration;
         }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
         {
             var options = new AzureADOptions();
             Configuration.Bind(“AzureActiveDirectory”, options);

            services.AddAuthentication(op => { op.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; })
             .AddJwtBearer(op =>
             {
                 op.Authority = options.Instance + options.TenantId;
                 op.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
                 {
                     ValidAudience = options.ClientId,
                     ValidateIssuer = false,
                     ValidateIssuerSigningKey = false
                 };
             });

            services.AddCors(o => o.AddPolicy(“MyPolicy”, builder =>
             {
                 builder.AllowAnyOrigin()
                        .AllowAnyMethod()
                        .AllowAnyHeader();
             }));

            services.AddMvc();
             services.Configure<MvcOptions>(op =>
             {
                 op.Filters.Add(new CorsAuthorizationFilterFactory(“CorsPolicy”));
             });
         }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
         {
             if (env.IsDevelopment())
             {
                 app.UseDeveloperExceptionPage();
             }
             else
             {
                 app.UseHsts();
             }

             app.UseHttpsRedirection();
             app.UseCors(“MyPolicy”);
             app.UseAuthentication();
             app.UseMvc();
         }
     }
}

Controller Code

[Authorize]
    [ApiController]
    [Route(“api/[controller]”)]
    [EnableCors(“CorsPolicy”)]
    public class TodoListController : ControllerBase

Testing

You can create a Client App with the same Client ID and get the ID Token to access the API.

You can create a Postman Request with same Client ID & Client Secret and get the Access Token to access the API.

Azure Active Directory Learnings–Zero to Production

In this post I would like to note down the AAD aspects.  This would be helpful for anyone who wanted to Integrate Authentication & Authorization with their applications.

Azure Active Directory

AAD is an Identity management system hosted in Microsoft Azure cloud.  It can offer authentication & authorization for applications.

AAD can provide Authentication & Authorization for Web Applications, Desktop Applications & Mobile Applications as well.

OAuth2 & OpenID Connect

OAuth2 is an Authorization protocol.

OpenID Connect can be used for both Authentication & Authorization

Comparison of SAML vs OAuth2 vs OIDC

image

App Registration for Frontend

App Registration + ID Token

App Registration for Backend

App Registration + Client Credential Flow

App Registration for Frontend & Backend

App Registration + ID Token + Access Token

Pro-Tools

Following are few tools than can help in your adventure: