|
| 1 | +--- |
| 2 | +page_title: 'Plugin Development - Acceptance Testing: Ephemeral Resources' |
| 3 | +description: >- |
| 4 | + Guidance on how to test ephemeral resources and data. |
| 5 | +--- |
| 6 | + |
| 7 | +<Highlight> |
| 8 | + |
| 9 | +Ephemeral resource support is in technical preview and offered without compatibility promises until Terraform 1.10 is generally available. |
| 10 | + |
| 11 | +</Highlight> |
| 12 | + |
| 13 | +# Ephemeral Resources |
| 14 | + |
| 15 | +[Ephemeral Resources](/terraform/language/v1.10.x/resources/ephemeral) are an abstraction that allows Terraform to reference external data, similar to [data sources](/terraform/language/data-sources), without persisting that data to plan or state artifacts. The `terraform-plugin-testing` module exclusively uses Terraform plan and state artifacts for it's assertion-based test checks, like [plan checks](/terraform/plugin/testing/acceptance-tests/plan-checks) or [state checks](/terraform/plugin/testing/acceptance-tests/state-checks), which means that ephemeral resource data cannot be asserted using these methods alone. |
| 16 | + |
| 17 | +In addition to this, ephemeral resources have a different [lifecycle](/terraform/language/v1.10.x/resources/ephemeral#lifecycle) then resources or data sources and must be [referenced](/terraform/language/v1.10.x/resources/ephemeral#referencing-ephemeral-resources) as a dependency to a managed resource or data source to be invoked. This means for acceptance testing ephemeral resource logic, provider developers should use a real world example where the ephemeral resource would be referenced. |
| 18 | + |
| 19 | +The following is a test for a hypothetical `examplecloud_secret` ephemeral resource which is referenced by a provider configuration that has a single managed resource. For this test to pass, the ephemeral `examplecloud_secret` resource must return valid data, specifically a kerberos `username`, `password`, and `realm`, which are used to configure the `dns` provider and create a DNS record. |
| 20 | + |
| 21 | +```go |
| 22 | +func TestExampleCloudSecret_DnsKerberos(t *testing.T) { |
| 23 | + resource.UnitTest(t, resource.TestCase{ |
| 24 | + // Ephemeral resources are only available in 1.10 and later |
| 25 | + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ |
| 26 | + tfversion.SkipBelow(tfversion.Version1_10_0), |
| 27 | + }, |
| 28 | + ExternalProviders: map[string]resource.ExternalProvider{ |
| 29 | + "dns": { |
| 30 | + Source: "hashicorp/dns", |
| 31 | + }, |
| 32 | + }, |
| 33 | + ProtoV5ProviderFactories: map[string]func() (tfprotov5.ProviderServer, error){ |
| 34 | + "examplecloud": providerserver.NewProtocol5WithError(New()), |
| 35 | + }, |
| 36 | + Steps: []resource.TestStep{ |
| 37 | + { |
| 38 | + Config: ` |
| 39 | + # Retrieves a secret containing user kerberos configuration |
| 40 | + ephemeral "examplecloud_secret" "krb" { |
| 41 | + name = "example_kerberos_user" |
| 42 | + } |
| 43 | +
|
| 44 | + # The usage of this provider and managed resource will indicate to Terraform |
| 45 | + # that the "ephemeral.examplecloud_secret.krb" resource is needed. |
| 46 | + provider "dns" { |
| 47 | + update { |
| 48 | + server = "ns.example.com" |
| 49 | + gssapi { |
| 50 | + realm = ephemeral.examplecloud_secret.krb.secret_data.realm |
| 51 | + username = ephemeral.examplecloud_secret.krb.secret_data.username |
| 52 | + password = ephemeral.examplecloud_secret.krb.secret_data.password |
| 53 | + } |
| 54 | + } |
| 55 | + } |
| 56 | +
|
| 57 | + # Successful creation of this resource indicates a success! |
| 58 | + resource "dns_a_record_set" "record_set" { |
| 59 | + zone = "example.com." |
| 60 | + addresses = [ |
| 61 | + "192.168.0.1", |
| 62 | + "192.168.0.2", |
| 63 | + "192.168.0.3", |
| 64 | + ] |
| 65 | + } |
| 66 | + `, |
| 67 | + }, |
| 68 | + }, |
| 69 | + }) |
| 70 | +} |
| 71 | +``` |
| 72 | + |
| 73 | +See the Terraform [ephemeral documentation](http://localhost:3000/terraform/language/v1.10.x/resources/ephemeral#referencing-ephemeral-resources) for more details on where ephemeral data can be referenced in configurations. |
| 74 | + |
| 75 | +## Testing ephemeral data with `echo` provider |
| 76 | + |
| 77 | +Test assertions on [result data](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/ephemeral#OpenResponse.Result) returned by an ephemeral resource during [`Open`](/terraform/plugin/framework/ephemeral-resources/open) can be arranged using the `echoprovider` package. |
| 78 | + |
| 79 | +This package contains a [Protocol V6 Terraform Provider](/terraform/plugin/terraform-plugin-protocol#protocol-version-6) named `echo`, with a single managed resource also named `echo`. Using the `echo` provider configuration and an instance of the managed resource, ephemeral data can be "echoed" from the provider configuration into Terraform state, where it can be referenced in test assertions with [state checks](/terraform/plugin/testing/acceptance-tests/state-checks). For example: |
| 80 | + |
| 81 | +```terraform |
| 82 | +ephemeral "examplecloud_secret" "krb" { |
| 83 | + name = "example_kerberos_user" |
| 84 | +} |
| 85 | +
|
| 86 | +provider "echo" { |
| 87 | + # Provide the ephemeral data we want to run test assertions against |
| 88 | + data = ephemeral.examplecloud_secret.krb.secret_data |
| 89 | +} |
| 90 | +
|
| 91 | +# The ephemeral data will be echoed into state |
| 92 | +resource "echo" "test_krb" {} |
| 93 | +``` |
| 94 | + |
| 95 | +<Highlight> |
| 96 | + |
| 97 | +This provider is designed specifically to be used as a utility for acceptance testing ephemeral data and is only available via the `terraform-plugin-testing` Go module. |
| 98 | + |
| 99 | +</Highlight> |
| 100 | + |
| 101 | +### Using `echo` provider in acceptance tests |
| 102 | + |
| 103 | +First, we include the `echo` provider using the [`echoprovider.NewProviderServer`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/echoprovider#NewProviderServer) function in the `(TestCase).ProtoV6ProviderFactories` property: |
| 104 | + |
| 105 | +```go |
| 106 | +import ( |
| 107 | + // .. other imports |
| 108 | + |
| 109 | + "github.com/hashicorp/terraform-plugin-testing/echoprovider" |
| 110 | +) |
| 111 | + |
| 112 | +func TestExampleCloudSecret(t *testing.T) { |
| 113 | + resource.UnitTest(t, resource.TestCase{ |
| 114 | + // Ephemeral resources are only available in 1.10 and later |
| 115 | + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ |
| 116 | + tfversion.SkipBelow(tfversion.Version1_10_0), |
| 117 | + }, |
| 118 | + // Include the provider we want to test: `examplecloud` |
| 119 | + ProtoV5ProviderFactories: map[string]func() (tfprotov5.ProviderServer, error){ |
| 120 | + "examplecloud": providerserver.NewProtocol5WithError(New()), |
| 121 | + }, |
| 122 | + // Include `echo` as a v6 provider from `terraform-plugin-testing` |
| 123 | + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ |
| 124 | + "echo": echoprovider.NewProviderServer(), |
| 125 | + }, |
| 126 | + Steps: []resource.TestStep{ |
| 127 | + // .. test step configurations can now use the `echo` and `examplecloud` providers |
| 128 | + }, |
| 129 | + }) |
| 130 | +} |
| 131 | +``` |
| 132 | + |
| 133 | +After including both providers, our test step `Config` references the ephemeral data from `examplecloud_secret` in the `echo` provider configuration `data` attribute: |
| 134 | + |
| 135 | +```go |
| 136 | +func TestExampleCloudSecret(t *testing.T) { |
| 137 | + resource.UnitTest(t, resource.TestCase{ |
| 138 | + // .. test case setup from previous step |
| 139 | + |
| 140 | + Steps: []resource.TestStep{ |
| 141 | + { |
| 142 | + Config: ` |
| 143 | + ephemeral "examplecloud_secret" "krb" { |
| 144 | + name = "example_kerberos_user" |
| 145 | + } |
| 146 | +
|
| 147 | + provider "echo" { |
| 148 | + data = ephemeral.examplecloud_secret.krb.secret_data |
| 149 | + } |
| 150 | +
|
| 151 | + resource "echo" "test_krb" {} |
| 152 | + `, |
| 153 | + }, |
| 154 | + }, |
| 155 | + }) |
| 156 | +} |
| 157 | +``` |
| 158 | + |
| 159 | +The `echo.test_krb` managed resource has a single computed `data` attribute, which will contain the provider configuration `data` results. This data is then used in assertions with the [state check](/terraform/plugin/testing/acceptance-tests/state-checks) functionality: |
| 160 | + |
| 161 | +```go |
| 162 | +func TestExampleCloudSecret(t *testing.T) { |
| 163 | + resource.UnitTest(t, resource.TestCase{ |
| 164 | + // .. test case setup from previous step |
| 165 | + |
| 166 | + Steps: []resource.TestStep{ |
| 167 | + { |
| 168 | + Config: ` |
| 169 | + ephemeral "examplecloud_secret" "krb" { |
| 170 | + name = "example_kerberos_user" |
| 171 | + } |
| 172 | +
|
| 173 | + provider "echo" { |
| 174 | + data = ephemeral.examplecloud_secret.krb.secret_data |
| 175 | + } |
| 176 | +
|
| 177 | + resource "echo" "test_krb" {} |
| 178 | + `, |
| 179 | + ConfigStateChecks: []statecheck.StateCheck{ |
| 180 | + statecheck.ExpectKnownValue("echo.test_krb", tfjsonpath.New("data").AtMapKey("realm"), knownvalue.StringExact("EXAMPLE.COM")), |
| 181 | + statecheck.ExpectKnownValue("echo.test_krb", tfjsonpath.New("data").AtMapKey("username"), knownvalue.StringExact("john-doe")), |
| 182 | + statecheck.ExpectKnownValue("echo.test_krb", tfjsonpath.New("data").AtMapKey("password"), knownvalue.StringRegexp(regexp.MustCompile(`^.{12}$`))), |
| 183 | + }, |
| 184 | + }, |
| 185 | + }, |
| 186 | + }) |
| 187 | +} |
| 188 | +``` |
| 189 | + |
| 190 | +`data` is a `dynamic` attribute, so whatever [type](/terraform/language/expressions/types) you pass in will be directly reflected in the managed resource `data` attribute. In the config above, we reference an object (`secret_data`) from the ephemeral resource instance, so the resulting type of `echo.test_krb.data` is also an `object`. |
| 191 | + |
| 192 | +You can also reference the entire ephemeral resource instance for assertions, rather than specific attributes: |
| 193 | + |
| 194 | +```go |
| 195 | +func TestExampleCloudSecret(t *testing.T) { |
| 196 | + resource.UnitTest(t, resource.TestCase{ |
| 197 | + // .. test case setup from previous step |
| 198 | + |
| 199 | + Steps: []resource.TestStep{ |
| 200 | + { |
| 201 | + Config: ` |
| 202 | + ephemeral "examplecloud_secret" "krb" { |
| 203 | + name = "example_kerberos_user" |
| 204 | + } |
| 205 | +
|
| 206 | + provider "echo" { |
| 207 | + data = ephemeral.examplecloud_secret.krb |
| 208 | + } |
| 209 | +
|
| 210 | + resource "echo" "test_krb" {} |
| 211 | + `, |
| 212 | + ConfigStateChecks: []statecheck.StateCheck{ |
| 213 | + statecheck.ExpectKnownValue("echo.test_krb", tfjsonpath.New("data").AtMapKey("name"), knownvalue.StringExact("example_kerberos_user")), |
| 214 | + }, |
| 215 | + }, |
| 216 | + }, |
| 217 | + }) |
| 218 | +} |
| 219 | +``` |
0 commit comments