diff --git a/samples/web-app-sql-database/python/terraform/README.md b/samples/web-app-sql-database/python/terraform/README.md index 6c274b7..382ff11 100644 --- a/samples/web-app-sql-database/python/terraform/README.md +++ b/samples/web-app-sql-database/python/terraform/README.md @@ -2,8 +2,6 @@ This directory contains Terraform modules and a deployment script for provisioning Azure services in LocalStack for Azure. Refer to the [Azure Web App with Azure SQL Database](../README.md) guide for details about the sample application. -> **NOTE**: Terraform modules do not install Azure Key Vault. This will be fixed soon. - ## Prerequisites Before deploying this solution, ensure you have the following tools installed: @@ -32,13 +30,12 @@ For more information, see [Get started with the az tool on LocalStack](https://a The [main.tf](main.tf) Terraform module creates the following Azure resources: 1. [Azure Resource Group](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/manage-resource-groups-cli): Logical container for all resources in the sample. -1. [Azure Resource Group](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/manage-resource-groups-cli): Logical container for all resources 2. [Azure SQL Server](https://learn.microsoft.com/en-us/azure/azure-sql/database/sql-database-paas-overview): Logical server hosting one or more Azure SQL Databases. 3. [Azure SQL Database](https://learn.microsoft.com/en-us/azure/azure-sql/database/): The `PlannerDB` database storing relational vacation activity data. 4. [Azure App Service Plan](https://learn.microsoft.com/en-us/azure/app-service/overview-hosting-plans): The compute resource that hosts the web application. 5. [Azure Web App](https://learn.microsoft.com/en-us/azure/app-service/overview): Hosts the Python Flask single-page application (*Vacation Planner*), connected to Azure SQL Database. -6. [App Service Source Control](https://learn.microsoft.com/en-us/rest/api/appservice/web-apps/create-or-update-source-control?view=rest-appservice-2024-11-01): (Optional) Configures automatic deployment from a public GitHub repository. -7. [Azure Key Vault](https://learn.microsoft.com/en-us/azure/key-vault/general/overview): Stores the SQL connection string in a secret. +6. [Azure Key Vault](https://learn.microsoft.com/en-us/azure/key-vault/general/overview): Stores the SQL connection string as a secret and a self-signed certificate for HTTPS. +7. [App Service Source Control](https://learn.microsoft.com/en-us/rest/api/appservice/web-apps/create-or-update-source-control?view=rest-appservice-2024-11-01): (Optional) Configures automatic deployment from a public GitHub repository. The system implements a Vacation Planner web application that stores and retrieves activity data from Azure SQL Database. For more information, see [Azure Web App with Azure SQL Database](../README.md). diff --git a/samples/web-app-sql-database/python/terraform/deploy.sh b/samples/web-app-sql-database/python/terraform/deploy.sh index f9bde1b..50bb8a3 100755 --- a/samples/web-app-sql-database/python/terraform/deploy.sh +++ b/samples/web-app-sql-database/python/terraform/deploy.sh @@ -217,7 +217,7 @@ fi # Create the zip package of the web app echo "Creating zip package of the web app..." -zip -r "$ZIPFILE" app.py activities.py database.py static templates requirements.txt +zip -r "$ZIPFILE" app.py activities.py database.py certificates.py static templates requirements.txt # Deploy the web app echo "Deploying web app [$WEB_APP_NAME] with zip file [$ZIPFILE]..." diff --git a/samples/web-app-sql-database/python/terraform/main.tf b/samples/web-app-sql-database/python/terraform/main.tf index 24c0e9a..d1c1baa 100644 --- a/samples/web-app-sql-database/python/terraform/main.tf +++ b/samples/web-app-sql-database/python/terraform/main.tf @@ -5,8 +5,12 @@ locals { sql_server_name = "${var.prefix}-sqlserver-${var.suffix}" app_service_plan_name = "${var.prefix}-app-service-plan-${var.suffix}" web_app_name = "${var.prefix}-webapp-${var.suffix}" + key_vault_name = "${var.prefix}-kv-${var.suffix}" } +# Retrieve the current Azure client configuration +data "azurerm_client_config" "current" {} + # Create a resource group resource "azurerm_resource_group" "example" { name = local.resource_group_name @@ -111,10 +115,10 @@ resource "azurerm_linux_web_app" "example" { app_settings = { SCM_DO_BUILD_DURING_DEPLOYMENT = "true" ENABLE_ORYX_BUILD = "true" - SQL_SERVER = azurerm_mssql_server.example.fully_qualified_domain_name - SQL_DATABASE = azurerm_mssql_database.example.name - SQL_USERNAME = var.sql_database_username - SQL_PASSWORD = var.sql_database_password + KEY_VAULT_NAME = azurerm_key_vault.example.name + SECRET_NAME = azurerm_key_vault_secret.sql_connection_string.name + KEYVAULT_URI = azurerm_key_vault.example.vault_uri + CERT_NAME = azurerm_key_vault_certificate.example.name LOGIN_NAME = var.login_name } @@ -125,6 +129,80 @@ resource "azurerm_linux_web_app" "example" { } } +# Create a Key Vault +resource "azurerm_key_vault" "example" { + name = local.key_vault_name + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "standard" + enable_rbac_authorization = false + soft_delete_retention_days = 7 + tags = var.tags + + lifecycle { + ignore_changes = [ + tags + ] + } +} + +# Grant the Web App managed identity access to Key Vault secrets and certificates +resource "azurerm_key_vault_access_policy" "web_app" { + key_vault_id = azurerm_key_vault.example.id + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = azurerm_linux_web_app.example.identity[0].principal_id + + secret_permissions = [ + "Get", + "List", + ] + + certificate_permissions = [ + "Get", + ] +} + +# Create a Key Vault secret for SQL connection string +resource "azurerm_key_vault_secret" "sql_connection_string" { + name = var.secret_name + value = "Server=tcp:${azurerm_mssql_server.example.fully_qualified_domain_name},1433;Database=${azurerm_mssql_database.example.name};User ID=${var.sql_database_username};Password=${var.sql_database_password};Encrypt=yes;TrustServerCertificate=no;Connection Timeout=30;" + key_vault_id = azurerm_key_vault.example.id +} + +# Create a self-signed certificate in Key Vault +resource "azurerm_key_vault_certificate" "example" { + name = var.cert_name + key_vault_id = azurerm_key_vault.example.id + + certificate_policy { + issuer_parameters { + name = "Self" + } + + key_properties { + exportable = true + key_size = 2048 + key_type = "RSA" + reuse_key = false + } + + secret_properties { + content_type = "application/x-pkcs12" + } + + x509_certificate_properties { + subject = "CN=${var.cert_subject}" + validity_in_months = 12 + + key_usage = [ + "digitalSignature", + "keyEncipherment", + ] + } + } +} + # Deploy code from a public GitHub repo resource "azurerm_app_service_source_control" "example" { count = var.repo_url == "" ? 0 : 1 diff --git a/samples/web-app-sql-database/python/terraform/outputs.tf b/samples/web-app-sql-database/python/terraform/outputs.tf index b671a22..7d14b48 100644 --- a/samples/web-app-sql-database/python/terraform/outputs.tf +++ b/samples/web-app-sql-database/python/terraform/outputs.tf @@ -20,4 +20,16 @@ output "web_app_name" { output "web_app_url" { value = azurerm_linux_web_app.example.default_hostname +} + +output "key_vault_name" { + value = azurerm_key_vault.example.name +} + +output "key_vault_url" { + value = azurerm_key_vault.example.vault_uri +} + +output "sql_connection_string_secret_uri" { + value = azurerm_key_vault_secret.sql_connection_string.id } \ No newline at end of file diff --git a/samples/web-app-sql-database/python/terraform/variables.tf b/samples/web-app-sql-database/python/terraform/variables.tf index 6359f5f..d54f76f 100644 --- a/samples/web-app-sql-database/python/terraform/variables.tf +++ b/samples/web-app-sql-database/python/terraform/variables.tf @@ -349,6 +349,24 @@ variable "login_name" { default = "paolo" } +variable "secret_name" { + description = "(Optional) Specifies the name of the Key Vault secret for the SQL connection string." + type = string + default = "sql-connection-string" +} + +variable "cert_name" { + description = "(Optional) Specifies the name of the Key Vault certificate." + type = string + default = "webapp-cert" +} + +variable "cert_subject" { + description = "(Optional) Specifies the subject of the self-signed certificate." + type = string + default = "sample-web-app-sql" +} + variable "tags" { description = "(Optional) Specifies the tags to be applied to the resources." type = map(string)