diff --git a/.gitignore b/.gitignore
index be72cee..2ca9f68 100644
--- a/.gitignore
+++ b/.gitignore
@@ -396,5 +396,8 @@ $RECYCLE.BIN/
*.tfstate.backup
*.tfplan
+# DS_Store
+.DS_Store
+
# Claude
.claude
\ No newline at end of file
diff --git a/README.md b/README.md
index d854978..f6c0390 100644
--- a/README.md
+++ b/README.md
@@ -112,8 +112,10 @@ $ export TF_CLI_ARGS_apply="-parallelism=1"
| [manage\_site\_connectivity](#input\_manage\_site\_connectivity) | Flag to indicate if site connectivity be managed. | `bool` | `false` | no |
| [manage\_sites](#input\_manage\_sites) | Flag to indicate if sites should be managed. | `bool` | `false` | no |
| [manage\_system](#input\_manage\_system) | Flag to indicate if system level configuration should be managed. | `bool` | `false` | no |
+| [manage\_tenant\_templates](#input\_manage\_tenant\_templates) | Flag to indicate if tenant templates should be managed. | `bool` | `false` | no |
| [manage\_tenants](#input\_manage\_tenants) | Flag to indicate if tenants be managed. | `bool` | `false` | no |
| [managed\_schemas](#input\_managed\_schemas) | List of schema names to be managed. By default all schemas will be managed. | `list(string)` | `[]` | no |
+| [managed\_tenant\_templates](#input\_managed\_tenant\_templates) | List of tenant template names to be managed. By default all tenant templates will be managed. | `list(string)` | `[]` | no |
| [managed\_tenants](#input\_managed\_tenants) | List of tenant names to be managed. By default all tenants will be managed. | `list(string)` | `[]` | no |
| [model](#input\_model) | As an alternative to YAML files, a native Terraform data structure can be provided as well. | `map(any)` | `{}` | no |
| [write\_default\_values\_file](#input\_write\_default\_values\_file) | Write all default values to a YAML file. Value is a path pointing to the file to be created. | `string` | `""` | no |
@@ -180,6 +182,9 @@ $ export TF_CLI_ARGS_apply="-parallelism=1"
| [mso_schema_template_deploy_ndo.template](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/resources/schema_template_deploy_ndo) | resource |
| [mso_schema_template_deploy_ndo.template2](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/resources/schema_template_deploy_ndo) | resource |
| [mso_schema_template_deploy_ndo.template3](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/resources/schema_template_deploy_ndo) | resource |
+| [mso_schema_template_deploy_ndo.tenant_template](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/resources/schema_template_deploy_ndo) | resource |
+| [mso_schema_template_deploy_ndo.tenant_template2](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/resources/schema_template_deploy_ndo) | resource |
+| [mso_schema_template_deploy_ndo.tenant_template3](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/resources/schema_template_deploy_ndo) | resource |
| [mso_schema_template_external_epg.schema_template_external_epg](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/resources/schema_template_external_epg) | resource |
| [mso_schema_template_external_epg_contract.schema_template_external_epg_contract](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/resources/schema_template_external_epg_contract) | resource |
| [mso_schema_template_external_epg_selector.schema_template_external_epg_selector](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/resources/schema_template_external_epg_selector) | resource |
@@ -190,15 +195,24 @@ $ export TF_CLI_ARGS_apply="-parallelism=1"
| [mso_schema_template_vrf.schema_template_vrf](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/resources/schema_template_vrf) | resource |
| [mso_schema_template_vrf_contract.schema_template_vrf_contract](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/resources/schema_template_vrf_contract) | resource |
| [mso_site.site](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/resources/site) | resource |
+| [mso_template.tenant_template](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/resources/template) | resource |
| [mso_tenant.tenant](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/resources/tenant) | resource |
+| [mso_tenant_policies_dhcp_relay_policy.tenant_policies_dhcp_relay_policy](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/resources/tenant_policies_dhcp_relay_policy) | resource |
+| [mso_tenant_policies_ipsla_monitoring_policy.tenant_policies_ipsla_monitoring_policy](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/resources/tenant_policies_ipsla_monitoring_policy) | resource |
+| [mso_tenant_policies_route_map_policy_multicast.tenant_policies_route_map_policy_multicast](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/resources/tenant_policies_route_map_policy_multicast) | resource |
| [terraform_data.validation](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/resources/data) | resource |
| [mso_rest.ndo_version](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/data-sources/rest) | data source |
| [mso_rest.schemas](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/data-sources/rest) | data source |
| [mso_rest.system_config](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/data-sources/rest) | data source |
+| [mso_rest.templates](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/data-sources/rest) | data source |
+| [mso_schema_template_anp_epg.schema_template_anp_epg](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/data-sources/schema_template_anp_epg) | data source |
+| [mso_schema_template_external_epg.schema_template_external_epg](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/data-sources/schema_template_external_epg) | data source |
| [mso_site.site](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/data-sources/site) | data source |
| [mso_site.template_site](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/data-sources/site) | data source |
| [mso_site.tenant_site](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/data-sources/site) | data source |
+| [mso_site.tenant_templates_site](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/data-sources/site) | data source |
| [mso_tenant.template_tenant](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/data-sources/tenant) | data source |
+| [mso_tenant.tenant_templates_tenant](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/data-sources/tenant) | data source |
| [mso_user.tenant_user](https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/data-sources/user) | data source |
## Modules
diff --git a/defaults/defaults.yaml b/defaults/defaults.yaml
index 3085dc1..d192d43 100644
--- a/defaults/defaults.yaml
+++ b/defaults/defaults.yaml
@@ -180,3 +180,27 @@ defaults:
logical_interface_name_suffix: ""
redirect_policy_name_suffix: ""
node_type: "firewall"
+ tenant_templates:
+ tenant_policies:
+ dhcp_relay_policies:
+ name_suffix: ""
+ providers:
+ use_server_vrf: false
+ ip_sla_policies:
+ name_suffix: ""
+ multiplier: 3
+ frequency: 60
+ sla_type: icmp
+ port: 0
+ http_version: HTTP10
+ http_uri: /
+ request_data_size: 28
+ timeout: 900
+ threshold: 900
+ type_of_service: 0
+ ipv6_traffic_class: 0
+ multicast_route_maps:
+ name_suffix: ""
+ entries:
+ rp_ip: 0.0.0.0
+ action: permit
diff --git a/main.tf b/main.tf
index 717d43c..e8a0b50 100644
--- a/main.tf
+++ b/main.tf
@@ -4,6 +4,7 @@ locals {
tenants = [for tenant in try(local.ndo.tenants, []) : tenant if var.manage_tenants && (length(var.managed_tenants) == 0 || contains(var.managed_tenants, tenant.name))]
ndo_version_full = jsondecode(data.mso_rest.ndo_version.content).version
ndo_version = regex("^[0-9]+[.][0-9]+", local.ndo_version_full)
+ tenant_templates = [for template in try(local.ndo.tenant_templates.tenant_policies, []) : template if var.manage_tenant_templates && (length(var.managed_tenant_templates) == 0 || contains(var.managed_tenant_templates, template.name))]
}
data "mso_rest" "ndo_version" {
diff --git a/ndo_deploy_templates.tf b/ndo_deploy_templates.tf
index 47370e0..04bef11 100644
--- a/ndo_deploy_templates.tf
+++ b/ndo_deploy_templates.tf
@@ -12,6 +12,7 @@ locals {
])
}
+
resource "mso_schema_template_deploy_ndo" "template" {
for_each = { for template in local.deploy_templates : template.key => template if var.deploy_templates && template.deploy_order == 1 }
schema_id = var.manage_schemas ? mso_schema.schema[each.value.schema_name].id : local.schema_ids[each.value.schema_name].id
@@ -82,3 +83,49 @@ resource "mso_schema_template_deploy_ndo" "template3" {
mso_schema_template_deploy_ndo.template2,
]
}
+
+locals {
+ unmanaged_templates = [for template in try(local.ndo.tenant_templates, []) : template if !var.manage_tenant_templates && var.deploy_templates]
+ deploy_tenant_templates = flatten([
+ for template in concat(local.tenant_templates, local.unmanaged_templates) : {
+ key = template.name
+ template_name = template.name
+ deploy_order = try(template.deploy_order, 1)
+ }
+ ])
+}
+resource "mso_schema_template_deploy_ndo" "tenant_template" {
+ for_each = { for template in local.deploy_tenant_templates : template.key => template if var.deploy_templates && template.deploy_order == 1 }
+ template_id = var.manage_tenant_templates ? mso_template.tenant_template[each.value.template_name].id : local.template_ids[each.value.template_name].id
+ template_type = "tenant"
+ template_name = each.value.template_name
+
+ depends_on = [
+ mso_template.tenant_template,
+ mso_tenant_policies_dhcp_relay_policy.tenant_policies_dhcp_relay_policy,
+ mso_tenant_policies_ipsla_monitoring_policy.tenant_policies_ipsla_monitoring_policy,
+ mso_tenant_policies_route_map_policy_multicast.tenant_policies_route_map_policy_multicast,
+ ]
+}
+
+resource "mso_schema_template_deploy_ndo" "tenant_template2" {
+ for_each = { for template in local.deploy_tenant_templates : template.key => template if var.deploy_templates && template.deploy_order == 2 }
+ template_id = var.manage_tenant_templates ? mso_template.tenant_template[each.value.template_name].id : local.template_ids[each.value.template_name].id
+ template_type = "tenant"
+ template_name = each.value.template_name
+
+ depends_on = [mso_schema_template_deploy_ndo.tenant_template]
+}
+
+resource "mso_schema_template_deploy_ndo" "tenant_template3" {
+ for_each = { for template in local.deploy_tenant_templates : template.key => template if var.deploy_templates && template.deploy_order == 3 }
+ template_id = var.manage_tenant_templates ? mso_template.tenant_template[each.value.template_name].id : local.template_ids[each.value.template_name].id
+ template_type = "tenant"
+ template_name = each.value.template_name
+
+ depends_on = [
+ mso_schema_template_deploy_ndo.tenant_template,
+ mso_schema_template_deploy_ndo.tenant_template2,
+ ]
+}
+
diff --git a/ndo_tenant_templates.tf b/ndo_tenant_templates.tf
new file mode 100644
index 0000000..5f7990e
--- /dev/null
+++ b/ndo_tenant_templates.tf
@@ -0,0 +1,219 @@
+locals {
+ tenant_templates_tenants = [
+ for template in local.tenant_templates : template.tenant
+ ]
+ template_ids = { for template in try(jsondecode(data.mso_rest.templates.content), []) : template.templateName => { "id" : template.templateId } if template.templateType == "tenantPolicy" }
+}
+
+data "mso_rest" "templates" {
+ path = "api/v1/templates/summaries"
+}
+
+data "mso_tenant" "tenant_templates_tenant" {
+ for_each = toset([for tenant in distinct(local.tenant_templates_tenants) : tenant if !contains(local.managed_tenants, tenant) && var.manage_tenant_templates])
+ name = each.value
+}
+
+locals {
+ tenant_templates_sites = flatten(distinct([
+ for template in local.tenant_templates : [
+ for site in try(template.sites, []) : {
+ key = "${template.name}/${site}"
+ template_name = template.name
+ site_name = site
+ }
+ ]
+ ]))
+}
+
+data "mso_site" "tenant_templates_site" {
+ for_each = toset(distinct([for site in local.tenant_templates_sites : site.site_name if !var.manage_sites && var.manage_tenant_templates]))
+ name = each.value
+}
+
+locals {
+ tenant_policies = flatten([
+ for template in local.tenant_templates : [{
+ name = template.name
+ tenant = contains(local.managed_tenants, template.tenant) ? mso_tenant.tenant[template.tenant].id : data.mso_tenant.tenant_templates_tenant[template.tenant].id
+ sites = [for site in try(template.sites, []) : var.manage_sites ? mso_site.site[site].id : data.mso_site.tenant_templates_site[site].id] }]
+ ])
+}
+
+resource "mso_template" "tenant_template" {
+ for_each = { for template in local.tenant_policies : template.name => template }
+ template_name = each.value.name
+ template_type = "tenant"
+ tenant_id = each.value.tenant
+ sites = each.value.sites
+}
+
+locals {
+ dhcp_provider_epgs = flatten([
+ for template in local.tenant_templates : [
+ for policy in try(template.dhcp_relay_policies, []) : [
+ for provider in try(policy.providers, []) : {
+ key = "${provider.schema}/${provider.template}/${provider.application_profile}/${provider.endpoint_group}"
+ schema = provider.schema
+ template = provider.template
+ application_profile = try(provider.application_profile, null)
+ endpoint_group = try(provider.endpoint_group, null)
+ } if provider.type == "epg"
+ ]
+ ]
+ ])
+ dhcp_provider_external_epgs = flatten([
+ for template in local.tenant_templates : [
+ for policy in try(template.dhcp_relay_policies, []) : [
+ for provider in try(policy.providers, []) : {
+ key = "${provider.schema}/${provider.template}/${provider.external_endpoint_group}"
+ schema = provider.schema
+ template = provider.template
+ external_endpoint_group = try(provider.external_endpoint_group, null)
+ } if provider.type == "external_epg"
+ ]
+ ]
+ ])
+}
+
+data "mso_schema_template_anp_epg" "schema_template_anp_epg" {
+ for_each = { for provider in distinct(local.dhcp_provider_epgs) : provider.key => provider if(!var.manage_schemas || (var.manage_schemas && !contains(local.managed_schemas, provider.schema))) }
+ schema_id = local.schema_ids[each.value.schema].id
+ template_name = each.value.template
+ anp_name = each.value.application_profile
+ name = each.value.endpoint_group
+}
+
+data "mso_schema_template_external_epg" "schema_template_external_epg" {
+ for_each = { for provider in distinct(local.dhcp_provider_external_epgs) : provider.key => provider if(!var.manage_schemas || (var.manage_schemas && !contains(local.managed_schemas, provider.schema))) }
+ schema_id = local.schema_ids[each.value.schema].id
+ template_name = each.value.template
+ external_epg_name = each.value.external_endpoint_group
+}
+
+locals {
+ dhcp_relay_policies = flatten([
+ for template in local.tenant_templates : [
+ for policy in try(template.dhcp_relay_policies, []) : {
+ name = policy.name
+ template_name = template.name
+ description = try(policy.description, null)
+ providers = [for provider in try(policy.providers, []) : {
+ key = "${template.name}/${policy.name}/${provider.name}/${provider.type}"
+ name = provider.name
+ type = provider.type
+ ip = provider.ip
+ schema = provider.schema
+ use_server_vrf = try(provider.use_server_vrf, local.defaults.ndo.tenant_templates.tenant_policies.dhcp_relay_policies.providers.use_server_vrf)
+ epg_path = provider.type == "epg" ? "${provider.schema}/${provider.template}/${try(provider.application_profile, "")}/${try(provider.endpoint_group, "")}" : null
+ ext_epg_path = provider.type == "external_epg" ? "${provider.schema}/${provider.template}/${try(provider.external_endpoint_group, "")}" : null
+ }]
+ }
+ ]
+ ])
+}
+
+resource "mso_tenant_policies_dhcp_relay_policy" "tenant_policies_dhcp_relay_policy" {
+ for_each = { for policy in local.dhcp_relay_policies : policy.name => policy }
+ name = each.value.name
+ template_id = mso_template.tenant_template[each.value.template_name].id
+ description = each.value.description
+ dynamic "dhcp_relay_providers" {
+ for_each = { for provider in try(each.value.providers, []) : provider.name => provider if provider.type == "epg" }
+ content {
+ dhcp_server_address = dhcp_relay_providers.value.ip
+ application_epg_uuid = !var.manage_schemas || (var.manage_schemas && !contains(local.managed_schemas, dhcp_relay_providers.value.schema)) ? data.mso_schema_template_anp_epg.schema_template_anp_epg[dhcp_relay_providers.value.epg_path].uuid : mso_schema_template_anp_epg.schema_template_anp_epg[dhcp_relay_providers.value.epg_path].uuid
+ dhcp_server_vrf_preference = dhcp_relay_providers.value.use_server_vrf
+ }
+ }
+ dynamic "dhcp_relay_providers" {
+ for_each = { for provider in try(each.value.providers, []) : provider.name => provider if provider.type == "external_epg" }
+ content {
+ dhcp_server_address = dhcp_relay_providers.value.ip
+ external_epg_uuid = !var.manage_schemas || (var.manage_schemas && !contains(local.managed_schemas, dhcp_relay_providers.value.schema)) ? data.mso_schema_template_external_epg.schema_template_external_epg[dhcp_relay_providers.value.ext_epg_path].uuid : mso_schema_template_external_epg.schema_template_external_epg[dhcp_relay_providers.value.ext_epg_path].uuid
+ dhcp_server_vrf_preference = dhcp_relay_providers.value.use_server_vrf
+ }
+ }
+
+ depends_on = [
+ mso_schema_template_anp_epg.schema_template_anp_epg,
+ mso_schema_template_external_epg.schema_template_external_epg
+ ]
+}
+
+locals {
+ ipsla_policies = flatten([
+ for template in local.tenant_templates : [
+ for policy in try(template.ip_sla_policies, []) : {
+ name = policy.name
+ template_name = template.name
+ description = try(policy.description, null)
+ sla_type = try(policy.sla_type, local.defaults.ndo.tenant_templates.tenant_policies.ip_sla_policies.sla_type)
+ destination_port = try(policy.sla_type, local.defaults.ndo.tenant_templates.tenant_policies.ip_sla_policies.sla_type) == "http" ? 80 : try(policy.sla_type, local.defaults.ndo.tenant_templates.tenant_policies.ip_sla_policies.sla_type) == "tcp" ? try(policy.port, local.defaults.ndo.tenant_templates.tenant_policies.ip_sla_policies.port) : null
+ http_version = try(policy.http_version, local.defaults.ndo.tenant_templates.tenant_policies.ip_sla_policies.http_version)
+ http_uri = try(policy.http_uri, local.defaults.ndo.tenant_templates.tenant_policies.ip_sla_policies.http_uri)
+ sla_frequency = try(policy.frequency, local.defaults.ndo.tenant_templates.tenant_policies.ip_sla_policies.frequency)
+ detect_multiplier = try(policy.multiplier, local.defaults.ndo.tenant_templates.tenant_policies.ip_sla_policies.multiplier)
+ request_data_size = try(policy.request_data_size, local.defaults.ndo.tenant_templates.tenant_policies.ip_sla_policies.request_data_size)
+ type_of_service = try(policy.type_of_service, local.defaults.ndo.tenant_templates.tenant_policies.ip_sla_policies.type_of_service)
+ operation_timeout = try(policy.timeout, local.defaults.ndo.tenant_templates.tenant_policies.ip_sla_policies.timeout)
+ threshold = try(policy.threshold, local.defaults.ndo.tenant_templates.tenant_policies.ip_sla_policies.threshold)
+ ipv6_traffic_class = try(policy.ipv6_traffic_class, local.defaults.ndo.tenant_templates.tenant_policies.ip_sla_policies.ipv6_traffic_class)
+ }
+ ]
+ ])
+}
+
+resource "mso_tenant_policies_ipsla_monitoring_policy" "tenant_policies_ipsla_monitoring_policy" {
+ for_each = { for policy in local.ipsla_policies : policy.name => policy }
+ template_id = mso_template.tenant_template[each.value.template_name].id
+ name = each.value.name
+ description = each.value.description
+ sla_type = each.value.sla_type
+ destination_port = each.value.destination_port
+ http_version = each.value.http_version
+ http_uri = each.value.http_uri
+ sla_frequency = each.value.sla_frequency
+ detect_multiplier = each.value.detect_multiplier
+ request_data_size = each.value.request_data_size
+ type_of_service = each.value.type_of_service
+ operation_timeout = each.value.operation_timeout
+ threshold = each.value.threshold
+ ipv6_traffic_class = each.value.ipv6_traffic_class
+}
+
+locals {
+ multicast_route_maps = flatten([
+ for template in local.tenant_templates : [
+ for policy in try(template.multicast_route_maps, []) : {
+ name = policy.name
+ template_name = template.name
+ description = try(policy.description, null)
+ entries = [for entry in try(policy.entries, []) : {
+ order = entry.order
+ group_ip = entry.group_ip
+ source_ip = entry.source_ip
+ rendezvous_point_ip = try(entry.rp_ip, local.defaults.ndo.tenant_templates.tenant_policies.multicast_route_maps.entries.rp_ip)
+ action = try(entry.action, local.defaults.ndo.tenant_templates.tenant_policies.multicast_route_maps.entries.action)
+ }]
+ }
+ ]
+ ])
+}
+
+resource "mso_tenant_policies_route_map_policy_multicast" "tenant_policies_route_map_policy_multicast" {
+ for_each = { for policy in local.multicast_route_maps : policy.name => policy }
+ template_id = mso_template.tenant_template[each.value.template_name].id
+ name = each.value.name
+ description = each.value.description
+ dynamic "route_map_multicast_entries" {
+ for_each = { for entry in try(each.value.entries, []) : entry.order => entry }
+ content {
+ order = route_map_multicast_entries.value.order
+ group_ip = route_map_multicast_entries.value.group_ip
+ source_ip = route_map_multicast_entries.value.source_ip
+ rendezvous_point_ip = route_map_multicast_entries.value.rendezvous_point_ip
+ action = route_map_multicast_entries.value.action
+ }
+ }
+}
\ No newline at end of file
diff --git a/variables.tf b/variables.tf
index 6b4e9ea..b40e9bd 100644
--- a/variables.tf
+++ b/variables.tf
@@ -59,6 +59,17 @@ variable "managed_schemas" {
default = []
}
+variable "manage_tenant_templates" {
+ description = "Flag to indicate if tenant templates should be managed."
+ type = bool
+ default = false
+}
+
+variable "managed_tenant_templates" {
+ description = "List of tenant template names to be managed. By default all tenant templates will be managed."
+ type = list(string)
+ default = []
+}
variable "deploy_templates" {
description = "Flag to indicate if templates should be deployed."
type = bool