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