hashicorp / terraform

Terraform enables you to safely and predictably create, change, and improve infrastructure. It is a source-available tool that codifies APIs into declarative configuration files that can be shared amongst team members, treated as code, edited, reviewed, and versioned.
https://www.terraform.io
Other
42.76k stars 9.56k forks source link

Allow provider updates with state move operations #29238

Open Theragus opened 3 years ago

Theragus commented 3 years ago

Terraform Version

terraform version 1.0.0 (running terraform cloud)

Terraform Configuration Files

Root:


terraform {
    required_providers {
    azurerm = {
      source = "hashicorp/azurerm"
      version = ">=2.66.0"
    }
  }
}

module "azurermmodule" {
  source  = "app.terraform.io/<redacted>/azurermmodule/azurerm"
  version = "1.0.1"
}

azurermmodule.tf


terraform {
    required_providers {
    azurerm = {
      source = "hashicorp/azurerm"
      version = ">=2.66.0"
    }
  }
}

resource "azurerm_lighthouse_assignment" "<redacted>" {
  scope = data.azurerm_subscription.current.id
  lighthouse_definition_id = "<id>
}

Debug Output

Error: Provider configuration not present │ │ To work with │ module.azurermmodule.redacted.redacted │ (orphan) its original provider configuration at │ module.azurermmodule.provider["registry.terraform.io/hashicorp/azurerm"] │ 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.azurermmodule.redacted.redacted │ (orphan), after which you can remove the provider configuration again.

Crash Output

Expected Behavior

Move resources into the implicit provider configuration.

Actual Behavior

Existing resources in the child module with explicit provider configuration cannot be moved into the implicit provider configuration from the root module, Since the resources in the state demand that the provider needs to exist in the child module.

Steps to Reproduce

  1. create a root module with a provider configuration (e.g. azurerm version 2.66.0)
  2. create a child module with the same provider configuration inside and some resources
  3. call the child module from the root module with a module block without a providers-block
  4. create the resources from the child module in your environment when you run the root module (terraform plan and apply) and that state is created for these resources
  5. remove the provider configuration block from the child module
  6. run terraform plan again, error message will appear

Additional Context

terraform state replace-provider doesn't seem to work for resources in child modules, because FQDN of module providers cannot be parsed into the command

References

jbardin commented 3 years ago

Hi @Theragus,

Terraform will normally allow the replacement of a provider within the configuration, and automatically update the effected resources the next time they are refreshed. I'm not familiar with the AzureRM resources, but here is a simple example using AWS which requires no additional setup:

# main.tf
terraform {
    required_providers {
    aws = {
      source = "hashicorp/aws"
    }
  }
}

module "mod" {
  source = "./mod"
}
# mod/main.tf
terraform {
    required_providers {
    aws = {
      source = "hashicorp/aws"
    }
  }
}

resource "aws_vpc" "test" {
  cidr_block = "10.0.0.0/24"
}

provider "aws" {
  region = "us-east-1"
}

Applying that configuration will store the provider address of module.mod.provider["registry.terraform.io/hashicorp/aws"] in the state. Moving the provider block to the root module, and refreshing or applying any change will update the stored address to provider["registry.terraform.io/hashicorp/aws"] without error.

The error you have here appears to indicate you are tying to move the provider while simultaneously removing the resources, which you cannot do. This is one of the primary reasons you cannot declare providers within a module, because you cannot remove that module since it contains the providers necessary to remove any of its resources.

If my assumption is correct here, you should be able to progress by moving the provider to the root module before removing any of the resources. If this is not the case, can you provide a complete example that reproduces the error you see?

Thanks!

Theragus commented 3 years ago

Hi @jbardin,

i will try to reproduce this error in the next couple of days. First, i didn't want to try to delete the resources, i just wanted to move the resources from the child module to the root. I got always the error that provider configuration was removed when removing the provider block from the child module and tried using the root provider config.

What for me strangely worked was to define the providers inside the module explicitely, something like this.

module "module" {
  source  = "app.terraform.io/org/modulename/azurerm"
  version = "1.0.0"
  providers = {
    azurerm = azurerm
  }
}

After that i could run a successful terraform apply without errors, removed the providers-block from the module, and rerun terraform apply again. The apply then ran again successfully and the child modules resources have been moved to the root provider.

jbardin commented 3 years ago

Thanks @Theragus,

I see now that you mentioned that in the title, but not in the example itself. Moving the resources and changing their provider cannot currently be done yet in a single operation. You will need to update the provider first before moving the resources. We are woking on an improved workflow for manipulating resource in the state, so this is something we are going to be looking at.

Thanks!

Theragus commented 3 years ago

@jbardin Thank you very much for your quick response, that was kinda the piece of information I was missing.

It's especially confusing because when you're using the provider's block with the same provider name on both sides of the equal sign, then a warning will appear that no provider configuration with that name inside the child module exists.

Bildschirmfoto 2021-07-26 um 17 15 20
jbardin commented 3 years ago

I see how that could be a bit confusing. That warning is there to try and push you to make sure the modules have required_providers defined for all providers needed by the module. While we can't enforce required_providers be present in all modules due to backwards compatibility constraints, we add the warning in this case because terraform has no way to verify that you are passing in a provider compatible with that module.