
2 3 0 Custom Terraform Provider: Resource Skeleton (Go)
In this episode, Andrew defines validateUUID() and providerConfigure() functions, and sets up the skeleton of resource().
Code Notes
schema.Resource:
Resource()represents a thing (entity) in Terraform that has a set of configurable attributes and a lifecycle (create, read, update and delete).
package main
import (
"context"
"log"
"fmt"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/plugin"
)
func main() {
plugin.Serve(&plugin.ServeOpts{
ProviderFunc: Provider,
})
fmt.Println("Hello, world!")
}
type Config struct {
Endpoint string
Token string
UserUuid string
}
func Provider() *schema.Provider {
var p *schema.Provider
p = &schema.Provider{
ResourcesMap: map[string]*schema.Resource{
"terratowns_home": Resource(),
},
DataSourcesMap: map[string]*schema.Resource{
},
Schema: map[string]*schema.Schema{
"endpoint": {
Type: schema.TypeString,
Required: true,
Description: "The endpoint for hte external service",
},
"token": {
Type: schema.TypeString,
Sensitive: true, // make the token as sensitive to hide it the logs
Required: true,
Description: "Bearer token for authorization",
},
"user_uuid": {
Type: schema.TypeString,
Required: true,
Description: "UUID for configuration",
ValidateFunc: validateUUID,
},
},
}
p.ConfigureContextFunc = providerConfigure(p)
return p
}
func validateUUID(v interface{}, k string) (ws []string, errors []error) {
log.Print("validateUUID:start")
value := v.(string)
if _, err := uuid.Parse(value); err != nil {
errors = append(errors, fmt.Errorf("invalid UUID format"))
}
log.Print("validateUUID:end")
return
}
func providerConfigure(p *schema.Provider) schema.ConfigureContextFunc {
return func(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics ) {
log.Print("providerConfigure:start")
config := Config{
Endpoint: d.Get("endpoint").(string),
Token: d.Get("token").(string),
UserUuid: d.Get("user_uuid").(string),
}
log.Print("providerConfigure:end")
return &config, nil
}
}
func Resource() *schema.Resource {
log.Print("Resource:start")
resource := &schema.Resource{
CreateContext: resourceHouseCreate,
ReadContext: resourceHouseRead,
UpdateContext: resourceHouseUpdate,
DeleteContext: resourceHouseDelete,
}
log.Print("Resource:start")
return resource
}
func resourceHouseCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
var diags diag.Diagnostics
return diags
}
func resourceHouseRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
var diags diag.Diagnostics
return diags
}
func resourceHouseUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
var diags diag.Diagnostics
return diags
}
func resourceHouseDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
var diags diag.Diagnostics
return diags
}validateUUID()
validateUUID()func validateUUID(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if _, err := uuid.Parse(value); err != nil {
errors = append(errors, fmt.Errorf("invalid UUID format"))
}
return
}Line 1, function signature
Parameters
The parameter
vis of typeinterface{}, which can accept values of any data type.The parameter
kshould be a string.
Return value
This function will return two slices - a slice of strings (
ws) and another of errors (errors).
Line 2 value
The string from the
v(interface{})'s argument will be assigned to the variablevalue.:=is Go's way of declaring a variable..(string)is Go's way of casting a type to a value.a bit deeper: technically speaking, this is called "type assertion" in go and it is a bit different from casting in other languages.
Lines 3-4
if _, errFirst, assign the two return values from the function
uuid.Parse(value)to the variables_anderr. Note that the function uuid is coming from Google'suuidpackage for Go (see the import statement in line 7 in the source code at the top).If the variable
err's data type is notnil(err != nil), it means that there is an error. In that case, append the error values to theerrorsslice which will be later returned.
Line 6
returnstatementNote that what will be returned is not declared after the
returnkeyword. This is characteristic of Go, where it is not necessary to specify one as the return values are already declared in the function signature (which is thefuncstatement, line 1).
configureProvider()
configureProvider()func providerConfigure(p *schema.Provider) schema.ConfigureContextFunc {
return func(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics ) {
config := Config{
Endpoint: d.Get("endpoint").(string),
Token: d.Get("token").(string),
UserUuid: d.Get("user_uuid").(string),
}
return &config, nil
}
}Function signature, line 1
Parameters
pis an object ofschema.Providertype, which is a Terraform provider.
Return values
Interestingly, this function returns another function, which is a
schema.ConfigureContextFunc.Parameters: this function to be returned takes a context object from the
contextpackage, and aResourceDataobject from theschemapackage.context: is a package used to manage and control the lifecycle of processes and requests in Go applications.
Lines 3-7: The function internally constructs an object called
configwhich has three string values. This will be later used for authenticating our http requests and connecting to the cloud service (TerraTowns).Return values: this function returns a pointer to the
configstruct and anil.nilindicates that the configuration was successful.

Resources
Development workflow documentation: 2.3.0 Resource Skeleton
Last updated