Skip to content

Commit ed0d90f

Browse files
New List Resource: azurerm_storage_sync (hashicorp#31995)
[FEATURE] * **New List Resource**: `azurerm_storage_sync`
1 parent e0979bc commit ed0d90f

6 files changed

Lines changed: 309 additions & 22 deletions

File tree

internal/services/storage/registration.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,5 +115,6 @@ func (r Registration) ListResources() []sdk.FrameworkListWrappedResource {
115115
return []sdk.FrameworkListWrappedResource{
116116
StorageAccountCustomerManagedKeyListResource{},
117117
StorageAccountListResource{},
118+
StorageSyncListResource{},
118119
}
119120
}

internal/services/storage/storage_sync_resource.go

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package storage
55

66
import (
7+
"context"
78
"fmt"
89
"log"
910
"time"
@@ -15,6 +16,7 @@ import (
1516
"github.com/hashicorp/go-azure-helpers/resourcemanager/tags"
1617
"github.com/hashicorp/go-azure-sdk/resource-manager/storagesync/2020-03-01/registeredserverresource"
1718
"github.com/hashicorp/go-azure-sdk/resource-manager/storagesync/2020-03-01/storagesyncservicesresource"
19+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1820
"github.com/hashicorp/terraform-provider-azurerm/helpers/tf"
1921
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
2022
"github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/validate"
@@ -23,17 +25,22 @@ import (
2325
"github.com/hashicorp/terraform-provider-azurerm/internal/timeouts"
2426
)
2527

28+
const storageSyncResourceName = "azurerm_storage_sync"
29+
30+
//go:generate go run ../../tools/generator-tests resourceidentity -resource-name storage_sync -service-package-name storage -properties "name,resource_group_name" -known-values "subscription_id:data.Subscriptions.Primary"
31+
2632
func resourceStorageSync() *pluginsdk.Resource {
2733
return &pluginsdk.Resource{
2834
Create: resourceStorageSyncCreate,
2935
Read: resourceStorageSyncRead,
3036
Update: resourceStorageSyncUpdate,
3137
Delete: resourceStorageSyncDelete,
3238

33-
Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error {
34-
_, err := storagesyncservicesresource.ParseStorageSyncServiceID(id)
35-
return err
36-
}),
39+
Importer: pluginsdk.ImporterValidatingIdentity(&storagesyncservicesresource.StorageSyncServiceId{}),
40+
41+
Identity: &schema.ResourceIdentity{
42+
SchemaFunc: pluginsdk.GenerateIdentitySchema(&storagesyncservicesresource.StorageSyncServiceId{}),
43+
},
3744

3845
Timeouts: &pluginsdk.ResourceTimeout{
3946
Create: pluginsdk.DefaultTimeout(30 * time.Minute),
@@ -91,7 +98,7 @@ func resourceStorageSyncCreate(d *pluginsdk.ResourceData, meta interface{}) erro
9198
}
9299
}
93100
if !response.WasNotFound(existing.HttpResponse) {
94-
return tf.ImportAsExistsError("azurerm_storage_sync", id.ID())
101+
return tf.ImportAsExistsError(storageSyncResourceName, id.ID())
95102
}
96103

97104
parameters := storagesyncservicesresource.StorageSyncServiceCreateParameters{
@@ -107,6 +114,10 @@ func resourceStorageSyncCreate(d *pluginsdk.ResourceData, meta interface{}) erro
107114
}
108115

109116
d.SetId(id.ID())
117+
if err = pluginsdk.SetResourceIdentityData(d, &id); err != nil {
118+
return err
119+
}
120+
110121
return resourceStorageSyncRead(d, meta)
111122
}
112123

@@ -130,41 +141,49 @@ func resourceStorageSyncRead(d *pluginsdk.ResourceData, meta interface{}) error
130141
}
131142
return fmt.Errorf("retrieving %s: %+v", *id, err)
132143
}
144+
145+
return resourceStorageSyncFlatten(ctx, d, id, resp.Model, registeredServerClient, true)
146+
}
147+
148+
func resourceStorageSyncFlatten(ctx context.Context, d *pluginsdk.ResourceData, id *storagesyncservicesresource.StorageSyncServiceId, model *storagesyncservicesresource.StorageSyncService, registeredServerClient *registeredserverresource.RegisteredServerResourceClient, includeRegisteredServers bool) error {
133149
d.Set("name", id.StorageSyncServiceName)
134150
d.Set("resource_group_name", id.ResourceGroupName)
135-
if model := resp.Model; model != nil {
151+
152+
if model != nil {
136153
d.Set("location", location.Normalize(model.Location))
137154

138155
if props := model.Properties; props != nil {
139156
d.Set("incoming_traffic_policy", string(pointer.From(props.IncomingTrafficPolicy)))
140157
}
141158

142-
if err = tags.FlattenAndSet(d, model.Tags); err != nil {
159+
if err := tags.FlattenAndSet(d, model.Tags); err != nil {
143160
return fmt.Errorf("setting `tags`: %+v", err)
144161
}
145162
}
146163

147-
storageSyncId := registeredserverresource.NewStorageSyncServiceID(id.SubscriptionId, id.ResourceGroupName, id.StorageSyncServiceName)
148-
registeredServersResp, err := registeredServerClient.RegisteredServersListByStorageSyncService(ctx, storageSyncId)
149-
if err != nil {
150-
if !response.WasNotFound(registeredServersResp.HttpResponse) {
151-
return fmt.Errorf("retrieving registered servers for %s: %+v", id, err)
164+
if includeRegisteredServers {
165+
storageSyncId := registeredserverresource.NewStorageSyncServiceID(id.SubscriptionId, id.ResourceGroupName, id.StorageSyncServiceName)
166+
registeredServersResp, err := registeredServerClient.RegisteredServersListByStorageSyncService(ctx, storageSyncId)
167+
if err != nil {
168+
if !response.WasNotFound(registeredServersResp.HttpResponse) {
169+
return fmt.Errorf("retrieving registered servers for %s: %+v", id, err)
170+
}
152171
}
153-
}
154172

155-
if model := registeredServersResp.Model; model != nil && model.Value != nil {
156-
registeredServers := make([]interface{}, 0)
157-
for _, registeredServer := range *model.Value {
158-
if registeredServer.Id != nil {
159-
registeredServers = append(registeredServers, *registeredServer.Id)
173+
if serverModel := registeredServersResp.Model; serverModel != nil && serverModel.Value != nil {
174+
registeredServers := make([]interface{}, 0, len(*serverModel.Value))
175+
for _, registeredServer := range *serverModel.Value {
176+
if registeredServer.Id != nil {
177+
registeredServers = append(registeredServers, *registeredServer.Id)
178+
}
179+
}
180+
if err := d.Set("registered_servers", registeredServers); err != nil {
181+
return fmt.Errorf("setting `registered_servers`: %+v", err)
160182
}
161-
}
162-
if err = d.Set("registered_servers", registeredServers); err != nil {
163-
return fmt.Errorf("setting `registered_servers`: %+v", err)
164183
}
165184
}
166185

167-
return nil
186+
return pluginsdk.SetResourceIdentityData(d, id)
168187
}
169188

170189
func resourceStorageSyncUpdate(d *pluginsdk.ResourceData, meta interface{}) error {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright IBM Corp. 2014, 2025
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package storage_test
5+
6+
import (
7+
"testing"
8+
9+
"github.com/hashicorp/terraform-plugin-testing/knownvalue"
10+
"github.com/hashicorp/terraform-plugin-testing/statecheck"
11+
"github.com/hashicorp/terraform-plugin-testing/tfjsonpath"
12+
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance"
13+
customstatecheck "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/statecheck"
14+
)
15+
16+
func TestAccStorageSync_resourceIdentity(t *testing.T) {
17+
data := acceptance.BuildTestData(t, "azurerm_storage_sync", "test")
18+
r := StorageSyncResource{}
19+
20+
checkedFields := map[string]struct{}{
21+
"subscription_id": {},
22+
"name": {},
23+
"resource_group_name": {},
24+
}
25+
26+
data.ResourceIdentityTest(t, []acceptance.TestStep{
27+
{
28+
Config: r.basic(data),
29+
ConfigStateChecks: []statecheck.StateCheck{
30+
customstatecheck.ExpectAllIdentityFieldsAreChecked("azurerm_storage_sync.test", checkedFields),
31+
statecheck.ExpectIdentityValue("azurerm_storage_sync.test", tfjsonpath.New("subscription_id"), knownvalue.StringExact(data.Subscriptions.Primary)),
32+
statecheck.ExpectIdentityValueMatchesStateAtPath("azurerm_storage_sync.test", tfjsonpath.New("name"), tfjsonpath.New("name")),
33+
statecheck.ExpectIdentityValueMatchesStateAtPath("azurerm_storage_sync.test", tfjsonpath.New("resource_group_name"), tfjsonpath.New("resource_group_name")),
34+
},
35+
},
36+
data.ImportBlockWithResourceIdentityStep(false),
37+
data.ImportBlockWithIDStep(false),
38+
}, false)
39+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright IBM Corp. 2014, 2025
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package storage
5+
6+
import (
7+
"context"
8+
"fmt"
9+
10+
"github.com/hashicorp/go-azure-helpers/lang/pointer"
11+
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonids"
12+
"github.com/hashicorp/go-azure-sdk/resource-manager/storagesync/2020-03-01/storagesyncservicesresource"
13+
"github.com/hashicorp/terraform-plugin-framework/list"
14+
"github.com/hashicorp/terraform-plugin-framework/resource"
15+
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
16+
"github.com/hashicorp/terraform-provider-azurerm/internal/sdk"
17+
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
18+
)
19+
20+
type StorageSyncListResource struct{}
21+
22+
var _ sdk.FrameworkListWrappedResource = new(StorageSyncListResource)
23+
24+
func (r StorageSyncListResource) ResourceFunc() *pluginsdk.Resource {
25+
return resourceStorageSync()
26+
}
27+
28+
func (r StorageSyncListResource) Metadata(_ context.Context, _ resource.MetadataRequest, response *resource.MetadataResponse) {
29+
response.TypeName = storageSyncResourceName
30+
}
31+
32+
func (r StorageSyncListResource) List(ctx context.Context, request list.ListRequest, stream *list.ListResultsStream, metadata sdk.ResourceMetadata) {
33+
client := metadata.Client.Storage.SyncServiceClient
34+
35+
var data sdk.DefaultListModel
36+
if diags := request.Config.Get(ctx, &data); diags.HasError() {
37+
stream.Results = list.ListResultsStreamDiagnostics(diags)
38+
return
39+
}
40+
41+
subscriptionID := metadata.SubscriptionId
42+
if !data.SubscriptionId.IsNull() {
43+
subscriptionID = data.SubscriptionId.ValueString()
44+
}
45+
46+
results := make([]storagesyncservicesresource.StorageSyncService, 0)
47+
48+
switch {
49+
case !data.ResourceGroupName.IsNull():
50+
resp, err := client.StorageSyncServicesListByResourceGroup(ctx, commonids.NewResourceGroupID(subscriptionID, data.ResourceGroupName.ValueString()))
51+
if err != nil {
52+
sdk.SetResponseErrorDiagnostic(stream, fmt.Sprintf("listing `%s`", storageSyncResourceName), err)
53+
return
54+
}
55+
56+
if resp.Model != nil && resp.Model.Value != nil {
57+
results = *resp.Model.Value
58+
}
59+
default:
60+
resp, err := client.StorageSyncServicesListBySubscription(ctx, commonids.NewSubscriptionID(subscriptionID))
61+
if err != nil {
62+
sdk.SetResponseErrorDiagnostic(stream, fmt.Sprintf("listing `%s`", storageSyncResourceName), err)
63+
return
64+
}
65+
66+
if resp.Model != nil && resp.Model.Value != nil {
67+
results = *resp.Model.Value
68+
}
69+
}
70+
71+
stream.Results = func(push func(list.ListResult) bool) {
72+
for _, item := range results {
73+
result := request.NewListResult(ctx)
74+
result.DisplayName = pointer.From(item.Name)
75+
76+
id, err := storagesyncservicesresource.ParseStorageSyncServiceIDInsensitively(pointer.From(item.Id))
77+
if err != nil {
78+
sdk.SetErrorDiagnosticAndPushListResult(result, push, "parsing Storage Sync Service ID", err)
79+
return
80+
}
81+
82+
rd := resourceStorageSync().Data(&terraform.InstanceState{})
83+
rd.SetId(id.ID())
84+
85+
if err := resourceStorageSyncFlatten(ctx, rd, id, &item, metadata.Client.Storage.SyncRegisteredServerClient, request.IncludeResource); err != nil {
86+
sdk.SetErrorDiagnosticAndPushListResult(result, push, fmt.Sprintf("encoding `%s` resource data", storageSyncResourceName), err)
87+
return
88+
}
89+
90+
sdk.EncodeListResult(ctx, rd, &result)
91+
if result.Diagnostics.HasError() {
92+
push(result)
93+
return
94+
}
95+
96+
if !push(result) {
97+
return
98+
}
99+
}
100+
}
101+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright IBM Corp. 2014, 2025
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package storage_test
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"regexp"
10+
"strconv"
11+
"testing"
12+
13+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
14+
"github.com/hashicorp/terraform-plugin-testing/knownvalue"
15+
"github.com/hashicorp/terraform-plugin-testing/querycheck"
16+
"github.com/hashicorp/terraform-plugin-testing/tfversion"
17+
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance"
18+
"github.com/hashicorp/terraform-provider-azurerm/internal/provider/framework"
19+
)
20+
21+
func TestAccStorageSync_list(t *testing.T) {
22+
data := acceptance.BuildTestData(t, "azurerm_storage_sync", "test")
23+
r := StorageSyncResource{}
24+
25+
resource.Test(t, resource.TestCase{
26+
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
27+
tfversion.SkipBelow(tfversion.Version1_14_0),
28+
},
29+
ProtoV5ProviderFactories: framework.ProtoV5ProviderFactoriesInit(context.Background(), "azurerm"),
30+
Steps: []resource.TestStep{
31+
{
32+
Config: r.basic(data),
33+
},
34+
{
35+
Query: true,
36+
Config: r.basicListQuery(),
37+
QueryResultChecks: []querycheck.QueryResultCheck{
38+
querycheck.ExpectLengthAtLeast("azurerm_storage_sync.list", 1),
39+
querycheck.ExpectIdentity(
40+
"azurerm_storage_sync.list",
41+
map[string]knownvalue.Check{
42+
"name": knownvalue.StringRegexp(regexp.MustCompile(strconv.Itoa(data.RandomInteger))),
43+
"resource_group_name": knownvalue.StringRegexp(regexp.MustCompile(strconv.Itoa(data.RandomInteger))),
44+
"subscription_id": knownvalue.StringExact(data.Subscriptions.Primary),
45+
},
46+
),
47+
},
48+
},
49+
{
50+
Query: true,
51+
Config: r.basicListQueryByResourceGroupName(data),
52+
QueryResultChecks: []querycheck.QueryResultCheck{
53+
querycheck.ExpectLength("azurerm_storage_sync.list", 1),
54+
querycheck.ExpectIdentity(
55+
"azurerm_storage_sync.list",
56+
map[string]knownvalue.Check{
57+
"name": knownvalue.StringRegexp(regexp.MustCompile(strconv.Itoa(data.RandomInteger))),
58+
"resource_group_name": knownvalue.StringRegexp(regexp.MustCompile(strconv.Itoa(data.RandomInteger))),
59+
"subscription_id": knownvalue.StringExact(data.Subscriptions.Primary),
60+
},
61+
),
62+
},
63+
},
64+
},
65+
})
66+
}
67+
68+
func (StorageSyncResource) basicListQuery() string {
69+
return `
70+
list "azurerm_storage_sync" "list" {
71+
provider = azurerm
72+
config {}
73+
}
74+
`
75+
}
76+
77+
func (StorageSyncResource) basicListQueryByResourceGroupName(data acceptance.TestData) string {
78+
return fmt.Sprintf(`
79+
list "azurerm_storage_sync" "list" {
80+
provider = azurerm
81+
config {
82+
resource_group_name = "acctestRG-ss-%d"
83+
}
84+
}
85+
`, data.RandomInteger)
86+
}

0 commit comments

Comments
 (0)