Skip to content

Commit

Permalink
azurerm_mssql_database - add support for `long_term_retention_polic…
Browse files Browse the repository at this point in the history
…y` and `short_term_retention_policy` (#8765)

Co-authored-by: Ramunas Leknickas <[email protected]>
Co-authored-by: Ramunas <[email protected]>
Co-authored-by: kt <[email protected]>
  • Loading branch information
4 people authored Oct 7, 2020
1 parent a4b36ce commit edacf0a
Show file tree
Hide file tree
Showing 7 changed files with 489 additions and 5 deletions.
7 changes: 7 additions & 0 deletions azurerm/helpers/azure/mssql.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,10 @@ func ValidateMsSqlElasticPoolName(i interface{}, k string) (_ []string, errors [

return nil, nil
}

func ValidateLongTermRetentionPoliciesIsoFormat(i interface{}, k string) (_ []string, errors []error) {
if m, regexErrs := validate.RegExHelper(i, k, `^P[0-9]*[YMWD]`); !m {
return nil, append(regexErrs, fmt.Errorf(`%q has to be a valid Duration format, starting with "P" and ending with either of the letters "YMWD"`, k))
}
return nil, nil
}
12 changes: 11 additions & 1 deletion azurerm/internal/services/mssql/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
)

type Client struct {
BackupLongTermRetentionPoliciesClient *sql.BackupLongTermRetentionPoliciesClient
BackupShortTermRetentionPoliciesClient *sql.BackupShortTermRetentionPoliciesClient
DatabasesClient *sql.DatabasesClient
DatabaseExtendedBlobAuditingPoliciesClient *sql.ExtendedDatabaseBlobAuditingPoliciesClient
DatabaseThreatDetectionPoliciesClient *sql.DatabaseThreatDetectionPoliciesClient
Expand All @@ -23,6 +25,12 @@ type Client struct {
}

func NewClient(o *common.ClientOptions) *Client {
BackupLongTermRetentionPoliciesClient := sql.NewBackupLongTermRetentionPoliciesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&BackupLongTermRetentionPoliciesClient.Client, o.ResourceManagerAuthorizer)

BackupShortTermRetentionPoliciesClient := sql.NewBackupShortTermRetentionPoliciesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&BackupShortTermRetentionPoliciesClient.Client, o.ResourceManagerAuthorizer)

databasesClient := sql.NewDatabasesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&databasesClient.Client, o.ResourceManagerAuthorizer)

Expand Down Expand Up @@ -63,7 +71,9 @@ func NewClient(o *common.ClientOptions) *Client {
o.ConfigureClient(&sqlVirtualMachinesClient.Client, o.ResourceManagerAuthorizer)

return &Client{
DatabasesClient: &databasesClient,
BackupLongTermRetentionPoliciesClient: &BackupLongTermRetentionPoliciesClient,
BackupShortTermRetentionPoliciesClient: &BackupShortTermRetentionPoliciesClient,
DatabasesClient: &databasesClient,
DatabaseExtendedBlobAuditingPoliciesClient: &databaseExtendedBlobAuditingPoliciesClient,
DatabaseThreatDetectionPoliciesClient: &databaseThreatDetectionPoliciesClient,
DatabaseVulnerabilityAssessmentRuleBaselinesClient: &databaseVulnerabilityAssessmentRuleBaselinesClient,
Expand Down
171 changes: 171 additions & 0 deletions azurerm/internal/services/mssql/helper/sql_retention_policies.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package helper

import (
"github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/v3.0/sql"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func LongTermRetentionPolicySchema() *schema.Schema {
return &schema.Schema{
Type: schema.TypeList,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
// WeeklyRetention - The weekly retention policy for an LTR backup in an ISO 8601 format.
"weekly_retention": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: azure.ValidateLongTermRetentionPoliciesIsoFormat,
},
// MonthlyRetention - The monthly retention policy for an LTR backup in an ISO 8601 format.
"monthly_retention": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: azure.ValidateLongTermRetentionPoliciesIsoFormat,
},
// YearlyRetention - The yearly retention policy for an LTR backup in an ISO 8601 format.
"yearly_retention": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: azure.ValidateLongTermRetentionPoliciesIsoFormat,
},
// WeekOfYear - The week of year to take the yearly backup in an ISO 8601 format.
"week_of_year": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
ValidateFunc: validation.IntBetween(1, 52),
},
},
},
}
}

func ShortTermRetentionPolicySchema() *schema.Schema {
return &schema.Schema{
Type: schema.TypeList,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"retention_days": {
Type: schema.TypeInt,
Required: true,
ValidateFunc: validation.IntBetween(7, 35),
},
},
},
}
}

func ExpandLongTermRetentionPolicy(input []interface{}) *sql.LongTermRetentionPolicyProperties {
if len(input) == 0 || input[0] == nil {
return nil
}

longTermRetentionPolicy := input[0].(map[string]interface{})

longTermPolicyProperties := sql.LongTermRetentionPolicyProperties{
WeeklyRetention: utils.String("PT0S"),
MonthlyRetention: utils.String("PT0S"),
YearlyRetention: utils.String("PT0S"),
WeekOfYear: utils.Int32(0),
}

if v, ok := longTermRetentionPolicy["weekly_retention"]; ok {
longTermPolicyProperties.WeeklyRetention = utils.String(v.(string))
}

if v, ok := longTermRetentionPolicy["monthly_retention"]; ok {
longTermPolicyProperties.MonthlyRetention = utils.String(v.(string))
}

if v, ok := longTermRetentionPolicy["yearly_retention"]; ok {
longTermPolicyProperties.YearlyRetention = utils.String(v.(string))
}

if v, ok := longTermRetentionPolicy["week_of_year"]; ok {
longTermPolicyProperties.WeekOfYear = utils.Int32(int32(v.(int)))
}

return &longTermPolicyProperties
}

func FlattenLongTermRetentionPolicy(longTermRetentionPolicy *sql.BackupLongTermRetentionPolicy, d *schema.ResourceData) []interface{} {
if longTermRetentionPolicy == nil {
return []interface{}{}
}

monthlyRetention := "PT0S"
if longTermRetentionPolicy.MonthlyRetention != nil {
monthlyRetention = *longTermRetentionPolicy.MonthlyRetention
}

weeklyRetention := "PT0S"
if longTermRetentionPolicy.WeeklyRetention != nil {
weeklyRetention = *longTermRetentionPolicy.WeeklyRetention
}

weekOfYear := int32(0)
if longTermRetentionPolicy.WeekOfYear != nil {
weekOfYear = *longTermRetentionPolicy.WeekOfYear
}

yearlyRetention := "PT0S"
if longTermRetentionPolicy.YearlyRetention != nil {
yearlyRetention = *longTermRetentionPolicy.YearlyRetention
}

return []interface{}{
map[string]interface{}{
"monthly_retention": monthlyRetention,
"weekly_retention": weeklyRetention,
"week_of_year": weekOfYear,
"yearly_retention": yearlyRetention,
},
}
}

func ExpandShortTermRetentionPolicy(input []interface{}) *sql.BackupShortTermRetentionPolicyProperties {
if len(input) == 0 || input[0] == nil {
return nil
}

shortTermRetentionPolicy := input[0].(map[string]interface{})

shortTermPolicyProperties := sql.BackupShortTermRetentionPolicyProperties{
RetentionDays: utils.Int32(7),
}

if v, ok := shortTermRetentionPolicy["retention_days"]; ok {
shortTermPolicyProperties.RetentionDays = utils.Int32(int32(v.(int)))
}

return &shortTermPolicyProperties
}

func FlattenShortTermRetentionPolicy(shortTermRetentionPolicy *sql.BackupShortTermRetentionPolicy, d *schema.ResourceData) []interface{} {
if shortTermRetentionPolicy == nil {
return []interface{}{}
}

retentionDays := int32(7)
if shortTermRetentionPolicy.RetentionDays != nil {
retentionDays = *shortTermRetentionPolicy.RetentionDays
}

return []interface{}{
map[string]interface{}{
"retention_days": retentionDays,
},
}
}
87 changes: 85 additions & 2 deletions azurerm/internal/services/mssql/mssql_database_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ func resourceArmMsSqlDatabase() *schema.Resource {
}, false),
},

"long_term_retention_policy": helper.LongTermRetentionPolicySchema(),

"short_term_retention_policy": helper.ShortTermRetentionPolicySchema(),

"max_size_gb": {
Type: schema.TypeInt,
Optional: true,
Expand Down Expand Up @@ -292,6 +296,8 @@ func resourceArmMsSqlDatabaseCreateUpdate(d *schema.ResourceData, meta interface
auditingClient := meta.(*clients.Client).MSSQL.DatabaseExtendedBlobAuditingPoliciesClient
serverClient := meta.(*clients.Client).MSSQL.ServersClient
threatClient := meta.(*clients.Client).MSSQL.DatabaseThreatDetectionPoliciesClient
longTermRetentionClient := meta.(*clients.Client).MSSQL.BackupLongTermRetentionPoliciesClient
shortTermRetentionClient := meta.(*clients.Client).MSSQL.BackupShortTermRetentionPoliciesClient
ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d)
defer cancel()

Expand Down Expand Up @@ -385,9 +391,10 @@ func resourceArmMsSqlDatabaseCreateUpdate(d *schema.ResourceData, meta interface
params.DatabaseProperties.RestorePointInTime = &date.Time{Time: restorePointInTime}
}

if v, ok := d.GetOk("sku_name"); ok {
skuName, ok := d.GetOk("sku_name")
if ok {
params.Sku = &sql.Sku{
Name: utils.String(v.(string)),
Name: utils.String(skuName.(string)),
}
}

Expand Down Expand Up @@ -436,13 +443,58 @@ func resourceArmMsSqlDatabaseCreateUpdate(d *schema.ResourceData, meta interface
}
}

// hyper-scale SKU's do not support LRP currently
if d.HasChange("long_term_retention_policy") {
v := d.Get("long_term_retention_policy")
longTermRetentionProps := helper.ExpandLongTermRetentionPolicy(v.([]interface{}))
if longTermRetentionProps != nil {
longTermRetentionPolicy := sql.BackupLongTermRetentionPolicy{}

if !strings.HasPrefix(skuName.(string), "HS") {
longTermRetentionPolicy.LongTermRetentionPolicyProperties = longTermRetentionProps
}

longTermRetentionfuture, err := longTermRetentionClient.CreateOrUpdate(ctx, serverId.ResourceGroup, serverId.Name, name, longTermRetentionPolicy)
if err != nil {
return fmt.Errorf("Error issuing create/update request for Sql Server %q (Database %q) Long Term Retention Policies (Resource Group %q): %+v", serverId.Name, name, serverId.ResourceGroup, err)
}

if err = longTermRetentionfuture.WaitForCompletionRef(ctx, longTermRetentionClient.Client); err != nil {
return fmt.Errorf("Error waiting for completion of Create/Update for Sql Server %q (Database %q) Long Term Retention Policies (Resource Group %q): %+v", serverId.Name, name, serverId.ResourceGroup, err)
}
}
}

if d.HasChange("short_term_retention_policy") {
v := d.Get("short_term_retention_policy")
backupShortTermPolicyProps := helper.ExpandShortTermRetentionPolicy(v.([]interface{}))
if backupShortTermPolicyProps != nil {
backupShortTermPolicy := sql.BackupShortTermRetentionPolicy{}

if !strings.HasPrefix(skuName.(string), "HS") {
backupShortTermPolicy.BackupShortTermRetentionPolicyProperties = backupShortTermPolicyProps
}

shortTermRetentionFuture, err := shortTermRetentionClient.CreateOrUpdate(ctx, serverId.ResourceGroup, serverId.Name, name, backupShortTermPolicy)
if err != nil {
return fmt.Errorf("Error issuing create/update request for Sql Server %q (Database %q) Short Term Retention Policies (Resource Group %q): %+v", serverId.Name, name, serverId.ResourceGroup, err)
}

if err = shortTermRetentionFuture.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("Error waiting for completion of Create/Update for Sql Server %q (Database %q) Short Term Retention Policies (Resource Group %q): %+v", serverId.Name, name, serverId.ResourceGroup, err)
}
}
}

return resourceArmMsSqlDatabaseRead(d, meta)
}

func resourceArmMsSqlDatabaseRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).MSSQL.DatabasesClient
threatClient := meta.(*clients.Client).MSSQL.DatabaseThreatDetectionPoliciesClient
auditingClient := meta.(*clients.Client).MSSQL.DatabaseExtendedBlobAuditingPoliciesClient
longTermRetentionClient := meta.(*clients.Client).MSSQL.BackupLongTermRetentionPoliciesClient
shortTermRetentionClient := meta.(*clients.Client).MSSQL.BackupShortTermRetentionPoliciesClient
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

Expand Down Expand Up @@ -470,6 +522,7 @@ func resourceArmMsSqlDatabaseRead(d *schema.ResourceData, meta interface{}) erro
}
d.Set("server_id", serverResp.ID)

skuName := ""
if props := resp.DatabaseProperties; props != nil {
d.Set("auto_pause_delay_in_minutes", props.AutoPauseDelay)
d.Set("collation", props.Collation)
Expand All @@ -485,6 +538,9 @@ func resourceArmMsSqlDatabaseRead(d *schema.ResourceData, meta interface{}) erro
} else if props.ReadScale == sql.DatabaseReadScaleDisabled {
d.Set("read_scale", false)
}
if props.CurrentServiceObjectiveName != nil {
skuName = *props.CurrentServiceObjectiveName
}
d.Set("sku_name", props.CurrentServiceObjectiveName)
d.Set("zone_redundant", props.ZoneRedundant)
}
Expand All @@ -506,6 +562,33 @@ func resourceArmMsSqlDatabaseRead(d *schema.ResourceData, meta interface{}) erro
return fmt.Errorf("failure in setting `extended_auditing_policy`: %+v", err)
}

// Hyper Scale SKU's do not currently support LRP and do not honour normal SRP operations
if !strings.HasPrefix(skuName, "HS") {
longTermPolicy, err := longTermRetentionClient.Get(ctx, id.ResourceGroup, id.MsSqlServer, id.Name)
if err != nil {
return fmt.Errorf("Error retrieving Long Term Policies for Database %q (Sql Server %q ;Resource Group %q): %+v", id.Name, id.MsSqlServer, id.ResourceGroup, err)
}
flattenlongTermPolicy := helper.FlattenLongTermRetentionPolicy(&longTermPolicy, d)
if err := d.Set("long_term_retention_policy", flattenlongTermPolicy); err != nil {
return fmt.Errorf("failure in setting `long_term_retention_policy`: %+v", err)
}

shortTermPolicy, err := shortTermRetentionClient.Get(ctx, id.ResourceGroup, id.MsSqlServer, id.Name)
if err != nil {
return fmt.Errorf("Error retrieving Short Term Policies for Database %q (Sql Server %q ;Resource Group %q): %+v", id.Name, id.MsSqlServer, id.ResourceGroup, err)
}

flattenShortTermPolicy := helper.FlattenShortTermRetentionPolicy(&shortTermPolicy, d)
if err := d.Set("short_term_retention_policy", flattenShortTermPolicy); err != nil {
return fmt.Errorf("failure in setting `short_term_retention_policy`: %+v", err)
}
} else {
// HS SKUs need the retention policies zeroing for state consistency
zero := make([]interface{}, 0)
d.Set("long_term_retention_policy", zero)
d.Set("short_term_retention_policy", zero)
}

return tags.FlattenAndSet(d, resp.Tags)
}

Expand Down
Loading

0 comments on commit edacf0a

Please sign in to comment.