Deploy Azure Durable Function with Zero Downtime

Deploy Azure Durable Function with Zero Downtime

Requirement

To deploy a new version of an Azure Durable Function using Terraform to a Function App with zero downtime

Release Pipeline

The production deployment is triggered when a modification to the main repository branch is committed.  The production release pipeline path looks like this:

  Plan & Apply Steps include the following tasks:

  • Install Terraform
  • Initialise Terraform – Command: init

  • Final Task:
    • For Plan => Plan Terraform – Command: plan
    • For Apply => Validate and Apply Terraform – Command: validate and apply
Deploy Function Step Tasks:
  • Deploy Azure Function App
Task settings:

Terraform Modules

The function app module that is used for all my Azure functions includes the following:
  • Application Insights: 
resource “azurerm_application_insights” “this” {
  name                = var.application_insights_name
  location            = var.location
  resource_group_name = var.resource_group_name
  workspace_id        = var.log_analytics_workspace_id
  application_type    = “web”
}
  • Function App: 
resource “azurerm_function_app” “functionapp” {
  name                       = var.function_app_name
  location                   = var.location
  resource_group_name        = var.resource_group_name
  app_service_plan_id        = var.app_service_plan_id
  storage_account_name       = var.storage_account_name
  storage_account_access_key = var.storage_account_access_key
  version                    = “~4”
  enable_builtin_logging     = false 
  app_settings               = merge(var.app_settings
    { “APPLICATIONINSIGHTS_CONNECTION_STRING” = azurerm_application_insights.this.connection_string }
    ,{ “APPINSIGHTS_INSTRUMENTATIONKEY” = azurerm_application_insights.this.instrumentation_key }
    ,{ “AzureFunctionsWebHost__hostid” = ${substr(var.function_app_name,0,29)}-01″ }
    ,{ “WEBSITE_ENABLE_SYNC_UPDATE_SITE” = “true” }
    ,{ “WEBSITE_RUN_FROM_PACKAGE” = “1” }
  identity {
    type = “SystemAssigned”
  }
  site_config {
    vnet_route_all_enabled = var.enable_vnet
    always_on              = true
    http2_enabled          = true
  }
  tags = var.tags
  lifecycle {
    ignore_changes = [
      app_settings[“DurableManagementStorage”]
      ,app_settings[“AzureFunctionsWebHost__hostid”]
      ,app_settings[“APPLICATIONINSIGHTS_CONNECTION_STRING”]
      ,app_settings[“APPINSIGHTS_INSTRUMENTATIONKEY”]
    ]
  }
}

Notes:

So the following configuration settings for the “production” and “stage” slots have to be added manually:

      • DurableManagementStorage => “stage” slot and “production” slot pointing to the slot storage accounts (see below)
      • AzureFunctionsWebHost__hostid
      • APPLICATIONINSIGHTS_CONNECTION_STRING
      • APPINSIGHTS_INSTRUMENTATIONKEY
  • Deployment Slot:
resource “azurerm_function_app_slot” “functionapp_slot” {
  name                       = “stage”
  location                   = var.location
  resource_group_name        = var.resource_group_name
  app_service_plan_id        = var.app_service_plan_id
  function_app_name          = var.function_app_name
  storage_account_name       = var.storage_account_name
  storage_account_access_key = var.storage_account_access_key
  version                    = “~4”
  enable_builtin_logging     = false 
  app_settings               = merge(var.app_settings
    { “APPLICATIONINSIGHTS_CONNECTION_STRING” = azurerm_application_insights.this.connection_string }
    ,{ “APPINSIGHTS_INSTRUMENTATIONKEY” = azurerm_application_insights.this.instrumentation_key }
    ,{ “AzureFunctionsWebHost__hostid” = ${substr(var.function_app_name,0,29)}-02″ }
    ,{ “WEBSITE_ENABLE_SYNC_UPDATE_SITE” = “true” }
    ,{ “WEBSITE_RUN_FROM_PACKAGE” = “1” }
    )
  identity {
    type = “SystemAssigned”
  }
  site_config {
    vnet_route_all_enabled = var.enable_vnet
    always_on              = true
    http2_enabled          = true
    auto_swap_slot_name    = “production”
  }
  tags = var.tags
  lifecycle {
    ignore_changes = [
      app_settings[“DurableManagementStorage”]
      ,app_settings[“AzureFunctionsWebHost__hostid”]
      ,app_settings[“APPLICATIONINSIGHTS_CONNECTION_STRING”]
      ,app_settings[“APPINSIGHTS_INSTRUMENTATIONKEY”]
    ]
 }
 depends_on = [azurerm_function_app.functionapp]
}

Notes:

    • See the Notes above for the Function App.
    • The “auto_swap_slot_name” is set to the “production” slot, therefore once the new version of the function is deployed to the “stage” slot, Azure will wait for the “production” slot to not be active before auto-swapping the slots, therefore no downtime will occur.
    •  The “depends_on” setting makes sure that the function app is created first before the deployment slot is created.

An example of the Terraform for a durable function

A durable function is required to have separate storage accounts to hold the state, this is so that the “stage” slot and the “production” slot app can be running at the same time and do not collide.

storage_account_name       = data.azurerm_storage_account.sacc.name
storage_account_access_key = data.azurerm_storage_account.sacc.primary_access_key
storage_account_connectionstring_slot01 = data.azurerm_storage_account.sacc_slot01.primary_connection_string
storage_account_connectionstring_slot02 = data.azurerm_storage_account.sacc_slot02.primary_connection_string
The corresponding data Terraform:

data “azurerm_resource_group” “sacc_rg” {
  name = “ccc-rg-weu-${var.shortenv}-shared-st-01″
}
data “azurerm_storage_account” “sacc” {
  name                = “cccstweu${var.shortenv}shared01″
  resource_group_name = data.azurerm_resource_group.sacc_rg.name
}
data “azurerm_storage_account” “sacc_slot01” {
  name                = “cccstweu${var.shortenv}slotstate01″
  resource_group_name = data.azurerm_resource_group.sacc_rg.name
}
data “azurerm_storage_account” “sacc_slot02” {
  name                = “cccstweu${var.shortenv}slotstate02″
  resource_group_name = data.azurerm_resource_group.sacc_rg.name
}
However, since “sticky keys” do not work or we cannot work out how to implement them, then these storage account connection strings have to be added manually to the “DurableManagementStorage” configuration setting for the stage and production slots.  In my current Terraform solution, I do not use the “storage_account_connectionstring_slot01” or the “storage_account_connectionstring_slot02” variables, these are currently just added as placeholders. 

After Deployment

Reference 1 below describes adding a Gate evaluation before deployment of the function to the “stage” slot, the idea is that the function should not be deployed to the “stage” slot if there are still executions in flight in the “stage” slot function.  Here are my settings for the gate:

See Reference 1 for details of the StatusCheck function.

Leave a Reply

Your email address will not be published. Required fields are marked *