From 790b0bacfc34fb46de0f114cfddc9026181fdefc Mon Sep 17 00:00:00 2001 From: kevindelmont <133667252+kevindelmont@users.noreply.github.com> Date: Thu, 31 Oct 2024 16:21:12 +0100 Subject: [PATCH] Added new Linux and Windows App service and Linux Function (#39) Added linux/windows apps and linux function new modules --- .github/workflows/standalone-scenarios.json | 5 + app_services.tf | 67 +++ .../111-windows-web-app/configuration.tfvars | 59 +++ .../configuration.tfvars | 106 ++++ .../113-linux-web-app/configuration.tfvars | 59 +++ .../configuration.tfvars | 106 ++++ .../configuration.tfvars | 55 ++ function_app.tf | 52 ++ locals.combined_objects.tf | 4 +- locals.tf | 3 + .../locals.dynamic_app_settings.tf | 27 + .../linux_function_app/locals.long_vars.tf | 7 + modules/webapps/linux_function_app/main.tf | 36 ++ .../linux_function_app/managed_identities.tf | 17 + modules/webapps/linux_function_app/module.tf | 445 ++++++++++++++++ modules/webapps/linux_function_app/output.tf | 94 ++++ .../linux_function_app/private_endpoint.tf | 18 + .../webapps/linux_function_app/variables.tf | 114 +++++ .../locals.dynamic_app_settings.tf | 27 + modules/webapps/linux_webapps/main.tf | 49 ++ .../linux_webapps/managed_identities.tf | 17 + modules/webapps/linux_webapps/module.tf | 483 ++++++++++++++++++ modules/webapps/linux_webapps/output.tf | 20 + .../webapps/linux_webapps/private_endpoint.tf | 16 + .../webapps/linux_webapps/storage_account.tf | 94 ++++ modules/webapps/linux_webapps/variables.tf | 97 ++++ .../webapps/windows_function_app/module.tf | 3 +- ...ivate._endpoint.tf => private_endpoint.tf} | 0 .../locals.dynamic_app_settings.tf | 27 + modules/webapps/windows_webapps/main.tf | 49 ++ .../windows_webapps/managed_identities.tf | 17 + modules/webapps/windows_webapps/module.tf | 472 +++++++++++++++++ modules/webapps/windows_webapps/output.tf | 20 + .../windows_webapps/private_endpoint.tf | 16 + .../windows_webapps/storage_account.tf | 94 ++++ modules/webapps/windows_webapps/variables.tf | 89 ++++ 36 files changed, 2860 insertions(+), 4 deletions(-) create mode 100644 examples/webapps/appservice/111-windows-web-app/configuration.tfvars create mode 100644 examples/webapps/appservice/112-windows-web-app-private/configuration.tfvars create mode 100644 examples/webapps/appservice/113-linux-web-app/configuration.tfvars create mode 100644 examples/webapps/appservice/114-linux-web-app-private/configuration.tfvars create mode 100644 examples/webapps/linux_function_app/102-function_app-linux/configuration.tfvars create mode 100644 modules/webapps/linux_function_app/locals.dynamic_app_settings.tf create mode 100644 modules/webapps/linux_function_app/locals.long_vars.tf create mode 100644 modules/webapps/linux_function_app/main.tf create mode 100644 modules/webapps/linux_function_app/managed_identities.tf create mode 100644 modules/webapps/linux_function_app/module.tf create mode 100644 modules/webapps/linux_function_app/output.tf create mode 100644 modules/webapps/linux_function_app/private_endpoint.tf create mode 100644 modules/webapps/linux_function_app/variables.tf create mode 100644 modules/webapps/linux_webapps/locals.dynamic_app_settings.tf create mode 100644 modules/webapps/linux_webapps/main.tf create mode 100644 modules/webapps/linux_webapps/managed_identities.tf create mode 100644 modules/webapps/linux_webapps/module.tf create mode 100644 modules/webapps/linux_webapps/output.tf create mode 100644 modules/webapps/linux_webapps/private_endpoint.tf create mode 100644 modules/webapps/linux_webapps/storage_account.tf create mode 100644 modules/webapps/linux_webapps/variables.tf rename modules/webapps/windows_function_app/{private._endpoint.tf => private_endpoint.tf} (100%) create mode 100644 modules/webapps/windows_webapps/locals.dynamic_app_settings.tf create mode 100644 modules/webapps/windows_webapps/main.tf create mode 100644 modules/webapps/windows_webapps/managed_identities.tf create mode 100644 modules/webapps/windows_webapps/module.tf create mode 100644 modules/webapps/windows_webapps/output.tf create mode 100644 modules/webapps/windows_webapps/private_endpoint.tf create mode 100644 modules/webapps/windows_webapps/storage_account.tf create mode 100644 modules/webapps/windows_webapps/variables.tf diff --git a/.github/workflows/standalone-scenarios.json b/.github/workflows/standalone-scenarios.json index 29fa2348a4..a39c586014 100644 --- a/.github/workflows/standalone-scenarios.json +++ b/.github/workflows/standalone-scenarios.json @@ -143,9 +143,14 @@ "webapps/appservice/107-appservice-private", "webapps/appservice/109-appservice-appgw", "webapps/appservice/110-appservice-auth", + "webapps/appservice/111-windows-web-app", + "webapps/appservice/112-windows-web-app-private", + "webapps/appservice/113-linux-web-app", + "webapps/appservice/114-linux-web-app-private", "webapps/function_app/101-function_app-private", "webapps/function_app/102-function_app-linux", "webapps/function_app/103-function_app-windows", + "webapps/windows_function_app/102-function_app-linux", "webapps/windows_function_app/103-function_app-windows", "webapps/static_site/101-simple-static-web-app" ] diff --git a/app_services.tf b/app_services.tf index 1e641216b5..d61b7dcc5a 100644 --- a/app_services.tf +++ b/app_services.tf @@ -49,3 +49,70 @@ resource "azurerm_app_service_virtual_network_swift_connection" "vnet_config" { app_service_id = module.app_services[each.key].id subnet_id = local.combined_objects_networking[try(each.value.vnet_integration.lz_key, local.client_config.landingzone_key)][each.value.vnet_integration.vnet_key].subnets[each.value.vnet_integration.subnet_key].id } + +module "windows_web_apps" { + source = "./modules/webapps/windows_webapps" + depends_on = [module.networking] + for_each = local.webapp.windows_web_apps + name = each.value.name + client_config = local.client_config + dynamic_app_settings = try(each.value.dynamic_app_settings, {}) + app_service_plan_id = can(each.value.app_service_plan_id) ? each.value.app_service_plan_id : local.combined_objects_app_service_plans[try(each.value.lz_key, local.client_config.landingzone_key)][each.value.app_service_plan_key].id + combined_objects = local.dynamic_app_settings_combined_objects + global_settings = local.global_settings + settings = each.value.settings + identity = try(each.value.identity, null) + app_settings = try(each.value.app_settings, null) + connection_string = try(each.value.connection_string, {}) + vnets = local.combined_objects_networking + virtual_subnets = local.combined_objects_virtual_subnets + subnet_id = can(each.value.subnet_id) || can(each.value.vnet_key) == false ? try(each.value.subnet_id, null) : local.combined_objects_networking[try(each.value.lz_key, local.client_config.landingzone_key)][each.value.vnet_key].subnets[each.value.subnet_key].id + remote_objects = { + subnets = try(local.combined_objects_networking[try(each.value.settings.lz_key, local.client_config.landingzone_key)][each.value.settings.vnet_key].subnets, null) + } + private_endpoints = try(each.value.private_endpoints, {}) + private_dns = local.combined_objects_private_dns + base_tags = local.global_settings.inherit_tags + resource_group = local.combined_objects_resource_groups[try(each.value.resource_group.lz_key, local.client_config.landingzone_key)][try(each.value.resource_group_key, each.value.resource_group.key)] + resource_group_name = can(each.value.resource_group.name) || can(each.value.resource_group_name) ? try(each.value.resource_group.name, each.value.resource_group_name) : null + location = try(local.global_settings.regions[each.value.region], null) + azuread_applications = local.combined_objects_azuread_applications + azuread_service_principal_passwords = local.combined_objects_azuread_service_principal_passwords +} + +output "windows_web_apps" { + value = module.windows_web_apps +} + +module "linux_web_apps" { + source = "./modules/webapps/linux_webapps" + depends_on = [module.networking] + for_each = local.webapp.linux_web_apps + name = each.value.name + client_config = local.client_config + dynamic_app_settings = try(each.value.dynamic_app_settings, {}) + app_service_plan_id = can(each.value.app_service_plan_id) ? each.value.app_service_plan_id : local.combined_objects_app_service_plans[try(each.value.lz_key, local.client_config.landingzone_key)][each.value.app_service_plan_key].id + combined_objects = local.dynamic_app_settings_combined_objects + global_settings = local.global_settings + settings = each.value.settings + identity = try(each.value.identity, null) + app_settings = try(each.value.app_settings, null) + connection_string = try(each.value.connection_string, {}) + vnets = local.combined_objects_networking + virtual_subnets = local.combined_objects_virtual_subnets + subnet_id = can(each.value.subnet_id) || can(each.value.vnet_key) == false ? try(each.value.subnet_id, null) : local.combined_objects_networking[try(each.value.lz_key, local.client_config.landingzone_key)][each.value.vnet_key].subnets[each.value.subnet_key].id + remote_objects = { + subnets = try(local.combined_objects_networking[try(each.value.settings.lz_key, local.client_config.landingzone_key)][each.value.settings.vnet_key].subnets, null) + } + private_endpoints = try(each.value.private_endpoints, {}) + private_dns = local.combined_objects_private_dns + base_tags = local.global_settings.inherit_tags + resource_group = local.combined_objects_resource_groups[try(each.value.resource_group.lz_key, local.client_config.landingzone_key)][try(each.value.resource_group_key, each.value.resource_group.key)] + resource_group_name = can(each.value.resource_group.name) || can(each.value.resource_group_name) ? try(each.value.resource_group.name, each.value.resource_group_name) : null + location = try(local.global_settings.regions[each.value.region], null) + azuread_applications = local.combined_objects_azuread_applications + azuread_service_principal_passwords = local.combined_objects_azuread_service_principal_passwords +} +output "linux_web_apps" { + value = module.linux_web_apps +} \ No newline at end of file diff --git a/examples/webapps/appservice/111-windows-web-app/configuration.tfvars b/examples/webapps/appservice/111-windows-web-app/configuration.tfvars new file mode 100644 index 0000000000..39aad97813 --- /dev/null +++ b/examples/webapps/appservice/111-windows-web-app/configuration.tfvars @@ -0,0 +1,59 @@ +global_settings = { + default_region = "region1" + regions = { + region1 = "australiaeast" + } + inherit_tags = true + tags = { + env = "to_be_set" + } +} + +resource_groups = { + windows_webapp_simple = { + name = "windows-webapp-simple" + region = "region1" + } +} + +# By default asp1 will inherit from the resource group location +app_service_plans = { + asp1 = { + resource_group_key = "windows_webapp_simple" + name = "asp-simple" + + sku = { + tier = "Standard" + size = "S1" + } + tags = { + env = "uat" + } + } +} + +windows_web_apps = { + windows_web_app_simple = { + name = "windows-web-app-simple" + resource_group_key = "windows_webapp_simple" + app_service_plan_key = "asp1" + + settings = { + https_only = "true" + always_on = "false" + + site_config = { + ftps_state = "Disabled" + always_on = "false" + + application_stack = { + current_stack = "dotnetcore" + dotnet_core_version = "v4.0" + } + } + } + app_settings = { + example_setting = "example-setting" + } + } +} \ No newline at end of file diff --git a/examples/webapps/appservice/112-windows-web-app-private/configuration.tfvars b/examples/webapps/appservice/112-windows-web-app-private/configuration.tfvars new file mode 100644 index 0000000000..ca599934c0 --- /dev/null +++ b/examples/webapps/appservice/112-windows-web-app-private/configuration.tfvars @@ -0,0 +1,106 @@ +global_settings = { + default_region = "region1" + regions = { + region1 = "australiaeast" + } + inherit_tags = true + tags = { + env = "to_be_set" + } +} + +resource_groups = { + windows_webapp_private = { + name = "windows-webapp-private" + region = "region1" + } +} + +# By default asp1 will inherit from the resource group location +app_service_plans = { + asp1 = { + resource_group_key = "windows_webapp_private" + name = "asp-simple" + + sku = { + tier = "Standard" + size = "S1" + } + tags = { + env = "uat" + } + } +} + + +vnets = { + demo_vnet = { + resource_group_key = "windows_webapp_private" + region = "region1" + vnet = { + name = "demo-vnet" + address_space = ["10.0.0.0/8"] + } + + subnets = { + demo_subnet = { + name = "demo-subnet" + cidr = ["10.0.1.0/24"] + #nsg_key = "" + enforce_private_link_endpoint_network_policies = true + delegation = { + name = "serverFarms" + service_delegation = "Microsoft.Web/serverFarms" + actions = [ + "Microsoft.Network/virtualNetworks/subnets/action" + ] + } + } + } +} + + +windows_web_apps = { + windows_web_app_private = { + name = "windows-web-app-private" + resource_group_key = "windows_webapp_private" + app_service_plan_key = "asp1" + + settings = { + https_only = "true" + always_on = "false" + public_network_access_enabled = false + virtual_network_subnet = { + subnet_key = "demo_subnet" + vnet_key = "demo_vnet" + #lz_key = "" + } + site_config = { + ftps_state = "Disabled" + always_on = "false" + + application_stack = { + current_stack = "dotnetcore" + dotnet_core_version = "v4.0" + } + ip_restriction_default_action = "Deny" + ip_restriction = [ + { + name = "demo-iprestriction" + priority = "100" + ip_address = "192.168.1.0/24" + # virtual_network_subnet = { + # subnet_key = "demo_subnet" + # vnet_key = "demo_vnet" + # lz_key = "" + # } + action = "Allow" + } + ] + } + } + app_settings = { + example_setting = "example-setting" + } + } +} \ No newline at end of file diff --git a/examples/webapps/appservice/113-linux-web-app/configuration.tfvars b/examples/webapps/appservice/113-linux-web-app/configuration.tfvars new file mode 100644 index 0000000000..e623bceb06 --- /dev/null +++ b/examples/webapps/appservice/113-linux-web-app/configuration.tfvars @@ -0,0 +1,59 @@ +global_settings = { + default_region = "region1" + regions = { + region1 = "australiaeast" + } + inherit_tags = true + tags = { + env = "to_be_set" + } +} + +resource_groups = { + linux_webapp_simple = { + name = "linux-webapp-simple" + region = "region1" + } +} + +# By default asp1 will inherit from the resource group location +app_service_plans = { + asp1 = { + resource_group_key = "linux_webapp_simple" + name = "asp-simple" + + sku = { + tier = "Standard" + size = "S1" + } + tags = { + env = "uat" + } + } +} + +linux_web_apps = { + linux_web_app_simple = { + name = "linux-web-app-simple" + resource_group_key = "linux_webapp_simple" + app_service_plan_key = "asp1" + + settings = { + https_only = "true" + always_on = "false" + + site_config = { + ftps_state = "Disabled" + always_on = "false" + + application_stack = { + java_server = "JAVA" + java_version = "11" + } + } + } + app_settings = { + example_setting = "example-setting" + } + } +} \ No newline at end of file diff --git a/examples/webapps/appservice/114-linux-web-app-private/configuration.tfvars b/examples/webapps/appservice/114-linux-web-app-private/configuration.tfvars new file mode 100644 index 0000000000..44a2df82bd --- /dev/null +++ b/examples/webapps/appservice/114-linux-web-app-private/configuration.tfvars @@ -0,0 +1,106 @@ +global_settings = { + default_region = "region1" + regions = { + region1 = "australiaeast" + } + inherit_tags = true + tags = { + env = "to_be_set" + } +} + +resource_groups = { + linux_webapp_private = { + name = "linux-webapp-private" + region = "region1" + } +} + +# By default asp1 will inherit from the resource group location +app_service_plans = { + asp1 = { + resource_group_key = "linux_webapp_private" + name = "asp-simple" + + sku = { + tier = "Standard" + size = "S1" + } + tags = { + env = "uat" + } + } +} + + +vnets = { + demo_vnet = { + resource_group_key = "linux_webapp_private" + region = "region1" + vnet = { + name = "demo-vnet" + address_space = ["10.0.0.0/8"] + } + + subnets = { + demo_subnet = { + name = "demo-subnet" + cidr = ["10.0.1.0/24"] + #nsg_key = "" + enforce_private_link_endpoint_network_policies = true + delegation = { + name = "serverFarms" + service_delegation = "Microsoft.Web/serverFarms" + actions = [ + "Microsoft.Network/virtualNetworks/subnets/action" + ] + } + } + } +} + + +linux_web_apps = { + linux_web_app_private = { + name = "linux-web-app-private" + resource_group_key = "linux_webapp_private" + app_service_plan_key = "asp1" + + settings = { + https_only = "true" + always_on = "false" + public_network_access_enabled = false + virtual_network_subnet = { + subnet_key = "demo_subnet" + vnet_key = "demo_vnet" + #lz_key = "" + } + site_config = { + ftps_state = "Disabled" + always_on = "false" + + application_stack = { + java_server = "JAVA" + java_version = "11" + } + ip_restriction_default_action = "Deny" + ip_restriction = [ + { + name = "demo-iprestriction" + priority = "100" + ip_address = "192.168.1.0/24" + # virtual_network_subnet = { + # subnet_key = "demo_subnet" + # vnet_key = "demo_vnet" + # lz_key = "" + # } + action = "Allow" + } + ] + } + } + app_settings = { + example_setting = "example-setting" + } + } +} \ No newline at end of file diff --git a/examples/webapps/linux_function_app/102-function_app-linux/configuration.tfvars b/examples/webapps/linux_function_app/102-function_app-linux/configuration.tfvars new file mode 100644 index 0000000000..b93afd88bc --- /dev/null +++ b/examples/webapps/linux_function_app/102-function_app-linux/configuration.tfvars @@ -0,0 +1,55 @@ +global_settings = { + default_region = "region1" + regions = { + region1 = "australiaeast" + } +} + +resource_groups = { + rg1 = { + name = "funapp-private" + region = "region1" + } +} + + +storage_accounts = { + sa1 = { + name = "functionsapptestsa" + resource_group_key = "rg1" + region = "region1" + account_tier = "Standard" + account_replication_type = "LRS" + } +} + +app_service_plans = { + asp1 = { + name = "azure-functions-test-service-plan" + resource_group_key = "rg1" + region = "region1" + kind = "functionapp" + reserved = true + + sku = { + tier = "Dynamic" + size = "Y1" + } + } +} + +linux_function_apps = { + faaps1 = { + name = "test-azure-functions" + resource_group_key = "rg1" + region = "region1" + app_service_plan_key = "asp1" + storage_account_key = "sa1" + settings = { + application_stack = { + node_version = "18" + } + site_config = {} + } + } +} \ No newline at end of file diff --git a/function_app.tf b/function_app.tf index 7830d3b5af..cd256f6128 100644 --- a/function_app.tf +++ b/function_app.tf @@ -100,6 +100,58 @@ data "azurerm_storage_account" "windows_function_apps" { if try(value.storage_account_key, null) != null } + name = local.combined_objects_storage_accounts[try(each.value.lz_key, local.client_config.landingzone_key)][each.value.storage_account_key].name + resource_group_name = local.combined_objects_storage_accounts[try(each.value.lz_key, local.client_config.landingzone_key)][each.value.storage_account_key].resource_group_name +} + +module "linux_function_apps" { + source = "./modules/webapps/linux_function_app" + depends_on = [module.networking] + for_each = local.webapp.linux_function_apps + + name = each.value.name + client_config = local.client_config + dynamic_app_settings = try(each.value.dynamic_app_settings, {}) + app_settings = try(each.value.app_settings, null) + combined_objects = local.dynamic_app_settings_combined_objects + service_plan_id = can(each.value.service_plan_id) || can(each.value.app_service_plan_key) == false ? try(each.value.app_service_plan_id, null) : local.combined_objects_app_service_plans[try(each.value.lz_key, local.client_config.landingzone_key)][each.value.app_service_plan_key].id + settings = each.value.settings + application_insight = try(each.value.application_insight_key, null) == null ? null : module.azurerm_application_insights[each.value.application_insight_key] + identity = try(each.value.identity, null) + connection_strings = try(each.value.connection_strings, {}) + storage_account_name = try(data.azurerm_storage_account.linux_function_apps[each.key].name, null) + storage_account_access_key = try(data.azurerm_storage_account.linux_function_apps[each.key].primary_access_key, null) + tags = try(each.value.tags, null) + # subnet_id = try( + # each.value.subnet_id, + # local.combined_objects_networking[try(each.value.settings.lz_key, local.client_config.landingzone_key)][each.value.settings.vnet_key].subnets[each.value.settings.subnet_key].id, + # null + # ) + global_settings = local.global_settings + private_dns = local.combined_objects_private_dns + private_endpoints = try(each.value.private_endpoints, {}) + vnets = local.combined_objects_networking + virtual_subnets = local.combined_objects_virtual_subnets + remote_objects = { + subnets = try(local.combined_objects_networking[try(each.value.settings.lz_key, local.client_config.landingzone_key)][each.value.settings.vnet_key].subnets, null) + } + + base_tags = local.global_settings.inherit_tags + resource_group = local.combined_objects_resource_groups[try(each.value.resource_group.lz_key, local.client_config.landingzone_key)][try(each.value.resource_group_key, each.value.resource_group.key)] + resource_group_name = can(each.value.resource_group.name) || can(each.value.resource_group_name) ? try(each.value.resource_group.name, each.value.resource_group_name) : null + location = try(local.global_settings.regions[each.value.region], null) +} + +output "linux_function_apps" { + value = module.linux_function_apps +} + +data "azurerm_storage_account" "linux_function_apps" { + for_each = { + for key, value in local.webapp.linux_function_apps : key => value + if try(value.storage_account_key, null) != null + } + name = local.combined_objects_storage_accounts[try(each.value.lz_key, local.client_config.landingzone_key)][each.value.storage_account_key].name resource_group_name = local.combined_objects_storage_accounts[try(each.value.lz_key, local.client_config.landingzone_key)][each.value.storage_account_key].resource_group_name } \ No newline at end of file diff --git a/locals.combined_objects.tf b/locals.combined_objects.tf index fb08cd36c5..eca0106078 100644 --- a/locals.combined_objects.tf +++ b/locals.combined_objects.tf @@ -14,7 +14,7 @@ locals { combined_objects_app_service_environments_all = merge(local.combined_objects_app_service_environments, local.combined_objects_app_service_environments_v3) combined_objects_app_service_environments_v3 = merge(tomap({ (local.client_config.landingzone_key) = merge(module.app_service_environments_v3, lookup(var.data_sources, "app_service_environments_v3", {})) }), lookup(var.remote_objects, "app_service_environments_v3", {})) combined_objects_app_service_plans = merge(tomap({ (local.client_config.landingzone_key) = merge(module.app_service_plans, lookup(var.data_sources, "app_service_plans", {})) }), lookup(var.remote_objects, "app_service_plans", {})) - combined_objects_app_services = merge(tomap({ (local.client_config.landingzone_key) = module.app_services }), lookup(var.remote_objects, "app_services", {}), lookup(var.data_sources, "app_services", {})) + combined_objects_app_services = merge(tomap({ (local.client_config.landingzone_key) = merge(module.app_services, module.linux_web_apps, module.windows_web_apps) }), lookup(var.remote_objects, "app_services", {}), lookup(var.data_sources, "app_services", {}), lookup(var.remote_objects, "linux_web_apps", {}), lookup(var.remote_objects, "windows_web_apps", {})) combined_objects_application_gateway_platforms = merge(tomap({ (local.client_config.landingzone_key) = module.application_gateway_platforms }), lookup(var.remote_objects, "application_gateway_platforms", {})) combined_objects_application_gateway_waf_policies = merge(tomap({ (local.client_config.landingzone_key) = module.application_gateway_waf_policies }), lookup(var.remote_objects, "application_gateway_waf_policies", {})) combined_objects_application_gateways = merge(tomap({ (local.client_config.landingzone_key) = module.application_gateways }), lookup(var.remote_objects, "application_gateways", {}), lookup(var.data_sources, "application_gateways", {})) @@ -84,7 +84,7 @@ locals { combined_objects_express_route_circuits = merge(tomap({ (local.client_config.landingzone_key) = module.express_route_circuits }), lookup(var.remote_objects, "express_route_circuits", {}), lookup(var.data_sources, "express_route_circuits", {})) combined_objects_front_door = merge(tomap({ (local.client_config.landingzone_key) = module.front_doors }), lookup(var.remote_objects, "front_doors", {})) combined_objects_front_door_waf_policies = merge(tomap({ (local.client_config.landingzone_key) = module.front_door_waf_policies }), lookup(var.remote_objects, "front_door_waf_policies", {})) - combined_objects_function_apps = merge(tomap({ (local.client_config.landingzone_key) = merge(module.function_apps, module.windows_function_apps) }), lookup(var.remote_objects, "function_apps", {}), lookup(var.remote_objects, "windows_function_apps", {})) + combined_objects_function_apps = merge(tomap({ (local.client_config.landingzone_key) = merge(module.function_apps, module.windows_function_apps, module.linux_function_apps) }), lookup(var.remote_objects, "function_apps", {}), lookup(var.remote_objects, "windows_function_apps", {}), lookup(var.remote_objects, "linux_function_apps", {})) combined_objects_image_definitions = merge(tomap({ (local.client_config.landingzone_key) = module.image_definitions }), lookup(var.remote_objects, "image_definitions", {})) combined_objects_integration_service_environment = merge(tomap({ (local.client_config.landingzone_key) = module.integration_service_environment }), lookup(var.remote_objects, "integration_service_environment", {})) combined_objects_iot_central_application = merge(tomap({ (local.client_config.landingzone_key) = module.iot_central_application }), lookup(var.remote_objects, "iot_central_application", {})) diff --git a/locals.tf b/locals.tf index 123f3aeeff..2d9c7a7c45 100644 --- a/locals.tf +++ b/locals.tf @@ -414,11 +414,14 @@ locals { app_service_environments_v3 = try(var.webapp.app_service_environments_v3, {}) app_service_plans = try(var.webapp.app_service_plans, {}) app_services = try(var.webapp.app_services, {}) + linux_web_apps = try(var.webapp.linux_web_apps, {}) + windows_web_apps = try(var.webapp.windows_web_apps, {}) azurerm_application_insights = try(var.webapp.azurerm_application_insights, {}) azurerm_application_insights_web_test = try(var.webapp.azurerm_application_insights_web_test, {}) azurerm_application_insights_standard_web_test = try(var.webapp.azurerm_application_insights_standard_web_test, {}) function_apps = try(var.webapp.function_apps, {}) windows_function_apps = try(var.webapp.windows_function_apps, {}) + linux_function_apps = try(var.webapp.linux_function_apps, {}) static_sites = try(var.webapp.static_sites, {}) } diff --git a/modules/webapps/linux_function_app/locals.dynamic_app_settings.tf b/modules/webapps/linux_function_app/locals.dynamic_app_settings.tf new file mode 100644 index 0000000000..3923f088d1 --- /dev/null +++ b/modules/webapps/linux_function_app/locals.dynamic_app_settings.tf @@ -0,0 +1,27 @@ +locals { + # Expected Variable: dynamic_app_settings = { + # "KEYVAULT_URL" = { + # keyvaults = { + # my_common_vault = { + # lz_key = "common_services_lz" + # attribute_key = "vault_uri" + # } + # } + # } + # } + dynamic_settings_to_process = { + for setting in + flatten( + [ + for setting_name, resources in var.dynamic_app_settings : [ + for resource_type_key, resource in resources : [ + for object_id_key, object_attributes in resource : { + key = setting_name + value = try(var.combined_objects[resource_type_key][object_attributes.lz_key][object_id_key][object_attributes.attribute_key], var.combined_objects[resource_type_key][var.client_config.landingzone_key][object_id_key][object_attributes.attribute_key]) + } + ] + ] + ] + ) : setting.key => setting.value + } +} diff --git a/modules/webapps/linux_function_app/locals.long_vars.tf b/modules/webapps/linux_function_app/locals.long_vars.tf new file mode 100644 index 0000000000..9b843a59d7 --- /dev/null +++ b/modules/webapps/linux_function_app/locals.long_vars.tf @@ -0,0 +1,7 @@ +locals { + ip_restrictions = try(var.settings.site_config.ip_restriction, []) + scm_ip_restrictions = try(var.settings.site_config.scm_ip_restriction, []) + site_config = try(var.settings.site_config, {}) + auth_settings_v2 = try(var.settings.auth_settings_v2, {}) + auth_settings = try(var.settings.auth_settings, {}) +} diff --git a/modules/webapps/linux_function_app/main.tf b/modules/webapps/linux_function_app/main.tf new file mode 100644 index 0000000000..d73efd3f57 --- /dev/null +++ b/modules/webapps/linux_function_app/main.tf @@ -0,0 +1,36 @@ +terraform { + required_providers { + azurecaf = { + source = "aztfmod/azurecaf" + } + } + +} + +locals { + module_tag = { + "module" = basename(abspath(path.module)) + } + tags = var.base_tags ? merge( + var.global_settings.tags, + try(var.resource_group.tags, null), + local.module_tag, + try(var.tags, null) + ) : merge( + local.module_tag, + try(var.tags, + null) + ) + + location = coalesce(var.location, var.resource_group.location) + resource_group_name = coalesce(var.resource_group_name, var.resource_group.name) + + app_settings = merge(try(var.app_settings, {}), try(local.dynamic_settings_to_process, {}), var.application_insight == null ? {} : + { + "APPINSIGHTS_INSTRUMENTATIONKEY" = var.application_insight.instrumentation_key, + "APPLICATIONINSIGHTS_CONNECTION_STRING" = var.application_insight.connection_string, + "ApplicationInsightsAgent_EXTENSION_VERSION" = "~2" + } + ) + +} diff --git a/modules/webapps/linux_function_app/managed_identities.tf b/modules/webapps/linux_function_app/managed_identities.tf new file mode 100644 index 0000000000..3c19ccb5d3 --- /dev/null +++ b/modules/webapps/linux_function_app/managed_identities.tf @@ -0,0 +1,17 @@ +locals { + managed_local_identities = flatten([ + for managed_identity_key in try(var.identity.managed_identity_keys, []) : [ + var.combined_objects.managed_identities[var.client_config.landingzone_key][managed_identity_key].id + ] + ]) + + managed_remote_identities = flatten([ + for keyvault_key, value in try(var.identity.remote, []) : [ + for managed_identity_key in value.managed_identity_keys : [ + var.combined_objects.managed_identities[keyvault_key][managed_identity_key].id + ] + ] + ]) + + managed_identities = concat(local.managed_local_identities, local.managed_remote_identities) +} diff --git a/modules/webapps/linux_function_app/module.tf b/modules/webapps/linux_function_app/module.tf new file mode 100644 index 0000000000..ae202ec467 --- /dev/null +++ b/modules/webapps/linux_function_app/module.tf @@ -0,0 +1,445 @@ + +resource "azurecaf_name" "plan" { + name = var.name + resource_type = "azurerm_function_app" + prefixes = var.global_settings.prefixes + random_length = var.global_settings.random_length + clean_input = true + passthrough = var.global_settings.passthrough + use_slug = var.global_settings.use_slug +} + +resource "azurerm_linux_function_app" "linux_function_app" { + #To avoid redeploy with existing customer + lifecycle { + ignore_changes = [name, virtual_network_subnet_id] + } + + location = local.location + name = azurecaf_name.plan.result + resource_group_name = local.resource_group_name + service_plan_id = var.service_plan_id + site_config { + always_on = lookup(local.site_config, "always_on", null) + api_definition_url = lookup(local.site_config, "api_definition_url", null) + api_management_api_id = lookup(local.site_config, "api_management_api_id", null) + app_command_line = lookup(local.site_config, "app_command_line", null) + app_scale_limit = lookup(local.site_config, "app_scale_limit", null) + application_insights_connection_string = lookup(local.site_config, "application_insights_connection_string", null) + application_insights_key = lookup(local.site_config, "application_insights_key", null) + dynamic "application_stack" { + for_each = lookup(local.site_config, "application_stack", {}) != {} ? [1] : [] + content { + #docker - (Optional) One or more docker blocks as defined below. + #dotnet_version - (Optional) The version of .NET to use. Possible values include 3.1, 6.0 and 7.0. + #use_dotnet_isolated_runtime - (Optional) Should the DotNet process use an isolated runtime. Defaults to false. + #java_version - (Optional) The Version of Java to use. Supported versions include 8, 11 & 17. + #node_version - (Optional) The version of Node to run. Possible values include 12, 14, 16 and 18. + #python_version - (Optional) The version of Python to run. Possible values are 3.11, 3.10, 3.9, 3.8 and 3.7. + #powershell_core_version - (Optional) The version of PowerShell Core to run. Possible values are 7, and 7.2. + #use_custom_runtime - (Optional) Should the Linux Function App use a custom runtime? + dynamic "docker" { + for_each = lookup(local.site_config.application_stack, "docker", {}) != {} ? [1] : [] + content { + registry_url = local.site_config.application_stack.docker.registry_url + image_name = local.site_config.application_stack.docker.image_name + image_tag = local.site_config.application_stack.docker.image_tag + registry_username = lookup(local.site_config.application_stack.docker, "registry_username", null) + registry_password = lookup(local.site_config.application_stack.docker, "registry_password", null) + } + + } + dotnet_version = lookup(local.site_config.application_stack, "dotnet_version", null) + use_dotnet_isolated_runtime = lookup(local.site_config.application_stack, "use_dotnet_isolated_runtime", null) + java_version = lookup(local.site_config.application_stack, "java_version", null) + node_version = lookup(local.site_config.application_stack, "node_version", null) + python_version = lookup(local.site_config.application_stack, "python_version", null) + powershell_core_version = lookup(local.site_config.application_stack, "powershell_core_version", null) + use_custom_runtime = lookup(local.site_config.application_stack, "use_custom_runtime", null) + } + } + dynamic "app_service_logs" { + for_each = lookup(local.site_config, "app_service_logs", {}) != {} ? [1] : [] + content { + disk_quota_mb = lookup(app_service_logs.value, "disk_quota_mb", 35) + retention_period_days = lookup(app_service_logs.value, "retention_period_days", null) + } + } + container_registry_managed_identity_client_id = lookup(local.site_config, "container_registry_managed_identity_client_id", null) + container_registry_use_managed_identity = lookup(local.site_config, "container_registry_use_managed_identity", null) + dynamic "cors" { + for_each = lookup(local.site_config, "cors", {}) != {} ? [1] : [] + + content { + allowed_origins = lookup(cors.value, "allowed_origins", null) + support_credentials = lookup(cors.value, "support_credentials", null) + } + } + default_documents = lookup(local.site_config, "default_documents", null) + elastic_instance_minimum = lookup(local.site_config, "elastic_instance_minimum", null) + ftps_state = lookup(local.site_config, "ftps_state", "Disabled") + health_check_path = lookup(local.site_config, "health_check_path", null) + health_check_eviction_time_in_min = lookup(local.site_config, "health_check_eviction_time_in_min", null) + http2_enabled = lookup(local.site_config, "http2_enabled", false) + dynamic "ip_restriction" { + for_each = length(local.ip_restrictions) > 0 ? local.ip_restrictions : [] + content { + action = lookup(ip_restriction.value, "action", null) + dynamic "headers" { + for_each = lookup(ip_restriction.value, "headers", {}) != {} ? [1] : [] + content { + #x_azure_fdid - (Optional) Specifies a list of Azure Front Door IDs. + #x_fd_health_probe - (Optional) Specifies if a Front Door Health Probe should be expected. The only possible value is 1. + #x_forwarded_for - (Optional) Specifies a list of addresses for which matching should be applied. Omitting this value means allow any. + #x_forwarded_host - (Optional) Specifies a list of Hosts for which matching should be applied. + + x_azure_fdid = lookup(ip_restriction.value, "x_azure_fdid", null) + x_fd_health_probe = lookup(ip_restriction.value, "x_fd_health_probe", null) + x_forwarded_for = lookup(ip_restriction.value, "x_forwarded_for", null) + x_forwarded_host = lookup(ip_restriction.value, "x_forwarded_host", null) + + + } + + + } + ip_address = lookup(ip_restriction.value, "ip_address", null) + name = lookup(ip_restriction.value, "name", null) + priority = lookup(ip_restriction.value, "priority", null) + service_tag = lookup(ip_restriction.value, "service_tag", null) + virtual_network_subnet_id = lookup(ip_restriction.value, "virtual_network_subnet_id", null) + } + } + load_balancing_mode = lookup(local.site_config, "load_balancing_mode", null) + managed_pipeline_mode = lookup(local.site_config, "managed_pipeline_mode", null) + minimum_tls_version = lookup(local.site_config, "minimum_tls_version", "1.2") + pre_warmed_instance_count = lookup(local.site_config, "pre_warmed_instance_count", null) + remote_debugging_enabled = lookup(local.site_config, "remote_debugging_enabled", false) + remote_debugging_version = lookup(local.site_config, "remote_debugging_version", null) + runtime_scale_monitoring_enabled = lookup(local.site_config, "runtime_scale_monitoring_enabled", null) + dynamic "scm_ip_restriction" { + for_each = length(local.scm_ip_restrictions) > 0 ? local.scm_ip_restrictions : [] + content { + action = lookup(scm_ip_restriction.value, "action", null) + dynamic "headers" { + for_each = lookup(scm_ip_restriction.value, "headers", {}) != {} ? [1] : [] + content { + #x_azure_fdid - (Optional) Specifies a list of Azure Front Door IDs. + #x_fd_health_probe - (Optional) Specifies if a Front Door Health Probe should be expected. The only possible value is 1. + #x_forwarded_for - (Optional) Specifies a list of addresses for which matching should be applied. Omitting this value means allow any. + #x_forwarded_host - (Optional) Specifies a list of Hosts for which matching should be applied. + + x_azure_fdid = lookup(ip_restriction.value, "x_azure_fdid", null) + x_fd_health_probe = lookup(ip_restriction.value, "x_fd_health_probe", null) + x_forwarded_for = lookup(ip_restriction.value, "x_forwarded_for", null) + x_forwarded_host = lookup(ip_restriction.value, "x_forwarded_host", null) + + } + + + } + ip_address = lookup(scm_ip_restriction.value, "ip_address", null) + name = lookup(scm_ip_restriction.value, "name", null) + priority = lookup(scm_ip_restriction.value, "priority", null) + service_tag = lookup(scm_ip_restriction.value, "service_tag", null) + virtual_network_subnet_id = lookup(scm_ip_restriction.value, "virtual_network_subnet_id", null) + } + } + scm_minimum_tls_version = lookup(local.site_config, "scm_minimum_tls_version", "1.2") + scm_use_main_ip_restriction = lookup(local.site_config, "scm_use_main_ip_restriction", null) + use_32_bit_worker = lookup(local.site_config, "use_32_bit_worker", true) + vnet_route_all_enabled = lookup(local.site_config, "vnet_route_all_enabled", false) + websockets_enabled = lookup(local.site_config, "websockets_enabled", false) + worker_count = lookup(local.site_config, "worker_count", null) + } + app_settings = local.app_settings + dynamic "auth_settings" { + for_each = lookup(var.settings, "auth_settings", {}) != {} ? [1] : [] + + content { + enabled = lookup(var.settings.auth_settings, "enabled", false) + additional_login_parameters = lookup(var.settings.auth_settings, "additional_login_parameters", null) + allowed_external_redirect_urls = lookup(var.settings.auth_settings, "allowed_external_redirect_urls", null) + default_provider = lookup(var.settings.auth_settings, "default_provider", null) + issuer = lookup(var.settings.auth_settings, "issuer", null) + runtime_version = lookup(var.settings.auth_settings, "runtime_version", null) + token_refresh_extension_hours = lookup(var.settings.auth_settings, "token_refresh_extension_hours", null) + token_store_enabled = lookup(var.settings.auth_settings, "token_store_enabled", null) + unauthenticated_client_action = lookup(var.settings.auth_settings, "unauthenticated_client_action", null) + + dynamic "active_directory" { + for_each = lookup(var.settings.auth_settings, "active_directory", {}) != {} ? [1] : [] + + content { + client_id = var.settings.auth_settings.active_directory.client_id + client_secret = lookup(var.settings.auth_settings.active_directory, "client_secret", null) + allowed_audiences = lookup(var.settings.auth_settings.active_directory, "allowed_audiences", null) + } + } + + dynamic "facebook" { + for_each = lookup(var.settings.auth_settings, "facebook", {}) != {} ? [1] : [] + + content { + app_id = var.settings.auth_settings.facebook.app_id + app_secret = var.settings.auth_settings.facebook.app_secret + oauth_scopes = lookup(var.settings.auth_settings.facebook, "oauth_scopes", null) + } + } + + dynamic "google" { + for_each = lookup(var.settings.auth_settings, "google", {}) != {} ? [1] : [] + + content { + client_id = var.settings.auth_settings.google.client_id + client_secret = var.settings.auth_settings.google.client_secret + oauth_scopes = lookup(var.settings.auth_settings.google, "oauth_scopes", null) + } + } + + dynamic "microsoft" { + for_each = lookup(var.settings.auth_settings, "microsoft", {}) != {} ? [1] : [] + + content { + client_id = var.settings.auth_settings.microsoft.client_id + client_secret = var.settings.auth_settings.microsoft.client_secret + oauth_scopes = lookup(var.settings.auth_settings.microsoft, "oauth_scopes", null) + } + } + + dynamic "twitter" { + for_each = lookup(var.settings.auth_settings, "twitter", {}) != {} ? [1] : [] + + content { + consumer_key = var.settings.auth_settings.twitter.consumer_key + consumer_secret = var.settings.auth_settings.twitter.consumer_secret + } + } + } + } + + dynamic "auth_settings_v2" { + for_each = lookup(var.settings, "auth_settings_v2", {}) != {} ? [1] : [] + content { + auth_enabled = lookup(local.auth_settings_v2, "auth_enabled", false) + runtime_version = lookup(local.auth_settings_v2, "runtime_version", "~1") + config_file_path = lookup(local.auth_settings_v2, "config_file_path", null) + require_authentication = lookup(local.auth_settings_v2, "require_authentication", null) + unauthenticated_action = lookup(local.auth_settings_v2, "unauthenticated_action", RedirectRoLoginPage) + default_provider = lookup(local.auth_settings_v2, "default_provider", null) + excluded_paths = lookup(local.auth_settings_v2, "excluded_paths", null) + require_https = lookup(local.auth_settings_v2, "require_https", null) + http_route_api_prefix = lookup(local.auth_settings_v2, "http_route_api_prefix", "/.auth") + forward_proxy_convention = lookup(local.auth_settings_v2, "forward_proxy_convention", null) + forward_proxy_custom_host_header_name = lookup(local.auth_settings_v2, "forward_proxy_custom_host_header_name", null) + forward_proxy_custom_scheme_header_name = lookup(local.auth_settings_v2, "forward_proxy_custom_scheme_header_name", null) + dynamic "apple_v2" { + for_each = lookup(local.auth_settings, "apple_v2", {}) != {} ? [1] : [] + content { + client_id = local.auth_settings_v2.apple_v2.client_id + client_secret_setting_name = local.auth_settings_v2.apple_v2.client_secret_setting_name + + } + } + + dynamic "active_directory_v2" { + for_each = lookup(local.auth_settings_v2, "active_directory_v2", {}) != {} ? [1] : [] + + content { + client_id = local.auth_settings_v2.active_directory_v2.client_id + tenant_auth_endpoint = local.auth_settings_v2.active_directory_v2.tenant_auth_endpoint + client_secret_setting_name = lookup(local.auth_settings_v2.active_directory_v2, "client_secret_setting_name", null) + client_secret_certificate_thumbprint = lookup(local.auth_settings_v2.active_directory_v2, "client_secret_certificate_thumbprint", null) + jwt_allowed_groups = lookup(local.auth_settings_v2.active_directory_v2, "jwt_allowed_groups", null) + jwt_allowed_client_applications = lookup(local.auth_settings_v2.active_directory_v2, "jwt_allowed_client_applications", null) + www_authentication_disabled = lookup(local.auth_settings_v2.active_directory_v2, "www_authentication_disabled", false) + allowed_groups = lookup(local.auth_settings_v2.active_directory_v2, "allowed_groups", null) + allowed_identities = lookup(local.auth_settings_v2.active_directory_v2, "allowed_identities", null) + allowed_applications = lookup(local.auth_settings_v2.active_directory_v2, "allowed_applications", null) + login_parameters = lookup(local.auth_settings_v2.active_directory_v2, "login_parameters", null) + allowed_audiences = lookup(local.auth_settings_v2.active_directory_v2, "allowed_audiences", null) + } + } + dynamic "azure_static_web_app_v2" { + for_each = lookup(var.settings.auth_settings_v2, "azure_static_web_app_v2", {}) != {} ? [1] : [] + content { + client_id = local.auth_settings_v2.azure_static_web_app_v2.client_id + } + } + + dynamic "custom_oidc_v2" { + for_each = lookup(local.auth_settings_v2, "custom_oidc_v2", {}) != {} ? [1] : [] + content { + name = local.auth_settings_v2.custom_oidc_v2.name + client_id = local.auth_settings_v2.custom_oidc_v2.client_id + openid_configuration_endpoint = local.auth_settings_v2.custom_oidc_v2.openid_configuration_endpoint + name_claim_type = lookup(local.auth_settings_v2.custom_oidc_v2, "name_claim_type", null) + scopes = lookup(local.auth_settings_v2.custom_oidc_v2, "scopes", null) + client_credential_method = lookup(local.auth_settings_v2.custom_oidc_v2, "client_credential_method", null) + client_secret_setting_name = lookup(local.auth_settings_v2.custom_oidc_v2, "client_secret_setting_name", null) + authorisation_endpoint = lookup(local.auth_settings_v2.custom_oidc_v2, "authorisation_endpoint", null) + token_endpoint = lookup(local.auth_settings_v2.custom_oidc_v2, "token_endpoint", null) + issuer_endpoint = lookup(local.auth_settings_v2.custom_oidc_v2, "issuer_endpoint", null) + certification_uri = lookup(local.auth_settings_v2.custom_oidc_v2, "certification_uri", null) + + } + + } + + dynamic "facebook_v2" { + for_each = lookup(local.auth_settings_v2, "facebook_v2", {}) != {} ? [1] : [] + + content { + app_id = local.auth_settings_v2.facebook_v2.app_id + app_secret_setting_name = local.auth_settings_v2.facebook_v2.app_secret_setting_name + graph_api_version = lookup(local.auth_settings_v2.facebook_v2, "graph_api_version", null) + login_scopes = lookup(local.auth_settings_v2.facebook_v2, "login_scopes", null) + } + } + + dynamic "github_v2" { + for_each = lookup(local.auth_settings_v2, "github_v2", {}) != {} ? [1] : [] + content { + client_id = local.auth_settings_v2.github_v2.client_id + client_secret_setting_name = local.auth_settings_v2.github_v2.client_secret_setting_name + login_scopes = lookup(local.auth_settings_v2.github_v2, "login_scopes", null) + } + } + + dynamic "google_v2" { + for_each = lookup(local.auth_settings_v2, "google_v2", {}) != {} ? [1] : [] + content { + client_id = local.auth_settings_v2.google_v2.client_id + client_secret_setting_name = local.auth_settings_v2.google_v2.client_secret_setting_name + allowed_audiences = lookup(local.auth_settings_v2.google_v2, "allowed_audiences", null) + login_scopes = lookup(local.auth_settings_v2.google_v2, "login_scopes", null) + } + } + + dynamic "microsoft_v2" { + for_each = lookup(local.auth_settings_v2, "microsoft_v2", {}) != {} ? [1] : [] + content { + client_id = local.auth_settings_v2.microsoft_v2.client_id + client_secret_setting_name = local.auth_settings_v2.microsoft_v2.client_secret_setting_name + allowed_audiences = lookup(local.auth_settings_v2.microsoft_v2, "allowed_audiences", null) + login_scopes = lookup(local.auth_settings_v2.microsoft_v2, "login_scopes", null) + } + } + + + + dynamic "twitter_v2" { + for_each = lookup(local.auth_settings_v2, "twitter_v2", {}) != {} ? [1] : [] + content { + consumer_key = local.auth_settings_v2.twitter_v2.consumer_key + consumer_secret_setting_name = local.auth_settings_v2.twitter_v2.consumer_secret_setting_name + } + } + + dynamic "login" { + for_each = lookup(local.auth_settings_v2, "login", {}) != {} ? [1] : [] + content { + logout_endpoint = lookup(local.auth_settings_v2.login, "logout_endpoint", null) + token_store_enabled = lookup(local.auth_settings_v2.login, "token_store_enabled", false) + token_refresh_extension_time = lookup(local.auth_settings_v2.login, "token_refresh_extension_time", null) + token_store_path = lookup(local.auth_settings_v2.login, "token_store_path", null) + token_store_sas_setting_name = lookup(local.auth_settings_v2.login, "token_store_sas_setting_name", null) + preserve_url_fragments_for_logins = lookup(local.auth_settings_v2.login, "preserve_url_fragments_for_logins", false) + allowed_external_redirect_urls = lookup(local.auth_settings_v2.login, "allowed_external_redirect_urls", null) + cookie_expiration_convention = lookup(local.auth_settings_v2.login, "cookie_expiration_convention", "FixedTime") + cookie_expiration_time = lookup(local.auth_settings_v2.login, "cookie_expiration_time", "08:00:00") + validate_nonce = lookup(local.auth_settings_v2.login, "validate_nonce", true) + nonce_expiration_time = lookup(local.auth_settings_v2.login, "nonce_expiration_time", "00:05:00") + } + + } + } + + } + dynamic "backup" { + for_each = lookup(var.settings, "backup", {}) != {} ? [1] : [] + + content { + name = var.settings.backup.name + storage_account_url = var.settings.backup.storage_account_url + enabled = lookup(var.settings.backup, "enabled", true) + + dynamic "schedule" { + for_each = try(var.settings.backup.schedule, {}) + + content { + frequency_interval = lookup(schedule.value, "frequency_interval", null) + frequency_unit = lookup(schedule.value, "frequency_unit", null) + keep_at_least_one_backup = lookup(schedule.value, "keep_at_least_one_backup", null) + retention_period_days = lookup(schedule.value, "retention_period_days", 30) + start_time = lookup(schedule.value, "start_time", null) + } + } + } + } + builtin_logging_enabled = try(var.settings.builtin_logging_enabled, true) + client_certificate_enabled = try(var.settings.client_certificate_enabled, null) + client_certificate_mode = try(var.settings.client_certificate_mode, null) + client_certificate_exclusion_paths = try(var.settings.client_certificate_exclusion_paths, null) + # Create connection strings. + dynamic "connection_string" { + for_each = var.connection_strings + content { + name = connection_string.value.name + type = connection_string.value.type + value = connection_string.value.value + } + } + daily_memory_time_quota = try(var.settings.daily_memory_time_quota, 0) + enabled = try(var.settings.enabled, null) + content_share_force_disabled = try(var.settings.content_share_force_disabled, null) + functions_extension_version = try(var.settings.functions_extension_version, "~4") + https_only = try(var.settings.https_only, false) + public_network_access_enabled = try(var.settings.public_network_access_enabled, null) + dynamic "identity" { + for_each = try(var.identity, null) == null ? [] : [1] + content { + type = var.identity.type + identity_ids = lower(var.identity.type) == "userassigned" ? local.managed_identities : null + } + } + + key_vault_reference_identity_id = can(var.settings.key_vault_reference_identity.key) ? var.combined_objects.managed_identities[try(var.settings.identity.lz_key, var.client_config.landingzone_key)][var.settings.key_vault_reference_identity.key].id : try(var.settings.key_vault_reference_identity.id, null) + dynamic "storage_account" { + for_each = lookup(var.settings, "storage_account", {}) + content { + access_key = var.settings.storage_account.access_key + account_name = var.settings.storage_account.account_name + name = var.settings.storage_account.name + share_name = var.settings.storage_account.share_name + type = var.settings.storage_account.type + mount_path = lookup(var.settings.storage_account.mount_path, null) + } + } + # Create a variable to hold the list of app setting names that the Linux Function App will not swap between Slots when a swap operation is triggered. + dynamic "sticky_settings" { + for_each = lookup(var.settings, "sticky_settings", {}) != {} ? [1] : [] + content { + app_setting_names = lookup(var.settings.sticky_settings, "app_setting_names", null) + connection_string_names = lookup(var.settings.sticky_settings, "connection_string_names", null) + } + } + + storage_account_access_key = try(var.storage_account_access_key, null) + storage_account_name = try(var.storage_account_name, null) + storage_key_vault_secret_id = try(var.settings.storage_key_vault_secret_id, null) + storage_uses_managed_identity = try(var.settings.storage_uses_managed_identity, null) + tags = merge(local.tags, try(var.settings.tags, {})) + # virtual_network_subnet_id = try(var.settings.virtual_network_subnet_id, null) + zip_deploy_file = try(var.settings.zip_deploy_file, null) +} + +resource "azurerm_app_service_virtual_network_swift_connection" "vnet_config" { + depends_on = [azurerm_linux_function_app.linux_function_app] + count = lookup(var.settings, "subnet_key", null) == null && lookup(var.settings, "subnet_id", null) == null && try(var.settings.virtual_network_subnet_id, null) == null ? 0 : 1 + app_service_id = azurerm_linux_function_app.linux_function_app.id + subnet_id = coalesce( + try(var.remote_objects.subnets[var.settings.subnet_key].id, null), + try(var.settings.subnet_id, null) + ) +} diff --git a/modules/webapps/linux_function_app/output.tf b/modules/webapps/linux_function_app/output.tf new file mode 100644 index 0000000000..eb0d41ded2 --- /dev/null +++ b/modules/webapps/linux_function_app/output.tf @@ -0,0 +1,94 @@ +/* +id - The ID of the Linux Function App. + +custom_domain_verification_id - The identifier used by App Service to perform domain ownership verification via DNS TXT record. + +default_hostname - The default hostname of the Linux Function App. + +hosting_environment_id - The ID of the App Service Environment used by Function App. + +identity - An identity block as defined below. + +kind - The Kind value for this Linux Function App. + +outbound_ip_address_list - A list of outbound IP addresses. For example ["52.23.25.3", "52.143.43.12"] + +outbound_ip_addresses - A comma separated list of outbound IP addresses as a string. For example 52.23.25.3,52.143.43.12. + +possible_outbound_ip_address_list - A list of possible outbound IP addresses, not all of which are necessarily in use. This is a superset of outbound_ip_address_list. For example ["52.23.25.3", "52.143.43.12"]. + +possible_outbound_ip_addresses - A comma separated list of possible outbound IP addresses as a string. For example 52.23.25.3,52.143.43.12,52.143.43.17. This is a superset of outbound_ip_addresses. + +site_credential - A site_credential block as defined below. +*/ +output "id" { + value = azurerm_linux_function_app.linux_function_app.id + description = "The ID of the Linux Function App." +} + +output "custom_domain_verification_id" { + value = azurerm_linux_function_app.linux_function_app.custom_domain_verification_id + description = "The identifier used by App Service to perform domain ownership verification via DNS TXT record." +} + +output "default_hostname" { + value = azurerm_linux_function_app.linux_function_app.default_hostname + description = "The default hostname of the Linux Function App." +} + +output "hosting_environment_id" { + value = azurerm_linux_function_app.linux_function_app.hosting_environment_id + description = "The ID of the App Service Environment used by Function App." +} + + +output "identity" { + value = azurerm_linux_function_app.linux_function_app.identity + description = "An identity block as defined below." +} + +output "kind" { + value = azurerm_linux_function_app.linux_function_app.kind + description = "The Kind value for this Linux Function App." +} + +output "outbound_ip_address_list" { + value = azurerm_linux_function_app.linux_function_app.outbound_ip_address_list + description = "A list of outbound IP addresses" +} + +output "outbound_ip_addresses" { + value = azurerm_linux_function_app.linux_function_app.outbound_ip_addresses + description = "A comma separated list of outbound IP addresses as a string" +} + +output "possible_outbound_ip_address_list" { + value = azurerm_linux_function_app.linux_function_app.possible_outbound_ip_address_list + description = "A list of possible outbound IP addresses, not all of which are necessarily in use. This is a superset of outbound_ip_address_list." +} + +output "possible_outbound_ip_addresses" { + value = azurerm_linux_function_app.linux_function_app.possible_outbound_ip_addresses + description = "A comma separated list of possible outbound IP addresses as a string. This is a superset of outbound_ip_addresses." +} + +output "principal_id" { + description = "The Principal ID associated with this Managed Service Identity." + value = try(azurerm_linux_function_app.linux_function_app.identity[0].principal_id, null) +} + +output "tenant_id" { + description = "The Tenant ID associated with this Managed Service Identity." + value = try(azurerm_linux_function_app.linux_function_app.identity[0].tenant_id, null) +} + +output "site_credential_name" { + description = "The Site Credentials Username used for publishing." + value = azurerm_linux_function_app.linux_function_app.site_credential[0].name +} + +output "site_credential_password" { + description = "The Site Credentials Password used for publishing." + value = azurerm_linux_function_app.linux_function_app.site_credential[0].password + sensitive = true +} diff --git a/modules/webapps/linux_function_app/private_endpoint.tf b/modules/webapps/linux_function_app/private_endpoint.tf new file mode 100644 index 0000000000..4e33d82010 --- /dev/null +++ b/modules/webapps/linux_function_app/private_endpoint.tf @@ -0,0 +1,18 @@ +module "private_endpoint" { + source = "../../networking/private_endpoint" + for_each = var.private_endpoints + + resource_id = azurerm_linux_function_app.linux_function_app.id + name = each.value.name + location = local.location + resource_group_name = local.resource_group_name + subnet_id = can(each.value.subnet_id) || can(each.value.virtual_subnet_key) ? try(each.value.subnet_id, var.virtual_subnets[try(each.value.lz_key, var.client_config.landingzone_key)][each.value.virtual_subnet_key].id) : var.vnets[try(each.value.lz_key, var.client_config.landingzone_key)][each.value.vnet_key].subnets[each.value.subnet_key].id + settings = each.value + global_settings = var.global_settings + tags = local.tags + base_tags = var.base_tags + private_dns = var.private_dns + client_config = var.client_config +} + + diff --git a/modules/webapps/linux_function_app/variables.tf b/modules/webapps/linux_function_app/variables.tf new file mode 100644 index 0000000000..a827c4e3ab --- /dev/null +++ b/modules/webapps/linux_function_app/variables.tf @@ -0,0 +1,114 @@ +variable "name" { + description = "The name of the resource" + +} + +variable "global_settings" { + description = "Global settings for the Azure resources" +} + +variable "service_plan_id" { + description = "The ID of the Service Plan" + +} + +variable "settings" { + description = "Settings for the Azure Function App" + +} + +variable "location" { + description = "The location of the Azure resources" + +} + +variable "resource_group_name" { + description = "The name of the resource group in which the resources will be created" + +} + +variable "resource_group" { + description = "The resource group object in which the resources will be created" +} + +variable "tags" { + description = "A mapping of tags to assign to the resource" + +} + +variable "app_settings" { + description = "App settings for the Azure Function App" + +} + +variable "connection_strings" { + description = "Connection strings for the Azure Function App" + +} + +variable "identity" { + description = "Managed Service Identity for the Azure Function App" + +} + + +variable "combined_objects" { + description = "Combined objects for the Azure Function App" +} + +variable "client_config" { + description = "Client configuration for the Azure Function App" + +} + + +variable "application_insight" { + description = "Application Insight for the Azure Function App" + +} + +variable "private_endpoints" { + description = "Private endpoints for the Azure Function App" + +} + +variable "base_tags" { + description = "Base tags for the resource to be inherited from the resource group." + type = bool +} + +variable "remote_objects" { + default = null +} + +variable "virtual_subnets" { + description = "Map of virtual_subnets objects" + default = {} + nullable = false +} + +variable "private_dns" { + description = "Map of private_dns objects" + default = {} + nullable = false +} + +variable "vnets" { + description = "Map of vnets objects" + default = {} + nullable = false +} + +variable "storage_account_name" { + description = "The name of the storage account" + +} + +variable "storage_account_access_key" { + description = "The access key of the storage account" + +} + +variable "dynamic_app_settings" { + default = {} +} \ No newline at end of file diff --git a/modules/webapps/linux_webapps/locals.dynamic_app_settings.tf b/modules/webapps/linux_webapps/locals.dynamic_app_settings.tf new file mode 100644 index 0000000000..3923f088d1 --- /dev/null +++ b/modules/webapps/linux_webapps/locals.dynamic_app_settings.tf @@ -0,0 +1,27 @@ +locals { + # Expected Variable: dynamic_app_settings = { + # "KEYVAULT_URL" = { + # keyvaults = { + # my_common_vault = { + # lz_key = "common_services_lz" + # attribute_key = "vault_uri" + # } + # } + # } + # } + dynamic_settings_to_process = { + for setting in + flatten( + [ + for setting_name, resources in var.dynamic_app_settings : [ + for resource_type_key, resource in resources : [ + for object_id_key, object_attributes in resource : { + key = setting_name + value = try(var.combined_objects[resource_type_key][object_attributes.lz_key][object_id_key][object_attributes.attribute_key], var.combined_objects[resource_type_key][var.client_config.landingzone_key][object_id_key][object_attributes.attribute_key]) + } + ] + ] + ] + ) : setting.key => setting.value + } +} diff --git a/modules/webapps/linux_webapps/main.tf b/modules/webapps/linux_webapps/main.tf new file mode 100644 index 0000000000..63c646298e --- /dev/null +++ b/modules/webapps/linux_webapps/main.tf @@ -0,0 +1,49 @@ +terraform { + required_providers { + azurecaf = { + source = "aztfmod/azurecaf" + } + # azurerm = { + # source = "hashicorp/azurerm" + # version = "~> 3.97.0" + # } + } + +} + +locals { + module_tag = { + "module" = basename(abspath(path.module)) + } + tags = var.base_tags ? merge( + var.global_settings.tags, + try(var.resource_group.tags, null), + local.module_tag, + try(var.settings.tags, null) + ) : merge( + local.module_tag, + try(var.settings.tags, + null) + ) + + app_settings = merge( + var.application_insight == null ? {} : { + "APPINSIGHTS_INSTRUMENTATIONKEY" = var.application_insight.instrumentation_key, + "APPLICATIONINSIGHTS_CONNECTION_STRING" = var.application_insight.connection_string, + "ApplicationInsightsAgent_EXTENSION_VERSION" = "~2" + }, + try(var.app_settings, {}), + try(local.dynamic_settings_to_process, {}) + ) + + location = coalesce(var.location, var.resource_group.location) + resource_group_name = coalesce(var.resource_group_name, var.resource_group.name) + backup_storage_account = can(var.settings.backup) ? var.storage_accounts[try(var.settings.backup.lz_key, var.client_config.landingzone_key)][var.settings.backup.storage_account_key] : null + backup_sas_url = can(var.settings.backup) ? "${local.backup_storage_account.primary_blob_endpoint}${local.backup_storage_account.containers[var.settings.backup.container_key].name}${data.azurerm_storage_account_blob_container_sas.backup[0].sas}" : null + + logs_storage_account = can(var.settings.logs) ? var.storage_accounts[try(var.settings.logs.lz_key, var.client_config.landingzone_key)][var.settings.logs.storage_account_key] : null + logs_sas_url = can(var.settings.logs) ? "${local.logs_storage_account.primary_blob_endpoint}${local.logs_storage_account.containers[var.settings.logs.container_key].name}${data.azurerm_storage_account_blob_container_sas.logs[0].sas}" : null + + http_logs_storage_account = can(var.settings.logs.http_logs) ? var.storage_accounts[try(var.settings.logs.http_logs.lz_key, var.client_config.landingzone_key)][var.settings.logs.http_logs.storage_account_key] : null + http_logs_sas_url = can(var.settings.logs.http_logs) ? "${local.http_logs_storage_account.primary_blob_endpoint}${local.http_logs_storage_account.containers[var.settings.logs.http_logs.container_key].name}${data.azurerm_storage_account_blob_container_sas.http_logs[0].sas}" : null +} diff --git a/modules/webapps/linux_webapps/managed_identities.tf b/modules/webapps/linux_webapps/managed_identities.tf new file mode 100644 index 0000000000..cb18c82023 --- /dev/null +++ b/modules/webapps/linux_webapps/managed_identities.tf @@ -0,0 +1,17 @@ +locals { + managed_local_identities = flatten([ + for managed_identity_key in try(var.identity.managed_identity_keys, []) : [ + var.combined_objects.managed_identities[var.client_config.landingzone_key][managed_identity_key].id + ] + ]) + + managed_remote_identities = flatten([ + for keyvault_key, value in try(var.identity.remote, []) : [ + for managed_identity_key in value.managed_identity_keys : [ + var.combined_objects.managed_identities[keyvault_key][managed_identity_key].id + ] + ] + ]) + + managed_identities = concat(local.managed_local_identities, local.managed_remote_identities) +} \ No newline at end of file diff --git a/modules/webapps/linux_webapps/module.tf b/modules/webapps/linux_webapps/module.tf new file mode 100644 index 0000000000..abd84c28cd --- /dev/null +++ b/modules/webapps/linux_webapps/module.tf @@ -0,0 +1,483 @@ + +resource "azurecaf_name" "linux_web_apps" { + name = var.name + resource_type = "azurerm_app_service" + prefixes = var.global_settings.prefixes + random_length = var.global_settings.random_length + clean_input = true + passthrough = var.global_settings.passthrough + use_slug = var.global_settings.use_slug +} + + +resource "azurerm_linux_web_app" "linux_web_apps" { + name = azurecaf_name.linux_web_apps.result + location = local.location + resource_group_name = local.resource_group_name + service_plan_id = var.app_service_plan_id + tags = merge(local.tags, try(var.settings.tags, {})) + client_affinity_enabled = lookup(var.settings, "client_affinity_enabled", null) + client_certificate_enabled = lookup(var.settings, "client_certificate_enabled", null) + client_certificate_mode = lookup(var.settings, "client_certificate_mode", null) + client_certificate_exclusion_paths = lookup(var.settings, "client_certificate_mode", null) + enabled = lookup(var.settings, "enabled", true) + https_only = lookup(var.settings, "https_only", null) + public_network_access_enabled = lookup(var.settings, "public_network_access_enabled", null) + + key_vault_reference_identity_id = can(var.settings.key_vault_reference_identity.key) ? var.combined_objects.managed_identities[try(var.settings.identity.lz_key, var.client_config.landingzone_key)][var.settings.key_vault_reference_identity.key].id : try(var.settings.key_vault_reference_identity.id, null) + + dynamic "identity" { + for_each = try(var.identity, null) == null ? [] : [1] + + content { + type = var.identity.type + identity_ids = lower(var.identity.type) == "userassigned" ? local.managed_identities : null + } + } + + + dynamic "site_config" { + for_each = lookup(var.settings, "site_config", {}) != {} ? [1] : [] + + content { + always_on = lookup(var.settings.site_config, "always_on", false) + api_management_api_id = lookup(var.settings.site_config, "api_management_api_id", null) + app_command_line = lookup(var.settings.site_config, "app_command_line", null) + container_registry_managed_identity_client_id = lookup(var.settings.site_config, "container_registry_managed_identity_client_id", null) + container_registry_use_managed_identity = lookup(var.settings.site_config, "container_registry_use_managed_identity", false) + ftps_state = lookup(var.settings.site_config, "ftps_state", null) + health_check_path = lookup(var.settings.site_config, "health_check_path", null) + health_check_eviction_time_in_min = lookup(var.settings.site_config, "health_check_eviction_time_in_min", null) + http2_enabled = lookup(var.settings.site_config, "http2_enabled", null) + load_balancing_mode = lookup(var.settings.site_config, "load_balancing_mode", null) + managed_pipeline_mode = lookup(var.settings.site_config, "managed_pipeline_mode", null) + minimum_tls_version = lookup(var.settings.site_config, "minimum_tls_version", null) + remote_debugging_enabled = lookup(var.settings.site_config, "remote_debugging_enabled", null) + remote_debugging_version = lookup(var.settings.site_config, "remote_debugging_version", null) + scm_minimum_tls_version = lookup(var.settings.site_config, "scm_minimum_tls_version", null) + scm_use_main_ip_restriction = lookup(var.settings.site_config, "scm_use_main_ip_restriction", null) + use_32_bit_worker = lookup(var.settings.site_config, "use_32_bit_worker", null) + websockets_enabled = lookup(var.settings.site_config, "websockets_enabled", null) + vnet_route_all_enabled = lookup(var.settings.site_config, "vnet_route_all_enabled", null) + worker_count = lookup(var.settings.site_config, "worker_count", null) + auto_heal_enabled = lookup(var.settings.site_config, "aut_heal_setting", null) + ip_restriction_default_action = lookup(var.settings.site_config, "ip_restriction_default_action", null) + + dynamic "application_stack" { + for_each = lookup(var.settings.site_config, "application_stack", {}) != {} ? [1] : [] + content { + docker_image_name = lookup(var.settings.site_config.application_stack, "docker_image_name", null) + docker_registry_url = lookup(var.settings.site_config.application_stack, "docker_registry_url", null) + docker_registry_username = lookup(var.settings.site_config.application_stack, "docker_registry_username", null) + docker_registry_password = lookup(var.settings.site_config.application_stack, "docker_registry_password", null) + dotnet_version = lookup(var.settings.site_config.application_stack, "dotnet_version", null) + go_version = lookup(var.settings.site_config.application_stack, "go_version", null) + java_version = lookup(var.settings.site_config.application_stack, "java_version", null) + java_server = lookup(var.settings.site_config.application_stack, "java_server", null) + java_server_version = lookup(var.settings.site_config.application_stack, "java_server_version", null) + node_version = lookup(var.settings.site_config.application_stack, "node_version", null) + php_version = lookup(var.settings.site_config.application_stack, "php_version", null) + python_version = lookup(var.settings.site_config.application_stack, "python_version", null) + ruby_version = lookup(var.settings.site_config.application_stack, "ruby_version", null) + } + } + dynamic "cors" { + for_each = lookup(var.settings.site_config, "cors", {}) != {} ? [1] : [] + + content { + allowed_origins = lookup(var.settings.site_config.cors, "allowed_origins", null) + support_credentials = lookup(var.settings.site_config.cors, "support_credentials", null) + } + } + + dynamic "ip_restriction" { + for_each = try(var.settings.site_config.ip_restriction, {}) + + content { + action = lookup(ip_restriction.value, "action", null) + ip_address = lookup(ip_restriction.value, "ip_address", null) + service_tag = lookup(ip_restriction.value, "service_tag", null) + virtual_network_subnet_id = can(ip_restriction.value.virtual_network_subnet_id) || can(ip_restriction.value.virtual_network_subnet.id) || can(ip_restriction.value.virtual_network_subnet.subnet_key) == false ? try(ip_restriction.value.virtual_network_subnet_id, ip_restriction.value.virtual_network_subnet.id, null) : var.combined_objects.networking[try(ip_restriction.value.virtual_network_subnet.lz_key, var.client_config.landingzone_key)][ip_restriction.value.virtual_network_subnet.vnet_key].subnets[ip_restriction.value.virtual_network_subnet.subnet_key].id + name = lookup(ip_restriction.value, "name", null) + priority = lookup(ip_restriction.value, "priority", null) + + + dynamic "headers" { + for_each = try(ip_restriction.headers, {}) + + content { + x_azure_fdid = lookup(headers.value, "x_azure_fdid", null) + x_fd_health_probe = lookup(headers.value, "x_fd_health_probe", null) + x_forwarded_for = lookup(headers.value, "x_forwarded_for", null) + x_forwarded_host = lookup(headers.value, "x_forwarded_host", null) + } + } + } + } + + dynamic "scm_ip_restriction" { + for_each = try(var.settings.site_config.scm_ip_restriction, {}) + + content { + ip_address = lookup(scm_ip_restriction.value, "ip_address", null) + service_tag = lookup(scm_ip_restriction.value, "service_tag", null) + virtual_network_subnet_id = can(scm_ip_restriction.value.virtual_network_subnet_id) ? scm_ip_restriction.value.virtual_network_subnet_id : can(scm_ip_restriction.value.virtual_network_subnet.id) ? scm_ip_restriction.value.virtual_network_subnet.id : can(scm_ip_restriction.value.virtual_network_subnet.subnet_key) ? var.combined_objects.networking[try(scm_ip_restriction.value.virtual_network_subnet.lz_key, var.client_config.landingzone_key)][scm_ip_restriction.value.virtual_network_subnet.vnet_key].subnets[scm_ip_restriction.value.virtual_network_subnet.subnet_key].id : null + name = lookup(scm_ip_restriction.value, "name", null) + priority = lookup(scm_ip_restriction.value, "priority", null) + action = lookup(scm_ip_restriction.value, "action", null) + dynamic "headers" { + for_each = try(scm_ip_restriction.headers, {}) + + content { + x_azure_fdid = lookup(headers.value, "x_azure_fdid", null) + x_fd_health_probe = lookup(headers.value, "x_fd_health_probe", null) + x_forwarded_for = lookup(headers.value, "x_forwarded_for", null) + x_forwarded_host = lookup(headers.value, "x_forwarded_host", null) + } + } + } + } + + dynamic "auto_heal_setting" { + for_each = lookup(var.settings.site_config, "auto_heal_setting", {}) != {} ? [1] : [] + content { + + dynamic "action" { + for_each = lookup(var.settings.site_config.auto_heal_setting, "action", {}) != {} ? [1] : [] + content { + action_type = lookup(var.settings.site_config.auto_heal_setting.action, "action_type", null) + minimum_process_execution_time = lookup(var.settings.site_config.auto_heal_setting.action, "minimum_process_execution_time", null) + } + } + + dynamic "trigger" { + for_each = lookup(var.settings.site_config.auto_heal_setting, "trigger", {}) != {} ? [1] : [] + content { + + dynamic "requests" { + for_each = lookup(var.settings.site_config.auto_heal_setting.trigger, "requests", {}) != {} ? [1] : [] + content { + count = lookup(var.settings.site_config.auto_heal_setting.trigger.requests, "count", null) + interval = lookup(var.settings.site_config.auto_heal_setting.trigger.requests, "interval", null) + } + } + + dynamic "slow_request" { + for_each = lookup(var.settings.site_config.auto_heal_setting.trigger, "slow_request", {}) != {} ? [ + 1 + ] : [] + content { + count = lookup(var.settings.site_config.auto_heal_setting.trigger.slow_request, "count", null) + interval = lookup(var.settings.site_config.auto_heal_setting.trigger.slow_request, "interval", null) + time_taken = lookup(var.settings.site_config.auto_heal_setting.trigger.slow_request, "time_taken", null) + path = lookup(var.settings.site_config.auto_heal_setting.trigger.slow_request, "path", null) + } + } + + dynamic "status_code" { + for_each = lookup(var.settings.site_config.auto_heal_setting.trigger, "status_code", {}) != {} ? [ + 1 + ] : [] + content { + count = lookup(var.settings.site_config.auto_heal_setting.trigger.status_code, "count", null) + interval = lookup(var.settings.site_config.auto_heal_setting.trigger.status_code, "interval", null) + status_code_range = lookup(var.settings.site_config.auto_heal_setting.trigger.status_code, "status_code_range", null) + sub_status = lookup(var.settings.site_config.auto_heal_setting.trigger.status_code, "sub_status", null) + win32_status_code = lookup(var.settings.site_config.auto_heal_setting.trigger.status_code, "win32_status_code", null) + path = lookup(var.settings.site_config.auto_heal_setting.trigger.status_code, "path", null) + } + } + } + } + } + } + } + } + + app_settings = local.app_settings + + dynamic "connection_string" { + for_each = var.connection_string + + content { + name = connection_string.value.name + type = connection_string.value.type + value = connection_string.value.value + } + } + dynamic "auth_settings" { + for_each = lookup(var.settings, "auth_settings", {}) != {} ? [1] : [] + + content { + enabled = lookup(var.settings.auth_settings, "enabled", false) + additional_login_parameters = lookup(var.settings.auth_settings, "additional_login_parameters ", null) + allowed_external_redirect_urls = lookup(var.settings.auth_settings, "allowed_external_redirect_urls", null) + default_provider = lookup(var.settings.auth_settings, "default_provider", null) + issuer = lookup(var.settings.auth_settings, "issuer", null) + runtime_version = lookup(var.settings.auth_settings, "runtime_version", null) + token_refresh_extension_hours = lookup(var.settings.auth_settings, "token_refresh_extension_hours", null) + token_store_enabled = lookup(var.settings.auth_settings, "token_store_enabled", null) + unauthenticated_client_action = lookup(var.settings.auth_settings, "unauthenticated_client_action", null) + + dynamic "active_directory" { + for_each = lookup(var.settings.auth_settings, "active_directory", {}) != {} ? [1] : [] + + content { + client_id = can(var.settings.auth_settings.active_directory.client_id_key) ? var.azuread_applications[try(var.settings.auth_settings.active_directory.client_id_lz_key, var.client_config.landingzone_key)][var.settings.auth_settings.active_directory.client_id_key].application_id : var.settings.auth_settings.active_directory.client_id + client_secret = can(var.settings.auth_settings.active_directory.client_secret_key) ? var.azuread_service_principal_passwords[try(var.settings.auth_settings.active_directory.client_secret_lz_key, var.client_config.landingzone_key)][var.settings.auth_settings.active_directory.client_secret_key].service_principal_password : try(var.settings.auth_settings.active_directory.client_secret, null) + allowed_audiences = lookup(var.settings.auth_settings.active_directory, "allowed_audiences", null) + } + } + + dynamic "facebook" { + for_each = lookup(var.settings.auth_settings, "facebook", {}) != {} ? [1] : [] + + content { + app_id = var.settings.auth_settings.facebook.app_id + app_secret = var.settings.auth_settings.facebook.app_secret + oauth_scopes = lookup(var.settings.auth_settings.facebook, "oauth_scopes", null) + } + } + + dynamic "google" { + for_each = lookup(var.settings.auth_settings, "google", {}) != {} ? [1] : [] + + content { + client_id = var.settings.auth_settings.google.client_id + client_secret = var.settings.auth_settings.google.client_secret + oauth_scopes = lookup(var.settings.auth_settings.google, "oauth_scopes", null) + } + } + + dynamic "microsoft" { + for_each = lookup(var.settings.auth_settings, "microsoft", {}) != {} ? [1] : [] + + content { + client_id = var.settings.auth_settings.microsoft.client_id + client_secret = var.settings.auth_settings.microsoft.client_secret + oauth_scopes = lookup(var.settings.auth_settings.microsoft, "oauth_scopes", null) + } + } + + dynamic "twitter" { + for_each = lookup(var.settings.auth_settings, "twitter", {}) != {} ? [1] : [] + + content { + consumer_key = var.settings.auth_settings.twitter.consumer_key + consumer_secret = var.settings.auth_settings.twitter.consumer_secret + } + } + } + } + dynamic "auth_settings_v2" { + for_each = lookup(var.settings, "auth_settings_v2", {}) != {} ? [1] : [] + content { + auth_enabled = lookup(var.settings.auth_settings_v2, "auth_enabled", false) + runtime_version = lookup(var.settings.auth_settings_v2, "runtime_version", null) + config_file_path = lookup(var.settings.auth_settings_v2, "config_file_path", null) + require_authentication = lookup(var.settings.auth_settings_v2, "require_authentication", null) + unauthenticated_action = lookup(var.settings.auth_settings_v2, "unauthenticated_action", null) + default_provider = lookup(var.settings.auth_settings_v2, "default_provider", null) + excluded_paths = lookup(var.settings.auth_settings_v2, "excluded_paths", null) + require_https = lookup(var.settings.auth_settings_v2, "require_https", null) + http_route_api_prefix = lookup(var.settings.auth_settings_v2, "http_route_api_prefix", null) + forward_proxy_convention = lookup(var.settings.auth_settings_v2, "forward_proxy_convention", null) + forward_proxy_custom_host_header_name = lookup(var.settings.auth_settings_v2, "forward_proxy_custom_host_header_name", null) + forward_proxy_custom_scheme_header_name = lookup(var.settings.auth_settings_v2, "forward_proxy_custom_scheme_header_name", null) + dynamic "login" { + for_each = lookup(var.settings.auth_settings_v2, "login", {}) != {} ? [1] : [] + + content { + logout_endpoint = lookup(var.settings.auth_settings_v2.login, "logout_endpoint", null) + token_store_enabled = lookup(var.settings.auth_settings_v2.login, "token_store_enabled", null) + token_refresh_extension_time = lookup(var.settings.auth_settings_v2.login, "token_refresh_extension_time", null) + token_store_path = lookup(var.settings.auth_settings_v2.login, "token_store_path", null) + token_store_sas_setting_name = lookup(var.settings.auth_settings_v2.login, "token_store_sas_setting_name", null) + preserve_url_fragments_for_logins = lookup(var.settings.auth_settings_v2.login, "preserve_url_fragments_for_logins", null) + allowed_external_redirect_urls = lookup(var.settings.auth_settings_v2.login, "allowed_external_redirect_urls", null) + cookie_expiration_convention = lookup(var.settings.auth_settings_v2.login, "cookie_expiration_convention", null) + cookie_expiration_time = lookup(var.settings.auth_settings_v2.login, "cookie_expiration_time", null) + validate_nonce = lookup(var.settings.auth_settings_v2.login, "validate_nonce", null) + nonce_expiration_time = lookup(var.settings.auth_settings_v2.login, "nonce_expiration_time", null) + } + } + dynamic "apple_v2" { + for_each = lookup(var.settings.auth_settings_v2, "apple_v2", {}) != {} ? [1] : [] + + content { + client_id = can(var.settings.auth_settings_v2.apple_v2.client_id_key) ? var.azuread_applications[try(var.settings.auth_settings_v2.apple_v2.client_id_lz_key, var.client_config.landingzone_key)][var.settings.auth_settings_v2.apple_v2.client_id_key].application_id : var.settings.auth_settings_v2.apple_v2.client_id + client_secret_setting_name = var.settings.auth_settings_v2.apple_v2.client_secret_setting_name + login_scopes = lookup(var.settings.auth_settings_v2.apple_v2, "login_scopes", null) + } + } + dynamic "azure_static_web_app_v2" { + for_each = lookup(var.settings.auth_settings_v2, "azure_static_web_app_v2", {}) != {} ? [1] : [] + + content { + client_id = can(var.settings.auth_settings_v2.azure_static_web_app_v2.client_id_key) ? var.azuread_applications[try(var.settings.auth_settings_v2.azure_static_web_app_v2.client_id_lz_key, var.client_config.landingzone_key)][var.settings.auth_settings_v2.azure_static_web_app_v2.client_id_key].application_id : var.settings.auth_settings_v2.azure_static_web_app_v2.client_id + } + } + dynamic "facebook_v2" { + for_each = lookup(var.settings.auth_settings_v2, "facebook_v2_v2", {}) != {} ? [1] : [] + + content { + app_id = can(var.settings.auth_settings_v2.facebook_v2.app_id_key) ? var.azuread_applications[try(var.settings.auth_settings_v2.facebook_v2.app_id_lz_key, var.client_config.landingzone_key)][var.settings.auth_settings_v2.facebook_v2.app_id_key].application_id : var.settings.auth_settings_v2.facebook_v2.app_id + app_secret_setting_name = var.settings.auth_settings_v2.facebook_v2.app_secret_setting_name + graph_api_version = lookup(var.settings.auth_settings_v2.facebook_v2, "graph_api_version", null) + login_scopes = lookup(var.settings.auth_settings_v2.facebook_v2, "login_scopes", null) + } + } + dynamic "github_v2" { + for_each = lookup(var.settings.auth_settings_v2, "github_v2", {}) != {} ? [1] : [] + content { + client_id = can(var.settings.auth_settings_v2.github_v2.client_id_key) ? var.azuread_applications[try(var.settings.auth_settings_v2.github_v2.client_id_lz_key, var.client_config.landingzone_key)][var.settings.auth_settings_v2.github_v2.client_id_key].application_id : var.settings.auth_settings_v2.github_v2.client_id + client_secret_setting_name = var.settings.auth_settings_v2.github_v2.client_secret_setting_name + login_scopes = lookup(var.settings.auth_settings_v2.github_v2, "login_scopes", null) + } + } + dynamic "google_v2" { + for_each = lookup(var.settings.auth_settings_v2, "google_v2", {}) != {} ? [1] : [] + + content { + client_id = can(var.settings.auth_settings_v2.google_v2.client_id_key) ? var.azuread_applications[try(var.settings.auth_settings_v2.google_v2.client_id_lz_key, var.client_config.landingzone_key)][var.settings.auth_settings_v2.google_v2.client_id_key].application_id : var.settings.auth_settings_v2.google_v2.client_id + client_secret_setting_name = var.settings.auth_settings_v2.google_v2.client_secret_setting_name + allowed_audiences = lookup(var.settings.auth_settings_v2.google_v2, "allowed_audiences", null) + login_scopes = lookup(var.settings.auth_settings_v2.google_v2, "login_scopes", null) + } + } + dynamic "microsoft_v2" { + for_each = lookup(var.settings.auth_settings_v2, "microsoft_v2", {}) != {} ? [1] : [] + + content { + client_id = can(var.settings.auth_settings_v2.microsoft_v2.client_id_key) ? var.azuread_applications[try(var.settings.auth_settings_v2.microsoft_v2.client_id_lz_key, var.client_config.landingzone_key)][var.settings.auth_settings_v2.microsoft_v2.client_id_key].application_id : var.settings.auth_settings_v2.microsoft_v2.client_id + client_secret_setting_name = var.settings.auth_settings_v2.microsoft_v2.client_secret_setting_name + allowed_audiences = lookup(var.settings.auth_settings_v2.microsoft_v2, "allowed_audiences", null) + login_scopes = lookup(var.settings.auth_settings_v2.microsoft_v2, "login_scopes", null) + } + } + dynamic "twitter_v2" { + for_each = lookup(var.settings.auth_settings_v2, "twitter_v2", {}) != {} ? [1] : [] + + content { + consumer_key = var.settings.auth_settings_v2.twitter_v2.consumer_key + consumer_secret_setting_name = var.settings.auth_settings_v2.twitter_v2.consumer_secret + } + } + dynamic "active_directory_v2" { + for_each = lookup(var.settings.auth_settings_v2, "active_directory_v2", {}) != {} ? [1] : [] + + content { + client_id = can(var.settings.auth_settings_v2.active_directory_v2.client_id_key) ? var.azuread_applications[try(var.settings.auth_settings_v2.active_directory_v2.client_id_lz_key, var.client_config.landingzone_key)][var.settings.auth_settings_v2.active_directory_v2.client_id_key].application_id : var.settings.auth_settings_v2.active_directory_v2.client_id + tenant_auth_endpoint = var.settings.auth_settings_v2.active_directory_v2.tenant_auth_endpoint + client_secret_setting_name = can(var.settings.auth_settings_v2.active_directory_v2.client_secret_setting_name_key) ? var.azuread_service_principal_passwords[try(var.settings.auth_settings_v2.active_directory_v2.client_secret_setting_name.lz_key, var.client_config.landingzone_key)][var.settings.auth_settings_v2.active_directory_v2.client_secret_setting_name.key].service_principal_password : try(var.settings.auth_settings_v2.active_directory_v2.client_secret_setting_name, null) + allowed_audiences = lookup(var.settings.auth_settings_v2.active_directory_v2, "allowed_audiences", null) + jwt_allowed_groups = lookup(var.settings.auth_settings_v2.active_directory_v2, "jwt_allowed_groups ", null) + jwt_allowed_client_applications = lookup(var.settings.auth_settings_v2.active_directory_v2, "jwt_allowed_client_applications ", null) + www_authentication_disabled = lookup(var.settings.auth_settings_v2.active_directory_v2, "www_authentication_disabled ", null) + allowed_groups = lookup(var.settings.auth_settings_v2.active_directory_v2, "allowed_groups", null) + allowed_identities = lookup(var.settings.auth_settings_v2.active_directory_v2, "allowed_identities", null) + allowed_applications = lookup(var.settings.auth_settings_v2.active_directory_v2, "allowed_applications", null) + login_parameters = lookup(var.settings.auth_settings_v2.active_directory_v2, "login_parameters", null) + } + } + } + } + + dynamic "storage_account" { + for_each = lookup(var.settings, "storage_account", {}) + content { + name = storage_account.value.name + type = storage_account.value.type + account_name = can(storage_account.value.account_key) ? var.storage_accounts[try(storage_account.value.lz_key, var.client_config.landingzone_key)][storage_account.value.account_key].name : try(storage_account.value.account_name, null) + share_name = storage_account.value.share_name + access_key = can(storage_account.value.account_key) ? var.storage_accounts[try(storage_account.value.lz_key, var.client_config.landingzone_key)][storage_account.value.account_key].primary_access_key : try(storage_account.value.access_key, null) + mount_path = lookup(storage_account.value, "mount_path", null) + } + } + + dynamic "backup" { + for_each = lookup(var.settings, "backup", {}) != {} ? [1] : [] + + content { + name = var.settings.backup.name + enabled = var.settings.backup.enabled + storage_account_url = try(var.settings.backup.storage_account_url, local.backup_sas_url) + + dynamic "schedule" { + for_each = lookup(var.settings.backup, "schedule", {}) != {} ? [1] : [] + + content { + frequency_interval = var.settings.backup.schedule.frequency_interval + frequency_unit = lookup(var.settings.backup.schedule, "frequency_unit", null) + keep_at_least_one_backup = lookup(var.settings.backup.schedule, "keep_at_least_one_backup", null) + retention_period_days = lookup(var.settings.backup.schedule, "retention_period_days", null) + start_time = lookup(var.settings.backup.schedule, "start_time", null) + } + } + } + } + + dynamic "logs" { + for_each = lookup(var.settings, "logs", {}) != {} ? [1] : [] + + content { + detailed_error_messages = try(var.settings.logs.detailed_error_messages, null) + failed_request_tracing = try(var.settings.logs.failed_request_tracing, null) + + dynamic "application_logs" { + for_each = lookup(var.settings.logs, "application_logs", {}) != {} ? [1] : [] + + content { + file_system_level = try(var.settings.logs.application_logs.file_system_level, null) + + dynamic "azure_blob_storage" { + for_each = lookup(var.settings.logs.application_logs, "azure_blob_storage", {}) != {} ? [1] : [] + + content { + level = var.settings.logs.application_logs.azure_blob_storage.level + sas_url = try(var.settings.logs.application_logs.azure_blob_storage.sas_url, local.logs_sas_url) + retention_in_days = var.settings.logs.application_logs.azure_blob_storage.retention_in_days + } + } + } + } + + dynamic "http_logs" { + for_each = lookup(var.settings.logs, "http_logs", {}) != {} ? [1] : [] + + content { + dynamic "azure_blob_storage" { + for_each = lookup(var.settings.logs.http_logs, "azure_blob_storage", {}) != {} ? [1] : [] + + content { + sas_url = try(var.settings.logs.http_logs.azure_blob_storage.sas_url, local.http_logs_sas_url) + retention_in_days = var.settings.logs.http_logs.azure_blob_storage.retention_in_days + } + } + dynamic "file_system" { + for_each = lookup(var.settings.logs.http_logs, "file_system", {}) != {} ? [1] : [] + + content { + retention_in_days = var.settings.logs.http_logs.file_system.retention_in_days + retention_in_mb = var.settings.logs.http_logs.file_system.retention_in_mb + } + } + } + } + } + } + + lifecycle { + ignore_changes = [virtual_network_subnet_id] + } +} + +resource "azurerm_app_service_virtual_network_swift_connection" "vnet_config" { + depends_on = [azurerm_linux_web_app.linux_web_apps] + count = lookup(var.settings, "subnet_key", null) == null && lookup(var.settings, "subnet_id", null) == null && try(var.settings.virtual_network_subnet_id, null) == null ? 0 : 1 + app_service_id = azurerm_linux_web_app.linux_web_apps.id + subnet_id = coalesce( + try(var.remote_objects.subnets[var.settings.subnet_key].id, null), + try(var.settings.subnet_id, null) + ) +} diff --git a/modules/webapps/linux_webapps/output.tf b/modules/webapps/linux_webapps/output.tf new file mode 100644 index 0000000000..ee6f367099 --- /dev/null +++ b/modules/webapps/linux_webapps/output.tf @@ -0,0 +1,20 @@ +output "id" { + value = azurerm_linux_web_app.linux_web_apps.id + description = "The ID of the App Service." +} +output "default_hostname" { + value = azurerm_linux_web_app.linux_web_apps.default_hostname + description = "The Default Hostname associated with the Linux Web App" +} +output "outbound_ip_addresses" { + value = azurerm_linux_web_app.linux_web_apps.outbound_ip_addresses + description = "A comma separated list of outbound IP addresses" +} +output "possible_outbound_ip_addresses" { + value = azurerm_linux_web_app.linux_web_apps.possible_outbound_ip_addresses + description = "A comma separated list of outbound IP addresses. not all of which are necessarily in use" +} +output "identity" { + value = try(azurerm_linux_web_app.linux_web_apps.identity.0.principal_id, null) + description = "The identity id of the linux web app." +} \ No newline at end of file diff --git a/modules/webapps/linux_webapps/private_endpoint.tf b/modules/webapps/linux_webapps/private_endpoint.tf new file mode 100644 index 0000000000..109d14cccb --- /dev/null +++ b/modules/webapps/linux_webapps/private_endpoint.tf @@ -0,0 +1,16 @@ +module "private_endpoint" { + source = "../../networking/private_endpoint" + for_each = var.private_endpoints + + resource_id = azurerm_linux_web_app.linux_web_apps.id + name = each.value.name + location = local.location + resource_group_name = local.resource_group_name + subnet_id = can(each.value.subnet_id) ? each.value.subnet_id : var.vnets[try(each.value.lz_key, var.client_config.landingzone_key)][each.value.vnet_key].subnets[each.value.subnet_key].id + settings = each.value + global_settings = var.global_settings + tags = local.tags + base_tags = var.base_tags + private_dns = var.private_dns + client_config = var.client_config +} \ No newline at end of file diff --git a/modules/webapps/linux_webapps/storage_account.tf b/modules/webapps/linux_webapps/storage_account.tf new file mode 100644 index 0000000000..20e35258fb --- /dev/null +++ b/modules/webapps/linux_webapps/storage_account.tf @@ -0,0 +1,94 @@ +data "azurerm_storage_account" "backup_storage_account" { + count = can(var.settings.backup) ? 1 : 0 + + name = local.backup_storage_account.name + resource_group_name = local.backup_storage_account.resource_group_name +} + +data "azurerm_storage_account_blob_container_sas" "backup" { + count = can(var.settings.backup) ? 1 : 0 + + connection_string = data.azurerm_storage_account.backup_storage_account.0.primary_connection_string + container_name = local.backup_storage_account.containers[var.settings.backup.container_key].name + https_only = true + + start = time_rotating.sas[0].id + expiry = timeadd(time_rotating.sas[0].id, format("%sh", var.settings.backup.sas_policy.expire_in_days * 24)) + + permissions { + read = true + add = true + create = true + write = true + delete = true + list = true + } +} + +data "azurerm_storage_account_blob_container_sas" "logs" { + count = can(var.settings.logs) ? 1 : 0 + + connection_string = data.azurerm_storage_account.backup_storage_account.0.primary_connection_string + container_name = local.logs_storage_account.containers[var.settings.logs.container_key].name + https_only = true + + start = time_rotating.logs_sas[0].id + expiry = timeadd(time_rotating.logs_sas[0].id, format("%sh", var.settings.logs.sas_policy.expire_in_days * 24)) + + permissions { + read = true + add = true + create = true + write = true + delete = true + list = true + } +} + + +data "azurerm_storage_account_blob_container_sas" "http_logs" { + count = can(var.settings.logs.http_logs) ? 1 : 0 + + connection_string = data.azurerm_storage_account.backup_storage_account.0.primary_connection_string + container_name = local.http_logs_storage_account.containers[var.settings.logs.http_logs.container_key].name + https_only = true + + start = time_rotating.http_logs_sas[0].id + expiry = timeadd(time_rotating.http_logs_sas[0].id, format("%sh", var.settings.logs.http_logs.sas_policy.expire_in_days * 24)) + + permissions { + read = true + add = true + create = true + write = true + delete = true + list = true + } +} + +resource "time_rotating" "sas" { + count = can(var.settings.backup.sas_policy) ? 1 : 0 + + rotation_minutes = lookup(var.settings.backup.sas_policy.rotation, "mins", null) + rotation_days = lookup(var.settings.backup.sas_policy.rotation, "days", null) + rotation_months = lookup(var.settings.backup.sas_policy.rotation, "months", null) + rotation_years = lookup(var.settings.backup.sas_policy.rotation, "years", null) +} + +resource "time_rotating" "logs_sas" { + count = can(var.settings.logs.sas_policy) ? 1 : 0 + + rotation_minutes = lookup(var.settings.logs.sas_policy.rotation, "mins", null) + rotation_days = lookup(var.settings.logs.sas_policy.rotation, "days", null) + rotation_months = lookup(var.settings.logs.sas_policy.rotation, "months", null) + rotation_years = lookup(var.settings.logs.sas_policy.rotation, "years", null) +} + +resource "time_rotating" "http_logs_sas" { + count = can(var.settings.logs.http_logs.sas_policy) ? 1 : 0 + + rotation_minutes = lookup(var.settings.logs.http_logs.sas_policy.rotation, "mins", null) + rotation_days = lookup(var.settings.logs.http_logs.sas_policy.rotation, "days", null) + rotation_months = lookup(var.settings.logs.http_logs.sas_policy.rotation, "months", null) + rotation_years = lookup(var.settings.logs.http_logs.sas_policy.rotation, "years", null) +} diff --git a/modules/webapps/linux_webapps/variables.tf b/modules/webapps/linux_webapps/variables.tf new file mode 100644 index 0000000000..cb5a789c50 --- /dev/null +++ b/modules/webapps/linux_webapps/variables.tf @@ -0,0 +1,97 @@ +variable "app_service_plan_id" { +} + +variable "app_settings" { + default = null +} + +variable "application_insight" { + default = null +} + +variable "azuread_applications" { +} + +variable "azuread_service_principal_passwords" { +} + +variable "base_tags" { + description = "Base tags for the resource to be inherited from the resource group." + type = bool +} + +variable "client_config" { + description = "Client configuration object (see module README.md)." +} + +variable "combined_objects" { + default = {} +} + +variable "connection_string" { + default = {} +} + +variable "diagnostic_profiles" { + default = {} +} + +variable "diagnostics" { + default = null +} + +variable "dynamic_app_settings" { + default = {} +} + +variable "global_settings" { + description = "Global settings object (see module README.md)" +} + +variable "identity" { + default = null +} + +variable "location" { + description = "(Required) Resource Location" + default = null +} + +variable "name" { + description = "(Required) Name of the App Service" +} + +variable "private_dns" { +} + +variable "private_endpoints" { +} + +variable "remote_objects" { + default = null +} + +variable "resource_group" { + description = "Resource group object to deploy the virtual machine" +} + +variable "resource_group_name" { + description = "Resource group object to deploy the virtual machine" + default = null +} + +variable "settings" { +} + +variable "storage_accounts" { + default = {} +} + +variable "subnet_id" { +} + +variable "virtual_subnets" { +} + +variable "vnets" { +} diff --git a/modules/webapps/windows_function_app/module.tf b/modules/webapps/windows_function_app/module.tf index b2a408690d..394352db0f 100644 --- a/modules/webapps/windows_function_app/module.tf +++ b/modules/webapps/windows_function_app/module.tf @@ -12,7 +12,7 @@ resource "azurecaf_name" "plan" { resource "azurerm_windows_function_app" "windows_function_app" { #To avoid redeploy with existing customer lifecycle { - ignore_changes = [name] + ignore_changes = [name, virtual_network_subnet_id] } location = local.location name = azurecaf_name.plan.result @@ -409,7 +409,6 @@ resource "azurerm_windows_function_app" "windows_function_app" { zip_deploy_file = try(var.settings.zip_deploy_file, null) } - resource "azurerm_app_service_virtual_network_swift_connection" "vnet_config" { depends_on = [azurerm_windows_function_app.windows_function_app] count = lookup(var.settings, "subnet_key", null) == null && lookup(var.settings, "subnet_id", null) == null && try(var.settings.virtual_network_subnet_id, null) == null ? 0 : 1 diff --git a/modules/webapps/windows_function_app/private._endpoint.tf b/modules/webapps/windows_function_app/private_endpoint.tf similarity index 100% rename from modules/webapps/windows_function_app/private._endpoint.tf rename to modules/webapps/windows_function_app/private_endpoint.tf diff --git a/modules/webapps/windows_webapps/locals.dynamic_app_settings.tf b/modules/webapps/windows_webapps/locals.dynamic_app_settings.tf new file mode 100644 index 0000000000..3923f088d1 --- /dev/null +++ b/modules/webapps/windows_webapps/locals.dynamic_app_settings.tf @@ -0,0 +1,27 @@ +locals { + # Expected Variable: dynamic_app_settings = { + # "KEYVAULT_URL" = { + # keyvaults = { + # my_common_vault = { + # lz_key = "common_services_lz" + # attribute_key = "vault_uri" + # } + # } + # } + # } + dynamic_settings_to_process = { + for setting in + flatten( + [ + for setting_name, resources in var.dynamic_app_settings : [ + for resource_type_key, resource in resources : [ + for object_id_key, object_attributes in resource : { + key = setting_name + value = try(var.combined_objects[resource_type_key][object_attributes.lz_key][object_id_key][object_attributes.attribute_key], var.combined_objects[resource_type_key][var.client_config.landingzone_key][object_id_key][object_attributes.attribute_key]) + } + ] + ] + ] + ) : setting.key => setting.value + } +} diff --git a/modules/webapps/windows_webapps/main.tf b/modules/webapps/windows_webapps/main.tf new file mode 100644 index 0000000000..6b3184bc0f --- /dev/null +++ b/modules/webapps/windows_webapps/main.tf @@ -0,0 +1,49 @@ +terraform { + required_providers { + azurecaf = { + source = "aztfmod/azurecaf" + } + # azurerm = { + # source = "hashicorp/azurerm" + # version = "~> 3.97.0" + # } + } + +} + +locals { + module_tag = { + "module" = basename(abspath(path.module)) + } + tags = var.base_tags ? merge( + var.global_settings.tags, + try(var.resource_group.tags, null), + local.module_tag, + try(var.settings.tags, null) + ) : merge( + local.module_tag, + try(var.settings.tags, + null) + ) + + app_settings = merge( + var.application_insight == null ? {} : { + "APPINSIGHTS_INSTRUMENTATIONKEY" = var.application_insight.instrumentation_key, + "APPLICATIONINSIGHTS_CONNECTION_STRING" = var.application_insight.connection_string, + "ApplicationInsightsAgent_EXTENSION_VERSION" = "~2" + }, + try(var.app_settings, {}), + try(local.dynamic_settings_to_process, {}) + ) + + location = coalesce(var.location, var.resource_group.location) + resource_group_name = coalesce(var.resource_group_name, var.resource_group.name) + backup_storage_account = can(var.settings.backup) ? var.storage_accounts[try(var.settings.backup.lz_key, var.client_config.landingzone_key)][var.settings.backup.storage_account_key] : null + backup_sas_url = can(var.settings.backup) ? "${local.backup_storage_account.primary_blob_endpoint}${local.backup_storage_account.containers[var.settings.backup.container_key].name}${data.azurerm_storage_account_blob_container_sas.backup[0].sas}" : null + + logs_storage_account = can(var.settings.logs) ? var.storage_accounts[try(var.settings.logs.lz_key, var.client_config.landingzone_key)][var.settings.logs.storage_account_key] : null + logs_sas_url = can(var.settings.logs) ? "${local.logs_storage_account.primary_blob_endpoint}${local.logs_storage_account.containers[var.settings.logs.container_key].name}${data.azurerm_storage_account_blob_container_sas.logs[0].sas}" : null + + http_logs_storage_account = can(var.settings.logs.http_logs) ? var.storage_accounts[try(var.settings.logs.http_logs.lz_key, var.client_config.landingzone_key)][var.settings.logs.http_logs.storage_account_key] : null + http_logs_sas_url = can(var.settings.logs.http_logs) ? "${local.http_logs_storage_account.primary_blob_endpoint}${local.http_logs_storage_account.containers[var.settings.logs.http_logs.container_key].name}${data.azurerm_storage_account_blob_container_sas.http_logs[0].sas}" : null +} diff --git a/modules/webapps/windows_webapps/managed_identities.tf b/modules/webapps/windows_webapps/managed_identities.tf new file mode 100644 index 0000000000..cb18c82023 --- /dev/null +++ b/modules/webapps/windows_webapps/managed_identities.tf @@ -0,0 +1,17 @@ +locals { + managed_local_identities = flatten([ + for managed_identity_key in try(var.identity.managed_identity_keys, []) : [ + var.combined_objects.managed_identities[var.client_config.landingzone_key][managed_identity_key].id + ] + ]) + + managed_remote_identities = flatten([ + for keyvault_key, value in try(var.identity.remote, []) : [ + for managed_identity_key in value.managed_identity_keys : [ + var.combined_objects.managed_identities[keyvault_key][managed_identity_key].id + ] + ] + ]) + + managed_identities = concat(local.managed_local_identities, local.managed_remote_identities) +} \ No newline at end of file diff --git a/modules/webapps/windows_webapps/module.tf b/modules/webapps/windows_webapps/module.tf new file mode 100644 index 0000000000..f49ea31b12 --- /dev/null +++ b/modules/webapps/windows_webapps/module.tf @@ -0,0 +1,472 @@ + +resource "azurecaf_name" "windows_web_apps" { + name = var.name + resource_type = "azurerm_app_service" + prefixes = var.global_settings.prefixes + random_length = var.global_settings.random_length + clean_input = true + passthrough = var.global_settings.passthrough + use_slug = var.global_settings.use_slug +} + + +resource "azurerm_windows_web_app" "windows_web_apps" { + name = azurecaf_name.windows_web_apps.result + location = local.location + resource_group_name = local.resource_group_name + service_plan_id = var.app_service_plan_id + tags = merge(local.tags, try(var.settings.tags, {})) + client_affinity_enabled = lookup(var.settings, "client_affinity_enabled", null) + client_certificate_enabled = lookup(var.settings, "client_certificate_enabled", null) + client_certificate_mode = lookup(var.settings, "client_certificate_mode", null) + enabled = lookup(var.settings, "enabled", true) + https_only = lookup(var.settings, "https_only", null) + public_network_access_enabled = lookup(var.settings, "public_network_access_enabled", null) + + key_vault_reference_identity_id = can(var.settings.key_vault_reference_identity.key) ? var.combined_objects.managed_identities[try(var.settings.identity.lz_key, var.client_config.landingzone_key)][var.settings.key_vault_reference_identity.key].id : try(var.settings.key_vault_reference_identity.id, null) + + dynamic "identity" { + for_each = try(var.identity, null) == null ? [] : [1] + + content { + type = var.identity.type + identity_ids = lower(var.identity.type) == "userassigned" ? local.managed_identities : null + } + } + + dynamic "site_config" { + for_each = lookup(var.settings, "site_config", {}) != {} ? [1] : [] + + content { + always_on = lookup(var.settings.site_config, "always_on", false) + api_management_api_id = lookup(var.settings.site_config, "api_management_api_id", null) + app_command_line = lookup(var.settings.site_config, "app_command_line", null) + container_registry_managed_identity_client_id = lookup(var.settings.site_config, "container_registry_managed_identity_client_id", null) + container_registry_use_managed_identity = lookup(var.settings.site_config, "container_registry_use_managed_identity", false) + ftps_state = lookup(var.settings.site_config, "ftps_state", null) + health_check_path = lookup(var.settings.site_config, "health_check_path", null) + health_check_eviction_time_in_min = lookup(var.settings.site_config, "health_check_eviction_time_in_min", null) + http2_enabled = lookup(var.settings.site_config, "http2_enabled", null) + load_balancing_mode = lookup(var.settings.site_config, "load_balancing_mode", null) + managed_pipeline_mode = lookup(var.settings.site_config, "managed_pipeline_mode", null) + minimum_tls_version = lookup(var.settings.site_config, "minimum_tls_version", null) + remote_debugging_enabled = lookup(var.settings.site_config, "remote_debugging_enabled", null) + remote_debugging_version = lookup(var.settings.site_config, "remote_debugging_version", null) + scm_minimum_tls_version = lookup(var.settings.site_config, "scm_minimum_tls_version", null) + scm_use_main_ip_restriction = lookup(var.settings.site_config, "scm_use_main_ip_restriction", null) + use_32_bit_worker = lookup(var.settings.site_config, "use_32_bit_worker", null) + websockets_enabled = lookup(var.settings.site_config, "websockets_enabled", null) + vnet_route_all_enabled = lookup(var.settings.site_config, "vnet_route_all_enabled", null) + worker_count = lookup(var.settings.site_config, "worker_count", null) + auto_heal_enabled = lookup(var.settings.site_config, "aut_heal_setting", null) + ip_restriction_default_action = lookup(var.settings.site_config, "ip_restriction_default_action", null) + + dynamic "application_stack" { + for_each = lookup(var.settings.site_config, "application_stack", {}) != {} ? [1] : [] + content { + current_stack = lookup(var.settings.site_config.application_stack, "current_stack", null) + dotnet_version = lookup(var.settings.site_config.application_stack, "dotnet_version", null) + dotnet_core_version = lookup(var.settings.site_config.application_stack, "dotnet_core_version", null) + tomcat_version = lookup(var.settings.site_config.application_stack, "tomcat_version", null) + java_embedded_server_enabled = lookup(var.settings.site_config.application_stack, "java_embedded_server_enabled", null) + java_version = lookup(var.settings.site_config.application_stack, "java_version", null) + node_version = lookup(var.settings.site_config.application_stack, "node_version", null) + php_version = lookup(var.settings.site_config.application_stack, "php_version", null) + python = lookup(var.settings.site_config.application_stack, "python", null) + } + } + dynamic "cors" { + for_each = lookup(var.settings.site_config, "cors", {}) != {} ? [1] : [] + + content { + allowed_origins = lookup(var.settings.site_config.cors, "allowed_origins", null) + support_credentials = lookup(var.settings.site_config.cors, "support_credentials", null) + } + } + dynamic "ip_restriction" { + for_each = try(var.settings.site_config.ip_restriction, {}) + + content { + action = lookup(ip_restriction.value, "action", null) + ip_address = lookup(ip_restriction.value, "ip_address", null) + service_tag = lookup(ip_restriction.value, "service_tag", null) + virtual_network_subnet_id = can(ip_restriction.value.virtual_network_subnet_id) || can(ip_restriction.value.virtual_network_subnet.id) || can(ip_restriction.value.virtual_network_subnet.subnet_key) == false ? try(ip_restriction.value.virtual_network_subnet_id, ip_restriction.value.virtual_network_subnet.id, null) : var.combined_objects.networking[try(ip_restriction.value.virtual_network_subnet.lz_key, var.client_config.landingzone_key)][ip_restriction.value.virtual_network_subnet.vnet_key].subnets[ip_restriction.value.virtual_network_subnet.subnet_key].id + name = lookup(ip_restriction.value, "name", null) + priority = lookup(ip_restriction.value, "priority", null) + + + dynamic "headers" { + for_each = try(ip_restriction.headers, {}) + + content { + x_azure_fdid = lookup(headers.value, "x_azure_fdid", null) + x_fd_health_probe = lookup(headers.value, "x_fd_health_probe", null) + x_forwarded_for = lookup(headers.value, "x_forwarded_for", null) + x_forwarded_host = lookup(headers.value, "x_forwarded_host", null) + } + } + } + } + dynamic "scm_ip_restriction" { + for_each = try(var.settings.site_config.scm_ip_restriction, {}) + + content { + ip_address = lookup(scm_ip_restriction.value, "ip_address", null) + service_tag = lookup(scm_ip_restriction.value, "service_tag", null) + virtual_network_subnet_id = can(scm_ip_restriction.value.virtual_network_subnet_id) ? scm_ip_restriction.value.virtual_network_subnet_id : can(scm_ip_restriction.value.virtual_network_subnet.id) ? scm_ip_restriction.value.virtual_network_subnet.id : can(scm_ip_restriction.value.virtual_network_subnet.subnet_key) ? var.combined_objects.networking[try(scm_ip_restriction.value.virtual_network_subnet.lz_key, var.client_config.landingzone_key)][scm_ip_restriction.value.virtual_network_subnet.vnet_key].subnets[scm_ip_restriction.value.virtual_network_subnet.subnet_key].id : null + name = lookup(scm_ip_restriction.value, "name", null) + priority = lookup(scm_ip_restriction.value, "priority", null) + action = lookup(scm_ip_restriction.value, "action", null) + dynamic "headers" { + for_each = try(scm_ip_restriction.headers, {}) + + content { + x_azure_fdid = lookup(headers.value, "x_azure_fdid", null) + x_fd_health_probe = lookup(headers.value, "x_fd_health_probe", null) + x_forwarded_for = lookup(headers.value, "x_forwarded_for", null) + x_forwarded_host = lookup(headers.value, "x_forwarded_host", null) + } + } + } + } + dynamic "auto_heal_setting" { + for_each = lookup(var.settings.site_config, "auto_heal_setting", {}) != {} ? [1] : [] + content { + + dynamic "action" { + for_each = lookup(var.settings.site_config.auto_heal_setting, "action", {}) != {} ? [1] : [] + content { + action_type = lookup(var.settings.site_config.auto_heal_setting.action, "action_type", null) + minimum_process_execution_time = lookup(var.settings.site_config.auto_heal_setting.action, "minimum_process_execution_time", null) + } + } + + dynamic "trigger" { + for_each = lookup(var.settings.site_config.auto_heal_setting, "trigger", {}) != {} ? [1] : [] + content { + + dynamic "requests" { + for_each = lookup(var.settings.site_config.auto_heal_setting.trigger, "requests", {}) != {} ? [1] : [] + content { + count = lookup(var.settings.site_config.auto_heal_setting.trigger.requests, "count", null) + interval = lookup(var.settings.site_config.auto_heal_setting.trigger.requests, "interval", null) + } + } + + dynamic "slow_request" { + for_each = lookup(var.settings.site_config.auto_heal_setting.trigger, "slow_request", {}) != {} ? [ + 1 + ] : [] + content { + count = lookup(var.settings.site_config.auto_heal_setting.trigger.slow_request, "count", null) + interval = lookup(var.settings.site_config.auto_heal_setting.trigger.slow_request, "interval", null) + time_taken = lookup(var.settings.site_config.auto_heal_setting.trigger.slow_request, "time_taken", null) + path = lookup(var.settings.site_config.auto_heal_setting.trigger.slow_request, "path", null) + } + } + + dynamic "status_code" { + for_each = lookup(var.settings.site_config.auto_heal_setting.trigger, "status_code", {}) != {} ? [ + 1 + ] : [] + content { + count = lookup(var.settings.site_config.auto_heal_setting.trigger.status_code, "count", null) + interval = lookup(var.settings.site_config.auto_heal_setting.trigger.status_code, "interval", null) + status_code_range = lookup(var.settings.site_config.auto_heal_setting.trigger.status_code, "status_code_range", null) + sub_status = lookup(var.settings.site_config.auto_heal_setting.trigger.status_code, "sub_status", null) + win32_status_code = lookup(var.settings.site_config.auto_heal_setting.trigger.status_code, "win32_status_code", null) + path = lookup(var.settings.site_config.auto_heal_setting.trigger.status_code, "path", null) + } + } + } + } + } + } + } + } + + app_settings = local.app_settings + + dynamic "connection_string" { + for_each = var.connection_string + + content { + name = connection_string.value.name + type = connection_string.value.type + value = connection_string.value.value + } + } + dynamic "auth_settings" { + for_each = lookup(var.settings, "auth_settings", {}) != {} ? [1] : [] + + content { + enabled = lookup(var.settings.auth_settings, "enabled", false) + additional_login_parameters = lookup(var.settings.auth_settings, "additional_login_parameters ", null) + allowed_external_redirect_urls = lookup(var.settings.auth_settings, "allowed_external_redirect_urls", null) + default_provider = lookup(var.settings.auth_settings, "default_provider", null) + issuer = lookup(var.settings.auth_settings, "issuer", null) + runtime_version = lookup(var.settings.auth_settings, "runtime_version", null) + token_refresh_extension_hours = lookup(var.settings.auth_settings, "token_refresh_extension_hours", null) + token_store_enabled = lookup(var.settings.auth_settings, "token_store_enabled", null) + unauthenticated_client_action = lookup(var.settings.auth_settings, "unauthenticated_client_action", null) + + dynamic "active_directory" { + for_each = lookup(var.settings.auth_settings, "active_directory", {}) != {} ? [1] : [] + + content { + client_id = can(var.settings.auth_settings.active_directory.client_id_key) ? var.azuread_applications[try(var.settings.auth_settings.active_directory.client_id_lz_key, var.client_config.landingzone_key)][var.settings.auth_settings.active_directory.client_id_key].application_id : var.settings.auth_settings.active_directory.client_id + client_secret = can(var.settings.auth_settings.active_directory.client_secret_key) ? var.azuread_service_principal_passwords[try(var.settings.auth_settings.active_directory.client_secret_lz_key, var.client_config.landingzone_key)][var.settings.auth_settings.active_directory.client_secret_key].service_principal_password : try(var.settings.auth_settings.active_directory.client_secret, null) + allowed_audiences = lookup(var.settings.auth_settings.active_directory, "allowed_audiences", null) + } + } + + dynamic "facebook" { + for_each = lookup(var.settings.auth_settings, "facebook", {}) != {} ? [1] : [] + + content { + app_id = var.settings.auth_settings.facebook.app_id + app_secret = var.settings.auth_settings.facebook.app_secret + oauth_scopes = lookup(var.settings.auth_settings.facebook, "oauth_scopes", null) + } + } + + dynamic "google" { + for_each = lookup(var.settings.auth_settings, "google", {}) != {} ? [1] : [] + + content { + client_id = var.settings.auth_settings.google.client_id + client_secret = var.settings.auth_settings.google.client_secret + oauth_scopes = lookup(var.settings.auth_settings.google, "oauth_scopes", null) + } + } + + dynamic "microsoft" { + for_each = lookup(var.settings.auth_settings, "microsoft", {}) != {} ? [1] : [] + + content { + client_id = var.settings.auth_settings.microsoft.client_id + client_secret = var.settings.auth_settings.microsoft.client_secret + oauth_scopes = lookup(var.settings.auth_settings.microsoft, "oauth_scopes", null) + } + } + + dynamic "twitter" { + for_each = lookup(var.settings.auth_settings, "twitter", {}) != {} ? [1] : [] + + content { + consumer_key = var.settings.auth_settings.twitter.consumer_key + consumer_secret = var.settings.auth_settings.twitter.consumer_secret + } + } + } + } + dynamic "auth_settings_v2" { + for_each = lookup(var.settings, "auth_settings_v2", {}) != {} ? [1] : [] + content { + auth_enabled = lookup(var.settings.auth_settings_v2, "auth_enabled", false) + runtime_version = lookup(var.settings.auth_settings_v2, "runtime_version", null) + config_file_path = lookup(var.settings.auth_settings_v2, "config_file_path", null) + require_authentication = lookup(var.settings.auth_settings_v2, "require_authentication", null) + unauthenticated_action = lookup(var.settings.auth_settings_v2, "unauthenticated_action", null) + default_provider = lookup(var.settings.auth_settings_v2, "default_provider", null) + excluded_paths = lookup(var.settings.auth_settings_v2, "excluded_paths", null) + require_https = lookup(var.settings.auth_settings_v2, "require_https", null) + http_route_api_prefix = lookup(var.settings.auth_settings_v2, "http_route_api_prefix", null) + forward_proxy_convention = lookup(var.settings.auth_settings_v2, "forward_proxy_convention", null) + forward_proxy_custom_host_header_name = lookup(var.settings.auth_settings_v2, "forward_proxy_custom_host_header_name", null) + forward_proxy_custom_scheme_header_name = lookup(var.settings.auth_settings_v2, "forward_proxy_custom_scheme_header_name", null) + dynamic "login" { + for_each = lookup(var.settings.auth_settings_v2, "login", {}) != {} ? [1] : [] + + content { + logout_endpoint = lookup(var.settings.auth_settings_v2.login, "logout_endpoint", null) + token_store_enabled = lookup(var.settings.auth_settings_v2.login, "token_store_enabled", null) + token_refresh_extension_time = lookup(var.settings.auth_settings_v2.login, "token_refresh_extension_time", null) + token_store_path = lookup(var.settings.auth_settings_v2.login, "token_store_path", null) + token_store_sas_setting_name = lookup(var.settings.auth_settings_v2.login, "token_store_sas_setting_name", null) + preserve_url_fragments_for_logins = lookup(var.settings.auth_settings_v2.login, "preserve_url_fragments_for_logins", null) + allowed_external_redirect_urls = lookup(var.settings.auth_settings_v2.login, "allowed_external_redirect_urls", null) + cookie_expiration_convention = lookup(var.settings.auth_settings_v2.login, "cookie_expiration_convention", null) + cookie_expiration_time = lookup(var.settings.auth_settings_v2.login, "cookie_expiration_time", null) + validate_nonce = lookup(var.settings.auth_settings_v2.login, "validate_nonce", null) + nonce_expiration_time = lookup(var.settings.auth_settings_v2.login, "nonce_expiration_time", null) + } + } + dynamic "apple_v2" { + for_each = lookup(var.settings.auth_settings_v2, "apple_v2", {}) != {} ? [1] : [] + + content { + client_id = can(var.settings.auth_settings_v2.apple_v2.client_id_key) ? var.azuread_applications[try(var.settings.auth_settings_v2.apple_v2.client_id_lz_key, var.client_config.landingzone_key)][var.settings.auth_settings_v2.apple_v2.client_id_key].application_id : var.settings.auth_settings_v2.apple_v2.client_id + client_secret_setting_name = var.settings.auth_settings_v2.apple_v2.client_secret_setting_name + login_scopes = lookup(var.settings.auth_settings_v2.apple_v2, "login_scopes", null) + } + } + dynamic "azure_static_web_app_v2" { + for_each = lookup(var.settings.auth_settings_v2, "azure_static_web_app_v2", {}) != {} ? [1] : [] + + content { + client_id = can(var.settings.auth_settings_v2.azure_static_web_app_v2.client_id_key) ? var.azuread_applications[try(var.settings.auth_settings_v2.azure_static_web_app_v2.client_id_lz_key, var.client_config.landingzone_key)][var.settings.auth_settings_v2.azure_static_web_app_v2.client_id_key].application_id : var.settings.auth_settings_v2.azure_static_web_app_v2.client_id + } + } + dynamic "facebook_v2" { + for_each = lookup(var.settings.auth_settings_v2, "facebook_v2_v2", {}) != {} ? [1] : [] + + content { + app_id = can(var.settings.auth_settings_v2.facebook_v2.app_id_key) ? var.azuread_applications[try(var.settings.auth_settings_v2.facebook_v2.app_id_lz_key, var.client_config.landingzone_key)][var.settings.auth_settings_v2.facebook_v2.app_id_key].application_id : var.settings.auth_settings_v2.facebook_v2.app_id + app_secret_setting_name = var.settings.auth_settings_v2.facebook_v2.app_secret_setting_name + graph_api_version = lookup(var.settings.auth_settings_v2.facebook_v2, "graph_api_version", null) + login_scopes = lookup(var.settings.auth_settings_v2.facebook_v2, "login_scopes", null) + } + } + dynamic "github_v2" { + for_each = lookup(var.settings.auth_settings_v2, "github_v2", {}) != {} ? [1] : [] + content { + client_id = can(var.settings.auth_settings_v2.github_v2.client_id_key) ? var.azuread_applications[try(var.settings.auth_settings_v2.github_v2.client_id_lz_key, var.client_config.landingzone_key)][var.settings.auth_settings_v2.github_v2.client_id_key].application_id : var.settings.auth_settings_v2.github_v2.client_id + client_secret_setting_name = var.settings.auth_settings_v2.github_v2.client_secret_setting_name + login_scopes = lookup(var.settings.auth_settings_v2.github_v2, "login_scopes", null) + } + } + dynamic "google_v2" { + for_each = lookup(var.settings.auth_settings_v2, "google_v2", {}) != {} ? [1] : [] + + content { + client_id = can(var.settings.auth_settings_v2.google_v2.client_id_key) ? var.azuread_applications[try(var.settings.auth_settings_v2.google_v2.client_id_lz_key, var.client_config.landingzone_key)][var.settings.auth_settings_v2.google_v2.client_id_key].application_id : var.settings.auth_settings_v2.google_v2.client_id + client_secret_setting_name = var.settings.auth_settings_v2.google_v2.client_secret_setting_name + allowed_audiences = lookup(var.settings.auth_settings_v2.google_v2, "allowed_audiences", null) + login_scopes = lookup(var.settings.auth_settings_v2.google_v2, "login_scopes", null) + } + } + dynamic "microsoft_v2" { + for_each = lookup(var.settings.auth_settings_v2, "microsoft_v2", {}) != {} ? [1] : [] + + content { + client_id = can(var.settings.auth_settings_v2.microsoft_v2.client_id_key) ? var.azuread_applications[try(var.settings.auth_settings_v2.microsoft_v2.client_id_lz_key, var.client_config.landingzone_key)][var.settings.auth_settings_v2.microsoft_v2.client_id_key].application_id : var.settings.auth_settings_v2.microsoft_v2.client_id + client_secret_setting_name = var.settings.auth_settings_v2.microsoft_v2.client_secret_setting_name + allowed_audiences = lookup(var.settings.auth_settings_v2.microsoft_v2, "allowed_audiences", null) + login_scopes = lookup(var.settings.auth_settings_v2.microsoft_v2, "login_scopes", null) + } + } + dynamic "twitter_v2" { + for_each = lookup(var.settings.auth_settings_v2, "twitter_v2", {}) != {} ? [1] : [] + + content { + consumer_key = var.settings.auth_settings_v2.twitter_v2.consumer_key + consumer_secret_setting_name = var.settings.auth_settings_v2.twitter_v2.consumer_secret + } + } + dynamic "active_directory_v2" { + for_each = lookup(var.settings.auth_settings_v2, "active_directory_v2", {}) != {} ? [1] : [] + + content { + client_id = can(var.settings.auth_settings_v2.active_directory_v2.client_id_key) ? var.azuread_applications[try(var.settings.auth_settings_v2.active_directory_v2.client_id_lz_key, var.client_config.landingzone_key)][var.settings.auth_settings_v2.active_directory_v2.client_id_key].application_id : var.settings.auth_settings_v2.active_directory_v2.client_id + tenant_auth_endpoint = var.settings.auth_settings_v2.active_directory_v2.tenant_auth_endpoint + client_secret_setting_name = can(var.settings.auth_settings_v2.active_directory_v2.client_secret_setting_name_key) ? var.azuread_service_principal_passwords[try(var.settings.auth_settings_v2.active_directory_v2.client_secret_setting_name.lz_key, var.client_config.landingzone_key)][var.settings.auth_settings_v2.active_directory_v2.client_secret_setting_name.key].service_principal_password : try(var.settings.auth_settings_v2.active_directory_v2.client_secret_setting_name, null) + allowed_audiences = lookup(var.settings.auth_settings_v2.active_directory_v2, "allowed_audiences", null) + jwt_allowed_groups = lookup(var.settings.auth_settings_v2.active_directory_v2, "jwt_allowed_groups ", null) + jwt_allowed_client_applications = lookup(var.settings.auth_settings_v2.active_directory_v2, "jwt_allowed_client_applications ", null) + www_authentication_disabled = lookup(var.settings.auth_settings_v2.active_directory_v2, "www_authentication_disabled ", null) + allowed_groups = lookup(var.settings.auth_settings_v2.active_directory_v2, "allowed_groups", null) + allowed_identities = lookup(var.settings.auth_settings_v2.active_directory_v2, "allowed_identities", null) + allowed_applications = lookup(var.settings.auth_settings_v2.active_directory_v2, "allowed_applications", null) + login_parameters = lookup(var.settings.auth_settings_v2.active_directory_v2, "login_parameters", null) + } + } + } + } + + dynamic "storage_account" { + for_each = lookup(var.settings, "storage_account", {}) + content { + name = storage_account.value.name + type = storage_account.value.type + account_name = can(storage_account.value.account_key) ? var.storage_accounts[try(storage_account.value.lz_key, var.client_config.landingzone_key)][storage_account.value.account_key].name : try(storage_account.value.account_name, null) + share_name = storage_account.value.share_name + access_key = can(storage_account.value.account_key) ? var.storage_accounts[try(storage_account.value.lz_key, var.client_config.landingzone_key)][storage_account.value.account_key].primary_access_key : try(storage_account.value.access_key, null) + mount_path = lookup(storage_account.value, "mount_path", null) + } + } + + dynamic "backup" { + for_each = lookup(var.settings, "backup", {}) != {} ? [1] : [] + + content { + name = var.settings.backup.name + enabled = var.settings.backup.enabled + storage_account_url = try(var.settings.backup.storage_account_url, local.backup_sas_url) + + dynamic "schedule" { + for_each = lookup(var.settings.backup, "schedule", {}) != {} ? [1] : [] + + content { + frequency_interval = var.settings.backup.schedule.frequency_interval + frequency_unit = lookup(var.settings.backup.schedule, "frequency_unit", null) + keep_at_least_one_backup = lookup(var.settings.backup.schedule, "keep_at_least_one_backup", null) + retention_period_days = lookup(var.settings.backup.schedule, "retention_period_days", null) + start_time = lookup(var.settings.backup.schedule, "start_time", null) + } + } + } + } + + dynamic "logs" { + for_each = lookup(var.settings, "logs", {}) != {} ? [1] : [] + + content { + detailed_error_messages = try(var.settings.logs.detailed_error_messages, null) + failed_request_tracing = try(var.settings.logs.failed_request_tracing, null) + + dynamic "application_logs" { + for_each = lookup(var.settings.logs, "application_logs", {}) != {} ? [1] : [] + + content { + file_system_level = try(var.settings.logs.application_logs.file_system_level, null) + + dynamic "azure_blob_storage" { + for_each = lookup(var.settings.logs.application_logs, "azure_blob_storage", {}) != {} ? [1] : [] + + content { + level = var.settings.logs.application_logs.azure_blob_storage.level + sas_url = try(var.settings.logs.application_logs.azure_blob_storage.sas_url, local.logs_sas_url) + retention_in_days = var.settings.logs.application_logs.azure_blob_storage.retention_in_days + } + } + } + } + + dynamic "http_logs" { + for_each = lookup(var.settings.logs, "http_logs", {}) != {} ? [1] : [] + + content { + dynamic "azure_blob_storage" { + for_each = lookup(var.settings.logs.http_logs, "azure_blob_storage", {}) != {} ? [1] : [] + + content { + sas_url = try(var.settings.logs.http_logs.azure_blob_storage.sas_url, local.http_logs_sas_url) + retention_in_days = var.settings.logs.http_logs.azure_blob_storage.retention_in_days + } + } + dynamic "file_system" { + for_each = lookup(var.settings.logs.http_logs, "file_system", {}) != {} ? [1] : [] + + content { + retention_in_days = var.settings.logs.http_logs.file_system.retention_in_days + retention_in_mb = var.settings.logs.http_logs.file_system.retention_in_mb + } + } + } + } + } + } +} + + +resource "azurerm_app_service_virtual_network_swift_connection" "vnet_config" { + depends_on = [azurerm_windows_web_app.windows_web_apps] + count = lookup(var.settings, "subnet_key", null) == null && lookup(var.settings, "subnet_id", null) == null && try(var.settings.virtual_network_subnet_id, null) == null ? 0 : 1 + app_service_id = azurerm_windows_web_app.windows_web_apps.id + subnet_id = coalesce( + try(var.remote_objects.subnets[var.settings.subnet_key].id, null), + try(var.settings.subnet_id, null) + ) +} + diff --git a/modules/webapps/windows_webapps/output.tf b/modules/webapps/windows_webapps/output.tf new file mode 100644 index 0000000000..c111dfd52e --- /dev/null +++ b/modules/webapps/windows_webapps/output.tf @@ -0,0 +1,20 @@ +output "id" { + value = azurerm_windows_web_app.windows_web_apps.id + description = "The ID of the App Service." +} +output "default_hostname" { + value = azurerm_windows_web_app.windows_web_apps.default_hostname + description = "The Default Hostname associated with the Linux Web App" +} +output "outbound_ip_addresses" { + value = azurerm_windows_web_app.windows_web_apps.outbound_ip_addresses + description = "A comma separated list of outbound IP addresses" +} +output "possible_outbound_ip_addresses" { + value = azurerm_windows_web_app.windows_web_apps.possible_outbound_ip_addresses + description = "A comma separated list of outbound IP addresses. not all of which are necessarily in use" +} +output "identity" { + value = try(azurerm_windows_web_app.windows_web_apps.identity.0.principal_id, null) + description = "The identity id of the windows web app." +} \ No newline at end of file diff --git a/modules/webapps/windows_webapps/private_endpoint.tf b/modules/webapps/windows_webapps/private_endpoint.tf new file mode 100644 index 0000000000..99a1bb5849 --- /dev/null +++ b/modules/webapps/windows_webapps/private_endpoint.tf @@ -0,0 +1,16 @@ +module "private_endpoint" { + source = "../../networking/private_endpoint" + for_each = var.private_endpoints + + resource_id = azurerm_windows_web_app.windows_web_apps.id + name = each.value.name + location = local.location + resource_group_name = local.resource_group_name + subnet_id = can(each.value.subnet_id) ? each.value.subnet_id : var.vnets[try(each.value.lz_key, var.client_config.landingzone_key)][each.value.vnet_key].subnets[each.value.subnet_key].id + settings = each.value + global_settings = var.global_settings + tags = local.tags + base_tags = var.base_tags + private_dns = var.private_dns + client_config = var.client_config +} \ No newline at end of file diff --git a/modules/webapps/windows_webapps/storage_account.tf b/modules/webapps/windows_webapps/storage_account.tf new file mode 100644 index 0000000000..20e35258fb --- /dev/null +++ b/modules/webapps/windows_webapps/storage_account.tf @@ -0,0 +1,94 @@ +data "azurerm_storage_account" "backup_storage_account" { + count = can(var.settings.backup) ? 1 : 0 + + name = local.backup_storage_account.name + resource_group_name = local.backup_storage_account.resource_group_name +} + +data "azurerm_storage_account_blob_container_sas" "backup" { + count = can(var.settings.backup) ? 1 : 0 + + connection_string = data.azurerm_storage_account.backup_storage_account.0.primary_connection_string + container_name = local.backup_storage_account.containers[var.settings.backup.container_key].name + https_only = true + + start = time_rotating.sas[0].id + expiry = timeadd(time_rotating.sas[0].id, format("%sh", var.settings.backup.sas_policy.expire_in_days * 24)) + + permissions { + read = true + add = true + create = true + write = true + delete = true + list = true + } +} + +data "azurerm_storage_account_blob_container_sas" "logs" { + count = can(var.settings.logs) ? 1 : 0 + + connection_string = data.azurerm_storage_account.backup_storage_account.0.primary_connection_string + container_name = local.logs_storage_account.containers[var.settings.logs.container_key].name + https_only = true + + start = time_rotating.logs_sas[0].id + expiry = timeadd(time_rotating.logs_sas[0].id, format("%sh", var.settings.logs.sas_policy.expire_in_days * 24)) + + permissions { + read = true + add = true + create = true + write = true + delete = true + list = true + } +} + + +data "azurerm_storage_account_blob_container_sas" "http_logs" { + count = can(var.settings.logs.http_logs) ? 1 : 0 + + connection_string = data.azurerm_storage_account.backup_storage_account.0.primary_connection_string + container_name = local.http_logs_storage_account.containers[var.settings.logs.http_logs.container_key].name + https_only = true + + start = time_rotating.http_logs_sas[0].id + expiry = timeadd(time_rotating.http_logs_sas[0].id, format("%sh", var.settings.logs.http_logs.sas_policy.expire_in_days * 24)) + + permissions { + read = true + add = true + create = true + write = true + delete = true + list = true + } +} + +resource "time_rotating" "sas" { + count = can(var.settings.backup.sas_policy) ? 1 : 0 + + rotation_minutes = lookup(var.settings.backup.sas_policy.rotation, "mins", null) + rotation_days = lookup(var.settings.backup.sas_policy.rotation, "days", null) + rotation_months = lookup(var.settings.backup.sas_policy.rotation, "months", null) + rotation_years = lookup(var.settings.backup.sas_policy.rotation, "years", null) +} + +resource "time_rotating" "logs_sas" { + count = can(var.settings.logs.sas_policy) ? 1 : 0 + + rotation_minutes = lookup(var.settings.logs.sas_policy.rotation, "mins", null) + rotation_days = lookup(var.settings.logs.sas_policy.rotation, "days", null) + rotation_months = lookup(var.settings.logs.sas_policy.rotation, "months", null) + rotation_years = lookup(var.settings.logs.sas_policy.rotation, "years", null) +} + +resource "time_rotating" "http_logs_sas" { + count = can(var.settings.logs.http_logs.sas_policy) ? 1 : 0 + + rotation_minutes = lookup(var.settings.logs.http_logs.sas_policy.rotation, "mins", null) + rotation_days = lookup(var.settings.logs.http_logs.sas_policy.rotation, "days", null) + rotation_months = lookup(var.settings.logs.http_logs.sas_policy.rotation, "months", null) + rotation_years = lookup(var.settings.logs.http_logs.sas_policy.rotation, "years", null) +} diff --git a/modules/webapps/windows_webapps/variables.tf b/modules/webapps/windows_webapps/variables.tf new file mode 100644 index 0000000000..d28664edb5 --- /dev/null +++ b/modules/webapps/windows_webapps/variables.tf @@ -0,0 +1,89 @@ +variable "app_service_plan_id" { +} + +variable "app_settings" { + default = null +} + +variable "application_insight" { + default = null +} + +variable "azuread_applications" {} + +variable "azuread_service_principal_passwords" {} + +variable "base_tags" { + description = "Base tags for the resource to be inherited from the resource group." + type = bool +} + +variable "client_config" { + description = "Client configuration object (see module README.md)." +} + +variable "combined_objects" { + default = {} +} + +variable "connection_string" { + default = {} +} + +variable "diagnostic_profiles" { + default = {} +} + +variable "diagnostics" { + default = null +} + +variable "dynamic_app_settings" { + default = {} +} + +variable "global_settings" { + description = "Global settings object (see module README.md)" +} + +variable "identity" { + default = null +} + +variable "location" { + description = "(Required) Resource Location" + default = null +} + +variable "name" { + description = "(Required) Name of the App Service" +} + +variable "private_dns" {} + +variable "private_endpoints" {} + +variable "remote_objects" { + default = null +} + +variable "resource_group" { + description = "Resource group object to deploy the virtual machine" +} + +variable "resource_group_name" { + description = "Resource group object to deploy the virtual machine" + default = null +} + +variable "settings" {} + +variable "storage_accounts" { + default = {} +} + +variable "subnet_id" {} + +variable "virtual_subnets" {} + +variable "vnets" {}