Implement a function
In this tutorial, you will add a function to a provider that calculates the tax rate for a fictional coffee-shop application called Hashicups. To do this, you will:
- Implement the function.
Thecompute_tax
function takes a price, a tax rate, and returns the total cost including tax. - Reference the function.
You will update your configuration to use the function and verify that it works as expected. You will add tests for the function in the next tutorial.
Provider-defined functions allow provider developers to define functions that encapsulate offline, computational logic. Practitioners can call functions from their Terraform configuration. Unlike resources and data sources, functions do not manage infrastructure or retrieve data from APIs.
Note
Provider-defined functions require Terraform version 1.8+.Prerequisites
To follow this tutorial, you need:
- Go 1.21+ installed and configured.
- Terraform v1.8+ installed locally.
- Docker and Docker Compose to run an instance of HashiCups locally.
Navigate to your terraform-provider-hashicups
directory.
Your code should match the 08-import-order
directory
from the example repository.
Implement the function
Provider functions are types that implement the function.Function
interface from the plugin framework.
The interface requires the following:
- 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 file named internal/provider/compute_tax_function.go
and paste in the contents below.
internal/provider/compute_tax_function.go
package provider import ( "context" "github.com/hashicorp/terraform-plugin-framework/function" "math") // Ensure the implementation satisfies the desired interfaces.var _ function.Function = &ComputeTaxFunction{} type ComputeTaxFunction struct{} func NewComputeTaxFunction() function.Function { return &ComputeTaxFunction{}} func (f *ComputeTaxFunction) Metadata(ctx context.Context, req function.MetadataRequest, resp *function.MetadataResponse) { resp.Name = "compute_tax"} func (f *ComputeTaxFunction) Definition(ctx context.Context, req function.DefinitionRequest, resp *function.DefinitionResponse) { resp.Definition = function.Definition{ Summary: "Compute tax for coffee", Description: "Given a price and tax rate, return the total cost including tax.", Parameters: []function.Parameter{ function.Float64Parameter{ Name: "price", Description: "Price of coffee item.", }, function.Float64Parameter{ Name: "rate", Description: "Tax rate. 0.085 == 8.5%", }, }, Return: function.Float64Return{}, }} func (f *ComputeTaxFunction) Run(ctx context.Context, req function.RunRequest, resp *function.RunResponse) { var price float64 var rate float64 var total float64 // Read Terraform argument data into the variables resp.Error = function.ConcatFuncErrors(resp.Error, req.Arguments.Get(ctx, &price, &rate)) total = math.Round((price+price*rate)*100) / 100 // Set the result resp.Error = function.ConcatFuncErrors(resp.Error, resp.Result.Set(ctx, total))}
The Run
method includes the code that implements the function itself. First,
it retrieves the function arguments, then calculates the total price, and
finally sets the result, returning any errors. Provider-defined functions do
all of their work locally. You should never call remote APIs from within the
Run
method.
Providers that include functions must implement the
provider.ProviderWithFunctions
interface from the plugin framework.
Open the internal/provider/provider.go
file and replace the var
block with
the following.
internal/provider/provider.go
// Ensure the implementation satisfies the expected interfaces.var ( _ provider.Provider = &hashicupsProvider{} _ provider.ProviderWithFunctions = &hashicupsProvider{})
The ProviderWithFunctions
interface requires a Functions
method that returns
a list of the functions supported by your provider. Add the following to the end
of provider.go
.
internal/provider/provider.go
func (p *hashicupsProvider) Functions(_ context.Context) []func() function.Function { return []func() function.Function{ NewComputeTaxFunction, }}
Add the required package by replacing the import
statement at the beginning of the file with the following.
internal/provider/provider.go
import ( "context" "os" "github.com/hashicorp-demoapp/hashicups-client-go" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/function" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog")
Build and install the updated provider.
$ go install .
Verify the function
Create an examples/compute_tax
directory and navigate to it.
$ mkdir examples/compute_tax && cd "$_"
Create a main.tf
Terraform configuration file in this directory that calls the
compute_tax
function.
examples/compute_tax/main.tf
terraform { required_providers { hashicups = { source = "hashicorp.com/edu/hashicups" } } required_version = ">= 1.8.0"} provider "hashicups" { username = "education" password = "test123" host = "http://localhost:19090"} output "total_price" { value = provider::hashicups::compute_tax(5.00, 0.085)}
This configuration includes an output value set by calling the compute_tax
function. This represents the total price of an item that costs $5.00 after a
tax rate of 8.5%.
You call provider-defined functions with the syntax
provider::<PROVIDER_NAME>::<FUNCTION_NAME>(<ARGUMENTS>)
.
Apply this configuration to ensure that the compute_tax
function returns the
total price after tax is applied.
$ terraform apply -auto-approve##...Changes to Outputs: + total_price = 5.43 You can apply this plan to save these new output values to the Terraform state,without changing any real infrastructure. Apply complete! Resources: 0 added, 0 changed, 0 destroyed. Outputs: total_price = 5.43
Navigate to the terraform-provider-hashicups
directory.
$ cd ../..
Next steps
Congratulations! You have enhanced the hashicups
provider by adding a
function.
If you were stuck during this tutorial, checkout the
09-functions
directory in the example repository to see the code implemented in this
tutorial.
- 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.
- The example repository contains directories corresponding to each tutorial in this 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.