Skip to main content
  1. Blog/

Terraform 0.13 — Module-Level For Each and the Provider Story

·961 words·5 mins
Osmond van Hemert
Author
Osmond van Hemert
Cloud Operations - This article is part of a series.
Part : This Article

HashiCorp released the first beta of Terraform 0.13 this week, and the headline feature has been on the community’s wish list for years: count and for_each support at the module level. If you’ve been writing Terraform professionally, you know exactly why this matters. If you haven’t, let me explain why infrastructure teams are quietly celebrating.

The Module Iteration Problem
#

Since Terraform introduced modules — reusable packages of infrastructure configuration — there’s been one glaring limitation. You could use count and for_each to create multiple instances of a resource, but not of a module. This meant that if you had a well-crafted module for, say, a standard web application stack (load balancer, compute instances, database, DNS records), and you wanted to deploy three instances of it for three different services, you had to write:

module "app_service_a" {
  source = "./modules/web-app"
  name   = "service-a"
  # ... all the variables
}

module "app_service_b" {
  source = "./modules/web-app"
  name   = "service-b"
  # ... all the same variables, copied
}

module "app_service_c" {
  source = "./modules/web-app"
  name   = "service-c"
  # ... and again
}

This wasn’t just ugly — it was a maintenance nightmare. Every time the module’s interface changed, you had to update every copy. In large organizations with dozens or hundreds of similar deployments, this led to either massive code duplication or elaborate workarounds using terragrunt or code generation scripts.

Terraform 0.13 fixes this cleanly:

module "app" {
  source   = "./modules/web-app"
  for_each = var.services

  name     = each.key
  config   = each.value
}

Three lines instead of thirty. And more importantly, adding a new service is a data change, not a code change. You add an entry to the services variable, and Terraform creates the entire stack.

Automatic Provider Installation
#

The second major change is how Terraform handles providers. In 0.12 and earlier, terraform init downloads providers from a monolithic namespace. All providers lived under hashicorp/ in the Terraform Registry, even community-maintained ones. This created confusion about ownership and support levels.

Terraform 0.13 introduces a new provider source syntax that uses a full namespace:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.0"
    }
    datadog = {
      source  = "DataDog/datadog"
      version = "~> 2.12"
    }
  }
}

This is more than cosmetic. It enables a truly decentralized provider ecosystem where anyone can publish and maintain providers under their own namespace. Vendors can own their Terraform providers directly, publish updates on their own schedule, and maintain them without going through HashiCorp’s release process.

For teams that build internal providers — and I know several that do — this is significant. You can now host providers in a private registry and reference them with clear namespacing. No more confusion about whether terraform-provider-custom-thing is the official version or someone’s fork.

Custom Validation Rules
#

A smaller but useful addition is custom validation rules for variables:

variable "instance_type" {
  type = string
  validation {
    condition     = can(regex("^t3\\.", var.instance_type))
    error_message = "Instance type must be from the t3 family for cost control."
  }
}

This moves validation left — catching configuration errors at terraform plan time rather than at apply time (or worse, in production). I’ve seen teams build elaborate CI checks and wrapper scripts to validate Terraform variables. Having it built into the language is cleaner and more discoverable.

The validation rules support arbitrary conditions using Terraform’s expression language, so you can enforce naming conventions, restrict resource sizes, validate CIDR ranges, or any other policy that can be expressed as a boolean condition.

depends_on for Modules
#

Another long-requested feature: modules now support depends_on. In complex infrastructures, you sometimes need to ensure that one module completes before another starts, even when there’s no direct data dependency between them. A common example is a networking module that creates a VPC and a monitoring module that needs the VPC’s NAT gateway to be functional before it can reach external endpoints.

Previously, you’d create artificial data dependencies — passing an output from module A to a variable in module B that module B didn’t actually use, just to force ordering. It worked, but it was confusing for anyone reading the code later.

Now you can express intent directly:

module "monitoring" {
  source     = "./modules/monitoring"
  depends_on = [module.networking]
}

The Upgrade Path
#

Terraform 0.13 includes an 0.13upgrade command that automatically rewrites your configuration files to use the new provider source syntax. In my testing, it handles straightforward configurations well. Complex setups with multiple provider aliases or unusual provider inheritance patterns may need manual attention.

The state format is also changing, which means you’ll need to run terraform state replace-provider for any existing state files. HashiCorp provides tooling for this, but it’s worth testing thoroughly in a non-production environment first. I’ve been bitten by state migration issues in previous Terraform upgrades, and a corrupted state file at 3 AM is nobody’s idea of a good time.

My Take
#

Terraform 0.13 addresses the most common complaints I hear from teams using Terraform at scale. Module iteration alone will eliminate thousands of lines of duplicated configuration across the industry. The provider namespace change sets up the ecosystem for sustainable growth. Custom validation rules reduce the need for external policy tools (though they don’t replace them — tools like Open Policy Agent still have a role for cross-resource and cross-module policies).

If you’re on Terraform 0.12, I’d recommend starting your upgrade planning now. Test with the beta, identify any breaking changes in your workflows, and plan a migration window. The features are worth the effort, and the longer you wait, the more state drift you’ll accumulate between your 0.12 and 0.13 configurations.

Infrastructure as code keeps getting better. The gap between “configuration management” and “real programming language” continues to narrow, and that’s good for everyone who manages cloud infrastructure.

Cloud Operations - This article is part of a series.
Part : This Article