Azure Application Gateway for dummies
Tags: azure, gateway, network
Published: 2022-02-27 17:35:59

---
title: Azure Application Gateway for dummies
published: true
description: With this post I try to explain components of Azure Application Gateway as simple as possible
tags: #azure #gateway #network
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kwh021f2bvvysp62mdty.png
---

## Motivation

I've been setting up **Azure Application Gateway** now for some years in various scenarios. As it is a quite versatile resource and I really always have to get my ducks in a row before I start applying it to a certain scenario. So this post here is merely a reference for myself - my future dummy me. By taking you along, maybe you also gain some information out of this post.

## What is Application Gateway?

**Azure Application Gateway** is a [layer 7 - application layer](https://en.wikipedia.org/wiki/OSI_model#Layer_7:_Application_layer) - load balancer and reverse proxy including an optional WAF - Web Application Firewall - to inspect and even block traffic towards a web application. **Web application** already suggests that it is only designed with HTTP/HTTPS traffic. On the other hand **Azure Loadbalancer** works on [layer 4 - the transport layer](https://en.wikipedia.org/wiki/OSI_model#Layer_4:_Transport_layer) - and hence is independent of application layer protocols - but of course can also handle HTTP/HTTPS.

More material:

- [What is Azure Application Gateway? - MS docs](https://docs.microsoft.com/en-us/azure/application-gateway/overview)
- [Adam Gordon proving a really good introduction to what Azure Application Gateway is in this video](https://youtu.be/n9uHSHO25cE)

> Fun fact: although Application Gateway seems to be intended to front a bunch of VMs producing ones web application content and functionality, I never used it for such a scenario; I don't do web applications and APIs like that - I always try to follow a PaaS-first approach

## What are the primitives in Application Gateway?

I do not want to repeat all the material that is already out there and rather focus on the various components and their role in the orchestration of the gateway.

For that I assembled this schematic overview with the most important components - or the components for the most common scenarios I had so far:

![Application Gateway schematic view](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2cffvwcqk3bwa71ejpjj.png)

I will try to explain these components by following a hypothetical traffic flow - so that it should be easier at which point of a flow the components come into play. This textual explanation is complemented with a corresponding [Bicep template in API version 2021-05-01](https://docs.microsoft.com/en-us/azure/templates/microsoft.network/2021-05-01/applicationgateways?tabs=bicep) - using **Bicep** here because it offers the least noise (compared to ARM, Pulumi, Terraform, ...) to show the essence of what configuration options are available.

> To understand configuration options in detail I usually check out [Azure REST API for Application Gateways - Create Or Update](https://docs.microsoft.com/en-us/rest/api/application-gateway/application-gateways/create-or-update) (or any other resource) and then transfer back to the Infrastructure-as-Code stack I use.

### Abbreviations used

| short | long |
| ---- | ---- |
| AG | Azure Application Gateway |
| APIM | Azure API Management |
| CNAME | [DNS CNAME Resource Record](https://de.wikipedia.org/wiki/CNAME_Resource_Record) |
| FQDN | [Fully qualified domain name](https://en.wikipedia.org/wiki/Fully_qualified_domain_name) |
| VNET | virtual network |

### Ingress

First let's pick up incoming traffic. This is done with an entry in `frontendIPConfigurations`. Here I can specify whether to pick up from a public IP address - another Azure resource external to AG, a private address in the gateway subnet or a private link configuration - specified in another section of the AG.

For the most common scenario, public IP, while DNS related referencing (e.g. putting a CNAME on the FQDN provided by Azure) is targetted towards this [public IP resource](https://docs.microsoft.com/en-us/azure/virtual-network/ip-services/public-ip-addresses), actual SSL certificate valid for the FQDN has to be linked or uploaded to AG.

```
    frontendIPConfigurations: [
      {
        id: 'string'
        name: 'string'
        properties: {
          privateIPAddress: 'string'
          privateIPAllocationMethod: 'string'
          privateLinkConfiguration: {
            id: 'string'
          }
          publicIPAddress: {
            id: 'string'
          }
          subnet: {
            id: 'string'
          }
        }
      }
    ]
    privateLinkConfigurations: [
      {
        id: 'string'
        name: 'string'
        properties: {
          ipConfigurations: [
            {
              id: 'string'
              name: 'string'
              properties: {
                primary: bool
                privateIPAddress: 'string'
                privateIPAllocationMethod: 'string'
                subnet: {
                  id: 'string'
                }
              }
            }
          ]
        }
      }
    ]    
```

> a sample for a private link configuration can be found in [my post](https://dev.to/kaiwalter/use-azure-application-gateway-private-link-configuration-for-an-internal-api-management-1d6o), where I use it to make an APIM instance available for another VNET without peering

Next element is an entry in `httpListeners`, `frontendPorts` and `sslCertificates` (in case HTTPS traffic is to be processed). `httpListener` picks up from a `frontendIPConfiguration` (to be referenced) and specifies which port (443, 80, 8080, ...) and protocol (Https, Http) is to be used. A configuration for **Https** requires that a certificate is made available to AG, either by specifying with `data` or `keyVaultSecretId` in `sslCertificates`. When using a **HTTP-01 certificate challenge** I often add a HTTP configuration [like in this post](https://dev.to/kaiwalter/handling-an-acme-challenge-response-with-a-temporary-azure-container-instance-3ae0) which points to a temporary backend handling the challenge. Also I then make the Http / Https configuration switchable like in [this sample](https://github.com/KaiWalter/container-apps-experimental/blob/7d73fd26ddb3f38f753e9104d0419d4578ac01df/ca-bicep/appgw.bicep#L16) - so as long as no certificate is yet bound on the AG I configure e.g. with protocol/port `Http/8080` and then switch immediately to protocol/port `Https/443` when certificate is uploaded.

> Best is not to point to actual backends as long as Https is not activated!

```
    httpListeners: [
      {
        id: 'string'
        name: 'string'
        properties: {
          customErrorConfigurations: [
            {
              customErrorPageUrl: 'string'
              statusCode: 'string'
            }
          ]
          firewallPolicy: {
            id: 'string'
          }
          frontendIPConfiguration: {
            id: 'string'
          }
          frontendPort: {
            id: 'string'
          }
          hostName: 'string'
          hostNames: [
            'string'
          ]
          protocol: 'string'
          requireServerNameIndication: bool
          sslCertificate: {
            id: 'string'
          }
          sslProfile: {
            id: 'string'
          }
        }
      }
    ]
    frontendPorts: [
      {
        id: 'string'
        name: 'string'
        properties: {
          port: int
        }
      }
    ]
    sslCertificates: [
      {
        id: 'string'
        name: 'string'
        properties: {
          data: 'string'
          keyVaultSecretId: 'string'
          password: 'string'
        }
      }
    ]    
```

SSL processing can additionally be adapted to your requirements with these elements - but would not necessarily be required:

```
    sslPolicy: {
      cipherSuites: [
        'string'
      ]
      disabledSslProtocols: [
        'string'
      ]
      minProtocolVersion: 'string'
      policyName: 'string'
      policyType: 'string'
    }
    sslProfiles: [
      {
        id: 'string'
        name: 'string'
        properties: {
          clientAuthConfiguration: {
            verifyClientCertIssuerDN: bool
          }
          sslPolicy: {
            cipherSuites: [
              'string'
            ]
            disabledSslProtocols: [
              'string'
            ]
            minProtocolVersion: 'string'
            policyName: 'string'
            policyType: 'string'
          }
          trustedClientCertificates: [
            {
              id: 'string'
            }
          ]
        }
      }
    ]
    trustedClientCertificates: [
      {
        id: 'string'
        name: 'string'
        properties: {
          data: 'string'
        }
      }
    ]
    trustedRootCertificates: [
      {
        id: 'string'
        name: 'string'
        properties: {
          data: 'string'
          keyVaultSecretId: 'string'
        }
      }
    ]
```

### Specifying backend(s)

No we assume traffic is inside the gateway and we have to determine where to send it to. An entry in `backendAddressPools` with the `backendAddresses` array specifies traffic targets. Either FQDN or IP addresses can be specified. When using FQDNs AG needs to be able to DNS resolve the domain - so DNS configuration needs to facilitate what is required here.

A common mistake in scenarios where [**AG is used to integrate APIM** in an internal VNET](https://docs.microsoft.com/en-us/azure/api-management/api-management-howto-integrate-internal-vnet-appgateway) is, that `{apim-service-name}.azure-api.net` would not point to the internal IP address and hence FQDN would not resolve correctly. In this scenario `ipAddress` has to be used **but** when integrated APIM services is operated on HTTPS, the service expects to be addressed with the correct `Host` HTTP header `{apim-service-name}.azure-api.net`, hence either `pickHostNameFromBackendAddress` or a specific `hostName` has to be specified in the corresponding `backendHttpSettingsCollection` entry. Same goes for the entry in `probes`.

**IMPORTANT:** the probe really has to be able to find and get a valid response from the backend. When the probe is not healthy - this can by checked in Azure Portal or `az network application-gateway show-backend-health -n {appGwName}` - AG will not forward traffic and reward you with a [502 bad gateway error](https://docs.microsoft.com/en-us/azure/application-gateway/application-gateway-troubleshooting-502).

```
    backendAddressPools: [
      {
        id: 'string'
        name: 'string'
        properties: {
          backendAddresses: [
            {
              fqdn: 'string'
              ipAddress: 'string'
            }
          ]
        }
      }
    ]
    backendHttpSettingsCollection: [
      {
        id: 'string'
        name: 'string'
        properties: {
          affinityCookieName: 'string'
          authenticationCertificates: [
            {
              id: 'string'
            }
          ]
          connectionDraining: {
            drainTimeoutInSec: int
            enabled: bool
          }
          cookieBasedAffinity: 'string'
          hostName: 'string'
          path: 'string'
          pickHostNameFromBackendAddress: bool
          port: int
          probe: {
            id: 'string'
          }
          probeEnabled: bool
          protocol: 'string'
          requestTimeout: int
          trustedRootCertificates: [
            {
              id: 'string'
            }
          ]
        }
      }
    ]
    probes: [
      {
        id: 'string'
        name: 'string'
        properties: {
          host: 'string'
          interval: int
          match: {
            body: 'string'
            statusCodes: [
              'string'
            ]
          }
          minServers: int
          path: 'string'
          pickHostNameFromBackendHttpSettings: bool
          port: int
          protocol: 'string'
          timeout: int
          unhealthyThreshold: int
        }
      }
    ]
```

For APIM the status endpoint can be used to be referenced in a probe:

```
    probes: [
      {
        name: 'api-gateway-probe'
        properties: {
          protocol: 'Https'
          port: 443
          path: '/status-0123456789abcdef'
          interval: 15
          timeout: 15
          host: apiGatewayHostname
          unhealthyThreshold: 3
          match: {
            statusCodes: [
              '200'
            ]
          }
        }
      }
    ]
```

### Request processing

Having specified ingress and backend, those components can we wired together with an entry in `requestRoutingRules`. `ruleType` `Basic` forwards all traffic 1:1. Limiting fowarding only for certain paths can be achieved with `ruleType` `PathBasedRouting` and entries in `urlPathMaps`. With `rewriteRuleSets` some [basic rewriting](https://docs.microsoft.com/en-us/azure/application-gateway/rewrite-http-headers-url) of the request is possible.

```
    requestRoutingRules: [
      {
        id: 'string'
        name: 'string'
        properties: {
          backendAddressPool: {
            id: 'string'
          }
          backendHttpSettings: {
            id: 'string'
          }
          httpListener: {
            id: 'string'
          }
          loadDistributionPolicy: {
            id: 'string'
          }
          priority: int
          redirectConfiguration: {
            id: 'string'
          }
          rewriteRuleSet: {
            id: 'string'
          }
          ruleType: 'string'
          urlPathMap: {
            id: 'string'
          }
        }
      }
    ]
    rewriteRuleSets: [
      {
        id: 'string'
        name: 'string'
        properties: {
          rewriteRules: [
            {
              actionSet: {
                requestHeaderConfigurations: [
                  {
                    headerName: 'string'
                    headerValue: 'string'
                  }
                ]
                responseHeaderConfigurations: [
                  {
                    headerName: 'string'
                    headerValue: 'string'
                  }
                ]
                urlConfiguration: {
                  modifiedPath: 'string'
                  modifiedQueryString: 'string'
                  reroute: bool
                }
              }
              conditions: [
                {
                  ignoreCase: bool
                  negate: bool
                  pattern: 'string'
                  variable: 'string'
                }
              ]
              name: 'string'
              ruleSequence: int
            }
          ]
        }
      }
    ]
    urlPathMaps: [
      {
        id: 'string'
        name: 'string'
        properties: {
          defaultBackendAddressPool: {
            id: 'string'
          }
          defaultBackendHttpSettings: {
            id: 'string'
          }
          defaultLoadDistributionPolicy: {
            id: 'string'
          }
          defaultRedirectConfiguration: {
            id: 'string'
          }
          defaultRewriteRuleSet: {
            id: 'string'
          }
          pathRules: [
            {
              id: 'string'
              name: 'string'
              properties: {
                backendAddressPool: {
                  id: 'string'
                }
                backendHttpSettings: {
                  id: 'string'
                }
                firewallPolicy: {
                  id: 'string'
                }
                loadDistributionPolicy: {
                  id: 'string'
                }
                paths: [
                  'string'
                ]
                redirectConfiguration: {
                  id: 'string'
                }
                rewriteRuleSet: {
                  id: 'string'
                }
              }
            }
          ]
        }
      }
    ]    
```

#### sample ruleset for client certificate extraction

One common scenario is to extract the client certificate with AG and then pass it to a downstream services:

```
    rewriteRuleSets: [
      {
        name: 'extract-client-cert'
        properties: {
          rewriteRules: [
            {
              ruleSequence: 100
              name: 'extract-cllient-cert'
              actionSet: {
                requestHeaderConfigurations: [
                  {
                    headerName: 'X-ARR-ClientCert'
                    headerValue: '{var_client_certificate}'
                  }
                ]
              }
            }
          ]
        }
      }
    ]
```

> [server variables that can be used for such a rewrite](https://docs.microsoft.com/en-us/azure/application-gateway/rewrite-http-headers-url#server-variables)

### Egress

The entry in `gatewayIPConfigurations` tells AG to which subnet the traffic is sent to.

Some limitations I experienced:

- the gateway subnet can only contain AG resources, no other Azure resources; also when migrating from AG v1 to v2 those could not be mixed in the same subnet; this also means when doing the AG to APIM internal integrations, that APIM has to reside in a different subnet
- a private link on the AG has to be in the same VNET, but a different subnet

```
    gatewayIPConfigurations: [
      {
        id: 'string'
        name: 'string'
        properties: {
          subnet: {
            id: 'string'
          }
        }
      }
    ]
```

## Conclusion

I know I did not touch all components and configuration options in Application Gateway with this post. I only showed the essential ones which are required to get traffic passing through AG.

Anyway I hope this information is useful to anybody out there. As mentioned and as you can see from the samples I provided, Application Gateway is an elementary component in many of the VNET isolated workloads I build. Hence my future me will certainly come back here from time to time ... fix typos and probably also add more findings and specifics.