From 4824f7544f4118693300720d4e2270ca288bccb7 Mon Sep 17 00:00:00 2001 From: neil-yechenwei Date: Tue, 7 Apr 2020 17:29:58 +0800 Subject: [PATCH 1/9] Add association between azurerm_nat_gateway and azurerm_public_ip --- .../internal/services/network/registration.go | 1 + ...e_arm_nat_gateway_public_ip_association.go | 249 ++++++++++++++++++ .../network/resource_arm_public_ip.go | 2 + ..._nat_gateway_public_ip_association_test.go | 56 ++++ 4 files changed, 308 insertions(+) create mode 100644 azurerm/internal/services/network/resource_arm_nat_gateway_public_ip_association.go create mode 100644 azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go diff --git a/azurerm/internal/services/network/registration.go b/azurerm/internal/services/network/registration.go index 1bd4e2ba5bc4..c6bdf4c1e225 100644 --- a/azurerm/internal/services/network/registration.go +++ b/azurerm/internal/services/network/registration.go @@ -86,6 +86,7 @@ func (r Registration) SupportedResources() map[string]*schema.Resource { "azurerm_private_endpoint": resourceArmPrivateEndpoint(), "azurerm_private_link_service": resourceArmPrivateLinkService(), "azurerm_public_ip": resourceArmPublicIp(), + "azurerm_nat_gateway_public_ip_association": resourceArmNatGatewayPublicIpAssociation(), "azurerm_public_ip_prefix": resourceArmPublicIpPrefix(), "azurerm_network_security_group": resourceArmNetworkSecurityGroup(), "azurerm_network_security_rule": resourceArmNetworkSecurityRule(), diff --git a/azurerm/internal/services/network/resource_arm_nat_gateway_public_ip_association.go b/azurerm/internal/services/network/resource_arm_nat_gateway_public_ip_association.go new file mode 100644 index 000000000000..f4a9bfc56dff --- /dev/null +++ b/azurerm/internal/services/network/resource_arm_nat_gateway_public_ip_association.go @@ -0,0 +1,249 @@ +package network + +import ( + "fmt" + "log" + "time" + + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-09-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/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmNatGatewayPublicIpAssociation() *schema.Resource { + return &schema.Resource{ + Create: resourceArmNatGatewayPublicIpAssociationCreate, + Read: resourceArmNatGatewayPublicIpAssociationRead, + Delete: resourceArmNatGatewayPublicIpAssociationDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Read: schema.DefaultTimeout(5 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "nat_gateway_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + }, + + "public_ip_address_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + }, + }, + } +} + +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.") + natGatewayId := d.Get("nat_gateway_id").(string) + publicIpAddressId := d.Get("public_ip_address_id").(string) + parsedNatGatewayId, err := azure.ParseAzureResourceID(natGatewayId) + if err != nil { + return fmt.Errorf("Error parsing nat_gateway_id '%s': %+v", natGatewayId, err) + } + + natGatewayName := parsedNatGatewayId.Path["natGateways"] + resourceGroup := parsedNatGatewayId.ResourceGroup + + parsedPublicIpAddressId, err := azure.ParseAzureResourceID(publicIpAddressId) + if err != nil { + return fmt.Errorf("Error parsing public_ip_address_id '%s': %+v", publicIpAddressId, err) + } + + publicIpAddressName := parsedPublicIpAddressId.Path["publicIPAddresses"] + + locks.ByName(natGatewayName, natGatewayResourceName) + defer locks.UnlockByName(natGatewayName, natGatewayResourceName) + locks.ByName(publicIpAddressName, publicIpResourceName) + defer locks.UnlockByName(publicIpAddressName, publicIpResourceName) + + natGateway, err := client.Get(ctx, resourceGroup, natGatewayName, "") + if err != nil { + if utils.ResponseWasNotFound(natGateway.Response) { + return fmt.Errorf("Nat Gateway %q (Resource Group %q) was not found!", natGatewayName, resourceGroup) + } + return fmt.Errorf("Error retrieving Nat Gateway %q (Resource Group %q): %+v", natGatewayName, resourceGroup, err) + } + + publicIpAddresses := make([]network.SubResource, 0) + + if natGateway.PublicIPAddresses != nil { + for _, existingPublicIPAddress := range *natGateway.PublicIPAddresses { + if id := existingPublicIPAddress.ID; id != nil { + if *id == publicIpAddressId { + if features.ShouldResourcesBeImported() { + return tf.ImportAsExistsError("azurerm_nat_gateway_public_ip_association", *natGateway.ID) + } + + continue + } + + publicIpAddresses = append(publicIpAddresses, existingPublicIPAddress) + } + } + } + + publicIpAddress := network.SubResource{ + ID: utils.String(publicIpAddressId), + } + publicIpAddresses = append(publicIpAddresses, publicIpAddress) + natGateway.PublicIPAddresses = &publicIpAddresses + + future, err := client.CreateOrUpdate(ctx, resourceGroup, natGatewayName, natGateway) + if err != nil { + return fmt.Errorf("Error updating Public IP Association for Nat Gateway %q (Resource Group %q): %+v", natGatewayName, resourceGroup, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for completion of Public IP Association for Nat Gateway %q (Resource Group %q): %+v", natGatewayName, resourceGroup, err) + } + + resp, err := client.Get(ctx, resourceGroup, natGatewayName, "") + if err != nil { + return fmt.Errorf("Error retrieving Nat Gateway %q (Resource Group %q): %+v", natGatewayName, resourceGroup, err) + } + d.SetId(*resp.ID) + + return resourceArmNatGatewayPublicIpAssociationRead(d, meta) +} + +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 := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + natGatewayName := id.Path["natGateways"] + publicIpAddressId := d.Get("public_ip_address_id").(string) + + natGateway, err := client.Get(ctx, resourceGroup, natGatewayName, "") + if err != nil { + if utils.ResponseWasNotFound(natGateway.Response) { + log.Printf("[DEBUG] Nat Gateway %q (Resource Group %q) could not be found - removing from state!", natGatewayName, resourceGroup) + d.SetId("") + return nil + } + return fmt.Errorf("Error retrieving Nat Gateway %q (Resource Group %q): %+v", natGatewayName, resourceGroup, err) + } + + props := natGateway.NatGatewayPropertiesFormat + if props == nil { + return fmt.Errorf("Error: `properties` was nil for Nat Gateway %q (Resource Group %q)", natGatewayName, resourceGroup) + } + publicIpAddresses := props.PublicIPAddresses + if publicIpAddresses == nil { + log.Printf("[DEBUG] Nat Gateway %q (Resource Group %q) doesn't have a Public IP - removing from state!", natGatewayName, resourceGroup) + d.SetId("") + return nil + } + + found := false + if props := natGateway.NatGatewayPropertiesFormat; props != nil { + if publicIPAddresses := props.PublicIPAddresses; publicIPAddresses != nil { + for _, publicIPAddress := range *publicIPAddresses { + if publicIPAddress.ID == nil { + continue + } + + if *publicIPAddress.ID == publicIpAddressId { + found = true + break + } + } + } + } + + if !found { + log.Printf("[DEBUG] Association between Nat Gateway %q (Resource Group %q) and Public IP %q was not found - removing from state!", natGatewayName, resourceGroup, 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 { + client := meta.(*clients.Client).Network.NatGatewayClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + natGatewayName := id.Path["natGateways"] + publicIpAddressId := d.Get("public_ip_address_id").(string) + + locks.ByName(natGatewayName, natGatewayResourceName) + defer locks.UnlockByName(natGatewayName, natGatewayResourceName) + + natGateway, err := client.Get(ctx, resourceGroup, natGatewayName, "") + if err != nil { + if utils.ResponseWasNotFound(natGateway.Response) { + return fmt.Errorf("Nat Gateway %q (Resource Group %q) was not found!", natGatewayName, resourceGroup) + } + + return fmt.Errorf("Error retrieving Nat Gateway %q (Resource Group %q): %+v", natGatewayName, resourceGroup, err) + } + + props := natGateway.NatGatewayPropertiesFormat + if props == nil { + return fmt.Errorf("Error: `properties` was nil for Nat Gateway %q (Resource Group %q)", natGatewayName, resourceGroup) + } + + publicIpAddresses := make([]network.SubResource, 0) + if publicIPAddresses := props.PublicIPAddresses; publicIPAddresses != nil { + for _, publicIPAddress := range *publicIPAddresses { + if publicIPAddress.ID == nil { + continue + } + + if *publicIPAddress.ID != publicIpAddressId { + publicIpAddresses = append(publicIpAddresses, publicIPAddress) + } + } + } + props.PublicIPAddresses = &publicIpAddresses + + future, err := client.CreateOrUpdate(ctx, resourceGroup, natGatewayName, natGateway) + if err != nil { + return fmt.Errorf("Error removing Public Ip Association for Nat Gateway %q (Resource Group %q): %+v", natGatewayName, resourceGroup, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for removal of Public Ip Association for Nat Gateway %q (Resource Group %q): %+v", natGatewayName, resourceGroup, err) + } + + return nil +} diff --git a/azurerm/internal/services/network/resource_arm_public_ip.go b/azurerm/internal/services/network/resource_arm_public_ip.go index 8d3790516939..86b162b370cb 100644 --- a/azurerm/internal/services/network/resource_arm_public_ip.go +++ b/azurerm/internal/services/network/resource_arm_public_ip.go @@ -20,6 +20,8 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) +var publicIpResourceName = "azurerm_public_ip" + func resourceArmPublicIp() *schema.Resource { return &schema.Resource{ Create: resourceArmPublicIpCreateUpdate, diff --git a/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go b/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go new file mode 100644 index 000000000000..483e05b3ec10 --- /dev/null +++ b/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go @@ -0,0 +1,56 @@ +package tests + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance" +) + +func TestAccAzureRMNatGatewayPublicIpAssociation_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_nat_gateway_public_ip_association", "test") + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNatGatewayPublicIpAssociation_basic(data), + }, + }, + }) +} + +func testAccAzureRMNatGatewayPublicIpAssociation_basic(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "neil-nat-rg" + location = "westus" +} + +resource "azurerm_public_ip" "test" { + name = "neil-pip" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + allocation_method = "Static" + sku = "Standard" +} + +resource "azurerm_nat_gateway" "test" { + name = "neil-gateway" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku_name = "Standard" +} + +resource "azurerm_nat_gateway_public_ip_association" "test" { + nat_gateway_id = azurerm_nat_gateway.test.id + public_ip_address_id = azurerm_public_ip.test.id +} +`) +} + From fc9b24efe09f5d188d15ac12ac1cc4057d11c11c Mon Sep 17 00:00:00 2001 From: neil-yechenwei Date: Wed, 8 Apr 2020 15:40:06 +0800 Subject: [PATCH 2/9] Add virtual resource association: azurerm_nat_gateway_public_ip --- .../network/data_source_nat_gateway.go | 13 - .../services/network/parse/nat_gateway.go | 31 ++ .../network/parse/nat_gateway_test.go | 70 +++++ .../network/resource_arm_nat_gateway.go | 15 - ...e_arm_nat_gateway_public_ip_association.go | 115 +++----- .../tests/data_source_nat_gateway_test.go | 1 - ..._nat_gateway_public_ip_association_test.go | 270 +++++++++++++++++- .../tests/resource_arm_nat_gateway_test.go | 3 - website/azurerm.erb | 4 + website/docs/d/nat_gateway.html.markdown | 2 - website/docs/r/nat_gateway.html.markdown | 3 - ...ateway_public_ip_association.html.markdown | 75 +++++ 12 files changed, 471 insertions(+), 131 deletions(-) create mode 100644 azurerm/internal/services/network/parse/nat_gateway.go create mode 100644 azurerm/internal/services/network/parse/nat_gateway_test.go create mode 100644 website/docs/r/nat_gateway_public_ip_association.html.markdown diff --git a/azurerm/internal/services/network/data_source_nat_gateway.go b/azurerm/internal/services/network/data_source_nat_gateway.go index 2b9445e36b96..8b67afa3bd12 100644 --- a/azurerm/internal/services/network/data_source_nat_gateway.go +++ b/azurerm/internal/services/network/data_source_nat_gateway.go @@ -35,15 +35,6 @@ func dataSourceArmNatGateway() *schema.Resource { Computed: true, }, - "public_ip_address_ids": { - Type: schema.TypeList, - Optional: true, - Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - "public_ip_prefix_ids": { Type: schema.TypeList, Optional: true, @@ -110,10 +101,6 @@ func dataSourceArmNatGatewayRead(d *schema.ResourceData, meta interface{}) error d.Set("idle_timeout_in_minutes", props.IdleTimeoutInMinutes) d.Set("resource_guid", props.ResourceGUID) - if err := d.Set("public_ip_address_ids", flattenArmNatGatewaySubResourceID(props.PublicIPAddresses)); err != nil { - return fmt.Errorf("Error setting `public_ip_address_ids`: %+v", err) - } - if err := d.Set("public_ip_prefix_ids", flattenArmNatGatewaySubResourceID(props.PublicIPPrefixes)); err != nil { return fmt.Errorf("Error setting `public_ip_prefix_ids`: %+v", err) } diff --git a/azurerm/internal/services/network/parse/nat_gateway.go b/azurerm/internal/services/network/parse/nat_gateway.go new file mode 100644 index 000000000000..c730d001f34d --- /dev/null +++ b/azurerm/internal/services/network/parse/nat_gateway.go @@ -0,0 +1,31 @@ +package parse + +import ( + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" +) + +type NatGatewayId struct { + Name string + ResourceGroup string +} + +func NatGatewayID(input string) (*NatGatewayId, error) { + id, err := azure.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + natGateway := NatGatewayId{ + ResourceGroup: id.ResourceGroup, + } + + if natGateway.Name, err = id.PopSegment("natGateways"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &natGateway, nil +} diff --git a/azurerm/internal/services/network/parse/nat_gateway_test.go b/azurerm/internal/services/network/parse/nat_gateway_test.go new file mode 100644 index 000000000000..d93cb021cf27 --- /dev/null +++ b/azurerm/internal/services/network/parse/nat_gateway_test.go @@ -0,0 +1,70 @@ +package parse + +import ( + "testing" +) + +func TestNatGatewayID(t *testing.T) { + testData := []struct { + Name string + Input string + Error bool + Expect *NatGatewayId + }{ + { + Name: "Empty", + Input: "", + Error: true, + }, + { + Name: "No Resource Groups Segment", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000", + Error: true, + }, + { + Name: "No Resource Groups Value", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups", + Error: true, + }, + { + Name: "Resource Group ID", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1", + Error: true, + }, + { + Name: "Missing Nat Gateway Value", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Network/natGateways", + Error: true, + }, + { + Name: "Nat Gateway ID", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Network/natGateways/gateway1", + Error: false, + Expect: &NatGatewayId{ + Name: "gateway1", + ResourceGroup: "group1", + }, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Name) + + actual, err := NatGatewayID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expected a value but got an error: %s", err) + } + + if actual.Name != v.Expect.Name { + t.Fatalf("Expected %q but got %q for Name", v.Expect.Name, actual.Name) + } + + if actual.ResourceGroup != v.Expect.ResourceGroup { + t.Fatalf("Expected %q but got %q for Resource Group", v.Expect.ResourceGroup, actual.ResourceGroup) + } + } +} diff --git a/azurerm/internal/services/network/resource_arm_nat_gateway.go b/azurerm/internal/services/network/resource_arm_nat_gateway.go index 573065b5087c..cce46af4647b 100644 --- a/azurerm/internal/services/network/resource_arm_nat_gateway.go +++ b/azurerm/internal/services/network/resource_arm_nat_gateway.go @@ -57,15 +57,6 @@ func resourceArmNatGateway() *schema.Resource { ValidateFunc: validation.IntBetween(4, 120), }, - "public_ip_address_ids": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: azure.ValidateResourceID, - }, - }, - "public_ip_prefix_ids": { Type: schema.TypeSet, Optional: true, @@ -118,7 +109,6 @@ func resourceArmNatGatewayCreateUpdate(d *schema.ResourceData, meta interface{}) location := azure.NormalizeLocation(d.Get("location").(string)) idleTimeoutInMinutes := d.Get("idle_timeout_in_minutes").(int) - publicIpAddressIds := d.Get("public_ip_address_ids").(*schema.Set).List() publicIpPrefixIds := d.Get("public_ip_prefix_ids").(*schema.Set).List() skuName := d.Get("sku_name").(string) zones := d.Get("zones").([]interface{}) @@ -128,7 +118,6 @@ func resourceArmNatGatewayCreateUpdate(d *schema.ResourceData, meta interface{}) Location: utils.String(location), NatGatewayPropertiesFormat: &network.NatGatewayPropertiesFormat{ IdleTimeoutInMinutes: utils.Int32(int32(idleTimeoutInMinutes)), - PublicIPAddresses: expandArmNatGatewaySubResourceID(publicIpAddressIds), PublicIPPrefixes: expandArmNatGatewaySubResourceID(publicIpPrefixIds), }, Sku: &network.NatGatewaySku{ @@ -191,10 +180,6 @@ func resourceArmNatGatewayRead(d *schema.ResourceData, meta interface{}) error { d.Set("idle_timeout_in_minutes", props.IdleTimeoutInMinutes) d.Set("resource_guid", props.ResourceGUID) - if err := d.Set("public_ip_address_ids", flattenArmNatGatewaySubResourceID(props.PublicIPAddresses)); err != nil { - return fmt.Errorf("Error setting `public_ip_address_ids`: %+v", err) - } - if err := d.Set("public_ip_prefix_ids", flattenArmNatGatewaySubResourceID(props.PublicIPPrefixes)); err != nil { return fmt.Errorf("Error setting `public_ip_prefix_ids`: %+v", err) } diff --git a/azurerm/internal/services/network/resource_arm_nat_gateway_public_ip_association.go b/azurerm/internal/services/network/resource_arm_nat_gateway_public_ip_association.go index f4a9bfc56dff..f4c1a9fc62b5 100644 --- a/azurerm/internal/services/network/resource_arm_nat_gateway_public_ip_association.go +++ b/azurerm/internal/services/network/resource_arm_nat_gateway_public_ip_association.go @@ -12,6 +12,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/network/parse" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -58,40 +59,27 @@ func resourceArmNatGatewayPublicIpAssociationCreate(d *schema.ResourceData, meta 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 := azure.ParseAzureResourceID(natGatewayId) + parsedNatGatewayId, err := parse.NatGatewayID(natGatewayId) if err != nil { - return fmt.Errorf("Error parsing nat_gateway_id '%s': %+v", natGatewayId, err) - } - - natGatewayName := parsedNatGatewayId.Path["natGateways"] - resourceGroup := parsedNatGatewayId.ResourceGroup - - parsedPublicIpAddressId, err := azure.ParseAzureResourceID(publicIpAddressId) - if err != nil { - return fmt.Errorf("Error parsing public_ip_address_id '%s': %+v", publicIpAddressId, err) + return err } - publicIpAddressName := parsedPublicIpAddressId.Path["publicIPAddresses"] - - locks.ByName(natGatewayName, natGatewayResourceName) - defer locks.UnlockByName(natGatewayName, natGatewayResourceName) - locks.ByName(publicIpAddressName, publicIpResourceName) - defer locks.UnlockByName(publicIpAddressName, publicIpResourceName) + locks.ByName(parsedNatGatewayId.Name, natGatewayResourceName) + defer locks.UnlockByName(parsedNatGatewayId.Name, natGatewayResourceName) - natGateway, err := client.Get(ctx, resourceGroup, natGatewayName, "") + 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!", natGatewayName, resourceGroup) + return fmt.Errorf("Nat Gateway %q (Resource Group %q) was not found!", parsedNatGatewayId.Name, parsedNatGatewayId.ResourceGroup) } - return fmt.Errorf("Error retrieving Nat Gateway %q (Resource Group %q): %+v", natGatewayName, resourceGroup, err) + return fmt.Errorf("failed to retrieve Nat Gateway %q (Resource Group %q): %+v", parsedNatGatewayId.Name, parsedNatGatewayId.ResourceGroup, err) } publicIpAddresses := make([]network.SubResource, 0) - if natGateway.PublicIPAddresses != nil { for _, existingPublicIPAddress := range *natGateway.PublicIPAddresses { - if id := existingPublicIPAddress.ID; id != nil { - if *id == publicIpAddressId { + if existingPublicIPAddress.ID != nil { + if *existingPublicIPAddress.ID == publicIpAddressId { if features.ShouldResourcesBeImported() { return tf.ImportAsExistsError("azurerm_nat_gateway_public_ip_association", *natGateway.ID) } @@ -110,18 +98,18 @@ func resourceArmNatGatewayPublicIpAssociationCreate(d *schema.ResourceData, meta publicIpAddresses = append(publicIpAddresses, publicIpAddress) natGateway.PublicIPAddresses = &publicIpAddresses - future, err := client.CreateOrUpdate(ctx, resourceGroup, natGatewayName, natGateway) + future, err := client.CreateOrUpdate(ctx, parsedNatGatewayId.ResourceGroup, parsedNatGatewayId.Name, natGateway) if err != nil { - return fmt.Errorf("Error updating Public IP Association for Nat Gateway %q (Resource Group %q): %+v", natGatewayName, 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("Error waiting for completion of Public IP Association for Nat Gateway %q (Resource Group %q): %+v", natGatewayName, 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, resourceGroup, natGatewayName, "") + resp, err := client.Get(ctx, parsedNatGatewayId.ResourceGroup, parsedNatGatewayId.Name, "") if err != nil { - return fmt.Errorf("Error retrieving Nat Gateway %q (Resource Group %q): %+v", natGatewayName, resourceGroup, err) + return fmt.Errorf("failed to retrieve Nat Gateway %q (Resource Group %q): %+v", parsedNatGatewayId.Name, parsedNatGatewayId.ResourceGroup, err) } d.SetId(*resp.ID) @@ -133,60 +121,28 @@ func resourceArmNatGatewayPublicIpAssociationRead(d *schema.ResourceData, meta i ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := azure.ParseAzureResourceID(d.Id()) + id, err := parse.NatGatewayID(d.Id()) if err != nil { return err } - resourceGroup := id.ResourceGroup - natGatewayName := id.Path["natGateways"] - publicIpAddressId := d.Get("public_ip_address_id").(string) - - natGateway, err := client.Get(ctx, resourceGroup, natGatewayName, "") + natGateway, err := client.Get(ctx, id.ResourceGroup, id.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!", natGatewayName, resourceGroup) + log.Printf("[DEBUG] Nat Gateway %q (Resource Group %q) could not be found - removing from state!", id.Name, id.ResourceGroup) d.SetId("") return nil } - return fmt.Errorf("Error retrieving Nat Gateway %q (Resource Group %q): %+v", natGatewayName, resourceGroup, err) + return fmt.Errorf("failed to retrieve Nat Gateway %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } - props := natGateway.NatGatewayPropertiesFormat - if props == nil { - return fmt.Errorf("Error: `properties` was nil for Nat Gateway %q (Resource Group %q)", natGatewayName, resourceGroup) - } - publicIpAddresses := props.PublicIPAddresses - if publicIpAddresses == nil { - log.Printf("[DEBUG] Nat Gateway %q (Resource Group %q) doesn't have a Public IP - removing from state!", natGatewayName, resourceGroup) - d.SetId("") - return nil - } - - found := false - if props := natGateway.NatGatewayPropertiesFormat; props != nil { - if publicIPAddresses := props.PublicIPAddresses; publicIPAddresses != nil { - for _, publicIPAddress := range *publicIPAddresses { - if publicIPAddress.ID == nil { - continue - } - - if *publicIPAddress.ID == publicIpAddressId { - found = true - break - } - } - } - } - - if !found { - log.Printf("[DEBUG] Association between Nat Gateway %q (Resource Group %q) and Public IP %q was not found - removing from state!", natGatewayName, resourceGroup, publicIpAddressId) + 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) d.SetId("") return nil } d.Set("nat_gateway_id", natGateway.ID) - d.Set("public_ip_address_id", publicIpAddressId) return nil } @@ -196,34 +152,27 @@ func resourceArmNatGatewayPublicIpAssociationDelete(d *schema.ResourceData, meta ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := azure.ParseAzureResourceID(d.Id()) + id, err := parse.NatGatewayID(d.Id()) if err != nil { return err } - resourceGroup := id.ResourceGroup - natGatewayName := id.Path["natGateways"] publicIpAddressId := d.Get("public_ip_address_id").(string) - locks.ByName(natGatewayName, natGatewayResourceName) - defer locks.UnlockByName(natGatewayName, natGatewayResourceName) + locks.ByName(id.Name, natGatewayResourceName) + defer locks.UnlockByName(id.Name, natGatewayResourceName) - natGateway, err := client.Get(ctx, resourceGroup, natGatewayName, "") + natGateway, err := client.Get(ctx, id.ResourceGroup, id.Name, "") if err != nil { if utils.ResponseWasNotFound(natGateway.Response) { - return fmt.Errorf("Nat Gateway %q (Resource Group %q) was not found!", natGatewayName, resourceGroup) + return fmt.Errorf("Nat Gateway %q (Resource Group %q) was not found!", id.Name, id.ResourceGroup) } - return fmt.Errorf("Error retrieving Nat Gateway %q (Resource Group %q): %+v", natGatewayName, resourceGroup, err) - } - - props := natGateway.NatGatewayPropertiesFormat - if props == nil { - return fmt.Errorf("Error: `properties` was nil for Nat Gateway %q (Resource Group %q)", natGatewayName, resourceGroup) + return fmt.Errorf("failed to retrieve Nat Gateway %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } publicIpAddresses := make([]network.SubResource, 0) - if publicIPAddresses := props.PublicIPAddresses; publicIPAddresses != nil { + if publicIPAddresses := natGateway.PublicIPAddresses; publicIPAddresses != nil { for _, publicIPAddress := range *publicIPAddresses { if publicIPAddress.ID == nil { continue @@ -234,15 +183,15 @@ func resourceArmNatGatewayPublicIpAssociationDelete(d *schema.ResourceData, meta } } } - props.PublicIPAddresses = &publicIpAddresses + natGateway.PublicIPAddresses = &publicIpAddresses - future, err := client.CreateOrUpdate(ctx, resourceGroup, natGatewayName, natGateway) + future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, natGateway) if err != nil { - return fmt.Errorf("Error removing Public Ip Association for Nat Gateway %q (Resource Group %q): %+v", natGatewayName, resourceGroup, err) + return fmt.Errorf("failed to remove Public Ip Association for Nat Gateway %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("Error waiting for removal of Public Ip Association for Nat Gateway %q (Resource Group %q): %+v", natGatewayName, resourceGroup, err) + 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 nil diff --git a/azurerm/internal/services/network/tests/data_source_nat_gateway_test.go b/azurerm/internal/services/network/tests/data_source_nat_gateway_test.go index c9607ed50fae..33799be696c3 100644 --- a/azurerm/internal/services/network/tests/data_source_nat_gateway_test.go +++ b/azurerm/internal/services/network/tests/data_source_nat_gateway_test.go @@ -35,7 +35,6 @@ func TestAccDataSourceAzureRMNatGateway_complete(t *testing.T) { { Config: testAccDataSourceNatGateway_complete(data), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(data.ResourceName, "public_ip_address_ids.#", "1"), resource.TestCheckResourceAttr(data.ResourceName, "public_ip_prefix_ids.#", "1"), resource.TestCheckResourceAttr(data.ResourceName, "sku_name", "Standard"), resource.TestCheckResourceAttr(data.ResourceName, "idle_timeout_in_minutes", "10"), diff --git a/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go b/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go index 483e05b3ec10..f6043efcde87 100644 --- a/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go +++ b/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go @@ -4,8 +4,13 @@ import ( "fmt" "testing" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-09-01/network" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/network/parse" ) func TestAccAzureRMNatGatewayPublicIpAssociation_basic(t *testing.T) { @@ -13,44 +18,287 @@ func TestAccAzureRMNatGatewayPublicIpAssociation_basic(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, Providers: acceptance.SupportedProviders, + // intentional as this is a Virtual Resource + CheckDestroy: testCheckAzureRMNatGatewayDestroy, Steps: []resource.TestStep{ { Config: testAccAzureRMNatGatewayPublicIpAssociation_basic(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMNatGatewayPublicIpAssociationExists(data.ResourceName), + ), }, + data.ImportStep("public_ip_address_id"), }, }) } +func TestAccAzureRMNatGatewayPublicIpAssociation_requiresImport(t *testing.T) { + if !features.ShouldResourcesBeImported() { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + data := acceptance.BuildTestData(t, "azurerm_nat_gateway_public_ip_association", "test") + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + // intentional as this is a Virtual Resource + CheckDestroy: testCheckAzureRMNatGatewayDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNatGatewayPublicIpAssociation_basic(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMNatGatewayPublicIpAssociationExists(data.ResourceName), + ), + }, + data.RequiresImportErrorStep(testAccAzureRMNatGatewayPublicIpAssociation_requiresImport), + }, + }) +} + +func TestAccAzureRMNatGatewayPublicIpAssociation_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_nat_gateway_public_ip_association", "test") + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + // intentional as this is a Virtual Resource + CheckDestroy: testCheckAzureRMNatGatewayDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNatGatewayPublicIpAssociation_complete(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMNatGatewayPublicIpAssociationExists(data.ResourceName), + ), + }, + data.ImportStep("public_ip_address_id"), + }, + }) +} + +func TestAccAzureRMNatGatewayPublicIpAssociation_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_nat_gateway_public_ip_association", "test") + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + // intentional as this is a Virtual Resource + CheckDestroy: testCheckAzureRMNatGatewayDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNatGatewayPublicIpAssociation_basic(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMNatGatewayPublicIpAssociationExists(data.ResourceName), + ), + }, + data.ImportStep("public_ip_address_id"), + { + Config: testAccAzureRMNatGatewayPublicIpAssociation_update(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMNatGatewayPublicIpAssociationExists(data.ResourceName), + ), + }, + data.ImportStep("public_ip_address_id"), + }, + }) +} + +func TestAccAzureRMNatGatewayPublicIpAssociation_deleted(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_nat_gateway_public_ip_association", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + // intentional as this is a Virtual Resource + CheckDestroy: testCheckAzureRMNatGatewayDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNatGatewayPublicIpAssociation_basic(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMNatGatewayPublicIpAssociationExists(data.ResourceName), + testCheckAzureRMNatGatewayPublicIpAssociationDisappears(data.ResourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccAzureRMNatGatewayPublicIpAssociation_requiresImport(data acceptance.TestData) string { + template := testAccAzureRMNatGatewayPublicIpAssociation_basic(data) + return fmt.Sprintf(` +%s + +resource "azurerm_nat_gateway_public_ip_association" "import" { + nat_gateway_id = azurerm_nat_gateway_public_ip_association.test.nat_gateway_id + public_ip_address_id = azurerm_nat_gateway_public_ip_association.test.public_ip_address_id +} +`, template) +} + +func testCheckAzureRMNatGatewayPublicIpAssociationExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + client := acceptance.AzureProvider.Meta().(*clients.Client).Network.NatGatewayClient + ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext + + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + id, err := parse.NatGatewayID(rs.Primary.ID) + if err != nil { + return err + } + publicIpAddressId := rs.Primary.Attributes["public_ip_address_id"] + + resp, err := client.Get(ctx, id.ResourceGroup, id.Name, "") + if err != nil { + return fmt.Errorf("failed to retrieve Nat Gateway %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + } + + found := false + if publicIpAddresses := resp.PublicIPAddresses; publicIpAddresses != nil { + for _, publicIpAddress := range *publicIpAddresses { + if *publicIpAddress.ID == publicIpAddressId { + found = true + break + } + } + } + + if !found { + return fmt.Errorf("Association between Nat Gateway %q and Public Ip %q was not found!", id.Name, publicIpAddressId) + } + + return nil + } +} + +func testCheckAzureRMNatGatewayPublicIpAssociationDisappears(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + client := acceptance.AzureProvider.Meta().(*clients.Client).Network.NatGatewayClient + ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext + + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + id, err := parse.NatGatewayID(rs.Primary.ID) + if err != nil { + return err + } + publicIpAddressId := rs.Primary.Attributes["public_ip_address_id"] + + resp, err := client.Get(ctx, id.ResourceGroup, id.Name, "") + if err != nil { + return fmt.Errorf("failed to retrieve Nat Gateway %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + } + + updatedAddresses := make([]network.SubResource, 0) + if publicIpAddresses := resp.PublicIPAddresses; publicIpAddresses != nil { + for _, publicIpAddress := range *publicIpAddresses { + if *publicIpAddress.ID != publicIpAddressId { + updatedAddresses = append(updatedAddresses, publicIpAddress) + } + } + } + resp.PublicIPAddresses = &updatedAddresses + + future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, resp) + if err != nil { + return fmt.Errorf("failed to remove Nat Gateway Public Ip Association for Nat Gateway %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("failed to wait for removal of Nat Gateway Public Ip Association for Nat Gateway %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) + } + + return nil + } +} + func testAccAzureRMNatGatewayPublicIpAssociation_basic(data acceptance.TestData) string { + template := testAccAzureRMNatGatewayPublicIpAssociation_template(data) return fmt.Sprintf(` -provider "azurerm" { - features {} +%s + +resource "azurerm_nat_gateway_public_ip_association" "test" { + nat_gateway_id = azurerm_nat_gateway.test.id + public_ip_address_id = azurerm_public_ip.test.id +} +`, template) } -resource "azurerm_resource_group" "test" { - name = "neil-nat-rg" - location = "westus" +func testAccAzureRMNatGatewayPublicIpAssociation_complete(data acceptance.TestData) string { + template := testAccAzureRMNatGatewayPublicIpAssociation_template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_nat_gateway_public_ip_association" "test" { + nat_gateway_id = azurerm_nat_gateway.test.id + public_ip_address_id = azurerm_public_ip.test.id } -resource "azurerm_public_ip" "test" { - name = "neil-pip" +resource "azurerm_public_ip" "test2" { + name = "acctest-PIP2-%d" location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name allocation_method = "Static" sku = "Standard" } -resource "azurerm_nat_gateway" "test" { - name = "neil-gateway" +resource "azurerm_nat_gateway_public_ip_association" "test2" { + nat_gateway_id = azurerm_nat_gateway.test.id + public_ip_address_id = azurerm_public_ip.test2.id +} +`, template, data.RandomInteger) +} + +func testAccAzureRMNatGatewayPublicIpAssociation_update(data acceptance.TestData) string { + template := testAccAzureRMNatGatewayPublicIpAssociation_template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_nat_gateway" "test2" { + name = "acctest-NatGateway2-%d" location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name sku_name = "Standard" } resource "azurerm_nat_gateway_public_ip_association" "test" { - nat_gateway_id = azurerm_nat_gateway.test.id + nat_gateway_id = azurerm_nat_gateway.test2.id public_ip_address_id = azurerm_public_ip.test.id } -`) +`, template, data.RandomInteger) } +func testAccAzureRMNatGatewayPublicIpAssociation_template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-ngpi-%d" + location = "%s" +} + +resource "azurerm_public_ip" "test" { + name = "acctest-PIP-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + allocation_method = "Static" + sku = "Standard" +} + +resource "azurerm_nat_gateway" "test" { + name = "acctest-NatGateway-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku_name = "Standard" +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} diff --git a/azurerm/internal/services/network/tests/resource_arm_nat_gateway_test.go b/azurerm/internal/services/network/tests/resource_arm_nat_gateway_test.go index 4da55448d34c..8f3c2f8b290e 100644 --- a/azurerm/internal/services/network/tests/resource_arm_nat_gateway_test.go +++ b/azurerm/internal/services/network/tests/resource_arm_nat_gateway_test.go @@ -42,7 +42,6 @@ func TestAccAzureRMNatGateway_complete(t *testing.T) { Config: testAccAzureRMNatGateway_complete(data), Check: resource.ComposeTestCheckFunc( testCheckAzureRMNatGatewayExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "public_ip_address_ids.#", "1"), resource.TestCheckResourceAttr(data.ResourceName, "public_ip_prefix_ids.#", "1"), resource.TestCheckResourceAttr(data.ResourceName, "sku_name", "Standard"), resource.TestCheckResourceAttr(data.ResourceName, "idle_timeout_in_minutes", "10"), @@ -72,7 +71,6 @@ func TestAccAzureRMNatGateway_update(t *testing.T) { Config: testAccAzureRMNatGateway_complete(data), Check: resource.ComposeTestCheckFunc( testCheckAzureRMNatGatewayExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "public_ip_address_ids.#", "1"), resource.TestCheckResourceAttr(data.ResourceName, "public_ip_prefix_ids.#", "1"), resource.TestCheckResourceAttr(data.ResourceName, "sku_name", "Standard"), resource.TestCheckResourceAttr(data.ResourceName, "idle_timeout_in_minutes", "10"), @@ -185,7 +183,6 @@ resource "azurerm_nat_gateway" "test" { name = "acctestnatGateway-%d" location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name - public_ip_address_ids = [azurerm_public_ip.test.id] public_ip_prefix_ids = [azurerm_public_ip_prefix.test.id] sku_name = "Standard" idle_timeout_in_minutes = 10 diff --git a/website/azurerm.erb b/website/azurerm.erb index b1ee83ef0670..2d025e094ac4 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -1893,6 +1893,10 @@ azurerm_nat_gateway +
  • + azurerm_nat_gateway_public_ip_association +
  • +
  • azurerm_network_interface
  • diff --git a/website/docs/d/nat_gateway.html.markdown b/website/docs/d/nat_gateway.html.markdown index 511f331edabc..e2eae8ca0cb5 100644 --- a/website/docs/d/nat_gateway.html.markdown +++ b/website/docs/d/nat_gateway.html.markdown @@ -28,8 +28,6 @@ The following attributes are exported: * `idle_timeout_in_minutes` - The idle timeout in minutes which is used for the NAT Gateway. -* `public_ip_address_ids` - A list of existing Public IP Address resource IDs which the NAT Gateway is using. - * `public_ip_prefix_ids` - A list of existing Public IP Prefix resource IDs which the NAT Gateway is using. * `resource_guid` - The Resource GUID of the NAT Gateway. diff --git a/website/docs/r/nat_gateway.html.markdown b/website/docs/r/nat_gateway.html.markdown index 9126807d0e6b..2579678b7e7b 100644 --- a/website/docs/r/nat_gateway.html.markdown +++ b/website/docs/r/nat_gateway.html.markdown @@ -40,7 +40,6 @@ resource "azurerm_nat_gateway" "example" { name = "nat-Gateway" location = azurerm_resource_group.example.location resource_group_name = azurerm_resource_group.example.name - public_ip_address_ids = [azurerm_public_ip.example.id] public_ip_prefix_ids = [azurerm_public_ip_prefix.example.id] sku_name = "Standard" idle_timeout_in_minutes = 10 @@ -60,8 +59,6 @@ The following arguments are supported: * `idle_timeout_in_minutes` - (Optional) The idle timeout which should be used in minutes. Defaults to `4`. -* `public_ip_address_ids` - (Optional) A list of Public IP Address ID's which should be associated with the NAT Gateway resource. - * `public_ip_prefix_ids` - (Optional) A list of Public IP Prefix ID's which should be associated with the NAT Gateway resource. * `sku_name` - (Optional) The SKU which should be used. At this time the only supported value is `Standard`. Defaults to `Standard`. diff --git a/website/docs/r/nat_gateway_public_ip_association.html.markdown b/website/docs/r/nat_gateway_public_ip_association.html.markdown new file mode 100644 index 000000000000..ae5cf704eda2 --- /dev/null +++ b/website/docs/r/nat_gateway_public_ip_association.html.markdown @@ -0,0 +1,75 @@ +--- +subcategory: "Network" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_nat_gateway_public_ip_association" +description: |- + Manages the association between a Nat Gateway and a Public IP. + +--- + +# azurerm_nat_gateway_public_ip_association + +Manages the association between a Nat Gateway and a Public IP. + +## Example Usage + +```hcl +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West Europe" +} + +resource "azurerm_public_ip" "example" { + name = "example-PIP" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + allocation_method = "Static" + sku = "Standard" +} + +resource "azurerm_nat_gateway" "example" { + name = "example-NatGateway" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + sku_name = "Standard" +} + +resource "azurerm_nat_gateway_public_ip_association" "example" { + nat_gateway_id = azurerm_nat_gateway.example.id + public_ip_address_id = azurerm_public_ip.example.id +} +``` + +## Argument Reference + +The following arguments are supported: + +* `nat_gateway_id` - (Required) The ID of the Nat Gateway. Changing this forces a new resource to be created. + +* `public_ip_address_id` - (Required) The ID of the Public IP which this Nat Gateway which should be connected to. Changing this forces a new resource to be created. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The (Terraform specific) ID of the Association between the Nat Gateway and the Public IP. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: + +* `create` - (Defaults to 30 minutes) Used when creating the association between the Nat Gateway and the Public IP. +* `read` - (Defaults to 5 minutes) Used when retrieving the association between the Nat Gateway and the Public IP. +* `delete` - (Defaults to 30 minutes) Used when deleting the association between the Nat Gateway and the Public IP. + +## Import + +Associations between Nat Gateway and Public IP Addresses can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_nat_gateway_public_ip_association.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Network/natGateways/gateway1 +``` From e742586b1f1f7c769237dbf9607da0585862121f Mon Sep 17 00:00:00 2001 From: neil-yechenwei Date: Wed, 8 Apr 2020 15:58:56 +0800 Subject: [PATCH 3/9] Update code --- ...arm_nat_gateway_public_ip_association_test.go | 16 ++++++++-------- ...t_gateway_public_ip_association.html.markdown | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go b/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go index f6043efcde87..92d4eb522b35 100644 --- a/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go +++ b/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go @@ -262,10 +262,10 @@ func testAccAzureRMNatGatewayPublicIpAssociation_update(data acceptance.TestData %s resource "azurerm_nat_gateway" "test2" { - name = "acctest-NatGateway2-%d" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - sku_name = "Standard" + name = "acctest-NatGateway2-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku_name = "Standard" } resource "azurerm_nat_gateway_public_ip_association" "test" { @@ -295,10 +295,10 @@ resource "azurerm_public_ip" "test" { } resource "azurerm_nat_gateway" "test" { - name = "acctest-NatGateway-%d" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - sku_name = "Standard" + name = "acctest-NatGateway-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku_name = "Standard" } `, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) } diff --git a/website/docs/r/nat_gateway_public_ip_association.html.markdown b/website/docs/r/nat_gateway_public_ip_association.html.markdown index ae5cf704eda2..238523351371 100644 --- a/website/docs/r/nat_gateway_public_ip_association.html.markdown +++ b/website/docs/r/nat_gateway_public_ip_association.html.markdown @@ -32,10 +32,10 @@ resource "azurerm_public_ip" "example" { } resource "azurerm_nat_gateway" "example" { - name = "example-NatGateway" - location = azurerm_resource_group.example.location - resource_group_name = azurerm_resource_group.example.name - sku_name = "Standard" + name = "example-NatGateway" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + sku_name = "Standard" } resource "azurerm_nat_gateway_public_ip_association" "example" { From 08ad9356463030f7a964b927bf3623c8bb7d89b3 Mon Sep 17 00:00:00 2001 From: neil-yechenwei Date: Wed, 8 Apr 2020 16:46:58 +0800 Subject: [PATCH 4/9] Update code --- azurerm/internal/services/network/resource_arm_public_ip.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/azurerm/internal/services/network/resource_arm_public_ip.go b/azurerm/internal/services/network/resource_arm_public_ip.go index 86b162b370cb..8d3790516939 100644 --- a/azurerm/internal/services/network/resource_arm_public_ip.go +++ b/azurerm/internal/services/network/resource_arm_public_ip.go @@ -20,8 +20,6 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) -var publicIpResourceName = "azurerm_public_ip" - func resourceArmPublicIp() *schema.Resource { return &schema.Resource{ Create: resourceArmPublicIpCreateUpdate, From b3b977c10ab8a9ec919a10888d3d7c3bd5745e5e Mon Sep 17 00:00:00 2001 From: neil-yechenwei Date: Thu, 9 Apr 2020 13:59:26 +0800 Subject: [PATCH 5/9] Update code --- .../services/network/data_source_nat_gateway.go | 14 ++++++++++++++ .../services/network/resource_arm_nat_gateway.go | 16 ++++++++++++++++ .../tests/data_source_nat_gateway_test.go | 1 + .../tests/resource_arm_nat_gateway_test.go | 3 +++ website/docs/d/nat_gateway.html.markdown | 4 ++++ website/docs/r/nat_gateway.html.markdown | 3 +++ 6 files changed, 41 insertions(+) diff --git a/azurerm/internal/services/network/data_source_nat_gateway.go b/azurerm/internal/services/network/data_source_nat_gateway.go index 8b67afa3bd12..3338bad96245 100644 --- a/azurerm/internal/services/network/data_source_nat_gateway.go +++ b/azurerm/internal/services/network/data_source_nat_gateway.go @@ -35,6 +35,16 @@ func dataSourceArmNatGateway() *schema.Resource { Computed: true, }, + "public_ip_address_ids": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Deprecated: "Deprecated in favor of `azurerm_nat_gateway_public_ip_association`", + }, + "public_ip_prefix_ids": { Type: schema.TypeList, Optional: true, @@ -101,6 +111,10 @@ func dataSourceArmNatGatewayRead(d *schema.ResourceData, meta interface{}) error d.Set("idle_timeout_in_minutes", props.IdleTimeoutInMinutes) d.Set("resource_guid", props.ResourceGUID) + if err := d.Set("public_ip_address_ids", flattenArmNatGatewaySubResourceID(props.PublicIPAddresses)); err != nil { + return fmt.Errorf("Error setting `public_ip_address_ids`: %+v", err) + } + if err := d.Set("public_ip_prefix_ids", flattenArmNatGatewaySubResourceID(props.PublicIPPrefixes)); err != nil { return fmt.Errorf("Error setting `public_ip_prefix_ids`: %+v", err) } diff --git a/azurerm/internal/services/network/resource_arm_nat_gateway.go b/azurerm/internal/services/network/resource_arm_nat_gateway.go index cce46af4647b..ecf94c0bd77a 100644 --- a/azurerm/internal/services/network/resource_arm_nat_gateway.go +++ b/azurerm/internal/services/network/resource_arm_nat_gateway.go @@ -57,6 +57,16 @@ func resourceArmNatGateway() *schema.Resource { ValidateFunc: validation.IntBetween(4, 120), }, + "public_ip_address_ids": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: azure.ValidateResourceID, + }, + Deprecated: "Deprecated in favor of `azurerm_nat_gateway_public_ip_association`", + }, + "public_ip_prefix_ids": { Type: schema.TypeSet, Optional: true, @@ -109,6 +119,7 @@ func resourceArmNatGatewayCreateUpdate(d *schema.ResourceData, meta interface{}) location := azure.NormalizeLocation(d.Get("location").(string)) idleTimeoutInMinutes := d.Get("idle_timeout_in_minutes").(int) + publicIpAddressIds := d.Get("public_ip_address_ids").(*schema.Set).List() publicIpPrefixIds := d.Get("public_ip_prefix_ids").(*schema.Set).List() skuName := d.Get("sku_name").(string) zones := d.Get("zones").([]interface{}) @@ -118,6 +129,7 @@ func resourceArmNatGatewayCreateUpdate(d *schema.ResourceData, meta interface{}) Location: utils.String(location), NatGatewayPropertiesFormat: &network.NatGatewayPropertiesFormat{ IdleTimeoutInMinutes: utils.Int32(int32(idleTimeoutInMinutes)), + PublicIPAddresses: expandArmNatGatewaySubResourceID(publicIpAddressIds), PublicIPPrefixes: expandArmNatGatewaySubResourceID(publicIpPrefixIds), }, Sku: &network.NatGatewaySku{ @@ -180,6 +192,10 @@ func resourceArmNatGatewayRead(d *schema.ResourceData, meta interface{}) error { d.Set("idle_timeout_in_minutes", props.IdleTimeoutInMinutes) d.Set("resource_guid", props.ResourceGUID) + if err := d.Set("public_ip_address_ids", flattenArmNatGatewaySubResourceID(props.PublicIPAddresses)); err != nil { + return fmt.Errorf("Error setting `public_ip_address_ids`: %+v", err) + } + if err := d.Set("public_ip_prefix_ids", flattenArmNatGatewaySubResourceID(props.PublicIPPrefixes)); err != nil { return fmt.Errorf("Error setting `public_ip_prefix_ids`: %+v", err) } diff --git a/azurerm/internal/services/network/tests/data_source_nat_gateway_test.go b/azurerm/internal/services/network/tests/data_source_nat_gateway_test.go index 33799be696c3..c9607ed50fae 100644 --- a/azurerm/internal/services/network/tests/data_source_nat_gateway_test.go +++ b/azurerm/internal/services/network/tests/data_source_nat_gateway_test.go @@ -35,6 +35,7 @@ func TestAccDataSourceAzureRMNatGateway_complete(t *testing.T) { { Config: testAccDataSourceNatGateway_complete(data), Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(data.ResourceName, "public_ip_address_ids.#", "1"), resource.TestCheckResourceAttr(data.ResourceName, "public_ip_prefix_ids.#", "1"), resource.TestCheckResourceAttr(data.ResourceName, "sku_name", "Standard"), resource.TestCheckResourceAttr(data.ResourceName, "idle_timeout_in_minutes", "10"), diff --git a/azurerm/internal/services/network/tests/resource_arm_nat_gateway_test.go b/azurerm/internal/services/network/tests/resource_arm_nat_gateway_test.go index 8f3c2f8b290e..4da55448d34c 100644 --- a/azurerm/internal/services/network/tests/resource_arm_nat_gateway_test.go +++ b/azurerm/internal/services/network/tests/resource_arm_nat_gateway_test.go @@ -42,6 +42,7 @@ func TestAccAzureRMNatGateway_complete(t *testing.T) { Config: testAccAzureRMNatGateway_complete(data), Check: resource.ComposeTestCheckFunc( testCheckAzureRMNatGatewayExists(data.ResourceName), + resource.TestCheckResourceAttr(data.ResourceName, "public_ip_address_ids.#", "1"), resource.TestCheckResourceAttr(data.ResourceName, "public_ip_prefix_ids.#", "1"), resource.TestCheckResourceAttr(data.ResourceName, "sku_name", "Standard"), resource.TestCheckResourceAttr(data.ResourceName, "idle_timeout_in_minutes", "10"), @@ -71,6 +72,7 @@ func TestAccAzureRMNatGateway_update(t *testing.T) { Config: testAccAzureRMNatGateway_complete(data), Check: resource.ComposeTestCheckFunc( testCheckAzureRMNatGatewayExists(data.ResourceName), + resource.TestCheckResourceAttr(data.ResourceName, "public_ip_address_ids.#", "1"), resource.TestCheckResourceAttr(data.ResourceName, "public_ip_prefix_ids.#", "1"), resource.TestCheckResourceAttr(data.ResourceName, "sku_name", "Standard"), resource.TestCheckResourceAttr(data.ResourceName, "idle_timeout_in_minutes", "10"), @@ -183,6 +185,7 @@ resource "azurerm_nat_gateway" "test" { name = "acctestnatGateway-%d" location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name + public_ip_address_ids = [azurerm_public_ip.test.id] public_ip_prefix_ids = [azurerm_public_ip_prefix.test.id] sku_name = "Standard" idle_timeout_in_minutes = 10 diff --git a/website/docs/d/nat_gateway.html.markdown b/website/docs/d/nat_gateway.html.markdown index e2eae8ca0cb5..74c9a51fd54a 100644 --- a/website/docs/d/nat_gateway.html.markdown +++ b/website/docs/d/nat_gateway.html.markdown @@ -28,6 +28,8 @@ The following attributes are exported: * `idle_timeout_in_minutes` - The idle timeout in minutes which is used for the NAT Gateway. +* `public_ip_address_ids` - A list of existing Public IP Address resource IDs which the NAT Gateway is using. + * `public_ip_prefix_ids` - A list of existing Public IP Prefix resource IDs which the NAT Gateway is using. * `resource_guid` - The Resource GUID of the NAT Gateway. @@ -38,6 +40,8 @@ The following attributes are exported: * `zones` - A list of Availability Zones which the NAT Gateway exists in. +~> **NOTE:** The field `public_ip_address_ids` has been deprecated in favor of `azurerm_nat_gateway_public_ip_association`. + ## Timeouts The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: diff --git a/website/docs/r/nat_gateway.html.markdown b/website/docs/r/nat_gateway.html.markdown index 2579678b7e7b..aa25c8e70737 100644 --- a/website/docs/r/nat_gateway.html.markdown +++ b/website/docs/r/nat_gateway.html.markdown @@ -40,6 +40,7 @@ resource "azurerm_nat_gateway" "example" { name = "nat-Gateway" location = azurerm_resource_group.example.location resource_group_name = azurerm_resource_group.example.name + public_ip_address_ids = [azurerm_public_ip.example.id] public_ip_prefix_ids = [azurerm_public_ip_prefix.example.id] sku_name = "Standard" idle_timeout_in_minutes = 10 @@ -59,6 +60,8 @@ The following arguments are supported: * `idle_timeout_in_minutes` - (Optional) The idle timeout which should be used in minutes. Defaults to `4`. +* `public_ip_address_ids` - (Optional / **Deprecated in favour of `azurerm_nat_gateway_public_ip_association`**) A list of Public IP Address ID's which should be associated with the NAT Gateway resource. + * `public_ip_prefix_ids` - (Optional) A list of Public IP Prefix ID's which should be associated with the NAT Gateway resource. * `sku_name` - (Optional) The SKU which should be used. At this time the only supported value is `Standard`. Defaults to `Standard`. From 31851d2a6020aa76d1c346a05fe7659397606a1e Mon Sep 17 00:00:00 2001 From: neil-yechenwei Date: Fri, 10 Apr 2020 17:02:49 +0800 Subject: [PATCH 6/9] Update code --- .../network/resource_arm_nat_gateway.go | 3 +- ...e_arm_nat_gateway_public_ip_association.go | 27 +++++++--------- ..._nat_gateway_public_ip_association_test.go | 32 ++++++------------- .../services/network/validate/nat_gateway.go | 22 +++++++++++++ 4 files changed, 45 insertions(+), 39 deletions(-) create mode 100644 azurerm/internal/services/network/validate/nat_gateway.go diff --git a/azurerm/internal/services/network/resource_arm_nat_gateway.go b/azurerm/internal/services/network/resource_arm_nat_gateway.go index ecf94c0bd77a..bdab4b2420cc 100644 --- a/azurerm/internal/services/network/resource_arm_nat_gateway.go +++ b/azurerm/internal/services/network/resource_arm_nat_gateway.go @@ -60,11 +60,12 @@ func resourceArmNatGateway() *schema.Resource { "public_ip_address_ids": { Type: schema.TypeSet, Optional: true, + Computed: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: azure.ValidateResourceID, }, - Deprecated: "Deprecated in favor of `azurerm_nat_gateway_public_ip_association`", + Deprecated: "Deprecated in favor of `azurerm_nat_gateway_public_ip_association`. The dependency relation between `azurerm_nat_gateway` and `azurerm_public_ip` isn't detected by implicit dependency. So `azurerm_nat_gateway_public_ip_association` is added to resolve this issue.", }, "public_ip_prefix_ids": { diff --git a/azurerm/internal/services/network/resource_arm_nat_gateway_public_ip_association.go b/azurerm/internal/services/network/resource_arm_nat_gateway_public_ip_association.go index f4c1a9fc62b5..a6336814ac58 100644 --- a/azurerm/internal/services/network/resource_arm_nat_gateway_public_ip_association.go +++ b/azurerm/internal/services/network/resource_arm_nat_gateway_public_ip_association.go @@ -10,9 +10,10 @@ import ( "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/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/network/parse" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/network/validate" + azSchema "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -23,9 +24,10 @@ func resourceArmNatGatewayPublicIpAssociation() *schema.Resource { Read: resourceArmNatGatewayPublicIpAssociationRead, Delete: resourceArmNatGatewayPublicIpAssociationDelete, - Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, - }, + Importer: azSchema.ValidateResourceIDPriorToImport(func(id string) error { + _, err := parse.NatGatewayID(id) + return err + }), Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(30 * time.Minute), @@ -38,7 +40,7 @@ func resourceArmNatGatewayPublicIpAssociation() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: azure.ValidateResourceID, + ValidateFunc: validate.NatGatewayID, }, "public_ip_address_id": { @@ -70,7 +72,7 @@ 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) } @@ -80,11 +82,7 @@ func resourceArmNatGatewayPublicIpAssociationCreate(d *schema.ResourceData, meta for _, existingPublicIPAddress := range *natGateway.PublicIPAddresses { if existingPublicIPAddress.ID != nil { if *existingPublicIPAddress.ID == publicIpAddressId { - if features.ShouldResourcesBeImported() { - return tf.ImportAsExistsError("azurerm_nat_gateway_public_ip_association", *natGateway.ID) - } - - continue + return tf.ImportAsExistsError("azurerm_nat_gateway_public_ip_association", *natGateway.ID) } publicIpAddresses = append(publicIpAddresses, existingPublicIPAddress) @@ -92,10 +90,9 @@ func resourceArmNatGatewayPublicIpAssociationCreate(d *schema.ResourceData, meta } } - publicIpAddress := network.SubResource{ + publicIpAddresses = append(publicIpAddresses, network.SubResource{ ID: utils.String(publicIpAddressId), - } - publicIpAddresses = append(publicIpAddresses, publicIpAddress) + }) natGateway.PublicIPAddresses = &publicIpAddresses future, err := client.CreateOrUpdate(ctx, parsedNatGatewayId.ResourceGroup, parsedNatGatewayId.Name, natGateway) @@ -165,7 +162,7 @@ func resourceArmNatGatewayPublicIpAssociationDelete(d *schema.ResourceData, meta natGateway, err := client.Get(ctx, id.ResourceGroup, id.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.Name, id.ResourceGroup) } return fmt.Errorf("failed to retrieve Nat Gateway %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) diff --git a/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go b/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go index 92d4eb522b35..b6d3bf02ea4a 100644 --- a/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go +++ b/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go @@ -9,7 +9,6 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/network/parse" ) @@ -27,22 +26,17 @@ func TestAccAzureRMNatGatewayPublicIpAssociation_basic(t *testing.T) { testCheckAzureRMNatGatewayPublicIpAssociationExists(data.ResourceName), ), }, + // `public_ip_address_id` cannot be retrieved in read function while importing. data.ImportStep("public_ip_address_id"), }, }) } func TestAccAzureRMNatGatewayPublicIpAssociation_requiresImport(t *testing.T) { - if !features.ShouldResourcesBeImported() { - t.Skip("Skipping since resources aren't required to be imported") - return - } - data := acceptance.BuildTestData(t, "azurerm_nat_gateway_public_ip_association", "test") resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acceptance.PreCheck(t) }, - Providers: acceptance.SupportedProviders, - // intentional as this is a Virtual Resource + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, CheckDestroy: testCheckAzureRMNatGatewayDestroy, Steps: []resource.TestStep{ { @@ -59,9 +53,8 @@ func TestAccAzureRMNatGatewayPublicIpAssociation_requiresImport(t *testing.T) { func TestAccAzureRMNatGatewayPublicIpAssociation_complete(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_nat_gateway_public_ip_association", "test") resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acceptance.PreCheck(t) }, - Providers: acceptance.SupportedProviders, - // intentional as this is a Virtual Resource + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, CheckDestroy: testCheckAzureRMNatGatewayDestroy, Steps: []resource.TestStep{ { @@ -78,9 +71,8 @@ func TestAccAzureRMNatGatewayPublicIpAssociation_complete(t *testing.T) { func TestAccAzureRMNatGatewayPublicIpAssociation_update(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_nat_gateway_public_ip_association", "test") resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acceptance.PreCheck(t) }, - Providers: acceptance.SupportedProviders, - // intentional as this is a Virtual Resource + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, CheckDestroy: testCheckAzureRMNatGatewayDestroy, Steps: []resource.TestStep{ { @@ -156,21 +148,15 @@ func testCheckAzureRMNatGatewayPublicIpAssociationExists(resourceName string) re return fmt.Errorf("failed to retrieve Nat Gateway %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } - found := false if publicIpAddresses := resp.PublicIPAddresses; publicIpAddresses != nil { for _, publicIpAddress := range *publicIpAddresses { if *publicIpAddress.ID == publicIpAddressId { - found = true - break + return nil } } } - if !found { - return fmt.Errorf("Association between Nat Gateway %q and Public Ip %q was not found!", id.Name, publicIpAddressId) - } - - return nil + return fmt.Errorf("Association between Nat Gateway %q and Public Ip %q was not found.", id.Name, publicIpAddressId) } } diff --git a/azurerm/internal/services/network/validate/nat_gateway.go b/azurerm/internal/services/network/validate/nat_gateway.go new file mode 100644 index 000000000000..7ec9cdd42e6f --- /dev/null +++ b/azurerm/internal/services/network/validate/nat_gateway.go @@ -0,0 +1,22 @@ +package validate + +import ( + "fmt" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/network/parse" +) + +func NatGatewayID(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be string", k)) + return + } + + if _, err := parse.NatGatewayID(v); err != nil { + errors = append(errors, fmt.Errorf("Can not parse %q as a resource id: %v", k, err)) + return + } + + return warnings, errors +} From db136866af3117f69ef9242c9ed83d610d3ebe80 Mon Sep 17 00:00:00 2001 From: neil-yechenwei Date: Mon, 13 Apr 2020 17:23:09 +0800 Subject: [PATCH 7/9] Update code --- .../resource_arm_nat_gateway_public_ip_association_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go b/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go index b6d3bf02ea4a..1038584175c0 100644 --- a/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go +++ b/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go @@ -37,6 +37,7 @@ func TestAccAzureRMNatGatewayPublicIpAssociation_requiresImport(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, Providers: acceptance.SupportedProviders, + // intentional as this is a Virtual Resource CheckDestroy: testCheckAzureRMNatGatewayDestroy, Steps: []resource.TestStep{ { @@ -55,6 +56,7 @@ func TestAccAzureRMNatGatewayPublicIpAssociation_complete(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, Providers: acceptance.SupportedProviders, + // intentional as this is a Virtual Resource CheckDestroy: testCheckAzureRMNatGatewayDestroy, Steps: []resource.TestStep{ { @@ -73,6 +75,7 @@ func TestAccAzureRMNatGatewayPublicIpAssociation_update(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, Providers: acceptance.SupportedProviders, + // intentional as this is a Virtual Resource CheckDestroy: testCheckAzureRMNatGatewayDestroy, Steps: []resource.TestStep{ { From c1d2d4f1fa671f59c4524aa66de18137f3308bb2 Mon Sep 17 00:00:00 2001 From: neil-yechenwei Date: Mon, 13 Apr 2020 17:38:16 +0800 Subject: [PATCH 8/9] Update code --- ...rce_arm_nat_gateway_public_ip_association_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go b/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go index 1038584175c0..c90846c39293 100644 --- a/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go +++ b/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go @@ -35,8 +35,8 @@ func TestAccAzureRMNatGatewayPublicIpAssociation_basic(t *testing.T) { func TestAccAzureRMNatGatewayPublicIpAssociation_requiresImport(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_nat_gateway_public_ip_association", "test") resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acceptance.PreCheck(t) }, - Providers: acceptance.SupportedProviders, + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, // intentional as this is a Virtual Resource CheckDestroy: testCheckAzureRMNatGatewayDestroy, Steps: []resource.TestStep{ @@ -54,8 +54,8 @@ func TestAccAzureRMNatGatewayPublicIpAssociation_requiresImport(t *testing.T) { func TestAccAzureRMNatGatewayPublicIpAssociation_complete(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_nat_gateway_public_ip_association", "test") resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acceptance.PreCheck(t) }, - Providers: acceptance.SupportedProviders, + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, // intentional as this is a Virtual Resource CheckDestroy: testCheckAzureRMNatGatewayDestroy, Steps: []resource.TestStep{ @@ -73,8 +73,8 @@ func TestAccAzureRMNatGatewayPublicIpAssociation_complete(t *testing.T) { func TestAccAzureRMNatGatewayPublicIpAssociation_update(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_nat_gateway_public_ip_association", "test") resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acceptance.PreCheck(t) }, - Providers: acceptance.SupportedProviders, + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, // intentional as this is a Virtual Resource CheckDestroy: testCheckAzureRMNatGatewayDestroy, Steps: []resource.TestStep{ From 2fc33aa08ad82b1b35a713b35d3fed0e180c6772 Mon Sep 17 00:00:00 2001 From: neil-yechenwei Date: Tue, 26 May 2020 08:50:59 +0800 Subject: [PATCH 9/9] Update code --- azurerm/internal/services/network/nat_gateway_data_source.go | 1 - ...ciation.go => nat_gateway_public_ip_association_resource.go} | 2 +- ...st.go => nat_gateway_public_ip_association_resource_test.go} | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) rename azurerm/internal/services/network/{resource_arm_nat_gateway_public_ip_association.go => nat_gateway_public_ip_association_resource.go} (98%) rename azurerm/internal/services/network/tests/{resource_arm_nat_gateway_public_ip_association_test.go => nat_gateway_public_ip_association_resource_test.go} (99%) diff --git a/azurerm/internal/services/network/nat_gateway_data_source.go b/azurerm/internal/services/network/nat_gateway_data_source.go index 3338bad96245..2b9445e36b96 100644 --- a/azurerm/internal/services/network/nat_gateway_data_source.go +++ b/azurerm/internal/services/network/nat_gateway_data_source.go @@ -42,7 +42,6 @@ func dataSourceArmNatGateway() *schema.Resource { Elem: &schema.Schema{ Type: schema.TypeString, }, - Deprecated: "Deprecated in favor of `azurerm_nat_gateway_public_ip_association`", }, "public_ip_prefix_ids": { diff --git a/azurerm/internal/services/network/resource_arm_nat_gateway_public_ip_association.go b/azurerm/internal/services/network/nat_gateway_public_ip_association_resource.go similarity index 98% rename from azurerm/internal/services/network/resource_arm_nat_gateway_public_ip_association.go rename to azurerm/internal/services/network/nat_gateway_public_ip_association_resource.go index a6336814ac58..e1a5e229ce27 100644 --- a/azurerm/internal/services/network/resource_arm_nat_gateway_public_ip_association.go +++ b/azurerm/internal/services/network/nat_gateway_public_ip_association_resource.go @@ -5,7 +5,7 @@ import ( "log" "time" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-09-01/network" + "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" diff --git a/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go b/azurerm/internal/services/network/tests/nat_gateway_public_ip_association_resource_test.go similarity index 99% rename from azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go rename to azurerm/internal/services/network/tests/nat_gateway_public_ip_association_resource_test.go index c90846c39293..39aa699e72ce 100644 --- a/azurerm/internal/services/network/tests/resource_arm_nat_gateway_public_ip_association_test.go +++ b/azurerm/internal/services/network/tests/nat_gateway_public_ip_association_resource_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-09-01/network" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2020-03-01/network" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance"