diff --git a/.teamcity/components/settings.kt b/.teamcity/components/settings.kt index 332c3392fc96..f566389f7b40 100644 --- a/.teamcity/components/settings.kt +++ b/.teamcity/components/settings.kt @@ -171,7 +171,7 @@ var serviceTestConfigurationOverrides = mapOf( "eventhub" to testConfiguration(useDevTestSubscription = true), "firewall" to testConfiguration(useDevTestSubscription = true), "keyvault" to testConfiguration(useDevTestSubscription = true), - "postgres" to testConfiguration(locationOverride = LocationConfiguration("northeurope", "centralus", "eastus", false), useDevTestSubscription = true), + "postgres" to testConfiguration(locationOverride = LocationConfiguration("northeurope", "centralus", "westeurope", false), useDevTestSubscription = true), "privatedns" to testConfiguration(useDevTestSubscription = true), "redis" to testConfiguration(useDevTestSubscription = true) ) diff --git a/internal/services/postgres/postgresql_flexible_server_resource.go b/internal/services/postgres/postgresql_flexible_server_resource.go index a19c71f6abd3..815e3af8ffa5 100644 --- a/internal/services/postgres/postgresql_flexible_server_resource.go +++ b/internal/services/postgres/postgresql_flexible_server_resource.go @@ -283,6 +283,20 @@ func resourcePostgresqlFlexibleServer() *pluginsdk.Resource { Optional: true, ValidateFunc: commonids.ValidateUserAssignedIdentityID, }, + "geo_backup_key_vault_key_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: keyVaultValidate.NestedItemIdWithOptionalVersion, + RequiredWith: []string{ + "identity", + "customer_managed_key.0.geo_backup_user_assigned_identity_id", + }, + }, + "geo_backup_user_assigned_identity_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: commonids.ValidateUserAssignedIdentityID, + }, }, }, }, @@ -1060,9 +1074,23 @@ func expandFlexibleServerDataEncryption(input []interface{}) *servers.DataEncryp det := servers.ArmServerKeyTypeAzureKeyVault dataEncryption := servers.DataEncryption{ - Type: &det, - PrimaryKeyURI: utils.String(v["key_vault_key_id"].(string)), - PrimaryUserAssignedIdentityId: utils.String(v["primary_user_assigned_identity_id"].(string)), + Type: &det, + } + + if keyVaultKeyId := v["key_vault_key_id"].(string); keyVaultKeyId != "" { + dataEncryption.PrimaryKeyURI = utils.String(keyVaultKeyId) + } + + if primaryUserAssignedIdentityId := v["primary_user_assigned_identity_id"].(string); primaryUserAssignedIdentityId != "" { + dataEncryption.PrimaryUserAssignedIdentityId = utils.String(primaryUserAssignedIdentityId) + } + + if geoBackupKeyVaultKeyId := v["geo_backup_key_vault_key_id"].(string); geoBackupKeyVaultKeyId != "" { + dataEncryption.GeoBackupKeyURI = utils.String(geoBackupKeyVaultKeyId) + } + + if geoBackupUserAssignedIdentityId := v["geo_backup_user_assigned_identity_id"].(string); geoBackupUserAssignedIdentityId != "" { + dataEncryption.GeoBackupUserAssignedIdentityId = utils.String(geoBackupUserAssignedIdentityId) } return &dataEncryption @@ -1085,5 +1113,16 @@ func flattenFlexibleServerDataEncryption(de *servers.DataEncryption) ([]interfac item["primary_user_assigned_identity_id"] = parsed.ID() } + if de.GeoBackupKeyURI != nil { + item["geo_backup_key_vault_key_id"] = *de.GeoBackupKeyURI + } + if identity := de.GeoBackupUserAssignedIdentityId; identity != nil { + parsed, err := commonids.ParseUserAssignedIdentityIDInsensitively(*identity) + if err != nil { + return nil, fmt.Errorf("parsing %q: %+v", *identity, err) + } + item["geo_backup_user_assigned_identity_id"] = parsed.ID() + } + return []interface{}{item}, nil } diff --git a/internal/services/postgres/postgresql_flexible_server_resource_test.go b/internal/services/postgres/postgresql_flexible_server_resource_test.go index 5983f8bf2954..cdce66ac6b9f 100644 --- a/internal/services/postgres/postgresql_flexible_server_resource_test.go +++ b/internal/services/postgres/postgresql_flexible_server_resource_test.go @@ -369,6 +369,21 @@ func TestAccPostgresqlFlexibleServer_upgradeVersion(t *testing.T) { }) } +func TestAccPostgresqlFlexibleServer_enableGeoRedundantBackup(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_postgresql_flexible_server", "test") + r := PostgresqlFlexibleServerResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.enableGeoRedundantBackup(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("administrator_password", "create_mode"), + }) +} + func (PostgresqlFlexibleServerResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := servers.ParseFlexibleServerID(state.ID) if err != nil { @@ -911,3 +926,83 @@ resource "azurerm_postgresql_flexible_server" "test" { } `, r.template(data), data.RandomInteger, version) } + +func (r PostgresqlFlexibleServerResource) enableGeoRedundantBackup(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_resource_group" "test2" { + name = "acctestRG-postgresql2-%d" + location = "%s" +} + +resource "azurerm_user_assigned_identity" "test2" { + name = "acctestmi2%s" + location = azurerm_resource_group.test2.location + resource_group_name = azurerm_resource_group.test2.name +} + +resource "azurerm_key_vault" "test2" { + name = "acctestkv2%s" + location = azurerm_resource_group.test2.location + resource_group_name = azurerm_resource_group.test2.name + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "standard" + purge_protection_enabled = true +} + +resource "azurerm_key_vault_access_policy" "server2" { + key_vault_id = azurerm_key_vault.test2.id + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = azurerm_user_assigned_identity.test2.principal_id + + key_permissions = ["Get", "List", "WrapKey", "UnwrapKey", "GetRotationPolicy", "SetRotationPolicy"] +} + +resource "azurerm_key_vault_access_policy" "client2" { + key_vault_id = azurerm_key_vault.test2.id + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_client_config.current.object_id + + key_permissions = ["Get", "Create", "Delete", "List", "Restore", "Recover", "UnwrapKey", "WrapKey", "Purge", "Encrypt", "Decrypt", "Sign", "Verify", "GetRotationPolicy", "SetRotationPolicy"] +} + +resource "azurerm_key_vault_key" "test2" { + name = "test2" + key_vault_id = azurerm_key_vault.test2.id + key_type = "RSA" + key_size = 2048 + key_opts = ["decrypt", "encrypt", "sign", "unwrapKey", "verify", "wrapKey"] + + depends_on = [ + azurerm_key_vault_access_policy.client2, + azurerm_key_vault_access_policy.server2, + ] +} + +resource "azurerm_postgresql_flexible_server" "test" { + name = "acctest-fs-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + administrator_login = "adminTerraform" + administrator_password = "QAZwsx123" + storage_mb = 32768 + version = "12" + sku_name = "B_Standard_B1ms" + zone = "2" + geo_redundant_backup_enabled = true + + identity { + type = "UserAssigned" + identity_ids = [azurerm_user_assigned_identity.test.id, azurerm_user_assigned_identity.test2.id] + } + + customer_managed_key { + key_vault_key_id = azurerm_key_vault_key.test.id + primary_user_assigned_identity_id = azurerm_user_assigned_identity.test.id + geo_backup_key_vault_key_id = azurerm_key_vault_key.test2.id + geo_backup_user_assigned_identity_id = azurerm_user_assigned_identity.test2.id + } +} +`, r.cmkTemplate(data), data.RandomInteger, data.Locations.Ternary, data.RandomString, data.RandomString, data.RandomInteger) +} diff --git a/website/docs/r/postgresql_flexible_server.html.markdown b/website/docs/r/postgresql_flexible_server.html.markdown index b59db01f87b7..6e50358248e5 100644 --- a/website/docs/r/postgresql_flexible_server.html.markdown +++ b/website/docs/r/postgresql_flexible_server.html.markdown @@ -166,7 +166,11 @@ A `customer_managed_key` block supports the following: * `primary_user_assigned_identity_id` - (Optional) Specifies the primary user managed identity id for a Customer Managed Key. Should be added with `identity_ids`. -~> **NOTE:** This is required when `type` is set to `UserAssigned`. +* `geo_backup_key_vault_key_id` - (Optional) The ID of the geo backup Key Vault Key. It can't cross region and need Customer Managed Key in same region as geo backup. + +* `geo_backup_user_assigned_identity_id` - (Optional) The geo backup user managed identity id for a Customer Managed Key. Should be added with `identity_ids`. It can't cross region and need identity in same region as geo backup. + +~> **NOTE:** `primary_user_assigned_identity_id` or `geo_backup_user_assigned_identity_id` is required when `type` is set to `UserAssigned`. ---