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 below to auto-refactor this — paste your broken module call and get corrected HCL instantly without leaking your account IDs or ARNs.
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.
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 configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.
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_tflint
tflint with the AWS ruleset will flag missing provider configurations before they hit your pipeline.
2. Checkov Policy (Static Analysis)
checkov -d . --check CKV_TF_1
Add 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: true
3. 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.