In this tutorial, you will add custom error messages to a Terraform provider that interacts with the API of a fictional coffee-shop application, HashiCups and view detailed Terraform provider logs.
»Prerequisites
To follow this tutorial, you need:
- a Golang 1.13+ installed and configured.
- the Terraform 0.14+ CLI installed locally. The Terraform binaries are located here.
- Docker and Docker Compose to run an instance of HashiCups locally.
Navigate to your terraform-provider-hashicups
directory. Then, checkout the implement-complex-read
branch. This step is optional but recommended to insure that you've accurately completed the previous steps.
$ git checkout implement-complex-read
Your directory should have the following structure.
$ tree -L 2
.
├── Makefile
├── README.md
├── docker_compose
│ ├── conf.json
│ └── docker-compose.yml
├── examples
│ ├── coffee
│ ├── main.tf
├── go.mod
├── go.sum
├── hashicups
│ ├── data_source_coffee.go
│ └── provider.go
├── main.go
└── vendor
If you’re stuck, refer to the debug-tf-provider
branch to see the changes implemented in this tutorial.
»Update error messages
In your hashicups/provider.go
file, the providerConfigure
function returns an interface{}
and a diag.Diagnostics
type. diag.Diagnostics
can return multiple errors and warnings to Terraform, giving users more robust error and warning messages.
func providerConfigure(...) (interface{}, diag.Diagnostics) {
...
}
The diag.FromError()
function casts standard Go errors to diag.Diagnostics
type notifying the user whenever the provider errors.
Replace the return nil, diag.FromError(err)
line in your providerConfigure
function with the following code snippet. There should be two replacements — one where the user's HashiCups credentials provided and the other where its not. Notice how this appends a diag.Diagnostic
type to the existing diags
variable; this allows your provider to return multiple error message. This will produce an error message containing Summary
and Detail
contents.
diags = append(diags, diag.Diagnostic{
Severity: diag.Error,
Summary: "Unable to create HashiCups client",
Detail: "Unable to auth user for authenticated HashiCups client",
})
return nil, diags
Your providerConfigure
function should look like the following.
func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) {
username := d.Get("username").(string)
password := d.Get("password").(string)
// Warning or errors can be collected in a slice type
var diags diag.Diagnostics
if (username != "") && (password != "") {
c, err := hashicups.NewClient(nil, &username, &password)
if err != nil {
- return nil, diag.FromErr(err)
+ diags = append(diags, diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Unable to create HashiCups client",
+ Detail: "Unable to auth user for authenticated HashiCups client",
+ })
+ return nil, diags
}
return c, diags
}
c, err := hashicups.NewClient(nil, nil, nil)
if err != nil {
- return nil, diag.FromErr(err)
+ diags = append(diags, diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: "Unable to create HashiCups client",
+ Detail: "Unable to auth user for unauthenticated HashiCups client",
+ })
+ return nil, diags
}
return c, diags
}
Notice how the Diagnostic
severity is set to diag.Error
. There are two levels of severity — diag.Error
and diag.Warning
. You will create and view a warning message in the next section.
»Test error message
Now that you’ve added a custom error message, verify that the provider returns your error message.
First, navigate to the terraform-provider-hashicups
root directory.
Then, build the binary and move it into your user Terraform plugins directory. This allows you to sideload and test your custom providers.
$ make install
go build -o terraform-provider-hashicups
mv terraform-provider-hashicups ~/.terraform.d/plugins/hashicorp.com/edu/hashicups/0.2/darwin_amd64
Navigate to the examples
directory. This contains a sample Terraform configuration for the Terraform HashiCups provider.
$ cd examples
Update your HashiCups provider configuration so it uses the wrong credentials.
provider "hashicups" {
username = "education"
- password = "test123"
+ password = "test1234"
}
Next, initialize your workspace to refresh your HashiCups provider, then apply. This should return the error message you defined above.
$ terraform init && terraform apply --auto-approve
## Output truncated...
module.psl.data.hashicups_coffees.all: Refreshing state...
Error: Unable to create HashiCups client
Unable to auth user for authenticated HashiCups client
Because your provider errors, your Terraform state doesn't update when you ran the terraform apply
.
»Add warning message
Add the following snippet after diags
is declared in providerConfigure
function.
diags = append(diags, diag.Diagnostic{
Severity: diag.Warning,
Summary: "Warning Message Summary",
Detail: "This is the detailed warning message from providerConfigure",
})
Your providerConfigure
function should look like the following.
func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) {
...
// Warning or errors can be collected in a slice type
var diags diag.Diagnostics
+ diags = append(diags, diag.Diagnostic{
+ Severity: diag.Warning,
+ Summary: "Warning Message Summary",
+ Detail: "This is the detailed warning message from providerConfigure",
+ })
...
}
Notice that you have set the Diagnostic
's severity to diag.Warning
. When you use your provider, both warning and messages appear because diags
contains both. A warning message will only appear when the provider also errors.
The Diagnostic
's detail contains additional information about the warning message, including which function it was triggered in. This could provide handy while debugging.
»Test warning message
Now that you’ve added a warning message and updated your Terraform log level, verify that the provider returns both your warning and error message.
First, navigate to the terraform-provider-hashicups
root directory.
Then, build the binary and move it into your user Terraform plugins directory. This allows you to sideload and test your custom providers.
$ make install
go build -o terraform-provider-hashicups
mv terraform-provider-hashicups ~/.terraform.d/plugins/hashicorp.com/edu/hashicups/0.2/darwin_amd64
Navigate to the examples
directory. This contains a sample Terraform configuration for the Terraform HashiCups provider.
$ cd examples
Next, initialize your workspace to refresh your HashiCups provider, then apply. This should return both a warning and error message.
$ terraform init && terraform apply --auto-approve
## Output truncated...
module.psl.data.hashicups_coffees.all: Refreshing state...
Warning: Warning Message Summary
This is the detailed warning message from providerConfigure
Error: Unable to create HashiCups client
Unable to authenticate user for authenticated HashiCups client
Because your provider errors, your Terraform state doesn't update when you ran the terraform apply
.
»Fix provider
Comment out or remove the warning message, then recompile your Terraform provider.
func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) {
...
// Warning or errors can be collected in a slice type
var diags diag.Diagnostics
- diags = append(diags, diag.Diagnostic{
- Severity: diag.Warning,
- Summary: "Warning Message Summary",
- Detail: "This is the detailed warning message from providerConfigure",
- })
...
}
Fix your Terraform configuration to use the correct credentials.
provider "hashicups" {
username = "education"
- password = "test1234"
+ password = "test123"
}
Finally, re-initializing your workspace and applying the configuration.
$ terraform init && terraform apply --auto-approve
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
There should be no errors or warning messages.
Tip: You can also retrieve detailed Terraform and provider logs by setting the environment variable TF_LOG
. Please include a detailed logs with any bug reports so the author can identify and address the bug. To learn more about log levels and how to interpret a crash log, refer to the Debugging Terraform Documentation.
»Next steps
Congratulations! You have added error and warning messages to your HashiCups provider and implemented a nested read function.
If you were stuck during this tutorial, checkout the debug-tf-providers
branch to see the changes implemented in this tutorial.