Deploy Keycloak on Azure App Service via docker with terraform
Tags: terraform, azure, keycloak, docker
Published: 2023-10-30 07:21:17

Keycloak is a powerful open-source Authentication and Authorization solution that offers extensive features and capabilities. Its popularity is increasing due to the superior feature set compared to its competitors' free versions. While Keycloak was initially built on [Spring Boot](https://spring.io/projects/spring-boot), the Keycloak team migrated to [Quarkus](https://quarkus.io/about/) for the latest versions. Currently, the latest Keycloak version is [21.1.1](https://quay.io/repository/keycloak/keycloak?tab=tags).

In the past, I used the **JBoss** version of Keycloak with Spring Boot, but it is no longer maintained and should be avoided for new projects. However, most articles and tutorials on setting up Keycloak still refer to or use the old version, which introduces several differences and breaking changes. Additionally, many articles only provide demos or examples that are not suitable for production environments, as they rely on default databases or docker-compose configurations that may not work with specific scenarios such as HTTPS or reverse proxies.

One interesting aspect of the newest Keycloak releases is the configurability of various parameters. However, not all of these parameters are adequately documented, making it challenging to use them in specific scenarios, such as `Azure App Service`.

In this article, I will guide you through the step-by-step process of setting up a Keycloak server hosted on Azure App Service using a custom Docker container. The setup will include configuring an external database and using **Terraform** for deployment. While this solution may not be the best fit for every scenario, it provides a quick and practical guide for those facing similar requirements.

## Step-by-Step Guide
Let's dive into the tutorial section, where I'll explain the steps I took to set up Keycloak on Azure App Service.

#### Docker Image Configuration
First, we need to set up the Dockerfile for the Keycloak image to be deployed. Keep in mind that you may want to mount a volume in the container for themes or custom configurations, although this guide won't cover those aspects. You can refer to the official documentation for customizing the Keycloak server, as it provides straightforward instructions.

Below is an example of a Dockerfile configuration:

```
FROM quay.io/keycloak/keycloak:21.1.1 as builder

# Enable health and metrics support
ENV KC_HEALTH_ENABLED=true
ENV KC_METRICS_ENABLED=true

# Configure a database vendor
ENV KC_DB=postgres

WORKDIR /opt/keycloak

RUN /opt/keycloak/bin/kc.sh build

FROM quay.io/keycloak/keycloak:21.1.1
COPY --from=builder /opt/keycloak/ /opt/keycloak/

EXPOSE 8080

ENTRYPOINT ["/opt/keycloak/bin/kc.sh", "start --optimized --hostname-strict false --http-enabled true --hostname-strict-https false"]
```
This Dockerfile builds the Keycloak image based on the `quay.io/keycloak/keycloak:21.1.1` image. It sets some options for execution in the entry point command:

- `hostname-strict > false`: This setting resolves the hostname from requests. For simplicity, we remove this check and allow the Azure DNS to handle the hostname.
- `http-enabled true`: This option enables the HTTP listener in the container.
- `hostname-strict-https false`: As we're using a proxy (Azure App Service), the requests are forwarded via HTTPS and communicated to the container through HTTP. Therefore, we relax the policy that requires strict HTTPS communication within the container. This configuration is crucial for our setup but is not explicitly available in the provided documentation.

#### Terraform: Setting up the Azure Container Registry (ACR)
The following Terraform script sets up the Azure Container Registry (ACR), where we will push our Keycloak image. We assume that the ACR already exists in Azure when running the pipeline.

```

resource "azurerm_container_registry" "acridentity" {
  name                = "[ouracrregistryname]"
  resource_group_name = "[your rg name]"
  location            = "[your rg location]"
  admin_enabled       = true
  sku                 = "Basic"
}

resource "azurerm_role_assignment" "acr" {
  role_definition_name = "AcrPull"
  scope                = azurerm_container_registry.acridentity.id
  principal_id         = azurerm_linux_web_app.keycloak.identity[0].principal_id
}

```
In addition, we grant the Keycloak Linux app (described later) the necessary role to pull the image from the repository.

#### Pipeline: Building and Pushing the Image
For this demonstration, I'll be using Azure DevOps, but the process of building and pushing the image to a private container registry can be adapted to other platforms or public registries.

```
      - job: "Build_And_Push_Keycloak_Image"
        displayName: "Build & Push Keycloak Image"
        pool:
          vmImage: "ubuntu-22.04"
        steps:
        - task: Docker@2
          displayName: Build and push the Keycloak image
          inputs:
            command: buildAndPush
            repository: $(imageRepository)
            dockerfile: $(dockerfilePath)
            containerRegistry: $(acrServiceConnection)
            tags: latest
```
This pipeline requires a few variables to be configured:

- `acrServiceConnection`: The service connection to the Azure Container Registry.
- `imageRepository`: The image repository in Azure where the image will be uploaded.
- `dockerfilePath`: The path to the Dockerfile in the repository.

### Terraform: Setting up the Database Server
The Terraform script configures a database server where the Keycloak server will connect:

```
resource "azurerm_postgresql_server" "keycloakdbserver" {
  resource_group_name              = "[your rg name]"
  location                         = "[your rg location]"
  name                             = "yourkeycloakdbserver"
  sku_name                         = "GP_Gen5_2"
  version                          = "11"
  ssl_enforcement_enabled          = true
  ssl_minimal_tls_version_enforced = "TLS1_2"
  administrator_login = "${var.KEYCLOAK_DB_USER}"
  administrator_login_password = "${var.KEYCLOAK_DB_PASSWORD}"
}

resource "azurerm_postgresql_database" "keycloakdb" {
  name                = "keycloak"
  resource_group_name = "[your rg name]"
  charset             = "UTF8"
  collation           = "English_United States.1252"
  server_name         = azurerm_postgresql_server.keycloakdbserver.name
}

resource "azurerm_postgresql_firewall_rule" "allowaccesstokeycloakdb" {
  name                = "allowaccesstokeycloakdb"
  resource_group_name = azurerm_resource_group.keycloakrg.name
  start_ip_address    = "0.0.0.0"
  end_ip_address      = "0.0.0.0"
  server_name         = azurerm_postgresql_server.keycloakdbserver.name
}
```
There are a few important considerations regarding this setup:

The newest version of Keycloak uses TLS for database connections and validates the algorithm used. Azure Postgres Flexible Server uses an older algorithm compared to the conventional Postgres server. Hence, we use a regular Azure DB server for Keycloak. Note that the regular Postgres Server will be discontinued and migrated to the flexible version by March 2025, and we hope Microsoft will fix the certificate issue by then.
We add a firewall rule to allow all Azure services to access the database, including the Keycloak server.
The resource group can be created via Terraform, or you can use an existing one.

#### Terraform: Setting up the Linux App (Web Server)
```

resource "azurerm_linux_web_app" "keycloak" {
  name                = "appkeycloak"
  location            = azurerm_resource_group.keycloakrg.location
  resource_group_name = azurerm_resource_group.keycloakrg.name

  service_plan_id         = azurerm_service_plan.spshared.id
  https_only              = true

  site_config {
    container_registry_use_managed_identity = true
    
    application_stack {
      docker_image     = "${azurerm_container_registry.acridentity.name}.azurecr.io/[ouracrregistryname]"
      docker_image_tag = "latest"
    }
  }

  identity {
    type = "SystemAssigned"
  }

  app_settings = {
    "DOCKER_REGISTRY_SERVER_URL" = "https://${azurerm_container_registry.acridentity.name}.azurecr.io"
    "KC_DB": "postgres"
    "KC_DB_URL_HOST": "${azurerm_postgresql_server.keycloakdbserver.fqdn}"
    "KC_DB_URL_PORT": 5432
    "KC_DB_URL_DATABASE": "${azurerm_postgresql_database.keycloakdb.name}"
    "KC_DB_USERNAME": "${var.KEYCLOAK_DB_USER}@${azurerm_postgresql_server.keycloakdbserver.name}"
    "KC_DB_PASSWORD": "${var.KEYCLOAK_DB_PASSWORD}"
    "KC_PROXY": "edge"
    "WEBSITES_PORT": 8080
    "KEYCLOAK_ADMIN" = "${var.KEYCLOAK_ADMIN_USER}"
    "KEYCLOAK_ADMIN_PASSWORD" = "${var.KEYCLOAK_ADMIN_PASSWORD}"
  }
}

```

The most important part of this setup is the environment variables (`app_settings`) that are passed to the app. The `KC_DB` prefixed settings enable the server to connect to our database. The `KC_PROXY` setting enables communication through HTTP between the reverse proxy and Keycloak. This mode is suitable for deployments with a highly secure internal network, where the reverse proxy maintains a secure connection (HTTP over TLS) with clients while communicating with Keycloak over HTTP. This is the case for our setup. The `WEBSITES_PORT` specifies the port where the container is listening (see [documentation](https://learn.microsoft.com/en-us/azure/app-service/configure-custom-container?tabs=debian&pivots=container-linux#configure-port-number)). With the `KEYCLOAK_` variables, we set the Keycloak admin username and password.

#### Verifying the Running App
Once we run the Terraform scripts and execute the pipeline, the infrastructure necessary for the Keycloak server to run will be set up. Afterward, the admin console will be accessible at the URL provided by Azure.

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

In conclusion, this article provides a step-by-step guide for setting up a Keycloak server hosted on Azure via App Service. It covers topics such as Docker container setup, Terraform scripts for infrastructure provisioning, and pipeline configuration for building and pushing the Keycloak image. By following this guide, developers can quickly deploy a Keycloak server in a production environment, utilizing a custom Docker container, an external database, and Azure resources.