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.
This module manages one Azure Batch application registration:
- π¦ Creates a single
azurerm_batch_applicationinside an existing Batch account. - π·οΈ Names the application uniquely within its account and optionally sets a friendly
display_name. - π’ Optionally pins a
default_versionreturned when a client requests the application without naming a version. - π Exposes
allow_updatesso a caller can make published package versions immutable for release integrity. - π§Ύ Emits the application's resource
idandnamefor 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.
If this module saves you time, a little support goes a long way:
- β Star the repository on GitHub.
- πΌ Connect on LinkedIn: linkedin.com/in/microsoftexpert
- β Buy me a coffee: buymeacoffee.com/microsoftexpert
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;
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;
Resource inventory
| Resource | Role | Cardinality |
|---|---|---|
azurerm_batch_application.this |
Keystone β the application registration in the Batch account | single |
| 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, andaccount_nameare force-new β changing any of them destroys and recreates the application. - π’
default_versionmust name a package version that already exists under the application; setting it before that package is uploaded will fail. - π
allow_updatesdefaults totrueat the provider (packages may be overwritten under the same version string). Leaving the module inputnullpreserves that default; set it tofalsefor immutable published versions. - π·οΈ This resource does not support
tags; the module's universal tail istimeoutsonly.
Least-privilege, at the Batch account scope:
Microsoft.Batch/batchAccounts/applications/writeMicrosoft.Batch/batchAccounts/applications/readMicrosoft.Batch/batchAccounts/applications/delete
The built-in Contributor role scoped to the Batch account (or its resource group) covers all three.
- An existing Batch account (this module references it by
account_name; it never creates it). - The
Microsoft.Batchresource provider registered on the subscription.
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
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 theazurermprovider β the provider will not initialize without it. Library modules never declare it.
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 |
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 = trueand 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_nameis 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_versionmust name a package version that already exists under the application. Uploading the1.4.0package 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 = trueexplicitly 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
}
β οΈ Eachdefault_versionmust 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 nodepends_onneeded. Remember the referenced package versions must be uploaded out of band before the pinneddefault_versionvalues will apply.
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.
}| 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 |
- Force-new identity.
name,resource_group_name, andaccount_nameare 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_versionordering. The application registration and its packages have separate lifecycles. Adefault_versioncan 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 setdefault_versionβ otherwise the apply fails referencing a version that is not yet present.allow_updatesposture. Leftnull, the module passes through the provider default (true, overwrites permitted). Setting it tofalsemakes 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 noprovider {}block. The caller's root module ownsprovider "azurerm" { features {} }; without it the provider will not initialize. This is expected and not a module defect.- No tags.
azurerm_batch_applicationdoes not supporttags, so the module's universal tail istimeoutsonly.
| 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_updatesat 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.
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 -checkThe 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.
Outputs:
id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-batch-prod/providers/Microsoft.Batch/batchAccounts/batchprodeastus/applications/invoice-renderer"
name = "invoice-renderer"
| 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. |
- Terraform Registry β
azurerm_batch_application - Terraform Registry β
azurerm_batch_account - Microsoft Learn β Azure Batch application packages
- Sibling modules β
terraform-azurerm-batch-account,terraform-azurerm-resource-group - This module's
SCOPE.mdβ the cross-module contract
π "Infrastructure as Code should be standardized, consistent, and secure."