Azure Container App (ACA) — Backend server installation

Support DQE
Support DQE
  • Updated

Note: All the elements described below are recommendations from DQE, based on deployment experience with different customers.

The customer is responsible for the integration into its own architecture.

An architect familiar with the context and internal infrastructure should align DQE's recommendations with the client infrastructure.

As part of the installation, the approach has been dockerized and all components are deployed in Docker.

  • Redis for data storage and processing
  • RabbitMQ – scheduler of actions performed by the DQE engine
  • The Redis database is deleted after each process

1. Deployment Architecture

The exchanges between the front end and the back end are detailed below.

To deploy an instance of the DQE One Server application on your Azure organization, DQE Software provides dedicated access to the container registry. There are different ways to deploy this container, but we recommend using an Azure Container App.

The procedure is explained in the installation section.

This involves the configuration of an Azure Application Gateway, or another load balancer, to expose this application with a public IP address and DNS. Otherwise, the Salesforce org where you installed the DQE One package will not be able to access the application.

To deploy this application, you need to create a YAML configuration file that is detailed in this document.

ACA

2. Flow Matrix

Here is a diagram describing all the flows coming in and out of the Azure Application Gateway in a standard DQE One data process.

All IP addresses and ports described in this diagram must be opened in your gateway or firewall for the process to complete. All incoming connections are made through HTTPS.

FlowMatrix

3. Salesforce Connections

Authentication

When you install the DQE One package on your Salesforce org, you will have to go through some configuration steps. During those steps, the Salesforce org will register to the DQE One Server. At this step, the server creates a unique password and key credentials that are stored in your org and used to authenticate to your DQE One Server instance.

RegistrationFlow

The security protocol used by Salesforce to authenticate users and allow the various operations performed by the package, such as bulk import and export, is JSON Web Token (JWT).

This JWT can only be validated by Salesforce for the users enabled in the connected app installed with the DQE One package.

You can find more information in the official Salesforce documentation here.

Process

Below is the description of the flows between Salesforce and the ACA DQE One Server, and between the ACA DQE One Server and the DQE servers.

Step 1 - Define a New Process

A Salesforce user defines a new process.

Description of the process

  • Object: Person Account
  • Type of processing: Email validation
  • Processed field: Email
  • Filters: see business rules

Step 2 - Start the Process

A Salesforce user starts the process. This user needs to be part of the authorized users in the connected app setup.

Step 3 - Send the Processing Request

The processing request is sent to the ACA DQE One Server.

Step 4 - Authenticate with Salesforce

The ACA DQE One Server authenticates with Salesforce through the connected app using a JWT token.

Step 5 - Export Data

Salesforce allows the server to perform a data export.

LaunchingProcess

Step 6 - Save Exported Data

The ACA DQE One Server exports the relevant data, such as the Id and Email fields of the Person Account object, and saves it in Azure File Storage in CSV format.

Step 7 - Process the Data

If the process is Data Quality processing: the ACA DQE One Server makes anonymized unit API calls on each email in the extracted file. This applies only to email qualification.

During the whole process, this is the only step where some data may be sent by REST API through a secure connection to the DQE Software production server.

If the process is Deduplication processing: the ACA DQE One Server calculates duplicate groups and reconciles records according to the matching rules defined during the workshops.

Step 8 - Generate the Result File

The ACA DQE One Server aggregates all processing responses into a final CSV file.

If the process is deduplication, the ACA DQE One Server calculates the data merge result within the identified duplicate groups. This result is stored in the DQE_Fusion_Json__c field created for this purpose on the processed object, while waiting to be used if the merge process is triggered automatically or manually at the end of processing.

Processing

Step 9 - Authenticate to Salesforce

The ACA DQE One Server authenticates to Salesforce.

Step 10 - Allow Import or Update

Salesforce allows the server to perform a data import or update.

Step 11 - Send Processing Results

The ACA DQE One Server sends the processing results to the relevant records, here Person Account, through the Bulk APIs exposed by Salesforce. Person Accounts are enriched with fields created for this purpose, such as the duplicate group number and, where applicable, the field storing the merge result for that group.

Step 12 - Delete CSV Files

The ACA DQE One Server deletes the CSV file initially received during export in step 6 and the generated CSV file containing the results from step 8.

Step 13 - Send the Processing Report

The ACA DQE One Server sends a statistical processing report to Salesforce, including the number of processed records and processing time. This report is visible in the DQE One application in the Runs object.

SendingResult

3.1. Composition and Services

The stack is composed of Docker images deployed as containers of a single Azure Container App. All containers of the app share the same network, so they communicate through localhost. For each service, we outline the attributes and their functions below.

Web Application (one-server)

A backend container that contains a microservices application. This application exposes all the APIs called by Salesforce to launch processes, manage processing queues, and instantiate processing workers. It is dependent on Redis and RabbitMQ to function properly. This service is exposed on the web through the Nginx reverse proxy, under the /unify/ path.

Container settings

  • image: The name of the image hosted on the DQE Azure Container Registry:

    <Name of the registry container>.azurecr.io/<name of the image>
  • starts once Redis and RabbitMQ are reachable
  • environment: SFAPIVERSION, PORT, REDIS_URL, CLOUDAMQP_URL, CUSTOMUI, UNIFYSERVERURL
  • command: bash ./entrypoint.sh

Worker (queue-worker)

A backend container that processes queued jobs asynchronously. It consumes tasks from the RabbitMQ queue and executes them. Uses the same Docker image as the web application.

Container settings

  • image: same image as the web application service
  • starts once RabbitMQ and the web application are reachable
  • environment: SFAPIVERSION, WORKDIRPATH, REDIS_URL, CLOUDAMQP_URL
  • command: python -u ./unify/queue_worker.pyc

User Interface (one-ui)

This service is a web application exposing a front used to configure deduplication rules. It must therefore also be exposed on the web, through Nginx at the root path /. This service also consumes some APIs from the backend, for example to retrieve metadata from the CRM.

  • image: <imageURL>
  • command: npm start
  • environment: PORT=8001, SESSION_SECRET

Nginx

A reverse proxy that forwards incoming HTTP requests from the Application Gateway to the web application and the user interface. HTTPS is terminated on the Application Gateway. Its configuration file is stored in the nginxconf Azure File Share and mounted into the container at runtime.

  • image: <imageURL>
  • ports: 80
  • volume: nginxconf:/etc/nginx/conf.d

Redis

This service is a key/value database used to store internal operating keys such as Salesforce org configurations, sessions, etc.

It is possible to use the official redis:alpine image, but as a security measure some cloud providers block the retrieval of public images and only allow images from private registries. To overcome this restriction, DQE also publishes a compatible Redis image on its private registry.

Parameters

  • image: <imageURL>
  • volumes: redisvol:/data (Azure File Share)

RabbitMQ

This service is a powerful queue manager allowing the reception and scheduling of processing requests. Until a process has been assigned to a worker and completed, it will be parked in the queue. This also allows for resiliency to failures — if the server is restarted, it resumes the last processing in the queue where it left off.

As with the Redis service image, you have the option to use a public version or the one in the private DQE registry.

Parameters

  • image: <imageURL>
  • volumes: rabbitvol:/var/lib/rabbitmq/mnesia (Azure File Share)
  • environment: RABBITMQ_DEFAULT_USER, RABBITMQ_DEFAULT_PASS, RABBITMQ_DEFAULT_VHOST, RABBITMQ_NODENAME

4. Installation

In this section, we describe the installation protocol to create an instance of DQE One Server as an Azure Container App.

We also provide an example of how to create an Azure Application Gateway.

However, this part will highly depend on your own organization. The customer technical team is responsible for applying the different recommendations.

For more information about configuring your Azure Application Gateway, refer to the Microsoft documentation here.

4.1. Create an Application Gateway

4.1.1. Basic Tab

The first tab should present a form as follows. Fill in the different parts as shown below.

Application Gateway basic tab

4.1.1.1. WAF Policy

The WAF (Web Application Firewall) is used to define the IP ranges authorized to call the Application Gateway, such as Salesforce IP ranges.

Create a new WAF from the WAF policy field in 4.1.1. Basic Tab.

WAF policy configuration

4.1.1.2. VNET

The VNET (Virtual Network) is used to connect the Application Gateway and the ACA (Azure Container App). All flows pass through that private network.

Create a new VNET from the Virtual network field in 4.1.1. Basic Tab.

VNET configuration

4.1.2. Frontends

This tab should present a form as follows. Fill in the different parts as shown below.

Application Gateway frontends

4.1.2.1. Public IP Address

The public IP is exposed by the Application Gateway. It is used to route traffic to the ACA.

Create a new public IP from the Public IP address field in 4.1.2. Frontends.

Public IP configuration

4.1.3. Backends

This tab should present a form as follows. Fill in the different parts as shown below.

Application Gateway backends

4.1.3.1. Backend Pool

The backend pool allows the Application Gateway to route flows through the VNET to the ACA. Create it empty for now: the ACA target is added in 4.4. Setting Up the Application Gateway Backend Pool, once the ACA exists.

Create a new backend pool from Add a backend pool in 4.1.3. Backends.

Backend pool configuration

4.1.4. Configuration

This tab should present a form as follows. Fill in the different parts as shown below.

Application Gateway configuration

4.1.4.1. Routing Rules - Listener

This configuration allows calls to the Application Gateway through the HTTPS protocol. The SSL certificate of your domain is uploaded here, on the HTTPS listener: HTTPS is terminated on the Application Gateway.

Routing rules listener

4.1.4.2. Routing Rules - Backend Target

The backend target is used by the Application Gateway to determine where to route incoming traffic. It uses the backend pool created previously.

Routing rules backend target

4.1.4.3. Routing Rules - Backend Setting

The backend setting is used by the Application Gateway to determine which protocol to use when routing traffic to the ACA. This traffic is sent through the private VNET.

  • Backend protocol: HTTP
  • Backend port: 80
  • Override with new host name: Yes
  • Host name override: Pick host name from backend target

Warning: ACA routes requests according to their host name. If the Application Gateway does not send the ACA host name (Pick host name from backend target), ACA answers 404 and the backend stays unhealthy. HTTP is accepted by the ACA because its ingress is configured with allowInsecure: true (see 4.3.4); the traffic stays inside the private VNET.

Routing rules backend setting

Then click Review + create.

4.2. Create a Container Apps Environment (ACAE)

4.2.1. Add a VNET Subnet for ACAE

Create a new subnet delegated to Microsoft.App/environments from the VNET created in 4.1.1.2. VNET. The subnet must be at least /27 and dedicated to the environment.

ACAE subnet configuration

4.2.2. Install Azure CLI

Azure CLI is required on your computer.

4.2.2.1. UNIX
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
az extension add --name containerapp --upgrade
4.2.2.2. Windows

Refer to the Microsoft documentation here, then run az extension add --name containerapp --upgrade.

4.2.3. Create ACAE

Note: The DQE One stack requires a Dedicated plan. All the containers of the app run in the same replica and need 5 vCPU / 10 Gi in total, which exceeds the maximum of 4 vCPU / 8 Gi per replica of the Consumption plan. The flag --enable-workload-profiles activates Dedicated plan support on the environment.

4.2.3.1. UNIX
az containerapp env create \
 --name <container apps environments name> \
 --resource-group <resource group> \
 --location <location> \
 --internal-only true \
 --enable-workload-profiles \
 --infrastructure-subnet-resource-id $(az network vnet subnet show \
    --resource-group <vnet resource group> \
    --vnet-name <vnet name (created on 4.1.1.2)> \
    --name <subnet name (created on 4.2.1)> \
    --query id -o tsv)
4.2.3.2. Windows
az containerapp env create `
 --name <container apps environments name> `
 --resource-group <resource group> `
 --location <location> `
 --internal-only true `
 --enable-workload-profiles `
 --infrastructure-subnet-resource-id $(az network vnet subnet show `
    --resource-group <vnet resource group> `
    --vnet-name <vnet name (created on 4.1.1.2)> `
    --name <subnet name (created on 4.2.1)> `
    --query id -o tsv)
4.2.3.3. Add a Dedicated workload profile (D8)

Add a Dedicated D8 workload profile (8 vCPU / 32 Gi) to the environment. This node provides enough capacity for the full stack (5 vCPU / 10 Gi total).

UNIX
az containerapp env workload-profile add \
  --name <container apps environments name> \
  --resource-group <resource group> \
  --workload-profile-name "Dedicated-D8" \
  --workload-profile-type D8 \
  --min-nodes 1 \
  --max-nodes 1
Windows
az containerapp env workload-profile add `
  --name <container apps environments name> `
  --resource-group <resource group> `
  --workload-profile-name "Dedicated-D8" `
  --workload-profile-type D8 `
  --min-nodes 1 `
  --max-nodes 1

4.3. Create a Container App from YAML

4.3.1. Create a Storage Account

4.3.1.1. Basics

Complete all necessary information.

Storage account basics tab
4.3.1.2. Advanced

Nothing to change.

4.3.1.3. Networking tab
Storage account networking tab

Note: if you restrict public network access to selected networks, add the ACAE subnet created in 4.2.1. Otherwise the file shares cannot be mounted and the containers do not start.

4.3.1.4. Data protection

Nothing to change.

4.3.1.5. Encryption

Nothing to change.

4.3.1.6. Tags

Nothing to change.

4.3.1.7. Review + Create

Create the storage account.

4.3.2. Create File Shares

The container app requires three file shares: rabbitvol, nginxconf, and redisvol.

File share creation

Add the default.conf file defined in 4.3.6. Configure Nginx to the nginxconf file share. No certificate is needed in this share: the SSL certificate is installed on the Application Gateway listener (4.1.4.1).

4.3.3. Create a Storage Environment

The procedure below must be executed for each previously created file share name:

  • redisvol
  • rabbitvol
  • nginxconf
4.3.3.1. UNIX
az containerapp env storage set \
  --access-mode ReadWrite \
  --azure-file-account-name <account storage name> \
  --azure-file-account-key <account storage key> \
  --azure-file-share-name <fileshare name (redisvol, rabbitvol, nginxconf)> \
  --storage-name <storage environment name (redisvol, rabbitvol, nginxconf)> \
  --name <container apps environments name (created on 4.2.3)> \
  --resource-group <resource group> \
  --output table
4.3.3.2. Windows
az containerapp env storage set `
  --access-mode ReadWrite `
  --azure-file-account-name <account storage name> `
  --azure-file-account-key <account storage key> `
  --azure-file-share-name <fileshare name (redisvol, rabbitvol, nginxconf)> `
  --storage-name <storage environment name (redisvol, rabbitvol, nginxconf)> `
  --name <container apps environments name (created on 4.2.3)> `
  --resource-group <resource group> `
  --output table

Note: --name is the name of the Container Apps environment, not of the container app: the storage is attached to the environment.

4.3.4. Container App YAML Configuration File

On your local machine, create a YAML file for the Azure Container App. The ACA format is different from the ACI format: volumes use storageType: AzureFile referencing the storage environment names created in 4.3.3, and registry credentials are declared in configuration.registries. All containers share the same network namespace within a single container app, so inter-container communication uses localhost.

You need to complete the following keys:

  • environmentId: the full resource ID of the ACAE created in 4.2.3. Format: /subscriptions/{subscriptionsId}/resourceGroups/{resourceGroupsName}/providers/Microsoft.App/managedEnvironments/{container apps environments name}
  • location: <Location>
  • configuration.registries: server and username provided by DQE
  • configuration.secrets: the registry password provided by DQE, a strong RabbitMQ password (also inserted in amqp-url, so avoid the characters @ : / ? #) and a long random session secret
  • volumes.storageName: must match the storage environment names created in 4.3.3: redisvol, rabbitvol, and nginxconf.

Key points:

  • Passwords are stored as ACA secrets (secretRef) and never appear in clear text in the container configuration. Do not share or commit the YAML file once completed.
  • ACA starts all containers at the same time. one-server waits for Redis and RabbitMQ, and queue-worker waits for RabbitMQ and one-server, before starting.
  • RabbitMQ volume: with the official rabbitmq image, only the mnesia data folder is mounted on the file share, because the .erlang.cookie file cannot be stored on an Azure File Share. RABBITMQ_NODENAME keeps the same node name across restarts so that queued processes are recovered. If you use the Bitnami image, mount /bitnami/rabbitmq/mnesia and use the variables RABBITMQ_USERNAME, RABBITMQ_PASSWORD and RABBITMQ_VHOST instead.
  • allowInsecure: true lets the ACA accept the HTTP traffic sent by the Application Gateway over the private VNET. Without it, ACA redirects HTTP requests to HTTPS.
location: <Location>
name: dqe-one
type: Microsoft.App/containerApps
properties:
  # Dedicated D8 workload profile created on 4.2.3.3
  workloadProfileName: "Dedicated-D8"
  environmentId: /subscriptions/{subscriptionsId}/resourceGroups/{resourceGroupsName}/providers/Microsoft.App/managedEnvironments/{container apps environments name (created on 4.2.3)}
  configuration:
    activeRevisionsMode: Single
    ingress:
      external: false       # Traffic comes from the Application Gateway via the private VNET
      targetPort: 80        # Nginx
      transport: http
      allowInsecure: true   # Accept HTTP from the Application Gateway (otherwise ACA redirects to HTTPS)
    registries:
      - server: <registry URL>
        username: <Username provided by DQE>
        passwordSecretRef: registry-password
    secrets:
      - name: registry-password
        value: <Password provided by DQE>
      - name: rabbitmq-password
        value: <RabbitMQ password>
      - name: amqp-url
        value: amqp://dqeone:<RabbitMQ password>@localhost:5672/admin
      - name: session-secret
        value: <Long random string>
  template:
    containers:

      # Redis — 0.5 vCPU / 1 Gi
      - name: redis
        image: <ImageURL>
        resources:
          cpu: 0.5
          memory: 1Gi
        volumeMounts:
          - volumeName: redisvol
            mountPath: /data

      # RabbitMQ (official image) — 1 vCPU / 2 Gi
      # Bitnami image: use RABBITMQ_USERNAME / RABBITMQ_PASSWORD / RABBITMQ_VHOST
      # and mountPath /bitnami/rabbitmq/mnesia instead
      - name: rabbitmq
        image: <ImageURL>
        resources:
          cpu: 1
          memory: 2Gi
        env:
          - name: RABBITMQ_DEFAULT_USER
            value: dqeone
          - name: RABBITMQ_DEFAULT_PASS
            secretRef: rabbitmq-password
          - name: RABBITMQ_DEFAULT_VHOST
            value: admin
          - name: RABBITMQ_NODENAME        # stable node name: queues are found again after a restart
            value: rabbit@localhost
        volumeMounts:
          - volumeName: rabbitvol
            mountPath: /var/lib/rabbitmq/mnesia   # data only: .erlang.cookie cannot live on an Azure File Share

      # Nginx — 0.25 vCPU / 0.5 Gi
      - name: nginx
        image: <ImageURL>
        resources:
          cpu: 0.25
          memory: 0.5Gi
        volumeMounts:
          - volumeName: nginxconf
            mountPath: /etc/nginx/conf.d

      # DQE One UI — 0.25 vCPU / 0.5 Gi
      - name: one-ui
        image: <ImageURL>
        command: ["npm", "start"]
        resources:
          cpu: 0.25
          memory: 0.5Gi
        env:
          - name: SESSION_SECRET
            secretRef: session-secret
          - name: PORT
            value: "8001"

      # DQE One web server — 0.5 vCPU / 1 Gi
      # Waits for Redis and RabbitMQ: ACA starts all containers at the same time
      - name: one-server
        image: <ImageURL>
        command:
          - bash
          - -c
          - "until (echo > /dev/tcp/127.0.0.1/6379) 2>/dev/null && (echo > /dev/tcp/127.0.0.1/5672) 2>/dev/null; do echo waiting-for-redis-rabbitmq; sleep 3; done; exec bash ./entrypoint.sh"
        resources:
          cpu: 0.5
          memory: 1Gi
        env:
          - name: SFAPIVERSION
            value: v65.0
          - name: REDIS_URL
            value: redis://localhost:6379
          - name: CLOUDAMQP_URL
            secretRef: amqp-url
          - name: CUSTOMUI
            value: http://localhost:8001
          - name: UNIFYSERVERURL
            value: http://localhost:8000
          - name: PORT
            value: "8000"

      # Queue Worker — 2.5 vCPU / 5 Gi
      - name: queue-worker
        image: <ImageURL>
        command:
          - bash
          - -c
          - "until (echo > /dev/tcp/127.0.0.1/5672) 2>/dev/null && (echo > /dev/tcp/127.0.0.1/8000) 2>/dev/null; do echo waiting-for-rabbitmq-server; sleep 3; done; exec python -u ./unify/queue_worker.pyc"
        resources:
          cpu: 2.5
          memory: 5Gi
        env:
          - name: SFAPIVERSION
            value: v65.0
          - name: WORKDIRPATH
            value: /app/unify
          - name: REDIS_URL
            value: redis://localhost:6379
          - name: CLOUDAMQP_URL
            secretRef: amqp-url

    # Azure File Share volumes — storageName must match the names created on 4.3.3
    volumes:
      - name: redisvol
        storageType: AzureFile
        storageName: redisvol
      - name: rabbitvol
        storageType: AzureFile
        storageName: rabbitvol
      - name: nginxconf
        storageType: AzureFile
        storageName: nginxconf

    scale:
      minReplicas: 1   # Always keep 1 replica running
      maxReplicas: 1   # Single replica — Redis and RabbitMQ are stateful

4.3.5. Deploy the Container App

On your local machine, open a command prompt and log in to Azure with Azure CLI:

az login

Create the Azure Container App from the previously created YAML file:

az containerapp create \
  --resource-group <your Resource Group Name> \
  --name dqe-one \
  --environment <container apps environments name (created on 4.2.3)> \
  --yaml <yaml file path>

After the operation is finished, you should get the following information:

Container app deployment result

The application is fully operational after about 1–2 minutes, once RabbitMQ has started. To follow the startup of a container:

az containerapp logs show --name dqe-one --resource-group <resource group> --container one-server --follow

4.3.6. Configure Nginx

Create a default.conf file with the following content. This configuration handles the HTTP connection between the Application Gateway and the ACA through the private VNET. The user interface is served at the root path, and the DQE One Server API under /unify/.

server {
    listen 80;

    # DQE One UI (CustomUI)
    location / {
        proxy_pass         http://127.0.0.1:8001;
        proxy_set_header   Host              $host;
        proxy_set_header   X-Real-IP         $remote_addr;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto https;
    }

    # DQE One Server API — the trailing "/" removes the /unify/ prefix
    location /unify/ {
        proxy_pass         http://127.0.0.1:8000/;
        proxy_set_header   Host              $host;
        proxy_set_header   X-Real-IP         $remote_addr;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto https;
        proxy_read_timeout 300s;
    }
}

Upload this file in the nginxconf file share previously created.

Warning: Restart the ACA so that Nginx loads the latest change:

az containerapp revision restart \
  --name dqe-one \
  --resource-group <resource group> \
  --revision $(az containerapp revision list --name dqe-one --resource-group <resource group> --query "[0].name" -o tsv)

4.3.7. Create a Private DNS Zone

The environment is internal: the ACA host name is not resolvable outside of Azure private DNS. Without a private DNS zone linked to the VNET, the Application Gateway cannot resolve the ACA host name and the backend stays unhealthy.

UNIX
ENV_DOMAIN=$(az containerapp env show --name <container apps environments name> --resource-group <resource group> --query properties.defaultDomain -o tsv)
ENV_IP=$(az containerapp env show --name <container apps environments name> --resource-group <resource group> --query properties.staticIp -o tsv)
VNET_ID=$(az network vnet show --resource-group <vnet resource group> --name <vnet name> --query id -o tsv)

az network private-dns zone create --resource-group <resource group> --name $ENV_DOMAIN
az network private-dns link vnet create --resource-group <resource group> --zone-name $ENV_DOMAIN \
  --name aca-vnet-link --virtual-network $VNET_ID --registration-enabled false
az network private-dns record-set a add-record --resource-group <resource group> --zone-name $ENV_DOMAIN \
  --record-set-name "*" --ipv4-address $ENV_IP
Windows
$ENV_DOMAIN = az containerapp env show --name <container apps environments name> --resource-group <resource group> --query properties.defaultDomain -o tsv
$ENV_IP = az containerapp env show --name <container apps environments name> --resource-group <resource group> --query properties.staticIp -o tsv
$VNET_ID = az network vnet show --resource-group <vnet resource group> --name <vnet name> --query id -o tsv

az network private-dns zone create --resource-group <resource group> --name $ENV_DOMAIN
az network private-dns link vnet create --resource-group <resource group> --zone-name $ENV_DOMAIN `
  --name aca-vnet-link --virtual-network $VNET_ID --registration-enabled false
az network private-dns record-set a add-record --resource-group <resource group> --zone-name $ENV_DOMAIN `
  --record-set-name "*" --ipv4-address $ENV_IP

4.4. Setting Up the Application Gateway Backend Pool

After the ACA is created, retrieve its host name (FQDN). ACA with internal ingress exposes a host name, not a direct IP:

az containerapp show \
  --name dqe-one \
  --resource-group <resource group> \
  --query "properties.configuration.ingress.fqdn" -o tsv

Go to Application Gateway > Backend Pool > {Your Backend Pool} > Edit the backend pool. Add the ACA FQDN as a backend target and select FQDN type, not IP address.

Application Gateway backend pool setup

4.5. Setting Up the Health Probe of the Application Gateway

The health probe is used by the Application Gateway to frequently check whether the application on the ACA is up. Set the health probe, then click Test. If the status is Healthy, the ACA is properly configured and started.

  • Protocol: HTTP
  • Pick host name from backend settings: Yes
  • Path: /
  • Backend settings: the backend setting created in 4.1.4.3 (port 80)

Warning: the root path / returns a 200 response through Nginx when the stack is up. Do not use HTTPS for the probe: the backend setting uses HTTP inside the private VNET.

Add a health probe from the Application Gateway:

Application Gateway health probe setup

Click Test > Add.

4.5.1. Set Up the DNS

You must contact your administrators, or anyone with access to the DNS zone, and ask them to add a DNS name pointing to your Application Gateway public IP. This DNS name must match the SSL certificate installed on the HTTPS listener.

4.5.2. WAF Configuration

The WAF is used to authorize or block specific IP addresses trying to call the Application Gateway. For Salesforce processes to work, the WAF must authorize Salesforce IP ranges.

WAF configuration

In the WAF, click Switch to prevention mode. This allows the WAF to use the custom rules below.

WAF prevention mode

In the custom rules, add:

  • Salesforce IP address ranges, including basic and Hyperforce ranges
  • DQE check license server: ask support
  • SF Automation 1: ask support
  • SF Automation 2: ask support

WAF custom rules

You can find the exhaustive list of Salesforce IP addresses here.

After completing these steps, check your deployment as described in 4.5. Setting Up the Health Probe of the Application Gateway.

Related to

Was this article helpful?

0 out of 0 found this helpful