Terraform
Terraform Version Checks
Terraform Version Checks are generic checks defined at the TestCase level that check logic against the Terraform CLI version. The checks are executed at the beginning of the TestCase before any TestStep is executed.
The Terraform CLI version is determined by the binary selected by the TF_ACC_TERRAFORM_PATH
environment variable value, installed by the TF_ACC_TERRAFORM_VERSION
value, or already existing based on the PATH
environment variable.
A version check will either return an error and fail the associated test, return a skip message and pass the associated test immediately by skipping, or it will return nothing and allow the associated test to run.
Terraform CLI prerelease versions include a -alphaYYYYMMDD
, -beta#
, or rc#
(release candidate) suffix for a minor version with 0
patch version. For example, 1.8.0-rc1
. Prereleases of Terraform are considered semantically equivalent to the associated minor version since prereleases are when any new features are introduced.
Built-in Version Checks and Variables
The terraform-plugin-testing
module provides a package tfversion
with built-in version checks for common use-cases. There are three types of version checks: Skip Checks, Require Checks, and Collection Checks.
Tip
Built-in version checks handle prereleases of a minor version as semantically equivalent to given minor versions. For example, if the test includes tfversion.SkipBelow(tfversion.Version1_8_0)
and the running Terraform CLI version is 1.8.0-rc1
, the test will run, not skip. This is intended to enable prerelease testing of new features.
Version Variables
The built-in checks in the tfversion
package typically require the use of the github.com/hashicorp/go-version
module version.Version
type. To simplify provider testing implementations, the tfversion
package provides built-in variables for common use case versions, such as each released minor and major Terraform version. These follow the pattern of Version{MAJOR}_{MINOR}_{PATCH}
with the major, minor, and patch version numbers, such as Version1_2_0
.
Skip Version Checks
Skip Version Checks will pass the associated test by skipping and provide a skip message if the detected Terraform CLI version satisfies the specified check criteria.
Check | Description |
---|---|
tfversion.SkipAbove(maximumVersion) | Skips the test if the Terraform CLI version is exclusively above the given maximum. |
tfversion.SkipBelow(minimumVersion) | Skips the test if the Terraform CLI version is exclusively below the given minimum. |
tfversion.SkipBetween(minimumVersion, maximumVersion) | Skips the test if the Terraform CLI version is between the given minimum (inclusive) and maximum (exclusive). |
tfversion.SkipIf(version) | Skips the test if the Terraform CLI version matches the given version. |
Example using tfversion.SkipBetween
The built-in tfversion.SkipBetween
version check is useful for skipping all patch versions associated with a minor version.
In the following example, we have written a test that skips all Terraform CLI patch versions associated with 0.14.0:
package example_test
import (
"testing"
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/tfversion"
)
func Test_Skip_TF14(t *testing.T) {
t.Parallel()
resource.UnitTest(t, resource.TestCase{
ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){
"test": func() (tfprotov6.ProviderServer, error) {
return nil, nil
},
},
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBetween(tfversion.Version0_14_0, tfversion.Version0_15_0),
},
Steps: []resource.TestStep{
{
Config: `//example test config`,
},
},
})
}
Require Version Checks
Require Version Checks will raise an error and fail the associated test if the detected Terraform CLI version does not satisfy the specified check requirements.
Check | Description |
---|---|
tfversion.RequireAbove(minimumVersion) | Fails the test if the Terraform CLI version is exclusively below the given minimum. |
tfversion.RequireBelow(maximumVersion) | Fails the test if the Terraform CLI version is inclusively above the given maximum. |
tfversion.RequireBetween(minimumVersion, maximumVersion) | Fails the test if the Terraform CLI version is outside the given minimum (exclusive) and maximum (inclusive). |
tfversion.RequireNot(version) | Fails the test if the Terraform CLI version matches the given version. |
Example using tfversion.RequireAbove
The built-in tfversion.RequireAbove
version check is useful for required tests that may use features only available in newer versions of the Terraform ClI.
In the following example, the test Terraform configuration uses the nullable
argument for an input variable, a feature that is only available in Terraform CLI versions 1.3.0
and above. The version check will fail the test with a specific error if the detected version is below 1.3.0
.
package example_test
import (
"testing"
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/tfversion"
)
func Test_Require_TF1_3(t *testing.T) {
t.Parallel()
resource.UnitTest(t, resource.TestCase{
ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){
"test": func() (tfprotov6.ProviderServer, error) {
return nil, nil
},
},
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.RequireAbove(tfversion.Version1_3_0),
},
Steps: []resource.TestStep{
{
Config: `variable "a" {
nullable = true
default = "hello"
}`,
},
},
})
}
Collection Version Checks
Collection Version Checks operate on multiple version checks and can be used to create more complex checks.
tfversion.Any(TerraformVersionChecks...)
will run the associated test by returning a nil error and empty skip message
if any of the given version sub-checks return a nil error and empty skip message. If none of the sub-checks return a nil error and empty skip message, then the check will return all sub-check errors and fail the associated test.
Otherwise, if none of the sub-checks return a non-nil error, the check will pass the associated test by skipping and return all sub-check skip messages.
tfversion.All(TerraformVersionChecks...)
will either fail or skip the associated test if any of the given sub-checks return a non-nil error or non-empty skip message. The check will return the
first non-nil error or non-empty skip message from the given sub-checks in the order that they are given. Otherwise, if all sub-checks return a nil error and empty skip message, then the check will return a nil error and empty skip message and run the associated test. This check should only be
used in conjunction with tfversion.Any()
as the behavior provided by this check is applied to the TerraformVersionChecks
field by default.
Example using tfversion.Any
In the following example, the test will only run if either the Terraform CLI version is above 1.2.0
or if it's below 1.0.0
but not version 0.15.0
, otherwise an error will be returned.
package example_test
import (
"testing"
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/tfversion"
)
func Test_Any(t *testing.T) {
t.Parallel()
resource.UnitTest(t, resource.TestCase{
ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){
"test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature
return nil, nil
},
},
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.Any(
tfversion.All(
tfversion.RequireNot(tfversion.Version0_15_0),
tfversion.RequireBelow(tfversion.Version1_0_0),
),
tfversion.RequireAbove(tfversion.Version1_2_0),
),
},
Steps: []resource.TestStep{
{
Config: `//example test config`,
},
},
})
}
Custom Version Checks
The package tfversion
also provides the TerraformVersionCheck
interface, which can be implemented for a custom version check.
The tfversion.CheckTerraformVersionRequest
has a TerraformVersion
field of type *version.Version
which contains the version of the Terraform CLI binary running the test.
The tfversion.CheckTerraformVersionResponse
has an Error
field and a Skip
field. The behavior of the version check depends on which field is populated. Populating the Error
field will fail the associated test with the given error.
Populating the Skip
field will pass the associated test by skipping the test with the given skip message. Only one of these fields should be populated.
Here is an example implementation of a version check returns an error if the detected Terraform CLI version matches the given version:
package example_test
import (
"context"
"fmt"
"github.com/hashicorp/go-version"
)
// Ensure implementation satisfies the tfversion.TerraformVersionCheck interface.
var _ tfversion.TerraformVersionCheck = requireNotCheck{}
// RequireNot will fail the test if the given version matches.
func RequireNot(v *version.Version) tfversion.TerraformVersionCheck {
return requireNotCheck{
version: v,
}
}
type requireNotCheck struct {
version *version.Version
}
func (s requireNotCheck) CheckTerraformVersion(ctx context.Context, req tfversion.CheckTerraformVersionRequest, resp *tfversion.CheckTerraformVersionResponse) {
if req.TerraformVersion.Equal(s.version) {
resp.Error = fmt.Errorf("unexpected Terraform CLI version: %s", s.version)
}
}
And example usage:
package example_test
import (
"testing"
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/tfversion"
)
func Test_RequireNot(t *testing.T) {
t.Parallel()
resource.UnitTest(t, resource.TestCase{
ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){
"test": func() (tfprotov6.ProviderServer, error) {
return nil, nil
},
},
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.RequireNot(tfversion.Version0_13_0),
},
Steps: []resource.TestStep{
{
Config: `//example test config`,
},
},
})
}