Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e96524f
Error on use of ImportStatePersist with plannable import
bbasata Apr 4, 2025
12842ab
Error on use of ImportStateVerify with plannable import
bbasata Apr 4, 2025
91b5a0c
Rename test functions for consistency and for running by pattern
bbasata Apr 4, 2025
2456d71
Import block works for a resource with a dependency
bbasata Apr 4, 2025
ccff991
Simplify no-op action check
bbasata Apr 4, 2025
35f3b06
Add prerelease changelog entries
bbasata Apr 4, 2025
90f5656
Add a catch-up prerelease changelog entry
bbasata Apr 4, 2025
e472964
Refactor test provider functions
bbasata Apr 4, 2025
cde5001
Fix 'No changes. Your infrastructure matches the configuration.' fals…
bbasata Apr 4, 2025
372066c
Log (or do not log) consistently
bbasata Apr 4, 2025
526853c
Fix format string syntax
bbasata Apr 4, 2025
c2a5b46
helper/resource: add ImportBlockWithResourceIdentity kind
bbasata Apr 5, 2025
971fa87
ImportBlockWithResourceIdentity requires Terraform 1.12.0+
bbasata Apr 11, 2025
30f231c
ImportBlockWithResourceIdentity requires Terraform 1.12.0+
bbasata Apr 11, 2025
d64375f
ImportBlockWithResourceIdentity requires Terraform 1.12.0+
bbasata Apr 11, 2025
d32eeda
Add changelog entry
bbasata Apr 14, 2025
836b3e6
Merge remote-tracking branch 'origin/main' into import-block-with-res…
bbasata Apr 15, 2025
dd2d50b
Update .changes/unreleased/NOTES-20250414-095537.yaml
bbasata Apr 15, 2025
7847b70
Update helper/resource/testing.go
bbasata Apr 15, 2025
9c6616f
Nicer version check
bbasata Apr 15, 2025
33e8faf
Remove incidental test steps
bbasata Apr 15, 2025
09e574a
Add type awareness to import block generating
bbasata Apr 16, 2025
82377fd
fixup! Add type awareness to import block generating
bbasata Apr 16, 2025
e374f8e
Add more list types to example resource identity schema
bbasata Apr 16, 2025
bc1ba78
Merge remote-tracking branch 'origin/main' into import-block-with-res…
bbasata Apr 16, 2025
20e4a75
Update ImportState tests that have inline config
bbasata Apr 16, 2025
f76430c
fixup! Update ImportState tests that have inline config
bbasata Apr 16, 2025
3f8f2ca
Use the correct working directory for apply + show
bbasata Apr 16, 2025
37a2c16
Fix identity values comparison when using ConfigFile
bbasata Apr 16, 2025
b5a1d3c
fixup! Fix identity values comparison when using ConfigFile
bbasata Apr 16, 2025
1826a6c
fixup! Fix identity values comparison when using ConfigFile
bbasata Apr 16, 2025
7fe7dac
Update .changes/unreleased/NOTES-20250414-095537.yaml
bbasata Apr 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/unreleased/NOTES-20250414-095537.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
kind: NOTES
body: 'This beta pre-release adds support for managed resource identity, which can be used with Terraform v1.12.0-beta2. Acceptance tests can use the `ImportBlockWithResourceIdentity` kind to exercise the import of a managed resource using its resource identity object values instead of using a string identifier.'
time: 2025-04-14T09:55:37.938453-04:00
custom:
Issue: "480"
291 changes: 291 additions & 0 deletions helper/resource/importstate/examplecloud_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/hashicorp/terraform-plugin-testing/internal/testing/testprovider"
"github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/datasource"
"github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/resource"
"github.com/hashicorp/terraform-plugin-testing/internal/teststep"
)

func examplecloudDataSource() testprovider.DataSource {
Expand Down Expand Up @@ -56,6 +57,16 @@ func examplecloudResource() testprovider.Resource {
"name": tftypes.NewValue(tftypes.String, "somevalue"),
},
),
NewIdentity: teststep.Pointer(tftypes.NewValue(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"id": tftypes.String,
},
},
map[string]tftypes.Value{
"id": tftypes.NewValue(tftypes.String, "westeurope/somevalue"),
},
)),
},
ReadResponse: &resource.ReadResponse{
NewState: tftypes.NewValue(
Expand All @@ -72,6 +83,16 @@ func examplecloudResource() testprovider.Resource {
"name": tftypes.NewValue(tftypes.String, "somevalue"),
},
),
NewIdentity: teststep.Pointer(tftypes.NewValue(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"id": tftypes.String,
},
},
map[string]tftypes.Value{
"id": tftypes.NewValue(tftypes.String, "westeurope/somevalue"),
},
)),
},
ImportStateResponse: &resource.ImportStateResponse{
State: tftypes.NewValue(
Expand All @@ -88,6 +109,16 @@ func examplecloudResource() testprovider.Resource {
"name": tftypes.NewValue(tftypes.String, "somevalue"),
},
),
Identity: teststep.Pointer(tftypes.NewValue(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"id": tftypes.String,
},
},
map[string]tftypes.Value{
"id": tftypes.NewValue(tftypes.String, "westeurope/somevalue"),
},
)),
},
SchemaResponse: &resource.SchemaResponse{
Schema: &tfprotov6.Schema{
Expand All @@ -100,6 +131,18 @@ func examplecloudResource() testprovider.Resource {
},
},
},
IdentitySchemaResponse: &resource.IdentitySchemaResponse{
Schema: &tfprotov6.ResourceIdentitySchema{
Version: 1,
IdentityAttributes: []*tfprotov6.ResourceIdentitySchemaAttribute{
{
Name: "id",
Type: tftypes.String,
RequiredForImport: true,
},
},
},
},
}
}

Expand Down Expand Up @@ -188,3 +231,251 @@ func examplecloudZoneRecord() testprovider.Resource {
},
}
}

func examplecloudResourceWithEveryIdentitySchemaType() testprovider.Resource {
return testprovider.Resource{
CreateResponse: &resource.CreateResponse{
NewState: tftypes.NewValue(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"hostname": tftypes.String,
"cabinet": tftypes.String,
"unit": tftypes.Number,
"active": tftypes.Bool,
"tags": tftypes.List{ElementType: tftypes.String},
"magic_numbers": tftypes.List{ElementType: tftypes.Number},
"beep_boop": tftypes.List{ElementType: tftypes.Bool},
},
},
map[string]tftypes.Value{
"hostname": tftypes.NewValue(tftypes.String, "mail.example.net"),
"cabinet": tftypes.NewValue(tftypes.String, "A1"),
"unit": tftypes.NewValue(tftypes.Number, 14),
"active": tftypes.NewValue(tftypes.Bool, true),
"tags": tftypes.NewValue(
tftypes.List{ElementType: tftypes.String}, []tftypes.Value{
tftypes.NewValue(tftypes.String, "storage"),
tftypes.NewValue(tftypes.String, "fast")}),
"magic_numbers": tftypes.NewValue(
tftypes.List{ElementType: tftypes.Number}, []tftypes.Value{
tftypes.NewValue(tftypes.Number, 5),
tftypes.NewValue(tftypes.Number, 2)}),
"beep_boop": tftypes.NewValue(
tftypes.List{ElementType: tftypes.Bool}, []tftypes.Value{
tftypes.NewValue(tftypes.Bool, false),
tftypes.NewValue(tftypes.Bool, true),
}),
},
),
NewIdentity: teststep.Pointer(tftypes.NewValue(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"cabinet": tftypes.String,
"unit": tftypes.Number,
"active": tftypes.Bool,
"tags": tftypes.List{ElementType: tftypes.String},
"magic_numbers": tftypes.List{ElementType: tftypes.Number},
"beep_boop": tftypes.List{ElementType: tftypes.Bool},
},
},
map[string]tftypes.Value{
"cabinet": tftypes.NewValue(tftypes.String, "A1"),
"unit": tftypes.NewValue(tftypes.Number, 14),
"active": tftypes.NewValue(tftypes.Bool, true),
"tags": tftypes.NewValue(
tftypes.List{ElementType: tftypes.String}, []tftypes.Value{
tftypes.NewValue(tftypes.String, "storage"),
tftypes.NewValue(tftypes.String, "fast"),
}),
"magic_numbers": tftypes.NewValue(
tftypes.List{ElementType: tftypes.Number}, []tftypes.Value{
tftypes.NewValue(tftypes.Number, 5),
tftypes.NewValue(tftypes.Number, 2)}),
"beep_boop": tftypes.NewValue(
tftypes.List{ElementType: tftypes.Bool}, []tftypes.Value{
tftypes.NewValue(tftypes.Bool, false),
tftypes.NewValue(tftypes.Bool, true),
}),
},
)),
},
ReadResponse: &resource.ReadResponse{
NewState: tftypes.NewValue(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"hostname": tftypes.String,
"cabinet": tftypes.String,
"unit": tftypes.Number,
"active": tftypes.Bool,
"tags": tftypes.List{ElementType: tftypes.String},
"magic_numbers": tftypes.List{ElementType: tftypes.Number},
"beep_boop": tftypes.List{ElementType: tftypes.Bool},
},
},
map[string]tftypes.Value{
"hostname": tftypes.NewValue(tftypes.String, "mail.example.net"),
"cabinet": tftypes.NewValue(tftypes.String, "A1"),
"unit": tftypes.NewValue(tftypes.Number, 14),
"active": tftypes.NewValue(tftypes.Bool, true),
"tags": tftypes.NewValue(
tftypes.List{ElementType: tftypes.String}, []tftypes.Value{
tftypes.NewValue(tftypes.String, "storage"),
tftypes.NewValue(tftypes.String, "fast")}),
"magic_numbers": tftypes.NewValue(
tftypes.List{ElementType: tftypes.Number}, []tftypes.Value{
tftypes.NewValue(tftypes.Number, 5),
tftypes.NewValue(tftypes.Number, 2)}),
"beep_boop": tftypes.NewValue(
tftypes.List{ElementType: tftypes.Bool}, []tftypes.Value{
tftypes.NewValue(tftypes.Bool, false),
tftypes.NewValue(tftypes.Bool, true),
}),
},
),
NewIdentity: teststep.Pointer(tftypes.NewValue(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"cabinet": tftypes.String,
"unit": tftypes.Number,
"active": tftypes.Bool,
"tags": tftypes.List{ElementType: tftypes.String},
"magic_numbers": tftypes.List{ElementType: tftypes.Number},
"beep_boop": tftypes.List{ElementType: tftypes.Bool},
},
},
map[string]tftypes.Value{
"cabinet": tftypes.NewValue(tftypes.String, "A1"),
"unit": tftypes.NewValue(tftypes.Number, 14),
"active": tftypes.NewValue(tftypes.Bool, true),
"tags": tftypes.NewValue(
tftypes.List{ElementType: tftypes.String}, []tftypes.Value{
tftypes.NewValue(tftypes.String, "storage"),
tftypes.NewValue(tftypes.String, "fast")}),
"magic_numbers": tftypes.NewValue(
tftypes.List{ElementType: tftypes.Number}, []tftypes.Value{
tftypes.NewValue(tftypes.Number, 5),
tftypes.NewValue(tftypes.Number, 2)}),
"beep_boop": tftypes.NewValue(
tftypes.List{ElementType: tftypes.Bool}, []tftypes.Value{
tftypes.NewValue(tftypes.Bool, false),
tftypes.NewValue(tftypes.Bool, true),
}),
},
)),
},
ImportStateResponse: &resource.ImportStateResponse{
State: tftypes.NewValue(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"hostname": tftypes.String,
"cabinet": tftypes.String,
"unit": tftypes.Number,
"active": tftypes.Bool,
"tags": tftypes.List{ElementType: tftypes.String},
"magic_numbers": tftypes.List{ElementType: tftypes.Number},
"beep_boop": tftypes.List{ElementType: tftypes.Bool},
},
},
map[string]tftypes.Value{
"hostname": tftypes.NewValue(tftypes.String, "mail.example.net"),
"cabinet": tftypes.NewValue(tftypes.String, "A1"),
"unit": tftypes.NewValue(tftypes.Number, 14),
"active": tftypes.NewValue(tftypes.Bool, true),
"tags": tftypes.NewValue(
tftypes.List{ElementType: tftypes.String}, []tftypes.Value{
tftypes.NewValue(tftypes.String, "storage"),
tftypes.NewValue(tftypes.String, "fast")}),
"magic_numbers": tftypes.NewValue(
tftypes.List{ElementType: tftypes.Number}, []tftypes.Value{
tftypes.NewValue(tftypes.Number, 5),
tftypes.NewValue(tftypes.Number, 2)}),
"beep_boop": tftypes.NewValue(
tftypes.List{ElementType: tftypes.Bool}, []tftypes.Value{
tftypes.NewValue(tftypes.Bool, false),
tftypes.NewValue(tftypes.Bool, true),
}),
},
),
Identity: teststep.Pointer(tftypes.NewValue(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"cabinet": tftypes.String,
"unit": tftypes.Number,
"active": tftypes.Bool,
"tags": tftypes.List{ElementType: tftypes.String},
"magic_numbers": tftypes.List{ElementType: tftypes.Number},
"beep_boop": tftypes.List{ElementType: tftypes.Bool},
},
},
map[string]tftypes.Value{
"cabinet": tftypes.NewValue(tftypes.String, "A1"),
"unit": tftypes.NewValue(tftypes.Number, 14),
"active": tftypes.NewValue(tftypes.Bool, true),
"tags": tftypes.NewValue(
tftypes.List{ElementType: tftypes.String}, []tftypes.Value{
tftypes.NewValue(tftypes.String, "storage"),
tftypes.NewValue(tftypes.String, "fast")}),
"magic_numbers": tftypes.NewValue(
tftypes.List{ElementType: tftypes.Number}, []tftypes.Value{
tftypes.NewValue(tftypes.Number, 5),
tftypes.NewValue(tftypes.Number, 2)}),
"beep_boop": tftypes.NewValue(
tftypes.List{ElementType: tftypes.Bool}, []tftypes.Value{
tftypes.NewValue(tftypes.Bool, false),
tftypes.NewValue(tftypes.Bool, true),
}),
},
)),
},
SchemaResponse: &resource.SchemaResponse{
Schema: &tfprotov6.Schema{
Block: &tfprotov6.SchemaBlock{
Attributes: []*tfprotov6.SchemaAttribute{
ComputedStringAttribute("hostname"),
RequiredStringAttribute("cabinet"),
RequiredNumberAttribute("unit"),
RequiredBoolAttribute("active"),
RequiredListAttribute("tags", tftypes.String),
OptionalComputedListAttribute("magic_numbers", tftypes.Number),
},
},
},
},
IdentitySchemaResponse: &resource.IdentitySchemaResponse{
Schema: &tfprotov6.ResourceIdentitySchema{
Version: 1,
IdentityAttributes: []*tfprotov6.ResourceIdentitySchemaAttribute{
{
Name: "cabinet",
Type: tftypes.String,
RequiredForImport: true,
},
{
Name: "unit",
Type: tftypes.Number,
OptionalForImport: true,
},
{
Name: "active",
Type: tftypes.Bool,
OptionalForImport: true,
},
{
Name: "tags",
Type: tftypes.List{
ElementType: tftypes.String,
},
OptionalForImport: true,
},
{
Name: "magic_numbers",
Type: tftypes.List{
ElementType: tftypes.Number,
},
OptionalForImport: true,
},
},
},
},
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@ func TestImportBlock_AsFirstStep(t *testing.T) {
Config: `resource "examplecloud_container" "test" {
name = "somevalue"
location = "westeurope"
}`,
}

import {
to = examplecloud_container.test
id = "westeurope/somevalue"
}
`,
ImportPlanChecks: r.ImportPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectResourceAction("examplecloud_container.test", plancheck.ResourceActionNoop),
Expand Down
28 changes: 28 additions & 0 deletions helper/resource/importstate/import_block_in_config_file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,31 @@ func TestImportBlock_InConfigFile(t *testing.T) {
},
})
}

func TestImportBlock_WithResourceIdentity_InConfigFile(t *testing.T) {
t.Parallel()

r.UnitTest(t, r.TestCase{
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.SkipBelow(tfversion.Version1_12_0), // ImportBlockWithResourceIdentity requires Terraform 1.12.0 or later
},
ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){
"examplecloud": providerserver.NewProviderServer(testprovider.Provider{
Resources: map[string]testprovider.Resource{
"examplecloud_container": examplecloudResource(),
},
}),
},
Steps: []r.TestStep{
{
ConfigFile: config.StaticFile(`testdata/1/examplecloud_container.tf`),
},
{
ResourceName: "examplecloud_container.test",
ImportState: true,
ImportStateKind: r.ImportBlockWithResourceIdentity,
ConfigFile: config.StaticFile(`testdata/examplecloud_container_import_with_identity.tf`),
},
},
})
}
Loading
Loading