From 3fa2a6cac74525658b1ca198520208564f0ca5c6 Mon Sep 17 00:00:00 2001 From: Marvin Buss Date: Wed, 19 Feb 2025 14:38:39 +0100 Subject: [PATCH 1/6] Add Postgresql Flexible Server Module --- .../terraform-postgresqlflexible.yml | 29 ++ .pre-commit-config.yaml | 3 + README.md | 1 + modules/postgresqlflexible/README.md | 259 +++++++++++++++++ modules/postgresqlflexible/README_footer.md | 0 modules/postgresqlflexible/README_header.md | 1 + modules/postgresqlflexible/connectivity.tf | 38 +++ modules/postgresqlflexible/data.tf | 5 + modules/postgresqlflexible/diagnostics.tf | 29 ++ modules/postgresqlflexible/main.tf | 89 ++++++ modules/postgresqlflexible/outputs.tf | 28 ++ modules/postgresqlflexible/terraform.tf | 14 + .../postgresqlflexible/tests/test.tftest.hcl | 47 ++++ modules/postgresqlflexible/variables.tf | 262 ++++++++++++++++++ 14 files changed, 805 insertions(+) create mode 100644 .github/workflows/terraform-postgresqlflexible.yml create mode 100644 modules/postgresqlflexible/README.md create mode 100644 modules/postgresqlflexible/README_footer.md create mode 100644 modules/postgresqlflexible/README_header.md create mode 100644 modules/postgresqlflexible/connectivity.tf create mode 100644 modules/postgresqlflexible/data.tf create mode 100644 modules/postgresqlflexible/diagnostics.tf create mode 100644 modules/postgresqlflexible/main.tf create mode 100644 modules/postgresqlflexible/outputs.tf create mode 100644 modules/postgresqlflexible/terraform.tf create mode 100644 modules/postgresqlflexible/tests/test.tftest.hcl create mode 100644 modules/postgresqlflexible/variables.tf diff --git a/.github/workflows/terraform-postgresqlflexible.yml b/.github/workflows/terraform-postgresqlflexible.yml new file mode 100644 index 0000000..00c856a --- /dev/null +++ b/.github/workflows/terraform-postgresqlflexible.yml @@ -0,0 +1,29 @@ +name: postgresqlflexible tests +on: + push: + branches: + - main + paths: + - "modules/postgresqlflexible/**" + - ".github/workflows/terraform-postgresqlflexible.yml" + + pull_request: + branches: + - main + paths: + - "modules/postgresqlflexible/**" + - ".github/workflows/terraform-postgresqlflexible.yml" + +jobs: + terraform_dev: + uses: ./.github/workflows/_terraformTestTemplate.yml + name: "Dev" + with: + environment: "dev" + config: "postgresqlflexible" + terraform_version: "1.10.4" + node_version: 20 + tenant_id: "37963dd4-f4e6-40f8-a7d6-24b97919e452" + subscription_id: "1fdab118-1638-419a-8b12-06c9543714a0" + secrets: + CLIENT_ID: ${{ secrets.CLIENT_ID }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7d73f6d..49dc94b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,6 +16,7 @@ exclude: | modules/fabricworkspace/README.md| modules/keyvault/README.md| modules/loganalytics/README.md| + modules/postgresqlflexible/README.md| modules/purview/README.md| modules/storage/README.md| modules/userassignedidentity/README.md| @@ -67,6 +68,8 @@ repos: args: ["-c", "./.terraform-docs.yml", "./modules/keyvault"] - id: terraform-docs-go args: ["-c", "./.terraform-docs.yml", "./modules/loganalytics"] + - id: terraform-docs-go + args: ["-c", "./.terraform-docs.yml", "./modules/postgresqlflexible"] - id: terraform-docs-go args: ["-c", "./.terraform-docs.yml", "./modules/purview"] - id: terraform-docs-go diff --git a/README.md b/README.md index c1b7b3e..f6629a7 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ This repository contains secure by default Terraform modules for Azure Services. - [Azure Databricks Workspace](/modules/databricksworkspace/) - [Azure Key Vault](/modules/keyvault/) - [Azure Log Analytics Workspace](/modules/loganalytics/) +- [Azure Postgresql Flexible Server](/modules/postgresqlflexible/) - [Azure Storage Account](/modules/storage/) - [Azure User Assigned Identity](/modules/userassignedidentity/) - [Azure Synapse Private Link Hub](/modules/synapseprivetlinkhub/) diff --git a/modules/postgresqlflexible/README.md b/modules/postgresqlflexible/README.md new file mode 100644 index 0000000..c889a22 --- /dev/null +++ b/modules/postgresqlflexible/README.md @@ -0,0 +1,259 @@ + +# Azure Postgresql Flexible Server Terraform Module + +## Documentation + + +## Requirements + +The following requirements are needed by this module: + +- [terraform](#requirement\_terraform) (>=0.12) + +- [azurerm](#requirement\_azurerm) (~> 4.0) + +- [time](#requirement\_time) (~> 0.9) + +## Modules + +No modules. + + + +## Required Inputs + +The following input variables are required: + +### [location](#input\_location) + +Description: Specifies the location of all resources. + +Type: `string` + +### [postgresql\_name](#input\_postgresql\_name) + +Description: Specifies the name of the postgresql flexible server. + +Type: `string` + +### [resource\_group\_name](#input\_resource\_group\_name) + +Description: Specifies the resource group name in which all resources will get deployed. + +Type: `string` + +### [subnet\_id](#input\_subnet\_id) + +Description: Specifies the resource id of a subnet in which the private endpoints get created. + +Type: `string` + +## Optional Inputs + +The following input variables are optional (have default values): + +### [connectivity\_delay\_in\_seconds](#input\_connectivity\_delay\_in\_seconds) + +Description: Specifies the delay in seconds after the private endpoint deployment (required for the DNS automation via Policies). + +Type: `number` + +Default: `120` + +### [customer\_managed\_key](#input\_customer\_managed\_key) + +Description: Specifies the customer managed key configurations. + +Type: + +```hcl +object({ + key_vault_id = string, + key_vault_key_versionless_id = string, + user_assigned_identity_id = string, + user_assigned_identity_client_id = string, + }) +``` + +Default: `null` + +### [diagnostics\_configurations](#input\_diagnostics\_configurations) + +Description: Specifies the diagnostic configuration for the service. + +Type: + +```hcl +list(object({ + log_analytics_workspace_id = optional(string, ""), + storage_account_id = optional(string, "") + })) +``` + +Default: `[]` + +### [postgresql\_active\_directory\_administrator](#input\_postgresql\_active\_directory\_administrator) + +Description: Specifies the entra id admin configuration for the postgresql flexible server. Please provide a group name and the object id of teh group. + +Type: + +```hcl +object({ + object_id = optional(string, "") + group_name = optional(string, "") + }) +``` + +Default: `{}` + +### [postgresql\_auto\_grow\_enabled](#input\_postgresql\_auto\_grow\_enabled) + +Description: Specifies whether auto grow is enabled for the postgresql flexible server. + +Type: `bool` + +Default: `false` + +### [postgresql\_backup\_retention\_days](#input\_postgresql\_backup\_retention\_days) + +Description: Specifies the backup retention in days of the postgresql flexible server. + +Type: `number` + +Default: `30` + +### [postgresql\_configuration](#input\_postgresql\_configuration) + +Description: Specifies the configuration of the postgresql flexible server. + +Type: `map(string)` + +Default: `{}` + +### [postgresql\_databases](#input\_postgresql\_databases) + +Description: Specifies the databases of the postgresql flexible server. + +Type: + +```hcl +map(object({ + charset = optional(string, "UTF8") + collation = optional(string, "en_US.utf8") + })) +``` + +Default: `{}` + +### [postgresql\_geo\_redundant\_backup\_enabled](#input\_postgresql\_geo\_redundant\_backup\_enabled) + +Description: Specifies whether the geo redundant backup should be enabled for the postgresql storage account. + +Type: `bool` + +Default: `false` + +### [postgresql\_high\_availability\_mode](#input\_postgresql\_high\_availability\_mode) + +Description: Specifies whether zone redundancy should be enabled for the postgresql flexible server. + +Type: `string` + +Default: `"ZoneRedundant"` + +### [postgresql\_maintenance\_window](#input\_postgresql\_maintenance\_window) + +Description: Specifies the maintenance window for the postgresql flexible server. + +Type: + +```hcl +optional(object({ + day_of_week = optional(number, 6) + start_hour = optional(number, 0) + start_minute = optional(number, 0) + }), {}) +``` + +Default: `{}` + +### [postgresql\_sku\_name](#input\_postgresql\_sku\_name) + +Description: Specifies the sku of the postgresql flexible server. + +Type: `string` + +Default: `"B_Standard_B1ms"` + +### [postgresql\_storage\_mb](#input\_postgresql\_storage\_mb) + +Description: Specifies the storage mb of the postgresql flexible server. + +Type: `number` + +Default: `32768` + +### [postgresql\_storage\_tier](#input\_postgresql\_storage\_tier) + +Description: Specifies the storage tier of the postgresql flexible server. + +Type: `string` + +Default: `null` + +### [postgresql\_version](#input\_postgresql\_version) + +Description: Specifies the version of the postgresql flexible server. + +Type: `number` + +Default: `16` + +### [postgresql\_zone\_redundancy\_enabled](#input\_postgresql\_zone\_redundancy\_enabled) + +Description: Specifies whether zone redundancy should be enabled for the postgresql flexible server. + +Type: `bool` + +Default: `false` + +### [private\_dns\_zone\_id\_postrgesql](#input\_private\_dns\_zone\_id\_postrgesql) + +Description: Specifies the resource ID of the private DNS zone for Azure Postgresql flexible server. Not required if DNS A-records get created via Azure Policy. + +Type: `string` + +Default: `""` + +### [tags](#input\_tags) + +Description: Specifies a key value map of tags to set on every taggable resources. + +Type: `map(string)` + +Default: `{}` + +## Outputs + +The following outputs are exported: + +### [postgresql\_flexible\_server\_fqdn](#output\_postgresql\_flexible\_server\_fqdn) + +Description: Specifies the fqdn of the postgres flexible server. + +### [postgresql\_flexible\_server\_id](#output\_postgresql\_flexible\_server\_id) + +Description: Specifies the resource id of the postgres flexible server. + +### [postgresql\_flexible\_server\_name](#output\_postgresql\_flexible\_server\_name) + +Description: Specifies the resource name of the postgres flexible server. + +### [postgresql\_flexible\_server\_setup\_completed](#output\_postgresql\_flexible\_server\_setup\_completed) + +Description: Specifies whether the connectivity and identity has been successfully configured. + + + + \ No newline at end of file diff --git a/modules/postgresqlflexible/README_footer.md b/modules/postgresqlflexible/README_footer.md new file mode 100644 index 0000000..e69de29 diff --git a/modules/postgresqlflexible/README_header.md b/modules/postgresqlflexible/README_header.md new file mode 100644 index 0000000..7aba925 --- /dev/null +++ b/modules/postgresqlflexible/README_header.md @@ -0,0 +1 @@ +# Azure Postgresql Flexible Server Terraform Module diff --git a/modules/postgresqlflexible/connectivity.tf b/modules/postgresqlflexible/connectivity.tf new file mode 100644 index 0000000..34632ec --- /dev/null +++ b/modules/postgresqlflexible/connectivity.tf @@ -0,0 +1,38 @@ +resource "azurerm_private_endpoint" "private_endpoint_postgresql_flexible_server" { + name = "${azurerm_postgresql_flexible_server.postgresql_flexible_server.name}-pe" + location = var.location + resource_group_name = azurerm_postgresql_flexible_server.postgresql_flexible_server.resource_group_name + tags = var.tags + + custom_network_interface_name = "${azurerm_postgresql_flexible_server.postgresql_flexible_server.name}-nic" + private_service_connection { + name = "${azurerm_postgresql_flexible_server.postgresql_flexible_server.name}-svc" + is_manual_connection = false + private_connection_resource_id = azurerm_postgresql_flexible_server.postgresql_flexible_server.id + subresource_names = ["postgresqlServer"] + } + subnet_id = var.subnet_id + dynamic "private_dns_zone_group" { + for_each = var.private_dns_zone_id_postrgesql == "" ? [] : [1] + content { + name = "${azurerm_postgresql_flexible_server.postgresql_flexible_server.name}-arecord" + private_dns_zone_ids = [ + var.private_dns_zone_id_postrgesql + ] + } + } + + lifecycle { + ignore_changes = [ + private_dns_zone_group + ] + } +} + +resource "time_sleep" "sleep_connectivity" { + create_duration = "${var.connectivity_delay_in_seconds}s" + + depends_on = [ + azurerm_private_endpoint.private_endpoint_postgresql_flexible_server + ] +} diff --git a/modules/postgresqlflexible/data.tf b/modules/postgresqlflexible/data.tf new file mode 100644 index 0000000..ce1f54f --- /dev/null +++ b/modules/postgresqlflexible/data.tf @@ -0,0 +1,5 @@ +data "azurerm_client_config" "current" {} + +data "azurerm_monitor_diagnostic_categories" "diagnostic_categories_postgresql_flexible_server" { + resource_id = azurerm_postgresql_flexible_server.postgresql_flexible_server.id +} diff --git a/modules/postgresqlflexible/diagnostics.tf b/modules/postgresqlflexible/diagnostics.tf new file mode 100644 index 0000000..362c08d --- /dev/null +++ b/modules/postgresqlflexible/diagnostics.tf @@ -0,0 +1,29 @@ +resource "azurerm_monitor_diagnostic_setting" "diagnostic_setting_postgresql_flexible_server" { + for_each = { for index, value in var.diagnostics_configurations : + index => { + log_analytics_workspace_id = value.log_analytics_workspace_id, + storage_account_id = value.storage_account_id + } + } + name = "applicationLogs-${each.key}" + target_resource_id = azurerm_postgresql_flexible_server.postgresql_flexible_server.id + log_analytics_workspace_id = each.value.log_analytics_workspace_id == "" ? null : each.value.log_analytics_workspace_id + storage_account_id = each.value.storage_account_id == "" ? null : each.value.storage_account_id + + dynamic "enabled_log" { + iterator = entry + for_each = data.azurerm_monitor_diagnostic_categories.diagnostic_categories_postgresql_flexible_server.log_category_groups + content { + category_group = entry.value + } + } + + dynamic "metric" { + iterator = entry + for_each = data.azurerm_monitor_diagnostic_categories.diagnostic_categories_postgresql_flexible_server.metrics + content { + category = entry.value + enabled = true + } + } +} diff --git a/modules/postgresqlflexible/main.tf b/modules/postgresqlflexible/main.tf new file mode 100644 index 0000000..277e63e --- /dev/null +++ b/modules/postgresqlflexible/main.tf @@ -0,0 +1,89 @@ +resource "azurerm_postgresql_flexible_server" "postgresql_flexible_server" { + name = var.postgresql_name + location = var.location + resource_group_name = var.resource_group_name + tags = var.tags + dynamic "identity" { + for_each = var.customer_managed_key != null ? [1] : [] + content { + type = "UserAssigned" + identity_ids = [ + var.customer_managed_key.user_assigned_identity_id + ] + } + } + + administrator_login = null + administrator_password = null + authentication { + active_directory_auth_enabled = true + password_auth_enabled = false + tenant_id = data.azurerm_client_config.current.tenant_id + } + auto_grow_enabled = var.postgresql_auto_grow_enabled + backup_retention_days = var.postgresql_backup_retention_days + create_mode = "Default" + dynamic "customer_managed_key" { + for_each = var.customer_managed_key != null ? [1] : [] + content { + key_vault_key_id = var.customer_managed_key.key_vault_id + primary_user_assigned_identity_id = var.customer_managed_key.user_assigned_identity_id + } + } + delegated_subnet_id = null + geo_redundant_backup_enabled = var.postgresql_geo_redundant_backup_enabled + dynamic "high_availability" { + for_each = var.postgresql_zone_redundancy_enabled ? [1] : [] + content { + mode = var.postgresql_high_availability_mode + } + } + maintenance_window { + day_of_week = var.postgresql_maintenance_window.day_of_week + start_hour = var.postgresql_maintenance_window.start_hour + start_minute = var.postgresql_maintenance_window.start_minute + } + public_network_access_enabled = false + replication_role = null + sku_name = var.postgresql_sku_name + storage_mb = var.postgresql_storage_mb + storage_tier = var.postgresql_storage_tier + version = var.postgresql_version + zone = null + + lifecycle { + ignore_changes = [ + zone, + high_availability[0].standby_availability_zone, + ] + } +} + +resource "azurerm_postgresql_flexible_server_database" "postgresql_flexible_server_database" { + for_each = var.postgresql_databases + + server_id = azurerm_postgresql_flexible_server.postgresql_flexible_server.id + name = each.key + + charset = each.value.charset + collation = each.value.collation +} + +resource "azurerm_postgresql_flexible_server_active_directory_administrator" "postgresql_flexible_server_active_directory_administrator" { + server_name = azurerm_postgresql_flexible_server.postgresql_flexible_server.name + resource_group_name = azurerm_postgresql_flexible_server.postgresql_flexible_server.resource_group_name + + object_id = var.postgresql_active_directory_administrator.object_id + principal_name = var.postgresql_active_directory_administrator.group_name + principal_type = "Group" + tenant_id = data.azurerm_client_config.current.tenant_id +} + +resource "azurerm_postgresql_flexible_server_configuration" "postgresql_flexible_server_configuration" { + for_each = var.postgresql_configuration + + server_id = azurerm_postgresql_flexible_server.postgresql_flexible_server.id + name = each.key + + value = each.value +} diff --git a/modules/postgresqlflexible/outputs.tf b/modules/postgresqlflexible/outputs.tf new file mode 100644 index 0000000..908dea2 --- /dev/null +++ b/modules/postgresqlflexible/outputs.tf @@ -0,0 +1,28 @@ +output "postgresql_flexible_server_id" { + description = "Specifies the resource id of the postgres flexible server." + value = azurerm_postgresql_flexible_server.postgresql_flexible_server.id + sensitive = false +} + +output "postgresql_flexible_server_name" { + description = "Specifies the resource name of the postgres flexible server." + value = azurerm_postgresql_flexible_server.postgresql_flexible_server.name + sensitive = false +} + +output "postgresql_flexible_server_fqdn" { + description = "Specifies the fqdn of the postgres flexible server." + value = azurerm_postgresql_flexible_server.postgresql_flexible_server.fqdn + sensitive = true +} + +output "postgresql_flexible_server_setup_completed" { + description = "Specifies whether the connectivity and identity has been successfully configured." + value = true + sensitive = false + + depends_on = [ + azurerm_postgresql_flexible_server_active_directory_administrator.postgresql_flexible_server_active_directory_administrator, + time_sleep.sleep_connectivity, + ] +} diff --git a/modules/postgresqlflexible/terraform.tf b/modules/postgresqlflexible/terraform.tf new file mode 100644 index 0000000..3e38630 --- /dev/null +++ b/modules/postgresqlflexible/terraform.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">=0.12" + + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "~> 4.0" + } + time = { + source = "hashicorp/time" + version = "~> 0.9" + } + } +} diff --git a/modules/postgresqlflexible/tests/test.tftest.hcl b/modules/postgresqlflexible/tests/test.tftest.hcl new file mode 100644 index 0000000..b0a7849 --- /dev/null +++ b/modules/postgresqlflexible/tests/test.tftest.hcl @@ -0,0 +1,47 @@ +run "create_postgresql_flexible_server" { + command = apply + + variables { + location = "northeurope" + resource_group_name = "tfmodule-test-rg" + tags = { + test = "postgresqlflexibleserver" + } + postgresql_name = "tftstr-001" + postgresql_auto_grow_enabled = false + postgresql_backup_retention_days = 30 + postgresql_geo_redundant_backup_enabled = true + postgresql_zone_redundancy_enabled = false + postgresql_high_availability_mode = "ZoneRedundant" + postgresql_maintenance_window = { + day_of_week = 6 + start_hour = 0 + start_minute = 0 + } + postgresql_sku_name = "B_Standard_B1ms" + postgresql_storage_mb = 32768 + postgresql_storage_tier = null + postgresql_version = 16 + postgresql_active_directory_administrator = { + object_id = "" + group_name = "" + } + postgresql_configuration = {} + postgresql_databases = { + testdb001 = { + charset = "UTF8" + collation = "en_US.utf8" + } + } + diagnostics_configurations = [] + subnet_id = "/subscriptions/1fdab118-1638-419a-8b12-06c9543714a0/resourceGroups/ptt-dev-networking-rg/providers/Microsoft.Network/virtualNetworks/spoke-ptt-dev-vnet001/subnets/TerraformTestSubnet" + connectivity_delay_in_seconds = 0 + private_dns_zone_id_postrgesql = "/subscriptions/e82c5267-9dc4-4f45-ac13-abdd5e130d27/resourceGroups/ptt-dev-privatedns-rg/providers/Microsoft.Network/privateDnsZones/privatelink.postgres.database.azure.com" + customer_managed_key = null + } + + assert { + condition = azurerm_postgresql_flexible_server.postgresql_flexible_server.resource_group_name == "tfmodule-test-rg" + error_message = "Failed to deploy." + } +} diff --git a/modules/postgresqlflexible/variables.tf b/modules/postgresqlflexible/variables.tf new file mode 100644 index 0000000..4098407 --- /dev/null +++ b/modules/postgresqlflexible/variables.tf @@ -0,0 +1,262 @@ +# General variables +variable "location" { + description = "Specifies the location of all resources." + type = string + sensitive = false + nullable = false +} + +variable "resource_group_name" { + description = "Specifies the resource group name in which all resources will get deployed." + type = string + sensitive = false + nullable = false + validation { + condition = length(var.resource_group_name) >= 2 + error_message = "Please specify a valid resource group name." + } +} + +variable "tags" { + description = "Specifies a key value map of tags to set on every taggable resources." + type = map(string) + sensitive = false + default = {} + nullable = false +} + +# Storage variables +variable "postgresql_name" { + description = "Specifies the name of the postgresql flexible server." + type = string + sensitive = false + nullable = false + validation { + condition = length(var.postgresql_name) >= 2 + error_message = "Please specify a valid name." + } +} + +variable "postgresql_auto_grow_enabled" { + description = "Specifies whether auto grow is enabled for the postgresql flexible server." + type = bool + sensitive = false + nullable = false + default = false +} + +variable "postgresql_backup_retention_days" { + description = "Specifies the backup retention in days of the postgresql flexible server." + type = number + sensitive = false + nullable = false + default = 30 + validation { + condition = var.postgresql_backup_retention_days >= 7 && var.postgresql_backup_retention_days <= 35 + error_message = "Please specify a valid value for backup retention in days between 7 and 35 days." + } +} + +variable "postgresql_geo_redundant_backup_enabled" { + description = "Specifies whether the geo redundant backup should be enabled for the postgresql storage account." + type = bool + sensitive = false + nullable = false + default = false +} + +variable "postgresql_zone_redundancy_enabled" { + description = "Specifies whether zone redundancy should be enabled for the postgresql flexible server." + type = bool + sensitive = false + nullable = false + default = false +} + +variable "postgresql_high_availability_mode" { + description = "Specifies whether zone redundancy should be enabled for the postgresql flexible server." + type = string + sensitive = false + nullable = false + default = "ZoneRedundant" + validation { + condition = contains(["ZoneRedundant", "SameZone"], var.postgresql_high_availability_mode) + error_message = "Please specify a valid zone redundancy mode." + } +} + +variable "postgresql_maintenance_window" { + description = "Specifies the maintenance window for the postgresql flexible server." + type = optional(object({ + day_of_week = optional(number, 6) + start_hour = optional(number, 0) + start_minute = optional(number, 0) + }), {}) + sensitive = false + nullable = false + default = {} + validation { + condition = alltrue( + var.postgresql_maintenance_window.day_of_week >= 0 && var.postgresql_maintenance_window.day_of_week <= 6, + var.postgresql_maintenance_window.start_hour >= 0 && var.postgresql_maintenance_window.start_hour <= 23, + var.postgresql_maintenance_window.start_minute >= 0 && var.postgresql_maintenance_window.start_minute <= 59, + ) + error_message = "Please specify a valid maintenance window." + } +} + +variable "postgresql_sku_name" { + description = "Specifies the sku of the postgresql flexible server." + type = string + sensitive = false + nullable = false + default = "B_Standard_B1ms" + # validation { + # condition = contains(["All", "AAD", "PrivateLink"], var.storage_account_allowed_copy_scope) + # error_message = "Please specify a valid allowed copy scope. Allowed values are: [ 'All', 'AAD', 'PrivateLink' ]" + # } +} + +variable "postgresql_storage_mb" { + description = "Specifies the storage mb of the postgresql flexible server." + type = number + sensitive = false + nullable = false + default = 32768 + validation { + condition = contains([32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4193280, 4194304, 8388608, 16777216, 33553408], var.postgresql_storage_mb) + error_message = "Please specify a valid allowed storage mb size. Please check the supported values." + } +} + +variable "postgresql_storage_tier" { + description = "Specifies the storage tier of the postgresql flexible server." + type = string + sensitive = false + nullable = true + default = null + validation { + condition = var.postgresql_storage_tier == null || contains(["P4", "P6", "P10", "P15", "P20", "P30", "P40", "P50", "P60", "P70", "P80"], var.postgresql_storage_mb) + error_message = "Please specify a valid storage tier that can be used in combination with the provided storage mb size." + } +} + +variable "postgresql_version" { + description = "Specifies the version of the postgresql flexible server." + type = number + sensitive = false + nullable = false + default = 16 + validation { + condition = contains([11, 12, 13, 14, 15, 16], var.postgresql_version) + error_message = "Please specify a valid version." + } +} + +variable "postgresql_active_directory_administrator" { + description = "Specifies the entra id admin configuration for the postgresql flexible server. Please provide a group name and the object id of teh group." + type = object({ + object_id = optional(string, "") + group_name = optional(string, "") + }) + sensitive = false + nullable = false + default = {} + validation { + condition = (var.postgresql_active_directory_administrator.object_id == "" && var.postgresql_active_directory_administrator.group_name == "") || (length(var.postgresql_active_directory_administrator.object_id) > 2 && length(var.postgresql_active_directory_administrator.group_name) > 2) + error_message = "Please specify a valid entra id administrator config and provide both values or none." + } +} + +variable "postgresql_configuration" { + description = "Specifies the configuration of the postgresql flexible server." + type = map(string) + sensitive = false + nullable = false + default = {} +} + +variable "postgresql_databases" { + description = "Specifies the databases of the postgresql flexible server." + type = map(object({ + charset = optional(string, "UTF8") + collation = optional(string, "en_US.utf8") + })) + sensitive = false + nullable = false + default = {} +} + +# Diagnostics variables +variable "diagnostics_configurations" { + description = "Specifies the diagnostic configuration for the service." + type = list(object({ + log_analytics_workspace_id = optional(string, ""), + storage_account_id = optional(string, "") + })) + sensitive = false + default = [] + validation { + condition = alltrue([ + length([for diagnostics_configuration in toset(var.diagnostics_configurations) : diagnostics_configuration if diagnostics_configuration.log_analytics_workspace_id == "" && diagnostics_configuration.storage_account_id == ""]) <= 0 + ]) + error_message = "Please specify a valid resource ID." + } +} + +# Network variables +variable "subnet_id" { + description = "Specifies the resource id of a subnet in which the private endpoints get created." + type = string + sensitive = false + validation { + condition = length(split("/", var.subnet_id)) == 11 + error_message = "Please specify a valid subnet id." + } +} + +variable "connectivity_delay_in_seconds" { + description = "Specifies the delay in seconds after the private endpoint deployment (required for the DNS automation via Policies)." + type = number + sensitive = false + nullable = false + default = 120 + validation { + condition = var.connectivity_delay_in_seconds >= 0 + error_message = "Please specify a valid non-negative number." + } +} + +variable "private_dns_zone_id_postrgesql" { + description = "Specifies the resource ID of the private DNS zone for Azure Postgresql flexible server. Not required if DNS A-records get created via Azure Policy." + type = string + sensitive = false + default = "" + validation { + condition = var.private_dns_zone_id_postrgesql == "" || (length(split("/", var.private_dns_zone_id_postrgesql)) == 9 && endswith(var.private_dns_zone_id_postrgesql, "privatelink.postgres.database.azure.com")) + error_message = "Please specify a valid resource ID for the private DNS Zone." + } +} + +# Customer-managed key variables +variable "customer_managed_key" { + description = "Specifies the customer managed key configurations." + type = object({ + key_vault_id = string, + key_vault_key_versionless_id = string, + user_assigned_identity_id = string, + user_assigned_identity_client_id = string, + }) + sensitive = false + nullable = true + default = null + validation { + condition = alltrue([ + var.customer_managed_key == null || length(split("/", try(var.customer_managed_key.key_vault_id, ""))) == 9, + var.customer_managed_key == null || startswith(try(var.customer_managed_key.key_vault_key_versionless_id, ""), "https://"), + var.customer_managed_key == null || length(split("/", try(var.customer_managed_key.user_assigned_identity_id, ""))) == 9, + var.customer_managed_key == null || length(try(var.customer_managed_key.user_assigned_identity_client_id, "")) >= 2, + ]) + error_message = "Please specify a valid resource ID." + } +} From 8ba166fda735eeb7d53e0b07165fefd68d7e3c6a Mon Sep 17 00:00:00 2001 From: Marvin Buss Date: Wed, 19 Feb 2025 14:43:20 +0100 Subject: [PATCH 2/6] Fix bug --- modules/postgresqlflexible/variables.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/postgresqlflexible/variables.tf b/modules/postgresqlflexible/variables.tf index 4098407..5181e81 100644 --- a/modules/postgresqlflexible/variables.tf +++ b/modules/postgresqlflexible/variables.tf @@ -87,11 +87,11 @@ variable "postgresql_high_availability_mode" { variable "postgresql_maintenance_window" { description = "Specifies the maintenance window for the postgresql flexible server." - type = optional(object({ + type = object({ day_of_week = optional(number, 6) start_hour = optional(number, 0) start_minute = optional(number, 0) - }), {}) + }) sensitive = false nullable = false default = {} From 156268dc08cefbc10fe8020c8c54afdde285e3f6 Mon Sep 17 00:00:00 2001 From: Marvin Buss Date: Wed, 19 Feb 2025 14:50:52 +0100 Subject: [PATCH 3/6] Test updated checks --- modules/postgresqlflexible/README.md | 4 ++-- modules/postgresqlflexible/variables.tf | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/postgresqlflexible/README.md b/modules/postgresqlflexible/README.md index c889a22..29ece24 100644 --- a/modules/postgresqlflexible/README.md +++ b/modules/postgresqlflexible/README.md @@ -169,11 +169,11 @@ Description: Specifies the maintenance window for the postgresql flexible server Type: ```hcl -optional(object({ +object({ day_of_week = optional(number, 6) start_hour = optional(number, 0) start_minute = optional(number, 0) - }), {}) + }) ``` Default: `{}` diff --git a/modules/postgresqlflexible/variables.tf b/modules/postgresqlflexible/variables.tf index 5181e81..8070025 100644 --- a/modules/postgresqlflexible/variables.tf +++ b/modules/postgresqlflexible/variables.tf @@ -97,9 +97,9 @@ variable "postgresql_maintenance_window" { default = {} validation { condition = alltrue( - var.postgresql_maintenance_window.day_of_week >= 0 && var.postgresql_maintenance_window.day_of_week <= 6, - var.postgresql_maintenance_window.start_hour >= 0 && var.postgresql_maintenance_window.start_hour <= 23, - var.postgresql_maintenance_window.start_minute >= 0 && var.postgresql_maintenance_window.start_minute <= 59, + (var.postgresql_maintenance_window.day_of_week >= 0 && var.postgresql_maintenance_window.day_of_week <= 6), + (var.postgresql_maintenance_window.start_hour >= 0 && var.postgresql_maintenance_window.start_hour <= 23), + (var.postgresql_maintenance_window.start_minute >= 0 && var.postgresql_maintenance_window.start_minute <= 59), ) error_message = "Please specify a valid maintenance window." } From cb9dd01e6c6227079334d025ed3aa27bcfb3384b Mon Sep 17 00:00:00 2001 From: Marvin Buss Date: Wed, 19 Feb 2025 14:56:23 +0100 Subject: [PATCH 4/6] Update conditions --- modules/postgresqlflexible/variables.tf | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/modules/postgresqlflexible/variables.tf b/modules/postgresqlflexible/variables.tf index 8070025..35380ee 100644 --- a/modules/postgresqlflexible/variables.tf +++ b/modules/postgresqlflexible/variables.tf @@ -96,11 +96,15 @@ variable "postgresql_maintenance_window" { nullable = false default = {} validation { - condition = alltrue( - (var.postgresql_maintenance_window.day_of_week >= 0 && var.postgresql_maintenance_window.day_of_week <= 6), - (var.postgresql_maintenance_window.start_hour >= 0 && var.postgresql_maintenance_window.start_hour <= 23), - (var.postgresql_maintenance_window.start_minute >= 0 && var.postgresql_maintenance_window.start_minute <= 59), - ) + condition = var.postgresql_maintenance_window.day_of_week >= 0 && var.postgresql_maintenance_window.day_of_week <= 6 + error_message = "Please specify a valid maintenance window." + } + validation { + condition = var.postgresql_maintenance_window.start_hour >= 0 && var.postgresql_maintenance_window.start_hour <= 23 + error_message = "Please specify a valid maintenance window." + } + validation { + condition = var.postgresql_maintenance_window.start_minute >= 0 && var.postgresql_maintenance_window.start_minute <= 59 error_message = "Please specify a valid maintenance window." } } From 63423a991de9a76ed24275dbbbab989f215d4bf6 Mon Sep 17 00:00:00 2001 From: Marvin Buss Date: Wed, 19 Feb 2025 14:56:40 +0100 Subject: [PATCH 5/6] lint --- modules/postgresqlflexible/variables.tf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/postgresqlflexible/variables.tf b/modules/postgresqlflexible/variables.tf index 35380ee..adab5b9 100644 --- a/modules/postgresqlflexible/variables.tf +++ b/modules/postgresqlflexible/variables.tf @@ -96,15 +96,15 @@ variable "postgresql_maintenance_window" { nullable = false default = {} validation { - condition = var.postgresql_maintenance_window.day_of_week >= 0 && var.postgresql_maintenance_window.day_of_week <= 6 + condition = var.postgresql_maintenance_window.day_of_week >= 0 && var.postgresql_maintenance_window.day_of_week <= 6 error_message = "Please specify a valid maintenance window." } validation { - condition = var.postgresql_maintenance_window.start_hour >= 0 && var.postgresql_maintenance_window.start_hour <= 23 + condition = var.postgresql_maintenance_window.start_hour >= 0 && var.postgresql_maintenance_window.start_hour <= 23 error_message = "Please specify a valid maintenance window." } validation { - condition = var.postgresql_maintenance_window.start_minute >= 0 && var.postgresql_maintenance_window.start_minute <= 59 + condition = var.postgresql_maintenance_window.start_minute >= 0 && var.postgresql_maintenance_window.start_minute <= 59 error_message = "Please specify a valid maintenance window." } } From c5aab2781c6714051f0189c6ea0ec03581e1d57e Mon Sep 17 00:00:00 2001 From: Marvin Buss Date: Wed, 19 Feb 2025 15:02:26 +0100 Subject: [PATCH 6/6] Fixed bug for admin user --- modules/postgresqlflexible/main.tf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/postgresqlflexible/main.tf b/modules/postgresqlflexible/main.tf index 277e63e..814d99e 100644 --- a/modules/postgresqlflexible/main.tf +++ b/modules/postgresqlflexible/main.tf @@ -70,6 +70,8 @@ resource "azurerm_postgresql_flexible_server_database" "postgresql_flexible_serv } resource "azurerm_postgresql_flexible_server_active_directory_administrator" "postgresql_flexible_server_active_directory_administrator" { + count = var.postgresql_active_directory_administrator.object_id != "" && var.postgresql_active_directory_administrator.group_name != "" ? 1 : 0 + server_name = azurerm_postgresql_flexible_server.postgresql_flexible_server.name resource_group_name = azurerm_postgresql_flexible_server.postgresql_flexible_server.resource_group_name