Data Lake Integration (Azure) - Manual Deploy
Web Central REST API
Overview
The instructions in this article are for System Integrators who want to deploy the Archibus - Azure Data Lake integration without Azure DevOps. When you have completed these instructions, you will have a running Azure Function that automatically syncs Archibus space data into an ADLS Gen2 account using a medallion (bronze/silver) architecture — deployed entirely through the Azure Portal and your local machine. After deployed, operational management is the same regardless of how you deployed. These instructions include the full Operator Configuration Reference and Scenario Verification sections.
Contents
- Prerequisites
- Step 1. Deploy Infrastructure (Bicep via Azure Portal)
- Step 2. Create the Function App
- Step 3. Assign RBAC Roles
- Step 4. Configure Application Settings
- Step 5. Deploy the Function Code
- Step 6. Populate SyncTableRegistry
- Step 7. Verify the Deployment
- Update the Function (Re-deployment)
- Operator Configuration Reference
- Scenario Verification
Prerequisites
Ensure the following are in place before starting.
Azure
- An Azure subscription and an existing resource group where all resources will be created.
- User Assigned Managed Identity (UAMI) with Contributor and User Access Administrator roles on the resource group.
Why two roles?
Contributor lets you create resources. User Access Administrator lets you assign Storage Blob Data Contributor and Storage Table Data Contributor roles to the UAMI on the new storage accounts; without it Step 3 will fail with a 403.
Tools
Pick one for Step 5 (code deployment)
- VS Code + Azure Functions extension (recommended) — install from the VS Code extensions marketplace
- Azure CLI — install from https://learn.microsoft.com/en-us/cli/azure/install-azure-cli
- Azure Portal Deployment Center — no local tools needed, but requires a Git repository or zip file
Archibus OIDC Credentials
The function authenticates to the Archibus REST API using the OAuth2 client credentials flow via your OIDC identity provider.
Contact your Archibus administrator to obtain the following:
- ARCHIBUS_CLIENT_ID - the OAuth2 client ID registered in your OIDC identity provider
- ARCHIBUS_CLIENT_SECRET - the corresponding client secret
- ARCHIBUS_TOKEN_URL - your OIDC token endpoint e.g.
https://<your-idp-domain>/oauth2/token - ARCHIBUS_BASE_URL - the base URL of your Archibus instance e.g.
https://<your-instance>/archibus
These credentials will be entered as application settings in Step 4 and they are never stored in source control.
Network Connectivity
The Azure Function requires outbound internet access to both:
- ARCHIBUS_BASE_URL - the Archibus REST API endpoint
- ARCHIBUS_TOKEN_URL - the OIDC token endpoint
If your Archibus instance is behind a VPN, corporate proxy, or private network, you must configure VNet integration or outbound firewall rules in the Function App before the sync will work. This is an infrastructure issue outside the scope of these instructions; make sure you contact your network/cloud team.
Step 1. Deploy Infrastructure (Bicep via Azure Portal)
This step creates the storage accounts, tables, containers, Application Insights, and UAMI using the existing Bicep template.
- Navigate to Azure Portal > your Resource Group > Create.
- Search for "Custom deployment".
- Select Deploy a custom template.
- In the editor, click Build your own template.
- Click Load file and upload
infra/main.bicepfrom the repository and then Save. - Complete the Bicep parameters using the table below.
| Parameter | What it represents | Naming rules |
|---|---|---|
funcStorageAccountName |
Function runtime + Table Storage account | 3–24 chars, lowercase letters and digits only (no hyphens). Must be globally unique across all of Azure. |
|
|
ADLS Gen2 data lake account | Same rules. Must be globally unique. |
|
|
Bronze blob container name | e.g. bronze |
|
|
Silver blob container name | e.g. silver |
|
|
SyncWatermarks table name |
3–63 chars, must start with a letter. e.g. |
|
|
SyncTableRegistry table name |
Must differ from watermark table. Same naming rules. e.g. |
|
|
UAMI resource name | 3–128 chars, letters/digits/hyphens/underscores |
|
|
Application Insights resource name | — |
|
|
Azure region | Defaults to resource group location if omitted |
The SyncLock table is NOT created by Bicep. It is auto-created by the function on its first sync run, so no manual action is needed.
The SyncSchemaHistory and SyncWatermarks tables are NOT pre-created by Bicep. They are auto-created by the function, but only after the first valid entity is confirmed, as a row in SyncTableRegistry whose table name actually exists in the Archibus API. No manual action is needed.
- Click Review + Create.
- Azure validates the template and configuration:
- Checks for syntax errors in the Bicep file.
- Verifies parameter values and resource naming rules.
- Review the deployment summary:
- Confirm subscription and resource group.
- Verify all resource names and configurations.
- After validation passes, click Create to start the deployment.
- Azure provisions the resources (Storage Accounts, Tables, Containers, Application Insights, UAMI).
- Monitor deployment progress from the Deployments section until status shows Success.
Step 2. Create the Function App
- Navigate to Azure Portal.
- Create a resource.
- Search "Function App" and click the Create.
- Complete following settings.
| Field | Value |
|---|---|
|
Hosting plan |
Flex Consumption |
|
Runtime stack |
Python |
|
Version |
3.14 |
|
Operating System |
Linux |
|
Storage account |
Select the funcStorageAccountName storage account created in Step 1. |
|
Application Insights |
Select the Application Insights instance created in Step 1. |
- Complete the remaining wizard fields (subscription, resource group, name, region).
- Click Review + create and then click the Create button.
Attach the UAMI after creation:
- Go to the new Function App > Identity (left menu) > User assigned tab.
- Click + Add.
- Select the UAMI created/used in Step 1 and then Add.
The SyncLock table is NOT created by Bicep. It is auto-created by the function on its first sync run and no manual action is needed.
The SyncSchemaHistory and SyncWatermarks tables are NOT pre-created by Bicep. They are auto-created by the function, but only after the first valid entity is confirmed and a row in SyncTableRegistry whose table name actually exists in the Archibus API. No manual action is needed.
Step 3. Assign RBAC Roles
The Function App's UAMI needs two role assignments, one on each storage account.
For each row in the table below:
- Navigate to the storage account in Azure Portal.
- Click Access Control (IAM).
- Click + Add and then Add role assignment.
- Select the role.
- Click Next.
- Assign access to Managed Identity.
- Select the UAMI.
- Review + assign.
| Storage account | Role to assign | Assignee |
|---|---|---|
|
(ADLS Gen2, HNS enabled) |
Storage Blob Data Contributor | UAMI |
|
(function runtime storage) |
Storage Table Data Contributor | UAMI |
Step 4. Configure Application Settings
- Navigate to the Function App > Configuration (left menu) > Application settings tab.
- Click + New application setting for each entry in the table below.
- Click Save after all settings are added and the Function App restarts automatically.
| Setting Name | Value |
|---|---|
|
|
Client ID of the UAMI and you will find it in the Portal: UAMI → Overview → Client ID |
|
|
e.g. https://<your-instance>/archibus |
|
|
OAuth2 client ID from your OIDC identity provider. |
|
|
OAuth2 client secret — treat as a password. |
|
|
OIDC token endpoint URL |
|
|
Name of the function runtime storage account from Step 1. |
|
|
Name of the ADLS Gen2 storage account from Step 1. |
|
|
Bronze container name e.g. |
|
|
Silver container name e.g. |
|
|
Watermark table name e.g. |
|
|
Registry table name e.g. |
|
|
6-field Azure Functions cron expression e.g. Minimum interval: 10 minutes. |
|
|
500 (max 10,000 — larger values reduce API round-trips but increase memory pressure) |
MAX_WORKERS (optional) |
This controls how many Archibus tables are synced in parallel. It defaults to 4 if not set. To use a non-default value, add it here as an additional application setting. |
Performance Warning
Increasing MAX_WORKERS raises the number of simultaneous connections to the Archibus REST API. Setting it too high can overwhelm the Archibus API server or corporate proxy, causing HTTP 429 (rate limit) or timeout errors. Start with the default of 4 and increase cautiously, monitoring Archibus API response times before and after each change.
Step 5. Deploy the Function Code
Choose one of the three options below.
Option A - VS Code (recommended)
- In VS Code, open the repository folder.
- Install the Azure Functions extension (search in the Extensions panel)
- Sign in to Azure via the Azure extension (left sidebar)
- In the Azure panel, right-click the function project folder and select Deploy to Function App.
- Select the Function App created in Step 2.
- Confirm the deployment.
Option B - Azure CLI
# Install dependencies into the package folder pip install -r requirements.txt --target .python_packages/lib/site-packages # Create the deployment zip zip -r function.zip . -x "*.git*" -x ".venv/*" -x "__pycache__/*" # Deploy az functionapp deployment source config-zip \ --resource-group <RESOURCE_GROUP_NAME> \ --name <FUNCTION_APP_NAME> \ --src function.zip
Option C - Azure Portal (Deployment Center)
- Navigate to Function App > Deployment Center (left menu)
- Select your source e.g. External Git or ZIP Deploy.
- For ZIP Deploy: upload the packaged zip file directly in the portal.
After the deployment, the function will attempt to trigger immediately. Because SyncTableRegistry is empty at this point, the invocation will process 0 entities and exits early. Neither SyncWatermarks rows nor the SyncSchemaHistory table will be created yet, and this is expected behavior. Both are created automatically after a valid entity is confirmed (Step 6 below).
SyncWatermarks and SyncSchemaHistory tables will also not exist yet at this point, and they are only created when a valid entry is added to SyncTableRegistry and confirmed to exist in the Archibus API.
Step 6. Populate SyncTableRegistry
The SyncTableRegistry table was created empty in Step 1 and the function will not sync any data until rows are added.
- Navigate to Azure Portal > Storage account {FUNC_STORAGE_ACCOUNT} > Storage browser > Tables > SyncTableRegistry.
- Click Add entity.
- Set the following fields for each Archibus table you want to sync:
- PartitionKey =
archibus(all lowercase, exact and see warning below) - RowKey = the Archibus table name e.g.
rm,bl,site
- PartitionKey =
- No additional columns are required.
- Click Insert.
- Repeat for each table. The function will pick up new entries on the next scheduled run (or immediately if triggered manually).
PartitionKey is case-sensitive
It must be exactly archibus (all lowercase). Rows with any other capitalization e.g. Archibus, ARCHIBUS are silently ignored and the function will log a WARNING and skip them.
Notes
- To stop syncing a table, delete its row from
SyncTableRegistry. Existing data in the bronze and silver containers is preserved and nothing is deleted from the data lake. - Deferred table creation - The
SyncWatermarksand theSyncSchemaHistorytables are only created after the first valid entity is confirmed; this means at least one row inSyncTableRegistrymaps to a table that actually exists in the Archibus API. IfSyncTableRegistryis empty, or every registered table is absent from the Archibus API, the function exits cleanly without creating either table.
Step 7. Verify the Deployment
Run through this checklist after Step 6 is complete and SyncTableRegistry has at least one row registered:
| Check | Details |
|---|---|
|
Azure Portal > Function App > Functions > sync_archibus > Monitor |
Confirm at least one successful invocation after you added rows to SyncTableRegistry |
|
Application Insights > Logs |
Run the query below to check for exceptions |
|
SyncWatermarks table |
One row per registered entity; status = success or no_changes |
|
SyncLock table |
A row with RowKey = sync_lock exists and locked = false after the run completes |
|
ADLS Gen2 bronze container |
Partition folder(s) exist under:
|
SyncWatermarks table itself will not exist until after the first successful sync cycle with a valid registry entry. If the table is absent, verify SyncTableRegistry has entries and the Archibus API is reachable.
Application Insights — exception check (KQL):
exceptions | where timestamp > ago(1h) | project timestamp, type, outerMessage, severityLevel | order by timestamp desc
Find sync/delete failures
traces | where severityLevel == 3 | where message startswith "Sync failed for" or message startswith "Deletion check failed for" | order by timestamp desc
Update the Function (Re-deployment)
To deploy a new version of the function code, re-run whichever deployment method you used in Step 5.
- If only an application setting changed, e.g.
SYNC_SCHEDULE,PAGE_SIZE: update the value directly in Azure Portal → Function App → Configuration → Application settings → Save. The change takes effect immediately — no redeploy needed. - If only Bicep/infrastructure changed with no code changes: re-run Step 1 and the Bicep deployment is idempotent and will apply the infrastructure change safely.
Operator Configuration Reference
Post-deployment operational changes can be made without redeploying:
| What to change | Where | How |
|---|---|---|
|
Add a table to sync |
SyncTableRegistry table |
Add row:
|
|
Remove a table from sync |
SyncTableRegistry table |
Delete the row. Existing lake data is preserved. |
|
Change sync schedule |
Function App > Configuration > SYNC_SCHEDULE |
Update value (6-field cron) > Save |
|
Change page size |
Function App > Configuration > PAGE_SIZE |
Update value > Save |
|
Change the parallel workers |
Function App > Configuration > MAX_WORKERS |
Update value > Save. See MAX_WORKERS Performance Warning above. |
|
Force full re-sync for an entity |
SyncWatermarks table |
Clear or delete the updatedAfter field on the entity's row |
|
Adjust stale-lock timeout |
SyncLock table |
Edit timeoutMinutes on the sync_lock row (default: 180) |
|
Acknowledge a schema error |
SyncWatermarks table |
Set e.g. |
incompatible_schema_change - What it means and how to clear it
This status is set when the function detects that Archibus has removed a column or changed a column's type.
When triggered, the function automatically performs a full sync and rebuilds the silver file to reflect the new schema. The status is a deliberate signal that a breaking change has occurred and it never auto-clears.
How to investigate
- Open
{FUNC_STORAGE_ACCOUNT}→ Tables →SyncWatermarks→ find the affected entity row. - Read
schemaRemovedColumnsto see which column was removed (this field is intentionally preserved across subsequent syncs so you can see the original cause at a glance). - For full drift history, open
SyncSchemaHistorytable, then filter byPartitionKey = <entity-name>and then look for rows withseverity = error
How to acknowledge
- Verify that downstream consumers (reports, Synapse, Databricks, etc.) have been updated to handle the schema change.
- Navigate to
{FUNC_STORAGE_ACCOUNT}→ Tables →SyncWatermarks - Open the affected entity row and then set
schemaStatusto any value other than incompatible_schema_change e.g.acknowledged - The next clean sync will automatically reset
schemaStatustook.
Scenario Verification
Use this section to verify the system is behaving correctly for each data-change scenario.
Storage accounts referenced below:
{FUNC_STORAGE_ACCOUNT}- function runtime storage (Azure Table Storage: SyncWatermarks, SyncTableRegistry, SyncLock, SyncSchemaHistory){ADLS_STORAGE_ACCOUNT}- ADLS Gen2 data lake (bronze and silver Parquet files)
Scenario 1. First Full Sync
Applies to: first deployment, or after clearing updatedAfter on a watermark row to force a re-sync.
| What to check | Where | Expected |
|---|---|---|
|
Invocation completed |
Function App → Monitor | Status: Success; duration reflects the number of registered entities. |
|
Run-lock released |
{FUNC_STORAGE_ACCOUNT} → Tables → SyncLock |
Row RowKey = sync_lock exists; locked = false after sync completes. |
|
Watermark created per entity |
{FUNC_STORAGE_ACCOUNT} → Tables → SyncWatermarks |
One row per registered entity; status = success; updatedAfter is a recent timestamp; lastDeleteStatus = success or no_deletions. |
|
Bronze data written |
|
One or more .parquet files present containing all records fetched from Archibus. |
|
Silver data written |
|
File exists; last-modified timestamp matches the sync run time. |
SyncWatermarks table itself is created on this first sync (not just the row). SyncSchemaHistory is also created and seeded with the initial schema baseline on the first sync for each entity.
Scenario 2. Record Updated or Added in Archibus
Applies to: next scheduled sync after a record is created or modified in Archibus.
| What to check | Where | Expected |
|---|---|---|
|
Watermark advanced |
{FUNC_STORAGE_ACCOUNT} → Tables → SyncWatermarks → entity row |
updatedAfter is newer than the previous sync; lastRecordCount > 0; status = success |
|
New bronze file written |
|
A new .parquet file appears from the previous sync; it contains only the changed or added record(s) |
|
Silver updated |
|
File last-modified timestamp is newer than the previous sync |
If no records have changed since the last sync: status = no_changes and no new bronze file is written, then this is correct and expected behavior.
Scenario 3. Record Deleted in Archibus
Applies to: deletion detection runs inline after each entity's sync on every cycle.
| What to check | Where | Expected |
|---|---|---|
|
Deletion detected |
|
lastDeleteStatus = success; lastDeleteCount > 0; lastDeleteCheckTimestamp updated |
|
Deleted record archived to bronze |
|
File exists and contains the deleted record (identified by its UUID); row count increased by the number of deletions |
|
Record removed from silver |
|
Deleted record's UUID is no longer present; total row count decreased accordingly |
If no deletions occurred then the following will be true. The deletion archive file is unchanged.
lastDeleteStatus = no_deletionslastDeleteCount = 0
Scenario 4. Column Removed or Type Changed in Archibus
When the Archibus schema API returns a different column set or type compared to the stored watermark. The function detects this before fetching records, performs a full sync, and rebuilds silver.
| What to check | Where | Expected |
|---|---|---|
| Schema error flagged | {FUNC_STORAGE_ACCOUNT} → Tables → SyncWatermarks → entity row |
|
| Drift event recorded | {FUNC_STORAGE_ACCOUNT} → Tables → SyncSchemaHistory → filter PartitionKey = <entity> |
A row with |
| Silver rebuilt | {ADLS_STORAGE_ACCOUNT} → silver → <table>/<table>_current.parquet |
File last-modified updated; the removed column is no longer present in the Parquet schema. |
| Warning in App Insights |
App Insights → Logs → KQL:
|
Warning-level trace for the affected entity naming the removed column or type change. |
Action required: incompatible_schema_change never auto-clears. See the acknowledgment steps in the Operator Configuration Reference above.
Scenario 5. Deleted Infrastructure Recovery
Self-healing behavior after manual deletion:
- Deleted bronze/silver ADLS containers → auto-recreated by the function on next sync
- Deleted
SyncWatermarks/SyncSchemaHistory→ auto-recreated on next valid sync cycle - Deleted
SyncTableRegistry→ only recreated by re-running Step 1 (Bicep deployment); entries must be re-added manually - Deleted
SyncLock→ auto-recreated on next sync invocation
