diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3ee72b8b..0e12cced 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -591,6 +591,10 @@ repos: args: ["./modules/terraform-aci-monitoring-policy"] - id: terraform-docs-system args: ["./modules/terraform-aci-monitoring-policy/examples/complete"] + - id: terraform-docs-system + args: ["./modules/terraform-aci-monitoring-policy-user-defined"] + - id: terraform-docs-system + args: ["./modules/terraform-aci-monitoring-policy-user-defined/examples/complete"] - id: terraform-docs-system args: ["./modules/terraform-aci-mpls-custom-qos-policy"] - id: terraform-docs-system diff --git a/README.md b/README.md index ec271aa4..b7a33a55 100644 --- a/README.md +++ b/README.md @@ -278,6 +278,7 @@ Additional example repositories: | [aci\_mcp](#module\_aci\_mcp) | ./modules/terraform-aci-mcp | n/a | | [aci\_mcp\_policy](#module\_aci\_mcp\_policy) | ./modules/terraform-aci-mcp-policy | n/a | | [aci\_monitoring\_policy](#module\_aci\_monitoring\_policy) | ./modules/terraform-aci-monitoring-policy | n/a | +| [aci\_monitoring\_policy\_user\_defined](#module\_aci\_monitoring\_policy\_user\_defined) | ./modules/terraform-aci-monitoring-policy-user-defined | n/a | | [aci\_mpls\_custom\_qos\_policy](#module\_aci\_mpls\_custom\_qos\_policy) | ./modules/terraform-aci-mpls-custom-qos-policy | n/a | | [aci\_mst\_policy](#module\_aci\_mst\_policy) | ./modules/terraform-aci-mst-policy | n/a | | [aci\_multicast\_route\_map](#module\_aci\_multicast\_route\_map) | ./modules/terraform-aci-multicast-route-map | n/a | diff --git a/aci_fabric_policies.tf b/aci_fabric_policies.tf index 6a272d53..3b4fbc99 100644 --- a/aci_fabric_policies.tf +++ b/aci_fabric_policies.tf @@ -1001,6 +1001,54 @@ module "aci_monitoring_policy" { ] } +locals { + monitoring_policies = flatten([ + for policy in try(local.fabric_policies.monitoring.policies, []) : { + name = "${policy.name}${local.defaults.apic.fabric_policies.monitoring.policies.name_suffix}" + description = try(policy.description, "") + snmp_trap_policies = [for snmp_policy in try(policy.snmp_traps, []) : { + name = "${snmp_policy.name}${local.defaults.apic.fabric_policies.monitoring.policies.snmp_traps.name_suffix}" + destination_group = try("${snmp_policy.destination_group}${local.defaults.apic.fabric_policies.monitoring.policies.snmp_traps.destination_group_suffix}", "") + }] + syslog_policies = [for syslog_policy in try(policy.syslogs, []) : { + name = "${syslog_policy.name}${local.defaults.apic.fabric_policies.monitoring.policies.syslogs.name_suffix}" + audit = try(syslog_policy.audit, local.defaults.apic.fabric_policies.monitoring.policies.syslogs.audit) + events = try(syslog_policy.events, local.defaults.apic.fabric_policies.monitoring.policies.syslogs.events) + faults = try(syslog_policy.faults, local.defaults.apic.fabric_policies.monitoring.policies.syslogs.faults) + session = try(syslog_policy.session, local.defaults.apic.fabric_policies.monitoring.policies.syslogs.session) + minimum_severity = try(syslog_policy.minimum_severity, local.defaults.apic.fabric_policies.monitoring.policies.syslogs.minimum_severity) + destination_group = try("${syslog_policy.destination_group}${local.defaults.apic.fabric_policies.monitoring.policies.syslogs.destination_group_suffix}", "") + }] + fault_severity_policies = [for policy in try(policy.fault_severity_policies, []) : { + class = policy.class + faults = [for fault in try(policy.faults, []) : { + fault_id = fault.fault_id + initial_severity = try(fault.initial_severity, local.defaults.apic.fabric_policies.monitoring.policies.fault_severity_policies.faults.initial_severity) + target_severity = try(fault.target_severity, local.defaults.apic.fabric_policies.monitoring.policies.fault_severity_policies.faults.target_severity) + description = try(fault.description, "") + }] + }] + } + ]) +} + +module "aci_monitoring_policy_user_defined" { + source = "./modules/terraform-aci-monitoring-policy-user-defined" + + for_each = { for pol in local.monitoring_policies : pol.name => pol if local.modules.aci_monitoring_policy_user_defined && var.manage_fabric_policies } + + name = each.value.name + description = each.value.description + snmp_trap_policies = each.value.snmp_trap_policies + syslog_policies = each.value.syslog_policies + fault_severity_policies = each.value.fault_severity_policies + + depends_on = [ + module.aci_snmp_trap_policy, + module.aci_syslog_policy, + ] +} + module "aci_management_access_policy" { source = "./modules/terraform-aci-management-access-policy" diff --git a/defaults/defaults.yaml b/defaults/defaults.yaml index 573be829..ee4520e6 100644 --- a/defaults/defaults.yaml +++ b/defaults/defaults.yaml @@ -385,6 +385,7 @@ defaults: monitoring: snmp_traps: name_suffix: "" + destination_group_suffix: "" destinations: port: 162 version: v2c @@ -405,12 +406,39 @@ defaults: local_severity: information console_admin_state: true console_severity: alerts + destination_group_suffix: "" destinations: port: 514 facility: local7 severity: warnings admin_state: true mgmt_epg: inb + policies: + name_suffix: "" + fault_severity_policies: + name_suffix: "" + faults: + initial_severity: "inherit" + target_severity: "inherit" + snmp_traps: + name_suffix: "" + destination_group_suffix: "" + syslogs: + name_suffix: "" + audit: true + events: true + faults: true + session: false + minimum_severity: warnings + format: aci + show_millisecond: false + show_timezone: false + admin_state: true + local_admin_state: true + local_severity: information + console_admin_state: true + console_severity: alerts + destination_group_suffix: "" span: destination_groups: name_suffix: "" diff --git a/defaults/modules.yaml b/defaults/modules.yaml index 37650358..a3c9f006 100644 --- a/defaults/modules.yaml +++ b/defaults/modules.yaml @@ -113,6 +113,7 @@ modules: aci_mcp: true aci_mcp_policy: true aci_monitoring_policy: true + aci_monitoring_policy_user_defined: true aci_access_monitoring_policy: true aci_mpls_custom_qos_policy: true aci_mst_policy: true diff --git a/modules/terraform-aci-monitoring-policy-user-defined/.terraform-docs.yml b/modules/terraform-aci-monitoring-policy-user-defined/.terraform-docs.yml new file mode 100644 index 00000000..a1fa0d82 --- /dev/null +++ b/modules/terraform-aci-monitoring-policy-user-defined/.terraform-docs.yml @@ -0,0 +1,34 @@ +version: '>= 0.14.0' + +formatter: markdown table + +content: |- + # Terraform ACI User-Defined Monitoring Policy Module + + Manages ACI User-Defined Monitoring Policy + + Location in GUI: + `Fabric` » `Fabric Policies` » `Policies` » `Monitoring` + + ## Examples + + ```hcl + {{ include "./examples/complete/main.tf" }} + ``` + + {{ .Requirements }} + + {{ .Providers }} + + {{ .Inputs }} + + {{ .Outputs }} + + {{ .Resources }} + +output: + file: README.md + mode: replace + +sort: + enabled: false diff --git a/modules/terraform-aci-monitoring-policy-user-defined/README.md b/modules/terraform-aci-monitoring-policy-user-defined/README.md new file mode 100644 index 00000000..a03a1c3b --- /dev/null +++ b/modules/terraform-aci-monitoring-policy-user-defined/README.md @@ -0,0 +1,83 @@ + +# Terraform ACI User-Defined Monitoring Policy Module + +Manages ACI User-Defined Monitoring Policy + +Location in GUI: +`Fabric` » `Fabric Policies` » `Policies` » `Monitoring` + +## Examples + +```hcl +module "aci_monitoring_policy_user_defined" { + source = "netascode/nac-aci/aci//modules/terraform-aci-monitoring-policy-user-defined" + version = "> 1.0.1" + + name = "MON1" + snmp_trap_policies = [{ + name = "SNMP_1" + destination_group = "SNMP_DEST_GROUP1" + }] + syslog_policies = [{ + name = "SYSLOG1" + audit = false + events = false + faults = false + session = true + minimum_severity = "alerts" + destination_group = "SYSLOG_DEST_GROUP1" + }] + fault_severity_policies = [{ + class = "snmpClient" + faults = [{ + fault_id = "F1368" + description = "Fault 1368 nice description" + initial_severity = "critical" + target_severity = "inherit" + }] + }] +} +``` + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.3.0 | +| [aci](#requirement\_aci) | >= 2.15.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aci](#provider\_aci) | >= 2.15.0 | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [name](#input\_name) | Track List name. | `string` | n/a | yes | +| [description](#input\_description) | Description. | `string` | `""` | no | +| [snmp\_trap\_policies](#input\_snmp\_trap\_policies) | List of SNMP trap policies. |
list(object({
name = string
destination_group = optional(string, "")
})) | `[]` | no |
+| [syslog\_policies](#input\_syslog\_policies) | List of syslog policies. Default value `audit`: true. Default value `events`: true. Default value `faults`: true. Default value `session`: false. Default value `minimum_severity`: `warnings`. | list(object({
name = string
audit = optional(bool, true)
events = optional(bool, true)
faults = optional(bool, true)
session = optional(bool, false)
minimum_severity = optional(string, "warnings")
destination_group = optional(string, "")
})) | `[]` | no |
+| [fault\_severity\_policies](#input\_fault\_severity\_policies) | List of Fault Severity Assignment Policies. | list(object({
class = string
faults = list(object({
fault_id = string
initial_severity = optional(string, "inherit")
target_severity = optional(string, "inherit")
description = optional(string, "")
}))
})) | `[]` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [dn](#output\_dn) | Distinguished name of Fabric `monFabricPol` object. |
+| [name](#output\_name) | User-Defined Fabric Monitoring Policy name. |
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [aci_rest_managed.faultSevAsnP](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource |
+| [aci_rest_managed.monFabricPol](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource |
+| [aci_rest_managed.monFabricTarget](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource |
+| [aci_rest_managed.snmpRsDestGroup](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource |
+| [aci_rest_managed.snmpSrc](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource |
+| [aci_rest_managed.syslogRsDestGroup](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource |
+| [aci_rest_managed.syslogSrc](https://registry.terraform.io/providers/CiscoDevNet/aci/latest/docs/resources/rest_managed) | resource |
+
\ No newline at end of file
diff --git a/modules/terraform-aci-monitoring-policy-user-defined/examples/complete/.terraform-docs.yml b/modules/terraform-aci-monitoring-policy-user-defined/examples/complete/.terraform-docs.yml
new file mode 100644
index 00000000..2993d1ab
--- /dev/null
+++ b/modules/terraform-aci-monitoring-policy-user-defined/examples/complete/.terraform-docs.yml
@@ -0,0 +1,24 @@
+version: '>= 0.14.0'
+
+formatter: markdown table
+
+content: |-
+ # Monitoring Policy Example
+
+ To run this example you need to execute:
+
+ ```bash
+ $ terraform init
+ $ terraform plan
+ $ terraform apply
+ ```
+
+ Note that this example will create resources. Resources can be destroyed with `terraform destroy`.
+
+ ```hcl
+ {{ include "./main.tf" }}
+ ```
+
+output:
+ file: README.md
+ mode: replace
diff --git a/modules/terraform-aci-monitoring-policy-user-defined/examples/complete/README.md b/modules/terraform-aci-monitoring-policy-user-defined/examples/complete/README.md
new file mode 100644
index 00000000..6cb7b151
--- /dev/null
+++ b/modules/terraform-aci-monitoring-policy-user-defined/examples/complete/README.md
@@ -0,0 +1,44 @@
+
+# Monitoring Policy Example
+
+To run this example you need to execute:
+
+```bash
+$ terraform init
+$ terraform plan
+$ terraform apply
+```
+
+Note that this example will create resources. Resources can be destroyed with `terraform destroy`.
+
+```hcl
+module "aci_monitoring_policy_user_defined" {
+ source = "netascode/nac-aci/aci//modules/terraform-aci-monitoring-policy-user-defined"
+ version = "> 1.0.1"
+
+ name = "MON1"
+ snmp_trap_policies = [{
+ name = "SNMP_1"
+ destination_group = "SNMP_DEST_GROUP1"
+ }]
+ syslog_policies = [{
+ name = "SYSLOG1"
+ audit = false
+ events = false
+ faults = false
+ session = true
+ minimum_severity = "alerts"
+ destination_group = "SYSLOG_DEST_GROUP1"
+ }]
+ fault_severity_policies = [{
+ class = "snmpClient"
+ faults = [{
+ fault_id = "F1368"
+ description = "Fault 1368 nice description"
+ initial_severity = "critical"
+ target_severity = "inherit"
+ }]
+ }]
+}
+```
+
\ No newline at end of file
diff --git a/modules/terraform-aci-monitoring-policy-user-defined/examples/complete/main.tf b/modules/terraform-aci-monitoring-policy-user-defined/examples/complete/main.tf
new file mode 100644
index 00000000..ad538449
--- /dev/null
+++ b/modules/terraform-aci-monitoring-policy-user-defined/examples/complete/main.tf
@@ -0,0 +1,28 @@
+module "aci_monitoring_policy_user_defined" {
+ source = "netascode/nac-aci/aci//modules/terraform-aci-monitoring-policy-user-defined"
+ version = "> 1.0.1"
+
+ name = "MON1"
+ snmp_trap_policies = [{
+ name = "SNMP_1"
+ destination_group = "SNMP_DEST_GROUP1"
+ }]
+ syslog_policies = [{
+ name = "SYSLOG1"
+ audit = false
+ events = false
+ faults = false
+ session = true
+ minimum_severity = "alerts"
+ destination_group = "SYSLOG_DEST_GROUP1"
+ }]
+ fault_severity_policies = [{
+ class = "snmpClient"
+ faults = [{
+ fault_id = "F1368"
+ description = "Fault 1368 nice description"
+ initial_severity = "critical"
+ target_severity = "inherit"
+ }]
+ }]
+}
diff --git a/modules/terraform-aci-monitoring-policy-user-defined/examples/complete/versions.tf b/modules/terraform-aci-monitoring-policy-user-defined/examples/complete/versions.tf
new file mode 100644
index 00000000..f4938bdc
--- /dev/null
+++ b/modules/terraform-aci-monitoring-policy-user-defined/examples/complete/versions.tf
@@ -0,0 +1,11 @@
+
+terraform {
+ required_version = ">= 1.3.0"
+
+ required_providers {
+ aci = {
+ source = "CiscoDevNet/aci"
+ version = ">= 2.15.0"
+ }
+ }
+}
diff --git a/modules/terraform-aci-monitoring-policy-user-defined/main.tf b/modules/terraform-aci-monitoring-policy-user-defined/main.tf
new file mode 100644
index 00000000..21e13b0c
--- /dev/null
+++ b/modules/terraform-aci-monitoring-policy-user-defined/main.tf
@@ -0,0 +1,81 @@
+locals {
+ faults = flatten([
+ for policy in var.fault_severity_policies : [
+ for fault in policy.faults : {
+ class = policy.class
+ fault_id = fault.fault_id
+ initial_severity = fault.initial_severity
+ target_severity = fault.target_severity
+ description = fault.description
+ }
+ ]
+ ])
+}
+
+resource "aci_rest_managed" "monFabricPol" {
+ dn = "uni/fabric/monfab-${var.name}"
+ class_name = "monFabricPol"
+ content = {
+ name = var.name
+ descr = var.description
+ }
+}
+
+resource "aci_rest_managed" "snmpSrc" {
+ for_each = { for s in var.snmp_trap_policies : s.name => s }
+ dn = "${aci_rest_managed.monFabricPol.dn}/snmpsrc-${each.value.name}"
+ class_name = "snmpSrc"
+ content = {
+ name = each.value.name
+ }
+}
+
+resource "aci_rest_managed" "snmpRsDestGroup" {
+ for_each = { for s in var.snmp_trap_policies : s.name => s if s.destination_group != null }
+ dn = "${aci_rest_managed.snmpSrc[each.value.name].dn}/rsdestGroup"
+ class_name = "snmpRsDestGroup"
+ content = {
+ tDn = "uni/fabric/snmpgroup-${each.value.destination_group}"
+ }
+}
+
+resource "aci_rest_managed" "syslogSrc" {
+ for_each = { for s in var.syslog_policies : s.name => s }
+ dn = "${aci_rest_managed.monFabricPol.dn}/slsrc-${each.value.name}"
+ class_name = "syslogSrc"
+ content = {
+ name = each.value.name
+ incl = join(",", concat(each.value.audit == true && each.value.events == true && each.value.faults == true && each.value.session == true ? ["all"] : [], each.value.audit == true ? ["audit"] : [], each.value.events == true ? ["events"] : [], each.value.faults == true ? ["faults"] : [], each.value.session == true ? ["session"] : []))
+ minSev = each.value.minimum_severity
+ }
+}
+
+resource "aci_rest_managed" "syslogRsDestGroup" {
+ for_each = { for s in var.syslog_policies : s.name => s if s.destination_group != null }
+ dn = "${aci_rest_managed.syslogSrc[each.value.name].dn}/rsdestGroup"
+ class_name = "syslogRsDestGroup"
+ content = {
+ tDn = "uni/fabric/slgroup-${each.value.destination_group}"
+ }
+}
+
+resource "aci_rest_managed" "monFabricTarget" {
+ for_each = { for s in var.fault_severity_policies : s.class => s }
+ dn = "${aci_rest_managed.monFabricPol.dn}/tarfab-${each.value.class}"
+ class_name = "monFabricTarget"
+ content = {
+ scope = each.value.class
+ }
+}
+
+resource "aci_rest_managed" "faultSevAsnP" {
+ for_each = { for f in local.faults : f.fault_id => f }
+ dn = "${aci_rest_managed.monFabricTarget[each.value.class].dn}/fsevp-${each.value.fault_id}"
+ class_name = "faultSevAsnP"
+ content = {
+ code = each.value.fault_id
+ initial = each.value.initial_severity
+ target = each.value.target_severity
+ descr = each.value.description
+ }
+}
diff --git a/modules/terraform-aci-monitoring-policy-user-defined/outputs.tf b/modules/terraform-aci-monitoring-policy-user-defined/outputs.tf
new file mode 100644
index 00000000..b514784c
--- /dev/null
+++ b/modules/terraform-aci-monitoring-policy-user-defined/outputs.tf
@@ -0,0 +1,9 @@
+output "dn" {
+ value = aci_rest_managed.monFabricPol.id
+ description = "Distinguished name of Fabric `monFabricPol` object."
+}
+
+output "name" {
+ value = aci_rest_managed.monFabricPol.content.name
+ description = "User-Defined Fabric Monitoring Policy name."
+}
\ No newline at end of file
diff --git a/modules/terraform-aci-monitoring-policy-user-defined/variables.tf b/modules/terraform-aci-monitoring-policy-user-defined/variables.tf
new file mode 100644
index 00000000..3d91406b
--- /dev/null
+++ b/modules/terraform-aci-monitoring-policy-user-defined/variables.tf
@@ -0,0 +1,135 @@
+variable "name" {
+ description = "Track List name."
+ type = string
+
+ validation {
+ condition = can(regex("^[a-zA-Z0-9_.:-]{0,64}$", var.name))
+ error_message = "Allowed characters: `a`-`z`, `A`-`Z`, `0`-`9`, `_`, `.`, `:`, `-`. Maximum characters: 64."
+ }
+}
+
+variable "description" {
+ description = "Description."
+ type = string
+ default = ""
+
+ validation {
+ condition = can(regex("^[a-zA-Z0-9\\\\!#$%()*,-./:;@ _{|}~?&+]{0,128}$", var.description))
+ error_message = "Allowed characters: `a`-`z`, `A`-`Z`, `0`-`9`, `\\`, `!`, `#`, `$`, `%`, `(`, `)`, `*`, `,`, `-`, `.`, `/`, `:`, `;`, `@`, ` `, `_`, `{`, `|`, }`, `~`, `?`, `&`, `+`. Maximum characters: 128."
+ }
+}
+
+variable "snmp_trap_policies" {
+ description = "List of SNMP trap policies."
+ type = list(object({
+ name = string
+ destination_group = optional(string, "")
+ }))
+ default = []
+
+ validation {
+ condition = alltrue([
+ for snmp in var.snmp_trap_policies : can(regex("^[a-zA-Z0-9_.:-]{0,64}$", snmp.name))
+ ])
+ error_message = "Allowed characters: `a`-`z`, `A`-`Z`, `0`-`9`, `_`, `.`, `:`, `-`. Maximum characters: 64."
+ }
+
+ validation {
+ condition = alltrue([
+ for snmp in var.snmp_trap_policies : can(regex("^[a-zA-Z0-9_.:-]{0,64}$", snmp.destination_group))
+ ])
+ error_message = "Allowed characters: `a`-`z`, `A`-`Z`, `0`-`9`, `_`, `.`, `:`, `-`. Maximum characters: 64."
+ }
+}
+
+variable "syslog_policies" {
+ description = "List of syslog policies. Default value `audit`: true. Default value `events`: true. Default value `faults`: true. Default value `session`: false. Default value `minimum_severity`: `warnings`."
+ type = list(object({
+ name = string
+ audit = optional(bool, true)
+ events = optional(bool, true)
+ faults = optional(bool, true)
+ session = optional(bool, false)
+ minimum_severity = optional(string, "warnings")
+ destination_group = optional(string, "")
+ }))
+ default = []
+
+ validation {
+ condition = alltrue([
+ for syslog in var.syslog_policies : can(regex("^[a-zA-Z0-9_.:-]{0,64}$", syslog.name))
+ ])
+ error_message = "Allowed characters `name`: `a`-`z`, `A`-`Z`, `0`-`9`, `_`, `.`, `:`, `-`. Maximum characters: 64."
+ }
+
+ validation {
+ condition = alltrue([
+ for syslog in var.syslog_policies : contains(["emergencies", "alerts", "critical", "errors", "warnings", "notifications", "information", "debugging"], syslog.minimum_severity)
+ ])
+ error_message = "`minimum_severity`: Allowed values are `emergencies`, `alerts`, `critical`, `errors`, `warnings`, `notifications`, `information` or `debugging`."
+ }
+
+ validation {
+ condition = alltrue([
+ for syslog in var.syslog_policies : can(regex("^[a-zA-Z0-9_.:-]{0,64}$", syslog.destination_group))
+ ])
+ error_message = "Allowed characters: `a`-`z`, `A`-`Z`, `0`-`9`, `_`, `.`, `:`, `-`. Maximum characters: 64."
+ }
+}
+
+variable "fault_severity_policies" {
+ description = "List of Fault Severity Assignment Policies."
+ type = list(object({
+ class = string
+ faults = list(object({
+ fault_id = string
+ initial_severity = optional(string, "inherit")
+ target_severity = optional(string, "inherit")
+ description = optional(string, "")
+ }))
+ }))
+ default = []
+
+ validation {
+ condition = alltrue([
+ for policy in var.fault_severity_policies : can(regex("^[a-zA-Z0-9]{0,64}$", policy.class))
+ ])
+ error_message = "`class`. Allowed characters: `a`-`z`, `A`-`Z`, `0`-`9`, `_`, `.`, `:`, `-`. Maximum characters: 64."
+ }
+ validation {
+ condition = alltrue(flatten([
+ for policy in var.fault_severity_policies : [
+ for fault in policy.faults : fault.description == null || try(can(regex("^[a-zA-Z0-9\\\\!#$%()*,-./:;@ _{|}~?&+]{0,128}$", fault.description)), false)
+ ]
+ ]))
+ error_message = "`faults.description`: Allowed characters: `a`-`z`, `A`-`Z`, `0`-`9`, `\\`, `!`, `#`, `$`, `%`, `(`, `)`, `*`, `,`, `-`, `.`, `/`, `:`, `;`, `@`, ` `, `_`, `{`, `|`, `}`, `~`, `?`, `&`, `+`. Maximum characters: 128."
+ }
+
+ validation {
+ condition = alltrue(flatten([
+ for policy in var.fault_severity_policies : [
+ for fault in policy.faults : contains(["warning", "minor", "major", "critical", "squelched", "inherit"], fault.initial_severity)
+ ]
+ ]))
+ error_message = "`initial_severity`: Allowed values are `warning`, `minor`, `major`, `critical`, `squelched` or `inherit`."
+ }
+
+ validation {
+ condition = alltrue(flatten([
+ for policy in var.fault_severity_policies : [
+ for fault in policy.faults : contains(["warning", "minor", "major", "critical", "inherit"], fault.target_severity)
+ ]
+ ]))
+ error_message = "`target_severity`: Allowed values are `warning`, `minor`, `major`, `critical` or `inherit`."
+ }
+
+ validation {
+ condition = alltrue(flatten([
+ for policy in var.fault_severity_policies : [
+ for fault in policy.faults : index(["warning", "minor", "major", "critical", "", "inherit"], fault.target_severity) >= index(["warning", "minor", "major", "critical", "squelched", "inherit"], fault.initial_severity)
+ ]
+ ]))
+ error_message = "`target_severity` level must be equal or higher than `initial_severity` level."
+ }
+
+}
\ No newline at end of file
diff --git a/modules/terraform-aci-monitoring-policy-user-defined/versions.tf b/modules/terraform-aci-monitoring-policy-user-defined/versions.tf
new file mode 100644
index 00000000..f4938bdc
--- /dev/null
+++ b/modules/terraform-aci-monitoring-policy-user-defined/versions.tf
@@ -0,0 +1,11 @@
+
+terraform {
+ required_version = ">= 1.3.0"
+
+ required_providers {
+ aci = {
+ source = "CiscoDevNet/aci"
+ version = ">= 2.15.0"
+ }
+ }
+}