Search code examples
dockerterraformcicd

Canonical way to make terraform use only local plugins


As far as I can see there are three ways to make Terraform use prepopulated plugins (to prevent downloads from web on init command).

  1. terraform provider mirror command + provider_installation in .terraformrc (or terraform.rc)
  2. terraform init -plugin-dir command
  3. warming up provider-plugin-cache

Are they all equivalent? Which one is recommended? My use case is building "deployer" docker image for CI/CD pipeline and also I am considering the possibility to use Terraform under Terraspace.


Solution

  • The first two of these are connected in that they all share the same underlying mechanism: the "filesystem mirror" plugin installation method.

    Using terraform init -plugin-dir makes Terraform in effect construct a one-off provider_installation block which contains only a single filesystem_mirror block referring to the given directory. It allows you to get that effect for just one installation operation, rather than configuring it in a central place for all future commands.

    Specifically, if you run terraform init -plugin-dir=/example then that's functionally equivalent to the following CLI configuration:

    provider_installation {
      filesystem_mirror {
        path = "/example"
      }
    }
    

    The plugin cache directory is different because Terraform will still access the configured installation methods (by default, the origin registry for each provider) but will skip downloading the plugin package file (the file actually containing the plugin code, as opposed to the metadata about the release) if it's already in the cache. Similarly, it'll save any new plugin package it downloads into the cache for future use.

    This therefore won't stop Terraform from trying to install any new plugins it encounters via network access to the origin registry. It is just an optimization to avoid re-downloading the same package repeatedly.


    There is a final approach which is similar to the first one but with a slight difference: Implied Local Mirror Directories.

    If you don't have a provider_installation block in your configuration then Terraform will construct one for itself by searching the implied mirror directories and treating any provider it finds there as a local-only one. For example, if /usr/share/terraform/plugins contains any version of registry.terraform.io/hashicorp/aws (the official AWS provider) then Terraform will behave as if it were configured as follows:

    provider_installation {
      filesystem_mirror {
        path    = "/usr/share/terraform/plugins"
        include = ["registry.terraform.io/hashicorp/aws"]
      }
      direct {
        exclude = ["registry.terraform.io/hashicorp/aws"]
      }
    }
    
    

    This therefore makes Terraform treat the local directory as the only possible installation source for that particular provider, but still allows Terraform to fetch any other providers from upstream if requested.


    If your requirement is for terraform init to not consult any remote services at all for the purposes of plugin installation, the approach directly intended for that case is to write a provider_installation block with only a filesystem_mirror block inside of it, which will therefore disable the direct {} installation method and thus prevent Terraform from trying to access the origin registry for any provider.