Skip to content

Commit

Permalink
Merge pull request #3804 from terraform-providers/f/app-service-backups
Browse files Browse the repository at this point in the history
r/app_service: support for backups
  • Loading branch information
tombuildsstuff authored Jul 17, 2019
2 parents 56e9347 + 580f68b commit 2f5243c
Show file tree
Hide file tree
Showing 8 changed files with 568 additions and 9 deletions.
188 changes: 188 additions & 0 deletions azurerm/helpers/azure/app_service_schedule_backup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
package azure

import (
"time"

"github.com/hashicorp/terraform/helper/validation"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"

"github.com/Azure/go-autorest/autorest/date"

"github.com/Azure/azure-sdk-for-go/services/web/mgmt/2018-02-01/web"
"github.com/hashicorp/terraform/helper/schema"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate"
)

func SchemaAppServiceBackup() *schema.Schema {
return &schema.Schema{
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validate.NoEmptyStrings,
},

"storage_account_url": {
Type: schema.TypeString,
Required: true,
Sensitive: true,
ValidateFunc: validate.URLIsHTTPS,
},

"enabled": {
Type: schema.TypeBool,
Optional: true,
Default: true,
},

"schedule": {
Type: schema.TypeList,
Required: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"frequency_interval": {
Type: schema.TypeInt,
Required: true,
ValidateFunc: validation.IntBetween(0, 1000),
},

"frequency_unit": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{
"Day",
"Hour",
}, false),
},

"keep_at_least_one_backup": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},

"retention_period_in_days": {
Type: schema.TypeInt,
Optional: true,
Default: 30,
ValidateFunc: validation.IntBetween(0, 9999999),
},

"start_time": {
Type: schema.TypeString,
Optional: true,
DiffSuppressFunc: suppress.RFC3339Time,
ValidateFunc: validate.RFC3339Time,
},
},
},
},
},
},
}
}

func ExpandAppServiceBackup(input []interface{}) *web.BackupRequest {
if len(input) == 0 {
return nil
}

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

name := vals["name"].(string)
storageAccountUrl := vals["storage_account_url"].(string)
enabled := vals["enabled"].(bool)

request := &web.BackupRequest{
BackupRequestProperties: &web.BackupRequestProperties{
BackupName: utils.String(name),
StorageAccountURL: utils.String(storageAccountUrl),
Enabled: utils.Bool(enabled),
},
}

scheduleRaw := vals["schedule"].([]interface{})
if len(scheduleRaw) > 0 {
schedule := scheduleRaw[0].(map[string]interface{})
backupSchedule := web.BackupSchedule{}

if v, ok := schedule["frequency_interval"].(int); ok {
backupSchedule.FrequencyInterval = utils.Int32(int32(v))
}

if v, ok := schedule["frequency_unit"]; ok {
backupSchedule.FrequencyUnit = web.FrequencyUnit(v.(string))
}

if v, ok := schedule["keep_at_least_one_backup"]; ok {
backupSchedule.KeepAtLeastOneBackup = utils.Bool(v.(bool))
}

if v, ok := schedule["retention_period_in_days"].(int); ok {
backupSchedule.RetentionPeriodInDays = utils.Int32(int32(v))
}

if v, ok := schedule["start_time"].(string); ok {
dateTimeToStart, _ := time.Parse(time.RFC3339, v) //validated by schema
backupSchedule.StartTime = &date.Time{Time: dateTimeToStart}
}

request.BackupRequestProperties.BackupSchedule = &backupSchedule
}

return request
}

func FlattenAppServiceBackup(input *web.BackupRequestProperties) []interface{} {
if input == nil {
return []interface{}{}
}

output := make(map[string]interface{})

if input.BackupName != nil {
output["name"] = *input.BackupName
}
if input.Enabled != nil {
output["enabled"] = *input.Enabled
}
if input.StorageAccountURL != nil {
output["storage_account_url"] = *input.StorageAccountURL
}

schedules := make([]interface{}, 0)
if input.BackupSchedule != nil {
v := *input.BackupSchedule

schedule := make(map[string]interface{})

if v.FrequencyInterval != nil {
schedule["frequency_interval"] = int(*v.FrequencyInterval)
}

schedule["frequency_unit"] = string(v.FrequencyUnit)

if v.KeepAtLeastOneBackup != nil {
schedule["keep_at_least_one_backup"] = *v.KeepAtLeastOneBackup
}
if v.RetentionPeriodInDays != nil {
schedule["retention_period_in_days"] = int(*v.RetentionPeriodInDays)
}
if v.StartTime != nil && !v.StartTime.IsZero() {
schedule["start_time"] = v.StartTime.Format(time.RFC3339)
}

schedules = append(schedules, schedule)
}
output["schedule"] = schedules

return []interface{}{
output,
}
}
55 changes: 46 additions & 9 deletions azurerm/resource_arm_app_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ func resourceArmAppService() *schema.Resource {

"logs": azure.SchemaAppServiceLogsConfig(),

"backup": azure.SchemaAppServiceBackup(),

"client_affinity_enabled": {
Type: schema.TypeBool,
Optional: true,
Expand Down Expand Up @@ -242,17 +244,17 @@ func resourceArmAppServiceCreate(d *schema.ResourceData, meta interface{}) error

createFuture, err := client.CreateOrUpdate(ctx, resGroup, name, siteEnvelope)
if err != nil {
return err
return fmt.Errorf("Error creating App Service %q (Resource Group %q): %s", name, resGroup, err)
}

err = createFuture.WaitForCompletionRef(ctx, client.Client)
if err != nil {
return err
return fmt.Errorf("Error waiting for App Service %q (Resource Group %q) to be created: %s", name, resGroup, err)
}

read, err := client.Get(ctx, resGroup, name)
if err != nil {
return err
return fmt.Errorf("Error retrieving App Service %q (Resource Group %q): %s", name, resGroup, err)
}
if read.ID == nil {
return fmt.Errorf("Cannot read App Service %q (resource group %q) ID", name, resGroup)
Expand Down Expand Up @@ -281,6 +283,14 @@ func resourceArmAppServiceCreate(d *schema.ResourceData, meta interface{}) error
return fmt.Errorf("Error updating diagnostic logs config for App Service %q (Resource Group %q): %+s", name, resGroup, err)
}

backupRaw := d.Get("backup").([]interface{})
if backup := azure.ExpandAppServiceBackup(backupRaw); backup != nil {
_, err = client.UpdateBackupConfiguration(ctx, resGroup, name, *backup)
if err != nil {
return fmt.Errorf("Error updating Backup Settings for App Service %q (Resource Group %q): %s", name, resGroup, err)
}
}

return resourceArmAppServiceUpdate(d, meta)
}

Expand All @@ -297,6 +307,7 @@ func resourceArmAppServiceUpdate(d *schema.ResourceData, meta interface{}) error
name := id.Path["sites"]

location := azure.NormalizeLocation(d.Get("location").(string))

appServicePlanId := d.Get("app_service_plan_id").(string)
enabled := d.Get("enabled").(bool)
httpsOnly := d.Get("https_only").(bool)
Expand Down Expand Up @@ -367,6 +378,21 @@ func resourceArmAppServiceUpdate(d *schema.ResourceData, meta interface{}) error
}
}

if d.HasChange("backup") {
backupRaw := d.Get("backup").([]interface{})
if backup := azure.ExpandAppServiceBackup(backupRaw); backup != nil {
_, err = client.UpdateBackupConfiguration(ctx, resGroup, name, *backup)
if err != nil {
return fmt.Errorf("Error updating Backup Settings for App Service %q (Resource Group %q): %s", name, resGroup, err)
}
} else {
_, err = client.DeleteBackupConfiguration(ctx, resGroup, name)
if err != nil {
return fmt.Errorf("Error removing Backup Settings for App Service %q (Resource Group %q): %s", name, resGroup, err)
}
}
}

if d.HasChange("client_affinity_enabled") {

affinity := d.Get("client_affinity_enabled").(bool)
Expand Down Expand Up @@ -478,6 +504,13 @@ func resourceArmAppServiceRead(d *schema.ResourceData, meta interface{}) error {
return fmt.Errorf("Error retrieving the AuthSettings for App Service %q (Resource Group %q): %+v", name, resGroup, err)
}

backupResp, err := client.GetBackupConfiguration(ctx, resGroup, name)
if err != nil {
if !utils.ResponseWasNotFound(backupResp.Response) {
return fmt.Errorf("Error retrieving the BackupConfiguration for App Service %q (Resource Group %q): %+v", name, resGroup, err)
}
}

logsResp, err := client.GetDiagnosticLogsConfiguration(ctx, resGroup, name)
if err != nil {
return fmt.Errorf("Error retrieving the DiagnosticsLogsConfiguration for App Service %q (Resource Group %q): %+v", name, resGroup, err)
Expand Down Expand Up @@ -545,15 +578,19 @@ func resourceArmAppServiceRead(d *schema.ResourceData, meta interface{}) error {
delete(appSettings, "DIAGNOSTICS_AZUREBLOBRETENTIONINDAYS")

if err := d.Set("app_settings", appSettings); err != nil {
return err
return fmt.Errorf("Error setting `app_settings`: %s", err)
}

if err := d.Set("backup", azure.FlattenAppServiceBackup(backupResp.BackupRequestProperties)); err != nil {
return fmt.Errorf("Error setting `backup`: %s", err)
}

if err := d.Set("storage_account", azure.FlattenAppServiceStorageAccounts(storageAccountsResp.Properties)); err != nil {
return err
return fmt.Errorf("Error setting `storage_account`: %s", err)
}

if err := d.Set("connection_string", flattenAppServiceConnectionStrings(connectionStringsResp.Properties)); err != nil {
return err
return fmt.Errorf("Error setting `connection_string`: %s", err)
}

siteConfig := azure.FlattenAppServiceSiteConfig(configResp.SiteConfig)
Expand All @@ -573,17 +610,17 @@ func resourceArmAppServiceRead(d *schema.ResourceData, meta interface{}) error {

scm := flattenAppServiceSourceControl(scmResp.SiteSourceControlProperties)
if err := d.Set("source_control", scm); err != nil {
return err
return fmt.Errorf("Error setting `source_control`: %s", err)
}

siteCred := flattenAppServiceSiteCredential(siteCredResp.UserProperties)
if err := d.Set("site_credential", siteCred); err != nil {
return err
return fmt.Errorf("Error setting `site_credential`: %s", err)
}

identity := azure.FlattenAppServiceIdentity(resp.Identity)
if err := d.Set("identity", identity); err != nil {
return err
return fmt.Errorf("Error setting `identity`: %s", err)
}

flattenAndSetTags(d, resp.Tags)
Expand Down
Loading

0 comments on commit 2f5243c

Please sign in to comment.