diff --git a/internal/services/mssqlmanagedinstance/mssql_managed_database_resource.go b/internal/services/mssqlmanagedinstance/mssql_managed_database_resource.go index 50036062d44f..1cff440f3270 100644 --- a/internal/services/mssqlmanagedinstance/mssql_managed_database_resource.go +++ b/internal/services/mssqlmanagedinstance/mssql_managed_database_resource.go @@ -9,12 +9,15 @@ import ( "time" "github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/v5.0/sql" // nolint: staticcheck + "github.com/Azure/go-autorest/autorest/date" "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" "github.com/hashicorp/terraform-provider-azurerm/internal/services/mssql/helper" "github.com/hashicorp/terraform-provider-azurerm/internal/services/mssqlmanagedinstance/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/services/sql/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/suppress" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/utils" ) @@ -24,6 +27,7 @@ type MsSqlManagedDatabaseModel struct { ManagedInstanceId string `tfschema:"managed_instance_id"` LongTermRetentionPolicy []LongTermRetentionPolicy `tfschema:"long_term_retention_policy"` ShortTermRetentionDays int32 `tfschema:"short_term_retention_days"` + PointInTimeRestore []PointInTimeRestore `tfschema:"point_in_time_restore"` } type LongTermRetentionPolicy struct { @@ -33,6 +37,11 @@ type LongTermRetentionPolicy struct { WeekOfYear int32 `tfschema:"week_of_year"` } +type PointInTimeRestore struct { + RestorePointInTime string `tfschema:"restore_point_in_time"` + SourceDatabaseId string `tfschema:"source_database_id"` +} + var _ sdk.Resource = MsSqlManagedDatabaseResource{} var _ sdk.ResourceWithUpdate = MsSqlManagedDatabaseResource{} @@ -74,6 +83,30 @@ func (r MsSqlManagedDatabaseResource) Arguments() map[string]*pluginsdk.Schema { ValidateFunc: validation.IntBetween(1, 35), Default: 7, }, + + "point_in_time_restore": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "restore_point_in_time": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + DiffSuppressFunc: suppress.RFC3339Time, + ValidateFunc: validation.IsRFC3339Time, + }, + "source_database_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.ManagedDatabaseID, + }, + }, + }, + }, } } @@ -119,7 +152,18 @@ func (r MsSqlManagedDatabaseResource) Create() sdk.ResourceFunc { } parameters := sql.ManagedDatabase{ - Location: managedInstance.Location, + Location: managedInstance.Location, + ManagedDatabaseProperties: &sql.ManagedDatabaseProperties{}, + } + + if len(model.PointInTimeRestore) > 0 { + restorePointInTime := model.PointInTimeRestore[0] + parameters.CreateMode = sql.ManagedDatabaseCreateModePointInTimeRestore + t, _ := time.Parse(time.RFC3339, restorePointInTime.RestorePointInTime) + parameters.RestorePointInTime = &date.Time{ + Time: t, + } + parameters.SourceDatabaseID = pointer.To(restorePointInTime.SourceDatabaseId) } metadata.Logger.Infof("Creating %s", id) @@ -274,6 +318,11 @@ func (r MsSqlManagedDatabaseResource) Read() sdk.ResourceFunc { model.ShortTermRetentionDays = *shortTermRetentionResp.RetentionDays } + d := metadata.ResourceData + if v, ok := d.GetOk("point_in_time_restore"); ok { + model.PointInTimeRestore = flattenManagedDatabasePointInTimeRestore(v) + } + return metadata.Encode(&model) }, } @@ -344,3 +393,28 @@ func flattenLongTermRetentionPolicy(ltrPolicy sql.ManagedInstanceLongTermRetenti return []LongTermRetentionPolicy{ltrModel} } + +func flattenManagedDatabasePointInTimeRestore(input interface{}) []PointInTimeRestore { + output := make([]PointInTimeRestore, 0) + + if input == nil { + return output + } + + attrs := input.([]interface{}) + + for _, attr := range attrs { + if attr == nil { + return output + } + + v := attr.(map[string]interface{}) + + output = append(output, PointInTimeRestore{ + RestorePointInTime: v["restore_point_in_time"].(string), + SourceDatabaseId: v["source_database_id"].(string), + }) + } + + return output +} diff --git a/internal/services/mssqlmanagedinstance/mssql_managed_database_resource_test.go b/internal/services/mssqlmanagedinstance/mssql_managed_database_resource_test.go index 8f5746e86f3f..371dd9ac2c82 100644 --- a/internal/services/mssqlmanagedinstance/mssql_managed_database_resource_test.go +++ b/internal/services/mssqlmanagedinstance/mssql_managed_database_resource_test.go @@ -7,6 +7,7 @@ import ( "context" "fmt" "testing" + "time" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" @@ -53,6 +54,28 @@ func TestAccMsSqlManagedDatabase_withRetentionPolicies(t *testing.T) { }) } +func TestAccMsSqlManagedDatabase_pointInTimeRestore(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_mssql_managed_database", "pitr") + r := MsSqlManagedDatabase{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + { + PreConfig: func() { time.Sleep(11 * time.Minute) }, + Config: r.pointInTimeRestore(data, time.Now().UTC().Format(time.RFC3339)), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(""), + }) +} + func (r MsSqlManagedDatabase) Exists(ctx context.Context, client *clients.Client, state *acceptance.InstanceState) (*bool, error) { id, err := parse.ManagedDatabaseID(state.ID) if err != nil { @@ -122,3 +145,19 @@ resource "azurerm_mssql_managed_database" "test" { } `, MsSqlManagedInstanceResource{}.basic(data), data.RandomInteger) } + +func (r MsSqlManagedDatabase) pointInTimeRestore(data acceptance.TestData, restorePointInTime string) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_mssql_managed_database" "pitr" { + managed_instance_id = azurerm_mssql_managed_instance.test.id + name = "acctest-%[2]d-pitr" + + point_in_time_restore { + restore_point_in_time = "%[3]s" + source_database_id = azurerm_mssql_managed_database.test.id + } +} +`, MsSqlManagedDatabase{}.basic(data), data.RandomInteger, restorePointInTime) +} diff --git a/website/docs/r/mssql_managed_database.html.markdown b/website/docs/r/mssql_managed_database.html.markdown index b75c419ca907..4fc7b4feb614 100644 --- a/website/docs/r/mssql_managed_database.html.markdown +++ b/website/docs/r/mssql_managed_database.html.markdown @@ -72,6 +72,8 @@ The following arguments are supported: * `short_term_retention_days` - (Optional) The backup retention period in days. This is how many days Point-in-Time Restore will be supported. +* `point_in_time_restore` - (Optional) A `point_in_time_restore` block as defined below. + --- A `long_term_retention_policy` block supports the following: @@ -81,6 +83,14 @@ A `long_term_retention_policy` block supports the following: * `yearly_retention` - (Optional) The yearly retention policy for an LTR backup in an ISO 8601 format. Valid value is between 1 to 10 years. e.g. `P1Y`, `P12M`, `P52W` or `P365D`. * `week_of_year` - (Optional) The week of year to take the yearly backup. Value has to be between `1` and `52`. +--- + +A `point_in_time_restore` block supports the following: + +* `restore_point_in_time` - (Required) The point in time for the restore from `source_database_id`. Changing this forces a new resource to be created. + +* `source_database_id` - (Required) The source database id that will be used to restore from. Changing this forces a new resource to be created. + ## Attributes Reference In addition to the Arguments listed above - the following Attributes are exported: