Build a function-only provider with the Terraform Plugin Framework
Terraform provider developers can include functions in their providers. Users can call provider-defined functions and use the return value in their Terraform projects. Most Terraform providers include resources and data sources, but there are some use cases where you may want to create a provider that only contains functions.
In this tutorial, you will create a custom Terraform provider using the Terraform Plugin Framework and implement a function to parse RFC 3339 timestamps. You will also implement testing, logging, and documentation generation for your provider.
To create your function-only provider, you will:
- Set up your development environment.
Clone theterraform-provider-scaffolding-framework
repository and add additional files needed for testing. This repository contains a scaffold for a generic Terraform provider. - Implement the provider.
Prepare the provider code for all future implementation details. - Install and verify the provider locally.
Configure your environment to manually test your provider during development. - Implement the function.
Write the function code, and verify that it works as expected. - Test the function.
Write tests for the function and run them. - Generate documentation.
Generate the documentation for your provider and function.
Prerequisites
To follow this tutorial, you will need:
- Go 1.21+ installed and configured.
- Terraform v1.8+ installed locally.
Set up your development environment
Clone the Terraform Provider Scaffolding Framework repository.
We recommend that you use this scaffolding repository as a starting point for any new providers you create. It contains a template for a new provider that you will extend and customize.
Rename the directory to terraform-provider-exampletime
.
Change into the cloned repository.
Rename the go.mod
module.
Then, install all the provider's dependencies.
Open the main.go
file in the terraform-provider-exampletime
repository's root
directory. This file implements the provider server which interfaces with Terraform.
Replace the existing code in main.go
with the following.
The main
function servers your provider so that Terraform can communicate with
it over RPC. This example uses a specially formatted provider address so that
you can test and develop your provider locally.
Implement provider type
Providers use an implementation of the provider.Provider
interface type as the
starting point for all implementation details. To implement your function-only
provider, ensure that your provider satisfies both the Provider
and
ProviderWithFunctions
interfaces.
develop your provider in the internal/provider
directory. The directory from
the scaffolding repository contains an example resource, data source, and
function that you can use as a starting point when you develop your own
providers. Remove these files for this tutorial.
Open the internal/provider/provider.go
file and replace the existing code with
the following.
The Provider
interface requires:
- A
Metadata
method that sets the provider name and version. - A
Schema
method that defines the attributes used to configure the provider. - A
Configure
method that configures the provider and API client. - A
DataSources
method that returns a list of the data sources implemented by the provider. - A
Resources
method that returns a list of the resources implemented by the provider.
Unlike providers that include resources and data sources, function-only
providers should avoid logic which is network-based, so they usually do not need
to implement any code in the Configure and Schema methods. In addition,
function-only providers will return nil
from the DataSources
and Resources
methods.
The ProviderWithFunctions
interface adds a required method:
- A
Functions
method that returns the list of functions implemented by the provider.
Later in this tutorial, you will update the Functions
method to return your
function.
The Provider
interface requires:
- A
Metadata
method that sets the provider name and version. - A
Schema
method that defines the attributes used to configure the provider. - A
Configure
method that configures the provider and API client. - A
DataSources
method that returns a list of the data sources implemented by the provider. - A
Resources
method that returns a list of the resources implemented by the provider.
Unlike providers that include resources and data sources, function-only
providers should not call upstream APIs, so they usually do not need to
implement any code in the Configure and Schema methods. In addition,
function-only providers will return nil
from the DataSources
and Resources
methods.
The ProviderWithFunctions
interface adds a required method:
- A
Functions
method that returns the list of functions implemented by the provider.
Later in this tutorial, you will update the Functions
method to return your
function.
Configure Terraform for local provider installation
Terraform installs providers and verifies their versions and checksums when you
run terraform init
. Terraform downloads your providers from either the
provider registry or a local registry. However, while building your provider, you
will want to test Terraform configuration against a local development build of
the provider. Development builds do not have an associated version number
or an official set of checksums listed in a provider registry.
You can reference a local build of a provider by setting a dev_overrides
block in a configuration file called .terraformrc
. This block overrides all
other configured installation methods. When you run terraform
commands,
Terraform will search for the .terraformrc
file in your home directory and
apply these configuration settings.
First, find the GOBIN
path where Go installs your binaries. Your path may vary depending on your Go environment variables.
If the GOBIN
go environment variable is not set, use the default path,
/Users/<Username>/go/bin
.
Create a new file called .terraformrc
in your home directory (~
), then add the dev_overrides
block below. Change the <PATH>
to the value returned from the go env GOBIN
command above.
Locally install provider and verify with Terraform
Your Terraform CLI is now ready to use the locally installed provider in the
GOBIN
path. Use the go install
command from the example repository's root
directory to compile the provider into a binary and install it in your GOBIN
path.
Create an examples/functions/rfc3339_parse
directory and navigate to it. You will create example configuration that references your provider in this directory.
Create a function.tf
file with the following.
This configuration refers to a "rfc3339_parse" function that your provider does
not yet support. You will implement this function in the next section.
You can invoke provider-defined functions with the syntax
provider::<PROVIDER_NAME>::<FUNCTION_NAME>(<ARGUMENTS>)
.
Running a Terraform plan now will report the provider override, as well as an error about the missing function. Despite the error, this step verifies that Terraform successfully referenced the locally installed provider.
Run a Terraform plan and review the warning and error.
Navigate to the terraform-provider-exampletime
directory.
Implement the function
Provider functions are types that implement the function.Function
interface
from the plugin framework.
The Function interface requires:
- A
Metadata
method that sets the function name. Unlike resources and data sources, function names do not start with the provider name. - A
Definition
method that defines the parameters, return value, and documentation for the function. - A
Run
method that executes the function code.
Create a new internal/provider/function_rfc3339_parse.go
file.
Note
This function is based on code from HashiCorp's time provider. You can review the time provider's code for an example of a provider that implements resources, data sources, and functions.
Add the function to your provider. Open the internal/provider/provider.go
file
and replace the Functions
method with the following.
Build and install the updated provider.
Verify the function
Navigate to the examples/functions/rfc3339_parse
directory.
Apply the configuration to ensure that the rfc3339
function parses the
formatted time string into a timestamp object.
Navigate to the terraform-provider-exampletime
directory.
Test the function
Create acceptance tests to verify that your functions work as expected.
Configure your tests to use the exampletime
provider. Open the
internal/provider/provider_test.go
file and replace the
testAccProtoV6ProviderFactories
variable definition with the following.
Add tests for your function by creating a new
internal/provider/function_rfc3339_parse_test.go
file with the following.
These tests ensure the function works as expected, including raising an error for invalid input. Like other unit tests, your Terraform provider tests should cover both normal and error cases.
Verify function testing functionality
Now that you implemented tests for the rfc3339_parse
function, run them.
Navigate to the internal/provider
directory.
Run Go testing with the TF_ACC
environment variable set. The test framework
will report that your function's test passed.
Navigate to the terraform-provider-exampletime
directory.
Generate function documentation
The tfplugindocs
tool will generate documentation for functions based on the
function definition. Open the function_rfc3339_parse.go
file and review the
Definition
method.
Add configuration examples
The tfplugindocs
tool will automatically include Terraform configuration
examples from files with the following naming conventions:
- Provider:
examples/provider/provider.tf
- Resources:
examples/resources/TYPE/resource.tf
- Data Sources:
examples/data-sources/TYPE/data-source.tf
- Functions:
examples/functions/TYPE/function.tf
Replace TYPE
with the name of the resource, data source, or function. For
example: examples/functions/rfc3339_parse/function.tf
.
The scaffolding framework includes examples that you can refer to when you create your own provider. Remove these examples for this tutorial.
Replace the examples/provider/provider.tf
file with the following.
If your provider requires any arguments, include an example of their usage in this file.
Now that you have implemented the documentation generation functionality for
your provider, run the go generate ./...
command to generate the
documentation.
View the docs/functions/rfc3339_parse.md
file to verify that it contains the
documentation for your function.
Next steps
Congratulations! You have created a function-only provider. While most Terraform providers will include resources and data sources, you can create and distribute function-only providers that do not rely on remote APIs.
- To learn more about the Terraform Plugin Framework, refer to the Terraform Plugin Framework documentation.
- For a full capability comparison between the SDKv2 and the Plugin Framework, refer to the Which SDK Should I Use? documentation.
- To create a provider than includes resources and data sources, follow the Custom Framework Providers tutorial collection.
- Submit any Terraform Plugin Framework bug reports or feature requests to the development team in the Terraform Plugin Framework Github repository.
- Submit any Terraform Plugin Framework questions in the Terraform Plugin Framework Discuss forum.