Skip to content

Commit 8e5b11b

Browse files
[New Resource] azurerm_resource_provider_feature_registration (#28303)
Co-authored-by: sreallymatt <106555974+sreallymatt@users.noreply.github.com>
1 parent 8bb30b3 commit 8e5b11b

8 files changed

+535
-4
lines changed

.github/labeler-issue-triage.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ service/relay:
291291
- '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_relay_((.|\n)*)###'
292292

293293
service/resource:
294-
- '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_(management_group_template_deployment\W+|management_lock\W+|resource_deployment_script_azure_cli\W+|resource_deployment_script_azure_power_shell\W+|resource_group\W+|resource_group_template_deployment\W+|resource_management_private_link\W+|resource_management_private_link_association\W+|resource_provider_registration\W+|resources|subscription_template_deployment|template_spec_version|tenant_template_deployment)((.|\n)*)###'
294+
- '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_(management_group_template_deployment\W+|management_lock\W+|resource_deployment_script_azure_cli\W+|resource_deployment_script_azure_power_shell\W+|resource_group\W+|resource_group_template_deployment\W+|resource_management_private_link\W+|resource_management_private_link_association\W+|resource_provider_feature_registration\W+|resource_provider_registration\W+|resources|subscription_template_deployment|template_spec_version|tenant_template_deployment)((.|\n)*)###'
295295

296296
service/search:
297297
- '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_search_s((.|\n)*)###'
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package custompollers
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"strings"
7+
"time"
8+
9+
"github.com/hashicorp/go-azure-sdk/resource-manager/resources/2021-07-01/features"
10+
"github.com/hashicorp/go-azure-sdk/sdk/client/pollers"
11+
)
12+
13+
var _ pollers.PollerType = &ResourceProviderFeatureRegistrationPoller{}
14+
15+
func NewResourceProviderFeatureRegistrationPoller(client *features.FeaturesClient, id features.FeatureId, target string) *ResourceProviderFeatureRegistrationPoller {
16+
return &ResourceProviderFeatureRegistrationPoller{
17+
client: client,
18+
id: id,
19+
targetState: target,
20+
}
21+
}
22+
23+
type ResourceProviderFeatureRegistrationPoller struct {
24+
client *features.FeaturesClient
25+
id features.FeatureId
26+
targetState string
27+
}
28+
29+
func (p *ResourceProviderFeatureRegistrationPoller) Poll(ctx context.Context) (*pollers.PollResult, error) {
30+
resp, err := p.client.Get(ctx, p.id)
31+
if err != nil {
32+
return nil, fmt.Errorf("retrieving %s: %+v", p.id, err)
33+
}
34+
35+
if resp.Model == nil || resp.Model.Properties == nil || resp.Model.Properties.State == nil {
36+
return nil, fmt.Errorf("retrieving %s: unable to determine registration state", p.id)
37+
}
38+
39+
if strings.EqualFold(*resp.Model.Properties.State, p.targetState) {
40+
return &pollers.PollResult{
41+
Status: pollers.PollingStatusSucceeded,
42+
PollInterval: 10 * time.Second,
43+
}, nil
44+
}
45+
46+
return &pollers.PollResult{
47+
Status: pollers.PollingStatusInProgress,
48+
PollInterval: 10 * time.Second,
49+
}, nil
50+
}

internal/services/resource/registration.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,12 @@ func (r Registration) DataSources() []sdk.DataSource {
7171
// Resources returns a list of Resources supported by this Service
7272
func (r Registration) Resources() []sdk.Resource {
7373
return []sdk.Resource{
74+
ResourceDeploymentScriptAzureCliResource{},
75+
ResourceDeploymentScriptAzurePowerShellResource{},
7476
ResourceManagementPrivateLinkAssociationResource{},
75-
ResourceProviderRegistrationResource{},
7677
ResourceManagementPrivateLinkResource{},
77-
ResourceDeploymentScriptAzurePowerShellResource{},
78-
ResourceDeploymentScriptAzureCliResource{},
78+
ResourceProviderFeatureRegistrationResource{},
79+
ResourceProviderRegistrationResource{},
7980
}
8081
}
8182

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package resource
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"strings"
10+
"time"
11+
12+
"github.com/hashicorp/go-azure-helpers/lang/response"
13+
"github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids"
14+
"github.com/hashicorp/go-azure-sdk/resource-manager/resources/2021-07-01/features"
15+
"github.com/hashicorp/go-azure-sdk/sdk/client/pollers"
16+
"github.com/hashicorp/terraform-provider-azurerm/internal/resourceproviders"
17+
"github.com/hashicorp/terraform-provider-azurerm/internal/sdk"
18+
"github.com/hashicorp/terraform-provider-azurerm/internal/services/resource/custompollers"
19+
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
20+
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation"
21+
)
22+
23+
//go:generate go run ../../tools/generator-tests resourceidentity -resource-name resource_provider_feature_registration -properties "name,provider_name" -known-values "subscription_id:data.Subscriptions.Secondary" -test-name "basicForResourceIdentity"
24+
25+
var _ sdk.ResourceWithIdentity = ResourceProviderFeatureRegistrationResource{}
26+
27+
type ResourceProviderFeatureRegistrationResource struct{}
28+
29+
func (r ResourceProviderFeatureRegistrationResource) Identity() resourceids.ResourceId {
30+
return new(features.FeatureId)
31+
}
32+
33+
type ResourceProviderFeatureRegistrationModel struct {
34+
Name string `tfschema:"name"`
35+
ProviderName string `tfschema:"provider_name"`
36+
}
37+
38+
func (r ResourceProviderFeatureRegistrationResource) Arguments() map[string]*pluginsdk.Schema {
39+
return map[string]*pluginsdk.Schema{
40+
"name": {
41+
Type: pluginsdk.TypeString,
42+
Required: true,
43+
ForceNew: true,
44+
ValidateFunc: validation.StringIsNotEmpty,
45+
},
46+
47+
"provider_name": {
48+
Type: pluginsdk.TypeString,
49+
Required: true,
50+
ForceNew: true,
51+
ValidateFunc: resourceproviders.EnhancedValidate,
52+
},
53+
}
54+
}
55+
56+
func (r ResourceProviderFeatureRegistrationResource) Attributes() map[string]*pluginsdk.Schema {
57+
return map[string]*pluginsdk.Schema{}
58+
}
59+
60+
func (r ResourceProviderFeatureRegistrationResource) ModelObject() interface{} {
61+
return &ResourceProviderFeatureRegistrationModel{}
62+
}
63+
64+
func (r ResourceProviderFeatureRegistrationResource) ResourceType() string {
65+
return "azurerm_resource_provider_feature_registration"
66+
}
67+
68+
func (r ResourceProviderFeatureRegistrationResource) Create() sdk.ResourceFunc {
69+
return sdk.ResourceFunc{
70+
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
71+
client := metadata.Client.Resource.FeaturesClient
72+
73+
var obj ResourceProviderFeatureRegistrationModel
74+
if err := metadata.Decode(&obj); err != nil {
75+
return err
76+
}
77+
78+
featureId := features.NewFeatureID(metadata.Client.Account.SubscriptionId, obj.ProviderName, obj.Name)
79+
80+
provider, err := client.Get(ctx, featureId)
81+
if err != nil {
82+
if response.WasNotFound(provider.HttpResponse) {
83+
return fmt.Errorf("%s was not found", featureId)
84+
}
85+
86+
return fmt.Errorf("retrieving %s: %+v", featureId, err)
87+
}
88+
89+
registrationState := ""
90+
if model := provider.Model; model != nil && model.Properties != nil && model.Properties.State != nil {
91+
registrationState = *model.Properties.State
92+
}
93+
94+
if registrationState == "" {
95+
return fmt.Errorf("retrieving %s: `registrationState` was nil", featureId)
96+
}
97+
98+
if strings.EqualFold(registrationState, Registered) {
99+
return metadata.ResourceRequiresImport(r.ResourceType(), featureId)
100+
}
101+
102+
if strings.EqualFold(registrationState, Pending) {
103+
return fmt.Errorf("%s which requires manual approval can not be managed by Terraform", featureId)
104+
}
105+
106+
resp, err := client.Register(ctx, featureId)
107+
if err != nil {
108+
return fmt.Errorf("registering %s: %+v", featureId, err)
109+
}
110+
111+
if resp.Model != nil && resp.Model.Properties != nil && resp.Model.Properties.State != nil {
112+
if strings.EqualFold(*resp.Model.Properties.State, Pending) {
113+
return fmt.Errorf("%s which requires manual approval can not be managed by terraform", featureId)
114+
}
115+
}
116+
117+
pollerType := custompollers.NewResourceProviderFeatureRegistrationPoller(client, featureId, Registered)
118+
poller := pollers.NewPoller(pollerType, 10*time.Second, pollers.DefaultNumberOfDroppedConnectionsToAllow)
119+
if err := poller.PollUntilDone(ctx); err != nil {
120+
return fmt.Errorf("waiting for registration of %s: %+v", featureId, err)
121+
}
122+
123+
metadata.SetID(featureId)
124+
return pluginsdk.SetResourceIdentityData(metadata.ResourceData, &featureId)
125+
},
126+
127+
Timeout: 30 * time.Minute,
128+
}
129+
}
130+
131+
func (r ResourceProviderFeatureRegistrationResource) Read() sdk.ResourceFunc {
132+
return sdk.ResourceFunc{
133+
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
134+
client := metadata.Client.Resource.FeaturesClient
135+
136+
featureId, err := features.ParseFeatureID(metadata.ResourceData.Id())
137+
if err != nil {
138+
return err
139+
}
140+
141+
resp, err := client.Get(ctx, *featureId)
142+
if err != nil {
143+
if response.WasNotFound(resp.HttpResponse) {
144+
return metadata.MarkAsGone(featureId)
145+
}
146+
147+
return fmt.Errorf("retrieving %s: %+v", featureId, err)
148+
}
149+
150+
registrationState := ""
151+
if model := resp.Model; model != nil && model.Properties != nil && model.Properties.State != nil {
152+
registrationState = *model.Properties.State
153+
}
154+
155+
if !strings.EqualFold(registrationState, Registered) {
156+
return metadata.MarkAsGone(featureId)
157+
}
158+
159+
if err := pluginsdk.SetResourceIdentityData(metadata.ResourceData, featureId); err != nil {
160+
return err
161+
}
162+
163+
return metadata.Encode(&ResourceProviderFeatureRegistrationModel{
164+
Name: featureId.FeatureName,
165+
ProviderName: featureId.ProviderName,
166+
})
167+
},
168+
Timeout: 5 * time.Minute,
169+
}
170+
}
171+
172+
func (r ResourceProviderFeatureRegistrationResource) Delete() sdk.ResourceFunc {
173+
return sdk.ResourceFunc{
174+
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
175+
client := metadata.Client.Resource.FeaturesClient
176+
177+
featureId, err := features.ParseFeatureID(metadata.ResourceData.Id())
178+
if err != nil {
179+
return err
180+
}
181+
182+
if _, err := client.Unregister(ctx, *featureId); err != nil {
183+
return fmt.Errorf("unregistering %s: %+v", featureId, err)
184+
}
185+
186+
pollerType := custompollers.NewResourceProviderFeatureRegistrationPoller(client, *featureId, Unregistered)
187+
poller := pollers.NewPoller(pollerType, 10*time.Second, pollers.DefaultNumberOfDroppedConnectionsToAllow)
188+
if err := poller.PollUntilDone(ctx); err != nil {
189+
return fmt.Errorf("waiting for registration of %s: %+v", featureId, err)
190+
}
191+
192+
return nil
193+
},
194+
Timeout: 30 * time.Minute,
195+
}
196+
}
197+
198+
func (r ResourceProviderFeatureRegistrationResource) IDValidationFunc() pluginsdk.SchemaValidateFunc {
199+
return features.ValidateFeatureID
200+
}
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 resource_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 TestAccResourceProviderFeatureRegistration_resourceIdentity(t *testing.T) {
17+
data := acceptance.BuildTestData(t, "azurerm_resource_provider_feature_registration", "test")
18+
r := ResourceProviderFeatureRegistrationResource{}
19+
20+
checkedFields := map[string]struct{}{
21+
"subscription_id": {},
22+
"name": {},
23+
"provider_name": {},
24+
}
25+
26+
data.ResourceIdentityTest(t, []acceptance.TestStep{
27+
{
28+
Config: r.basicForResourceIdentity(data),
29+
ConfigStateChecks: []statecheck.StateCheck{
30+
customstatecheck.ExpectAllIdentityFieldsAreChecked("azurerm_resource_provider_feature_registration.test", checkedFields),
31+
statecheck.ExpectIdentityValue("azurerm_resource_provider_feature_registration.test", tfjsonpath.New("subscription_id"), knownvalue.StringExact(data.Subscriptions.Secondary)),
32+
statecheck.ExpectIdentityValueMatchesStateAtPath("azurerm_resource_provider_feature_registration.test", tfjsonpath.New("name"), tfjsonpath.New("name")),
33+
statecheck.ExpectIdentityValueMatchesStateAtPath("azurerm_resource_provider_feature_registration.test", tfjsonpath.New("provider_name"), tfjsonpath.New("provider_name")),
34+
},
35+
},
36+
data.ImportBlockWithResourceIdentityStep(false),
37+
data.ImportBlockWithIDStep(false),
38+
}, false)
39+
}

0 commit comments

Comments
 (0)