Skip to content

Commit

Permalink
Merge pull request #24483 from hashicorp/f/web-update-to-2023-01-01
Browse files Browse the repository at this point in the history
dependencies: `azurerm_service_plan` - update to use `hashicorp/go-azure-sdk`
  • Loading branch information
jackofallops authored Jan 16, 2024
2 parents f3a692b + 6c7e375 commit 61443ac
Show file tree
Hide file tree
Showing 125 changed files with 19,397 additions and 923 deletions.
4 changes: 3 additions & 1 deletion internal/clients/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,9 @@ func (client *Client) Build(ctx context.Context, o *common.ClientOptions) error
if client.AppPlatform, err = appPlatform.NewClient(o); err != nil {
return fmt.Errorf("building clients for AppPlatform: %+v", err)
}
client.AppService = appService.NewClient(o)
if client.AppService, err = appService.NewClient(o); err != nil {
return fmt.Errorf("building clients for AppService: %+v", err)
}
if client.ArcKubernetes, err = arckubernetes.NewClient(o); err != nil {
return fmt.Errorf("building clients for ArcKubernetes: %+v", err)
}
Expand Down
18 changes: 12 additions & 6 deletions internal/services/appservice/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@
package client

import (
"fmt"

"github.com/hashicorp/go-azure-sdk/resource-manager/web/2023-01-01/appserviceplans"
"github.com/hashicorp/terraform-provider-azurerm/internal/common"
"github.com/tombuildsstuff/kermit/sdk/web/2022-09-01/web"
)

type Client struct {
AppServiceEnvironmentClient *web.AppServiceEnvironmentsClient
BaseClient *web.BaseClient
ServicePlanClient *web.AppServicePlansClient
ServicePlanClient *appserviceplans.AppServicePlansClient
WebAppsClient *web.AppsClient
}

func NewClient(o *common.ClientOptions) *Client {
func NewClient(o *common.ClientOptions) (*Client, error) {
appServiceEnvironmentClient := web.NewAppServiceEnvironmentsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&appServiceEnvironmentClient.Client, o.ResourceManagerAuthorizer)

Expand All @@ -25,13 +28,16 @@ func NewClient(o *common.ClientOptions) *Client {
webAppServiceClient := web.NewAppsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&webAppServiceClient.Client, o.ResourceManagerAuthorizer)

servicePlanClient := web.NewAppServicePlansClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&servicePlanClient.Client, o.ResourceManagerAuthorizer)
servicePlanClient, err := appserviceplans.NewAppServicePlansClientWithBaseURI(o.Environment.ResourceManager)
if err != nil {
return nil, fmt.Errorf("building Api client: %+v", err)
}
o.Configure(servicePlanClient.Client, o.Authorizers.ResourceManager)

return &Client{
AppServiceEnvironmentClient: &appServiceEnvironmentClient,
BaseClient: &baseClient,
ServicePlanClient: &servicePlanClient,
ServicePlanClient: servicePlanClient,
WebAppsClient: &webAppServiceClient,
}
}, nil
}
11 changes: 6 additions & 5 deletions internal/services/appservice/helpers/service_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"strings"

"github.com/hashicorp/go-azure-helpers/resourcemanager/commonids"
"github.com/hashicorp/terraform-provider-azurerm/internal/sdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/parse"
"github.com/hashicorp/terraform-provider-azurerm/utils"
Expand Down Expand Up @@ -169,23 +170,23 @@ func ServicePlanInfoForApp(ctx context.Context, metadata sdk.ResourceMetaData, i
if props.ServerFarmID == nil {
return nil, nil, fmt.Errorf("determining Service Plan ID for %s: %+v", id, err)
}
servicePlanId, err := parse.ServicePlanID(*props.ServerFarmID)
servicePlanId, err := commonids.ParseAppServicePlanIDInsensitively(*props.ServerFarmID)
if err != nil {
return nil, nil, err
}

sp, err := servicePlanClient.Get(ctx, servicePlanId.ResourceGroup, servicePlanId.ServerfarmName)
if err != nil || sp.Kind == nil {
sp, err := servicePlanClient.Get(ctx, *servicePlanId)
if err != nil || sp.Model.Kind == nil {
return nil, nil, fmt.Errorf("reading Service Plan for %s: %+v", id, err)
}

osType = utils.String("windows")
if strings.Contains(strings.ToLower(*sp.Kind), "linux") {
if strings.Contains(strings.ToLower(*sp.Model.Kind), "linux") {
osType = utils.String("linux")
}

planSku = utils.String("")
if sku := sp.Sku; sku != nil {
if sku := sp.Model.Sku; sku != nil {
planSku = sku.Name
}

Expand Down
160 changes: 91 additions & 69 deletions internal/services/appservice/linux_function_app_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/hashicorp/go-azure-helpers/resourcemanager/location"
"github.com/hashicorp/terraform-provider-azurerm/internal/sdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/helpers"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/migration"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/parse"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/appservice/validate"
kvValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/validate"
Expand Down Expand Up @@ -85,6 +86,8 @@ var _ sdk.ResourceWithCustomImporter = LinuxFunctionAppResource{}

var _ sdk.ResourceWithCustomizeDiff = LinuxFunctionAppResource{}

var _ sdk.ResourceWithStateMigration = LinuxFunctionAppResource{}

func (r LinuxFunctionAppResource) ModelObject() interface{} {
return &LinuxFunctionAppModel{}
}
Expand Down Expand Up @@ -114,7 +117,7 @@ func (r LinuxFunctionAppResource) Arguments() map[string]*pluginsdk.Schema {
"service_plan_id": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validate.ServicePlanID,
ValidateFunc: commonids.ValidateAppServicePlanID,
Description: "The ID of the App Service Plan within which to create this Function App",
},

Expand Down Expand Up @@ -375,19 +378,49 @@ func (r LinuxFunctionAppResource) Create() sdk.ResourceFunc {

id := parse.NewFunctionAppID(subscriptionId, functionApp.ResourceGroup, functionApp.Name)

servicePlanId, err := parse.ServicePlanID(functionApp.ServicePlanId)
servicePlanId, err := commonids.ParseAppServicePlanID(functionApp.ServicePlanId)
if err != nil {
return err
}

servicePlan, err := servicePlanClient.Get(ctx, servicePlanId.ResourceGroup, servicePlanId.ServerfarmName)
servicePlan, err := servicePlanClient.Get(ctx, *servicePlanId)
if err != nil {
return fmt.Errorf("reading %s: %+v", servicePlanId, err)
}

var planSKU *string
if sku := servicePlan.Sku; sku != nil && sku.Name != nil {
planSKU = sku.Name
availabilityRequest := web.ResourceNameAvailabilityRequest{
Name: pointer.To(functionApp.Name),
Type: web.CheckNameResourceTypesMicrosoftWebsites,
}
if servicePlanModel := servicePlan.Model; servicePlanModel != nil {
if sku := servicePlanModel.Sku; sku != nil && sku.Name != nil {
planSKU = sku.Name
}

if ase := servicePlanModel.Properties.HostingEnvironmentProfile; ase != nil {
// Attempt to check the ASE for the appropriate suffix for the name availability request.
// This varies between internal and external ASE Types, and potentially has other names in other clouds
// We use the "internal" as the fallback here, if we can read the ASE, we'll get the full one
nameSuffix := "appserviceenvironment.net"
if ase.Id != nil {
aseId, err := parse.AppServiceEnvironmentID(*ase.Id)
nameSuffix = fmt.Sprintf("%s.%s", aseId.HostingEnvironmentName, nameSuffix)
if err != nil {
metadata.Logger.Warnf("could not parse App Service Environment ID determine FQDN for name availability check, defaulting to `%s.%s.appserviceenvironment.net`", functionApp.Name, servicePlanId)
} else {
existingASE, err := aseClient.Get(ctx, aseId.ResourceGroup, aseId.HostingEnvironmentName)
if err != nil {
metadata.Logger.Warnf("could not read App Service Environment to determine FQDN for name availability check, defaulting to `%s.%s.appserviceenvironment.net`", functionApp.Name, servicePlanId)
} else if props := existingASE.AppServiceEnvironment; props != nil && props.DNSSuffix != nil && *props.DNSSuffix != "" {
nameSuffix = *props.DNSSuffix
}
}
}

availabilityRequest.Name = pointer.To(fmt.Sprintf("%s.%s", functionApp.Name, nameSuffix))
availabilityRequest.IsFqdn = pointer.To(true)
}
}
// Only send for ElasticPremium and Consumption plan
elasticOrConsumptionPlan := helpers.PlanIsElastic(planSKU) || helpers.PlanIsConsumption(planSKU)
Expand All @@ -402,35 +435,6 @@ func (r LinuxFunctionAppResource) Create() sdk.ResourceFunc {
return metadata.ResourceRequiresImport(r.ResourceType(), id)
}

availabilityRequest := web.ResourceNameAvailabilityRequest{
Name: utils.String(functionApp.Name),
Type: web.CheckNameResourceTypesMicrosoftWebsites,
}

if ase := servicePlan.HostingEnvironmentProfile; ase != nil {
// Attempt to check the ASE for the appropriate suffix for the name availability request.
// This varies between internal and external ASE Types, and potentially has other names in other clouds
// We use the "internal" as the fallback here, if we can read the ASE, we'll get the full one
nameSuffix := "appserviceenvironment.net"
if ase.ID != nil {
aseId, err := parse.AppServiceEnvironmentID(*ase.ID)
nameSuffix = fmt.Sprintf("%s.%s", aseId.HostingEnvironmentName, nameSuffix)
if err != nil {
metadata.Logger.Warnf("could not parse App Service Environment ID determine FQDN for name availability check, defaulting to `%s.%s.appserviceenvironment.net`", functionApp.Name, servicePlanId)
} else {
existingASE, err := aseClient.Get(ctx, aseId.ResourceGroup, aseId.HostingEnvironmentName)
if err != nil {
metadata.Logger.Warnf("could not read App Service Environment to determine FQDN for name availability check, defaulting to `%s.%s.appserviceenvironment.net`", functionApp.Name, servicePlanId)
} else if props := existingASE.AppServiceEnvironment; props != nil && props.DNSSuffix != nil && *props.DNSSuffix != "" {
nameSuffix = *props.DNSSuffix
}
}
}

availabilityRequest.Name = utils.String(fmt.Sprintf("%s.%s", functionApp.Name, nameSuffix))
availabilityRequest.IsFqdn = utils.Bool(true)
}

checkName, err := client.CheckNameAvailability(ctx, availabilityRequest)
if err != nil {
return fmt.Errorf("checking name availability for Linux %s: %+v", id, err)
Expand Down Expand Up @@ -743,7 +747,6 @@ func (r LinuxFunctionAppResource) Read() sdk.ResourceFunc {
state := LinuxFunctionAppModel{
Name: id.SiteName,
ResourceGroup: id.ResourceGroup,
ServicePlanId: utils.NormalizeNilableString(props.ServerFarmID),
Location: location.NormalizeNilable(functionApp.Location),
Enabled: utils.NormaliseNilableBool(functionApp.Enabled),
ClientCertMode: string(functionApp.ClientCertMode),
Expand All @@ -758,6 +761,12 @@ func (r LinuxFunctionAppResource) Read() sdk.ResourceFunc {
PublicNetworkAccess: !strings.EqualFold(pointer.From(props.PublicNetworkAccess), helpers.PublicNetworkAccessDisabled),
}

servicePlanId, err := commonids.ParseAppServicePlanIDInsensitively(*props.ServerFarmID)
if err != nil {
return err
}
state.ServicePlanId = servicePlanId.ID()

state.PublishingFTPBasicAuthEnabled = basicAuthFTP
state.PublishingDeployBasicAuthEnabled = basicAuthWebDeploy

Expand Down Expand Up @@ -882,13 +891,6 @@ func (r LinuxFunctionAppResource) Update() sdk.ResourceFunc {
return fmt.Errorf("reading Linux %s: %v", id, err)
}

// Some service plan updates are allowed - see customiseDiff for exceptions
var serviceFarmId string
if metadata.ResourceData.HasChange("service_plan_id") {
serviceFarmId = state.ServicePlanId
existing.SiteProperties.ServerFarmID = utils.String(serviceFarmId)
}

_, planSKU, err := helpers.ServicePlanInfoForApp(ctx, metadata, *id)
if err != nil {
return err
Expand All @@ -897,19 +899,21 @@ func (r LinuxFunctionAppResource) Update() sdk.ResourceFunc {
// Some service plan updates are allowed - see customiseDiff for exceptions
if metadata.ResourceData.HasChange("service_plan_id") {
existing.SiteProperties.ServerFarmID = utils.String(state.ServicePlanId)
servicePlanId, err := parse.ServicePlanID(state.ServicePlanId)
servicePlanId, err := commonids.ParseAppServicePlanID(state.ServicePlanId)
if err != nil {
return err
}

servicePlanClient := metadata.Client.AppService.ServicePlanClient
servicePlan, err := servicePlanClient.Get(ctx, servicePlanId.ResourceGroup, servicePlanId.ServerfarmName)
servicePlan, err := servicePlanClient.Get(ctx, *servicePlanId)
if err != nil {
return fmt.Errorf("reading new service plan (%s) for Linux %s: %+v", servicePlanId.ServerfarmName, id, err)
return fmt.Errorf("reading new service plan (%s) for Linux %s: %+v", servicePlanId.ServerFarmName, id, err)
}

if sku := servicePlan.Sku; sku != nil && sku.Name != nil {
planSKU = sku.Name
if model := servicePlan.Model; model != nil {
if sku := servicePlan.Model.Sku; sku != nil && sku.Name != nil {
planSKU = sku.Name
}
}
}

Expand Down Expand Up @@ -1199,16 +1203,16 @@ func (r LinuxFunctionAppResource) CustomImporter() sdk.ResourceRunFunc {
if props.ServerFarmID == nil {
return fmt.Errorf("determining Service Plan ID for Linux %s: %+v", id, err)
}
servicePlanId, err := parse.ServicePlanID(*props.ServerFarmID)
servicePlanId, err := commonids.ParseAppServicePlanIDInsensitively(*props.ServerFarmID)
if err != nil {
return err
}

sp, err := servicePlanClient.Get(ctx, servicePlanId.ResourceGroup, servicePlanId.ServerfarmName)
if err != nil || sp.Kind == nil {
sp, err := servicePlanClient.Get(ctx, *servicePlanId)
if err != nil || sp.Model == nil || sp.Model.Kind == nil {
return fmt.Errorf("reading Service Plan for Linux %s: %+v", id, err)
}
if !strings.Contains(strings.ToLower(*sp.Kind), "linux") && !strings.Contains(strings.ToLower(*sp.Kind), "elastic") && !strings.Contains(strings.ToLower(*sp.Kind), "functionapp") {
if !strings.Contains(strings.ToLower(*sp.Model.Kind), "linux") && !strings.Contains(strings.ToLower(*sp.Model.Kind), "elastic") && !strings.Contains(strings.ToLower(*sp.Model.Kind), "functionapp") {
return fmt.Errorf("specified Service Plan is not a Linux Functionapp plan")
}

Expand All @@ -1222,44 +1226,58 @@ func (r LinuxFunctionAppResource) CustomizeDiff() sdk.ResourceFunc {
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.AppService.ServicePlanClient
rd := metadata.ResourceDiff

if rd.HasChange("service_plan_id") {
currentPlanIdRaw, newPlanIdRaw := rd.GetChange("service_plan_id")
if newPlanIdRaw.(string) == "" {
// Plans creating a new service_plan inline will be empty as `Computed` known after apply
return nil
}
newPlanId, err := parse.ServicePlanID(newPlanIdRaw.(string))
newPlanId, err := commonids.ParseAppServicePlanID(newPlanIdRaw.(string))
if err != nil {
return fmt.Errorf("reading new plan id %+v", err)
}

var currentTierIsDynamic, newTierIsDynamic, newTierIsBasic bool

newPlan, err := client.Get(ctx, newPlanId.ResourceGroup, newPlanId.ServerfarmName)
newPlan, err := client.Get(ctx, *newPlanId)
if err != nil {
return fmt.Errorf("could not read new Service Plan to check tier %s: %+v", newPlanId, err)
return fmt.Errorf("could not read new Service Plan to check tier %s: %+v ", newPlanId, err)
}
if planSku := newPlan.Sku; planSku != nil {
if tier := planSku.Tier; tier != nil {
newTierIsDynamic = strings.EqualFold(*tier, "dynamic")
newTierIsBasic = strings.EqualFold(*tier, "basic")
if newPlan.Model != nil {
if planSku := newPlan.Model.Sku; planSku != nil {
if tier := planSku.Tier; tier != nil {
newTierIsDynamic = strings.EqualFold(*tier, "dynamic")
newTierIsBasic = strings.EqualFold(*tier, "basic")
}
}
}
if _, ok := rd.GetOk("backup"); ok && newTierIsDynamic {
return fmt.Errorf("cannot specify backup configuration for Dynamic tier Service Plans, Standard or higher is required")
}
if _, ok := rd.GetOk("backup"); ok && newTierIsBasic {
return fmt.Errorf("cannot specify backup configuration for Basic tier Service Plans, Standard or higher is required")
}

if strings.EqualFold(currentPlanIdRaw.(string), newPlanIdRaw.(string)) || currentPlanIdRaw.(string) == "" {
// State migration escape for correcting case in serverFarms
// change of case here will not move the app to a new Service Plan
// also if the current Service Plan is empty, this is a new resource, so can skip this
return nil
}

// Service Plans can only be updated in place when both New and Existing are not Dynamic
if currentPlanIdRaw.(string) != "" {
currentPlanId, err := parse.ServicePlanID(currentPlanIdRaw.(string))
currentPlanId, err := commonids.ParseAppServicePlanIDInsensitively(currentPlanIdRaw.(string))
if err != nil {
return fmt.Errorf("reading existing plan id %+v", err)
}

currentPlan, err := client.Get(ctx, currentPlanId.ResourceGroup, currentPlanId.ServerfarmName)
if err != nil {
currentPlan, err := client.Get(ctx, *currentPlanId)
if err != nil || currentPlan.Model == nil {
return fmt.Errorf("could not read old Service Plan to check tier %s: %+v", currentPlanId, err)
}

if planSku := currentPlan.Sku; planSku != nil {
if planSku := currentPlan.Model.Sku; planSku != nil {
if tier := planSku.Tier; tier != nil {
currentTierIsDynamic = strings.EqualFold(*tier, "dynamic")
}
Expand All @@ -1271,12 +1289,7 @@ func (r LinuxFunctionAppResource) CustomizeDiff() sdk.ResourceFunc {
}
}
}
if _, ok := rd.GetOk("backup"); ok && newTierIsDynamic {
return fmt.Errorf("cannot specify backup configuration for Dynamic tier Service Plans, Standard or higher is required")
}
if _, ok := rd.GetOk("backup"); ok && newTierIsBasic {
return fmt.Errorf("cannot specify backup configuration for Basic tier Service Plans, Standard or higher is required")
}

}
return nil
},
Expand Down Expand Up @@ -1378,3 +1391,12 @@ func (m *LinuxFunctionAppModel) unpackLinuxFunctionAppSettings(input web.StringD

m.AppSettings = appSettings
}

func (r LinuxFunctionAppResource) StateUpgraders() sdk.StateUpgradeData {
return sdk.StateUpgradeData{
SchemaVersion: 1,
Upgraders: map[int]pluginsdk.StateUpgrade{
0: migration.LinuxFunctionAppV0toV1{},
},
}
}
Loading

0 comments on commit 61443ac

Please sign in to comment.