| // Copyright IBM Corp. 2014, 2026 | |
| // SPDX-License-Identifier: BUSL-1.1 | |
| // Package init contains the list of backends that can be initialized and | |
| // basic helper functions for initializing those backends. | |
| package init | |
| import ( | |
| "sync" | |
| "github.com/hashicorp/terraform-svchost/disco" | |
| "github.com/hashicorp/terraform/internal/backend" | |
| "github.com/hashicorp/terraform/internal/tfdiags" | |
| "github.com/zclconf/go-cty/cty" | |
| "github.com/hashicorp/terraform/internal/backend/backendrun" | |
| backendLocal "github.com/hashicorp/terraform/internal/backend/local" | |
| backendRemote "github.com/hashicorp/terraform/internal/backend/remote" | |
| backendAzure "github.com/hashicorp/terraform/internal/backend/remote-state/azure" | |
| backendConsul "github.com/hashicorp/terraform/internal/backend/remote-state/consul" | |
| backendCos "github.com/hashicorp/terraform/internal/backend/remote-state/cos" | |
| backendGCS "github.com/hashicorp/terraform/internal/backend/remote-state/gcs" | |
| backendHTTP "github.com/hashicorp/terraform/internal/backend/remote-state/http" | |
| backendInmem "github.com/hashicorp/terraform/internal/backend/remote-state/inmem" | |
| backendKubernetes "github.com/hashicorp/terraform/internal/backend/remote-state/kubernetes" | |
| backendOCI "github.com/hashicorp/terraform/internal/backend/remote-state/oci" | |
| backendOSS "github.com/hashicorp/terraform/internal/backend/remote-state/oss" | |
| backendPg "github.com/hashicorp/terraform/internal/backend/remote-state/pg" | |
| backendS3 "github.com/hashicorp/terraform/internal/backend/remote-state/s3" | |
| backendCloud "github.com/hashicorp/terraform/internal/cloud" | |
| ) | |
| // backends is the list of available backends. This is a global variable | |
| // because backends are currently hardcoded into Terraform and can't be | |
| // modified without recompilation. | |
| // | |
| // To read an available backend, use the Backend function. This ensures | |
| // safe concurrent read access to the list of built-in backends. | |
| // | |
| // Backends are hardcoded into Terraform because the API for backends uses | |
| // complex structures and supporting that over the plugin system is currently | |
| // prohibitively difficult. For those wanting to implement a custom backend, | |
| // they can do so with recompilation. | |
| var backends map[string]backend.InitFn | |
| var backendsLock sync.Mutex | |
| // RemovedBackends is a record of previously supported backends which have | |
| // since been deprecated and removed. | |
| var RemovedBackends map[string]string | |
| // Init initializes the backends map with all our hardcoded backends. | |
| func Init(services *disco.Disco) { | |
| backendsLock.Lock() | |
| defer backendsLock.Unlock() | |
| backends = map[string]backend.InitFn{ | |
| "local": func() backend.Backend { return backendLocal.New() }, | |
| "remote": func() backend.Backend { return backendRemote.New(services) }, | |
| // Remote State backends. | |
| "azurerm": func() backend.Backend { return backendAzure.New() }, | |
| "consul": func() backend.Backend { return backendConsul.New() }, | |
| "cos": func() backend.Backend { return backendCos.New() }, | |
| "gcs": func() backend.Backend { return backendGCS.New() }, | |
| "http": func() backend.Backend { return backendHTTP.New() }, | |
| "inmem": func() backend.Backend { return backendInmem.New() }, | |
| "kubernetes": func() backend.Backend { return backendKubernetes.New() }, | |
| "oss": func() backend.Backend { return backendOSS.New() }, | |
| "pg": func() backend.Backend { return backendPg.New() }, | |
| "s3": func() backend.Backend { return backendS3.New() }, | |
| "oci": func() backend.Backend { return backendOCI.New() }, | |
| // HCP Terraform 'backend' | |
| // This is an implementation detail only, used for the cloud package | |
| "cloud": func() backend.Backend { return backendCloud.New(services) }, | |
| } | |
| RemovedBackends = map[string]string{ | |
| "artifactory": `The "artifactory" backend is not supported in Terraform v1.3 or later.`, | |
| "azure": `The "azure" backend name has been removed, please use "azurerm".`, | |
| "etcd": `The "etcd" backend is not supported in Terraform v1.3 or later.`, | |
| "etcdv3": `The "etcdv3" backend is not supported in Terraform v1.3 or later.`, | |
| "manta": `The "manta" backend is not supported in Terraform v1.3 or later.`, | |
| "swift": `The "swift" backend is not supported in Terraform v1.3 or later.`, | |
| } | |
| } | |
| // Backend returns the initialization factory for the given backend, or | |
| // nil if none exists. | |
| func Backend(name string) backend.InitFn { | |
| backendsLock.Lock() | |
| defer backendsLock.Unlock() | |
| return backends[name] | |
| } | |
| func BackendExists(name string) bool { | |
| backendsLock.Lock() | |
| defer backendsLock.Unlock() | |
| _, ok := backends[name] | |
| return ok | |
| } | |
| // Set sets a new backend in the list of backends. If f is nil then the | |
| // backend will be removed from the map. If this backend already exists | |
| // then it will be overwritten. | |
| // | |
| // This method sets this backend globally and care should be taken to do | |
| // this only before Terraform is executing to prevent odd behavior of backends | |
| // changing mid-execution. | |
| func Set(name string, f backend.InitFn) { | |
| backendsLock.Lock() | |
| defer backendsLock.Unlock() | |
| if f == nil { | |
| delete(backends, name) | |
| return | |
| } | |
| backends[name] = f | |
| } | |
| // deprecatedBackendShim is used to wrap a backend and inject a deprecation | |
| // warning into the Validate method. | |
| type deprecatedBackendShim struct { | |
| backend.Backend | |
| Message string | |
| } | |
| // PrepareConfig delegates to the wrapped backend to validate its config | |
| // and then appends shim's deprecation warning. | |
| func (b deprecatedBackendShim) PrepareConfig(obj cty.Value) (cty.Value, tfdiags.Diagnostics) { | |
| newObj, diags := b.Backend.PrepareConfig(obj) | |
| return newObj, diags.Append(tfdiags.SimpleWarning(b.Message)) | |
| } | |
| // DeprecateBackend can be used to wrap a backend to retrun a deprecation | |
| // warning during validation. | |
| func deprecateBackend(b backend.Backend, message string) backend.Backend { | |
| // Since a Backend wrapped by deprecatedBackendShim can no longer be | |
| // asserted as an Operations Backend or Local backend, disallow those types here | |
| // entirely. If something other than a basic backend.Backend needs to be | |
| // deprecated, we can add that functionality to schema.Backend or the | |
| // backend itself. | |
| if _, ok := b.(backendrun.OperationsBackend); ok { | |
| panic("cannot use DeprecateBackend on a Backend that supports operations") | |
| } | |
| if _, ok := b.(backendrun.Local); ok { | |
| panic("cannot use DeprecateBackend on a Local Backend") | |
| } | |
| return deprecatedBackendShim{ | |
| Backend: b, | |
| Message: message, | |
| } | |
| } |