Skip to content

Latest commit

Β 

History

1 Commit

Folders and files

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

Repository files navigation

☁️ Azure Batch Application Terraform Module

Registers a single application within an existing Azure Batch account β€” the naming and versioning envelope under which application packages are published for pools and tasks to consume. Targets hashicorp/azurerm ~> 4.0.


Terraform azurerm Module Version Type Resources


🧩 Overview

This module manages one Azure Batch application registration:

  • πŸ“¦ Creates a single azurerm_batch_application inside an existing Batch account.
  • 🏷️ Names the application uniquely within its account and optionally sets a friendly display_name.
  • πŸ”’ Optionally pins a default_version returned when a client requests the application without naming a version.
  • πŸ”’ Exposes allow_updates so a caller can make published package versions immutable for release integrity.
  • 🧾 Emits the application's resource id and name for downstream pool and task references.

πŸ’‘ Why it matters: In Azure Batch, an application is the versioning container; the packages that live under it (the actual zipped payloads a compute node unpacks) are uploaded out of band. Getting the registration, its default version, and its update posture right up front keeps package promotion predictable and auditable β€” without ever coupling package binaries into Terraform state.


❀️ Support this project

If this module saves you time, a little support goes a long way:


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

This module is a leaf: it consumes a Batch account by name and produces one application registration. The packages themselves are provisioned outside Terraform.

flowchart LR
  acct["terraform-azurerm-batch-account"]
  me["terraform-azurerm-batch-application"]
  v["azurerm_batch_application"]
  pkg["application packages (out of band)"]
  acct -->|"account_name"| me
  me -->|"creates"| v
  v -.->|"holds versioned"| pkg
  classDef me fill:#0078D4,stroke:#004578,color:#ffffff;
  classDef target fill:#004578,stroke:#002d4d,color:#ffffff;
  classDef ext fill:#f2f2f2,stroke:#c8c8c8,color:#111111;
  class me me;
  class v target;
  class acct,pkg ext;
Loading

🧬 What this module builds

A thin renderer around one keystone resource: identity and configuration inputs flow in, the id and name flow out.

flowchart LR
  in_id["name / resource_group_name / account_name"]
  in_cfg["allow_updates / default_version / display_name"]
  res["azurerm_batch_application.this"]
  out_id["id"]
  out_name["name"]
  in_id -->|"input"| res
  in_cfg -->|"input"| res
  res -->|"output"| out_id
  res -->|"output"| out_name
  classDef me fill:#0078D4,stroke:#004578,color:#ffffff;
  class res me;
Loading

Resource inventory

Resource Role Cardinality
azurerm_batch_application.this Keystone β€” the application registration in the Batch account single

βœ… Provider / Versions

Requirement Value
Terraform >= 1.12.0
hashicorp/azurerm ~> 4.0
Provider block None inside this module β€” the caller configures provider "azurerm", including the mandatory features {} block, plus auth, subscription, and tenant.

Schema notes that bite

  • πŸ” name, resource_group_name, and account_name are force-new β€” changing any of them destroys and recreates the application.
  • πŸ”’ default_version must name a package version that already exists under the application; setting it before that package is uploaded will fail.
  • πŸ”“ allow_updates defaults to true at the provider (packages may be overwritten under the same version string). Leaving the module input null preserves that default; set it to false for immutable published versions.
  • 🏷️ This resource does not support tags; the module's universal tail is timeouts only.

πŸ”‘ Required Azure RBAC Roles / Permissions

Least-privilege, at the Batch account scope:

  • Microsoft.Batch/batchAccounts/applications/write
  • Microsoft.Batch/batchAccounts/applications/read
  • Microsoft.Batch/batchAccounts/applications/delete

The built-in Contributor role scoped to the Batch account (or its resource group) covers all three.


🧰 Azure Prerequisites

  • An existing Batch account (this module references it by account_name; it never creates it).
  • The Microsoft.Batch resource provider registered on the subscription.

πŸ“ Module Structure

terraform-azurerm-batch-application/
β”œβ”€β”€ providers.tf   # required_version + azurerm ~> 4.0 pin; no provider block
β”œβ”€β”€ variables.tf   # typed inputs: identity, config, and the timeouts tail
β”œβ”€β”€ main.tf        # keystone azurerm_batch_application.this + dynamic timeouts
β”œβ”€β”€ outputs.tf     # id (first), then name
β”œβ”€β”€ README.md      # this document
β”œβ”€β”€ SCOPE.md       # cross-module contract
β”œβ”€β”€ LICENSE        # MIT
└── .gitignore     # canonical library ignore set

βš™οΈ Quick Start

The smallest real call β€” reference an existing Batch account and name the application:

# The caller configures the provider, auth, and the mandatory features {} block.
provider "azurerm" {
  features {}
}

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

  name                = "invoice-renderer"
  resource_group_name = "rg-batch-prod"
  account_name        = "batchprodeastus"
}

ℹ️ features {} is required on the azurerm provider β€” the provider will not initialize without it. Library modules never declare it.


πŸ”Œ Cross-Module Contract

Consumes

Input Type Source module
resource_group_name string terraform-azurerm-resource-group
account_name string terraform-azurerm-batch-account

Emits

Output Description Consumed by
id Resource ID of the Batch application references
name Application name pool / task application references

πŸ“š Example Library

1 Β· Minimal β€” reference an existing account
module "batch_application" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-batch-application.git?ref=v1.0.0"

  name                = "invoice-renderer"
  resource_group_name = "rg-batch-prod"
  account_name        = "batchprodeastus"
}

πŸ’‘ The empty-of-options call inherits the provider default allow_updates = true and sets no default version.

2 Β· With a display name
module "batch_application" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-batch-application.git?ref=v1.0.0"

  name                = "invoice-renderer"
  resource_group_name = "rg-batch-prod"
  account_name        = "batchprodeastus"
  display_name        = "Invoice Rendering Engine"
}

ℹ️ display_name is cosmetic β€” it does not have to be unique and does not affect package references.

3 Β· Immutable published packages
module "batch_application" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-batch-application.git?ref=v1.0.0"

  name                = "invoice-renderer"
  resource_group_name = "rg-batch-prod"
  account_name        = "batchprodeastus"
  allow_updates       = false
}

πŸ”’ With allow_updates = false, a package version string can be published once and never overwritten β€” the more conservative choice for release integrity. Re-publishing the same version fails until the version is deleted.

4 Β· Pin a default version
module "batch_application" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-batch-application.git?ref=v1.0.0"

  name                = "invoice-renderer"
  resource_group_name = "rg-batch-prod"
  account_name        = "batchprodeastus"
  default_version     = "1.4.0"
}

⚠️ default_version must name a package version that already exists under the application. Uploading the 1.4.0 package is an out-of-band step that must complete before this applies cleanly.

5 Β· Explicitly permissive updates
module "batch_application" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-batch-application.git?ref=v1.0.0"

  name                = "dev-sandbox-app"
  resource_group_name = "rg-batch-dev"
  account_name        = "batchdeveastus"
  allow_updates       = true
}

πŸ’‘ Setting allow_updates = true explicitly documents intent in a dev account where iterating on the same version string is convenient. This matches the provider default, so it changes nothing operationally.

6 Β· Display name plus default version
module "batch_application" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-batch-application.git?ref=v1.0.0"

  name                = "media-transcoder"
  resource_group_name = "rg-batch-prod"
  account_name        = "batchprodeastus"
  display_name        = "Media Transcoding Pipeline"
  default_version     = "2.0.1"
}

ℹ️ Common production shape: a friendly name for the portal plus a pinned default so tasks that omit a version resolve deterministically.

7 Β· Immutable packages with a pinned default
module "batch_application" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-batch-application.git?ref=v1.0.0"

  name                = "risk-engine"
  resource_group_name = "rg-batch-prod"
  account_name        = "batchprodeastus"
  allow_updates       = false
  default_version     = "3.2.0"
}

πŸ”’ The combination locked-versions caller wants for regulated workloads: no overwrite of a published version, and a deterministic default resolution.

8 Β· Per-operation timeouts
module "batch_application" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-batch-application.git?ref=v1.0.0"

  name                = "invoice-renderer"
  resource_group_name = "rg-batch-prod"
  account_name        = "batchprodeastus"

  timeouts = {
    create = "30m"
    delete = "30m"
  }
}

ℹ️ Timeout values are Go duration strings (e.g. "30m"). Omit any operation to keep the provider default.

9 Β· Consuming account name from a sibling module
module "batch_application" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-batch-application.git?ref=v1.0.0"

  name                = "invoice-renderer"
  resource_group_name = module.resource_group.name
  account_name        = module.batch_account.name
}

πŸ’‘ Wire the account and resource group by reference so a rename in the upstream module flows through without hand-edits here.

10 Β· A named local for reuse
locals {
  batch_account = {
    resource_group_name = "rg-batch-prod"
    account_name        = "batchprodeastus"
  }
}

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

  name                = "invoice-renderer"
  resource_group_name = local.batch_account.resource_group_name
  account_name        = local.batch_account.account_name
}

ℹ️ Handy when several applications share one account β€” the account coordinates live in one place.

11 Β· Several applications with for_each
locals {
  applications = {
    renderer   = { display_name = "Invoice Rendering Engine",   default_version = "1.4.0" }
    transcoder = { display_name = "Media Transcoding Pipeline", default_version = "2.0.1" }
    risk       = { display_name = "Risk Engine",                default_version = "3.2.0" }
  }
}

module "batch_application" {
  source   = "git::https://github.com/microsoftexpert/terraform-azurerm-batch-application.git?ref=v1.0.0"
  for_each = local.applications

  name                = each.key
  resource_group_name = "rg-batch-prod"
  account_name        = "batchprodeastus"
  display_name        = each.value.display_name
  default_version     = each.value.default_version
  allow_updates       = false
}

πŸ’‘ A stable string key per application means adding or removing one never re-indexes the others.

12 Β· A version map into for_each
variable "app_versions" {
  type = map(string)
  default = {
    renderer   = "1.4.0"
    transcoder = "2.0.1"
  }
}

module "batch_application" {
  source   = "git::https://github.com/microsoftexpert/terraform-azurerm-batch-application.git?ref=v1.0.0"
  for_each = var.app_versions

  name                = each.key
  resource_group_name = "rg-batch-prod"
  account_name        = "batchprodeastus"
  default_version     = each.value
}

⚠️ Each default_version must correspond to a package already uploaded for that application, or the apply fails on that instance.

13 Β· Collecting the emitted IDs
module "batch_application" {
  source   = "git::https://github.com/microsoftexpert/terraform-azurerm-batch-application.git?ref=v1.0.0"
  for_each = toset(["renderer", "transcoder", "risk"])

  name                = each.value
  resource_group_name = "rg-batch-prod"
  account_name        = "batchprodeastus"
}

output "application_ids" {
  value = { for k, m in module.batch_application : k => m.id }
}

ℹ️ A keyed map of application IDs is convenient for wiring pool package references or diagnostics downstream.

14 Β· πŸ—οΈ End-to-end composition

Wire a resource group and a Batch account into this module so the application is created against real upstream outputs.

provider "azurerm" {
  features {}
}

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

  name     = "rg-batch-prod"
  location = "eastus"
}

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

  name                = "batchprodeastus"
  resource_group_name = module.resource_group.name
  location            = module.resource_group.location
}

module "batch_application" {
  source   = "git::https://github.com/microsoftexpert/terraform-azurerm-batch-application.git?ref=v1.0.0"
  for_each = {
    renderer   = { display_name = "Invoice Rendering Engine",   default_version = "1.4.0" }
    transcoder = { display_name = "Media Transcoding Pipeline", default_version = "2.0.1" }
  }

  name                = each.key
  resource_group_name = module.resource_group.name
  account_name        = module.batch_account.name
  display_name        = each.value.display_name
  default_version     = each.value.default_version
  allow_updates       = false
}

output "batch_application_ids" {
  value = { for k, m in module.batch_application : k => m.id }
}

πŸ’‘ The implicit references (module.batch_account.name) express the ordering β€” the account is created before its applications, with no depends_on needed. Remember the referenced package versions must be uploaded out of band before the pinned default_version values will apply.


πŸ“₯ Inputs

Required

Name Type Description
name string Application name, unique within the Batch account. Force-new.
resource_group_name string Resource group containing the Batch account. Force-new.
account_name string Name of the Batch account. Force-new.

Optional

Name Type Default Description
allow_updates bool null (provider default true) Whether packages may be overwritten under the same version string. Set false for immutable versions.
default_version string null Package version returned when a client omits a version. Must name an existing package.
display_name string null Optional friendly display name.
timeouts object(...) null Optional per-operation timeouts.
Full input schemas
variable "name" {
  type = string
  # Name of the application, unique within the Batch account.
  # Immutable β€” changing it forces a new resource.
}

variable "resource_group_name" {
  type = string
  # Name of the resource group containing the Batch account.
  # Immutable β€” changing it forces a new resource.
}

variable "account_name" {
  type = string
  # Name of the Batch account. Immutable β€” changing it forces a new resource.
}

variable "allow_updates" {
  type    = bool
  default = null
  # Whether packages within the application may be overwritten using the same
  # version string. Leave null for the provider default (true). Set to false to
  # make published package versions immutable β€” the more conservative choice for
  # release integrity.
}

variable "default_version" {
  type    = string
  default = null
  # Optional package version returned when a client requests the application
  # without specifying a version. Must name an existing package.
}

variable "display_name" {
  type    = string
  default = null
  # Optional display name for the application.
}

variable "timeouts" {
  type = object({
    create = optional(string)
    read   = optional(string)
    update = optional(string)
    delete = optional(string)
  })
  default = null
  # Optional per-operation timeouts (Go duration strings such as "30m").
  # Leave null for provider defaults.
}

🧾 Outputs

Output Description Kind
id Resource ID of the Batch application Passthrough
name Name of the application, as created Passthrough
account_name Name of the parent Batch account, read back from the created resource's ID rather than echoed from configuration Passthrough
resource_group_name Name of the resource group containing the parent Batch account Passthrough
batch_account_id Resource ID of the parent Batch account, parsed out of this resource's own ID Derived
display_name Friendly name of the application as stored, or null if none was set Passthrough
has_display_name Whether a friendly name was supplied Derived
default_version Package version served when this application is requested without a version, or null if none is set Passthrough
has_default_version Whether a default version is set Derived
package_versions_can_be_overwritten Whether an existing package version inside this application may be replaced with different content Passthrough
existing_packages_can_be_deleted Whether packages already uploaded to this application may be removed Passthrough
new_package_versions_can_always_be_added Constant true, and it is the half of the allow_updates story that gets forgotten Constant
this_provider_cannot_create_application_packages Constant true, and it is the fact that decides how this module is used Constant
default_version_must_name_an_existing_package Constant true, quoting the provider's and the management API's own wording: the property can only be set to the name of an existing package Constant
default_version_requires_a_second_apply_on_a_new_application Constant true, and it follows from the two facts above rather than from anything visible in a plan Constant
deleting_the_default_package_clears_this_setting Constant true, and it produces drift with no error attached Constant
name_is_portable_to_linux_pools Whether the application name uses only letters, digits and underscores, which is what Microsoft documents as permitted for an application ID on Linux nodes -- Windows nodes additionally allow hyphens Derived
name_is_case_insensitive_within_the_account Constant true Constant
create_fails_if_the_application_already_exists Constant true Constant
name_and_account_are_force_new Constant true Constant
packages_are_stored_in_the_accounts_linked_storage Constant true, and it is where the security review actually belongs Constant
is_scoped_to_one_batch_account Constant true Constant
emits_no_secrets Constant true Constant
resource_supports_no_tags Constant true Constant

🧠 Architecture Notes

  • Force-new identity. name, resource_group_name, and account_name are immutable. A change to any of them is a destroy-and-recreate, which discards the application registration (packages under it are a separate concern). Treat these as the stable identity of the record.
  • default_version ordering. The application registration and its packages have separate lifecycles. A default_version can only point at a package that already exists, and packages are uploaded out of band. If you manage both in one pipeline, upload the package first, then set default_version β€” otherwise the apply fails referencing a version that is not yet present.
  • allow_updates posture. Left null, the module passes through the provider default (true, overwrites permitted). Setting it to false makes each published version write-once β€” the recommended posture for production release integrity, at the cost of having to delete a version before republishing it.
  • features {} dependence. Like every module in this suite, this module declares no provider {} block. The caller's root module owns provider "azurerm" { features {} }; without it the provider will not initialize. This is expected and not a module defect.
  • No tags. azurerm_batch_application does not support tags, so the module's universal tail is timeouts only.

🧱 Design Principles

Concern Default (empty call) Opt-out / opt-in (caller types it)
Package overwrite (allow_updates) null β†’ provider default true; the conservative false (immutable versions) is documented for release-integrity workloads set allow_updates = false for write-once versions
Default version resolution none pinned set default_version to a package that already exists
Tagging not applicable β€” the resource does not support tags β€”

The module keeps allow_updates at the provider default rather than forcing a value, so behavior is predictable for callers who expect stock Batch semantics β€” while documenting the immutable-package posture so a caller can opt into it deliberately.


πŸš€ Runbook

Plan-only / static analysis β€” a human applies from CI. Always pin ?ref=v1.0.0, never a branch.

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

πŸ§ͺ Testing

The offline proof gate that runs without any Azure credentials or apply:

Check What it proves
terraform init -backend=false Providers resolve and the ~> 4.0 pin is satisfiable.
terraform validate The configuration is internally consistent and every argument matches the live schema.
terraform fmt -check Canonical formatting.

What only a real terraform plan against a live account exercises: that account_name resolves to an existing Batch account, and that any default_version names a package that has actually been uploaded. Those are apply-time facts, not static ones.


πŸ’¬ Example Output

Outputs:

id   = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-batch-prod/providers/Microsoft.Batch/batchAccounts/batchprodeastus/applications/invoice-renderer"
name = "invoice-renderer"

πŸ” Troubleshooting

Symptom Cause Fix
Apply fails: default version does not exist default_version names a package version that has not been uploaded to the application. Upload the package version out of band first, then apply with default_version set β€” or leave default_version null until the package exists.
Apply fails: application name already in use Another application with the same name already exists in the Batch account. Choose a unique name within the account, or import the existing application into state.
Republishing a version is rejected allow_updates = false makes published versions write-once. Delete the existing version out of band before republishing, or set allow_updates = true if overwrites are acceptable.
Every change wants to recreate the resource You changed name, resource_group_name, or account_name β€” all force-new. Keep the identity triple stable; use a new module instance for a genuinely new application.
Provider fails to initialize The caller's root module is missing provider "azurerm" { features {} }. Add the features {} block to the root provider configuration.

πŸ”— Related Docs


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