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

new resource:azurerm_application_load_balancer_subnet_association #23628

Merged
merged 10 commits into from
Nov 22, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,6 @@ resource "azurerm_application_load_balancer" "test" {

func (r ApplicationLoadBalancerResource) requiresImport(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {
}
}

%s

resource "azurerm_application_load_balancer" "import" {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
package servicenetworking

import (
"context"
"fmt"
"time"

"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonids"
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema"
"github.com/hashicorp/go-azure-helpers/resourcemanager/location"
"github.com/hashicorp/go-azure-helpers/resourcemanager/tags"
"github.com/hashicorp/go-azure-sdk/resource-manager/servicenetworking/2023-05-01-preview/associationsinterface"
"github.com/hashicorp/go-azure-sdk/resource-manager/servicenetworking/2023-05-01-preview/trafficcontrollerinterface"
"github.com/hashicorp/terraform-provider-azurerm/internal/sdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/servicenetworking/validate"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
)

type ApplicationLoadBalancerSubnetAssociationResource struct{}

type AssociationResourceModel struct {
Name string `tfschema:"name"`
ApplicationLoadBalancerId string `tfschema:"application_load_balancer_id"`
SubnetId string `tfschema:"subnet_id"`
Tags map[string]interface{} `tfschema:"tags"`
}

var _ sdk.ResourceWithUpdate = ApplicationLoadBalancerSubnetAssociationResource{}

func (t ApplicationLoadBalancerSubnetAssociationResource) Arguments() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
"name": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validate.ApplicationLoadBalancerSubnetAssociationName(),
},

"application_load_balancer_id": commonschema.ResourceIDReferenceRequiredForceNew(associationsinterface.TrafficControllerId{}),

"subnet_id": commonschema.ResourceIDReferenceRequired(commonids.SubnetId{}),

"tags": commonschema.Tags(),
}
}

func (t ApplicationLoadBalancerSubnetAssociationResource) Attributes() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{}
}

func (t ApplicationLoadBalancerSubnetAssociationResource) ModelObject() interface{} {
return &AssociationResourceModel{}
}

func (t ApplicationLoadBalancerSubnetAssociationResource) ResourceType() string {
return "azurerm_application_load_balancer_subnet_association"
}

func (t ApplicationLoadBalancerSubnetAssociationResource) IDValidationFunc() pluginsdk.SchemaValidateFunc {
return associationsinterface.ValidateAssociationID
}

func (t ApplicationLoadBalancerSubnetAssociationResource) Create() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
trafficControllerClient := metadata.Client.ServiceNetworking.TrafficControllerInterface
client := metadata.Client.ServiceNetworking.AssociationsInterface

var config AssociationResourceModel
if err := metadata.Decode(&config); err != nil {
return fmt.Errorf("decoding %v", err)
}

albId, err := trafficcontrollerinterface.ParseTrafficControllerID(config.ApplicationLoadBalancerId)
if err != nil {
return err
}

id := associationsinterface.NewAssociationID(albId.SubscriptionId, albId.ResourceGroupName, albId.TrafficControllerName, config.Name)
existing, err := client.Get(ctx, id)
if err != nil && !response.WasNotFound(existing.HttpResponse) {
return fmt.Errorf("checking for presence of exisiting %s: %+v", id, err)
}

if !response.WasNotFound(existing.HttpResponse) {
return metadata.ResourceRequiresImport(t.ResourceType(), id)
}

controller, err := trafficControllerClient.Get(ctx, *albId)
if err != nil {
return fmt.Errorf("retrieving parent %s: %+v", *albId, err)
}

if controller.Model == nil {
return fmt.Errorf("retrieving parent %s: model was nil", *albId)
}

association := associationsinterface.Association{
Location: location.Normalize(controller.Model.Location),
Properties: &associationsinterface.AssociationProperties{
Subnet: &associationsinterface.AssociationSubnet{
Id: config.SubnetId,
},
AssociationType: associationsinterface.AssociationTypeSubnets,
},
Tags: tags.Expand(config.Tags),
}

if err := client.CreateOrUpdateThenPoll(ctx, id, association); err != nil {
return fmt.Errorf("creating %s: %+v", id, err)
}

metadata.SetID(id)
return nil
},
}
}

func (t ApplicationLoadBalancerSubnetAssociationResource) Read() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 5 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.ServiceNetworking.AssociationsInterface

id, err := associationsinterface.ParseAssociationID(metadata.ResourceData.Id())
if err != nil {
return err
}

resp, err := client.Get(ctx, *id)
if err != nil {
if response.WasNotFound(resp.HttpResponse) {
return metadata.MarkAsGone(id)
}
return fmt.Errorf("retreiving %s: %v", *id, err)
}

trafficControllerId := associationsinterface.NewTrafficControllerID(id.SubscriptionId, id.ResourceGroupName, id.TrafficControllerName)
state := AssociationResourceModel{
Name: id.AssociationName,
ApplicationLoadBalancerId: trafficControllerId.ID(),
}

if model := resp.Model; model != nil {
state.Tags = tags.Flatten(model.Tags)

if prop := model.Properties; prop != nil {
if prop.Subnet != nil {
parsedSubnetId, err := commonids.ParseSubnetIDInsensitively(prop.Subnet.Id)
if err != nil {
return err
}
state.SubnetId = parsedSubnetId.ID()
}
}
}

return metadata.Encode(&state)
},
}
}

func (t ApplicationLoadBalancerSubnetAssociationResource) Update() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.ServiceNetworking.AssociationsInterface

var config AssociationResourceModel
if err := metadata.Decode(&config); err != nil {
return fmt.Errorf("decoding %v", err)
}

id, err := associationsinterface.ParseAssociationID(metadata.ResourceData.Id())
if err != nil {
return err
}

// Although `AssociationSubnetUpdate` defined in the SDK contains the `subnetId`, while per testing it can not be updated
// Tracked on https://github.com/Azure/azure-rest-api-specs/issues/26657
associationUpdate := associationsinterface.AssociationUpdate{}

if metadata.ResourceData.HasChange("tags") {
associationUpdate.Tags = tags.Expand(config.Tags)
}

if _, err = client.Update(ctx, *id, associationUpdate); err != nil {
return fmt.Errorf("updating %s: %v", *id, err)
}

return nil
},
}
}

func (t ApplicationLoadBalancerSubnetAssociationResource) Delete() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.ServiceNetworking.AssociationsInterface

id, err := associationsinterface.ParseAssociationID(metadata.ResourceData.Id())
if err != nil {
return err
}

if err = client.DeleteThenPoll(ctx, *id); err != nil {
return fmt.Errorf("deleting %s: %v", *id, err)
}

return nil
},
}
}
Loading
Loading