Skip to content
Open
Show file tree
Hide file tree
Changes from 12 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
@@ -0,0 +1,231 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package network

import (
"context"
"fmt"
"strings"
"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-sdk/resource-manager/network/2025-01-01/natgateways"
"github.com/hashicorp/terraform-provider-azurerm/internal/locks"
"github.com/hashicorp/terraform-provider-azurerm/internal/sdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
)

type NatGatewayPublicIpV6AssociationResource struct{}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should use IPv6 or ipv6, althought the SDK generated with IPAddressV6, the standard spelling is a lowercased v6 as IPv6. we should rename all other words in this PR too.

Suggested change
type NatGatewayPublicIpV6AssociationResource struct{}
type NatGatewayPublicIPv6AssociationResource struct{}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated


var _ sdk.Resource = NatGatewayPublicIpV6AssociationResource{}

type NatGatewayPublicIpV6AssociationModel struct {
NatGatewayId string `tfschema:"nat_gateway_id"`
PublicIpAddressId string `tfschema:"public_ip_address_id"`
}

func (r NatGatewayPublicIpV6AssociationResource) Arguments() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
"nat_gateway_id": commonschema.ResourceIDReferenceRequiredForceNew(&natgateways.NatGatewayId{}),

"public_ip_address_id": commonschema.ResourceIDReferenceRequiredForceNew(&commonids.PublicIPAddressId{}),
}
}

func (r NatGatewayPublicIpV6AssociationResource) Attributes() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{}
}

func (r NatGatewayPublicIpV6AssociationResource) ModelObject() interface{} {
return &NatGatewayPublicIpV6AssociationModel{}
}

func (r NatGatewayPublicIpV6AssociationResource) ResourceType() string {
return "azurerm_nat_gateway_public_ip_v6_association"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should it be no breaks in ipv6?

Suggested change
return "azurerm_nat_gateway_public_ip_v6_association"
return "azurerm_nat_gateway_public_ipv6_association"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

}

func (r NatGatewayPublicIpV6AssociationResource) Create() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.Network.NatGateways

var state NatGatewayPublicIpV6AssociationModel
if err := metadata.Decode(&state); err != nil {
return err
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return err
return fmt.Errorf("decoding: %+v", err)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

}

publicIpAddressId, err := commonids.ParsePublicIPAddressID(state.PublicIpAddressId)
if err != nil {
return err
}

natGatewayId, err := natgateways.ParseNatGatewayID(state.NatGatewayId)
if err != nil {
return err
}

locks.ByID(natGatewayId.ID())
defer locks.UnlockByID(natGatewayId.ID())

natGateway, err := client.Get(ctx, *natGatewayId, natgateways.DefaultGetOperationOptions())
if err != nil {
if response.WasNotFound(natGateway.HttpResponse) {
return fmt.Errorf("%s was not found", *natGatewayId)
}
return fmt.Errorf("retrieving %s: %+v", *natGatewayId, err)
}

id := commonids.NewCompositeResourceID(natGatewayId, publicIpAddressId)

if model := natGateway.Model; model != nil {
if props := model.Properties; props != nil {
publicIpAddressesV6 := make([]natgateways.SubResource, 0)

if v := props.PublicIPAddressesV6; v != nil {
for _, existingPublicIPAddress := range *v {
if existingPublicIPAddress.Id == nil {
continue
}

if strings.EqualFold(*existingPublicIPAddress.Id, publicIpAddressId.ID()) {
return metadata.ResourceRequiresImport(r.ResourceType(), id)
}

publicIpAddressesV6 = append(publicIpAddressesV6, existingPublicIPAddress)
}
}

publicIpAddressesV6 = append(publicIpAddressesV6, natgateways.SubResource{
Id: pointer.To(state.PublicIpAddressId),
})
props.PublicIPAddressesV6 = pointer.To(publicIpAddressesV6)
}
}

if err := client.CreateOrUpdateThenPoll(ctx, *natGatewayId, *natGateway.Model); err != nil {
return fmt.Errorf("updating %s: %+v", *natGatewayId, err)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return fmt.Errorf("updating %s: %+v", *natGatewayId, err)
return fmt.Errorf("creating %s: %+v", id, err)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

}

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

func (r NatGatewayPublicIpV6AssociationResource) Read() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 5 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.Network.NatGateways

id, err := commonids.ParseCompositeResourceID(metadata.ResourceData.Id(), &natgateways.NatGatewayId{}, &commonids.PublicIPAddressId{})
if err != nil {
return err
}

natGateway, err := client.Get(ctx, *id.First, natgateways.DefaultGetOperationOptions())
if err != nil {
if response.WasNotFound(natGateway.HttpResponse) {
return metadata.MarkAsGone(id)
}
return fmt.Errorf("retrieving %s: %+v", *id.First, err)
}

state := NatGatewayPublicIpV6AssociationModel{
NatGatewayId: id.First.ID(),
PublicIpAddressId: id.Second.ID(),
}

if model := natGateway.Model; model != nil {
if props := model.Properties; props != nil {
if props.PublicIPAddressesV6 == nil {
return metadata.MarkAsGone(id)
}

publicIPAddressFound := false
for _, pip := range *props.PublicIPAddressesV6 {
if pip.Id == nil {
continue
}

if strings.EqualFold(*pip.Id, id.Second.ID()) {
publicIPAddressFound = true
break
}
}

if !publicIPAddressFound {
return metadata.MarkAsGone(id)
}
}
}

Copy link
Copy Markdown
Collaborator

@wuxu92 wuxu92 Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to handle if model or props is nil as well, and we can make it clear:

Suggested change
if model := natGateway.Model; model != nil {
if props := model.Properties; props != nil {
if props.PublicIPAddressesV6 == nil {
return metadata.MarkAsGone(id)
}
publicIPAddressFound := false
for _, pip := range *props.PublicIPAddressesV6 {
if pip.Id == nil {
continue
}
if strings.EqualFold(*pip.Id, id.Second.ID()) {
publicIPAddressFound = true
break
}
}
if !publicIPAddressFound {
return metadata.MarkAsGone(id)
}
}
}
if natGateway.Model == nil || natGateway.Model.Properties == nil {
return fmt.Errorf("retrieving %s: `model` or `properties` was nil", id)
}
publicIPAddressFound := false
for _, pip := range pointer.From(model.Properties.PublicIPAddressesV6) {
if strings.EqualFold(pointer.From(pip.Id), id.Second.ID()) {
publicIPAddressFound = true
break
}
}
if !publicIPAddressFound {
return metadata.MarkAsGone(id)
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

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

func (r NatGatewayPublicIpV6AssociationResource) Delete() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.Network.NatGateways

id, err := commonids.ParseCompositeResourceID(metadata.ResourceData.Id(), &natgateways.NatGatewayId{}, &commonids.PublicIPAddressId{})
if err != nil {
return err
}

locks.ByID(id.First.ID())
defer locks.UnlockByID(id.First.ID())

natGateway, err := client.Get(ctx, *id.First, natgateways.DefaultGetOperationOptions())
if err != nil {
if response.WasNotFound(natGateway.HttpResponse) {
return fmt.Errorf("%s was not found", *id.First)
}
return fmt.Errorf("retrieving %s: %+v", *id.First, err)
}

if model := natGateway.Model; model != nil {
if props := model.Properties; props != nil {
publicIpAddressesV6 := make([]natgateways.SubResource, 0)

if v := props.PublicIPAddressesV6; v != nil {
for _, publicIPAddress := range *v {
if publicIPAddress.Id == nil {
continue
}

if !strings.EqualFold(*publicIPAddress.Id, id.Second.ID()) {
publicIpAddressesV6 = append(publicIpAddressesV6, publicIPAddress)
}
}
}
props.PublicIPAddressesV6 = pointer.To(publicIpAddressesV6)
}
}

if err := client.CreateOrUpdateThenPoll(ctx, *id.First, *natGateway.Model); err != nil {
return fmt.Errorf("removing association between %s and %s: %+v", *id.First, *id.Second, err)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return fmt.Errorf("removing association between %s and %s: %+v", *id.First, *id.Second, err)
return fmt.Errorf("deleting %s: %+v", *id, err)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

}

return nil
},
}
}

func (r NatGatewayPublicIpV6AssociationResource) IDValidationFunc() pluginsdk.SchemaValidateFunc {
return func(input interface{}, key string) (warnings []string, errors []error) {
_, err := commonids.ParseCompositeResourceID(input.(string), &natgateways.NatGatewayId{}, &commonids.PublicIPAddressId{})
if err != nil {
errors = append(errors, err)
}
return warnings, errors
}
}
Loading
Loading