diff --git a/internal/services/postgres/postgresql_flexible_server_resource.go b/internal/services/postgres/postgresql_flexible_server_resource.go index 929cac449a3b..42caf7700565 100644 --- a/internal/services/postgres/postgresql_flexible_server_resource.go +++ b/internal/services/postgres/postgresql_flexible_server_resource.go @@ -139,6 +139,7 @@ func resourcePostgresqlFlexibleServer() *pluginsdk.Resource { ValidateFunc: validation.StringInSlice([]string{ string(servers.CreateModeDefault), string(servers.CreateModePointInTimeRestore), + string(servers.CreateModeReplica), }, false), }, @@ -248,6 +249,14 @@ func resourcePostgresqlFlexibleServer() *pluginsdk.Resource { Computed: true, }, + "replication_role": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(servers.ReplicationRoleNone), + }, false), + }, + "identity": commonschema.SystemAssignedUserAssignedIdentityOptional(), "customer_managed_key": { @@ -303,6 +312,10 @@ func resourcePostgresqlFlexibleServerCreate(d *pluginsdk.ResourceData, meta inte createMode := d.Get("create_mode").(string) + if _, ok := d.GetOk("replication_role"); ok { + return fmt.Errorf("`replication_role` cannot be set while creating") + } + if servers.CreateMode(createMode) == servers.CreateModePointInTimeRestore { if _, ok := d.GetOk("source_server_id"); !ok { return fmt.Errorf("`source_server_id` is required when `create_mode` is `PointInTimeRestore`") @@ -312,6 +325,12 @@ func resourcePostgresqlFlexibleServerCreate(d *pluginsdk.ResourceData, meta inte } } + if servers.CreateMode(createMode) == servers.CreateModeReplica { + if _, ok := d.GetOk("source_server_id"); !ok { + return fmt.Errorf("`source_server_id` is required when `create_mode` is `Replica`") + } + } + if createMode == "" || servers.CreateMode(createMode) == servers.CreateModeDefault { if _, ok := d.GetOk("administrator_login"); !ok { return fmt.Errorf("`administrator_login` is required when `create_mode` is `Default`") @@ -464,6 +483,9 @@ func resourcePostgresqlFlexibleServerRead(d *pluginsdk.ResourceData, meta interf d.Set("version", props.Version) d.Set("fqdn", props.FullyQualifiedDomainName) + // Currently, `replicationRole` is set to `Primary` when `createMode` is `Replica` and `replicationRole` is updated to `None`. Service team confirmed it should be set to `None` for this scenario. See more details from https://github.com/Azure/azure-rest-api-specs/issues/22499 + d.Set("replication_role", d.Get("replication_role").(string)) + if network := props.Network; network != nil { publicNetworkAccess := false if network.PublicNetworkAccess != nil { @@ -584,6 +606,25 @@ func resourcePostgresqlFlexibleServerUpdate(d *pluginsdk.ResourceData, meta inte } } + if d.HasChange("replication_role") { + createMode := d.Get("create_mode").(string) + replicationRole := d.Get("replication_role").(string) + if createMode == string(servers.CreateModeReplica) && replicationRole == string(servers.ReplicationRoleNone) { + replicationRole := servers.ReplicationRoleNone + parameters := servers.ServerForUpdate{ + Properties: &servers.ServerPropertiesForUpdate{ + ReplicationRole: &replicationRole, + }, + } + + if err := client.UpdateThenPoll(ctx, *id, parameters); err != nil { + return fmt.Errorf("updating `replication_role` for %s: %+v", *id, err) + } + } else { + return fmt.Errorf("`replication_role` only can be updated to `None` for replica server") + } + } + if d.HasChange("administrator_password") { parameters.Properties.AdministratorLoginPassword = utils.String(d.Get("administrator_password").(string)) } diff --git a/internal/services/postgres/postgresql_flexible_server_resource_test.go b/internal/services/postgres/postgresql_flexible_server_resource_test.go index 2436838e0a97..57ceccaf4291 100644 --- a/internal/services/postgres/postgresql_flexible_server_resource_test.go +++ b/internal/services/postgres/postgresql_flexible_server_resource_test.go @@ -285,6 +285,36 @@ func TestAccPostgresqlFlexibleServer_createWithCustomerManagedKey(t *testing.T) }) } +func TestAccPostgresqlFlexibleServer_replica(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_postgresql_flexible_server", "test") + r := PostgresqlFlexibleServerResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("administrator_password", "create_mode"), + { + PreConfig: func() { time.Sleep(15 * time.Minute) }, + Config: r.replica(data), + Check: acceptance.ComposeTestCheckFunc( + check.That("azurerm_postgresql_flexible_server.replica").ExistsInAzure(r), + ), + }, + data.ImportStep("administrator_password", "create_mode"), + { + Config: r.updateReplicationRole(data), + Check: acceptance.ComposeTestCheckFunc( + check.That("azurerm_postgresql_flexible_server.replica").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 { @@ -777,3 +807,34 @@ resource "azurerm_postgresql_flexible_server" "test" { } `, r.cmkTemplate(data), data.RandomInteger) } + +func (r PostgresqlFlexibleServerResource) replica(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_postgresql_flexible_server" "replica" { + name = "acctest-fs-replica-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + zone = "2" + create_mode = "Replica" + source_server_id = azurerm_postgresql_flexible_server.test.id +} +`, r.basic(data), data.RandomInteger) +} + +func (r PostgresqlFlexibleServerResource) updateReplicationRole(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_postgresql_flexible_server" "replica" { + name = "acctest-fs-replica-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + zone = "2" + create_mode = "Replica" + source_server_id = azurerm_postgresql_flexible_server.test.id + replication_role = "None" +} +`, r.basic(data), data.RandomInteger) +} diff --git a/website/docs/r/postgresql_flexible_server.html.markdown b/website/docs/r/postgresql_flexible_server.html.markdown index 5382dad7a0ee..ef3d41f62b1c 100644 --- a/website/docs/r/postgresql_flexible_server.html.markdown +++ b/website/docs/r/postgresql_flexible_server.html.markdown @@ -100,7 +100,7 @@ The following arguments are supported: * `geo_redundant_backup_enabled` - (Optional) Is Geo-Redundant backup enabled on the PostgreSQL Flexible Server. Defaults to `false`. Changing this forces a new PostgreSQL Flexible Server to be created. -* `create_mode` - (Optional) The creation mode which can be used to restore or replicate existing servers. Possible values are `Default` and `PointInTimeRestore`. Changing this forces a new PostgreSQL Flexible Server to be created. +* `create_mode` - (Optional) The creation mode which can be used to restore or replicate existing servers. Possible values are `Default`, `PointInTimeRestore` and `Replica`. Changing this forces a new PostgreSQL Flexible Server to be created. * `delegated_subnet_id` - (Optional) The ID of the virtual network subnet to create the PostgreSQL Flexible Server. The provided subnet should not have any other resource deployed in it and this subnet will be delegated to the PostgreSQL Flexible Server, if not already delegated. Changing this forces a new PostgreSQL Flexible Server to be created. @@ -116,9 +116,13 @@ The following arguments are supported: * `point_in_time_restore_time_in_utc` - (Optional) The point in time to restore from `source_server_id` when `create_mode` is `PointInTimeRestore`. Changing this forces a new PostgreSQL Flexible Server to be created. +* `replication_role` - (Optional) The replication role for the PostgreSQL Flexible Server. Possible value is `None`. + +~> **NOTE:** The `replication_role` cannot be set while creating and only can be updated to `None` for replica server. + * `sku_name` - (Optional) The SKU Name for the PostgreSQL Flexible Server. The name of the SKU, follows the `tier` + `name` pattern (e.g. `B_Standard_B1ms`, `GP_Standard_D2s_v3`, `MO_Standard_E4s_v3`). -* `source_server_id` - (Optional) The resource ID of the source PostgreSQL Flexible Server to be restored. Required when `create_mode` is `PointInTimeRestore`. Changing this forces a new PostgreSQL Flexible Server to be created. +* `source_server_id` - (Optional) The resource ID of the source PostgreSQL Flexible Server to be restored. Required when `create_mode` is `PointInTimeRestore` or `Replica`. Changing this forces a new PostgreSQL Flexible Server to be created. * `storage_mb` - (Optional) The max storage allowed for the PostgreSQL Flexible Server. Possible values are `32768`, `65536`, `131072`, `262144`, `524288`, `1048576`, `2097152`, `4194304`, `8388608`, and `16777216`.