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

r/nat_gateway_public_ip_association: using a custom ID / support for delta updates in azurerm_nat_gateway #7106

Merged
merged 8 commits into from May 28, 2020
Expand Up @@ -3,11 +3,11 @@ package network
import (
"fmt"
"log"
"strings"
"time"

"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2020-03-01/network"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks"
Expand All @@ -18,14 +18,14 @@ import (
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func resourceArmNatGatewayPublicIpAssociation() *schema.Resource {
func resourceArmNATGatewayPublicIpAssociation() *schema.Resource {
return &schema.Resource{
Create: resourceArmNatGatewayPublicIpAssociationCreate,
Read: resourceArmNatGatewayPublicIpAssociationRead,
Delete: resourceArmNatGatewayPublicIpAssociationDelete,
Create: resourceArmNATGatewayPublicIpAssociationCreate,
Read: resourceArmNATGatewayPublicIpAssociationRead,
Delete: resourceArmNATGatewayPublicIpAssociationDelete,

Importer: azSchema.ValidateResourceIDPriorToImport(func(id string) error {
_, err := parse.NatGatewayID(id)
_, err := parse.NatGatewayPublicIPAddressAssociationID(id)
return err
}),

Expand All @@ -47,18 +47,18 @@ func resourceArmNatGatewayPublicIpAssociation() *schema.Resource {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: azure.ValidateResourceID,
ValidateFunc: validate.PublicIPAddressID,
},
},
}
}

func resourceArmNatGatewayPublicIpAssociationCreate(d *schema.ResourceData, meta interface{}) error {
func resourceArmNATGatewayPublicIpAssociationCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Network.NatGatewayClient
ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d)
defer cancel()

log.Printf("[INFO] preparing arguments for Nat Gateway <-> Public Ip Association creation.")
log.Printf("[INFO] preparing arguments for NAT Gateway <-> Public IP Association creation.")
natGatewayId := d.Get("nat_gateway_id").(string)
publicIpAddressId := d.Get("public_ip_address_id").(string)
parsedNatGatewayId, err := parse.NatGatewayID(natGatewayId)
Expand All @@ -72,21 +72,24 @@ func resourceArmNatGatewayPublicIpAssociationCreate(d *schema.ResourceData, meta
natGateway, err := client.Get(ctx, parsedNatGatewayId.ResourceGroup, parsedNatGatewayId.Name, "")
if err != nil {
if utils.ResponseWasNotFound(natGateway.Response) {
return fmt.Errorf("Nat Gateway %q (Resource Group %q) was not found.", parsedNatGatewayId.Name, parsedNatGatewayId.ResourceGroup)
return fmt.Errorf("NAT Gateway %q (Resource Group %q) was not found.", parsedNatGatewayId.Name, parsedNatGatewayId.ResourceGroup)
}
return fmt.Errorf("failed to retrieve Nat Gateway %q (Resource Group %q): %+v", parsedNatGatewayId.Name, parsedNatGatewayId.ResourceGroup, err)
return fmt.Errorf("failed to retrieve NAT Gateway %q (Resource Group %q): %+v", parsedNatGatewayId.Name, parsedNatGatewayId.ResourceGroup, err)
}

id := fmt.Sprintf("%s|%s", *natGateway.ID, publicIpAddressId)
publicIpAddresses := make([]network.SubResource, 0)
if natGateway.PublicIPAddresses != nil {
for _, existingPublicIPAddress := range *natGateway.PublicIPAddresses {
if existingPublicIPAddress.ID != nil {
if *existingPublicIPAddress.ID == publicIpAddressId {
return tf.ImportAsExistsError("azurerm_nat_gateway_public_ip_association", *natGateway.ID)
}
if existingPublicIPAddress.ID == nil {
continue
}

publicIpAddresses = append(publicIpAddresses, existingPublicIPAddress)
if strings.EqualFold(*existingPublicIPAddress.ID, publicIpAddressId) {
return tf.ImportAsExistsError("azurerm_nat_gateway_public_ip_association", id)
}

publicIpAddresses = append(publicIpAddresses, existingPublicIPAddress)
}
}

Expand All @@ -97,98 +100,119 @@ func resourceArmNatGatewayPublicIpAssociationCreate(d *schema.ResourceData, meta

future, err := client.CreateOrUpdate(ctx, parsedNatGatewayId.ResourceGroup, parsedNatGatewayId.Name, natGateway)
if err != nil {
return fmt.Errorf("failed to update Public IP Association for Nat Gateway %q (Resource Group %q): %+v", parsedNatGatewayId.Name, parsedNatGatewayId.ResourceGroup, err)
return fmt.Errorf("failed to update Public IP Association for NAT Gateway %q (Resource Group %q): %+v", parsedNatGatewayId.Name, parsedNatGatewayId.ResourceGroup, err)
}

if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("failed to wait for completion of Public IP Association for Nat Gateway %q (Resource Group %q): %+v", parsedNatGatewayId.Name, parsedNatGatewayId.ResourceGroup, err)
return fmt.Errorf("failed to wait for completion of Public IP Association for NAT Gateway %q (Resource Group %q): %+v", parsedNatGatewayId.Name, parsedNatGatewayId.ResourceGroup, err)
}

resp, err := client.Get(ctx, parsedNatGatewayId.ResourceGroup, parsedNatGatewayId.Name, "")
if err != nil {
return fmt.Errorf("failed to retrieve Nat Gateway %q (Resource Group %q): %+v", parsedNatGatewayId.Name, parsedNatGatewayId.ResourceGroup, err)
}
d.SetId(*resp.ID)
d.SetId(id)

return resourceArmNatGatewayPublicIpAssociationRead(d, meta)
return resourceArmNATGatewayPublicIpAssociationRead(d, meta)
}

func resourceArmNatGatewayPublicIpAssociationRead(d *schema.ResourceData, meta interface{}) error {
func resourceArmNATGatewayPublicIpAssociationRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Network.NatGatewayClient
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

id, err := parse.NatGatewayID(d.Id())
id, err := parse.NatGatewayPublicIPAddressAssociationID(d.Id())
if err != nil {
return err
}

natGateway, err := client.Get(ctx, id.ResourceGroup, id.Name, "")
natGateway, err := client.Get(ctx, id.NatGateway.ResourceGroup, id.NatGateway.Name, "")
if err != nil {
if utils.ResponseWasNotFound(natGateway.Response) {
log.Printf("[DEBUG] Nat Gateway %q (Resource Group %q) could not be found - removing from state!", id.Name, id.ResourceGroup)
log.Printf("[DEBUG] NAT Gateway %q (Resource Group %q) could not be found - removing from state!", id.NatGateway.Name, id.NatGateway.ResourceGroup)
d.SetId("")
return nil
}
return fmt.Errorf("failed to retrieve Nat Gateway %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err)
return fmt.Errorf("failed to retrieve NAT Gateway %q (Resource Group %q): %+v", id.NatGateway.Name, id.NatGateway.ResourceGroup, err)
}

if natGateway.NatGatewayPropertiesFormat == nil {
return fmt.Errorf("`properties` was nil for NAT Gateway %q (Resource Group %q)", id.NatGateway.Name, id.NatGateway.ResourceGroup)
}
props := *natGateway.NatGatewayPropertiesFormat

if props.PublicIPAddresses == nil {
log.Printf("[DEBUG] NAT Gateway %q (Resource Group %q) doesn't have any Public IP's - removing from state!", id.NatGateway.Name, id.NatGateway.ResourceGroup)
d.SetId("")
return nil
}

publicIPAddressId := ""
for _, pip := range *props.PublicIPAddresses {
if pip.ID == nil {
continue
}

if strings.EqualFold(*pip.ID, id.PublicIPAddressID) {
publicIPAddressId = *pip.ID
break
}
}

if natGateway.PublicIPAddresses == nil {
log.Printf("[DEBUG] Nat Gateway %q (Resource Group %q) doesn't have a Public IP - removing from state!", id.Name, id.ResourceGroup)
if publicIPAddressId == "" {
log.Printf("[DEBUG] Association between NAT Gateway %q (Resource Group %q) and Public IP Address %q was not found - removing from state", id.NatGateway.Name, id.NatGateway.ResourceGroup, id.PublicIPAddressID)
d.SetId("")
return nil
}

d.Set("nat_gateway_id", natGateway.ID)
d.Set("public_ip_address_id", publicIPAddressId)

return nil
}

func resourceArmNatGatewayPublicIpAssociationDelete(d *schema.ResourceData, meta interface{}) error {
func resourceArmNATGatewayPublicIpAssociationDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Network.NatGatewayClient
ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d)
defer cancel()

id, err := parse.NatGatewayID(d.Id())
id, err := parse.NatGatewayPublicIPAddressAssociationID(d.Id())
if err != nil {
return err
}

publicIpAddressId := d.Get("public_ip_address_id").(string)

locks.ByName(id.Name, natGatewayResourceName)
defer locks.UnlockByName(id.Name, natGatewayResourceName)
locks.ByName(id.NatGateway.Name, natGatewayResourceName)
defer locks.UnlockByName(id.NatGateway.Name, natGatewayResourceName)

natGateway, err := client.Get(ctx, id.ResourceGroup, id.Name, "")
natGateway, err := client.Get(ctx, id.NatGateway.ResourceGroup, id.NatGateway.Name, "")
if err != nil {
if utils.ResponseWasNotFound(natGateway.Response) {
return fmt.Errorf("Nat Gateway %q (Resource Group %q) was not found.", id.Name, id.ResourceGroup)
return fmt.Errorf("NAT Gateway %q (Resource Group %q) was not found", id.NatGateway.Name, id.NatGateway.ResourceGroup)
}

return fmt.Errorf("failed to retrieve Nat Gateway %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err)
return fmt.Errorf("retrieving NAT Gateway %q (Resource Group %q): %+v", id.NatGateway.Name, id.NatGateway.ResourceGroup, err)
}
if natGateway.NatGatewayPropertiesFormat == nil {
return fmt.Errorf("retrieving NAT Gateway %q (Resource Group %q): `properties` was nil", id.NatGateway.Name, id.NatGateway.ResourceGroup)
}

publicIpAddresses := make([]network.SubResource, 0)
if publicIPAddresses := natGateway.PublicIPAddresses; publicIPAddresses != nil {
if publicIPAddresses := natGateway.NatGatewayPropertiesFormat.PublicIPAddresses; publicIPAddresses != nil {
for _, publicIPAddress := range *publicIPAddresses {
if publicIPAddress.ID == nil {
continue
}

if *publicIPAddress.ID != publicIpAddressId {
if !strings.EqualFold(*publicIPAddress.ID, id.PublicIPAddressID) {
publicIpAddresses = append(publicIpAddresses, publicIPAddress)
}
}
}
natGateway.PublicIPAddresses = &publicIpAddresses
natGateway.NatGatewayPropertiesFormat.PublicIPAddresses = &publicIpAddresses

future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, natGateway)
future, err := client.CreateOrUpdate(ctx, id.NatGateway.ResourceGroup, id.NatGateway.Name, natGateway)
if err != nil {
return fmt.Errorf("failed to remove Public Ip Association for Nat Gateway %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err)
return fmt.Errorf("removing association between NAT Gateway %q (Resource Group %q) and Public IP Address %q: %+v", id.NatGateway.Name, id.NatGateway.ResourceGroup, id.PublicIPAddressID, err)
}

if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("failed to wait for removal of Public Ip Association for Nat Gateway %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err)
return fmt.Errorf("waiting for association between Public IP ID %q for NAT Gateway %q (Resource Group %q) to be removed: %+v", id.PublicIPAddressID, id.NatGateway.Name, id.NatGateway.ResourceGroup, err)
}

return nil
Expand Down