Skip to content

Commit

Permalink
azurerm_cost_anomaly_alert - Support for scoping alert to a specifi…
Browse files Browse the repository at this point in the history
…c subscription_id (hashicorp#24258)

* Add subscription_id scope to cost anomaly alert

* Update formatting

* Cleanup update logic

* Set subscription_id to optional

* Apply suggestions from code review

Co-authored-by: kt <kt@katbyte.me>

* Tidy up test

---------

Co-authored-by: kt <kt@katbyte.me>
  • Loading branch information
2 people authored and rizkybiz committed Feb 29, 2024
1 parent 924ffad commit aa14852
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 6 deletions.
33 changes: 27 additions & 6 deletions internal/services/costmanagement/anomaly_alert_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ 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-sdk/resource-manager/costmanagement/2022-06-01-preview/scheduledactions"
"github.com/hashicorp/go-azure-sdk/resource-manager/costmanagement/2022-10-01/views"
"github.com/hashicorp/terraform-provider-azurerm/internal/sdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/costmanagement/parse"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/costmanagement/validate"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation"
Expand All @@ -39,6 +40,14 @@ func (AnomalyAlertResource) Arguments() map[string]*pluginsdk.Schema {
ValidateFunc: validation.StringIsNotEmpty,
},

"subscription_id": {
Type: pluginsdk.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
ValidateFunc: commonids.ValidateSubscriptionID,
},

"email_subject": {
Type: pluginsdk.TypeString,
Required: true,
Expand Down Expand Up @@ -80,8 +89,14 @@ func (r AnomalyAlertResource) Create() sdk.ResourceFunc {
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.CostManagement.ScheduledActionsClient
subscriptionId := metadata.Client.Account.SubscriptionId
id := scheduledactions.NewScopedScheduledActionID(fmt.Sprint("/subscriptions/", subscriptionId), metadata.ResourceData.Get("name").(string))

var subscriptionId string
if v, ok := metadata.ResourceData.GetOk("subscription_id"); ok {
subscriptionId = v.(string)
} else {
subscriptionId = fmt.Sprint("/subscriptions/", metadata.Client.Account.SubscriptionId)
}
id := scheduledactions.NewScopedScheduledActionID(subscriptionId, metadata.ResourceData.Get("name").(string))

existing, err := client.GetByScope(ctx, id)
if err != nil && !response.WasNotFound(existing.HttpResponse) {
Expand All @@ -94,7 +109,7 @@ func (r AnomalyAlertResource) Create() sdk.ResourceFunc {
emailAddressesRaw := metadata.ResourceData.Get("email_addresses").(*pluginsdk.Set).List()
emailAddresses := utils.ExpandStringSlice(emailAddressesRaw)

viewId := parse.NewAnomalyAlertViewID(subscriptionId, "ms:DailyAnomalyByResourceGroup")
viewId := views.NewScopedViewID(subscriptionId, "ms:DailyAnomalyByResourceGroup")

schedule := scheduledactions.ScheduleProperties{
Frequency: scheduledactions.ScheduleFrequencyDaily,
Expand Down Expand Up @@ -154,8 +169,13 @@ func (r AnomalyAlertResource) Update() sdk.ResourceFunc {
emailAddressesRaw := metadata.ResourceData.Get("email_addresses").(*pluginsdk.Set).List()
emailAddresses := utils.ExpandStringSlice(emailAddressesRaw)

subscriptionId := metadata.Client.Account.SubscriptionId
viewId := parse.NewAnomalyAlertViewID(subscriptionId, "ms:DailyAnomalyByResourceGroup")
var subscriptionId string
if v, ok := metadata.ResourceData.GetOk("subscription_id"); ok {
subscriptionId = v.(string)
} else {
subscriptionId = fmt.Sprint("/subscriptions/", metadata.Client.Account.SubscriptionId)
}
viewId := views.NewScopedViewID(subscriptionId, "ms:DailyAnomalyByResourceGroup")

schedule := scheduledactions.ScheduleProperties{
Frequency: scheduledactions.ScheduleFrequencyDaily,
Expand Down Expand Up @@ -212,6 +232,7 @@ func (AnomalyAlertResource) Read() sdk.ResourceFunc {
metadata.ResourceData.Set("name", model.Name)
if props := model.Properties; props != nil {
metadata.ResourceData.Set("display_name", props.DisplayName)
metadata.ResourceData.Set("subscription_id", fmt.Sprint("/", *props.Scope))
metadata.ResourceData.Set("email_subject", props.Notification.Subject)
metadata.ResourceData.Set("email_addresses", props.Notification.To)
metadata.ResourceData.Set("message", props.Notification.Message)
Expand Down
30 changes: 30 additions & 0 deletions internal/services/costmanagement/anomaly_alert_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ func TestAccResourceAnomalyAlert_update(t *testing.T) {
})
}

func TestAccResourceAnomalyAlert_complete(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_cost_anomaly_alert", "test")
testResource := AnomalyAlertResource{}
data.ResourceTest(t, testResource, []acceptance.TestStep{
data.ApplyStep(testResource.completeConfig, testResource),
data.ImportStep(),
data.ApplyStep(testResource.updateConfig, testResource),
data.ImportStep(),
})
}

func TestAccResourceAnomalyAlert_requiresImport(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_cost_anomaly_alert", "test")
testResource := AnomalyAlertResource{}
Expand Down Expand Up @@ -69,6 +80,25 @@ resource "azurerm_cost_anomaly_alert" "test" {
`, data.RandomInteger, data.RandomInteger)
}

func (AnomalyAlertResource) completeConfig(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}
data "azurerm_subscription" "test" {}
resource "azurerm_cost_anomaly_alert" "test" {
name = "-acctest-%d"
display_name = "acctest %d"
subscription_id = data.azurerm_subscription.test.id
email_subject = "Hi"
email_addresses = ["test@test.com", "test@hashicorp.developer"]
message = "Cost anomaly complete test"
}
`, data.RandomInteger, data.RandomInteger)
}

func (r AnomalyAlertResource) requiresImportConfig(data acceptance.TestData) string {
template := r.basicConfig(data)
return fmt.Sprintf(`
Expand Down
5 changes: 5 additions & 0 deletions website/docs/r/cost_anomaly_alert.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ description: |-

Manages a Cost Anomaly Alert.

~> **Note:** Anomaly alerts are sent based on the current access of the rule creator at the time that the email is sent. Learn more [here](https://learn.microsoft.com/en-us/azure/cost-management-billing/understand/analyze-unexpected-charges#create-an-anomaly-alert).

## Example Usage

```hcl
resource "azurerm_cost_anomaly_alert" "example" {
name = "alertname"
display_name = "Alert DisplayName"
subscription_id = "/subscriptions/00000000-0000-0000-0000-000000000000"
email_subject = "My Test Anomaly Alert"
email_addresses = ["example@test.net"]
}
Expand All @@ -29,6 +32,8 @@ The following arguments are supported:

* `display_name` - (Required) The display name which should be used for this Cost Anomaly Alert.

* `subscription_id` - The ID of the Subscription this Cost Anomaly Alert is scoped to. Changing this forces a new resource to be created. When not supplied this defaults to the subscription configured in the provider.

* `email_addresses` - (Required) Specifies a list of email addresses which the Anomaly Alerts are send to.

* `email_subject` - (Required) The email subject of the Cost Anomaly Alerts. Maximum length of the subject is 70.
Expand Down

0 comments on commit aa14852

Please sign in to comment.