Create infrastructure for Azure WebJob using Bicep

In my previous posts I wrote about creating infrastructure for Azure WebJob using PowerShell or Terraform. Most recent approach for creating Azure resources is Azure Bicep. Bicep is a Domain Specific Language (DSL) for deploying Azure resources declaratively. Bicep code is transpiled to standard ARM Template. Bicep file for creating infrastructure for Azure WebJob is:

var baseName = resourceGroup().name
var baseNameLower = toLower(baseName)

resource appServicePlan 'Microsoft.Web/serverfarms@2018-02-01' = {
  name: baseName
  location: resourceGroup().location
  sku: {
    name: 'B1'
    capacity: 1
  }
}

resource appService 'Microsoft.Web/sites@2018-11-01' = {
  name: baseNameLower
  location: resourceGroup().location
  properties: {
    serverFarmId: appServicePlan.id
    clientAffinityEnabled: false
  }
}

resource config 'Microsoft.Web/sites/config@2018-11-01' = {
  parent: appService
  name: 'web'
  properties: {
    use32BitWorkerProcess: false
    alwaysOn: true
  }
}

resource appSettings 'Microsoft.Web/sites/config@2018-11-01' = {
  parent: appService
  name: 'appsettings'
  properties: {
    APPINSIGHTS_INSTRUMENTATIONKEY: appInsights.properties.InstrumentationKey
  }
}

resource connectionStrings 'Microsoft.Web/sites/config@2018-11-01' = {
  parent: appService
  name: 'connectionstrings'
  properties: {
    AzureWebJobsDashboard: {
      value: 'DefaultEndpointsProtocol=https;AccountName=${baseNameLower};AccountKey=${listKeys(storageAccounts.id, '2019-06-01').keys[0].value};EndpointSuffix=${environment().suffixes.storage}'
      type: 'Custom'
    }
    AzureWebJobsStorage: {
      value: 'DefaultEndpointsProtocol=https;AccountName=${baseNameLower};AccountKey=${listKeys(storageAccounts.id, '2019-06-01').keys[0].value};EndpointSuffix=${environment().suffixes.storage}'
      type: 'Custom'
    }
  }
}

resource storageAccounts 'Microsoft.Storage/storageAccounts@2019-06-01' = {
  name: baseNameLower
  location: resourceGroup().location
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
    tier: 'Standard'
  }
}

resource appInsights 'Microsoft.Insights/components@2015-05-01' = {
  name: baseName
  location: resourceGroup().location
  kind: 'web'
  properties: {
    Application_Type: 'web'
  }
} 

Resource group is then deployed using standart Azure CLI commands like using ARM template:

az group create --name WebJobTest --location westeurope
az deployment group create --resource-group WebJobTest --template-file azuredeploy.bicep --verbose

Deploy Azure WebJob using Terraform

In my previous post I wrote about creating infrastructure for Azure WebJob using Terraform. Using that solution is created empty infrastructure ready for deployment of Azure WebJob, but the deployment of Azure WebJob have to be done separately.


Udemy course: Migrate Windows service to Azure

Terraform provides also options to deploy Azure WebJob immediately after required Azure resources are created. It is posible by combination of Terraform provisioner together with Azure CLI command az webapp deployment as demonstrated in following code snippet:

resource "null_resource" "webjob" {
  provisioner "local-exec" {
    when = create
    command = "az webapp deployment source config-zip -g ${var.resource_group_name} -n ${var.resource_group_name} --src ${var.deployment_package}"
  }
  depends_on = [ azurerm_app_service.as ]
}

Terraform provisioner is executed locally at creation of resource and is placed in null_resource which has dependency on azurerm_app_service.as resource. Complete main.tf file which is responsible for creating Azure resources and then deployment of Azure WebJob is:

terraform {
  required_providers {
    azurerm = {
      source = "hashicorp/azurerm"
      version = ">= 2.0"
    }
  }
}

provider "azurerm" {
  features {}
}

variable "resource_group_name" {
  type = string
  description = "Resource group name"
  default = "WinServiceToAzureTest"
}

variable "location_name" {
  type = string
  description = "Resource location"
  default = "westeurope"
}

variable "deployment_package" {
  type = string
  description = "Deployment package"
  default = "Publish.zip"
}

resource "azurerm_resource_group" "rg" {
  name = var.resource_group_name
  location = var.location_name
}

resource "azurerm_app_service_plan" "asp" {
  name = var.resource_group_name
  location = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  sku {
    tier = "Basic"
    size = "B1"
  }
}

resource "azurerm_app_service" "as" {
  name = var.resource_group_name
  location = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  app_service_plan_id = azurerm_app_service_plan.asp.id
  client_affinity_enabled = false
  site_config {
    use_32_bit_worker_process = false
    always_on = true
  }
  app_settings = {
    "APPINSIGHTS_INSTRUMENTATIONKEY" = azurerm_application_insights.ai.instrumentation_key
  }
  connection_string {
    name = "AzureWebJobsDashboard"
    type = "Custom"
    value = "DefaultEndpointsProtocol=https;AccountName=${lower(var.resource_group_name)};AccountKey=${azurerm_storage_account.sa.primary_access_key};EndpointSuffix=core.windows.net"
  }
  connection_string {
    name = "AzureWebJobsStorage"
    type = "Custom"
    value = "DefaultEndpointsProtocol=https;AccountName=${lower(var.resource_group_name)};AccountKey=${azurerm_storage_account.sa.primary_access_key};EndpointSuffix=core.windows.net"
  }  
}

resource "azurerm_storage_account" "sa" {
  name = lower(var.resource_group_name)
  location = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name  
  account_tier = "Standard"
  account_replication_type = "LRS"
}

resource "azurerm_application_insights" "ai" {
  name = var.resource_group_name
  location = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  application_type = "web"  
}

resource "null_resource" "webjob" {
  provisioner "local-exec" {
    when = create
    command = "az webapp deployment source config-zip -g ${var.resource_group_name} -n ${var.resource_group_name} --src ${var.deployment_package}"
  }
  depends_on = [ azurerm_app_service.as ]
}

If you are interested in Azure WebJobs and how to use them to migrage Windows service to Azure, take my Udemy course Migrate Windows service to Azure.

Create infrastructure for Azure WebJob using Terraform

In one of my post I wrote about creating infrastructure for Azure WebJob using PowerShell. It is imperative approach using PowerShell cmdlets. More DevOps friendly alternative to this is to use declarative approach together with infrastructure as code approach using Terraform. Infrastructure for Azure WebJob consists of following resources:

  • App Service Plan – scalable cluster of web servers
  • Web App – hosting environment running on App Service plan
  • Storage Account – stores data about Azure WebJob execution
  • Application Insights – monitoring


Udemy course: Migrate Windows service to Azure

To create required Azure resources create file named main.tf and place following content into it:

terraform {
  required_providers {
    azurerm = {
      source = "hashicorp/azurerm"
      version = ">= 2.0"
    }
  }
}

provider "azurerm" {
  features {}
}

variable "resource_group_name" {
  type = string
  description = "Resource group name"
  default = "WinServiceToAzureTest"
}

variable "location_name" {
  type = string
  description = "Resource location"
  default = "westeurope"
}

resource "azurerm_resource_group" "rg" {
  name = var.resource_group_name
  location = var.location_name
}

resource "azurerm_app_service_plan" "asp" {
  name = var.resource_group_name
  location = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  sku {
    tier = "Basic"
    size = "B1"
  }
}

resource "azurerm_app_service" "as" {
  name = var.resource_group_name
  location = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  app_service_plan_id = azurerm_app_service_plan.asp.id
}

resource "azurerm_storage_account" "sa" {
  name = lower(var.resource_group_name)
  location = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name  
  account_tier = "Standard"
  account_replication_type = "LRS"
}

resource "azurerm_application_insights" "ai" {
  name = var.resource_group_name
  location = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  application_type = "web"  
}

Then run terraform init command to download providers and initialize state. To see planned infrastructure changes run the terraform plan command. As a final step the infrastructure will be created using terraform apply command.
Azure WebJob requires some additional settings which can be done manually using Azure Portal, but it is possible to implement them in code. Next code snippet shows changes in azurerm_app_service resource where is disabled client affinity, added site config, instrumentation key for Application Insights and connection strings to Storage Account:

resource "azurerm_app_service" "as" {
  name = var.resource_group_name
  location = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  app_service_plan_id = azurerm_app_service_plan.asp.id
  client_affinity_enabled = false
  site_config {
    use_32_bit_worker_process = false
    always_on = true
  }
  app_settings = {
    "APPINSIGHTS_INSTRUMENTATIONKEY" = azurerm_application_insights.ai.instrumentation_key
  }
  connection_string {
    name = "AzureWebJobsDashboard"
    type = "Custom"
    value = "DefaultEndpointsProtocol=https;AccountName=${lower(var.resource_group_name)};AccountKey=${azurerm_storage_account.sa.primary_access_key};EndpointSuffix=core.windows.net"
  }
  connection_string {
    name = "AzureWebJobsStorage"
    type = "Custom"
    value = "DefaultEndpointsProtocol=https;AccountName=${lower(var.resource_group_name)};AccountKey=${azurerm_storage_account.sa.primary_access_key};EndpointSuffix=core.windows.net"
  }  
}

With this changes are Azure resources prepared for deployment of Azure WebJob.

If you are interested in Azure WebJobs and how to use them to migrage Windows service to Azure, take my Udemy course Migrate Windows service to Azure.

Create infrastructure for Azure WebJob using PowerShell

Azure infrastructure for Azure WebJob consists from following resources:

  • App Service Plan – scalable cluster of web servers
  • Web App – hosting environment running on App Service plan
  • Storage Account – stores data about Azure WebJob execution
  • Application Insights – monitoring

All there resources can be created using following PowerShell script:

param
(
    $resourceGroup = $(throw "Resource group is required"),
    $location = "westeurope"
)

$resourceGroupLower = $resourceGroup.ToLower()

New-AzResourceGroup -Name $resourceGroup -Location $location -ErrorAction Stop

New-AzAppServicePlan -Name $resourceGroup -ResourceGroupName $resourceGroup `
    -Location $location -Tier Basic -WorkerSize Small -NumberofWorkers 1 -ErrorAction Stop

New-AzWebApp -Name $resourceGroupLower -ResourceGroupName $resourceGroup `
    -Location $location -AppServicePlan $resourceGroup -ErrorAction Stop

New-AzStorageAccount -Name $resourceGroupLower -ResourceGroupName $resourceGroup `
    -Location $location -SkuName Standard_LRS -Kind StorageV2 -ErrorAction Stop

New-AzApplicationInsights -Name $resourceGroup -ResourceGroupName $resourceGroup `
    -Location $location -Kind web -ErrorAction Stop

PowerShell script has following parameters:

  • $resourceGroup – resource group name
  • $location – location name with West Europe as default value


Udemy course: Improve your productivity with PowerShell

Deploy Azure WebJob using PowerShell

There are many posts on thema of Azure WebJob deployment using PowerShell. But I try to find the most simple solution possible and it is using the combination of PowerShell and Azure CLI.


Udemy course: Migrate Windows service to Azure

Following PowerShell script deploys Azure WebJob:

param
(
    [string] $buildOutput = $(throw "Directory with build output is required"),
    [string] $resourceGroupName = $(throw "Resource group name is required"),
    [string] $webAppName = $(throw "Web app name is required"),
    [string] $webJobName = $(throw "Web job name is required"),
    [string] $webJobType = "triggered"
)

$currentDir = (Get-Item .).FullName
$tempDir = "$currentDir\Temp"
$webJobDir = "$tempDir\App_Data\jobs\$webJobType\$webJobName"

New-Item $webJobDir -ItemType Directory
Copy-Item "$buildOutput\*" -Destination $webJobDir -Recurse
Compress-Archive -Path "$tempDir\*" -DestinationPath ".\$webJobName.zip"
Remove-Item $tempDir -Recurse -Force

az webapp deployment source config-zip -g $resourceGroupName -n $webAppName --src "$webJobName.zip"

PowerShell script has following parameters:

  • $buildOutput – Azure WebJob build output, in my case it is bin\Release\net472 folder
  • $resourceGroupName – Azure resource group name
  • $webAppName – Azure Web App name running on Azure App Service
  • $webJobName – Azure WebJob name
  • $webJobType – Azure WebJob type (triggered/continuous)

PowerShell creates temp directory and copy build output to the temp directory. The key part here is to use defined directory structure App_Data\jobs\$webJobType\$webJobName to deploy Azure WebJob to valid directory in Azure WebApp. Then create ZIP archive and remove temp directory. Then deploys Azure WebJob using Azure CLI command az webapp deployment to Azure.

If you are interested in Azure WebJobs and how to use them for Windows service migration to Azure, take my Udemy course Migrate Windows service to Azure where you learn more about Azure WebJob implementation, deployment, configuration and monitoring.