From 0b76d88ba47ac6ce525f23cfd4912c87f8bda32d Mon Sep 17 00:00:00 2001 From: Tao <104055472+teowa@users.noreply.github.com> Date: Tue, 26 Mar 2024 11:30:02 +0800 Subject: [PATCH] `azurerm_monitor_scheduled_query_rules_alert_v2` - support `identity` (#25365) --- ...scheduled_query_rules_alert_v2_resource.go | 70 ++++++--- ...uled_query_rules_alert_v2_resource_test.go | 137 ++++++++++++++++++ ...heduled_query_rules_alert_v2.html.markdown | 44 ++++++ 3 files changed, 228 insertions(+), 23 deletions(-) diff --git a/internal/services/monitor/monitor_scheduled_query_rules_alert_v2_resource.go b/internal/services/monitor/monitor_scheduled_query_rules_alert_v2_resource.go index b42904bbc91a..78db558edbd9 100644 --- a/internal/services/monitor/monitor_scheduled_query_rules_alert_v2_resource.go +++ b/internal/services/monitor/monitor_scheduled_query_rules_alert_v2_resource.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" "github.com/hashicorp/go-azure-helpers/resourcemanager/location" "github.com/hashicorp/go-azure-sdk/resource-manager/insights/2023-03-15-preview/scheduledqueryrules" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" @@ -20,28 +21,29 @@ import ( ) type ScheduledQueryRulesAlertV2Model struct { - Name string `tfschema:"name"` - ResourceGroupName string `tfschema:"resource_group_name"` - Actions []ScheduledQueryRulesAlertV2ActionsModel `tfschema:"action"` - AutoMitigate bool `tfschema:"auto_mitigation_enabled"` - CheckWorkspaceAlertsStorageConfigured bool `tfschema:"workspace_alerts_storage_enabled"` - Criteria []ScheduledQueryRulesAlertV2CriteriaModel `tfschema:"criteria"` - Description string `tfschema:"description"` - DisplayName string `tfschema:"display_name"` - Enabled bool `tfschema:"enabled"` - EvaluationFrequency string `tfschema:"evaluation_frequency"` - Location string `tfschema:"location"` - MuteActionsDuration string `tfschema:"mute_actions_after_alert_duration"` - OverrideQueryTimeRange string `tfschema:"query_time_range_override"` - Scopes []string `tfschema:"scopes"` - Severity scheduledqueryrules.AlertSeverity `tfschema:"severity"` - SkipQueryValidation bool `tfschema:"skip_query_validation"` - Tags map[string]string `tfschema:"tags"` - TargetResourceTypes []string `tfschema:"target_resource_types"` - WindowSize string `tfschema:"window_duration"` - CreatedWithApiVersion string `tfschema:"created_with_api_version"` - IsLegacyLogAnalyticsRule bool `tfschema:"is_a_legacy_log_analytics_rule"` - IsWorkspaceAlertsStorageConfigured bool `tfschema:"is_workspace_alerts_storage_configured"` + Name string `tfschema:"name"` + ResourceGroupName string `tfschema:"resource_group_name"` + Actions []ScheduledQueryRulesAlertV2ActionsModel `tfschema:"action"` + AutoMitigate bool `tfschema:"auto_mitigation_enabled"` + CheckWorkspaceAlertsStorageConfigured bool `tfschema:"workspace_alerts_storage_enabled"` + Criteria []ScheduledQueryRulesAlertV2CriteriaModel `tfschema:"criteria"` + Description string `tfschema:"description"` + DisplayName string `tfschema:"display_name"` + Enabled bool `tfschema:"enabled"` + EvaluationFrequency string `tfschema:"evaluation_frequency"` + Location string `tfschema:"location"` + MuteActionsDuration string `tfschema:"mute_actions_after_alert_duration"` + OverrideQueryTimeRange string `tfschema:"query_time_range_override"` + Scopes []string `tfschema:"scopes"` + Severity scheduledqueryrules.AlertSeverity `tfschema:"severity"` + SkipQueryValidation bool `tfschema:"skip_query_validation"` + Tags map[string]string `tfschema:"tags"` + TargetResourceTypes []string `tfschema:"target_resource_types"` + WindowSize string `tfschema:"window_duration"` + CreatedWithApiVersion string `tfschema:"created_with_api_version"` + IsLegacyLogAnalyticsRule bool `tfschema:"is_a_legacy_log_analytics_rule"` + IsWorkspaceAlertsStorageConfigured bool `tfschema:"is_workspace_alerts_storage_configured"` + Identity []identity.ModelSystemAssignedUserAssigned `tfschema:"identity"` } type ScheduledQueryRulesAlertV2ActionsModel struct { @@ -374,6 +376,8 @@ func (r ScheduledQueryRulesAlertV2Resource) Arguments() map[string]*pluginsdk.Sc Optional: true, }, + "identity": commonschema.SystemOrUserAssignedIdentityOptional(), + "tags": commonschema.Tags(), "target_resource_types": { @@ -426,6 +430,12 @@ func (r ScheduledQueryRulesAlertV2Resource) Create() sdk.ResourceFunc { if !response.WasNotFound(existing.HttpResponse) { return metadata.ResourceRequiresImport(r.ResourceType(), id) } + + ExpanededIdentity, err := identity.ExpandSystemOrUserAssignedMapFromModel(model.Identity) + if err != nil { + return fmt.Errorf("expanding SystemOrUserAssigned Identity: %+v", err) + } + kind := scheduledqueryrules.KindLogAlert properties := &scheduledqueryrules.ScheduledQueryRuleResource{ Kind: &kind, @@ -439,7 +449,8 @@ func (r ScheduledQueryRulesAlertV2Resource) Create() sdk.ResourceFunc { SkipQueryValidation: &model.SkipQueryValidation, TargetResourceTypes: &model.TargetResourceTypes, }, - Tags: &model.Tags, + Identity: ExpanededIdentity, + Tags: &model.Tags, } properties.Properties.Actions = expandScheduledQueryRulesAlertV2ActionsModel(model.Actions) @@ -584,6 +595,13 @@ func (r ScheduledQueryRulesAlertV2Resource) Update() sdk.ResourceFunc { } } + if metadata.ResourceData.HasChange("identity") { + model.Identity, err = identity.ExpandSystemOrUserAssignedMapFromModel(resourceModel.Identity) + if err != nil { + return fmt.Errorf("expanding SystemOrUserAssigned Identity: %+v", err) + } + } + if metadata.ResourceData.HasChange("tags") { model.Tags = &resourceModel.Tags } @@ -622,10 +640,16 @@ func (r ScheduledQueryRulesAlertV2Resource) Read() sdk.ResourceFunc { return fmt.Errorf("retrieving %s: model was nil", id) } + flattenedIdentity, err := identity.FlattenSystemOrUserAssignedMapToModel(model.Identity) + if err != nil { + return fmt.Errorf("flattening SystemOrUserAssigned Identity: %+v", err) + } + state := ScheduledQueryRulesAlertV2Model{ Name: id.ScheduledQueryRuleName, ResourceGroupName: id.ResourceGroupName, Location: location.Normalize(model.Location), + Identity: *flattenedIdentity, } properties := &model.Properties diff --git a/internal/services/monitor/monitor_scheduled_query_rules_alert_v2_resource_test.go b/internal/services/monitor/monitor_scheduled_query_rules_alert_v2_resource_test.go index 4d9dbb44f68f..acbafc70929a 100644 --- a/internal/services/monitor/monitor_scheduled_query_rules_alert_v2_resource_test.go +++ b/internal/services/monitor/monitor_scheduled_query_rules_alert_v2_resource_test.go @@ -89,6 +89,69 @@ func TestAccMonitorScheduledQueryRulesAlertV2_update(t *testing.T) { }) } +func TestAccMonitorScheduledQueryRulesAlertV2_identitySystemAssigned(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_monitor_scheduled_query_rules_alert_v2", "test") + r := MonitorScheduledQueryRulesAlertV2Resource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.identitySystemAssigned(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("identity.#").HasValue("1"), + check.That(data.ResourceName).Key("identity.0.type").HasValue("SystemAssigned"), + check.That(data.ResourceName).Key("identity.0.principal_id").IsUUID(), + check.That(data.ResourceName).Key("identity.0.tenant_id").IsUUID(), + ), + }, + data.ImportStep(), + }) +} + +func TestAccMonitorScheduledQueryRulesAlertV2_identityUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_monitor_scheduled_query_rules_alert_v2", "test") + r := MonitorScheduledQueryRulesAlertV2Resource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("identity.#").HasValue("0"), + ), + }, + data.ImportStep(), + { + Config: r.identitySystemAssigned(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("identity.#").HasValue("1"), + check.That(data.ResourceName).Key("identity.0.type").HasValue("SystemAssigned"), + check.That(data.ResourceName).Key("identity.0.principal_id").IsUUID(), + check.That(data.ResourceName).Key("identity.0.tenant_id").IsUUID(), + ), + }, + data.ImportStep(), + { + Config: r.identityUserAssigned(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("identity.0.type").HasValue("UserAssigned"), + check.That(data.ResourceName).Key("identity.0.identity_ids.#").HasValue("1"), + check.That(data.ResourceName).Key("identity.0.principal_id").IsEmpty(), + check.That(data.ResourceName).Key("identity.0.tenant_id").IsUUID(), + ), + }, + data.ImportStep(), + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("identity.#").HasValue("0"), + ), + }, + data.ImportStep(), + }) +} + func (r MonitorScheduledQueryRulesAlertV2Resource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := scheduledqueryrules.ParseScheduledQueryRuleID(state.ID) if err != nil { @@ -129,6 +192,18 @@ resource "azurerm_monitor_action_group" "test" { resource_group_name = azurerm_resource_group.test.name short_name = "test mag" } + +resource "azurerm_user_assigned_identity" "test" { + name = "acctestUAI-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_role_assignment" "test" { + scope = azurerm_application_insights.test.id + role_definition_name = "Reader" + principal_id = azurerm_user_assigned_identity.test.principal_id +} `, data.RandomInteger, data.Locations.Primary) } @@ -297,3 +372,65 @@ resource "azurerm_monitor_scheduled_query_rules_alert_v2" "test" { } `, template, data.RandomInteger, data.Locations.Primary) } + +func (r MonitorScheduledQueryRulesAlertV2Resource) identitySystemAssigned(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_monitor_scheduled_query_rules_alert_v2" "test" { + name = "acctest-isqr-%d" + resource_group_name = azurerm_resource_group.test.name + location = "%s" + evaluation_frequency = "PT5M" + window_duration = "PT5M" + scopes = [azurerm_application_insights.test.id] + severity = 3 + criteria { + query = <<-QUERY + requests + | summarize CountByCountry=count() by client_CountryOrRegion + QUERY + time_aggregation_method = "Count" + threshold = 5.0 + operator = "Equal" + } + identity { + type = "SystemAssigned" + } +} +`, template, data.RandomInteger, data.Locations.Primary) +} + +func (r MonitorScheduledQueryRulesAlertV2Resource) identityUserAssigned(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_monitor_scheduled_query_rules_alert_v2" "test" { + name = "acctest-isqr-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = "%s" + evaluation_frequency = "PT5M" + window_duration = "PT5M" + scopes = [azurerm_application_insights.test.id] + severity = 3 + criteria { + query = <<-QUERY + requests + | summarize CountByCountry=count() by client_CountryOrRegion + QUERY + time_aggregation_method = "Count" + threshold = 5.0 + operator = "Equal" + } + identity { + type = "UserAssigned" + identity_ids = [ + azurerm_user_assigned_identity.test.id, + ] + } + depends_on = [azurerm_role_assignment.test] +} +`, template, data.RandomInteger, data.Locations.Primary) +} diff --git a/website/docs/r/monitor_scheduled_query_rules_alert_v2.html.markdown b/website/docs/r/monitor_scheduled_query_rules_alert_v2.html.markdown index e5f4ba8e8a2b..1de3023c469b 100644 --- a/website/docs/r/monitor_scheduled_query_rules_alert_v2.html.markdown +++ b/website/docs/r/monitor_scheduled_query_rules_alert_v2.html.markdown @@ -31,6 +31,18 @@ resource "azurerm_monitor_action_group" "example" { short_name = "test mag" } +resource "azurerm_user_assigned_identity" "example" { + name = "example-uai" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name +} + +resource "azurerm_role_assignment" "example" { + scope = azurerm_application_insights.example.id + role_definition_name = "Reader" + principal_id = azurerm_user_assigned_identity.example.principal_id +} + resource "azurerm_monitor_scheduled_query_rules_alert_v2" "example" { name = "example-msqrv2" resource_group_name = azurerm_resource_group.example.name @@ -77,10 +89,18 @@ resource "azurerm_monitor_scheduled_query_rules_alert_v2" "example" { } } + identity { + type = "UserAssigned" + identity_ids = [ + azurerm_user_assigned_identity.example.id, + ] + } tags = { key = "value" key2 = "value2" } + + depends_on = [azurerm_role_assignment.example] } ``` @@ -134,6 +154,8 @@ The following arguments are supported: * `target_resource_types` - (Optional) List of resource type of the target resource(s) on which the alert is created/updated. For example if the scope is a resource group and targetResourceTypes is `Microsoft.Compute/virtualMachines`, then a different alert will be fired for each virtual machine in the resource group which meet the alert criteria. +* `identity` - (Optional) An `identity` block as defined below. + --- An `action` block supports the following: @@ -186,6 +208,16 @@ A `failing_periods` block supports the following: -> **Note** `number_of_evaluation_periods` must be `1` for queries that do not project timestamp column +--- + +An `identity` block supports the following: + +* `type` - (Required) Specifies the type of Managed Service Identity that should be configured on this Scheduled Query Rule. Possible values are `SystemAssigned`, `UserAssigned`. + +* `identity_ids` - (Optional) A list of User Assigned Managed Identity IDs to be assigned to this Scheduled Query Rule. + +~> **NOTE:** This is required when `type` is set to `UserAssigned`. The identity associated must have required roles, read the [Azure documentation](https://learn.microsoft.com/en-us/azure/azure-monitor/alerts/alerts-create-log-alert-rule#configure-the-alert-rule-details) for more information. + ## Attributes Reference In addition to the Arguments listed above - the following Attributes are exported: @@ -198,6 +230,18 @@ In addition to the Arguments listed above - the following Attributes are exporte * `is_workspace_alerts_storage_configured` - The flag indicates whether this Scheduled Query Rule has been configured to be stored in the customer's storage. +* `identity` - An `identity` block as defined below. + +--- + +A `identity` block exports the following: + +* `principal_id` - The Principal ID for the Service Principal associated with the Managed Service Identity of this App Service slot. + +* `tenant_id` - The Tenant ID for the Service Principal associated with the Managed Service Identity of this App Service slot. + +-> You can access the Principal ID via `azurerm_monitor_scheduled_query_rules_alert_v2.example.identity[0].principal_id` and the Tenant ID via `azurerm_monitor_scheduled_query_rules_alert_v2.example.identity[0].tenant_id` + ## Timeouts The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: