Providers not found in plugin-dir

Hi

We’re running Terraform in a GitLab CI/CD pipeline on a self hosted instance. In the pipeline, it is not possible for Terraform to download providers on the fly. Because of that, we’d like to pre-provide providers in some directory.

I am aware about https://developer.hashicorp.com/terraform/tutorials/automation/automate-terraform#pre-installed-plugins

Following these guidelines terraform init command has been set to run: terraform init -input=false -plugin-dir=/opt/terraform/plugins

But — it fails :slightly_frowning_face:

+ terraform init -input=false -plugin-dir=/opt/terraform/plugins
Initializing the backend...
Successfully configured the backend "http"! Terraform will automatically
use this backend unless the backend configuration changes.
Initializing modules...
Downloading git::https://company/terraform/modules/kvm.git for kvm...
- kvm in .terraform/modules/kvm
Initializing provider plugins...
- Finding dmacvicar/libvirt versions matching "0.7.6"...
- Finding hashicorp/dns versions matching "3.4.0"...
╷
│ Error: Failed to query available provider packages
│ 
│ Could not retrieve the list of available versions for provider
│ dmacvicar/libvirt: provider registry.terraform.io/dmacvicar/libvirt was not
│ found in any of the search locations
│ 
│   - /opt/terraform/plugins
╵
╷
│ Error: Failed to query available provider packages
│ 
│ Could not retrieve the list of available versions for provider
│ hashicorp/dns: provider registry.terraform.io/hashicorp/dns was not found
│ in any of the search locations
│ 
│   - /opt/terraform/plugins
╵

Contents of /opt/terraform/plugins are like this:

+ ls -lR /opt/terraform/plugins
/opt/terraform/plugins:
total 34584
drwxr-xr-x. 3 root root       23 Apr 15 12:38 registry.terraform.io
-rwxr-xr-x. 1 root root 18059264 Dec 12 15:32 terraform-provider-dns_v3.4.0_x5
-rwxr-xr-x. 2 root root 17326080 Nov 20 00:29 terraform-provider-libvirt_v0.7.6

/opt/terraform/plugins/registry.terraform.io:
total 0
drwxr-xr-x. 3 root root  21 Apr 15 12:38 dmacvicar

/opt/terraform/plugins/registry.terraform.io/dmacvicar:
total 0
drwxr-xr-x. 2 root root 47 Apr 15 12:38 libvirt

/opt/terraform/plugins/registry.terraform.io/dmacvicar/libvirt:
total 16920
-rwxr-xr-x. 2 root root 17326080 Nov 20 00:29 terraform-provider-libvirt_v0.7.6
$ terraform -version
Terraform v1.5.7
on linux_amd64

What am I doing wrong?

It seems, that the ZIP files for the providers need to be provided and not the unpacked files.

The directory structure/contents have now been made like this:

$ tree /opt/terraform/plugins
/opt/terraform/plugins
└── registry.terraform.io
    ├── dmacvicar
    │   └── libvirt
    │       └── terraform-provider-libvirt_0.7.6_linux_amd64.zip
    └── hashicorp
        └── dns
            └── terraform-provider-dns_3.4.0_linux_amd64.zip

5 directories, 2 files

$ ls -l /opt/terraform/plugins/registry.terraform.io/*/*/*
-rw-r--r--. 1 root root 6588449 Apr 15 13:14 /opt/terraform/plugins/registry.terraform.io/dmacvicar/libvirt/terraform-provider-libvirt_0.7.6_linux_amd64.zip
-rw-r--r--. 1 root root 6831200 Apr 15 13:17 /opt/terraform/plugins/registry.terraform.io/hashicorp/dns/terraform-provider-dns_3.4.0_linux_amd64.zip

That’s working fine. But that’s not how I understand the official documentation. Or am I misunderstanding it?

Hi @com_hashicorp,

The new structure you described in your second comment is using what the documentation refers to as the “packed layout”, where the packages are represented using their original zip files directly instead of using the contents of those zip files.

Terraform accepts that form but note that it means Terraform will re-extract that zip file each time you use terraform init, because Terraform ultimately needs to be able to run the executable inside.

I’m not entirely sure why your original attempt didn’t work, but I imagine there’s something slightly incorrect about the directory layout you created that is preventing Terraform from understanding it as the “unpacked” layout. Running terraform init with the environment variable TF_LOG=trace will cause Terraform to produce the internal logs that Terraform developers use for debugging, which might give some more clues about how Terraform understood your directory and why it concluded that no packages were available.

Hello @apparentlymart ,

happy to supply you debug output :slight_smile: See below.

I just realized, that I’ve got a ~/.terraformrc with this:

$ cat ~libvirt2821/.terraformrc
# ~/.terraformrc
provider_installation {
  direct {
    exclude = ["registry.terraform.io/*/*"]
  }
  network_mirror {
    url = "https://artifactory.company.tld/artifactory/api/terraform/terraform-remote/providers/"
  }
}

I removed it now, but I still get the exact same error.

And I’m now on Terraform v1.8.0.

+ ls -lR /opt/terraform/plugins
/opt/terraform/plugins:
total 34584
-rw-r--r--. 1 root root     4892 Nov 20 00:32 CHANGELOG.md
-rw-r--r--. 1 root root    10126 Nov 20 00:32 LICENSE
-rw-r--r--. 1 root root     6605 Nov 20 00:32 README.md
-rwxr-xr-x. 1 root root 18059264 Dec 12 15:32 terraform-provider-dns_v3.4.0_x5
-rwxr-xr-x. 1 root root 17326080 Nov 20 00:29 terraform-provider-libvirt_v0.7.6

+ env TF_LOG=trace terraform init -input=false -plugin-dir=/opt/terraform/plugins
2024-04-16T12:46:50.487+0200 [INFO]  Terraform version: 1.8.0
2024-04-16T12:46:50.487+0200 [DEBUG] using github.com/hashicorp/go-tfe v1.41.0
2024-04-16T12:46:50.487+0200 [DEBUG] using github.com/hashicorp/hcl/v2 v2.20.0
2024-04-16T12:46:50.487+0200 [DEBUG] using github.com/hashicorp/terraform-svchost v0.1.1
2024-04-16T12:46:50.487+0200 [DEBUG] using github.com/zclconf/go-cty v1.14.3
2024-04-16T12:46:50.487+0200 [INFO]  Go runtime version: go1.22.1
2024-04-16T12:46:50.487+0200 [INFO]  CLI args: []string{"terraform", "init", "-input=false", "-plugin-dir=/opt/terraform/plugins"}
2024-04-16T12:46:50.487+0200 [TRACE] Stdout is not a terminal
2024-04-16T12:46:50.487+0200 [TRACE] Stderr is not a terminal
2024-04-16T12:46:50.487+0200 [TRACE] Stdin is not a terminal
2024-04-16T12:46:50.487+0200 [DEBUG] Attempting to open CLI config file: /home/libvirt2821/.terraformrc
2024-04-16T12:46:50.487+0200 [INFO]  Loading CLI configuration from /home/libvirt2821/.terraformrc
2024-04-16T12:46:50.488+0200 [DEBUG] Explicit provider installation configuration is set
2024-04-16T12:46:50.488+0200 [TRACE] Selected provider installation method cliconfig.ProviderInstallationDirect with includes [] and excludes [registry.terraform.io/*/*]
2024-04-16T12:46:50.488+0200 [TRACE] Selected provider installation method cliconfig.ProviderInstallationNetworkMirror("https://artifactory.company.tld/artifactory/api/terraform/terraform-remote/providers/") with includes [] and excludes []
2024-04-16T12:46:50.488+0200 [INFO]  CLI command args: []string{"init", "-input=false", "-plugin-dir=/opt/terraform/plugins"}
2024-04-16T12:46:50.492+0200 [TRACE] Meta.Backend: built configuration for "http" backend with hash value 3307601501
2024-04-16T12:46:50.492+0200 [TRACE] Meta.Backend: backend has not previously been initialized in this working directory
2024-04-16T12:46:50.492+0200 [DEBUG] New state was assigned lineage "498ca57b-86a1-b82f-5713-8a4eee6dca66"
2024-04-16T12:46:50.492+0200 [TRACE] Meta.Backend: moving from default local state only to "http" backend
Initializing the backend...
2024-04-16T12:46:50.492+0200 [DEBUG] checking for provisioner in "/opt/terraform/plugins"
2024-04-16T12:46:50.492+0200 [TRACE] backend/local: state manager for workspace "default" will:
 - read initial snapshot from terraform.tfstate
 - write new snapshots to terraform.tfstate
 - create any backup at terraform.tfstate.backup
2024-04-16T12:46:50.492+0200 [TRACE] statemgr.Filesystem: reading initial snapshot from terraform.tfstate
2024-04-16T12:46:50.492+0200 [TRACE] statemgr.Filesystem: snapshot file has nil snapshot, but that's okay
2024-04-16T12:46:50.492+0200 [TRACE] statemgr.Filesystem: read nil snapshot
2024-04-16T12:46:50.492+0200 [TRACE] Meta.Backend: ignoring local "default" workspace because its state is empty
2024-04-16T12:46:50.493+0200 [DEBUG] New state was assigned lineage "7403f958-ed9d-cf3f-6cc0-1022837fe76f"
2024-04-16T12:46:50.494+0200 [TRACE] Preserving existing state lineage "7403f958-ed9d-cf3f-6cc0-1022837fe76f"
Successfully configured the backend "http"! Terraform will automatically
use this backend unless the backend configuration changes.
2024-04-16T12:46:50.496+0200 [TRACE] Meta.Backend: instantiated backend of type *http.Backend
2024-04-16T12:46:50.496+0200 [DEBUG] checking for provisioner in "/opt/terraform/plugins"
2024-04-16T12:46:50.496+0200 [TRACE] Meta.Backend: backend *http.Backend does not support operations, so wrapping it in a local backend
2024-04-16T12:46:50.496+0200 [DEBUG] GET https://scm.company.tld/api/v4/projects/7261/terraform/state/vm
2024-04-16T12:46:50.516+0200 [ERROR] Checkpoint error: Get "https://checkpoint-api.hashicorp.com/v1/check/terraform?arch=amd64&os=linux&signature=4670b4da-900f-1457-24d5-4f3c0d53fad1&version=1.8.0": dial tcp 18.165.183.63:443: connect: network is unreachable
2024-04-16T12:46:50.670+0200 [TRACE] ModuleInstaller: installing child modules for . into .terraform/modules
Initializing modules...
2024-04-16T12:46:50.671+0200 [DEBUG] Module installer: begin my_mod
2024-04-16T12:46:50.671+0200 [TRACE] ModuleInstaller: my_mod is not yet installed
2024-04-16T12:46:50.671+0200 [TRACE] ModuleInstaller: cleaning directory .terraform/modules/my_mod prior to install of my_mod
2024-04-16T12:46:50.671+0200 [TRACE] ModuleInstaller: my_mod address "git::https://scm.company.tld/tci/tools/terraform/modules/my_mod.git?depth=1&ref=v3.0.0" will be handled by go-getter
2024-04-16T12:46:50.671+0200 [TRACE] getmodules: fetching "git::https://scm.company.tld/tci/tools/terraform/modules/my_mod.git?depth=1&ref=v3.0.0" to ".terraform/modules/my_mod"
Downloading git::https://scm.company.tld/tci/tools/terraform/modules/my_mod.git?depth=1&ref=v3.0.0 for my_mod...
2024-04-16T12:46:51.449+0200 [TRACE] ModuleInstaller: my_mod "git::https://scm.company.tld/tci/tools/terraform/modules/my_mod.git?depth=1&ref=v3.0.0" was downloaded to .terraform/modules/my_mod
- my_mod in .terraform/modules/my_mod
2024-04-16T12:46:51.454+0200 [DEBUG] Module installer: my_mod installed at .terraform/modules/my_mod
2024-04-16T12:46:51.454+0200 [TRACE] modsdir: writing modules manifest to .terraform/modules/modules.json
2024-04-16T12:46:51.460+0200 [DEBUG] init: overriding provider plugin search paths
2024-04-16T12:46:51.460+0200 [DEBUG] will search for provider plugins in [/opt/terraform/plugins]
Initializing provider plugins...
- Finding dmacvicar/libvirt versions matching "0.7.6"...
- Finding hashicorp/dns versions matching "3.4.0"...
╷
│ Error: Failed to query available provider packages
│ 
│ Could not retrieve the list of available versions for provider
│ dmacvicar/libvirt: provider registry.terraform.io/dmacvicar/libvirt was not
│ found in any of the search locations
│ 
│   - /opt/terraform/plugins
╵
╷
│ Error: Failed to query available provider packages
│ 
│ Could not retrieve the list of available versions for provider
│ hashicorp/dns: provider registry.terraform.io/hashicorp/dns was not found
│ in any of the search locations
│ 
│   - /opt/terraform/plugins
╵

Hi @com_hashicorp,

Thanks for that extra context.

Looking again at your original directory listings today I think I see the problem: you are missing the path segment that specifies which version of the provider the package is for.

The “unpacked layout” expects the following directory structure:

HOSTNAME/NAMESPACE/TYPE/VERSION/TARGET

…and that’s the path to the directory containing the contents of the provider’s distribution zip file, so there would be at least one executable file under that directory.

So for that libvirt plugin in particular, the correct path for the plugin executable would be:

registry.terraform.io/dmacvicar/libvirt/0.7.6/linux_amd64/terraform-provider-libvirt_v0.7.6

Hi @apparentlymart ,

hm, going to give it a try. But that’s NOT what’s shown in the documentation at Running Terraform in automation | Terraform | HashiCorp Developer, is it? There, they’ve got:

$  ls -lah /usr/lib/custom-terraform-plugins
-rwxrwxr-x 1 user user  84M Jun 13 15:13 terraform-provider-aws-v1.0.0-x3
-rwxrwxr-x 1 user user  84M Jun 13 15:15 terraform-provider-rundeck-v2.3.0-x3
-rwxrwxr-x 1 user user  84M Jun 13 15:15 terraform-provider-mysql-v1.2.0-x3

And that’s exactly what I was copying.

I’ve now got the following directory:

$ find /opt/terraform/plugins -ls
   787198      0 drwxrwxr-x   3  root     root           35 Apr 15 13:18 /opt/terraform/plugins
     1692      0 drwxr-xr-x   4  root     root           40 Apr 15 13:17 /opt/terraform/plugins/registry.terraform.io
   263492      0 drwxr-xr-x   3  root     root           21 Apr 15 12:38 /opt/terraform/plugins/registry.terraform.io/dmacvicar
   534782      0 drwxr-xr-x   3  root     root          112 Apr 16 19:53 /opt/terraform/plugins/registry.terraform.io/dmacvicar/libvirt
   535168      8 -rw-r--r--   1  root     root         4892 Nov 20 00:32 /opt/terraform/plugins/registry.terraform.io/dmacvicar/libvirt/CHANGELOG.md
   535169     12 -rw-r--r--   1  root     root        10126 Nov 20 00:32 /opt/terraform/plugins/registry.terraform.io/dmacvicar/libvirt/LICENSE
   535170      8 -rw-r--r--   1  root     root         6605 Nov 20 00:32 /opt/terraform/plugins/registry.terraform.io/dmacvicar/libvirt/README.md
   535171  16920 -rwxr-xr-x   1  root     root     17326080 Nov 20 00:29 /opt/terraform/plugins/registry.terraform.io/dmacvicar/libvirt/terraform-provider-libvirt_v0.7.6
   266075      0 drwxr-xr-x   3  root     root           25 Apr 16 19:53 /opt/terraform/plugins/registry.terraform.io/dmacvicar/libvirt/0.7.6
   534783      0 drwxr-xr-x   2  root     root           47 Apr 16 19:53 /opt/terraform/plugins/registry.terraform.io/dmacvicar/libvirt/0.7.6/linux_amd64
   535172  16920 -rwxr-xr-x   1  root     root     17326080 Nov 20 00:29 /opt/terraform/plugins/registry.terraform.io/dmacvicar/libvirt/0.7.6/linux_amd64/terraform-provider-libvirt_v0.7.6
   787199      0 drwxr-xr-x   3  root     root           17 Apr 15 13:17 /opt/terraform/plugins/registry.terraform.io/hashicorp
     1693      0 drwxr-xr-x   2  root     root           46 Apr 16 19:46 /opt/terraform/plugins/registry.terraform.io/hashicorp/dns
     1695  17636 -rwxr-xr-x   1  root     root     18059264 Dec 12 15:32 /opt/terraform/plugins/registry.terraform.io/hashicorp/dns/terraform-provider-dns_v3.4.0_x5

And, yes, with that, it DOES work. Only having /opt/terraform/plugins/registry.terraform.io/dmacvicar/libvirt/terraform-provider-libvirt_v0.7.6 was not sufficient.

(Yes, it now failed for dns, but that’s okay. It would work with proper directories.)

Thanks a lot for your help!

Did I actually misunderstand the docs?

Thanks.

The documentation I referred to when I was writing my answer was this one:

The doc you were referring to unfortunately seems to be very stale and is describing how Terraform CLI behaved in Terraform v0.12 and earlier, before there was any support for non-HashiCorp providers. I guess it was missed because that guide is not specifically about provider mirrors but only mentions them as a side-topic. It would probably be better for the guide to just mention the idea of mirrors and then link to the main docs on this topic that I linked above.

1 Like

Oh, and one more thing:

That libvirt provider seems to have some other files in its package in addition to just the plugin executable. If that’s true then you should extract the entire zip file into the leaf directory, because otherwise a checksum calculated from the mirror directory won’t match a checksum calculated from the official package, and so you will probably run into problems with dependency lock file mismatches.

(Since the system where you are running Terraform is configured to never use the origin registry that one can’t possibly learn the checksum of the official package anyway, but having an incorrect checksum in the lock file will cause problems if you use this Terraform configuration in a different location later, such as in a local development environment. So less confusing all round to get it right from the start, even if your segregated execution environment won’t care about it today.)

1 Like