From aea2bf09588f0099e7bd7bd5cbeb4aa8e600d3c7 Mon Sep 17 00:00:00 2001 From: magodo Date: Thu, 28 May 2020 17:13:54 +0800 Subject: [PATCH 1/9] `azurerm_monitor_metric_alert` supports multiple scopes and different criterias Originally, the resource only allows user to specify one scope. Furthermore, it only allow user to specify static criteria, while there are dynamic criterias and "webtest available location" criterias. This PR tries to support both facets and not to introduce breaking change. --- .../monitor/monitor_metric_alert_resource.go | 534 ++++++++++++++---- .../monitor_metric_alert_resource_test.go | 374 +++++++----- .../docs/r/monitor_metric_alert.html.markdown | 31 +- 3 files changed, 664 insertions(+), 275 deletions(-) diff --git a/azurerm/internal/services/monitor/monitor_metric_alert_resource.go b/azurerm/internal/services/monitor/monitor_metric_alert_resource.go index bf1431e60fc2..a621f8428369 100644 --- a/azurerm/internal/services/monitor/monitor_metric_alert_resource.go +++ b/azurerm/internal/services/monitor/monitor_metric_alert_resource.go @@ -2,10 +2,13 @@ package monitor import ( "bytes" + "errors" "fmt" "log" "time" + "github.com/Azure/go-autorest/autorest/date" + "github.com/Azure/azure-sdk-for-go/services/preview/monitor/mgmt/2019-06-01/insights" "github.com/hashicorp/go-azure-helpers/response" "github.com/hashicorp/terraform-plugin-sdk/helper/hashcode" @@ -15,11 +18,136 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/location" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) +func monitorMetricAlertMultiMetricCriteriaSchema(override map[string]*schema.Schema) map[string]*schema.Schema { + base := map[string]*schema.Schema{ + "metric_namespace": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "metric_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "aggregation": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "Average", + "Count", + "Minimum", + "Maximum", + "Total", + }, false), + }, + "dimension": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "operator": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "Include", + "Exclude", + }, false), + }, + "values": { + Type: schema.TypeList, + Required: true, + MinItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + } + for k, v := range override { + base[k] = v + } + return base +} + +var ( + monitorMetricAlertStaticMetricCriteriaSchema = monitorMetricAlertMultiMetricCriteriaSchema( + map[string]*schema.Schema{ + "operator": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(insights.OperatorEquals), + string(insights.OperatorGreaterThan), + string(insights.OperatorGreaterThanOrEqual), + string(insights.OperatorLessThan), + string(insights.OperatorLessThanOrEqual), + string(insights.OperatorNotEquals), + }, false), + }, + "threshold": { + Type: schema.TypeFloat, + Required: true, + }, + }, + ) + monitorMetricAlertDynamicMetricCriteriaSchema = monitorMetricAlertMultiMetricCriteriaSchema( + map[string]*schema.Schema{ + "operator": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(insights.DynamicThresholdOperatorLessThan), + string(insights.DynamicThresholdOperatorGreaterThan), + string(insights.DynamicThresholdOperatorGreaterOrLessThan), + }, false), + }, + "alert_sensitivity": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(insights.Low), + string(insights.Medium), + string(insights.High), + }, false), + }, + + "evaluation_total_count": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntAtLeast(1), + Default: 4, + }, + + "evaluation_failure_count": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntAtLeast(1), + Default: 4, + }, + + "ignore_data_before": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.IsRFC3339Time, + }, + }, + ) +) + func resourceArmMonitorMetricAlert() *schema.Resource { return &schema.Resource{ Create: resourceArmMonitorMetricAlertCreateUpdate, @@ -48,16 +176,10 @@ func resourceArmMonitorMetricAlert() *schema.Resource { "resource_group_name": azure.SchemaResourceGroupName(), - // TODO: Multiple resource IDs (Remove MaxItems) support is missing in SDK - // Issue to track: https://github.com/Azure/azure-sdk-for-go/issues/2920 - // But to prevent potential state migration in the future, let's stick to use Set now - - // lintignore:S018 "scopes": { Type: schema.TypeSet, Required: true, MinItems: 1, - MaxItems: 1, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: azure.ValidateResourceID, @@ -65,77 +187,65 @@ func resourceArmMonitorMetricAlert() *schema.Resource { Set: schema.HashString, }, + "target_resource_type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: `The resource type (e.g. Microsoft.Compute/virtualMachines) of the target resource. Required when using subscription, resource group scope or multiple scopes.`, + }, + + "target_resource_location": { + Type: schema.TypeString, + Optional: true, + Computed: true, + StateFunc: location.StateFunc, + DiffSuppressFunc: location.DiffSuppressFunc, + Description: `The location of the target resource. Required when using subscription, resource group scope or multiple scopes.`, + }, + + // static criteria "criteria": { - Type: schema.TypeList, - Required: true, - MinItems: 1, + Type: schema.TypeSet, + Optional: true, + MinItems: 1, + AtLeastOneOf: []string{"dynamic_criteria", "webtest_location_availability_criteria"}, + Elem: &schema.Resource{ + Schema: monitorMetricAlertStaticMetricCriteriaSchema, + }, + }, + + "dynamic_criteria": { + Type: schema.TypeSet, + Optional: true, + MinItems: 1, + AtLeastOneOf: []string{"criteria", "webtest_location_availability_criteria"}, + Elem: &schema.Resource{ + Schema: monitorMetricAlertDynamicMetricCriteriaSchema, + }, + }, + + "webtest_location_availability_criteria": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + MaxItems: 1, + AtLeastOneOf: []string{"criteria", "dynamic_criteria"}, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "metric_namespace": { + "webtest_id": { Type: schema.TypeString, Required: true, - ValidateFunc: validation.StringIsNotEmpty, + ValidateFunc: azure.ValidateResourceID, }, - "metric_name": { + "component_id": { Type: schema.TypeString, Required: true, - ValidateFunc: validation.StringIsNotEmpty, - }, - "aggregation": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - "Average", - "Count", - "Minimum", - "Maximum", - "Total", - }, false), - }, - "operator": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - string(insights.OperatorEquals), - string(insights.OperatorGreaterThan), - string(insights.OperatorGreaterThanOrEqual), - string(insights.OperatorLessThan), - string(insights.OperatorLessThanOrEqual), - string(insights.OperatorNotEquals), - }, false), - }, - "threshold": { - Type: schema.TypeFloat, - Required: true, + ValidateFunc: azure.ValidateResourceID, }, - "dimension": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringIsNotEmpty, - }, - "operator": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - "Include", - "Exclude", - }, false), - }, - "values": { - Type: schema.TypeList, - Required: true, - MinItems: 1, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - }, - }, + "failed_location_count": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntAtLeast(1), }, }, }, @@ -249,8 +359,9 @@ func resourceArmMonitorMetricAlertCreateUpdate(d *schema.ResourceData, meta inte severity := d.Get("severity").(int) frequency := d.Get("frequency").(string) windowSize := d.Get("window_size").(string) - criteriaRaw := d.Get("criteria").([]interface{}) actionRaw := d.Get("action").(*schema.Set).List() + targetResourceType := d.Get("target_resource_type").(string) + targetResourceLocation := d.Get("target_resource_location").(string) t := d.Get("tags").(map[string]interface{}) expandedTags := tags.Expand(t) @@ -258,15 +369,17 @@ func resourceArmMonitorMetricAlertCreateUpdate(d *schema.ResourceData, meta inte parameters := insights.MetricAlertResource{ Location: utils.String(azure.NormalizeLocation("Global")), MetricAlertProperties: &insights.MetricAlertProperties{ - Enabled: utils.Bool(enabled), - AutoMitigate: utils.Bool(autoMitigate), - Description: utils.String(description), - Severity: utils.Int32(int32(severity)), - EvaluationFrequency: utils.String(frequency), - WindowSize: utils.String(windowSize), - Scopes: utils.ExpandStringSlice(scopesRaw), - Criteria: expandMonitorMetricAlertCriteria(criteriaRaw), - Actions: expandMonitorMetricAlertAction(actionRaw), + Enabled: utils.Bool(enabled), + AutoMitigate: utils.Bool(autoMitigate), + Description: utils.String(description), + Severity: utils.Int32(int32(severity)), + EvaluationFrequency: utils.String(frequency), + WindowSize: utils.String(windowSize), + Scopes: utils.ExpandStringSlice(scopesRaw), + Criteria: expandMonitorMetricAlertCriteria(d), + Actions: expandMonitorMetricAlertAction(actionRaw), + TargetResourceType: utils.String(targetResourceType), + TargetResourceRegion: utils.String(targetResourceLocation), }, Tags: expandedTags, } @@ -321,12 +434,34 @@ func resourceArmMonitorMetricAlertRead(d *schema.ResourceData, meta interface{}) if err := d.Set("scopes", utils.FlattenStringSlice(alert.Scopes)); err != nil { return fmt.Errorf("Error setting `scopes`: %+v", err) } - if err := d.Set("criteria", flattenMonitorMetricAlertCriteria(alert.Criteria)); err != nil { - return fmt.Errorf("Error setting `criteria`: %+v", err) + + // Determine the correct criteria schema to set + var criteriaSchema string + switch c := alert.Criteria.(type) { + case insights.MetricAlertSingleResourceMultipleMetricCriteria: + criteriaSchema = "criteria" + case insights.MetricAlertMultipleResourceMultipleMetricCriteria: + if c.AllOf == nil || len(*c.AllOf) == 0 { + return errors.New("nil or empty contained criterias of MultipleResourceMultipleMetricCriteria") + } + switch (*c.AllOf)[0].(type) { + case insights.DynamicMetricCriteria: + criteriaSchema = "dynamic_criteria" + case insights.MetricCriteria: + criteriaSchema = "criteria" + } + case insights.WebtestLocationAvailabilityCriteria: + criteriaSchema = "webtest_location_availability_criteria" } + if err := d.Set(criteriaSchema, flattenMonitorMetricAlertCriteria(alert.Criteria)); err != nil { + return fmt.Errorf("Error setting `%s`: %+v", criteriaSchema, err) + } + if err := d.Set("action", flattenMonitorMetricAlertAction(alert.Actions)); err != nil { return fmt.Errorf("Error setting `action`: %+v", err) } + d.Set("target_resource_type", alert.TargetResourceType) + d.Set("target_resource_location", alert.TargetResourceRegion) } return tags.FlattenAndSet(d, resp.Tags) } @@ -352,37 +487,97 @@ func resourceArmMonitorMetricAlertDelete(d *schema.ResourceData, meta interface{ return nil } -func expandMonitorMetricAlertCriteria(input []interface{}) *insights.MetricAlertSingleResourceMultipleMetricCriteria { - criteria := make([]insights.MetricCriteria, 0) +func expandMonitorMetricAlertCriteria(d *schema.ResourceData) insights.BasicMetricAlertCriteria { + switch { + case d.Get("criteria").(*schema.Set).Len() != 0: + return expandMonitorMetricAlertMetricCriteria(d.Get("criteria").(*schema.Set).List()) + case d.Get("dynamic_criteria").(*schema.Set).Len() != 0: + return expandMonitorMetricAlertDynamicMetricCriteria(d.Get("dynamic_criteria").(*schema.Set).List()) + case d.Get("webtest_location_availability_criteria").(*schema.Set).Len() != 0: + return expandMonitorMetricAlertWebtestLocAvailCriteria(d.Get("webtest_location_availability_criteria").([]interface{})) + default: + // Guaranteed by schema `AtLeastOne` constraint + panic("will never happen") + } +} + +func expandMonitorMetricAlertMetricCriteria(input []interface{}) insights.BasicMetricAlertCriteria { + criterias := make([]insights.BasicMultiMetricCriteria, 0) for i, item := range input { v := item.(map[string]interface{}) - - dimensions := make([]insights.MetricDimension, 0) - for _, dimension := range v["dimension"].([]interface{}) { - dVal := dimension.(map[string]interface{}) - dimensions = append(dimensions, insights.MetricDimension{ - Name: utils.String(dVal["name"].(string)), - Operator: utils.String(dVal["operator"].(string)), - Values: utils.ExpandStringSlice(dVal["values"].([]interface{})), - }) - } - - criteria = append(criteria, insights.MetricCriteria{ + dimensions := expandMonitorMetricAlertMultiMetricCriteriaDimension(v["dimension"].([]interface{})) + criterias = append(criterias, insights.MetricCriteria{ Name: utils.String(fmt.Sprintf("Metric%d", i+1)), MetricNamespace: utils.String(v["metric_namespace"].(string)), MetricName: utils.String(v["metric_name"].(string)), TimeAggregation: v["aggregation"].(string), + Dimensions: &dimensions, Operator: insights.Operator(v["operator"].(string)), Threshold: utils.Float(v["threshold"].(float64)), - Dimensions: &dimensions, }) } - return &insights.MetricAlertSingleResourceMultipleMetricCriteria{ - AllOf: &criteria, - OdataType: insights.OdataTypeMicrosoftAzureMonitorSingleResourceMultipleMetricCriteria, + return &insights.MetricAlertMultipleResourceMultipleMetricCriteria{ + AllOf: &criterias, + OdataType: insights.OdataTypeMicrosoftAzureMonitorMultipleResourceMultipleMetricCriteria, + } +} +func expandMonitorMetricAlertDynamicMetricCriteria(input []interface{}) insights.BasicMetricAlertCriteria { + criterias := make([]insights.BasicMultiMetricCriteria, 0) + for i, item := range input { + v := item.(map[string]interface{}) + dimensions := expandMonitorMetricAlertMultiMetricCriteriaDimension(v["dimension"].([]interface{})) + var ignoreDataBefore *date.Time + if v := v["ignore_data_before"].(string); v != "" { + // Guaranteed in schema validation func. + t, _ := time.Parse(time.RFC3339, v) + ignoreDataBefore = &date.Time{Time: t} + } + criterias = append(criterias, insights.DynamicMetricCriteria{ + Name: utils.String(fmt.Sprintf("Metric%d", i+1)), + MetricNamespace: utils.String(v["metric_namespace"].(string)), + MetricName: utils.String(v["metric_name"].(string)), + TimeAggregation: v["aggregation"].(string), + Dimensions: &dimensions, + Operator: insights.DynamicThresholdOperator(v["operator"].(string)), + AlertSensitivity: insights.DynamicThresholdSensitivity(v["alert_sensitivity"].(string)), + FailingPeriods: &insights.DynamicThresholdFailingPeriods{ + NumberOfEvaluationPeriods: utils.Float(float64(v["evaluation_total_count"].(int))), + MinFailingPeriodsToAlert: utils.Float(float64(v["evaluation_failure_count"].(int))), + }, + IgnoreDataBefore: ignoreDataBefore, + }) + } + return &insights.MetricAlertMultipleResourceMultipleMetricCriteria{ + AllOf: &criterias, + OdataType: insights.OdataTypeMicrosoftAzureMonitorMultipleResourceMultipleMetricCriteria, + } +} + +func expandMonitorMetricAlertWebtestLocAvailCriteria(input []interface{}) insights.BasicMetricAlertCriteria { + if len(input) == 0 { + return nil + } + v := input[0].(map[string]interface{}) + return &insights.WebtestLocationAvailabilityCriteria{ + WebTestID: utils.String(v["webtest_id"].(string)), + ComponentID: utils.String(v["component_id"].(string)), + FailedLocationCount: utils.Float(float64(v["failed_location_count"].(int))), } } +func expandMonitorMetricAlertMultiMetricCriteriaDimension(input []interface{}) []insights.MetricDimension { + result := make([]insights.MetricDimension, 0) + for _, dimension := range input { + dVal := dimension.(map[string]interface{}) + result = append(result, insights.MetricDimension{ + Name: utils.String(dVal["name"].(string)), + Operator: utils.String(dVal["operator"].(string)), + Values: utils.ExpandStringSlice(dVal["values"].([]interface{})), + }) + } + return result +} + func expandMonitorMetricAlertAction(input []interface{}) *[]insights.MetricAlertAction { actions := make([]insights.MetricAlertAction, 0) for _, item := range input { @@ -405,35 +600,107 @@ func expandMonitorMetricAlertAction(input []interface{}) *[]insights.MetricAlert } func flattenMonitorMetricAlertCriteria(input insights.BasicMetricAlertCriteria) (result []interface{}) { - result = make([]interface{}, 0) - if input == nil { - return + switch criteria := input.(type) { + case insights.MetricAlertSingleResourceMultipleMetricCriteria: + // As service is gonna deprecate data type of `MetricAlertSingleResourceMultipleMetricCriteria`, + // we use the same function to handle both single/multiple ResourceMultipleMetricCriteria cases. + // This is possible because their `AllOf` member implement the same interface: `MultiMetricCriteria`. + if criteria.AllOf == nil { + return nil + } + l := make([]insights.BasicMultiMetricCriteria, len(*criteria.AllOf)) + for i, c := range *criteria.AllOf { + l[i] = c + } + return flattenMonitorMetricAlertMultiMetricCriteria(&l) + case insights.MetricAlertMultipleResourceMultipleMetricCriteria: + return flattenMonitorMetricAlertMultiMetricCriteria(criteria.AllOf) + case insights.WebtestLocationAvailabilityCriteria: + return flattenMonitorMetricAlertWebtestLocAvailCriteria(&criteria) + default: + return nil } - criteria, ok := input.AsMetricAlertSingleResourceMultipleMetricCriteria() - if !ok || criteria == nil || criteria.AllOf == nil { - return +} + +func flattenMonitorMetricAlertMultiMetricCriteria(input *[]insights.BasicMultiMetricCriteria) []interface{} { + if input == nil { + return nil } - for _, metric := range *criteria.AllOf { + result := make([]interface{}, 0) + + for _, criteria := range *input { v := make(map[string]interface{}) + var ( + metricName string + metricNamespace string + timeAggregation interface{} + dimensions []insights.MetricDimension + ) + + switch criteria := criteria.(type) { + case insights.MetricCriteria: + if criteria.MetricName != nil { + metricName = *criteria.MetricName + } + if criteria.MetricNamespace != nil { + metricNamespace = *criteria.MetricNamespace + } + timeAggregation = criteria.TimeAggregation + if criteria.Dimensions != nil { + dimensions = *criteria.Dimensions + } - if metric.MetricNamespace != nil { - v["metric_namespace"] = *metric.MetricNamespace - } - if metric.MetricName != nil { - v["metric_name"] = *metric.MetricName - } - if aggr, ok := metric.TimeAggregation.(string); ok { - v["aggregation"] = aggr - } + // MetricCriteria specific properties + v["operator"] = string(criteria.Operator) - v["operator"] = string(metric.Operator) + threshold := 0.0 + if criteria.Threshold != nil { + threshold = *criteria.Threshold + } + v["threshold"] = threshold + case insights.DynamicMetricCriteria: + if criteria.MetricName != nil { + metricName = *criteria.MetricName + } + if criteria.MetricNamespace != nil { + metricNamespace = *criteria.MetricNamespace + } + timeAggregation = criteria.TimeAggregation + if criteria.Dimensions != nil { + dimensions = *criteria.Dimensions + } + // DynamicMetricCriteria specific properties + v["operator"] = string(criteria.Operator) + v["alert_sensitivity"] = string(criteria.AlertSensitivity) + var ( + nEvl = 1 + nFailEvl = 1 + ) + if period := criteria.FailingPeriods; period != nil { + if period.NumberOfEvaluationPeriods != nil { + nEvl = int(*period.NumberOfEvaluationPeriods) + } + if period.MinFailingPeriodsToAlert != nil { + nFailEvl = int(*period.MinFailingPeriodsToAlert) + } + } + v["evaluation_total_count"] = nEvl + v["evaluation_failure_count"] = nFailEvl - if metric.Threshold != nil { - v["threshold"] = *metric.Threshold + ignoreDataBefore := "" + if criteria.IgnoreDataBefore != nil { + ignoreDataBefore = criteria.IgnoreDataBefore.Format(time.RFC3339) + } + v["ignore_data_before"] = ignoreDataBefore } - if metric.Dimensions != nil { + + // Common properties + v["metric_name"] = metricName + v["metric_namespace"] = metricNamespace + v["aggregation"] = timeAggregation + if dimensions != nil { dimResult := make([]map[string]interface{}, 0) - for _, dimension := range *metric.Dimensions { + for _, dimension := range dimensions { dVal := make(map[string]interface{}) if dimension.Name != nil { dVal["name"] = *dimension.Name @@ -449,10 +716,37 @@ func flattenMonitorMetricAlertCriteria(input insights.BasicMetricAlertCriteria) result = append(result, v) } - return result } +func flattenMonitorMetricAlertWebtestLocAvailCriteria(input *insights.WebtestLocationAvailabilityCriteria) []interface{} { + if input == nil { + return nil + } + webtestID := "" + if input.WebTestID != nil { + webtestID = *input.WebTestID + } + + componentID := "" + if input.ComponentID != nil { + componentID = *input.ComponentID + } + + failedLocationCount := 0 + if input.FailedLocationCount != nil { + failedLocationCount = int(*input.FailedLocationCount) + } + + return []interface{}{ + map[string]interface{}{ + "webtest_id": webtestID, + "component_id": componentID, + "failed_location_count": failedLocationCount, + }, + } +} + func flattenMonitorMetricAlertAction(input *[]insights.MetricAlertAction) (result []interface{}) { result = make([]interface{}, 0) if input == nil { diff --git a/azurerm/internal/services/monitor/tests/monitor_metric_alert_resource_test.go b/azurerm/internal/services/monitor/tests/monitor_metric_alert_resource_test.go index 8200d8e4198c..dee0b1de33e5 100644 --- a/azurerm/internal/services/monitor/tests/monitor_metric_alert_resource_test.go +++ b/azurerm/internal/services/monitor/tests/monitor_metric_alert_resource_test.go @@ -3,6 +3,7 @@ package tests import ( "fmt" "net/http" + "strings" "testing" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" @@ -23,15 +24,6 @@ func TestAccAzureRMMonitorMetricAlert_basic(t *testing.T) { Config: testAccAzureRMMonitorMetricAlert_basic(data), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMonitorMetricAlertExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "enabled", "true"), - resource.TestCheckResourceAttr(data.ResourceName, "scopes.#", "1"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.#", "1"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.metric_namespace", "Microsoft.Storage/storageAccounts"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.metric_name", "UsedCapacity"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.aggregation", "Average"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.operator", "GreaterThan"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.threshold", "55.5"), - resource.TestCheckResourceAttr(data.ResourceName, "action.#", "0"), ), }, data.ImportStep(), @@ -73,35 +65,6 @@ func TestAccAzureRMMonitorMetricAlert_complete(t *testing.T) { Config: testAccAzureRMMonitorMetricAlert_complete(data), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMonitorMetricAlertExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "enabled", "true"), - resource.TestCheckResourceAttr(data.ResourceName, "auto_mitigate", "false"), - resource.TestCheckResourceAttr(data.ResourceName, "severity", "4"), - resource.TestCheckResourceAttr(data.ResourceName, "description", "This is a complete metric alert resource."), - resource.TestCheckResourceAttr(data.ResourceName, "frequency", "PT30M"), - resource.TestCheckResourceAttr(data.ResourceName, "window_size", "PT12H"), - resource.TestCheckResourceAttr(data.ResourceName, "scopes.#", "1"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.#", "2"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.metric_namespace", "Microsoft.Storage/storageAccounts"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.metric_name", "Transactions"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.aggregation", "Maximum"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.operator", "Equals"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.threshold", "99"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.dimension.#", "2"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.dimension.0.name", "GeoType"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.dimension.0.operator", "Include"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.dimension.0.values.#", "1"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.dimension.0.values.0", "*"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.dimension.1.name", "ApiName"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.dimension.1.operator", "Include"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.dimension.1.values.#", "1"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.dimension.1.values.0", "*"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.1.metric_namespace", "Microsoft.Storage/storageAccounts"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.1.metric_name", "UsedCapacity"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.1.aggregation", "Total"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.1.operator", "GreaterThanOrEqual"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.1.threshold", "66.6"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.1.dimension.#", "0"), - resource.TestCheckResourceAttr(data.ResourceName, "action.#", "2"), ), }, data.ImportStep(), @@ -121,83 +84,108 @@ func TestAccAzureRMMonitorMetricAlert_basicAndCompleteUpdate(t *testing.T) { Config: testAccAzureRMMonitorMetricAlert_basic(data), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMonitorMetricAlertExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "enabled", "true"), - resource.TestCheckResourceAttr(data.ResourceName, "auto_mitigate", "true"), - resource.TestCheckResourceAttr(data.ResourceName, "severity", "3"), - resource.TestCheckResourceAttr(data.ResourceName, "description", ""), - resource.TestCheckResourceAttr(data.ResourceName, "frequency", "PT1M"), - resource.TestCheckResourceAttr(data.ResourceName, "window_size", "PT5M"), - resource.TestCheckResourceAttr(data.ResourceName, "scopes.#", "1"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.#", "1"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.metric_namespace", "Microsoft.Storage/storageAccounts"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.metric_name", "UsedCapacity"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.aggregation", "Average"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.operator", "GreaterThan"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.threshold", "55.5"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.dimension.#", "0"), - resource.TestCheckResourceAttr(data.ResourceName, "action.#", "0"), ), }, { Config: testAccAzureRMMonitorMetricAlert_complete(data), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMonitorMetricAlertExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "enabled", "true"), - resource.TestCheckResourceAttr(data.ResourceName, "auto_mitigate", "false"), - resource.TestCheckResourceAttr(data.ResourceName, "severity", "4"), - resource.TestCheckResourceAttr(data.ResourceName, "description", "This is a complete metric alert resource."), - resource.TestCheckResourceAttr(data.ResourceName, "frequency", "PT30M"), - resource.TestCheckResourceAttr(data.ResourceName, "window_size", "PT12H"), - resource.TestCheckResourceAttr(data.ResourceName, "scopes.#", "1"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.#", "2"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.metric_namespace", "Microsoft.Storage/storageAccounts"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.metric_name", "Transactions"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.aggregation", "Maximum"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.operator", "Equals"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.threshold", "99"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.dimension.#", "2"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.dimension.0.name", "GeoType"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.dimension.0.operator", "Include"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.dimension.0.values.#", "1"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.dimension.0.values.0", "*"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.dimension.1.name", "ApiName"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.dimension.1.operator", "Include"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.dimension.1.values.#", "1"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.dimension.1.values.0", "*"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.1.metric_namespace", "Microsoft.Storage/storageAccounts"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.1.metric_name", "UsedCapacity"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.1.aggregation", "Total"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.1.operator", "GreaterThanOrEqual"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.1.threshold", "66.6"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.1.dimension.#", "0"), - resource.TestCheckResourceAttr(data.ResourceName, "action.#", "2"), ), }, { Config: testAccAzureRMMonitorMetricAlert_basic(data), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMonitorMetricAlertExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "enabled", "true"), - resource.TestCheckResourceAttr(data.ResourceName, "auto_mitigate", "true"), - resource.TestCheckResourceAttr(data.ResourceName, "severity", "3"), - resource.TestCheckResourceAttr(data.ResourceName, "description", ""), - resource.TestCheckResourceAttr(data.ResourceName, "frequency", "PT1M"), - resource.TestCheckResourceAttr(data.ResourceName, "window_size", "PT5M"), - resource.TestCheckResourceAttr(data.ResourceName, "scopes.#", "1"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.#", "1"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.metric_namespace", "Microsoft.Storage/storageAccounts"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.metric_name", "UsedCapacity"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.aggregation", "Average"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.operator", "GreaterThan"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.threshold", "55.5"), - resource.TestCheckResourceAttr(data.ResourceName, "criteria.0.dimension.#", "0"), - resource.TestCheckResourceAttr(data.ResourceName, "action.#", "0"), ), }, }, }) } +func TestAccAzureRMMonitorMetricAlert_multiScope(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_monitor_metric_alert", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMMonitorMetricAlertDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMonitorMetricAlert_multiScope(data, "azurerm_linux_virtual_machine.test1.id", "azurerm_linux_virtual_machine.test2.id"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMonitorMetricAlertExists(data.ResourceName), + ), + }, + data.ImportStep(), + { + Config: testAccAzureRMMonitorMetricAlert_multiScope(data, "azurerm_linux_virtual_machine.test1.id"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMonitorMetricAlertExists(data.ResourceName), + ), + }, + data.ImportStep(), + { + Config: testAccAzureRMMonitorMetricAlert_multiScope(data, "azurerm_linux_virtual_machine.test1.id", "azurerm_linux_virtual_machine.test2.id"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMonitorMetricAlertExists(data.ResourceName), + ), + }, + data.ImportStep(), + }, + }) +} + +func testCheckAzureRMMonitorMetricAlertDestroy(s *terraform.State) error { + conn := acceptance.AzureProvider.Meta().(*clients.Client).Monitor.MetricAlertsClient + ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_monitor_metric_alert" { + continue + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + resp, err := conn.Get(ctx, resourceGroup, name) + if err != nil { + return nil + } + if resp.StatusCode != http.StatusNotFound { + return fmt.Errorf("Metric alert still exists:\n%#v", resp) + } + } + return nil +} + +func testCheckAzureRMMonitorMetricAlertExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acceptance.AzureProvider.Meta().(*clients.Client).Monitor.MetricAlertsClient + ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext + + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + name := rs.Primary.Attributes["name"] + resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] + if !hasResourceGroup { + return fmt.Errorf("Bad: no resource group found in state for Metric Alert Instance: %s", name) + } + + resp, err := conn.Get(ctx, resourceGroup, name) + if err != nil { + return fmt.Errorf("Bad: Get on monitorMetricAlertsClient: %+v", err) + } + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: Metric Alert Instance %q (resource group: %q) does not exist", name, resourceGroup) + } + + return nil + } +} + func testAccAzureRMMonitorMetricAlert_basic(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { @@ -229,6 +217,8 @@ resource "azurerm_monitor_metric_alert" "test" { operator = "GreaterThan" threshold = 55.5 } + + window_size = "PT1H" } `, data.RandomInteger, data.Locations.Primary, data.RandomString, data.RandomInteger) } @@ -250,6 +240,7 @@ resource "azurerm_monitor_metric_alert" "import" { operator = "GreaterThan" threshold = 55.5 } + window_size = "PT1H" } `, template) } @@ -299,8 +290,8 @@ resource "azurerm_monitor_metric_alert" "test" { criteria { metric_namespace = "Microsoft.Storage/storageAccounts" metric_name = "Transactions" - aggregation = "Maximum" - operator = "Equals" + aggregation = "Total" + operator = "GreaterThan" threshold = 99 dimension { @@ -308,20 +299,6 @@ resource "azurerm_monitor_metric_alert" "test" { operator = "Include" values = ["*"] } - - dimension { - name = "ApiName" - operator = "Include" - values = ["*"] - } - } - - criteria { - metric_namespace = "Microsoft.Storage/storageAccounts" - metric_name = "UsedCapacity" - aggregation = "Total" - operator = "GreaterThanOrEqual" - threshold = 66.6 } action { @@ -335,53 +312,142 @@ resource "azurerm_monitor_metric_alert" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomString) } -func testCheckAzureRMMonitorMetricAlertDestroy(s *terraform.State) error { - conn := acceptance.AzureProvider.Meta().(*clients.Client).Monitor.MetricAlertsClient - ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext - for _, rs := range s.RootModule().Resources { - if rs.Type != "azurerm_monitor_metric_alert" { - continue - } +func testAccAzureRMMonitorMetricAlert_vmTemplate(data acceptance.TestData, n int) string { + return fmt.Sprintf(` +resource "azurerm_subnet" "test%[2]d" { + name = "internal%[2]d" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefix = "10.0.%[2]d.0/24" +} - name := rs.Primary.Attributes["name"] - resourceGroup := rs.Primary.Attributes["resource_group_name"] +resource "azurerm_network_interface" "test%[2]d" { + name = "acctestnic%[2]d-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + ip_configuration { + name = "internal" + subnet_id = azurerm_subnet.test%d.id + private_ip_address_allocation = "Dynamic" + } +} - resp, err := conn.Get(ctx, resourceGroup, name) - if err != nil { - return nil - } - if resp.StatusCode != http.StatusNotFound { - return fmt.Errorf("Metric alert still exists:\n%#v", resp) - } - } - return nil +resource "azurerm_linux_virtual_machine" "test%[2]d" { + name = "acctestVM%[2]d-%[1]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_F2" + admin_username = "adminuser" + admin_password = "P@$$w0rd1234!" + disable_password_authentication = false + network_interface_ids = [ + azurerm_network_interface.test%d.id, + ] + + os_disk { + caching = "ReadWrite" + storage_account_type = "Standard_LRS" + } + + source_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } +} +`, data.RandomInteger, n, n, n) } -func testCheckAzureRMMonitorMetricAlertExists(resourceName string) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := acceptance.AzureProvider.Meta().(*clients.Client).Monitor.MetricAlertsClient - ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext +func testAccAzureRMMonitorMetricAlert_multiVMTemplate(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} - // Ensure we have enough information in state to look up in API - rs, ok := s.RootModule().Resources[resourceName] - if !ok { - return fmt.Errorf("Not found: %s", resourceName) - } +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} - name := rs.Primary.Attributes["name"] - resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] - if !hasResourceGroup { - return fmt.Errorf("Bad: no resource group found in state for Metric Alert Instance: %s", name) - } +resource "azurerm_virtual_network" "test" { + name = "acctestnw-%d" + address_space = ["10.0.0.0/16"] + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} - resp, err := conn.Get(ctx, resourceGroup, name) - if err != nil { - return fmt.Errorf("Bad: Get on monitorMetricAlertsClient: %+v", err) - } - if resp.StatusCode == http.StatusNotFound { - return fmt.Errorf("Bad: Metric Alert Instance %q (resource group: %q) does not exist", name, resourceGroup) - } +%s - return nil - } +%s +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, + testAccAzureRMMonitorMetricAlert_vmTemplate(data, 1), + testAccAzureRMMonitorMetricAlert_vmTemplate(data, 2), + ) +} + +func testAccAzureRMMonitorMetricAlert_multiScope(data acceptance.TestData, scopes ...string) string { + return fmt.Sprintf(` +%s + +resource "azurerm_monitor_metric_alert" "test" { + name = "acctestMetricAlert-%d" + resource_group_name = azurerm_resource_group.test.name + scopes = [ + %s + ] + dynamic_criteria { + metric_namespace = "Microsoft.Compute/virtualMachines" + metric_name = "Network In" + aggregation = "Total" + + operator = "GreaterOrLessThan" + alert_sensitivity = "Medium" + } + window_size = "PT1H" + frequency = "PT5M" + target_resource_type = "Microsoft.Compute/virtualMachines" + target_resource_location = "%s" +} +`, testAccAzureRMMonitorMetricAlert_multiVMTemplate(data), data.RandomInteger, strings.Join(scopes, ",\n"), data.Locations.Primary) +} + +func testAccAzureRMMonitorMetricAlert_vmScopeDynamicCriteria(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctestnw-%d" + address_space = ["10.0.0.0/16"] + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +%s + +resource "azurerm_monitor_metric_alert" "test" { + name = "acctestMetricAlert-%d" + resource_group_name = azurerm_resource_group.test.name + scopes = [azurerm_linux_virtual_machine.test1.id] + dynamic_criteria { + metric_namespace = "Microsoft.Compute/virtualMachines" + metric_name = "Network In" + aggregation = "Total" + + operator = "GreaterOrLessThan" + alert_sensitivity = "Medium" + } + window_size = "PT1H" + frequency = "PT5M" + target_resource_type = "Microsoft.Compute/virtualMachines" + target_resource_location = "eastus" +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, testAccAzureRMMonitorMetricAlert_vmTemplate(data, 1), data.RandomInteger) } diff --git a/website/docs/r/monitor_metric_alert.html.markdown b/website/docs/r/monitor_metric_alert.html.markdown index 76f087058bab..d484a48d6ea1 100644 --- a/website/docs/r/monitor_metric_alert.html.markdown +++ b/website/docs/r/monitor_metric_alert.html.markdown @@ -70,13 +70,20 @@ The following arguments are supported: * `name` - (Required) The name of the Metric Alert. Changing this forces a new resource to be created. * `resource_group_name` - (Required) The name of the resource group in which to create the Metric Alert instance. * `scopes` - (Required) A set of strings of resource IDs at which the metric criteria should be applied. -* `criteria` - (Required) One or more `criteria` blocks as defined below. +* `criteria` - (Optional) One or more (static) `criteria` blocks as defined below. +* `dynamic_criteria` - (Optional) One or more `dynamic_criteria` blocks as defined below. +* `webtest_location_availability_criteria` - (Optional) One or more `webtest_location_availability_criteria` blocks as defined below. + +-> **NOTE** At least one of `criteria`, `dynamic_criteria` or `webtest_location_availability_criteria` need to be specified. + * `action` - (Optional) One or more `action` blocks as defined below. * `enabled` - (Optional) Should this Metric Alert be enabled? Defaults to `true`. * `auto_mitigate` - (Optional) Should the alerts in this Metric Alert be auto resolved? Defaults to `true`. * `description` - (Optional) The description of this Metric Alert. * `frequency` - (Optional) The evaluation frequency of this Metric Alert, represented in ISO 8601 duration format. Possible values are `PT1M`, `PT5M`, `PT15M`, `PT30M` and `PT1H`. Defaults to `PT1M`. * `severity` - (Optional) The severity of this Metric Alert. Possible values are `0`, `1`, `2`, `3` and `4`. Defaults to `3`. +* `target_resource_type` - (Optional) The resource type (e.g. `Microsoft.Compute/virtualMachines`) of the target resource. Required when using subscription scope, resource group scope or multiple scopes. +* `target_resource_location` - (Optional) The location of the target resource. Required when using subscription scope, resource group scope or multiple scopes. * `window_size` - (Optional) The period of time that is used to monitor alert activity, represented in ISO 8601 duration format. This value must be greater than `frequency`. Possible values are `PT1M`, `PT5M`, `PT15M`, `PT30M`, `PT1H`, `PT6H`, `PT12H` and `P1D`. Defaults to `PT5M`. * `tags` - (Optional) A mapping of tags to assign to the resource. @@ -100,6 +107,28 @@ A `criteria` block supports the following: --- +A `dynamic_criteria` block supports the following: + +* `metric_namespace` - (Required) One of the metric namespaces to be monitored. +* `metric_name` - (Required) One of the metric names to be monitored. +* `aggregation` - (Required) The statistic that runs over the metric values. Possible values are `Average`, `Count`, `Minimum`, `Maximum` and `Total`. +* `operator` - (Required) The criteria operator. Possible values are `LessThan`, `GreaterThan` and `GreaterOrLessThan`. +* `alert_sensitivity` - (Required) The extent of deviation required to trigger an alert. Possible values are `Low`, `Medium` and `High`. +* `dimension` - (Optional) One or more `dimension` blocks as defined below. +* `evaluation_total_account` - (Optional) The number of aggregated lookback points. The lookback time window is calculated based on the aggregation granularity (`window_size`) and the selected number of aggregated points. +* `evaluation_failure_count` - (Optional) The number of violations to trigger an alert. Should be smaller or equal to `evaluation_total_account`. +* `ignore_data_before` - (Optional) The [ISO8601](https://en.wikipedia.org/wiki/ISO_8601) date from which to start learning the metric historical data and calculate the dynamic thresholds. + +--- + +A `webtest_location_availability_criteria` block supports the following: + +* `webtest_id` - (Required) The ID of the Application Insights Web Test. +* `component_id` - (Required) The ID of the Application Insights Resource. +* `failed_location_count` - (Required) The number of failed locations. + +--- + A `dimension` block supports the following: * `name` - (Required) One of the dimension names. From b30f6ce0f3c4e672935fb009729f4fda3b34f1c0 Mon Sep 17 00:00:00 2001 From: magodo Date: Sun, 31 May 2020 13:04:17 +0800 Subject: [PATCH 2/9] Fix CI --- .../monitor/monitor_metric_alert_resource.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/azurerm/internal/services/monitor/monitor_metric_alert_resource.go b/azurerm/internal/services/monitor/monitor_metric_alert_resource.go index a621f8428369..841537eb96ea 100644 --- a/azurerm/internal/services/monitor/monitor_metric_alert_resource.go +++ b/azurerm/internal/services/monitor/monitor_metric_alert_resource.go @@ -453,6 +453,7 @@ func resourceArmMonitorMetricAlertRead(d *schema.ResourceData, meta interface{}) case insights.WebtestLocationAvailabilityCriteria: criteriaSchema = "webtest_location_availability_criteria" } + // lintignore:R001 if err := d.Set(criteriaSchema, flattenMonitorMetricAlertCriteria(alert.Criteria)); err != nil { return fmt.Errorf("Error setting `%s`: %+v", criteriaSchema, err) } @@ -502,11 +503,11 @@ func expandMonitorMetricAlertCriteria(d *schema.ResourceData) insights.BasicMetr } func expandMonitorMetricAlertMetricCriteria(input []interface{}) insights.BasicMetricAlertCriteria { - criterias := make([]insights.BasicMultiMetricCriteria, 0) + criteria := make([]insights.BasicMultiMetricCriteria, 0) for i, item := range input { v := item.(map[string]interface{}) dimensions := expandMonitorMetricAlertMultiMetricCriteriaDimension(v["dimension"].([]interface{})) - criterias = append(criterias, insights.MetricCriteria{ + criteria = append(criteria, insights.MetricCriteria{ Name: utils.String(fmt.Sprintf("Metric%d", i+1)), MetricNamespace: utils.String(v["metric_namespace"].(string)), MetricName: utils.String(v["metric_name"].(string)), @@ -517,12 +518,12 @@ func expandMonitorMetricAlertMetricCriteria(input []interface{}) insights.BasicM }) } return &insights.MetricAlertMultipleResourceMultipleMetricCriteria{ - AllOf: &criterias, + AllOf: &criteria, OdataType: insights.OdataTypeMicrosoftAzureMonitorMultipleResourceMultipleMetricCriteria, } } func expandMonitorMetricAlertDynamicMetricCriteria(input []interface{}) insights.BasicMetricAlertCriteria { - criterias := make([]insights.BasicMultiMetricCriteria, 0) + criteria := make([]insights.BasicMultiMetricCriteria, 0) for i, item := range input { v := item.(map[string]interface{}) dimensions := expandMonitorMetricAlertMultiMetricCriteriaDimension(v["dimension"].([]interface{})) @@ -532,7 +533,7 @@ func expandMonitorMetricAlertDynamicMetricCriteria(input []interface{}) insights t, _ := time.Parse(time.RFC3339, v) ignoreDataBefore = &date.Time{Time: t} } - criterias = append(criterias, insights.DynamicMetricCriteria{ + criteria = append(criteria, insights.DynamicMetricCriteria{ Name: utils.String(fmt.Sprintf("Metric%d", i+1)), MetricNamespace: utils.String(v["metric_namespace"].(string)), MetricName: utils.String(v["metric_name"].(string)), @@ -548,7 +549,7 @@ func expandMonitorMetricAlertDynamicMetricCriteria(input []interface{}) insights }) } return &insights.MetricAlertMultipleResourceMultipleMetricCriteria{ - AllOf: &criterias, + AllOf: &criteria, OdataType: insights.OdataTypeMicrosoftAzureMonitorMultipleResourceMultipleMetricCriteria, } } From fa94069c01a06f2deeea0dae4333a45eaced1439 Mon Sep 17 00:00:00 2001 From: magodo Date: Mon, 1 Jun 2020 17:18:34 +0800 Subject: [PATCH 3/9] resolve review comment --- .../monitor/monitor_metric_alert_resource.go | 25 +-- .../monitor_metric_alert_resource_test.go | 155 +++++++++--------- .../docs/r/monitor_metric_alert.html.markdown | 6 +- 3 files changed, 96 insertions(+), 90 deletions(-) diff --git a/azurerm/internal/services/monitor/monitor_metric_alert_resource.go b/azurerm/internal/services/monitor/monitor_metric_alert_resource.go index 841537eb96ea..7b9af16c393e 100644 --- a/azurerm/internal/services/monitor/monitor_metric_alert_resource.go +++ b/azurerm/internal/services/monitor/monitor_metric_alert_resource.go @@ -24,7 +24,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) -func monitorMetricAlertMultiMetricCriteriaSchema(override map[string]*schema.Schema) map[string]*schema.Schema { +func buildMonitorMetricAlertMultiMetricCriteriaSchema(extra map[string]*schema.Schema) map[string]*schema.Schema { base := map[string]*schema.Schema{ "metric_namespace": { Type: schema.TypeString, @@ -77,14 +77,14 @@ func monitorMetricAlertMultiMetricCriteriaSchema(override map[string]*schema.Sch }, }, } - for k, v := range override { + for k, v := range extra { base[k] = v } return base } var ( - monitorMetricAlertStaticMetricCriteriaSchema = monitorMetricAlertMultiMetricCriteriaSchema( + monitorMetricAlertStaticMetricCriteriaSchema = buildMonitorMetricAlertMultiMetricCriteriaSchema( map[string]*schema.Schema{ "operator": { Type: schema.TypeString, @@ -104,7 +104,7 @@ var ( }, }, ) - monitorMetricAlertDynamicMetricCriteriaSchema = monitorMetricAlertMultiMetricCriteriaSchema( + monitorMetricAlertDynamicMetricCriteriaSchema = buildMonitorMetricAlertMultiMetricCriteriaSchema( map[string]*schema.Schema{ "operator": { Type: schema.TypeString, @@ -208,17 +208,19 @@ func resourceArmMonitorMetricAlert() *schema.Resource { Type: schema.TypeSet, Optional: true, MinItems: 1, - AtLeastOneOf: []string{"dynamic_criteria", "webtest_location_availability_criteria"}, + ExactlyOneOf: []string{"dynamic_criteria", "webtest_location_availability_criteria"}, Elem: &schema.Resource{ Schema: monitorMetricAlertStaticMetricCriteriaSchema, }, }, "dynamic_criteria": { - Type: schema.TypeSet, - Optional: true, - MinItems: 1, - AtLeastOneOf: []string{"criteria", "webtest_location_availability_criteria"}, + Type: schema.TypeSet, + Optional: true, + MinItems: 1, + // Curently, it allows to define only one dynamic criteria in one metric alert. + MaxItems: 1, + ExactlyOneOf: []string{"criteria", "webtest_location_availability_criteria"}, Elem: &schema.Resource{ Schema: monitorMetricAlertDynamicMetricCriteriaSchema, }, @@ -229,7 +231,7 @@ func resourceArmMonitorMetricAlert() *schema.Resource { Optional: true, MinItems: 1, MaxItems: 1, - AtLeastOneOf: []string{"criteria", "dynamic_criteria"}, + ExactlyOneOf: []string{"criteria", "dynamic_criteria"}, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "webtest_id": { @@ -442,8 +444,9 @@ func resourceArmMonitorMetricAlertRead(d *schema.ResourceData, meta interface{}) criteriaSchema = "criteria" case insights.MetricAlertMultipleResourceMultipleMetricCriteria: if c.AllOf == nil || len(*c.AllOf) == 0 { - return errors.New("nil or empty contained criterias of MultipleResourceMultipleMetricCriteria") + return errors.New("nil or empty contained criteria of MultipleResourceMultipleMetricCriteria") } + // `MinItems` defined in schema guaranteed there is at least one element. switch (*c.AllOf)[0].(type) { case insights.DynamicMetricCriteria: criteriaSchema = "dynamic_criteria" diff --git a/azurerm/internal/services/monitor/tests/monitor_metric_alert_resource_test.go b/azurerm/internal/services/monitor/tests/monitor_metric_alert_resource_test.go index dee0b1de33e5..fdab37be4253 100644 --- a/azurerm/internal/services/monitor/tests/monitor_metric_alert_resource_test.go +++ b/azurerm/internal/services/monitor/tests/monitor_metric_alert_resource_test.go @@ -3,7 +3,6 @@ package tests import ( "fmt" "net/http" - "strings" "testing" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" @@ -111,21 +110,21 @@ func TestAccAzureRMMonitorMetricAlert_multiScope(t *testing.T) { CheckDestroy: testCheckAzureRMMonitorMetricAlertDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMMonitorMetricAlert_multiScope(data, "azurerm_linux_virtual_machine.test1.id", "azurerm_linux_virtual_machine.test2.id"), + Config: testAccAzureRMMonitorMetricAlert_multiScope(data, true), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMonitorMetricAlertExists(data.ResourceName), ), }, data.ImportStep(), { - Config: testAccAzureRMMonitorMetricAlert_multiScope(data, "azurerm_linux_virtual_machine.test1.id"), + Config: testAccAzureRMMonitorMetricAlert_multiScope(data, false), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMonitorMetricAlertExists(data.ResourceName), ), }, data.ImportStep(), { - Config: testAccAzureRMMonitorMetricAlert_multiScope(data, "azurerm_linux_virtual_machine.test1.id", "azurerm_linux_virtual_machine.test2.id"), + Config: testAccAzureRMMonitorMetricAlert_multiScope(data, true), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMonitorMetricAlertExists(data.ResourceName), ), @@ -312,28 +311,47 @@ resource "azurerm_monitor_metric_alert" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomString) } -func testAccAzureRMMonitorMetricAlert_vmTemplate(data acceptance.TestData, n int) string { +func testAccAzureRMMonitorMetricAlert_multiVMTemplate(data acceptance.TestData) string { return fmt.Sprintf(` -resource "azurerm_subnet" "test%[2]d" { - name = "internal%[2]d" +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "%[2]s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctestnw-%[1]d" + address_space = ["10.0.0.0/16"] + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +####################### +# VM - 1 +####################### +resource "azurerm_subnet" "test1" { + name = "internal1" resource_group_name = azurerm_resource_group.test.name virtual_network_name = azurerm_virtual_network.test.name - address_prefix = "10.0.%[2]d.0/24" + address_prefix = "10.0.1.0/24" } -resource "azurerm_network_interface" "test%[2]d" { - name = "acctestnic%[2]d-%[1]d" +resource "azurerm_network_interface" "test1" { + name = "acctestnic%[1]d-1" location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name ip_configuration { name = "internal" - subnet_id = azurerm_subnet.test%d.id + subnet_id = azurerm_subnet.test1.id private_ip_address_allocation = "Dynamic" } } -resource "azurerm_linux_virtual_machine" "test%[2]d" { - name = "acctestVM%[2]d-%[1]d" +resource "azurerm_linux_virtual_machine" "test1" { + name = "acctestVM1-%[1]d" resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location size = "Standard_F2" @@ -341,7 +359,7 @@ resource "azurerm_linux_virtual_machine" "test%[2]d" { admin_password = "P@$$w0rd1234!" disable_password_authentication = false network_interface_ids = [ - azurerm_network_interface.test%d.id, + azurerm_network_interface.test1.id, ] os_disk { @@ -356,86 +374,71 @@ resource "azurerm_linux_virtual_machine" "test%[2]d" { version = "latest" } } -`, data.RandomInteger, n, n, n) -} - -func testAccAzureRMMonitorMetricAlert_multiVMTemplate(data acceptance.TestData) string { - return fmt.Sprintf(` -provider "azurerm" { - features {} -} -resource "azurerm_resource_group" "test" { - name = "acctestRG-%d" - location = "%s" +####################### +# VM - 2 +####################### +resource "azurerm_subnet" "test2" { + name = "internal2" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefix = "10.0.2.0/24" } -resource "azurerm_virtual_network" "test" { - name = "acctestnw-%d" - address_space = ["10.0.0.0/16"] +resource "azurerm_network_interface" "test2" { + name = "acctestnic%[1]d-2" location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name + ip_configuration { + name = "internal" + subnet_id = azurerm_subnet.test2.id + private_ip_address_allocation = "Dynamic" + } } -%s - -%s -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, - testAccAzureRMMonitorMetricAlert_vmTemplate(data, 1), - testAccAzureRMMonitorMetricAlert_vmTemplate(data, 2), - ) -} - -func testAccAzureRMMonitorMetricAlert_multiScope(data acceptance.TestData, scopes ...string) string { - return fmt.Sprintf(` -%s - -resource "azurerm_monitor_metric_alert" "test" { - name = "acctestMetricAlert-%d" - resource_group_name = azurerm_resource_group.test.name - scopes = [ - %s +resource "azurerm_linux_virtual_machine" "test2" { + name = "acctestVM2-%[1]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_F2" + admin_username = "adminuser" + admin_password = "P@$$w0rd1234!" + disable_password_authentication = false + network_interface_ids = [ + azurerm_network_interface.test2.id, ] - dynamic_criteria { - metric_namespace = "Microsoft.Compute/virtualMachines" - metric_name = "Network In" - aggregation = "Total" - operator = "GreaterOrLessThan" - alert_sensitivity = "Medium" + os_disk { + caching = "ReadWrite" + storage_account_type = "Standard_LRS" } - window_size = "PT1H" - frequency = "PT5M" - target_resource_type = "Microsoft.Compute/virtualMachines" - target_resource_location = "%s" -} -`, testAccAzureRMMonitorMetricAlert_multiVMTemplate(data), data.RandomInteger, strings.Join(scopes, ",\n"), data.Locations.Primary) -} -func testAccAzureRMMonitorMetricAlert_vmScopeDynamicCriteria(data acceptance.TestData) string { - return fmt.Sprintf(` -provider "azurerm" { - features {} + source_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } } - -resource "azurerm_resource_group" "test" { - name = "acctestRG-%d" - location = "%s" +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) } -resource "azurerm_virtual_network" "test" { - name = "acctestnw-%d" - address_space = ["10.0.0.0/16"] - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name -} +func testAccAzureRMMonitorMetricAlert_multiScope(data acceptance.TestData, multiScope bool) string { + scope := ` +azurerm_linux_virtual_machine.test1.id, +azurerm_linux_virtual_machine.test2.id, +` + if !multiScope { + scope = "azurerm_linux_virtual_machine.test1.id" + } + return fmt.Sprintf(` %s resource "azurerm_monitor_metric_alert" "test" { name = "acctestMetricAlert-%d" resource_group_name = azurerm_resource_group.test.name - scopes = [azurerm_linux_virtual_machine.test1.id] + scopes = [%s] dynamic_criteria { metric_namespace = "Microsoft.Compute/virtualMachines" metric_name = "Network In" @@ -447,7 +450,7 @@ resource "azurerm_monitor_metric_alert" "test" { window_size = "PT1H" frequency = "PT5M" target_resource_type = "Microsoft.Compute/virtualMachines" - target_resource_location = "eastus" + target_resource_location = "%s" } -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, testAccAzureRMMonitorMetricAlert_vmTemplate(data, 1), data.RandomInteger) +`, testAccAzureRMMonitorMetricAlert_multiVMTemplate(data), data.RandomInteger, scope, data.Locations.Primary) } diff --git a/website/docs/r/monitor_metric_alert.html.markdown b/website/docs/r/monitor_metric_alert.html.markdown index d484a48d6ea1..42ce5e4f59b1 100644 --- a/website/docs/r/monitor_metric_alert.html.markdown +++ b/website/docs/r/monitor_metric_alert.html.markdown @@ -71,10 +71,10 @@ The following arguments are supported: * `resource_group_name` - (Required) The name of the resource group in which to create the Metric Alert instance. * `scopes` - (Required) A set of strings of resource IDs at which the metric criteria should be applied. * `criteria` - (Optional) One or more (static) `criteria` blocks as defined below. -* `dynamic_criteria` - (Optional) One or more `dynamic_criteria` blocks as defined below. -* `webtest_location_availability_criteria` - (Optional) One or more `webtest_location_availability_criteria` blocks as defined below. +* `dynamic_criteria` - (Optional) A `dynamic_criteria` block as defined below. +* `webtest_location_availability_criteria` - (Optional) A `webtest_location_availability_criteria` block as defined below. --> **NOTE** At least one of `criteria`, `dynamic_criteria` or `webtest_location_availability_criteria` need to be specified. +-> **NOTE** One and exactly one of `criteria`, `dynamic_criteria` or `webtest_location_availability_criteria` must to be specified. * `action` - (Optional) One or more `action` blocks as defined below. * `enabled` - (Optional) Should this Metric Alert be enabled? Defaults to `true`. From f5a49aa48179bc52c26f5f32f62060d9ab9e15ca Mon Sep 17 00:00:00 2001 From: magodo Date: Fri, 19 Jun 2020 16:35:48 +0800 Subject: [PATCH 4/9] typo --- website/docs/r/monitor_metric_alert.html.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/r/monitor_metric_alert.html.markdown b/website/docs/r/monitor_metric_alert.html.markdown index 42ce5e4f59b1..a36687de76cb 100644 --- a/website/docs/r/monitor_metric_alert.html.markdown +++ b/website/docs/r/monitor_metric_alert.html.markdown @@ -115,8 +115,8 @@ A `dynamic_criteria` block supports the following: * `operator` - (Required) The criteria operator. Possible values are `LessThan`, `GreaterThan` and `GreaterOrLessThan`. * `alert_sensitivity` - (Required) The extent of deviation required to trigger an alert. Possible values are `Low`, `Medium` and `High`. * `dimension` - (Optional) One or more `dimension` blocks as defined below. -* `evaluation_total_account` - (Optional) The number of aggregated lookback points. The lookback time window is calculated based on the aggregation granularity (`window_size`) and the selected number of aggregated points. -* `evaluation_failure_count` - (Optional) The number of violations to trigger an alert. Should be smaller or equal to `evaluation_total_account`. +* `evaluation_total_count` - (Optional) The number of aggregated lookback points. The lookback time window is calculated based on the aggregation granularity (`window_size`) and the selected number of aggregated points. +* `evaluation_failure_count` - (Optional) The number of violations to trigger an alert. Should be smaller or equal to `evaluation_total_count`. * `ignore_data_before` - (Optional) The [ISO8601](https://en.wikipedia.org/wiki/ISO_8601) date from which to start learning the metric historical data and calculate the dynamic thresholds. --- From d7e49cb93c50fdd4dd6c681d3ffc5643bd542196 Mon Sep 17 00:00:00 2001 From: magodo Date: Sat, 11 Jul 2020 22:03:58 +0800 Subject: [PATCH 5/9] modify per review --- .../monitor/monitor_metric_alert_resource.go | 300 ++++++++++-------- .../monitor_metric_alert_resource_test.go | 94 ++---- 2 files changed, 188 insertions(+), 206 deletions(-) diff --git a/azurerm/internal/services/monitor/monitor_metric_alert_resource.go b/azurerm/internal/services/monitor/monitor_metric_alert_resource.go index 7b9af16c393e..ec51a08a302e 100644 --- a/azurerm/internal/services/monitor/monitor_metric_alert_resource.go +++ b/azurerm/internal/services/monitor/monitor_metric_alert_resource.go @@ -24,130 +24,6 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) -func buildMonitorMetricAlertMultiMetricCriteriaSchema(extra map[string]*schema.Schema) map[string]*schema.Schema { - base := map[string]*schema.Schema{ - "metric_namespace": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringIsNotEmpty, - }, - "metric_name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringIsNotEmpty, - }, - "aggregation": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - "Average", - "Count", - "Minimum", - "Maximum", - "Total", - }, false), - }, - "dimension": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringIsNotEmpty, - }, - "operator": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - "Include", - "Exclude", - }, false), - }, - "values": { - Type: schema.TypeList, - Required: true, - MinItems: 1, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - }, - }, - }, - } - for k, v := range extra { - base[k] = v - } - return base -} - -var ( - monitorMetricAlertStaticMetricCriteriaSchema = buildMonitorMetricAlertMultiMetricCriteriaSchema( - map[string]*schema.Schema{ - "operator": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - string(insights.OperatorEquals), - string(insights.OperatorGreaterThan), - string(insights.OperatorGreaterThanOrEqual), - string(insights.OperatorLessThan), - string(insights.OperatorLessThanOrEqual), - string(insights.OperatorNotEquals), - }, false), - }, - "threshold": { - Type: schema.TypeFloat, - Required: true, - }, - }, - ) - monitorMetricAlertDynamicMetricCriteriaSchema = buildMonitorMetricAlertMultiMetricCriteriaSchema( - map[string]*schema.Schema{ - "operator": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - string(insights.DynamicThresholdOperatorLessThan), - string(insights.DynamicThresholdOperatorGreaterThan), - string(insights.DynamicThresholdOperatorGreaterOrLessThan), - }, false), - }, - "alert_sensitivity": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - string(insights.Low), - string(insights.Medium), - string(insights.High), - }, false), - }, - - "evaluation_total_count": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntAtLeast(1), - Default: 4, - }, - - "evaluation_failure_count": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntAtLeast(1), - Default: 4, - }, - - "ignore_data_before": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validation.IsRFC3339Time, - }, - }, - ) -) - func resourceArmMonitorMetricAlert() *schema.Resource { return &schema.Resource{ Create: resourceArmMonitorMetricAlertCreateUpdate, @@ -210,7 +86,74 @@ func resourceArmMonitorMetricAlert() *schema.Resource { MinItems: 1, ExactlyOneOf: []string{"dynamic_criteria", "webtest_location_availability_criteria"}, Elem: &schema.Resource{ - Schema: monitorMetricAlertStaticMetricCriteriaSchema, + Schema: map[string]*schema.Schema{ + "metric_namespace": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "metric_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "aggregation": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "Average", + "Count", + "Minimum", + "Maximum", + "Total", + }, false), + }, + "dimension": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "operator": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "Include", + "Exclude", + }, false), + }, + "values": { + Type: schema.TypeList, + Required: true, + MinItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "operator": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(insights.OperatorEquals), + string(insights.OperatorGreaterThan), + string(insights.OperatorGreaterThanOrEqual), + string(insights.OperatorLessThan), + string(insights.OperatorLessThanOrEqual), + string(insights.OperatorNotEquals), + }, false), + }, + "threshold": { + Type: schema.TypeFloat, + Required: true, + }, + }, }, }, @@ -222,7 +165,96 @@ func resourceArmMonitorMetricAlert() *schema.Resource { MaxItems: 1, ExactlyOneOf: []string{"criteria", "webtest_location_availability_criteria"}, Elem: &schema.Resource{ - Schema: monitorMetricAlertDynamicMetricCriteriaSchema, + Schema: map[string]*schema.Schema{ + "metric_namespace": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "metric_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "aggregation": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "Average", + "Count", + "Minimum", + "Maximum", + "Total", + }, false), + }, + "dimension": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "operator": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "Include", + "Exclude", + }, false), + }, + "values": { + Type: schema.TypeList, + Required: true, + MinItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "operator": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(insights.DynamicThresholdOperatorLessThan), + string(insights.DynamicThresholdOperatorGreaterThan), + string(insights.DynamicThresholdOperatorGreaterOrLessThan), + }, false), + }, + "alert_sensitivity": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(insights.Low), + string(insights.Medium), + string(insights.High), + }, false), + }, + + "evaluation_total_count": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntAtLeast(1), + Default: 4, + }, + + "evaluation_failure_count": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntAtLeast(1), + Default: 4, + }, + + "ignore_data_before": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.IsRFC3339Time, + }, + }, }, }, @@ -368,6 +400,10 @@ func resourceArmMonitorMetricAlertCreateUpdate(d *schema.ResourceData, meta inte t := d.Get("tags").(map[string]interface{}) expandedTags := tags.Expand(t) + criteria, err := expandMonitorMetricAlertCriteria(d) + if err != nil { + return fmt.Errorf(`Expandding criteria: %+v`, err) + } parameters := insights.MetricAlertResource{ Location: utils.String(azure.NormalizeLocation("Global")), MetricAlertProperties: &insights.MetricAlertProperties{ @@ -378,7 +414,7 @@ func resourceArmMonitorMetricAlertCreateUpdate(d *schema.ResourceData, meta inte EvaluationFrequency: utils.String(frequency), WindowSize: utils.String(windowSize), Scopes: utils.ExpandStringSlice(scopesRaw), - Criteria: expandMonitorMetricAlertCriteria(d), + Criteria: criteria, Actions: expandMonitorMetricAlertAction(actionRaw), TargetResourceType: utils.String(targetResourceType), TargetResourceRegion: utils.String(targetResourceLocation), @@ -491,17 +527,17 @@ func resourceArmMonitorMetricAlertDelete(d *schema.ResourceData, meta interface{ return nil } -func expandMonitorMetricAlertCriteria(d *schema.ResourceData) insights.BasicMetricAlertCriteria { +func expandMonitorMetricAlertCriteria(d *schema.ResourceData) (insights.BasicMetricAlertCriteria, error) { switch { case d.Get("criteria").(*schema.Set).Len() != 0: - return expandMonitorMetricAlertMetricCriteria(d.Get("criteria").(*schema.Set).List()) + return expandMonitorMetricAlertMetricCriteria(d.Get("criteria").(*schema.Set).List()), nil case d.Get("dynamic_criteria").(*schema.Set).Len() != 0: - return expandMonitorMetricAlertDynamicMetricCriteria(d.Get("dynamic_criteria").(*schema.Set).List()) + return expandMonitorMetricAlertDynamicMetricCriteria(d.Get("dynamic_criteria").(*schema.Set).List()), nil case d.Get("webtest_location_availability_criteria").(*schema.Set).Len() != 0: - return expandMonitorMetricAlertWebtestLocAvailCriteria(d.Get("webtest_location_availability_criteria").([]interface{})) + return expandMonitorMetricAlertWebtestLocAvailCriteria(d.Get("webtest_location_availability_criteria").([]interface{})), nil default: // Guaranteed by schema `AtLeastOne` constraint - panic("will never happen") + return nil, errors.New("unknwon criteria type") } } diff --git a/azurerm/internal/services/monitor/tests/monitor_metric_alert_resource_test.go b/azurerm/internal/services/monitor/tests/monitor_metric_alert_resource_test.go index fdab37be4253..e69bef747a39 100644 --- a/azurerm/internal/services/monitor/tests/monitor_metric_alert_resource_test.go +++ b/azurerm/internal/services/monitor/tests/monitor_metric_alert_resource_test.go @@ -110,21 +110,21 @@ func TestAccAzureRMMonitorMetricAlert_multiScope(t *testing.T) { CheckDestroy: testCheckAzureRMMonitorMetricAlertDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMMonitorMetricAlert_multiScope(data, true), + Config: testAccAzureRMMonitorMetricAlert_multiScope(data, 2), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMonitorMetricAlertExists(data.ResourceName), ), }, data.ImportStep(), { - Config: testAccAzureRMMonitorMetricAlert_multiScope(data, false), + Config: testAccAzureRMMonitorMetricAlert_multiScope(data, 1), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMonitorMetricAlertExists(data.ResourceName), ), }, data.ImportStep(), { - Config: testAccAzureRMMonitorMetricAlert_multiScope(data, true), + Config: testAccAzureRMMonitorMetricAlert_multiScope(data, 2), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMonitorMetricAlertExists(data.ResourceName), ), @@ -311,7 +311,7 @@ resource "azurerm_monitor_metric_alert" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomString) } -func testAccAzureRMMonitorMetricAlert_multiVMTemplate(data acceptance.TestData) string { +func testAccAzureRMMonitorMetricAlert_multiVMTemplate(data acceptance.TestData, count int) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -329,29 +329,29 @@ resource "azurerm_virtual_network" "test" { resource_group_name = azurerm_resource_group.test.name } -####################### -# VM - 1 -####################### -resource "azurerm_subnet" "test1" { - name = "internal1" +resource "azurerm_subnet" "test" { + count = %[3]d + name = "internal-${count.index}" resource_group_name = azurerm_resource_group.test.name virtual_network_name = azurerm_virtual_network.test.name - address_prefix = "10.0.1.0/24" + address_prefix = "10.0.${count.index}.0/24" } -resource "azurerm_network_interface" "test1" { - name = "acctestnic%[1]d-1" +resource "azurerm_network_interface" "test" { + count = %[3]d + name = "acctestnic-${count.index}" location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name ip_configuration { name = "internal" - subnet_id = azurerm_subnet.test1.id + subnet_id = azurerm_subnet.test[count.index].id private_ip_address_allocation = "Dynamic" } } -resource "azurerm_linux_virtual_machine" "test1" { - name = "acctestVM1-%[1]d" +resource "azurerm_linux_virtual_machine" "test" { + count = %[3]d + name = "acctestVM-${count.index}" resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location size = "Standard_F2" @@ -359,7 +359,7 @@ resource "azurerm_linux_virtual_machine" "test1" { admin_password = "P@$$w0rd1234!" disable_password_authentication = false network_interface_ids = [ - azurerm_network_interface.test1.id, + azurerm_network_interface.test[count.index].id, ] os_disk { @@ -374,71 +374,17 @@ resource "azurerm_linux_virtual_machine" "test1" { version = "latest" } } - -####################### -# VM - 2 -####################### -resource "azurerm_subnet" "test2" { - name = "internal2" - resource_group_name = azurerm_resource_group.test.name - virtual_network_name = azurerm_virtual_network.test.name - address_prefix = "10.0.2.0/24" -} - -resource "azurerm_network_interface" "test2" { - name = "acctestnic%[1]d-2" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - ip_configuration { - name = "internal" - subnet_id = azurerm_subnet.test2.id - private_ip_address_allocation = "Dynamic" - } -} - -resource "azurerm_linux_virtual_machine" "test2" { - name = "acctestVM2-%[1]d" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location - size = "Standard_F2" - admin_username = "adminuser" - admin_password = "P@$$w0rd1234!" - disable_password_authentication = false - network_interface_ids = [ - azurerm_network_interface.test2.id, - ] - - os_disk { - caching = "ReadWrite" - storage_account_type = "Standard_LRS" - } - - source_image_reference { - publisher = "Canonical" - offer = "UbuntuServer" - sku = "16.04-LTS" - version = "latest" - } -} -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +`, data.RandomInteger, data.Locations.Primary, count) } -func testAccAzureRMMonitorMetricAlert_multiScope(data acceptance.TestData, multiScope bool) string { - scope := ` -azurerm_linux_virtual_machine.test1.id, -azurerm_linux_virtual_machine.test2.id, -` - if !multiScope { - scope = "azurerm_linux_virtual_machine.test1.id" - } - +func testAccAzureRMMonitorMetricAlert_multiScope(data acceptance.TestData, count int) string { return fmt.Sprintf(` %s resource "azurerm_monitor_metric_alert" "test" { name = "acctestMetricAlert-%d" resource_group_name = azurerm_resource_group.test.name - scopes = [%s] + scopes = azurerm_linux_virtual_machine.test.*.id dynamic_criteria { metric_namespace = "Microsoft.Compute/virtualMachines" metric_name = "Network In" @@ -452,5 +398,5 @@ resource "azurerm_monitor_metric_alert" "test" { target_resource_type = "Microsoft.Compute/virtualMachines" target_resource_location = "%s" } -`, testAccAzureRMMonitorMetricAlert_multiVMTemplate(data), data.RandomInteger, scope, data.Locations.Primary) +`, testAccAzureRMMonitorMetricAlert_multiVMTemplate(data, count), data.RandomInteger, data.Locations.Primary) } From e407b98f4803676d0c7f0227a488b763dcbefb2a Mon Sep 17 00:00:00 2001 From: magodo Date: Wed, 15 Jul 2020 10:40:12 +0800 Subject: [PATCH 6/9] minor code/doc change --- .../monitor/monitor_metric_alert_resource.go | 7 +++++-- .../docs/r/monitor_metric_alert.html.markdown | 18 +++++++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/azurerm/internal/services/monitor/monitor_metric_alert_resource.go b/azurerm/internal/services/monitor/monitor_metric_alert_resource.go index ec51a08a302e..0b2b80c2862f 100644 --- a/azurerm/internal/services/monitor/monitor_metric_alert_resource.go +++ b/azurerm/internal/services/monitor/monitor_metric_alert_resource.go @@ -402,7 +402,7 @@ func resourceArmMonitorMetricAlertCreateUpdate(d *schema.ResourceData, meta inte criteria, err := expandMonitorMetricAlertCriteria(d) if err != nil { - return fmt.Errorf(`Expandding criteria: %+v`, err) + return fmt.Errorf(`Expanding criteria: %+v`, err) } parameters := insights.MetricAlertResource{ Location: utils.String(azure.NormalizeLocation("Global")), @@ -491,7 +491,10 @@ func resourceArmMonitorMetricAlertRead(d *schema.ResourceData, meta interface{}) } case insights.WebtestLocationAvailabilityCriteria: criteriaSchema = "webtest_location_availability_criteria" + default: + return fmt.Errorf("Unknown criteria type") } + // lintignore:R001 if err := d.Set(criteriaSchema, flattenMonitorMetricAlertCriteria(alert.Criteria)); err != nil { return fmt.Errorf("Error setting `%s`: %+v", criteriaSchema, err) @@ -639,7 +642,7 @@ func expandMonitorMetricAlertAction(input []interface{}) *[]insights.MetricAlert return &actions } -func flattenMonitorMetricAlertCriteria(input insights.BasicMetricAlertCriteria) (result []interface{}) { +func flattenMonitorMetricAlertCriteria(input insights.BasicMetricAlertCriteria) []interface{} { switch criteria := input.(type) { case insights.MetricAlertSingleResourceMultipleMetricCriteria: // As service is gonna deprecate data type of `MetricAlertSingleResourceMultipleMetricCriteria`, diff --git a/website/docs/r/monitor_metric_alert.html.markdown b/website/docs/r/monitor_metric_alert.html.markdown index a36687de76cb..15a0bf81fe5d 100644 --- a/website/docs/r/monitor_metric_alert.html.markdown +++ b/website/docs/r/monitor_metric_alert.html.markdown @@ -71,10 +71,16 @@ The following arguments are supported: * `resource_group_name` - (Required) The name of the resource group in which to create the Metric Alert instance. * `scopes` - (Required) A set of strings of resource IDs at which the metric criteria should be applied. * `criteria` - (Optional) One or more (static) `criteria` blocks as defined below. + +-> **NOTE** One of either `criteria`, `dynamic_criteria` or `webtest_location_availability_criteria` must be specified. + * `dynamic_criteria` - (Optional) A `dynamic_criteria` block as defined below. + +-> **NOTE** One of either `criteria`, `dynamic_criteria` or `webtest_location_availability_criteria` must be specified. + * `webtest_location_availability_criteria` - (Optional) A `webtest_location_availability_criteria` block as defined below. --> **NOTE** One and exactly one of `criteria`, `dynamic_criteria` or `webtest_location_availability_criteria` must to be specified. +-> **NOTE** One of either `criteria`, `dynamic_criteria` or `webtest_location_availability_criteria` must be specified. * `action` - (Optional) One or more `action` blocks as defined below. * `enabled` - (Optional) Should this Metric Alert be enabled? Defaults to `true`. @@ -82,8 +88,14 @@ The following arguments are supported: * `description` - (Optional) The description of this Metric Alert. * `frequency` - (Optional) The evaluation frequency of this Metric Alert, represented in ISO 8601 duration format. Possible values are `PT1M`, `PT5M`, `PT15M`, `PT30M` and `PT1H`. Defaults to `PT1M`. * `severity` - (Optional) The severity of this Metric Alert. Possible values are `0`, `1`, `2`, `3` and `4`. Defaults to `3`. -* `target_resource_type` - (Optional) The resource type (e.g. `Microsoft.Compute/virtualMachines`) of the target resource. Required when using subscription scope, resource group scope or multiple scopes. -* `target_resource_location` - (Optional) The location of the target resource. Required when using subscription scope, resource group scope or multiple scopes. +* `target_resource_type` - (Optional) The resource type (e.g. `Microsoft.Compute/virtualMachines`) of the target resource. + +-> This is Required when using a Subscription as scope, a Resource Group as scope or Multiple Scopes. + +* `target_resource_location` - (Optional) The location of the target resource. + +-> This is Required when using a Subscription as scope, a Resource Group as scope or Multiple Scopes. + * `window_size` - (Optional) The period of time that is used to monitor alert activity, represented in ISO 8601 duration format. This value must be greater than `frequency`. Possible values are `PT1M`, `PT5M`, `PT15M`, `PT30M`, `PT1H`, `PT6H`, `PT12H` and `P1D`. Defaults to `PT5M`. * `tags` - (Optional) A mapping of tags to assign to the resource. From 8369b4dbc06685abb6b1528ccb2f485bb7f58d3e Mon Sep 17 00:00:00 2001 From: magodo Date: Wed, 15 Jul 2020 12:25:40 +0800 Subject: [PATCH 7/9] Rename/complement test for application insight web test criteria --- .../parse/application_insights.go | 59 ++++++++ .../parse/application_insights_test.go | 139 ++++++++++++++++++ .../validate/application_insights.go | 37 +++++ .../monitor/monitor_metric_alert_resource.go | 24 +-- .../monitor_metric_alert_resource_test.go | 91 +++++++++++- .../docs/r/monitor_metric_alert.html.markdown | 12 +- 6 files changed, 342 insertions(+), 20 deletions(-) create mode 100644 azurerm/internal/services/applicationinsights/parse/application_insights.go create mode 100644 azurerm/internal/services/applicationinsights/parse/application_insights_test.go create mode 100644 azurerm/internal/services/applicationinsights/validate/application_insights.go diff --git a/azurerm/internal/services/applicationinsights/parse/application_insights.go b/azurerm/internal/services/applicationinsights/parse/application_insights.go new file mode 100644 index 000000000000..0953801e91f2 --- /dev/null +++ b/azurerm/internal/services/applicationinsights/parse/application_insights.go @@ -0,0 +1,59 @@ +package parse + +import ( + "fmt" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" +) + +type ApplicationInsightsId struct { + ResourceGroup string + Name string +} + +func ApplicationInsightsID(input string) (*ApplicationInsightsId, error) { + id, err := azure.ParseAzureResourceID(input) + if err != nil { + return nil, fmt.Errorf("parsing Application Insights ID %q: %+v", input, err) + } + + appId := ApplicationInsightsId{ + ResourceGroup: id.ResourceGroup, + } + + if appId.Name, err = id.PopSegment("components"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &appId, nil +} + +type ApplicationInsightsWebTestId struct { + ResourceGroup string + Name string +} + +func ApplicationInsightsWebTestID(input string) (*ApplicationInsightsWebTestId, error) { + id, err := azure.ParseAzureResourceID(input) + if err != nil { + return nil, fmt.Errorf("parsing Application Insights Web Test ID %q: %+v", input, err) + } + + testid := ApplicationInsightsWebTestId{ + ResourceGroup: id.ResourceGroup, + } + + if testid.Name, err = id.PopSegment("webtests"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &testid, nil +} diff --git a/azurerm/internal/services/applicationinsights/parse/application_insights_test.go b/azurerm/internal/services/applicationinsights/parse/application_insights_test.go new file mode 100644 index 000000000000..f34c57d9fbb1 --- /dev/null +++ b/azurerm/internal/services/applicationinsights/parse/application_insights_test.go @@ -0,0 +1,139 @@ +package parse + +import ( + "testing" +) + +func TestApplicationInsightsID(t *testing.T) { + testData := []struct { + Name string + Input string + Error bool + Expect *ApplicationInsightsId + }{ + { + Name: "Empty", + Input: "", + Error: true, + }, + { + Name: "No Resource Groups Segment", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000", + Error: true, + }, + { + Name: "No Resource Groups Value", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/", + Error: true, + }, + { + Name: "No Provider", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers", + Error: true, + }, + { + Name: "No component", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/microsoft.insights/components", + Error: true, + }, + { + Name: "Correct", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/microsoft.insights/components/appinsights1", + Expect: &ApplicationInsightsId{ + ResourceGroup: "group1", + Name: "appinsights1", + }, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Name) + + actual, err := ApplicationInsightsID(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") + } + + if actual.ResourceGroup != v.Expect.ResourceGroup { + t.Fatalf("Expected %q but got %q for Resource Group", v.Expect.ResourceGroup, actual.ResourceGroup) + } + + if actual.Name != v.Expect.Name { + t.Fatalf("Expected %q but got %q for Name", v.Expect.Name, actual.Name) + } + } +} + +func TestApplicationInsightsWebTestID(t *testing.T) { + testData := []struct { + Name string + Input string + Error bool + Expect *ApplicationInsightsWebTestId + }{ + { + Name: "Empty", + Input: "", + Error: true, + }, + { + Name: "No Resource Groups Segment", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000", + Error: true, + }, + { + Name: "No Resource Groups Value", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/", + Error: true, + }, + { + Name: "No Provider", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers", + Error: true, + }, + { + Name: "No webtest", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/microsoft.insights/webtests", + Error: true, + }, + { + Name: "Correct", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/microsoft.insights/webtests/test1", + Expect: &ApplicationInsightsWebTestId{ + ResourceGroup: "group1", + Name: "test1", + }, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Name) + + actual, err := ApplicationInsightsWebTestID(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") + } + + if actual.ResourceGroup != v.Expect.ResourceGroup { + t.Fatalf("Expected %q but got %q for Resource Group", v.Expect.ResourceGroup, actual.ResourceGroup) + } + + if actual.Name != v.Expect.Name { + t.Fatalf("Expected %q but got %q for Name", v.Expect.Name, actual.Name) + } + } +} diff --git a/azurerm/internal/services/applicationinsights/validate/application_insights.go b/azurerm/internal/services/applicationinsights/validate/application_insights.go new file mode 100644 index 000000000000..2722004664db --- /dev/null +++ b/azurerm/internal/services/applicationinsights/validate/application_insights.go @@ -0,0 +1,37 @@ +package validate + +import ( + "fmt" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/applicationinsights/parse" +) + +func ApplicationInsightsID(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be string", k)) + return + } + + if _, err := parse.ApplicationInsightsID(v); err != nil { + errors = append(errors, fmt.Errorf("parsing %q as a resource id: %v", k, err)) + return + } + + return warnings, errors +} + +func ApplicationInsightsWebTestID(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be string", k)) + return + } + + if _, err := parse.ApplicationInsightsWebTestID(v); err != nil { + errors = append(errors, fmt.Errorf("parsing %q as a resource id: %v", k, err)) + return + } + + return warnings, errors +} diff --git a/azurerm/internal/services/monitor/monitor_metric_alert_resource.go b/azurerm/internal/services/monitor/monitor_metric_alert_resource.go index 0b2b80c2862f..413148ce97a7 100644 --- a/azurerm/internal/services/monitor/monitor_metric_alert_resource.go +++ b/azurerm/internal/services/monitor/monitor_metric_alert_resource.go @@ -19,6 +19,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/location" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/applicationinsights/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" @@ -84,7 +85,7 @@ func resourceArmMonitorMetricAlert() *schema.Resource { Type: schema.TypeSet, Optional: true, MinItems: 1, - ExactlyOneOf: []string{"dynamic_criteria", "webtest_location_availability_criteria"}, + ExactlyOneOf: []string{"dynamic_criteria", "application_insights_web_test_location_availability_criteria"}, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "metric_namespace": { @@ -163,7 +164,7 @@ func resourceArmMonitorMetricAlert() *schema.Resource { MinItems: 1, // Curently, it allows to define only one dynamic criteria in one metric alert. MaxItems: 1, - ExactlyOneOf: []string{"criteria", "webtest_location_availability_criteria"}, + ExactlyOneOf: []string{"criteria", "application_insights_web_test_location_availability_criteria"}, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "metric_namespace": { @@ -258,7 +259,7 @@ func resourceArmMonitorMetricAlert() *schema.Resource { }, }, - "webtest_location_availability_criteria": { + "application_insights_web_test_location_availability_criteria": { Type: schema.TypeList, Optional: true, MinItems: 1, @@ -266,15 +267,15 @@ func resourceArmMonitorMetricAlert() *schema.Resource { ExactlyOneOf: []string{"criteria", "dynamic_criteria"}, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "webtest_id": { + "web_test_id": { Type: schema.TypeString, Required: true, - ValidateFunc: azure.ValidateResourceID, + ValidateFunc: validate.ApplicationInsightsWebTestID, }, "component_id": { Type: schema.TypeString, Required: true, - ValidateFunc: azure.ValidateResourceID, + ValidateFunc: validate.ApplicationInsightsID, }, "failed_location_count": { Type: schema.TypeInt, @@ -490,7 +491,7 @@ func resourceArmMonitorMetricAlertRead(d *schema.ResourceData, meta interface{}) criteriaSchema = "criteria" } case insights.WebtestLocationAvailabilityCriteria: - criteriaSchema = "webtest_location_availability_criteria" + criteriaSchema = "application_insights_web_test_location_availability_criteria" default: return fmt.Errorf("Unknown criteria type") } @@ -536,8 +537,8 @@ func expandMonitorMetricAlertCriteria(d *schema.ResourceData) (insights.BasicMet return expandMonitorMetricAlertMetricCriteria(d.Get("criteria").(*schema.Set).List()), nil case d.Get("dynamic_criteria").(*schema.Set).Len() != 0: return expandMonitorMetricAlertDynamicMetricCriteria(d.Get("dynamic_criteria").(*schema.Set).List()), nil - case d.Get("webtest_location_availability_criteria").(*schema.Set).Len() != 0: - return expandMonitorMetricAlertWebtestLocAvailCriteria(d.Get("webtest_location_availability_criteria").([]interface{})), nil + case len(d.Get("application_insights_web_test_location_availability_criteria").([]interface{})) != 0: + return expandMonitorMetricAlertWebtestLocAvailCriteria(d.Get("application_insights_web_test_location_availability_criteria").([]interface{})), nil default: // Guaranteed by schema `AtLeastOne` constraint return nil, errors.New("unknwon criteria type") @@ -602,9 +603,10 @@ func expandMonitorMetricAlertWebtestLocAvailCriteria(input []interface{}) insigh } v := input[0].(map[string]interface{}) return &insights.WebtestLocationAvailabilityCriteria{ - WebTestID: utils.String(v["webtest_id"].(string)), + WebTestID: utils.String(v["web_test_id"].(string)), ComponentID: utils.String(v["component_id"].(string)), FailedLocationCount: utils.Float(float64(v["failed_location_count"].(int))), + OdataType: insights.OdataTypeMicrosoftAzureMonitorWebtestLocationAvailabilityCriteria, } } @@ -783,7 +785,7 @@ func flattenMonitorMetricAlertWebtestLocAvailCriteria(input *insights.WebtestLoc return []interface{}{ map[string]interface{}{ - "webtest_id": webtestID, + "web_test_id": webtestID, "component_id": componentID, "failed_location_count": failedLocationCount, }, diff --git a/azurerm/internal/services/monitor/tests/monitor_metric_alert_resource_test.go b/azurerm/internal/services/monitor/tests/monitor_metric_alert_resource_test.go index e69bef747a39..a25129464889 100644 --- a/azurerm/internal/services/monitor/tests/monitor_metric_alert_resource_test.go +++ b/azurerm/internal/services/monitor/tests/monitor_metric_alert_resource_test.go @@ -134,6 +134,25 @@ func TestAccAzureRMMonitorMetricAlert_multiScope(t *testing.T) { }) } +func TestAccAzureRMMonitorMetricAlert_applicationInsightsWebTest(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_monitor_metric_alert", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMMonitorMetricAlertDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMonitorMetricAlert_applicationInsightsWebTest(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMonitorMetricAlertExists(data.ResourceName), + ), + }, + data.ImportStep(), + }, + }) +} + func testCheckAzureRMMonitorMetricAlertDestroy(s *terraform.State) error { conn := acceptance.AzureProvider.Meta().(*clients.Client).Monitor.MetricAlertsClient ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext @@ -330,7 +349,7 @@ resource "azurerm_virtual_network" "test" { } resource "azurerm_subnet" "test" { - count = %[3]d + count = %[3]d name = "internal-${count.index}" resource_group_name = azurerm_resource_group.test.name virtual_network_name = azurerm_virtual_network.test.name @@ -338,7 +357,7 @@ resource "azurerm_subnet" "test" { } resource "azurerm_network_interface" "test" { - count = %[3]d + count = %[3]d name = "acctestnic-${count.index}" location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name @@ -350,7 +369,7 @@ resource "azurerm_network_interface" "test" { } resource "azurerm_linux_virtual_machine" "test" { - count = %[3]d + count = %[3]d name = "acctestVM-${count.index}" resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location @@ -400,3 +419,69 @@ resource "azurerm_monitor_metric_alert" "test" { } `, testAccAzureRMMonitorMetricAlert_multiVMTemplate(data, count), data.RandomInteger, data.Locations.Primary) } + +func testAccAzureRMMonitorMetricAlert_applicationInsightsWebTestTemplate(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "%[2]s" +} + +resource "azurerm_application_insights" "test" { + name = "acctestAppInsight-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + application_type = "web" +} + +resource "azurerm_application_insights_web_test" "test" { + name = "acctestAppInsight-webtest-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + application_insights_id = azurerm_application_insights.test.id + kind = "ping" + frequency = 300 + timeout = 60 + enabled = true + geo_locations = ["us-tx-sn1-azr", "us-il-ch1-azr"] + + configuration = < + + + + +XML + lifecycle { + ignore_changes = [tags] + } +} +`, data.RandomInteger, data.Locations.Primary) +} + +func testAccAzureRMMonitorMetricAlert_applicationInsightsWebTest(data acceptance.TestData) string { + template := testAccAzureRMMonitorMetricAlert_applicationInsightsWebTestTemplate(data) + return fmt.Sprintf(` +%s + +resource "azurerm_monitor_metric_alert" "test" { + name = "acctestMetricAlert-%d" + resource_group_name = azurerm_resource_group.test.name + scopes = [ + azurerm_application_insights.test.id, + azurerm_application_insights_web_test.test.id, + ] + application_insights_web_test_location_availability_criteria { + web_test_id = azurerm_application_insights_web_test.test.id + component_id = azurerm_application_insights.test.id + failed_location_count = 2 + } + window_size = "PT15M" + frequency = "PT1M" +} +`, template, data.RandomInteger) +} diff --git a/website/docs/r/monitor_metric_alert.html.markdown b/website/docs/r/monitor_metric_alert.html.markdown index 15a0bf81fe5d..102022d8c277 100644 --- a/website/docs/r/monitor_metric_alert.html.markdown +++ b/website/docs/r/monitor_metric_alert.html.markdown @@ -72,15 +72,15 @@ The following arguments are supported: * `scopes` - (Required) A set of strings of resource IDs at which the metric criteria should be applied. * `criteria` - (Optional) One or more (static) `criteria` blocks as defined below. --> **NOTE** One of either `criteria`, `dynamic_criteria` or `webtest_location_availability_criteria` must be specified. +-> **NOTE** One of either `criteria`, `dynamic_criteria` or `application_insights_web_test_location_availability_criteria` must be specified. * `dynamic_criteria` - (Optional) A `dynamic_criteria` block as defined below. --> **NOTE** One of either `criteria`, `dynamic_criteria` or `webtest_location_availability_criteria` must be specified. +-> **NOTE** One of either `criteria`, `dynamic_criteria` or `application_insights_web_test_location_availability_criteria` must be specified. -* `webtest_location_availability_criteria` - (Optional) A `webtest_location_availability_criteria` block as defined below. +* `application_insights_web_test_location_availability_criteria` - (Optional) A `application_insights_web_test_location_availability_criteria` block as defined below. --> **NOTE** One of either `criteria`, `dynamic_criteria` or `webtest_location_availability_criteria` must be specified. +-> **NOTE** One of either `criteria`, `dynamic_criteria` or `application_insights_web_test_location_availability_criteria` must be specified. * `action` - (Optional) One or more `action` blocks as defined below. * `enabled` - (Optional) Should this Metric Alert be enabled? Defaults to `true`. @@ -133,9 +133,9 @@ A `dynamic_criteria` block supports the following: --- -A `webtest_location_availability_criteria` block supports the following: +A `application_insights_web_test_location_availability_criteria` block supports the following: -* `webtest_id` - (Required) The ID of the Application Insights Web Test. +* `web_test_id` - (Required) The ID of the Application Insights Web Test. * `component_id` - (Required) The ID of the Application Insights Resource. * `failed_location_count` - (Required) The number of failed locations. From d974130dce4eb02b1d29dd3df199fa761505d0ee Mon Sep 17 00:00:00 2001 From: magodo Date: Wed, 15 Jul 2020 13:04:52 +0800 Subject: [PATCH 8/9] typo --- .../internal/services/monitor/monitor_metric_alert_resource.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azurerm/internal/services/monitor/monitor_metric_alert_resource.go b/azurerm/internal/services/monitor/monitor_metric_alert_resource.go index 413148ce97a7..0bbec82fe8d5 100644 --- a/azurerm/internal/services/monitor/monitor_metric_alert_resource.go +++ b/azurerm/internal/services/monitor/monitor_metric_alert_resource.go @@ -541,7 +541,7 @@ func expandMonitorMetricAlertCriteria(d *schema.ResourceData) (insights.BasicMet return expandMonitorMetricAlertWebtestLocAvailCriteria(d.Get("application_insights_web_test_location_availability_criteria").([]interface{})), nil default: // Guaranteed by schema `AtLeastOne` constraint - return nil, errors.New("unknwon criteria type") + return nil, errors.New("unknown criteria type") } } From 48941e6085bd8fa2b42a6d52e57513441d7d17a6 Mon Sep 17 00:00:00 2001 From: jackofallops Date: Thu, 16 Jul 2020 11:08:37 +0100 Subject: [PATCH 9/9] feedback updates --- .../monitor/monitor_metric_alert_resource.go | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/azurerm/internal/services/monitor/monitor_metric_alert_resource.go b/azurerm/internal/services/monitor/monitor_metric_alert_resource.go index 0bbec82fe8d5..3d97784d7f50 100644 --- a/azurerm/internal/services/monitor/monitor_metric_alert_resource.go +++ b/azurerm/internal/services/monitor/monitor_metric_alert_resource.go @@ -2,14 +2,12 @@ package monitor import ( "bytes" - "errors" "fmt" "log" "time" - "github.com/Azure/go-autorest/autorest/date" - "github.com/Azure/azure-sdk-for-go/services/preview/monitor/mgmt/2019-06-01/insights" + "github.com/Azure/go-autorest/autorest/date" "github.com/hashicorp/go-azure-helpers/response" "github.com/hashicorp/terraform-plugin-sdk/helper/hashcode" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" @@ -85,7 +83,7 @@ func resourceArmMonitorMetricAlert() *schema.Resource { Type: schema.TypeSet, Optional: true, MinItems: 1, - ExactlyOneOf: []string{"dynamic_criteria", "application_insights_web_test_location_availability_criteria"}, + ExactlyOneOf: []string{"criteria", "dynamic_criteria", "application_insights_web_test_location_availability_criteria"}, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "metric_namespace": { @@ -164,7 +162,7 @@ func resourceArmMonitorMetricAlert() *schema.Resource { MinItems: 1, // Curently, it allows to define only one dynamic criteria in one metric alert. MaxItems: 1, - ExactlyOneOf: []string{"criteria", "application_insights_web_test_location_availability_criteria"}, + ExactlyOneOf: []string{"criteria", "dynamic_criteria", "application_insights_web_test_location_availability_criteria"}, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "metric_namespace": { @@ -264,7 +262,7 @@ func resourceArmMonitorMetricAlert() *schema.Resource { Optional: true, MinItems: 1, MaxItems: 1, - ExactlyOneOf: []string{"criteria", "dynamic_criteria"}, + ExactlyOneOf: []string{"criteria", "dynamic_criteria", "application_insights_web_test_location_availability_criteria"}, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "web_test_id": { @@ -481,7 +479,7 @@ func resourceArmMonitorMetricAlertRead(d *schema.ResourceData, meta interface{}) criteriaSchema = "criteria" case insights.MetricAlertMultipleResourceMultipleMetricCriteria: if c.AllOf == nil || len(*c.AllOf) == 0 { - return errors.New("nil or empty contained criteria of MultipleResourceMultipleMetricCriteria") + return fmt.Errorf("nil or empty contained criteria of MultipleResourceMultipleMetricCriteria") } // `MinItems` defined in schema guaranteed there is at least one element. switch (*c.AllOf)[0].(type) { @@ -496,9 +494,10 @@ func resourceArmMonitorMetricAlertRead(d *schema.ResourceData, meta interface{}) return fmt.Errorf("Unknown criteria type") } - // lintignore:R001 - if err := d.Set(criteriaSchema, flattenMonitorMetricAlertCriteria(alert.Criteria)); err != nil { - return fmt.Errorf("Error setting `%s`: %+v", criteriaSchema, err) + monitorMetricAlertCriteria := flattenMonitorMetricAlertCriteria(alert.Criteria) + + if err := d.Set(criteriaSchema, monitorMetricAlertCriteria); err != nil { + return fmt.Errorf("failed setting `%s`: %+v", criteriaSchema, err) } if err := d.Set("action", flattenMonitorMetricAlertAction(alert.Actions)); err != nil { @@ -541,7 +540,7 @@ func expandMonitorMetricAlertCriteria(d *schema.ResourceData) (insights.BasicMet return expandMonitorMetricAlertWebtestLocAvailCriteria(d.Get("application_insights_web_test_location_availability_criteria").([]interface{})), nil default: // Guaranteed by schema `AtLeastOne` constraint - return nil, errors.New("unknown criteria type") + return nil, fmt.Errorf("unknown criteria type") } }