Skip to content

Commit 6a3ec6c

Browse files
author
Neil Ye
authored
Remove forceNew for zone and standby_availability_zone of azurerm_postgresql_flexible_server (#13507)
fixes #13443 Symptom: Currently, TF would recreate this resource when zone and standby_availability_zone is changed. Expected behavior: After confirmed with service team, TF shouldn't recreate this resource when zone and standby_availability_zone is changed, which means failover is triggered once zone and standby_availability_zone is changed and failover between zone and standby_availability_zone is only supported when their values are exchanged. Failover is implemented by separate API not update API.
1 parent a031099 commit 6a3ec6c

File tree

3 files changed

+140
-10
lines changed

3 files changed

+140
-10
lines changed

internal/services/postgres/postgresql_flexible_server_resource.go

+76-7
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,16 @@ func resourcePostgresqlFlexibleServer() *pluginsdk.Resource {
100100
}, false),
101101
},
102102

103-
"zone": azure.SchemaZoneComputed(),
103+
"zone": {
104+
Type: pluginsdk.TypeString,
105+
Optional: true,
106+
Computed: true,
107+
ValidateFunc: validation.StringInSlice([]string{
108+
"1",
109+
"2",
110+
"3",
111+
}, false),
112+
},
104113

105114
"create_mode": {
106115
Type: pluginsdk.TypeString,
@@ -194,7 +203,17 @@ func resourcePostgresqlFlexibleServer() *pluginsdk.Resource {
194203
string(postgresqlflexibleservers.HighAvailabilityModeZoneRedundant),
195204
}, false),
196205
},
197-
"standby_availability_zone": azure.SchemaZoneComputed(),
206+
207+
"standby_availability_zone": {
208+
Type: pluginsdk.TypeString,
209+
Optional: true,
210+
Computed: true,
211+
ValidateFunc: validation.StringInSlice([]string{
212+
"1",
213+
"2",
214+
"3",
215+
}, false),
216+
},
198217
},
199218
},
200219
},
@@ -219,6 +238,7 @@ func resourcePostgresqlFlexibleServer() *pluginsdk.Resource {
219238
},
220239
}
221240
}
241+
222242
func resourcePostgresqlFlexibleServerCreate(d *pluginsdk.ResourceData, meta interface{}) error {
223243
subscriptionId := meta.(*clients.Client).Account.SubscriptionId
224244
client := meta.(*clients.Client).Postgres.FlexibleServersClient
@@ -281,7 +301,7 @@ func resourcePostgresqlFlexibleServerCreate(d *pluginsdk.ResourceData, meta inte
281301
Network: expandArmServerNetwork(d),
282302
Version: postgresqlflexibleservers.ServerVersion(d.Get("version").(string)),
283303
Storage: expandArmServerStorage(d),
284-
HighAvailability: expandFlexibleServerHighAvailability(d.Get("high_availability").([]interface{})),
304+
HighAvailability: expandFlexibleServerHighAvailability(d.Get("high_availability").([]interface{}), true),
285305
Backup: expandArmServerBackup(d),
286306
},
287307
Sku: sku,
@@ -426,6 +446,35 @@ func resourcePostgresqlFlexibleServerUpdate(d *pluginsdk.ResourceData, meta inte
426446
ServerPropertiesForUpdate: &postgresqlflexibleservers.ServerPropertiesForUpdate{},
427447
}
428448

449+
var requireFailover bool
450+
// failover is only supported when `zone` and `standby_availability_zone` is exchanged
451+
switch {
452+
case d.HasChange("zone") && d.HasChange("high_availability.0.standby_availability_zone"):
453+
resp, err := client.Get(ctx, id.ResourceGroup, id.Name)
454+
if err != nil {
455+
return err
456+
}
457+
458+
if props := resp.ServerProperties; props != nil {
459+
zone := d.Get("zone").(string)
460+
standbyZone := d.Get("high_availability.0.standby_availability_zone").(string)
461+
462+
if props.AvailabilityZone != nil && props.HighAvailability != nil && props.HighAvailability.StandbyAvailabilityZone != nil {
463+
if zone == *props.HighAvailability.StandbyAvailabilityZone && standbyZone == *props.AvailabilityZone {
464+
requireFailover = true
465+
} else {
466+
return fmt.Errorf("failover only supports exchange between `zone` and `standby_availability_zone`")
467+
}
468+
} else {
469+
return fmt.Errorf("`standby_availability_zone` cannot be added after PostgreSQL Flexible Server is created")
470+
}
471+
}
472+
case !d.HasChange("zone") && !d.HasChange("high_availability.0.standby_availability_zone"):
473+
requireFailover = false
474+
default:
475+
return fmt.Errorf("`zone` and `standby_availability_zone` should only be either exchanged with each other or unchanged")
476+
}
477+
429478
if d.HasChange("administrator_password") {
430479
parameters.ServerPropertiesForUpdate.AdministratorLoginPassword = utils.String(d.Get("administrator_password").(string))
431480
}
@@ -455,7 +504,7 @@ func resourcePostgresqlFlexibleServerUpdate(d *pluginsdk.ResourceData, meta inte
455504
}
456505

457506
if d.HasChange("high_availability") {
458-
parameters.HighAvailability = expandFlexibleServerHighAvailability(d.Get("high_availability").([]interface{}))
507+
parameters.HighAvailability = expandFlexibleServerHighAvailability(d.Get("high_availability").([]interface{}), false)
459508
}
460509

461510
future, err := client.Update(ctx, id.ResourceGroup, id.Name, parameters)
@@ -466,6 +515,23 @@ func resourcePostgresqlFlexibleServerUpdate(d *pluginsdk.ResourceData, meta inte
466515
if err := future.WaitForCompletionRef(ctx, client.Client); err != nil {
467516
return fmt.Errorf("waiting for the update of the Postgresql Flexible Server %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err)
468517
}
518+
519+
if requireFailover {
520+
restartParameters := &postgresqlflexibleservers.RestartParameter{
521+
RestartWithFailover: utils.Bool(true),
522+
FailoverMode: postgresqlflexibleservers.FailoverModePlannedFailover,
523+
}
524+
525+
future, err := client.Restart(ctx, id.ResourceGroup, id.Name, restartParameters)
526+
if err != nil {
527+
return fmt.Errorf("failing over %s: %+v", *id, err)
528+
}
529+
530+
if err := future.WaitForCompletionRef(ctx, client.Client); err != nil {
531+
return fmt.Errorf("waiting for failover of %s: %+v", *id, err)
532+
}
533+
}
534+
469535
return resourcePostgresqlFlexibleServerRead(d, meta)
470536
}
471537

@@ -613,7 +679,7 @@ func flattenArmServerMaintenanceWindow(input *postgresqlflexibleservers.Maintena
613679
}
614680
}
615681

616-
func expandFlexibleServerHighAvailability(inputs []interface{}) *postgresqlflexibleservers.HighAvailability {
682+
func expandFlexibleServerHighAvailability(inputs []interface{}, isCreate bool) *postgresqlflexibleservers.HighAvailability {
617683
if len(inputs) == 0 || inputs[0] == nil {
618684
return &postgresqlflexibleservers.HighAvailability{
619685
Mode: postgresqlflexibleservers.HighAvailabilityModeDisabled,
@@ -626,8 +692,11 @@ func expandFlexibleServerHighAvailability(inputs []interface{}) *postgresqlflexi
626692
Mode: postgresqlflexibleservers.HighAvailabilityMode(input["mode"].(string)),
627693
}
628694

629-
if v, ok := input["standby_availability_zone"]; ok && v.(string) != "" {
630-
result.StandbyAvailabilityZone = utils.String(v.(string))
695+
// service team confirmed it doesn't support to update `standby_availability_zone` after the PostgreSQL Flexible Server resource is created
696+
if isCreate {
697+
if v, ok := input["standby_availability_zone"]; ok && v.(string) != "" {
698+
result.StandbyAvailabilityZone = utils.String(v.(string))
699+
}
631700
}
632701

633702
return &result

internal/services/postgres/postgresql_flexible_server_resource_test.go

+60-1
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,35 @@ func TestAccPostgresqlflexibleServer_pitr(t *testing.T) {
202202
})
203203
}
204204

205+
func TestAccPostgresqlflexibleServer_failover(t *testing.T) {
206+
data := acceptance.BuildTestData(t, "azurerm_postgresql_flexible_server", "test")
207+
r := PostgresqlFlexibleServerResource{}
208+
209+
data.ResourceTest(t, r, []acceptance.TestStep{
210+
{
211+
Config: r.failover(data, "1", "2"),
212+
Check: acceptance.ComposeTestCheckFunc(
213+
check.That(data.ResourceName).ExistsInAzure(r),
214+
),
215+
},
216+
data.ImportStep("administrator_password", "create_mode"),
217+
{
218+
Config: r.failover(data, "2", "1"),
219+
Check: acceptance.ComposeTestCheckFunc(
220+
check.That(data.ResourceName).ExistsInAzure(r),
221+
),
222+
},
223+
data.ImportStep("administrator_password", "create_mode"),
224+
{
225+
Config: r.failover(data, "1", "2"),
226+
Check: acceptance.ComposeTestCheckFunc(
227+
check.That(data.ResourceName).ExistsInAzure(r),
228+
),
229+
},
230+
data.ImportStep("administrator_password", "create_mode"),
231+
})
232+
}
233+
205234
func (PostgresqlFlexibleServerResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
206235
id, err := parse.FlexibleServerID(state.ID)
207236
if err != nil {
@@ -319,7 +348,7 @@ resource "azurerm_postgresql_flexible_server" "test" {
319348
320349
high_availability {
321350
mode = "ZoneRedundant"
322-
standby_availability_zone = "1"
351+
standby_availability_zone = "2"
323352
}
324353
325354
maintenance_window {
@@ -483,3 +512,33 @@ resource "azurerm_postgresql_flexible_server" "pitr" {
483512
}
484513
`, r.basic(data), data.RandomInteger, time.Now().Add(time.Duration(15)*time.Minute).UTC().Format(time.RFC3339))
485514
}
515+
516+
func (r PostgresqlFlexibleServerResource) failover(data acceptance.TestData, parimaryZone string, standbyZone string) string {
517+
return fmt.Sprintf(`
518+
%s
519+
520+
resource "azurerm_postgresql_flexible_server" "test" {
521+
name = "acctest-fs-%d"
522+
resource_group_name = azurerm_resource_group.test.name
523+
location = azurerm_resource_group.test.location
524+
version = "12"
525+
administrator_login = "adminTerraform"
526+
administrator_password = "QAZwsx123"
527+
zone = "%s"
528+
backup_retention_days = 10
529+
storage_mb = 131072
530+
sku_name = "GP_Standard_D2s_v3"
531+
532+
maintenance_window {
533+
day_of_week = 0
534+
start_hour = 0
535+
start_minute = 0
536+
}
537+
538+
high_availability {
539+
mode = "ZoneRedundant"
540+
standby_availability_zone = "%s"
541+
}
542+
}
543+
`, r.template(data), data.RandomInteger, parimaryZone, standbyZone)
544+
}

website/docs/r/postgresql_flexible_server.html.markdown

+4-2
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,9 @@ The following arguments are supported:
9191

9292
* `administrator_password` - (Optional) The Password associated with the `administrator_login` for the PostgreSQL Flexible Server. Required when `create_mode` is `Default`.
9393

94-
* `zone` - (Optional) The availability zone of the PostgreSQL Flexible Server. Possible values are `1`, `2` and `3`. Changing this forces a new PostgreSQL Flexible Server to be created.
94+
* `zone` - (Optional) The Availability Zone of the PostgreSQL Flexible Server. Possible values are `1`, `2` and `3`.
95+
96+
~> **NOTE:** The Availability Zones available would change per the region.
9597

9698
* `backup_retention_days` - (Optional) The backup retention days for the PostgreSQL Flexible Server. Possible values are between `7` and `35` days.
9799

@@ -135,7 +137,7 @@ A `high_availability` block supports the following:
135137

136138
* `mode` - (Required) The high availability mode for the PostgreSQL Flexible Server. The only possible value is `ZoneRedundant`.
137139

138-
* `standby_availability_zone` - (Optional) The availability zone of the standby Flexible Server. Possible values are `1`, `2` and `3`.
140+
* `standby_availability_zone` - (Optional) The Availability Zone of the standby Flexible Server. Possible values are `1`, `2` and `3`.
139141

140142
## Attributes Reference
141143

0 commit comments

Comments
 (0)