Terraform
Migrating providers
Providers are Terraform plugins that define resources and data sources for practitioners to use. You serve your providers with a provider server so they can interact with Terraform. The provider schema defines the allowed attributes and other configuration data about your provider.
This page explains how to migrate a provider server, definition, and schema from SDKv2 to the plugin framework. Refer to the Migrating schema page in this migration guide to learn how to migrate your provider's schema to the framework.
Background
Terraform connects to your provider over RPC. The provider server connects to
Terraform and handles RPC calls between Terraform and your provider code. When
you migrate your provider from SDKv2 to Terraform, you must update your provider
server, provider definition, and provider schema. When you migrate using the
terraform-plugin-mux
module, your provider server will serve both the SDKv2
and framework versions of your provider. Once migration is complete, remove the
SDKv2 versions of your provider server, definition, and schema.
Serving the provider
You must update your provider's main.go
file to serve your framework provider.
Refer to Provider Servers in the
framework documentation for details about framework provider servers.
Migrate over multiple release cycles
You can use the terraform-plugin-mux
module to help you iteratively migrate
individual resources and data sources to the framework across multiple release
cycles. The module multiplexes, or muxes, data using SDKv2 and the framework
concurrently. Refer to Migration using a mux
server for more information.
Migrate in a single release cycle
If you are migrating your provider in a single release cycle, update your
provider's main()
function to serve the framework provider instead of the
SDKv2 version.
In SDKv2, the provider package's main
function serves the provider by calling
plugin.Serve
.
The following code shows a basic implementation for serving an SDKv2 provider.
SDKv2
func main() {
plugin.Serve(
&plugin.ServeOpts{
ProviderFunc: provider.New,
ProviderAddr: "registry.terraform.io/<namespace>/<provider_name>",
},
)
}
In the framework, serve your provider by calling providerserver.Serve
in your
provider package's main
function. Refer to Provider
Servers in the framework
documentation for details about provider servers.
The following code shows an equivalent implementation for serving a provider in the framework.
Framework
func main() {
err := providerserver.Serve(
context.Background(),
provider.New,
providerserver.ServeOpts{
Address: "registry.terraform.io/<namespace>/<provider_name>",
},
)
if err != nil {
log.Fatal(err)
}
}
Provider definition
Providers built with SDKv2 use a schema.Provider
struct to define their
behavior, while framework providers use a type that you define that implements
the provider.Provider
interface. Refer to
Providers in the framework
documentation for details about framework provider definitions.
When migrating using a mux provider server, you will implement both the SDKv2 and framework versions of your provider definition during the migration. Once you have completed the migration, remove the SDKv2 provider definition and related mux server code from your provider server.
The
ProviderFunc
field on plugin.ServeOpts
requires a pointer to schema.Provider
to access
your provider's schema. This is typically satisfied by calling a function that
returns a pointer to schema.Provider
.
The ResourcesMap
and DataSourcesMap
fields each contain a map of strings to
functions that each return a pointer to a schema.Resource
struct for the given
resource or data source.
The following example shows a basic implementation of an SDKv2 provider.
SDKv2
func New() *schema.Provider {
return &schema.Provider{
Schema: map[string]*schema.Schema{},
ConfigureContextFunc: configureContextFunc(),
ResourcesMap: map[string]*schema.Resource{
"resource_example": resourceExample(),
},
DataSourcesMap: map[string]*schema.Resource{
"dataSource_example": dataSourceExample(),
},
/* ... */
}
}
In the framework, the second argument to your provider.Serve
function requires
a function that returns a type satisfying the provider.Provider
interface.
The following code shows a typical implementation. In this implementation, the
Resources
method returns a slice of functions that return types that implement
the resource.Resource
interface. The DataSources
method returns a slice of
functions that return types that implement the datasource.DataSource
interface. Refer to the
Resources and Data
Sources pages in this guide
to implement these functions for your provider.
Framework
type exampleCloudProvider struct {
}
func New() provider.Provider {
return &exampleCloudProvider{}
}
func (p *exampleCloudProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) {
resp.TypeName = "examplecloud"
}
func (p *exampleCloudProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) {
resp.Schema = schema.Schema{}
}
func (p *exampleCloudProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
}
func (p *exampleCloudProvider) Resources(ctx context.Context) []func() resource.Resource {
return []func() resource.Resource {
func() resource.Resource {
return resourceExample{}
},
}
}
func (p *exampleCloudProvider) DataSources(ctx context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource {
func() datasource.DataSource {
return dataSourceExample{}
},
}
}
Migration notes
Remember the following differences between SDKv2 and the framework when you migrate your provider definition.
- In SDKv2, your provider's
New
function returns a pointer to aschema.Provider
struct. In the framework,New
returns a type that you define which satisfies theprovider.Provider
interface. - In SDKv2,
Schema
is a field onschema.Provider
that containsmap[string]*schema.Schema
, which maps attribute names toschema.Schema
structs. In the framework,Schema
is a method you define on your provider that returns your provider'sschema.Schema
struct. - In SDKv2,
ConfigureContextFunc
is a field onschema.Provider
containing a function that configures the provider. In the framework,Configure
is a function you define on your provider that configures your provider. - In SDKv2,
ResourcesMap
is a field onschema.Provider
containingmap[string]*schema.Resource
, which maps resource names toschema.Resource
structs. In the framework,Resources
is a method you define on your provider that returns[]func() resource.Resource
, a slice of resource types that you define which satisfy theresource.Resource
interface. - In SDKv2,
DataSourcesMap
is a field onschema.Provider
containingmap[string]*schema.Resource
, which maps data source names toschema.Resource
structs. SDKv2 data sources and resources both useschema.Resource
. In the framework,DataSources
is a method you define on your provider that returns[]func() datasource.DataSource
, a slice of data source types that you define which satisfy thedatasource.DataSource
interface.
Example
The following example shows how to set up a provider schema, configuration, resources, and data sources using SDKv2.
SDKv2
func New() (*schema.Provider, error) {
return &schema.Provider{
Schema: map[string]*schema.Schema{
"attribute": {
/* ... */
},
},
ConfigureContextFunc: configureProvider,
ResourcesMap: map[string]*schema.Resource{
"exampleResource": exampleResource(),
/* ... */
},
DataSourcesMap: map[string]*schema.Resource{
"exampleDataSource": exampleDataSource(),
/* ... */
},
}, nil
}
The following shows the same section of provider code after the migration.
Framework
var _ provider.Provider = (*exampleProvider)(nil)
func New() provider.Provider {
return &exampleProvider{}
}
func (p *exampleProvider) Resources(_ context.Context) []func() resource.Resource {
return []func() resource.Resource{
func() resource.Resource {
return &exampleResource{}
},
/* ... */
}
}
func (p *exampleProvider) DataSources(_ context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{
func() datasource.DataSource {
return &exampleDataSource{}
},
/* ... */
}
}
func (p *exampleProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"attribute": schema.SingleNestedBlock{
/* ... */
},
},
}
}
func (p *exampleProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) {
resp.TypeName = "example"
}
func (p *exampleProvider) Configure(_ context.Context, _ provider.ConfigureRequest, resp *provider.ConfigureResponse) {
/* ... */
}
Provider schema
A provider schema defines the attributes and behaviors of the provider. For example, a provider that connects to a third-party API may define attributes for the base URL or a required authentication token.
When you use a muxed provider server to migrate from SDKv2 to the framework, the SDKv2 version of your provider definition uses an SDKv2 provider schema, and your framework provider definition uses a framework provider schema. Once you have completed the migration, remove the SDKv2 provider schema along with the provider definition and related mux code from your provider server.
In SDKv2, you implement a provider schema by populating the Schema
field on
the schema.Provider
struct. The Schema
field contains a
map[string]*schema.Schema
. Each map entry represents the name of the attribute
and pointer to a schema.Schema
struct that defines that attribute's behavior.
In the framework, the Schema
method returns the provider schema. The Schema
method is part of the
provider.Provider
interface that your provider must implement. Schema
returns a struct
containing fields for Attributes
and Blocks
. These Attributes
and Blocks
contain map[string]schema.Attribute
and map[string]schema.Block
,
respectively. Refer to Providers -
Schema in the framework
documentation for details about framework provider schema.
The following example defines the provider schema in the Schema
field within
the schema.Provider
struct.
SDKv2
func New() *schema.Provider {
return &schema.Provider{
Schema: map[string]*schema.Schema{
/* ... */
},
The following code shows the Schema
method, which returns the provider schema.
SDKv2
func (p *ExampleCloudProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) {
resp.Schema = schema.Schema{
/* ... */
}
}
Refer to the Migrating schema page in this migration guide to learn how to migrate schema to the framework.
Migration notes
- In SDKv2,
schema.Schema
is a struct that defines attributes and behaviors, such asType
orOptional
. In the frameworkschema.Schema
is a struct that includes attributes and blocks.
Examples
Refer to the following examples when you migrate your provider.
Nested blocks and attributes
This example shows how to use a nested block and a nested attribute for the SDKv2 and framework examples, respectively. Refer to the Blocks with Computed Fields page in this guide for more details.
The following example shows the configuration of the example_attribute
attribute for the provider's example_block
configuration block in SDKv2.
SDKv2
Schema: map[string]*schema.Schema{
"example_block": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"example_attribute": {
Type: schema.TypeString,
Optional: true,
ValidateDiagFunc: validation.ToDiagFunc(validation.IsURLWithScheme(SupportedProxySchemesStr())),
ConflictsWith: []string{"example_block.0.another_attribute"},
},
/* ... */
The following shows the same section of provider code after the migration.
This code implements the example_attribute
attribute for the example_Block
block with the framework.
Framework
func (p *exampleProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) {
resp.Schema = schema.Schema{
/*...*/
Blocks: map[string]schema.Block{
"example_block": schema.ListNestedBlock{
NestedObject: schema.NestedBlockObject{
Attributes: map[string]schema.Attribute{
"example_attribute": schema.StringAttribute{
Optional: true,
Validators: []validator.String{
attribute_validator.UrlWithScheme(supportedProxySchemesStr()...),
stringvalidator.ConflictsWith(path.MatchRelative().AtParent().AtName("another_attribute")),
},
},
},
Validators: []validator.List{
listvalidator.SizeAtMost(1),
},