Skip to content

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.