How can I use 2 providers in the same terraform config? - terraform

My main.tf file starts with
terraform {
required_version = ">= 0.13.7"
required_providers {
aws = {
source = "hashicorp/aws"
version = "= 2.32.0"
}
foobar = {
source = "terraform.foo.com/foo/bar"
}
}
}
The catch here is that foo/bar is the module I'm developing locally so I also has this terraformrc file:
provider_installation {
dev_overrides {
"terraform.foo.com/foo/bar" = "/Users/appuser/foobar/bin/darwin-amd64"
}
}
Here's the errors I run into when running ✗ terraform init
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/aws versions matching "2.32.0"...
- Finding latest version of terraform.foo.com/foo/bar...
Warning: Provider development overrides are in effect
The following provider development overrides are set in the CLI configuration:
- "terraform.foo.com/foo/bar" = "/Users/appuser/foobar/bin/darwin-amd64"
Error: Failed to query available provider packages
Could not retrieve the list of available versions for provider hashicorp/aws:
no available releases match the given constraints 2.32.0
Error: Failed to query available provider packages
Could not retrieve the list of available versions for provider
"terraform.foo.com/foo/bar": no available releases
match the given constraints
Update: when I remove terraformrc it does seem to work but I am not able to load the 2nd provider this way (since it relies on override):
terraform init
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/aws versions matching "2.32.0"...
- Finding latest version of hashicorp/foo/bar...
- Installing hashicorp/aws v2.32.0...
- Installed hashicorp/aws v2.32.0 (self-signed, key ID 34365D9472D7468F)
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/plugins/signing.html
Error: Failed to query available provider packages
Could not retrieve the list of available versions for provider
hashicorp/foo/bar: provider registry registry.terraform.io does not
have a provider named registry.terraform.io/hashicorp/foo/bar

The dev_overrides setting is, in spite of being placed inside the provider_installation block due to its thematic similarity, actually a runtime setting rather than an installation setting. Specifically, it asks Terraform to ignore whatever version of the provider terraform init previously selected and to use the given overridden provider instead.
Unfortunately, this model only works well if you're overriding a provider that already has at least one published version available, so that terraform init can select and install that version but then terraform apply (for example) can then ignore what terraform init installed, and use the override instead. (Terraform behaves this way because part of terraform init's responsibility is to update the Dependency Lock File, and so it needs to find at least one selectable version of each provider in order to produce a complete lock file.)
One way you could avoid this design quirk is to place a fake "release" of this provider in a local directory that you'd then configure as a filesystem mirror for that provider, in your .terraformrc file.
For example, if you create a directory /tmp/tf-workaround/terraform.foo.com/foo/bar/0.0.1/darwin_amd64 and place into it an empty file named terraform-provider-bar then that should be sufficient for terraform init to detect this as an available "published" version of the provider if given an CLI configuration like this:
provider_installation {
dev_overrides {
"terraform.foo.com/foo/bar" = "/Users/appuser/foobar/bin/darwin-amd64"
}
filesystem_mirror {
path = "/tmp/tf-workaround"
include = ["terraform.foo.com/foo/bar"]
}
direct {
exclude = ["terraform.foo.com/foo/bar"]
}
}
terraform init should then find the placeholder empty file and "install" it into .terraform/providers as normal. That empty file won't actually work as a valid plugin, but that's okay because terraform apply will ignore it anyway and will use the directory given in dev_overrides instead.
However, the dependency lock file will contain an incorrect entry for terraform.foo.com/foo/bar after installation, so if you intend to commit this test configuration to version control then you may wish to manually remove that block to reduce confusion once there really is a release of this provider.
The less-complex way to work here is to test a provider during development with a configuration that only includes that provider, and wait until you've actually published the provider somewhere before you use it "for real" as part of a larger system. In that case, you'd typically skip running terraform init altogether because the only external dependency would be the overridden provider, and so nothing additional would need to be installed.

The fix (which was to add direct {}) was found in TF docs:
provider_installation {
# Use /home/developer/tmp/terraform-null as an overridden package directory
# for the hashicorp/null provider. This disables the version and checksum
# verifications for this provider and forces Terraform to look for the
# null provider plugin in the given directory.
dev_overrides {
"hashicorp/null" = "/home/developer/tmp/terraform-null"
}
# For all other providers, install them directly from their origin provider
# registries as normal. If you omit this, Terraform will _only_ use
# the dev_overrides block, and so no other providers will be available.
direct {}
}
Actually I'm still getting
Error: Failed to query available provider packages
Could not retrieve the list of available versions for provider
hashicorp/foo/bar: could not connect to hashicorp: Failed
to request discovery document: Get
"https://hashicorp/.well-known/terraform.json": dial tcp: lookup hashicorp on
100.217.9.1:53: no such host

Related

How can I fix Failed to query available provider packages when doing local provider development with terraform init?

Context: I'm developing a new TF provider. In order to test it, I use the following piece of advice from TF docs:
provider_installation {
# Use /home/developer/tmp/terraform-null as an overridden package directory
# for the hashicorp/null provider. This disables the version and checksum
# verifications for this provider and forces Terraform to look for the
# null provider plugin in the given directory.
dev_overrides {
"hashicorp/null" = "/home/developer/tmp/terraform-null"
}
# For all other providers, install them directly from their origin provider
# registries as normal. If you omit this, Terraform will _only_ use
# the dev_overrides block, and so no other providers will be available.
direct {}
}
And when I run terraform plan / terraform apply my provider does work without any issues. However when I try to run terraform init I'm running into:
Error: Failed to query available provider packages
Could not retrieve the list of available versions for provider
hashicorp/null: could not connect to hashicorp: Failed
to request discovery document: Get
"https://hashicorp/.well-known/terraform.json": dial tcp: lookup hashicorp on
100.217.9.1:53: no such host
Is there a way I could fix it?
For the context, my main.tf file starts with
terraform {
required_providers {
null = {
source = "hashicorp/null"
}
}
}
When I googled around, I found a related blog post and terraform plan seems to work for the author since he doesn't uses other plugins which is not the case for me unfortunately.
This issue on GitHub seems to show the same issues.

"Invalid legacy provider address" error on Terraform

I'm trying to deploy a bitbucket pipeline using terraform v0.14.3 to create resources in google cloud. after running terraform command, the pipeline fails with this error:
Error: Invalid legacy provider address
This configuration or its associated state refers to the unqualified provider
"google".
You must complete the Terraform 0.13 upgrade process before upgrading to later
versions.
We updated our local version of terraform to v.0.13.0 and then ran: terraform 0.13upgrade as referenced in this guide: https://www.terraform.io/upgrade-guides/0-13.html. A versions.tf file was generated requiring terraform version >=0.13 and our required provider block now looks like this:
terraform {
backend "gcs" {
bucket = "some-bucket"
prefix = "terraform/state"
credentials = "key.json" #this is just a bitbucket pipeline variable
}
required_providers {
google = {
source = "hashicorp/google"
version = "~> 2.20.0"
}
}
}
provider "google" {
project = var.project_ID
credentials = "key.json"
region = var.project_region
}
We still get the same error when initiating the bitbucket pipeline. Does anyone know how to get past this error? Thanks in advance.
Solution
If you are using a newer version of Terraform, such as v0.14.x, you should:
use the replace-provider subcommand
terraform state replace-provider \
-auto-approve \
"registry.terraform.io/-/google" \
"hashicorp/google"
#=>
Terraform will perform the following actions:
~ Updating provider:
- registry.terraform.io/-/google
+ registry.terraform.io/hashicorp/google
Changing x resources:
. . .
Successfully replaced provider for x resources.
initialize Terraform again:
terraform init
#=>
Initializing the backend...
Initializing provider plugins...
- Reusing previous version of hashicorp/google from the dependency lock file
- Using previously-installed hashicorp/google vx.xx.x
Terraform has been successfully initialized!
You may now begin working with Terraform. Try . . .
This should take care of installing the provider.
Explanation
Terraform only supports upgrades from one major feature upgrade at a time. Your older state file was, more than likely, created using a version earlier than v0.13.x.
If you did not run the apply command before you upgraded your Terraform version, you can expect this error: the upgrade from v0.13.x to v0.14.x was not complete.
You can find more information here.
in our case, we were on aws and had similar error
...
Error: Invalid legacy provider address
This configuration or its associated state refers to the unqualified provider
"aws".
the steps to resolve were :
ensure syntax was upgraded by running terraform init again
check the warnings and resolve them
and finally updating the statefile with following method.
# update provider in state file
terraform state replace-provider -- -/aws hashicorp/aws
# reinit
terraform init
specific to ops problem, if issue still occurs, verify access to the bucket location from local and from pipeline. also verify the version of terraform running in pipeline. depending on configuration it may be the remote statefile is/can not be updated.
Same issue for me. I ran:
terraform providers
That gave me:
Providers required by configuration:
registry.terraform.io/hashicorp/google
Providers required by state:
registry.terraform.io/-/google
So I ran:
terraform state replace-provider registry.terraform.io/-/google registry.terraform.io/hashicorp/google
That did the trick.
To add on, I had installed terraform 0.14.6 but the state seemed to be stuck in 0.12. In my case I had 3 references that were off, this article helped me pinpoint which ones (all the entries in "Providers required by state" which had a - in the link. https://github.com/hashicorp/terraform/issues/27615
I corrected it by running the replace-provider command for each entry which was off, then running terraform init. I note doing this and running a git diff, the tfstate has been updated and now uses 0.14.x terraform instead of my previous 0.12.x. i.e.
terraform providers
terraform state replace-provider registry.terraform.io/-/azurerm registry.terraform.io/hashicorp/azurerm
Explanation: Your terraform project contains tf.state file that is outdated and refereeing to old provider address. The Error message will present this error:
Error: Invalid legacy provider address
This configuration or its associated state refers to the unqualified provider
<some-provider>.
You must complete the Terraform <some-version> upgrade process before upgrading to later
versions.
Solution: In order to solve this issue you should change the tf.state references to link to the newer required providers, update the tf.state file and initialize the project again. The steps are:
Create / Edit the required providers block with the relevant package name and version, I'd rather doing it on versions.tf file.
example:
terraform {
required_version = ">= 0.14"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 3.35.0"
}
}
}
Run terraform providers command to present the required providers from configuration against the required providers that saved on state.
example:
Providers required by configuration:
.
├── provider[registry.terraform.io/hashicorp/aws] >= 3.35.0
Providers required by state:
provider[registry.terraform.io/-/aws]
Switch and reassign the required provider source address in the terraform state ( using terraform state replace-provider command) so we can tell terraform how to interpret the legacy provider.
The terraform state replace-provider subcommand allows re-assigning
provider source addresses recorded in the Terraform state, and so we
can use this command to tell Terraform how to reinterpret the "legacy"
provider addresses as properly-namespaced providers that match with
the provider source addresses in the configuration.
Warning: The terraform state replace-provider subcommand, like all of
the terraform state subcommands, will create a new state snapshot and
write it to the configured backend. After the command succeeds the
latest state snapshot will use syntax that Terraform v0.12 cannot
understand, so you should perform this step only when you are ready to
permanently upgrade to Terraform v0.13.
example:
terraform state replace-provider registry.terraform.io/-/aws registry.terraform.io/hashicorp/aws
output:
~ Updating provider:
- registry.terraform.io/-/aws
+ registry.terraform.io/hashicorp/aws
run terraform init to update references.
While you were under TF13 did you apply state at least once for the running project?
According to TF docs: https://www.terraform.io/upgrade-guides/0-14.html
There is no automatic update command (separately) in 0.14 (like there was in 0.13). The only way to upgrade is to force state on a project at least once, while under command when moving TF13 to 14.
You can also try terraform init in the project directory.
my case was like this
Error: Invalid legacy provider address
This configuration or its associated state refers to the unqualified provider
"openstack".
You must complete the Terraform 0.13 upgrade process before upgrading to later
versions.
for resolving the issue
remove the .terraform folder
the execute the following command
terraform state replace-provider -- -/openstack terraform-provider-openstack/openstack
after this command, you will see the below print, enter yes
Terraform will perform the following actions:
~ Updating provider:
- registry.terraform.io/-/openstack
+ registry.terraform.io/terraform-provider-openstack/openstack
Changing 11 resources:
openstack_compute_servergroup_v2.kubernetes_master
openstack_networking_network_v2.kube_router
openstack_compute_instance_v2.kubernetes_worker
openstack_networking_subnet_v2.internal
openstack_networking_subnet_v2.kube_router
data.openstack_networking_network_v2.external_network
openstack_compute_instance_v2.kubernetes_etcd
openstack_networking_router_interface_v2.internal
openstack_networking_router_v2.internal
openstack_compute_instance_v2.kubernetes_master
openstack_networking_network_v2.internal
Do you want to make these changes?
Only 'yes' will be accepted to continue.
Enter a value: yes
Successfully replaced provider for 11 resources.
I recently ran into this using Terraform Cloud for the remote backend. We had some older AWS-related workspaces set to version 0.12.4 (in the cloud) that errored out with "Invalid legacy provider address" and refused to run with the latest Terraform client 1.1.8.
I am adding my answer because it is much simpler than the other answers. We did not do any of the following:
terraform providers
terraform 0.13upgrade
remove the .terraform folder
terraform state replace-provider
Instead we simply:
In a clean folder (no local state, using local terraform.exe version 0.13.7) ran 'terraform init'
Made a small insignificant change (to ensure apply would write state) to a .tf file in the workspace
In Terraform Cloud set the workspace version to 0.13.7
Using local 0.13.7 terraform.exe ran apply - that saved new state.
Now we can use cloud and local terraform.exe version 1.1.8 and no more problems.
Note that we did also need to update a few AWS S3-related resources to the newer AWS provider syntax to get all our workspaces working with the latest provider.
We encountered a similar problem in our operational environments today. We successfully completed the terraform 0.13upgrade command. This indeed introduced a versions.tf file.
However, performing a terraform init with this setup was still not possible, and the following error popped up:
Error: Invalid legacy provider address
Further investigation in the state file revealed that, for some resources, the provider block was not updated. We hence had to run the following command to finalize the upgrade process.
terraform state replace-provider "registry.terraform.io/-/google" "hashicorp/google"
EDIT Deployment to the next environment revealed that this was caused by conditional resources. To easily enable/disable some resources we leverage the count attribute and use either 0 or 1. For the resources with count = 0, that were unaltered with Terraform 0.13, the provider was not updated.
I was using terragrunt with remote s3 state and dynamo db and sadly this does not work for me. So posting it here might help someone else.
A long way to make this work, as terragrunt state replace-provider does work for me
download the state file from s3
aws s3 cp s3://bucket-name/path/terraform.tfstate terraform.tfstate --profile profile
replace the provider using terraform
terraform state replace-provider "registry.terraform.io/-/random" "hashicorp/random"
terraform state replace-provider "registry.terraform.io/-/aws" "hashicorp/aws"
upload the state file back to s3 as even terragrunt state push terraform.tfstate does not work for me
aws s3 cp terraform.tfstate s3://bucket-name/path/terraform.tfstate --profile profile
terragrunt apply
the command will throw error with digest value,
update the dynamo db table digest value that received in previous command
Initializing the backend...
Error refreshing state: state data in S3 does not have the expected content.
This may be caused by unusually long delays in S3 processing a previous state
update. Please wait for a minute or two and try again. If this problem
persists, and neither S3 nor DynamoDB are experiencing an outage, you may need
to manually verify the remote state and update the Digest value stored in the
DynamoDB table to the following value: fe2840edf8064d9225eea6c3ef2e5d1d
finally, run terragrunt apply
The other way that this can be strange is if you are using terraform workspaces - especially with the remote state files.
Using a terraform workspace - the order of operations is important.
terraform init - connecting to the default workspace
terraform workspace switch <env> - Even if you have specified the workspace here, the init will happen using the default workspace.
This is an assumption that terraform makes - sometimes erroneously
To fix this - you can run your init using:
TF_WORKSPACE=<your_env> terraform init
Or remove the default workspace.

Multiple provider versions with Terraform

Does anyone know if it is possible to have a Terraform script that uses multiple provider versions?
For example azurerm version 2.0.0 to create one resource, and 1.4.0 for another?
I tried specifying the providers, as documented here: https://www.terraform.io/docs/configuration/providers.html
However it doesn't seem to work as it tries to resolve a single provider that fullfills both 1.4.0 and 2.0.0.
It errors like:
No provider "azurerm" plugins meet the constraint "=1.4.0,=2.0.0".
I'm asking this because we have a large Terraform codebase and I would like to migrate bits by bits if doable.
There used to be a similar question raised, here: Terraform: How to install multiple versions of provider plugins?
But it got no valid answer
How to use multiple version of the same Terraform provider
This allowed us a smooth transition from helm2 to helm3, while enabling new deployments to use helm3 right away, therefore reducing the accumulation of tech debt.
Of course you can do the same for most providers
How we've solved this
So the idea is to download a specific version of our provider (helm 0.10.6 in my case) and move it to one of the filesystem mirrors terraform uses by default. The key part is the renaming of our plugin binary. In the zip we can find terraform-provider-helm_v0.10.6, but we rename it to terraform-provider-helm2_v0.10.6
PLUGIN_PATH=/usr/share/terraform/plugins/registry.terraform.io/hashicorp/helm2/0.10.6/linux_amd64
mkdir -p $PLUGIN_PATH
curl -sLo_ 'https://releases.hashicorp.com/terraform-provider-helm/0.10.6/terraform-provider-helm_0.10.6_linux_amd64.zip'
unzip -p _ 'terraform-provider-helm*' > ${PLUGIN_PATH}/terraform-provider-helm2_v0.10.6
rm _
chmod 755 ${PLUGIN_PATH}/terraform-provider-helm2_v0.10.6
Then when we declare our two provider plugins
We can use hashicorp/helm2 plugin from the filesystem mirror, and let terraform directly download the latest hashicorp/helm provider, which uses helm3
terraform {
required_providers {
helm2 = {
source = "hashicorp/helm2"
}
helm = {
source = "hashicorp/helm"
version = ">= 2.0.0"
}
}
}
# you will find the doc here https://registry.terraform.io/providers/hashicorp/helm/0.10.6/docs
provider "helm2" {
install_tiller = false
namespace = "kube-system"
kubernetes {
...
}
}
# you will find the doc at latest version https://registry.terraform.io/providers/hashicorp/helm/latest/docs
provider "helm" {
kubernetes {
...
}
}
When initializing terraform, you will find that
- Finding latest version of hashicorp/helm...
- Finding latest version of hashicorp/helm2...
- Installing hashicorp/helm v2.0.2...
- Installed hashicorp/helm v2.0.2 (signed by HashiCorp)
- Installing hashicorp/helm2 v0.10.6...
- Installed hashicorp/helm2 v0.10.6 (unauthenticated)
Using it
Its pretty straightforward from this point. By default, helm resources will pick our updated helm provider at v2.0.2. You must explicitly use provider = helm2 for old resources (helm_repositoryand helm_releases in our case). Once migrated, you can remove it to use the default helm provider.
No you cannot do what you want. Terraform expects your constraint to match one plugin version as eluded to in:
Plugin Names and Versions
If multiple versions of a plugin are installed, Terraform will use the
newest version that meets the configuration's version constraints.
So your constraint cannot be parsed to match anyone plugin, hence the error

Should I version the .terraform folder?

I am starting to use Terraform and I have a .terraform folder created by "terraform init/apply" containing :
./plugins/linux_amd64/lock.json
./plugins/linux_amd64/terraform-provider-google
./plugins/modules/modules.json
terraform.tfstate
Should I version these files ? I would say no ...
The .terraform directory is a local cache where Terraform retains some files it will need for subsequent operations against this configuration. Its contents are not intended to be included in version control.
However, you can ensure that you can faithfully reproduce this directory on other systems by specifying certain things in your configuration that inform what Terraform will place in there:
Use required_providers in a terraform block to specify an exact version constraint for the Google Cloud Platform provider:
terraform {
required_providers {
google = "3.0.0"
}
}
(this relates to the .terraform/plugins directory)
In each module you call (which seems to be none so far, but perhaps in future), ensure its source refers to an exact version rather than to a floating branch (for VCS modules) or set version to an exact version (for modules from Terraform Registry):
module "example"
source = "git::https://github.com/example/example.git?ref=v2.0.0"
# ...
}
module "example"
source = "hashicorp/consul/aws"
version = "v1.2.0
}
(this relates to the .terraform/modules directory)
If you are using a remote backend, include the full configuration in the backend block inside the terraform block, rather than using the -backend-config argument to terraform init.
(this relates to the .terraform/terraform.tfstate file, which remembers your active backend configuration for later operations)

Terraform not finding third party plugin in directory specified for plugins

Attempting to get Terraform to work on windows 10 64 bit, using the Virtualbox provider plugin listed here (https://github.com/terra-farm/terraform-provider-virtualbox). I've verified that the plugin exists in %APPData%/terraform.d/plugins/windows_amd64 but it says it's not there. Have tried the following with no luck
Tried copying terraform.d to local instead of roaming for %APPData%.
Tried in the root directory where the terraform executible exists
Tried just the virtualbox folder instead of the entire plugin in both %APPData% locations
Tried in the folder where the terraform files exists
None have worked. It acts as if the folder it says to place the plugin, and where it's looking are mismatched, but I doubt something like that would have made it to release, so I'm at a loss as to why it's not seeing the plugin.
Terraform is the latest version. Using the following in my example.tf (the only tf file in the directory that i execute terraform form)
resource "virtualbox_vm" "node" {
count = 2
name = format("node-%02d", count.index + 1)
image = "https://app.vagrantup.com/ubuntu/boxes/bionic64/versions/20180903.0.0/providers/virtualbox.box"
cpus = 2
memory = "512 mib"
user_data = file("user_data")
network_adapter {
type = "hostonly"
host_interface = "vboxnet1"
}
}
output "IPAddr" {
value = element(virtualbox_vm.node.*.network_adapter.0.ipv4_address, 1)
}
output "IPAddr_2" {
value = element(virtualbox_vm.node.*.network_adapter.0.ipv4_address, 2)
}
Note: This answer was true at the time I wrote it, but Terraform v0.13 and later have a different directory layout for provider plugins, and some different options for configuring them. For more information, see the Provider Installation section of the CLI Configuration documentation. (and note that the CLI configuration is something different than the .tf files you use to describe your infrastructure; it contains global settings for Terraform CLI when running on your particular computer.)
Terraform looks for plugins in a number of locations, but the primary place for manually-installed plugins is in the "User Plugins Directory", which is either ~/.terraform.d/plugins on Unix systems or %APPDATA%\terraform.d\plugins on Windows.
The .terraform/plugins directory is not the place to put plugins you're installing manually. That directory is managed by Terraform itself and is the location for automatically-installed plugins. If you place plugins in that directory manually, terraform init may delete them as part of plugin installation.
Terraform also requires the provider executable to follow a particular naming scheme: terraform-provider-providername_vX.Y.Z, where the _vX.Y.Z part is technically optional but strongly recommended in order for version constraints to operate correctly. On Windows in particular, the file must also have the suffix .exe, because Terraform plugins are separate programs that Terraform will launch.
To debug Terraform's plugin discovery process, you can set the environment variable TF_LOG=debug before you run terraform init. In that output there will be lines like this:
2019/09/03 10:36:26 [DEBUG] checking for provider in "/home/username/.terraform.d/plugins"
If it finds any plugins in the various search paths, it will additionally produce lines like this:
2019/09/03 10:36:26 [DEBUG] found valid plugin: "example", "1.2.0", "/home/username/.terraform.d/plugins/terraform-provider-test_v1.2.0"
If there are any provider version constraints in the configuration than they must include whatever provider version you've installed. For example, with the above discovered provider that is example v1.2.0, a version constraint like ~> 2.0.0 would exclude it from consideration, even though Terraform discovered it.
To see how provider versions are constrained by your configuration, run terraform providers. If there are no constraints then it will just list the provider names, but if any constraints are present then they will be included in the output.

Resources