Skip to content

Latest commit

Β 

History

1 Commit

Folders and files

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

Repository files navigation

☁️ Azure Synapse Azure Integration Runtime Terraform Module

Registers one Azure (fully managed) integration runtime on a Synapse workspace β€” the Microsoft-hosted compute a data flow or a copy activity runs on. Targets hashicorp/azurerm ~> 4.0.

Terraform azurerm Module Type Resources Caveat


🧩 Overview

  • 🧩 Creates one azurerm_synapse_integration_runtime_azure β€” a named, Microsoft-hosted runtime on the workspace.
  • 🚩 Leads with a defect in the provider itself: its name error message states a rule its regular expression does not enforce. The message says "no consecutive dashes"; the pattern permits them, verified against the pattern.
  • βš–οΈ Documents the mirror-image defect on the self-hosted sibling β€” same message text, different pattern, wrong about the opposite half.
  • πŸ”’ States that core_count is a closed set, not a range: 8, 16, 32, 48, 80, 144 or 272, with large steps between them.
  • ❄️ Explains that time_to_live_min defaults to 0, which means no cluster reuse at all β€” every data flow pays a cold start β€” and that the provider validates this field with nothing.
  • 🎯 Warns that the three compute settings govern data flows only, so tuning them changes nothing for a copy-only pipeline.
  • 🌍 Notes that AutoResolve moves the data-residency decision out of your Terraform and into run time.
  • 🏷️ Carries no tags β€” the resource has none. The universal tail is timeouts only.

πŸ’‘ Why it matters: a managed runtime is easy to create and easy to misjudge. The default is a cluster that is torn down after every activity, so the first thing people notice is that data flows are slow; the fix is one field the provider does not validate at all. Meanwhile the compute settings that look like the tuning surface are ignored entirely by a copy activity, so a cost investigation that starts here finds nothing to explain.


❀️ Support this project

If these Terraform modules have been helpful to you or your organization, I'd appreciate your support in any of the following ways:

Whether it's a star, a professional connection, or a coffee, every gesture helps keep these modules actively maintained and continually improving. Thank you for being part of the community!


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

flowchart TB
  rg["terraform-azurerm-resource-group"]
  ws["terraform-azurerm-synapse-workspace"]
  this["terraform-azurerm-synapse-integration-runtime-azure"]
  shir["terraform-azurerm-synapse-integration-runtime-self-hosted"]
  sqlp["terraform-azurerm-synapse-sql-pool"]
  spark["terraform-azurerm-synapse-spark-pool"]
  aad["terraform-azurerm-synapse-workspace-aad-admin"]

  rg -->|"name"| ws
  ws -->|"id"| this
  ws -->|"id"| shir
  ws -->|"id"| sqlp
  ws -->|"id"| spark
  ws -->|"id"| aad
  this -->|"name, selected by a pipeline activity"| ws
  shir -->|"the other runtime type, for data Azure cannot reach"| ws

  classDef me fill:#0078D4,stroke:#004578,color:#ffffff;
  classDef keystone fill:#004578,stroke:#002b4d,color:#ffffff;
  classDef sib fill:#eef3f8,stroke:#b9c7d6,color:#1b2a3a;
  class this me;
  class ws keystone;
  class rg,shir,sqlp,spark,aad sib;
Loading

The edge back to the workspace is not a Terraform reference: a pipeline activity selects a runtime by name, as a string in its own definition. Nothing here reports that no activity uses this runtime, and nothing reports that one names a runtime that does not exist.


🧬 What this module builds

flowchart TB
  subgraph inputs["Inputs"]
    ident["name plus synapse_workspace_id, both force-new"]
    loc["location, force-new, AutoResolve or a region"]
    compute["compute_type, core_count, time_to_live_min -- data flows only"]
    desc["description"]
  end

  this["azurerm_synapse_integration_runtime_azure.this"]

  subgraph outputs["Outputs"]
    oid["id, name, synapse_workspace_id, synapse_workspace_name, resource_group_name"]
    oloc["location, region_is_resolved_at_run_time"]
    ocomp["compute_type, core_count, time_to_live_min, cluster_is_torn_down_after_every_activity"]
    ofact["name_uses_consecutive_dashes, the_two_runtime_types_do_not_share_a_name_rule, creating_this_starts_no_compute"]
  end

  ident --> this
  loc -->|"AutoResolve defers the residency decision to run time"| this
  compute -->|"ignored entirely by a copy activity"| this
  desc --> this
  this --> oid
  this --> oloc
  this --> ocomp
  this --> ofact

  classDef me fill:#0078D4,stroke:#004578,color:#ffffff;
  classDef sib fill:#eef3f8,stroke:#b9c7d6,color:#1b2a3a;
  class this me;
  class ident,loc,compute,desc,oid,oloc,ocomp,ofact sib;
Loading

Resource inventory

Resource Count Notes
azurerm_synapse_integration_runtime_azure 1 The keystone this. No children β€” a runtime owns nothing.

βœ… Provider / Versions

Requirement Value
Terraform >= 1.12.0
hashicorp/azurerm ~> 4.0
Provider block None in this module β€” the caller configures the provider, its authentication, and the mandatory features {} block.

Schema notes that bite

  • πŸ”΄ The provider's name error message states a rule its regular expression does not enforce. The message says "no consecutive dashes"; the pattern permits them. The three-character minimum the same message claims is enforced. This module mirrors the pattern and reports the gap.
  • πŸ”΄ The self-hosted sibling carries the same message and a different pattern, wrong about the opposite half: it forbids consecutive dashes and accepts a one-character name.
  • core_count is a closed set β€” 8, 16, 32, 48, 80, 144, 272 β€” not a range. An intermediate value is rejected rather than rounded, and 80 to 144 is nearly double.
  • time_to_live_min is validated by nothing at all and defaults to 0, which means no cluster reuse.
  • compute_type is case-sensitive PascalCase: General, ComputeOptimized, MemoryOptimized.
  • location accepts the literal AutoResolve or a region, and the region half falls back to a plain non-empty check when the provider has not fetched Azure's location list β€” so the same value can pass in one run and fail in another. A region is normalised and diff-suppressed on the normalised form.
  • The three compute settings are sent inside the data-flow properties, so a copy-only pipeline uses this runtime without touching any of them.
  • Three force-new fields, all declared on the resource itself: name, synapse_workspace_id, location.
  • This resource does have an existence check on create β€” an apply over an existing runtime fails and tells you to import.
  • No tags. The location here is where the compute runs, not an ARM resource location.

πŸ”‘ Required Azure RBAC Roles / Permissions

Least-privilege, at the smallest scope that works:

  • Microsoft.Synapse/workspaces/integrationRuntimes/write and .../integrationRuntimes/delete on the target workspace. A custom role scoped to the workspace is enough; Contributor on the workspace, or on its resource group, covers this more broadly than necessary.
  • Microsoft.Synapse/workspaces/integrationRuntimes/read on the workspace, for the existence check before creating and for every refresh.

ℹ️ Plan access is not credential access on this resource. A managed runtime holds no key, and the provider marks nothing on it sensitive. That is not true of the self-hosted sibling, whose refresh reads two authorization keys β€” see example 11.


Azure Prerequisites

  • An existing Synapse workspace (the synapse-workspace module).
  • A region that can host the requested compute, if location is pinned rather than left at AutoResolve. Nothing here checks that a region can supply the requested core_count, and the failure arrives when an activity runs rather than at apply.
  • A pipeline or data flow that selects this runtime by name, if it is to do anything. The registration alone runs nothing and costs nothing.
  • The caller configures the provider "azurerm" { features {} } block, auth, and subscription.

πŸ“ Module Structure

terraform-azurerm-synapse-integration-runtime-azure/
β”œβ”€β”€ providers.tf    # required_version and the pinned azurerm; no provider block
β”œβ”€β”€ variables.tf    # identity, location, the three compute settings, description, timeouts
β”œβ”€β”€ main.tf         # the single keystone azurerm_synapse_integration_runtime_azure.this
β”œβ”€β”€ outputs.tf      # id first, then identity and compute, then the quiet facts
β”œβ”€β”€ README.md       # this file
β”œβ”€β”€ SCOPE.md        # the cross-module contract
β”œβ”€β”€ LICENSE         # MIT
└── .gitignore

βš™οΈ Quick Start

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

  name                 = "ir-dataflow-prod"
  synapse_workspace_id = var.synapse_workspace_id
  location             = "eastus2"
}

⚠️ The caller configures provider "azurerm" { features {} }, its authentication, and its subscription. This module declares no provider block.

❄️ This call gives you a cluster that is torn down after every activity, because time_to_live_min defaults to 0. That is the provider's default and it is rarely what you want. See example 4.


πŸ”Œ Cross-Module Contract

Consumes

Input Type Source
synapse_workspace_id string terraform-azurerm-synapse-workspace output id

Emits

Output Consumed by
id management locks, role assignments, review
name the pipeline activity that selects this runtime
cluster_is_torn_down_after_every_activity performance review
region_is_resolved_at_run_time residency review
name_uses_consecutive_dashes naming review

πŸ“š Example Library

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

  name                 = "ir-dataflow-prod"
  synapse_workspace_id = var.synapse_workspace_id
  location             = "eastus2"
}

πŸ’‘ The three compute settings are omitted, so the provider's defaults apply: General, 8 cores, and a time-to-live of 0.

2 Β· Letting Azure choose the region
module "dataflow_runtime" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-synapse-integration-runtime-azure.git?ref=v1.0.0"

  name                 = "ir-dataflow-auto"
  synapse_workspace_id = var.synapse_workspace_id

  location = "AutoResolve" # exact casing; see example 3
}

output "residency_decided_elsewhere" {
  value = module.dataflow_runtime.region_is_resolved_at_run_time
}

⚠️ AutoResolve moves a data-residency decision out of your Terraform. The compute region is chosen per run from the sink's location, so where the data is processed is decided at run time by the pipeline, not here. Pin a region where residency is a requirement rather than a preference.

3 Β· Why the casing of `AutoResolve` matters
# REJECTED at terraform plan, offline and without credentials:
#   location = "autoresolve"
#
#   Error: location must be spelled "AutoResolve" in exactly that casing.

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

  name                 = "ir-dataflow-auto"
  synapse_workspace_id = var.synapse_workspace_id

  location = "AutoResolve"
}

# ACCEPTED -- a region may be written any way you like:
#   location = "East US 2"   -> normalised to "eastus2" by the provider

ℹ️ The provider special-cases the exact string AutoResolve and hands anything else to its region check β€” which compares against a location list fetched from Azure and falls back to a plain non-empty check when that list has not been fetched. So autoresolve passes or fails depending on connectivity. A region name has no such problem.

4 Β· The default that makes data flows slow
module "dataflow_runtime" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-synapse-integration-runtime-azure.git?ref=v1.0.0"

  name                 = "ir-dataflow-prod"
  synapse_workspace_id = var.synapse_workspace_id
  location             = "eastus2"

  # Keep the cluster warm between activities. The provider's default is 0.
  time_to_live_min = 15
}

output "cold_start_every_time" {
  value = module.dataflow_runtime.cluster_is_torn_down_after_every_activity
}

❄️ time_to_live_min = 0 means no cluster reuse. Every data-flow activity pays a cold start measured in minutes rather than seconds, so a pipeline of many short data flows spends most of its wall-clock time starting clusters.

πŸ’° The trade is real in both directions: a warm cluster is billed for the whole time it stays warm. Pick the TTL from how closely your activities follow each other, not from a round number.

⚠️ The provider applies no validation at all to this field β€” no minimum, no maximum. This module rejects a negative value because none is meaningful, and deliberately does not invent an upper bound.

5 Β· `core_count` is a set, not a dial
# REJECTED at terraform plan, offline and without credentials:
#   core_count = 64
#
#   Error: core_count must be one of 8, 16, 32, 48, 80, 144 or 272.

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

  name                 = "ir-dataflow-prod"
  synapse_workspace_id = var.synapse_workspace_id
  location             = "eastus2"

  core_count       = 48 # 8, 16, 32, 48, 80, 144, 272 -- nothing between
  time_to_live_min = 15
}

⚠️ The steps get large: 48 to 80 is two thirds bigger, and 80 to 144 is nearly double. "A bit more compute" is not available β€” the next size up is the only option, and it is what the bill will reflect.

6 Β· Compute shape, and what it does not affect
module "dataflow_runtime" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-synapse-integration-runtime-azure.git?ref=v1.0.0"

  name                 = "ir-dataflow-prod"
  synapse_workspace_id = var.synapse_workspace_id
  location             = "eastus2"

  compute_type     = "MemoryOptimized" # PascalCase; "memoryoptimized" is rejected
  core_count       = 32
  time_to_live_min = 20
}

output "these_settings_are_data_flow_only" {
  value = module.dataflow_runtime.compute_settings_govern_data_flows_only
}

🚨 All three compute settings govern data flows only. They are sent inside the runtime's data-flow properties, so a pipeline that runs copy activities and no data flow uses this runtime without touching any of them. Tuning them to fix a slow copy changes nothing, and a cost investigation that starts here will find nothing to explain.

7 Β· The name rule the provider misdocuments
module "dataflow_runtime" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-synapse-integration-runtime-azure.git?ref=v1.0.0"

  # ACCEPTED here, even though the provider's own error message says
  # "no consecutive dashes" -- its regular expression permits them.
  name                 = "ir--dataflow"
  synapse_workspace_id = var.synapse_workspace_id
  location             = "eastus2"
}

output "relying_on_a_provider_gap" {
  value = module.dataflow_runtime.name_uses_consecutive_dashes
}

🚩 This module mirrors the provider's pattern, not its message. A rule the provider does not enforce may be one Azure does not enforce either, and a validation {} failure would block terraform destroy as well as apply β€” so the condition is reported rather than refused.

⚠️ A true here means you are relying on that gap. The self-hosted sibling would reject the same name. Prefer single dashes and the question does not arise.

8 Β· Two runtimes, two different name rules
# The AZURE runtime: minimum 3 characters ENFORCED, consecutive dashes PERMITTED.
#   "ir--dataflow"  accepted
#   "ab"            rejected

# The SELF-HOSTED runtime: consecutive dashes REJECTED, one-character name ACCEPTED.
#   "ir--onprem"    rejected
#   "i"             accepted

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

  name                 = "ir-dataflow-prod" # legal on both
  synapse_workspace_id = var.synapse_workspace_id
  location             = "eastus2"
}

output "do_not_assume_one_convention" {
  value = module.dataflow_runtime.the_two_runtime_types_do_not_share_a_name_rule
}

ℹ️ Both resources carry the same error message text and different regular expressions, and each message is wrong about the opposite half. A naming convention validated against one is not validated against the other. A name of at least three characters using only single dashes is legal on both, which is the convention worth adopting.

9 Β· Several runtimes for different workloads
locals {
  runtimes = {
    "ir-dataflow-heavy" = { compute_type = "MemoryOptimized", core_count = 80, ttl = 30 }
    "ir-dataflow-light" = { compute_type = "General", core_count = 8, ttl = 10 }
    "ir-copy-only"      = { compute_type = "General", core_count = 8, ttl = 0 }
  }
}

module "dataflow_runtimes" {
  source   = "git::https://github.com/microsoftexpert/terraform-azurerm-synapse-integration-runtime-azure.git?ref=v1.0.0"
  for_each = local.runtimes

  name                 = each.key
  synapse_workspace_id = var.synapse_workspace_id
  location             = "eastus2"

  compute_type     = each.value.compute_type
  core_count       = each.value.core_count
  time_to_live_min = each.value.ttl
}

output "which_runtimes_cold_start" {
  value = { for k, m in module.dataflow_runtimes : k => m.cluster_is_torn_down_after_every_activity }
}

πŸ’‘ The ir-copy-only entry leaves the TTL at 0 deliberately: a copy activity does not use the data-flow cluster, so keeping one warm for it would be paying for nothing.

⚠️ The map key becomes the name, which is force-new. Renaming a key destroys one runtime and creates another β€” and every pipeline that named the old one is now pointing at nothing, with no plan reporting it.

10 Β· Moving a runtime, and what forces replacement
output "what_replaces_this_runtime" {
  value = module.dataflow_runtime.force_new_fields
}
# All three of these are force-new:
#   name                 -- and pipelines name the runtime by this string
#   synapse_workspace_id -- a runtime belongs to one workspace
#   location             -- the compute region cannot be moved in place
#
# The compute settings and the description all update in place, which is what
# makes tuning safe and renaming not.

⚠️ Changing location from a pinned region to AutoResolve, or between regions, is a replacement rather than a reconfiguration.

11 Β· This is not the self-hosted runtime
# THIS module -> azurerm_synapse_integration_runtime_azure
#   Microsoft-hosted compute, with a region and a core count. No credentials.
module "dataflow_runtime" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-synapse-integration-runtime-azure.git?ref=v1.0.0"

  name                 = "ir-dataflow-prod"
  synapse_workspace_id = var.synapse_workspace_id
  location             = "eastus2"
}

# The SIBLING -> azurerm_synapse_integration_runtime_self_hosted
#   A REGISTRATION for compute you run yourself, to reach data Azure cannot.
#   No location, no core count -- and two authorization keys that the provider
#   reads on EVERY refresh and marks sensitive on NEITHER.
module "onprem_runtime" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-synapse-integration-runtime-self-hosted.git?ref=v1.0.0"

  name                 = "ir-onprem-sql"
  synapse_workspace_id = var.synapse_workspace_id
}

πŸ”’ The difference that matters operationally: plan access on the self-hosted runtime is credential access, and on this one it is not. Reach for this module for compute in Azure, and for the sibling only when the data is somewhere Azure cannot reach.

12 Β· Reviewing cost and residency across environments
output "runtime_review" {
  value = {
    workspace       = module.dataflow_runtime.synapse_workspace_name
    resource_group  = module.dataflow_runtime.resource_group_name
    region          = module.dataflow_runtime.location
    auto_region     = module.dataflow_runtime.region_is_resolved_at_run_time
    compute         = module.dataflow_runtime.compute_type
    cores           = module.dataflow_runtime.core_count
    warm_minutes    = module.dataflow_runtime.time_to_live_min
    cold_start_each = module.dataflow_runtime.cluster_is_torn_down_after_every_activity
  }
}

πŸ’‘ cores and warm_minutes together are the cost shape of a data-flow cluster: it is charged per core-hour while it is up. cold_start_each is the performance shape. The two pull in opposite directions, which is exactly why both are emitted rather than one being defaulted to an opinion.

13 Β· πŸ—οΈ End-to-end composition
module "data_rg" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-resource-group.git?ref=v1.0.0"

  name     = "rg-data-platform"
  location = "eastus2"

  tags = {
    environment = "prod"
    owner       = "data-platform-team"
  }
}

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

  name                                 = "syn-platform-prod"
  resource_group_name                  = module.data_rg.name
  location                             = module.data_rg.location
  storage_data_lake_gen2_filesystem_id = var.filesystem_id

  azuread_authentication_only   = true
  public_network_access_enabled = false

  tags = module.data_rg.tags
}

module "workspace_admin" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-synapse-workspace-aad-admin.git?ref=v1.0.0"

  synapse_workspace_id = module.synapse.id

  login     = "Synapse Platform Admins"
  object_id = var.platform_admins_group_object_id
  tenant_id = var.tenant_id
}

# Managed compute, pinned to the workspace's region and kept warm.
module "dataflow_runtime" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-synapse-integration-runtime-azure.git?ref=v1.0.0"

  name                 = "ir-dataflow-prod"
  synapse_workspace_id = module.synapse.id
  location             = module.data_rg.location

  compute_type     = "MemoryOptimized"
  core_count       = 32
  time_to_live_min = 20

  description = "Data-flow compute for the nightly warehouse load. Cost owner: data-platform-team."
}

module "warehouse" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-synapse-sql-pool.git?ref=v1.0.0"

  name                 = "dwmain"
  synapse_workspace_id = module.synapse.id
  sku_name             = "DW100c"

  tags = module.data_rg.tags
}

output "runtime_name_for_pipeline_definitions" {
  value = module.dataflow_runtime.name
}

output "runtime_keeps_a_warm_cluster" {
  value = !module.dataflow_runtime.cluster_is_torn_down_after_every_activity
}

πŸ’‘ The runtime takes module.data_rg.location, so the compute runs in the same region as the workspace and the warehouse rather than wherever a sink happens to be. That is a residency decision made here, on purpose, instead of at run time.

🚨 Nothing in this composition makes a pipeline use the runtime. An activity selects it by the string in runtime_name_for_pipeline_definitions, in a pipeline definition Terraform does not own β€” so wire that output through rather than retyping the name.


πŸ“₯ Inputs

Identity β€” name and synapse_workspace_id, both required and both force-new.

Placement β€” location, required and force-new. AutoResolve or a region.

Compute β€” compute_type, core_count and time_to_live_min, all optional, all in-place, and all data-flow only.

Universal tail β€” timeouts only. This resource has no tags.

Full input schema
Name Type Default Required Notes
name string β€” yes At least 3 chars, letters, numbers and dashes, first and last alphanumeric. Force-new. Consecutive dashes are permitted by the provider despite its message.
synapse_workspace_id string β€” yes Full Synapse workspace Resource ID, anchored at both ends. Force-new.
location string β€” yes "AutoResolve" (exact casing) or an Azure region in any casing. Force-new.
compute_type string null β†’ provider default "General" no "General", "ComputeOptimized" or "MemoryOptimized", PascalCase. Data flows only.
core_count number null β†’ provider default 8 no One of 8, 16, 32, 48, 80, 144, 272. A closed set, not a range. Data flows only.
time_to_live_min number null β†’ provider default 0 no Whole minutes, zero or greater. 0 means no cluster reuse. The provider validates this field with nothing.
description string null no Free text. Must be non-empty when supplied.
timeouts object({ create, read, update, delete }) null no Go duration strings. Provider defaults: create 30m, read 5m, update 30m, delete 30m.

⚠️ A key not declared in the timeouts object type is silently discarded by Terraform's type conversion rather than reported β€” a misspelled key produces no error and no timeout.


🧾 Outputs

Output Description Notes
id Resource ID of the runtime Emitted first
name The string a pipeline names to select this runtime Force-new
synapse_workspace_id The parent workspace, as Azure holds it Rebuilt from the record's own ID
synapse_workspace_name Workspace name, parsed from that ID Derived locally
resource_group_name Resource group, parsed from that ID Derived locally
location AutoResolve or a normalised region Force-new
region_is_resolved_at_run_time True when location is AutoResolve Residency review
compute_type Data-flow compute shape Data flows only
core_count Cores for the data-flow compute Cost review
time_to_live_min Warm-cluster minutes Cost review
cluster_is_torn_down_after_every_activity True when the TTL is 0 Performance review
description The description as Azure holds it null when unset
name_uses_consecutive_dashes True when the name relies on a provider gap Naming review
the_two_runtime_types_do_not_share_a_name_rule Constant true Naming review
compute_settings_govern_data_flows_only Constant true Read this one first
creating_this_starts_no_compute Constant true Design review
force_new_fields ["name", "synapse_workspace_id", "location"] Change planning
fields_azure_returns_on_read The fields in which drift is detectable at all Drift review
this_resource_supports_no_azure_resource_tags Constant true Tagging policy

πŸ”’ No secret is emitted, because this resource holds none.


🧠 Architecture Notes

The provider misdocuments its own name rule, and this module mirrors the rule rather than the documentation. The error message on name states that consecutive dashes are not allowed. Its regular expression permits them β€” that was checked against the pattern itself rather than taken on trust. This module reproduces the pattern character for character and reports the condition through name_uses_consecutive_dashes instead of adding the missing check, because a rule the provider does not enforce may be one Azure does not enforce either, and a validation {} failure blocks terraform destroy as well as apply. The same message on the self-hosted sibling is wrong about the opposite half, which is why neither runtime's name rule can be inferred from the other's.

The default is a cluster that never survives an activity. time_to_live_min defaults to 0, so a data flow starts a cluster, runs, and tears it down. For a pipeline of many short data flows, most of the wall-clock time is cluster startup. The fix is a single field β€” and the provider applies no validation to it at all, no minimum and no maximum, so nothing pushes back on a value of 0 or 6000. This module rejects a negative value because none is meaningful and deliberately does not invent a ceiling: the upper bound is a cost decision, and a warm cluster is billed for every minute it stays warm.

Two of the three compute settings look like general tuning and are not. compute_type, core_count and time_to_live_min are all sent inside the runtime's data-flow properties. A copy activity assigned to this runtime uses none of them. That makes a whole class of investigation dead-ended from the start: raising core_count to speed up a slow copy changes nothing, and a bill attributed to this runtime is a data-flow bill or nothing at all.

core_count is a set with large gaps. 8, 16, 32, 48, 80, 144, 272. There is no intermediate value to reach for, so the decision is always which step, and the steps near the top nearly double. The module mirrors the provider's set exactly rather than approximating it as a range.

AutoResolve is a residency decision, made elsewhere. It is a legitimate and convenient choice, and it means the compute region is selected per run from the sink's location rather than fixed by this configuration. Where residency is a requirement rather than a preference, pin the region β€” and region_is_resolved_at_run_time exists so that a review can tell the two apart without reading the value.

Creating this record starts nothing. It is a definition. The cluster starts when an activity runs, which makes an apply here fast and free β€” and also means an apply proves nothing about whether the compute can actually start. A region that cannot supply the requested core count fails at run time.


🧱 Design Principles

Concern Default in this module Opt-out
The name pattern Mirrors the provider's regex, not its message. The consecutive-dash rule the message claims is reported, never enforced β€”
compute_type / core_count Validated against the provider's own closed sets, with the sets named in the error messages β€”
time_to_live_min Negative rejected; no upper bound invented β€” the ceiling is a cost decision, not a published limit β€”
location casing Only the near miss is rejected: a differently-cased autoresolve, whose acceptance depends on connectivity β€”
Region set Never enumerated. The set changes, and a validation {} failure blocks terraform destroy as well as apply β€”
Provider defaults Left in place. Omitting a compute setting sends nothing, so an empty call behaves exactly as the provider documents set the field
Cost and performance Reported through derived outputs rather than defaulted to an opinion β€” the two pull in opposite directions β€”
tags Not offered β€” the resource has none. Tag the workspace β€”

πŸ”’ There is no secret on this resource and no secure-by-default toggle to set. The one governance-relevant choice is location, and the module's position is to report whether the residency decision was made here or deferred to run time, rather than to pick for you.


πŸš€ Runbook

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

Pin the module at a tag β€” ?ref=v1.0.0 β€” never a branch. Everything above is plan-only static analysis; a human applies from CI.


πŸ§ͺ Testing

What terraform validate covers, offline, with no credentials:

  • Every validation {} block in this module β€” the name pattern and its Resource-ID lookalike check, the anchored synapse_workspace_id shape, the three on location (non-empty, the AutoResolve near miss, and not a Resource ID), the compute_type enum, the core_count closed set, the two on time_to_live_min (non-negative and whole), the description non-empty check, and the timeouts duration shapes.
  • Type conversion of the timeouts object β€” with the caveat that undeclared keys are discarded silently rather than reported.

What only terraform plan reaches (credentials required):

  • That the workspace exists and that its ID parses.
  • That the caller's identity holds Microsoft.Synapse/workspaces/integrationRuntimes/write on it.
  • That no runtime of this name already exists β€” this resource does have an existence check.
  • The provider's own region check for location, and only when it has fetched Azure's location list.

What neither reaches, at any stage:

  • Whether the chosen region can supply the requested core_count. That fails when an activity runs.
  • Whether any pipeline or data flow selects this runtime. The reference is a string Terraform cannot see.
  • Whether the time-to-live is right for the workload. That is a wall-clock and cost measurement, not a check.

πŸ’¬ Example Output

Outputs:

id                                        = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-data-platform/providers/Microsoft.Synapse/workspaces/syn-platform-prod/integrationRuntimes/ir-dataflow-prod"
name                                      = "ir-dataflow-prod"
synapse_workspace_id                      = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-data-platform/providers/Microsoft.Synapse/workspaces/syn-platform-prod"
synapse_workspace_name                    = "syn-platform-prod"
resource_group_name                       = "rg-data-platform"
location                                  = "eastus2"
region_is_resolved_at_run_time            = false
compute_type                              = "MemoryOptimized"
core_count                                = 32
time_to_live_min                          = 20
cluster_is_torn_down_after_every_activity = false
name_uses_consecutive_dashes              = false
force_new_fields                          = [
  "name",
  "synapse_workspace_id",
  "location",
]

πŸ” Troubleshooting

Symptom Cause Fix
Every data flow takes minutes to start time_to_live_min is 0, the provider's default, so the cluster is torn down after each activity Set a time-to-live. See cluster_is_torn_down_after_every_activity
A warm cluster is costing more than expected It is billed per core-hour for the whole time it stays warm Lower time_to_live_min or core_count. The two are the whole cost shape
Raising core_count did not speed up a copy activity The compute settings govern data flows only Expected. See compute_settings_govern_data_flows_only
core_count must be one of 8, 16, 32, 48, 80, 144 or 272 An intermediate value such as 64 was supplied. It is a closed set, not a range Pick the next step up
compute_type must be exactly "General", "ComputeOptimized" or "MemoryOptimized" A lower-case spelling was used; the provider compares case-sensitively Use PascalCase
location must be spelled "AutoResolve" in exactly that casing autoresolve was passed, whose acceptance by the provider depends on connectivity Use AutoResolve, or a region name in any casing
A name with consecutive dashes was accepted here and rejected on the self-hosted runtime The two resources have different regular expressions and the same error message Use at least 3 characters and single dashes β€” legal on both. See the_two_runtime_types_do_not_share_a_name_rule
Renaming the runtime broke pipelines, with no plan warning name is force-new, and pipelines name a runtime as a string Terraform cannot see Wire the name output into pipeline definitions rather than retyping it
An activity fails at run time with a capacity error Nothing checks that the chosen region can supply the requested core count Pin a different region, or lower core_count
A resource with the ID … already exists This resource has a real existence check, and a runtime of that name is already there terraform import it rather than creating
Error: Missing required argument: features The caller has no provider "azurerm" { features {} } block Add one to the root module. This module declares no provider block

πŸ”— Related Docs

  • azurerm_synapse_integration_runtime_azure β€” the provider resource this module wraps.
  • azurerm_synapse_integration_runtime_self_hosted β€” the self-hosted runtime, a different resource.
  • Integration runtime in Azure Data Factory and Synapse Analytics β€” Microsoft's platform documentation for the runtime types and what each one is for.
  • Data flow performance and tuning β€” Microsoft's guidance on compute size and time-to-live.
  • terraform-azurerm-synapse-workspace β€” the parent workspace.
  • terraform-azurerm-synapse-integration-runtime-self-hosted β€” the self-hosted runtime.
  • terraform-azurerm-synapse-linked-service β€” names this runtime so a connection is made through it rather than through Synapse's auto-resolve runtime.
  • terraform-azurerm-synapse-sql-pool and terraform-azurerm-synapse-spark-pool β€” the other workloads in the workspace.
  • This module's SCOPE.md β€” the cross-module contract.

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