diff --git a/function_app/README.md b/function_app/README.md index 21faed1b..5654ad80 100644 --- a/function_app/README.md +++ b/function_app/README.md @@ -7,113 +7,92 @@ Module that allows the creation of an Azure function app. ![architecture](./docs/module-arch.drawio.png) ## How to use it +Use the example Terraform template, saved in `terraform-azurerm-v3/tree/azurerm_linux_function_app_migration/tests`, to test this module. -```ts -# -# APP CONFIGURATION -# - -locals { - function_app = { - app_settings_common = { - FUNCTIONS_WORKER_RUNTIME = "python" - WEBSITE_RUN_FROM_PACKAGE = "1" - WEBSITE_VNET_ROUTE_ALL = "1" - WEBSITE_DNS_SERVER = "168.63.129.16" - FUNCTIONS_WORKER_PROCESS_COUNT = 1 - } - app_settings_1 = { - } - app_settings_2 = { - } - } - - func_python = { - app_settings_common = local.function_app.app_settings_common - app_settings_1 = { - } - app_settings_2 = { - } - } -} - -# #tfsec:ignore:azure-storage-queue-services-logging-enabled:exp:2022-05-01 # already ignored, maybe a bug in tfsec -module "func_python" { - source = "git::https://github.com/pagopa/terraform-azurerm-v3.git//function_app?ref=v3.15.0" - - count = var.function_python_diego_enabled ? 1 : 0 - - resource_group_name = azurerm_resource_group.funcs_diego_rg.name - name = "${local.project}-fn-py" - location = var.location - health_check_path = "/api/v1/info" - - os_type = "linux" - linux_fx_version = "python|3.9" - runtime_version = "~4" - - always_on = true - application_insights_instrumentation_key = data.azurerm_application_insights.application_insights.instrumentation_key - - app_service_plan_id = azurerm_app_service_plan.funcs_diego[0].id - - app_settings = merge( - local.func_python.app_settings_common, {} - ) +## How to migrate from ```azurerm_function_app``` to ```azurerm_linux_function_app``` +The following script will remove and import the deprecated resources as new ones. - subnet_id = module.funcs_diego_snet.id - - allowed_subnets = [ - module.funcs_diego_snet.id, - ] - - tags = var.tags +``` +#!/bin/bash + +function removeAndImport() { + # + # Remove and import a Terraform resource + # + local old_resource_name="$1" + # Square brackets are not processed normally by grep, otherwise + esc_old_resource_name=$(echo "$old_resource_name" | sed 's/\[/\\[/g; s/\]/\\]/g') + + local new_resource_name="$2" + + if [ -z "$old_resource_name" ] || [ -z "$new_resource_name" ]; then + echo "You need to define the resources to be removed and imported in order to proceed" + exit 1 + fi + if [ "$(terraform show | grep $esc_old_resource_name)" ]; then + # Get the resource ID + function_app_id=$(terraform show -json | jq --arg resource "$old_resource_name" '.values.root_module.child_modules[].resources[] | select(.address==$resource).values.id' | tr -d '"') + if [ $? -eq 0 ]; then + # Remove the resource from the state file + terraform state rm "$old_resource_name" + if [ $? -eq 0 ]; then + # Import the resource as "module.func_python.azurerm_linux_function_app.this" + terraform import $new_resource_name $function_app_id + if [ $? -eq 0 ]; then + echo "Successfully imported the function app resource as a Linux function app with ID: $function_app_id" + else + echo "I can't import the resource $new_resource_name" + fi + else + echo "I can't remove the resource $old_resource_name from your Terraform state" + fi + else + echo "I can't find the resource $old_resource_name in your Terraform state" + fi + else + echo "The $old_resource_name resource was not found in the state file." + fi } -module "func_python_staging_slot" { - source = "git::https://github.com/pagopa/terraform-azurerm-v3.git//function_app_slot?ref=v3.15.0" - - count = var.function_python_diego_enabled ? 1 : 0 +# Check if the "Terraform" and "jq" commands are available +if ! which terraform &> /dev/null && which jq &> /dev/null; then + echo "Please install terraform and jq before proceeding." + exit 1 +fi - name = "staging" - location = var.location - resource_group_name = azurerm_resource_group.funcs_diego_rg.name - function_app_name = module.func_python[0].name - function_app_id = module.func_python[0].id - app_service_plan_id = module.func_python[0].app_service_plan_id - health_check_path = "/api/v1/info" +removeAndImport "module.func_python.azurerm_function_app.this" "module.func_python.azurerm_linux_function_app.this" - storage_account_name = module.func_python[0].storage_account.name - storage_account_access_key = module.func_python[0].storage_account.primary_access_key +removeAndImport "module.func_python.azurerm_app_service_plan.this[0]" "module.func_python.azurerm_service_plan.this[0]" +``` - os_type = "linux" - linux_fx_version = "python|3.9" - always_on = "true" - runtime_version = "~4" - application_insights_instrumentation_key = data.azurerm_application_insights.application_insights.instrumentation_key +## Note about migrating from ```azurerm_function_app``` to ```azurerm_linux_function_app``` - app_settings = merge( - local.func_python.app_settings_common, {} - ) +Since the resource `azurerm_function_app` has been deprecated in version 3.0 of the AzureRM provider, the newer `azurerm_linux_function_app` resource is used in this module, thus the following variables have been removed since they are not used anymore: +- os_type +- app_service_plan_info/sku_tier +- linux_fx_version - subnet_id = module.funcs_diego_snet.id +## How to configure the Linux framework - allowed_subnets = [ - module.funcs_diego_snet.id, - ] +You need to specify **only** one variable of the following list: +- 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 (In-Preview). +- 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.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? - tags = var.tags -} -``` +Of course, the values listed above may change in the future, so please check which ones are current. ## Migration from v2 Output for resource `azurerm_function_app_host_keys` changed -See [Generic resorce migration](../docs/MIGRATION_GUIDE_GENERIC_RESOURCES.md) +See [Generic resource migration](../.docs/MIGRATION_GUIDE_GENERIC_RESOURCES.md) - - +======= ## Requirements | Name | Version | @@ -138,13 +117,13 @@ See [Generic resorce migration](../docs/MIGRATION_GUIDE_GENERIC_RESOURCES.md) | Name | Type | |------|------| -| [azurerm_app_service_plan.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/app_service_plan) | resource | | [azurerm_app_service_virtual_network_swift_connection.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/app_service_virtual_network_swift_connection) | resource | -| [azurerm_function_app.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/function_app) | resource | +| [azurerm_linux_function_app.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/linux_function_app) | resource | | [azurerm_monitor_metric_alert.function_app_health_check](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_metric_alert) | resource | | [azurerm_private_endpoint.blob](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_endpoint) | resource | | [azurerm_private_endpoint.queue](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_endpoint) | resource | | [azurerm_private_endpoint.table](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_endpoint) | resource | +| [azurerm_service_plan.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/service_plan) | resource | | [azurerm_storage_container.internal_container](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_container) | resource | | [azurerm_storage_management_policy.internal_deleteafterdays](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_management_policy) | resource | | [azurerm_storage_queue.internal_queue](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_queue) | resource | @@ -159,12 +138,14 @@ See [Generic resorce migration](../docs/MIGRATION_GUIDE_GENERIC_RESOURCES.md) | [allowed\_subnets](#input\_allowed\_subnets) | List of subnet ids, The Virtual Network Subnet ID used for this IP Restriction. | `list(string)` | `[]` | no | | [always\_on](#input\_always\_on) | (Optional) Should the app be loaded at all times? Defaults to null. | `bool` | `null` | no | | [app\_service\_plan\_id](#input\_app\_service\_plan\_id) | The external app service plan id to associate to the function. If null a new plan is created, use app\_service\_plan\_info to configure it. | `string` | `null` | no | -| [app\_service\_plan\_info](#input\_app\_service\_plan\_info) | Allows to configurate the internal service plan |
object({
kind = string # The kind of the App Service Plan to create. Possible values are Windows (also available as App), Linux, elastic (for Premium Consumption) and FunctionApp (for a Consumption Plan).
sku_tier = string # Specifies the plan's pricing tier.
sku_size = string # Specifies the plan's instance size.
maximum_elastic_worker_count = number # The maximum number of total workers allowed for this ElasticScaleEnabled App Service Plan.
})
|
{
"kind": "elastic",
"maximum_elastic_worker_count": 1,
"sku_size": "EP1",
"sku_tier": "ElasticPremium"
}
| no | +| [app\_service\_plan\_info](#input\_app\_service\_plan\_info) | Allows to configurate the internal service plan |
object({
kind = string # The kind of the App Service Plan to create. Possible values are Windows (also available as App), Linux, elastic (for Premium Consumption) and FunctionApp (for a Consumption Plan).
sku_size = string # Specifies the plan's instance size.
maximum_elastic_worker_count = number # The maximum number of total workers allowed for this ElasticScaleEnabled App Service Plan.
})
|
{
"kind": "Linux",
"maximum_elastic_worker_count": 0,
"sku_size": "P1v3"
}
| no | | [app\_service\_plan\_name](#input\_app\_service\_plan\_name) | Name of the app service plan. If null it will be 'computed' | `string` | `null` | no | | [app\_settings](#input\_app\_settings) | n/a | `map(any)` | `{}` | no | | [application\_insights\_instrumentation\_key](#input\_application\_insights\_instrumentation\_key) | Application insights instrumentation key | `string` | n/a | yes | | [cors](#input\_cors) | n/a |
object({
allowed_origins = list(string) # A list of origins which should be able to make cross-origin calls. * can be used to allow all calls.
})
| `null` | no | +| [docker](#input\_docker) | ##################### Framework choice ##################### | `any` | `{}` | no | | [domain](#input\_domain) | Specifies the domain of the Function App. | `string` | `null` | no | +| [dotnet\_version](#input\_dotnet\_version) | n/a | `string` | `null` | no | | [enable\_healthcheck](#input\_enable\_healthcheck) | Enable the healthcheck alert. Default is true | `bool` | `true` | no | | [export\_keys](#input\_export\_keys) | n/a | `bool` | `false` | no | | [health\_check\_maxpingfailures](#input\_health\_check\_maxpingfailures) | Max ping failures allowed | `number` | `10` | no | @@ -172,11 +153,13 @@ See [Generic resorce migration](../docs/MIGRATION_GUIDE_GENERIC_RESOURCES.md) | [healthcheck\_threshold](#input\_healthcheck\_threshold) | The healthcheck threshold. If metric average is under this value, the alert will be triggered. Default is 50 | `number` | `50` | no | | [https\_only](#input\_https\_only) | (Required) Can the Function App only be accessed via HTTPS?. Defaults true | `bool` | `true` | no | | [internal\_storage](#input\_internal\_storage) | n/a |
object({
enable = bool
private_endpoint_subnet_id = string
private_dns_zone_blob_ids = list(string)
private_dns_zone_queue_ids = list(string)
private_dns_zone_table_ids = list(string)
queues = list(string) # Queues names
containers = list(string) # Containers names
blobs_retention_days = number
})
|
{
"blobs_retention_days": 1,
"containers": [],
"enable": false,
"private_dns_zone_blob_ids": [],
"private_dns_zone_queue_ids": [],
"private_dns_zone_table_ids": [],
"private_endpoint_subnet_id": "dummy",
"queues": []
}
| no | -| [linux\_fx\_version](#input\_linux\_fx\_version) | (Required) Linux App Framework and version for the AppService, e.g. DOCKER\|(golang:latest). Use null if function app is on windows | `string` | n/a | yes | +| [java\_version](#input\_java\_version) | n/a | `string` | `null` | no | | [location](#input\_location) | n/a | `string` | n/a | yes | | [name](#input\_name) | (Required) Specifies the name of the Function App. Changing this forces a new resource to be created. | `string` | n/a | yes | -| [os\_type](#input\_os\_type) | (Optional) A string indicating the Operating System type for this function app. This value will be linux for Linux derivatives, or an empty string for Windows (default). When set to linux you must also set azurerm\_app\_service\_plan arguments as kind = Linux and reserved = true | `string` | `null` | no | +| [node\_version](#input\_node\_version) | n/a | `string` | `null` | no | +| [powershell\_core\_version](#input\_powershell\_core\_version) | n/a | `string` | `null` | no | | [pre\_warmed\_instance\_count](#input\_pre\_warmed\_instance\_count) | The number of pre-warmed instances for this function app. Only affects apps on the Premium plan. | `number` | `1` | no | +| [python\_version](#input\_python\_version) | n/a | `string` | `null` | no | | [resource\_group\_name](#input\_resource\_group\_name) | n/a | `string` | n/a | yes | | [runtime\_version](#input\_runtime\_version) | The runtime version associated with the Function App. Version ~3 is required for Linux Function Apps. | `string` | `"~3"` | no | | [storage\_account\_durable\_name](#input\_storage\_account\_durable\_name) | Storage account name only used by the durable function. If null it will be 'computed' | `string` | `null` | no | @@ -186,6 +169,8 @@ See [Generic resorce migration](../docs/MIGRATION_GUIDE_GENERIC_RESOURCES.md) | [system\_identity\_enabled](#input\_system\_identity\_enabled) | Enable the System Identity and create relative Service Principal. | `bool` | `false` | no | | [tags](#input\_tags) | n/a | `map(any)` | n/a | yes | | [use\_32\_bit\_worker\_process](#input\_use\_32\_bit\_worker\_process) | (Optional) Should the Function App run in 32 bit mode, rather than 64 bit mode? Defaults to false. | `bool` | `false` | no | +| [use\_custom\_runtime](#input\_use\_custom\_runtime) | n/a | `string` | `null` | no | +| [use\_dotnet\_isolated\_runtime](#input\_use\_dotnet\_isolated\_runtime) | n/a | `string` | `null` | no | | [vnet\_integration](#input\_vnet\_integration) | (optional) Enable vnet integration. Wheter it's true the subnet\_id should not be null. | `bool` | `true` | no | ## Outputs @@ -205,5 +190,4 @@ See [Generic resorce migration](../docs/MIGRATION_GUIDE_GENERIC_RESOURCES.md) | [storage\_account\_internal\_function](#output\_storage\_account\_internal\_function) | Storage account used by the function for internal operations. | | [storage\_account\_internal\_function\_name](#output\_storage\_account\_internal\_function\_name) | Storage account used by the function for internal operations. | | [storage\_account\_name](#output\_storage\_account\_name) | n/a | -| [system\_identity\_principal](#output\_system\_identity\_principal) | Service Principal of the System Identity generated by Azure. | - +| [system\_identity\_principal](#output\_system\_identity\_principal) | Service Principal of the System Identity generated by Azure. | \ No newline at end of file diff --git a/function_app/main.tf b/function_app/main.tf index 477c25d0..eafd960e 100644 --- a/function_app/main.tf +++ b/function_app/main.tf @@ -156,27 +156,6 @@ resource "azurerm_private_endpoint" "table" { tags = var.tags } -resource "azurerm_app_service_plan" "this" { - count = var.app_service_plan_id == null ? 1 : 0 - - name = var.app_service_plan_name != null ? var.app_service_plan_name : format("%s-plan", var.name) - location = var.location - resource_group_name = var.resource_group_name - kind = var.app_service_plan_info.kind - - sku { - tier = var.app_service_plan_info.sku_tier - size = var.app_service_plan_info.sku_size - # capacity is only for isolated envs - } - - maximum_elastic_worker_count = var.app_service_plan_info.kind == "elastic" ? var.app_service_plan_info.maximum_elastic_worker_count : null - reserved = var.app_service_plan_info.kind == "Linux" ? true : null - per_site_scaling = false - - tags = var.tags -} - locals { allowed_ips = [for ip in var.allowed_ips : { ip_address = ip, virtual_network_subnet_id = null }] allowed_subnets = [for s in var.allowed_subnets : { ip_address = null, virtual_network_subnet_id = s }] @@ -187,27 +166,107 @@ locals { internal_containers = var.internal_storage.enable ? var.internal_storage.containers : [] } -resource "azurerm_function_app" "this" { +# this datasource has been introduced within version 2.27.0 +data "azurerm_function_app_host_keys" "this" { + count = var.export_keys ? 1 : 0 + name = var.name resource_group_name = var.resource_group_name + depends_on = [azurerm_linux_function_app.this] +} + +# Manages an App Service Virtual Network Association +resource "azurerm_app_service_virtual_network_swift_connection" "this" { + count = var.vnet_integration ? 1 : 0 + + app_service_id = azurerm_linux_function_app.this.id + subnet_id = var.subnet_id +} + +resource "azurerm_monitor_metric_alert" "function_app_health_check" { + count = var.enable_healthcheck ? 1 : 0 + + name = "[${var.domain != null ? "${var.domain} | " : ""}${azurerm_linux_function_app.this.name}] Health Check Failed" + resource_group_name = var.resource_group_name + scopes = [azurerm_linux_function_app.this.id] + description = "Function availability is under threshold level. Runbook: -" + severity = 1 + frequency = "PT5M" + auto_mitigate = false + enabled = true + + criteria { + metric_namespace = "Microsoft.Web/sites" + metric_name = "HealthCheckStatus" + aggregation = "Average" + operator = "LessThan" + threshold = var.healthcheck_threshold + } + + dynamic "action" { + for_each = var.action + content { + action_group_id = action.value["action_group_id"] + webhook_properties = action.value["webhook_properties"] + } + } +} + +resource "azurerm_service_plan" "this" { + count = var.app_service_plan_id == null ? 1 : 0 + + name = var.app_service_plan_name != null ? var.app_service_plan_name : format("%s-plan", var.name) location = var.location - version = var.runtime_version - app_service_plan_id = var.app_service_plan_id != null ? var.app_service_plan_id : azurerm_app_service_plan.this[0].id + resource_group_name = var.resource_group_name + os_type = "Linux" + sku_name = var.app_service_plan_info.sku_size + + per_site_scaling_enabled = false + + tags = var.tags +} + +resource "azurerm_linux_function_app" "this" { + name = var.name + resource_group_name = var.resource_group_name + location = var.location + functions_extension_version = var.runtime_version + service_plan_id = var.app_service_plan_id != null ? var.app_service_plan_id : azurerm_service_plan.this[0].id # The backend storage account name which will be used by this Function App (such as the dashboard, logs) storage_account_name = module.storage_account.name storage_account_access_key = module.storage_account.primary_access_key https_only = var.https_only - os_type = var.os_type + client_certificate_enabled = var.client_certificate_enabled site_config { - min_tls_version = "1.2" + minimum_tls_version = "1.2" ftps_state = "Disabled" http2_enabled = true always_on = var.always_on pre_warmed_instance_count = var.pre_warmed_instance_count vnet_route_all_enabled = var.subnet_id == null ? false : true - use_32_bit_worker_process = var.use_32_bit_worker_process - linux_fx_version = var.linux_fx_version + use_32_bit_worker = var.use_32_bit_worker_process + application_insights_key = var.application_insights_instrumentation_key + + application_stack { + dotnet_version = var.dotnet_version + use_dotnet_isolated_runtime = var.use_dotnet_isolated_runtime + java_version = var.java_version + python_version = var.python_version + node_version = var.node_version + powershell_core_version = var.powershell_core_version + use_custom_runtime = var.use_custom_runtime + dynamic "docker" { + for_each = length(var.docker) > 0 ? [1] : [] + content { + registry_url = var.docker.registry_url + image_name = var.docker.image_name + image_tag = var.docker.image_tag + registry_username = var.docker.registry_username + registry_password = var.docker.registry_password + } + } + } dynamic "ip_restriction" { for_each = local.ip_restrictions @@ -238,7 +297,6 @@ resource "azurerm_function_app" "this" { # https://docs.microsoft.com/en-us/azure/azure-functions/functions-app-settings app_settings = merge( { - APPINSIGHTS_INSTRUMENTATIONKEY = var.application_insights_instrumentation_key # No downtime on slots swap WEBSITE_ADD_SITENAME_BINDINGS_IN_APPHOST_CONFIG = 1 # default value for health_check_path, override it in var.app_settings if needed @@ -256,7 +314,7 @@ resource "azurerm_function_app" "this" { var.app_settings, ) - enable_builtin_logging = false + builtin_logging_enabled = false dynamic "identity" { for_each = var.system_identity_enabled ? [1] : [] @@ -265,53 +323,18 @@ resource "azurerm_function_app" "this" { } } - tags = var.tags -} - -# this datasource has been introduced within version 2.27.0 -data "azurerm_function_app_host_keys" "this" { - count = var.export_keys ? 1 : 0 - - name = var.name - resource_group_name = var.resource_group_name - depends_on = [azurerm_function_app.this] -} - -# Manages an App Service Virtual Network Association -resource "azurerm_app_service_virtual_network_swift_connection" "this" { - count = var.vnet_integration ? 1 : 0 - - app_service_id = azurerm_function_app.this.id - subnet_id = var.subnet_id -} - - - -resource "azurerm_monitor_metric_alert" "function_app_health_check" { - count = var.enable_healthcheck ? 1 : 0 - - name = "[${var.domain != null ? "${var.domain} | " : ""}${azurerm_function_app.this.name}] Health Check Failed" - resource_group_name = var.resource_group_name - scopes = [azurerm_function_app.this.id] - description = "Function availability is under threshold level. Runbook: -" - severity = 1 - frequency = "PT5M" - auto_mitigate = false - enabled = true - - criteria { - metric_namespace = "Microsoft.Web/sites" - metric_name = "HealthCheckStatus" - aggregation = "Average" - operator = "LessThan" - threshold = var.healthcheck_threshold + lifecycle { + ignore_changes = [ + virtual_network_subnet_id + ] } - dynamic "action" { - for_each = var.action - content { - action_group_id = action.value["action_group_id"] - webhook_properties = action.value["webhook_properties"] - } + sticky_settings { + app_setting_names = concat( + ["SLOT_TASK_HUBNAME"], + var.sticky_settings, + ) } -} + + tags = var.tags +} \ No newline at end of file diff --git a/function_app/outputs.tf b/function_app/outputs.tf index 387a4169..412498b5 100644 --- a/function_app/outputs.tf +++ b/function_app/outputs.tf @@ -1,5 +1,5 @@ output "id" { - value = azurerm_function_app.this.id + value = azurerm_linux_function_app.this.id } output "name" { @@ -7,12 +7,12 @@ output "name" { } output "default_hostname" { - value = azurerm_function_app.this.default_hostname + value = azurerm_linux_function_app.this.default_hostname sensitive = true } output "possible_outbound_ip_addresses" { - value = azurerm_function_app.this.possible_outbound_ip_addresses + value = azurerm_linux_function_app.this.possible_outbound_ip_addresses } output "default_key" { @@ -31,11 +31,11 @@ output "primary_key" { } output "app_service_plan_id" { - value = var.app_service_plan_id != null ? var.app_service_plan_id : azurerm_app_service_plan.this[0].id + value = var.app_service_plan_id != null ? var.app_service_plan_id : azurerm_service_plan.this[0].id } output "app_service_plan_name" { - value = var.app_service_plan_id != null ? var.app_service_plan_name : azurerm_app_service_plan.this[0].name + value = var.app_service_plan_id != null ? var.app_service_plan_name : azurerm_service_plan.this[0].name } output "storage_account_name" { @@ -67,6 +67,6 @@ output "storage_account_internal_function" { } output "system_identity_principal" { - value = var.system_identity_enabled ? azurerm_function_app.this.identity[0].principal_id : null + value = var.system_identity_enabled ? azurerm_linux_function_app.this.identity[0].principal_id : null description = "Service Principal of the System Identity generated by Azure." } diff --git a/function_app/tests/README.md b/function_app/tests/README.md new file mode 100644 index 00000000..f70e4cb7 --- /dev/null +++ b/function_app/tests/README.md @@ -0,0 +1,9 @@ +# Test for Azure the function_app module + +Terraform template to test the Azure function_app module + + +## How to use it +- terraform init +- terraform plan +- terraform apply diff --git a/function_app/tests/main.tf b/function_app/tests/main.tf new file mode 100644 index 00000000..cde57e05 --- /dev/null +++ b/function_app/tests/main.tf @@ -0,0 +1,90 @@ +# +# APP CONFIGURATION +# + +resource "random_id" "function_id" { + byte_length = 4 +} + +resource "azurerm_application_insights" "example" { + name = "${var.project}-${random_id.function_id.hex}-appinsights" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + application_type = "web" +} + +resource "azurerm_resource_group" "example" { + name = "${var.project}-${random_id.function_id.hex}-rg" + location = var.location +} + +resource "azurerm_virtual_network" "example" { + name = "${var.project}-${random_id.function_id.hex}-vn" + address_space = var.address_space + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name +} + +resource "azurerm_subnet" "example" { + name = "${var.project}-${random_id.function_id.hex}-subnet" + resource_group_name = azurerm_resource_group.example.name + virtual_network_name = azurerm_virtual_network.example.name + address_prefixes = var.address_prefixes + + delegation { + name = "${var.project}-${random_id.function_id.hex}-delegation" + + service_delegation { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +locals { + function_app = { + app_settings_common = { + FUNCTIONS_WORKER_PROCESS_COUNT = 4 + } + app_settings_1 = { + } + app_settings_2 = { + } + } + + func_python = { + app_settings_common = local.function_app.app_settings_common + app_settings_1 = { + } + app_settings_2 = { + } + } +} + +# #tfsec:ignore:azure-storage-queue-services-logging-enabled:exp:2022-05-01 # already ignored, maybe a bug in tfsec +module "func_python" { + source = "../../function_app" + + resource_group_name = azurerm_resource_group.example.name + name = "${var.project}-${random_id.function_id.hex}-fn-py" + location = var.location + health_check_path = "/api/v1/info" + python_version = "3.9" + runtime_version = "~4" + + always_on = true + application_insights_instrumentation_key = azurerm_application_insights.example.instrumentation_key + + app_settings = merge( + local.func_python.app_settings_common, {} + ) + + subnet_id = azurerm_subnet.example.id + + allowed_subnets = [ + azurerm_subnet.example.id, + ] + + tags = merge(var.tags, + { Name = "${var.project}-${random_id.function_id.hex}_function_app" }) +} \ No newline at end of file diff --git a/function_app/tests/variables.tf b/function_app/tests/variables.tf new file mode 100644 index 00000000..237a3fbe --- /dev/null +++ b/function_app/tests/variables.tf @@ -0,0 +1,29 @@ +variable "address_prefixes" { + type = list(any) + default = ["10.0.1.0/26"] +} + +variable "address_space" { + type = list(any) + default = ["10.0.0.0/16"] +} + +variable "location" { + type = string + default = "westeurope" +} + +variable "project" { + type = string + default = "example" +} + +variable "tags" { + type = map(any) + description = "Tags for infrastructure resources." + default = { + CreatedBy = "Terraform" + Source = "https://github.com/pagopa/terraform-azurerm-v3" + } +} + diff --git a/function_app/tests/versions.tf b/function_app/tests/versions.tf new file mode 100644 index 00000000..772f1ca9 --- /dev/null +++ b/function_app/tests/versions.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.3.0" + + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">= 3.30.0, <= 3.43.0" + } + } +} + +provider "azurerm" { + features {} +} \ No newline at end of file diff --git a/function_app/variables.tf b/function_app/variables.tf index ddc790dc..fc094adb 100644 --- a/function_app/variables.tf +++ b/function_app/variables.tf @@ -68,7 +68,6 @@ variable "app_service_plan_id" { variable "app_service_plan_info" { type = object({ kind = string # The kind of the App Service Plan to create. Possible values are Windows (also available as App), Linux, elastic (for Premium Consumption) and FunctionApp (for a Consumption Plan). - sku_tier = string # Specifies the plan's pricing tier. sku_size = string # Specifies the plan's instance size. maximum_elastic_worker_count = number # The maximum number of total workers allowed for this ElasticScaleEnabled App Service Plan. }) @@ -76,10 +75,9 @@ variable "app_service_plan_info" { description = "Allows to configurate the internal service plan" default = { - kind = "elastic" - sku_tier = "ElasticPremium" - sku_size = "EP1" - maximum_elastic_worker_count = 1 + kind = "Linux" + sku_size = "P1v3" + maximum_elastic_worker_count = 0 } } @@ -101,11 +99,6 @@ variable "use_32_bit_worker_process" { default = false } -variable "linux_fx_version" { - type = string - description = "(Required) Linux App Framework and version for the AppService, e.g. DOCKER|(golang:latest). Use null if function app is on windows" -} - variable "application_insights_instrumentation_key" { type = string description = "Application insights instrumentation key" @@ -116,12 +109,6 @@ variable "app_settings" { default = {} } -variable "os_type" { - type = string - description = "(Optional) A string indicating the Operating System type for this function app. This value will be linux for Linux derivatives, or an empty string for Windows (default). When set to linux you must also set azurerm_app_service_plan arguments as kind = Linux and reserved = true" - default = null -} - variable "https_only" { type = bool description = "(Required) Can the Function App only be accessed via HTTPS?. Defaults true" @@ -211,6 +198,18 @@ variable "system_identity_enabled" { default = false } +variable "client_certificate_enabled" { + type = bool + description = "Should the function app use Client Certificates" + default = false +} + +variable "sticky_settings" { + type = list(string) + description = "(Optional) A list of app_setting names that the Linux Function App will not swap between Slots when a swap operation is triggered" + default = [] +} + # ------------------- # Alerts variables # ------------------- @@ -237,3 +236,38 @@ variable "action" { )) default = [] } +###################### +# Framework choice +###################### +variable "docker" { + type = any + default = {} +} +variable "dotnet_version" { + type = string + default = null +} +variable "use_dotnet_isolated_runtime" { + type = string + default = null +} +variable "java_version" { + type = string + default = null +} +variable "node_version" { + type = string + default = null +} +variable "python_version" { + type = string + default = null +} +variable "powershell_core_version" { + type = string + default = null +} +variable "use_custom_runtime" { + type = string + default = null +} \ No newline at end of file diff --git a/function_app/versions.tf b/function_app/versions.tf index 1b40c239..b0bab47e 100644 --- a/function_app/versions.tf +++ b/function_app/versions.tf @@ -8,3 +8,4 @@ terraform { } } } +