diff --git a/internal/services/storagemover/registration.go b/internal/services/storagemover/registration.go index b8d842179e33..0cf962f7fb96 100644 --- a/internal/services/storagemover/registration.go +++ b/internal/services/storagemover/registration.go @@ -66,5 +66,7 @@ func (r Registration) EphemeralResources() []func() ephemeral.EphemeralResource } func (r Registration) ListResources() []sdk.FrameworkListWrappedResource { - return []sdk.FrameworkListWrappedResource{} + return []sdk.FrameworkListWrappedResource{ + StorageMoverJobDefinitionListResource{}, + } } 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..0d0876ff5df6 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(` @@ -150,7 +150,7 @@ resource "azurerm_storage_mover_project" "test" { `, StorageMoverAgentTestResource{}.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/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