Introduction
A module in Terraform is a logical, reusable grouping of resources. Terraform modules can be classified into two major categories - root modules and child modules. This article provides a range of possible scenarios of failures or known issues that may occur when using Terraform modules. For more information around Terraform modules, refer to the modules documentation.
Problems
When declaring child modules in Terraform configuration, your child modules may inherit provider configurations from their parent module via either implicitly or explicitly. The references to the provider can be missing and the error Error: Provider configuration not present surfaces.
Scenario 1 - Child module with alias providers
When using the provider alias in a child module, the parent module with the module block declaration that references to the child module is required to have the providers meta-argument in order to pass the aliased provider explicitly from the parent module.
Terraform v0.12.20
Configuring remote state backend...
Initializing Terraform configuration...
2020/04/14 21:01:09 [DEBUG] Using modified User-Agent: Terraform/0.12.20 TFE/v202003-1
Error: Provider configuration not present
To work with module.xxxx.infoblox_record_host.host its original
provider configuration at module.xxxx.provider.infoblox.abc01
is required, but it has been removed. This occurs when a provider
configuration is removed while objects created by that provider still exist in
the state. Re-add the provider configuration to destroy
module.xxxx.infoblox_record_host.host, after which you can remove the
provider configuration again.
Example - Child module with alias providers
- Directory example for the current experience.
Test Folder
└── repro
..└── east.tf
..└── west.tf
└── instance
..└── main.tf
- Configuration example for the current experience.
..└── east.tf
module "east" {
source = "./instance"
region = "us-east-1" # equivalient to the namespace argument
}
..└── west.tf
module "west" {
source = "./instance"
region = "us-west-1" # equivalient to the namespace argument
}
..└── main.tf
variable "region" {}
provider "aws" {
region = var.region
alias = "instancemaker"
}
data "aws_ami" "ubuntu" {
provider = aws.instancemaker
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["012345678901"] # Canonical
}
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
provider = aws.instancemaker
}
- Initial Apply
repro $ terraform apply
module.west.aws_instance.web: Creating...
module.east.aws_instance.web: Creating...
module.west.aws_instance.web: Still creating... [10s elapsed]
module.east.aws_instance.web: Still creating... [10s elapsed]
module.east.aws_instance.web: Creation complete after 15s [id=i-004349cc6d6b67a28]
module.west.aws_instance.web: Creation complete after 16s [id=i-00d3fda99fba64919]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
- Delete the west.tf Module file.
repro $ mv west.tf west.tf.deleted
repro $ ls
east-provider.tf instance west-provider.tf
east.tf terraform.tfstate west.tf.deleted
- Next, apply reproduces the error which results in a failed apply
repro $ terraform apply
Error: Provider configuration not present
To work with module.west.data.aws_ami.ubuntu its original provider
configuration at
module.west.provider["registry.terraform.io/hashicorp/aws"].instancemaker is
required, but it has been removed. This occurs when a provider configuration
is removed while objects created by that provider still exist in the state.
Re-add the provider configuration to destroy module.west.data.aws_ami.ubuntu,
after which you can remove the provider configuration again.
Solution - Child module with alias providers
The multiple provider configuration documentation provides you an example of how to declare an aliased provider and the alternate provider configurations documentation guides you on how to pass an aliased provider into a Terraform module.
- Directory example for your future experience. Note the two new provider files.
Test Folder
└── repro
..└── east-provider.tf
..└── module-east.tf
..└── west-provider.tf
..└── module-west.tf
└── instance
..└── main.tf
- Configuration example for your future experience.
..└──east-provider.tf
provider "aws" {
region = "us-east-1"
alias = "east"
}
..└──module-east.tf
module "east" {
source = "./instance"
providers = {
aws.instancemaker = aws.east
}
}
..└──west-provider.tf
provider "aws" {
region = "us-west-1"
alias = "west"
}
..└──module-west.tf
module "west" {
source = "./instance"
providers = {
aws.instancemaker = aws.west
}
}
..└──main.tf
provider "aws" {
alias = "instancemaker"
}
data "aws_ami" "ubuntu" {
provider = aws.instancemaker
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["012345678901"] # Canonical
}
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
provider = aws.instancemaker
}
- Initial apply
repro $ terraform apply
module.east.aws_instance.web: Creating...
module.west.aws_instance.web: Creating...
module.east.aws_instance.web: Still creating... [10s elapsed]
module.west.aws_instance.web: Still creating... [10s elapsed]
module.east.aws_instance.web: Creation complete after 15s [id=i-09c72061ccbb45b4c]
module.west.aws_instance.web: Creation complete after 16s [id=i-0c48db7f076273d03]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
- Delete the west.tf Module file first.
repro $ mv west.tf west.tf.deleted
repro $ ls
east-provider.tf instance west-provider.tf
east.tf terraform.tfstate west.tf.deleted
repro $ terraform apply
module.west.aws_instance.web: Destroying... [id=i-0c48db7f076273d03]
module.west.aws_instance.web: Still destroying... [id=i-0c48db7f076273d03, 10s elapsed]
module.west.aws_instance.web: Still destroying... [id=i-0c48db7f076273d03, 20s elapsed]
module.west.aws_instance.web: Still destroying... [id=i-0c48db7f076273d03, 30s elapsed]
module.west.aws_instance.web: Still destroying... [id=i-0c48db7f076273d03, 40s elapsed]
module.west.aws_instance.web: Still destroying... [id=i-0c48db7f076273d03, 50s elapsed]
module.west.aws_instance.web: Still destroying... [id=i-0c48db7f076273d03, 1m0s elapsed]
module.west.aws_instance.web: Destruction complete after 1m3s
Apply complete! Resources: 0 added, 0 changed, 1 destroyed.
- Delete the west-provider.tf file last will result in a successful apply
repro $ mv west-provider.tf west-provider.tf.deleted
repro $ ls
east-provider.tf terraform.tfstate west.tf.deleted
east.tf terraform.tfstate.backup
instance west-provider.tf.deleted
repro $ terraform apply
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Scenario 2 - Destroy module with reference to default providers
A child module may declare a provider without aliasing or furthermore, the provider declaration can be completely omitted as this configuration is optional where it follows the default behavior of the providers inheritance. In this scenario, a parent module is able to pass an alternate provider to a child module explicitly by following the guide in the Selecting Alternate Provider Configurations document. When a child module has a reference to a non-aliased provider (default provider) is removed from the source code in the module, where the reference to the provider still exists in the state, the destroy operation may fail due to the reference to the provider in the state file. The solution is to declare a non-aliased provider in the root module when removing the child module in this scenario.
Example - Destroy module with reference to default providers
- The directory layout
.
├── m
│ └── m.tf
├── main.tf
- The file
main.tf
terraform {
required_providers {
github = {
source = "integrations/github"
version = ">= 4.1"
}
}
}
module "m" {
source = "./m"
}
provider "github" {
token = "xxxxxxxx"
}
- The file
m/m.tf
containing this content:
terraform {
required_providers {
github = {
source = "integrations/github"
version = "~> 4.0"
}
}
}
data "github_branch" "development" {
repository = "git/git"
branch = "master"
}
resource "github_repository" "example" {
name = "test-blah-blah"
description = "My awesome codebase"
visibility = "public"
}
- Perform terraform apply and then comment out the module and provider
terraform {
required_providers {
github = {
source = "integrations/github"
version = ">= 4.1"
}
}
}
#module "m" {
# source = "./m"
#}
#provider "github" {
# token = "xxxxxxxx"
#}
- Removing the module m from the code and performing terraform plan results in the error
2021/07/07 17:44:59 [TRACE] statemgr.Filesystem: removing lock metadata file .terraform.tfstate.lock.info Error: Provider configuration not present 2021/07/07 17:44:59 [TRACE] statemgr.Filesystem: unlocking terraform.tfstate using fcntl flock To work with module.m.data.github_branch.development its original provider configuration at provider["registry.terraform.io/integrations/github"] is required, but it has been removed. This occurs when a provider configuration is removed while objects created by that provider still exist in the state. Re-add the provider configuration to destroy module.m.data.github_branch.development, after which you can remove the provider configuration again.
- Observing the state file, the module resources have references to the provider provider[\"registry.terraform.io/integrations/github\"]"
{
"version": 4,
"terraform_version": "0.13.7",
"serial": 3,
"lineage": "4f55684b-d7a1-72eb-4b12-8803328a9e87",
"outputs": {},
"resources": [
{
"module": "module.m",
"mode": "data",
"type": "github_branch",
"name": "development",
"provider": "provider[\"registry.terraform.io/integrations/github\"]",
"instances": [
{
"schema_version": 0,
"attributes": {
"branch": null,
"etag": null,
"id": null,
"ref": null,
"repository": null,
"sha": null
}
}
]
},
{
"module": "module.m",
"mode": "managed",
"type": "github_repository",
"name": "example",
"provider": "provider[\"registry.terraform.io/integrations/github\"]",
"instances": [
{
"schema_version": 0,
"attributes": {
"allow_merge_commit": true,
"allow_rebase_merge": true,
"allow_squash_merge": true,
"archive_on_destroy": null,
"archived": false,
"auto_init": null,
"default_branch": "main",
"delete_branch_on_merge": false,
"description": "My awesome codebase",
"etag": "W/\"4a77829e31012a1eed31e207ac9fa64444adf7e3374ff49e94e2eba9c61e68ec\"",
"full_name": "tmekaumput/test-blah-blah",
"git_clone_url": "git://github.com/xxxx/test-blah-blah.git",
"gitignore_template": null,
"has_downloads": false,
"has_issues": false,
"has_projects": false,
"has_wiki": false,
"homepage_url": "",
"html_url": "https://github.com/xxxx/test-blah-blah",
"http_clone_url": "https://github.com/xxxx/test-blah-blah.git",
"id": "test-blah-blah",
"is_template": false,
"license_template": null,
"name": "test-blah-blah",
"node_id": "MDEwOlJlcG9zaXRvcnkzOTA5NTMxMDc=",
"pages": [],
"private": false,
"repo_id": 390953107,
"ssh_clone_url": "git@github.com:xxxx/test-blah-blah.git",
"svn_url": "https://github.com/xxxx/test-blah-blah",
"template": [],
"topics": null,
"visibility": "public",
"vulnerability_alerts": false
},
"private": "bnVsbA=="
}
]
}
]
}
Solution - Destroy module with reference to default providers
- The error message provider["registry.terraform.io/integrations/github"] indicates the provider declaration is required.
- Adding the provider declaration back into the root module.
terraform {
required_providers {
github = {
source = "integrations/github"
version = ">= 4.1"
}
}
}
# module "m" {
# source = "./m"
# }
provider "github" {
token = "xxxxxxx"
}
- Perform terraform plan again will show the successful outcome
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
module.m.github_repository.example: Refreshing state... [id=test-blah-blah]
------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
- destroy
Terraform will perform the following actions:
# module.m.github_repository.example will be destroyed
- resource "github_repository" "example" {
- allow_merge_commit = true -> null
- allow_rebase_merge = true -> null
- allow_squash_merge = true -> null
- archived = false -> null
- default_branch = "main" -> null
- delete_branch_on_merge = false -> null
- description = "My awesome codebase" -> null
- etag = "W/\"4a77829e31012a1eed31e207ac9fa64444adf7e3374ff49e94e2eba9c61e68ec\"" -> null
- full_name = "xxxxxxx/test-blah-blah" -> null
- git_clone_url = "git://github.com/tmekaumput/test-blah-blah.git" -> null
- has_downloads = false -> null
- has_issues = false -> null
- has_projects = false -> null
- has_wiki = false -> null
- html_url = "https://github.com/xxxxxxx/test-blah-blah" -> null
- http_clone_url = "https://github.com/tmekaumput/test-blah-blah.git" -> null
- id = "test-blah-blah" -> null
- is_template = false -> null
- name = "test-blah-blah" -> null
- node_id = "MDEwOlJlcG9zaXRvcnkzOTA5NTMxMDc=" -> null
- private = false -> null
- repo_id = 390953107 -> null
- ssh_clone_url = "git@github.com:xxxxxxx/test-blah-blah.git" -> null
- svn_url = "https://github.com/xxxxxxx/test-blah-blah" -> null
- visibility = "public" -> null
- vulnerability_alerts = false -> null
}
Plan: 0 to add, 0 to change, 1 to destroy.
------------------------------------------------------------------------
Additional information
If you continue to experience the issues after following the guides, please contact HashiCorp Support to request for further assistance.