Terraform
Combining Protocol Version 5 Providers
The tf5muxserver
package enables combining any number of protocol version 5 provider servers into a single server.
Use this package to:
- Support multiple development teams across separate codebases.
- Support multiple provider SDK implementations. For example, you could downgrade an existing terraform-plugin-framework provider to protocol version 5 and then combine it with one or more providers built with terraform-plugin-sdk/v2.
Compatibility
Protocol version 5 provider servers are compatible with Terraform CLI 0.12 and later.
The following provider server implementations are supported:
- terraform-plugin-sdk/v2: A higher-level SDK that makes Terraform provider development easier by abstracting implementation details.
- terraform-plugin-go tf5server: A lower-level SDK to develop Terraform providers for more advanced use cases.
- tf6to5server: A package to translate protocol version 6 providers into protocol version 5.
Requirements
To be combined, provider servers must meet the following requirements:
- All provider schemas (except block
MinItems
/MaxItems
) must match. - All provider meta schemas must match.
- Only one provider may implement each resource and data source.
If publishing to the Terraform Registry, set metadata.protocol_versions
to ["5.0"]
in the Terraform Registry manifest file.
Code Implementation
Use the tf5muxserver.NewMuxServer()
function to create a combined provider server. Most providers implement this method in the provider main()
function of the root directory main.go
file or within a shared internal package to enable testing for the combined provider. You can use tf5server.WithManagedDebug()
to enable debugger support.
The following example implements tf5muxserver.NewMuxServer()
in a main()
function.
package main
import (
"context"
"flag"
"log"
"github.com/example/terraform-provider-example/internal/provider1"
"github.com/example/terraform-provider-example/internal/provider2"
"github.com/example/terraform-provider-example/internal/framework_provider"
"github.com/hashicorp/terraform-plugin-framework/providerserver"
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
"github.com/hashicorp/terraform-plugin-go/tfprotov5/tf5server"
"github.com/hashicorp/terraform-plugin-mux/tf5muxserver"
)
var (
// Version can be updated by goreleaser on release
version string = "dev"
)
func main() {
debugFlag := flag.Bool("debug", false, "Start provider in debug mode.")
flag.Parse()
ctx := context.Background()
providers := []func() tfprotov5.ProviderServer{
// Example terraform-plugin-sdk/v2 providers
provider1.New(version)().GRPCProvider,
provider2.New(version)().GRPCProvider,
// Example terraform-plugin-framework provider
providerserver.NewProtocol5(
framework_provider.New(version)(),
),
}
muxServer, err := tf5muxserver.NewMuxServer(ctx, providers...)
if err != nil {
log.Fatal(err)
}
var serveOpts []tf5server.ServeOpt
if *debugFlag {
serveOpts = append(serveOpts, tf5server.WithManagedDebug())
}
err = tf5server.Serve(
"registry.terraform.io/example/example",
muxServer.ProviderServer,
serveOpts...,
)
if err != nil {
log.Fatal(err)
}
}
Testing Implementation
You can test individual providers using the same acceptance tests as before. Set the ProtoV5ProviderFactories
field of TestCase
with an instance of the mux server, instead of declaring the provider with other TestCase
fields such as ProviderFactories
.
The following example uses the acceptance testing framework to create a test for two providers.
resource.Test(t, resource.TestCase{
// ... other TestCase fields ...
ProtoV5ProviderFactories: map[string]func() (tfprotov5.ProviderServer, error) {
"example": func() (tfprotov5.ProviderServer, error) {
ctx := context.Background()
providers := []func() tfprotov5.ProviderServer{
// Example terraform-plugin-sdk/v2 providers
provider1.New(version)().GRPCProvider,
provider2.New(version)().GRPCProvider,
// Example terraform-plugin-framework provider
providerserver.NewProtocol5(
framework_provider.New(version)(),
),
}
muxServer, err := tf5muxserver.NewMuxServer(ctx, providers...)
if err != nil {
return nil, err
}
return muxServer.ProviderServer(), nil
},
},
})
Refer to the acceptance tests documentation for more details.