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_database - add support for long_term_retention_policy and short_term_retention_policy #8765

Merged
merged 8 commits into from
Oct 7, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
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