Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

azurerm_mssql_managed_database - support for retention policies #20845

Merged
merged 7 commits into from
Mar 9, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions internal/services/mssql/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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,
Expand Down
174 changes: 171 additions & 3 deletions internal/services/mssql/mssql_managed_database_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}

Expand Down Expand Up @@ -49,6 +62,14 @@ func (r MsSqlManagedDatabaseResource) Arguments() map[string]*pluginsdk.Schema {
ForceNew: true,
ValidateFunc: validate.ManagedInstanceID,
},

"long_term_retention_policy": helper.LongTermRetentionPolicySchema(),

"short_term_retention_days": {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there are default value for this returned from the API?

Type: pluginsdk.TypeInt,
Optional: true,
ValidateFunc: validation.IntBetween(1, 35),
},
}
}

Expand All @@ -62,6 +83,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 {
Expand Down Expand Up @@ -106,18 +129,106 @@ 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),
},
}
_, err := shortTermRetentionClient.CreateOrUpdate(ctx, id.ResourceGroup, id.ManagedInstanceName, id.DatabaseName, shortTermRetentionPolicy)
if err != nil {
return fmt.Errorf("setting Short Term Retention Policy for %s: %+v", id, err)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be simplified to:

Suggested change
_, err := shortTermRetentionClient.CreateOrUpdate(ctx, id.ResourceGroup, id.ManagedInstanceName, id.DatabaseName, shortTermRetentionPolicy)
if err != nil {
return fmt.Errorf("setting Short Term Retention Policy for %s: %+v", id, err)
}
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
},
}
}

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),
},
}
_, err := shortTermRetentionClient.CreateOrUpdate(ctx, id.ResourceGroup, id.ManagedInstanceName, id.DatabaseName, shortTermRetentionPolicy)
if err != nil {
return fmt.Errorf("updating Short Term Retention Policy for %s: %+v", id, err)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can simplify this to:

Suggested change
_, err := shortTermRetentionClient.CreateOrUpdate(ctx, id.ResourceGroup, id.ManagedInstanceName, id.DatabaseName, shortTermRetentionPolicy)
if err != nil {
return fmt.Errorf("updating Short Term Retention Policy for %s: %+v", id, err)
}
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 {
Expand All @@ -135,7 +246,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)
Expand All @@ -145,6 +256,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)
},
}
Expand Down Expand Up @@ -174,3 +301,44 @@ func (r MsSqlManagedDatabaseResource) Delete() sdk.ResourceFunc {
},
}
}

func expandLongTermRetentionPolicy(ltrPolicy []LongTermRetentionPolicy) sql.BaseLongTermRetentionPolicyProperties {
return sql.BaseLongTermRetentionPolicyProperties{
WeeklyRetention: &ltrPolicy[0].WeeklyRetention,
MonthlyRetention: &ltrPolicy[0].MonthlyRetention,
YearlyRetention: &ltrPolicy[0].YearlyRetention,
WeekOfYear: &ltrPolicy[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}
}
63 changes: 63 additions & 0 deletions internal/services/mssql/mssql_managed_database_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}
14 changes: 14 additions & 0 deletions website/docs/r/mssql_managed_database.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* `short_term_retention_days` - (Optional) The backup retention period in days. This is how many days Point-in-Time Restore will be supported.
* `short_term_retention_days` - (Optional) The backup retention period in days. This is how many days Point-in-Time Restore will be supported for.


---

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`.
Comment on lines +72 to +74
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should these be

Suggested change
* `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`.
* `weekly_retention_duration` - (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_duration` - (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_duration` - (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`.

or even this as retention is in the block name?

Suggested change
* `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`.
* `weekly_duration` - (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_duration` - (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_duration` - (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`.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've named them this way to keep them consistent with the equivalent in azurerm_mssql_database:

But happy to change the names here if that makes more sense?

* `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.
Expand All @@ -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
Expand Down