Skip to content

Commit

Permalink
Support in-place update of app service slots (#1436)
Browse files Browse the repository at this point in the history
* Support in-place update of app service slots

- Upgrade to 2018-02-01 of the App Service API
- Remove `ForceNew` on several slot attributes and update them in place

* Translate IP restrictions addresses in new web app API

- Translate between CIDR-format IP addresses (a.b.c.d/x) and IP/subnet
mask combination
- Add additional test IP restriction to capture CIDR address translation
  • Loading branch information
phekmat authored and tombuildsstuff committed Jul 15, 2018
1 parent 43c1767 commit d3f9cdd
Show file tree
Hide file tree
Showing 30 changed files with 2,766 additions and 3,199 deletions.
2 changes: 1 addition & 1 deletion azurerm/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import (
"github.com/Azure/azure-sdk-for-go/services/servicebus/mgmt/2017-04-01/servicebus"
"github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2017-10-01/storage"
"github.com/Azure/azure-sdk-for-go/services/trafficmanager/mgmt/2017-05-01/trafficmanager"
"github.com/Azure/azure-sdk-for-go/services/web/mgmt/2016-09-01/web"
"github.com/Azure/azure-sdk-for-go/services/web/mgmt/2018-02-01/web"

This comment has been minimized.

Copy link
@mirandazh

mirandazh Aug 7, 2018

Hey @phekmat @tombuildsstuff do you happen to know if there is any time plan for Azure Government supports api version - 2018-02-01?
We have a deploy job failed due to this change, because No registered resource provider found for location 'usgovvirginia' and API version '2018-02-01' for type 'serverFarms'. The supported api-versions are '2016-09-01, 2016-03-01, 2015-08-01, 2015-07-01, 2015-06-01, 2015-05-01, 2015-04-01, 2015-02-01, 2014-11-01, 2014-06-01, 2014-04-01, 2014-04-01-preview'.

This comment has been minimized.

Copy link
@tombuildsstuff

tombuildsstuff Aug 7, 2018

Contributor

@mirandazh thanks for raising this - so that we can keep track of this in a single place I'm going to reply in #1732

mainStorage "github.com/Azure/azure-sdk-for-go/storage"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/adal"
Expand Down
29 changes: 24 additions & 5 deletions azurerm/helpers/schema/app_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package schema

import (
"log"
"net"
"strings"

"github.com/Azure/azure-sdk-for-go/services/web/mgmt/2016-09-01/web"
"github.com/Azure/azure-sdk-for-go/services/web/mgmt/2018-02-01/web"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
Expand Down Expand Up @@ -219,16 +221,25 @@ func ExpandAppServiceSiteConfig(input interface{}) web.SiteConfig {
if v, ok := config["ip_restriction"]; ok {
ipSecurityRestrictions := v.([]interface{})
restrictions := make([]web.IPSecurityRestriction, 0)

for _, ipSecurityRestriction := range ipSecurityRestrictions {
restriction := ipSecurityRestriction.(map[string]interface{})

ipAddress := restriction["ip_address"].(string)
mask := restriction["subnet_mask"].(string)
// the 2018-02-01 API expects a blank subnet mask and an IP address in CIDR format: a.b.c.d/x
// so translate the IP and mask if necessary
restrictionMask := ""
cidrAddress := ipAddress
if mask != "" {
ipNet := net.IPNet{IP: net.ParseIP(ipAddress), Mask: net.IPMask(net.ParseIP(mask))}
cidrAddress = ipNet.String()
} else if !strings.Contains(ipAddress, "/") {
cidrAddress += "/32"
}

restrictions = append(restrictions, web.IPSecurityRestriction{
IPAddress: &ipAddress,
SubnetMask: &mask,
IPAddress: &cidrAddress,
SubnetMask: &restrictionMask,
})
}
siteConfig.IPSecurityRestrictions = &restrictions
Expand Down Expand Up @@ -324,7 +335,15 @@ func FlattenAppServiceSiteConfig(input *web.SiteConfig) []interface{} {
for _, v := range *vs {
result := make(map[string]interface{}, 0)
if ip := v.IPAddress; ip != nil {
result["ip_address"] = *ip
// the 2018-02-01 API uses CIDR format (a.b.c.d/x), so translate that back to IP and mask
if strings.Contains(*ip, "/") {
ipAddr, ipNet, _ := net.ParseCIDR(*ip)
result["ip_address"] = ipAddr.String()
mask := net.IP(ipNet.Mask)
result["subnet_mask"] = mask.String()
} else {
result["ip_address"] = *ip
}
}
if subnet := v.SubnetMask; subnet != nil {
result["subnet_mask"] = *subnet
Expand Down
2 changes: 1 addition & 1 deletion azurerm/resource_arm_app_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"log"
"regexp"

"github.com/Azure/azure-sdk-for-go/services/web/mgmt/2016-09-01/web"
"github.com/Azure/azure-sdk-for-go/services/web/mgmt/2018-02-01/web"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
azSchema "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/schema"
Expand Down
2 changes: 1 addition & 1 deletion azurerm/resource_arm_app_service_active_slot.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"fmt"
"log"

"github.com/Azure/azure-sdk-for-go/services/web/mgmt/2016-09-01/web"
"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/utils"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"fmt"
"log"

"github.com/Azure/azure-sdk-for-go/services/web/mgmt/2016-09-01/web"
"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/utils"
)
Expand Down
6 changes: 2 additions & 4 deletions azurerm/resource_arm_app_service_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"log"
"regexp"

"github.com/Azure/azure-sdk-for-go/services/web/mgmt/2016-09-01/web"
"github.com/Azure/azure-sdk-for-go/services/web/mgmt/2018-02-01/web"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
Expand Down Expand Up @@ -267,9 +267,7 @@ func flattenAppServicePlanSku(profile *web.SkuDescription) []interface{} {

func expandAppServicePlanProperties(d *schema.ResourceData, name string) *web.AppServicePlanProperties {
configs := d.Get("properties").([]interface{})
properties := web.AppServicePlanProperties{
Name: &name,
}
properties := web.AppServicePlanProperties{}
if len(configs) == 0 {
return &properties
}
Expand Down
69 changes: 46 additions & 23 deletions azurerm/resource_arm_app_service_slot.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"fmt"
"log"

"github.com/Azure/azure-sdk-for-go/services/web/mgmt/2016-09-01/web"
"github.com/Azure/azure-sdk-for-go/services/web/mgmt/2018-02-01/web"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
azSchema "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/schema"
Expand Down Expand Up @@ -51,30 +51,18 @@ func resourceArmAppServiceSlot() *schema.Resource {
Type: schema.TypeBool,
Optional: true,
Computed: true,

// TODO: (tombuildsstuff) support Update once the API is fixed:
// https://github.com/Azure/azure-rest-api-specs/issues/1697
ForceNew: true,
},

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

// TODO: (tombuildsstuff) support Update once the API is fixed:
// https://github.com/Azure/azure-rest-api-specs/issues/1697
ForceNew: true,
},

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

// TODO: (tombuildsstuff) support Update once the API is fixed:
// https://github.com/Azure/azure-rest-api-specs/issues/1697
ForceNew: true,
},

"app_settings": {
Expand Down Expand Up @@ -120,9 +108,7 @@ func resourceArmAppServiceSlot() *schema.Resource {
},
},

// TODO: (tombuildsstuff) support Update once the API is fixed:
// https://github.com/Azure/azure-rest-api-specs/issues/1697
"tags": tagsForceNewSchema(),
"tags": tagsSchema(),

"default_site_hostname": {
Type: schema.TypeString,
Expand Down Expand Up @@ -164,10 +150,6 @@ func resourceArmAppServiceSlotCreate(d *schema.ResourceData, meta interface{}) e
}

// NOTE: these seem like sensible defaults, in lieu of any better documentation.
skipDNSRegistration := false
forceDNSRegistration := false
skipCustomDomainVerification := true
ttlInSeconds := "60"
ctx := meta.(*ArmClient).StopContext

resp, err := client.Get(ctx, resGroup, appServiceName)
Expand All @@ -178,7 +160,7 @@ func resourceArmAppServiceSlotCreate(d *schema.ResourceData, meta interface{}) e
return fmt.Errorf("Error making Read request on AzureRM App Service %q: %+v", appServiceName, err)
}

createFuture, err := client.CreateOrUpdateSlot(ctx, resGroup, appServiceName, siteEnvelope, slot, &skipDNSRegistration, &skipCustomDomainVerification, &forceDNSRegistration, ttlInSeconds)
createFuture, err := client.CreateOrUpdateSlot(ctx, resGroup, appServiceName, siteEnvelope, slot)
if err != nil {
return err
}
Expand Down Expand Up @@ -213,8 +195,36 @@ func resourceArmAppServiceSlotUpdate(d *schema.ResourceData, meta interface{}) e

resGroup := id.ResourceGroup
appServiceName := id.Path["sites"]
location := azureRMNormalizeLocation(d.Get("location").(string))
appServicePlanId := d.Get("app_service_plan_id").(string)
slot := id.Path["slots"]
siteConfig := azSchema.ExpandAppServiceSiteConfig(d.Get("site_config"))
enabled := d.Get("enabled").(bool)
httpsOnly := d.Get("https_only").(bool)
tags := d.Get("tags").(map[string]interface{})
siteEnvelope := web.Site{
Location: &location,
Tags: expandTags(tags),
SiteProperties: &web.SiteProperties{
ServerFarmID: utils.String(appServicePlanId),
Enabled: utils.Bool(enabled),
HTTPSOnly: utils.Bool(httpsOnly),
SiteConfig: &siteConfig,
},
}
if v, ok := d.GetOk("client_affinity_enabled"); ok {
enabled := v.(bool)
siteEnvelope.SiteProperties.ClientAffinityEnabled = utils.Bool(enabled)
}
createFuture, err := client.CreateOrUpdateSlot(ctx, resGroup, appServiceName, siteEnvelope, slot)
if err != nil {
return err
}

err = createFuture.WaitForCompletion(ctx, client.Client)
if err != nil {
return err
}
if d.HasChange("site_config") {
// update the main configuration
siteConfig := azSchema.ExpandAppServiceSiteConfig(d.Get("site_config"))
Expand All @@ -227,6 +237,20 @@ func resourceArmAppServiceSlotUpdate(d *schema.ResourceData, meta interface{}) e
}
}

if d.HasChange("client_affinity_enabled") {
affinity := d.Get("client_affinity_enabled").(bool)
sitePatchResource := web.SitePatchResource{
ID: utils.String(d.Id()),
SitePatchResourceProperties: &web.SitePatchResourceProperties{
ClientAffinityEnabled: &affinity,
},
}
_, err := client.UpdateSlot(ctx, resGroup, appServiceName, sitePatchResource, slot)
if err != nil {
return fmt.Errorf("Error updating App Service ARR Affinity setting %q: %+v", slot, err)
}
}

if d.HasChange("app_settings") {
// update the AppSettings
appSettings := expandAppServiceAppSettings(d)
Expand Down Expand Up @@ -341,9 +365,8 @@ func resourceArmAppServiceSlotDelete(d *schema.ResourceData, meta interface{}) e

deleteMetrics := true
deleteEmptyServerFarm := false
skipDNSRegistration := true
ctx := meta.(*ArmClient).StopContext
resp, err := client.DeleteSlot(ctx, resGroup, appServiceName, slot, &deleteMetrics, &deleteEmptyServerFarm, &skipDNSRegistration)
resp, err := client.DeleteSlot(ctx, resGroup, appServiceName, slot, &deleteMetrics, &deleteEmptyServerFarm)
if err != nil {
if !utils.ResponseWasNotFound(resp) {
return err
Expand Down
Loading

0 comments on commit d3f9cdd

Please sign in to comment.