Integration Modules: Logic App, Function App, Container App, Data Factory¶
The integration modules are Terraform modules that deploy an integration service in your spoke, already connected to the spoke (network, Key Vault, storage account, Log Analytics) and built on a secure baseline. You choose a few settings, the module does the rest. This page describes the module set, what each module deploys, its defaults, and how a workload team uses it. How to run the deployment: Deploy integration resources.
Optional offering, reference design
Which modules and versions your platform offers depends on your platform. The defaults on this page come from the Terraform of the modules. Your platform team may have changed them or pinned another version.
The module set¶
One root module takes a map per service type. You fill only what you need. An empty map deploys nothing.
| Service | Input map | Deploys (summary) |
|---|---|---|
| Logic App | logic_apps |
A Standard Logic App on a shared workflow plan, private endpoint only, built-in authentication, own storage account |
| Function App | function_apps (and service_plans) |
A Linux Function App, VNet integration, private endpoint, identity-based access to the spoke storage account |
| Container App | container_apps_environment |
A Container Apps environment (internal), container apps and jobs in t-shirt sizes |
| Data Factory | data_factories |
A Data Factory with managed virtual network, private endpoint and linked services to the spoke Key Vault and storage |
Together they make up the modules ingredient of the platform (Service description).
flowchart LR
cfg["Your configuration<br/>logic_apps, function_apps,<br/>container_apps_environment, data_factories"] --> root["Root module"]
out["spoke_config<br/>(spoke outputs)"] --> root
root --> la["Logic App"]
root --> fa["Function App + service plans"]
root --> ca["Container Apps environment,<br/>apps and jobs"]
root --> adf["Data Factory"]
la & fa & ca & adf -->|"read"| spoke["Spoke: VNet and subnets,<br/>Key Vault, storage account,<br/>Log Analytics"]
How a module uses your spoke¶
Every module needs the description of your spoke in one variable, spoke_config. It is filled from a file with the spoke
outputs (see Deploy integration resources).
Field of spoke_config |
Meaning |
|---|---|
name, environment, location, tags |
Name, environment, region and tags of the spoke. Modules inherit tags. The Container App module needs the environment to be nonprod or prod (see below). |
subscription_id, tenant_id, resource_group_name |
Where the spoke lives |
virtual_network_id, virtual_network_name |
The spoke network |
subnet_ids, subnet_names |
Maps of the spoke subnets. Modules pick subnets by name (for example for private endpoints and for outbound integration). |
key_vault_id, key_vault_name |
The spoke Key Vault |
storage_account_id, storage_account_name |
The spoke storage account |
log_analytics_workspace_id, log_analytics_workspace_name |
Where logs go |
The modules read these resources (Key Vault, storage account, Log Analytics workspace) as existing resources. They do not create them. A module that cannot find a named subnet fails, so check the subnet names in your spoke first.
Private endpoints: the modules do not manage the private DNS zone groups of their private endpoints. Name resolution for the endpoints is expected to be arranged by the platform, for example by Azure Policy. Ask your platform team.
Logic App¶
A Standard Logic App with a hardened baseline.
logic_apps = {
orders = {
subnets = {
private_endpoint_subnet_name = "<private-endpoint-subnet>"
logic_app_outbound_subnet_name = "<logic-app-outbound-subnet>"
}
authentication_settings = {
client_id = "<entra-app-client-id>"
}
environment_variables = {}
}
}
| Setting | Default and effect |
|---|---|
| Plan | One shared workflow service plan for all Logic Apps of the spoke, Windows, created by the root module from default_logic_apps_plan: size S (WS1; also M = WS2, L = WS3), at least 2 instances, zone balancing on. Plan name asp-<spoke name>-logicapps in a resource group rg-<spoke name>-logicapps unless you set names. |
| Resource group | A group per Logic App is created by default (create_resource_group, true). You can give an existing one. |
| Network | Public network access off. Private endpoint in the subnet you name, outbound traffic through the outbound subnet (VNet integration). vnet_private_ports_count reserves private ports. |
| Authentication | Always on. Unauthenticated requests get a 401. Allowed audiences, groups and identities are set in authentication_settings. /runtime/* is excluded so run history can show inputs and outputs. |
| Storage | A dedicated storage account for the Logic App, for performance and security isolation, with a deny-by-default firewall that allows only the outbound subnet |
| Identity | System-assigned and a user-assigned managed identity |
| Hardening | HTTPS only, FTPS disabled, basic authentication for FTP and web deploy disabled, HTTP/2 on |
| Monitoring | Application Insights connected to the spoke Log Analytics workspace, diagnostic settings for the app and its storage |
| Health check | A workflow named HealthCheck with trigger Request is used for the health check. Both names can be changed (names of at most 12 and 7 characters). |
Resource names: logic-<name>, appi-<name>, pe-logic-<name>, id-<name>.
Deploy the workflows. The module repository contains two small building blocks for a pipeline: one packages the Logic
App project (it uses parameters.azure.json as parameters.json when it exists and leaves out local.settings.json), the other
deploys the package to the Logic App Standard resource. Your platform team gives you the pipeline template.
Function App¶
A Function App on a service plan that you define, with identity-based access to the spoke storage account.
service_plans = {
shared-linux = {
name = "plan-<name>"
sku_name = "EP1"
os_type = "Linux"
tags = {}
}
}
function_apps = {
processor = {
name = "func-<name>"
service_plan_key = "shared-linux"
virtual_network_subnet_id = "<integration-subnet-id>"
app_settings = { "ENV" = "dev" }
private_endpoints = {
default = { subnet_resource_id = "<private-endpoint-subnet-id>" }
}
}
}
| Setting | Default and effect |
|---|---|
| Plan | Choose with service_plan_key (a plan from service_plans) or service_plan_resource_id (an existing plan). Several apps can share a plan. |
| Flex Consumption (FC1) | Set sku_name = "FC1" on the plan and function_app_uses_fc1 = true on the app, with fc1_runtime_name and fc1_runtime_version (defaults node and 20). One app per FC1 plan, Linux only. |
| Network | VNet integration through virtual_network_subnet_id, and a private endpoint is required: always give at least one entry in private_endpoints. |
| Storage | No keys. The app uses its system-assigned identity on the spoke storage account: the module gives it Storage Blob Data Owner and Storage Blob Data Contributor on that account, creates a private blob container with the app's name, and sets the identity-based AzureWebJobsStorage__... settings. Do not set AzureWebJobsStorage yourself. The module removes it. |
| Identity | System-assigned is always on. You can add user-assigned identities through managed_identities. |
| Site settings | Secure defaults in site_config: FTPS only, minimum TLS 1.3 (1.2 for the SCM site), always on, 32-bit worker off. Authentication v2 and IP restrictions are available. |
| Application Insights | Optional, through application_insights |
| Resource group | The spoke resource group by default. A dedicated group with create_resource_group_for_function_apps and function_apps_resource_group_name. |
Linux in the root module
The module repository contains a Linux and a Windows Function App module. The input of the root module accepts the fields of the Linux module. Check with your platform team before you plan a Windows Function App.
Container App¶
A Container Apps environment in your spoke with container apps and container jobs.
container_apps_environment = {
main = {
resource_name = "<name>"
environment = "nonprod"
vnet_integration_subnet_name = "<container-apps-subnet>"
container_registry_server = "<registry>.azurecr.io"
container_registry_identity = { name = "<pull-identity>", resource_group_name = "<resource-group>" }
container_apps = [
{ name = "api", image = "<registry>.azurecr.io/<repository>/api", size = "S", min_replicas = 1, max_replicas = 3 }
]
scheduled_container_jobs = [
{ name = "nightly", image = "<registry>.azurecr.io/<repository>/job", schedule_cron = "0 2 * * *" }
]
storage_queues = [{ name = "work", alert_on_message_added_to_queue = false }]
}
}
| Setting | Default and effect |
|---|---|
| Environment | Name cae-<name>-<np or p>-<region code>. environment must be nonprod or prod. If you do not set it, the environment of spoke_config is used, so that must be one of the two. The environment is internal (internal load balancer) in the subnet you name, on the Consumption workload profile. Logs go to the spoke Log Analytics workspace. |
| Images | You give the image name per app or job. A shared tag container_image_tag (default latest) is appended. The pull happens with a user-assigned managed identity that you name (container_registry_identity) on the registry container_registry_server. This is where the shared container registry is used (Shared services). |
| Sizes | S 0.25 vCPU, 0.5 GiB. M 0.5 vCPU, 1 GiB. L 1 vCPU, 2 GiB. XL 2 vCPU, 4 GiB. XXL 4 vCPU, 8 GiB. Default S. |
| Container apps | Per app: size, health check, min_replicas (default 0), max_replicas (default 1), environment variables, ingress (default on), optional Dapr |
| Container jobs | Three kinds: manual, event (starts when a message arrives in an Azure Storage queue of the spoke storage account, one execution at a time, polling every 30 seconds) and scheduled (cron expression). Defaults per job: timeout 600 seconds, 3 retries, parallelism 1. |
| Storage helpers | storage_containers, storage_queues and storage_tables create those items in the spoke storage account |
| Secrets | A user-assigned identity of the environment gets Key Vault Certificate User on the spoke Key Vault. The Application Insights connection string is stored in the spoke Key Vault as a secret. |
| Application Insights | One per environment, 30 days retention |
| Alerts | Optional, through monitoring_config: failed requests, exceptions, and messages added to chosen queues. Notifications by email and/or to a Teams channel (through its email address) are switched on per environment. All are off by default. |
Names: ca-<...> for apps, cj-<...> for manual jobs, cj-event-<...> for event jobs and cj-cron-<...> for scheduled jobs.
Data Factory¶
A Data Factory for batch ETL, with a private network model.
data_factories = {
main = {
resource_name = "<name>"
private_endpoint_subnet_name = "<private-endpoint-subnet>"
team_members = ["<alert-recipient>"]
enable_alert_failed_pipeline = true
}
}
| Setting | Default and effect |
|---|---|
| Network | Public network access off, managed virtual network on, a private endpoint in the subnet you name |
| Integration runtime | An Azure integration runtime in the managed network: 8 cores (allowed values 8, 16, 32, 48, 64, 80, 128, 144, 256, 272), compute type General (or MemoryOptimized), time to live 10 minutes |
| Linked services | To the spoke Key Vault and the spoke storage account (blob), authenticated with the managed identity of the factory. They are managed by Terraform, so changes made in the factory to these two are overwritten. |
| Managed private endpoints | To the Key Vault (vault) and the storage account (blob). The module approves them: through the API for the Key Vault, and for storage through an Azure CLI call at deployment. The deployment therefore needs the Azure CLI and the right to approve private endpoint connections on those resources. |
| Access | The factory identity gets Key Vault Secrets User on the Key Vault and Storage Blob Data Contributor on the storage account |
| Git integration | Optional vsts_configuration (an Azure DevOps Git repository) and global_parameters, both managed by Terraform |
| Monitoring | Diagnostic logs (pipeline runs, trigger runs) to the spoke Log Analytics workspace. An action group DefaultActionGroup emails team_members. An alert for failed pipelines when enable_alert_failed_pipeline is true (default false). |
| Resource group | A new resource group per Data Factory |
The private DNS zone group of the Data Factory private endpoint is not managed by the module. It is expected to come from Azure Policy.
Monitoring¶
| Module | What the module sets up | What stays with you |
|---|---|---|
| Logic App | Application Insights, diagnostic settings to the spoke workspace, a health check workflow name | Alerts on your workflows, a HealthCheck workflow in your app |
| Function App | Optional Application Insights | Alert rules and the logging of your function code |
| Container App | Application Insights, optional alerts and action groups | Choosing which alerts to switch on, application logging |
| Data Factory | Diagnostics, optional failed-pipeline alert, an action group | Recipients, alerts on data quality |
Platform-level monitoring: Operations.
Responsibilities¶
| Platform team | Workload team | |
|---|---|---|
| Module code, defaults, new versions, pipeline templates | Yes | |
| Choosing the modules, sizes, plans and options in your configuration | Yes | |
| Spoke, subnets, Key Vault, storage account, Log Analytics | Yes | |
| The application: workflows, function code, container images, pipelines | Yes | |
| Private endpoint DNS, firewall rules | Yes (on request) | Requests |
| Data-plane security | Yes |
The generic split is described in Responsibilities.