From 31e8553077d37e1b677e3e8467b98ac7e6298f25 Mon Sep 17 00:00:00 2001 From: ziyeqf <51212351+ziyeqf@users.noreply.github.com> Date: Fri, 20 Oct 2023 13:42:41 +0800 Subject: [PATCH 01/10] new resource:`azurerm_application_load_balancer_association` Signed-off-by: ziyeqf <51212351+ziyeqf@users.noreply.github.com> --- ...tion_load_balancer_association_resource.go | 230 ++++++++++++++++++ ...load_balancer_association_resource_test.go | 188 ++++++++++++++ .../servicenetworking/registration.go | 1 + ...on_load_balancer_association.html.markdown | 94 +++++++ 4 files changed, 513 insertions(+) create mode 100644 internal/services/servicenetworking/application_load_balancer_association_resource.go create mode 100644 internal/services/servicenetworking/application_load_balancer_association_resource_test.go create mode 100644 website/docs/r/application_load_balancer_association.html.markdown diff --git a/internal/services/servicenetworking/application_load_balancer_association_resource.go b/internal/services/servicenetworking/application_load_balancer_association_resource.go new file mode 100644 index 000000000000..3d7f9db0221b --- /dev/null +++ b/internal/services/servicenetworking/application_load_balancer_association_resource.go @@ -0,0 +1,230 @@ +package servicenetworking + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "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-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-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" +) + +type AssociationResource struct{} + +type AssociationModel struct { + Name string `tfschema:"name"` + ApplicationLoadBalancerId string `tfschema:"application_load_balancer_id"` + SubnetId string `tfschema:"subnet_id"` + Tags map[string]string `tfschema:"tags"` +} + +var _ sdk.ResourceWithUpdate = AssociationResource{} + +func (t AssociationResource) Arguments() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "application_load_balancer_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: associationsinterface.ValidateTrafficControllerID, + }, + + "subnet_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: commonids.ValidateSubnetID, + }, + + "tags": commonschema.Tags(), + } +} + +func (t AssociationResource) Attributes() map[string]*schema.Schema { + return map[string]*schema.Schema{} +} + +func (t AssociationResource) ModelObject() interface{} { + return &AssociationModel{} +} + +func (t AssociationResource) ResourceType() string { + return "azurerm_application_load_balancer_association" +} + +func (t AssociationResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return associationsinterface.ValidateAssociationID +} +func (t AssociationResource) 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 AssociationModel + if err := metadata.Decode(&config); err != nil { + return fmt.Errorf("decoding %v", err) + } + + parsedTrafficControllerId, err := associationsinterface.ParseTrafficControllerID(config.ApplicationLoadBalancerId) + if err != nil { + return err + } + + controllerId := trafficcontrollerinterface.NewTrafficControllerID(parsedTrafficControllerId.SubscriptionId, parsedTrafficControllerId.ResourceGroupName, parsedTrafficControllerId.TrafficControllerName) + controller, err := trafficControllerClient.Get(ctx, controllerId) + if err != nil { + return fmt.Errorf("retrieving %s: %+v", controllerId, err) + } + + if controller.Model == nil { + return fmt.Errorf("retrieving %s: Model was nil", controllerId) + } + + loc := controller.Model.Location + + id := associationsinterface.NewAssociationID(parsedTrafficControllerId.SubscriptionId, parsedTrafficControllerId.ResourceGroupName, parsedTrafficControllerId.TrafficControllerName, config.Name) + existing, err := client.Get(ctx, id) + if err != nil { + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for presence of exisiting %s: %+v", id, err) + } + } + + if !response.WasNotFound(existing.HttpResponse) { + return tf.ImportAsExistsError(t.ResourceType(), id.ID()) + } + + association := associationsinterface.Association{ + Location: location.Normalize(loc), + Properties: &associationsinterface.AssociationProperties{ + Subnet: &associationsinterface.AssociationSubnet{ + Id: config.SubnetId, + }, + AssociationType: associationsinterface.AssociationTypeSubnets, + }, + } + + if len(config.Tags) > 0 { + association.Tags = &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 AssociationResource) 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.ID(), err) + } + + trafficControllerId := associationsinterface.NewTrafficControllerID(id.SubscriptionId, id.ResourceGroupName, id.TrafficControllerName) + state := AssociationModel{ + Name: id.AssociationName, + ApplicationLoadBalancerId: trafficControllerId.ID(), + } + + if model := resp.Model; model != nil { + state.Tags = pointer.From(model.Tags) + + if prop := model.Properties; prop != nil { + if prop.Subnet != nil { + state.SubnetId = prop.Subnet.Id + } + } + } + + return metadata.Encode(&state) + }, + } +} + +func (t AssociationResource) 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 plan AssociationModel + if err := metadata.Decode(&plan); err != nil { + return fmt.Errorf("decoding %v", err) + } + + id, err := associationsinterface.ParseAssociationID(metadata.ResourceData.Id()) + if err != nil { + return fmt.Errorf("parsing id %v", err) + } + + // thought `AssociationSubnetUpdate` is defined in the SDK, per testing the subnet id can not be updated. + associationUpdate := associationsinterface.AssociationUpdate{} + + if metadata.ResourceData.HasChange("tags") { + associationUpdate.Tags = &plan.Tags + } + + if _, err = client.Update(ctx, *id, associationUpdate); err != nil { + return fmt.Errorf("updating `azurerm_application_load_balancer_association` %s: %v", id.ID(), err) + } + + return nil + }, + } +} + +func (t AssociationResource) 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.ID(), err) + } + + return nil + }, + } +} diff --git a/internal/services/servicenetworking/application_load_balancer_association_resource_test.go b/internal/services/servicenetworking/application_load_balancer_association_resource_test.go new file mode 100644 index 000000000000..f701d9394588 --- /dev/null +++ b/internal/services/servicenetworking/application_load_balancer_association_resource_test.go @@ -0,0 +1,188 @@ +package servicenetworking_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/servicenetworking/2023-05-01-preview/associationsinterface" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" +) + +type AssociationResource struct{} + +func (r AssociationResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := associationsinterface.ParseAssociationID(state.ID) + if err != nil { + return nil, fmt.Errorf("while parsing resource ID: %+v", err) + } + + resp, err := clients.ServiceNetworking.AssociationsInterface.Get(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return pointer.To(false), nil + } + return nil, fmt.Errorf("while checking existence for %q: %+v", id.String(), err) + } + return pointer.To(resp.Model != nil), nil +} + +func TestAccApplicationLoadBalancerAssociation_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_application_load_balancer_association", "test") + + r := AssociationResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccApplicationLoadBalancerAssociation_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_application_load_balancer_association", "test") + + r := AssociationResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccApplicationLoadBalancerAssociation_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_application_load_balancer_association", "test") + + r := AssociationResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccApplicationLoadBalancerAssociation_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_application_load_balancer_association", "test") + + r := AssociationResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func (r AssociationResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestrg-alb-%[1]d" + location = "%[2]s" +} + +resource "azurerm_application_load_balancer" "test" { + name = "acctestalb-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_virtual_network" "test" { + name = "acctestvnet%[1]d" + address_space = ["10.0.0.0/16"] + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_subnet" "test" { + name = "acctestsubnet%[1]d" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.0.1.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.ServiceNetworking/trafficControllers" + actions = ["Microsoft.Network/virtualNetworks/subnets/join/action"] + } + } +} + +`, data.RandomInteger, data.Locations.Primary) +} + +func (r AssociationResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features { + } +} + +%s + +resource "azurerm_application_load_balancer_association" "test" { + name = "acct-%d" + application_load_balancer_id = azurerm_application_load_balancer.test.id + subnet_id = azurerm_subnet.test.id +} +`, r.template(data), data.RandomInteger) +} + +func (r AssociationResource) complete(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features { + } +} + +%s + +resource "azurerm_application_load_balancer_association" "test" { + name = "acct-%d" + application_load_balancer_id = azurerm_application_load_balancer.test.id + subnet_id = azurerm_subnet.test.id + tags = { + key = "value" + } +} +`, r.template(data), data.RandomInteger) +} + +func (r AssociationResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` + %s + +resource "azurerm_application_load_balancer_association" "import" { + name = azurerm_application_load_balancer_association.test.name + application_load_balancer_id = azurerm_application_load_balancer_association.test.application_load_balancer_id + subnet_id = azurerm_application_load_balancer_association.test.subnet_id +} +`, r.basic(data)) +} diff --git a/internal/services/servicenetworking/registration.go b/internal/services/servicenetworking/registration.go index 52f9f267fee9..ef4e5039db61 100644 --- a/internal/services/servicenetworking/registration.go +++ b/internal/services/servicenetworking/registration.go @@ -20,6 +20,7 @@ func (r Registration) Resources() []sdk.Resource { return []sdk.Resource{ ApplicationLoadBalancerResource{}, FrontendsResource{}, + AssociationResource{}, } } diff --git a/website/docs/r/application_load_balancer_association.html.markdown b/website/docs/r/application_load_balancer_association.html.markdown new file mode 100644 index 000000000000..44f20115df58 --- /dev/null +++ b/website/docs/r/application_load_balancer_association.html.markdown @@ -0,0 +1,94 @@ +--- +subcategory: "Service Networking" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_application_load_balancer_association" +description: |- + Manages an Application Gateway for Containers Association. +--- + +# azurerm_application_load_balancer_association + +Manages an Application Gateway for Containers Association. + +## Example Usage + +```hcl +resource "azurerm_resource_group" "example" { + name = "example-rg" + location = "northeurope" +} + +resource "azurerm_application_load_balancer" "example" { + name = "example-alb" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name +} + +resource "azurerm_virtual_network" "example" { + name = "example-vnet" + address_space = ["10.0.0.0/16"] + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name +} + +resource "azurerm_subnet" "example" { + name = "example-subnet" + resource_group_name = azurerm_resource_group.example.name + virtual_network_name = azurerm_virtual_network.example.name + address_prefixes = ["10.0.1.0/24"] + + delegation { + name = "delegation" + + service_delegation { + name = "Microsoft.ServiceNetworking/trafficControllers" + actions = ["Microsoft.Network/virtualNetworks/subnets/join/action"] + } + } +} + +resource "azurerm_application_load_balancer_association" "example" { + name = "example" + application_load_balancer_id = azurerm_application_load_balancer.example.id + subnet_id = azurerm_subnet_example.id +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `name` - (Required) The name which should be used for this Application Gateway for Containers Association. Changing this forces a new resource to be created. + +* `application_load_balancer_id` - (Required) The ID of the Application Gateway for Containers. Changing this forces a new resource to be created. + +* `subnet_id` - (Required) The ID of the subnet which the Application Gateway for Containers associated to. Changing this forces a new resource to be created. + +**Note:** The subnet should be delegated by `Microsoft.ServiceNetworking/trafficControllers` as the example. + +--- + +* `tags` - (Optional) A mapping of tags which should be assigned to the Application Gateway for Containers Association. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The ID of the Application Gateway for Containers Association. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/language/resources/syntax#operation-timeouts) for certain actions: + +* `create` - (Defaults to 30 minutes) Used when creating the Application Gateway for Containers Association. +* `read` - (Defaults to 5 minutes) Used when retrieving the Application Gateway for Containers Association. +* `update` - (Defaults to 30 minutes) Used when updating the Application Gateway for Containers Association. +* `delete` - (Defaults to 30 minutes) Used when deleting the Application Gateway for Containers Association. + +## Import + +Application Gateway for Containers Associations can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_application_load_balancer_association.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.ServiceNetworking/trafficControllers/alb1/associations/association1 +``` From 1a1cdfdba4b906e5a34e0cdff279c72c1e791a30 Mon Sep 17 00:00:00 2001 From: ziyeqf <51212351+ziyeqf@users.noreply.github.com> Date: Fri, 20 Oct 2023 17:28:42 +0800 Subject: [PATCH 02/10] update per comments --- ...tion_load_balancer_association_resource.go | 56 +++++++++---------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/internal/services/servicenetworking/application_load_balancer_association_resource.go b/internal/services/servicenetworking/application_load_balancer_association_resource.go index 3d7f9db0221b..ff43d2c82ba7 100644 --- a/internal/services/servicenetworking/application_load_balancer_association_resource.go +++ b/internal/services/servicenetworking/application_load_balancer_association_resource.go @@ -3,6 +3,7 @@ package servicenetworking import ( "context" "fmt" + "regexp" "time" "github.com/hashicorp/go-azure-helpers/lang/pointer" @@ -12,8 +13,6 @@ import ( "github.com/hashicorp/go-azure-helpers/resourcemanager/location" "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-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" @@ -30,13 +29,13 @@ type AssociationModel struct { var _ sdk.ResourceWithUpdate = AssociationResource{} -func (t AssociationResource) Arguments() map[string]*schema.Schema { - return map[string]*schema.Schema{ +func (t AssociationResource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ "name": { Type: pluginsdk.TypeString, Required: true, ForceNew: true, - ValidateFunc: validation.StringIsNotEmpty, + ValidateFunc: validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,62}[a-zA-Z0-9]$`), "the name must begin with a letter or number, end with a letter, number or underscore, and may contain only letters, numbers, underscores, periods, or hyphens. The value must be 1-64 characters long."), }, "application_load_balancer_id": { @@ -57,8 +56,8 @@ func (t AssociationResource) Arguments() map[string]*schema.Schema { } } -func (t AssociationResource) Attributes() map[string]*schema.Schema { - return map[string]*schema.Schema{} +func (t AssociationResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{} } func (t AssociationResource) ModelObject() interface{} { @@ -84,37 +83,28 @@ func (t AssociationResource) Create() sdk.ResourceFunc { return fmt.Errorf("decoding %v", err) } - parsedTrafficControllerId, err := associationsinterface.ParseTrafficControllerID(config.ApplicationLoadBalancerId) + albId, err := associationsinterface.ParseTrafficControllerID(config.ApplicationLoadBalancerId) if err != nil { return err } - controllerId := trafficcontrollerinterface.NewTrafficControllerID(parsedTrafficControllerId.SubscriptionId, parsedTrafficControllerId.ResourceGroupName, parsedTrafficControllerId.TrafficControllerName) - controller, err := trafficControllerClient.Get(ctx, controllerId) - if err != nil { - return fmt.Errorf("retrieving %s: %+v", controllerId, 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 controller.Model == nil { - return fmt.Errorf("retrieving %s: Model was nil", controllerId) + if !response.WasNotFound(existing.HttpResponse) { + return metadata.ResourceRequiresImport(t.ResourceType(), id) } - loc := controller.Model.Location - - id := associationsinterface.NewAssociationID(parsedTrafficControllerId.SubscriptionId, parsedTrafficControllerId.ResourceGroupName, parsedTrafficControllerId.TrafficControllerName, config.Name) - existing, err := client.Get(ctx, id) + controllerId := trafficcontrollerinterface.NewTrafficControllerID(albId.SubscriptionId, albId.ResourceGroupName, albId.TrafficControllerName) + controller, err := trafficControllerClient.Get(ctx, controllerId) if err != nil { - if !response.WasNotFound(existing.HttpResponse) { - return fmt.Errorf("checking for presence of exisiting %s: %+v", id, err) - } - } - - if !response.WasNotFound(existing.HttpResponse) { - return tf.ImportAsExistsError(t.ResourceType(), id.ID()) + return fmt.Errorf("retrieving %s: %+v", controllerId, err) } association := associationsinterface.Association{ - Location: location.Normalize(loc), Properties: &associationsinterface.AssociationProperties{ Subnet: &associationsinterface.AssociationSubnet{ Id: config.SubnetId, @@ -123,6 +113,10 @@ func (t AssociationResource) Create() sdk.ResourceFunc { }, } + if controller.Model != nil { + association.Location = location.Normalize(controller.Model.Location) + } + if len(config.Tags) > 0 { association.Tags = &config.Tags } @@ -167,7 +161,11 @@ func (t AssociationResource) Read() sdk.ResourceFunc { if prop := model.Properties; prop != nil { if prop.Subnet != nil { - state.SubnetId = prop.Subnet.Id + parsedSubnedId, err := commonids.ParseSubnetID(prop.Subnet.Id) + if err != nil { + return err + } + state.SubnetId = parsedSubnedId.ID() } } } @@ -193,7 +191,7 @@ func (t AssociationResource) Update() sdk.ResourceFunc { return fmt.Errorf("parsing id %v", err) } - // thought `AssociationSubnetUpdate` is defined in the SDK, per testing the subnet id can not be updated. + // Thought `AssociationSubnetUpdate` defined in the SDK contains the `subnetId`, while per testing the it can not be updated associationUpdate := associationsinterface.AssociationUpdate{} if metadata.ResourceData.HasChange("tags") { @@ -201,7 +199,7 @@ func (t AssociationResource) Update() sdk.ResourceFunc { } if _, err = client.Update(ctx, *id, associationUpdate); err != nil { - return fmt.Errorf("updating `azurerm_application_load_balancer_association` %s: %v", id.ID(), err) + return fmt.Errorf("updating %s: %v", id.ID(), err) } return nil From 5674fea10b0cc7ff88c6119c411676e6e7974175 Mon Sep 17 00:00:00 2001 From: ziyeqf <51212351+ziyeqf@users.noreply.github.com> Date: Fri, 20 Oct 2023 17:30:29 +0800 Subject: [PATCH 03/10] typo Signed-off-by: ziyeqf <51212351+ziyeqf@users.noreply.github.com> --- .../application_load_balancer_association_resource.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/services/servicenetworking/application_load_balancer_association_resource.go b/internal/services/servicenetworking/application_load_balancer_association_resource.go index ff43d2c82ba7..ca2010842605 100644 --- a/internal/services/servicenetworking/application_load_balancer_association_resource.go +++ b/internal/services/servicenetworking/application_load_balancer_association_resource.go @@ -161,11 +161,11 @@ func (t AssociationResource) Read() sdk.ResourceFunc { if prop := model.Properties; prop != nil { if prop.Subnet != nil { - parsedSubnedId, err := commonids.ParseSubnetID(prop.Subnet.Id) + parsedSubnetId, err := commonids.ParseSubnetID(prop.Subnet.Id) if err != nil { return err } - state.SubnetId = parsedSubnedId.ID() + state.SubnetId = parsedSubnetId.ID() } } } @@ -191,7 +191,7 @@ func (t AssociationResource) Update() sdk.ResourceFunc { return fmt.Errorf("parsing id %v", err) } - // Thought `AssociationSubnetUpdate` defined in the SDK contains the `subnetId`, while per testing the it can not be updated + // Thought `AssociationSubnetUpdate` defined in the SDK contains the `subnetId`, while per testing it can not be updated associationUpdate := associationsinterface.AssociationUpdate{} if metadata.ResourceData.HasChange("tags") { From 3407b4e4be740114ca7c587e562d9ebf9da2b1e0 Mon Sep 17 00:00:00 2001 From: ziyeqf <51212351+ziyeqf@users.noreply.github.com> Date: Mon, 23 Oct 2023 09:48:54 +0800 Subject: [PATCH 04/10] update per comment --- .../application_load_balancer_association_resource.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/internal/services/servicenetworking/application_load_balancer_association_resource.go b/internal/services/servicenetworking/application_load_balancer_association_resource.go index ca2010842605..97130ce5bb18 100644 --- a/internal/services/servicenetworking/application_load_balancer_association_resource.go +++ b/internal/services/servicenetworking/application_load_balancer_association_resource.go @@ -83,7 +83,7 @@ func (t AssociationResource) Create() sdk.ResourceFunc { return fmt.Errorf("decoding %v", err) } - albId, err := associationsinterface.ParseTrafficControllerID(config.ApplicationLoadBalancerId) + albId, err := trafficcontrollerinterface.ParseTrafficControllerID(config.ApplicationLoadBalancerId) if err != nil { return err } @@ -98,10 +98,9 @@ func (t AssociationResource) Create() sdk.ResourceFunc { return metadata.ResourceRequiresImport(t.ResourceType(), id) } - controllerId := trafficcontrollerinterface.NewTrafficControllerID(albId.SubscriptionId, albId.ResourceGroupName, albId.TrafficControllerName) - controller, err := trafficControllerClient.Get(ctx, controllerId) + controller, err := trafficControllerClient.Get(ctx, *albId) if err != nil { - return fmt.Errorf("retrieving %s: %+v", controllerId, err) + return fmt.Errorf("retrieving %s: %+v", *albId, err) } association := associationsinterface.Association{ From 4d8b3c36ef88a919a5657960c0439957f75c0f6f Mon Sep 17 00:00:00 2001 From: ziyeqf <51212351+ziyeqf@users.noreply.github.com> Date: Tue, 24 Oct 2023 17:20:04 +0800 Subject: [PATCH 05/10] update per comments Signed-off-by: ziyeqf <51212351+ziyeqf@users.noreply.github.com> --- ...tion_load_balancer_association_resource.go | 33 ++++++++++--------- ...load_balancer_association_resource_test.go | 7 ++++ .../servicenetworking/registration.go | 2 +- ...lication_load_balancer_association_name.go | 14 ++++++++ ...on_load_balancer_association.html.markdown | 4 +-- 5 files changed, 41 insertions(+), 19 deletions(-) create mode 100644 internal/services/servicenetworking/validate/application_load_balancer_association_name.go diff --git a/internal/services/servicenetworking/application_load_balancer_association_resource.go b/internal/services/servicenetworking/application_load_balancer_association_resource.go index 97130ce5bb18..4aae29949366 100644 --- a/internal/services/servicenetworking/application_load_balancer_association_resource.go +++ b/internal/services/servicenetworking/application_load_balancer_association_resource.go @@ -3,7 +3,6 @@ package servicenetworking import ( "context" "fmt" - "regexp" "time" "github.com/hashicorp/go-azure-helpers/lang/pointer" @@ -14,11 +13,11 @@ import ( "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" - "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" ) -type AssociationResource struct{} +type ApplicationLoadBalancerAssociationResource struct{} type AssociationModel struct { Name string `tfschema:"name"` @@ -27,15 +26,15 @@ type AssociationModel struct { Tags map[string]string `tfschema:"tags"` } -var _ sdk.ResourceWithUpdate = AssociationResource{} +var _ sdk.ResourceWithUpdate = ApplicationLoadBalancerAssociationResource{} -func (t AssociationResource) Arguments() map[string]*pluginsdk.Schema { +func (t ApplicationLoadBalancerAssociationResource) Arguments() map[string]*pluginsdk.Schema { return map[string]*pluginsdk.Schema{ "name": { Type: pluginsdk.TypeString, Required: true, ForceNew: true, - ValidateFunc: validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,62}[a-zA-Z0-9]$`), "the name must begin with a letter or number, end with a letter, number or underscore, and may contain only letters, numbers, underscores, periods, or hyphens. The value must be 1-64 characters long."), + ValidateFunc: validate.ApplicationLoadBalancerAssociationName(), }, "application_load_balancer_id": { @@ -56,22 +55,22 @@ func (t AssociationResource) Arguments() map[string]*pluginsdk.Schema { } } -func (t AssociationResource) Attributes() map[string]*pluginsdk.Schema { +func (t ApplicationLoadBalancerAssociationResource) Attributes() map[string]*pluginsdk.Schema { return map[string]*pluginsdk.Schema{} } -func (t AssociationResource) ModelObject() interface{} { +func (t ApplicationLoadBalancerAssociationResource) ModelObject() interface{} { return &AssociationModel{} } -func (t AssociationResource) ResourceType() string { +func (t ApplicationLoadBalancerAssociationResource) ResourceType() string { return "azurerm_application_load_balancer_association" } -func (t AssociationResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { +func (t ApplicationLoadBalancerAssociationResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { return associationsinterface.ValidateAssociationID } -func (t AssociationResource) Create() sdk.ResourceFunc { +func (t ApplicationLoadBalancerAssociationResource) Create() sdk.ResourceFunc { return sdk.ResourceFunc{ Timeout: 30 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { @@ -112,10 +111,12 @@ func (t AssociationResource) Create() sdk.ResourceFunc { }, } - if controller.Model != nil { - association.Location = location.Normalize(controller.Model.Location) + if controller.Model == nil { + return fmt.Errorf("retrieving %s: model was nil", *albId) } + association.Location = location.Normalize(controller.Model.Location) + if len(config.Tags) > 0 { association.Tags = &config.Tags } @@ -130,7 +131,7 @@ func (t AssociationResource) Create() sdk.ResourceFunc { } } -func (t AssociationResource) Read() sdk.ResourceFunc { +func (t ApplicationLoadBalancerAssociationResource) Read() sdk.ResourceFunc { return sdk.ResourceFunc{ Timeout: 5 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { @@ -174,7 +175,7 @@ func (t AssociationResource) Read() sdk.ResourceFunc { } } -func (t AssociationResource) Update() sdk.ResourceFunc { +func (t ApplicationLoadBalancerAssociationResource) Update() sdk.ResourceFunc { return sdk.ResourceFunc{ Timeout: 30 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { @@ -206,7 +207,7 @@ func (t AssociationResource) Update() sdk.ResourceFunc { } } -func (t AssociationResource) Delete() sdk.ResourceFunc { +func (t ApplicationLoadBalancerAssociationResource) Delete() sdk.ResourceFunc { return sdk.ResourceFunc{ Timeout: 30 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { diff --git a/internal/services/servicenetworking/application_load_balancer_association_resource_test.go b/internal/services/servicenetworking/application_load_balancer_association_resource_test.go index f701d9394588..93f1855f9406 100644 --- a/internal/services/servicenetworking/application_load_balancer_association_resource_test.go +++ b/internal/services/servicenetworking/application_load_balancer_association_resource_test.go @@ -66,6 +66,13 @@ func TestAccApplicationLoadBalancerAssociation_update(t *testing.T) { ), }, data.ImportStep(), + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), }) } diff --git a/internal/services/servicenetworking/registration.go b/internal/services/servicenetworking/registration.go index ef4e5039db61..e36f34571e7e 100644 --- a/internal/services/servicenetworking/registration.go +++ b/internal/services/servicenetworking/registration.go @@ -20,7 +20,7 @@ func (r Registration) Resources() []sdk.Resource { return []sdk.Resource{ ApplicationLoadBalancerResource{}, FrontendsResource{}, - AssociationResource{}, + ApplicationLoadBalancerAssociationResource{}, } } diff --git a/internal/services/servicenetworking/validate/application_load_balancer_association_name.go b/internal/services/servicenetworking/validate/application_load_balancer_association_name.go new file mode 100644 index 000000000000..3a00471379bc --- /dev/null +++ b/internal/services/servicenetworking/validate/application_load_balancer_association_name.go @@ -0,0 +1,14 @@ +package validate + +import ( + "regexp" + + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" +) + +func ApplicationLoadBalancerAssociationName() pluginsdk.SchemaValidateFunc { + return validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,62}[a-zA-Z0-9]$`), + "the name must begin with a letter or number, end with a letter, number or underscore, and may contain only letters, numbers, underscores, periods, or hyphens. The value must be 1-64 characters long.", + ) +} diff --git a/website/docs/r/application_load_balancer_association.html.markdown b/website/docs/r/application_load_balancer_association.html.markdown index 44f20115df58..185afee7582b 100644 --- a/website/docs/r/application_load_balancer_association.html.markdown +++ b/website/docs/r/application_load_balancer_association.html.markdown @@ -15,7 +15,7 @@ Manages an Application Gateway for Containers Association. ```hcl resource "azurerm_resource_group" "example" { name = "example-rg" - location = "northeurope" + location = "westeurope" } resource "azurerm_application_load_balancer" "example" { @@ -64,7 +64,7 @@ The following arguments are supported: * `subnet_id` - (Required) The ID of the subnet which the Application Gateway for Containers associated to. Changing this forces a new resource to be created. -**Note:** The subnet should be delegated by `Microsoft.ServiceNetworking/trafficControllers` as the example. +**Note:** The subnet to be used must have a delegation for `Microsoft.ServiceNetworking/trafficControllers` as shown in the example above. --- From a6ee14362a9dc54a7e83487aa0e20708938e9065 Mon Sep 17 00:00:00 2001 From: ziyeqf <51212351+ziyeqf@users.noreply.github.com> Date: Thu, 26 Oct 2023 10:13:25 +0800 Subject: [PATCH 06/10] update per comments Signed-off-by: ziyeqf <51212351+ziyeqf@users.noreply.github.com> --- ...tion_load_balancer_association_resource.go | 66 ++++++++----------- ...load_balancer_association_resource_test.go | 2 +- 2 files changed, 27 insertions(+), 41 deletions(-) diff --git a/internal/services/servicenetworking/application_load_balancer_association_resource.go b/internal/services/servicenetworking/application_load_balancer_association_resource.go index 4aae29949366..ee124603e718 100644 --- a/internal/services/servicenetworking/application_load_balancer_association_resource.go +++ b/internal/services/servicenetworking/application_load_balancer_association_resource.go @@ -5,11 +5,11 @@ import ( "fmt" "time" - "github.com/hashicorp/go-azure-helpers/lang/pointer" "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" @@ -19,11 +19,11 @@ import ( type ApplicationLoadBalancerAssociationResource struct{} -type AssociationModel struct { - Name string `tfschema:"name"` - ApplicationLoadBalancerId string `tfschema:"application_load_balancer_id"` - SubnetId string `tfschema:"subnet_id"` - Tags map[string]string `tfschema:"tags"` +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 = ApplicationLoadBalancerAssociationResource{} @@ -37,19 +37,9 @@ func (t ApplicationLoadBalancerAssociationResource) Arguments() map[string]*plug ValidateFunc: validate.ApplicationLoadBalancerAssociationName(), }, - "application_load_balancer_id": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: associationsinterface.ValidateTrafficControllerID, - }, + "application_load_balancer_id": commonschema.ResourceIDReferenceRequiredForceNew(associationsinterface.TrafficControllerId{}), - "subnet_id": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: commonids.ValidateSubnetID, - }, + "subnet_id": commonschema.ResourceIDReferenceRequiredForceNew(commonids.SubnetId{}), "tags": commonschema.Tags(), } @@ -60,7 +50,7 @@ func (t ApplicationLoadBalancerAssociationResource) Attributes() map[string]*plu } func (t ApplicationLoadBalancerAssociationResource) ModelObject() interface{} { - return &AssociationModel{} + return &AssociationResourceModel{} } func (t ApplicationLoadBalancerAssociationResource) ResourceType() string { @@ -77,7 +67,7 @@ func (t ApplicationLoadBalancerAssociationResource) Create() sdk.ResourceFunc { trafficControllerClient := metadata.Client.ServiceNetworking.TrafficControllerInterface client := metadata.Client.ServiceNetworking.AssociationsInterface - var config AssociationModel + var config AssociationResourceModel if err := metadata.Decode(&config); err != nil { return fmt.Errorf("decoding %v", err) } @@ -99,26 +89,22 @@ func (t ApplicationLoadBalancerAssociationResource) Create() sdk.ResourceFunc { controller, err := trafficControllerClient.Get(ctx, *albId) if err != nil { - return fmt.Errorf("retrieving %s: %+v", *albId, err) + 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, }, - } - - if controller.Model == nil { - return fmt.Errorf("retrieving %s: model was nil", *albId) - } - - association.Location = location.Normalize(controller.Model.Location) - - if len(config.Tags) > 0 { - association.Tags = &config.Tags + Tags: tags.Expand(config.Tags), } if err := client.CreateOrUpdateThenPoll(ctx, id, association); err != nil { @@ -147,21 +133,21 @@ func (t ApplicationLoadBalancerAssociationResource) Read() sdk.ResourceFunc { if response.WasNotFound(resp.HttpResponse) { return metadata.MarkAsGone(id) } - return fmt.Errorf("retreiving %s: %v", id.ID(), err) + return fmt.Errorf("retreiving %s: %v", *id, err) } trafficControllerId := associationsinterface.NewTrafficControllerID(id.SubscriptionId, id.ResourceGroupName, id.TrafficControllerName) - state := AssociationModel{ + state := AssociationResourceModel{ Name: id.AssociationName, ApplicationLoadBalancerId: trafficControllerId.ID(), } if model := resp.Model; model != nil { - state.Tags = pointer.From(model.Tags) + state.Tags = tags.Flatten(model.Tags) if prop := model.Properties; prop != nil { if prop.Subnet != nil { - parsedSubnetId, err := commonids.ParseSubnetID(prop.Subnet.Id) + parsedSubnetId, err := commonids.ParseSubnetIDInsensitively(prop.Subnet.Id) if err != nil { return err } @@ -181,25 +167,25 @@ func (t ApplicationLoadBalancerAssociationResource) Update() sdk.ResourceFunc { Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { client := metadata.Client.ServiceNetworking.AssociationsInterface - var plan AssociationModel + var plan AssociationResourceModel if err := metadata.Decode(&plan); err != nil { return fmt.Errorf("decoding %v", err) } id, err := associationsinterface.ParseAssociationID(metadata.ResourceData.Id()) if err != nil { - return fmt.Errorf("parsing id %v", err) + return err } // Thought `AssociationSubnetUpdate` defined in the SDK contains the `subnetId`, while per testing it can not be updated associationUpdate := associationsinterface.AssociationUpdate{} if metadata.ResourceData.HasChange("tags") { - associationUpdate.Tags = &plan.Tags + associationUpdate.Tags = tags.Expand(plan.Tags) } if _, err = client.Update(ctx, *id, associationUpdate); err != nil { - return fmt.Errorf("updating %s: %v", id.ID(), err) + return fmt.Errorf("updating %s: %v", *id, err) } return nil @@ -219,7 +205,7 @@ func (t ApplicationLoadBalancerAssociationResource) Delete() sdk.ResourceFunc { } if err = client.DeleteThenPoll(ctx, *id); err != nil { - return fmt.Errorf("deleting %s: %v", id.ID(), err) + return fmt.Errorf("deleting %s: %v", *id, err) } return nil diff --git a/internal/services/servicenetworking/application_load_balancer_association_resource_test.go b/internal/services/servicenetworking/application_load_balancer_association_resource_test.go index 93f1855f9406..fe43e13b799c 100644 --- a/internal/services/servicenetworking/application_load_balancer_association_resource_test.go +++ b/internal/services/servicenetworking/application_load_balancer_association_resource_test.go @@ -27,7 +27,7 @@ func (r AssociationResource) Exists(ctx context.Context, clients *clients.Client if response.WasNotFound(resp.HttpResponse) { return pointer.To(false), nil } - return nil, fmt.Errorf("while checking existence for %q: %+v", id.String(), err) + return nil, fmt.Errorf("while checking existence of %s: %+v", *id, err) } return pointer.To(resp.Model != nil), nil } From ab9fb6381a6bdde64f555e63d9d45f23fd50cda4 Mon Sep 17 00:00:00 2001 From: ziyeqf <51212351+ziyeqf@users.noreply.github.com> Date: Fri, 27 Oct 2023 01:19:20 +0800 Subject: [PATCH 07/10] rename to `azurerm_application_load_balancer_subnet_association` Signed-off-by: ziyeqf <51212351+ziyeqf@users.noreply.github.com> --- ...tion_load_balancer_association_resource.go | 2 +- ...load_balancer_association_resource_test.go | 20 +++++++++---------- ...on_load_balancer_association.html.markdown | 12 +++++------ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/internal/services/servicenetworking/application_load_balancer_association_resource.go b/internal/services/servicenetworking/application_load_balancer_association_resource.go index ee124603e718..cce92b9470a1 100644 --- a/internal/services/servicenetworking/application_load_balancer_association_resource.go +++ b/internal/services/servicenetworking/application_load_balancer_association_resource.go @@ -54,7 +54,7 @@ func (t ApplicationLoadBalancerAssociationResource) ModelObject() interface{} { } func (t ApplicationLoadBalancerAssociationResource) ResourceType() string { - return "azurerm_application_load_balancer_association" + return "azurerm_application_load_balancer_subnet_association" } func (t ApplicationLoadBalancerAssociationResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { diff --git a/internal/services/servicenetworking/application_load_balancer_association_resource_test.go b/internal/services/servicenetworking/application_load_balancer_association_resource_test.go index fe43e13b799c..65750fb65321 100644 --- a/internal/services/servicenetworking/application_load_balancer_association_resource_test.go +++ b/internal/services/servicenetworking/application_load_balancer_association_resource_test.go @@ -33,7 +33,7 @@ func (r AssociationResource) Exists(ctx context.Context, clients *clients.Client } func TestAccApplicationLoadBalancerAssociation_basic(t *testing.T) { - data := acceptance.BuildTestData(t, "azurerm_application_load_balancer_association", "test") + data := acceptance.BuildTestData(t, "azurerm_application_load_balancer_subnet_association", "test") r := AssociationResource{} data.ResourceTest(t, r, []acceptance.TestStep{ @@ -48,7 +48,7 @@ func TestAccApplicationLoadBalancerAssociation_basic(t *testing.T) { } func TestAccApplicationLoadBalancerAssociation_update(t *testing.T) { - data := acceptance.BuildTestData(t, "azurerm_application_load_balancer_association", "test") + data := acceptance.BuildTestData(t, "azurerm_application_load_balancer_subnet_association", "test") r := AssociationResource{} data.ResourceTest(t, r, []acceptance.TestStep{ @@ -77,7 +77,7 @@ func TestAccApplicationLoadBalancerAssociation_update(t *testing.T) { } func TestAccApplicationLoadBalancerAssociation_complete(t *testing.T) { - data := acceptance.BuildTestData(t, "azurerm_application_load_balancer_association", "test") + data := acceptance.BuildTestData(t, "azurerm_application_load_balancer_subnet_association", "test") r := AssociationResource{} data.ResourceTest(t, r, []acceptance.TestStep{ @@ -92,7 +92,7 @@ func TestAccApplicationLoadBalancerAssociation_complete(t *testing.T) { } func TestAccApplicationLoadBalancerAssociation_requiresImport(t *testing.T) { - data := acceptance.BuildTestData(t, "azurerm_application_load_balancer_association", "test") + data := acceptance.BuildTestData(t, "azurerm_application_load_balancer_subnet_association", "test") r := AssociationResource{} data.ResourceTest(t, r, []acceptance.TestStep{ @@ -154,7 +154,7 @@ provider "azurerm" { %s -resource "azurerm_application_load_balancer_association" "test" { +resource "azurerm_application_load_balancer_subnet_association" "test" { name = "acct-%d" application_load_balancer_id = azurerm_application_load_balancer.test.id subnet_id = azurerm_subnet.test.id @@ -171,7 +171,7 @@ provider "azurerm" { %s -resource "azurerm_application_load_balancer_association" "test" { +resource "azurerm_application_load_balancer_subnet_association" "test" { name = "acct-%d" application_load_balancer_id = azurerm_application_load_balancer.test.id subnet_id = azurerm_subnet.test.id @@ -186,10 +186,10 @@ func (r AssociationResource) requiresImport(data acceptance.TestData) string { return fmt.Sprintf(` %s -resource "azurerm_application_load_balancer_association" "import" { - name = azurerm_application_load_balancer_association.test.name - application_load_balancer_id = azurerm_application_load_balancer_association.test.application_load_balancer_id - subnet_id = azurerm_application_load_balancer_association.test.subnet_id +resource "azurerm_application_load_balancer_subnet_association" "import" { + name = azurerm_application_load_balancer_subnet_association.test.name + application_load_balancer_id = azurerm_application_load_balancer_subnet_association.test.application_load_balancer_id + subnet_id = azurerm_application_load_balancer_subnet_association.test.subnet_id } `, r.basic(data)) } diff --git a/website/docs/r/application_load_balancer_association.html.markdown b/website/docs/r/application_load_balancer_association.html.markdown index 185afee7582b..560604534c26 100644 --- a/website/docs/r/application_load_balancer_association.html.markdown +++ b/website/docs/r/application_load_balancer_association.html.markdown @@ -1,14 +1,14 @@ --- subcategory: "Service Networking" layout: "azurerm" -page_title: "Azure Resource Manager: azurerm_application_load_balancer_association" +page_title: "Azure Resource Manager: azurerm_application_load_balancer_subnet_association" description: |- - Manages an Application Gateway for Containers Association. + Manages an association between an Application Gateway for Containers and a Subnet. --- -# azurerm_application_load_balancer_association +# azurerm_application_load_balancer_subnet_association -Manages an Application Gateway for Containers Association. +Manages an association between an Application Gateway for Containers and a Subnet. ## Example Usage @@ -47,7 +47,7 @@ resource "azurerm_subnet" "example" { } } -resource "azurerm_application_load_balancer_association" "example" { +resource "azurerm_application_load_balancer_subnet_association" "example" { name = "example" application_load_balancer_id = azurerm_application_load_balancer.example.id subnet_id = azurerm_subnet_example.id @@ -90,5 +90,5 @@ The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/l Application Gateway for Containers Associations can be imported using the `resource id`, e.g. ```shell -terraform import azurerm_application_load_balancer_association.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.ServiceNetworking/trafficControllers/alb1/associations/association1 +terraform import azurerm_application_load_balancer_subnet_association.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.ServiceNetworking/trafficControllers/alb1/associations/association1 ``` From 41d261eabba2407c43461bcde33a54d43f55b425 Mon Sep 17 00:00:00 2001 From: ziyeqf <51212351+ziyeqf@users.noreply.github.com> Date: Thu, 2 Nov 2023 13:58:35 +0800 Subject: [PATCH 08/10] typo Signed-off-by: ziyeqf <51212351+ziyeqf@users.noreply.github.com> --- .../docs/r/application_load_balancer_association.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/application_load_balancer_association.html.markdown b/website/docs/r/application_load_balancer_association.html.markdown index 560604534c26..d055d8f38945 100644 --- a/website/docs/r/application_load_balancer_association.html.markdown +++ b/website/docs/r/application_load_balancer_association.html.markdown @@ -50,7 +50,7 @@ resource "azurerm_subnet" "example" { resource "azurerm_application_load_balancer_subnet_association" "example" { name = "example" application_load_balancer_id = azurerm_application_load_balancer.example.id - subnet_id = azurerm_subnet_example.id + subnet_id = azurerm_subnet.example.id } ``` From 6db2907211959c23c80b569f3f73200cd768ad3b Mon Sep 17 00:00:00 2001 From: ziyeqf <51212351+ziyeqf@users.noreply.github.com> Date: Fri, 10 Nov 2023 12:31:12 +0800 Subject: [PATCH 09/10] Update per comment --- ...d_balancer_subnet_association_resource.go} | 36 ++++++++++--------- ...ancer_subnet_association_resource_test.go} | 28 +++++++-------- .../servicenetworking/registration.go | 2 +- ..._load_balancer_subnet_association_name.go} | 2 +- ...balancer_subnet_association.html.markdown} | 0 5 files changed, 35 insertions(+), 33 deletions(-) rename internal/services/servicenetworking/{application_load_balancer_association_resource.go => application_load_balancer_subnet_association_resource.go} (80%) rename internal/services/servicenetworking/{application_load_balancer_association_resource_test.go => application_load_balancer_subnet_association_resource_test.go} (84%) rename internal/services/servicenetworking/validate/{application_load_balancer_association_name.go => application_load_balancer_subnet_association_name.go} (85%) rename website/docs/r/{application_load_balancer_association.html.markdown => application_load_balancer_subnet_association.html.markdown} (100%) diff --git a/internal/services/servicenetworking/application_load_balancer_association_resource.go b/internal/services/servicenetworking/application_load_balancer_subnet_association_resource.go similarity index 80% rename from internal/services/servicenetworking/application_load_balancer_association_resource.go rename to internal/services/servicenetworking/application_load_balancer_subnet_association_resource.go index cce92b9470a1..574c39d718eb 100644 --- a/internal/services/servicenetworking/application_load_balancer_association_resource.go +++ b/internal/services/servicenetworking/application_load_balancer_subnet_association_resource.go @@ -17,7 +17,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" ) -type ApplicationLoadBalancerAssociationResource struct{} +type ApplicationLoadBalancerSubnetAssociationResource struct{} type AssociationResourceModel struct { Name string `tfschema:"name"` @@ -26,41 +26,42 @@ type AssociationResourceModel struct { Tags map[string]interface{} `tfschema:"tags"` } -var _ sdk.ResourceWithUpdate = ApplicationLoadBalancerAssociationResource{} +var _ sdk.ResourceWithUpdate = ApplicationLoadBalancerSubnetAssociationResource{} -func (t ApplicationLoadBalancerAssociationResource) Arguments() map[string]*pluginsdk.Schema { +func (t ApplicationLoadBalancerSubnetAssociationResource) Arguments() map[string]*pluginsdk.Schema { return map[string]*pluginsdk.Schema{ "name": { Type: pluginsdk.TypeString, Required: true, ForceNew: true, - ValidateFunc: validate.ApplicationLoadBalancerAssociationName(), + ValidateFunc: validate.ApplicationLoadBalancerSubnetAssociationName(), }, "application_load_balancer_id": commonschema.ResourceIDReferenceRequiredForceNew(associationsinterface.TrafficControllerId{}), - "subnet_id": commonschema.ResourceIDReferenceRequiredForceNew(commonids.SubnetId{}), + "subnet_id": commonschema.ResourceIDReferenceRequired(commonids.SubnetId{}), "tags": commonschema.Tags(), } } -func (t ApplicationLoadBalancerAssociationResource) Attributes() map[string]*pluginsdk.Schema { +func (t ApplicationLoadBalancerSubnetAssociationResource) Attributes() map[string]*pluginsdk.Schema { return map[string]*pluginsdk.Schema{} } -func (t ApplicationLoadBalancerAssociationResource) ModelObject() interface{} { +func (t ApplicationLoadBalancerSubnetAssociationResource) ModelObject() interface{} { return &AssociationResourceModel{} } -func (t ApplicationLoadBalancerAssociationResource) ResourceType() string { +func (t ApplicationLoadBalancerSubnetAssociationResource) ResourceType() string { return "azurerm_application_load_balancer_subnet_association" } -func (t ApplicationLoadBalancerAssociationResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { +func (t ApplicationLoadBalancerSubnetAssociationResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { return associationsinterface.ValidateAssociationID } -func (t ApplicationLoadBalancerAssociationResource) Create() sdk.ResourceFunc { + +func (t ApplicationLoadBalancerSubnetAssociationResource) Create() sdk.ResourceFunc { return sdk.ResourceFunc{ Timeout: 30 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { @@ -117,7 +118,7 @@ func (t ApplicationLoadBalancerAssociationResource) Create() sdk.ResourceFunc { } } -func (t ApplicationLoadBalancerAssociationResource) Read() sdk.ResourceFunc { +func (t ApplicationLoadBalancerSubnetAssociationResource) Read() sdk.ResourceFunc { return sdk.ResourceFunc{ Timeout: 5 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { @@ -161,14 +162,14 @@ func (t ApplicationLoadBalancerAssociationResource) Read() sdk.ResourceFunc { } } -func (t ApplicationLoadBalancerAssociationResource) Update() sdk.ResourceFunc { +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 plan AssociationResourceModel - if err := metadata.Decode(&plan); err != nil { + var config AssociationResourceModel + if err := metadata.Decode(&config); err != nil { return fmt.Errorf("decoding %v", err) } @@ -177,11 +178,12 @@ func (t ApplicationLoadBalancerAssociationResource) Update() sdk.ResourceFunc { return err } - // Thought `AssociationSubnetUpdate` defined in the SDK contains the `subnetId`, while per testing it can not be updated + // 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(plan.Tags) + associationUpdate.Tags = tags.Expand(config.Tags) } if _, err = client.Update(ctx, *id, associationUpdate); err != nil { @@ -193,7 +195,7 @@ func (t ApplicationLoadBalancerAssociationResource) Update() sdk.ResourceFunc { } } -func (t ApplicationLoadBalancerAssociationResource) Delete() sdk.ResourceFunc { +func (t ApplicationLoadBalancerSubnetAssociationResource) Delete() sdk.ResourceFunc { return sdk.ResourceFunc{ Timeout: 30 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { diff --git a/internal/services/servicenetworking/application_load_balancer_association_resource_test.go b/internal/services/servicenetworking/application_load_balancer_subnet_association_resource_test.go similarity index 84% rename from internal/services/servicenetworking/application_load_balancer_association_resource_test.go rename to internal/services/servicenetworking/application_load_balancer_subnet_association_resource_test.go index 65750fb65321..450e00695a95 100644 --- a/internal/services/servicenetworking/application_load_balancer_association_resource_test.go +++ b/internal/services/servicenetworking/application_load_balancer_subnet_association_resource_test.go @@ -14,9 +14,9 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" ) -type AssociationResource struct{} +type SubnetAssociationResource struct{} -func (r AssociationResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { +func (r SubnetAssociationResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := associationsinterface.ParseAssociationID(state.ID) if err != nil { return nil, fmt.Errorf("while parsing resource ID: %+v", err) @@ -32,10 +32,10 @@ func (r AssociationResource) Exists(ctx context.Context, clients *clients.Client return pointer.To(resp.Model != nil), nil } -func TestAccApplicationLoadBalancerAssociation_basic(t *testing.T) { +func TestAccApplicationLoadBalancerSubnetAssociation_basic(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_application_load_balancer_subnet_association", "test") - r := AssociationResource{} + r := SubnetAssociationResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.basic(data), @@ -47,10 +47,10 @@ func TestAccApplicationLoadBalancerAssociation_basic(t *testing.T) { }) } -func TestAccApplicationLoadBalancerAssociation_update(t *testing.T) { +func TestAccApplicationLoadBalancerSubnetAssociation_update(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_application_load_balancer_subnet_association", "test") - r := AssociationResource{} + r := SubnetAssociationResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.basic(data), @@ -76,10 +76,10 @@ func TestAccApplicationLoadBalancerAssociation_update(t *testing.T) { }) } -func TestAccApplicationLoadBalancerAssociation_complete(t *testing.T) { +func TestAccApplicationLoadBalancerSubnetAssociation_complete(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_application_load_balancer_subnet_association", "test") - r := AssociationResource{} + r := SubnetAssociationResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.complete(data), @@ -91,10 +91,10 @@ func TestAccApplicationLoadBalancerAssociation_complete(t *testing.T) { }) } -func TestAccApplicationLoadBalancerAssociation_requiresImport(t *testing.T) { +func TestAccApplicationLoadBalancerSubnetAssociation_requiresImport(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_application_load_balancer_subnet_association", "test") - r := AssociationResource{} + r := SubnetAssociationResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { Config: r.basic(data), @@ -106,7 +106,7 @@ func TestAccApplicationLoadBalancerAssociation_requiresImport(t *testing.T) { }) } -func (r AssociationResource) template(data acceptance.TestData) string { +func (r SubnetAssociationResource) template(data acceptance.TestData) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { name = "acctestrg-alb-%[1]d" @@ -145,7 +145,7 @@ resource "azurerm_subnet" "test" { `, data.RandomInteger, data.Locations.Primary) } -func (r AssociationResource) basic(data acceptance.TestData) string { +func (r SubnetAssociationResource) basic(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { features { @@ -162,7 +162,7 @@ resource "azurerm_application_load_balancer_subnet_association" "test" { `, r.template(data), data.RandomInteger) } -func (r AssociationResource) complete(data acceptance.TestData) string { +func (r SubnetAssociationResource) complete(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { features { @@ -182,7 +182,7 @@ resource "azurerm_application_load_balancer_subnet_association" "test" { `, r.template(data), data.RandomInteger) } -func (r AssociationResource) requiresImport(data acceptance.TestData) string { +func (r SubnetAssociationResource) requiresImport(data acceptance.TestData) string { return fmt.Sprintf(` %s diff --git a/internal/services/servicenetworking/registration.go b/internal/services/servicenetworking/registration.go index e36f34571e7e..422104402b06 100644 --- a/internal/services/servicenetworking/registration.go +++ b/internal/services/servicenetworking/registration.go @@ -20,7 +20,7 @@ func (r Registration) Resources() []sdk.Resource { return []sdk.Resource{ ApplicationLoadBalancerResource{}, FrontendsResource{}, - ApplicationLoadBalancerAssociationResource{}, + ApplicationLoadBalancerSubnetAssociationResource{}, } } diff --git a/internal/services/servicenetworking/validate/application_load_balancer_association_name.go b/internal/services/servicenetworking/validate/application_load_balancer_subnet_association_name.go similarity index 85% rename from internal/services/servicenetworking/validate/application_load_balancer_association_name.go rename to internal/services/servicenetworking/validate/application_load_balancer_subnet_association_name.go index 3a00471379bc..ff89e898f0fb 100644 --- a/internal/services/servicenetworking/validate/application_load_balancer_association_name.go +++ b/internal/services/servicenetworking/validate/application_load_balancer_subnet_association_name.go @@ -7,7 +7,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" ) -func ApplicationLoadBalancerAssociationName() pluginsdk.SchemaValidateFunc { +func ApplicationLoadBalancerSubnetAssociationName() pluginsdk.SchemaValidateFunc { return validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,62}[a-zA-Z0-9]$`), "the name must begin with a letter or number, end with a letter, number or underscore, and may contain only letters, numbers, underscores, periods, or hyphens. The value must be 1-64 characters long.", ) diff --git a/website/docs/r/application_load_balancer_association.html.markdown b/website/docs/r/application_load_balancer_subnet_association.html.markdown similarity index 100% rename from website/docs/r/application_load_balancer_association.html.markdown rename to website/docs/r/application_load_balancer_subnet_association.html.markdown From 24ff0e0767162cd179e11cd88a7efb6ec6ed6045 Mon Sep 17 00:00:00 2001 From: ziyeqf <51212351+ziyeqf@users.noreply.github.com> Date: Mon, 20 Nov 2023 10:05:45 +0800 Subject: [PATCH 10/10] update per comment --- .../application_load_balancer_resource_test.go | 5 ----- .../application_load_balancer_subnet_association_name.go | 7 +++++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/internal/services/servicenetworking/application_load_balancer_resource_test.go b/internal/services/servicenetworking/application_load_balancer_resource_test.go index 683683f2a44a..78ac1e6f8b04 100644 --- a/internal/services/servicenetworking/application_load_balancer_resource_test.go +++ b/internal/services/servicenetworking/application_load_balancer_resource_test.go @@ -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" { diff --git a/internal/services/servicenetworking/validate/application_load_balancer_subnet_association_name.go b/internal/services/servicenetworking/validate/application_load_balancer_subnet_association_name.go index ff89e898f0fb..f7de55c5e23e 100644 --- a/internal/services/servicenetworking/validate/application_load_balancer_subnet_association_name.go +++ b/internal/services/servicenetworking/validate/application_load_balancer_subnet_association_name.go @@ -8,7 +8,10 @@ import ( ) func ApplicationLoadBalancerSubnetAssociationName() pluginsdk.SchemaValidateFunc { - return validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,62}[a-zA-Z0-9]$`), - "the name must begin with a letter or number, end with a letter, number or underscore, and may contain only letters, numbers, underscores, periods, or hyphens. The value must be 1-64 characters long.", + return validation.All( + validation.StringLenBetween(1, 64), + validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9]`), "the name must begin with a letter or number."), + validation.StringMatch(regexp.MustCompile(`[a-zA-Z0-9]$`), "the name must end with a letter or number."), + validation.StringMatch(regexp.MustCompile(`[a-zA-Z0-9_.-]{0,64}`), "the name may contain only letters, numbers, underscores, periods, or hyphens."), ) }