Terraform provider DRY

hi all,

in my root/providers.tf file:

terraform {
  required_version = ">= 1.6.6"
  required_providers {
    openstack = {
      source  = "terraform-provider-openstack/openstack"
      version = "~> 2.1.0"
    }
  }
}

then I have in my root/mymodule/providers.tf file the same content as above:

terraform {
  required_version = ">= 1.6.6"
  required_providers {
    openstack = {
      source  = "terraform-provider-openstack/openstack"
      version = "~> 2.1.0"
    }
  }
}

in module.myinstance is separate branch and the provider configuration is like this:

terraform {
  required_version = ">= 1.6.6"
  required_providers {
    openstack = {
      source  = "terraform-provider-openstack/openstack"
      version = "~> 2.1.0"
    }
  }
}

the output of $ terraform providers

Providers required by configuration:
.
├── provider[registry.terraform.io/terraform-provider-openstack/openstack] ~> 2.1.0
└── module.mymodule
    ├── provider[registry.terraform.io/terraform-provider-openstack/openstack]  ~> 2.1.0
    └── module.myinstance
        └── provider[registry.terraform.io/terraform-provider-openstack/openstack] ~> 2.1.0

Providers required by state:

    provider[registry.terraform.io/terraform-provider-openstack/openstack]

when I run now terraform init it works fine, but I dont want doplicate the provider configuration in mymodule/providers.tf file. I want to use the provider configuration from the root/providers.tf file. How can I do that? because when I want to update the provider version I have to do it in many files.

thank you all

so no one have an idea! how I can manage this!

I try also something like below inside root/mymodule/providers.tf and module.myinstance I change the providers.tf like below, without required_version and version for the provider:

terraform {
  required_providers {
    openstack = {
      source  = "terraform-provider-openstack/openstack"
    }
  }
}

when I run terraform init it works, and when I run terraform providers I get below output:-

Providers required by configuration:
.
├── provider[registry.terraform.io/terraform-provider-openstack/openstack] ~> 2.1.0
└── module.mymodule
    ├── provider[registry.terraform.io/terraform-provider-openstack/openstack]
    └── module.myinstance
        └── provider[registry.terraform.io/terraform-provider-openstack/openstack] ~> 2.1.0

Providers required by state:

    provider[registry.terraform.io/terraform-provider-openstack/openstack]

is thar “right” way to do it ?

when I remove the providers.tf in root/mymodule/providers.tf and module.myinstance and then run terraform init -upgrade I get below error:

- Installed hashicorp/null v3.2.2 (signed by HashiCorp)
Partner and community providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://www.terraform.io/docs/cli/plugins/signing.html
â•·
│ Error: Failed to query available provider packages
│ 
│ Could not retrieve the list of available versions for provider hashicorp/openstack: provider registry registry.terraform.io does not have a provider named
│ registry.terraform.io/hashicorp/openstack
│ 
│ Did you intend to use terraform-provider-openstack/openstack? If so, you must specify that source address in each module which requires that provider. To
│ see which modules are currently depending on hashicorp/openstack, run the following command:
│     terraform providers

so what I understand is to create the providers.tf in every module and then add beow code:

terraform {
  required_providers {
    openstack = {
      source  = "terraform-provider-openstack/openstack"
    }
  }
}

my question is if I upgrade the version in the future, other modules will use the new version or still the old one ?

Hi @hi03b,

Terraform modules are discrete namespaces, and different modules may be expecting different requirements. In order for modules to behave consistently regardless of the which parent module which imports them, they need to declare the required source and version of their providers. Different modules may have conflicting local provider names, different version requirements, or different sources, and may be not work correctly otherwise even if the configuration matches the provider schemas.

Some providers, namely those in the hashicorp org, don’t always need explicit requirements to function within modules. That was done for backward compatibility after 3rd party providers were introduced, but in hindsight it was probably a mistake and they should have been included in the requirements as well.

Only one version of each particular provider can be used at a time, so the version constraints for all modules are summed up to determine the version to be initialized.

1 Like

Thank you so much for your answer and explanation @jbardin.

So my project have 18 modules and all of them have the required providers and their version.

Now if I want upgrade the root provider version I have to do that 18x so that take time…

Now I create symlink to the root/probiders.ts inside each module…

Is this good idea or still bad :see_no_evil:

If the modules are independent, then yes you should declare the version constraints for the providers the module is compatible with.

If these are all tightly coupled in a single project, then the modules don’t necessarily all need the latest version declared (or even a version at all), you can declare the exact version you want to run in one module. As long as all the modules have compatible version constraints, the newest or most precise version would have to be the one installed.

1 Like

Okay thanks again @jbardin … I helps me to understand the structure.

The 18 modules are sites which I create. So all 18 modules have exact l providers like the root one.

Last question please:

Should I leave as symlink to root/providers? In this way if I change it in root/providers it’s for all the same…

Or should I create providers.tf in each module without version.

terraform {
  required_providers {
    openstack = {
      source  = "terraform-provider-openstack/openstack"
    }
  }
}

For me is important to do it in right way…emphasized text

If these are all used as a single project and never split up or need to be upgraded individually, then either way is fine. If the symlink causes you a problem in the future you can always change it.

1 Like

You are right… thank you again @jbardin

This is the exact reason (one of them, anyway) why I developed Metastructure. It uses super-dry Handlebars templates to generate intrinsically WET artifacts (like providers) from a common project config.

@karmaniverous what does this mean ? Why it’s hidden ?

Um apparently somebody thought it was spam. Just search “metastructure” on GitHub, should go a long way toward solving your problem!

Thank you @karmaniverous, it’s not spam, I already search google and GitHub to solve my problem, until I asked here and @jbardin give me very helpful answers. And I could solve it :slight_smile:

1 Like

I mean they thought my response was spam lol. Anyway glad you solved your problem!

1 Like

Ah sorry, can you send me your answer again or as DM please :slight_smile:
Thank you so much @karmaniverous

This topic was automatically closed 62 days after the last reply. New replies are no longer allowed.