Azure Pipelines

Configure ACR – Azure DevOps

Azure DevOps allows us to create a service connection to an Azure Container Registry (ACR) to then use in your pipelines.

Setup ACR in Pipelines

I don’t currently have an ACR so I’ll use the Azure CLI to create one to use for this article.

Login to Azure

az login

Create the resource group for the registry

az group create --name taz-acr-group --location uksouth

Create the container

az acr create --resource-group taz-acr-group --name tazcontainers --sku Basic

Now I have a container registry I can configure Azure DevOps.

In Azure DevOps go to project settings

Then select service connections

Then new service connection

Then select docker registry

Select ACR and the fill in the details for the registry

Make sure you tick ‘Grant access permissions to all pipelines’ and save the connection.

Your new connection will appear in the service connections list.

And that’s it, your connection is now ready to be used in your project pipelines.


Getting Started with Azure Front Door and Terraform

What is Azure Front Door?

Azure Front Door is basically a layer 7 global load balancer, global router with url based routing, WAF (Web Application Firewall) and web traffic manager all in one.

I recommend reading the Azure Front Door documentation for further details.

Create Azure Front Door

To create an Azure Front Door you can use the Azure Portal, there are a couple of examples you can follow to do that:

Creating Azure Front Door via the Azure Portal is a good start point to understand how it works, but for this example I am going to create IaC (Infrastructure as Code) to setup a basic Azure Front Door.

I have recently started using Terraform for building Azure resources and so I will use that here to create an Azure Front Door.


  • Make sure I can build Terraform configurations (I am using a Docker container from my previous article – IaC with Containers)
  • Update Terraform to latest (at the time of writing it was 0.12.26)
  • Make sure the configuration is shareable
  • Support multiple configurations and rules

Right, I’ve got my container, updated Terraform and now need to look up sharing Terraform configurations.

Terraform uses modules for sharing configurations and the documentation is quite good. This seems a lot nicer than building linked ARM (Azure Resource Management) templates, as you can have shareable modules locally without having to use blob storage.

You can also take advantage of the Terraform Public Registry or sign up for Terraform Cloud which supports using a Private Registry.


So I need to create a folder for the module (I’ll name it frontdoor), a,, and

Terraform includes azurerm_frontdoor resource in order to create an Azure Front Door.

Azure Front Door has a lot of settings and there are many parts, so let’s go through them a bit at a time.

Note: a lot of the sections allow a list of items (Load Balancing, Routing Rule, Backend Pool, Frontend Endpoint, etc.), this is to allow for multiple configurations and rules to be setup in one go.

  • Azure Front Door name
  • Resource Group name for Azure Front Door
  • Load balancer enabled
  • Backend pools
    • Certificate name check – enforce name check on HTTPS requests
    • Send/Receive Timeout – timeout forwarding the request to the backend
  • Tags – always good to tag your resources
# Create front door
resource "azurerm_frontdoor" "instance" {
  name                                         = var.frontdoor_name
  resource_group_name                          = var.frontdoor_resource_group_name
  enforce_backend_pools_certificate_name_check = var.enforce_backend_pools_certificate_name_check
  load_balancer_enabled                        = var.frontdoor_loadbalancer_enabled
  backend_pools_send_receive_timeout_seconds   = var.backend_pools_send_receive_timeout_seconds
  tags                                         = var.tags
Load Balancing
  • Name
  • Sample size – number of samples to use for load balancing decisions
  • Successful samples required – how many samples must succeed to be considered successful
  • Additional latency – how many milliseconds for probes to fall into the low latency bucket
  dynamic "backend_pool_load_balancing" {
    for_each = var.frontdoor_loadbalancer
    content {
      name                            =
      sample_size                     = backend_pool_load_balancing.value.sample_size
      successful_samples_required     = backend_pool_load_balancing.value.successful_samples_required
      additional_latency_milliseconds = backend_pool_load_balancing.value.successful_samples_required
Routing Rule
  • Name
  • Accepted protocols – e.g. Http, Https
  • Patterns for route match – e.g. “/*”, “/mypath”, “/mypath/*”
  • Enabled
  • Forwarding or Redirect configuration
  dynamic "routing_rule" {
    for_each = var.frontdoor_routing_rule
    content {
        name               =
        accepted_protocols = routing_rule.value.accepted_protocols
        patterns_to_match  = routing_rule.value.patterns_to_match        
        frontend_endpoints = values({for x, endpoint in var.frontend_endpoint : x =>})
        dynamic "forwarding_configuration" {
          for_each = routing_rule.value.configuration == "Forwarding" ? routing_rule.value.forwarding_configuration : []
          content {
            backend_pool_name                     = forwarding_configuration.value.backend_pool_name
            cache_enabled                         = forwarding_configuration.value.cache_enabled                           
            cache_use_dynamic_compression         = forwarding_configuration.value.cache_use_dynamic_compression 
            cache_query_parameter_strip_directive = forwarding_configuration.value.cache_query_parameter_strip_directive
            custom_forwarding_path                = forwarding_configuration.value.custom_forwarding_path
            forwarding_protocol                   = forwarding_configuration.value.forwarding_protocol
        dynamic "redirect_configuration" {
          for_each = routing_rule.value.configuration == "Redirecting" ? routing_rule.value.redirect_configuration : []
          content {
            custom_host         = redirect_configuration.value.custom_host
            redirect_protocol   = redirect_configuration.value.redirect_protocol
            redirect_type       = redirect_configuration.value.redirect_type
            custom_fragment     = redirect_configuration.value.custom_fragment
            custom_path         = redirect_configuration.value.custom_path
            custom_query_string = redirect_configuration.value.custom_query_string

As the Frontend Endpoints are configured separately, being able to find a way to reuse the names to configure the frontend_endpoints for the routing was invaluable. The values function allows to read just the values from the given object field. The expression is very similar to C#, using a lambda (=>) to project just the name field to then get values from.

frontend_endpoints = values({for x, endpoint in var.frontend_endpoint : x =>})
Health Probe
  • Name
  • Enabled
  • Path
  • Protocol – e.g. Http, Https
  • Probe method – e.g. HEAD, GET
  • Interval – interval between each health probe
 dynamic "backend_pool_health_probe" {
    for_each = var.frontdoor_health_probe
    content {
      name                =
      enabled             = backend_pool_health_probe.value.enabled
      path                = backend_pool_health_probe.value.path
      protocol            = backend_pool_health_probe.value.protocol
      probe_method        = backend_pool_health_probe.value.probe_method
      interval_in_seconds = backend_pool_health_probe.value.interval_in_seconds
Backend Pool
  • Name
  • Load Balancer name
  • Health probe name
  • Backend
    • Enabled
    • Host Header
    • Address
    • HTTP port
    • HTTPS port
    • Priority
    • Weight
  dynamic "backend_pool" {
    for_each = var.frontdoor_backend
    content {
       name                =      
       load_balancing_name = backend_pool.value.loadbalancing_name
       health_probe_name   = backend_pool.value.health_probe_name

       dynamic "backend" {
        for_each = backend_pool.value.backend
        content {
          enabled     = backend.value.enabled
          address     = backend.value.address
          host_header = backend.value.host_header
          http_port   = backend.value.http_port
          https_port  = backend.value.https_port
          priority    = backend.value.priority
          weight      = backend.value.weight

Frontend Endpoint
  • Name
  • Host Name
  • Custom Domain
  • Session Affinity
  • WAF Policy ID
  dynamic "frontend_endpoint" {
    for_each = var.frontend_endpoint
    content {
      name                                    =
      host_name                               = frontend_endpoint.value.host_name
      custom_https_provisioning_enabled       = frontend_endpoint.value.custom_https_provisioning_enabled    
      session_affinity_enabled                = frontend_endpoint.value.session_affinity_enabled
      session_affinity_ttl_seconds            = frontend_endpoint.value.session_affinity_ttl_seconds
      web_application_firewall_policy_link_id = frontend_endpoint.value.waf_policy_link_id
      dynamic "custom_https_configuration" {
        for_each = frontend_endpoint.value.custom_https_provisioning_enabled == false ? [] : list(frontend_endpoint.value.custom_https_configuration.certificate_source)
        content {
          certificate_source = custom_https_configuration.value.certificate_source

All the variables that are defined for the Module.

variable "frontdoor_resource_group_name" {
  description = "(Required) Resource Group name"
  type = string

variable "frontdoor_name" {
  description = "(Required) Name of the Azure Front Door to create"
  type = string

variable "frontdoor_loadbalancer_enabled" {
  description = "(Required) Enable the load balancer for Azure Front Door"
  type = bool

variable "enforce_backend_pools_certificate_name_check" {
  description = "Enforce the certificate name check for Azure Front Door"
  type = bool
  default = false

variable "backend_pools_send_receive_timeout_seconds" {
  description = "Set the send/receive timeout for Azure Front Door"
  type = number
  default = 60

variable "tags" {
  description = "(Required) Tags for Azure Front Door"  

variable "frontend_endpoint" {
  description = "(Required) Frontend Endpoints for Azure Front Door"

variable "frontdoor_routing_rule" {
  description = "(Required) Routing rules for Azure Front Door"

variable "frontdoor_loadbalancer" {
  description = "(Required) Load Balancer settings for Azure Front Door"

variable "frontdoor_health_probe" {
  description = "(Required) Health Probe settings for Azure Front Door"

variable "frontdoor_backend" {
  description = "(Required) Backend settings for Azure Front Door"

Example of Use

Make sure that Terraform is not less than 0.12.x and that the provider (azurerm) is using the latest version (at the time of writing this was 2.14.0).

terraform {
  required_version = ">= 0.12"
# Configure the Azure Provider
provider "azurerm" {
  # whilst the `version` attribute is optional, we recommend pinning to a given version of the Provider
  version = "=2.14.0"
  features {}

# Create a resource group
resource "azurerm_resource_group" "instance" {
  name     = "my-frontdoor-rg"
  location = "westeurope"

# Create Front Door
module "front-door" {
  source                                            = "./modules/frontdoor"    
  tags                                              = { Department = "Ops"}
  frontdoor_resource_group_name                     =
  frontdoor_name                                    = "my-frontdoor"
  frontdoor_loadbalancer_enabled                    = true
  backend_pools_send_receive_timeout_seconds        = 240
  frontend_endpoint      = [{
      name                                    = "my-frontdoor-frontend-endpoint"
      host_name                               = ""
      custom_https_provisioning_enabled       = false
      custom_https_configuration              = { certificate_source = "FrontDoor"}
      session_affinity_enabled                = false
      session_affinity_ttl_seconds            = 0
      waf_policy_link_id                      = ""

  frontdoor_routing_rule = [{
      name               = "my-routing-rule"
      accepted_protocols = ["Http", "Https"] 
      patterns_to_match  = ["/*"]
      enabled            = true              
      configuration      = "Forwarding"
      forwarding_configuration = [{
        backend_pool_name                     = "backendBing"
        cache_enabled                         = false       
        cache_use_dynamic_compression         = false       
        cache_query_parameter_strip_directive = "StripNone" 
        custom_forwarding_path                = ""
        forwarding_protocol                   = "MatchRequest"   

  frontdoor_loadbalancer =  [{      
      name                            = "loadbalancer"
      sample_size                     = 4
      successful_samples_required     = 2
      additional_latency_milliseconds = 0

  frontdoor_health_probe = [{      
      name                = "healthprobe"
      enabled             = true
      path                = "/"
      protocol            = "Http"
      probe_method        = "HEAD"
      interval_in_seconds = 60

  frontdoor_backend =  [{
      name               = "backendBing"
      loadbalancing_name = "loadbalancer"
      health_probe_name  = "healthprobe"
      backend = [{
        enabled     = true
        host_header = ""
        address     = ""
        http_port   = 80
        https_port  = 443
        priority    = 1
        weight      = 50

The code for this article and full module can be found in my GitHub repository.

I’ve ran the example with the newly created module, let’s take a look at the Azure Portal to see if an Azure Front Door was created.

It looks like everything was setup and working, selecting the link forwarded to as the example was configured to do.

Note: Azure Front Door configuration can be viewed and updated via the Azure CLI.


I am sure there is more to learn about Terraform and Azure Front Door and this configuration may well get updated in the future as I learn more. I’ve not only gained a better understanding of what Terraform has to offer, but what Azure Front Door has to offer as well.