diff --git a/azurerm/internal/services/network/nat_gateway_public_ip_prefix_association_resource.go b/azurerm/internal/services/network/nat_gateway_public_ip_prefix_association_resource.go new file mode 100644 index 000000000000..796f1df260ff --- /dev/null +++ b/azurerm/internal/services/network/nat_gateway_public_ip_prefix_association_resource.go @@ -0,0 +1,218 @@ +package network + +import ( + "fmt" + "log" + "strings" + "time" + + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2020-11-01/network" + "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" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/network/parse" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/network/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/pluginsdk" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceNATGatewayPublicIpPrefixAssociation() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Create: resourceNATGatewayPublicIpPrefixAssociationCreate, + Read: resourceNATGatewayPublicIpPrefixAssociationRead, + Delete: resourceNATGatewayPublicIpPrefixAssociationDelete, + + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := parse.NatGatewayPublicIPPrefixAssociationID(id) + return err + }), + + Timeouts: &pluginsdk.ResourceTimeout{ + Create: pluginsdk.DefaultTimeout(30 * time.Minute), + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + Delete: pluginsdk.DefaultTimeout(30 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "nat_gateway_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NatGatewayID, + }, + + "public_ip_prefix_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.PublicIpPrefixID, + }, + }, + } +} + +func resourceNATGatewayPublicIpPrefixAssociationCreate(d *pluginsdk.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 Prefix Association creation.") + natGatewayId := d.Get("nat_gateway_id").(string) + publicIpPrefixId := d.Get("public_ip_prefix_id").(string) + parsedNatGatewayId, err := parse.NatGatewayID(natGatewayId) + if err != nil { + return err + } + + locks.ByName(parsedNatGatewayId.Name, natGatewayResourceName) + defer locks.UnlockByName(parsedNatGatewayId.Name, natGatewayResourceName) + + 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("failed to retrieve NAT Gateway %q (Resource Group %q): %+v", parsedNatGatewayId.Name, parsedNatGatewayId.ResourceGroup, err) + } + + id := fmt.Sprintf("%s|%s", *natGateway.ID, publicIpPrefixId) + publicIpPrefixes := make([]network.SubResource, 0) + if natGateway.PublicIPPrefixes != nil { + for _, existingPublicIPPrefix := range *natGateway.PublicIPPrefixes { + if existingPublicIPPrefix.ID == nil { + continue + } + + if strings.EqualFold(*existingPublicIPPrefix.ID, publicIpPrefixId) { + return tf.ImportAsExistsError("azurerm_nat_gateway_public_ip_prefix_association", id) + } + + publicIpPrefixes = append(publicIpPrefixes, existingPublicIPPrefix) + } + } + + publicIpPrefixes = append(publicIpPrefixes, network.SubResource{ + ID: utils.String(publicIpPrefixId), + }) + natGateway.PublicIPPrefixes = &publicIpPrefixes + + future, err := client.CreateOrUpdate(ctx, parsedNatGatewayId.ResourceGroup, parsedNatGatewayId.Name, natGateway) + if err != nil { + return fmt.Errorf("failed to update Public IP Prefix 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 Prefix Association for NAT Gateway %q (Resource Group %q): %+v", parsedNatGatewayId.Name, parsedNatGatewayId.ResourceGroup, err) + } + + d.SetId(id) + + return resourceNATGatewayPublicIpPrefixAssociationRead(d, meta) +} + +func resourceNATGatewayPublicIpPrefixAssociationRead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Network.NatGatewayClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.NatGatewayPublicIPPrefixAssociationID(d.Id()) + if err != nil { + return err + } + + 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.NatGateway.Name, id.NatGateway.ResourceGroup) + d.SetId("") + return nil + } + 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.PublicIPPrefixes == nil { + log.Printf("[DEBUG] NAT Gateway %q (Resource Group %q) doesn't have any Public IP Prefixes - removing from state!", id.NatGateway.Name, id.NatGateway.ResourceGroup) + d.SetId("") + return nil + } + + publicIPPrefixId := "" + for _, pipp := range *props.PublicIPPrefixes { + if pipp.ID == nil { + continue + } + + if strings.EqualFold(*pipp.ID, id.PublicIPPrefixID) { + publicIPPrefixId = *pipp.ID + break + } + } + + if publicIPPrefixId == "" { + log.Printf("[DEBUG] Association between NAT Gateway %q (Resource Group %q) and Public IP Prefix %q was not found - removing from state", id.NatGateway.Name, id.NatGateway.ResourceGroup, id.PublicIPPrefixID) + d.SetId("") + return nil + } + + d.Set("nat_gateway_id", natGateway.ID) + d.Set("public_ip_prefix_id", publicIPPrefixId) + + return nil +} + +func resourceNATGatewayPublicIpPrefixAssociationDelete(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Network.NatGatewayClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.NatGatewayPublicIPPrefixAssociationID(d.Id()) + if err != nil { + return err + } + + locks.ByName(id.NatGateway.Name, natGatewayResourceName) + defer locks.UnlockByName(id.NatGateway.Name, natGatewayResourceName) + + 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.NatGateway.Name, id.NatGateway.ResourceGroup) + } + + 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) + } + + publicIpPrefixes := make([]network.SubResource, 0) + if publicIPPrefixes := natGateway.NatGatewayPropertiesFormat.PublicIPPrefixes; publicIPPrefixes != nil { + for _, publicIPPrefix := range *publicIPPrefixes { + if publicIPPrefix.ID == nil { + continue + } + + if !strings.EqualFold(*publicIPPrefix.ID, id.PublicIPPrefixID) { + publicIpPrefixes = append(publicIpPrefixes, publicIPPrefix) + } + } + } + natGateway.NatGatewayPropertiesFormat.PublicIPPrefixes = &publicIpPrefixes + + future, err := client.CreateOrUpdate(ctx, id.NatGateway.ResourceGroup, id.NatGateway.Name, natGateway) + if err != nil { + return fmt.Errorf("removing association between NAT Gateway %q (Resource Group %q) and Public IP Prefix %q: %+v", id.NatGateway.Name, id.NatGateway.ResourceGroup, id.PublicIPPrefixID, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for association between Public IP Prefix ID %q for NAT Gateway %q (Resource Group %q) to be removed: %+v", id.PublicIPPrefixID, id.NatGateway.Name, id.NatGateway.ResourceGroup, err) + } + + return nil +} diff --git a/azurerm/internal/services/network/nat_gateway_public_ip_prefix_association_resource_test.go b/azurerm/internal/services/network/nat_gateway_public_ip_prefix_association_resource_test.go new file mode 100644 index 000000000000..1ac0f00601a3 --- /dev/null +++ b/azurerm/internal/services/network/nat_gateway_public_ip_prefix_association_resource_test.go @@ -0,0 +1,202 @@ +package network_test + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2020-11-01/network" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance/check" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/network/parse" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/pluginsdk" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +type NatGatewayPublicIpPrefixAssociationResource struct { +} + +func TestAccNatGatewayPublicIpPrefixAssociation_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_nat_gateway_public_ip_prefix_association", "test") + r := NatGatewayPublicIpPrefixAssociationResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + // intentional as this is a Virtual Resource + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccNatGatewayPublicIpPrefixAssociation_updateNatGateway(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_nat_gateway_public_ip_prefix_association", "test") + r := NatGatewayPublicIpPrefixAssociationResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + // intentional as this is a Virtual Resource + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.updateNatGateway(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccNatGatewayPublicIpPrefixAssociation_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_nat_gateway_public_ip_prefix_association", "test") + r := NatGatewayPublicIpPrefixAssociationResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + // intentional as this is a Virtual Resource + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccNatGatewayPublicIpPrefixAssociation_deleted(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_nat_gateway_public_ip_prefix_association", "test") + r := NatGatewayPublicIpPrefixAssociationResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + // intentional as this is a Virtual Resource + data.DisappearsStep(acceptance.DisappearsStepData{ + Config: r.basic, + TestResource: r, + }), + }) +} + +func (t NatGatewayPublicIpPrefixAssociationResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := parse.NatGatewayPublicIPPrefixAssociationID(state.ID) + if err != nil { + return nil, err + } + + resp, err := clients.Network.NatGatewayClient.Get(ctx, id.NatGateway.ResourceGroup, id.NatGateway.Name, "") + if err != nil { + return nil, fmt.Errorf("reading Nat Gateway Public IP Prefix Association (%s): %+v", id, err) + } + + return utils.Bool(resp.ID != nil), nil +} + +func (NatGatewayPublicIpPrefixAssociationResource) Destroy(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := parse.NatGatewayPublicIPPrefixAssociationID(state.ID) + if err != nil { + return nil, err + } + + resp, err := client.Network.NatGatewayClient.Get(ctx, id.NatGateway.ResourceGroup, id.NatGateway.Name, "") + if err != nil { + return nil, fmt.Errorf("reading Nat Gateway Public IP Prefix Association (%s): %+v", id, err) + } + + updatedPrefixes := make([]network.SubResource, 0) + if publicIpPrefixes := resp.PublicIPPrefixes; publicIpPrefixes != nil { + for _, publicIpPrefix := range *publicIpPrefixes { + if !strings.EqualFold(*publicIpPrefix.ID, id.PublicIPPrefixID) { + updatedPrefixes = append(updatedPrefixes, publicIpPrefix) + } + } + } + resp.PublicIPPrefixes = &updatedPrefixes + + future, err := client.Network.NatGatewayClient.CreateOrUpdate(ctx, id.NatGateway.ResourceGroup, id.NatGateway.Name, resp) + if err != nil { + return nil, fmt.Errorf("failed to remove Nat Gateway Public IP Prefix Association for Nat Gateway %q: %+v", id, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Network.NatGatewayClient.Client); err != nil { + return nil, fmt.Errorf("failed to wait for removal of Nat Gateway Public IP Prefix Association for Nat Gateway %q: %+v", id, err) + } + + return utils.Bool(true), nil +} + +func (r NatGatewayPublicIpPrefixAssociationResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +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" +} + +resource "azurerm_nat_gateway_public_ip_prefix_association" "test" { + nat_gateway_id = azurerm_nat_gateway.test.id + public_ip_prefix_id = azurerm_public_ip_prefix.test.id +} +`, r.template(data), data.RandomInteger) +} + +func (r NatGatewayPublicIpPrefixAssociationResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_nat_gateway_public_ip_prefix_association" "import" { + nat_gateway_id = azurerm_nat_gateway_public_ip_prefix_association.test.nat_gateway_id + public_ip_prefix_id = azurerm_nat_gateway_public_ip_prefix_association.test.public_ip_prefix_id +} +`, r.basic(data)) +} + +func (r NatGatewayPublicIpPrefixAssociationResource) updateNatGateway(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +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" + tags = { + Hello = "World" + } +} + +resource "azurerm_nat_gateway_public_ip_prefix_association" "test" { + nat_gateway_id = azurerm_nat_gateway.test.id + public_ip_prefix_id = azurerm_public_ip_prefix.test.id +} +`, r.template(data), data.RandomInteger) +} + +func (NatGatewayPublicIpPrefixAssociationResource) 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_prefix" "test" { + name = "acctestpublicIPPrefix-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + prefix_length = 30 + zones = ["1"] +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} diff --git a/azurerm/internal/services/network/nat_gateway_resource.go b/azurerm/internal/services/network/nat_gateway_resource.go index 0ed8d0122884..0b52ed078e47 100644 --- a/azurerm/internal/services/network/nat_gateway_resource.go +++ b/azurerm/internal/services/network/nat_gateway_resource.go @@ -67,16 +67,19 @@ func resourceNatGateway() *pluginsdk.Resource { ValidateFunc: azure.ValidateResourceID, }, // TODO: remove in 3.0 - Deprecated: "Inline Public IP Address ID Deprecations have been deprecated in favour of the `azurerm_nat_gateway_public_ip_association` pluginsdk. This field will be removed in the next major version of the Azure Provider.", + Deprecated: "Inline Public IP Address ID Associations have been deprecated in favour of the `azurerm_nat_gateway_public_ip_association` pluginsdk. This field will be removed in the next major version of the Azure Provider.", }, "public_ip_prefix_ids": { Type: pluginsdk.TypeSet, Optional: true, + Computed: true, Elem: &pluginsdk.Schema{ Type: pluginsdk.TypeString, ValidateFunc: azure.ValidateResourceID, }, + // TODO: remove in 3.0 + Deprecated: "Inline Public IP Prefix ID Associations have been deprecated in favour of the `azurerm_nat_gateway_public_ip_prefix_association` pluginsdk. This field will be removed in the next major version of the Azure Provider.", }, "sku_name": { diff --git a/azurerm/internal/services/network/parse/nat_gateway_public_ip_prefix_association.go b/azurerm/internal/services/network/parse/nat_gateway_public_ip_prefix_association.go new file mode 100644 index 000000000000..7c73de21e8df --- /dev/null +++ b/azurerm/internal/services/network/parse/nat_gateway_public_ip_prefix_association.go @@ -0,0 +1,34 @@ +package parse + +import ( + "fmt" + "strings" +) + +type NatGatewayPublicIPPrefixAssociationId struct { + NatGateway NatGatewayId + PublicIPPrefixID string +} + +func NatGatewayPublicIPPrefixAssociationID(input string) (*NatGatewayPublicIPPrefixAssociationId, error) { + segments := strings.Split(input, "|") + if len(segments) != 2 { + return nil, fmt.Errorf("Expected an ID in the format `{natGatewayID}|{publicIPPrefixID} but got %q", input) + } + + natGatewayId, err := NatGatewayID(segments[0]) + if err != nil { + return nil, fmt.Errorf("parsing NAT Gateway ID %q: %+v", segments[0], err) + } + + // whilst we need the Resource ID, we may as well validate it + publicIPPrefix := segments[1] + if _, err := PublicIpPrefixID(publicIPPrefix); err != nil { + return nil, fmt.Errorf("parsing Public IP Address ID %q: %+v", publicIPPrefix, err) + } + + return &NatGatewayPublicIPPrefixAssociationId{ + NatGateway: *natGatewayId, + PublicIPPrefixID: publicIPPrefix, + }, nil +} diff --git a/azurerm/internal/services/network/parse/nat_gateway_public_ip_prefix_association_test.go b/azurerm/internal/services/network/parse/nat_gateway_public_ip_prefix_association_test.go new file mode 100644 index 000000000000..9fad24ab681e --- /dev/null +++ b/azurerm/internal/services/network/parse/nat_gateway_public_ip_prefix_association_test.go @@ -0,0 +1,78 @@ +package parse + +import ( + "testing" +) + +func TestNatGatewayPublicIPPrefixAssociationID(t *testing.T) { + testData := []struct { + Name string + Input string + Error bool + Expect *NatGatewayPublicIPPrefixAssociationId + }{ + { + Name: "Empty", + Input: "", + Error: true, + }, + { + Name: "One Segment", + Input: "hello", + Error: true, + }, + { + Name: "Two Segments Invalid ID's", + Input: "hello|world", + 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: true, + }, + { + Name: "Public IP Address ID", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/providers/Microsoft.Network/publicIPPrefixes/myPublicIPPrefix1", + Error: true, + }, + { + Name: "Nat Gateway / Public IP Association ID", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Network/natGateways/gateway1|/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/providers/Microsoft.Network/publicIPPrefixes/myPublicIPPrefix1", + Error: false, + Expect: &NatGatewayPublicIPPrefixAssociationId{ + NatGateway: NatGatewayId{ + Name: "gateway1", + ResourceGroup: "group1", + }, + PublicIPPrefixID: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/providers/Microsoft.Network/publicIPPrefixes/myPublicIPPrefix1", + }, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Name) + + actual, err := NatGatewayPublicIPPrefixAssociationID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expected a value but got an error: %s", err) + } + + if actual.NatGateway.Name != v.Expect.NatGateway.Name { + t.Fatalf("Expected %q but got %q for Name", v.Expect.NatGateway.Name, actual.NatGateway.Name) + } + + if actual.NatGateway.ResourceGroup != v.Expect.NatGateway.ResourceGroup { + t.Fatalf("Expected %q but got %q for Resource Group", v.Expect.NatGateway.ResourceGroup, actual.NatGateway.ResourceGroup) + } + } +} diff --git a/azurerm/internal/services/network/registration.go b/azurerm/internal/services/network/registration.go index b95094314050..f2021dca033b 100644 --- a/azurerm/internal/services/network/registration.go +++ b/azurerm/internal/services/network/registration.go @@ -79,6 +79,7 @@ func (r Registration) SupportedResources() map[string]*pluginsdk.Resource { "azurerm_private_link_service": resourcePrivateLinkService(), "azurerm_public_ip": resourcePublicIp(), "azurerm_nat_gateway_public_ip_association": resourceNATGatewayPublicIpAssociation(), + "azurerm_nat_gateway_public_ip_prefix_association": resourceNATGatewayPublicIpPrefixAssociation(), "azurerm_public_ip_prefix": resourcePublicIpPrefix(), "azurerm_network_security_group": resourceNetworkSecurityGroup(), "azurerm_network_security_rule": resourceNetworkSecurityRule(), diff --git a/website/docs/r/nat_gateway.html.markdown b/website/docs/r/nat_gateway.html.markdown index c0d701b1edb6..f35b03acf846 100644 --- a/website/docs/r/nat_gateway.html.markdown +++ b/website/docs/r/nat_gateway.html.markdown @@ -60,7 +60,7 @@ The following arguments are supported: * `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. +* `public_ip_prefix_ids` - (Optional) / **Deprecated in favour of `azurerm_nat_gateway_public_ip_prefix_association`**) 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_prefix_association.html.markdown b/website/docs/r/nat_gateway_public_ip_prefix_association.html.markdown new file mode 100644 index 000000000000..405b62ebe788 --- /dev/null +++ b/website/docs/r/nat_gateway_public_ip_prefix_association.html.markdown @@ -0,0 +1,73 @@ +--- +subcategory: "Network" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_nat_gateway_public_ip_prefix_association" +description: |- + Manages the association between a Nat Gateway and a Public IP Prefix. + +--- + +# azurerm_nat_gateway_public_ip_association + +Manages the association between a Nat Gateway and a Public IP Prefix. + +## Example Usage + +```hcl +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West Europe" +} + +resource "azurerm_public_ip_prefix" "example" { + name = "example" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + prefix_length = 30 + zones = ["1"] +} + +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_prefix_association" "example" { + nat_gateway_id = azurerm_nat_gateway.example.id + public_ip_prefix_id = azurerm_public_ip_prefix.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_prefix_id` - (Required) The ID of the Public IP Prefix 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 Prefix. + +## 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 Prefix. +* `read` - (Defaults to 5 minutes) Used when retrieving the association between the Nat Gateway and the Public IP Prefix. +* `delete` - (Defaults to 30 minutes) Used when deleting the association between the Nat Gateway and the Public IP Prefix. + +## Import + +Associations between Nat Gateway and Public IP Prefixes can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_nat_gateway_public_ip_prefix_association.example "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Network/natGateways/gateway1|/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/providers/Microsoft.Network/publicIPPrefixes/myPublicIpPrefix1" +``` + +-> **Note:** This is a Terraform Specific ID in the format `{natGatewayID}|{publicIPPrefixID}`