diff --git a/internal/services/network/nat_gateway_resource.go b/internal/services/network/nat_gateway_resource.go index d332ec3e9b5a..dc61cf8a3914 100644 --- a/internal/services/network/nat_gateway_resource.go +++ b/internal/services/network/nat_gateway_resource.go @@ -4,6 +4,7 @@ package network import ( + "context" "fmt" "log" "time" @@ -49,6 +50,16 @@ func resourceNatGateway() *pluginsdk.Resource { SchemaFunc: pluginsdk.GenerateIdentitySchema(&natgateways.NatGatewayId{}), }, + CustomizeDiff: func(_ context.Context, diff *schema.ResourceDiff, _ interface{}) error { + if diff.Get("sku_name").(string) == string(natgateways.NatGatewaySkuNameStandardVTwo) { + if !diff.GetRawConfig().AsValueMap()["zones"].IsNull() { + return fmt.Errorf("%s resources with `sku_name` set to `%s` are zone-redundant by default, Azure automatically deploys across all available zones. The `zones` argument must be omitted", natGatewayResourceName, natgateways.NatGatewaySkuNameStandardVTwo) + } + } + + return nil + }, + Schema: resourceNatGatewaySchema(), } } @@ -74,15 +85,26 @@ func resourceNatGatewaySchema() map[string]*pluginsdk.Schema { }, "sku_name": { - Type: pluginsdk.TypeString, - Optional: true, - Default: string(natgateways.NatGatewaySkuNameStandard), - ValidateFunc: validation.StringInSlice([]string{ - string(natgateways.NatGatewaySkuNameStandard), - }, false), + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + Default: string(natgateways.NatGatewaySkuNameStandard), + ValidateFunc: validation.StringInSlice(natgateways.PossibleValuesForNatGatewaySkuName(), false), }, - "zones": commonschema.ZonesMultipleOptionalForceNew(), + "zones": { + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + DiffSuppressOnRefresh: true, + DiffSuppressFunc: func(_, _, _ string, d *schema.ResourceData) bool { + return d.Get("sku_name").(string) == string(natgateways.NatGatewaySkuNameStandardVTwo) + }, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, "resource_guid": { Type: pluginsdk.TypeString, diff --git a/internal/services/network/nat_gateway_resource_test.go b/internal/services/network/nat_gateway_resource_test.go index cc0621b68d36..a53ff711d2b1 100644 --- a/internal/services/network/nat_gateway_resource_test.go +++ b/internal/services/network/nat_gateway_resource_test.go @@ -75,6 +75,21 @@ func TestAccNatGateway_update(t *testing.T) { }) } +func TestAccNatGateway_standardVTwo(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_nat_gateway", "test") + r := NatGatewayResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.standardVTwo(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func (t NatGatewayResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := natgateways.ParseNatGatewayID(state.ID) if err != nil { @@ -208,3 +223,23 @@ resource "azurerm_nat_gateway_public_ip_prefix_association" "test" { } `, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomInteger) } + +func (NatGatewayResource) standardVTwo(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-network-%d" + location = "%s" +} + +resource "azurerm_nat_gateway" "test" { + name = "acctestnatGateway-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku_name = "StandardV2" +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} diff --git a/internal/services/network/public_ip_data_source_test.go b/internal/services/network/public_ip_data_source_test.go index b3d5b8d1c724..374efdf6b81f 100644 --- a/internal/services/network/public_ip_data_source_test.go +++ b/internal/services/network/public_ip_data_source_test.go @@ -39,7 +39,7 @@ func TestAccDataSourcePublicIP_static(t *testing.T) { }) } -func TestAccDataSourcePublicIP_dynamic(t *testing.T) { +func TestAccDataSourcePublicIP_staticMinimal(t *testing.T) { data := acceptance.BuildTestData(t, "data.azurerm_public_ip", "test") r := PublicIPDataSource{} @@ -48,13 +48,13 @@ func TestAccDataSourcePublicIP_dynamic(t *testing.T) { data.DataSourceTest(t, []acceptance.TestStep{ { - Config: r.dynamic(data, "IPv4"), + Config: r.staticMinimal(data, "IPv4"), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).Key("name").HasValue(name), check.That(data.ResourceName).Key("resource_group_name").HasValue(resourceGroupName), check.That(data.ResourceName).Key("domain_name_label").HasValue(""), check.That(data.ResourceName).Key("fqdn").HasValue(""), - check.That(data.ResourceName).Key("ip_address").HasValue(""), + check.That(data.ResourceName).Key("ip_address").Exists(), check.That(data.ResourceName).Key("ip_version").HasValue("IPv4"), check.That(data.ResourceName).Key("tags.%").HasValue("1"), check.That(data.ResourceName).Key("tags.environment").HasValue("test"), @@ -100,7 +100,7 @@ data "azurerm_public_ip" "test" { `, resourceGroupName, data.Locations.Primary, name, data.RandomInteger) } -func (PublicIPDataSource) dynamic(data acceptance.TestData, ipVersion string) string { +func (PublicIPDataSource) staticMinimal(data acceptance.TestData, ipVersion string) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -115,8 +115,8 @@ resource "azurerm_public_ip" "test" { name = "acctestpublicip-%d" location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name - allocation_method = "Dynamic" - sku = "Basic" + allocation_method = "Static" + sku = "Standard" ip_version = "%s" diff --git a/internal/services/network/public_ip_prefix_resource.go b/internal/services/network/public_ip_prefix_resource.go index 6ab93c6d60b3..7a6dd8651dd6 100644 --- a/internal/services/network/public_ip_prefix_resource.go +++ b/internal/services/network/public_ip_prefix_resource.go @@ -4,8 +4,11 @@ package network import ( + "context" + "errors" "fmt" "log" + "strings" "time" "github.com/hashicorp/go-azure-helpers/lang/pointer" @@ -65,13 +68,11 @@ func resourcePublicIpPrefix() *pluginsdk.Resource { }, "sku": { - Type: pluginsdk.TypeString, - Optional: true, - ForceNew: true, - Default: string(publicipprefixes.PublicIPPrefixSkuNameStandard), - ValidateFunc: validation.StringInSlice([]string{ - string(publicipprefixes.PublicIPPrefixSkuNameStandard), - }, false), + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + Default: string(publicipprefixes.PublicIPPrefixSkuNameStandard), + ValidateFunc: validation.StringInSlice(publicipprefixes.PossibleValuesForPublicIPPrefixSkuName(), false), }, "sku_tier": { @@ -110,6 +111,17 @@ func resourcePublicIpPrefix() *pluginsdk.Resource { "tags": commonschema.Tags(), }, + + CustomizeDiff: pluginsdk.CustomDiffWithAll( + pluginsdk.CustomizeDiffShim(func(_ context.Context, d *pluginsdk.ResourceDiff, _ interface{}) error { + skuTier := d.Get("sku_tier").(string) + sku := d.Get("sku").(string) + if strings.EqualFold(skuTier, string(publicipprefixes.PublicIPPrefixSkuTierGlobal)) && !strings.EqualFold(sku, string(publicipprefixes.PublicIPPrefixSkuNameStandard)) { + return errors.New("`sku` must be set to `Standard` when `sku_tier` is set to `Global`") + } + return nil + }), + ), } } diff --git a/internal/services/network/public_ip_prefix_resource_test.go b/internal/services/network/public_ip_prefix_resource_test.go index 33d11e1cf248..ae14327c9a37 100644 --- a/internal/services/network/public_ip_prefix_resource_test.go +++ b/internal/services/network/public_ip_prefix_resource_test.go @@ -69,9 +69,10 @@ func TestAccPublicIpPrefix_globalTier(t *testing.T) { data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.sku_tier(data, string(publicipprefixes.PublicIPPrefixSkuTierGlobal)), + Config: r.sku_tier(data, string(publicipprefixes.PublicIPPrefixSkuNameStandard), string(publicipprefixes.PublicIPPrefixSkuTierGlobal)), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("sku").HasValue(string(publicipprefixes.PublicIPPrefixSkuNameStandard)), check.That(data.ResourceName).Key("sku_tier").HasValue(string(publicipprefixes.PublicIPPrefixSkuTierGlobal)), ), }, @@ -104,9 +105,10 @@ func TestAccPublicIpPrefix_regionalTier(t *testing.T) { data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.sku_tier(data, string(publicipprefixes.PublicIPPrefixSkuTierRegional)), + Config: r.sku_tier(data, string(publicipprefixes.PublicIPPrefixSkuNameStandard), string(publicipprefixes.PublicIPPrefixSkuTierRegional)), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("sku").HasValue(string(publicipprefixes.PublicIPPrefixSkuNameStandard)), check.That(data.ResourceName).Key("sku_tier").HasValue(string(publicipprefixes.PublicIPPrefixSkuTierRegional)), ), }, @@ -393,7 +395,7 @@ resource "azurerm_public_ip_prefix" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomInteger) } -func (PublicIpPrefixResource) sku_tier(data acceptance.TestData, tier string) string { +func (PublicIpPrefixResource) sku_tier(data acceptance.TestData, sku string, tier string) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -408,9 +410,10 @@ resource "azurerm_public_ip_prefix" "test" { resource_group_name = azurerm_resource_group.test.name name = "acctestpublicipprefix-%d" location = azurerm_resource_group.test.location + sku = "%s" sku_tier = "%s" } -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, tier) +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, sku, tier) } func (PublicIpPrefixResource) zonesSingle(data acceptance.TestData) string { diff --git a/internal/services/network/public_ip_resource.go b/internal/services/network/public_ip_resource.go index 12e36c7b0d6e..b400dd92a47d 100644 --- a/internal/services/network/public_ip_resource.go +++ b/internal/services/network/public_ip_resource.go @@ -5,6 +5,7 @@ package network import ( "context" + "errors" "fmt" "log" "strings" @@ -109,10 +110,7 @@ func resourcePublicIp() *pluginsdk.Resource { ForceNew: true, Default: string(publicipaddresses.PublicIPAddressSkuNameStandard), // https://azure.microsoft.com/en-us/updates/upgrade-to-standard-sku-public-ip-addresses-in-azure-by-30-september-2025-basic-sku-will-be-retired/ - ValidateFunc: validation.StringInSlice([]string{ - string(publicipaddresses.PublicIPAddressSkuNameBasic), - string(publicipaddresses.PublicIPAddressSkuNameStandard), - }, false), + ValidateFunc: validation.StringInSlice(publicipaddresses.PossibleValuesForPublicIPAddressSkuName(), false), }, "sku_tier": { @@ -182,6 +180,22 @@ func resourcePublicIp() *pluginsdk.Resource { }, CustomizeDiff: pluginsdk.CustomDiffWithAll( + pluginsdk.CustomizeDiffShim(func(_ context.Context, d *pluginsdk.ResourceDiff, _ interface{}) error { + sku := d.Get("sku").(string) + if strings.EqualFold(sku, string(publicipaddresses.PublicIPAddressSkuNameBasic)) && d.HasChanges("name", "resource_group_name", "location", "allocation_method", "edge_zone", "ip_version", "sku", "sku_tier", "public_ip_prefix_id", "ip_tags", "zones") { + return errors.New(publicIPBasicSkuCreateDeprecationMessage) + } + + return nil + }), + pluginsdk.CustomizeDiffShim(func(_ context.Context, d *pluginsdk.ResourceDiff, _ interface{}) error { + skuTier := d.Get("sku_tier").(string) + sku := d.Get("sku").(string) + if strings.EqualFold(skuTier, string(publicipaddresses.PublicIPAddressSkuTierGlobal)) && !strings.EqualFold(sku, string(publicipaddresses.PublicIPAddressSkuNameStandard)) { + return errors.New("`sku` must be set to `Standard` when `sku_tier` is set to `Global`") + } + return nil + }), pluginsdk.ForceNewIfChange("domain_name_label_scope", func(ctx context.Context, old, new, meta interface{}) bool { return old.(string) != "" || new.(string) == "" }), @@ -189,6 +203,8 @@ func resourcePublicIp() *pluginsdk.Resource { } } +const publicIPBasicSkuCreateDeprecationMessage = "creation of new `Basic` SKU public IP addresses is no longer permitted following its deprecation on March 31, 2025. This also affects `allocation_method` set to `Dynamic`, as it is only available with the `Basic` SKU. For more information, see https://azure.microsoft.com/updates/upgrade-to-standard-sku-public-ip-addresses-in-azure-by-30-september-2025-basic-sku-will-be-retired/" + func resourcePublicIpCreate(d *pluginsdk.ResourceData, meta interface{}) error { client := meta.(*clients.Client).Network.PublicIPAddresses subscriptionId := meta.(*clients.Client).Account.SubscriptionId @@ -213,9 +229,9 @@ func resourcePublicIpCreate(d *pluginsdk.ResourceData, meta interface{}) error { sku := d.Get("sku").(string) ipAllocationMethod := d.Get("allocation_method").(string) - if strings.EqualFold(sku, "standard") { - if !strings.EqualFold(ipAllocationMethod, "static") { - return fmt.Errorf("static IP allocation must be used when creating Standard SKU public IP addresses") + if strings.EqualFold(sku, string(publicipaddresses.PublicIPAddressSkuNameStandard)) || strings.EqualFold(sku, string(publicipaddresses.PublicIPAddressSkuNameStandardVTwo)) { + if !strings.EqualFold(ipAllocationMethod, string(publicipaddresses.IPAllocationMethodStatic)) { + return fmt.Errorf("`allocation_method` must be set to `Static` when `sku` is set to `Standard` or `StandardV2`") } } diff --git a/internal/services/network/public_ip_resource_test.go b/internal/services/network/public_ip_resource_test.go index d9c5bf2b63e9..f93f569135b0 100644 --- a/internal/services/network/public_ip_resource_test.go +++ b/internal/services/network/public_ip_resource_test.go @@ -21,13 +21,13 @@ import ( type PublicIpResource struct{} -func TestAccPublicIpStatic_basic(t *testing.T) { +func TestAccPublicIp_basic(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_public_ip", "test") r := PublicIpResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.static_basic(data), + Config: r.basic(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("ip_address").Exists(), @@ -40,13 +40,13 @@ func TestAccPublicIpStatic_basic(t *testing.T) { }) } -func TestAccPublicIpStatic_requiresImport(t *testing.T) { +func TestAccPublicIp_requiresImport(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_public_ip", "test") r := PublicIpResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.static_basic(data), + Config: r.basic(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("ip_address").Exists(), @@ -95,14 +95,14 @@ func TestAccPublicIp_zonesMultiple(t *testing.T) { }) } -func TestAccPublicIpStatic_basic_withDNSLabel(t *testing.T) { +func TestAccPublicIp_standard_withDNSLabel(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_public_ip", "test") r := PublicIpResource{} dnl := fmt.Sprintf("acctestdnl-%d", data.RandomInteger) data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.basic_withDNSLabel(data, dnl), + Config: r.standard_withDNSLabel(data, dnl), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("ip_address").Exists(), @@ -114,7 +114,7 @@ func TestAccPublicIpStatic_basic_withDNSLabel(t *testing.T) { }) } -func TestAccPublicIpStatic_standard_withIPv6(t *testing.T) { +func TestAccPublicIp_standard_withIPv6(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_public_ip", "test") r := PublicIpResource{} @@ -130,30 +130,13 @@ func TestAccPublicIpStatic_standard_withIPv6(t *testing.T) { }) } -func TestAccPublicIpDynamic_basic_withIPv6(t *testing.T) { +func TestAccPublicIp_standard_defaultsToIPv4(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_public_ip", "test") r := PublicIpResource{} - ipVersion := "IPv6" data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.dynamic_basic_withIPVersion(data, ipVersion), - Check: acceptance.ComposeTestCheckFunc( - check.That(data.ResourceName).ExistsInAzure(r), - check.That(data.ResourceName).Key("ip_version").HasValue("IPv6"), - ), - }, - data.ImportStep(), - }) -} - -func TestAccPublicIpStatic_basic_defaultsToIPv4(t *testing.T) { - data := acceptance.BuildTestData(t, "azurerm_public_ip", "test") - r := PublicIpResource{} - - data.ResourceTest(t, r, []acceptance.TestStep{ - { - Config: r.static_basic(data), + Config: r.basic(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("ip_version").HasValue("IPv4"), @@ -163,14 +146,14 @@ func TestAccPublicIpStatic_basic_defaultsToIPv4(t *testing.T) { }) } -func TestAccPublicIpStatic_basic_withIPv4(t *testing.T) { +func TestAccPublicIp_standard_withIPv4(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_public_ip", "test") r := PublicIpResource{} ipVersion := "IPv4" data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.static_basic_withIPVersion(data, ipVersion), + Config: r.standard_withIPVersion(data, ipVersion), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("ip_version").HasValue("IPv4"), @@ -180,13 +163,13 @@ func TestAccPublicIpStatic_basic_withIPv4(t *testing.T) { }) } -func TestAccPublicIpStatic_standard(t *testing.T) { +func TestAccPublicIp_standardVTwo(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_public_ip", "test") r := PublicIpResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.standard(data), + Config: r.standardVTwo(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), @@ -195,7 +178,7 @@ func TestAccPublicIpStatic_standard(t *testing.T) { }) } -func TestAccPublicIpStatic_standard_withDDoS(t *testing.T) { +func TestAccPublicIp_standard_withDDoS(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_public_ip", "test") r := PublicIpResource{} @@ -220,19 +203,19 @@ func TestAccPublicIpStatic_standard_withDDoS(t *testing.T) { }) } -func TestAccPublicIpStatic_disappears(t *testing.T) { +func TestAccPublicIp_disappears(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_public_ip", "test") r := PublicIpResource{} data.ResourceTest(t, r, []acceptance.TestStep{ data.DisappearsStep(acceptance.DisappearsStepData{ - Config: r.static_basic, + Config: r.basic, TestResource: r, }), }) } -func TestAccPublicIpStatic_idleTimeout(t *testing.T) { +func TestAccPublicIp_idleTimeout(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_public_ip", "test") r := PublicIpResource{} @@ -254,7 +237,7 @@ func TestAccPublicIpStatic_idleTimeout(t *testing.T) { }) } -func TestAccPublicIpStatic_withTags(t *testing.T) { +func TestAccPublicIp_withTags(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_public_ip", "test") r := PublicIpResource{} @@ -279,13 +262,13 @@ func TestAccPublicIpStatic_withTags(t *testing.T) { }) } -func TestAccPublicIpStatic_update(t *testing.T) { +func TestAccPublicIp_update(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_public_ip", "test") r := PublicIpResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.static_basic(data), + Config: r.basic(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), @@ -301,7 +284,7 @@ func TestAccPublicIpStatic_update(t *testing.T) { }) } -func TestAccPublicIpStatic_standardPrefix(t *testing.T) { +func TestAccPublicIp_standardPrefix(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_public_ip", "test") r := PublicIpResource{} @@ -315,7 +298,7 @@ func TestAccPublicIpStatic_standardPrefix(t *testing.T) { }) } -func TestAccPublicIpStatic_standardPrefixWithTags(t *testing.T) { +func TestAccPublicIp_standardPrefixWithTags(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_public_ip", "test") r := PublicIpResource{} @@ -340,91 +323,94 @@ func TestAccPublicIpStatic_standardPrefixWithTags(t *testing.T) { }) } -func TestAccPublicIpDynamic_basic(t *testing.T) { +func TestAccPublicIp_canLabelBe63(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_public_ip", "test") r := PublicIpResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.dynamic_basic(data), + Config: r.canLabelBe63(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("ip_address").Exists(), + check.That(data.ResourceName).Key("allocation_method").HasValue("Static"), ), }, data.ImportStep(), }) } -func TestAccPublicIpStatic_canLabelBe63(t *testing.T) { +func TestAccPublicIp_ipTags(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_public_ip", "test") r := PublicIpResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.canLabelBe63(data), + Config: r.standard_IpTags(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), - check.That(data.ResourceName).Key("ip_address").Exists(), - check.That(data.ResourceName).Key("allocation_method").HasValue("Static"), + check.That(data.ResourceName).Key("ip_tags.RoutingPreference").HasValue("Internet"), ), }, data.ImportStep(), }) } -func TestAccPublicIpStatic_ipTags(t *testing.T) { +func TestAccPublicIp_globalTier(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_public_ip", "test") r := PublicIpResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.standard_IpTags(data), + Config: r.skuTier(data, string(publicipaddresses.PublicIPAddressSkuNameStandard), string(publicipaddresses.PublicIPAddressSkuTierGlobal)), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), - check.That(data.ResourceName).Key("ip_tags.RoutingPreference").HasValue("Internet"), + check.That(data.ResourceName).Key("ip_address").Exists(), + check.That(data.ResourceName).Key("sku").HasValue(string(publicipaddresses.PublicIPAddressSkuNameStandard)), + check.That(data.ResourceName).Key("sku_tier").HasValue(string(publicipaddresses.PublicIPAddressSkuTierGlobal)), ), }, data.ImportStep(), }) } -func TestAccPublicIpStatic_globalTier(t *testing.T) { +func TestAccPublicIp_regionalTier(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_public_ip", "test") r := PublicIpResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.globalTier(data), + Config: r.skuTier(data, string(publicipaddresses.PublicIPAddressSkuNameStandard), string(publicipaddresses.PublicIPAddressSkuTierRegional)), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("ip_address").Exists(), - check.That(data.ResourceName).Key("sku").HasValue("Standard"), - check.That(data.ResourceName).Key("sku_tier").HasValue("Global"), + check.That(data.ResourceName).Key("sku").HasValue(string(publicipaddresses.PublicIPAddressSkuNameStandard)), + check.That(data.ResourceName).Key("sku_tier").HasValue(string(publicipaddresses.PublicIPAddressSkuTierRegional)), ), }, data.ImportStep(), }) } -func TestAccPublicIpStatic_regionalTier(t *testing.T) { +func TestAccPublicIp_standardV2RegionalTier(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_public_ip", "test") r := PublicIpResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.regionalTier(data), + Config: r.skuTier(data, string(publicipaddresses.PublicIPAddressSkuNameStandardVTwo), string(publicipaddresses.PublicIPAddressSkuTierRegional)), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), check.That(data.ResourceName).Key("ip_address").Exists(), - check.That(data.ResourceName).Key("sku").HasValue("Basic"), - check.That(data.ResourceName).Key("sku_tier").HasValue("Regional"), + check.That(data.ResourceName).Key("sku").HasValue(string(publicipaddresses.PublicIPAddressSkuNameStandardVTwo)), + check.That(data.ResourceName).Key("sku_tier").HasValue(string(publicipaddresses.PublicIPAddressSkuTierRegional)), ), }, data.ImportStep(), }) } -func TestAccPublicIpStatic_edgeZone(t *testing.T) { +func TestAccPublicIp_edgeZone(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_public_ip", "test") r := PublicIpResource{} @@ -468,11 +454,7 @@ func (PublicIpResource) Destroy(ctx context.Context, client *clients.Client, sta return pointer.To(true), nil } -func (r PublicIpResource) basic(data acceptance.TestData) string { - return r.static_basic(data) -} - -func (PublicIpResource) static_basic(data acceptance.TestData) string { +func (PublicIpResource) basic(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -488,7 +470,7 @@ resource "azurerm_public_ip" "test" { location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name allocation_method = "Static" - sku = "Basic" + sku = "Standard" } `, data.RandomInteger, data.Locations.Primary, data.RandomInteger) } @@ -502,12 +484,12 @@ resource "azurerm_public_ip" "import" { location = azurerm_public_ip.test.location resource_group_name = azurerm_public_ip.test.resource_group_name allocation_method = azurerm_public_ip.test.allocation_method - sku = "Basic" + sku = azurerm_public_ip.test.sku } -`, r.static_basic(data)) +`, r.basic(data)) } -func (PublicIpResource) basic_withDNSLabel(data acceptance.TestData, dnsNameLabel string) string { +func (PublicIpResource) standard_withDNSLabel(data acceptance.TestData, dnsNameLabel string) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -523,35 +505,13 @@ resource "azurerm_public_ip" "test" { location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name allocation_method = "Static" - sku = "Basic" + sku = "Standard" domain_name_label = "%s" } `, data.RandomInteger, data.Locations.Primary, data.RandomInteger, dnsNameLabel) } -func (PublicIpResource) static_basic_withIPVersion(data acceptance.TestData, ipVersion string) string { - return fmt.Sprintf(` -provider "azurerm" { - features {} -} - -resource "azurerm_resource_group" "test" { - name = "acctestRG-%d" - location = "%s" -} - -resource "azurerm_public_ip" "test" { - name = "acctestpublicip-%d" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - allocation_method = "Static" - sku = "Basic" - ip_version = "%s" -} -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, ipVersion) -} - -func (PublicIpResource) standard(data acceptance.TestData) string { +func (PublicIpResource) standardVTwo(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -567,7 +527,7 @@ resource "azurerm_public_ip" "test" { location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name allocation_method = "Static" - sku = "Standard" + sku = "StandardV2" } `, data.RandomInteger, data.Locations.Primary, data.RandomInteger) } @@ -754,7 +714,7 @@ resource "azurerm_public_ip" "test" { location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name allocation_method = "Static" - sku = "Basic" + sku = "Standard" domain_name_label = "acctest-%d" domain_name_label_scope = "TenantReuse" idle_timeout_in_minutes = 30 @@ -778,56 +738,12 @@ resource "azurerm_public_ip" "test" { location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name allocation_method = "Static" - sku = "Basic" + sku = "Standard" idle_timeout_in_minutes = %d } `, data.RandomInteger, data.Locations.Primary, data.RandomInteger, idleTimeout) } -func (PublicIpResource) dynamic_basic(data acceptance.TestData) string { - return fmt.Sprintf(` -provider "azurerm" { - features {} -} - -resource "azurerm_resource_group" "test" { - name = "acctestRG-%d" - location = "%s" -} - -resource "azurerm_public_ip" "test" { - name = "acctestpublicip-%d" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - allocation_method = "Dynamic" - sku = "Basic" -} -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) -} - -func (PublicIpResource) dynamic_basic_withIPVersion(data acceptance.TestData, ipVersion string) string { - return fmt.Sprintf(` -provider "azurerm" { - features {} -} - -resource "azurerm_resource_group" "test" { - name = "acctestRG-%d" - location = "%s" -} - -resource "azurerm_public_ip" "test" { - name = "acctestpublicip-%d" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - allocation_method = "Dynamic" - sku = "Basic" - - ip_version = "%s" -} -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, ipVersion) -} - func (PublicIpResource) withTags(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { @@ -844,7 +760,7 @@ resource "azurerm_public_ip" "test" { location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name allocation_method = "Static" - sku = "Basic" + sku = "Standard" tags = { environment = "Production" @@ -870,7 +786,7 @@ resource "azurerm_public_ip" "test" { location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name allocation_method = "Static" - sku = "Basic" + sku = "Standard" tags = { environment = "staging" @@ -896,7 +812,7 @@ resource "azurerm_public_ip" "test" { resource_group_name = azurerm_resource_group.test.name allocation_method = "Static" - sku = "Basic" + sku = "Standard" domain_name_label = "%s" } `, data.RandomInteger, data.Locations.Primary, data.RandomInteger, fmt.Sprintf("test%s", strings.ToLower(data.RandomStringOfLength(59)))) // prepend with test (4+59) to ensure the string starts with a letter @@ -928,29 +844,7 @@ resource "azurerm_public_ip" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomInteger) } -func (PublicIpResource) globalTier(data acceptance.TestData) string { - return fmt.Sprintf(` -provider "azurerm" { - features {} -} - -resource "azurerm_resource_group" "test" { - name = "acctestRG-%d" - location = "%s" -} - -resource "azurerm_public_ip" "test" { - name = "acctestpublicip-%d" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - allocation_method = "Static" - sku = "Standard" - sku_tier = "Global" -} -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) -} - -func (PublicIpResource) regionalTier(data acceptance.TestData) string { +func (PublicIpResource) skuTier(data acceptance.TestData, sku string, tier string) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -966,10 +860,10 @@ resource "azurerm_public_ip" "test" { location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name allocation_method = "Static" - sku = "Basic" - sku_tier = "Regional" + sku = "%s" + sku_tier = "%s" } -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, sku, tier) } func (PublicIpResource) zonesSingle(data acceptance.TestData) string { diff --git a/internal/services/network/public_ips_data_source_test.go b/internal/services/network/public_ips_data_source_test.go index e755236aad16..4c6c4737d145 100644 --- a/internal/services/network/public_ips_data_source_test.go +++ b/internal/services/network/public_ips_data_source_test.go @@ -55,24 +55,21 @@ func TestAccDataSourcePublicIPs_assigned(t *testing.T) { }) } -func TestAccDataSourcePublicIPs_allocationType(t *testing.T) { +func TestAccDataSourcePublicIPs_staticAllocationType(t *testing.T) { data := acceptance.BuildTestData(t, "data.azurerm_public_ips", "test") r := PublicIPsResource{} staticDataSourceName := "data.azurerm_public_ips.static" - dynamicDataSourceName := "data.azurerm_public_ips.dynamic" data.DataSourceTest(t, []acceptance.TestStep{ { - Config: r.allocationType(data), + Config: r.staticAllocationType(data), }, { - Config: r.allocationTypeDataSources(data), + Config: r.staticAllocationTypeDataSources(data), Check: acceptance.ComposeTestCheckFunc( acceptance.TestCheckResourceAttr(staticDataSourceName, "public_ips.#", "3"), acceptance.TestCheckResourceAttr(staticDataSourceName, "public_ips.0.name", fmt.Sprintf("acctestpips%s-0", data.RandomString)), - acceptance.TestCheckResourceAttr(dynamicDataSourceName, "public_ips.#", "4"), - acceptance.TestCheckResourceAttr(dynamicDataSourceName, "public_ips.0.name", fmt.Sprintf("acctestpipd%s-0", data.RandomString)), ), }, }) @@ -164,7 +161,7 @@ resource "azurerm_public_ip" "test" { location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name allocation_method = "Static" - sku = "Basic" + sku = "Standard" idle_timeout_in_minutes = 30 tags = { @@ -178,7 +175,7 @@ resource "azurerm_public_ip" "test2" { location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name allocation_method = "Static" - sku = "Basic" + sku = "Standard" idle_timeout_in_minutes = 30 tags = { @@ -199,7 +196,7 @@ data "azurerm_public_ips" "test" { `, r.prefix(data)) } -func (PublicIPsResource) allocationType(data acceptance.TestData) string { +func (PublicIPsResource) staticAllocationType(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -210,48 +207,29 @@ resource "azurerm_resource_group" "test" { location = "%s" } -resource "azurerm_public_ip" "dynamic" { - count = 4 - name = "acctestpipd%s-${count.index}" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - allocation_method = "Dynamic" - sku = "Basic" - idle_timeout_in_minutes = 30 - - tags = { - environment = "test" - } -} - resource "azurerm_public_ip" "static" { count = 3 name = "acctestpips%s-${count.index}" location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name allocation_method = "Static" - sku = "Basic" + sku = "Standard" idle_timeout_in_minutes = 30 tags = { environment = "test" } } -`, data.RandomInteger, data.Locations.Primary, data.RandomString, data.RandomString) +`, data.RandomInteger, data.Locations.Primary, data.RandomString) } -func (r PublicIPsResource) allocationTypeDataSources(data acceptance.TestData) string { +func (r PublicIPsResource) staticAllocationTypeDataSources(data acceptance.TestData) string { return fmt.Sprintf(` %s -data "azurerm_public_ips" "dynamic" { - resource_group_name = azurerm_resource_group.test.name - allocation_type = "Dynamic" -} - data "azurerm_public_ips" "static" { resource_group_name = azurerm_resource_group.test.name allocation_type = "Static" } -`, r.allocationType(data)) +`, r.staticAllocationType(data)) } diff --git a/website/docs/r/nat_gateway.html.markdown b/website/docs/r/nat_gateway.html.markdown index 9e3d62cda4b5..f5e96a64da35 100644 --- a/website/docs/r/nat_gateway.html.markdown +++ b/website/docs/r/nat_gateway.html.markdown @@ -3,7 +3,7 @@ subcategory: "Network" layout: "azurerm" page_title: "Azure Resource Manager: azurerm_nat_gateway" description: |- - Manages a Azure NAT Gateway. + Manages an Azure NAT Gateway. --- # azurerm_nat_gateway @@ -41,13 +41,15 @@ The following arguments are supported: * `idle_timeout_in_minutes` - (Optional) The idle timeout which should be used in minutes. Defaults to `4`. -* `sku_name` - (Optional) The SKU which should be used. At this time the only supported value is `Standard`. Defaults to `Standard`. +* `sku_name` - (Optional) The SKU which should be used. Possible values are `Standard` and `StandardV2`. Defaults to `Standard`. Changing this forces a new resource to be created. -* `tags` - (Optional) A mapping of tags to assign to the resource. +* `zones` - (Optional) A list of Availability Zones in which this NAT Gateway should be located. Changing this forces a new resource to be created. + +-> **Note:** For `Standard`, `zones` may be omitted for a no-zone deployment or set to a single Availability Zone. For more information, please see the [Azure documentation](https://learn.microsoft.com/azure/nat-gateway/nat-overview#availability-zones). -* `zones` - (Optional) A list of Availability Zones in which this NAT Gateway should be located. Changing this forces a new NAT Gateway to be created. +~> **Note:** `zones` must be omitted when `sku_name` is set to `StandardV2`. `StandardV2` NAT Gateways are zone-redundant by default and Azure automatically deploys across all available zones. For more information, please see the [Azure documentation](https://learn.microsoft.com/azure/nat-gateway/nat-overview#standardv2-nat-gateway). --> **Note:** Only one Availability Zone can be defined. For more information, please check out the [Azure documentation](https://learn.microsoft.com/en-us/azure/nat-gateway/nat-overview#availability-zones) +* `tags` - (Optional) A mapping of tags to assign to the resource. ## Attributes Reference @@ -68,7 +70,7 @@ The `timeouts` block allows you to specify [timeouts](https://developer.hashicor ## Import -NAT Gateway can be imported using the `resource id`, e.g. +A NAT Gateway can be imported using the `resource id`, e.g. ```shell terraform import azurerm_nat_gateway.test /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Network/natGateways/gateway1 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 dd4664fe11f2..8a3a59b333e4 100644 --- a/website/docs/r/nat_gateway_public_ip_association.html.markdown +++ b/website/docs/r/nat_gateway_public_ip_association.html.markdown @@ -48,6 +48,8 @@ The following arguments are supported: * `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. +~> **Note:** When `nat_gateway_id` references a `StandardV2` NAT Gateway, `public_ip_address_id` must reference a `StandardV2` Public IP. Azure rejects `Standard` Public IPs with `StandardV2` NAT Gateways, and this incompatibility is not validated during terraform plan phase. + ## Attributes Reference In addition to the Arguments listed above - the following Attributes are exported: 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 index e36f9ed95e33..1e2158244d3e 100644 --- a/website/docs/r/nat_gateway_public_ip_prefix_association.html.markdown +++ b/website/docs/r/nat_gateway_public_ip_prefix_association.html.markdown @@ -48,6 +48,8 @@ The following arguments are supported: * `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. +~> **Note:** When `nat_gateway_id` references a `StandardV2` NAT Gateway, `public_ip_prefix_id` must reference a `StandardV2` Public IP Prefix. Azure rejects `Standard` Public IP Prefixes with `StandardV2` NAT Gateways, and this incompatibility is not validated during terraform plan phase. + ## Attributes Reference In addition to the Arguments listed above - the following Attributes are exported: diff --git a/website/docs/r/public_ip.html.markdown b/website/docs/r/public_ip.html.markdown index 4d958444bf87..0846f0af1827 100644 --- a/website/docs/r/public_ip.html.markdown +++ b/website/docs/r/public_ip.html.markdown @@ -46,6 +46,8 @@ The following arguments are supported: ~> **Note:** `Dynamic` Public IP Addresses aren't allocated until they're assigned to a resource (such as a Virtual Machine or a Load Balancer) by design within Azure. See `ip_address` argument. +!> **Note:** `Dynamic` allocation is only available with `Basic` SKU public IP addresses. Since `Basic` SKU public IP addresses have been deprecated (see `sku` below), `Dynamic` allocation is no longer available for new public IP addresses. + --- * `zones` - (Optional) A collection containing the availability zone to allocate the Public IP in. Changing this forces a new resource to be created. @@ -72,15 +74,17 @@ The following arguments are supported: * `ip_version` - (Optional) The IP Version to use, IPv6 or IPv4. Changing this forces a new resource to be created. Defaults to `IPv4`. --> **Note:** Only `static` IP address allocation is supported for IPv6. +-> **Note:** Only `Static` IP address allocation is supported for IPv6. * `public_ip_prefix_id` - (Optional) If specified then public IP address allocated will be provided from the public IP prefix resource. Changing this forces a new resource to be created. * `reverse_fqdn` - (Optional) A fully qualified domain name that resolves to this public IP address. If the reverseFqdn is specified, then a PTR DNS record is created pointing from the IP address in the in-addr.arpa domain to the reverse FQDN. -* `sku` - (Optional) The SKU of the Public IP. Accepted values are `Basic` and `Standard`. Defaults to `Standard`. Changing this forces a new resource to be created. +* `sku` - (Optional) The SKU of the Public IP. Possible values are `Basic`, `Standard`, and `StandardV2`. Defaults to `Standard`. Changing this forces a new resource to be created. + +-> **Note:** Public IP `Standard` and `StandardV2` SKUs require `allocation_method` to be set to `Static`. --> **Note:** Public IP Standard SKUs require `allocation_method` to be set to `Static`. +!> **Note:** `sku` can no longer be set to `Basic` as of 31 March 2025 for new resources. This also affects `allocation_method` set to `Dynamic`, as it is only available with the `Basic` SKU. Please see the Azure Update [retirement notification](https://azure.microsoft.com/updates/upgrade-to-standard-sku-public-ip-addresses-in-azure-by-30-september-2025-basic-sku-will-be-retired/) for more information. * `sku_tier` - (Optional) The SKU Tier that should be used for the Public IP. Possible values are `Regional` and `Global`. Defaults to `Regional`. Changing this forces a new resource to be created. diff --git a/website/docs/r/public_ip_prefix.html.markdown b/website/docs/r/public_ip_prefix.html.markdown index c19e163487c1..9d3b01a86f5c 100644 --- a/website/docs/r/public_ip_prefix.html.markdown +++ b/website/docs/r/public_ip_prefix.html.markdown @@ -45,11 +45,11 @@ The following arguments are supported: -> **Note:** When `ip_version` is set to `IPv6`, `custom_ip_prefix_id` must reference a regional (child) range rather than a global (parent) range. For more details on creating a Public IP Prefix from a custom IP prefix, see [here](https://learn.microsoft.com/en-us/azure/virtual-network/ip-services/manage-custom-ip-address-prefix#create-a-public-ip-prefix-from-a-custom-ip-prefix). -* `sku` - (Optional) The SKU of the Public IP Prefix. Accepted values are `Standard`. Defaults to `Standard`. Changing this forces a new resource to be created. +* `sku` - (Optional) The SKU of the Public IP Prefix. Possible values are `Standard` and `StandardV2`. Defaults to `Standard`. Changing this forces a new resource to be created. --> **Note:** Public IP Prefix can only be created with Standard SKUs at this time. +* `sku_tier` - (Optional) The SKU Tier that should be used for the Public IP Prefix. Possible values are `Regional` and `Global`. Defaults to `Regional`. Changing this forces a new resource to be created. -* `sku_tier` - (Optional) The SKU Tier that should be used for the Public IP. Possible values are `Regional` and `Global`. Defaults to `Regional`. Changing this forces a new resource to be created. +-> **Note:** When `sku_tier` is set to `Global`, `sku` must be set to `Standard`. * `ip_version` - (Optional) The IP Version to use, `IPv6` or `IPv4`. Changing this forces a new resource to be created. Default is `IPv4`.