diff --git a/azurerm/internal/services/network/availability_zone.go b/azurerm/internal/services/network/availability_zone.go new file mode 100644 index 000000000000..125a17b4accd --- /dev/null +++ b/azurerm/internal/services/network/availability_zone.go @@ -0,0 +1,30 @@ +package network + +import ( + "context" + + "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2020-06-01/resources" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" +) + +func getZones(ctx context.Context, client *resources.ProvidersClient, resourceType, location string) (*[]string, error) { + provider, err := client.Get(ctx, "Microsoft.Network", "") + if err != nil { + return nil, err + } + normalizedLocation := azure.NormalizeLocation(location) + for _, resource := range *provider.ResourceTypes { + if resource.ResourceType == nil || *resource.ResourceType != resourceType { + continue + } + if resource.ZoneMappings == nil { + continue + } + for _, zone := range *resource.ZoneMappings { + if zone.Location != nil && azure.NormalizeLocation(*zone.Location) == normalizedLocation { + return zone.Zones, nil + } + } + } + return nil, nil +} diff --git a/azurerm/internal/services/network/public_ip_prefix_resource.go b/azurerm/internal/services/network/public_ip_prefix_resource.go index 1134f9a1a410..fa6fc1a43043 100644 --- a/azurerm/internal/services/network/public_ip_prefix_resource.go +++ b/azurerm/internal/services/network/public_ip_prefix_resource.go @@ -3,6 +3,7 @@ package network import ( "fmt" "log" + "strings" "time" "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2020-11-01/network" @@ -103,6 +104,20 @@ func resourcePublicIpPrefixCreateUpdate(d *pluginsdk.ResourceData, meta interfac t := d.Get("tags").(map[string]interface{}) zones := azure.ExpandZones(d.Get("zones").([]interface{})) + // TODO: remove in 3.0 + // to address this breaking change : https://azure.microsoft.com/en-us/updates/zone-behavior-change/, by setting a location's available zone list as default value to keep the behavior unchanged, + // to create a non-zonal resource, user can set zones to ["no_zone"] + if zones == nil || len(*zones) == 0 { + allZones, err := getZones(ctx, meta.(*clients.Client).Resource.ResourceProvidersClient, "publicIPPrefixes", location) + if err != nil { + return fmt.Errorf("failed to get available zones for resourceType: publicIPAddresses, location: %s:%+v", location, err) + } else { + zones = allZones + } + } else if (*zones)[0] == "no_zone" { + zones = nil + } + publicIpPrefix := network.PublicIPPrefix{ Location: &location, Sku: &network.PublicIPPrefixSku{ @@ -151,7 +166,25 @@ func resourcePublicIpPrefixRead(d *pluginsdk.ResourceData, meta interface{}) err d.Set("name", id.PublicIPPrefixeName) d.Set("resource_group_name", id.ResourceGroup) - d.Set("zones", resp.Zones) + // TODO: remove in 3.0 + zones := make([]string, 0) + if resp.Location != nil && resp.Sku != nil && strings.EqualFold(string(resp.Sku.Name), "standard") { + allZones, err := getZones(ctx, meta.(*clients.Client).Resource.ResourceProvidersClient, "publicIPPrefixes", *resp.Location) + if err != nil { + return fmt.Errorf("failed to get available zones for resourceType: publicIPPrefixes, location: %s:%+v", *resp.Location, err) + } + switch { + case allZones == nil || len(*allZones) == 0: + zones = make([]string, 0) + case resp.Zones == nil || len(*resp.Zones) == 0: + zones = append(zones, "no_zone") + case len(*resp.Zones) == 1: + zones = append(zones, (*resp.Zones)[0]) + default: + zones = make([]string, 0) + } + } + d.Set("zones", &zones) if location := resp.Location; location != nil { d.Set("location", azure.NormalizeLocation(*location)) } diff --git a/azurerm/internal/services/network/public_ip_resource.go b/azurerm/internal/services/network/public_ip_resource.go index 56160d239cca..bf0dec216655 100644 --- a/azurerm/internal/services/network/public_ip_resource.go +++ b/azurerm/internal/services/network/public_ip_resource.go @@ -176,7 +176,21 @@ func resourcePublicIpCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) e return tf.ImportAsExistsError("azurerm_public_ip", id.ID()) } } - + // TODO: remove in 3.0 + // to address this breaking change : https://azure.microsoft.com/en-us/updates/zone-behavior-change/, by setting a location's available zone list as default value to keep the behavior unchanged, + // to create a non-zonal resource, user can set zones to ["no_zone"] + if strings.EqualFold(sku, "standard") { + if zones == nil || len(*zones) == 0 { + allZones, err := getZones(ctx, meta.(*clients.Client).Resource.ResourceProvidersClient, "publicIPAddresses", location) + if err != nil { + return fmt.Errorf("failed to get available zones for resourceType: publicIPAddresses, location: %s:%+v", location, err) + } else { + zones = allZones + } + } else if (*zones)[0] == "no_zone" { + zones = nil + } + } publicIp := network.PublicIPAddress{ Name: utils.String(id.Name), Location: &location, @@ -267,7 +281,25 @@ func resourcePublicIpRead(d *pluginsdk.ResourceData, meta interface{}) error { d.Set("name", id.Name) d.Set("resource_group_name", id.ResourceGroup) - d.Set("zones", resp.Zones) + // TODO: remove in 3.0 + zones := make([]string, 0) + if resp.Location != nil && resp.Sku != nil && strings.EqualFold(string(resp.Sku.Name), "standard") { + allZones, err := getZones(ctx, meta.(*clients.Client).Resource.ResourceProvidersClient, "publicIPAddresses", *resp.Location) + if err != nil { + return fmt.Errorf("failed to get available zones for resourceType: publicIPAddresses, location: %s:%+v", *resp.Location, err) + } + switch { + case allZones == nil || len(*allZones) == 0: + zones = make([]string, 0) + case resp.Zones == nil || len(*resp.Zones) == 0: + zones = append(zones, "no_zone") + case len(*resp.Zones) == 1: + zones = append(zones, (*resp.Zones)[0]) + default: + zones = make([]string, 0) + } + } + d.Set("zones", &zones) if location := resp.Location; location != nil { d.Set("location", azure.NormalizeLocation(*location)) } diff --git a/azurerm/internal/services/network/public_ip_resource_test.go b/azurerm/internal/services/network/public_ip_resource_test.go index 0b7c3b54ef3a..1277d704e431 100644 --- a/azurerm/internal/services/network/public_ip_resource_test.go +++ b/azurerm/internal/services/network/public_ip_resource_test.go @@ -771,9 +771,9 @@ resource "azurerm_public_ip" "test" { resource_group_name = azurerm_resource_group.test.name allocation_method = "Static" - domain_name_label = "k2345678-1-2345678-2-2345678-3-2345678-4-2345678-5-2345678-6-23" + domain_name_label = "%s" } -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomStringOfLength(63)) } func (PublicIPResource) standard_IpTags(data acceptance.TestData) string { diff --git a/azurerm/internal/services/resource/client/client.go b/azurerm/internal/services/resource/client/client.go index e4bbcf54d77b..93b6411c8836 100644 --- a/azurerm/internal/services/resource/client/client.go +++ b/azurerm/internal/services/resource/client/client.go @@ -13,6 +13,7 @@ type Client struct { GroupsClient *resources.GroupsClient LocksClient *locks.ManagementLocksClient ProvidersClient *providers.ProvidersClient + ResourceProvidersClient *resources.ProvidersClient ResourcesClient *resources.Client TemplateSpecsVersionsClient *templatespecs.VersionsClient } @@ -31,6 +32,10 @@ func NewClient(o *common.ClientOptions) *Client { providersClient := providers.NewProvidersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&providersClient.Client, o.ResourceManagerAuthorizer) + // add a secondary ProvidersClient to use latest resources sdk + resourceProvidersClient := resources.NewProvidersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&resourceProvidersClient.Client, o.ResourceManagerAuthorizer) + resourcesClient := resources.NewClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&resourcesClient.Client, o.ResourceManagerAuthorizer) @@ -42,6 +47,7 @@ func NewClient(o *common.ClientOptions) *Client { DeploymentsClient: &deploymentsClient, LocksClient: &locksClient, ProvidersClient: &providersClient, + ResourceProvidersClient: &resourceProvidersClient, ResourcesClient: &resourcesClient, TemplateSpecsVersionsClient: &templatespecsVersionsClient, } diff --git a/website/docs/r/public_ip.html.markdown b/website/docs/r/public_ip.html.markdown index 120855bab1c3..6c366a8fa655 100644 --- a/website/docs/r/public_ip.html.markdown +++ b/website/docs/r/public_ip.html.markdown @@ -70,7 +70,7 @@ The following arguments are supported: * `zones` - (Optional) A collection containing the availability zone to allocate the Public IP in. --> **Please Note**: Availability Zones are only supported with a [Standard SKU](https://docs.microsoft.com/en-us/azure/virtual-network/virtual-network-ip-addresses-overview-arm#standard) and [in select regions](https://docs.microsoft.com/en-us/azure/availability-zones/az-overview) at this time. Standard SKU Public IP Addresses that do not specify a zone are zone redundant by default. +-> **Please Note**: Availability Zones are only supported with a [Standard SKU](https://docs.microsoft.com/en-us/azure/virtual-network/virtual-network-ip-addresses-overview-arm#standard) and [in select regions](https://docs.microsoft.com/en-us/azure/availability-zones/az-overview) at this time. Standard SKU Public IP Addresses that do not specify a zone are zone redundant by default. Set it to `no_zone` to create a non-zonal resource. ## Attributes Reference diff --git a/website/docs/r/public_ip_prefix.html.markdown b/website/docs/r/public_ip_prefix.html.markdown index d917eb9a45cd..a1bb4ae4e34b 100644 --- a/website/docs/r/public_ip_prefix.html.markdown +++ b/website/docs/r/public_ip_prefix.html.markdown @@ -53,7 +53,7 @@ The following arguments are supported: * `zones` - (Optional) A collection containing the availability zone to allocate the Public IP Prefix in. --> **Please Note**: Availability Zones are [only supported in several regions at this time](https://docs.microsoft.com/en-us/azure/availability-zones/az-overview). +-> **Please Note**: Availability Zones are [only supported in several regions at this time](https://docs.microsoft.com/en-us/azure/availability-zones/az-overview). Set it to `no_zone` to create a non-zonal resource. ## Attributes Reference