diff --git a/internal/services/storagemover/registration.go b/internal/services/storagemover/registration.go index b8d842179e33..7514455f507a 100644 --- a/internal/services/storagemover/registration.go +++ b/internal/services/storagemover/registration.go @@ -66,5 +66,12 @@ func (r Registration) EphemeralResources() []func() ephemeral.EphemeralResource } func (r Registration) ListResources() []sdk.FrameworkListWrappedResource { - return []sdk.FrameworkListWrappedResource{} + return []sdk.FrameworkListWrappedResource{ + StorageMoverListResource{}, + StorageMoverAgentListResource{}, + StorageMoverSourceEndpointListResource{}, + StorageMoverTargetEndpointListResource{}, + StorageMoverProjectListResource{}, + StorageMoverJobDefinitionListResource{}, + } } diff --git a/internal/services/storagemover/storage_mover_agent_resource.go b/internal/services/storagemover/storage_mover_agent_resource.go index 6f598df2dc2b..f24b2103be36 100644 --- a/internal/services/storagemover/storage_mover_agent_resource.go +++ b/internal/services/storagemover/storage_mover_agent_resource.go @@ -10,6 +10,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/2023-03-01/agents" "github.com/hashicorp/go-azure-sdk/resource-manager/storagemover/2023-03-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_agent -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 StorageMoverAgentResourceModel struct { Name string `tfschema:"name"` StorageMoverId string `tfschema:"storage_mover_id"` @@ -28,7 +31,14 @@ type StorageMoverAgentResourceModel struct { type StorageMoverAgentResource struct{} -var _ sdk.ResourceWithUpdate = StorageMoverAgentResource{} +var ( + _ sdk.ResourceWithIdentity = StorageMoverAgentResource{} + _ sdk.ResourceWithUpdate = StorageMoverAgentResource{} +) + +func (r StorageMoverAgentResource) Identity() resourceids.ResourceId { + return &agents.AgentId{} +} func (r StorageMoverAgentResource) ResourceType() string { return "azurerm_storage_mover_agent" @@ -126,6 +136,9 @@ func (r StorageMoverAgentResource) Create() sdk.ResourceFunc { } metadata.SetID(id) + if err := pluginsdk.SetResourceIdentityData(metadata.ResourceData, &id); err != nil { + return err + } return nil }, } @@ -189,21 +202,28 @@ func (r StorageMoverAgentResource) Read() sdk.ResourceFunc { return fmt.Errorf("retrieving %s: %+v", *id, err) } - state := StorageMoverAgentResourceModel{ - Name: id.AgentName, - StorageMoverId: storagemovers.NewStorageMoverID(id.SubscriptionId, id.ResourceGroupName, id.StorageMoverName).ID(), - } + return r.flatten(metadata, id, resp.Model) + }, + } +} - if model := resp.Model; model != nil { - state.ArcVmUuid = model.Properties.ArcVMUuid - state.ArcVirtualMachineId = model.Properties.ArcResourceId +func (r StorageMoverAgentResource) flatten(metadata sdk.ResourceMetaData, id *agents.AgentId, model *agents.Agent) error { + state := StorageMoverAgentResourceModel{ + Name: id.AgentName, + StorageMoverId: storagemovers.NewStorageMoverID(id.SubscriptionId, id.ResourceGroupName, id.StorageMoverName).ID(), + } - state.Description = pointer.From(model.Properties.Description) - } + if model != nil { + state.ArcVmUuid = model.Properties.ArcVMUuid + state.ArcVirtualMachineId = model.Properties.ArcResourceId + state.Description = pointer.From(model.Properties.Description) + } - return metadata.Encode(&state) - }, + if err := pluginsdk.SetResourceIdentityData(metadata.ResourceData, id); err != nil { + return err } + + return metadata.Encode(&state) } func (r StorageMoverAgentResource) Delete() sdk.ResourceFunc { diff --git a/internal/services/storagemover/storage_mover_agent_resource_identity_gen_test.go b/internal/services/storagemover/storage_mover_agent_resource_identity_gen_test.go new file mode 100644 index 000000000000..53245ada2e89 --- /dev/null +++ b/internal/services/storagemover/storage_mover_agent_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 TestAccStorageMoverAgent_resourceIdentity(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_storage_mover_agent", "test") + r := StorageMoverAgentResource{} + + 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_agent.test", checkedFields), + statecheck.ExpectIdentityValueMatchesStateAtPath("azurerm_storage_mover_agent.test", tfjsonpath.New("name"), tfjsonpath.New("name")), + customstatecheck.ExpectStateContainsIdentityValueAtPath("azurerm_storage_mover_agent.test", tfjsonpath.New("resource_group_name"), tfjsonpath.New("storage_mover_id")), + customstatecheck.ExpectStateContainsIdentityValueAtPath("azurerm_storage_mover_agent.test", tfjsonpath.New("storage_mover_name"), tfjsonpath.New("storage_mover_id")), + customstatecheck.ExpectStateContainsIdentityValueAtPath("azurerm_storage_mover_agent.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_agent_resource_list.go b/internal/services/storagemover/storage_mover_agent_resource_list.go new file mode 100644 index 000000000000..09079987b211 --- /dev/null +++ b/internal/services/storagemover/storage_mover_agent_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/2023-03-01/agents" + "github.com/hashicorp/go-azure-sdk/resource-manager/storagemover/2023-03-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 StorageMoverAgentListResource struct{} + +type StorageMoverAgentListModel struct { + StorageMoverId types.String `tfsdk:"storage_mover_id"` +} + +var _ sdk.FrameworkListWrappedResource = new(StorageMoverAgentListResource) + +func (StorageMoverAgentListResource) Metadata(_ context.Context, _ resource.MetadataRequest, response *resource.MetadataResponse) { + response.TypeName = StorageMoverAgentResource{}.ResourceType() +} + +func (StorageMoverAgentListResource) ResourceFunc() *pluginsdk.Resource { + return sdk.WrappedResource(StorageMoverAgentResource{}) +} + +func (StorageMoverAgentListResource) 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 (StorageMoverAgentListResource) List(ctx context.Context, request list.ListRequest, stream *list.ListResultsStream, metadata sdk.ResourceMetadata) { + client := metadata.Client.StorageMover.AgentsClient + + var data StorageMoverAgentListModel + 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`", StorageMoverAgentResource{}.ResourceType()), err) + return + } + + resp, err := client.ListComplete(ctx, agents.NewStorageMoverID(storageMoverID.SubscriptionId, storageMoverID.ResourceGroupName, storageMoverID.StorageMoverName)) + if err != nil { + sdk.SetResponseErrorDiagnostic(stream, fmt.Sprintf("listing `%s`", StorageMoverAgentResource{}.ResourceType()), err) + return + } + + resource := StorageMoverAgentResource{} + 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 := agents.ParseAgentIDInsensitively(pointer.From(item.Id)) + if err != nil { + sdk.SetErrorDiagnosticAndPushListResult(result, push, "parsing Storage Mover Agent 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_agent_resource_list_test.go b/internal/services/storagemover/storage_mover_agent_resource_list_test.go new file mode 100644 index 000000000000..6af05770ebd3 --- /dev/null +++ b/internal/services/storagemover/storage_mover_agent_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 TestAccStorageMoverAgent_list(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_storage_mover_agent", "testlist") + r := StorageMoverAgentResource{} + resourceName := fmt.Sprintf("acctest-sa-%d", data.RandomInteger) + storageMoverName := fmt.Sprintf("acctest-ssm-%d", data.RandomInteger) + resourceGroupName := fmt.Sprintf("acctestRG-%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_agent.list", 1), + querycheck.ExpectIdentity("azurerm_storage_mover_agent.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 StorageMoverAgentResource) listQuery() string { + return ` +list "azurerm_storage_mover_agent" "list" { + provider = azurerm + config { + storage_mover_id = azurerm_storage_mover.test.id + } +} +` +} diff --git a/internal/services/storagemover/storage_mover_agent_resource_test.go b/internal/services/storagemover/storage_mover_agent_resource_test.go index d5287d4651f3..b943bf163108 100644 --- a/internal/services/storagemover/storage_mover_agent_resource_test.go +++ b/internal/services/storagemover/storage_mover_agent_resource_test.go @@ -6,7 +6,6 @@ package storagemover_test import ( "context" "fmt" - "os" "testing" "github.com/hashicorp/go-azure-helpers/lang/pointer" @@ -19,11 +18,11 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" ) -type StorageMoverAgentTestResource struct{} +type StorageMoverAgentResource struct{} func TestAccStorageMoverAgent_basic(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_mover_agent", "test") - r := StorageMoverAgentTestResource{} + r := StorageMoverAgentResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.basic(data), @@ -37,7 +36,7 @@ func TestAccStorageMoverAgent_basic(t *testing.T) { func TestAccStorageMoverAgent_requiresImport(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_mover_agent", "test") - r := StorageMoverAgentTestResource{} + r := StorageMoverAgentResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { @@ -52,7 +51,7 @@ func TestAccStorageMoverAgent_requiresImport(t *testing.T) { func TestAccStorageMoverAgent_complete(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_mover_agent", "test") - r := StorageMoverAgentTestResource{} + r := StorageMoverAgentResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.complete(data), @@ -66,7 +65,7 @@ func TestAccStorageMoverAgent_complete(t *testing.T) { func TestAccStorageMoverAgent_update(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_mover_agent", "test") - r := StorageMoverAgentTestResource{} + r := StorageMoverAgentResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.complete(data), @@ -85,7 +84,7 @@ func TestAccStorageMoverAgent_update(t *testing.T) { }) } -func (r StorageMoverAgentTestResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { +func (r StorageMoverAgentResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := agents.ParseAgentID(state.ID) if err != nil { return nil, err @@ -102,7 +101,8 @@ func (r StorageMoverAgentTestResource) Exists(ctx context.Context, clients *clie return pointer.To(resp.Model != nil), nil } -func (r StorageMoverAgentTestResource) template(data acceptance.TestData) string { +func (r StorageMoverAgentResource) template(data acceptance.TestData) string { + clientData := data.Client() randomUUID, _ := uuid.GenerateUUID() return fmt.Sprintf(` @@ -209,8 +209,8 @@ resource "azurerm_linux_virtual_machine" "test" { uuid = "%[3]s" location = azurerm_resource_group.test.location tenant_id = data.azurerm_client_config.current.tenant_id - client_id = data.azurerm_client_config.current.client_id - client_secret = "%[4]s" + client_id = "%[4]s" + client_secret = "%[5]s" subscription_id = data.azurerm_client_config.current.subscription_id }) destination = "/home/adminuser/install_arc_agent.sh" @@ -226,6 +226,18 @@ resource "azurerm_linux_virtual_machine" "test" { } } +resource "azurerm_user_assigned_identity" "test" { + name = "acctest-uai-%[1]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} + +resource "azurerm_role_assignment" "test" { + scope = azurerm_resource_group.test.id + role_definition_name = "Contributor" + principal_id = azurerm_user_assigned_identity.test.principal_id +} + resource "azurerm_storage_mover" "test" { name = "acctest-ssm-%[1]d" resource_group_name = azurerm_resource_group.test.name @@ -244,10 +256,10 @@ data "azurerm_arc_machine" "test" { } -`, data.RandomInteger, data.Locations.Primary, randomUUID, os.Getenv("ARM_CLIENT_SECRET")) +`, data.RandomInteger, data.Locations.Primary, randomUUID, clientData.Default.ClientID, clientData.Default.ClientSecret) } -func (r StorageMoverAgentTestResource) basic(data acceptance.TestData) string { +func (r StorageMoverAgentResource) basic(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` @@ -271,7 +283,7 @@ resource "azurerm_storage_mover_agent" "test" { `, template, data.RandomInteger) } -func (r StorageMoverAgentTestResource) requiresImport(data acceptance.TestData) string { +func (r StorageMoverAgentResource) requiresImport(data acceptance.TestData) string { config := r.basic(data) return fmt.Sprintf(` %s @@ -287,7 +299,7 @@ resource "azurerm_storage_mover_agent" "import" { `, config) } -func (r StorageMoverAgentTestResource) complete(data acceptance.TestData) string { +func (r StorageMoverAgentResource) complete(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` @@ -312,7 +324,7 @@ resource "azurerm_storage_mover_agent" "test" { `, template, data.RandomInteger) } -func (r StorageMoverAgentTestResource) update(data acceptance.TestData) string { +func (r StorageMoverAgentResource) update(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` diff --git a/internal/services/storagemover/storage_mover_job_definition_resource.go b/internal/services/storagemover/storage_mover_job_definition_resource.go index 1024c6b098c9..08e36874c6ec 100644 --- a/internal/services/storagemover/storage_mover_job_definition_resource.go +++ b/internal/services/storagemover/storage_mover_job_definition_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/jobdefinitions" "github.com/hashicorp/go-azure-sdk/resource-manager/storagemover/2025-07-01/projects" "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_job_definition -service-package-name storagemover -properties "name" -compare-values "subscription_id:storage_mover_project_id,resource_group_name:storage_mover_project_id,storage_mover_name:storage_mover_project_id,project_name:storage_mover_project_id" + type StorageMoverJobDefinitionResourceModel struct { Name string `tfschema:"name"` StorageMoverProjectId string `tfschema:"storage_mover_project_id"` @@ -32,7 +35,14 @@ type StorageMoverJobDefinitionResourceModel struct { type StorageMoverJobDefinitionResource struct{} -var _ sdk.ResourceWithUpdate = StorageMoverJobDefinitionResource{} +var ( + _ sdk.ResourceWithIdentity = StorageMoverJobDefinitionResource{} + _ sdk.ResourceWithUpdate = StorageMoverJobDefinitionResource{} +) + +func (r StorageMoverJobDefinitionResource) Identity() resourceids.ResourceId { + return &jobdefinitions.JobDefinitionId{} +} func (r StorageMoverJobDefinitionResource) ResourceType() string { return "azurerm_storage_mover_job_definition" @@ -174,6 +184,9 @@ func (r StorageMoverJobDefinitionResource) Create() sdk.ResourceFunc { } metadata.SetID(id) + if err := pluginsdk.SetResourceIdentityData(metadata.ResourceData, &id); err != nil { + return err + } return nil }, } @@ -254,29 +267,32 @@ func (r StorageMoverJobDefinitionResource) Read() sdk.ResourceFunc { return fmt.Errorf("retrieving %s: %+v", *id, err) } - state := StorageMoverJobDefinitionResourceModel{ - Name: id.JobDefinitionName, - StorageMoverProjectId: projects.NewProjectID(id.SubscriptionId, id.ResourceGroupName, id.StorageMoverName, id.ProjectName).ID(), - } - - if v := resp.Model; v != nil { - state.AgentName = pointer.From(v.Properties.AgentName) - - state.CopyMode = v.Properties.CopyMode - - state.Description = pointer.From(v.Properties.Description) - - state.SourceName = v.Properties.SourceName + return r.flatten(metadata, id, resp.Model) + }, + } +} - state.SourceSubpath = pointer.From(v.Properties.SourceSubpath) +func (r StorageMoverJobDefinitionResource) flatten(metadata sdk.ResourceMetaData, id *jobdefinitions.JobDefinitionId, model *jobdefinitions.JobDefinition) error { + state := StorageMoverJobDefinitionResourceModel{ + Name: id.JobDefinitionName, + StorageMoverProjectId: projects.NewProjectID(id.SubscriptionId, id.ResourceGroupName, id.StorageMoverName, id.ProjectName).ID(), + } - state.TargetName = v.Properties.TargetName + if model != nil { + state.AgentName = pointer.From(model.Properties.AgentName) + state.CopyMode = model.Properties.CopyMode + state.Description = pointer.From(model.Properties.Description) + state.SourceName = model.Properties.SourceName + state.SourceSubpath = pointer.From(model.Properties.SourceSubpath) + state.TargetName = model.Properties.TargetName + state.TargetSubpath = pointer.From(model.Properties.TargetSubpath) + } - state.TargetSubpath = pointer.From(v.Properties.TargetSubpath) - } - return metadata.Encode(&state) - }, + if err := pluginsdk.SetResourceIdentityData(metadata.ResourceData, id); err != nil { + return err } + + return metadata.Encode(&state) } func (r StorageMoverJobDefinitionResource) Delete() sdk.ResourceFunc { diff --git a/internal/services/storagemover/storage_mover_job_definition_resource_identity_gen_test.go b/internal/services/storagemover/storage_mover_job_definition_resource_identity_gen_test.go new file mode 100644 index 000000000000..6f809ff14c34 --- /dev/null +++ b/internal/services/storagemover/storage_mover_job_definition_resource_identity_gen_test.go @@ -0,0 +1,42 @@ +// 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 TestAccStorageMoverJobDefinition_resourceIdentity(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_storage_mover_job_definition", "test") + r := StorageMoverJobDefinitionResource{} + + checkedFields := map[string]struct{}{ + "name": {}, + "project_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_job_definition.test", checkedFields), + statecheck.ExpectIdentityValueMatchesStateAtPath("azurerm_storage_mover_job_definition.test", tfjsonpath.New("name"), tfjsonpath.New("name")), + customstatecheck.ExpectStateContainsIdentityValueAtPath("azurerm_storage_mover_job_definition.test", tfjsonpath.New("project_name"), tfjsonpath.New("storage_mover_project_id")), + customstatecheck.ExpectStateContainsIdentityValueAtPath("azurerm_storage_mover_job_definition.test", tfjsonpath.New("resource_group_name"), tfjsonpath.New("storage_mover_project_id")), + customstatecheck.ExpectStateContainsIdentityValueAtPath("azurerm_storage_mover_job_definition.test", tfjsonpath.New("storage_mover_name"), tfjsonpath.New("storage_mover_project_id")), + customstatecheck.ExpectStateContainsIdentityValueAtPath("azurerm_storage_mover_job_definition.test", tfjsonpath.New("subscription_id"), tfjsonpath.New("storage_mover_project_id")), + }, + }, + data.ImportBlockWithResourceIdentityStep(false), + data.ImportBlockWithIDStep(false), + }, false) +} diff --git a/internal/services/storagemover/storage_mover_job_definition_resource_list.go b/internal/services/storagemover/storage_mover_job_definition_resource_list.go new file mode 100644 index 000000000000..6b0adb1956fd --- /dev/null +++ b/internal/services/storagemover/storage_mover_job_definition_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/jobdefinitions" + "github.com/hashicorp/go-azure-sdk/resource-manager/storagemover/2025-07-01/projects" + "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 StorageMoverJobDefinitionListResource struct{} + +type StorageMoverJobDefinitionListModel struct { + StorageMoverProjectId types.String `tfsdk:"storage_mover_project_id"` +} + +var _ sdk.FrameworkListWrappedResource = new(StorageMoverJobDefinitionListResource) + +func (StorageMoverJobDefinitionListResource) Metadata(_ context.Context, _ resource.MetadataRequest, response *resource.MetadataResponse) { + response.TypeName = StorageMoverJobDefinitionResource{}.ResourceType() +} + +func (StorageMoverJobDefinitionListResource) ResourceFunc() *pluginsdk.Resource { + return sdk.WrappedResource(StorageMoverJobDefinitionResource{}) +} + +func (StorageMoverJobDefinitionListResource) ListResourceConfigSchema(_ context.Context, _ list.ListResourceSchemaRequest, response *list.ListResourceSchemaResponse) { + response.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "storage_mover_project_id": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + typehelpers.WrappedStringValidator{Func: projects.ValidateProjectID}, + }, + }, + }, + } +} + +func (StorageMoverJobDefinitionListResource) List(ctx context.Context, request list.ListRequest, stream *list.ListResultsStream, metadata sdk.ResourceMetadata) { + client := metadata.Client.StorageMover.JobDefinitionsClient + + var data StorageMoverJobDefinitionListModel + diags := request.Config.Get(ctx, &data) + if diags.HasError() { + stream.Results = list.ListResultsStreamDiagnostics(diags) + return + } + + projectID, err := projects.ParseProjectID(data.StorageMoverProjectId.ValueString()) + if err != nil { + sdk.SetResponseErrorDiagnostic(stream, fmt.Sprintf("parsing Storage Mover Project ID for `%s`", StorageMoverJobDefinitionResource{}.ResourceType()), err) + return + } + + resp, err := client.ListComplete(ctx, jobdefinitions.NewProjectID(projectID.SubscriptionId, projectID.ResourceGroupName, projectID.StorageMoverName, projectID.ProjectName)) + if err != nil { + sdk.SetResponseErrorDiagnostic(stream, fmt.Sprintf("listing `%s`", StorageMoverJobDefinitionResource{}.ResourceType()), err) + return + } + + resource := StorageMoverJobDefinitionResource{} + 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 := jobdefinitions.ParseJobDefinitionIDInsensitively(pointer.From(item.Id)) + if err != nil { + sdk.SetErrorDiagnosticAndPushListResult(result, push, "parsing Storage Mover Job Definition 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_job_definition_resource_list_test.go b/internal/services/storagemover/storage_mover_job_definition_resource_list_test.go new file mode 100644 index 000000000000..f6370528e90f --- /dev/null +++ b/internal/services/storagemover/storage_mover_job_definition_resource_list_test.go @@ -0,0 +1,61 @@ +// 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 TestAccStorageMoverJobDefinition_list(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_storage_mover_job_definition", "testlist") + r := StorageMoverJobDefinitionResource{} + resourceName := fmt.Sprintf("acctest-sjd-%d", data.RandomInteger) + projectName := fmt.Sprintf("acctest-sp-%d", data.RandomInteger) + storageMoverName := fmt.Sprintf("acctest-ssm-%d", data.RandomInteger) + resourceGroupName := fmt.Sprintf("acctestRG-%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_job_definition.list", 1), + querycheck.ExpectIdentity("azurerm_storage_mover_job_definition.list", map[string]knownvalue.Check{ + "name": knownvalue.StringExact(resourceName), + "project_name": knownvalue.StringExact(projectName), + "resource_group_name": knownvalue.StringExact(resourceGroupName), + "storage_mover_name": knownvalue.StringExact(storageMoverName), + "subscription_id": knownvalue.StringExact(data.Subscriptions.Primary), + }), + }, + }, + }, + }) +} + +func (r StorageMoverJobDefinitionResource) listQuery() string { + return ` +list "azurerm_storage_mover_job_definition" "list" { + provider = azurerm + config { + storage_mover_project_id = azurerm_storage_mover_project.test.id + } +} +` +} diff --git a/internal/services/storagemover/storage_mover_job_definition_resource_test.go b/internal/services/storagemover/storage_mover_job_definition_resource_test.go index 4d0e188b772c..7658288e05ff 100644 --- a/internal/services/storagemover/storage_mover_job_definition_resource_test.go +++ b/internal/services/storagemover/storage_mover_job_definition_resource_test.go @@ -17,11 +17,11 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" ) -type StorageMoverJobDefinitionTestResource struct{} +type StorageMoverJobDefinitionResource struct{} func TestAccStorageMoverJobDefinition_basic(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_mover_job_definition", "test") - r := StorageMoverJobDefinitionTestResource{} + r := StorageMoverJobDefinitionResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.basic(data), @@ -35,7 +35,7 @@ func TestAccStorageMoverJobDefinition_basic(t *testing.T) { func TestAccStorageMoverJobDefinition_requiresImport(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_mover_job_definition", "test") - r := StorageMoverJobDefinitionTestResource{} + r := StorageMoverJobDefinitionResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.basic(data), @@ -49,7 +49,7 @@ func TestAccStorageMoverJobDefinition_requiresImport(t *testing.T) { func TestAccStorageMoverJobDefinition_complete(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_mover_job_definition", "test") - r := StorageMoverJobDefinitionTestResource{} + r := StorageMoverJobDefinitionResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.complete(data), @@ -63,7 +63,7 @@ func TestAccStorageMoverJobDefinition_complete(t *testing.T) { func TestAccStorageMoverJobDefinition_update(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_mover_job_definition", "test") - r := StorageMoverJobDefinitionTestResource{} + r := StorageMoverJobDefinitionResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.complete(data), @@ -82,7 +82,7 @@ func TestAccStorageMoverJobDefinition_update(t *testing.T) { }) } -func (r StorageMoverJobDefinitionTestResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { +func (r StorageMoverJobDefinitionResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := jobdefinitions.ParseJobDefinitionID(state.ID) if err != nil { return nil, err @@ -99,7 +99,7 @@ func (r StorageMoverJobDefinitionTestResource) Exists(ctx context.Context, clien return pointer.To(resp.Model != nil), nil } -func (r StorageMoverJobDefinitionTestResource) template(data acceptance.TestData) string { +func (r StorageMoverJobDefinitionResource) template(data acceptance.TestData) string { return fmt.Sprintf(` @@ -147,10 +147,10 @@ resource "azurerm_storage_mover_project" "test" { name = "acctest-sp-%[2]d" storage_mover_id = azurerm_storage_mover.test.id } -`, StorageMoverAgentTestResource{}.template(data), data.RandomInteger, data.Locations.Primary, data.RandomString) +`, StorageMoverAgentResource{}.template(data), data.RandomInteger, data.Locations.Primary, data.RandomString) } -func (r StorageMoverJobDefinitionTestResource) basic(data acceptance.TestData) string { +func (r StorageMoverJobDefinitionResource) basic(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` provider "azurerm" { @@ -174,7 +174,7 @@ resource "azurerm_storage_mover_job_definition" "test" { `, template, data.RandomInteger) } -func (r StorageMoverJobDefinitionTestResource) requiresImport(data acceptance.TestData) string { +func (r StorageMoverJobDefinitionResource) requiresImport(data acceptance.TestData) string { config := r.basic(data) return fmt.Sprintf(` %s @@ -190,7 +190,7 @@ resource "azurerm_storage_mover_job_definition" "import" { `, config) } -func (r StorageMoverJobDefinitionTestResource) complete(data acceptance.TestData) string { +func (r StorageMoverJobDefinitionResource) complete(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` provider "azurerm" { @@ -217,7 +217,7 @@ resource "azurerm_storage_mover_job_definition" "test" { `, template, data.RandomInteger) } -func (r StorageMoverJobDefinitionTestResource) update(data acceptance.TestData) string { +func (r StorageMoverJobDefinitionResource) update(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` provider "azurerm" { 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/internal/services/storagemover/storage_mover_resource.go b/internal/services/storagemover/storage_mover_resource.go index c6c6b90e09bb..d67728447928 100644 --- a/internal/services/storagemover/storage_mover_resource.go +++ b/internal/services/storagemover/storage_mover_resource.go @@ -11,12 +11,15 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" "github.com/hashicorp/go-azure-sdk/resource-manager/storagemover/2025-07-01/storagemovers" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" ) +//go:generate go run ../../tools/generator-tests resourceidentity -resource-name storage_mover -service-package-name storagemover -properties "name,resource_group_name" -known-values "subscription_id:data.Subscriptions.Primary" + type StorageMoverModel struct { Name string `tfschema:"name"` ResourceGroupName string `tfschema:"resource_group_name"` @@ -27,7 +30,14 @@ type StorageMoverModel struct { type StorageMoverResource struct{} -var _ sdk.ResourceWithUpdate = StorageMoverResource{} +var ( + _ sdk.ResourceWithIdentity = StorageMoverResource{} + _ sdk.ResourceWithUpdate = StorageMoverResource{} +) + +func (r StorageMoverResource) Identity() resourceids.ResourceId { + return &storagemovers.StorageMoverId{} +} func (r StorageMoverResource) ResourceType() string { return "azurerm_storage_mover" @@ -106,6 +116,9 @@ func (r StorageMoverResource) Create() sdk.ResourceFunc { } metadata.SetID(id) + if err := pluginsdk.SetResourceIdentityData(metadata.ResourceData, &id); err != nil { + return err + } return nil }, @@ -180,27 +193,35 @@ func (r StorageMoverResource) Read() sdk.ResourceFunc { return fmt.Errorf("retrieving %s: model was nil", *id) } - state := StorageMoverModel{ - Name: id.StorageMoverName, - ResourceGroupName: id.ResourceGroupName, - Location: location.Normalize(model.Location), - } + return r.flatten(metadata, id, model) + }, + } +} - description := "" - if properties := model.Properties; properties != nil { - if properties.Description != nil { - description = *properties.Description - } - } - state.Description = description +func (r StorageMoverResource) flatten(metadata sdk.ResourceMetaData, id *storagemovers.StorageMoverId, model *storagemovers.StorageMover) error { + state := StorageMoverModel{ + Name: id.StorageMoverName, + ResourceGroupName: id.ResourceGroupName, + Location: location.Normalize(model.Location), + } - if model.Tags != nil { - state.Tags = *model.Tags - } + description := "" + if properties := model.Properties; properties != nil { + if properties.Description != nil { + description = *properties.Description + } + } + state.Description = description - return metadata.Encode(&state) - }, + if model.Tags != nil { + state.Tags = *model.Tags + } + + if err := pluginsdk.SetResourceIdentityData(metadata.ResourceData, id); err != nil { + return err } + + return metadata.Encode(&state) } func (r StorageMoverResource) Delete() sdk.ResourceFunc { diff --git a/internal/services/storagemover/storage_mover_resource_identity_gen_test.go b/internal/services/storagemover/storage_mover_resource_identity_gen_test.go new file mode 100644 index 000000000000..9d8602268dfd --- /dev/null +++ b/internal/services/storagemover/storage_mover_resource_identity_gen_test.go @@ -0,0 +1,39 @@ +// Copyright IBM Corp. 2014, 2025 +// SPDX-License-Identifier: MPL-2.0 + +package storagemover_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "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 TestAccStorageMover_resourceIdentity(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_storage_mover", "test") + r := StorageMoverResource{} + + checkedFields := map[string]struct{}{ + "subscription_id": {}, + "name": {}, + "resource_group_name": {}, + } + + data.ResourceIdentityTest(t, []acceptance.TestStep{ + { + Config: r.basic(data), + ConfigStateChecks: []statecheck.StateCheck{ + customstatecheck.ExpectAllIdentityFieldsAreChecked("azurerm_storage_mover.test", checkedFields), + statecheck.ExpectIdentityValue("azurerm_storage_mover.test", tfjsonpath.New("subscription_id"), knownvalue.StringExact(data.Subscriptions.Primary)), + statecheck.ExpectIdentityValueMatchesStateAtPath("azurerm_storage_mover.test", tfjsonpath.New("name"), tfjsonpath.New("name")), + statecheck.ExpectIdentityValueMatchesStateAtPath("azurerm_storage_mover.test", tfjsonpath.New("resource_group_name"), tfjsonpath.New("resource_group_name")), + }, + }, + data.ImportBlockWithResourceIdentityStep(false), + data.ImportBlockWithIDStep(false), + }, false) +} diff --git a/internal/services/storagemover/storage_mover_resource_list.go b/internal/services/storagemover/storage_mover_resource_list.go new file mode 100644 index 000000000000..d542f7226c35 --- /dev/null +++ b/internal/services/storagemover/storage_mover_resource_list.go @@ -0,0 +1,98 @@ +// Copyright IBM Corp. 2014, 2026 +// SPDX-License-Identifier: MPL-2.0 + +package storagemover + +import ( + "context" + "fmt" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" + "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/resource" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" +) + +type StorageMoverListResource struct{} + +var _ sdk.FrameworkListWrappedResource = new(StorageMoverListResource) + +func (StorageMoverListResource) Metadata(_ context.Context, _ resource.MetadataRequest, response *resource.MetadataResponse) { + response.TypeName = StorageMoverResource{}.ResourceType() +} + +func (StorageMoverListResource) ResourceFunc() *pluginsdk.Resource { + return sdk.WrappedResource(StorageMoverResource{}) +} + +func (StorageMoverListResource) List(ctx context.Context, request list.ListRequest, stream *list.ListResultsStream, metadata sdk.ResourceMetadata) { + client := metadata.Client.StorageMover.StorageMoversClient + + var data sdk.DefaultListModel + diags := request.Config.Get(ctx, &data) + if diags.HasError() { + stream.Results = list.ListResultsStreamDiagnostics(diags) + return + } + + var results []storagemovers.StorageMover + subscriptionID := metadata.SubscriptionId + if !data.SubscriptionId.IsNull() { + subscriptionID = data.SubscriptionId.ValueString() + } + + resource := StorageMoverResource{} + + switch { + case !data.ResourceGroupName.IsNull(): + resp, err := client.ListComplete(ctx, commonids.NewResourceGroupID(subscriptionID, data.ResourceGroupName.ValueString())) + if err != nil { + sdk.SetResponseErrorDiagnostic(stream, fmt.Sprintf("listing `%s`", resource.ResourceType()), err) + return + } + + results = resp.Items + default: + resp, err := client.ListBySubscriptionComplete(ctx, commonids.NewSubscriptionID(subscriptionID)) + if err != nil { + sdk.SetResponseErrorDiagnostic(stream, fmt.Sprintf("listing `%s`", resource.ResourceType()), err) + return + } + + results = resp.Items + } + + stream.Results = func(push func(list.ListResult) bool) { + for _, item := range results { + result := request.NewListResult(ctx) + result.DisplayName = pointer.From(item.Name) + + id, err := storagemovers.ParseStorageMoverIDInsensitively(pointer.From(item.Id)) + if err != nil { + sdk.SetErrorDiagnosticAndPushListResult(result, push, "parsing Storage Mover 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_resource_list_test.go b/internal/services/storagemover/storage_mover_resource_list_test.go new file mode 100644 index 000000000000..bcf957329002 --- /dev/null +++ b/internal/services/storagemover/storage_mover_resource_list_test.go @@ -0,0 +1,79 @@ +// 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 TestAccStorageMover_list(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_storage_mover", "testlist") + r := StorageMoverResource{} + resourceName := 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.subscriptionListQuery(), + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectLengthAtLeast("azurerm_storage_mover.list", 1), + querycheck.ExpectIdentity("azurerm_storage_mover.list", map[string]knownvalue.Check{ + "name": knownvalue.StringExact(resourceName), + "resource_group_name": knownvalue.StringExact(resourceGroupName), + "subscription_id": knownvalue.StringExact(data.Subscriptions.Primary), + }), + }, + }, + { + Query: true, + Config: r.resourceGroupListQuery(), + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectLength("azurerm_storage_mover.list", 1), + querycheck.ExpectIdentity("azurerm_storage_mover.list", map[string]knownvalue.Check{ + "name": knownvalue.StringExact(resourceName), + "resource_group_name": knownvalue.StringExact(resourceGroupName), + "subscription_id": knownvalue.StringExact(data.Subscriptions.Primary), + }), + }, + }, + }, + }) +} + +func (r StorageMoverResource) subscriptionListQuery() string { + return ` +list "azurerm_storage_mover" "list" { + provider = azurerm +} +` +} + +func (r StorageMoverResource) resourceGroupListQuery() string { + return ` +list "azurerm_storage_mover" "list" { + provider = azurerm + config { + resource_group_name = azurerm_resource_group.test.name + } +} +` +} diff --git a/internal/services/storagemover/storage_mover_resource_test.go b/internal/services/storagemover/storage_mover_resource_test.go index cf782054bfd9..ecbd24a641b4 100644 --- a/internal/services/storagemover/storage_mover_resource_test.go +++ b/internal/services/storagemover/storage_mover_resource_test.go @@ -17,11 +17,11 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" ) -type StorageMoverTestResource struct{} +type StorageMoverResource struct{} func TestAccStorageMover_basic(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_mover", "test") - r := StorageMoverTestResource{} + r := StorageMoverResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.basic(data), @@ -35,7 +35,7 @@ func TestAccStorageMover_basic(t *testing.T) { func TestAccStorageMover_requiresImport(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_mover", "test") - r := StorageMoverTestResource{} + r := StorageMoverResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.basic(data), @@ -49,7 +49,7 @@ func TestAccStorageMover_requiresImport(t *testing.T) { func TestAccStorageMover_complete(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_mover", "test") - r := StorageMoverTestResource{} + r := StorageMoverResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.complete(data), @@ -63,7 +63,7 @@ func TestAccStorageMover_complete(t *testing.T) { func TestAccStorageMover_update(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_mover", "test") - r := StorageMoverTestResource{} + r := StorageMoverResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.complete(data), @@ -82,7 +82,7 @@ func TestAccStorageMover_update(t *testing.T) { }) } -func (r StorageMoverTestResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { +func (r StorageMoverResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := storagemovers.ParseStorageMoverID(state.ID) if err != nil { return nil, err @@ -99,7 +99,7 @@ func (r StorageMoverTestResource) Exists(ctx context.Context, clients *clients.C return pointer.To(resp.Model != nil), nil } -func (r StorageMoverTestResource) template(data acceptance.TestData) string { +func (r StorageMoverResource) template(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -112,7 +112,7 @@ resource "azurerm_resource_group" "test" { `, data.RandomInteger, data.Locations.Primary) } -func (r StorageMoverTestResource) basic(data acceptance.TestData) string { +func (r StorageMoverResource) basic(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` %s @@ -125,7 +125,7 @@ resource "azurerm_storage_mover" "test" { `, template, data.RandomInteger, data.Locations.Primary) } -func (r StorageMoverTestResource) requiresImport(data acceptance.TestData) string { +func (r StorageMoverResource) requiresImport(data acceptance.TestData) string { config := r.basic(data) return fmt.Sprintf(` %s @@ -138,7 +138,7 @@ resource "azurerm_storage_mover" "import" { `, config, data.Locations.Primary) } -func (r StorageMoverTestResource) complete(data acceptance.TestData) string { +func (r StorageMoverResource) complete(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` %s @@ -155,7 +155,7 @@ resource "azurerm_storage_mover" "test" { `, template, data.RandomInteger, data.Locations.Primary) } -func (r StorageMoverTestResource) update(data acceptance.TestData) string { +func (r StorageMoverResource) update(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` %s diff --git a/internal/services/storagemover/storage_mover_source_endpoint_resource.go b/internal/services/storagemover/storage_mover_source_endpoint_resource.go index 0bb9ca8bf801..df7d9577b497 100644 --- a/internal/services/storagemover/storage_mover_source_endpoint_resource.go +++ b/internal/services/storagemover/storage_mover_source_endpoint_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/endpoints" "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_source_endpoint -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 StorageMoverSourceEndpointModel struct { Name string `tfschema:"name"` StorageMoverId string `tfschema:"storage_mover_id"` @@ -29,7 +32,14 @@ type StorageMoverSourceEndpointModel struct { type StorageMoverSourceEndpointResource struct{} -var _ sdk.ResourceWithUpdate = StorageMoverSourceEndpointResource{} +var ( + _ sdk.ResourceWithIdentity = StorageMoverSourceEndpointResource{} + _ sdk.ResourceWithUpdate = StorageMoverSourceEndpointResource{} +) + +func (r StorageMoverSourceEndpointResource) Identity() resourceids.ResourceId { + return &endpoints.EndpointId{} +} func (r StorageMoverSourceEndpointResource) ResourceType() string { return "azurerm_storage_mover_source_endpoint" @@ -145,6 +155,9 @@ func (r StorageMoverSourceEndpointResource) Create() sdk.ResourceFunc { } metadata.SetID(id) + if err := pluginsdk.SetResourceIdentityData(metadata.ResourceData, &id); err != nil { + return err + } return nil }, } @@ -212,31 +225,39 @@ func (r StorageMoverSourceEndpointResource) Read() sdk.ResourceFunc { return fmt.Errorf("retrieving %s: %+v", *id, err) } - state := StorageMoverSourceEndpointModel{ - Name: id.EndpointName, - StorageMoverId: storagemovers.NewStorageMoverID(id.SubscriptionId, id.ResourceGroupName, id.StorageMoverName).ID(), - } + return r.flatten(metadata, id, resp.Model) + }, + } +} - if model := resp.Model; model != nil { - if v, ok := model.Properties.(endpoints.NfsMountEndpointProperties); ok { - state.Export = v.Export - state.Host = v.Host +func (r StorageMoverSourceEndpointResource) flatten(metadata sdk.ResourceMetaData, id *endpoints.EndpointId, model *endpoints.Endpoint) error { + state := StorageMoverSourceEndpointModel{ + Name: id.EndpointName, + StorageMoverId: storagemovers.NewStorageMoverID(id.SubscriptionId, id.ResourceGroupName, id.StorageMoverName).ID(), + } - if v := v.NfsVersion; v != nil { - state.NfsVersion = *v - } + if model != nil { + if v, ok := model.Properties.(endpoints.NfsMountEndpointProperties); ok { + state.Export = v.Export + state.Host = v.Host - des := "" - if v.Description != nil { - des = *v.Description - } - state.Description = des - } + if v := v.NfsVersion; v != nil { + state.NfsVersion = *v } - return metadata.Encode(&state) - }, + description := "" + if v.Description != nil { + description = *v.Description + } + state.Description = description + } + } + + if err := pluginsdk.SetResourceIdentityData(metadata.ResourceData, id); err != nil { + return err } + + return metadata.Encode(&state) } func (r StorageMoverSourceEndpointResource) Delete() sdk.ResourceFunc { diff --git a/internal/services/storagemover/storage_mover_source_endpoint_resource_identity_gen_test.go b/internal/services/storagemover/storage_mover_source_endpoint_resource_identity_gen_test.go new file mode 100644 index 000000000000..ccb9ffb06d63 --- /dev/null +++ b/internal/services/storagemover/storage_mover_source_endpoint_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 TestAccStorageMoverSourceEndpoint_resourceIdentity(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_storage_mover_source_endpoint", "test") + r := StorageMoverSourceEndpointResource{} + + 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_source_endpoint.test", checkedFields), + statecheck.ExpectIdentityValueMatchesStateAtPath("azurerm_storage_mover_source_endpoint.test", tfjsonpath.New("name"), tfjsonpath.New("name")), + customstatecheck.ExpectStateContainsIdentityValueAtPath("azurerm_storage_mover_source_endpoint.test", tfjsonpath.New("resource_group_name"), tfjsonpath.New("storage_mover_id")), + customstatecheck.ExpectStateContainsIdentityValueAtPath("azurerm_storage_mover_source_endpoint.test", tfjsonpath.New("storage_mover_name"), tfjsonpath.New("storage_mover_id")), + customstatecheck.ExpectStateContainsIdentityValueAtPath("azurerm_storage_mover_source_endpoint.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_source_endpoint_resource_list.go b/internal/services/storagemover/storage_mover_source_endpoint_resource_list.go new file mode 100644 index 000000000000..4768f0f74f26 --- /dev/null +++ b/internal/services/storagemover/storage_mover_source_endpoint_resource_list.go @@ -0,0 +1,109 @@ +// 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/endpoints" + "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 StorageMoverSourceEndpointListResource struct{} + +type StorageMoverSourceEndpointListModel struct { + StorageMoverId types.String `tfsdk:"storage_mover_id"` +} + +var _ sdk.FrameworkListWrappedResource = new(StorageMoverSourceEndpointListResource) + +func (StorageMoverSourceEndpointListResource) Metadata(_ context.Context, _ resource.MetadataRequest, response *resource.MetadataResponse) { + response.TypeName = StorageMoverSourceEndpointResource{}.ResourceType() +} + +func (StorageMoverSourceEndpointListResource) ResourceFunc() *pluginsdk.Resource { + return sdk.WrappedResource(StorageMoverSourceEndpointResource{}) +} + +func (StorageMoverSourceEndpointListResource) 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 (StorageMoverSourceEndpointListResource) List(ctx context.Context, request list.ListRequest, stream *list.ListResultsStream, metadata sdk.ResourceMetadata) { + client := metadata.Client.StorageMover.EndpointsClient + + var data StorageMoverSourceEndpointListModel + 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`", StorageMoverSourceEndpointResource{}.ResourceType()), err) + return + } + + resp, err := client.ListComplete(ctx, endpoints.NewStorageMoverID(storageMoverID.SubscriptionId, storageMoverID.ResourceGroupName, storageMoverID.StorageMoverName)) + if err != nil { + sdk.SetResponseErrorDiagnostic(stream, fmt.Sprintf("listing `%s`", StorageMoverSourceEndpointResource{}.ResourceType()), err) + return + } + + resource := StorageMoverSourceEndpointResource{} + stream.Results = func(push func(list.ListResult) bool) { + for _, item := range resp.Items { + if _, ok := item.Properties.(endpoints.NfsMountEndpointProperties); !ok { + continue + } + + result := request.NewListResult(ctx) + result.DisplayName = pointer.From(item.Name) + + id, err := endpoints.ParseEndpointIDInsensitively(pointer.From(item.Id)) + if err != nil { + sdk.SetErrorDiagnosticAndPushListResult(result, push, "parsing Storage Mover Source Endpoint 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_source_endpoint_resource_list_test.go b/internal/services/storagemover/storage_mover_source_endpoint_resource_list_test.go new file mode 100644 index 000000000000..687fdd85928e --- /dev/null +++ b/internal/services/storagemover/storage_mover_source_endpoint_resource_list_test.go @@ -0,0 +1,83 @@ +// 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 TestAccStorageMoverSourceEndpoint_list(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_storage_mover_source_endpoint", "testlist") + r := StorageMoverSourceEndpointResource{} + resourceName := fmt.Sprintf("acctest-smse-%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.listConfig(data)}, + { + Query: true, + Config: r.listQuery(), + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectLength("azurerm_storage_mover_source_endpoint.list", 1), + querycheck.ExpectIdentity("azurerm_storage_mover_source_endpoint.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 StorageMoverSourceEndpointResource) listConfig(data acceptance.TestData) string { + template := StorageMoverTargetEndpointResource{}.template(data) + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_storage_mover_target_endpoint" "other" { + name = "acctest-smte-%d" + storage_mover_id = azurerm_storage_mover.test.id + storage_account_id = azurerm_storage_account.test.id + storage_container_name = azurerm_storage_container.test.name +} + +resource "azurerm_storage_mover_source_endpoint" "test" { + name = "acctest-smse-%d" + storage_mover_id = azurerm_storage_mover.test.id + host = "192.168.0.1" +} +`, template, data.RandomInteger, data.RandomInteger) +} + +func (r StorageMoverSourceEndpointResource) listQuery() string { + return ` +list "azurerm_storage_mover_source_endpoint" "list" { + provider = azurerm + config { + storage_mover_id = azurerm_storage_mover.test.id + } +} +` +} diff --git a/internal/services/storagemover/storage_mover_source_endpoint_resource_test.go b/internal/services/storagemover/storage_mover_source_endpoint_resource_test.go index 2d1b790debce..a0fbe1e0f6b5 100644 --- a/internal/services/storagemover/storage_mover_source_endpoint_resource_test.go +++ b/internal/services/storagemover/storage_mover_source_endpoint_resource_test.go @@ -17,11 +17,11 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" ) -type StorageMoverSourceEndpointTestResource struct{} +type StorageMoverSourceEndpointResource struct{} func TestAccStorageMoverSourceEndpoint_basic(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_mover_source_endpoint", "test") - r := StorageMoverSourceEndpointTestResource{} + r := StorageMoverSourceEndpointResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.basic(data), @@ -35,7 +35,7 @@ func TestAccStorageMoverSourceEndpoint_basic(t *testing.T) { func TestAccStorageMoverSourceEndpoint_requiresImport(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_mover_source_endpoint", "test") - r := StorageMoverSourceEndpointTestResource{} + r := StorageMoverSourceEndpointResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.basic(data), @@ -49,7 +49,7 @@ func TestAccStorageMoverSourceEndpoint_requiresImport(t *testing.T) { func TestAccStorageMoverSourceEndpoint_complete(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_mover_source_endpoint", "test") - r := StorageMoverSourceEndpointTestResource{} + r := StorageMoverSourceEndpointResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.complete(data), @@ -63,7 +63,7 @@ func TestAccStorageMoverSourceEndpoint_complete(t *testing.T) { func TestAccStorageMoverSourceEndpoint_update(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_mover_source_endpoint", "test") - r := StorageMoverSourceEndpointTestResource{} + r := StorageMoverSourceEndpointResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.complete(data), @@ -82,7 +82,7 @@ func TestAccStorageMoverSourceEndpoint_update(t *testing.T) { }) } -func (r StorageMoverSourceEndpointTestResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { +func (r StorageMoverSourceEndpointResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := endpoints.ParseEndpointID(state.ID) if err != nil { return nil, err @@ -99,7 +99,7 @@ func (r StorageMoverSourceEndpointTestResource) Exists(ctx context.Context, clie return pointer.To(resp.Model != nil), nil } -func (r StorageMoverSourceEndpointTestResource) template(data acceptance.TestData) string { +func (r StorageMoverSourceEndpointResource) template(data acceptance.TestData) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { name = "acctest-rg-%d" @@ -113,7 +113,7 @@ resource "azurerm_storage_mover" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomInteger) } -func (r StorageMoverSourceEndpointTestResource) basic(data acceptance.TestData) string { +func (r StorageMoverSourceEndpointResource) basic(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` @@ -131,7 +131,7 @@ resource "azurerm_storage_mover_source_endpoint" "test" { `, template, data.RandomInteger) } -func (r StorageMoverSourceEndpointTestResource) requiresImport(data acceptance.TestData) string { +func (r StorageMoverSourceEndpointResource) requiresImport(data acceptance.TestData) string { config := r.basic(data) return fmt.Sprintf(` @@ -145,7 +145,7 @@ resource "azurerm_storage_mover_source_endpoint" "import" { `, config) } -func (r StorageMoverSourceEndpointTestResource) complete(data acceptance.TestData) string { +func (r StorageMoverSourceEndpointResource) complete(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` provider "azurerm" { @@ -165,7 +165,7 @@ resource "azurerm_storage_mover_source_endpoint" "test" { `, template, data.RandomInteger) } -func (r StorageMoverSourceEndpointTestResource) update(data acceptance.TestData) string { +func (r StorageMoverSourceEndpointResource) update(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` provider "azurerm" { diff --git a/internal/services/storagemover/storage_mover_target_endpoint_resource.go b/internal/services/storagemover/storage_mover_target_endpoint_resource.go index 0b4871a2fe16..4c613af7f858 100644 --- a/internal/services/storagemover/storage_mover_target_endpoint_resource.go +++ b/internal/services/storagemover/storage_mover_target_endpoint_resource.go @@ -12,6 +12,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/commonids" + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" "github.com/hashicorp/go-azure-sdk/resource-manager/storagemover/2025-07-01/endpoints" "github.com/hashicorp/go-azure-sdk/resource-manager/storagemover/2025-07-01/storagemovers" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" @@ -20,6 +21,8 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" ) +//go:generate go run ../../tools/generator-tests resourceidentity -resource-name storage_mover_target_endpoint -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 StorageMoverTargetEndpointModel struct { Name string `tfschema:"name"` StorageMoverId string `tfschema:"storage_mover_id"` @@ -30,7 +33,14 @@ type StorageMoverTargetEndpointModel struct { type StorageMoverTargetEndpointResource struct{} -var _ sdk.ResourceWithUpdate = StorageMoverTargetEndpointResource{} +var ( + _ sdk.ResourceWithIdentity = StorageMoverTargetEndpointResource{} + _ sdk.ResourceWithUpdate = StorageMoverTargetEndpointResource{} +) + +func (r StorageMoverTargetEndpointResource) Identity() resourceids.ResourceId { + return &endpoints.EndpointId{} +} func (r StorageMoverTargetEndpointResource) ResourceType() string { return "azurerm_storage_mover_target_endpoint" @@ -133,6 +143,9 @@ func (r StorageMoverTargetEndpointResource) Create() sdk.ResourceFunc { } metadata.SetID(id) + if err := pluginsdk.SetResourceIdentityData(metadata.ResourceData, &id); err != nil { + return err + } return nil }, } @@ -200,27 +213,35 @@ func (r StorageMoverTargetEndpointResource) Read() sdk.ResourceFunc { return fmt.Errorf("retrieving %s: %+v", *id, err) } - state := StorageMoverTargetEndpointModel{ - Name: id.EndpointName, - StorageMoverId: storagemovers.NewStorageMoverID(id.SubscriptionId, id.ResourceGroupName, id.StorageMoverName).ID(), - } + return r.flatten(metadata, id, resp.Model) + }, + } +} - if model := resp.Model; model != nil { - if v, ok := model.Properties.(endpoints.AzureStorageBlobContainerEndpointProperties); ok { - state.StorageContainerName = v.BlobContainerName - state.StorageAccountId = v.StorageAccountResourceId +func (r StorageMoverTargetEndpointResource) flatten(metadata sdk.ResourceMetaData, id *endpoints.EndpointId, model *endpoints.Endpoint) error { + state := StorageMoverTargetEndpointModel{ + Name: id.EndpointName, + StorageMoverId: storagemovers.NewStorageMoverID(id.SubscriptionId, id.ResourceGroupName, id.StorageMoverName).ID(), + } - des := "" - if v.Description != nil { - des = *v.Description - } - state.Description = des - } + if model != nil { + if v, ok := model.Properties.(endpoints.AzureStorageBlobContainerEndpointProperties); ok { + state.StorageContainerName = v.BlobContainerName + state.StorageAccountId = v.StorageAccountResourceId + + description := "" + if v.Description != nil { + description = *v.Description } + state.Description = description + } + } - return metadata.Encode(&state) - }, + if err := pluginsdk.SetResourceIdentityData(metadata.ResourceData, id); err != nil { + return err } + + return metadata.Encode(&state) } func (r StorageMoverTargetEndpointResource) Delete() sdk.ResourceFunc { diff --git a/internal/services/storagemover/storage_mover_target_endpoint_resource_identity_gen_test.go b/internal/services/storagemover/storage_mover_target_endpoint_resource_identity_gen_test.go new file mode 100644 index 000000000000..0e185bf9ea12 --- /dev/null +++ b/internal/services/storagemover/storage_mover_target_endpoint_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 TestAccStorageMoverTargetEndpoint_resourceIdentity(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_storage_mover_target_endpoint", "test") + r := StorageMoverTargetEndpointResource{} + + 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_target_endpoint.test", checkedFields), + statecheck.ExpectIdentityValueMatchesStateAtPath("azurerm_storage_mover_target_endpoint.test", tfjsonpath.New("name"), tfjsonpath.New("name")), + customstatecheck.ExpectStateContainsIdentityValueAtPath("azurerm_storage_mover_target_endpoint.test", tfjsonpath.New("resource_group_name"), tfjsonpath.New("storage_mover_id")), + customstatecheck.ExpectStateContainsIdentityValueAtPath("azurerm_storage_mover_target_endpoint.test", tfjsonpath.New("storage_mover_name"), tfjsonpath.New("storage_mover_id")), + customstatecheck.ExpectStateContainsIdentityValueAtPath("azurerm_storage_mover_target_endpoint.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_target_endpoint_resource_list.go b/internal/services/storagemover/storage_mover_target_endpoint_resource_list.go new file mode 100644 index 000000000000..d86673879f8d --- /dev/null +++ b/internal/services/storagemover/storage_mover_target_endpoint_resource_list.go @@ -0,0 +1,109 @@ +// 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/endpoints" + "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 StorageMoverTargetEndpointListResource struct{} + +type StorageMoverTargetEndpointListModel struct { + StorageMoverId types.String `tfsdk:"storage_mover_id"` +} + +var _ sdk.FrameworkListWrappedResource = new(StorageMoverTargetEndpointListResource) + +func (StorageMoverTargetEndpointListResource) Metadata(_ context.Context, _ resource.MetadataRequest, response *resource.MetadataResponse) { + response.TypeName = StorageMoverTargetEndpointResource{}.ResourceType() +} + +func (StorageMoverTargetEndpointListResource) ResourceFunc() *pluginsdk.Resource { + return sdk.WrappedResource(StorageMoverTargetEndpointResource{}) +} + +func (StorageMoverTargetEndpointListResource) 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 (StorageMoverTargetEndpointListResource) List(ctx context.Context, request list.ListRequest, stream *list.ListResultsStream, metadata sdk.ResourceMetadata) { + client := metadata.Client.StorageMover.EndpointsClient + + var data StorageMoverTargetEndpointListModel + 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`", StorageMoverTargetEndpointResource{}.ResourceType()), err) + return + } + + resp, err := client.ListComplete(ctx, endpoints.NewStorageMoverID(storageMoverID.SubscriptionId, storageMoverID.ResourceGroupName, storageMoverID.StorageMoverName)) + if err != nil { + sdk.SetResponseErrorDiagnostic(stream, fmt.Sprintf("listing `%s`", StorageMoverTargetEndpointResource{}.ResourceType()), err) + return + } + + resource := StorageMoverTargetEndpointResource{} + stream.Results = func(push func(list.ListResult) bool) { + for _, item := range resp.Items { + if _, ok := item.Properties.(endpoints.AzureStorageBlobContainerEndpointProperties); !ok { + continue + } + + result := request.NewListResult(ctx) + result.DisplayName = pointer.From(item.Name) + + id, err := endpoints.ParseEndpointIDInsensitively(pointer.From(item.Id)) + if err != nil { + sdk.SetErrorDiagnosticAndPushListResult(result, push, "parsing Storage Mover Target Endpoint 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_target_endpoint_resource_list_test.go b/internal/services/storagemover/storage_mover_target_endpoint_resource_list_test.go new file mode 100644 index 000000000000..e3361dc828a7 --- /dev/null +++ b/internal/services/storagemover/storage_mover_target_endpoint_resource_list_test.go @@ -0,0 +1,83 @@ +// 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 TestAccStorageMoverTargetEndpoint_list(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_storage_mover_target_endpoint", "testlist") + r := StorageMoverTargetEndpointResource{} + resourceName := fmt.Sprintf("acctest-smte-%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.listConfig(data)}, + { + Query: true, + Config: r.listQuery(), + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectLength("azurerm_storage_mover_target_endpoint.list", 1), + querycheck.ExpectIdentity("azurerm_storage_mover_target_endpoint.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 StorageMoverTargetEndpointResource) listConfig(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_storage_mover_source_endpoint" "other" { + name = "acctest-smse-%d" + storage_mover_id = azurerm_storage_mover.test.id + host = "192.168.0.1" +} + +resource "azurerm_storage_mover_target_endpoint" "test" { + name = "acctest-smte-%d" + storage_mover_id = azurerm_storage_mover.test.id + storage_account_id = azurerm_storage_account.test.id + storage_container_name = azurerm_storage_container.test.name +} +`, template, data.RandomInteger, data.RandomInteger) +} + +func (r StorageMoverTargetEndpointResource) listQuery() string { + return ` +list "azurerm_storage_mover_target_endpoint" "list" { + provider = azurerm + config { + storage_mover_id = azurerm_storage_mover.test.id + } +} +` +} diff --git a/internal/services/storagemover/storage_mover_target_endpoint_resource_test.go b/internal/services/storagemover/storage_mover_target_endpoint_resource_test.go index 49102c1c2ba0..ee5e4cb37819 100644 --- a/internal/services/storagemover/storage_mover_target_endpoint_resource_test.go +++ b/internal/services/storagemover/storage_mover_target_endpoint_resource_test.go @@ -17,11 +17,11 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" ) -type StorageMoverTargetEndpointTestResource struct{} +type StorageMoverTargetEndpointResource struct{} func TestAccStorageMoverTargetEndpoint_basic(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_mover_target_endpoint", "test") - r := StorageMoverTargetEndpointTestResource{} + r := StorageMoverTargetEndpointResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.basic(data), @@ -35,7 +35,7 @@ func TestAccStorageMoverTargetEndpoint_basic(t *testing.T) { func TestAccStorageMoverTargetEndpoint_requiresImport(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_mover_target_endpoint", "test") - r := StorageMoverTargetEndpointTestResource{} + r := StorageMoverTargetEndpointResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.basic(data), @@ -49,7 +49,7 @@ func TestAccStorageMoverTargetEndpoint_requiresImport(t *testing.T) { func TestAccStorageMoverTargetEndpoint_complete(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_mover_target_endpoint", "test") - r := StorageMoverTargetEndpointTestResource{} + r := StorageMoverTargetEndpointResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.complete(data), @@ -63,7 +63,7 @@ func TestAccStorageMoverTargetEndpoint_complete(t *testing.T) { func TestAccStorageMoverTargetEndpoint_update(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_mover_target_endpoint", "test") - r := StorageMoverTargetEndpointTestResource{} + r := StorageMoverTargetEndpointResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.complete(data), @@ -82,7 +82,7 @@ func TestAccStorageMoverTargetEndpoint_update(t *testing.T) { }) } -func (r StorageMoverTargetEndpointTestResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { +func (r StorageMoverTargetEndpointResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := endpoints.ParseEndpointID(state.ID) if err != nil { return nil, err @@ -99,7 +99,7 @@ func (r StorageMoverTargetEndpointTestResource) Exists(ctx context.Context, clie return pointer.To(resp.Model != nil), nil } -func (r StorageMoverTargetEndpointTestResource) template(data acceptance.TestData) string { +func (r StorageMoverTargetEndpointResource) template(data acceptance.TestData) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { name = "acctest-rg-%d" @@ -129,7 +129,7 @@ resource "azurerm_storage_mover" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomString, data.RandomString, data.RandomInteger) } -func (r StorageMoverTargetEndpointTestResource) basic(data acceptance.TestData) string { +func (r StorageMoverTargetEndpointResource) basic(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` provider "azurerm" { @@ -147,7 +147,7 @@ resource "azurerm_storage_mover_target_endpoint" "test" { `, template, data.RandomInteger) } -func (r StorageMoverTargetEndpointTestResource) requiresImport(data acceptance.TestData) string { +func (r StorageMoverTargetEndpointResource) requiresImport(data acceptance.TestData) string { config := r.basic(data) return fmt.Sprintf(` %s @@ -161,7 +161,7 @@ resource "azurerm_storage_mover_target_endpoint" "import" { `, config) } -func (r StorageMoverTargetEndpointTestResource) complete(data acceptance.TestData) string { +func (r StorageMoverTargetEndpointResource) complete(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` provider "azurerm" { @@ -180,7 +180,7 @@ resource "azurerm_storage_mover_target_endpoint" "test" { `, template, data.RandomInteger) } -func (r StorageMoverTargetEndpointTestResource) update(data acceptance.TestData) string { +func (r StorageMoverTargetEndpointResource) update(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` provider "azurerm" { diff --git a/website/docs/list-resources/storage_mover.html.markdown b/website/docs/list-resources/storage_mover.html.markdown new file mode 100644 index 000000000000..22289a07b6ed --- /dev/null +++ b/website/docs/list-resources/storage_mover.html.markdown @@ -0,0 +1,41 @@ +--- +subcategory: "Storage Mover" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_storage_mover" +description: |- + Lists Storage Mover resources. +--- + +# List resource: azurerm_storage_mover + +Lists Storage Mover resources. + +## Example Usage + +### List all Storage Movers in the subscription + +```hcl +list "azurerm_storage_mover" "example" { + provider = azurerm + config {} +} +``` + +### List all Storage Movers in a specific resource group + +```hcl +list "azurerm_storage_mover" "example" { + provider = azurerm + config { + resource_group_name = "example-rg" + } +} +``` + +## Argument Reference + +This list resource supports the following arguments: + +* `resource_group_name` - (Optional) The name of the resource group to query. + +* `subscription_id` - (Optional) The Subscription ID to query. Defaults to the value specified in the Provider Configuration. \ No newline at end of file diff --git a/website/docs/list-resources/storage_mover_agent.html.markdown b/website/docs/list-resources/storage_mover_agent.html.markdown new file mode 100644 index 000000000000..57006baa4b82 --- /dev/null +++ b/website/docs/list-resources/storage_mover_agent.html.markdown @@ -0,0 +1,30 @@ +--- +subcategory: "Storage Mover" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_storage_mover_agent" +description: |- + Lists Storage Mover Agent resources. +--- + +# List resource: azurerm_storage_mover_agent + +Lists Storage Mover Agent resources. + +## Example Usage + +### List Agents in a Storage Mover + +```hcl +list "azurerm_storage_mover_agent" "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 diff --git a/website/docs/list-resources/storage_mover_job_definition.html.markdown b/website/docs/list-resources/storage_mover_job_definition.html.markdown new file mode 100644 index 000000000000..8a2a8f8e1a6e --- /dev/null +++ b/website/docs/list-resources/storage_mover_job_definition.html.markdown @@ -0,0 +1,30 @@ +--- +subcategory: "Storage Mover" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_storage_mover_job_definition" +description: |- + Lists Storage Mover Job Definition resources. +--- + +# List resource: azurerm_storage_mover_job_definition + +Lists Storage Mover Job Definition resources. + +## Example Usage + +### List Job Definitions in a Storage Mover Project + +```hcl +list "azurerm_storage_mover_job_definition" "example" { + provider = azurerm + config { + storage_mover_project_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example-rg/providers/Microsoft.StorageMover/storageMovers/example-mover/projects/example-project" + } +} +``` + +## Argument Reference + +This list resource supports the following arguments: + +* `storage_mover_project_id` - (Required) The ID of the Storage Mover Project to query. \ No newline at end of file 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 diff --git a/website/docs/list-resources/storage_mover_source_endpoint.html.markdown b/website/docs/list-resources/storage_mover_source_endpoint.html.markdown new file mode 100644 index 000000000000..04bec136fb3d --- /dev/null +++ b/website/docs/list-resources/storage_mover_source_endpoint.html.markdown @@ -0,0 +1,30 @@ +--- +subcategory: "Storage Mover" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_storage_mover_source_endpoint" +description: |- + Lists Storage Mover Source Endpoint resources. +--- + +# List resource: azurerm_storage_mover_source_endpoint + +Lists Storage Mover Source Endpoint resources. + +## Example Usage + +### List Source Endpoints in a Storage Mover + +```hcl +list "azurerm_storage_mover_source_endpoint" "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 diff --git a/website/docs/list-resources/storage_mover_target_endpoint.html.markdown b/website/docs/list-resources/storage_mover_target_endpoint.html.markdown new file mode 100644 index 000000000000..11a74bb7d250 --- /dev/null +++ b/website/docs/list-resources/storage_mover_target_endpoint.html.markdown @@ -0,0 +1,30 @@ +--- +subcategory: "Storage Mover" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_storage_mover_target_endpoint" +description: |- + Lists Storage Mover Target Endpoint resources. +--- + +# List resource: azurerm_storage_mover_target_endpoint + +Lists Storage Mover Target Endpoint resources. + +## Example Usage + +### List Target Endpoints in a Storage Mover + +```hcl +list "azurerm_storage_mover_target_endpoint" "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