This module enables parsing, comparison and canonical representation of
Terraform Registry provider addresses
(such as registry.terraform.io/grafana/grafana or hashicorp/aws),
module addresses (such as hashicorp/subnets/cidr),
and component addresses (such as hashicorp/aws or example.com/myorg/mycomponent).
Provider addresses can be found in
terraform show -json <FILE>(full_name)terraform version -json(provider_selections)terraform providers schema -json(keys ofprovider_schemas)- within
required_providersblock in Terraform configuration (*.tf) - Terraform CLI configuration file
- Plugin reattach configurations
Module addresses can be found within source argument
of module block in Terraform configuration (*.tf)
and parts of the address (namespace and name) in the Registry API.
Component addresses can be found within the source argument
of the component block in Terraform Stack configuration (*.tfcomponent.hcl)
and parts of the address (namespace and name) in the Registry API.
The module assumes compatibility with Terraform v0.12 and later, which have the mentioned JSON output produced by corresponding CLI flags.
We recommend carefully reading the ambigouous provider addresses
section below which may impact versions 0.12 and 0.13.
Other libraries which may help with consuming most of the above Terraform outputs in automation:
pAddr, err := ParseProviderSource("hashicorp/aws")
if err != nil {
// deal with error
}
// pAddr == Provider{
// Type: "aws",
// Namespace: "hashicorp",
// Hostname: DefaultProviderRegistryHost,
// }mAddr, err := ParseModuleSource("hashicorp/consul/aws//modules/consul-cluster")
if err != nil {
// deal with error
}
// mAddr == Module{
// Package: ModulePackage{
// Host: DefaultProviderRegistryHost,
// Namespace: "hashicorp",
// Name: "consul",
// TargetSystem: "aws",
// },
// Subdir: "modules/consul-cluster",
// },cAddr, err := ParseComponentSource("hashicorp/aws//modules/vpc")
if err != nil {
// deal with error
}
// cAddr == Component{
// Package: ComponentPackage{
// Host: DefaultModuleRegistryHost,
// Namespace: "hashicorp",
// Name: "aws",
// },
// Subdir: "modules/vpc",
// }Modules can also be sourced from other sources
and these other sources (outside of Terraform Registry)
have different address formats, such as ./local or
github.com/hashicorp/example.
This library does not recognize such other address formats and it will return error upon parsing these.
Qualified addresses with namespace (such as hashicorp/aws)
are used exclusively in all recent versions (0.14+) of Terraform.
If you only work with Terraform v0.14.0+ configuration/output, you may
safely ignore the rest of this section and related part of the API.
There are a few types of ambiguous addresses you may comes accross:
- Terraform
v0.12uses "namespace-less address", such asaws. - Terraform
v0.13may use-as a placeholder for the unknown namespace, resulting in address such as-/aws. - Terraform
v0.14+configuration still allows ambiguous providers throughprovider "<NAME>" {}block without corresponding entry insiderequired_providers, but these providers are always resolved ashashicorp/<NAME>and all JSON outputs only use that resolved address.
Both ambiguous address formats are accepted by ParseProviderSource()
pAddr, err := ParseProviderSource("aws")
if err != nil {
// deal with error
}
// pAddr == Provider{
// Type: "aws",
// Namespace: UnknownProviderNamespace, // "?"
// Hostname: DefaultProviderRegistryHost, // "registry.terraform.io"
// }
pAddr.HasKnownNamespace() // == false
pAddr.IsLegacy() // == falsepAddr, err := ParseProviderSource("-/aws")
if err != nil {
// deal with error
}
// pAddr == Provider{
// Type: "aws",
// Namespace: LegacyProviderNamespace, // "-"
// Hostname: DefaultProviderRegistryHost, // "registry.terraform.io"
// }
pAddr.HasKnownNamespace() // == true
pAddr.IsLegacy() // == trueHowever NewProvider() will panic if you pass an empty namespace
or any placeholder indicating unknown namespace.
NewProvider(DefaultProviderRegistryHost, "", "aws") // panic
NewProvider(DefaultProviderRegistryHost, "-", "aws") // panic
NewProvider(DefaultProviderRegistryHost, "?", "aws") // panicIf you come across an ambiguous address, you should resolve it to a fully qualified one and use that one instead.
The Registry API provides the safest way of resolving an ambiguous address.
# grafana (redirected to its own namespace)
$ curl -s https://registry.terraform.io/v1/providers/-/grafana/versions | jq '(.id, .moved_to)'
"terraform-providers/grafana"
"grafana/grafana"
# aws (provider without redirection)
$ curl -s https://registry.terraform.io/v1/providers/-/aws/versions | jq '(.id, .moved_to)'
"hashicorp/aws"
nullWhen you cache results, ensure you have invalidation mechanism in place as target (migrated) namespace may change.
Like any other legacy address terraform is also ambiguous. Such address may
(most unlikely) represent a custom-built provider called terraform,
or the now archived hashicorp/terraform provider in the registry,
or (most likely) the terraform provider built into 0.11+, which is
represented via a dedicated FQN of terraform.io/builtin/terraform in 0.13+.
You may be able to differentiate between these different providers if you know the version of Terraform.
Alternatively you may just treat the address as the builtin provider, i.e. assume all of its logic including schema is contained within Terraform Core.
In such case you should construct the address in the following way
pAddr := NewProvider(BuiltInProviderHost, BuiltInProviderNamespace, "terraform")