Azure

Availability

Overview

This guide walks you through deploying Orbeon Forms on Microsoft Azure using:

We will use a self-signed certificate and a single-node Kubernetes cluster for demonstration purposes. In production, you would likely use a certificate signed by a trusted certificate authority (CA) and a multi-node cluster.

The users/groups will be accessed via the OpenID Connect (OIDC) protocol and WildFly's native OIDC support implementation. Entra ID groups will be mapped to WildFly roles, which will be used to control access to Orbeon Forms.

We will create two groups/roles:

  • orbeon-user

  • orbeon-admin

As well as two test users:

  • testuser1 (member of orbeon-user)

  • testuser2 (member of orbeon-user and orbeon-admin)

Orbeon Forms will be accessible only to users from the orbeon-user group (i.e. to both testuser1 and testuser2). Form Builder and Forms Admin pages will be accessible only to users from the orbeon-admin group (i.e. to testuser2 only).

A more complete example Bash script is available on GitHub. It includes more commands, which will check if the resources already exist, among other things, but it follows roughly the same steps as described here.

Some values in the commands used in this guide will be hardcoded for simplicity. In a real-world scenario, you would likely want to parameterize them, for example by using environment variables.

Requirements

The main requirement is an account with an Azure subscription.

The following utilities will be used during the installation process:

  • az (Azure CLI)

  • psql (PostgreSQL client)

  • kubectl (Kubernetes deployment)

  • jq (JSON manipulation)

  • base64 (Kubernetes passwords encoding)

  • core Linux utilities such as cat, curl, echo, etc.

All steps described below can also be done manually via the Azure UI.

Login and general configuration

The very first step is to login to Azure and set the Microsoft Graph API scope:

The following providers need to be registered:

Self-signed certificate

Generate a self-signed certificate for the application:

This command uses the keystore filename (application.keystore) and passwords (password) used by default in WildFly's configuration (standalone.xml file). Using a stronger password is recommended.

Entra ID

Retrieve the Entra ID domain:

It should look like contoso.onmicrosoft.com.

Users

Create two test users testuser1 and testuser2 (replace contoso.onmicrosoft.com with your domain):

The user principal name (UPN) follows the format prefix@domain.

Optionally, you can associate an email address with the user. For this, we use the Microsoft Graph API:

To retrieve the user ID:

Groups

Create two groups orbeon-user and orbeon-admin:

Add users to groups:

Application

Create an application called Orbeon Forms. This is needed to expose our users and groups to Orbeon Forms via the OIDC protocol.

Retrieve the application ID:

You can also retrieve it from the output of the previous command.

Add an API identifier URI to the application:

Retrieve the application object ID:

Note that this is not the same as the application ID (id vs appId).

To have access to the groups/roles in both the OIDC ID and access tokens, we need to add a scope to the application. If we don't do this, WildFly's OIDC implementation will be unable to retrieve the groups/roles.

Add a scope called groups.access to the application:

The scope ID is generated using uuidgen. Note that the jq command above is used to inject the scope ID into the JSON body. This can be done manually or in other ways as well.

For an existing scope, you can retrieve the scope ID from its name using the following command:

Pre-authorize the application, so that the users won't have to explicitly consent to the permissions:

Add a client secret:

Beware: the command above will update/overwrite any existing secret with the same name.

Only security group membership claims will be included as group IDs:

Add optional OIDC claims (groups included as roles, email):

Note that we have updated the application manifest multiple times, using the az rest --method PATCH command, but this was mainly done for demonstration purposes. In a real-world scenario, you would likely update the application manifest only once, with all the changes.

Grant Microsoft Graph permissions:

Grant admin consent for permissions above:

Configuration files

Get the tenant ID:

Build the OIDC provider URL:

Build the scope API URL:

Generate the OIDC configuration file:

The above configuration will return users as IDs. If you want to return users as emails, you can use email instead of oid as the value for principal-attribute.

The oidc.json file will look like this:

In OIDC, we will refer to the groups by their IDs (not their display names):

Generate the Form Builder permissions file:

The form-builder-permissions.xml file will look like this:

Extract the web.xml file from the Orbeon Forms WAR file or download web.template.xml from GitHub, and make sure the following lines are present:

Replace ENTRA_ID_USER_GROUP_ID and ENTRA_ID_ADMIN_GROUP_ID with the actual group IDs.

Download standalone.postgresql.azure.xml from GitHub and make sure the following lines are present:

Replace DATABASE_SERVER with your database server name, which must be unique across Azure.

We need jboss-web-xml to be configured for PostgreSQL:

We will also configure the Orbeon Forms properties in properties-local.xml to use PostgreSQL instead of SQLite:

Resource group

Create a resource group:

It will be used by all the resources we create (Azure Storage, PostgreSQL, etc.).

Storage

Create a storage account:

The storage account name must be unique across Azure.

Create a storage share:

To upload a file to the storage share, use the following command:

We will need to upload the following configuration files:

  • application.keystore

  • form-builder-permissions.xml

  • jboss-web.xml

  • license.xml (get a free trial license here if needed)

  • oidc.json

  • properties-local.xml

  • standalone.xml

  • web.xml

Retrieve the storage account access key:

Alternatively, all configuration files could be included in a custom Docker image, but this is less flexible when configuration needs to be changed.

Database

Create a PostgreSQL database server:

The database server name must be unique across Azure.

Retrieve your client's public IP address:

This will allow you to configure a firewall rule to allow access to the database server only from your client's IP address.

Alternatively, you can call az postgres flexible-server create without --public-access None. This will automatically create the firewall rule above.

Create the orbeon database:

Make the database administrator password available to the psql command:

Create the orbeon database user:

Note that Azure PostgreSQL users need to follow the format username@servername.

Grant privileges to the orbeon database user:

Create the Orbeon Forms database schema:

The SQL files needed to create the Orbeon Forms database schema can be downloaded from PostgreSQL database setup.

You can then delete the local-ip-allowed firewall rule:

Alternatively, you can use other databases, such as Azure SQL Database or Azure Database for MySQL.

Container registry

If you need to use a custom Docker image, create an Azure Container Registry:

The container registry name must be unique across Azure.

Then login to the Azure Container Registry:

Retrieve the Azure Container Registry ID:

Create a Dockerfile to customize the Orbeon Forms Docker image:

Build the Docker image:

Tag the Docker image with the full Azure Container Registry URL

Push the Docker image to the Azure Container Registry:

Kubernetes

Create an Azure Kubernetes Service (AKS) cluster:

The AKS cluster name must be unique across Azure.

Retrieve AKS credentials, save them locally to ~/.kube/config, and set the AKS cluster as the current context:

If you use a custom Docker image, you need to grant permission to the cluster to pull images from the Azure Container Registry.

Generate the storage account name/key secret file

The Azure Storage account name and key need to be encoded in Base64.

Import the storage account name/key secret

Generate the persistence volume configuration file:

Import the persistence volume configuration:

Generate the persistence volume claim configuration file:

Import the persistence volume claim configuration:

Depending on whether you use the default Orbeon Forms image or a custom one, you will need to set the image name as follows:

Generate the deployment configuration file:

Note that the standalone.xml file is mounted in the container as docker-entrypoint-wildfly.d/standalone.xml. This is because WildFly needs to move/rename that file, so it needs to be copied to instead of mounted directly in the /opt/jboss/wildfly/standalone/configuration configuration directory. See this issue on GitHub for more information. The WildFly version of Orbeon Forms will copy any standalone.xml found in docker-entrypoint-wildfly.d to the WildFly configuration directory.

Import the deployment configuration:

You can display information about you Kubernetes contexts, cluster, pods, nodes, and service with the following commands:

Retrieve the cluster's external/public IP:

Orbeon Forms will be available from the following URL:

Update the Entra ID redirect URIs with the actual Orbeon Forms URL:

Retrieve the Kubernetes pod name, which can be used to retrieve the logs (see below):

Alternatively, you can use Azure Container Instances (ACI) for a simpler deployment, but with limited support for file mounts, port mappings, etc.

Private network

The last step is to configure a private network to allow the Kubernetes cluster to access the PostgreSQL database server.

Create a private DNS zone:

The network resource group we will use below is the Kubernetes node resource group. It follows the following format:

Retrieve the virtual network name:

Retrieve the virtual network ID:

Link the private DNS zone to the Kubernetes cluster virtual network:

Retrieve the subnet name:

Retrieve the subnet ID:

Retrieve the database server ID:

Create a private endpoint:

Retrieve the private IP:

Create a private DNS record:

The database server will now be reachable from the Kubernetes cluster as $DATABASE_SERVER.private.postgres.database.azure.com.

Accessing the application and its logs

Orbeon Forms is now available from the following URL:

You can display and follow the Orbeon Forms logs using the following command:

Limitations

Here is a list of limitations and possible improvements:

  • Groups/roles are returned by Entra ID as IDs, not names.

  • The example Bash script has been tested on macOS only, but should work on Linux and Windows, using the Windows Subsystem for Linux (WSL).

  • No actual load balancing is configured in the Kubernetes cluster.

  • The TLS/SSL configuration should be done at the Application Gateway Ingress Controller level and not at the WildFly level.

  • Azure Resource Manager (ARM) templates could be used to automate the deployment.

Last updated