How to Fix 'Provider Configuration Not Set for AWS in Module' in Terraform
Threat/Impact Level: HIGH | Exploitability/Downtime Risk: HIGH | Time to Fix: 10 mins
TL;DR
- What broke: A child module is consuming an
awsprovider that was never explicitly configured or passed from the root module — Terraform's provider inheritance does not work implicitly for aliased or multi-region providers. - How to fix it: Declare the provider in the root module and pass it to the child module via the
providersmeta-argument inside themoduleblock. - Quick path: Use our Client-Side Sandbox above to auto-refactor this — paste your broken module call and get corrected HCL instantly without leaking your account IDs or ARNs.
▼ 🔍 Root Cause Breakdown & Kernel Diagnostics [Click to Expand Deep Dive]
The Incident (What Does the Error Mean?)
You hit this during terraform init, plan, or apply:
Error: Provider configuration not set for 'aws' in module
on main.tf line 12, in module "vpc":
12: module "vpc" {
The child module requires a provider named "aws" but no configuration
was passed for this provider. Either configure a provider with that
name in the module, or pass an existing provider configuration to
the module using the `providers` argument.Immediate consequence: Terraform hard-stops. No plan is generated. No infrastructure is touched. Your pipeline is dead at the gate — but the real danger is what engineers do next: they start copy-pasting provider blocks inside child modules, which breaks reusability and creates drift.
This error is most common in three scenarios:
- Multi-region deployments — you've declared
provider "aws" { alias = "us-west-2" }in root but forgot to wire it to the module. - Refactored monorepos — a module was extracted from root and the provider wiring was never added back.
- Third-party modules — a registry module expects you to pass a specific provider alias it cannot self-configure.
▼ 🔍 Detailed Impact & Blast Radius Analysis [Click to Expand Deep Dive]
The Attack Vector / Blast Radius
This isn't just a broken build — misunderstanding this error leads to architectural debt that compounds fast:
Blast Radius if misconfigured (not just unfixed):
- Hardcoded provider blocks inside child modules destroy reusability. Every environment (dev/staging/prod) now requires forking the module instead of parameterizing the provider. You've just created three diverging codebases.
- Implicit default provider reliance means if the default
awsprovider region is wrong (e.g.,us-east-1when you needeu-central-1), resources silently deploy to the wrong region. IAM policies, VPC peering, and KMS keys become region-mismatched. This is a data residency compliance violation in regulated environments (GDPR, FedRAMP). - In CI/CD pipelines, this error causes
terraform planto exit non-zero, blocking PRs — but if someone works around it by embedding credentials directly in the child module to satisfy the provider, you now have credential sprawl baked into module source code. - Multi-account architectures (AWS Organizations, Control Tower) are entirely broken by this pattern. The
assume_roleARN in each account-specific provider never gets passed down, so every module call defaults to the pipeline's base role — completely undermining account isolation.
How to Fix It (The Solution)
Basic Fix — Pass the Default Provider
If you're using a single, non-aliased AWS provider, the child module should inherit it automatically unless the module's own required_providers block declares it explicitly. The fix is to ensure the root module's provider is wired:
# root/main.tf
provider "aws" {
region = "us-east-1"
}
module "vpc" {
source = "./modules/vpc"
+ providers = {
+ aws = aws
+ }
}# modules/vpc/versions.tf
terraform {
required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = ">= 5.0"
+ }
}
}Enterprise Best Practice — Aliased Multi-Region / Multi-Account Providers
This is the pattern that breaks at scale. You have aliased providers for cross-region or cross-account deployments and the module call never maps them:
# root/main.tf — BROKEN
provider "aws" {
region = "us-east-1"
}
provider "aws" {
alias = "west"
region = "us-west-2"
}
module "dr_vpc" {
source = "./modules/vpc"
- # No providers argument — Terraform cannot infer which aliased provider to use
+ providers = {
+ aws = aws.west
+ }
}# modules/vpc/variables.tf — BROKEN (provider hardcoded inside module)
- provider "aws" {
- region = "us-west-2"
- }
# modules/vpc/versions.tf — CORRECT (module declares requirement, root satisfies it)
+ terraform {
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = ">= 5.0"
+ }
+ }
+ }For multi-account (assume_role pattern):
# root/providers.tf
provider "aws" {
alias = "prod_account"
region = "us-east-1"
+ assume_role {
+ role_arn = "arn:aws:iam::123456789012:role/TerraformDeployRole"
+ }
}
module "prod_networking" {
source = "./modules/networking"
+ providers = {
+ aws = aws.prod_account
+ }
}Rule: Child modules must never contain
providerblocks with configuration. They declarerequired_providersfor version constraints only. All provider configuration (region, credentials, assume_role) lives exclusively in the root module and is injected viaproviders = {}.
💡 Tired of pasting proprietary infrastructure configs into public LLMs? Generic AI interfaces log your company's ARNs, connection strings, and private keys. StackEngine enforces client-side zero-trust boundaries—the sandbox above executes 100% locally in your browser. Drop your broken manifest above to redact secrets locally and synthesize deterministic patch drafts with your own API key.
(For CI/CD pipeline automation, install via
npm i -g stackengine).
▼ 🔍 Continuous Pipeline Governance & CI/CD Guardrails [Click to Expand Deep Dive]
Prevention in CI/CD
Don't rely on engineers remembering this. Encode it:
1. `terraform validate` as a Pre-Commit Hook
# .pre-commit-config.yaml
repos:
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.83.5
hooks:
- id: terraform_validate
- id: terraform_tflinttflint with the AWS ruleset will flag missing provider configurations before they hit your pipeline.
2. Checkov Policy (Static Analysis)
checkov -d . --check CKV_TF_1Add to your GitHub Actions or GitLab CI stage before terraform plan:
- name: Checkov Scan
uses: bridgecrewio/checkov-action@master
with:
directory: .
framework: terraform
halt_on_broken_analyses: true3. OPA / Conftest Policy (Enforce Provider Passthrough)
# policy/terraform/provider_passthrough.rego
package terraform.module_providers
deny[msg] {
module := input.configuration.root_module.module_calls[name]
not module.providers
msg := sprintf(
"Module '%s' has no explicit providers map. All module calls must declare a providers argument.",
[name]
)
}terraform show -json tfplan.binary | conftest test --policy policy/ -4. Terraform `required_providers` Version Pinning in Every Module
Make it a team standard: every modules/*/versions.tf file must exist and declare required_providers. A missing versions.tf in a module PR fails review. This surfaces the provider contract explicitly and prevents the silent inheritance assumption that causes this error.
Official Grounding & Normative Specifications
The technical diagnostics, error recovery protocols, and prevention patterns in this specification are grounded in official vendor documentation and standards:
Core concepts governing declarative infrastructure lifecycle, state lock contention, and provider graph synthesis.
Federal security guidelines for secret exfiltration prevention, automated integrity checks, and peer sign-offs.
Core memory subsystem controls, pressure stall information (PSI), and kernel OOM invocation mechanics.
Industry-standard specification for container execution boundaries and POSIX namespace isolation.