Skip to content

Latest commit

Β 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

☁️ Azure Data Factory Integration Runtime β€” Azure (managed) Terraform Module

The managed, serverless compute a Data Factory uses for data flow activities, targeting hashicorp/azurerm ~> 4.0.

Terraform azurerm Module Type Resources Caveat


🧩 Overview

  • βš™οΈ Creates one azurerm_data_factory_integration_runtime_azure β€” the compute behind data flow activities, provisioned on demand.
  • 🧭 location accepts a magic value that is not a region. AutoResolve makes Data Factory pick a region per activity, so the runtime has no fixed place.
  • πŸ”’ core_count is a closed set, not a range β€” 8, 16, 32, 48, 80, 144, 272. A value like 12 or 64 is refused.
  • ⏱️ time_to_live_min defaults to 0, so the cluster is torn down after every activity and the next data flow pays full startup again. Raising it keeps the cluster billed while idle.
  • βœ… The provider's one cross-field rule fires at terraform plan and needs credentials. This module checks the same rule at terraform validate β€” offline.

πŸ’‘ Why it matters: every argument except the name and the vnet toggle is a cost decision. None of them changes what a pipeline does; all of them change what it costs and how fast it starts.


❀️ Support this project

If this module saved you time:


πŸ—ΊοΈ Where this fits in the family

flowchart TB
  RG["terraform-azurerm-resource-group"]
  ADF["terraform-azurerm-data-factory"]
  THIS["terraform-azurerm-data-factory-integration-runtime-azure"]
  SELF["azurerm_data_factory_integration_runtime_self_hosted, its own module"]
  SSIS["azurerm_data_factory_integration_runtime_azure_ssis, its own module"]
  LS["a linked service or activity"]
  FLOW["a data flow activity"]
  CLUSTER["a data flow cluster, provisioned on demand and billed while warm"]

  RG -->|"name"| ADF
  ADF -->|"id"| THIS
  ADF -->|"id"| SELF
  ADF -->|"id"| SSIS
  THIS -->|"name, referenced by NAME and never by id"| LS
  LS -->|"runs on"| FLOW
  FLOW -->|"starts and bills"| CLUSTER
  THIS -->|"no credential of its own"| CLUSTER
  SELF -->|"carries authorization keys a node registers with"| CLUSTER

  classDef this fill:#0078D4,stroke:#004578,color:#ffffff,stroke-width:2px
  classDef keystone fill:#004578,stroke:#00243d,color:#ffffff,stroke-width:2px
  classDef sibling fill:#eef3f8,stroke:#b9c8d8,color:#1b2733
  class THIS this
  class ADF keystone
  class RG,SELF,SSIS,LS,FLOW,CLUSTER sibling
Loading

Two edges carry the point: this runtime is referenced by name, never by ID, and it holds no credential of its own β€” unlike the self-hosted runtime, whose authorization keys let a node register with the factory.


🧬 What this module builds

flowchart TB
  subgraph INPUTS["Inputs"]
    NAME["name (force-new)"]
    ADFID["data_factory_id (force-new)"]
    LOC["location (force-new), a region OR the magic value AutoResolve"]
    CLUSTER["compute_type, core_count, time_to_live_min, cleanup_enabled"]
    VNET["virtual_network_enabled (force-new) and the interactive authoring TTL"]
    DESC["description"]
  end

  THIS["azurerm_data_factory_integration_runtime_azure.this"]

  subgraph OUTPUTS["Outputs"]
    ONAME["name, the value linked services reference"]
    OAUTO["location_is_auto_resolve, and the data-residency consequence"]
    OCOST["cluster_stays_warm_and_billed, torn_down_after_every_activity, larger_than_default"]
    OPAIR["the pairing the PROVIDER checks at plan and this module checks at plan"]
    OFN["force_new_fields, which includes a boolean"]
    OFACTS["the constant facts, including no credential of its own"]
  end

  NAME --> THIS
  ADFID --> THIS
  LOC --> THIS
  CLUSTER --> THIS
  VNET --> THIS
  DESC --> THIS

  THIS --> ONAME
  LOC --> OAUTO
  CLUSTER --> OCOST
  VNET --> OPAIR
  VNET --> OFN
  THIS --> OFACTS

  classDef this fill:#0078D4,stroke:#004578,color:#ffffff,stroke-width:2px
  classDef sibling fill:#eef3f8,stroke:#b9c8d8,color:#1b2733
  class THIS this
  class NAME,ADFID,LOC,CLUSTER,VNET,DESC,ONAME,OAUTO,OCOST,OPAIR,OFN,OFACTS sibling
Loading
Resource Count Notes
azurerm_data_factory_integration_runtime_azure.this 1 The keystone. A real ARM child of the factory.
timeouts dynamic, 0..1 All four keys exist β€” Terraform's, not the cluster's.

βœ… Provider / Versions

Requirement Value
Terraform >= 1.12.0
hashicorp/azurerm ~> 4.0
Provider block None. The caller configures the provider, including the mandatory features {} block.

Schema notes that bite β€” verified against the live provider source, not inferred from the schema:

  • πŸ”΄ The interactive-authoring pairing is enforced in a CustomizeDiff. Setting interactive_authoring_time_to_live_in_minutes requires virtual_network_enabled = true, and the provider checks it in a CustomizeDiff β€” which fires at terraform plan and needs credentials. Both operands are plain arguments, so this module also checks it in a validation {} at terraform validate, offline.
  • πŸ”΄ location accepts AutoResolve, which is not a region. An auto-resolve runtime picks a region per activity from the source and sink β€” so a data-residency requirement is not satisfied merely because the factory is in the right region.
  • πŸ”΄ core_count is a CLOSED SET: 8, 16, 32, 48, 80, 144, 272. An intermediate value is refused.
  • πŸ”΄ virtual_network_enabled is FORCE-NEW. It reads like a toggle and is a rebuild; any warm cluster is lost.
  • ⚠️ compute_type is case-sensitive, one of General, ComputeOptimized, MemoryOptimized, defaulting to General.
  • ⚠️ time_to_live_min defaults to 0. Non-zero keeps the cluster warm and billed whether or not work arrives.
  • ⚠️ cleanup_enabled = false with time_to_live_min = 0 is accepted and close to meaningless.
  • ⚠️ Setting the interactive TTL at all enables the feature β€” there is no separate switch.
  • ⚠️ Timeouts default to 30m / 5m / 30m / 30m and bound Terraform's management of the record, not cluster startup or activity duration.
  • ℹ️ The requires-import error names this resource correctly.

πŸ”‘ Required Azure RBAC Roles / Permissions

Permission Scope Why
Microsoft.DataFactory/factories/integrationruntimes/write the Data Factory Create and update.
Microsoft.DataFactory/factories/integrationruntimes/read the Data Factory Refresh and plan.
Microsoft.DataFactory/factories/integrationruntimes/delete the Data Factory Destroy β€” see below.
Data Factory Contributor the Data Factory The built-in role containing all three.

πŸ”’ This runtime holds no credential of its own. It runs under the factory's identity and whatever the linked services carry, so nothing on this resource is secret and no output is sensitive. That is not true of the self-hosted runtime.

πŸ’° Creating this resource provisions no cluster and starts no billing. Charges begin when a data flow activity runs, and continue for time_to_live_min afterwards.

⚠️ Delete is the operation to review. Linked services and activities select a runtime by name, and nothing points back from the runtime to them.


Azure Prerequisites

  • An existing Data Factory, and its Resource ID.
  • A decision about location: a real Azure region, or AutoResolve. They are different modes, not a value and a default.
  • If interactive authoring is wanted, virtual_network_enabled = true.
  • The Microsoft.DataFactory resource provider registered in the subscription.

πŸ“ Module Structure

terraform-azurerm-data-factory-integration-runtime-azure/
β”œβ”€β”€ providers.tf    # required_version + the pinned azurerm; no provider block
β”œβ”€β”€ variables.tf    # 11 typed inputs, 10 validations
β”œβ”€β”€ main.tf         # the keystone, the cost-shaped locals, one dynamic block
β”œβ”€β”€ outputs.tf      # 39 outputs; id first
β”œβ”€β”€ README.md       # this file
β”œβ”€β”€ SCOPE.md        # the cross-module contract
β”œβ”€β”€ LICENSE         # MIT
└── .gitignore

βš™οΈ Quick Start

provider "azurerm" {
  features {}
}

module "data_flow_runtime" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-data-factory-integration-runtime-azure.git?ref=v1.0.0"

  name            = "ir-dataflow"
  data_factory_id = var.data_factory_id
  location        = "eastus"
}

ℹ️ The empty call gives you an 8-core General cluster with no warm window β€” cheap, and slow to start.


πŸ”Œ Cross-Module Contract

Consumes

Input Type Typical source
data_factory_id string terraform-azurerm-data-factory β†’ id
location string the caller β€” a region or AutoResolve

Emits (selected β€” 39 in total)

Output Description
name The value linked services and activities reference.
location_is_auto_resolve Whether the magic value was used.
cluster_stays_warm_and_billed Billed while idle.
force_new_fields Includes a boolean.
the_interactive_authoring_pairing_is_enforced_at_PLAN_by_the_provider Constant. Why this module duplicates it.

πŸ“š Example Library

1 Β· The minimum call
module "runtime" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-data-factory-integration-runtime-azure.git?ref=v1.0.0"

  name            = "ir-dataflow"
  data_factory_id = var.data_factory_id
  location        = "eastus"
}

πŸ’‘ 8 cores, General, time_to_live_min = 0 β€” the cluster is torn down after every activity.

2 Β· An auto-resolve runtime
module "auto" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-data-factory-integration-runtime-azure.git?ref=v1.0.0"

  name            = "ir-autoresolve"
  data_factory_id = var.data_factory_id
  location        = "AutoResolve"
}

output "has_a_fixed_region" {
  value = !module.auto.location_is_auto_resolve # false
}

⚠️ AutoResolve is not a region. Data Factory picks one per activity from the source and sink, so this runtime has no fixed place β€” which matters for data residency and for latency.

3 Β· Asserting a fixed region for data residency
module "eu_only" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-data-factory-integration-runtime-azure.git?ref=v1.0.0"

  name            = "ir-eu"
  data_factory_id = var.data_factory_id
  location        = var.runtime_location
}

check "compute_has_a_fixed_region" {
  assert {
    condition     = !module.eu_only.location_is_auto_resolve
    error_message = "An auto-resolve runtime chooses a region per activity; it cannot satisfy a residency requirement."
  }
}

πŸ”’ Placing the factory in the right region does not constrain where an auto-resolve runtime's compute runs.

4 Β· An intermediate core count is refused
# ❌ NOT ACCEPTED β€” core_count is a closed set, not a range
module "wrong_size" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-data-factory-integration-runtime-azure.git?ref=v1.0.0"

  name            = "ir-wrong"
  data_factory_id = var.data_factory_id
  location        = "eastus"

  core_count = 64 # 48 and 80 are legal; 64 is not
}

⚠️ The accepted values are 8, 16, 32, 48, 80, 144, 272. Anyone sizing by doubling from 32 will land on 64 and be refused.

5 Β· A warm cluster β€” and what it costs
module "warm" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-data-factory-integration-runtime-azure.git?ref=v1.0.0"

  name            = "ir-warm"
  data_factory_id = var.data_factory_id
  location        = "eastus"

  time_to_live_min = 15
}

output "billing_shape" {
  value = {
    warm_and_billed = module.warm.cluster_stays_warm_and_billed          # true
    torn_down       = module.warm.cluster_is_torn_down_after_every_activity # false
  }
}

⚠️ The cluster is billed for the whole 15 minutes whether or not another activity arrives. The trade is latency against cost, and the provider's default sits at the cheap, slow end.

6 Β· Disabling cleanup with no time to live
module "meaningless" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-data-factory-integration-runtime-azure.git?ref=v1.0.0"

  name            = "ir-odd"
  data_factory_id = var.data_factory_id
  location        = "eastus"

  cleanup_enabled  = false
  time_to_live_min = 0 # the provider's default
}

output "reported" {
  value = module.meaningless.cleanup_disabled_with_no_time_to_live # true
}

ℹ️ Accepted, and close to meaningless β€” disabling cleanup keeps the cluster only until the TTL is reached, and there is no TTL to reach. Reported rather than refused; the intent was probably to set both.

7 Β· A larger, specialised cluster
module "memory_heavy" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-data-factory-integration-runtime-azure.git?ref=v1.0.0"

  name            = "ir-memory"
  data_factory_id = var.data_factory_id
  location        = "eastus"

  compute_type     = "MemoryOptimized"
  core_count       = 48
  time_to_live_min = 10
}

check "cost_shape_is_intentional" {
  assert {
    condition     = module.memory_heavy.is_larger_than_the_default_cluster
    error_message = "Expected a larger-than-default cluster for this workload."
  }
}

ℹ️ compute_type and core_count change cost and speed, never behaviour.

8 Β· A lowercase compute type is refused
# ❌ NOT ACCEPTED β€” the set is case-sensitive
module "wrong_case" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-data-factory-integration-runtime-azure.git?ref=v1.0.0"

  name            = "ir-wrong-case"
  data_factory_id = var.data_factory_id
  location        = "eastus"

  compute_type = "general" # wants "General"
}
9 Β· Interactive authoring without a managed vnet β€” caught offline
# ❌ NOT ACCEPTED β€” and refused at `terraform plan`, offline and without credentials
module "interactive_without_vnet" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-data-factory-integration-runtime-azure.git?ref=v1.0.0"

  name            = "ir-interactive"
  data_factory_id = var.data_factory_id
  location        = "eastus"

  interactive_authoring_time_to_live_in_minutes = 30
  # virtual_network_enabled left at false
}

πŸ”’ This is the module's substantive improvement. The provider enforces the same rule in a CustomizeDiff, which fires at terraform plan and needs credentials. Both operands are plain arguments, so this module checks it at terraform validate β€” offline, and reachable by a caller with no subscription. The provider's check remains the authority; this one simply arrives earlier.

10 Β· Interactive authoring, correctly paired
module "interactive" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-data-factory-integration-runtime-azure.git?ref=v1.0.0"

  name            = "ir-interactive"
  data_factory_id = var.data_factory_id
  location        = "eastus"

  virtual_network_enabled                       = true
  interactive_authoring_time_to_live_in_minutes = 30 # 10, 30, 60 or 120
}

output "interactive_on" {
  value = module.interactive.interactive_authoring_enabled # true
}

ℹ️ Setting the TTL is what enables the feature β€” there is no separate switch, which is why the module emits interactive_authoring_enabled as a derived boolean.

11 Β· The boolean that is a rebuild
module "runtime" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-data-factory-integration-runtime-azure.git?ref=v1.0.0"

  name            = "ir-dataflow"          # changing this REPLACES
  data_factory_id = var.data_factory_id    # changing this REPLACES
  location        = "eastus"               # changing this REPLACES
  virtual_network_enabled = true           # changing this ALSO REPLACES

  core_count = 16                          # changing this updates IN PLACE
}

output "what_forces_replacement" {
  value = module.runtime.force_new_fields
  # ["name", "data_factory_id", "location", "virtual_network_enabled"]
}

⚠️ virtual_network_enabled reads like a toggle and is a rebuild. Moving an existing runtime into or out of the managed virtual network destroys and recreates it, losing any warm cluster.

12 Β· Many runtimes from one map
locals {
  runtimes = {
    small = { cores = 8, ttl = 0 }
    large = { cores = 48, ttl = 15 }
  }
}

module "runtimes" {
  for_each = local.runtimes

  source = "git::https://github.com/microsoftexpert/terraform-azurerm-data-factory-integration-runtime-azure.git?ref=v1.0.0"

  name             = "ir-${each.key}"
  data_factory_id  = var.data_factory_id
  location         = "eastus"
  core_count       = each.value.cores
  time_to_live_min = each.value.ttl
}

output "warm_runtimes" {
  value = [for k, m in module.runtimes : k if m.cluster_stays_warm_and_billed]
}

πŸ’‘ A useful review artefact: the list of runtimes that cost money while idle.

13 Β· Timeouts bound Terraform, not the cluster
module "runtime" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-data-factory-integration-runtime-azure.git?ref=v1.0.0"

  name            = "ir-dataflow"
  data_factory_id = var.data_factory_id
  location        = "eastus"

  timeouts = {
    create = "45m"
    read   = "10m"
    update = "45m"
    delete = "45m"
  }
}

⚠️ These bound how long Terraform waits to manage the runtime record. Nothing here affects cluster startup, activity duration, or the warm window β€” that last one is time_to_live_min.

14 Β· πŸ—οΈ End-to-end composition
provider "azurerm" {
  features {}
}

module "rg" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-resource-group.git?ref=v1.0.0"

  name     = "rg-analytics-eastus"
  location = "eastus"
}

module "adf" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-data-factory.git?ref=v1.0.0"

  name                = "adf-analytics-eastus"
  resource_group_name = module.rg.name
  location            = module.rg.location

  managed_virtual_network_enabled = true

  identity = {
    type = "SystemAssigned"
  }
}

module "runtime" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-data-factory-integration-runtime-azure.git?ref=v1.0.0"

  name            = "ir-dataflow"
  data_factory_id = module.adf.id
  location        = module.rg.location # a real region, not AutoResolve

  compute_type     = "General"
  core_count       = 16
  time_to_live_min = 10

  virtual_network_enabled                       = true
  interactive_authoring_time_to_live_in_minutes = 30

  description = "Data flow compute for the curated pipelines."
}

check "runtime_is_what_we_intended" {
  assert {
    condition     = !module.runtime.location_is_auto_resolve
    error_message = "Compute must have a fixed region for this workload."
  }
  assert {
    condition     = module.runtime.cluster_stays_warm_and_billed
    error_message = "Expected a warm cluster; a TTL of 0 makes every data flow pay full startup."
  }
}

output "runtime_name" {
  value = module.runtime.name # linked services reference this, NOT the id
}

πŸ”’ The factory's managed_virtual_network_enabled and the runtime's virtual_network_enabled are separate settings on separate resources. This module checks its own pairing offline; the factory's is the caller's.


πŸ“₯ Inputs

Required: name, data_factory_id, location. The cluster: compute_type, core_count, time_to_live_min, cleanup_enabled. The managed vnet: virtual_network_enabled, interactive_authoring_time_to_live_in_minutes. Metadata: description, timeouts. There is no tags variable β€” the resource supports none.

Full input schemas
Name Type Default Notes
name string β€” Force-new. Non-empty only; a leading / is refused.
data_factory_id string β€” Force-new. Anchored Resource-ID validator.
location string β€” Force-new. A region or AutoResolve. Not validated against a region list.
compute_type string "General" Case-sensitive set of three.
core_count number 8 Closed set: 8/16/32/48/80/144/272.
time_to_live_min number 0 Non-negative whole number. Non-zero is billed while idle.
cleanup_enabled bool true False keeps the cluster until the TTL.
virtual_network_enabled bool false Force-new. Carries the interactive-authoring pairing check.
interactive_authoring_time_to_live_in_minutes number null 10/30/60/120. Setting it enables the feature.
description string null Empty string refused.
timeouts object({ create, read, update, delete }) null Terraform's, not the cluster's.

🧾 Outputs

Output Description Notes
id The runtime's Resource ID. First, by convention.
name The runtime's name. What linked services reference.
data_factory_id, data_factory_name The parent factory. Name parsed from the end of the ID.
resource_group_name, subscription_id Where the factory lives.
location As configured. May not be a region.
location_is_auto_resolve The magic value. Assert on this for residency.
an_auto_resolve_runtime_has_no_fixed_region Constant.
compute_type, core_count, time_to_live_min, cleanup_enabled The cluster's shape.
the_core_count_is_a_closed_set_not_a_range Constant.
cluster_is_torn_down_after_every_activity True at the default.
cluster_stays_warm_and_billed Billed while idle. Assert on this.
cleanup_disabled_with_no_time_to_live Accepted, near-meaningless.
is_larger_than_the_default_cluster, is_a_specialised_compute_type Cost signals.
this_runtime_costs_money_only_while_it_runs Constant.
virtual_network_enabled, interactive_authoring_time_to_live_in_minutes The vnet pairing.
interactive_authoring_enabled Derived boolean. State implied by a number.
the_interactive_authoring_pairing_is_enforced_at_PLAN_by_the_provider Constant. Why this module duplicates it.
turning_the_virtual_network_on_or_off_replaces_the_runtime Constant.
force_new_fields, fields_that_can_change_after_creation The change surface. Includes a boolean.
description Metadata.
import_address, the_import_guard_names_this_resource_correctly Imports.
this_runtime_is_referenced_by_NAME_not_by_id Constant. Renaming breaks silent references.
the_module_cannot_see_what_runs_on_this_runtime Constant. Read before deleting.
no_credential_is_configured_here, no_secret_is_accepted_or_emitted_by_this_module Constants. Unlike self-hosted.
destroying_the_factory_destroys_this_runtime_too Constant.
lifecycle_prevent_destroy_is_not_available_to_a_module_caller Constant.
this_is_a_real_azure_resource_not_a_composite Constant.
this_resource_supports_no_azure_resource_tags Constant.
the_timeouts_bound_terraform_not_the_cluster Constant.

No output is sensitive, and none can be β€” every argument is a name, a size or a duration.


🧠 Architecture Notes

A cross-field rule moved from plan-time to validate-time. Interactive authoring requires virtual_network_enabled = true, and the provider enforces that in a CustomizeDiff β€” which is evaluated inside the provider and therefore needs credentials and a round trip. Both operands are plain top-level arguments, so this module also checks the pairing in a validation {}. Both refusals land at terraform plan, but this one is decided from the configuration alone β€” no credentials, no round trip. The check is placed on virtual_network_enabled and reads the other one-directionally, because a validation condition must reference its own variable and two variables validating each other is rejected as a cycle. The provider's check remains the authority; this one arrives earlier, and somewhere a caller without a subscription can reach β€” which is also the only place the improvement is demonstrable, and where the harness proves it.

location may not be a place. AutoResolve is a distinct mode, not a default: Data Factory chooses where each activity runs based on its source and sink. A data-residency requirement is therefore not satisfied by placing the factory correctly. The module reports the mode rather than refusing it, because auto-resolve is legitimate and common β€” and it does not validate against a region list, because the set of regions changes and a failing validation would block terraform destroy as well as apply. The report is case-insensitive and says so: this module has not verified whether Azure treats the magic value case-sensitively, so it does not claim either way.

Almost everything else is cost. compute_type and core_count select the VM family and size; time_to_live_min decides how long the cluster stays warm β€” and billed β€” after an activity finishes. The provider's default of 0 is the cheap, slow end: every data flow pays full cluster startup, typically several minutes. None of these arguments changes what a pipeline does. The module emits each consequence as a derived flag because none of them is visible in a plan.

One boolean is a rebuild. virtual_network_enabled is force-new. Moving an existing runtime into or out of the managed virtual network destroys and recreates it, losing any warm cluster β€” and it looks exactly like an in-place toggle.

References are by name. Linked services and activities select an integration runtime by name, not by Resource ID. So name is the output a composition consumes, the ID is for imports and RBAC scoping, and renaming the runtime breaks every reference β€” silently, in configuration this module cannot see.

lifecycle is not valid inside a module block, so a caller cannot add prevent_destroy. A CanNotDelete lock prevents deletion but not the replacement that editing name, location or virtual_network_enabled would cause.


🧱 Design Principles

Concern This module's default Opt-out
Secrets None accepted, none emitted. This runtime holds no credential. Not applicable.
Cost The provider's defaults β€” 8 cores, General, no warm window. Raise core_count or time_to_live_min.
A warm, billed cluster Reported via cluster_stays_warm_and_billed. Ignore the output.
AutoResolve Reported, never refused β€” it is a legitimate mode. Ignore the output.
A region list Not validated. The set changes, and refusing would block destroy. β€”
core_count Enforced against the provider's closed set. None β€” the set is closed.
compute_type Enforced, case-sensitively. None.
Interactive authoring without a vnet Refused offline, without credentials β€” the provider needs a round trip. None.
cleanup_enabled = false with no TTL Reported, not refused. Ignore the output.
tags Not offered β€” the resource supports none. Tag the factory.

πŸ”’ There is no security toggle to close here: the resource holds no credential. The exposures are the delete, the silent rename, and the bill β€” the module documents the first two and reports the third.


πŸš€ Runbook

terraform init -backend=false
terraform validate
terraform fmt -check

Pin the module with ?ref=v1.0.0 β€” never a branch. This library is plan-only: a human applies from CI.


πŸ§ͺ Testing

terraform plan and the module's own validation {} blocks cover, offline and without credentials:

  • the interactive-authoring pairing β€” the rule the provider checks only inside a CustomizeDiff that needs credentials, refused here in both its failing forms (TTL with the flag unset, and TTL with the flag explicitly false);
  • every legal core_count accepted and the intermediate values 12, 64 and 0 refused;
  • every legal compute_type accepted and a lowercase one refused;
  • all four interactive TTLs accepted when correctly paired, and 45 refused;
  • AutoResolve accepted in both casings, alongside a real region;
  • every derived flag β€” the warm/torn-down pair, cleanup_disabled_with_no_time_to_live, interactive_authoring_enabled, location_is_auto_resolve β€” each with one fixture per branch through terraform console;
  • structural assertions that every output this README promises is actually declared.

⚠️ terraform validate reaches none of that through a module call. Validate evaluates no module variable values, so this module's validation {} blocks are never reached and it reports success. The refusals above land at plan β€” still offline and without credentials.

Only a real apply can tell you whether the factory has a managed virtual network, what a cluster actually costs, or what currently references the runtime by name.


πŸ’¬ Example Output

runtime_name = "ir-dataflow"

id                                    = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-analytics-eastus/providers/Microsoft.DataFactory/factories/adf-analytics-eastus/integrationruntimes/ir-dataflow"
location                              = "eastus"
location_is_auto_resolve              = false
compute_type                          = "General"
core_count                            = 16
time_to_live_min                      = 10
cluster_stays_warm_and_billed         = true
cluster_is_torn_down_after_every_activity = false
is_larger_than_the_default_cluster    = true
interactive_authoring_enabled         = true
force_new_fields                      = ["name", "data_factory_id", "location", "virtual_network_enabled"]

πŸ” Troubleshooting

Symptom Cause Fix
core_count must be one of 8, 16, 32, 48, 80, 144 or 272. on 64 It is a closed set, not a range. Use 48 or 80.
compute_type must be General, ComputeOptimized or MemoryOptimized. on "general" Case-sensitive. Capitalise it.
when interactive_authoring_time_to_live_in_minutesis set,virtual_network_enabledmust be set totrue. The pairing. This module catches it at plan; the provider catches it at plan. Set virtual_network_enabled = true.
interactive_authoring_time_to_live_in_minutes must be 10, 30, 60 or 120. A closed set. Pick one of the four.
Data flows are slow to start time_to_live_min is 0 β€” the provider's default. Raise it, and accept the idle billing.
The runtime costs money with nothing running A non-zero TTL keeps the cluster warm and billed. Check cluster_stays_warm_and_billed.
Disabling cleanup appears to do nothing time_to_live_min is 0, so there is no TTL to reach. Check cleanup_disabled_with_no_time_to_live.
Compute ran outside the expected region location is AutoResolve β€” a mode, not a region. Check location_is_auto_resolve; set a real region.
Toggling the managed vnet destroyed the runtime virtual_network_enabled is force-new. Expected. The warm cluster is lost.
A pipeline stopped finding its runtime after a rename Runtimes are referenced by name. Update the referencing linked services and activities.
A slow cluster start is not covered by timeouts Those bound Terraform's management of the record. Nothing here bounds cluster startup.

πŸ”— Related Docs


πŸ’™ "Infrastructure as Code should be standardized, consistent, and secure."