Skip to content

Commit

Permalink
azurerm_monitor_smart_detector_alert_rule - a state migration to wo…
Browse files Browse the repository at this point in the history
…rk around the previously incorrect id casing (#19513)
  • Loading branch information
mbfrahry authored Dec 2, 2022
1 parent a473030 commit 9a5502b
Show file tree
Hide file tree
Showing 8 changed files with 308 additions and 14 deletions.
23 changes: 18 additions & 5 deletions internal/services/monitor/migration/autoscale_setting_v1_to_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import (
"context"
"log"

"github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/monitor/parse"
"github.com/hashicorp/terraform-provider-azurerm/internal/tags"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
)

Expand All @@ -19,9 +18,17 @@ func (s AutoscaleSettingUpgradeV1ToV2) Schema() map[string]*pluginsdk.Schema {
Required: true,
},

"resource_group_name": commonschema.ResourceGroupName(),
"resource_group_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"location": commonschema.Location(),
"location": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"target_resource_id": {
Type: pluginsdk.TypeString,
Expand Down Expand Up @@ -289,7 +296,13 @@ func (s AutoscaleSettingUpgradeV1ToV2) Schema() map[string]*pluginsdk.Schema {
},
},

"tags": tags.Schema(),
"tags": {
Type: pluginsdk.TypeMap,
Optional: true,
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
},
},
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package migration

import (
"context"
"log"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/monitor/parse"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
)

type SmartDetectorAlertRuleV0ToV1 struct{}

func (s SmartDetectorAlertRuleV0ToV1) Schema() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
"name": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
},

"resource_group_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"detector_type": {
Type: pluginsdk.TypeString,
Required: true,
},

"scope_resource_ids": {
Type: pluginsdk.TypeSet,
Required: true,
},

"severity": {
Type: pluginsdk.TypeString,
Required: true,
},

"frequency": {
Type: pluginsdk.TypeString,
Required: true,
},

"action_group": {
Type: pluginsdk.TypeList,
Required: true,
MaxItems: 1,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"ids": {
Type: pluginsdk.TypeSet,
Required: true,
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
},
},

"email_subject": {
Type: pluginsdk.TypeString,
Optional: true,
},

"webhook_payload": {
Type: pluginsdk.TypeString,
Optional: true,
},
},
},
},

"description": {
Type: pluginsdk.TypeString,
Optional: true,
},

"enabled": {
Type: pluginsdk.TypeBool,
Optional: true,
Default: true,
},

"throttling_duration": {
Type: pluginsdk.TypeString,
Optional: true,
},

"tags": {
Type: pluginsdk.TypeMap,
Optional: true,
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
},
},
}
}

func (s SmartDetectorAlertRuleV0ToV1) UpgradeFunc() pluginsdk.StateUpgraderFunc {
return func(ctx context.Context, rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) {
oldId := rawState["id"].(string)
newId, err := parse.SmartDetectorAlertRuleIDInsensitively(oldId)
if err != nil {
return nil, err
}

log.Printf("[DEBUG] Updating ID from %q to %q", oldId, newId)

rawState["id"] = newId.ID()
return rawState, nil
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/hashicorp/terraform-provider-azurerm/helpers/tf"
commonValidate "github.com/hashicorp/terraform-provider-azurerm/helpers/validate"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/monitor/migration"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/monitor/parse"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/monitor/validate"
"github.com/hashicorp/terraform-provider-azurerm/internal/tags"
Expand All @@ -36,6 +37,11 @@ func resourceMonitorSmartDetectorAlertRule() *pluginsdk.Resource {
Delete: pluginsdk.DefaultTimeout(30 * time.Minute),
},

SchemaVersion: 1,
StateUpgraders: pluginsdk.StateUpgrades(map[int]pluginsdk.StateUpgrade{
0: migration.SmartDetectorAlertRuleV0ToV1{},
}),

Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error {
_, err := parse.SmartDetectorAlertRuleID(id)
return err
Expand Down
48 changes: 46 additions & 2 deletions internal/services/monitor/parse/smart_detector_alert_rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func (id SmartDetectorAlertRuleId) String() string {
}

func (id SmartDetectorAlertRuleId) ID() string {
fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.AlertsManagement/smartdetectoralertrules/%s"
fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.AlertsManagement/smartDetectorAlertRules/%s"
return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.Name)
}

Expand All @@ -57,7 +57,51 @@ func SmartDetectorAlertRuleID(input string) (*SmartDetectorAlertRuleId, error) {
return nil, fmt.Errorf("ID was missing the 'resourceGroups' element")
}

if resourceId.Name, err = id.PopSegment("smartdetectoralertrules"); err != nil {
if resourceId.Name, err = id.PopSegment("smartDetectorAlertRules"); err != nil {
return nil, err
}

if err := id.ValidateNoEmptySegments(input); err != nil {
return nil, err
}

return &resourceId, nil
}

// SmartDetectorAlertRuleIDInsensitively parses an SmartDetectorAlertRule ID into an SmartDetectorAlertRuleId struct, insensitively
// This should only be used to parse an ID for rewriting, the SmartDetectorAlertRuleID
// method should be used instead for validation etc.
//
// Whilst this may seem strange, this enables Terraform have consistent casing
// which works around issues in Core, whilst handling broken API responses.
func SmartDetectorAlertRuleIDInsensitively(input string) (*SmartDetectorAlertRuleId, error) {
id, err := resourceids.ParseAzureResourceID(input)
if err != nil {
return nil, err
}

resourceId := SmartDetectorAlertRuleId{
SubscriptionId: id.SubscriptionID,
ResourceGroup: id.ResourceGroup,
}

if resourceId.SubscriptionId == "" {
return nil, fmt.Errorf("ID was missing the 'subscriptions' element")
}

if resourceId.ResourceGroup == "" {
return nil, fmt.Errorf("ID was missing the 'resourceGroups' element")
}

// find the correct casing for the 'smartDetectorAlertRules' segment
smartDetectorAlertRulesKey := "smartDetectorAlertRules"
for key := range id.Path {
if strings.EqualFold(key, smartDetectorAlertRulesKey) {
smartDetectorAlertRulesKey = key
break
}
}
if resourceId.Name, err = id.PopSegment(smartDetectorAlertRulesKey); err != nil {
return nil, err
}

Expand Down
123 changes: 120 additions & 3 deletions internal/services/monitor/parse/smart_detector_alert_rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ var _ resourceids.Id = SmartDetectorAlertRuleId{}

func TestSmartDetectorAlertRuleIDFormatter(t *testing.T) {
actual := NewSmartDetectorAlertRuleID("12345678-1234-9876-4563-123456789012", "group1", "rule1").ID()
expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.AlertsManagement/smartdetectoralertrules/rule1"
expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.AlertsManagement/smartDetectorAlertRules/rule1"
if actual != expected {
t.Fatalf("Expected %q but got %q", expected, actual)
}
Expand Down Expand Up @@ -63,13 +63,13 @@ func TestSmartDetectorAlertRuleID(t *testing.T) {

{
// missing value for Name
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.AlertsManagement/smartdetectoralertrules/",
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.AlertsManagement/smartDetectorAlertRules/",
Error: true,
},

{
// valid
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.AlertsManagement/smartdetectoralertrules/rule1",
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.AlertsManagement/smartDetectorAlertRules/rule1",
Expected: &SmartDetectorAlertRuleId{
SubscriptionId: "12345678-1234-9876-4563-123456789012",
ResourceGroup: "group1",
Expand Down Expand Up @@ -110,3 +110,120 @@ func TestSmartDetectorAlertRuleID(t *testing.T) {
}
}
}

func TestSmartDetectorAlertRuleIDInsensitively(t *testing.T) {
testData := []struct {
Input string
Error bool
Expected *SmartDetectorAlertRuleId
}{

{
// empty
Input: "",
Error: true,
},

{
// missing SubscriptionId
Input: "/",
Error: true,
},

{
// missing value for SubscriptionId
Input: "/subscriptions/",
Error: true,
},

{
// missing ResourceGroup
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/",
Error: true,
},

{
// missing value for ResourceGroup
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/",
Error: true,
},

{
// missing Name
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.AlertsManagement/",
Error: true,
},

{
// missing value for Name
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.AlertsManagement/smartDetectorAlertRules/",
Error: true,
},

{
// valid
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.AlertsManagement/smartDetectorAlertRules/rule1",
Expected: &SmartDetectorAlertRuleId{
SubscriptionId: "12345678-1234-9876-4563-123456789012",
ResourceGroup: "group1",
Name: "rule1",
},
},

{
// lower-cased segment names
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.AlertsManagement/smartdetectoralertrules/rule1",
Expected: &SmartDetectorAlertRuleId{
SubscriptionId: "12345678-1234-9876-4563-123456789012",
ResourceGroup: "group1",
Name: "rule1",
},
},

{
// upper-cased segment names
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.AlertsManagement/SMARTDETECTORALERTRULES/rule1",
Expected: &SmartDetectorAlertRuleId{
SubscriptionId: "12345678-1234-9876-4563-123456789012",
ResourceGroup: "group1",
Name: "rule1",
},
},

{
// mixed-cased segment names
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.AlertsManagement/SmArTdEtEcToRaLeRtRuLeS/rule1",
Expected: &SmartDetectorAlertRuleId{
SubscriptionId: "12345678-1234-9876-4563-123456789012",
ResourceGroup: "group1",
Name: "rule1",
},
},
}

for _, v := range testData {
t.Logf("[DEBUG] Testing %q", v.Input)

actual, err := SmartDetectorAlertRuleIDInsensitively(v.Input)
if err != nil {
if v.Error {
continue
}

t.Fatalf("Expect a value but got an error: %s", err)
}
if v.Error {
t.Fatal("Expect an error but didn't get one")
}

if actual.SubscriptionId != v.Expected.SubscriptionId {
t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId)
}
if actual.ResourceGroup != v.Expected.ResourceGroup {
t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup)
}
if actual.Name != v.Expected.Name {
t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name)
}
}
}
2 changes: 1 addition & 1 deletion internal/services/monitor/resourceids.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package monitor

//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ActionGroup -rewrite=true -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Insights/actionGroups/actionGroup1
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ActionRule -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.AlertsManagement/actionRules/actionRule1
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SmartDetectorAlertRule -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.AlertsManagement/smartdetectoralertrules/rule1
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SmartDetectorAlertRule -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.AlertsManagement/smartDetectorAlertRules/rule1 -rewrite=true
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ActivityLogAlert -rewrite=true -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Insights/activityLogAlerts/alert1
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=AutoscaleSetting -rewrite=true -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Insights/autoScaleSettings/setting1 -rewrite=true
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=LogProfile -id=/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Insights/logProfiles/profile1
Expand Down
Loading

0 comments on commit 9a5502b

Please sign in to comment.