diff --git a/Makefile b/Makefile index 8f55041..7e832b7 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ LCAF_ENV_FILE = .lcafenv # Source repository for repo manifests REPO_MANIFESTS_URL ?= https://github.com/launchbynttdata/launch-common-automation-framework.git # Branch of source repository for repo manifests. Other tags not currently supported. -REPO_BRANCH ?= refs/tags/1.0.0 +REPO_BRANCH ?= refs/tags/1.7.1 # Path to seed manifest in repository referenced in REPO_MANIFESTS_URL REPO_MANIFEST ?= manifests/terraform_modules/seed/manifest.xml diff --git a/README.md b/README.md index 180e162..81a5aa4 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,8 @@ No providers. | [signalr](#module\_signalr) | terraform.registry.launch.nttdata.com/module_primitive/signalr/azurerm | ~> 1.0 | | [log\_analytics\_workspace](#module\_log\_analytics\_workspace) | terraform.registry.launch.nttdata.com/module_primitive/log_analytics_workspace/azurerm | ~> 1.0 | | [diagnostic\_setting](#module\_diagnostic\_setting) | terraform.registry.launch.nttdata.com/module_primitive/monitor_diagnostic_setting/azurerm | ~> 1.0 | +| [action\_group](#module\_action\_group) | terraform.registry.launch.nttdata.com/module_primitive/monitor_action_group/azurerm | ~> 1.0.0 | +| [metric\_alert](#module\_metric\_alert) | terraform.registry.launch.nttdata.com/module_primitive/monitor_metric_alert/azurerm | ~> 2.0.0 | ## Resources @@ -163,6 +165,8 @@ No resources. | [enabled\_log](#input\_enabled\_log) | n/a |
list(object({
category_group = optional(string, "allLogs")
category = optional(string, null)
})) | `null` | no |
| [metric](#input\_metric) | n/a | object({
category = optional(string)
enabled = optional(bool)
}) | `null` | no |
| [tags](#input\_tags) | A mapping of tags to assign to the resource. | `map(string)` | `{}` | no |
+| [metric\_alerts](#input\_metric\_alerts) | n/a | map(object({
description = string
action_groups = optional(set(string), [])
enabled = optional(bool, true)
severity = optional(number, 3)
frequency = optional(string, "PT1M")
webhook_properties = optional(map(string))
criterias = optional(list(object({
threshold = number
metric_namespace = string
metric_name = string
aggregation = string
operator = string
dimensions = optional(list(object({
name = string
operator = string
values = list(string)
})))
})), [])
dynamic_criteria = optional(object({
alert_sensitivity = string
metric_name = string
metric_namespace = string
aggregation = string
operator = string
dimensions = optional(list(object({
name = string
operator = string
values = list(string)
})))
}))
})) | `{}` | no |
+| [action\_groups](#input\_action\_groups) | n/a | map(object({
action_group_name = string
short_name = string
arm_role_receivers = optional(set(string))
email_receivers = optional(set(string))
})) | `{}` | no |
## Outputs
@@ -172,4 +176,6 @@ No resources.
| [signalr\_name](#output\_signalr\_name) | n/a |
| [location](#output\_location) | n/a |
| [resource\_group\_name](#output\_resource\_group\_name) | n/a |
+| [metric\_alerts](#output\_metric\_alerts) | n/a |
+| [action\_groups](#output\_action\_groups) | n/a |
diff --git a/examples/complete/README.md b/examples/complete/README.md
index aae104c..d8b5e46 100644
--- a/examples/complete/README.md
+++ b/examples/complete/README.md
@@ -26,6 +26,12 @@ No resources.
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
+| [logical\_product\_family](#input\_logical\_product\_family) | Name of the product family for which the resource is created | `string` | `"launch"` | no |
+| [logical\_product\_service](#input\_logical\_product\_service) | Name of the product service for which the resource is created | `string` | `"signalr"` | no |
+| [environment](#input\_environment) | Environment in which the resource should be provisioned like dev, qa, prod etc. | `string` | `"dev"` | no |
+| [environment\_number](#input\_environment\_number) | The environment count for the respective environment. Defaults to 000. Increments in value of 1 | `string` | `"000"` | no |
+| [resource\_number](#input\_resource\_number) | The resource count for the respective resource. Defaults to 000. Increments in value of 1 | `string` | `"000"` | no |
+| [use\_azure\_region\_abbr](#input\_use\_azure\_region\_abbr) | Abbreviate the region in the resource names | `bool` | `true` | no |
| [region](#input\_region) | Azure Region in which the infra needs to be provisioned | `string` | `"eastus"` | no |
| [log\_analytics\_workspace\_sku](#input\_log\_analytics\_workspace\_sku) | Specifies the SKU of the Log Analytics Workspace. Possible values are Free, PerNode, Premium, Standard, Standalone, Unlimited, CapacityReservation, and PerGB2018 (new SKU as of 2018-04-03). Defaults to PerGB2018. | `string` | `"PerGB2018"` | no |
| [log\_analytics\_workspace\_retention\_in\_days](#input\_log\_analytics\_workspace\_retention\_in\_days) | The workspace data retention in days. Possible values are either 7 (Free Tier only) or range between 30 and 730. | `number` | `"30"` | no |
@@ -33,6 +39,7 @@ No resources.
| [log\_analytics\_destination\_type](#input\_log\_analytics\_destination\_type) | (Optional) Specifies the type of destination for the logs. Possible values are 'Dedicated' or 'AzureDiagnostics'. | `string` | `"AzureDiagnostics"` | no |
| [enabled\_log](#input\_enabled\_log) | n/a | list(object({
category_group = optional(string, "allLogs")
category = optional(string, null)
})) | [| no | | [metric](#input\_metric) | n/a |
{
"category_group": "allLogs"
}
]
object({
category = optional(string, "AllMetrics")
enabled = optional(bool, false)
}) | {
"category": "AllMetrics"
} | no |
+| [metric\_alerts](#input\_metric\_alerts) | n/a | map(object({
description = string
action_groups = optional(set(string), [])
enabled = optional(bool, true)
severity = optional(number, 3)
frequency = optional(string)
webhook_properties = optional(map(string))
criterias = optional(list(object({
threshold = number
metric_namespace = string
metric_name = string
aggregation = string
operator = string
dimensions = optional(list(object({
name = string
operator = string
values = list(string)
})))
})), [])
dynamic_criteria = optional(object({
alert_sensitivity = string
metric_name = string
metric_namespace = string
aggregation = string
operator = string
dimensions = optional(list(object({
name = string
operator = string
values = list(string)
})))
}))
})) | `{}` | no |
| [tags](#input\_tags) | A mapping of tags to assign to the resource. | `map(string)` | `{}` | no |
## Outputs
@@ -43,4 +50,6 @@ No resources.
| [signalr\_name](#output\_signalr\_name) | n/a |
| [location](#output\_location) | n/a |
| [resource\_group\_name](#output\_resource\_group\_name) | n/a |
+| [metric\_alerts](#output\_metric\_alerts) | n/a |
+| [action\_groups](#output\_action\_groups) | n/a |
diff --git a/examples/complete/main.tf b/examples/complete/main.tf
index bd844bb..5cfa3ee 100644
--- a/examples/complete/main.tf
+++ b/examples/complete/main.tf
@@ -13,6 +13,13 @@
module "signalr" {
source = "../.."
+ logical_product_family = var.logical_product_family
+ logical_product_service = var.logical_product_service
+ environment = var.environment
+ environment_number = var.environment_number
+ resource_number = var.resource_number
+ use_azure_region_abbr = var.use_azure_region_abbr
+
signalr_location = var.region
cors_allowed_origins = ["*"]
@@ -26,5 +33,7 @@ module "signalr" {
enabled_log = var.enabled_log
metric = var.metric
+ metric_alerts = var.metric_alerts
+
tags = local.tags
}
diff --git a/examples/complete/outputs.tf b/examples/complete/outputs.tf
index 1090bc0..9bdbda4 100644
--- a/examples/complete/outputs.tf
+++ b/examples/complete/outputs.tf
@@ -25,3 +25,11 @@ output "location" {
output "resource_group_name" {
value = module.signalr.resource_group_name
}
+
+output "metric_alerts" {
+ value = module.signalr.metric_alerts
+}
+
+output "action_groups" {
+ value = module.signalr.action_groups
+}
diff --git a/examples/complete/test.tfvars b/examples/complete/test.tfvars
index 2fcdac3..50b034b 100644
--- a/examples/complete/test.tfvars
+++ b/examples/complete/test.tfvars
@@ -1 +1,21 @@
// empty.
+
+logical_product_family = "launch"
+logical_product_service = "signalr"
+environment = "test"
+environment_number = "001"
+resource_number = "001"
+use_azure_region_abbr = true
+
+metric_alerts = {
+ "SystemErrorsHigh" = {
+ description = "Perecentage of system errors are higher than usual"
+ dynamic_criteria = {
+ alert_sensitivity = "Low"
+ metric_name = "SystemErrors"
+ metric_namespace = "Microsoft.SignalRService/SignalR"
+ aggregation = "Maximum"
+ operator = "GreaterThan"
+ }
+ }
+}
diff --git a/examples/complete/variables.tf b/examples/complete/variables.tf
index 9a7f578..9d82a85 100644
--- a/examples/complete/variables.tf
+++ b/examples/complete/variables.tf
@@ -10,6 +10,46 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+# naming variables
+
+variable "logical_product_family" {
+ description = "Name of the product family for which the resource is created"
+ type = string
+ default = "launch"
+}
+
+variable "logical_product_service" {
+ description = "Name of the product service for which the resource is created"
+ type = string
+ default = "signalr"
+}
+
+variable "environment" {
+ description = "Environment in which the resource should be provisioned like dev, qa, prod etc."
+ type = string
+ default = "dev"
+}
+
+variable "environment_number" {
+ description = "The environment count for the respective environment. Defaults to 000. Increments in value of 1"
+ type = string
+ default = "000"
+}
+
+variable "resource_number" {
+ description = "The resource count for the respective resource. Defaults to 000. Increments in value of 1"
+ type = string
+ default = "000"
+}
+
+variable "use_azure_region_abbr" {
+ description = "Abbreviate the region in the resource names"
+ type = bool
+ default = true
+}
+
+# signalr variables
+
variable "region" {
description = "Azure Region in which the infra needs to be provisioned"
type = string
@@ -63,6 +103,50 @@ variable "metric" {
}
}
+variable "metric_alerts" {
+ type = map(object({
+ description = string
+ action_groups = optional(set(string), [])
+ enabled = optional(bool, true)
+ severity = optional(number, 3)
+ frequency = optional(string)
+ webhook_properties = optional(map(string))
+
+ criterias = optional(list(object({
+ threshold = number
+ metric_namespace = string
+ metric_name = string
+ aggregation = string
+ operator = string
+ dimensions = optional(list(object({
+ name = string
+ operator = string
+ values = list(string)
+ })))
+ })), [])
+
+ dynamic_criteria = optional(object({
+ alert_sensitivity = string
+ metric_name = string
+ metric_namespace = string
+ aggregation = string
+ operator = string
+ dimensions = optional(list(object({
+ name = string
+ operator = string
+ values = list(string)
+ })))
+ }))
+ }))
+ default = {}
+
+ validation {
+ condition = alltrue(
+ [for alert in var.metric_alerts : !(alert.criterias == null && alert.dynamic_criteria == null)],
+ )
+ error_message = "At least one of 'criteria', 'dynamic_criteria' must be defined for all metric alerts"
+ }
+}
variable "tags" {
description = "A mapping of tags to assign to the resource."
type = map(string)
diff --git a/go.mod b/go.mod
index da4b567..b98fb8b 100644
--- a/go.mod
+++ b/go.mod
@@ -5,6 +5,7 @@ go 1.21
require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0
+ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/monitor/armmonitor v0.11.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/signalr/armsignalr v1.2.0
github.com/gruntwork-io/terratest v0.43.12
github.com/launchbynttdata/lcaf-component-terratest v1.0.3
diff --git a/go.sum b/go.sum
index d69fe62..4e4f1ec 100644
--- a/go.sum
+++ b/go.sum
@@ -198,6 +198,8 @@ github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xP
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0 h1:PTFGRSlMKCQelWwxUyYVEUqseBJVemLyqWJjvMyt0do=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0/go.mod h1:LRr2FzBTQlONPPa5HREE5+RjSCTXl7BwOvYOaWTqCaI=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/monitor/armmonitor v0.11.0 h1:Ds0KRF8ggpEGg4Vo42oX1cIt/IfOhHWJBikksZbVxeg=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/monitor/armmonitor v0.11.0/go.mod h1:jj6P8ybImR+5topJ+eH6fgcemSFBmU6/6bFF8KkwuDI=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.1.1 h1:7CBQ+Ei8SP2c6ydQTGCCrS35bDxgTMfoP2miAwK++OU=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.1.1/go.mod h1:c/wcGeGx5FUPbM/JltUYHZcKmigwyVLJlDq+4HdtXaw=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/signalr/armsignalr v1.2.0 h1:Y8CF7FyuVVDyX5W6Azwjj3PpwUZVbXBOCyQytv/0QEA=
diff --git a/main.tf b/main.tf
index dc25225..7a79a00 100644
--- a/main.tf
+++ b/main.tf
@@ -92,3 +92,39 @@ module "diagnostic_setting" {
enabled_log = var.enabled_log
metric = var.metric
}
+
+# This module is used to create an Azure Monitor Action Group.
+module "action_group" {
+ for_each = var.action_groups
+ source = "terraform.registry.launch.nttdata.com/module_primitive/monitor_action_group/azurerm"
+ version = "~> 1.0.0"
+
+ action_group_name = each.key
+ resource_group_name = module.resource_group.name
+ short_name = each.value.short_name
+ tags = var.tags
+ arm_role_receivers = each.value.arm_role_receivers
+ email_receivers = each.value.email_receivers
+ depends_on = [module.resource_group]
+}
+
+module "metric_alert" {
+ for_each = var.metric_alerts
+ source = "terraform.registry.launch.nttdata.com/module_primitive/monitor_metric_alert/azurerm"
+ version = "~> 2.0.0"
+
+ name = each.key
+ resource_group_name = module.resource_group.name
+ scopes = [module.signalr.signalr_id]
+ description = each.value.description
+ frequency = each.value.frequency
+ severity = each.value.severity
+ enabled = each.value.enabled
+ action_group_ids = [for key in each.value.action_groups : module.action_group[key].action_group_id]
+ webhook_properties = each.value.webhook_properties
+
+ criteria = each.value.criterias
+
+ dynamic_criteria = each.value.dynamic_criteria
+ depends_on = [module.resource_group, module.action_group]
+}
diff --git a/outputs.tf b/outputs.tf
index 1090bc0..d10cfc2 100644
--- a/outputs.tf
+++ b/outputs.tf
@@ -25,3 +25,21 @@ output "location" {
output "resource_group_name" {
value = module.signalr.resource_group_name
}
+
+output "metric_alerts" {
+ value = {
+ for key, value in module.metric_alert : key => {
+ id = value.metric_alert_id
+ name = value.name
+ }
+ }
+}
+
+output "action_groups" {
+ value = {
+ for key, value in module.action_group : key => {
+ id = value.action_group_id
+ name = value.name
+ }
+ }
+}
diff --git a/tests/testimpl/test_impl.go b/tests/testimpl/test_impl.go
index bb9a8b5..9d533e6 100644
--- a/tests/testimpl/test_impl.go
+++ b/tests/testimpl/test_impl.go
@@ -9,6 +9,7 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
+ armMetric "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/monitor/armmonitor"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/signalr/armsignalr"
"github.com/gruntwork-io/terratest/modules/terraform"
"github.com/launchbynttdata/lcaf-component-terratest/types"
@@ -33,16 +34,21 @@ func TestSignalRExists(t *testing.T, ctx types.TestContext) {
},
}
- clientFactory, err := armsignalr.NewClientFactory(subscriptionId, credential, &options)
+ signalrClient, err := armsignalr.NewClient(subscriptionId, credential, &options)
if err != nil {
t.Fatalf("failed to create SignalR client: %v", err)
}
+ armMetricAlertsClient, err := armMetric.NewMetricAlertsClient(subscriptionId, credential, &options)
+ if err != nil {
+ t.Fatalf("Error getting Metric Alerts client: %v", err)
+ }
+
t.Run("doesSignalRExist", func(t *testing.T) {
resourceGroupName := terraform.Output(t, ctx.TerratestTerraformOptions(), "resource_group_name")
signalrName := terraform.Output(t, ctx.TerratestTerraformOptions(), "signalr_name")
- res, err := clientFactory.NewClient().Get(context.Background(), resourceGroupName, signalrName, nil)
+ res, err := signalrClient.Get(context.Background(), resourceGroupName, signalrName, nil)
if err != nil {
t.Fatalf("failed to finish the request: %v", err)
}
@@ -50,4 +56,20 @@ func TestSignalRExists(t *testing.T, ctx types.TestContext) {
assert.Equal(t, *res.Name, signalrName)
})
+ t.Run("doesMetricAlertsExist", func(t *testing.T) {
+ resourceGroupName := terraform.Output(t, ctx.TerratestTerraformOptions(), "resource_group_name")
+ metricAlertsMap := terraform.OutputMapOfObjects(t, ctx.TerratestTerraformOptions(), "metric_alerts")
+ var metricAlertsName string
+ for _, v := range metricAlertsMap {
+ metricAlertsName = v.(map[string]interface{})["name"].(string)
+ break // Access the first metric alert and break
+ }
+
+ metricAlerts, err := armMetricAlertsClient.Get(context.Background(), resourceGroupName, metricAlertsName, nil)
+ if err != nil {
+ t.Fatalf("Error getting MetricAlerts: %v", err)
+ }
+
+ assert.Equal(t, metricAlertsName, *metricAlerts.Name)
+ })
}
diff --git a/variables.tf b/variables.tf
index 8aced1a..014ecf0 100644
--- a/variables.tf
+++ b/variables.tf
@@ -246,3 +246,58 @@ variable "tags" {
type = map(string)
default = {}
}
+
+variable "metric_alerts" {
+ type = map(object({
+ description = string
+ action_groups = optional(set(string), [])
+ enabled = optional(bool, true)
+ severity = optional(number, 3)
+ frequency = optional(string, "PT1M")
+ webhook_properties = optional(map(string))
+
+ criterias = optional(list(object({
+ threshold = number
+ metric_namespace = string
+ metric_name = string
+ aggregation = string
+ operator = string
+ dimensions = optional(list(object({
+ name = string
+ operator = string
+ values = list(string)
+ })))
+ })), [])
+
+ dynamic_criteria = optional(object({
+ alert_sensitivity = string
+ metric_name = string
+ metric_namespace = string
+ aggregation = string
+ operator = string
+ dimensions = optional(list(object({
+ name = string
+ operator = string
+ values = list(string)
+ })))
+ }))
+ }))
+ default = {}
+
+ validation {
+ condition = alltrue(
+ [for alert in var.metric_alerts : !(alert.criterias == null && alert.dynamic_criteria == null)],
+ )
+ error_message = "At least one of 'criteria', 'dynamic_criteria' must be defined for all metric alerts"
+ }
+}
+
+variable "action_groups" {
+ type = map(object({
+ action_group_name = string
+ short_name = string
+ arm_role_receivers = optional(set(string))
+ email_receivers = optional(set(string))
+ }))
+ default = {}
+}