-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
App Service: Add schedule backup functionality #3330
Changes from 10 commits
96c45ec
26d6238
8cf19ab
26aff5b
07d21f1
58bc928
3f3b4d0
35aaa54
cac2b23
aad82ef
4db2c17
e106789
4bb7dd3
784c654
9d150ee
27933c4
e40f9d9
dbbe58a
7e5b03f
64ceb64
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package azure | ||
|
||
import ( | ||
"time" | ||
|
||
"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/suppress" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" | ||
) | ||
|
||
func SchemaAppServiceScheduleBackup() *schema.Schema { | ||
return &schema.Schema{ | ||
Type: schema.TypeList, | ||
Optional: true, | ||
MaxItems: 1, | ||
Elem: &schema.Resource{ | ||
Schema: map[string]*schema.Schema{ | ||
"frequency_interval": { | ||
Type: schema.TypeInt, | ||
Required: true, | ||
}, | ||
|
||
"frequency_unit": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Default: "Day", | ||
tombuildsstuff marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}, | ||
|
||
"keep_at_least_one_backup": { | ||
Type: schema.TypeBool, | ||
Optional: true, | ||
Default: false, | ||
}, | ||
|
||
"retention_period_in_days": { | ||
Type: schema.TypeInt, | ||
Optional: true, | ||
Default: 30, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thoughts around validation here too? |
||
}, | ||
|
||
"start_time": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
DiffSuppressFunc: suppress.RFC3339Time, | ||
ValidateFunc: validate.RFC3339Time, | ||
}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func ExpandAppServiceScheduleBackup(input interface{}) web.BackupSchedule { | ||
configs := input.([]interface{}) | ||
backupSchedule := web.BackupSchedule{} | ||
|
||
if len(configs) == 0 { | ||
return backupSchedule | ||
} | ||
|
||
config := configs[0].(map[string]interface{}) | ||
|
||
if v, ok := config["frequency_interval"].(int); ok { | ||
backupSchedule.FrequencyInterval = utils.Int32(int32(v)) | ||
} | ||
|
||
if v, ok := config["frequency_unit"]; ok { | ||
backupSchedule.FrequencyUnit = web.FrequencyUnit(v.(string)) | ||
} | ||
|
||
if v, ok := config["keep_at_least_one_backup"]; ok { | ||
backupSchedule.KeepAtLeastOneBackup = utils.Bool(v.(bool)) | ||
} | ||
|
||
if v, ok := config["retention_period_in_days"].(int); ok { | ||
backupSchedule.RetentionPeriodInDays = utils.Int32(int32(v)) | ||
} | ||
|
||
if v, ok := config["start_time"].(string); ok { | ||
dateTimeToStart, _ := time.Parse(time.RFC3339, v) //validated by schema | ||
backupSchedule.StartTime = &date.Time{Time: (dateTimeToStart)} | ||
} | ||
|
||
return backupSchedule | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -69,6 +69,19 @@ func resourceArmAppService() *schema.Resource { | |
|
||
"site_config": azure.SchemaAppServiceSiteConfig(), | ||
|
||
"backup_schedule": azure.SchemaAppServiceScheduleBackup(), | ||
|
||
"storage_account_url": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Sensitive: true, | ||
namku marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}, | ||
|
||
"backup_name": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
namku marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could we move all of these into a |
||
|
||
"client_affinity_enabled": { | ||
Type: schema.TypeBool, | ||
Optional: true, | ||
|
@@ -199,6 +212,9 @@ func resourceArmAppServiceCreate(d *schema.ResourceData, meta interface{}) error | |
name := d.Get("name").(string) | ||
resGroup := d.Get("resource_group_name").(string) | ||
|
||
storageAccountURL := d.Get("storage_account_url").(string) | ||
backupName := d.Get("backup_name").(string) | ||
|
||
if requireResourcesToBeImported && d.IsNewResource() { | ||
existing, err := client.Get(ctx, resGroup, name) | ||
if err != nil { | ||
|
@@ -269,6 +285,22 @@ func resourceArmAppServiceCreate(d *schema.ResourceData, meta interface{}) error | |
return err | ||
} | ||
|
||
backupSchedule := azure.ExpandAppServiceScheduleBackup(d.Get("backup_schedule")) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't believe we need this in the create method because this will be covered by the update that we go into immediately after creating. |
||
if storageAccountURL != "" { | ||
request := web.BackupRequest{ | ||
BackupRequestProperties: &web.BackupRequestProperties{ | ||
BackupName: utils.String(backupName), | ||
StorageAccountURL: utils.String(storageAccountURL), | ||
Enabled: utils.Bool(true), | ||
BackupSchedule: &backupSchedule, | ||
}, | ||
} | ||
_, err = client.UpdateBackupConfiguration(ctx, resGroup, name, request) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
read, err := client.Get(ctx, resGroup, name) | ||
if err != nil { | ||
return err | ||
|
@@ -294,6 +326,9 @@ func resourceArmAppServiceUpdate(d *schema.ResourceData, meta interface{}) error | |
resGroup := id.ResourceGroup | ||
name := id.Path["sites"] | ||
|
||
storageAccountURL := d.Get("storage_account_url").(string) | ||
backupName := d.Get("backup_name").(string) | ||
|
||
location := azureRMNormalizeLocation(d.Get("location").(string)) | ||
appServicePlanId := d.Get("app_service_plan_id").(string) | ||
enabled := d.Get("enabled").(bool) | ||
|
@@ -338,6 +373,29 @@ func resourceArmAppServiceUpdate(d *schema.ResourceData, meta interface{}) error | |
} | ||
} | ||
|
||
if d.HasChange("backup_schedule") { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're going to run into a perpetual Terraform state difference here if a user changes There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 to this - could we make this change? |
||
backupSchedule := azure.ExpandAppServiceScheduleBackup(d.Get("backup_schedule")) | ||
if storageAccountURL != "" { | ||
request := web.BackupRequest{ | ||
BackupRequestProperties: &web.BackupRequestProperties{ | ||
BackupName: utils.String(backupName), | ||
StorageAccountURL: utils.String(storageAccountURL), | ||
Enabled: utils.Bool(true), | ||
BackupSchedule: &backupSchedule, | ||
}, | ||
} | ||
_, err = client.UpdateBackupConfiguration(ctx, resGroup, name, request) | ||
if err != nil { | ||
return err | ||
} | ||
} else { | ||
err = resourceArmDeleteScheduleBackup(d, meta) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could we also read these values back into the State in the Read function (the Read function contains some examples of this)? this allows diff's to be detected |
||
if d.HasChange("client_affinity_enabled") { | ||
|
||
affinity := d.Get("client_affinity_enabled").(bool) | ||
|
@@ -674,3 +732,23 @@ func flattenAppServiceSiteCredential(input *web.UserProperties) []interface{} { | |
|
||
return append(results, result) | ||
} | ||
|
||
func resourceArmDeleteScheduleBackup(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*ArmClient).appServicesClient | ||
ctx := meta.(*ArmClient).StopContext | ||
|
||
id, err := parseAzureResourceID(d.Id()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
resGroup := id.ResourceGroup | ||
name := id.Path["sites"] | ||
|
||
_, err = client.DeleteBackupConfiguration(ctx, resGroup, name) | ||
if err != nil { | ||
return err | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. rather than calling this method - could we switch to calling these 4 lined directly above? |
||
|
||
return nil | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,3 @@ | ||||||
# Example: a Basic schedule backup App Service | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could we make this:
Suggested change
|
||||||
|
||||||
This example provisions a schedule backup App Service. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could we make this:
Suggested change
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
resource "azurerm_resource_group" "testrg" { | ||
name = "${var.prefix}-resources" | ||
location = "${var.location}" | ||
} | ||
|
||
resource "azurerm_storage_account" "testsa" { | ||
name = "${var.prefix}sa" | ||
resource_group_name = "${azurerm_resource_group.testrg.name}" | ||
location = "${azurerm_resource_group.testrg.location}" | ||
account_tier = "Standard" | ||
account_replication_type = "LRS" | ||
} | ||
|
||
resource "azurerm_storage_container" "test" { | ||
name = "${var.prefix}-sc" | ||
resource_group_name = "${azurerm_resource_group.testrg.name}" | ||
storage_account_name = "${azurerm_storage_account.testsa.name}" | ||
container_access_type = "private" | ||
} | ||
|
||
resource "azurerm_app_service_plan" "test" { | ||
name = "${var.prefix}-splan" | ||
location = "${azurerm_resource_group.testrg.location}" | ||
resource_group_name = "${azurerm_resource_group.testrg.name}" | ||
|
||
sku { | ||
tier = "Standard" | ||
size = "S1" | ||
} | ||
} | ||
|
||
data "azurerm_storage_account_sas" "test" { | ||
connection_string = "${azurerm_storage_account.testsa.primary_connection_string}" | ||
https_only = true | ||
|
||
resource_types { | ||
service = false | ||
container = false | ||
object = true | ||
} | ||
|
||
services { | ||
blob = true | ||
queue = false | ||
table = false | ||
file = false | ||
} | ||
|
||
start = "2019-03-21" | ||
expiry = "2020-03-21" | ||
|
||
permissions { | ||
read = false | ||
write = true | ||
delete = false | ||
list = false | ||
add = false | ||
create = false | ||
update = false | ||
process = false | ||
} | ||
} | ||
|
||
resource "azurerm_app_service" "test" { | ||
name = "${var.prefix}-appservice" | ||
location = "${azurerm_resource_group.testrg.location}" | ||
resource_group_name = "${azurerm_resource_group.testrg.name}" | ||
app_service_plan_id = "${azurerm_app_service_plan.test.id}" | ||
storage_account_url = "https://${azurerm_storage_account.testsa.name}.blob.core.windows.net/${azurerm_storage_container.test.name}${data.azurerm_storage_account_sas.test.sas}&sr=b" | ||
|
||
backup_schedule { | ||
frequency_interval = "30" | ||
# frequency_unit = "Day" | ||
# keep_at_least_one_backup = false | ||
# retention_period_in_days = "9" | ||
# start_time = "2019-04-29T09:40:00+02:00" | ||
} | ||
|
||
site_config { | ||
dotnet_framework_version = "v4.0" | ||
scm_type = "LocalGit" | ||
} | ||
|
||
app_settings = { | ||
"SOME_KEY" = "some-value" | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
output "app_service_name" { | ||
value = "${azurerm_app_service.test.name}" | ||
} | ||
|
||
output "app_service_default_hostname" { | ||
value = "https://${azurerm_app_service.test.default_site_hostname}" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
variable "prefix" { | ||
description = "The prefix used for all resources in this example" | ||
} | ||
|
||
variable "location" { | ||
description = "The Azure location where all resources in this example should be created" | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any validation we can add here? Like greater than 0 and Less than some number?