diff --git a/internal/services/mssql/client/client.go b/internal/services/mssql/client/client.go index 7f5e18e8879e..2cd8c2c861eb 100644 --- a/internal/services/mssql/client/client.go +++ b/internal/services/mssql/client/client.go @@ -23,6 +23,8 @@ type Client struct { LongTermRetentionPoliciesClient *sql.LongTermRetentionPoliciesClient ManagedDatabasesClient *sql.ManagedDatabasesClient ManagedInstancesClient *sql.ManagedInstancesClient + ManagedInstancesLongTermRetentionPoliciesClient *sql.ManagedInstanceLongTermRetentionPoliciesClient + ManagedInstancesShortTermRetentionPoliciesClient *sql.ManagedBackupShortTermRetentionPoliciesClient ManagedInstanceVulnerabilityAssessmentsClient *sql.ManagedInstanceVulnerabilityAssessmentsClient ManagedInstanceServerSecurityAlertPoliciesClient *sql.ManagedServerSecurityAlertPoliciesClient OutboundFirewallRulesClient *sql.OutboundFirewallRulesClient @@ -96,6 +98,12 @@ func NewClient(o *common.ClientOptions) *Client { managedInstancesClient := sql.NewManagedInstancesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&managedInstancesClient.Client, o.ResourceManagerAuthorizer) + managedInstancesLongTermRetentionPoliciesClient := sql.NewManagedInstanceLongTermRetentionPoliciesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&managedInstancesLongTermRetentionPoliciesClient.Client, o.ResourceManagerAuthorizer) + + managedInstancesShortTermRetentionPoliciesClient := sql.NewManagedBackupShortTermRetentionPoliciesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&managedInstancesShortTermRetentionPoliciesClient.Client, o.ResourceManagerAuthorizer) + managedInstancesAdministratorsClient := sql.NewManagedInstanceAdministratorsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&managedInstancesAdministratorsClient.Client, o.ResourceManagerAuthorizer) @@ -182,7 +190,9 @@ func NewClient(o *common.ClientOptions) *Client { ManagedInstanceAzureADOnlyAuthenticationsClient: &managedInstanceAzureADOnlyAuthenticationsClient, ManagedInstanceEncryptionProtectorClient: &managedInstanceEncryptionProtectorsClient, ManagedInstanceKeysClient: &managedInstanceKeysClient, + ManagedInstancesLongTermRetentionPoliciesClient: &managedInstancesLongTermRetentionPoliciesClient, ManagedInstanceServerSecurityAlertPoliciesClient: &managedInstanceServerSecurityAlertPoliciesClient, + ManagedInstancesShortTermRetentionPoliciesClient: &managedInstancesShortTermRetentionPoliciesClient, ManagedInstanceVulnerabilityAssessmentsClient: &managedInstanceVulnerabilityAssessmentsClient, ManagedInstancesClient: &managedInstancesClient, OutboundFirewallRulesClient: &outboundFirewallRulesClient, diff --git a/internal/services/mssql/mssql_managed_database_resource.go b/internal/services/mssql/mssql_managed_database_resource.go index 54424cac2b44..337d0dc9b7a2 100644 --- a/internal/services/mssql/mssql_managed_database_resource.go +++ b/internal/services/mssql/mssql_managed_database_resource.go @@ -6,19 +6,32 @@ import ( "time" "github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/v5.0/sql" // nolint: staticcheck + "github.com/hashicorp/go-azure-helpers/lang/pointer" "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/mssql/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/validation" "github.com/hashicorp/terraform-provider-azurerm/utils" ) type MsSqlManagedDatabaseModel struct { - Name string `tfschema:"name"` - ManagedInstanceId string `tfschema:"managed_instance_id"` + Name string `tfschema:"name"` + ManagedInstanceId string `tfschema:"managed_instance_id"` + LongTermRetentionPolicy []LongTermRetentionPolicy `tfschema:"long_term_retention_policy"` + ShortTermRetentionDays int32 `tfschema:"short_term_retention_days"` +} + +type LongTermRetentionPolicy struct { + WeeklyRetention string `tfschema:"weekly_retention"` + MonthlyRetention string `tfschema:"monthly_retention"` + YearlyRetention string `tfschema:"yearly_retention"` + WeekOfYear int32 `tfschema:"week_of_year"` } var _ sdk.Resource = MsSqlManagedDatabaseResource{} +var _ sdk.ResourceWithUpdate = MsSqlManagedDatabaseResource{} type MsSqlManagedDatabaseResource struct{} @@ -49,6 +62,15 @@ func (r MsSqlManagedDatabaseResource) Arguments() map[string]*pluginsdk.Schema { ForceNew: true, ValidateFunc: validate.ManagedInstanceID, }, + + "long_term_retention_policy": helper.LongTermRetentionPolicySchema(), + + "short_term_retention_days": { + Type: pluginsdk.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 35), + Default: 7, + }, } } @@ -62,6 +84,8 @@ func (r MsSqlManagedDatabaseResource) Create() sdk.ResourceFunc { Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { client := metadata.Client.MSSQL.ManagedDatabasesClient instancesClient := metadata.Client.MSSQL.ManagedInstancesClient + longTermRetentionClient := metadata.Client.MSSQL.ManagedInstancesLongTermRetentionPoliciesClient + shortTermRetentionClient := metadata.Client.MSSQL.ManagedInstancesShortTermRetentionPoliciesClient var model MsSqlManagedDatabaseModel if err := metadata.Decode(&model); err != nil { @@ -106,6 +130,35 @@ func (r MsSqlManagedDatabaseResource) Create() sdk.ResourceFunc { return fmt.Errorf("waiting for creation of %s: %+v", id, err) } + if len(model.LongTermRetentionPolicy) > 0 { + longTermRetentionProps := expandLongTermRetentionPolicy(model.LongTermRetentionPolicy) + + longTermRetentionPolicy := sql.ManagedInstanceLongTermRetentionPolicy{ + BaseLongTermRetentionPolicyProperties: &longTermRetentionProps, + } + + longTermRetentionFuture, err := longTermRetentionClient.CreateOrUpdate(ctx, id.ResourceGroup, id.ManagedInstanceName, id.DatabaseName, longTermRetentionPolicy) + if err != nil { + return fmt.Errorf("setting Long Term Retention Policies for %s: %+v", id, err) + } + + if err = longTermRetentionFuture.WaitForCompletionRef(ctx, longTermRetentionClient.Client); err != nil { + return fmt.Errorf("waiting for update of Long Term Retention Policies for %s: %+v", id, err) + } + } + + if model.ShortTermRetentionDays > 0 { + + shortTermRetentionPolicy := sql.ManagedBackupShortTermRetentionPolicy{ + ManagedBackupShortTermRetentionPolicyProperties: &sql.ManagedBackupShortTermRetentionPolicyProperties{ + RetentionDays: pointer.To(model.ShortTermRetentionDays), + }, + } + if _, err := shortTermRetentionClient.CreateOrUpdate(ctx, id.ResourceGroup, id.ManagedInstanceName, id.DatabaseName, shortTermRetentionPolicy); err != nil { + return fmt.Errorf("setting Short Term Retention Policy for %s: %+v", id, err) + } + } + metadata.SetID(id) return nil @@ -113,11 +166,68 @@ func (r MsSqlManagedDatabaseResource) Create() sdk.ResourceFunc { } } +func (r MsSqlManagedDatabaseResource) Update() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + longTermRetentionClient := metadata.Client.MSSQL.ManagedInstancesLongTermRetentionPoliciesClient + shortTermRetentionClient := metadata.Client.MSSQL.ManagedInstancesShortTermRetentionPoliciesClient + + var model MsSqlManagedDatabaseModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + managedInstanceId, err := parse.ManagedInstanceID(model.ManagedInstanceId) + if err != nil { + return fmt.Errorf("parsing `managed_instance_id`: %v", err) + } + + id := parse.NewManagedDatabaseID(managedInstanceId.SubscriptionId, + managedInstanceId.ResourceGroup, managedInstanceId.Name, model.Name) + + d := metadata.ResourceData + + if d.HasChange("long_term_retention_policy") { + longTermRetentionProps := expandLongTermRetentionPolicy(model.LongTermRetentionPolicy) + + longTermRetentionPolicy := sql.ManagedInstanceLongTermRetentionPolicy{ + BaseLongTermRetentionPolicyProperties: &longTermRetentionProps, + } + + longTermRetentionFuture, err := longTermRetentionClient.CreateOrUpdate(ctx, id.ResourceGroup, id.ManagedInstanceName, id.DatabaseName, longTermRetentionPolicy) + if err != nil { + return fmt.Errorf("updating Long Term Retention Policies for %s: %+v", id, err) + } + + if err = longTermRetentionFuture.WaitForCompletionRef(ctx, longTermRetentionClient.Client); err != nil { + return fmt.Errorf("waiting for update of Long Term Retention Policies for %s: %+v", id, err) + } + } + + if d.HasChange("short_term_retention_days") { + + shortTermRetentionPolicy := sql.ManagedBackupShortTermRetentionPolicy{ + ManagedBackupShortTermRetentionPolicyProperties: &sql.ManagedBackupShortTermRetentionPolicyProperties{ + RetentionDays: pointer.To(model.ShortTermRetentionDays), + }, + } + if _, err := shortTermRetentionClient.CreateOrUpdate(ctx, id.ResourceGroup, id.ManagedInstanceName, id.DatabaseName, shortTermRetentionPolicy); err != nil { + return fmt.Errorf("updating Short Term Retention Policy for %s: %+v", id, err) + } + } + return nil + }, + } +} + func (r MsSqlManagedDatabaseResource) Read() sdk.ResourceFunc { return sdk.ResourceFunc{ Timeout: 5 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { client := metadata.Client.MSSQL.ManagedDatabasesClient + longTermRetentionClient := metadata.Client.MSSQL.ManagedInstancesLongTermRetentionPoliciesClient + shortTermRetentionClient := metadata.Client.MSSQL.ManagedInstancesShortTermRetentionPoliciesClient id, err := parse.ManagedDatabaseID(metadata.ResourceData.Id()) if err != nil { @@ -135,7 +245,7 @@ func (r MsSqlManagedDatabaseResource) Read() sdk.ResourceFunc { if utils.ResponseWasNotFound(result.Response) { return metadata.MarkAsGone(id) } - return fmt.Errorf("retrieving %s: %v", id, err) + return fmt.Errorf("retrieving %s: %v", *id, err) } managedInstanceId := parse.NewManagedInstanceID(id.SubscriptionId, id.ResourceGroup, id.ManagedInstanceName) @@ -145,6 +255,22 @@ func (r MsSqlManagedDatabaseResource) Read() sdk.ResourceFunc { ManagedInstanceId: managedInstanceId.ID(), } + ltrResp, err := longTermRetentionClient.Get(ctx, id.ResourceGroup, id.ManagedInstanceName, id.DatabaseName) + if err != nil { + return fmt.Errorf("retrieving Long Term Retention Policy for %s: %v", *id, err) + } + + model.LongTermRetentionPolicy = flattenLongTermRetentionPolicy(ltrResp) + + shortTermRetentionResp, err := shortTermRetentionClient.Get(ctx, id.ResourceGroup, id.ManagedInstanceName, id.DatabaseName) + if err != nil { + return fmt.Errorf("retrieving Short Term Retention Policy for %s: %v", *id, err) + } + + if shortTermRetentionResp.RetentionDays != nil { + model.ShortTermRetentionDays = *shortTermRetentionResp.RetentionDays + } + return metadata.Encode(&model) }, } @@ -174,3 +300,44 @@ func (r MsSqlManagedDatabaseResource) Delete() sdk.ResourceFunc { }, } } + +func expandLongTermRetentionPolicy(ltrPolicy []LongTermRetentionPolicy) sql.BaseLongTermRetentionPolicyProperties { + return sql.BaseLongTermRetentionPolicyProperties{ + WeeklyRetention: <rPolicy[0].WeeklyRetention, + MonthlyRetention: <rPolicy[0].MonthlyRetention, + YearlyRetention: <rPolicy[0].YearlyRetention, + WeekOfYear: <rPolicy[0].WeekOfYear, + } +} + +func flattenLongTermRetentionPolicy(ltrPolicy sql.ManagedInstanceLongTermRetentionPolicy) []LongTermRetentionPolicy { + + ltrModel := LongTermRetentionPolicy{} + + weeklyRetention := "" + if ltrPolicy.WeeklyRetention != nil { + weeklyRetention = *ltrPolicy.WeeklyRetention + } + + monthlyRetention := "" + if ltrPolicy.MonthlyRetention != nil { + monthlyRetention = *ltrPolicy.MonthlyRetention + } + + yearlyRetention := "" + if ltrPolicy.YearlyRetention != nil { + yearlyRetention = *ltrPolicy.YearlyRetention + } + + ltrModel = LongTermRetentionPolicy{ + WeeklyRetention: weeklyRetention, + MonthlyRetention: monthlyRetention, + YearlyRetention: yearlyRetention, + } + + if ltrPolicy.WeekOfYear != nil { + ltrModel.WeekOfYear = *ltrPolicy.WeekOfYear + } + + return []LongTermRetentionPolicy{ltrModel} +} diff --git a/internal/services/mssql/mssql_managed_database_resource_test.go b/internal/services/mssql/mssql_managed_database_resource_test.go index bc92f46125b6..7b5a2f78bc72 100644 --- a/internal/services/mssql/mssql_managed_database_resource_test.go +++ b/internal/services/mssql/mssql_managed_database_resource_test.go @@ -29,6 +29,27 @@ func TestAccMsSqlManagedDatabase_basic(t *testing.T) { }) } +func TestAccMsSqlManagedDatabase_withRetentionPolicies(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_mssql_managed_database", "test") + r := MsSqlManagedDatabase{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.withRetentionPolicies(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(""), + { + Config: r.withRetentionPoliciesUpdated(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + }) +} + func (r MsSqlManagedDatabase) Exists(ctx context.Context, client *clients.Client, state *acceptance.InstanceState) (*bool, error) { id, err := parse.ManagedDatabaseID(state.ID) if err != nil { @@ -56,3 +77,45 @@ resource "azurerm_mssql_managed_database" "test" { } `, MsSqlManagedInstanceResource{}.basic(data), data.RandomInteger) } + +func (r MsSqlManagedDatabase) withRetentionPolicies(data acceptance.TestData) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_mssql_managed_database" "test" { + managed_instance_id = azurerm_mssql_managed_instance.test.id + name = "acctest-%[2]d" + + long_term_retention_policy { + weekly_retention = "P1W" + monthly_retention = "P1M" + yearly_retention = "P1Y" + week_of_year = 1 + } + + short_term_retention_days = 3 + +} +`, MsSqlManagedInstanceResource{}.basic(data), data.RandomInteger) +} + +func (r MsSqlManagedDatabase) withRetentionPoliciesUpdated(data acceptance.TestData) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_mssql_managed_database" "test" { + managed_instance_id = azurerm_mssql_managed_instance.test.id + name = "acctest-%[2]d" + + long_term_retention_policy { + weekly_retention = "P10D" + monthly_retention = "P1M" + yearly_retention = "P1Y" + week_of_year = 4 + } + + short_term_retention_days = 4 + +} +`, MsSqlManagedInstanceResource{}.basic(data), data.RandomInteger) +} diff --git a/website/docs/r/mssql_managed_database.html.markdown b/website/docs/r/mssql_managed_database.html.markdown index e1ae4cbd35b4..139444cee99a 100644 --- a/website/docs/r/mssql_managed_database.html.markdown +++ b/website/docs/r/mssql_managed_database.html.markdown @@ -61,8 +61,21 @@ The following arguments are supported: * `managed_instance_id` - (Required) The ID of the Azure SQL Managed Instance on which to create this Managed Database. Changing this forces a new resource to be created. +* `long_term_retention_policy` - (Optional) A `long_term_retention_policy` block as defined below. + +* `short_term_retention_days` - (Optional) The backup retention period in days. This is how many days Point-in-Time Restore will be supported. + --- +A `long_term_retention_policy` block supports the following: + +* `weekly_retention` - (Optional) The weekly retention policy for an LTR backup in an ISO 8601 format. Valid value is between 1 to 520 weeks. e.g. `P1Y`, `P1M`, `P1W` or `P7D`. +* `monthly_retention` - (Optional) The monthly retention policy for an LTR backup in an ISO 8601 format. Valid value is between 1 to 120 months. e.g. `P1Y`, `P1M`, `P4W` or `P30D`. +* `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`. + +## Attributes Reference + The following attributes are exported: * `id` - The Azure SQL Managed Database ID. @@ -73,6 +86,7 @@ The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/l * `read` - (Defaults to 5 minutes) Used when retrieving the Mssql Managed Database. * `create` - (Defaults to 30 minutes) Used when creating the Mssql Managed Database. +* `update` - (Defaults to 30 minutes) Used when updating the Mssql Managed Database. * `delete` - (Defaults to 30 minutes) Used when deleting the Mssql Managed Database. ## Import