Deployment pipeline of Application Gateway with App Service
Published: 2021-11-23 14:03:40

# Summary
In this article, I would share problems I faced when deploying and configuring Azure Application Gateway and App Service. At this point, I have not solved problems of App Service redirection for HTTP protocol, but I was able to successfully build it for HTTPS. The sample code is here [network-sample-app](https://github.com/koheikawata/network-sample-app).

# TOC
- [HTTP](#http)
  - [1. Context](#context)
  - [2. Problem](#problem)
  - [3. Cause](#cause)
  - [4. Workaround failed](#cause)
- [HTTPS](#https)
  - [1. Probe problem](#probe-problem)
  - [2. Probe cause](#probe-cause)
  - [3. Probe solution](#probe-solution)

# HTTP
## Context <a name="context"></a>
I was trying to build a sample template with Web App and Application Gateway using azure-quickstart-templates [web-app-with-app-gateway-v2](https://github.com/Azure/azure-quickstart-templates/blob/master/quickstarts/microsoft.web/web-app-with-app-gateway-v2/azuredeploy.json).
- Public IP DNS: `kawatawebapp-evrlkrxuhtnti.japaneast.cloudapp.azure.com`
- App Service endpoint: `kawatawebapp-evrlkrxuhtnti.azurewebsites.net`
- Frontend protocol: **HTTP**
- Backend protocol: **HTTP**
- Backend inbound is through **Service Endpoint** from Virtual Network subnet to App Service

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aez78xfzktt80otludby.png)

## Problem <a name="problem"></a>
At first, it looks working well. I can see the default App Service view through a browser. 

**Before API code deployment**
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l419mq2vxdg573ehxcw4.png)

However, whe I deployed [a sample .NET Web API](https://github.com/koheikawata/network-sample-app/blob/main/src/TravelApi/TravelApi/Controllers/TravelPlanController.cs) with a GET request route `api/travelplan`, it does not work. The browser redirects the response and showed 403 Forbidden status code.

**Request to `.japaneast.cloudapp.azure.com/api/travelplan`**
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sprohtymfbq6fz4ghhky.png) 

**Response from `.azurewebsites.net/api/travelplan`**
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fioxnxzt8jfl7owv6klq.png)

## Cause <a name="cause"></a>

The domain name of original request is `.cloudapp.azure.com` but a redirection of App Service sets `.azurewebsites.net` in the location header. This is what a Microsoft official documentation [Troubleshoot App Service issues in Application Gateway](https://docs.microsoft.com/en-us/azure/application-gateway/troubleshoot-app-service-redirection-app-service-url) explains.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0l8697rky6drdgwngjih.png)
 
## Workaround failed <a name="workaround"></a>

In the official documentation, I found two workarounds.

- Rewrite the location header
- Use a custom domain name

I do not have my own domain name, and I tried the first one that rewrites the location header. Application Gateway Rewrite configuration is added like below.

**ARM template**
```
"rewriteRuleSets": [
  {
    "name": "rewriteLocationHeader",
    "properties": {
      "rewriteRules": [
        {
          "ruleSequence": 100,
          "conditions": [
              {
                  "variable": "http_resp_Location",
                  "pattern": "(https?):\\/\\/.*azurewebsites\\.net(.*)$",
                  "ignoreCase": true
              }
          ],
          "name": "NewRewrite",
          "actionSet": {
              "responseHeaderConfigurations": [
                  {
                      "headerName": "Location",
                      "headerValue": "{http_resp_Location_1}://kawatawebapp-evrlkrxuhtnti.japaneast.cloudapp.azure.com{http_resp_Location_2}"
                  }
              ]
          }
        }
      ]
    }
  }
]
```
It keeps the original domain name `.cloudapp.azure.com` in the response header, but it does not respond and got request timeout.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/llhb08s8gsr17jzgzu6w.png)

# HTTPS

Acutally, I have not found a solution for the issue above yet, but I tried SSL with Key Vault to see if it works. A self-signed certificate is stored in the Key Vault and Application Gateway retrieves it for SSL communication. The code is in thie respository [network-sample-app](https://github.com/koheikawata/network-sample-app).

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q2dq4vi56wv9pc1rsyf9.png)

## Probe problem <a name="probe-problem"></a>

This SSL configuration works, but sometimes I faced errors of 502 Bad Gateway or 404 Not Found. I looked through the problem and found this happens because of Application Gateway Probe configuration, probe path `"/"`. In the beginning, I set up the configuration below.

**ARM template**
```
"probes": [
  {
    "Name": "BackendProbe",
    "properties": {
      "Protocol": "Https",
      "Path": "/",
      "Interval": 30,
      "Timeout": 10,
      "UnhealthyThreshold": 3,
      "MinServers": 0,
      "PickHostNameFromBackendHttpSettings": true
    }
  }
]
```

- Before code deploy

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ms5tpbrbn9i5l4uid2jk.png)
 
- After code deploy

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pxn7gb7ke5nblqkee9vu.png)

## Probe cause <a name="probe-cause"></a>

According to the Microsoft documentation [Application Gateway health monitoring overview](https://docs.microsoft.com/en-us/azure/application-gateway/application-gateway-probe-overview), **Azure Application Gateway automatically removes any resource considered unhealthy from the pool**. [This sample API](https://github.com/koheikawata/network-sample-app/blob/main/src/TravelApi/TravelApi/Controllers/TravelPlanController.cs) does not have the root path and showed errors.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f2frj58vomrof4s0yvrx.png)

## Probe solution <a name="probe-solution"></a>

So I put the code below so the custom probe works correctly with the path `/api/travelplan` after the code deployment.

```
"probes": [
  {
    "Name": "BackendProbe",
    "properties": {
      "Protocol": "Https",
      "Path": "/api/travelplan",
      "Interval": 30,
      "Timeout": 10,
      "UnhealthyThreshold": 3,
      "MinServers": 0,
      "PickHostNameFromBackendHttpSettings": true
    }
  }
]
```

Finally, it works.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bw13h54a77t12ve8v2iz.png)

Below is the pattern table what response you receive depending on probe path, code deployment, request route.

|Code|Not deployed|Deployed|Deployed|
|:----|:----|:----|:----|
|Request route|/|/|/api/travelplan|
|Probe path "/"|200|502|502|
|Probe path "/api/travelplan"|502|404|200|

In the code sample, I use `"/health"` route path for the custom probe. You need to change the code something like below.

**Startup.cs**
```csharp
public void ConfigureServices(IServiceCollection services)
{
    ...

    services.AddHealthChecks();
}
```

```csharp
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    ...

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
        endpoints.MapHealthChecks("/health");
    });
}
```

**ARM template**

```
{
  "type": "Microsoft.Web/sites",
  
  ...

  "properties": {
    "siteConfig": {
      "healthCheckPath": "/health"
    }
  }
}
```

```
"probes": [
  {
    "Name": "BackendProbe",
    "properties": {
      "Protocol": "Https",
      "Path": "/health",
      "Interval": 30,
      "Timeout": 10,
      "UnhealthyThreshold": 3,
      "MinServers": 0,
      "PickHostNameFromBackendHttpSettings": true
    }
  }
]
```