Terraform/Tofu Modules
Merkalis publishes OpenTofu modules that compose into a complete Kastoria environment on Azure. The sample deployment below illustrates what they provision when applied together; the module pages that follow document each one.
Sample Deployment
The diagram below shows the resources created when the sample environment configuration is applied. It is a reference deployment that the Merkalis Terraform/OpenTofu modules listed below are designed to produce.
graph TB
subgraph AZURE["Azure"]
subgraph INFRA["Azure subscription (infra)"]
ACR["ACR<br/>infra-rg"]
end
subgraph DEV["Azure subscription (dev) · RG rg"]
UMI["User Managed Identity<br/>umi-*-secrets<br/>umi-*-app-id"]
subgraph NET["Networking"]
subgraph VNET["VNet vnet<br/>10.x.0.0/16"]
SUB_COMPUTE["compute · 10.x.0.0/21<br/>Service Endpoints: Microsoft.ContainerRegistry, Microsoft.KeyVault, Microsoft.Storage<br/>delegated to Microsoft.App"]
SUB_STORAGE["storage · 10.x.8.0/24<br/>Service Endpoints: Microsoft.Storage"]
end
end
subgraph SEC["Secrets"]
KV["Key Vault kv-*-1a99<br/>RBAC · deny-by-default"]
UMI_SEC["umi-*-secrets"]
KV -.->|Key Vault Secrets User| UMI_SEC
end
subgraph OBJ["fa:fa-id-badge Object store - sharded"]
subgraph SN["node storage account(s)<br/>1..n"]
SN_BLOB["BLOB store"]
SN_TAB["table store"]
end
subgraph SB["block storage account(s)<br/>1..n"]
SB_BLOB["BLOB store"]
SB_TAB["table store"]
end
end
subgraph COMPUTE["Container App Environment"]
LA["Log Analytics logs"]
OHIF["app-*--ohif<br/>OHIF viewer · :8080 ext"]
KAST["fa:fa-id-badge app-*--kastoria<br/>:3000 ext"]
SMART["fa:fa-id-badge app-*--smartlaunchapi<br/>:4000 ext"]
PROXY["app-*--kastoria-proxy<br/>:8080 int"]
OTEL["otel-gateway<br/>OTel collector · :4317 int"]
JPROC["fa:fa-id-badge job-*-stdyproc<br/>study processor"]
end
end
end
NET ~~~ SEC
UMI ~~~ NET
PROXY <--> KAST
PROXY <--> SMART
PROXY <--> OHIF
ACR -.->|AcrPull| PROXY
ACR -.->|AcrPull| OTEL
KAST <-.-> OBJ
SMART <-.-> OBJ
JPROC <-.-> OBJ
KAST -.->|metrics/traces| OTEL
SMART -.->|metrics/traces| OTEL
OHIF -.->|metrics/traces| OTEL
JPROC -.->|metrics/traces| OTEL
USER --> PROXY
OHIF --> LA
KAST --> LA
SMART --> LA
PROXY --> LA
JPROC --> LA
classDef computeFill fill:#dbeafe,stroke:#1e40af,color:#000;
classDef storageFill fill:#dcfce7,stroke:#166534,color:#000;
classDef gatewayFill fill:#fef3c7,stroke:#92400e,color:#000;
class SUB_COMPUTE computeFill
class SUB_STORAGE storageFill
class SUB_GATEWAY gatewayFill
class LA,OHIF,KAST,SMART,PROXY,CFAPP,OTEL,JPROC computeFill
class SN,SB storageFill
class SN_BLOB,SN_TAB,SB_BLOB,SB_TAB storageFill
Notes
- The Azure Container Registry (ACR) and resource group (infra-rg) are shown in a separate subscription as that is how they are deployed at Merkalis. Having an ACR is a deployment prerequisite.
- The VNet is segmented into 2 subnets (compute and storage). The diagram denotes which subnet each component is a member of by using matching colors.
- All communications on the compute subnet are secured by mTLS.
- All communications to/from the storage subnets are secured by TLS 1.2
Modules
Documentation for the Merkalis Azure modules is available below. Each page covers the module's features, prerequisites, inputs, outputs, and example usage.
| Module | Description |
|---|---|
| Networking | Provisions a Virtual Network (vNet) and subnets with consistent FinOps tagging |
| Secrets | Provisions a purge-protected Azure Key Vault with RBAC and a reader managed identity |
| Object Store | Provisions sharded Azure Storage Accounts for Blob object storage with mirrored Azure Tables |
| File Store | Provisions sharded Azure Storage Accounts with SMB file shares encrypted at rest and in transit |
| Queue | Provisions a dedicated, key-disabled Azure Storage Account with a single queue for Notification Events |
| Compute | Provisions a shared Container App Environment with container apps and jobs |
| Gateway | Provisions the reverse proxy entrypoint, with optional Cloudflare Tunnel and mTLS proxy |
| Telemetry | Provisions an OpenTelemetry collector container app with optional Grafana Cloud pipelines |
How the module outputs and inputs wire together — and the effective build order — is documented in Module Dependencies.
A complete, hand-composed configuration that provisions the sample deployment
above — including the full main.tf and a walk-through of every wire — lives
in Sample Deployment.