diff --git a/.changelog/6821.txt b/.changelog/6821.txt new file mode 100644 index 00000000000..e5709045c07 --- /dev/null +++ b/.changelog/6821.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +sql: added field `deletion_policy` to resource `google_sql_database` +``` diff --git a/google/resource_sql_database.go b/google/resource_sql_database.go index 46c76a6cb83..385efa0e28d 100644 --- a/google/resource_sql_database.go +++ b/google/resource_sql_database.go @@ -75,6 +75,15 @@ a value of 'UTF8' at creation time.`, and Postgres' [Collation Support](https://www.postgresql.org/docs/9.6/static/collation.html) for more details and supported values. Postgres databases only support a value of 'en_US.UTF8' at creation time.`, + }, + "deletion_policy": { + Type: schema.TypeString, + Optional: true, + Default: "ABANDON", + Description: `The deletion policy for the database. Setting ABANDON allows the resource +to be abandoned rather than deleted. This is useful for Postgres, where databases cannot be +deleted from the API if there are users other than cloudsqlsuperuser with access. Possible +values are: "ABANDON".`, }, "project": { Type: schema.TypeString, @@ -207,6 +216,12 @@ func resourceSQLDatabaseRead(d *schema.ResourceData, meta interface{}) error { return handleNotFoundError(transformSQLDatabaseReadError(err), d, fmt.Sprintf("SQLDatabase %q", d.Id())) } + // Explicitly set virtual fields to default values if unset + if _, ok := d.GetOkExists("deletion_policy"); !ok { + if err := d.Set("deletion_policy", "ABANDON"); err != nil { + return fmt.Errorf("Error setting deletion_policy: %s", err) + } + } if err := d.Set("project", project); err != nil { return fmt.Errorf("Error reading Database: %s", err) } @@ -337,6 +352,11 @@ func resourceSQLDatabaseDelete(d *schema.ResourceData, meta interface{}) error { } var obj map[string]interface{} + if deletionPolicy := d.Get("deletion_policy"); deletionPolicy == "ABANDON" { + // Allows for database to be abandoned without deletion to avoid deletion failing + // for Postgres databases in some circumstances due to existing SQL users + return nil + } log.Printf("[DEBUG] Deleting Database %q", d.Id()) // err == nil indicates that the billing_project value was found @@ -380,6 +400,11 @@ func resourceSQLDatabaseImport(d *schema.ResourceData, meta interface{}) ([]*sch } d.SetId(id) + // Explicitly set virtual fields to default values on import + if err := d.Set("deletion_policy", "ABANDON"); err != nil { + return nil, fmt.Errorf("Error setting deletion_policy: %s", err) + } + return []*schema.ResourceData{d}, nil } diff --git a/google/resource_sql_database_generated_test.go b/google/resource_sql_database_generated_test.go index 4a985898d36..115f6dd5bb8 100644 --- a/google/resource_sql_database_generated_test.go +++ b/google/resource_sql_database_generated_test.go @@ -69,6 +69,53 @@ resource "google_sql_database_instance" "instance" { `, context) } +func TestAccSQLDatabase_sqlDatabaseDeletionPolicyExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "deletion_protection": false, + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckSQLDatabaseDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccSQLDatabase_sqlDatabaseDeletionPolicyExample(context), + }, + { + ResourceName: "google_sql_database.database_deletion_policy", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccSQLDatabase_sqlDatabaseDeletionPolicyExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_sql_database" "database_deletion_policy" { + name = "tf-test-my-database%{random_suffix}" + instance = google_sql_database_instance.instance.name + deletion_policy = "ABANDON" +} + +# See versions at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/sql_database_instance#database_version +resource "google_sql_database_instance" "instance" { + name = "tf-test-my-database-instance%{random_suffix}" + region = "us-central1" + database_version = "POSTGRES_14" + settings { + tier = "db-g1-small" + } + + deletion_protection = "%{deletion_protection}" +} +`, context) +} + func testAccCheckSQLDatabaseDestroyProducer(t *testing.T) func(s *terraform.State) error { return func(s *terraform.State) error { for name, rs := range s.RootModule().Resources { diff --git a/website/docs/r/sql_database.html.markdown b/website/docs/r/sql_database.html.markdown index a48eb549f96..b9c98138efd 100644 --- a/website/docs/r/sql_database.html.markdown +++ b/website/docs/r/sql_database.html.markdown @@ -52,6 +52,33 @@ resource "google_sql_database_instance" "instance" { deletion_protection = "true" } ``` +
+ + Open in Cloud Shell + +
+## Example Usage - Sql Database Deletion Policy + + +```hcl +resource "google_sql_database" "database_deletion_policy" { + name = "my-database" + instance = google_sql_database_instance.instance.name + deletion_policy = "ABANDON" +} + +# See versions at https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/sql_database_instance#database_version +resource "google_sql_database_instance" "instance" { + name = "my-database-instance" + region = "us-central1" + database_version = "POSTGRES_14" + settings { + tier = "db-g1-small" + } + + deletion_protection = "true" +} +``` ## Argument Reference @@ -91,6 +118,11 @@ The following arguments are supported: * `project` - (Optional) The ID of the project in which the resource belongs. If it is not provided, the provider project is used. +* `deletion_policy` - (Optional) The deletion policy for the database. Setting ABANDON allows the resource +to be abandoned rather than deleted. This is useful for Postgres, where databases cannot be +deleted from the API if there are users other than cloudsqlsuperuser with access. Possible +values are: "ABANDON". + ## Attributes Reference