diff --git a/internal/services/storagemover/registration.go b/internal/services/storagemover/registration.go index b8d842179e33..1b3567a76bd9 100644 --- a/internal/services/storagemover/registration.go +++ b/internal/services/storagemover/registration.go @@ -66,5 +66,7 @@ func (r Registration) EphemeralResources() []func() ephemeral.EphemeralResource } func (r Registration) ListResources() []sdk.FrameworkListWrappedResource { - return []sdk.FrameworkListWrappedResource{} + return []sdk.FrameworkListWrappedResource{ + StorageMoverProjectListResource{}, + } } diff --git a/internal/services/storagemover/storage_mover_project_resource.go b/internal/services/storagemover/storage_mover_project_resource.go index ae3e25861277..9be578c5a671 100644 --- a/internal/services/storagemover/storage_mover_project_resource.go +++ b/internal/services/storagemover/storage_mover_project_resource.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/pointer" "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" "github.com/hashicorp/go-azure-sdk/resource-manager/storagemover/2025-07-01/projects" "github.com/hashicorp/go-azure-sdk/resource-manager/storagemover/2025-07-01/storagemovers" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" @@ -18,6 +19,8 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" ) +//go:generate go run ../../tools/generator-tests resourceidentity -resource-name storage_mover_project -service-package-name storagemover -properties "name" -compare-values "subscription_id:storage_mover_id,resource_group_name:storage_mover_id,storage_mover_name:storage_mover_id" + type StorageMoverProjectResourceModel struct { Name string `tfschema:"name"` StorageMoverId string `tfschema:"storage_mover_id"` @@ -26,7 +29,14 @@ type StorageMoverProjectResourceModel struct { type StorageMoverProjectResource struct{} -var _ sdk.ResourceWithUpdate = StorageMoverProjectResource{} +var ( + _ sdk.ResourceWithIdentity = StorageMoverProjectResource{} + _ sdk.ResourceWithUpdate = StorageMoverProjectResource{} +) + +func (r StorageMoverProjectResource) Identity() resourceids.ResourceId { + return &projects.ProjectId{} +} func (r StorageMoverProjectResource) ResourceType() string { return "azurerm_storage_mover_project" @@ -109,6 +119,9 @@ func (r StorageMoverProjectResource) Create() sdk.ResourceFunc { } metadata.SetID(id) + if err := pluginsdk.SetResourceIdentityData(metadata.ResourceData, &id); err != nil { + return err + } return nil }, } @@ -173,20 +186,28 @@ func (r StorageMoverProjectResource) Read() sdk.ResourceFunc { return fmt.Errorf("retrieving %s: %+v", *id, err) } - state := StorageMoverProjectResourceModel{ - Name: id.ProjectName, - StorageMoverId: storagemovers.NewStorageMoverID(id.SubscriptionId, id.ResourceGroupName, id.StorageMoverName).ID(), - } + return r.flatten(metadata, id, resp.Model) + }, + } +} - if resp.Model != nil { - if properties := resp.Model.Properties; properties != nil { - state.Description = pointer.From(properties.Description) - } - } +func (r StorageMoverProjectResource) flatten(metadata sdk.ResourceMetaData, id *projects.ProjectId, model *projects.Project) error { + state := StorageMoverProjectResourceModel{ + Name: id.ProjectName, + StorageMoverId: storagemovers.NewStorageMoverID(id.SubscriptionId, id.ResourceGroupName, id.StorageMoverName).ID(), + } - return metadata.Encode(&state) - }, + if model != nil { + if properties := model.Properties; properties != nil { + state.Description = pointer.From(properties.Description) + } + } + + if err := pluginsdk.SetResourceIdentityData(metadata.ResourceData, id); err != nil { + return err } + + return metadata.Encode(&state) } func (r StorageMoverProjectResource) Delete() sdk.ResourceFunc { diff --git a/internal/services/storagemover/storage_mover_project_resource_identity_gen_test.go b/internal/services/storagemover/storage_mover_project_resource_identity_gen_test.go new file mode 100644 index 000000000000..8673992c72d0 --- /dev/null +++ b/internal/services/storagemover/storage_mover_project_resource_identity_gen_test.go @@ -0,0 +1,40 @@ +// Copyright IBM Corp. 2014, 2025 +// SPDX-License-Identifier: MPL-2.0 + +package storagemover_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + customstatecheck "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/statecheck" +) + +func TestAccStorageMoverProject_resourceIdentity(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_storage_mover_project", "test") + r := StorageMoverProjectResource{} + + checkedFields := map[string]struct{}{ + "name": {}, + "resource_group_name": {}, + "storage_mover_name": {}, + "subscription_id": {}, + } + + data.ResourceIdentityTest(t, []acceptance.TestStep{ + { + Config: r.basic(data), + ConfigStateChecks: []statecheck.StateCheck{ + customstatecheck.ExpectAllIdentityFieldsAreChecked("azurerm_storage_mover_project.test", checkedFields), + statecheck.ExpectIdentityValueMatchesStateAtPath("azurerm_storage_mover_project.test", tfjsonpath.New("name"), tfjsonpath.New("name")), + customstatecheck.ExpectStateContainsIdentityValueAtPath("azurerm_storage_mover_project.test", tfjsonpath.New("resource_group_name"), tfjsonpath.New("storage_mover_id")), + customstatecheck.ExpectStateContainsIdentityValueAtPath("azurerm_storage_mover_project.test", tfjsonpath.New("storage_mover_name"), tfjsonpath.New("storage_mover_id")), + customstatecheck.ExpectStateContainsIdentityValueAtPath("azurerm_storage_mover_project.test", tfjsonpath.New("subscription_id"), tfjsonpath.New("storage_mover_id")), + }, + }, + data.ImportBlockWithResourceIdentityStep(false), + data.ImportBlockWithIDStep(false), + }, false) +} diff --git a/internal/services/storagemover/storage_mover_project_resource_list.go b/internal/services/storagemover/storage_mover_project_resource_list.go new file mode 100644 index 000000000000..fae7728be3f3 --- /dev/null +++ b/internal/services/storagemover/storage_mover_project_resource_list.go @@ -0,0 +1,105 @@ +// Copyright IBM Corp. 2014, 2026 +// SPDX-License-Identifier: MPL-2.0 + +package storagemover + +import ( + "context" + "fmt" + + "github.com/hashicorp/go-azure-helpers/framework/typehelpers" + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-sdk/resource-manager/storagemover/2025-07-01/projects" + "github.com/hashicorp/go-azure-sdk/resource-manager/storagemover/2025-07-01/storagemovers" + "github.com/hashicorp/terraform-plugin-framework/list" + "github.com/hashicorp/terraform-plugin-framework/list/schema" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" +) + +type StorageMoverProjectListResource struct{} + +type StorageMoverProjectListModel struct { + StorageMoverId types.String `tfsdk:"storage_mover_id"` +} + +var _ sdk.FrameworkListWrappedResource = new(StorageMoverProjectListResource) + +func (StorageMoverProjectListResource) Metadata(_ context.Context, _ resource.MetadataRequest, response *resource.MetadataResponse) { + response.TypeName = StorageMoverProjectResource{}.ResourceType() +} + +func (StorageMoverProjectListResource) ResourceFunc() *pluginsdk.Resource { + return sdk.WrappedResource(StorageMoverProjectResource{}) +} + +func (StorageMoverProjectListResource) ListResourceConfigSchema(_ context.Context, _ list.ListResourceSchemaRequest, response *list.ListResourceSchemaResponse) { + response.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "storage_mover_id": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + typehelpers.WrappedStringValidator{Func: storagemovers.ValidateStorageMoverID}, + }, + }, + }, + } +} + +func (StorageMoverProjectListResource) List(ctx context.Context, request list.ListRequest, stream *list.ListResultsStream, metadata sdk.ResourceMetadata) { + client := metadata.Client.StorageMover.ProjectsClient + + var data StorageMoverProjectListModel + diags := request.Config.Get(ctx, &data) + if diags.HasError() { + stream.Results = list.ListResultsStreamDiagnostics(diags) + return + } + + storageMoverID, err := storagemovers.ParseStorageMoverID(data.StorageMoverId.ValueString()) + if err != nil { + sdk.SetResponseErrorDiagnostic(stream, fmt.Sprintf("parsing Storage Mover ID for `%s`", StorageMoverProjectResource{}.ResourceType()), err) + return + } + + resp, err := client.ListComplete(ctx, projects.NewStorageMoverID(storageMoverID.SubscriptionId, storageMoverID.ResourceGroupName, storageMoverID.StorageMoverName)) + if err != nil { + sdk.SetResponseErrorDiagnostic(stream, fmt.Sprintf("listing `%s`", StorageMoverProjectResource{}.ResourceType()), err) + return + } + + resource := StorageMoverProjectResource{} + stream.Results = func(push func(list.ListResult) bool) { + for _, item := range resp.Items { + result := request.NewListResult(ctx) + result.DisplayName = pointer.From(item.Name) + + id, err := projects.ParseProjectIDInsensitively(pointer.From(item.Id)) + if err != nil { + sdk.SetErrorDiagnosticAndPushListResult(result, push, "parsing Storage Mover Project ID", err) + return + } + + meta := sdk.NewResourceMetaData(metadata.Client, resource) + meta.SetID(id) + + if err := resource.flatten(meta, id, &item); err != nil { + sdk.SetErrorDiagnosticAndPushListResult(result, push, fmt.Sprintf("encoding `%s` resource data", resource.ResourceType()), err) + return + } + + sdk.EncodeListResult(ctx, meta.ResourceData, &result) + if result.Diagnostics.HasError() { + push(result) + return + } + + if !push(result) { + return + } + } + } +} diff --git a/internal/services/storagemover/storage_mover_project_resource_list_test.go b/internal/services/storagemover/storage_mover_project_resource_list_test.go new file mode 100644 index 000000000000..258e9c379e80 --- /dev/null +++ b/internal/services/storagemover/storage_mover_project_resource_list_test.go @@ -0,0 +1,59 @@ +// Copyright IBM Corp. 2014, 2026 +// SPDX-License-Identifier: MPL-2.0 + +package storagemover_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/querycheck" + "github.com/hashicorp/terraform-plugin-testing/tfversion" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/provider/framework" +) + +func TestAccStorageMoverProject_list(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_storage_mover_project", "testlist") + r := StorageMoverProjectResource{} + resourceName := fmt.Sprintf("acctest-sp-%d", data.RandomInteger) + storageMoverName := fmt.Sprintf("acctest-ssm-%d", data.RandomInteger) + resourceGroupName := fmt.Sprintf("acctest-rg-%d", data.RandomInteger) + + resource.Test(t, resource.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_14_0), + }, + ProtoV5ProviderFactories: framework.ProtoV5ProviderFactoriesInit(context.Background(), "azurerm"), + Steps: []resource.TestStep{ + {Config: r.basic(data)}, + { + Query: true, + Config: r.listQuery(), + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectLength("azurerm_storage_mover_project.list", 1), + querycheck.ExpectIdentity("azurerm_storage_mover_project.list", map[string]knownvalue.Check{ + "name": knownvalue.StringExact(resourceName), + "resource_group_name": knownvalue.StringExact(resourceGroupName), + "storage_mover_name": knownvalue.StringExact(storageMoverName), + "subscription_id": knownvalue.StringExact(data.Subscriptions.Primary), + }), + }, + }, + }, + }) +} + +func (r StorageMoverProjectResource) listQuery() string { + return ` +list "azurerm_storage_mover_project" "list" { + provider = azurerm + config { + storage_mover_id = azurerm_storage_mover.test.id + } +} +` +} diff --git a/internal/services/storagemover/storage_mover_project_resource_test.go b/internal/services/storagemover/storage_mover_project_resource_test.go index 30918d8e15d9..cda0f95effb2 100644 --- a/internal/services/storagemover/storage_mover_project_resource_test.go +++ b/internal/services/storagemover/storage_mover_project_resource_test.go @@ -17,11 +17,11 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" ) -type StorageMoverProjectTestResource struct{} +type StorageMoverProjectResource struct{} func TestAccStorageMoverProject_basic(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_mover_project", "test") - r := StorageMoverProjectTestResource{} + r := StorageMoverProjectResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.basic(data), @@ -35,7 +35,7 @@ func TestAccStorageMoverProject_basic(t *testing.T) { func TestAccStorageMoverProject_requiresImport(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_mover_project", "test") - r := StorageMoverProjectTestResource{} + r := StorageMoverProjectResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.basic(data), @@ -49,7 +49,7 @@ func TestAccStorageMoverProject_requiresImport(t *testing.T) { func TestAccStorageMoverProject_complete(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_mover_project", "test") - r := StorageMoverProjectTestResource{} + r := StorageMoverProjectResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.complete(data), @@ -63,7 +63,7 @@ func TestAccStorageMoverProject_complete(t *testing.T) { func TestAccStorageMoverProject_update(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_mover_project", "test") - r := StorageMoverProjectTestResource{} + r := StorageMoverProjectResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.complete(data), @@ -82,7 +82,7 @@ func TestAccStorageMoverProject_update(t *testing.T) { }) } -func (r StorageMoverProjectTestResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { +func (r StorageMoverProjectResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := projects.ParseProjectID(state.ID) if err != nil { return nil, err @@ -99,7 +99,7 @@ func (r StorageMoverProjectTestResource) Exists(ctx context.Context, clients *cl return pointer.To(resp.Model != nil), nil } -func (r StorageMoverProjectTestResource) template(data acceptance.TestData) string { +func (r StorageMoverProjectResource) template(data acceptance.TestData) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { name = "acctest-rg-%d" @@ -114,7 +114,7 @@ resource "azurerm_storage_mover" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomInteger) } -func (r StorageMoverProjectTestResource) basic(data acceptance.TestData) string { +func (r StorageMoverProjectResource) basic(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` provider "azurerm" { @@ -130,7 +130,7 @@ resource "azurerm_storage_mover_project" "test" { `, template, data.RandomInteger) } -func (r StorageMoverProjectTestResource) requiresImport(data acceptance.TestData) string { +func (r StorageMoverProjectResource) requiresImport(data acceptance.TestData) string { config := r.basic(data) return fmt.Sprintf(` %s @@ -142,7 +142,7 @@ resource "azurerm_storage_mover_project" "import" { `, config) } -func (r StorageMoverProjectTestResource) complete(data acceptance.TestData) string { +func (r StorageMoverProjectResource) complete(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` provider "azurerm" { @@ -159,7 +159,7 @@ resource "azurerm_storage_mover_project" "test" { `, template, data.RandomInteger) } -func (r StorageMoverProjectTestResource) update(data acceptance.TestData) string { +func (r StorageMoverProjectResource) update(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` provider "azurerm" { diff --git a/website/docs/list-resources/storage_mover_project.html.markdown b/website/docs/list-resources/storage_mover_project.html.markdown new file mode 100644 index 000000000000..aaab9edc1be9 --- /dev/null +++ b/website/docs/list-resources/storage_mover_project.html.markdown @@ -0,0 +1,30 @@ +--- +subcategory: "Storage Mover" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_storage_mover_project" +description: |- + Lists Storage Mover Project resources. +--- + +# List resource: azurerm_storage_mover_project + +Lists Storage Mover Project resources. + +## Example Usage + +### List Projects in a Storage Mover + +```hcl +list "azurerm_storage_mover_project" "example" { + provider = azurerm + config { + storage_mover_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example-rg/providers/Microsoft.StorageMover/storageMovers/example-mover" + } +} +``` + +## Argument Reference + +This list resource supports the following arguments: + +* `storage_mover_id` - (Required) The ID of the Storage Mover to query. \ No newline at end of file