diff --git a/internal/services/network/public_ip_data_source.go b/internal/services/network/public_ip_data_source.go index f23cafb7816d..3c80d60c5112 100644 --- a/internal/services/network/public_ip_data_source.go +++ b/internal/services/network/public_ip_data_source.go @@ -45,6 +45,16 @@ func dataSourcePublicIP() *pluginsdk.Resource { Computed: true, }, + "ddos_protection_mode": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "ddos_protection_plan_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + "ip_version": { Type: pluginsdk.TypeString, Computed: true, @@ -137,6 +147,14 @@ func dataSourcePublicIPRead(d *pluginsdk.ResourceData, meta interface{}) error { reverseFqdn = *dnsSettings.ReverseFqdn } } + + if ddosSetting := props.DdosSettings; ddosSetting != nil { + d.Set("ddos_protection_mode", string(ddosSetting.ProtectionMode)) + if subResource := ddosSetting.DdosProtectionPlan; subResource != nil { + d.Set("ddos_protection_plan_id", subResource.ID) + } + } + d.Set("domain_name_label", domainNameLabel) d.Set("fqdn", fqdn) d.Set("reverse_fqdn", reverseFqdn) diff --git a/internal/services/network/public_ip_resource.go b/internal/services/network/public_ip_resource.go index f404cf8a03d5..36105472d8a2 100644 --- a/internal/services/network/public_ip_resource.go +++ b/internal/services/network/public_ip_resource.go @@ -64,6 +64,23 @@ func resourcePublicIp() *pluginsdk.Resource { }, // Optional + "ddos_protection_mode": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + string(network.DdosSettingsProtectionModeDisabled), + string(network.DdosSettingsProtectionModeEnabled), + string(network.DdosSettingsProtectionModeVirtualNetworkInherited), + }, false), + Default: string(network.DdosSettingsProtectionModeVirtualNetworkInherited), + }, + + "ddos_protection_plan_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validate.DdosProtectionPlanID, + }, + "edge_zone": commonschema.EdgeZoneOptionalForceNew(), "ip_version": { @@ -174,7 +191,7 @@ func resourcePublicIpCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) e location := azure.NormalizeLocation(d.Get("location").(string)) sku := d.Get("sku").(string) - sku_tier := d.Get("sku_tier").(string) + skuTier := d.Get("sku_tier").(string) t := d.Get("tags").(map[string]interface{}) idleTimeout := d.Get("idle_timeout_in_minutes").(int) @@ -187,21 +204,35 @@ func resourcePublicIpCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) e } } + ddosProtectionMode := d.Get("ddos_protection_mode").(string) + publicIp := network.PublicIPAddress{ Name: utils.String(id.Name), ExtendedLocation: expandEdgeZone(d.Get("edge_zone").(string)), Location: &location, Sku: &network.PublicIPAddressSku{ Name: network.PublicIPAddressSkuName(sku), - Tier: network.PublicIPAddressSkuTier(sku_tier), + Tier: network.PublicIPAddressSkuTier(skuTier), }, PublicIPAddressPropertiesFormat: &network.PublicIPAddressPropertiesFormat{ PublicIPAllocationMethod: network.IPAllocationMethod(ipAllocationMethod), PublicIPAddressVersion: ipVersion, IdleTimeoutInMinutes: utils.Int32(int32(idleTimeout)), + DdosSettings: &network.DdosSettings{ + ProtectionMode: network.DdosSettingsProtectionMode(ddosProtectionMode), + }, }, Tags: tags.Expand(t), } + ddosProtectionPlanId, planOk := d.GetOk("ddos_protection_plan_id") + if planOk { + if !strings.EqualFold(ddosProtectionMode, "enabled") { + return fmt.Errorf("ddos protection plan id can only be set when ddos protection is enabled") + } + publicIp.PublicIPAddressPropertiesFormat.DdosSettings.DdosProtectionPlan = &network.SubResource{ + ID: utils.String(ddosProtectionPlanId.(string)), + } + } zones := zones.ExpandUntyped(d.Get("zones").(*schema.Set).List()) if len(zones) > 0 { @@ -306,6 +337,13 @@ func resourcePublicIpRead(d *pluginsdk.ResourceData, meta interface{}) error { d.Set("domain_name_label", settings.DomainNameLabel) } + if ddosSetting := props.DdosSettings; ddosSetting != nil { + d.Set("ddos_protection_mode", string(ddosSetting.ProtectionMode)) + if subResource := ddosSetting.DdosProtectionPlan; subResource != nil { + d.Set("ddos_protection_plan_id", subResource.ID) + } + } + d.Set("ip_tags", flattenPublicIpPropsIpTags(props.IPTags)) d.Set("ip_address", props.IPAddress) diff --git a/internal/services/network/public_ip_resource_test.go b/internal/services/network/public_ip_resource_test.go index 6c8833445892..55eb4d404a78 100644 --- a/internal/services/network/public_ip_resource_test.go +++ b/internal/services/network/public_ip_resource_test.go @@ -30,6 +30,7 @@ func TestAccPublicIpStatic_basic(t *testing.T) { check.That(data.ResourceName).Key("ip_address").Exists(), check.That(data.ResourceName).Key("allocation_method").HasValue("Static"), check.That(data.ResourceName).Key("ip_version").HasValue("IPv4"), + check.That(data.ResourceName).Key("ddos_protection_mode").HasValue("VirtualNetworkInherited"), ), }, data.ImportStep(), @@ -191,6 +192,31 @@ func TestAccPublicIpStatic_standard(t *testing.T) { }) } +func TestAccPublicIpStatic_standard_withDDoS(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_public_ip", "test") + r := PublicIPResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.standardDDoSDisabled(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("ddos_protection_mode").HasValue("Disabled"), + ), + }, + data.ImportStep(), + { + Config: r.standardDDoSEnabled(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("ddos_protection_mode").HasValue("Enabled"), + check.That(data.ResourceName).Key("ddos_protection_plan_id").Exists(), + ), + }, + data.ImportStep(), + }) +} + func TestAccPublicIpStatic_disappears(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_public_ip", "test") r := PublicIPResource{} @@ -550,6 +576,57 @@ resource "azurerm_public_ip" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomInteger) } +func (PublicIPResource) standardDDoSDisabled(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" + ddos_protection_mode = "Disabled" +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} + +func (PublicIPResource) standardDDoSEnabled(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_network_ddos_protection_plan" "test" { + name = "acctestddospplan-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + +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" + ddos_protection_mode = "Enabled" + ddos_protection_plan_id = azurerm_network_ddos_protection_plan.test.id +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} + func (PublicIPResource) standardPrefix(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { diff --git a/website/docs/d/public_ip.html.markdown b/website/docs/d/public_ip.html.markdown index d0685d16bafe..311bd6967149 100644 --- a/website/docs/d/public_ip.html.markdown +++ b/website/docs/d/public_ip.html.markdown @@ -104,6 +104,8 @@ output "public_ip_address" { * `id` - The ID of the Public IP address. * `domain_name_label` - The label for the Domain Name. * `idle_timeout_in_minutes` - Specifies the timeout for the TCP idle connection. +* `ddos_protection_mode` - The DDoS protection mode of the public IP. +* `ddos_protection_plan_id` - The ID of DDoS protection plan associated with the public IP. * `fqdn` - Fully qualified domain name of the A DNS record associated with the public IP. This is the concatenation of the domainNameLabel and the regionalized DNS zone. * `ip_address` - The IP address value that was allocated. * `ip_version` - The IP version being used, for example `IPv4` or `IPv6`. diff --git a/website/docs/r/public_ip.html.markdown b/website/docs/r/public_ip.html.markdown index f4f220f56d47..21df55419a91 100644 --- a/website/docs/r/public_ip.html.markdown +++ b/website/docs/r/public_ip.html.markdown @@ -50,6 +50,12 @@ The following arguments are supported: -> **Note:** Availability Zones are only supported with a [Standard SKU](https://docs.microsoft.com/azure/virtual-network/virtual-network-ip-addresses-overview-arm#standard) and [in select regions](https://docs.microsoft.com/azure/availability-zones/az-overview) at this time. Standard SKU Public IP Addresses that do not specify a zone are **not** zone-redundant by default. +* `ddos_protection_mode` - (Optional) The DDoS protection mode of the public IP. Possible values are `Disabled`, `Enabled`, and `VirtualNetworkInherited`. Defaults to `VirtualNetworkInherited`. + +* `ddos_protection_plan_id` - (Optional) The ID of DDoS protection plan associated with the public IP. + +-> **Note:** `ddos_protection_plan_id` can only be set when `ddos_protection_mode` is `Enabled`. + * `domain_name_label` - (Optional) Label for the Domain Name. Will be used to make up the FQDN. If a domain name label is specified, an A DNS record is created for the public IP in the Microsoft Azure DNS system. * `edge_zone` - (Optional) Specifies the Edge Zone within the Azure Region where this Public IP should exist. Changing this forces a new Public IP to be created.