From 762ca463b3aa517e888cd1f6e0ea1280df190b94 Mon Sep 17 00:00:00 2001 From: Marcus Robinson Date: Tue, 7 Jan 2025 18:38:13 +0000 Subject: [PATCH 1/8] Add Terraform configuration for Azure Firewall and route tables to move to core --- Makefile | 15 +- .../terraform/firewall}/firewall.tf | 32 ++- .../terraform/firewall}/import_state.sh | 67 +++--- core/terraform/firewall/locals.tf | 14 ++ core/terraform/firewall/main.tf | 9 + core/terraform/firewall/outputs.tf | 3 + .../terraform/firewall}/remove_state.sh | 32 +-- core/terraform/firewall/rules.tf | 198 +++++++++++++++++ core/terraform/firewall/variables.tf | 61 +++++ core/terraform/main.tf | 21 ++ core/terraform/network/outputs.tf | 21 ++ core/terraform/routetable.tf | 70 ++++++ .../shared_services/firewall/porter.yaml | 15 +- .../firewall/terraform/data.tf | 82 +------ .../firewall/terraform/locals.tf | 9 - .../firewall/terraform/routetable.tf | 89 -------- .../firewall/terraform/rules.tf | 210 +----------------- .../firewall/terraform/variables.tf | 10 - 18 files changed, 474 insertions(+), 484 deletions(-) rename {templates/shared_services/firewall/terraform => core/terraform/firewall}/firewall.tf (70%) rename {templates/shared_services/firewall/terraform => core/terraform/firewall}/import_state.sh (51%) create mode 100644 core/terraform/firewall/locals.tf create mode 100644 core/terraform/firewall/main.tf create mode 100644 core/terraform/firewall/outputs.tf rename {templates/shared_services/firewall/terraform => core/terraform/firewall}/remove_state.sh (57%) create mode 100644 core/terraform/firewall/rules.tf create mode 100644 core/terraform/firewall/variables.tf create mode 100644 core/terraform/routetable.tf delete mode 100644 templates/shared_services/firewall/terraform/routetable.tf diff --git a/Makefile b/Makefile index 90e7686808..91414dc7bc 100644 --- a/Makefile +++ b/Makefile @@ -25,10 +25,6 @@ build-and-push-airlock-processor: build-airlock-processor push-airlock-processor help: ## 💬 This help message :) @grep -E '[a-zA-Z_-]+:.*?## .*$$' $(firstword $(MAKEFILE_LIST)) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-25s\033[0m %s\n", $$1, $$2}' -# to move your environment from the single 'core' deployment (which includes the firewall) -# toward the shared services model, where it is split out - run the following make target before a tre-deploy -# This will remove + import the resource state into a shared service -migrate-firewall-state: prepare-tf-state bootstrap: $(call target_title, "Bootstrap Terraform") \ @@ -96,15 +92,12 @@ push-resource-processor-vm-porter-image: push-airlock-processor: $(call push_image,"airlock-processor","${MAKEFILE_DIR}/airlock_processor/_version.py") -# # These targets are for a graceful migration of Firewall -# # from terraform state in Core to a Shared Service. -# # See https://github.com/microsoft/AzureTRE/issues/1177 -prepare-tf-state: +migrate-firewall-state: $(call target_title, "Preparing terraform state") \ && . ${MAKEFILE_DIR}/devops/scripts/check_dependencies.sh nodocker,env \ - && pushd ${MAKEFILE_DIR}/core/terraform > /dev/null && ../../shared_services/firewall/terraform/remove_state.sh && popd > /dev/null \ - && pushd ${MAKEFILE_DIR}/templates/shared_services/firewall/terraform > /dev/null && ./import_state.sh && popd > /dev/null -# / End migration targets + && pushd ${MAKEFILE_DIR}/templates/shared_services/firewall/terraform > /dev/null && ${MAKEFILE_DIR}/core/terraform/firewall/remove_state.sh && popd > /dev/null \ + && pushd ${MAKEFILE_DIR}/core/terraform > /dev/null && ${MAKEFILE_DIR}/core/terraform/firewall/import_state.sh && popd > /dev/null + deploy-core: tre-start $(call target_title, "Deploying TRE") \ diff --git a/templates/shared_services/firewall/terraform/firewall.tf b/core/terraform/firewall/firewall.tf similarity index 70% rename from templates/shared_services/firewall/terraform/firewall.tf rename to core/terraform/firewall/firewall.tf index ae94aecff0..76ef0ee8e7 100644 --- a/templates/shared_services/firewall/terraform/firewall.tf +++ b/core/terraform/firewall/firewall.tf @@ -1,43 +1,37 @@ resource "azurerm_public_ip" "fwtransit" { name = "pip-fw-${var.tre_id}" resource_group_name = local.core_resource_group_name - location = data.azurerm_resource_group.rg.location + location = var.location allocation_method = "Static" sku = "Standard" - tags = local.tre_shared_service_tags + tags = var.tre_core_tags lifecycle { ignore_changes = [tags, zones] } } -moved { - from = azurerm_public_ip.fwpip - to = azurerm_public_ip.fwtransit -} - resource "azurerm_public_ip" "fwmanagement" { count = local.effective_firewall_sku == "Basic" ? 1 : 0 name = "pip-fw-management-${var.tre_id}" - resource_group_name = local.core_resource_group_name - location = data.azurerm_resource_group.rg.location + resource_group_name = var.resource_group_name + location = var.location allocation_method = "Static" sku = "Standard" - tags = local.tre_shared_service_tags + tags = var.tre_core_tags lifecycle { ignore_changes = [tags, zones] } } - resource "azurerm_firewall" "fw" { name = local.firewall_name resource_group_name = local.core_resource_group_name - location = data.azurerm_resource_group.rg.location + location = var.location sku_tier = local.effective_firewall_sku sku_name = "AZFW_VNet" firewall_policy_id = azurerm_firewall_policy.root.id - tags = local.tre_shared_service_tags + tags = var.tre_core_tags ip_configuration { name = "fw-ip-configuration" - subnet_id = data.azurerm_subnet.firewall.id + subnet_id = var.firewall_subnet_id public_ip_address_id = azurerm_public_ip.fwtransit.id } @@ -45,7 +39,7 @@ resource "azurerm_firewall" "fw" { for_each = local.effective_firewall_sku == "Basic" ? [1] : [] content { name = "mgmtconfig" - subnet_id = data.azurerm_subnet.firewall_management.id + subnet_id = var.firewall_management_subnet_id public_ip_address_id = azurerm_public_ip.fwmanagement[0].id } } @@ -60,7 +54,7 @@ data "azurerm_monitor_diagnostic_categories" "firewall" { resource "azurerm_monitor_diagnostic_setting" "firewall" { name = "diagnostics-fw-${var.tre_id}" target_resource_id = azurerm_firewall.fw.id - log_analytics_workspace_id = data.azurerm_log_analytics_workspace.tre.id + log_analytics_workspace_id = var.log_analytics_workspace_id log_analytics_destination_type = "Dedicated" dynamic "enabled_log" { @@ -78,10 +72,10 @@ resource "azurerm_monitor_diagnostic_setting" "firewall" { resource "azurerm_firewall_policy" "root" { name = local.firewall_policy_name - resource_group_name = local.core_resource_group_name - location = data.azurerm_resource_group.rg.location + resource_group_name = var.resource_group_name + location = var.location sku = local.effective_firewall_sku - tags = local.tre_shared_service_tags + tags = var.tre_core_tags lifecycle { ignore_changes = [tags] } } diff --git a/templates/shared_services/firewall/terraform/import_state.sh b/core/terraform/firewall/import_state.sh similarity index 51% rename from templates/shared_services/firewall/terraform/import_state.sh rename to core/terraform/firewall/import_state.sh index ea27f7d513..92ce35ded5 100755 --- a/templates/shared_services/firewall/terraform/import_state.sh +++ b/core/terraform/firewall/import_state.sh @@ -19,7 +19,7 @@ terraform init -input=false -backend=true -reconfigure -upgrade \ -backend-config="resource_group_name=${TF_VAR_mgmt_resource_group_name}" \ -backend-config="storage_account_name=${TF_VAR_mgmt_storage_account_name}" \ -backend-config="container_name=${TF_VAR_terraform_state_container_name}" \ - -backend-config="key=${TRE_ID}-shared-service-firewall" + -backend-config="key=${TRE_ID}" # Import a resource if it exists in Azure but doesn't exist in Terraform tf_state_list="$(terraform state list)" @@ -29,7 +29,9 @@ function import_if_exists() { CMD=$3 # Check if the resource exists in Terraform - TF_RESOURCE_EXISTS=$(echo "$tf_state_list" | grep -q ^"${ADDRESS}"$; echo $?) + echo "Checking if ${ADDRESS} exists in Terraform state..." + ESCAPED_ADDRESS=$(printf '%q' "${ADDRESS}") + TF_RESOURCE_EXISTS=$(echo "$tf_state_list" | grep -q ^"${ESCAPED_ADDRESS}"$; echo $?) if [[ ${TF_RESOURCE_EXISTS} -eq 0 ]]; then echo "${ADDRESS} already in TF State, ignoring..." @@ -44,6 +46,8 @@ function import_if_exists() { ${CMD} > /dev/null AZ_RESOURCE_EXISTS=$? + + # If resource exists in Terraform, it's already managed -- don't do anything # If resource doesn't exist in Terraform and doesn't exist in Azure, it will be created -- don't do anything # If resource doesn't exist in Terraform but exist in Azure, we need to import it @@ -53,45 +57,26 @@ function import_if_exists() { fi } -import_if_exists azurerm_firewall.fw "/subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/azureFirewalls/fw-${TRE_ID}" || echo "Resource already exists" - -# Firewall rules -import_if_exists azurerm_firewall_application_rule_collection.resource_processor_subnet \ - "/subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/azureFirewalls/fw-${TRE_ID}/applicationRuleCollections/arc-resource_processor_subnet" \ - "az network firewall show --ids /subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/azureFirewalls/fw-${TRE_ID}/applicationRuleCollections/arc-resource_processor_subnet" - -import_if_exists azurerm_firewall_application_rule_collection.shared_subnet \ - "/subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/azureFirewalls/fw-${TRE_ID}/applicationRuleCollections/arc-shared_subnet" \ - "az network firewall show --ids /subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/azureFirewalls/fw-${TRE_ID}/applicationRuleCollections/arc-shared_subnet" - -import_if_exists azurerm_firewall_application_rule_collection.web_app_subnet \ - "/subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/azureFirewalls/fw-${TRE_ID}/applicationRuleCollections/arc-web_app_subnet" \ - "az network firewall show --ids /subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/azureFirewalls/fw-${TRE_ID}/applicationRuleCollections/arc-web_app_subnet" +# Firewall +import_if_exists module.firewall.azurerm_firewall.fw "/subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/azureFirewalls/fw-${TRE_ID}" -import_if_exists azurerm_firewall_network_rule_collection.general \ - "/subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/azureFirewalls/fw-${TRE_ID}/networkRuleCollections/general" \ - "az network firewall show --ids /subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/azureFirewalls/fw-${TRE_ID}/networkRuleCollections/general" +# Firewall IPs +if [[ "${FIREWALL_SKU}" == "Basic" ]]; then + import_if_exists module.firewall.azurerm_public_ip.fwmanagement[0] "/subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/publicIPAddresses/pip-fw-management-${TRE_ID}" +fi -import_if_exists azurerm_firewall_network_rule_collection.resource_processor_subnet \ - "/subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/azureFirewalls/fw-${TRE_ID}/networkRuleCollections/nrc-resource_processor_subnet" \ - "az network firewall show --ids /subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/azureFirewalls/fw-${TRE_ID}/networkRuleCollections/nrc-resource_processor_subnet" +import_if_exists module.firewall.azurerm_public_ip.fwtransit "/subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/publicIPAddresses/pip-fw-${TRE_ID}" -import_if_exists azurerm_firewall_network_rule_collection.web_app_subnet \ - "/subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/azureFirewalls/fw-${TRE_ID}/networkRuleCollections/nrc-web_app_subnet" \ - "az network firewall show --ids /subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/azureFirewalls/fw-${TRE_ID}/networkRuleCollections/nrc-web_app_subnet" +# Firewall policy +import_if_exists module.firewall.azurerm_firewall_policy.root "/subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/firewallPolicies/fw-policy-${TRE_ID}" +import_if_exists module.firewall.azurerm_firewall_policy_rule_collection_group.core \ + "/subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/firewallPolicies/fw-policy-${TRE_ID}/ruleCollectionGroups/rcg-core" # Diagnostic settings -import_if_exists azurerm_monitor_diagnostic_setting.firewall \ - "/subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/azureFirewalls/fw-${TRE_ID}|diagnostics-firewall-${TRE_ID}" \ - "az monitor diagnostic-settings show --resource /subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/rg-${TRE_ID}/providers/microsoft.network/azureFirewalls/fw-${TRE_ID} --name diagnostics-firewall-${TRE_ID}" - - -import_if_exists azurerm_public_ip.fwpip "/subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/publicIPAddresses/pip-fw-${TRE_ID}" - - -import_if_exists azurerm_subnet_route_table_association.rt_web_app_subnet_association \ - "/subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/virtualNetworks/vnet-${TRE_ID}/subnets/WebAppSubnet" +import_if_exists module.firewall.azurerm_monitor_diagnostic_setting.firewall \ + "/subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/azureFirewalls/fw-${TRE_ID}|diagnostics-fw-${TRE_ID}" \ + "az monitor diagnostic-settings show --resource /subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/rg-${TRE_ID}/providers/microsoft.network/azureFirewalls/fw-${TRE_ID} --name diagnostics-fw-${TRE_ID}" # Route tables import_if_exists azurerm_route_table.rt \ @@ -102,3 +87,15 @@ import_if_exists azurerm_subnet_route_table_association.rt_shared_subnet_associa import_if_exists azurerm_subnet_route_table_association.rt_resource_processor_subnet_association \ "/subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/virtualNetworks/vnet-${TRE_ID}/subnets/ResourceProcessorSubnet" + +import_if_exists azurerm_subnet_route_table_association.rt_web_app_subnet_association \ + "/subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/virtualNetworks/vnet-${TRE_ID}/subnets/WebAppSubnet" + +import_if_exists azurerm_subnet_route_table_association.rt_airlock_processor_subnet_association \ + "/subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/virtualNetworks/vnet-${TRE_ID}/subnets/AirlockProcessorSubnet" + +import_if_exists azurerm_subnet_route_table_association.rt_airlock_storage_subnet_association \ + "/subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/virtualNetworks/vnet-${TRE_ID}/subnets/AirlockStorageSubnet" + +import_if_exists azurerm_subnet_route_table_association.rt_airlock_events_subnet_association \ + "/subscriptions/${ARM_SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP_ID}/providers/Microsoft.Network/virtualNetworks/vnet-${TRE_ID}/subnets/AirlockEventsSubnet" diff --git a/core/terraform/firewall/locals.tf b/core/terraform/firewall/locals.tf new file mode 100644 index 0000000000..8369d9c021 --- /dev/null +++ b/core/terraform/firewall/locals.tf @@ -0,0 +1,14 @@ +locals { + core_resource_group_name = "rg-${var.tre_id}" + firewall_name = "fw-${var.tre_id}" + firewall_diagnostic_categories_enabled = [ + "AZFWApplicationRule", + "AZFWNetworkRule", + "AZFWDnsProxy", + ] + + firewall_policy_name = "fw-policy-${var.tre_id}" + + default_firewall_sku = "Standard" + effective_firewall_sku = coalesce(var.firewall_sku, local.default_firewall_sku) +} diff --git a/core/terraform/firewall/main.tf b/core/terraform/firewall/main.tf new file mode 100644 index 0000000000..a4eb095f9c --- /dev/null +++ b/core/terraform/firewall/main.tf @@ -0,0 +1,9 @@ +terraform { + # In modules we should only specify the min version + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">= 3.117" + } + } +} diff --git a/core/terraform/firewall/outputs.tf b/core/terraform/firewall/outputs.tf new file mode 100644 index 0000000000..a58f89dc2b --- /dev/null +++ b/core/terraform/firewall/outputs.tf @@ -0,0 +1,3 @@ +output "private_ip_address" { + value = azurerm_firewall.fw.ip_configuration[0].private_ip_address +} diff --git a/templates/shared_services/firewall/terraform/remove_state.sh b/core/terraform/firewall/remove_state.sh similarity index 57% rename from templates/shared_services/firewall/terraform/remove_state.sh rename to core/terraform/firewall/remove_state.sh index 3b68a8c2ec..e47cb4ad13 100755 --- a/templates/shared_services/firewall/terraform/remove_state.sh +++ b/core/terraform/firewall/remove_state.sh @@ -12,12 +12,13 @@ terraform init -input=false -backend=true -reconfigure -upgrade \ -backend-config="resource_group_name=${TF_VAR_mgmt_resource_group_name}" \ -backend-config="storage_account_name=${TF_VAR_mgmt_storage_account_name}" \ -backend-config="container_name=${TF_VAR_terraform_state_container_name}" \ - -backend-config="key=${TRE_ID}" + -backend-config="key=${TRE_ID}-shared-service-firewall" tf_state_list="$(terraform state list)" function remove_if_present() { echo -n "Checking $1 ..." - found=$(echo "$tf_state_list" | grep -q ^"$1"$; echo $?) + ESCAPED_ADDRESS=$(printf '%q' "${1}") + found=$(echo "$tf_state_list" | grep -q ^"$ESCAPED_ADDRESS"$; echo $?) if [[ $found -eq 0 ]]; then echo " removing" @@ -27,17 +28,22 @@ function remove_if_present() { fi } +# routetable.tf remove_if_present azurerm_route_table.rt -remove_if_present azurerm_subnet_route_table_association.rt_resource_processor_subnet_association remove_if_present azurerm_subnet_route_table_association.rt_shared_subnet_association +remove_if_present azurerm_subnet_route_table_association.rt_resource_processor_subnet_association remove_if_present azurerm_subnet_route_table_association.rt_web_app_subnet_association -remove_if_present module.firewall -remove_if_present module.firewall.azurerm_public_ip.fwpip -remove_if_present module.firewall.azurerm_monitor_diagnostic_setting.firewall -remove_if_present module.firewall.azurerm_firewall_network_rule_collection.web_app_subnet -remove_if_present module.firewall.azurerm_firewall_network_rule_collection.resource_processor_subnet -remove_if_present module.firewall.azurerm_firewall_network_rule_collection.general -remove_if_present module.firewall.azurerm_firewall_application_rule_collection.web_app_subnet -remove_if_present module.firewall.azurerm_firewall_application_rule_collection.shared_subnet -remove_if_present module.firewall.azurerm_firewall_application_rule_collection.resource_processor_subnet -remove_if_present module.firewall.azurerm_firewall.fw +remove_if_present azurerm_subnet_route_table_association.rt_airlock_processor_subnet_association +remove_if_present azurerm_subnet_route_table_association.rt_airlock_storage_subnet_association +remove_if_present azurerm_subnet_route_table_association.rt_airlock_events_subnet_association + +# rules.tf +remove_if_present azurerm_firewall_network_rule_collection.core + +# firewall.tf +remove_if_present azurerm_public_ip.fwtransit +remove_if_present azurerm_public_ip.fwmanagement[0] +remove_if_present azurerm_firewall.fw +remove_if_present azurerm_monitor_diagnostic_categories.firewall +remove_if_present azurerm_monitor_diagnostic_setting.firewall +remove_if_present azurerm_firewall_policy.root diff --git a/core/terraform/firewall/rules.tf b/core/terraform/firewall/rules.tf new file mode 100644 index 0000000000..8ff1cc6915 --- /dev/null +++ b/core/terraform/firewall/rules.tf @@ -0,0 +1,198 @@ +resource "azurerm_firewall_policy_rule_collection_group" "core" { + name = "rcg-core" + firewall_policy_id = azurerm_firewall_policy.root.id + priority = 500 + + network_rule_collection { + name = "nrc-general" + priority = 201 + action = "Allow" + + rule { + name = "time" + protocols = [ + "UDP" + ] + destination_addresses = [ + "*" + ] + destination_ports = [ + "123" + ] + source_addresses = [ + "*" + ] + } + } + + network_rule_collection { + name = "nrc-resource-processor-subnet" + priority = 202 + action = "Allow" + + rule { + name = "azure-services" + protocols = [ + "TCP" + ] + destination_addresses = [ + "AzureActiveDirectory", + "AzureResourceManager", + "AzureContainerRegistry", + "Storage", + "AzureKeyVault" + ] + destination_ports = [ + "443" + ] + source_ip_groups = [var.resource_processor_ip_group_id] + } + } + + network_rule_collection { + name = "nrc-web-app-subnet" + priority = 203 + action = "Allow" + + rule { + name = "azure-services" + protocols = [ + "TCP" + ] + destination_addresses = [ + "AzureActiveDirectory", + "AzureContainerRegistry", + "AzureResourceManager" + ] + destination_ports = [ + "443" + ] + source_ip_groups = [var.web_app_ip_group_id] + } + } + + application_rule_collection { + name = "arc-resource-processor-subnet" + priority = 301 + action = "Allow" + + rule { + name = "os-package-sources" + protocols { + port = "443" + type = "Https" + } + protocols { + port = "80" + type = "Http" + } + destination_fqdns = [ + "packages.microsoft.com", + "keyserver.ubuntu.com", + "api.snapcraft.io", + "azure.archive.ubuntu.com", + "security.ubuntu.com", + "entropy.ubuntu.com", + ] + source_ip_groups = [var.resource_processor_ip_group_id] + } + + rule { + name = "docker-sources" + protocols { + port = "443" + type = "Https" + } + protocols { + port = "80" + type = "Http" + } + destination_fqdns = [ + "download.docker.com", + "registry-1.docker.io", + "auth.docker.io", + ] + source_ip_groups = [var.resource_processor_ip_group_id] + } + # This rule is needed to support Gov Cloud. + # The az cli uses msal lib which requires access to this fqdn for authentication. + rule { + name = "microsoft-login" + protocols { + port = "443" + type = "Https" + } + destination_fqdns = [ + "login.microsoftonline.com", + ] + source_ip_groups = [var.resource_processor_ip_group_id] + } + + + } + + application_rule_collection { + name = "arc-shared-subnet" + priority = 302 + action = "Allow" + + rule { + name = "nexus-bootstrap" + protocols { + port = "443" + type = "Https" + } + protocols { + port = "80" + type = "Http" + } + destination_fqdns = [ + "keyserver.ubuntu.com", + "packages.microsoft.com", + "download.docker.com", + "azure.archive.ubuntu.com" + ] + source_ip_groups = [var.shared_services_ip_group_id] + } + } + + application_rule_collection { + name = "arc-web-app-subnet" + priority = 303 + action = "Allow" + + rule { + name = "microsoft-graph" + protocols { + port = "443" + type = "Https" + } + destination_fqdns = [ + var.microsoft_graph_fqdn + ] + source_ip_groups = [var.web_app_ip_group_id] + } + } + + application_rule_collection { + name = "arc-airlock-processor-subnet" + priority = 304 + action = "Allow" + + rule { + name = "functions-runtime" + protocols { + port = "443" + type = "Https" + } + destination_fqdns = [ + "functionscdn.azureedge.net" + ] + source_ip_groups = [var.airlock_processor_ip_group_id] + } + } + + depends_on = [ + azurerm_firewall.fw + ] +} diff --git a/core/terraform/firewall/variables.tf b/core/terraform/firewall/variables.tf new file mode 100644 index 0000000000..a0550b503b --- /dev/null +++ b/core/terraform/firewall/variables.tf @@ -0,0 +1,61 @@ +variable "tre_id" { + type = string + description = "Unique TRE ID" +} + +variable "location" { + type = string +} + +variable "resource_group_name" { + type = string +} + +variable "firewall_sku" { + type = string + default = "" +} + +variable "firewall_subnet_id" { + type = string + description = "Subnet ID for the firewall" +} + +variable "firewall_management_subnet_id" { + type = string + description = "Subnet ID for the firewall management" +} + +variable "tre_core_tags" { + type = map(string) + description = "Tags to apply to all resources" +} + +variable "microsoft_graph_fqdn" { + type = string + description = "Microsoft Graph FQDN" +} + +variable "log_analytics_workspace_id" { + type = string +} + +variable "resource_processor_ip_group_id" { + type = string + description = "Resource Processor IP Group" +} + +variable "web_app_ip_group_id" { + type = string + description = "Web App IP Group" +} + +variable "airlock_processor_ip_group_id" { + type = string + description = "Airlock Processor IP Group" +} + +variable "shared_services_ip_group_id" { + type = string + description = "Shared Services IP Group" +} diff --git a/core/terraform/main.tf b/core/terraform/main.tf index 49693884c1..6ddbd9d183 100644 --- a/core/terraform/main.tf +++ b/core/terraform/main.tf @@ -92,6 +92,27 @@ module "network" { arm_environment = var.arm_environment } +module "firewall" { + source = "./firewall" + tre_id = var.tre_id + firewall_sku = var.firewall_sku + firewall_subnet_id = module.network.azure_firewall_subnet_id + location = var.location + resource_group_name = azurerm_resource_group.core.name + tre_core_tags = local.tre_core_tags + microsoft_graph_fqdn = regex("(?:(?P[^:/?#]+):)?(?://(?P[^/?#:]*))?", module.terraform_azurerm_environment_configuration.microsoft_graph_endpoint).fqdn + log_analytics_workspace_id = module.azure_monitor.log_analytics_workspace_id + firewall_management_subnet_id = module.network.firewall_management_subnet_id + resource_processor_ip_group_id = module.network.resource_processor_ip_group_id + shared_services_ip_group_id = module.network.shared_services_ip_group_id + web_app_ip_group_id = module.network.web_app_ip_group_id + airlock_processor_ip_group_id = module.network.airlock_processor_ip_group_id + + depends_on = [ + module.network, + ] +} + module "appgateway" { source = "./appgateway" tre_id = var.tre_id diff --git a/core/terraform/network/outputs.tf b/core/terraform/network/outputs.tf index 3e0aab407d..56b9a62ed7 100644 --- a/core/terraform/network/outputs.tf +++ b/core/terraform/network/outputs.tf @@ -10,6 +10,10 @@ output "azure_firewall_subnet_id" { value = azurerm_subnet.azure_firewall.id } +output "firewall_management_subnet_id" { + value = azurerm_subnet.firewall_management.id +} + output "app_gw_subnet_id" { value = azurerm_subnet.app_gw.id } @@ -83,3 +87,20 @@ output "queue_core_dns_zone_id" { output "table_core_dns_zone_id" { value = azurerm_private_dns_zone.private_dns_zones["privatelink.table.core.windows.net"].id } + +# IP Groups +output "resource_processor_ip_group_id" { + value = azurerm_ip_group.resource_processor.id +} + +output "shared_services_ip_group_id" { + value = azurerm_ip_group.shared.id +} + +output "airlock_processor_ip_group_id" { + value = azurerm_ip_group.airlock_processor.id +} + +output "web_app_ip_group_id" { + value = azurerm_ip_group.webapp.id +} diff --git a/core/terraform/routetable.tf b/core/terraform/routetable.tf new file mode 100644 index 0000000000..06e467be84 --- /dev/null +++ b/core/terraform/routetable.tf @@ -0,0 +1,70 @@ +resource "azurerm_route_table" "rt" { + name = "rt-${var.tre_id}" + resource_group_name = azurerm_resource_group.core.name + location = var.location + tags = local.tre_core_tags + + lifecycle { ignore_changes = [tags] } + + route { + name = "DefaultRoute" + address_prefix = "0.0.0.0/0" + next_hop_type = "VirtualAppliance" + next_hop_in_ip_address = module.firewall.private_ip_address + } +} + +resource "azurerm_subnet_route_table_association" "rt_shared_subnet_association" { + subnet_id = module.network.shared_subnet_id + route_table_id = azurerm_route_table.rt.id + + depends_on = [ + module.firewall + ] +} + +resource "azurerm_subnet_route_table_association" "rt_resource_processor_subnet_association" { + subnet_id = module.network.resource_processor_subnet_id + route_table_id = azurerm_route_table.rt.id + + # Not waiting for the rules will block traffic prematurally. + depends_on = [ + module.firewall + ] +} + +resource "azurerm_subnet_route_table_association" "rt_web_app_subnet_association" { + subnet_id = module.network.web_app_subnet_id + route_table_id = azurerm_route_table.rt.id + + depends_on = [ + module.firewall + ] +} + +resource "azurerm_subnet_route_table_association" "rt_airlock_processor_subnet_association" { + subnet_id = module.network.airlock_processor_subnet_id + route_table_id = azurerm_route_table.rt.id + + depends_on = [ + module.firewall + ] +} + +resource "azurerm_subnet_route_table_association" "rt_airlock_storage_subnet_association" { + subnet_id = module.network.airlock_storage_subnet_id + route_table_id = azurerm_route_table.rt.id + + depends_on = [ + module.firewall + ] +} + +resource "azurerm_subnet_route_table_association" "rt_airlock_events_subnet_association" { + subnet_id = module.network.airlock_events_subnet_id + route_table_id = azurerm_route_table.rt.id + + depends_on = [ + module.firewall + ] +} diff --git a/templates/shared_services/firewall/porter.yaml b/templates/shared_services/firewall/porter.yaml index d5e7003d14..77bf4aeeb3 100644 --- a/templates/shared_services/firewall/porter.yaml +++ b/templates/shared_services/firewall/porter.yaml @@ -1,7 +1,7 @@ --- schemaVersion: 1.0.0 name: tre-shared-service-firewall -version: 1.2.8 +version: 1.3.0 description: "An Azure TRE Firewall shared service" dockerfile: Dockerfile.tmpl registry: azuretre @@ -45,13 +45,6 @@ parameters: type: string default: "W10=" # b64 for [] description: "Network rule collection array" - - name: firewall_sku - type: string - default: Standard - description: The firewall and its policy SKU tier - - name: microsoft_graph_fqdn - type: string - default: "graph.microsoft.com" - name: arm_environment type: string @@ -67,8 +60,6 @@ install: tre_resource_id: ${ bundle.parameters.id } api_driven_rule_collections_b64: ${ bundle.parameters.rule_collections } api_driven_network_rule_collections_b64: ${ bundle.parameters.network_rule_collections } - firewall_sku: ${ bundle.parameters.firewall_sku } - microsoft_graph_fqdn: ${ bundle.parameters.microsoft_graph_fqdn } backendConfig: use_azuread_auth: "true" use_oidc: "true" @@ -85,8 +76,6 @@ upgrade: tre_resource_id: ${ bundle.parameters.id } api_driven_rule_collections_b64: ${ bundle.parameters.rule_collections } api_driven_network_rule_collections_b64: ${ bundle.parameters.network_rule_collections } - firewall_sku: ${ bundle.parameters.firewall_sku } - microsoft_graph_fqdn: ${ bundle.parameters.microsoft_graph_fqdn } backendConfig: use_azuread_auth: "true" use_oidc: "true" @@ -103,8 +92,6 @@ uninstall: tre_resource_id: ${ bundle.parameters.id } api_driven_rule_collections_b64: ${ bundle.parameters.rule_collections } api_driven_network_rule_collections_b64: ${ bundle.parameters.network_rule_collections } - firewall_sku: ${ bundle.parameters.firewall_sku } - microsoft_graph_fqdn: ${ bundle.parameters.microsoft_graph_fqdn } backendConfig: use_azuread_auth: "true" use_oidc: "true" diff --git a/templates/shared_services/firewall/terraform/data.tf b/templates/shared_services/firewall/terraform/data.tf index 8eb3d9da07..fe32363738 100644 --- a/templates/shared_services/firewall/terraform/data.tf +++ b/templates/shared_services/firewall/terraform/data.tf @@ -1,80 +1,3 @@ -data "azurerm_subnet" "firewall" { - name = "AzureFirewallSubnet" - virtual_network_name = "vnet-${var.tre_id}" - resource_group_name = local.core_resource_group_name -} - -data "azurerm_subnet" "firewall_management" { - name = "AzureFirewallManagementSubnet" - virtual_network_name = "vnet-${var.tre_id}" - resource_group_name = local.core_resource_group_name -} - -data "azurerm_subnet" "shared" { - name = "SharedSubnet" - virtual_network_name = "vnet-${var.tre_id}" - resource_group_name = local.core_resource_group_name -} - -data "azurerm_subnet" "resource_processor" { - name = "ResourceProcessorSubnet" - virtual_network_name = "vnet-${var.tre_id}" - resource_group_name = local.core_resource_group_name -} - -data "azurerm_subnet" "web_app" { - name = "WebAppSubnet" - virtual_network_name = "vnet-${var.tre_id}" - resource_group_name = local.core_resource_group_name -} - -data "azurerm_subnet" "airlock_processor" { - name = "AirlockProcessorSubnet" - virtual_network_name = "vnet-${var.tre_id}" - resource_group_name = local.core_resource_group_name -} - -data "azurerm_subnet" "airlock_storage" { - name = "AirlockStorageSubnet" - virtual_network_name = "vnet-${var.tre_id}" - resource_group_name = local.core_resource_group_name -} - -data "azurerm_subnet" "airlock_events" { - name = "AirlockEventsSubnet" - virtual_network_name = "vnet-${var.tre_id}" - resource_group_name = local.core_resource_group_name -} - -data "azurerm_log_analytics_workspace" "tre" { - name = "log-${var.tre_id}" - resource_group_name = local.core_resource_group_name -} - -data "azurerm_resource_group" "rg" { - name = local.core_resource_group_name -} - -data "azurerm_ip_group" "resource_processor" { - name = "ipg-resource-processor" - resource_group_name = local.core_resource_group_name -} - -data "azurerm_ip_group" "shared" { - name = "ipg-shared" - resource_group_name = local.core_resource_group_name -} - -data "azurerm_ip_group" "web" { - name = "ipg-web-app" - resource_group_name = local.core_resource_group_name -} - -data "azurerm_ip_group" "airlock_processor" { - name = "ipg-airlock-processor" - resource_group_name = local.core_resource_group_name -} - data "azurerm_ip_group" "referenced" { for_each = toset(distinct(flatten( [for collection in concat(local.api_driven_network_rule_collection, local.api_driven_application_rule_collection) : @@ -84,3 +7,8 @@ data "azurerm_ip_group" "referenced" { name = each.value resource_group_name = local.core_resource_group_name } + +data "azurerm_firewall_policy" "root" { + name = local.firewall_policy_name + resource_group_name = local.core_resource_group_name +} diff --git a/templates/shared_services/firewall/terraform/locals.tf b/templates/shared_services/firewall/terraform/locals.tf index 83762737da..4d50f511cd 100644 --- a/templates/shared_services/firewall/terraform/locals.tf +++ b/templates/shared_services/firewall/terraform/locals.tf @@ -1,11 +1,5 @@ locals { core_resource_group_name = "rg-${var.tre_id}" - firewall_name = "fw-${var.tre_id}" - firewall_diagnostic_categories_enabled = [ - "AZFWApplicationRule", - "AZFWNetworkRule", - "AZFWDnsProxy", - ] tre_shared_service_tags = { tre_id = var.tre_id tre_shared_service_id = var.tre_resource_id @@ -15,7 +9,4 @@ locals { api_driven_network_rule_collection = jsondecode(base64decode(var.api_driven_network_rule_collections_b64)) firewall_policy_name = "fw-policy-${var.tre_id}" - - default_firewall_sku = "Standard" - effective_firewall_sku = coalesce(var.firewall_sku, local.default_firewall_sku) } diff --git a/templates/shared_services/firewall/terraform/routetable.tf b/templates/shared_services/firewall/terraform/routetable.tf deleted file mode 100644 index f0e4388d9b..0000000000 --- a/templates/shared_services/firewall/terraform/routetable.tf +++ /dev/null @@ -1,89 +0,0 @@ -resource "azurerm_route_table" "rt" { - name = "rt-${var.tre_id}" - resource_group_name = local.core_resource_group_name - location = data.azurerm_resource_group.rg.location - bgp_route_propagation_enabled = true - tags = local.tre_shared_service_tags - - lifecycle { ignore_changes = [tags] } - - route { - name = "DefaultRoute" - address_prefix = "0.0.0.0/0" - next_hop_type = "VirtualAppliance" - next_hop_in_ip_address = azurerm_firewall.fw.ip_configuration[0].private_ip_address - } -} - -resource "azurerm_subnet_route_table_association" "rt_shared_subnet_association" { - subnet_id = data.azurerm_subnet.shared.id - route_table_id = azurerm_route_table.rt.id - - depends_on = [ - azurerm_firewall.fw, - azurerm_firewall_policy_rule_collection_group.core, - azurerm_firewall_policy_rule_collection_group.dynamic_network, - azurerm_firewall_policy_rule_collection_group.dynamic_application - ] -} - -resource "azurerm_subnet_route_table_association" "rt_resource_processor_subnet_association" { - subnet_id = data.azurerm_subnet.resource_processor.id - route_table_id = azurerm_route_table.rt.id - - # Not waiting for the rules will block traffic prematurally. - depends_on = [ - azurerm_firewall.fw, - azurerm_firewall_policy_rule_collection_group.core, - azurerm_firewall_policy_rule_collection_group.dynamic_network, - azurerm_firewall_policy_rule_collection_group.dynamic_application - ] -} - -resource "azurerm_subnet_route_table_association" "rt_web_app_subnet_association" { - subnet_id = data.azurerm_subnet.web_app.id - route_table_id = azurerm_route_table.rt.id - - depends_on = [ - azurerm_firewall.fw, - azurerm_firewall_policy_rule_collection_group.core, - azurerm_firewall_policy_rule_collection_group.dynamic_network, - azurerm_firewall_policy_rule_collection_group.dynamic_application - ] -} - -resource "azurerm_subnet_route_table_association" "rt_airlock_processor_subnet_association" { - subnet_id = data.azurerm_subnet.airlock_processor.id - route_table_id = azurerm_route_table.rt.id - - depends_on = [ - azurerm_firewall.fw, - azurerm_firewall_policy_rule_collection_group.core, - azurerm_firewall_policy_rule_collection_group.dynamic_network, - azurerm_firewall_policy_rule_collection_group.dynamic_application - ] -} - -resource "azurerm_subnet_route_table_association" "rt_airlock_storage_subnet_association" { - subnet_id = data.azurerm_subnet.airlock_storage.id - route_table_id = azurerm_route_table.rt.id - - depends_on = [ - azurerm_firewall.fw, - azurerm_firewall_policy_rule_collection_group.core, - azurerm_firewall_policy_rule_collection_group.dynamic_network, - azurerm_firewall_policy_rule_collection_group.dynamic_application - ] -} - -resource "azurerm_subnet_route_table_association" "rt_airlock_events_subnet_association" { - subnet_id = data.azurerm_subnet.airlock_events.id - route_table_id = azurerm_route_table.rt.id - - depends_on = [ - azurerm_firewall.fw, - azurerm_firewall_policy_rule_collection_group.core, - azurerm_firewall_policy_rule_collection_group.dynamic_network, - azurerm_firewall_policy_rule_collection_group.dynamic_application - ] -} diff --git a/templates/shared_services/firewall/terraform/rules.tf b/templates/shared_services/firewall/terraform/rules.tf index 53991a6ce1..6d7869aa15 100644 --- a/templates/shared_services/firewall/terraform/rules.tf +++ b/templates/shared_services/firewall/terraform/rules.tf @@ -1,206 +1,6 @@ -resource "azurerm_firewall_policy_rule_collection_group" "core" { - name = "rcg-core" - firewall_policy_id = azurerm_firewall_policy.root.id - priority = 500 - - network_rule_collection { - name = "nrc-general" - priority = 201 - action = "Allow" - - rule { - name = "time" - protocols = [ - "UDP" - ] - destination_addresses = [ - "*" - ] - destination_ports = [ - "123" - ] - source_addresses = [ - "*" - ] - } - } - - network_rule_collection { - name = "nrc-resource-processor-subnet" - priority = 202 - action = "Allow" - - rule { - name = "azure-services" - protocols = [ - "TCP" - ] - destination_addresses = [ - "AzureActiveDirectory", - "AzureResourceManager", - "AzureContainerRegistry", - "Storage", - "AzureKeyVault" - ] - destination_ports = [ - "443" - ] - source_ip_groups = [data.azurerm_ip_group.resource_processor.id] - } - } - - network_rule_collection { - name = "nrc-web-app-subnet" - priority = 203 - action = "Allow" - - rule { - name = "azure-services" - protocols = [ - "TCP" - ] - destination_addresses = [ - "AzureActiveDirectory", - "AzureContainerRegistry", - "AzureResourceManager" - ] - destination_ports = [ - "443" - ] - source_ip_groups = [data.azurerm_ip_group.web.id] - } - } - - application_rule_collection { - name = "arc-resource-processor-subnet" - priority = 301 - action = "Allow" - - rule { - name = "os-package-sources" - protocols { - port = "443" - type = "Https" - } - protocols { - port = "80" - type = "Http" - } - destination_fqdns = [ - "packages.microsoft.com", - "keyserver.ubuntu.com", - "api.snapcraft.io", - "azure.archive.ubuntu.com", - "security.ubuntu.com", - "entropy.ubuntu.com", - ] - source_ip_groups = [data.azurerm_ip_group.resource_processor.id] - } - - rule { - name = "docker-sources" - protocols { - port = "443" - type = "Https" - } - protocols { - port = "80" - type = "Http" - } - destination_fqdns = [ - "download.docker.com", - "registry-1.docker.io", - "auth.docker.io", - ] - source_ip_groups = [data.azurerm_ip_group.resource_processor.id] - } - # This rule is needed to support Gov Cloud. - # The az cli uses msal lib which requires access to this fqdn for authentication. - rule { - name = "microsoft-login" - protocols { - port = "443" - type = "Https" - } - destination_fqdns = [ - "login.microsoftonline.com", - ] - source_ip_groups = [data.azurerm_ip_group.resource_processor.id] - } - - - } - - application_rule_collection { - name = "arc-shared-subnet" - priority = 302 - action = "Allow" - - rule { - name = "nexus-bootstrap" - protocols { - port = "443" - type = "Https" - } - protocols { - port = "80" - type = "Http" - } - destination_fqdns = [ - "keyserver.ubuntu.com", - "packages.microsoft.com", - "download.docker.com", - "azure.archive.ubuntu.com" - ] - source_ip_groups = [data.azurerm_ip_group.shared.id] - } - } - - application_rule_collection { - name = "arc-web-app-subnet" - priority = 303 - action = "Allow" - - rule { - name = "microsoft-graph" - protocols { - port = "443" - type = "Https" - } - destination_fqdns = [ - var.microsoft_graph_fqdn - ] - source_ip_groups = [data.azurerm_ip_group.web.id] - } - } - - application_rule_collection { - name = "arc-airlock-processor-subnet" - priority = 304 - action = "Allow" - - rule { - name = "functions-runtime" - protocols { - port = "443" - type = "Https" - } - destination_fqdns = [ - "functionscdn.azureedge.net" - ] - source_ip_groups = [data.azurerm_ip_group.airlock_processor.id] - } - } - - depends_on = [ - azurerm_firewall.fw - ] -} - - resource "azurerm_firewall_policy_rule_collection_group" "dynamic_network" { name = "rcg-dynamic-network" - firewall_policy_id = azurerm_firewall_policy.root.id + firewall_policy_id = data.azurerm_firewall_policy.root.id priority = 510 dynamic "network_rule_collection" { @@ -216,7 +16,7 @@ resource "azurerm_firewall_policy_rule_collection_group" "dynamic_network" { content { name = rule.value.name - # description = rule.value.description + description = rule.value.description source_addresses = try(rule.value.source_addresses, []) source_ip_groups = concat( try(rule.value.source_ip_group_ids, []), @@ -231,15 +31,11 @@ resource "azurerm_firewall_policy_rule_collection_group" "dynamic_network" { } } } - - depends_on = [ - azurerm_firewall_policy_rule_collection_group.core - ] } resource "azurerm_firewall_policy_rule_collection_group" "dynamic_application" { name = "rcg-dynamic-application" - firewall_policy_id = azurerm_firewall_policy.root.id + firewall_policy_id = data.azurerm_firewall_policy.root.id priority = 520 dynamic "application_rule_collection" { diff --git a/templates/shared_services/firewall/terraform/variables.tf b/templates/shared_services/firewall/terraform/variables.tf index a1017e157f..9ac2916b11 100644 --- a/templates/shared_services/firewall/terraform/variables.tf +++ b/templates/shared_services/firewall/terraform/variables.tf @@ -3,11 +3,6 @@ variable "tre_id" { description = "Unique TRE ID" } -variable "microsoft_graph_fqdn" { - type = string - description = "Microsoft Graph FQDN" -} - variable "tre_resource_id" { type = string description = "Resource ID" @@ -22,8 +17,3 @@ variable "api_driven_network_rule_collections_b64" { type = string default = "W10=" #b64 for [] } - -variable "firewall_sku" { - type = string - default = "" -} From 49da7fc833f2be0c67beef956b18ffa76d33ed9c Mon Sep 17 00:00:00 2001 From: Tamir Kamara <26870601+tamirkamara@users.noreply.github.com> Date: Sun, 19 Jan 2025 06:22:00 +0000 Subject: [PATCH 2/8] Airlock function storage to use manage identity --- core/terraform/airlock/airlock_processor.tf | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/core/terraform/airlock/airlock_processor.tf b/core/terraform/airlock/airlock_processor.tf index 80a6968e97..a95bf54eaa 100644 --- a/core/terraform/airlock/airlock_processor.tf +++ b/core/terraform/airlock/airlock_processor.tf @@ -21,9 +21,8 @@ resource "azurerm_storage_account" "sa_airlock_processor_func_app" { allow_nested_items_to_be_public = false cross_tenant_replication_enabled = false local_user_enabled = false - # Function Host Storage doesn't seem to be able to use a User Managed ID, which is why we continue to use a key. - shared_access_key_enabled = true - tags = var.tre_core_tags + shared_access_key_enabled = false + tags = var.tre_core_tags dynamic "identity" { for_each = var.enable_cmk_encryption ? [1] : [] @@ -57,9 +56,7 @@ resource "azurerm_linux_function_app" "airlock_function_app" { ftp_publish_basic_authentication_enabled = false webdeploy_publish_basic_authentication_enabled = false storage_account_name = azurerm_storage_account.sa_airlock_processor_func_app.name - - # Function Host Storage doesn't seem to be able to use a User Managed ID, which is why we continue to use a key. - storage_account_access_key = azurerm_storage_account.sa_airlock_processor_func_app.primary_access_key + storage_uses_managed_identity = true tags = var.tre_core_tags @@ -86,6 +83,8 @@ resource "azurerm_linux_function_app" "airlock_function_app" { "TRE_ID" = var.tre_id "WEBSITE_CONTENTOVERVNET" = 1 "STORAGE_ENDPOINT_SUFFIX" = module.terraform_azurerm_environment_configuration.storage_suffix + "AzureWebJobsStorage__clientId" = azurerm_user_assigned_identity.airlock_id.client_id + "AzureWebJobsStorage__credential" = "managedidentity" } site_config { From 83d3f0781f1af4a2a57dcf599094d4c3b546c02e Mon Sep 17 00:00:00 2001 From: Tamir Kamara <26870601+tamirkamara@users.noreply.github.com> Date: Sun, 19 Jan 2025 06:23:27 +0000 Subject: [PATCH 3/8] version --- CHANGELOG.md | 1 + core/version.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72a55a77b0..bd8b90dffd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ ENHANCEMENTS: * Add option to force tunnel TRE's Firewall ([#4237](https://github.com/microsoft/AzureTRE/issues/4237)) * Add EventGrid diagnostics to identify airlock issues ([#4258](https://github.com/microsoft/AzureTRE/issues/4258)) * Surface the server-layout parameter of Guacamole [server-layout](https://guacamole.apache.org/doc/gug/configuring-guacamole.html#session-settings) ([#4234](https://github.com/microsoft/AzureTRE/issues/4234)) +* Airlock function host storage to use the user-assigned managed identity ([#TBD](https://github.com/microsoft/AzureTRE/issues/TBD)) BUG FIXES: * Update KeyVault references in API to use the version so Terraform cascades the update ([#4112](https://github.com/microsoft/AzureTRE/pull/4112)) diff --git a/core/version.txt b/core/version.txt index d0f18418d1..b663def5a3 100644 --- a/core/version.txt +++ b/core/version.txt @@ -1 +1 @@ -__version__ = "0.11.17" +__version__ = "0.11.18" From 5015aaeb57ef078dc7e21e12dbcef2f28065f3ee Mon Sep 17 00:00:00 2001 From: Tamir Kamara <26870601+tamirkamara@users.noreply.github.com> Date: Sun, 19 Jan 2025 06:27:12 +0000 Subject: [PATCH 4/8] changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd8b90dffd..df675c090e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,7 +35,7 @@ ENHANCEMENTS: * Add option to force tunnel TRE's Firewall ([#4237](https://github.com/microsoft/AzureTRE/issues/4237)) * Add EventGrid diagnostics to identify airlock issues ([#4258](https://github.com/microsoft/AzureTRE/issues/4258)) * Surface the server-layout parameter of Guacamole [server-layout](https://guacamole.apache.org/doc/gug/configuring-guacamole.html#session-settings) ([#4234](https://github.com/microsoft/AzureTRE/issues/4234)) -* Airlock function host storage to use the user-assigned managed identity ([#TBD](https://github.com/microsoft/AzureTRE/issues/TBD)) +* Airlock function host storage to use the user-assigned managed identity ([#4276](https://github.com/microsoft/AzureTRE/issues/4276)) BUG FIXES: * Update KeyVault references in API to use the version so Terraform cascades the update ([#4112](https://github.com/microsoft/AzureTRE/pull/4112)) From 92d47e787f8814cc492fe7474119a18c9d0f26b2 Mon Sep 17 00:00:00 2001 From: Tamir Kamara <26870601+tamirkamara@users.noreply.github.com> Date: Sun, 19 Jan 2025 12:46:04 +0000 Subject: [PATCH 5/8] storage permissions --- core/terraform/airlock/identity.tf | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/terraform/airlock/identity.tf b/core/terraform/airlock/identity.tf index 9711f19ab6..7f452ebdbb 100644 --- a/core/terraform/airlock/identity.tf +++ b/core/terraform/airlock/identity.tf @@ -52,3 +52,11 @@ resource "azurerm_role_assignment" "api_sa_data_contributor" { role_definition_name = "Storage Blob Data Contributor" principal_id = var.api_principal_id } + +# Permissions needed for the Function Host to work correctly. +resource "azurerm_role_assignment" "function_host_storage" { + for_each = toset(["Storage Account Contributor", "Storage Blob Data Owner", "Storage Queue Data Contributor"]) + scope = azurerm_storage_account.sa_airlock_processor_func_app.id + role_definition_name = each.value + principal_id = azurerm_user_assigned_identity.airlock_id.principal_id +} From 57a30dfb70a8194352ac99ad718eba899daf1a56 Mon Sep 17 00:00:00 2001 From: Marcus Robinson Date: Wed, 29 Jan 2025 09:43:34 +0000 Subject: [PATCH 6/8] Fix merge issues. --- core/terraform/firewall/variables.tf | 5 +++++ core/terraform/main.tf | 1 + core/terraform/routetable.tf | 25 +++++++++++++++++++++++++ core/terraform/variables.tf | 5 +++++ 4 files changed, 36 insertions(+) diff --git a/core/terraform/firewall/variables.tf b/core/terraform/firewall/variables.tf index a0550b503b..458bd0bf1e 100644 --- a/core/terraform/firewall/variables.tf +++ b/core/terraform/firewall/variables.tf @@ -26,6 +26,11 @@ variable "firewall_management_subnet_id" { description = "Subnet ID for the firewall management" } +variable "firewall_force_tunnel_ip" { + type = string + default = "" +} + variable "tre_core_tags" { type = map(string) description = "Tags to apply to all resources" diff --git a/core/terraform/main.tf b/core/terraform/main.tf index 6ddbd9d183..fd13096d8f 100644 --- a/core/terraform/main.tf +++ b/core/terraform/main.tf @@ -97,6 +97,7 @@ module "firewall" { tre_id = var.tre_id firewall_sku = var.firewall_sku firewall_subnet_id = module.network.azure_firewall_subnet_id + firewall_force_tunnel_ip = var.firewall_force_tunnel_ip location = var.location resource_group_name = azurerm_resource_group.core.name tre_core_tags = local.tre_core_tags diff --git a/core/terraform/routetable.tf b/core/terraform/routetable.tf index 06e467be84..c770fdb4cb 100644 --- a/core/terraform/routetable.tf +++ b/core/terraform/routetable.tf @@ -68,3 +68,28 @@ resource "azurerm_subnet_route_table_association" "rt_airlock_events_subnet_asso module.firewall ] } + + +resource "azurerm_route_table" "fw_tunnel_rt" { + count = var.firewall_force_tunnel_ip != "" ? 1 : 0 + name = "rt-fw-tunnel-${var.tre_id}" + resource_group_name = azurerm_resource_group.core.name + location = azurerm_resource_group.core.location + bgp_route_propagation_enabled = true + tags = local.tre_core_tags + lifecycle { ignore_changes = [tags] } + + route { + name = "ForceTunnelRoute" + address_prefix = "0.0.0.0/0" + next_hop_type = "VirtualAppliance" + next_hop_in_ip_address = var.firewall_force_tunnel_ip + } +} + +resource "azurerm_subnet_route_table_association" "rt_fw_tunnel_subnet_association" { + count = var.firewall_force_tunnel_ip != "" ? 1 : 0 + subnet_id = module.network.azure_firewall_subnet_id + route_table_id = azurerm_route_table.fw_tunnel_rt[0].id +} + diff --git a/core/terraform/variables.tf b/core/terraform/variables.tf index 1f1004d8bb..21985144be 100644 --- a/core/terraform/variables.tf +++ b/core/terraform/variables.tf @@ -186,6 +186,11 @@ variable "firewall_sku" { default = "" } +variable "firewall_force_tunnel_ip" { + type = string + default = "" +} + variable "app_gateway_sku" { description = "Application Gateway SKU" type = string From e51783540c7376fe81dc8fbe4c0e1a4752cf5f8a Mon Sep 17 00:00:00 2001 From: Marcus Robinson Date: Fri, 7 Feb 2025 20:51:15 +0000 Subject: [PATCH 7/8] remove forced tunneling from shared service --- Makefile | 3 +-- templates/shared_services/firewall/porter.yaml | 3 --- .../firewall/template_schema.json | 16 ---------------- 3 files changed, 1 insertion(+), 21 deletions(-) diff --git a/Makefile b/Makefile index 87d807a7dc..64a025568f 100644 --- a/Makefile +++ b/Makefile @@ -304,8 +304,7 @@ deploy-shared-service: firewall-install: . ${MAKEFILE_DIR}/devops/scripts/check_dependencies.sh env \ && $(MAKE) bundle-build bundle-publish bundle-register deploy-shared-service \ - DIR=${MAKEFILE_DIR}/templates/shared_services/firewall/ BUNDLE_TYPE=shared_service \ - PROPS="$${FIREWALL_SKU+--firewall_sku $${FIREWALL_SKU} }$${FIREWALL_FORCE_TUNNEL_IP+--firewall_force_tunnel_ip $${FIREWALL_FORCE_TUNNEL_IP} }" + DIR=${MAKEFILE_DIR}/templates/shared_services/firewall/ BUNDLE_TYPE=shared_service static-web-upload: $(call target_title, "Uploading to static website") \ diff --git a/templates/shared_services/firewall/porter.yaml b/templates/shared_services/firewall/porter.yaml index 2069138e5a..77bf4aeeb3 100644 --- a/templates/shared_services/firewall/porter.yaml +++ b/templates/shared_services/firewall/porter.yaml @@ -47,9 +47,6 @@ parameters: description: "Network rule collection array" - name: arm_environment type: string - - name: firewall_force_tunnel_ip - type: string - default: "" mixins: - terraform: diff --git a/templates/shared_services/firewall/template_schema.json b/templates/shared_services/firewall/template_schema.json index 23b0f8a526..c92f7544c8 100644 --- a/templates/shared_services/firewall/template_schema.json +++ b/templates/shared_services/firewall/template_schema.json @@ -6,22 +6,6 @@ "description": "Provides Firewall shared service", "required": [], "properties": { - "firewall_sku": { - "type": "string", - "title": "Firewall SKU", - "description": "The SKU that will be used when deploying The Firewall.", - "default": "Standard", - "enum": [ - "Basic", - "Standard", - "Premium" - ] - }, - "firewall_force_tunnel_ip": { - "type": "string", - "title": "Force Tunnel IP", - "description": "Optionally specify an IP address to forward all traffic to" - }, "rule_collections": { "$id": "#properties/rule_collections", "title": "application rule collections", From f9426237ebf2f914e73cecdc806036f974e7bdf9 Mon Sep 17 00:00:00 2001 From: Marcus Robinson Date: Fri, 7 Feb 2025 20:56:21 +0000 Subject: [PATCH 8/8] Fix linting --- core/terraform/routetable.tf | 8 ++++---- templates/shared_services/firewall/terraform/rules.tf | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/terraform/routetable.tf b/core/terraform/routetable.tf index c770fdb4cb..3901b521eb 100644 --- a/core/terraform/routetable.tf +++ b/core/terraform/routetable.tf @@ -1,8 +1,8 @@ resource "azurerm_route_table" "rt" { - name = "rt-${var.tre_id}" - resource_group_name = azurerm_resource_group.core.name - location = var.location - tags = local.tre_core_tags + name = "rt-${var.tre_id}" + resource_group_name = azurerm_resource_group.core.name + location = var.location + tags = local.tre_core_tags lifecycle { ignore_changes = [tags] } diff --git a/templates/shared_services/firewall/terraform/rules.tf b/templates/shared_services/firewall/terraform/rules.tf index 6d7869aa15..c9225f3c86 100644 --- a/templates/shared_services/firewall/terraform/rules.tf +++ b/templates/shared_services/firewall/terraform/rules.tf @@ -15,7 +15,7 @@ resource "azurerm_firewall_policy_rule_collection_group" "dynamic_network" { for_each = network_rule_collection.value.rules content { - name = rule.value.name + name = rule.value.name description = rule.value.description source_addresses = try(rule.value.source_addresses, []) source_ip_groups = concat(