Add VWAN deployment capability (#287)

* Add VWAN capabilities to upstream branch (#250)

* Initial MVP for virtual wan and hub resources
* Update resource dependencies
* Refactor to create dedicated resources for vwan
* Refactor to simplify for management resources
* Replace `try()` with `lookup()`
* Update custom settings for Virtual WAN
* Add DNS links for spokes connected to Virtual Hubs
* Add virtual hub connections

* Fix incorrect VPN gateway name (#251)

* Fix incorrect VPN gateway name

* Refactor test framework for VWAN additions (#265)

* Refactor test deployments

* Update minimum supported provider version

* Fix linting error

* Update root_name

* Update unit test pipeline

* Fix certificate path error

* Rename job display names

* Update e2e test pipeline

* Update location variable

* Remove unused TF_PLAN_OUT variable

* Update parallelism environment variable

* Update path for terraform destroy

* Increase job timeouts for e2e

* Update OPA value generator for pwsh

* Add `planned_values.json` for each test case

* Remove trailing whitespace

* Update OPA tests script for new framework

* Add OPA tasks to Unit Tests job

* Remove `.sh` script (to be unified with `.ps1` version)

* Refactor OPA installation scripts

* Update execution bit

* Update task names

* Add readme to test framework

* Add VWAN config to connectivity settings

* Remove unsupported tags object from config

* Update minimum supported version to fix #271

* Fix #271 error deleting firewall

* Updates to fix #272

* Fix formatting error on fix for #273

* Fix to prevent lock file versions error

* Update rego files to reflect changes for #272

* Updated for latest test framework plans

* Update conftest baseline

* Add opt-out for `terraform destroy`

* Update for remote backend configuration

* Update dependsOn for test jobs

* Update execution bit on script file

* Output variables to pipeline

* Update auth config for backend

* Update backend config for SPN auth

* Update comment

* Move random `root_id` generation to strategy job

* Add SPN credentials to backend configuration

* Do not try to overwrite readonly variable

* Rename function for linting error

* Remove `use_microsoft_graph` due to error

* Add `az logout` step

* Troubleshoot `terraform init` error

* Map dependent variables

* Add `az cli` login to init step

* Troubleshoot auth issue for `terraform init`

* Add `ARM_CLIENT_SECRET` to `terraform init` steps

* Add dependent variables to e2e test jobs

* Split e2e tests into multiple jobs

* Update condition in test loop

* Rename jobs

* Update timeout on clean-up

* Update condition format

* Update dependencies

* Update conditions

* Update conditions

* Update timeout and conditions for e2e tests

* Rename tasks

* Update logic for `terraform destroy`

* Update logic for `terraform destroy`

* Update condition

* Rename e2e clean-up job
This commit is contained in:
Kevin Rowlandson 2022-03-09 14:16:09 +00:00 коммит произвёл GitHub
Родитель 72ad2a3dbe
Коммит 739a3f9053
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
95 изменённых файлов: 22394 добавлений и 19214 удалений

Просмотреть файл

@ -8,6 +8,10 @@ azp-strategy:
@echo "==> Running script..."
./tests/scripts/azp-strategy.ps1
azp-backend:
@echo "==> Running script..."
./tests/scripts/azp-backend.sh
azp-spn-generator:
@echo "==> Running script..."
./tests/scripts/azp-spn-generator.sh
@ -46,7 +50,7 @@ tf-destroy:
opa-install:
@echo "==> Running script..."
./tests/scripts/opa-install.sh
./tests/scripts/opa-install-linux.sh
opa-run-tests:
@echo "==> Running script..."

Просмотреть файл

@ -109,7 +109,7 @@ Please refer to the [Deploy Identity Resources][wiki_deploy_identity_resources]
## Terraform versions
This module has been tested using Terraform `0.15.0` and AzureRM Provider `2.77.0` as a baseline, and various versions to up the most recent at the time of release.
This module has been tested using Terraform `0.15.0` and AzureRM Provider `2.96.0` as a baseline, and various versions to up the most recent at the time of release.
In some cases, individual versions of the AzureRM provider may cause errors.
If this happens, we advise upgrading to the latest version and checking our [troubleshooting][wiki_troubleshooting] guide before [raising an issue](https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/issues).
@ -132,7 +132,7 @@ terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 2.77.0"
version = ">= 2.96.0"
}
}
}

Просмотреть файл

@ -66,7 +66,7 @@ terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 2.77.0"
version = ">= 2.96.0"
}
}
}

Просмотреть файл

@ -46,7 +46,7 @@ terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 2.77.0"
version = ">= 2.96.0"
}
}
}

Просмотреть файл

@ -43,7 +43,7 @@ terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 2.77.0"
version = ">= 2.96.0"
}
}
}

Просмотреть файл

@ -25,7 +25,7 @@ terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 2.77.0"
version = ">= 2.96.0"
}
}
}

Просмотреть файл

@ -32,7 +32,7 @@ terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 2.77.0"
version = ">= 2.96.0"
}
}
}

Просмотреть файл

@ -46,7 +46,7 @@ terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 2.77.0"
version = ">= 2.96.0"
}
}
}

Просмотреть файл

@ -39,7 +39,7 @@ terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 2.77.0"
version = ">= 2.96.0"
}
}
}

Просмотреть файл

@ -54,7 +54,7 @@ terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 2.77.0"
version = ">= 2.96.0"
}
}
}

Просмотреть файл

@ -40,7 +40,7 @@ terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 2.77.0"
version = ">= 2.96.0"
}
}
}

Просмотреть файл

@ -83,7 +83,7 @@ terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 2.77.0"
version = ">= 2.96.0"
}
}
}

Просмотреть файл

@ -77,7 +77,7 @@ terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 2.77.0"
version = ">= 2.96.0"
}
}
}

Просмотреть файл

@ -51,7 +51,7 @@ terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 2.77.0"
version = ">= 2.96.0"
}
}
}

Просмотреть файл

@ -2,7 +2,7 @@
Before getting started with this module, please take note of the following considerations:
1. This module requires a minimum `azurerm` provider version of `2.77.0`.
1. This module requires a minimum `azurerm` provider version of `2.96.0`.
1. This module requires a minimum Terraform version `0.15.0`.

Просмотреть файл

@ -53,7 +53,7 @@ terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 2.77.0"
version = ">= 2.96.0"
}
}
}
@ -111,7 +111,7 @@ terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 2.77.0"
version = ">= 2.96.0"
configuration_aliases = [
azurerm.connectivity,
azurerm.management,

Просмотреть файл

@ -1,174 +1,110 @@
# The following locals are used to extract the Resource Group
# configuration from the solution module outputs.
locals {
es_connectivity_resource_groups = module.connectivity_resources.configuration.azurerm_resource_group
}
# The following locals are used to build the map of Resource
# Groups to deploy.
locals {
azurerm_resource_group_connectivity = {
for resource in local.es_connectivity_resource_groups :
for resource in module.connectivity_resources.configuration.azurerm_resource_group :
resource.resource_id => resource
if resource.managed_by_module
if resource.managed_by_module &&
contains(["connectivity", "ddos", "dns"], resource.scope)
}
}
# The following locals are used to extract the Virtual Network
# configuration from the solution module outputs.
locals {
es_connectivity_virtual_network = module.connectivity_resources.configuration.azurerm_virtual_network
}
# The following locals are used to build the map of Virtual
# Networks to deploy.
locals {
azurerm_virtual_network_connectivity = {
for resource in local.es_connectivity_virtual_network :
for resource in module.connectivity_resources.configuration.azurerm_virtual_network :
resource.resource_id => resource
if resource.managed_by_module
}
}
# The following locals are used to extract the Subnets
# configuration from the solution module outputs.
locals {
es_connectivity_subnet = module.connectivity_resources.configuration.azurerm_subnet
}
# The following locals are used to build the map of Subnets
# to deploy.
locals {
azurerm_subnet_connectivity = {
for resource in local.es_connectivity_subnet :
for resource in module.connectivity_resources.configuration.azurerm_subnet :
resource.resource_id => resource
if resource.managed_by_module
}
}
# The following locals are used to extract the Virtual Network
# Gateway configuration from the solution module outputs.
locals {
es_connectivity_virtual_network_gateway = module.connectivity_resources.configuration.azurerm_virtual_network_gateway
}
# The following locals are used to build the map of Virtual
# Network Gateways to deploy.
locals {
azurerm_virtual_network_gateway_connectivity = {
for resource in local.es_connectivity_virtual_network_gateway :
for resource in module.connectivity_resources.configuration.azurerm_virtual_network_gateway :
resource.resource_id => resource
if resource.managed_by_module
}
}
# The following locals are used to extract the Public IP
# configuration from the solution module outputs.
locals {
es_connectivity_public_ip = module.connectivity_resources.configuration.azurerm_public_ip
}
# The following locals are used to build the map of Public
# IPs to deploy.
locals {
azurerm_public_ip_connectivity = {
for resource in local.es_connectivity_public_ip :
for resource in module.connectivity_resources.configuration.azurerm_public_ip :
resource.resource_id => resource
if resource.managed_by_module
}
}
# The following locals are used to extract the Azure Firewall
# configuration from the solution module outputs.
locals {
es_connectivity_firewall = module.connectivity_resources.configuration.azurerm_firewall
}
# The following locals are used to build the map of Azure
# Firewalls to deploy.
locals {
azurerm_firewall_connectivity = {
for resource in local.es_connectivity_firewall :
for resource in module.connectivity_resources.configuration.azurerm_firewall :
resource.resource_id => resource
if resource.managed_by_module
if resource.managed_by_module &&
resource.scope == "connectivity"
}
}
# The following locals are used to extract the DDoS Protection
# Plan configuration from the solution module outputs.
locals {
es_connectivity_network_ddos_protection_plan = module.connectivity_resources.configuration.azurerm_network_ddos_protection_plan
}
# The following locals are used to build the map of DDoS
# Protection Plans to deploy.
locals {
azurerm_network_ddos_protection_plan_connectivity = {
for resource in local.es_connectivity_network_ddos_protection_plan :
for resource in module.connectivity_resources.configuration.azurerm_network_ddos_protection_plan :
resource.resource_id => resource
if resource.managed_by_module
}
}
# The following locals are used to extract the Private DNS Zone
# configuration from the solution module outputs.
locals {
es_connectivity_private_dns_zone = module.connectivity_resources.configuration.azurerm_private_dns_zone
}
# The following locals are used to build the map of Private DNS
# Zones to deploy.
locals {
azurerm_private_dns_zone_connectivity = {
for resource in local.es_connectivity_private_dns_zone :
for resource in module.connectivity_resources.configuration.azurerm_private_dns_zone :
resource.resource_id => resource
if resource.managed_by_module
}
}
# The following locals are used to extract the Public DNS Zone
# configuration from the solution module outputs.
locals {
es_connectivity_dns_zone = module.connectivity_resources.configuration.azurerm_dns_zone
}
# The following locals are used to build the map of Public DNS
# Zones to deploy.
locals {
azurerm_dns_zone_connectivity = {
for resource in local.es_connectivity_dns_zone :
for resource in module.connectivity_resources.configuration.azurerm_dns_zone :
resource.resource_id => resource
if resource.managed_by_module
}
}
# The following locals are used to extract the Private DNS Zone
# Virtual Network Links configuration from the solution module outputs.
locals {
es_connectivity_private_dns_zone_virtual_network_link = module.connectivity_resources.configuration.azurerm_private_dns_zone_virtual_network_link
}
# The following locals are used to build the map of Private DNS Zone
# Virtual Network Links to deploy.
locals {
azurerm_private_dns_zone_virtual_network_link_connectivity = {
for resource in local.es_connectivity_private_dns_zone_virtual_network_link :
for resource in module.connectivity_resources.configuration.azurerm_private_dns_zone_virtual_network_link :
resource.resource_id => resource
if resource.managed_by_module
}
}
# The following locals are used to extract the Virtual Network
# Peering configuration from the solution module outputs.
locals {
es_connectivity_virtual_network_peering = module.connectivity_resources.configuration.azurerm_virtual_network_peering
}
# The following locals are used to build the map of Virtual
# Network Peerings to deploy.
locals {
azurerm_virtual_network_peering_connectivity = {
for resource in local.es_connectivity_virtual_network_peering :
for resource in module.connectivity_resources.configuration.azurerm_virtual_network_peering :
resource.resource_id => resource
if resource.managed_by_module
}

Просмотреть файл

@ -1,78 +1,48 @@
# The following locals are used to extract the Resource Group
# configuration from the solution module outputs.
locals {
es_management_resource_groups = module.management_resources.configuration.azurerm_resource_group
}
# The following locals are used to build the map of Resource
# Groups to deploy.
locals {
azurerm_resource_group_management = {
for resource in local.es_management_resource_groups :
for resource in module.management_resources.configuration.azurerm_resource_group :
resource.resource_id => resource
if resource.managed_by_module
}
}
# The following locals are used to extract the Log Analytics
# configuration from the solution module outputs.
locals {
es_management_log_analytics_workspaces = module.management_resources.configuration.azurerm_log_analytics_workspace
}
# The following locals are used to build the map of Log
# Analytics workspaces to deploy.
locals {
azurerm_log_analytics_workspace_management = {
for resource in local.es_management_log_analytics_workspaces :
for resource in module.management_resources.configuration.azurerm_log_analytics_workspace :
resource.resource_id => resource
if resource.managed_by_module
}
}
# The following locals are used to extract the Log Analytics
# Solutions configuration from the solution module outputs.
locals {
es_management_log_analytics_solution = module.management_resources.configuration.azurerm_log_analytics_solution
}
# The following locals are used to build the map of Log
# Analytics workspaces to deploy.
locals {
azurerm_log_analytics_solution_management = {
for resource in local.es_management_log_analytics_solution :
for resource in module.management_resources.configuration.azurerm_log_analytics_solution :
resource.resource_id => resource
if resource.managed_by_module
}
}
# The following locals are used to extract the Automation
# Account configuration from the solution module outputs.
locals {
es_management_automation_account = module.management_resources.configuration.azurerm_automation_account
}
# The following locals are used to build the map of Log
# Analytics workspaces to deploy.
locals {
azurerm_automation_account_management = {
for resource in local.es_management_automation_account :
for resource in module.management_resources.configuration.azurerm_automation_account :
resource.resource_id => resource
if resource.managed_by_module
}
}
# The following locals are used to extract the Log Analytics
# Linked Service configuration from the solution module outputs.
locals {
es_management_log_analytics_linked_service = module.management_resources.configuration.azurerm_log_analytics_linked_service
}
# The following locals are used to build the map of Log
# Analytics workspaces to deploy.
locals {
azurerm_log_analytics_linked_service_management = {
for resource in local.es_management_log_analytics_linked_service :
for resource in module.management_resources.configuration.azurerm_log_analytics_linked_service :
resource.resource_id => resource
if resource.managed_by_module
}

Просмотреть файл

@ -85,8 +85,8 @@ locals {
azurerm_policy_set_definition_external_lookup = {
for policy_set_definition_id in keys(transpose(local.policy_assignments_with_managed_identity_using_external_policy_set_definition)) :
policy_set_definition_id => {
name = basename(policy_set_definition_id)
management_group_name = regex(local.regex_split_resource_id, policy_set_definition_id)[0] == "/providers/Microsoft.Management/managementGroups/" ? regex(local.regex_split_resource_id, policy_set_definition_id)[1] : null
name = basename(policy_set_definition_id)
management_group_id = regex(local.regex_split_resource_id, policy_set_definition_id)[0] == "/providers/Microsoft.Management/managementGroups/" ? regex(local.regex_split_resource_id, policy_set_definition_id)[1] : null
}
}
}
@ -96,7 +96,7 @@ data "azurerm_policy_set_definition" "external_lookup" {
for_each = local.azurerm_policy_set_definition_external_lookup
name = each.value.name
management_group_name = each.value.management_group_name
management_group_name = each.value.management_group_id
}
# Create a list of Policy Definitions IDs used by all assigned Policy Set Definitions
@ -134,16 +134,16 @@ locals {
external_policy_definitions_from_azurerm_policy_set_definition_external_lookup = {
for policy_definition_id in local.external_policy_definition_ids_from_policy_set_definitions :
policy_definition_id => {
name = basename(policy_definition_id)
management_group_name = regex(local.regex_split_resource_id, policy_definition_id)[0] == "/providers/Microsoft.Management/managementGroups/" ? regex(local.regex_split_resource_id, policy_definition_id)[1] : null
name = basename(policy_definition_id)
management_group_id = regex(local.regex_split_resource_id, policy_definition_id)[0] == "/providers/Microsoft.Management/managementGroups/" ? regex(local.regex_split_resource_id, policy_definition_id)[1] : null
}
}
# From Policy Assignments using Policy Definitions
external_policy_definitions_from_internal_policy_assignments = {
for policy_definition_id in keys(transpose(local.policy_assignments_with_managed_identity_using_external_policy_definition)) :
policy_definition_id => {
name = basename(policy_definition_id)
management_group_name = regex(local.regex_split_resource_id, policy_definition_id)[0] == "/providers/Microsoft.Management/managementGroups/" ? regex(local.regex_split_resource_id, policy_definition_id)[1] : null
name = basename(policy_definition_id)
management_group_id = regex(local.regex_split_resource_id, policy_definition_id)[0] == "/providers/Microsoft.Management/managementGroups/" ? regex(local.regex_split_resource_id, policy_definition_id)[1] : null
}
}
# Then create a single list containing all Policy Definitions to lookup from Azure
@ -158,7 +158,7 @@ data "azurerm_policy_definition" "external_lookup" {
for_each = local.azurerm_policy_definition_external_lookup
name = each.value.name
management_group_name = each.value.management_group_name
management_group_name = each.value.management_group_id
}
# Extract the Role Definition IDs from the internal and external

Просмотреть файл

@ -42,6 +42,17 @@ locals {
disable_base_module_tags = var.disable_base_module_tags
}
# The following locals are used to ensure non-null values
# are assigned to each of the corresponding inputs for
# correct processing in `lookup()` functions
locals {
enforcement_mode_default = {
enforcement_mode = null
}
connectivity_resources_advanced = coalesce(local.configure_connectivity_resources.advanced, local.empty_map)
management_resources_advanced = coalesce(local.configure_management_resources.advanced, local.empty_map)
}
# The following locals are used to define a set of module
# tags applied to all resources unless disabled by the
# input variable "disable_module_tags" and prepare the
@ -109,19 +120,19 @@ locals {
default_create_duration_delay = "30s"
default_destroy_duration_delay = "0s"
create_duration_delay = {
after_azurerm_management_group = try(var.create_duration_delay["azurerm_management_group"], local.default_create_duration_delay)
after_azurerm_policy_assignment = try(var.create_duration_delay["azurerm_policy_assignment"], local.default_create_duration_delay)
after_azurerm_policy_definition = try(var.create_duration_delay["azurerm_policy_definition"], local.default_create_duration_delay)
after_azurerm_policy_set_definition = try(var.create_duration_delay["azurerm_policy_set_definition"], local.default_create_duration_delay)
after_azurerm_role_assignment = try(var.create_duration_delay["azurerm_role_assignment"], local.default_create_duration_delay)
after_azurerm_role_definition = try(var.create_duration_delay["azurerm_role_definition"], local.default_create_duration_delay)
after_azurerm_management_group = lookup(var.create_duration_delay, "azurerm_management_group", local.default_create_duration_delay)
after_azurerm_policy_assignment = lookup(var.create_duration_delay, "azurerm_policy_assignment", local.default_create_duration_delay)
after_azurerm_policy_definition = lookup(var.create_duration_delay, "azurerm_policy_definition", local.default_create_duration_delay)
after_azurerm_policy_set_definition = lookup(var.create_duration_delay, "azurerm_policy_set_definition", local.default_create_duration_delay)
after_azurerm_role_assignment = lookup(var.create_duration_delay, "azurerm_role_assignment", local.default_create_duration_delay)
after_azurerm_role_definition = lookup(var.create_duration_delay, "azurerm_role_definition", local.default_create_duration_delay)
}
destroy_duration_delay = {
after_azurerm_management_group = try(var.destroy_duration_delay["azurerm_management_group"], local.default_destroy_duration_delay)
after_azurerm_policy_assignment = try(var.destroy_duration_delay["azurerm_policy_assignment"], local.default_destroy_duration_delay)
after_azurerm_policy_definition = try(var.destroy_duration_delay["azurerm_policy_definition"], local.default_destroy_duration_delay)
after_azurerm_policy_set_definition = try(var.destroy_duration_delay["azurerm_policy_set_definition"], local.default_destroy_duration_delay)
after_azurerm_role_assignment = try(var.destroy_duration_delay["azurerm_role_assignment"], local.default_destroy_duration_delay)
after_azurerm_role_definition = try(var.destroy_duration_delay["azurerm_role_definition"], local.default_destroy_duration_delay)
after_azurerm_management_group = lookup(var.destroy_duration_delay, "azurerm_management_group", local.default_destroy_duration_delay)
after_azurerm_policy_assignment = lookup(var.destroy_duration_delay, "azurerm_policy_assignment", local.default_destroy_duration_delay)
after_azurerm_policy_definition = lookup(var.destroy_duration_delay, "azurerm_policy_definition", local.default_destroy_duration_delay)
after_azurerm_policy_set_definition = lookup(var.destroy_duration_delay, "azurerm_policy_set_definition", local.default_destroy_duration_delay)
after_azurerm_role_assignment = lookup(var.destroy_duration_delay, "azurerm_role_assignment", local.default_destroy_duration_delay)
after_azurerm_role_definition = lookup(var.destroy_duration_delay, "azurerm_role_definition", local.default_destroy_duration_delay)
}
}

71
locals.virtual_wan.tf Normal file
Просмотреть файл

@ -0,0 +1,71 @@
# The following locals are used to build the map of Resource
# Groups to deploy.
locals {
azurerm_resource_group_virtual_wan = {
for resource in module.connectivity_resources.configuration.azurerm_resource_group :
resource.resource_id => resource
if resource.managed_by_module &&
resource.scope == "virtual_wan"
}
}
# The following locals are used to build the map of Azure
# Virtual WANs to deploy.
locals {
azurerm_virtual_wan_virtual_wan = {
for resource in module.connectivity_resources.configuration.azurerm_virtual_wan :
resource.resource_id => resource
if resource.managed_by_module
}
}
# The following locals are used to build the map of Azure
# Virtual Hubs to deploy.
locals {
azurerm_virtual_hub_virtual_wan = {
for resource in module.connectivity_resources.configuration.azurerm_virtual_hub :
resource.resource_id => resource
if resource.managed_by_module
}
}
# The following locals are used to build the map of Azure
# Expressroute Gateways to deploy.
locals {
azurerm_express_route_gateway_virtual_wan = {
for resource in module.connectivity_resources.configuration.azurerm_express_route_gateway :
resource.resource_id => resource
if resource.managed_by_module
}
}
# The following locals are used to build the map of Azure
# VPN Gateways to deploy.
locals {
azurerm_vpn_gateway_virtual_wan = {
for resource in module.connectivity_resources.configuration.azurerm_vpn_gateway :
resource.resource_id => resource
if resource.managed_by_module
}
}
# The following locals are used to build the map of Azure
# Firewalls to deploy.
locals {
azurerm_firewall_virtual_wan = {
for resource in module.connectivity_resources.configuration.azurerm_firewall :
resource.resource_id => resource
if resource.managed_by_module &&
resource.scope == "virtual_wan"
}
}
# The following locals are used to build the map of Virtual
# Network Peerings to deploy.
locals {
azurerm_virtual_hub_connection = {
for resource in module.connectivity_resources.configuration.azurerm_virtual_hub_connection :
resource.resource_id => resource
if resource.managed_by_module
}
}

30
main.tf
Просмотреть файл

@ -17,9 +17,9 @@ module "management_group_archetypes" {
template_file_variables = local.template_file_variables
default_location = local.default_location
enforcement_mode = merge(
try(module.connectivity_resources.configuration.archetype_config_overrides[basename(each.key)].enforcement_mode, null),
try(module.identity_resources.configuration.archetype_config_overrides[basename(each.key)].enforcement_mode, null),
try(module.management_resources.configuration.archetype_config_overrides[basename(each.key)].enforcement_mode, null),
lookup(module.connectivity_resources.configuration.archetype_config_overrides, basename(each.key), local.enforcement_mode_default).enforcement_mode,
lookup(module.identity_resources.configuration.archetype_config_overrides, basename(each.key), local.enforcement_mode_default).enforcement_mode,
lookup(module.management_resources.configuration.archetype_config_overrides, basename(each.key), local.enforcement_mode_default).enforcement_mode,
)
}
@ -40,13 +40,13 @@ module "management_resources" {
tags = local.management_resources_tags
# Optional input variables (advanced configuration)
resource_prefix = try(local.configure_management_resources.advanced.resource_prefix, local.empty_string)
resource_suffix = try(local.configure_management_resources.advanced.resource_suffix, local.empty_string)
existing_resource_group_name = try(local.configure_management_resources.advanced.existing_resource_group_name, local.empty_string)
existing_log_analytics_workspace_resource_id = try(local.configure_management_resources.advanced.existing_log_analytics_workspace_resource_id, local.empty_string)
existing_automation_account_resource_id = try(local.configure_management_resources.advanced.existing_automation_account_resource_id, local.empty_string)
link_log_analytics_to_automation_account = try(local.configure_management_resources.advanced.link_log_analytics_to_automation_account, true)
custom_settings_by_resource_type = try(local.configure_management_resources.advanced.custom_settings_by_resource_type, local.empty_map)
resource_prefix = lookup(local.management_resources_advanced, "resource_prefix", local.empty_string)
resource_suffix = lookup(local.management_resources_advanced, "resource_suffix", local.empty_string)
existing_resource_group_name = lookup(local.management_resources_advanced, "existing_resource_group_name", local.empty_string)
existing_log_analytics_workspace_resource_id = lookup(local.management_resources_advanced, "existing_log_analytics_workspace_resource_id", local.empty_string)
existing_automation_account_resource_id = lookup(local.management_resources_advanced, "existing_automation_account_resource_id", local.empty_string)
link_log_analytics_to_automation_account = lookup(local.management_resources_advanced, "link_log_analytics_to_automation_account", true)
custom_settings_by_resource_type = lookup(local.management_resources_advanced, "custom_settings_by_resource_type", local.empty_map)
}
# The following module is used to generate the configuration
@ -78,8 +78,10 @@ module "connectivity_resources" {
tags = local.connectivity_resources_tags
# Optional input variables (advanced configuration)
resource_prefix = try(local.configure_connectivity_resources.advanced.resource_prefix, local.empty_string)
resource_suffix = try(local.configure_connectivity_resources.advanced.resource_suffix, local.empty_string)
existing_ddos_protection_plan_resource_id = try(local.configure_connectivity_resources.advanced.existing_resource_group_name, local.empty_string)
custom_settings_by_resource_type = try(local.configure_connectivity_resources.advanced.custom_settings_by_resource_type, local.empty_map)
resource_prefix = lookup(local.connectivity_resources_advanced, "resource_prefix", null)
resource_suffix = lookup(local.connectivity_resources_advanced, "resource_suffix", null)
existing_ddos_protection_plan_resource_id = lookup(local.connectivity_resources_advanced, "existing_ddos_protection_plan_resource_id", null)
existing_virtual_wan_resource_id = lookup(local.connectivity_resources_advanced, "existing_virtual_wan_resource_id", null)
resource_group_per_virtual_hub_location = lookup(local.connectivity_resources_advanced, "resource_group_per_virtual_hub_location", false)
custom_settings_by_resource_type = lookup(local.connectivity_resources_advanced, "custom_settings_by_resource_type", null)
}

Просмотреть файл

@ -19,8 +19,10 @@ locals {
location = var.location
tags = var.tags
resource_prefix = coalesce(var.resource_prefix, local.root_id)
resource_suffix = length(var.resource_suffix) > 0 ? "-${var.resource_suffix}" : local.empty_string
resource_suffix = var.resource_suffix != null ? "-${var.resource_suffix}" : local.empty_string
existing_ddos_protection_plan_resource_id = var.existing_ddos_protection_plan_resource_id
existing_virtual_wan_resource_id = var.existing_virtual_wan_resource_id != null ? var.existing_virtual_wan_resource_id : local.empty_string
resource_group_per_virtual_hub_location = var.resource_group_per_virtual_hub_location
custom_settings = var.custom_settings_by_resource_type
}
@ -36,22 +38,91 @@ locals {
coalesce(hub_network.config.location, local.location) => hub_network
}
hub_network_locations = keys(local.hub_networks_by_location)
ddos_location = coalesce(local.settings.ddos_protection_plan.config.location, local.location)
dns_location = coalesce(local.settings.dns.config.location, local.location)
virtual_hubs = local.settings.vwan_hub_networks
# We generate the virtual_hubs_by_location as a map
# to ensure the user has provided unique values for
# each hub location. If duplicates are found,
# terraform will throw an error at this point.
# By default we recommend creating all Virtual WAN
# resources in a single Resource Group as per:
# https://docs.microsoft.com/en-us/azure/virtual-wan/virtual-wan-faq#can-hubs-be-created-in-different-resource-group-in-virtual-wan
# As this is only an issue for customers using the
# Portal to manage Virtual WAN resources, the following
# logic is used to allow a customer to use dedicated Resource
# Groups per location if preferred.
virtual_hubs_by_location = {
for virtual_hub in local.virtual_hubs :
coalesce(virtual_hub.config.location, local.location) => virtual_hub
}
virtual_hubs_by_location_for_resource_group_per_location = {
for virtual_hub in local.virtual_hubs :
coalesce(virtual_hub.config.location, local.location) => virtual_hub
if local.resource_group_per_virtual_hub_location
}
virtual_hubs_by_location_for_shared_resource_group = {
for virtual_hub in local.virtual_hubs :
coalesce(virtual_hub.config.location, local.location) => virtual_hub
if !local.resource_group_per_virtual_hub_location
}
# The following objects are used to identify azurerm_virtual_hub
# resources which need to be associated with a new or existing
# azurerm_virtual_wan resource
virtual_hubs_by_location_for_managed_virtual_wan = {
for virtual_hub in local.virtual_hubs :
coalesce(virtual_hub.config.location, local.location) => virtual_hub
if local.existing_virtual_wan_resource_id == ""
}
virtual_hubs_by_location_for_existing_virtual_wan = {
for virtual_hub in local.virtual_hubs :
coalesce(virtual_hub.config.location, local.location) => virtual_hub
if local.existing_virtual_wan_resource_id != ""
}
# Need to know the full list of virtual_hub_locations
# for azurerm_virtual_hub resource deployments.
virtual_hub_locations = keys(local.virtual_hubs_by_location)
# The azurerm_virtual_wan resource will be created in the
# default location of the connectivity module if a new.
virtual_wan_locations = anytrue(
[
length(local.virtual_hubs_by_location_for_managed_virtual_wan) > 0,
length(local.virtual_hubs_by_location_for_shared_resource_group) > 0,
]
) ? [local.location, ] : local.empty_list
ddos_location = coalesce(local.settings.ddos_protection_plan.config.location, local.location)
dns_location = coalesce(local.settings.dns.config.location, local.location)
connectivity_locations = distinct(concat(
local.hub_network_locations,
keys(local.virtual_hubs_by_location_for_resource_group_per_location),
))
result_when_location_missing = {
enabled = false
}
}
# Logic to determine whether specific resources
# should be created by this module
# - Resource Groups
locals {
deploy_ddos_protection_plan = local.enabled && local.settings.ddos_protection_plan.enabled
deploy_dns = local.enabled && local.settings.dns.enabled
deploy_resource_groups = {
connectivity = {
for location, hub_network in local.hub_networks_by_location :
for location in local.connectivity_locations :
location =>
local.enabled &&
hub_network.enabled
anytrue(
[
lookup(local.hub_networks_by_location, location, local.result_when_location_missing).enabled,
lookup(local.virtual_hubs_by_location_for_resource_group_per_location, location, local.result_when_location_missing).enabled,
]
)
}
virtual_wan = {
for location in local.virtual_wan_locations :
location =>
local.enabled &&
anytrue(concat(
values(local.virtual_hubs_by_location_for_managed_virtual_wan).*.enabled,
values(local.virtual_hubs_by_location_for_shared_resource_group).*.enabled,
))
}
ddos = {
(local.ddos_location) = local.deploy_ddos_protection_plan
@ -60,6 +131,28 @@ locals {
(local.dns_location) = local.deploy_dns
}
}
}
# Logic to determine whether specific resources
# should be created by this module
# - DDoS Protection Plan
locals {
deploy_ddos_protection_plan = local.enabled && local.settings.ddos_protection_plan.enabled
}
# Logic to determine whether specific resources
# should be created by this module
# - DNS
locals {
deploy_dns = local.enabled && local.settings.dns.enabled
deploy_private_dns_zone_virtual_network_link_on_hubs = local.deploy_dns && local.settings.dns.config.enable_private_dns_zone_virtual_network_link_on_hubs
deploy_private_dns_zone_virtual_network_link_on_spokes = local.deploy_dns && local.settings.dns.config.enable_private_dns_zone_virtual_network_link_on_spokes
}
# Logic to determine whether specific resources
# should be created by this module
# - Hub networks
locals {
deploy_hub_network = {
for location, hub_network in local.hub_networks_by_location :
location =>
@ -73,7 +166,7 @@ locals {
hub_network.config.virtual_network_gateway.enabled &&
hub_network.config.virtual_network_gateway.config.address_prefix != local.empty_string
}
deploy_virtual_network_gateway_expressroute = {
deploy_virtual_network_gateway_express_route = {
for location, hub_network in local.hub_networks_by_location :
location =>
local.deploy_virtual_network_gateway[location] &&
@ -95,10 +188,52 @@ locals {
for location, hub_network in local.hub_networks_by_location :
location =>
local.deploy_dns &&
local.deploy_hub_network[location] &&
hub_network.config.enable_outbound_virtual_network_peering
}
deploy_private_dns_zone_virtual_network_link_on_hubs = local.deploy_dns && local.settings.dns.config.enable_private_dns_zone_virtual_network_link_on_hubs
deploy_private_dns_zone_virtual_network_link_on_spokes = local.deploy_dns && local.settings.dns.config.enable_private_dns_zone_virtual_network_link_on_spokes
}
# Logic to determine whether specific resources
# should be created by this module
# - VWAN hub networks
locals {
deploy_virtual_wan = {
(local.location) = (
local.enabled &&
local.existing_virtual_wan_resource_id == "" &&
anytrue(values(local.deploy_virtual_hub))
)
}
deploy_virtual_hub = {
for location, virtual_hub in local.virtual_hubs_by_location :
location =>
local.enabled &&
virtual_hub.enabled
}
deploy_virtual_hub_express_route_gateway = {
for location, virtual_hub in local.virtual_hubs_by_location :
location =>
local.deploy_virtual_hub[location] &&
virtual_hub.config.expressroute_gateway.enabled
}
deploy_virtual_hub_vpn_gateway = {
for location, virtual_hub in local.virtual_hubs_by_location :
location =>
local.deploy_virtual_hub[location] &&
virtual_hub.config.vpn_gateway.enabled
}
deploy_virtual_hub_azure_firewall = {
for location, virtual_hub in local.virtual_hubs_by_location :
location =>
local.deploy_virtual_hub[location] &&
virtual_hub.config.azure_firewall.enabled
}
deploy_virtual_hub_connection = {
for location, virtual_hub in local.virtual_hubs_by_location :
location =>
local.deploy_virtual_hub[location] &&
virtual_hub.config.enable_virtual_hub_connections
}
}
# Configuration settings for resource type:
@ -107,11 +242,17 @@ locals {
# Determine the name of each Resource Group per scope and location
resource_group_names_by_scope_and_location = {
connectivity = {
for location in local.hub_network_locations :
for location in local.connectivity_locations :
location =>
try(local.custom_settings.azurerm_resource_group["connectivity"][location].name,
"${local.resource_prefix}-connectivity-${location}${local.resource_suffix}")
}
virtual_wan = {
for location in local.virtual_wan_locations :
location =>
try(local.custom_settings.azurerm_resource_group["virtual_wan"][location].name,
"${local.resource_prefix}-connectivity${local.resource_suffix}")
}
ddos = {
(local.ddos_location) = try(local.custom_settings.azurerm_resource_group["ddos"][local.ddos_location].name,
"${local.resource_prefix}-ddos${local.resource_suffix}")
@ -315,7 +456,7 @@ locals {
{
# Resource logic attributes
resource_id = local.er_gateway_resource_id[location]
managed_by_module = local.deploy_virtual_network_gateway_expressroute[location]
managed_by_module = local.deploy_virtual_network_gateway_express_route[location]
# Resource definition attributes
name = local.er_gateway_name[location]
resource_group_name = local.resource_group_names_by_scope_and_location["connectivity"][location]
@ -348,7 +489,7 @@ locals {
azurerm_public_ip = {
# Resource logic attributes
resource_id = "${local.virtual_network_resource_group_id[location]}/providers/Microsoft.Network/publicIPAddresses/${local.er_gateway_name[location]}-pip"
managed_by_module = local.deploy_virtual_network_gateway_expressroute[location]
managed_by_module = local.deploy_virtual_network_gateway_express_route[location]
# Resource definition attributes
name = "${local.er_gateway_name[location]}-pip"
resource_group_name = local.resource_group_names_by_scope_and_location["connectivity"][location]
@ -471,21 +612,36 @@ locals {
# Configuration settings for resource type:
# - azurerm_firewall
# For VWAN, VPN gateway is required for Security Partner Provider integration
locals {
azfw_name = {
for location in local.hub_network_locations :
location => "${local.resource_prefix}-fw-${location}${local.resource_suffix}"
}
virtual_hub_azfw_name = {
for location in local.virtual_hub_locations :
location => "${local.resource_prefix}-fw-hub-${location}${local.resource_suffix}"
}
azfw_resource_id_prefix = {
for location in local.hub_network_locations :
location =>
"${local.virtual_network_resource_group_id[location]}/providers/Microsoft.Network/azureFirewalls"
}
virtual_hub_azfw_resource_id_prefix = {
for location in local.virtual_hub_locations :
location =>
"${local.virtual_hub_resource_group_id[location]}/providers/Microsoft.Network/azureFirewalls"
}
azfw_resource_id = {
for location in local.hub_network_locations :
location =>
"${local.azfw_resource_id_prefix[location]}/${local.azfw_name[location]}"
}
virtual_hub_azfw_resource_id = {
for location in local.virtual_hub_locations :
location =>
"${local.virtual_hub_azfw_resource_id_prefix[location]}/${local.virtual_hub_azfw_name[location]}"
}
azfw_zones = {
for location, hub_network in local.hub_networks_by_location :
location =>
@ -502,59 +658,302 @@ locals {
location =>
length(local.azfw_zones[location]) > 0
}
azurerm_firewall = [
for location, hub_network in local.hub_networks_by_location :
{
# Resource logic attributes
resource_id = local.azfw_resource_id[location]
managed_by_module = local.deploy_azure_firewall[location]
# Resource definition attributes
name = local.azfw_name[location]
resource_group_name = local.resource_group_names_by_scope_and_location["connectivity"][location]
location = location
ip_configuration = try(
local.custom_settings.azurerm_firewall["connectivity"][location].ip_configuration,
[
azurerm_firewall = concat(
[
for location, hub_network in local.hub_networks_by_location :
{
# Resource logic attributes
resource_id = local.azfw_resource_id[location]
managed_by_module = local.deploy_azure_firewall[location]
scope = "connectivity"
# Resource definition attributes
name = local.azfw_name[location]
resource_group_name = local.resource_group_names_by_scope_and_location["connectivity"][location]
location = location
ip_configuration = try(
local.custom_settings.azurerm_firewall["connectivity"][location].ip_configuration,
[
{
name = "${local.azfw_name[location]}-pip"
public_ip_address_id = "${local.virtual_network_resource_group_id[location]}/providers/Microsoft.Network/publicIPAddresses/${local.azfw_name[location]}-pip"
subnet_id = "${local.virtual_network_resource_id[location]}/subnets/AzureFirewallSubnet"
}
]
)
sku_name = "AZFW_VNet"
sku_tier = try(local.custom_settings.azurerm_firewall["connectivity"][location].sku_tier, "Standard")
firewall_policy_id = try(local.custom_settings.azurerm_firewall["connectivity"][location].firewall_policy_id, null)
dns_servers = try(local.custom_settings.azurerm_firewall["connectivity"][location].dns_servers, null)
private_ip_ranges = try(local.custom_settings.azurerm_firewall["connectivity"][location].private_ip_ranges, null)
management_ip_configuration = try(local.custom_settings.azurerm_firewall["connectivity"][location].management_ip_configuration, local.empty_list)
threat_intel_mode = try(local.custom_settings.azurerm_firewall["connectivity"][location].threat_intel_mode, null)
virtual_hub = local.empty_list
zones = try(local.custom_settings.azurerm_firewall["connectivity"][location].zones, local.azfw_zones[location])
tags = try(local.custom_settings.azurerm_firewall["connectivity"][location].tags, local.tags)
# Child resource definition attributes
azurerm_public_ip = {
# Resource logic attributes
resource_id = "${local.virtual_network_resource_group_id[location]}/providers/Microsoft.Network/publicIPAddresses/${local.azfw_name[location]}-pip"
managed_by_module = local.deploy_azure_firewall[location]
# Resource definition attributes
name = "${local.azfw_name[location]}-pip"
resource_group_name = local.resource_group_names_by_scope_and_location["connectivity"][location]
location = location
sku = try(local.custom_settings.azurerm_public_ip["connectivity"]["azfw"][location].sku, "Standard")
allocation_method = try(local.custom_settings.azurerm_public_ip["connectivity"]["azfw"][location].allocation_method, "Static")
ip_version = try(local.custom_settings.azurerm_public_ip["connectivity"]["azfw"][location].ip_version, null)
idle_timeout_in_minutes = try(local.custom_settings.azurerm_public_ip["connectivity"]["azfw"][location].idle_timeout_in_minutes, null)
domain_name_label = try(local.custom_settings.azurerm_public_ip["connectivity"]["azfw"][location].domain_name_label, null)
reverse_fqdn = try(local.custom_settings.azurerm_public_ip["connectivity"]["azfw"][location].reverse_fqdn, null)
public_ip_prefix_id = try(local.custom_settings.azurerm_public_ip["connectivity"]["azfw"][location].public_ip_prefix_id, null)
ip_tags = try(local.custom_settings.azurerm_public_ip["connectivity"]["azfw"][location].ip_tags, null)
tags = try(local.custom_settings.azurerm_public_ip["connectivity"]["azfw"][location].tags, local.tags)
availability_zone = try(
local.custom_settings.azurerm_public_ip["connectivity"]["azfw"][location].availability_zone,
local.azfw_zones_enabled[location] ? "Zone-Redundant" : "No-Zone"
)
}
}
],
[
for location, virtual_hub in local.virtual_hubs_by_location :
{
# Resource logic attributes
resource_id = local.virtual_hub_azfw_resource_id[location]
managed_by_module = local.deploy_virtual_hub_azure_firewall[location]
scope = "virtual_wan"
# Resource definition attributes
name = local.virtual_hub_azfw_name[location]
resource_group_name = local.virtual_hub_resource_group_name[location]
location = location
ip_configuration = try(
local.custom_settings.azurerm_firewall["virtual_wan"][location].ip_configuration,
local.empty_list
)
sku_name = "AZFW_Hub"
sku_tier = try(local.custom_settings.azurerm_firewall["virtual_wan"][location].sku_tier, "Standard")
firewall_policy_id = try(local.custom_settings.azurerm_firewall["virtual_wan"][location].firewall_policy_id, null)
dns_servers = try(local.custom_settings.azurerm_firewall["virtual_wan"][location].dns_servers, null)
private_ip_ranges = try(local.custom_settings.azurerm_firewall["virtual_wan"][location].private_ip_ranges, null)
management_ip_configuration = try(local.custom_settings.azurerm_firewall["virtual_wan"][location].management_ip_configuration, local.empty_list)
threat_intel_mode = "" # If virtual_hub_settting is specified, the threat_intel_mode has to be explicitly set as "".
virtual_hub = [
{
name = "${local.azfw_name[location]}-pip"
public_ip_address_id = "${local.virtual_network_resource_group_id[location]}/providers/Microsoft.Network/publicIPAddresses/${local.azfw_name[location]}-pip"
subnet_id = "${local.virtual_network_resource_id[location]}/subnets/AzureFirewallSubnet"
virtual_hub_id = local.virtual_hub_resource_id[location]
public_ip_count = try(local.custom_settings.azurerm_firewall["virtual_wan"][location].virtual_hub[0].public_ip_count, 1)
}
]
)
sku_name = try(local.custom_settings.azurerm_firewall["connectivity"][location].sku_name, "AZFW_VNet")
sku_tier = try(local.custom_settings.azurerm_firewall["connectivity"][location].sku_tier, "Standard")
firewall_policy_id = try(local.custom_settings.azurerm_firewall["connectivity"][location].firewall_policy_id, null)
dns_servers = try(local.custom_settings.azurerm_firewall["connectivity"][location].dns_servers, null)
private_ip_ranges = try(local.custom_settings.azurerm_firewall["connectivity"][location].private_ip_ranges, null)
management_ip_configuration = try(local.custom_settings.azurerm_firewall["connectivity"][location].management_ip_configuration, local.empty_list)
threat_intel_mode = try(local.custom_settings.azurerm_firewall["connectivity"][location].threat_intel_mode, null)
virtual_hub = try(local.custom_settings.azurerm_firewall["connectivity"][location].virtual_hub, local.empty_list)
zones = try(local.custom_settings.azurerm_firewall["connectivity"][location].zones, local.azfw_zones[location])
tags = try(local.custom_settings.azurerm_firewall["connectivity"][location].tags, local.tags)
# Child resource definition attributes
azurerm_public_ip = {
# Resource logic attributes
resource_id = "${local.virtual_network_resource_group_id[location]}/providers/Microsoft.Network/publicIPAddresses/${local.azfw_name[location]}-pip"
managed_by_module = local.deploy_azure_firewall[location]
# Resource definition attributes
name = "${local.azfw_name[location]}-pip"
resource_group_name = local.resource_group_names_by_scope_and_location["connectivity"][location]
location = location
sku = try(local.custom_settings.azurerm_public_ip["connectivity"]["azfw"][location].sku, "Standard")
allocation_method = try(local.custom_settings.azurerm_public_ip["connectivity"]["azfw"][location].allocation_method, "Static")
ip_version = try(local.custom_settings.azurerm_public_ip["connectivity"]["azfw"][location].ip_version, null)
idle_timeout_in_minutes = try(local.custom_settings.azurerm_public_ip["connectivity"]["azfw"][location].idle_timeout_in_minutes, null)
domain_name_label = try(local.custom_settings.azurerm_public_ip["connectivity"]["azfw"][location].domain_name_label, null)
reverse_fqdn = try(local.custom_settings.azurerm_public_ip["connectivity"]["azfw"][location].reverse_fqdn, null)
public_ip_prefix_id = try(local.custom_settings.azurerm_public_ip["connectivity"]["azfw"][location].public_ip_prefix_id, null)
ip_tags = try(local.custom_settings.azurerm_public_ip["connectivity"]["azfw"][location].ip_tags, null)
tags = try(local.custom_settings.azurerm_public_ip["connectivity"]["azfw"][location].tags, local.tags)
availability_zone = try(
local.custom_settings.azurerm_public_ip["connectivity"]["azfw"][location].availability_zone,
local.azfw_zones_enabled[location] ? "Zone-Redundant" : "No-Zone"
)
zones = try(local.custom_settings.azurerm_firewall["virtual_wan"][location].zones, null)
tags = try(local.custom_settings.azurerm_firewall["virtual_wan"][location].tags, local.tags)
# Child resource definition attributes
azurerm_public_ip = {}
}
]
)
}
# Configuration settings for resource type:
# - azurerm_virtual_wan
# We only support creation of a single azurerm_virtual_wan resource
# per module deployment. This uses the default location set at the
# scope of the connectivity child module.
locals {
virtual_wan_name = {
for location in local.virtual_wan_locations :
location =>
try(local.custom_settings.azurerm_virtual_wan["virtual_wan"][location].name,
"${local.resource_prefix}-vwan-${location}${local.resource_suffix}")
}
virtual_wan_resource_group_id = {
for location in local.virtual_wan_locations :
location =>
local.resource_group_config_by_scope_and_location["virtual_wan"][location].resource_id
}
virtual_wan_resource_id_prefix = {
for location in local.virtual_wan_locations :
location =>
"${local.virtual_wan_resource_group_id[location]}/providers/Microsoft.Network/virtualWans"
}
virtual_wan_resource_id = {
for location in local.virtual_wan_locations :
location =>
"${local.virtual_wan_resource_id_prefix[location]}/${local.virtual_wan_name[location]}"
}
azurerm_virtual_wan = [
for location in local.virtual_wan_locations :
{
# Resource logic attributes
resource_id = local.virtual_wan_resource_id[location]
managed_by_module = local.deploy_virtual_wan[location]
# Resource definition attributes
name = local.virtual_wan_name[location]
resource_group_name = local.resource_group_names_by_scope_and_location["virtual_wan"][location]
location = location
# Optional definition attributes
disable_vpn_encryption = try(local.custom_settings.azurerm_virtual_wan["virtual_wan"][location].disable_vpn_encryption, false)
allow_branch_to_branch_traffic = try(local.custom_settings.azurerm_virtual_wan["virtual_wan"][location].allow_branch_to_branch_traffic, true)
office365_local_breakout_category = try(local.custom_settings.azurerm_virtual_wan["virtual_wan"][location].office365_local_breakout_category, "None")
type = try(local.custom_settings.azurerm_virtual_wan["virtual_wan"][location].type, "Standard")
tags = try(local.custom_settings.azurerm_virtual_wan["virtual_wan"][location].tags, local.tags)
}
]
}
# Configuration settings for resource type:
# - azurerm_virtual_hub
locals {
virtual_hub_name = {
for location in local.virtual_hub_locations :
location =>
try(local.custom_settings.azurerm_virtual_hub["virtual_wan"][location].name,
"${local.resource_prefix}-hub-${location}${local.resource_suffix}")
}
virtual_hub_resource_group_name = {
for location in local.virtual_hub_locations :
location => (
contains(keys(local.virtual_hubs_by_location_for_resource_group_per_location), location) ?
local.resource_group_names_by_scope_and_location["connectivity"][location] :
local.resource_group_names_by_scope_and_location["virtual_wan"][local.virtual_wan_locations[0]]
)
}
virtual_hub_resource_group_id = {
for location in local.virtual_hub_locations :
location => (
contains(keys(local.virtual_hubs_by_location_for_resource_group_per_location), location) ?
local.resource_group_config_by_scope_and_location["connectivity"][location].resource_id :
local.resource_group_config_by_scope_and_location["virtual_wan"][local.virtual_wan_locations[0]].resource_id
)
}
virtual_hub_resource_id_prefix = {
for location in local.virtual_hub_locations :
location =>
"${local.virtual_hub_resource_group_id[location]}/providers/Microsoft.Network/virtualHubs"
}
virtual_hub_resource_id = {
for location in local.virtual_hub_locations :
location =>
"${local.virtual_hub_resource_id_prefix[location]}/${local.virtual_hub_name[location]}"
}
azurerm_virtual_hub = [
for location, virtual_hub in local.virtual_hubs_by_location :
{
# Resource logic attributes
resource_id = local.virtual_hub_resource_id[location]
managed_by_module = local.deploy_virtual_hub[location]
# Resource definition attributes
name = local.virtual_hub_name[location]
resource_group_name = local.virtual_hub_resource_group_name[location]
location = location
# Optional definition attributes
sku = coalesce(virtual_hub.config.sku, "Standard")
address_prefix = virtual_hub.config.address_prefix
virtual_wan_id = length(local.existing_virtual_wan_resource_id) > 0 ? local.existing_virtual_wan_resource_id : (
length(local.virtual_wan_locations) > 0 ?
lookup(local.virtual_wan_resource_id, local.virtual_wan_locations[0], null) :
null
)
tags = try(local.custom_settings.azurerm_virtual_hub["virtual_wan"][location].tags, local.tags)
route = [
for route in virtual_hub.config.routes :
{
address_prefixes = route.address_prefixes
next_hop_ip_address = route.next_hop_ip_address
}
]
}
]
}
# Configuration settings for resource type:
# - azurerm_express_route_gateway
locals {
virtual_hub_express_route_gateway_name = {
for location in local.virtual_hub_locations :
location =>
try(local.custom_settings.azurerm_express_route_gateway["virtual_wan"][location].name,
"${local.resource_prefix}-ergw-${location}${local.resource_suffix}")
}
virtual_hub_express_route_gateway_resource_id_prefix = {
for location in local.virtual_hub_locations :
location =>
"${local.virtual_hub_resource_group_id[location]}/providers/Microsoft.Network/expressRouteGateways"
}
virtual_hub_express_route_gateway_resource_id = {
for location in local.virtual_hub_locations :
location =>
"${local.virtual_hub_express_route_gateway_resource_id_prefix[location]}/${local.virtual_hub_express_route_gateway_name[location]}"
}
azurerm_express_route_gateway = [
for location, virtual_hub in local.virtual_hubs_by_location :
{
# Resource logic attributes
resource_id = local.virtual_hub_express_route_gateway_resource_id[location]
managed_by_module = local.deploy_virtual_hub_express_route_gateway[location]
# Resource definition attributes
name = local.virtual_hub_express_route_gateway_name[location]
resource_group_name = local.virtual_hub_resource_group_name[location]
location = location
virtual_hub_id = local.virtual_hub_resource_id[location]
scale_units = virtual_hub.config.expressroute_gateway.config.scale_unit
# Optional definition attributes
tags = try(local.custom_settings.azurerm_express_route_gateway["virtual_wan"][location].tags, local.tags)
}
]
}
# Configuration settings for resource type:
# - azurerm_vpn_gateway
locals {
virtual_hub_vpn_gateway_name = {
for location in local.virtual_hub_locations :
location =>
try(local.custom_settings.azurerm_vpn_gateway["virtual_wan"][location].name,
"${local.resource_prefix}-vpngw-${location}${local.resource_suffix}")
}
virtual_hub_vpn_gateway_resource_id_prefix = {
for location in local.virtual_hub_locations :
location =>
"${local.virtual_hub_resource_group_id[location]}/providers/Microsoft.Network/expressRouteGateways"
}
virtual_hub_vpn_gateway_resource_id = {
for location in local.virtual_hub_locations :
location =>
"${local.virtual_hub_vpn_gateway_resource_id_prefix[location]}/${local.virtual_hub_vpn_gateway_name[location]}"
}
azurerm_vpn_gateway = [
for location, virtual_hub in local.virtual_hubs_by_location :
{
# Resource logic attributes
resource_id = local.virtual_hub_vpn_gateway_resource_id[location]
managed_by_module = local.deploy_virtual_hub_vpn_gateway[location]
# Resource definition attributes
name = local.virtual_hub_vpn_gateway_name[location]
resource_group_name = local.virtual_hub_resource_group_name[location]
location = location
virtual_hub_id = local.virtual_hub_resource_id[location]
# Optional definition attributes
routing_preference = coalesce(virtual_hub.config.vpn_gateway.config.routing_preference, "Microsoft Network")
scale_unit = virtual_hub.config.vpn_gateway.config.scale_unit
tags = try(local.custom_settings.azurerm_vpn_gateway["virtual_wan"][location].tags, local.tags)
bgp_settings = [
for bgp_setting in virtual_hub.config.vpn_gateway.config.bgp_settings :
{
asn = bgp_setting.asn
peer_weight = bgp_setting.peer_weight
instance_0_bgp_peering_address = [
for instance_bgp_peering_address in bgp_setting.instance_0_bgp_peering_address :
{
custom_ips = instance_bgp_peering_address.custom_ips
}
]
instance_1_bgp_peering_address = [
for instance_bgp_peering_address in bgp_setting.instance_1_bgp_peering_address :
{
custom_ips = instance_bgp_peering_address.custom_ips
}
]
}
]
}
]
}
@ -562,10 +961,14 @@ locals {
# Configuration settings for resource type:
# - azurerm_public_ip
locals {
azurerm_public_ip = concat(
local.azurerm_virtual_network_gateway.*.azurerm_public_ip,
local.azurerm_firewall.*.azurerm_public_ip,
)
azurerm_public_ip = [
for azurerm_public_ip in concat(
local.azurerm_virtual_network_gateway.*.azurerm_public_ip,
local.azurerm_firewall.*.azurerm_public_ip,
) :
azurerm_public_ip
if length(azurerm_public_ip) > 0
]
}
# Configuration settings for resource type:
@ -749,7 +1152,7 @@ locals {
managed_by_module = local.deploy_private_dns_zone_virtual_network_link_on_hubs
}
]
spoke_virtual_networks_for_dns = flatten(
spoke_virtual_networks_for_dns = flatten([
[
for location, hub_config in local.hub_networks_by_location :
[
@ -760,12 +1163,26 @@ locals {
managed_by_module = local.deploy_private_dns_zone_virtual_network_link_on_spokes
}
]
],
[
for location, virtual_hub_config in local.virtual_hubs_by_location :
[
for spoke_resource_id in virtual_hub_config.config.spoke_virtual_network_resource_ids :
{
resource_id = spoke_resource_id
name = "${split("/", spoke_resource_id)[2]}-${uuidv5("url", spoke_resource_id)}"
managed_by_module = local.deploy_private_dns_zone_virtual_network_link_on_spokes
}
]
]
)
virtual_networks_for_dns = concat(
])
# Distinct is used to allow for situations where
# the same spoke is associated with multiple hub
# networks for peering.
virtual_networks_for_dns = distinct(concat(
local.hub_virtual_networks_for_dns,
local.spoke_virtual_networks_for_dns,
)
))
azurerm_private_dns_zone_virtual_network_link = flatten(
[
for zone in local.azurerm_private_dns_zone :
@ -817,6 +1234,31 @@ locals {
)
}
# Configuration settings for resource type:
# - azurerm_virtual_hub_connection
locals {
azurerm_virtual_hub_connection = flatten(
[
for location, virtual_hub_config in local.virtual_hubs_by_location :
[
for spoke_resource_id in virtual_hub_config.config.spoke_virtual_network_resource_ids :
{
# Resource logic attributes
resource_id = "${local.virtual_hub_resource_id[location]}/hubVirtualNetworkConnections/peering-${uuidv5("url", spoke_resource_id)}"
managed_by_module = local.deploy_virtual_hub_connection[location]
# Resource definition attributes
name = "peering-${uuidv5("url", spoke_resource_id)}"
virtual_hub_id = local.virtual_hub_resource_id[location]
remote_virtual_network_id = spoke_resource_id
# Optional definition attributes
internet_security_enabled = false
routing = local.empty_list
}
]
]
)
}
# Archetype configuration overrides
locals {
archetype_config_overrides = {
@ -900,6 +1342,7 @@ locals {
key != "managed_by_module" &&
key != "scope"
}
scope = resource.scope
managed_by_module = local.deploy_resource_groups[resource.scope][resource.location]
}
]
@ -978,7 +1421,69 @@ locals {
if resource.managed_by_module &&
key != "resource_id" &&
key != "managed_by_module" &&
key != "azurerm_public_ip"
key != "azurerm_public_ip" &&
key != "scope"
}
scope = resource.scope
managed_by_module = resource.managed_by_module
}
]
azurerm_virtual_wan = [
for resource in local.azurerm_virtual_wan :
{
resource_id = resource.resource_id
resource_name = resource.name
template = {
for key, value in resource :
key => value
if resource.managed_by_module &&
key != "resource_id" &&
key != "managed_by_module"
}
managed_by_module = resource.managed_by_module
}
]
azurerm_virtual_hub = [
for resource in local.azurerm_virtual_hub :
{
resource_id = resource.resource_id
resource_name = resource.name
template = {
for key, value in resource :
key => value
if resource.managed_by_module &&
key != "resource_id" &&
key != "managed_by_module"
}
managed_by_module = resource.managed_by_module
}
]
azurerm_express_route_gateway = [
for resource in local.azurerm_express_route_gateway :
{
resource_id = resource.resource_id
resource_name = resource.name
template = {
for key, value in resource :
key => value
if resource.managed_by_module &&
key != "resource_id" &&
key != "managed_by_module"
}
managed_by_module = resource.managed_by_module
}
]
azurerm_vpn_gateway = [
for resource in local.azurerm_vpn_gateway :
{
resource_id = resource.resource_id
resource_name = resource.name
template = {
for key, value in resource :
key => value
if resource.managed_by_module &&
key != "resource_id" &&
key != "managed_by_module"
}
managed_by_module = resource.managed_by_module
}
@ -1052,7 +1557,23 @@ locals {
for key, value in resource :
key => value
if resource.managed_by_module &&
key != "resource_id"
key != "resource_id" &&
key != "managed_by_module"
}
managed_by_module = resource.managed_by_module
}
]
azurerm_virtual_hub_connection = [
for resource in local.azurerm_virtual_hub_connection :
{
resource_id = resource.resource_id
resource_name = resource.name
template = {
for key, value in resource :
key => value
if resource.managed_by_module &&
key != "resource_id" &&
key != "managed_by_module"
}
managed_by_module = resource.managed_by_module
}
@ -1064,53 +1585,76 @@ locals {
locals {
debug_output = {
deploy_resource_groups = local.deploy_resource_groups
deploy_hub_network = local.deploy_hub_network
deploy_virtual_network_gateway = local.deploy_virtual_network_gateway
deploy_virtual_network_gateway_expressroute = local.deploy_virtual_network_gateway_expressroute
deploy_virtual_network_gateway_vpn = local.deploy_virtual_network_gateway_vpn
deploy_azure_firewall = local.deploy_azure_firewall
resource_group_names_by_scope_and_location = local.resource_group_names_by_scope_and_location
resource_group_config_by_scope_and_location = local.resource_group_config_by_scope_and_location
azurerm_resource_group = local.azurerm_resource_group
ddos_resource_group_id = local.ddos_resource_group_id
ddos_protection_plan_name = local.ddos_protection_plan_name
ddos_protection_plan_resource_id = local.ddos_protection_plan_resource_id
azurerm_network_ddos_protection_plan = local.azurerm_network_ddos_protection_plan
hub_network_locations = local.hub_network_locations
ddos_location = local.ddos_location
dns_location = local.dns_location
virtual_network_resource_group_id = local.virtual_network_resource_group_id
virtual_network_resource_id_prefix = local.virtual_network_resource_id_prefix
virtual_network_resource_id = local.virtual_network_resource_id
azurerm_virtual_network = local.azurerm_virtual_network
subnets_by_virtual_network = local.subnets_by_virtual_network
azurerm_subnet = local.azurerm_subnet
er_gateway_name = local.er_gateway_name
er_gateway_resource_id_prefix = local.er_gateway_resource_id_prefix
er_gateway_resource_id = local.er_gateway_resource_id
er_gateway_config = local.er_gateway_config
vpn_gateway_name = local.vpn_gateway_name
vpn_gateway_resource_id_prefix = local.vpn_gateway_resource_id_prefix
vpn_gateway_resource_id = local.vpn_gateway_resource_id
vpn_gateway_config = local.vpn_gateway_config
azurerm_virtual_network_gateway = local.azurerm_virtual_network_gateway
azfw_name = local.azfw_name
azfw_resource_id_prefix = local.azfw_resource_id_prefix
azfw_resource_id = local.azfw_resource_id
azurerm_firewall = local.azurerm_firewall
azurerm_public_ip = local.azurerm_public_ip
enable_private_link_by_service = local.enable_private_link_by_service
private_link_locations = local.private_link_locations
lookup_private_link_dns_zone_by_service = local.lookup_private_link_dns_zone_by_service
lookup_private_link_group_id_by_service = local.lookup_private_link_group_id_by_service
services_by_private_link_dns_zone = local.services_by_private_link_dns_zone
private_dns_zone_enabled = local.private_dns_zone_enabled
azurerm_private_dns_zone = local.azurerm_private_dns_zone
azurerm_dns_zone = local.azurerm_dns_zone
hub_virtual_networks_for_dns = local.hub_virtual_networks_for_dns
spoke_virtual_networks_for_dns = local.spoke_virtual_networks_for_dns
virtual_networks_for_dns = local.virtual_networks_for_dns
azurerm_private_dns_zone_virtual_network_link = local.azurerm_private_dns_zone_virtual_network_link
hub_networks = local.hub_networks
hub_networks_by_location = local.hub_networks_by_location
hub_network_locations = local.hub_network_locations
virtual_hubs = local.virtual_hubs
virtual_hubs_by_location = local.virtual_hubs_by_location
virtual_hub_locations = local.virtual_hub_locations
virtual_hubs_by_location_for_resource_group_per_location = local.virtual_hubs_by_location_for_resource_group_per_location
virtual_hubs_by_location_for_shared_resource_group = local.virtual_hubs_by_location_for_shared_resource_group
virtual_hubs_by_location_for_managed_virtual_wan = local.virtual_hubs_by_location_for_managed_virtual_wan
virtual_hubs_by_location_for_existing_virtual_wan = local.virtual_hubs_by_location_for_existing_virtual_wan
virtual_wan_locations = local.virtual_wan_locations
ddos_location = local.ddos_location
dns_location = local.dns_location
connectivity_locations = local.connectivity_locations
result_when_location_missing = local.result_when_location_missing
deploy_resource_groups = local.deploy_resource_groups
deploy_ddos_protection_plan = local.deploy_ddos_protection_plan
deploy_dns = local.deploy_dns
deploy_private_dns_zone_virtual_network_link_on_hubs = local.deploy_private_dns_zone_virtual_network_link_on_hubs
deploy_private_dns_zone_virtual_network_link_on_spokes = local.deploy_private_dns_zone_virtual_network_link_on_spokes
deploy_hub_network = local.deploy_hub_network
deploy_virtual_network_gateway = local.deploy_virtual_network_gateway
deploy_virtual_network_gateway_express_route = local.deploy_virtual_network_gateway_express_route
deploy_virtual_network_gateway_vpn = local.deploy_virtual_network_gateway_vpn
deploy_azure_firewall = local.deploy_azure_firewall
deploy_outbound_virtual_network_peering = local.deploy_outbound_virtual_network_peering
deploy_virtual_wan = local.deploy_virtual_wan
deploy_virtual_hub = local.deploy_virtual_hub
deploy_virtual_hub_express_route_gateway = local.deploy_virtual_hub_express_route_gateway
deploy_virtual_hub_vpn_gateway = local.deploy_virtual_hub_vpn_gateway
deploy_virtual_hub_azure_firewall = local.deploy_virtual_hub_azure_firewall
deploy_virtual_hub_connection = local.deploy_virtual_hub_connection
resource_group_names_by_scope_and_location = local.resource_group_names_by_scope_and_location
resource_group_config_by_scope_and_location = local.resource_group_config_by_scope_and_location
azurerm_resource_group = local.azurerm_resource_group
ddos_resource_group_id = local.ddos_resource_group_id
ddos_protection_plan_name = local.ddos_protection_plan_name
ddos_protection_plan_resource_id = local.ddos_protection_plan_resource_id
azurerm_network_ddos_protection_plan = local.azurerm_network_ddos_protection_plan
virtual_network_resource_group_id = local.virtual_network_resource_group_id
virtual_network_resource_id_prefix = local.virtual_network_resource_id_prefix
virtual_network_resource_id = local.virtual_network_resource_id
azurerm_virtual_network = local.azurerm_virtual_network
subnets_by_virtual_network = local.subnets_by_virtual_network
azurerm_subnet = local.azurerm_subnet
er_gateway_name = local.er_gateway_name
er_gateway_resource_id_prefix = local.er_gateway_resource_id_prefix
er_gateway_resource_id = local.er_gateway_resource_id
er_gateway_config = local.er_gateway_config
vpn_gateway_name = local.vpn_gateway_name
vpn_gateway_resource_id_prefix = local.vpn_gateway_resource_id_prefix
vpn_gateway_resource_id = local.vpn_gateway_resource_id
vpn_gateway_config = local.vpn_gateway_config
azurerm_virtual_network_gateway = local.azurerm_virtual_network_gateway
azfw_name = local.azfw_name
azfw_resource_id_prefix = local.azfw_resource_id_prefix
azfw_resource_id = local.azfw_resource_id
azurerm_firewall = local.azurerm_firewall
azurerm_public_ip = local.azurerm_public_ip
enable_private_link_by_service = local.enable_private_link_by_service
private_link_locations = local.private_link_locations
lookup_private_link_dns_zone_by_service = local.lookup_private_link_dns_zone_by_service
lookup_private_link_group_id_by_service = local.lookup_private_link_group_id_by_service
services_by_private_link_dns_zone = local.services_by_private_link_dns_zone
private_dns_zone_enabled = local.private_dns_zone_enabled
azurerm_private_dns_zone = local.azurerm_private_dns_zone
azurerm_dns_zone = local.azurerm_dns_zone
hub_virtual_networks_for_dns = local.hub_virtual_networks_for_dns
spoke_virtual_networks_for_dns = local.spoke_virtual_networks_for_dns
virtual_networks_for_dns = local.virtual_networks_for_dns
azurerm_private_dns_zone_virtual_network_link = local.azurerm_private_dns_zone_virtual_network_link
}
}

Просмотреть файл

@ -85,7 +85,60 @@ variable "settings" {
})
})
)
vwan_hub_networks = list(object({}))
vwan_hub_networks = list(
object({
enabled = bool
config = object({
address_prefix = string
location = string
sku = string
routes = list(
object({
address_prefixes = list(string)
next_hop_ip_address = string
})
)
expressroute_gateway = object({
enabled = bool
config = object({
scale_unit = number
})
})
vpn_gateway = object({
enabled = bool
config = object({
bgp_settings = list(
object({
asn = number
peer_weight = number
instance_0_bgp_peering_address = list(
object({
custom_ips = list(string)
})
)
instance_1_bgp_peering_address = list(
object({
custom_ips = list(string)
})
)
})
)
routing_preference = string
scale_unit = number
})
})
azure_firewall = object({
enabled = bool
config = object({
enable_dns_proxy = bool
sku_tier = string
})
})
spoke_virtual_network_resource_ids = list(string)
enable_virtual_hub_connections = bool
})
})
)
ddos_protection_plan = object({
enabled = bool
config = object({
@ -155,7 +208,7 @@ variable "resource_prefix" {
default = ""
validation {
condition = can(regex("^[a-zA-Z0-9-]{2,10}$", var.resource_prefix)) || var.resource_prefix == ""
condition = can(regex("^[a-zA-Z0-9-]{2,10}$", var.resource_prefix)) || var.resource_prefix == null
error_message = "Value must be between 2 to 10 characters long, consisting of alphanumeric characters and hyphens."
}
}
@ -166,7 +219,7 @@ variable "resource_suffix" {
default = ""
validation {
condition = can(regex("^[a-zA-Z0-9-]{2,36}$", var.resource_suffix)) || var.resource_suffix == ""
condition = can(regex("^[a-zA-Z0-9-]{2,36}$", var.resource_suffix)) || var.resource_suffix == null
error_message = "Value must be between 2 to 36 characters long, consisting of alphanumeric characters and hyphens."
}
@ -178,13 +231,28 @@ variable "existing_ddos_protection_plan_resource_id" {
default = ""
}
variable "existing_virtual_wan_resource_id" {
type = string
description = "If specified, module will skip creation of the Virtual WAN and use existing. All Virtual Hubs created by the module will be associated with the specified Virtual WAN."
default = ""
}
variable "resource_group_per_virtual_hub_location" {
type = bool
description = "If set to true, module will place each Virtual Hub (and associated resources) in a location-specific Resource Group. Default behaviour is to colocate Virtual Hub resources in the same Resource Group as the Virtual WAN resource."
default = false
}
variable "custom_settings_by_resource_type" {
type = any
description = "If specified, allows full customization of common settings for all resources (by type) deployed by this module."
default = {}
validation {
condition = can([for k in keys(var.custom_settings_by_resource_type) : contains(["azurerm_resource_group", "azurerm_virtual_network", "azurerm_subnet", "azurerm_virtual_network_gateway", "azurerm_public_ip", "azurerm_firewall", "azurerm_network_ddos_protection_plan", "azurerm_dns_zone", "azurerm_virtual_network_peering"], k)]) || var.custom_settings_by_resource_type == {}
condition = (
can([for k in keys(var.custom_settings_by_resource_type) : contains(["azurerm_resource_group", "azurerm_virtual_network", "azurerm_subnet", "azurerm_virtual_network_gateway", "azurerm_public_ip", "azurerm_firewall", "azurerm_network_ddos_protection_plan", "azurerm_dns_zone", "azurerm_virtual_network_peering"], k)]) ||
var.custom_settings_by_resource_type == null
)
error_message = "Invalid key specified. Please check the list of allowed resource types supported by the connectivity module for caf-enterprise-scale."
}
}

Просмотреть файл

@ -168,7 +168,6 @@ locals {
workspace_id = try(local.custom_settings_la_linked_service.workspace_id, local.log_analytics_workspace_resource_id)
read_access_id = try(local.custom_settings_la_linked_service.read_access_id, local.automation_account_resource_id) # This should be used for linking to an Automation Account resource.
write_access_id = null # DO NOT USE. This should be used for linking to a Log Analytics Cluster resource
tags = try(local.custom_settings_la_linked_service.tags, local.tags)
resource_group_name = coalesce(
try(local.custom_settings_la_linked_service.resource_group_name, null),
local.resource_group_name,

Просмотреть файл

@ -52,7 +52,8 @@ output "azurerm_role_definition" {
# Assignment data is returned to the root module.
output "azurerm_role_assignment" {
value = {
enterprise_scale = azurerm_role_assignment.enterprise_scale
enterprise_scale = azurerm_role_assignment.enterprise_scale
policy_assignment = azurerm_role_assignment.policy_assignment
}
description = "Returns the configuration data for all Role Assignments created by this module."
}

Просмотреть файл

@ -8,11 +8,11 @@ resource "azurerm_policy_definition" "enterprise_scale" {
display_name = each.value.template.properties.displayName
# Optional resource attributes
description = try(each.value.template.properties.description, "${each.value.template.name} Policy Definition at scope ${each.value.scope_id}")
management_group_name = try(basename(each.value.scope_id), null)
policy_rule = try(length(each.value.template.properties.policyRule) > 0, false) ? jsonencode(each.value.template.properties.policyRule) : null
metadata = try(length(each.value.template.properties.metadata) > 0, false) ? jsonencode(each.value.template.properties.metadata) : null
parameters = try(length(each.value.template.properties.parameters) > 0, false) ? jsonencode(each.value.template.properties.parameters) : null
description = try(each.value.template.properties.description, "${each.value.template.name} Policy Definition at scope ${each.value.scope_id}")
management_group_id = try(basename(each.value.scope_id), null)
policy_rule = try(length(each.value.template.properties.policyRule) > 0, false) ? jsonencode(each.value.template.properties.policyRule) : null
metadata = try(length(each.value.template.properties.metadata) > 0, false) ? jsonencode(each.value.template.properties.metadata) : null
parameters = try(length(each.value.template.properties.parameters) > 0, false) ? jsonencode(each.value.template.properties.parameters) : null
# Set explicit dependency on Management Group deployments
depends_on = [

Просмотреть файл

@ -24,10 +24,10 @@ resource "azurerm_policy_set_definition" "enterprise_scale" {
}
# Optional resource attributes
description = try(each.value.template.properties.description, "${each.value.template.properties.displayName} Policy Set Definition at scope ${each.value.scope_id}")
management_group_name = try(basename(each.value.scope_id), null)
metadata = try(length(each.value.template.properties.metadata) > 0, false) ? jsonencode(each.value.template.properties.metadata) : null
parameters = try(length(each.value.template.properties.parameters) > 0, false) ? jsonencode(each.value.template.properties.parameters) : null
description = try(each.value.template.properties.description, "${each.value.template.properties.displayName} Policy Set Definition at scope ${each.value.scope_id}")
management_group_id = try(basename(each.value.scope_id), null)
metadata = try(length(each.value.template.properties.metadata) > 0, false) ? jsonencode(each.value.template.properties.metadata) : null
parameters = try(length(each.value.template.properties.parameters) > 0, false) ? jsonencode(each.value.template.properties.parameters) : null
# Set explicit dependency on Management Group and Policy Definition deployments
depends_on = [

256
resources.virtual_wan.tf Normal file
Просмотреть файл

@ -0,0 +1,256 @@
resource "azurerm_resource_group" "virtual_wan" {
for_each = local.azurerm_resource_group_virtual_wan
provider = azurerm.connectivity
# Mandatory resource attributes
name = each.value.template.name
location = each.value.template.location
tags = each.value.template.tags
}
resource "azurerm_virtual_wan" "virtual_wan" {
for_each = local.azurerm_virtual_wan_virtual_wan
provider = azurerm.connectivity
# Mandatory resource attributes
name = each.value.template.name
resource_group_name = each.value.template.resource_group_name
location = each.value.template.location
# Optional resource attributes
disable_vpn_encryption = each.value.template.disable_vpn_encryption
allow_branch_to_branch_traffic = each.value.template.allow_branch_to_branch_traffic
office365_local_breakout_category = each.value.template.office365_local_breakout_category
type = each.value.template.type
tags = each.value.template.tags
# Set explicit dependencies
depends_on = [
azurerm_resource_group.connectivity,
azurerm_resource_group.virtual_wan,
]
}
resource "azurerm_virtual_hub" "virtual_wan" {
for_each = local.azurerm_virtual_hub_virtual_wan
provider = azurerm.connectivity
# Mandatory resource attributes
name = each.value.template.name
resource_group_name = each.value.template.resource_group_name
location = each.value.template.location
# Optional resource attributes
sku = each.value.template.sku
address_prefix = each.value.template.address_prefix
virtual_wan_id = each.value.template.virtual_wan_id
tags = each.value.template.tags
# Dynamic configuration blocks
dynamic "route" {
for_each = each.value.template.route
content {
# Mandatory attributes
address_prefixes = route.value["address_prefixes"]
next_hop_ip_address = route.value["next_hop_ip_address"]
}
}
# Set explicit dependencies
depends_on = [
azurerm_resource_group.connectivity,
azurerm_resource_group.virtual_wan,
azurerm_virtual_wan.virtual_wan,
]
}
resource "azurerm_express_route_gateway" "virtual_wan" {
for_each = local.azurerm_express_route_gateway_virtual_wan
provider = azurerm.connectivity
# Mandatory resource attributes
name = each.value.template.name
resource_group_name = each.value.template.resource_group_name
location = each.value.template.location
virtual_hub_id = each.value.template.virtual_hub_id
scale_units = each.value.template.scale_units
# Optional resource attributes
tags = each.value.template.tags
# Set explicit dependencies
depends_on = [
azurerm_resource_group.connectivity,
azurerm_resource_group.virtual_wan,
azurerm_virtual_wan.virtual_wan,
azurerm_virtual_hub.virtual_wan,
]
}
resource "azurerm_vpn_gateway" "virtual_wan" {
for_each = local.azurerm_vpn_gateway_virtual_wan
provider = azurerm.connectivity
# Mandatory resource attributes
name = each.value.template.name
resource_group_name = each.value.template.resource_group_name
location = each.value.template.location
virtual_hub_id = each.value.template.virtual_hub_id
# Optional resource attributes
routing_preference = each.value.template.routing_preference
scale_unit = each.value.template.scale_unit
tags = each.value.template.tags
# Dynamic configuration blocks
dynamic "bgp_settings" {
for_each = each.value.template.bgp_settings
content {
# Mandatory attributes
asn = bgp_settings.value["asn"]
peer_weight = bgp_settings.value["peer_weight"]
# Dynamic configuration blocks
dynamic "instance_0_bgp_peering_address" {
for_each = bgp_settings.value["instance_0_bgp_peering_address"]
content {
custom_ips = instance_0_bgp_peering_address.value["custom_ips"]
}
}
dynamic "instance_1_bgp_peering_address" {
for_each = bgp_settings.value["instance_1_bgp_peering_address"]
content {
custom_ips = instance_1_bgp_peering_address.value["custom_ips"]
}
}
}
}
# Set explicit dependencies
depends_on = [
azurerm_resource_group.connectivity,
azurerm_resource_group.virtual_wan,
azurerm_virtual_wan.virtual_wan,
azurerm_virtual_hub.virtual_wan,
]
}
resource "azurerm_firewall" "virtual_wan" {
for_each = local.azurerm_firewall_virtual_wan
provider = azurerm.connectivity
# Mandatory resource attributes
name = each.value.template.name
resource_group_name = each.value.template.resource_group_name
location = each.value.template.location
# Optional resource attributes
sku_name = each.value.template.sku_name
sku_tier = each.value.template.sku_tier
firewall_policy_id = each.value.template.firewall_policy_id
dns_servers = each.value.template.dns_servers
private_ip_ranges = each.value.template.private_ip_ranges
threat_intel_mode = each.value.template.threat_intel_mode
zones = each.value.template.zones
tags = each.value.template.tags
# Dynamic configuration blocks
dynamic "ip_configuration" {
for_each = each.value.template.ip_configuration
content {
# Mandatory attributes
name = ip_configuration.value["name"]
public_ip_address_id = ip_configuration.value["public_ip_address_id"]
# Optional attributes
subnet_id = try(ip_configuration.value["subnet_id"], null)
}
}
dynamic "management_ip_configuration" {
for_each = each.value.template.management_ip_configuration
content {
# Mandatory attributes
name = management_ip_configuration.value["name"]
public_ip_address_id = management_ip_configuration.value["public_ip_address_id"]
# Optional attributes
subnet_id = try(management_ip_configuration.value["subnet_id"], null)
}
}
dynamic "virtual_hub" {
for_each = each.value.template.virtual_hub
content {
# Mandatory attributes
virtual_hub_id = virtual_hub.value["virtual_hub_id"]
# Optional attributes
public_ip_count = try(virtual_hub.value["public_ip_count"], null)
}
}
# Set explicit dependencies
depends_on = [
azurerm_resource_group.connectivity,
azurerm_resource_group.virtual_wan,
azurerm_virtual_wan.virtual_wan,
azurerm_virtual_hub.virtual_wan,
]
}
resource "azurerm_virtual_hub_connection" "virtual_wan" {
for_each = local.azurerm_virtual_hub_connection
provider = azurerm.connectivity
# Mandatory resource attributes
name = each.value.template.name
virtual_hub_id = each.value.template.virtual_hub_id
remote_virtual_network_id = each.value.template.remote_virtual_network_id
# Optional resource attributes
internet_security_enabled = each.value.template.internet_security_enabled
# Dynamic configuration blocks
dynamic "routing" {
for_each = each.value.template.routing
content {
# Optional attributes
associated_route_table_id = lookup(routing.value, "associated_route_table_id", null)
dynamic "propagated_route_table" {
for_each = lookup(routing.value, "propagated_route_table", local.empty_list)
content {
# Optional attributes
labels = lookup(propagated_route_table.value, "labels", null)
route_table_ids = lookup(propagated_route_table.value, "route_table_ids", null)
}
}
dynamic "static_vnet_route" {
for_each = lookup(routing.value, "static_vnet_route", local.empty_list)
content {
# Optional attributes
name = lookup(static_vnet_route.value, "name", null)
address_prefixes = lookup(static_vnet_route.value, "address_prefixes", null)
next_hop_ip_address = lookup(static_vnet_route.value, "next_hop_ip_address", null)
}
}
}
}
# Set explicit dependencies
depends_on = [
azurerm_resource_group.connectivity,
azurerm_resource_group.virtual_wan,
azurerm_virtual_wan.virtual_wan,
azurerm_virtual_hub.virtual_wan,
]
}

Просмотреть файл

@ -3,7 +3,7 @@ terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 2.77.0"
version = ">= 2.96.0"
configuration_aliases = [
azurerm.connectivity,
azurerm.management,

117
tests/README.md Normal file
Просмотреть файл

@ -0,0 +1,117 @@
# Test Framework for the Terraform Module for Cloud Adoption Framework Enterprise-scale
This folder contains code relating to the test framework for this module.
Testing is currently performed in the following stages:
1. Code Review (GitHub Actions)
1. Unit Tests (Azure Pipelines)
1. E2E Tests (Azure Pipelines)
The decision to break testing up in this manner was to ensure developers get quick feedback when working on bug fixes and new features, whilst providing greater assurance that the latest updates work as expected and do not break existing functionality.
## Code Review (GitHub Actions)
The first quality check ensures all code complies with recommended coding practices.
We use [GitHub Super-Linter (v4.1.0)](https://github.com/github/super-linter/tree/v4.1.0) to perform this initial check across the code base.
By running this within a GitHub Action, anyone contributing to the code can get quick feedback on each commit pushed to GitHub.
GitHub Super-Linter is configured to run checks against the full codebase using the following Linters:
| *Language* | *Linter* |
| ---------- | -------- |
| **JSON** | [jsonlint](https://github.com/zaach/jsonlint) |
| **Markdown** | [markdownlint](https://github.com/igorshubovych/markdownlint-cli#readme) |
| **PowerShell** | [PSScriptAnalyzer](https://github.com/PowerShell/Psscriptanalyzer) |
| **Shell** | [Shellcheck](https://github.com/koalaman/shellcheck) / [executable bit check] / [shfmt](https://github.com/mvdan/sh) |
| **Terraform** | [tflint](https://github.com/terraform-linters/tflint) / [terrascan](https://github.com/accurics/terrascan) |
| **YAML** | [YamlLint](https://github.com/adrienverge/yamllint) |
This is also a mandatory check on all PR's being raised against the `main` branch.
## Unit Tests (Azure Pipelines)
As linting only let's you know if the code is well written (according to a pre-determined set of standards), we also need to determine whether the code generates a valid Terraform plan.
To verify this, we have a set of unit tests which run additional checks against the module using a series of test deployments.
To give assurance that the module works with the specified range of supported versions of Terraform and the Azure provider, we use a [matrix strategy](#multi_job_configuration_matrix_strategy)) to automatically generate parallel running jobs for each version combination.
The Unit Tests consist of the following tasks:
| *Task Name* | *Description* |
| --- | --- |
| **Install Terraform Pre-requisites** | Ensures the required version of Terraform is installed on the agent. |
| **Prepare Terraform Environment** | Retrieves credentials for the target test environment and sets a unique value for the `root_id` input variable.<sup>1</sup> |
| **Terraform Linting (terraform fmt)** | Runs `terraform fmt` against the entire repository in `-check` mode to ensure Terraform code is correctly formatted. |
| **Install OPA/Conftest Pre-requisites** | Ensure the required version of `Conftest`, `jq`, `yq` and `yamllint` are installed on the agent. |
| **Test 001 (terraform init) Baseline** | Initialize the root module for this test instance. |
| **Test 001 (terraform plan) Baseline** | Generate a Terraform plan for this test instance. |
| **Test 001 (conftest) Baseline** | Run Conftest to ensure the Terraform plan matches the expected configuration for this test instance. |
| **Test 002 (terraform init) Add Custom Core** | Initialize the root module for this test instance. |
| **Test 002 (terraform plan) Add Custom Core** | Generate a Terraform plan for this test instance. |
| **Test 002 (conftest) Add Custom Core** | Run Conftest to ensure the Terraform plan matches the expected configuration for this test instance. |
| **Test 003 (terraform init) Add Management and Connectivity** | Initialize the root module for this test instance. |
| **Test 003 (terraform plan) Add Management and Connectivity** | Generate a Terraform plan for this test instance. |
| **Test 003 (conftest) Add Management and Connectivity** | Run Conftest to ensure the Terraform plan matches the expected configuration for this test instance. |
<sup>1</sup> *Each job uses a dedicated SPN (with certificate based authentication) to connect to Azure.*
*This is to minimize the risk of API rate limiting when running highly parallel resource deployments in the pipeline.*
## E2E Tests (Azure Pipelines)
The E2E Tests consist of the following tasks:
| *Task Name* | *Description* |
| --- | --- |
| **Install Terraform Pre-requisites** | Ensures the required version of Terraform is installed on the agent. |
| **Prepare Terraform Environment** | Retrieves credentials for the target test environment and sets a unique value for the `root_id` input variable.<sup>1</sup> |
| **Terraform Linting (terraform fmt)** | Runs `terraform fmt` against the entire repository in `-check` mode to ensure Terraform code is correctly formatted. |
| **Test 001 (terraform init) Baseline** | Initialize the root module for this test instance. |
| **Test 001 (terraform plan) Baseline** | Generate a Terraform plan for this test instance. |
| **Test 001 (terraform apply) Baseline** | Apply the Terraform plan for this test instance. |
| **Test 002 (terraform init) Add Custom Core** | Initialize the root module for this test instance. |
| **Test 002 (terraform plan) Add Custom Core** | Generate a Terraform plan for this test instance. |
| **Test 002 (terraform apply) Add Custom Core** | Apply the Terraform plan for this test instance. |
| **Test 003 (terraform init) Add Management and Connectivity** | Initialize the root module for this test instance. |
| **Test 003 (terraform plan) Add Management and Connectivity** | Generate a Terraform plan for this test instance. |
| **Test 003 (terraform apply) Add Management and Connectivity** | Apply the Terraform plan for this test instance. |
| **Clean-up Test Environment (terraform destroy)** | Run `terraform destroy` to clean-up the test environment.<sup>2</sup> |
> <sup>1</sup> *Each job uses a dedicated SPN (with certificate based authentication) to connect to Azure.*
> *This is to minimize the risk of API rate limiting when running highly parallel resource deployments in the pipeline.*
>
> <sup>2</sup> *The* `terraform destroy` *task uses the* `always()` *condition to ensure the environment is cleaned-up if any of the previous tasks fail after a partial deployment.*
## Why Azure Pipelines?
The Unit Tests and E2E Tests need valid Azure credentials to authenticate with the Azure platform for Terraform to work.
These tests are run on Azure Pipelines as a security measure, allowing contributed code from forked repositories to be reviewed before tests are manually triggered by a repository contributor using [comment triggers](https://docs.microsoft.com/azure/devops/pipelines/repos/github?view=azure-devops&tabs=yaml#comment-triggers).
Although GitHub Actions could technically run these jobs, GitHub prevents access to secrets for jobs triggered from forks.
## Multi-job configuration (`matrix` strategy)
Azure Pipelines provides the option to define a [multi-job configuration](https://docs.microsoft.com/azure/devops/pipelines/process/phases?view=azure-devops&tabs=yaml#multi-job-configuration).
This enables multi-configuration testing to be implemented from a common set of tasks, with the benefit of running multiple jobs on multiple agents in parallel.
Our implementation uses a programmatically generated `matrix` strategy to ensure we can meet our testing requirements.
This is designed to ensure the module works with different combinations of Terraform and Azure provider versions.
The strategy is generated by a [PowerShell script](https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/blob/main/tests/scripts/azp-strategy.ps1), and is used by both the Unit and E2E tests.
The current strategy consists of running tests against the following version combinations:
- Terraform versions:
- Minimum version supported by the module (`0.15.0`)
- Latest `0.15.x` version
- Latest `1.0.x` version
- Azure provider for Terraform versions:
- Minimum version supported by the module (`v2.77.0`)
- Latest version
The latest versions are determined programmatically by querying the publisher APIs.
This negates the need to update the code or pipeline to ensure the latest version is being tested.
With the frequency at which we run tests these combinations give reasonable assurance that the module will work with all version combinations up to the latest versions, not withstanding any which temporarily introduce bugs.
The `matrix` strategy also uses the [Microsoft.Subscription/aliases@2021-10-01](https://docs.microsoft.com/rest/api/subscription/2020-09-01/alias) API to map Subscriptions to each job within the Matrix.
This ensures that each job has dedicated Subscriptions to deploy resources into, and place within the Management Group hierarchy.
In combination with the dedicated SPN per job, this also increases the API rate limits available to the pipeline.

Просмотреть файл

@ -1,154 +0,0 @@
data "azurerm_client_config" "connectivity" {
provider = azurerm.connectivity
}
data "azurerm_client_config" "management" {
provider = azurerm.management
}
module "test_root_id_1" {
source = "../../"
providers = {
azurerm = azurerm.management
azurerm.connectivity = azurerm.connectivity
azurerm.management = azurerm.management
}
# Base module configuration settings
root_parent_id = data.azurerm_client_config.management.tenant_id
root_id = var.root_id_1
root_name = "${var.root_name}-1"
default_location = var.location
default_tags = local.default_tags
# Tuning delay timers to improve pipeline completion success rate
create_duration_delay = var.create_duration_delay
destroy_duration_delay = var.destroy_duration_delay
}
module "test_root_id_2" {
source = "../../"
providers = {
azurerm = azurerm.management
azurerm.connectivity = azurerm.connectivity
azurerm.management = azurerm.management
}
# Base module configuration settings
root_parent_id = data.azurerm_client_config.management.tenant_id
root_id = var.root_id_2
root_name = "${var.root_name}-2"
default_location = var.location
default_tags = local.default_tags
# Configuration settings for optional landing zones
deploy_corp_landing_zones = true
deploy_online_landing_zones = true
deploy_sap_landing_zones = true
deploy_demo_landing_zones = true
# Tuning delay timers to improve pipeline completion success rate
create_duration_delay = var.create_duration_delay
destroy_duration_delay = var.destroy_duration_delay
}
module "test_root_id_3" {
source = "../../"
providers = {
azurerm = azurerm.management
azurerm.connectivity = azurerm.connectivity
azurerm.management = azurerm.management
}
# Base module configuration settings
root_parent_id = data.azurerm_client_config.management.tenant_id
root_id = var.root_id_3
root_name = "${var.root_name}-3"
library_path = "${path.root}/lib"
default_location = var.location
default_tags = local.default_tags
# Configuration settings for optional landing zones
deploy_corp_landing_zones = true
deploy_online_landing_zones = true
deploy_sap_landing_zones = true
deploy_demo_landing_zones = false
# Configuration settings for core resources
custom_landing_zones = local.custom_landing_zones
archetype_config_overrides = local.archetype_config_overrides
subscription_id_overrides = local.subscription_id_overrides
# Configuration settings for management resources
deploy_management_resources = true
configure_management_resources = local.configure_management_resources
subscription_id_management = data.azurerm_client_config.management.subscription_id
# Configuration settings for connectivity resources
deploy_connectivity_resources = true
configure_connectivity_resources = local.configure_connectivity_resources
subscription_id_connectivity = data.azurerm_client_config.connectivity.subscription_id
# For testing custom template file variables
template_file_variables = local.custom_template_file_variables
# Tuning delay timers to improve pipeline completion success rate
create_duration_delay = var.create_duration_delay
destroy_duration_delay = var.destroy_duration_delay
}
module "test_root_id_3_lz1" {
source = "../../"
providers = {
azurerm = azurerm.management
azurerm.connectivity = azurerm.connectivity
azurerm.management = azurerm.management
}
root_parent_id = "${var.root_id_3}-landing-zones"
root_id = var.root_id_3
deploy_core_landing_zones = false
library_path = "${path.root}/lib"
default_location = var.location
default_tags = local.default_tags
custom_landing_zones = {
"${var.root_id_3}-scoped-lz1" = {
display_name = "Scoped LZ1"
parent_management_group_id = "${var.root_id_3}-landing-zones"
subscription_ids = []
archetype_config = {
archetype_id = "customer_online"
parameters = {
Deny-Resource-Locations = {
listOfAllowedLocations = [
"northcentralus",
"southcentralus",
]
}
}
access_control = {}
}
}
}
# For testing custom template file variables
template_file_variables = local.custom_template_file_variables
# Tuning delay timers to improve pipeline completion success rate
create_duration_delay = var.create_duration_delay
destroy_duration_delay = var.destroy_duration_delay
# Set dependency to ensure correct operation
depends_on = [
module.test_root_id_3,
]
}

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -1,6 +0,0 @@
# Configure shared settings.
locals {
default_tags = {
deployedBy = "terraform/azure/caf-enterprise-scale/tests/deployment"
}
}

Просмотреть файл

@ -0,0 +1,32 @@
output "connectivity" {
value = {
configure_connectivity_resources = local.configure_connectivity_resources
}
}
output "core" {
value = {
custom_landing_zones = local.custom_landing_zones
archetype_config_overrides = local.archetype_config_overrides
subscription_id_overrides = local.subscription_id_overrides
custom_template_file_variables = local.custom_template_file_variables
}
}
output "management" {
value = {
configure_management_resources = local.configure_management_resources
}
}
output "nested" {
value = {
custom_landing_zones = local.nested_custom_landing_zones
}
}
output "shared" {
value = {
default_tags = local.default_tags
}
}

Просмотреть файл

@ -6,8 +6,8 @@ locals {
{
enabled = true
config = {
address_space = ["10.100.0.0/16", ]
location = var.location
address_space = ["10.100.0.0/22", ]
location = var.primary_location
link_to_ddos_protection_plan = false
dns_servers = []
bgp_community = ""
@ -16,8 +16,8 @@ locals {
enabled = true
config = {
address_prefix = "10.100.1.0/24"
gateway_sku_expressroute = "ErGw2AZ"
gateway_sku_vpn = "VpnGw2AZ"
gateway_sku_expressroute = "ErGw1AZ"
gateway_sku_vpn = "VpnGw1AZ"
}
}
azure_firewall = {
@ -36,8 +36,106 @@ locals {
enable_outbound_virtual_network_peering = false
}
},
{
enabled = true
config = {
address_space = ["10.101.0.0/22", ]
location = var.secondary_location
link_to_ddos_protection_plan = false
dns_servers = []
bgp_community = ""
subnets = []
virtual_network_gateway = {
enabled = false
config = {
address_prefix = "10.101.1.0/24"
gateway_sku_expressroute = "ErGw1AZ"
gateway_sku_vpn = "VpnGw1AZ"
}
}
azure_firewall = {
enabled = false
config = {
address_prefix = "10.101.0.0/24"
enable_dns_proxy = true
availability_zones = {
zone_1 = true
zone_2 = true
zone_3 = true
}
}
}
spoke_virtual_network_resource_ids = []
enable_outbound_virtual_network_peering = false
}
},
]
vwan_hub_networks = [
{
enabled = true
config = {
address_prefix = "10.200.0.0/22"
location = var.primary_location
sku = ""
routes = []
expressroute_gateway = {
enabled = true
config = {
scale_unit = 1
}
}
vpn_gateway = {
enabled = true
config = {
bgp_settings = []
routing_preference = ""
scale_unit = 1
}
}
azure_firewall = {
enabled = true
config = {
enable_dns_proxy = false
sku_tier = "Standard"
}
}
spoke_virtual_network_resource_ids = []
enable_virtual_hub_connections = true
}
},
{
enabled = true
config = {
address_prefix = "10.201.0.0/22"
location = var.secondary_location
sku = ""
routes = []
expressroute_gateway = {
enabled = false
config = {
scale_unit = 1
}
}
vpn_gateway = {
enabled = false
config = {
bgp_settings = []
routing_preference = ""
scale_unit = 1
}
}
azure_firewall = {
enabled = false
config = {
enable_dns_proxy = false
sku_tier = "Standard"
}
}
spoke_virtual_network_resource_ids = []
enable_virtual_hub_connections = true
}
},
]
vwan_hub_networks = []
ddos_protection_plan = {
enabled = false
config = {

Просмотреть файл

@ -2,9 +2,9 @@
# addition the core resource hierarchy.
locals {
custom_landing_zones = {
"${var.root_id_3}-secure" = {
"${var.root_id}-secure" = {
display_name = "Secure Workloads (HITRUST/HIPAA)"
parent_management_group_id = "${var.root_id_3}-landing-zones"
parent_management_group_id = "${var.root_id}-landing-zones"
subscription_ids = []
archetype_config = {
archetype_id = "customer_secure"
@ -23,8 +23,8 @@ locals {
}
Deploy-HITRUST-HIPAA = {
CertificateThumbprints = ""
DeployDiagnosticSettingsforNetworkSecurityGroupsrgName = "${var.root_id_3}-rg"
DeployDiagnosticSettingsforNetworkSecurityGroupsstoragePrefix = var.root_id_3
DeployDiagnosticSettingsforNetworkSecurityGroupsrgName = "${var.root_id}-rg"
DeployDiagnosticSettingsforNetworkSecurityGroupsstoragePrefix = var.root_id
installedApplicationsOnWindowsVM = ""
listOfLocations = [
"eastus",
@ -34,9 +34,9 @@ locals {
access_control = {}
}
}
"${var.root_id_3}-web-global" = {
"${var.root_id}-web-global" = {
display_name = "Global Web Applications"
parent_management_group_id = "${var.root_id_3}-online"
parent_management_group_id = "${var.root_id}-online"
subscription_ids = []
archetype_config = {
archetype_id = "default_empty"
@ -44,9 +44,9 @@ locals {
access_control = {}
}
}
"${var.root_id_3}-web-us" = {
"${var.root_id}-web-us" = {
display_name = "US Web Applications"
parent_management_group_id = "${var.root_id_3}-online"
parent_management_group_id = "${var.root_id}-online"
subscription_ids = []
archetype_config = {
archetype_id = "customer_online"
@ -67,9 +67,9 @@ locals {
access_control = {}
}
}
"${var.root_id_3}-web-emea" = {
"${var.root_id}-web-emea" = {
display_name = "EMEA Web Applications"
parent_management_group_id = "${var.root_id_3}-online"
parent_management_group_id = "${var.root_id}-online"
subscription_ids = []
archetype_config = {
archetype_id = "customer_online"
@ -129,8 +129,8 @@ locals {
}
Deploy-HITRUST-HIPAA = {
CertificateThumbprints = ""
DeployDiagnosticSettingsforNetworkSecurityGroupsrgName = "${var.root_id_3}-rg"
DeployDiagnosticSettingsforNetworkSecurityGroupsstoragePrefix = var.root_id_3
DeployDiagnosticSettingsforNetworkSecurityGroupsrgName = "${var.root_id}-rg"
DeployDiagnosticSettingsforNetworkSecurityGroupsstoragePrefix = var.root_id
installedApplicationsOnWindowsVM = ""
listOfLocations = [
"eastus",

Просмотреть файл

@ -23,7 +23,7 @@ locals {
security_center = {
enabled = true
config = {
email_security_contact = "test.user@replace_me"
email_security_contact = var.email_security_contact
enable_defender_for_app_services = true
enable_defender_for_arm = true
enable_defender_for_containers = true
@ -40,7 +40,7 @@ locals {
location = null
tags = {
deployedBy = "terraform/azure/caf-enterprise-scale"
deployedBy = "${local.default_tags.deployedBy}/management"
}
advanced = null
}

Просмотреть файл

@ -0,0 +1,21 @@
locals {
nested_custom_landing_zones = {
"${var.root_id}-custom-lz1" = {
display_name = "Nested Custom LZ1"
parent_management_group_id = "${var.root_id}-landing-zones"
subscription_ids = []
archetype_config = {
archetype_id = "customer_online"
parameters = {
Deny-Resource-Locations = {
listOfAllowedLocations = [
"northcentralus",
"southcentralus",
]
}
}
access_control = {}
}
}
}
}

Просмотреть файл

@ -0,0 +1,6 @@
# Configure shared settings.
locals {
default_tags = {
deployedBy = "terraform/azure/caf-enterprise-scale/test_framework"
}
}

Просмотреть файл

@ -0,0 +1,19 @@
variable "root_id" {
type = string
default = "test"
}
variable "primary_location" {
type = string
default = "northeurope"
}
variable "secondary_location" {
type = string
default = "westeurope"
}
variable "email_security_contact" {
type = string
default = "test.user@replace_me"
}

Просмотреть файл

@ -0,0 +1,7 @@
data "azurerm_client_config" "connectivity" {
provider = azurerm.connectivity
}
data "azurerm_client_config" "management" {
provider = azurerm.management
}

Просмотреть файл

@ -0,0 +1,21 @@
module "test_core" {
source = "../../../"
providers = {
azurerm = azurerm.management
azurerm.connectivity = azurerm.connectivity
azurerm.management = azurerm.management
}
# Base module configuration settings
root_parent_id = data.azurerm_client_config.management.tenant_id
root_id = var.root_id
root_name = var.root_name
default_location = var.primary_location
default_tags = module.settings.shared.default_tags
# Tuning delay timers to improve pipeline completion success rate
create_duration_delay = var.create_duration_delay
destroy_duration_delay = var.destroy_duration_delay
}

Просмотреть файл

@ -6,10 +6,7 @@
output "resource_ids" {
value = {
for module_name, module_output in {
test_root_id_1 = module.test_root_id_1
test_root_id_2 = module.test_root_id_2
test_root_id_3 = module.test_root_id_3
test_root_id_3_lz1 = module.test_root_id_3_lz1
test_core = module.test_core
} :
module_name => {
for resource_type, resource_instances in module_output :

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -0,0 +1,13 @@
provider "azurerm" {
features {}
}
provider "azurerm" {
alias = "connectivity"
features {}
}
provider "azurerm" {
alias = "management"
features {}
}

Просмотреть файл

@ -0,0 +1,7 @@
# Obtain configuration settings.
module "settings" {
source = "../settings"
root_id = var.root_id
primary_location = var.primary_location
}

Просмотреть файл

@ -2,25 +2,14 @@ terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "2.77.0"
version = "2.96.0"
configuration_aliases = [
azurerm.connectivity,
azurerm.management,
]
}
}
}
provider "azurerm" {
features {}
}
provider "azurerm" {
alias = "connectivity"
features {}
}
provider "azurerm" {
alias = "management"
features {}
backend "local" {
path = "../tfstate/test_framework.tfstate"
}
}

Просмотреть файл

@ -1,16 +1,6 @@
variable "root_id_1" {
variable "root_id" {
type = string
default = "root-1"
}
variable "root_id_2" {
type = string
default = "root-2"
}
variable "root_id_3" {
type = string
default = "root-3"
default = "12345"
}
variable "root_name" {
@ -18,9 +8,14 @@ variable "root_name" {
default = "Test Framework"
}
variable "location" {
variable "primary_location" {
type = string
default = "uksouth"
default = "northeurope"
}
variable "secondary_location" {
type = string
default = "westeurope"
}
variable "create_duration_delay" {

Просмотреть файл

@ -0,0 +1,7 @@
data "azurerm_client_config" "connectivity" {
provider = azurerm.connectivity
}
data "azurerm_client_config" "management" {
provider = azurerm.management
}

Просмотреть файл

@ -0,0 +1,48 @@
module "test_core" {
source = "../../../"
providers = {
azurerm = azurerm.management
azurerm.connectivity = azurerm.connectivity
azurerm.management = azurerm.management
}
# Base module configuration settings
root_parent_id = data.azurerm_client_config.management.tenant_id
root_id = var.root_id
root_name = var.root_name
default_location = var.primary_location
default_tags = module.settings.shared.default_tags
# Tuning delay timers to improve pipeline completion success rate
create_duration_delay = var.create_duration_delay
destroy_duration_delay = var.destroy_duration_delay
# Configuration settings for optional landing zones
deploy_corp_landing_zones = true
deploy_online_landing_zones = true
deploy_sap_landing_zones = true
deploy_demo_landing_zones = true
# Configure path for custom library folder and
# custom template file variables
library_path = "${path.root}/../test_lib"
template_file_variables = module.settings.core.custom_template_file_variables
# Configuration settings for core resources
deploy_core_landing_zones = true
custom_landing_zones = module.settings.core.custom_landing_zones
archetype_config_overrides = module.settings.core.archetype_config_overrides
subscription_id_overrides = module.settings.core.subscription_id_overrides
# Configuration settings for management resources
deploy_management_resources = false
configure_management_resources = module.settings.management.configure_management_resources
subscription_id_management = data.azurerm_client_config.management.subscription_id
# Configuration settings for connectivity resources
deploy_connectivity_resources = false
configure_connectivity_resources = module.settings.connectivity.configure_connectivity_resources
subscription_id_connectivity = data.azurerm_client_config.connectivity.subscription_id
}

Просмотреть файл

@ -0,0 +1,19 @@
# The following output gives the a summary of all resources
# created by the enterprise_scale module, formatted to allow
# easy identification of the resource IDs as stored in the
# Terraform state.
output "resource_ids" {
value = {
for module_name, module_output in {
test_core = module.test_core
} :
module_name => {
for resource_type, resource_instances in module_output :
resource_type => {
for resource_name, resource_configs in resource_instances :
resource_name => keys(resource_configs)
}
}
}
}

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -0,0 +1,13 @@
provider "azurerm" {
features {}
}
provider "azurerm" {
alias = "connectivity"
features {}
}
provider "azurerm" {
alias = "management"
features {}
}

Просмотреть файл

@ -0,0 +1,7 @@
# Obtain configuration settings.
module "settings" {
source = "../settings"
root_id = var.root_id
primary_location = var.primary_location
}

Просмотреть файл

@ -0,0 +1,15 @@
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "2.96.0"
configuration_aliases = [
azurerm.connectivity,
azurerm.management,
]
}
}
backend "local" {
path = "../tfstate/test_framework.tfstate"
}
}

Просмотреть файл

@ -0,0 +1,31 @@
variable "root_id" {
type = string
default = "12345"
}
variable "root_name" {
type = string
default = "Test Framework"
}
variable "primary_location" {
type = string
default = "northeurope"
}
variable "secondary_location" {
type = string
default = "westeurope"
}
variable "create_duration_delay" {
type = map(string)
default = {
azurerm_management_group = "120s"
}
}
variable "destroy_duration_delay" {
type = map(string)
default = {}
}

Просмотреть файл

@ -0,0 +1,7 @@
data "azurerm_client_config" "connectivity" {
provider = azurerm.connectivity
}
data "azurerm_client_config" "management" {
provider = azurerm.management
}

Просмотреть файл

@ -0,0 +1,146 @@
module "test_core" {
source = "../../../"
providers = {
azurerm = azurerm.management
azurerm.connectivity = azurerm.connectivity
azurerm.management = azurerm.management
}
# Base module configuration settings
root_parent_id = data.azurerm_client_config.management.tenant_id
root_id = var.root_id
root_name = var.root_name
default_location = var.primary_location
default_tags = module.settings.shared.default_tags
# Tuning delay timers to improve pipeline completion success rate
create_duration_delay = var.create_duration_delay
destroy_duration_delay = var.destroy_duration_delay
# Configuration settings for optional landing zones
deploy_corp_landing_zones = true
deploy_online_landing_zones = true
deploy_sap_landing_zones = true
deploy_demo_landing_zones = false
# Configure path for custom library folder and
# custom template file variables
library_path = "${path.root}/../test_lib"
template_file_variables = module.settings.core.custom_template_file_variables
# Configuration settings for core resources
deploy_core_landing_zones = true
custom_landing_zones = module.settings.core.custom_landing_zones
archetype_config_overrides = module.settings.core.archetype_config_overrides
subscription_id_overrides = module.settings.core.subscription_id_overrides
# Configuration settings for management resources
deploy_management_resources = false
configure_management_resources = module.settings.management.configure_management_resources
subscription_id_management = data.azurerm_client_config.management.subscription_id
# Configuration settings for connectivity resources
deploy_connectivity_resources = false
configure_connectivity_resources = module.settings.connectivity.configure_connectivity_resources
subscription_id_connectivity = data.azurerm_client_config.connectivity.subscription_id
}
module "test_core_nested" {
source = "../../../"
providers = {
azurerm = azurerm.management
azurerm.connectivity = azurerm.connectivity
azurerm.management = azurerm.management
}
# Base module configuration settings
root_parent_id = "${var.root_id}-landing-zones"
root_id = var.root_id
root_name = var.root_name
default_location = var.primary_location
default_tags = module.settings.shared.default_tags
# Tuning delay timers to improve pipeline completion success rate
create_duration_delay = var.create_duration_delay
destroy_duration_delay = var.destroy_duration_delay
# Configure path for custom library folder and
# custom template file variables
library_path = "${path.root}/../test_lib"
template_file_variables = module.settings.core.custom_template_file_variables
# Configuration settings for core resources
deploy_core_landing_zones = false
custom_landing_zones = module.settings.nested.custom_landing_zones
# Set dependency to ensure correct operation
depends_on = [
module.test_core,
]
}
module "test_management" {
source = "../../../"
providers = {
azurerm = azurerm.management
azurerm.connectivity = azurerm.connectivity
azurerm.management = azurerm.management
}
# Base module configuration settings
root_parent_id = data.azurerm_client_config.management.tenant_id
root_id = var.root_id
root_name = var.root_name
default_location = var.primary_location
default_tags = module.settings.shared.default_tags
# Configure path for custom library folder and
# custom template file variables
library_path = "${path.root}/../test_lib"
template_file_variables = module.settings.core.custom_template_file_variables
# Configuration settings for core resources
deploy_core_landing_zones = false
# Configuration settings for management resources
deploy_management_resources = true
configure_management_resources = module.settings.management.configure_management_resources
subscription_id_management = data.azurerm_client_config.management.subscription_id
}
module "test_connectivity" {
source = "../../../"
providers = {
azurerm = azurerm.management
azurerm.connectivity = azurerm.connectivity
azurerm.management = azurerm.management
}
# Base module configuration settings
root_parent_id = data.azurerm_client_config.management.tenant_id
root_id = var.root_id
root_name = var.root_name
default_location = var.primary_location
default_tags = module.settings.shared.default_tags
# Configure path for custom library folder and
# custom template file variables
library_path = "${path.root}/../test_lib"
template_file_variables = module.settings.core.custom_template_file_variables
# Configuration settings for core resources
deploy_core_landing_zones = false
# Configuration settings for connectivity resources
deploy_connectivity_resources = true
configure_connectivity_resources = module.settings.connectivity.configure_connectivity_resources
subscription_id_connectivity = data.azurerm_client_config.connectivity.subscription_id
}

Просмотреть файл

@ -0,0 +1,22 @@
# The following output gives the a summary of all resources
# created by the enterprise_scale module, formatted to allow
# easy identification of the resource IDs as stored in the
# Terraform state.
output "resource_ids" {
value = {
for module_name, module_output in {
test_core = module.test_core
test_core_nested = module.test_core_nested
test_management = module.test_management
test_connectivity = module.test_connectivity
} :
module_name => {
for resource_type, resource_instances in module_output :
resource_type => {
for resource_name, resource_configs in resource_instances :
resource_name => keys(resource_configs)
}
}
}
}

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -0,0 +1,13 @@
provider "azurerm" {
features {}
}
provider "azurerm" {
alias = "connectivity"
features {}
}
provider "azurerm" {
alias = "management"
features {}
}

Просмотреть файл

@ -0,0 +1,7 @@
# Obtain configuration settings.
module "settings" {
source = "../settings"
root_id = var.root_id
primary_location = var.primary_location
}

Просмотреть файл

@ -0,0 +1,15 @@
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "2.96.0"
configuration_aliases = [
azurerm.connectivity,
azurerm.management,
]
}
}
backend "local" {
path = "../tfstate/test_framework.tfstate"
}
}

Просмотреть файл

@ -0,0 +1,31 @@
variable "root_id" {
type = string
default = "12345"
}
variable "root_name" {
type = string
default = "Test Framework"
}
variable "primary_location" {
type = string
default = "northeurope"
}
variable "secondary_location" {
type = string
default = "westeurope"
}
variable "create_duration_delay" {
type = map(string)
default = {
azurerm_management_group = "120s"
}
}
variable "destroy_duration_delay" {
type = map(string)
default = {}
}

Просмотреть файл

@ -11,10 +11,10 @@ violation[management_group_display_name] {
management_group_display_name := sprintf("The management_group_display_name planned values:\n \n %v \n \n are not equal to the management_group_display_name changed values:\n \n %v", [mgs_plan_display_name, mgs_change_display_name])
}
# # # Compare the management_group_name and fail if they are not equal.
violation[management_group_name] {
# # # Compare the management_group_id and fail if they are not equal.
violation[management_group_id] {
mgs_plan_name != mgs_change_name
management_group_name := sprintf("The management_group_name planned values:\n \n %v \n \n are not equal to the management_group_name changed values:\n \n %v", [mgs_plan_name, mgs_change_name])
management_group_id := sprintf("The management_group_id planned values:\n \n %v \n \n are not equal to the management_group_id changed values:\n \n %v", [mgs_plan_name, mgs_change_name])
}
########################

Просмотреть файл

@ -14,10 +14,10 @@ import data.child_modules
# policy_definition_name := sprintf("The policy_definition_name planned values:\n \n %v \n \n are not equal to the policy_definition_name changed values:\n \n %v", [plc_def_plan_name, plc_def_change_name])
# }
# # # # Compare the policy_definition_management_group_name and fail if they are not equal.
# violation[policy_definition_management_group_name] {
# plc_def_plan_management_group_name != plc_def_change_management_group_name
# policy_definition_management_group_name := sprintf("The policy_definition_management_group_name planned values:\n \n %v \n \n are not equal to the policy_definition_management_group_name changed values:\n \n %v", [plc_def_plan_management_group_name, plc_def_change_management_group_name])
# # # # Compare the policy_definition_management_group_id and fail if they are not equal.
# violation[policy_definition_management_group_id] {
# plc_def_plan_management_group_id != plc_def_change_management_group_id
# policy_definition_management_group_id := sprintf("The policy_definition_management_group_id planned values:\n \n %v \n \n are not equal to the policy_definition_management_group_id changed values:\n \n %v", [plc_def_plan_management_group_id, plc_def_change_management_group_id])
# }
# # # # Compare the policy_definition_metadata and fail if they are not equal.
@ -62,24 +62,24 @@ plc_def_change_name[module_name] = pl_defs {
]
}
# # # Get the management_group_name from all policy definitions in planned_values.yml
plc_def_plan_management_group_name[module_name] = pl_defs {
# # # Get the management_group_id from all policy definitions in planned_values.yml
plc_def_plan_management_group_id[module_name] = pl_defs {
module := child_modules[_]
module_name := module.address
pl_defs := [pl_def |
module.resources[i].type == "azurerm_policy_definition"
pl_def := module.resources[i].values.management_group_name
pl_def := module.resources[i].values.management_group_id
]
}
# # # Get the management_group_name from all policy definitions in the opa.json
plc_def_change_management_group_name[module_name] = pl_defs {
# # # Get the management_group_id from all policy definitions in the opa.json
plc_def_change_management_group_id[module_name] = pl_defs {
module := input.resource_changes[_]
module_name := module.module_address
pl_defs := [pl_def |
input.resource_changes[r].type == "azurerm_policy_definition"
input.resource_changes[r].module_address == module.module_address
pl_def := input.resource_changes[r].change.after.management_group_name
pl_def := input.resource_changes[r].change.after.management_group_id
]
}

Просмотреть файл

@ -6,10 +6,10 @@ import data.child_modules
# Rules
########################
# # # Compare the policy_set_definition_management_group_name and fail if they are not equal.
violation[policy_set_definition_management_group_name] {
plc_set_def_plan_management_group_name != plc_set_def_change_management_group_name
policy_set_definition_management_group_name := sprintf("The policy_set_definition_management_group_name planned values:\n \n %v \n \n are not equal to the policy_set_definition_management_group_name changed values:\n \n %v", [plc_set_def_plan_management_group_name, plc_set_def_change_management_group_name])
# # # Compare the policy_set_definition_management_group_id and fail if they are not equal.
violation[policy_set_definition_management_group_id] {
plc_set_def_plan_management_group_id != plc_set_def_change_management_group_id
policy_set_definition_management_group_id := sprintf("The policy_set_definition_management_group_id planned values:\n \n %v \n \n are not equal to the policy_set_definition_management_group_id changed values:\n \n %v", [plc_set_def_plan_management_group_id, plc_set_def_change_management_group_id])
}
# # # Compare the policy_set_definition_metadata and fail if they are not equal.
@ -40,24 +40,24 @@ violation[policy_set_definition_reference] {
# Library
########################
# # # Get the management_group_name from all policy set definitions in planned_values.yml
plc_set_def_plan_management_group_name[module_name] = plcs {
# # # Get the management_group_id from all policy set definitions in planned_values.yml
plc_set_def_plan_management_group_id[module_name] = plcs {
module := child_modules[_]
module_name := module.address
plcs := [plc |
module.resources[i].type == "azurerm_policy_set_definition"
plc := module.resources[i].values.management_group_name
plc := module.resources[i].values.management_group_id
]
}
# # # Get the management_group_name from all policy set definitions in the opa.json
plc_set_def_change_management_group_name[module_name] = plcs {
# # # Get the management_group_id from all policy set definitions in the opa.json
plc_set_def_change_management_group_id[module_name] = plcs {
module := input.resource_changes[_]
module_name := module.module_address
plcs := [plc |
input.resource_changes[r].type == "azurerm_policy_set_definition"
input.resource_changes[r].module_address == module.module_address
plc := input.resource_changes[r].change.after.management_group_name
plc := input.resource_changes[r].change.after.management_group_id
]
}

Просмотреть файл

@ -1,5 +1,5 @@
---
name: 'SPN generator'
name: "SPN generator"
trigger: none
@ -7,16 +7,16 @@ pool:
vmImage: ubuntu-20.04
variables:
- group: csu-tf-environment
- group: csu-tf-environment
jobs:
- job: run_spn_generator
displayName: 'Run SPN generator'
steps:
- task: Bash@3
displayName: 'Create or update SPN settings'
inputs:
targetType: 'inline'
script: 'make azp-spn-generator'
env:
ARM_CLIENT_SECRET: $(ARM_CLIENT_SECRET)
- job: run_spn_generator
displayName: "Run SPN Generator"
steps:
- task: Bash@3
displayName: "Create or update SPN settings"
inputs:
targetType: "inline"
script: "make azp-spn-generator"
env:
ARM_CLIENT_SECRET: $(ARM_CLIENT_SECRET)

Просмотреть файл

@ -0,0 +1,10 @@
---
steps:
- task: Bash@3
name: prepare_backend
displayName: "Prepare Backend Storage"
inputs:
targetType: "inline"
script: "make azp-backend"
env:
ARM_CLIENT_SECRET: $(ARM_CLIENT_SECRET)

Просмотреть файл

@ -1,27 +1,15 @@
---
steps:
- task: Bash@3
displayName: 'Terraform (install)'
inputs:
targetType: 'inline'
script: 'make tf-install'
- task: Bash@3
displayName: "Install Terraform Pre-requisites"
inputs:
targetType: "inline"
script: "make tf-install"
- task: Bash@3
displayName: 'Terraform (prepare)'
inputs:
targetType: 'inline'
script: 'make tf-prepare'
env:
ARM_CLIENT_SECRET: $(ARM_CLIENT_SECRET)
- task: Bash@3
displayName: 'Terraform (fmt)'
inputs:
targetType: 'inline'
script: 'make tf-fmt'
- task: Bash@3
displayName: 'Terraform (init)'
inputs:
targetType: 'inline'
script: 'make tf-init'
- task: Bash@3
displayName: "Prepare Terraform Environment"
inputs:
targetType: "inline"
script: "make tf-prepare"
env:
ARM_CLIENT_SECRET: $(ARM_CLIENT_SECRET)

Просмотреть файл

@ -0,0 +1,56 @@
---
parameters:
- name: module_path
type: string
- name: run_type
type: string
steps:
- task: Bash@3
displayName: "[terraform init]"
inputs:
targetType: "inline"
script: "make tf-init"
env:
ARM_CLIENT_SECRET: $(ARM_CLIENT_SECRET)
TEST_MODULE_PATH: "${{ parameters.module_path }}"
condition: and(succeeded(), in('${{ parameters.run_type }}', 'unit', 'e2e', 'destroy'))
- task: Bash@3
displayName: "[terraform plan]"
inputs:
targetType: "inline"
script: "make tf-plan"
env:
ARM_CLIENT_SECRET: $(ARM_CLIENT_SECRET)
TEST_MODULE_PATH: "${{ parameters.module_path }}"
condition: and(succeeded(), in('${{ parameters.run_type }}', 'unit', 'e2e'))
- task: Bash@3
displayName: "[conftest run]"
inputs:
targetType: "inline"
script: "make opa-run-tests"
env:
TEST_MODULE_PATH: "${{ parameters.module_path }}"
condition: and(succeeded(), eq('${{ parameters.run_type }}', 'unit'))
- task: Bash@3
displayName: "[terraform apply]"
inputs:
targetType: "inline"
script: "make tf-apply"
env:
ARM_CLIENT_SECRET: $(ARM_CLIENT_SECRET)
TEST_MODULE_PATH: "${{ parameters.module_path }}"
condition: and(succeeded(), eq('${{ parameters.run_type }}', 'e2e'))
- task: Bash@3
displayName: "[terraform destroy]"
inputs:
targetType: "inline"
script: "make tf-destroy"
env:
ARM_CLIENT_SECRET: $(ARM_CLIENT_SECRET)
TEST_MODULE_PATH: "${{ parameters.module_path }}"
condition: and(succeeded(), eq('${{ parameters.run_type }}', 'destroy'))

Просмотреть файл

@ -1,11 +1,11 @@
---
steps:
- task: PowerShell@2
name: build_strategy
displayName: "Generate Build Strategy"
inputs:
targetType: 'inline'
script: 'make azp-strategy'
env:
ARM_CLIENT_SECRET: $(ARM_CLIENT_SECRET)
BILLING_SCOPE: $(BILLING_SCOPE)
- task: PowerShell@2
name: build_strategy
displayName: "Generate Build Strategy"
inputs:
targetType: "inline"
script: "make azp-strategy"
env:
ARM_CLIENT_SECRET: $(ARM_CLIENT_SECRET)
BILLING_SCOPE: $(BILLING_SCOPE)

Просмотреть файл

@ -1,5 +1,5 @@
---
name: 'Tests (E2E)'
name: "Tests (E2E)"
trigger: none
@ -7,43 +7,109 @@ pool:
vmImage: ubuntu-20.04
variables:
- group: csu-tf-environment
- group: csu-tf-environment
jobs:
- job: matrix_generator
displayName: 'Matrix Generator'
steps:
- template: templates/tests-strategy.yml
- job: matrix_generator
displayName: "Matrix Generator"
steps:
- template: templates/tests-strategy.yml
- job: run_e2e_tests
displayName: 'E2E Tests'
dependsOn: matrix_generator
strategy:
matrix: $[ dependencies.matrix_generator.outputs['build_strategy.matrix_json'] ]
steps:
- template: templates/tests-common.yml
- job: backend_generator
displayName: "Backend Storage Generator"
steps:
- template: templates/tests-backend.yml
- task: Bash@3
displayName: 'Terraform (plan)'
inputs:
targetType: 'inline'
script: 'make tf-plan'
env:
ARM_CLIENT_SECRET: $(ARM_CLIENT_SECRET)
- job: run_e2e_tests_001
displayName: "E2E Tests 001"
dependsOn:
- matrix_generator
- backend_generator
strategy:
matrix: $[ dependencies.matrix_generator.outputs['build_strategy.matrix_json'] ]
variables:
STORAGE_ACCOUNT_RSG_NAME: $[ dependencies.backend_generator.outputs['prepare_backend.STORAGE_ACCOUNT_RSG_NAME'] ]
STORAGE_ACCOUNT_NAME: $[ dependencies.backend_generator.outputs['prepare_backend.STORAGE_ACCOUNT_NAME'] ]
STORAGE_CONTAINER_NAME: $[ dependencies.backend_generator.outputs['prepare_backend.STORAGE_CONTAINER_NAME'] ]
timeoutInMinutes: 30
steps:
- template: templates/tests-common.yml
- task: Bash@3
displayName: 'Terraform (apply)'
inputs:
targetType: 'inline'
script: 'make tf-apply'
env:
ARM_CLIENT_SECRET: $(ARM_CLIENT_SECRET)
- template: templates/tests-loop.yml
parameters:
module_path: "tests/modules/test_001_baseline"
run_type: e2e
- task: Bash@3
displayName: 'Terraform (destroy)'
inputs:
targetType: 'inline'
script: 'make tf-destroy'
env:
ARM_CLIENT_SECRET: $(ARM_CLIENT_SECRET)
condition: always()
- job: run_e2e_tests_002
displayName: "E2E Tests 002"
dependsOn:
- matrix_generator
- backend_generator
- run_e2e_tests_001
strategy:
matrix: $[ dependencies.matrix_generator.outputs['build_strategy.matrix_json'] ]
variables:
STORAGE_ACCOUNT_RSG_NAME: $[ dependencies.backend_generator.outputs['prepare_backend.STORAGE_ACCOUNT_RSG_NAME'] ]
STORAGE_ACCOUNT_NAME: $[ dependencies.backend_generator.outputs['prepare_backend.STORAGE_ACCOUNT_NAME'] ]
STORAGE_CONTAINER_NAME: $[ dependencies.backend_generator.outputs['prepare_backend.STORAGE_CONTAINER_NAME'] ]
timeoutInMinutes: 30
steps:
- template: templates/tests-common.yml
- template: templates/tests-loop.yml
parameters:
module_path: "tests/modules/test_002_add_custom_core"
run_type: e2e
- job: run_e2e_tests_003
displayName: "E2E Tests 003"
dependsOn:
- matrix_generator
- backend_generator
- run_e2e_tests_002
strategy:
matrix: $[ dependencies.matrix_generator.outputs['build_strategy.matrix_json'] ]
variables:
STORAGE_ACCOUNT_RSG_NAME: $[ dependencies.backend_generator.outputs['prepare_backend.STORAGE_ACCOUNT_RSG_NAME'] ]
STORAGE_ACCOUNT_NAME: $[ dependencies.backend_generator.outputs['prepare_backend.STORAGE_ACCOUNT_NAME'] ]
STORAGE_CONTAINER_NAME: $[ dependencies.backend_generator.outputs['prepare_backend.STORAGE_CONTAINER_NAME'] ]
timeoutInMinutes: 60
steps:
- template: templates/tests-common.yml
- template: templates/tests-loop.yml
parameters:
module_path: "tests/modules/test_003_add_mgmt_conn"
run_type: e2e
- job: run_e2e_clean_up
displayName: "E2E Clean-up"
dependsOn:
- matrix_generator
- backend_generator
- run_e2e_tests_003
strategy:
matrix: $[ dependencies.matrix_generator.outputs['build_strategy.matrix_json'] ]
variables:
STORAGE_ACCOUNT_RSG_NAME: $[ dependencies.backend_generator.outputs['prepare_backend.STORAGE_ACCOUNT_RSG_NAME'] ]
STORAGE_ACCOUNT_NAME: $[ dependencies.backend_generator.outputs['prepare_backend.STORAGE_ACCOUNT_NAME'] ]
STORAGE_CONTAINER_NAME: $[ dependencies.backend_generator.outputs['prepare_backend.STORAGE_CONTAINER_NAME'] ]
timeoutInMinutes: 60
cancelTimeoutInMinutes: 60
condition: |
or
(
and
(
or(failed(), canceled()),
ne(variables.ALWAYS_DESTROY, 'false')
),
succeeded()
)
steps:
- template: templates/tests-common.yml
- template: templates/tests-loop.yml
parameters:
module_path: "tests/modules/test_001_baseline"
run_type: destroy

Просмотреть файл

@ -15,30 +15,48 @@ jobs:
steps:
- template: templates/tests-strategy.yml
- job: backend_generator
displayName: "Backend Storage Generator"
steps:
- template: templates/tests-backend.yml
- job: run_unit_tests
displayName: "Unit Tests"
dependsOn: matrix_generator
dependsOn:
- matrix_generator
- backend_generator
strategy:
matrix: $[ dependencies.matrix_generator.outputs['build_strategy.matrix_json'] ]
variables:
STORAGE_ACCOUNT_RSG_NAME: $[ dependencies.backend_generator.outputs['prepare_backend.STORAGE_ACCOUNT_RSG_NAME'] ]
STORAGE_ACCOUNT_NAME: $[ dependencies.backend_generator.outputs['prepare_backend.STORAGE_ACCOUNT_NAME'] ]
STORAGE_CONTAINER_NAME: $[ dependencies.backend_generator.outputs['prepare_backend.STORAGE_CONTAINER_NAME'] ]
steps:
- template: templates/tests-common.yml
- task: Bash@3
displayName: "Terraform (plan)"
displayName: "[terraform fmt]"
inputs:
targetType: "inline"
script: "make tf-plan"
env:
ARM_CLIENT_SECRET: $(ARM_CLIENT_SECRET)
script: "make tf-fmt"
- task: Bash@3
displayName: "Opa Conftest (install)"
displayName: "[conftest install]"
inputs:
targetType: "inline"
script: "make opa-install"
- task: Bash@3
displayName: "Conftest (run tests)"
inputs:
targetType: "inline"
script: "make opa-run-tests"
- template: templates/tests-loop.yml
parameters:
module_path: "tests/modules/test_001_baseline"
run_type: unit
- template: templates/tests-loop.yml
parameters:
module_path: "tests/modules/test_002_add_custom_core"
run_type: unit
- template: templates/tests-loop.yml
parameters:
module_path: "tests/modules/test_003_add_mgmt_conn"
run_type: unit

73
tests/scripts/azp-backend.sh Executable file
Просмотреть файл

@ -0,0 +1,73 @@
#!/usr/bin/bash
set -e
#
# Shell Script
# - Terraform Create or Update Azure Backend Storage
#
echo "==> Authenticating cli..."
az login \
--service-principal \
--tenant "$ARM_TENANT_ID" \
--username "$ARM_CLIENT_ID" \
--password "$ARM_CLIENT_SECRET" \
--query [?isDefault]
echo "==> Setting active Subscription..."
az account set \
--subscription "$ARM_SUBSCRIPTION_ID"
az account list \
--query "[?isDefault]"
echo "==> Create or update Resource Group..."
RSG_NAME="$DEFAULT_PREFIX"
az group create \
--name "$RSG_NAME" \
--location "$PRIMARY_LOCATION" \
--query 'properties.provisioningState' \
--out tsv
# Set STORAGE_ACCOUNT_RSG_NAME to an output variable for downstream consumption.
echo "##vso[task.setVariable variable=STORAGE_ACCOUNT_RSG_NAME;isOutput=true]$RSG_NAME"
echo "==> Create or update Storage Account..."
# Storage account name must be lowercase alphanumeric
SA_NAME=$(
echo "$DEFAULT_PREFIX$PRIMARY_LOCATION" |
tr '[:upper:]' '[:lower:]' |
tr -cd '[:alnum:]'
)
SA_ID=$(
az storage account create \
--name "$SA_NAME" \
--resource-group "$RSG_NAME" \
--location "$PRIMARY_LOCATION" \
--kind 'StorageV2' \
--access-tier 'Hot' \
--sku 'Standard_LRS' \
--min-tls-version 'TLS1_2' \
--query 'id' \
--out tsv
)
# Set STORAGE_ACCOUNT_NAME to an output variable for downstream consumption.
echo "##vso[task.setVariable variable=STORAGE_ACCOUNT_NAME;isOutput=true]$SA_NAME"
echo "==> Create or update Storage Account permissions..."
az role assignment create \
--role 'Storage Blob Data Contributor' \
--assignee "$ARM_CLIENT_ID" \
--scope "$SA_ID"
echo "==> Create or update Storage Account container..."
SC_NAME="tfstate"
az storage container create \
--name "$SC_NAME" \
--auth-mode 'login' \
--account-name "$SA_NAME" \
--query 'created' \
--out tsv
# Set STORAGE_CONTAINER_NAME to an output variable for downstream consumption.
echo "##vso[task.setVariable variable=STORAGE_CONTAINER_NAME;isOutput=true]$SC_NAME"

Просмотреть файл

@ -24,7 +24,7 @@ echo "==> Create or update Resource Group..."
RSG_NAME="$DEFAULT_PREFIX"
az group create \
--name "$RSG_NAME" \
--location "$DEFAULT_LOCATION" \
--location "$PRIMARY_LOCATION" \
--query 'properties.provisioningState' \
--out tsv
@ -44,7 +44,7 @@ if [ -z "$KV_EXISTS" ]; then
az keyvault create \
--resource-group "$RSG_NAME" \
--name "$KEY_VAULT_NAME" \
--location "$DEFAULT_LOCATION" \
--location "$PRIMARY_LOCATION" \
--query 'properties.provisioningState' \
--out tsv
else

Просмотреть файл

@ -23,6 +23,15 @@ $jsonDepth = 4
$terraformUrl = "https://api.github.com/repos/hashicorp/terraform/tags"
$azurermProviderUrl = "https://registry.terraform.io/v1/providers/hashicorp/azurerm"
function Get-RandomId {
[CmdletBinding()]
[OutputType([String])]
param (
[Int]$Length = 8
)
return -join ((48..57) + (97..122) | Get-Random -Count $Length | ForEach-Object { [char]$_ })
}
########################################
# Terraform Versions
# - Base Version: "0.15.0"
@ -44,11 +53,11 @@ $terraformVersionsCount = $terraformVersions.Count
#######################################
# Terraform AzureRM Provider Versions
# - Base Version: (2.77.0)
# - Base Version: (2.96.0)
# - Latest Versions: (latest 1)
#######################################
$azurermProviderVersionBase = "2.77.0"
$azurermProviderVersionBase = "2.96.0"
$azurermProviderVersionLatest = (Invoke-RestMethod -Method Get -Uri $azurermProviderUrl).version
#######################################
@ -149,6 +158,7 @@ for ($i = 0; $i -lt $terraformVersionsCount; $i++) {
$matrixObject | Add-Member `
-NotePropertyName $jobName1 `
-NotePropertyValue @{
TF_ROOT_ID = Get-RandomId
TF_VERSION = $terraformVersion
TF_AZ_VERSION = $azurermProviderVersionBase
TF_JOB_ID = $jobId1
@ -159,6 +169,7 @@ for ($i = 0; $i -lt $terraformVersionsCount; $i++) {
$matrixObject | Add-Member `
-NotePropertyName $jobName2 `
-NotePropertyValue @{
TF_ROOT_ID = Get-RandomId
TF_VERSION = $terraformVersion
TF_AZ_VERSION = $azurermProviderVersionLatest
TF_JOB_ID = $jobId2

Просмотреть файл

Просмотреть файл

@ -0,0 +1,65 @@
#!/usr/bin/pwsh
#
# PowerShell Script
# - Conftest Install
#
# Install Scoop
if (Get-command -name scoop -ErrorAction SilentlyContinue) {
Write-Output "==> Scoop exists, skip install"
scoop --version
scoop update
}
else {
Write-Output "`n"
Write-Output "==> To run Conftest tests on Windows, some utilities need to be installed with Scoop"
Write-Output "==> To install Scoop on Windows, run this command from a new terminal:"
Write-Output "`n"
Write-Output "Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https:\\get.scoop.sh')"
Write-Output "`n"
Write-Output "==> After installing Scoop, run: ./opa-values-generator.ps1"
Write-Output "`n"
exit
}
# Install Terraform
if (Get-command -name terraform -ErrorAction SilentlyContinue) {
Write-Output "==> Terraform exists, skip install"
terraform version
}
else {
Write-Output "==> Install Terraform on Windows..."
scoop install terraform
}
# Install jq
if (Get-command -name jq -ErrorAction SilentlyContinue) {
Write-Output "==> jq exists, skip install"
jq --version
}
else {
Write-Output "==> Install jq on Windows..."
scoop install jq
}
# Install yq
if (Get-command -name yq -ErrorAction SilentlyContinue) {
Write-Output "==> yq exists, skip install"
yq --version
}
else {
Write-Output "==> Install yq on Windows..."
scoop install yq
}
# Install Conftest
if (Get-command -name conftest -ErrorAction SilentlyContinue) {
Write-Output "==> conftest exists, skip install"
conftest --version
}
else {
Write-Output "==> Install conftest on Windows..."
scoop bucket add instrumenta https://github.com/instrumenta/scoop-instrumenta
scoop install conftest
}

Просмотреть файл

@ -6,7 +6,8 @@ set -e
# - OPA Run Tests
#
# # Parameters
TF_PLAN_JSON="terraform-plan-$TF_VERSION-$TF_AZ_VERSION"
TF_WORKSPACE="$PIPELINE_WORKSPACE/s/$TEST_MODULE_PATH"
TF_PLAN_OUT="$TF_WORKSPACE/terraform-plan-$TF_VERSION-$TF_AZ_VERSION"
# # # Store data temporarily
TEMP_FILE_01=$(mktemp).json
@ -14,43 +15,42 @@ TEMP_FILE_02=$(mktemp).json
# # # Update the planned_values.json with the latest parameters
echo "==> Update planned values..."
cd "$PIPELINE_WORKSPACE/s/tests/deployment"
jq '(.. | strings) |= gsub("root-id-1"; "'"$TF_ROOT_ID_1"'")' planned_values.json >"$TEMP_FILE_01"
jq '(.. | strings) |= gsub("root-id-2"; "'"$TF_ROOT_ID_2"'")' "$TEMP_FILE_01" >"$TEMP_FILE_02"
jq '(.. | strings) |= gsub("root-id-3"; "'"$TF_ROOT_ID_3"'")' "$TEMP_FILE_02" >"$TEMP_FILE_01"
cd "$TF_WORKSPACE"
jq '(.. | strings) |= gsub("root-id-1"; "'"$TF_ROOT_ID"'")' planned_values.json >"$TEMP_FILE_01"
jq '(.. | strings) |= gsub("root-name"; "ES-'"$TF_VERSION"'-'"$TF_AZ_VERSION"'")' "$TEMP_FILE_01" >"$TEMP_FILE_02"
jq '(.. | strings) |= gsub("eastus"; "eastus")' "$TEMP_FILE_02" >"$TF_PLAN_JSON"_updated_planned_values.json
jq '(.. | strings) |= gsub("northeurope"; "northeurope")' "$TEMP_FILE_02" >"$TEMP_FILE_01"
jq '(.. | strings) |= gsub("westeurope"; "westeurope")' "$TEMP_FILE_01" >"$TF_PLAN_OUT"_updated_planned_values.json
echo "==> Module Location - $DEFAULT_LOCATION"
echo "==> Azure {TF_ROOT_ID_1} - ${TF_ROOT_ID_1}"
echo "==> Azure TF_ROOT_ID_1 - $TF_ROOT_ID_1"
echo "==> Module Locations - $PRIMARY_LOCATION ($SECONDARY_LOCATION)"
echo "==> Azure {TF_ROOT_ID} - ${TF_ROOT_ID}"
echo "==> Azure TF_ROOT_ID - $TF_ROOT_ID"
wait
echo "==> Converting to yaml..."
yq <"$TF_PLAN_JSON"_updated_planned_values.json e -P - >../opa/policy/"$TF_PLAN_JSON"_updated_planned_values.yml
yq <"$TF_PLAN_OUT"_updated_planned_values.json e -P - >"$TF_PLAN_OUT"_updated_planned_values.yml
wait
echo "==> Check yaml for errors..."
yamllint -d relaxed ../opa/policy/"$TF_PLAN_JSON"_updated_planned_values.yml
yamllint -d relaxed "$TF_PLAN_OUT"_updated_planned_values.yml
echo "==> Running conftest..."
echo
echo "==> Testing management_groups..."
conftest test "$TF_PLAN_JSON".json -p ../opa/policy/management_groups.rego -d ../opa/policy/"$TF_PLAN_JSON"_updated_planned_values.yml
conftest test "$TF_PLAN_OUT".json -p ../../opa/policy/management_groups.rego -d "$TF_PLAN_OUT"_updated_planned_values.yml
echo
echo "==> Testing role_definitions..."
conftest test "$TF_PLAN_JSON".json -p ../opa/policy/role_definitions.rego -d ../opa/policy/"$TF_PLAN_JSON"_updated_planned_values.yml
conftest test "$TF_PLAN_OUT".json -p ../../opa/policy/role_definitions.rego -d "$TF_PLAN_OUT"_updated_planned_values.yml
echo
echo "==> Testing role_assignments..."
conftest test "$TF_PLAN_JSON".json -p ../opa/policy/role_assignments.rego -d ../opa/policy/"$TF_PLAN_JSON"_updated_planned_values.yml
conftest test "$TF_PLAN_OUT".json -p ../../opa/policy/role_assignments.rego -d "$TF_PLAN_OUT"_updated_planned_values.yml
echo
echo "==> Testing policy_set_definitions..."
conftest test "$TF_PLAN_JSON".json -p ../opa/policy/policy_set_definitions.rego -d ../opa/policy/"$TF_PLAN_JSON"_updated_planned_values.yml
conftest test "$TF_PLAN_OUT".json -p ../../opa/policy/policy_set_definitions.rego -d "$TF_PLAN_OUT"_updated_planned_values.yml
echo
echo "==> Testing policy_definitions..."
conftest test "$TF_PLAN_JSON".json -p ../opa/policy/policy_definitions.rego -d ../opa/policy/"$TF_PLAN_JSON"_updated_planned_values.yml
conftest test "$TF_PLAN_OUT".json -p ../../opa/policy/policy_definitions.rego -d "$TF_PLAN_OUT"_updated_planned_values.yml
echo
echo "==> Testing policy_assignments..."
conftest test "$TF_PLAN_JSON".json -p ../opa/policy/policy_assignments.rego -d ../opa/policy/"$TF_PLAN_JSON"_updated_planned_values.yml
conftest test "$TF_PLAN_OUT".json -p ../../opa/policy/policy_assignments.rego -d "$TF_PLAN_OUT"_updated_planned_values.yml

Просмотреть файл

@ -1,3 +1,5 @@
#!/usr/bin/pwsh
###############################################
# Run tests and generate testing values.
###############################################
@ -6,9 +8,7 @@
# The script will install all the necessary components locally and run the tests.
# After completing the tests, follow the script prompt for the next steps.
# # Parameters
$PLAN_NAME = "terraform-plan"
$CONFIRM = "y"
# # #? Run a local test against a different module configuration:
@ -16,142 +16,114 @@ $CONFIRM = "y"
# # #* Copy paste the variables.tf file from deployment folder and adjust your main.tf
###############################################
# # #* Path of the tested _es terraform module
$MODULE_PATH = "../deployment"
$BASE_PATH = $(Get-Location).Path
$MODULE_PATHS = @(
"$($BASE_PATH)/../modules/test_001_baseline"
"$($BASE_PATH)/../modules/test_002_add_custom_core"
"$($BASE_PATH)/../modules/test_003_add_mgmt_conn"
)
###############################################
$PWSH_OS = $PSVersionTable.OS
$PWSH_PLATFORM = $PSVersionTable.Platform
# Install Scoop
if (Get-command -name scoop -ErrorAction SilentlyContinue) {
Write-Output "==> Scoop exists, skip install"
scoop --version
scoop update
Write-Output "################################################"
Write-Output "==> Initiate installation of pre-requisites..."
Write-Output "==> OS : $PWSH_OS"
Write-Output "==> Platform : $PWSH_PLATFORM"
Write-Output "`n"
if (($PWSH_OS -like "*Windows*") -and ($PWSH_PLATFORM -eq "Win32NT")) {
./opa-install-windows.ps1
}
else {
Write-Output "`n"
Write-Output "==> To run Conftest tests on Windows, some utilities need to be installed with Scoop"
Write-Output "==> To install Scoop on Windows, run this command from a new terminal:"
Write-Output "`n"
Write-Output "Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https:\\get.scoop.sh')"
Write-Output "`n"
Write-Output "==> After installing Scoop, run: .\opa-values-generator.ps1"
Write-Output "`n"
exit
elseif (($PWSH_OS -like "Darwin*") -and ($PWSH_PLATFORM -eq "Unix")) {
Write-Output "Support for MacOS still in development. Please ensure pre-requisites are manually installed and re-run this script if errors occur due to missing software."
}
elseif (($PWSH_OS -like "Linux*") -and ($PWSH_PLATFORM -eq "Unix")) {
source opa-install-linux.sh
}
# Install Terraform
if (Get-command -name terraform -ErrorAction SilentlyContinue) {
Write-Output "==> Terraform exists, skip install"
terraform version
Write-Output "`n"
Write-Output "==> Completed installation of pre-requisites."
Write-Output "################################################"
Write-Output "`n"
foreach ($MODULE_PATH in $MODULE_PATHS) {
if (-not ($MODULE_PATH | Test-Path)) { Throw "The directory does not exist, check entries in MODULE_PATHS variable on .\opa-values-generator.ps1 :line 18" }
$TF_PLAN_OUT = "$MODULE_PATH/terraform_plan"
$PLANNED_VALUES = "$MODULE_PATH/planned_values"
$MODULE_NAME = Split-Path $MODULE_PATH -Leaf
Write-Output "==> ($MODULE_NAME) - Change to the module root directory..."
Set-Location $MODULE_PATH
Write-Output "==> ($MODULE_NAME) - Initializing infrastructure..."
terraform init -upgrade
Write-Output "==> ($MODULE_NAME) - Planning infrastructure..."
terraform plan `
-var="root_id=root-id-1" `
-var="root_name=root-name" `
-var="primary_location=northeurope" `
-var="secondary_location=westeurope" `
-out="$TF_PLAN_OUT"
Write-Output "==> ($MODULE_NAME) - Converting plan to *.json..."
terraform show -json "$TF_PLAN_OUT" | Out-File -FilePath "$TF_PLAN_OUT.json"
Write-Output "==> ($MODULE_NAME) - Removing the original plan..."
Remove-Item -Path "$TF_PLAN_OUT"
Write-Output "==> ($MODULE_NAME) - Saving planned values to a temporary planned_values.json..."
Get-Content -Path "$TF_PLAN_OUT.json" | jq '.planned_values.root_module' | Out-File -FilePath "$PLANNED_VALUES.json"
Write-Output "==> ($MODULE_NAME) - Converting to yaml..."
Get-Content -Path "$PLANNED_VALUES.json" | yq e -P - | Tee-Object "$PLANNED_VALUES.yml"
# # # Run OPA Tests
Set-Location $MODULE_PATH
Write-Output "==> ($MODULE_NAME) - Running conftest..."
Write-Output "==> ($MODULE_NAME) - Testing management_groups..."
conftest test "$TF_PLAN_OUT.json" -p ../../opa/policy/management_groups.rego -d "$PLANNED_VALUES.yml"
Write-Output "==> ($MODULE_NAME) - Testing role_definitions..."
conftest test "$TF_PLAN_OUT.json" -p ../../opa/policy/role_definitions.rego -d "$PLANNED_VALUES.yml"
Write-Output "==> ($MODULE_NAME) - Testing role_assignments..."
conftest test "$TF_PLAN_OUT.json" -p ../../opa/policy/role_assignments.rego -d "$PLANNED_VALUES.yml"
Write-Output "==> ($MODULE_NAME) - Testing policy_set_definitions..."
conftest test "$TF_PLAN_OUT.json" -p ../../opa/policy/policy_set_definitions.rego -d "$PLANNED_VALUES.yml"
Write-Output "==> ($MODULE_NAME) - Testing policy_definitions..."
conftest test "$TF_PLAN_OUT.json" -p ../../opa/policy/policy_definitions.rego -d "$PLANNED_VALUES.yml"
Write-Output "==> ($MODULE_NAME) - Testing policy_assignments..."
conftest test "$TF_PLAN_OUT.json" -p ../../opa/policy/policy_assignments.rego -d "$PLANNED_VALUES.yml"
# # # Remove comments and $CONFIRM parameter for CMD prompt.
# # # $CONFIRM = Read-Host "Do you want to prepare files for repository (y/n)?"
if ($CONFIRM -eq 'y') {
Write-Output "`n"
Remove-Item -Path "$TF_PLAN_OUT.json"
Write-Output "==> ($MODULE_NAME) - $TF_PLAN_OUT.json has been removed"
Write-Output "`n"
Remove-Item -Path "$PLANNED_VALUES.yml"
Write-Output "==> ($MODULE_NAME) - $PLANNED_VALUES.yml has been removed"
Write-Output "`n"
}
else {
Write-Warning -Message "($MODULE_NAME) - $TF_PLAN_OUT.json can contain sensitive data"
Write-Warning -Message "($MODULE_NAME) - Exposing $TF_PLAN_OUT.json in a repository can cause security breach"
Write-Output "`n"
Write-Output "($MODULE_NAME) - From within your terraform root module: conftest test $TF_PLAN_OUT.json -p ../../opa/policy/ -d $PLANNED_VALUES.yml"
Write-Output "`n"
}
Write-Output "==> ($MODULE_NAME) - Return to scripts directory..."
Set-Location $BASE_PATH
}
else {
Write-Output "==> Install Terraform on Windows..."
scoop install terraform
}
# Install jq
if (Get-command -name jq -ErrorAction SilentlyContinue) {
Write-Output "==> jq exists, skip install"
jq --version
}
else {
Write-Output "==> Install jq on Windows..."
scoop install jq
}
# Install yq
if (Get-command -name yq -ErrorAction SilentlyContinue) {
Write-Output "==> yq exists, skip install"
yq --version
}
else {
Write-Output "==> Install yq on Windows..."
scoop install yq
}
# Install Conftest
if (Get-command -name conftest -ErrorAction SilentlyContinue) {
Write-Output "==> conftest exists, skip install"
conftest --version
}
else {
Write-Output "==> Install conftest on Windows..."
scoop bucket add instrumenta https://github.com/instrumenta/scoop-instrumenta
scoop install conftest
}
if (-not ($MODULE_PATH | Test-Path)) { Throw "The directory does not exist, check path on .\opa-values-generator.ps1 :line 18" }
Write-Output "==> Change to the module root directory..."
Set-Location $MODULE_PATH
Write-Output "==> Initializing infrastructure..."
terraform init
Write-Output "==> Planning infrastructure..."
terraform plan `
-var="root_id_1=root-id-1" `
-var="root_id_2=root-id-2" `
-var="root_id_3=root-id-3" `
-var="root_name=root-name" `
-var="location=eastus" `
-out="$PLAN_NAME"
Write-Output "==> Converting plan to *.json..."
terraform show -json $PLAN_NAME | Out-File -FilePath .\$PLAN_NAME.json
Write-Output "==> Removing the original plan..."
Remove-Item -Path .\$PLAN_NAME
Write-Output "==> Saving planned values to a temporary planned_values.json..."
Get-Content -Path .\$PLAN_NAME.json | jq '.planned_values.root_module' | Out-File -FilePath .\planned_values.json
Write-Output "==> Converting to yaml..."
Get-Content -Path .\planned_values.json | yq e -P - | Tee-Object ..\opa\policy\planned_values.yml
# # # Run OPA Tests
Set-Location $MODULE_PATH
Write-Output "==> Running conftest..."
Write-Output "==> Testing management_groups..."
conftest test "$PLAN_NAME.json" -p ..\opa\policy\management_groups.rego -d ..\opa\policy\planned_values.yml
Write-Output "==> Testing role_definitions..."
conftest test "$PLAN_NAME.json" -p ..\opa\policy\role_definitions.rego -d ..\opa\policy\planned_values.yml
Write-Output "==> Testing role_assignments..."
conftest test "$PLAN_NAME.json" -p ..\opa\policy\role_assignments.rego -d ..\opa\policy\planned_values.yml
Write-Output "==> Testing policy_set_definitions..."
conftest test "$PLAN_NAME.json" -p ..\opa\policy\policy_set_definitions.rego -d ..\opa\policy\planned_values.yml
Write-Output "==> Testing policy_definitions..."
conftest test "$PLAN_NAME.json" -p ..\opa\policy\policy_definitions.rego -d ..\opa\policy\planned_values.yml
Write-Output "==> Testing policy_assignments..."
conftest test "$PLAN_NAME.json" -p ..\opa\policy\policy_assignments.rego -d ..\opa\policy\planned_values.yml
# # # Remove comments and $CONFIRM parameter for CMD prompt.
# # # $CONFIRM = Read-Host "Do you want to prepare files for repository (y/n)?"
if ($CONFIRM -eq 'y') {
Write-Output "`n"
Remove-Item -Path .\$PLAN_NAME.json
Write-Output "$PLAN_NAME.json has been removed from your root module"
Write-Output "`n"
Remove-Item -Path ..\opa\policy\planned_values.yml
Write-Output "planned_values.yml has been removed from your \opa\policy\ directory"
Write-Output "`n"
}
else {
Write-Warning -Message "$PLAN_NAME.json can contain sensitive data"
Write-Warning -Message "Exposing $PLAN_NAME.json in a repository can cause security breach"
Write-Output "`n"
Write-Output "From within your terraform root module: conftest test $PLAN_NAME.json -p ..\opa\policy\ -d ..\opa\policy\planned_values.yml"
Write-Output "`n"
}

Просмотреть файл

@ -1,107 +0,0 @@
#!/usr/bin/env bash
set -e
#
# Shell Script
# - OPA Run Tests
###############################################
# Run tests and generate testing values.
###############################################
# # Parameters
PLAN_NAME=terraform-plan
CONFIRM="y"
# shellcheck source=tests/scripts/opa-install.sh
source opa-install.sh
# Run this locally to test your terraform configuration and generate the values needed for the automation pipeline.
# The script will install all the necessary components locally and run the tests.
# After completing the tests, follow the script prompt for the next steps.
#
# # #? Run a local test against a different module configuration:
# # #* Update the path to run the tests on a different folder (example: ../deployment_2)
# # #* Copy paste the variables.tf file from deployment folder and adjust your main.tf
###############################################
# # #* Path of the tested _es terraform module
MODULE_PATH="../deployment"
###############################################
echo
if [ ! -d "$MODULE_PATH" ]; then
echo "The ${MODULE_PATH} directory does not exist, check path on .\opa-values-generator.sh :line 26"
exit
fi
echo "==> Change to the module root directory..."
cd $MODULE_PATH
echo "==> Initializing infrastructure..."
terraform init
echo "==> Planning infrastructure..."
terraform plan \
-var="root_id_1=root-id-1" \
-var="root_id_2=root-id-2" \
-var="root_id_3=root-id-3" \
-var="root_name=root-name" \
-var="location=eastus" \
-out=$PLAN_NAME
echo "==> Converting plan to *.json..."
terraform show -json "$PLAN_NAME" >"$PLAN_NAME".json
echo "==> Removing the original plan..."
rm "$PLAN_NAME"
echo "==> Saving planned values to a temporary planned_values.json..."
jq <"$PLAN_NAME.json" '.planned_values.root_module' >planned_values.json
echo "==> Converting to yaml..."
yq <planned_values.json e -P - >../opa/policy/planned_values.yml
echo "==> Check yaml for errors..."
yamllint -d relaxed ../opa/policy/planned_values.yml
echo "==> Running conftest..."
cd $MODULE_PATH
echo
echo "==> Testing management_groups..."
conftest test "$PLAN_NAME".json -p ../opa/policy/management_groups.rego -d ../opa/policy/planned_values.yml
echo
echo "==> Testing role_definitions..."
conftest test "$PLAN_NAME".json -p ../opa/policy/role_definitions.rego -d ../opa/policy/planned_values.yml
echo
echo "==> Testing role_assignments..."
conftest test "$PLAN_NAME".json -p ../opa/policy/role_assignments.rego -d ../opa/policy/planned_values.yml
echo
echo "==> Testing policy_set_definitions..."
conftest test "$PLAN_NAME".json -p ../opa/policy/policy_set_definitions.rego -d ../opa/policy/planned_values.yml
echo
echo "==> Testing policy_definitions..."
conftest test "$PLAN_NAME".json -p ../opa/policy/policy_definitions.rego -d ../opa/policy/planned_values.yml
echo
echo "==> Testing policy_assignments..."
conftest test "$PLAN_NAME".json -p ../opa/policy/policy_assignments.rego -d ../opa/policy/planned_values.yml
# # # Remove "<<-EOF $CONFIRM EOF" for CMD prompt.
echo
read -r -p "Do you want to prepare files for repository (y/n)?" CONT <<-EOF
$CONFIRM
EOF
if [ "$CONT" = "y" ]; then
rm $PLAN_NAME.json
echo
echo "$PLAN_NAME.json has been removed from your root module"
echo
rm ../opa/policy/planned_values.yml
echo "planned_values.yml has been removed from your /opa/policy/ directory"
echo
else
echo
echo "$PLAN_NAME.json can contain sensitive data"
echo
echo "Exposing $PLAN_NAME.json in a repository can cause security breach"
echo
echo "From within your terraform root module: conftest test $PLAN_NAME.json -p ../opa/policy/ -d ../opa/policy/planned_values.yml"
fi

Просмотреть файл

@ -6,12 +6,16 @@ set -e
# - Terraform Apply
#
TF_WORKSPACE="$PIPELINE_WORKSPACE/s/$TEST_MODULE_PATH"
TF_PLAN_OUT="$TF_WORKSPACE/terraform-plan-$TF_VERSION-$TF_AZ_VERSION"
TF_STATE="../tfstate/terraform-$TF_VERSION-$TF_AZ_VERSION.tfstate"
echo "==> Switching directories..."
cd "$PIPELINE_WORKSPACE/s/tests/deployment"
cd "$TF_WORKSPACE"
echo "==> Applying infrastructure..."
terraform apply \
-auto-approve \
-parallelism=50 \
-state="./terraform-$TF_VERSION-$TF_AZ_VERSION.tfstate" \
"terraform-plan-$TF_VERSION-$TF_AZ_VERSION"
-parallelism="$PARALLELISM" \
-state="$TF_STATE" \
"$TF_PLAN_OUT"

Просмотреть файл

@ -6,20 +6,20 @@ set -e
# - Terraform Destroy
#
TF_WORKSPACE="$PIPELINE_WORKSPACE/s/$TEST_MODULE_PATH"
echo "==> Switching directories..."
cd "$PIPELINE_WORKSPACE/s/tests/deployment"
cd "$TF_WORKSPACE"
echo "==> Destroying infrastructure..."
# shellcheck disable=SC2153 # Environment variables set by pipeline
terraform destroy \
-var "location=$DEFAULT_LOCATION" \
-var "root_id_1=$TF_ROOT_ID_1" \
-var "root_id_2=$TF_ROOT_ID_2" \
-var "root_id_3=$TF_ROOT_ID_3" \
-var "root_id=$TF_ROOT_ID" \
-var "root_name=ES-$TF_VERSION-$TF_AZ_VERSION" \
-var "primary_location=$PRIMARY_LOCATION" \
-var "secondary_location=$SECONDARY_LOCATION" \
-auto-approve \
-parallelism=256 \
-state="./terraform-$TF_VERSION-$TF_AZ_VERSION.tfstate"
-parallelism="$PARALLELISM"
status=$?
if [ $status -ne 0 ]; then
@ -34,7 +34,7 @@ if [ $status -ne 0 ]; then
IFS=$'\n'
TF_ROOT_ID=("$TF_ROOT_ID_1" "$TF_ROOT_ID_2" "$TF_ROOT_ID_3")
TF_ROOT_ID=("$TF_ROOT_ID")
for x in "${TF_ROOT_ID[@]}"; do
echo "==> Retrieving management group structure..."
TMP_FILE="./data.json"

Просмотреть файл

@ -6,8 +6,57 @@ set -e
# - Terraform Initialize
#
echo "==> Switching directories..."
cd "$PIPELINE_WORKSPACE/s/tests/deployment"
TF_WORKSPACE="$PIPELINE_WORKSPACE/s/$TEST_MODULE_PATH"
echo "==> Initializaing infrastructure..."
echo "==> Switching directories..."
cd "$TF_WORKSPACE"
echo "==> Creating terraform_override.tf with required_provider and local backend configuration..."
tee terraform_override.tf <<TFCONFIG
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "$TF_AZ_VERSION"
configuration_aliases = [
azurerm.connectivity,
azurerm.management,
]
}
}
backend "azurerm" {
resource_group_name = "$STORAGE_ACCOUNT_RSG_NAME"
storage_account_name = "$STORAGE_ACCOUNT_NAME"
container_name = "$STORAGE_CONTAINER_NAME"
key = "terraform-$TF_VERSION-$TF_AZ_VERSION.tfstate"
}
}
TFCONFIG
echo "==> Creating providers_override.tf with subscription configuration and credentials..."
cat >providers_override.tf <<TFCONFIG
provider "azurerm" {
features {}
alias = "connectivity"
subscription_id = "$TF_SUBSCRIPTION_ID_CONNECTIVITY"
client_id = "$ARM_CERTIFICATE_CLIENT_ID"
client_certificate_path = "$ARM_CERTIFICATE_PATH"
client_certificate_password = "$ARM_CERTIFICATE_PASSWORD"
tenant_id = "$ARM_TENANT_ID"
}
provider "azurerm" {
features {}
alias = "management"
subscription_id = "$TF_SUBSCRIPTION_ID_MANAGEMENT"
client_id = "$ARM_CERTIFICATE_CLIENT_ID"
client_certificate_path = "$ARM_CERTIFICATE_PATH"
client_certificate_password = "$ARM_CERTIFICATE_PASSWORD"
tenant_id = "$ARM_TENANT_ID"
}
TFCONFIG
echo "==> Initializaing Terraform workspace..."
terraform init

Просмотреть файл

@ -5,23 +5,25 @@ set -e
# Shell Script
# - Terraform Plan
#
TF_PLAN_JSON="terraform-plan-$TF_VERSION-$TF_AZ_VERSION"
TF_WORKSPACE="$PIPELINE_WORKSPACE/s/$TEST_MODULE_PATH"
TF_PLAN_OUT="$TF_WORKSPACE/terraform-plan-$TF_VERSION-$TF_AZ_VERSION"
TF_STATE="../tfstate/terraform-$TF_VERSION-$TF_AZ_VERSION.tfstate"
echo "==> Switching directories..."
cd "$PIPELINE_WORKSPACE/s/tests/deployment"
cd "$TF_WORKSPACE"
echo "==> Planning infrastructure..."
terraform plan \
-var "location=$DEFAULT_LOCATION" \
-var "root_id_1=$TF_ROOT_ID_1" \
-var "root_id_2=$TF_ROOT_ID_2" \
-var "root_id_3=$TF_ROOT_ID_3" \
-var "root_id=$TF_ROOT_ID" \
-var "root_name=ES-$TF_VERSION-$TF_AZ_VERSION" \
-state="./terraform-$TF_VERSION-$TF_AZ_VERSION.tfstate" \
-out="terraform-plan-$TF_VERSION-$TF_AZ_VERSION"
-var "primary_location=$PRIMARY_LOCATION" \
-var "secondary_location=$SECONDARY_LOCATION" \
-state="$TF_STATE" \
-out="$TF_PLAN_OUT"
echo "==> Convert plan to JSON..."
cd "$PIPELINE_WORKSPACE/s/tests/deployment" && terraform show -json "$TF_PLAN_JSON" >"$TF_PLAN_JSON".json
terraform show -json "$TF_PLAN_OUT" >"$TF_PLAN_OUT".json
echo "==> List all plan to JSON..."
cd "$PIPELINE_WORKSPACE/s/tests/deployment" && find . -name "*.json"
find . -name "*.json"

Просмотреть файл

@ -6,8 +6,10 @@ set -e
# - Terraform Prepare
#
CREDENTIALS_WORKSPACE="$PIPELINE_WORKSPACE/s/tests"
echo "==> Switching directories..."
cd "$PIPELINE_WORKSPACE/s/tests/deployment"
cd "$CREDENTIALS_WORKSPACE"
echo "==> Authenticating cli..."
az login \
@ -51,59 +53,12 @@ openssl pkcs12 \
echo "==> Deleting SPN certificate in PEM format..."
shred -uz "$SPN_NAME.pem"
echo "==> Creating provider.tf with required_provider version and credentials..."
cat >provider.tf <<TFCONFIG
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "$TF_AZ_VERSION"
configuration_aliases = [
azurerm.connectivity,
azurerm.management,
]
}
}
}
echo "==> Storing Client Certificate Details"
echo "##vso[task.setvariable variable=ARM_CERTIFICATE_CLIENT_ID;]$CERTIFICATE_CLIENT_ID"
echo "##vso[task.setvariable variable=ARM_CERTIFICATE_PATH;]$CREDENTIALS_WORKSPACE/$SPN_NAME.pfx"
echo "##vso[task.setvariable variable=ARM_CERTIFICATE_PASSWORD;]$CERTIFICATE_PASSWORD"
provider "azurerm" {
features {}
alias = "connectivity"
subscription_id = "$TF_SUBSCRIPTION_ID_CONNECTIVITY"
client_id = "$CERTIFICATE_CLIENT_ID"
client_certificate_path = "$SPN_NAME.pfx"
client_certificate_password = "$CERTIFICATE_PASSWORD"
tenant_id = "$ARM_TENANT_ID"
}
provider "azurerm" {
features {}
alias = "management"
subscription_id = "$TF_SUBSCRIPTION_ID_MANAGEMENT"
client_id = "$CERTIFICATE_CLIENT_ID"
client_certificate_path = "$SPN_NAME.pfx"
client_certificate_password = "$CERTIFICATE_PASSWORD"
tenant_id = "$ARM_TENANT_ID"
}
TFCONFIG
echo "==> Generating root id's..."
ROOT_ID_1="${RANDOM}-es"
ROOT_ID_2="${RANDOM}-es"
ROOT_ID_3="${RANDOM}-es"
echo "==> Azure Root ID 1 - $ROOT_ID_1"
echo "##vso[task.setvariable variable=TF_ROOT_ID_1;]$ROOT_ID_1"
echo "==> Azure Root ID 2 - $ROOT_ID_2"
echo "##vso[task.setvariable variable=TF_ROOT_ID_2;]$ROOT_ID_2"
echo "==> Azure Root ID 3 - $ROOT_ID_3"
echo "##vso[task.setvariable variable=TF_ROOT_ID_3;]$ROOT_ID_3"
echo "==> Displaying environment variables..."
echo "==> Terraform Version - $TF_VERSION"
echo "==> Terraform Provider Version - $TF_AZ_VERSION"
echo "==> Terraform Variable (Root ID) - $TF_ROOT_ID"
echo "==> Terraform Version - $TF_VERSION"
echo "==> Terraform Provider Version - $TF_AZ_VERSION"
echo "==> Terraform Variable (Root Name) - ES-$TF_VERSION-$TF_AZ_VERSION"

Просмотреть файл

@ -245,7 +245,60 @@ variable "configure_connectivity_resources" {
})
})
)
vwan_hub_networks = list(object({}))
vwan_hub_networks = list(
object({
enabled = bool
config = object({
address_prefix = string
location = string
sku = string
routes = list(
object({
address_prefixes = list(string)
next_hop_ip_address = string
})
)
expressroute_gateway = object({
enabled = bool
config = object({
scale_unit = number
})
})
vpn_gateway = object({
enabled = bool
config = object({
bgp_settings = list(
object({
asn = number
peer_weight = number
instance_0_bgp_peering_address = list(
object({
custom_ips = list(string)
})
)
instance_1_bgp_peering_address = list(
object({
custom_ips = list(string)
})
)
})
)
routing_preference = string
scale_unit = number
})
})
azure_firewall = object({
enabled = bool
config = object({
enable_dns_proxy = bool
sku_tier = string
})
})
spoke_virtual_network_resource_ids = list(string)
enable_virtual_hub_connections = bool
})
})
)
ddos_protection_plan = object({
enabled = bool
config = object({