Skip to content

Commit

Permalink
New Resource: azurerm_monitor_scheduled_query_rules_alert & `azurer…
Browse files Browse the repository at this point in the history
…m_monitor_scheduled_query_rules_log` (#5053)

* New Resource: `azurerm_monitor_scheduled_query_rules`

* Acceptance tests passing

* Fix linting errors; minor cleanup

* Minor updates and bug fixes

- Fix some sprintf formatting
- Improve documentation for cross-resource query
- Don't create an empty metric_trigger {} if user doesn't specify block

* Remove heredoc in docs example

* fix response import

* Update tests and docs to pass checks

* Add moved tests to registration

* First pass update docs post-review

* First pass at separating into two resources

* Rename action to alert in docs

* Fix code review items

* Fix lint issues and minor typos

* Fix lint issues #2

* Fix lint issues #3

* Fix documentation error

* Add monitor helpers

* First pass address latest review

* Validate metric_trigger query; add example to docs

* Address review comments

- Fix test crash
- Move common functions to helpers
- Add more schema validations
- Add Update tests

* Fix type assertions; simplify cross-resource query example

* Review updates

- Add metric alerts to LogToMetric tests and docs
- Fix type assert from change to TypeList
- Fix a couple typos

* Add MaxItems for cross-resource query

* Address review comments

- Improve docs
- Use heredoc strings in docs and test queries
- Move shared functions into monitor package
- Add underscores to test function names

* Fix markdown formatting
  • Loading branch information
mbfrahry authored Mar 4, 2020
2 parents a50a865 + 0625b05 commit 685016f
Show file tree
Hide file tree
Showing 16 changed files with 2,512 additions and 9 deletions.
5 changes: 5 additions & 0 deletions azurerm/internal/services/monitor/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type Client struct {
DiagnosticSettingsCategoryClient *insights.DiagnosticSettingsCategoryClient
LogProfilesClient *insights.LogProfilesClient
MetricAlertsClient *insights.MetricAlertsClient
ScheduledQueryRulesClient *insights.ScheduledQueryRulesClient
}

func NewClient(o *common.ClientOptions) *Client {
Expand Down Expand Up @@ -44,6 +45,9 @@ func NewClient(o *common.ClientOptions) *Client {
MetricAlertsClient := insights.NewMetricAlertsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&MetricAlertsClient.Client, o.ResourceManagerAuthorizer)

ScheduledQueryRulesClient := insights.NewScheduledQueryRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&ScheduledQueryRulesClient.Client, o.ResourceManagerAuthorizer)

return &Client{
AutoscaleSettingsClient: &AutoscaleSettingsClient,
ActionGroupsClient: &ActionGroupsClient,
Expand All @@ -53,5 +57,6 @@ func NewClient(o *common.ClientOptions) *Client {
DiagnosticSettingsCategoryClient: &DiagnosticSettingsCategoryClient,
LogProfilesClient: &LogProfilesClient,
MetricAlertsClient: &MetricAlertsClient,
ScheduledQueryRulesClient: &ScheduledQueryRulesClient,
}
}
138 changes: 138 additions & 0 deletions azurerm/internal/services/monitor/common_monitor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package monitor

import (
"fmt"

"github.com/Azure/azure-sdk-for-go/services/preview/monitor/mgmt/2019-06-01/insights"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func flattenAzureRmScheduledQueryRulesAlertAction(input *insights.AzNsActionGroup) []interface{} {
v := make(map[string]interface{})

if input != nil {
if input.ActionGroup != nil {
v["action_group"] = *input.ActionGroup
}
v["email_subject"] = input.EmailSubject
v["custom_webhook_payload"] = input.CustomWebhookPayload
}
return []interface{}{v}
}

func expandMonitorScheduledQueryRulesCommonSource(d *schema.ResourceData) *insights.Source {
authorizedResourceIDs := d.Get("authorized_resource_ids").(*schema.Set).List()
dataSourceID := d.Get("data_source_id").(string)
query, ok := d.GetOk("query")
source := insights.Source{
AuthorizedResources: utils.ExpandStringSlice(authorizedResourceIDs),
DataSourceID: utils.String(dataSourceID),
QueryType: insights.ResultCount,
}
if ok {
source.Query = utils.String(query.(string))
}

return &source
}

func flattenAzureRmScheduledQueryRulesAlertMetricTrigger(input *insights.LogMetricTrigger) []interface{} {
result := make(map[string]interface{})

if input == nil {
return []interface{}{}
}

result["operator"] = string(input.ThresholdOperator)

if input.Threshold != nil {
result["threshold"] = *input.Threshold
}

result["metric_trigger_type"] = string(input.MetricTriggerType)

if input.MetricColumn != nil {
result["metric_column"] = *input.MetricColumn
}
return []interface{}{result}
}

func flattenAzureRmScheduledQueryRulesAlertTrigger(input *insights.TriggerCondition) []interface{} {
result := make(map[string]interface{})

result["operator"] = string(input.ThresholdOperator)

if input.Threshold != nil {
result["threshold"] = *input.Threshold
}

if input.MetricTrigger != nil {
result["metric_trigger"] = flattenAzureRmScheduledQueryRulesAlertMetricTrigger(input.MetricTrigger)
}

return []interface{}{result}
}

func flattenAzureRmScheduledQueryRulesLogCriteria(input *[]insights.Criteria) []interface{} {
result := make([]interface{}, 0)

if input != nil {
for _, criteria := range *input {
v := make(map[string]interface{})

v["dimension"] = flattenAzureRmScheduledQueryRulesLogDimension(criteria.Dimensions)
v["metric_name"] = *criteria.MetricName

result = append(result, v)
}
}

return result
}

func flattenAzureRmScheduledQueryRulesLogDimension(input *[]insights.Dimension) []interface{} {
result := make([]interface{}, 0)

if input != nil {
for _, dimension := range *input {
v := make(map[string]interface{})

if dimension.Name != nil {
v["name"] = *dimension.Name
}

if dimension.Operator != nil {
v["operator"] = *dimension.Operator
}

if dimension.Values != nil {
v["values"] = *dimension.Values
}

result = append(result, v)
}
}

return result
}

// ValidateThreshold checks that a threshold value is between 0 and 10000
// and is a whole number. The azure-sdk-for-go expects this value to be a float64
// but the user validation rules want an integer.
func validateMonitorScheduledQueryRulesAlertThreshold(i interface{}, k string) (warnings []string, errors []error) {
v, ok := i.(float64)
if !ok {
errors = append(errors, fmt.Errorf("expected type of %q to be float64", k))
}

if v != float64(int64(v)) {
errors = append(errors, fmt.Errorf("%q must be a whole number", k))
}

if v < 0 || v > 10000 {
errors = append(errors, fmt.Errorf("%q must be between 0 and 10000 (inclusive)", k))
}

return warnings, errors
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
package monitor

import (
"fmt"
"strconv"
"time"

"github.com/Azure/azure-sdk-for-go/services/preview/monitor/mgmt/2019-06-01/insights"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients"
"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 dataSourceArmMonitorScheduledQueryRulesAlert() *schema.Resource {
return &schema.Resource{
Read: dataSourceArmMonitorScheduledQueryRulesAlertRead,

Timeouts: &schema.ResourceTimeout{
Read: schema.DefaultTimeout(5 * time.Minute),
},

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
},

"resource_group_name": azure.SchemaResourceGroupNameForDataSource(),

"location": azure.SchemaLocationForDataSource(),

"authorized_resource_ids": {
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"action": {
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"action_group": {
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"custom_webhook_payload": {
Type: schema.TypeString,
Computed: true,
},
"email_subject": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
"data_source_id": {
Type: schema.TypeString,
Computed: true,
},
"description": {
Type: schema.TypeString,
Computed: true,
},
"enabled": {
Type: schema.TypeBool,
Computed: true,
},
"frequency": {
Type: schema.TypeInt,
Computed: true,
},
"query": {
Type: schema.TypeString,
Computed: true,
},
"query_type": {
Type: schema.TypeString,
Computed: true,
},
"severity": {
Type: schema.TypeInt,
Computed: true,
},
"throttling": {
Type: schema.TypeInt,
Computed: true,
},
"time_window": {
Type: schema.TypeInt,
Computed: true,
},
"trigger": {
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"metric_trigger": {
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"metric_column": {
Type: schema.TypeString,
Computed: true,
},
"metric_trigger_type": {
Type: schema.TypeString,
Computed: true,
},
"operator": {
Type: schema.TypeString,
Computed: true,
},
"threshold": {
Type: schema.TypeFloat,
Computed: true,
},
},
},
},
"operator": {
Type: schema.TypeString,
Computed: true,
},
"threshold": {
Type: schema.TypeFloat,
Computed: true,
},
},
},
},

"tags": tags.SchemaDataSource(),
},
}
}

func dataSourceArmMonitorScheduledQueryRulesAlertRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Monitor.ScheduledQueryRulesClient
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

resourceGroup := d.Get("resource_group_name").(string)
name := d.Get("name").(string)

resp, err := client.Get(ctx, resourceGroup, name)
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
return fmt.Errorf("[DEBUG] Scheduled Query Rule %q was not found in Resource Group %q: %+v", name, resourceGroup, err)
}
return fmt.Errorf("Error getting Scheduled Query Rule %q (resource group %q): %+v", name, resourceGroup, err)
}

d.SetId(*resp.ID)

d.Set("name", name)
d.Set("resource_group_name", resourceGroup)
if location := resp.Location; location != nil {
d.Set("location", azure.NormalizeLocation(*location))
}

d.Set("description", resp.Description)
if resp.Enabled == insights.True {
d.Set("enabled", true)
} else {
d.Set("enabled", false)
}

action, ok := resp.Action.(insights.AlertingAction)
if !ok {
return fmt.Errorf("Wrong action type in Scheduled Query Rule %q (resource group %q): %T", name, resourceGroup, resp.Action)
}
if err = d.Set("action", flattenAzureRmScheduledQueryRulesAlertAction(action.AznsAction)); err != nil {
return fmt.Errorf("Error setting `action`: %+v", err)
}
severity, err := strconv.Atoi(string(action.Severity))
if err != nil {
return fmt.Errorf("Error converting action.Severity %q in query rule %q to int (resource group %q): %+v", action.Severity, name, resourceGroup, err)
}
d.Set("severity", severity)
d.Set("throttling", action.ThrottlingInMin)
if err = d.Set("trigger", flattenAzureRmScheduledQueryRulesAlertTrigger(action.Trigger)); err != nil {
return fmt.Errorf("Error setting `trigger`: %+v", err)
}

if schedule := resp.Schedule; schedule != nil {
if schedule.FrequencyInMinutes != nil {
d.Set("frequency", schedule.FrequencyInMinutes)
}
if schedule.TimeWindowInMinutes != nil {
d.Set("time_window", schedule.TimeWindowInMinutes)
}
}

if source := resp.Source; source != nil {
if source.AuthorizedResources != nil {
d.Set("authorized_resource_ids", utils.FlattenStringSlice(source.AuthorizedResources))
}
if source.DataSourceID != nil {
d.Set("data_source_id", source.DataSourceID)
}
if source.Query != nil {
d.Set("query", source.Query)
}
d.Set("query_type", string(source.QueryType))
}

return tags.FlattenAndSet(d, resp.Tags)
}
Loading

0 comments on commit 685016f

Please sign in to comment.