Installation Script mode
This page describes the Installation Script deployment mode for Omada Identity Cloud Private – the recommended and fully supported path for first-time and standard deployments. For an overview of deployment modes and shared prerequisites, see the Deployment guide.
Overview
A single installation script (install.sh) orchestrates the entire deployment end-to-end:
- Preflight validation: OS, disk space, network connectivity checks.
- Toolchain bootstrap: Installs Terraform, Helm, kubectl, kubelogin, skopeo, jq, ODBC Driver 18, Python, and Ansible automatically, and Azure CLI if it is missing. You still need Azure CLI before you run the installer, to log in (see Step 3).
- Infrastructure provisioning: Terraform backend, shared global infrastructure, and workspace-specific resources.
- Artifact publishing: Container images to ACR and release artifacts to blob storage.
- Configuration validation: Key Vault certificate and secret checks before compute provisioning.
- Node setup: Ansible playbooks for AKS configuration and application deployment.
- Health check: Automated post-deployment readiness check.
Before starting, make sure the Installation Script prerequisites and Networking prerequisites are in place.
Step 1: Extract the installation package
Omada provides the Omada Identity Cloud Private installation package via SharePoint. When you download it, it will be a .zip file named oicp_install_<version>.zip, where <version> is the release version you are installing.
If unzip isn't already installed (common on minimal VM images), install it first: sudo apt update && sudo apt install -y unzip.
Extract it and navigate to the installation directory:
Tool: Bash shell
unzip oicp_install_<version>.zip
cd scripts/install
If ./install.sh fails with Permission denied, the scripts lost their execute permission when the package was extracted. This can happen with packages for the July 2026 and August 2026 releases, or when you extract the package on Windows and copy the files to the VM. To fix it, run the following command from the directory where you extracted the package, and then run the installer again.
find scripts -type f -name '*.sh' -exec chmod +x {} +
Before installing the product, you can confirm that the downloaded release packages actually came from Omada and have not been altered in transit. See Release package signing.
Step 2: Configure deployment-config.json
The deployment-config.json file in the installation directory is the single configuration input for the entire deployment. Edit it with your environment-specific values before running the script.
You can create multiple named copies of this file (for example, for different environments) and pass the desired one via the optional --config parameter, described later.
Use an absolute path, or a file name with no directory component, placed directly in scripts/install. A relative path with subdirectories (for example, --config ./configs/dev.json) is not honored as given: only the file name is used, resolved against scripts/install, so the file must be stored there under that name.
Key parameters to configure:
| Section | Parameter | Description |
|---|---|---|
azure | subscription_id | Azure subscription GUID. |
azure | tenant_id | Azure AD (Microsoft Entra ID) tenant GUID. |
azure | location | Azure region (for example, swedencentral). |
deployment | workspace_prefix | Workspace identifier – 2 lowercase letters + 4 digits (for example, ab0001). |
deployment | project_name | Project name for resource naming (for example, oicp). Keep it short – see the naming note below for a hard length limit. |
deployment | omada_license_string | Omada license key – provided by Omada. |
deployment | update_private_dns_records | Whether to update private DNS records automatically. Defaults to true. |
backend | storage_account_name | Storage account for Terraform state. |
backend | container_name | Blob container name for state files. |
backend | key | Terraform state file name. Defaults to terraform.tfstate; the shared-infrastructure state is stored as <key>.shared. |
release | package_url | Secure URL to the Cloud Private release package – provided by Omada. This is a signed, credential-bearing URL – treat it like a password. Don't paste it into chat, tickets, or slides; it expires on its own, but anyone with it can download the package until then. Must contain a /releases/<version>/ path segment, as in the SAS URLs Omada sends – the installer extracts <version> from this segment to name the local deployment directory (Deployment_OICP_<version>) and exits immediately, before any other check runs, if the pattern isn't found. |
release | checksum_url | Optional URL to the release package SHA-256 checksum. If omitted (and no local release-package.sha256 exists), checksum verification is skipped with a warning. |
security | entra_admin_group.login / object_id | Existing Entra ID admin group. Leave both empty to have the script create the group for you (it will prompt, unless you pass --auto-create-groups). |
security | sql_admin_group.login / object_id | Existing Entra ID SQL admin group. Same create-or-provide behavior as entra_admin_group, with one difference: if you supply an object_id for an existing group, the identity running the installer must already be a member of that group or the azure phase fails. Terraform makes this group the SQL Server's Entra admin, and the caller needs membership for the Ansible SQL-login step. When the script creates the group itself, it adds the caller automatically. |
shared | dns_zone_name | Public DNS zone for Cloud Private custom domains (for example, mycompany.example.com). Required – it names the zone whether the zone is referenced or created (see create_dns_zone). With create_dns_zone set to false, the zone must already exist in Azure – the installer validates this and fails if not found. |
shared | create_dns_zone | Whether the installer creates the DNS zone. Defaults to false, meaning the zone named in dns_zone_name must already exist. Set to true to have the installer create it. |
shared | dns_zone_resource_group | Resource group of the existing DNS zone. |
shared | certificate_secret_name | Name of the Azure Key Vault certificate. If certificate_file (below) is set, the installer imports it automatically with no prompt. If it's empty and the certificate isn't already in Key Vault, the script doesn't fail immediately – it presents an interactive menu to retry, import a certificate file on the spot, or quit (see the confirmations note under Step 4). For --non-interactive runs, where that menu cannot be answered and the phase fails instead, set certificate_file. See Key Vault certificate and secrets. |
shared | certificate_file | Optional path to a certificate file (.pfx or .pem) on the machine running the installer. When set and the Key Vault certificate is missing, the installer imports it. The certificate password is read from the OMADA_CERT_PASSWORD environment variable, never from the config file. |
infrastructure | aks_system_node_size | VM SKU for the AKS system node pool (Linux; current automation default: Standard_D2s_v5). See AKS node pools for how the cluster's node pools are split. |
infrastructure | aks_app_node_size | VM SKU for the AKS application node pool (Windows; current automation default: Standard_D8s_v5). |
infrastructure | aks_linux_node_size | VM SKU for the AKS general-purpose Linux node pool (default: Standard_D4s_v5). This node pool is shared with Linux CAG workloads. |
infrastructure | allowed_ip_addresses | Public IP allowlist applied to network rules on Key Vault, Storage, the SQL Server firewall, Service Bus, Event Hub, and NSGs. Defaults to []. The machine running the installer (or the pipeline agent) must be included in this list, or it will be locked out of these resources mid-install. The installer auto-adds its own detected public IP only when the list is empty; if you pre-populate it with other IPs (for example, office or VPN egress ranges), you must add the installer's IP yourself. On Service Bus and Event Hub the allowlist is only enforced when enable_private_endpoints is true; otherwise those two allow all traffic by default. |
infrastructure | enable_private_endpoints | Whether to provision Azure Private Link endpoints for Key Vault, Storage, SQL Server, Event Hub, Service Bus, SignalR, and App Services. Defaults to false. Enabling it also moves Event Hub and all four Service Bus namespaces from the Standard to the Premium tier, which changes their cost. |
infrastructure | enable_public_access | Controls public network access for App Services and SignalR only. Defaults to true. It does not affect the Key Vault, SQL Server, Event Hub, or Service Bus – those four always remain reachable over the public network by default, protected only by the allowed_ip_addresses firewall rules. Setting this to false does not, by itself, produce a fully private deployment. |
infrastructure | logging_level | Which AKS diagnostic log categories are collected: minimal (default) collects controller, scheduler, and CSI logs; full adds admin audit, autoscaler, cloud-controller, and guard logs. Any other value fails Terraform validation. See Logging level. |
infrastructure | application_insights_sampling_percentage | Telemetry sampling rate for Application Insights, from 0–100. Defaults to 100 (all telemetry captured). |
secrets | aks-extCertBase64, aks-extKeyBase64, aks-pfxCertificate, aks-pfxPassword | Optional. Base64-encoded certificates and keys can be supplied here to pre-populate Key Vault; otherwise add them to Key Vault when the installer reaches the secrets_validate phase. See Key Vault certificate and secrets. The aks-containerRegistryDockerConfigJson secret is also required by validation but is generated automatically from the container registry credentials if not supplied. |
Example deployment-config.json
The following example shows a fully filled-in deployment-config.json for a development environment.
{
"azure": {
"subscription_id": "00000000-0000-0000-0000-000000000000", // Your Azure subscription GUID
"tenant_id": "00000000-0000-0000-0000-000000000000", // Your Microsoft Entra ID tenant GUID
"location": "swedencentral" // Azure region for all resources
},
"deployment": {
"workspace_prefix": "ab0001", // 2 lowercase letters + 4 digits, e.g. ab0001
"project_name": "oicp", // Used to derive resource names; keep short and lowercase
"omada_license_string": "<provided-by-omada>",
"update_private_dns_records": true // Set to false to manage DNS records manually
},
"backend": {
"storage_account_name": "tfstateoicp", // Storage account for Terraform state; must be globally unique
"container_name": "tfstate", // Blob container name; "tfstate" is a safe default
"key": "terraform.tfstate" // State file name; default is "terraform.tfstate"
},
"release": {
"package_url": "<secure-url-provided-by-omada>",
"checksum_url": "<checksum-url-provided-by-omada>" // Optional; omit to skip checksum verification
},
"security": {
"entra_admin_group": {
"login": "omada-admins", // Display name of the Entra ID admin group
"object_id": "", // Leave empty to have the script create the group
"tenant_id": "" // Leave empty to use the signed-in Azure CLI session's tenant
},
"sql_admin_group": {
"login": "omada-sql-admins", // Display name of the SQL admin group
"object_id": "", // Leave empty to have the script create the group
"tenant_id": "" // Leave empty to use the signed-in Azure CLI session's tenant
}
},
"shared": {
"dns_zone_name": "omada.mycompany.com", // Required; the zone to reference or create
"create_dns_zone": false, // false: zone must already exist; true: installer creates it
"dns_zone_resource_group": "my-dns-rg", // Resource group containing the DNS zone
"certificate_secret_name": "omada-tls-cert", // Name of the certificate in Key Vault
"certificate_file": "" // Optional local .pfx/.pem to import if the certificate is missing
},
"infrastructure": {
"aks_system_node_size": "Standard_D2s_v5", // AKS system node pool VM SKU
"aks_app_node_size": "Standard_D8s_v5", // AKS application node pool VM SKU
"aks_linux_node_size": "Standard_D4s_v5", // AKS general-purpose Linux node pool VM SKU (optional to set; defaults to Standard_D4s_v5 if omitted — the pool itself is always provisioned)
"enable_private_endpoints": false, // Set to true to enable Azure Private Link
"enable_public_access": true, // Controls public access for App Services/SignalR only – see table above
"allowed_ip_addresses": ["203.0.113.10"], // IPs allowed through the resource firewalls; if empty, the installer detects your public IP and writes it back into this file
"logging_level": "minimal", // "minimal" (default) or "full"
"application_insights_sampling_percentage": 100 // Telemetry sampling rate (0–100)
},
"secrets": {
"aks-extCertBase64": "", // Base64-encoded external TLS certificate (.crt)
"aks-extKeyBase64": "", // Base64-encoded external TLS private key (.key)
"aks-pfxCertificate": "", // Base64-encoded PFX certificate
"aks-pfxPassword": "" // PFX certificate password
}
}
How to fill in this file:
You do not need to populate every field before running the script. As a starting point:
-
Fill in first: these specific fields are required, and the script will not start without them –
azure.subscription_id,azure.tenant_id,azure.location,deployment.workspace_prefix,deployment.project_name,release.package_url,backend.storage_account_name, andshared.certificate_secret_name. Other fields in the same sections (for example,deployment.omada_license_string,backend.container_name/key,release.checksum_url) have defaults or are validated later, not at startup. -
Confirm prerequisites exist (
shared): the certificate doesn't strictly need to exist beforehand for an interactive run – if it's missing, the script offers to import one on the spot. Setcertificate_fileto skip that prompt and have it import automatically instead. For--non-interactiveruns,certificate_filemust be set, since there is no prompt to answer if the certificate is missing. See Key Vault certificate and secrets. Similarly, the DNS zone named indns_zone_namemust already exist in Azure, unless you setcreate_dns_zonetotrueto have the installer create it. -
Choose your approach (
security): leaveobject_idempty to let the script create your Entra ID admin groups automatically, or fill them in to use existing groups.noteIf
entra_admin_groupandsql_admin_groupare left without anobject_id, the script checks whether a group with the configured (or default) name already exists, and prompts to create it if not. When running with--non-interactive, you must either pre-populate both groups'object_idvalues or pass--auto-create-groups; otherwise the script will fail at this step rather than prompt. -
Leave as-is unless you have specific requirements (
infrastructure): the defaults work for most standard deployments. -
Optional shortcut (
secrets): supply base64-encoded certificates here to let the script push them to Key Vault automatically. If left empty, add them to Key Vault when the script reaches thesecrets_validatephase. See Key Vault certificate and secrets.
-
Comments (prefixed
//) are included for reference and must be removed from your actual file before use. -
Workspace naming: The
workspace_prefixmust follow the format 2 lowercase letters + 4 digits exactly (for example,ab0001). -
Combined length limit:
project_nameandworkspace_prefixtogether form the name of the workspace's shared storage account (<project_name><workspace_prefix>sharedsa– dashes are stripped). Azure caps storage account names at 24 characters, and configuration validation fails immediately if the combination exceeds it. Keepproject_nameshort.This limit applies to the workspace's shared storage account, not to the Terraform state account named in
backend.storage_account_name.
Key Vault certificate and secrets
The installer creates the Key Vault, <project_name>-shared-kv, in the terraform_iac_shared phase. On a first installation, you cannot upload anything to it in advance. Provide the certificate and secrets in one of these ways:
- Unattended (
--non-interactive) runs: setshared.certificate_file, with the certificate password in theOMADA_CERT_PASSWORDenvironment variable, and fill in thesecretssection ofdeployment-config.json. The installer imports both during thesecrets_validatephase. - Interactive runs: if anything is missing at
secrets_validate, the installer prints the Key Vault name and a link to it in the Azure portal, then waits. Add the missing items in the Azure portal and choose Re-validate. - An unattended run that stopped at
secrets_validate: populate the Key Vault named in the error, then rerun the installer with--resume.
You can check the required secrets with validate-secrets.sh --vault-name <project_name>-shared-kv (add --list to see the required secrets without checking their values). It flags any required secrets that are missing or still set to a placeholder. Because it only works once the Key Vault exists, use it after a stopped run or before an upgrade, not before a first installation. It does not validate the TLS certificate named in shared.certificate_secret_name – the installer checks that certificate separately during the secrets_validate phase.
Infrastructure sizing
The default VM SKUs are configured in deployment-config.json via the aks_system_node_size, aks_app_node_size, and aks_linux_node_size parameters. For sizing profiles, SKU recommendations, and cost optimization guidance, see Azure infrastructure sizing in the Omada Identity Cloud Private overview.
After initial deployment, monitor CPU and memory usage in the Azure portal to determine if scaling is needed.
Logging level
The logging_level field in the deployment-config.json file controls the verbosity of AKS cluster controller logs.
AKS cluster controller logs record the activity of Kubernetes controllers – the automated systems that manage cluster health and state. This includes pod restarts, node status checks, resource allocation decisions, and other routine cluster maintenance operations.
| Value | Description |
|---|---|
minimal | Default. Logs essential cluster events only. Recommended for most deployments. |
full | Logs all controller activity, including routine checks and updates. This mode generates high-volume logs and can rapidly fill the Log Analytics workspace. |
Logging verbosity is separate from telemetry sampling, which is controlled by application_insights_sampling_percentage in the same section.
Step 3: Azure login
If Azure CLI is not already installed (common on a fresh VM), install it first. The installer installs it only later, in the tools phase, so az login fails with command not found until then.
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
This command needs curl, which minimal VM images may also lack. If it is missing, install it first: sudo apt update && sudo apt install -y curl.
Authenticate with Azure CLI and verify you are on the correct subscription:
Tool: Bash shell
az login --tenant <your-tenant-id>
az account set --subscription <your-subscription-id>
az account show
Ensure your account has the Azure roles described in Identity – Contributor (or Owner) and User Access Administrator (or Owner) at subscription scope.
Step 4: Run the installation script
From the installation directory, run:
Tool: Bash shell
./install.sh --config ./deployment-config.json
The ./deployment-config.json is the default value for --config, so this is equivalent to running ./install.sh with no arguments. It is provided here for full reference.
By default, the script logs progress to the following, relative to the installation directory (scripts/install) you're running it from (where <project> is your project_name and <workspace> is your workspace_prefix):
../../Deployment_OICP_<version>/run-<project>-<workspace>/logs/install-<timestamp>.log
Set the INSTALL_WORK_DIR environment variable to relocate the entire deployment directory elsewhere – useful if the installation directory's disk does not have enough room. For example:
INSTALL_WORK_DIR=/mnt/bigdisk/oicp-work ./install.sh --config ./deployment-config.json
When set, the deployment directory (and its logs) live at ${INSTALL_WORK_DIR}/Deployment_OICP_<version>/... instead of the default path above – adjust the tail command below accordingly.
Currently, the preflight disk check measures free space on the disk that holds the installation package, not on INSTALL_WORK_DIR. That disk must still have 30 GB free, or 12 GB when resuming after the release package is already staged, even if INSTALL_WORK_DIR points to a larger disk.
If you set INSTALL_WORK_DIR, set it to the same value every time you run --resume. See Resuming an interrupted installation.
Progress is shown live in the terminal in both interactive and non-interactive mode. To follow it from a separate terminal session instead – for example, if the installer is running in the background or over a connection you do not want to keep occupied – execute (from scripts/install – adjust the path if INSTALL_WORK_DIR is set):
Tool: Bash shell
tail -f ../../Deployment_OICP_<version>/run-<project>-<workspace>/logs/install-*.log
Interactive confirmations during installation
The script pauses for confirmation at several points during a fresh installation, typically:
- At startup, after configuration is loaded (press Enter to continue).
- Before creating Terraform backend resources (resource group, storage account, container), if they don't already exist.
- Before publishing release artifacts to blob storage, and again before publishing container images to the registry.
- Before each
terraform apply(once for shared infrastructure, once for workspace-specific infrastructure). - If your detected OS version differs from Ubuntu 24.04.
- If some of the requested VM sizes are not available in the selected Azure region (continuing may cause the deployment to fail).
- During certificate validation (
secrets_validatephase): if a required certificate is missing or invalid, the script presents a menu to retry, import a certificate file interactively, or quit. Have your certificate files (.pfx or .pem) accessible before running the script. - On a re-run where the release package is already staged: whether to reuse it rather than downloading and extracting it again.
For the Yes/No prompts above, answer y to proceed. The certificate-validation menu is a numbered choice, not a Yes/No prompt – see its own bullet above for the options it presents.
There is one prompt where y can be destructive. If a phase fails after workspace infrastructure has been applied, the installer offers a rollback:
Rollback triggered. This will destroy all resources in workspace '<workspace_prefix>'.
Proceed with rollback (terraform destroy)? [y/N]
This prompt defaults to No, and answering y runs terraform destroy against the workspace. Rollback on failure is enabled by default; set ROLLBACK_ON_FAILURE=false in the environment to disable it for a run. Upgrade runs force it off and refuse the rollback outright.
Run the script in a regular interactive terminal session (not piped, backgrounded, or invoked from automation) so these prompts can be answered.
In --non-interactive mode, each prompt resolves to its default instead of pausing:
- Prompts that default to Yes (the startup pause, Terraform backend resource creation, the two artifact-publish confirmations, and each
terraform apply) are accepted automatically. A non-interactive run therefore creates backend resources and applies Terraform changes without pausing for review. - Prompts that default to No (the OS-mismatch confirmation and the VM-size availability warning) fail the run immediately. The rollback confirmation also defaults to No, so an unattended run never destroys the workspace.
The certificate validation menu does not block unattended runs – it automatically quits, failing the phase, if a certificate is missing.
Resuming an interrupted installation
If the installation is interrupted or a phase fails, resume from the last completed phase:
Tool: Bash shell
./install.sh --config ./deployment-config.json --resume
If you set INSTALL_WORK_DIR for the original run, set it to the same value for --resume. Otherwise, the installer does not find the existing checkpoints and starts again from the beginning. For example:
Tool: Bash shell
INSTALL_WORK_DIR=/mnt/bigdisk/oicp-work ./install.sh --config ./deployment-config.json --resume
Transient Azure errors, such as a Terraform apply failing with HTTP response was nil; connection may have been reset, are usually resolved by resuming. For details, see Known issue: Terraform apply fails with a connection reset error.
To clear all checkpoints and restart the entire installation:
Tool: Bash shell
./install.sh --config ./deployment-config.json --force
-
Each phase records its completion as a
<phase_id>.donefile containing the UTC timestamp of when it ran, stored inDeployment_OICP_<version>/run-<project>-<workspace>/(underINSTALL_WORK_DIR, if set). Deleting a phase's.donefile will cause that phase to be re-executed the next time--resumeis run. -
--forceclears all checkpoints but does not destroy Azure resources. To tear down infrastructure, use Azure portal orterraform destroy.
Automatic rollback on failure
If the installer fails after workspace-specific infrastructure has already been provisioned (the terraform_iac phase has completed) and --skip-terraform was not used, it prompts to roll back:
Prompt:
Proceed with rollback (terraform destroy)?– default: No
- Confirming runs
terraform destroyagainst the workspace's infrastructure on a best-effort basis. - Declining (or a failure earlier than
terraform_iac, or a run with--skip-terraform) leaves resources as they are; the log notes that resources may be in a partial state. - In
--non-interactivemode, this prompt is never shown – it silently resolves to No, so resources are left in place and the run exits with its original error. Follow up with--resumeto continue, or tear down manually through the Azure portal orterraform destroy.
There is currently no flag to disable this behavior when it would otherwise apply.
Common options
| Flag | Description |
|---|---|
-h, --help | Print usage information and exit. |
--resume | Skip completed phases and continue from the last failure. |
--force | Clear all checkpoints and re-run all phases. |
--verbose, -v | Enable debug output. |
--dry-run | Run the Ansible phases in check mode (Ansible --check). This affects the Ansible phases only; Terraform still plans and applies changes. |
--skip-tools | Skip installing the toolchain. A verification step still runs regardless of this flag and requires az, terraform, kubectl, kubelogin, helm, skopeo, python3, jq, and ODBC Driver 18 to already be on PATH – the run fails immediately if any are missing. Use this flag only when the full toolchain is already installed and verified. |
--skip-terraform | Skip both Terraform phases, along with artifact publishing (blob and container registry), tfvars sync, and Key Vault secret validation — the entire Phase 7 block runs only when Terraform isn't skipped. Use only when a prior run already completed Phase 7 and you're re-running later phases. |
--skip-ansible | Skip the Ansible configuration phase. |
--tags <tags> | Restrict Ansible execution to specific tags (comma-separated), for example --tags dns_records. The value applies to all four Ansible sub-phases, and each sub-phase is still checkpointed as complete when it exits successfully – including the ones where the tag matched no tasks. A later --resume skips those, so use --force or delete the relevant .done files before a full re-run. |
--non-interactive | Disable interactive prompts; fail on missing input (CI/CD mode). |
--auto-create-groups | Create Entra ID groups automatically without prompting. |
--entra-group-name <name> | Override the default Entra admin group name (omada-admins). |
--sql-group-name <name> | Override the default SQL admin group name (omada-sql-admins). |
--update-config | Write newly created Entra/SQL group IDs back into deployment-config.json. |
--workspace <prefix> | Override deployment.workspace_prefix from the command line. |
--no-cache | Force re-download of the release package instead of reusing a cached copy. |
--config <path> | Path to the deployment configuration file (default: ./deployment-config.json). |
--upgrade | Acknowledge a forward product-version change against an existing workspace. Required when upgrading – see the Upgrade guide. |
--drain-timeout <mins> | On any run against an existing environment, how long to wait for an import already in flight to finish (default: 30; 0 fails immediately instead of waiting). |
--skip-import-drain | Still pause imports, but do not wait for an import already in flight to finish. |
-h, --help | Print the full option list and exit. |
During Ansible phases, you may see deprecation warnings related to ansible.module_utils._text imports (to_bytes, to_native, to_text). These warnings come from the Azure Ansible collection and do not affect task execution with the current pinned Ansible Core version. For details, see Known issue: Ansible module deprecation warnings.
Stage dependency overview
The installer runs the following phases in order, with each phase checkpointed on success. When using --resume, the script skips phases whose checkpoint already exists and continues from the first incomplete one – including resuming partway through the Ansible stage, since each Ansible sub-phase has its own checkpoint.
Four phases are an exception and always run on every invocation, checkpoint or not:
config– to reload configuration values into the current shellterraform_backend– to re-validate, not recreate, the Terraform state backendimport_gate– an import can start between runs, so a stale checkpoint must never skip the checkpause_imports– a resumed run must re-establish the pause rather than assume an earlier attempt still holds it
None of the four performs destructive or duplicate work when re-run. In upgrade mode, upgrade_snapshot also always runs; a re-run adopts the snapshot the earlier attempt captured rather than taking a new one, so the restore anchor stays fixed at the first attempt.
| Order | Stage | Purpose |
|---|---|---|
| 1 | preflight | OS, disk, network checks. |
| 2 | config | Load and validate deployment-config.json. |
| 3 | tools | Install toolchain (Terraform, Ansible, kubectl, etc.). |
| 4 | azure | Azure auth, RBAC and quota checks, Entra/SQL group provisioning. |
| 5 | terraform_backend | Create or validate Terraform state storage. |
| 6 | release | Download and extract Cloud Private release package. |
| 7 | version_guard | Compare the release version against the workspace resource group's ProductVersion tag and block an unacknowledged change. |
| 8 | import_gate | Refuse to continue while an import is active. |
| 9 | pause_imports | Pause imports for the rest of the run and wait for any import already in flight to finish. Skipped on a genuine fresh install; released automatically when the run ends, however it ends. |
| 10 | terraform_iac_shared | Provision shared global infrastructure. |
| 11 | artifacts_publish_blob | Publish release artifacts to blob storage. |
| 12 | artifacts_publish_acr | Publish container images to ACR. |
| 13 | tfvars_sync | Sync release-derived values into Terraform variables. |
| 14 | secrets_validate | Validate Key Vault certificates and secrets. |
| 15 | terraform_iac | Provision workspace-specific infrastructure. |
| 16 | ansible_db_preaks | Tier 1 – pre-AKS database setup. |
| 17 | ansible_deploy_aks | Phase 1 – deploy AKS cluster, ES pod, Timer pod, the four Cloud Application Gateway worker pods, and DNS records. |
| 18 | ansible_configure_es | Tier 2+3 – ES configuration (changeset import, provisioning, connectivity). |
| 19 | ansible_deploy_aks_phase2 | Phase 2 – deploy RoPE and CagOps pods. |
| 20 | health_check | Automated post-deployment health check. Detailed documentation: Post-deployment health check. |
| 21 | environment_snapshot_post | Capture the environment's post-deployment state as JSON. Runs on fresh installs as well as upgrades, and is attempted best-effort even when an earlier phase fails. |
Two further phases run only with --upgrade: upgrade_snapshot, which captures the pre-upgrade state before anything is modified, and upgrade_verify, which checks that the upgrade actually took effect and then marks it complete. See Upgrade verification in the Upgrade guide.
Completed phases are marked with checkpoint files under Deployment_OICP_<version>/run-<project>-<workspace>/.
Review the installation log for the health_check phase result and address any reported issues before handing over the environment. For the full validation checklist, see Deployment validation checklist in the Deployment guide.
If the health check reports failures or warnings and you need to troubleshoot further, see Collecting debug logs to gather diagnostic data for analysis or to send to Omada Support.