Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New resource: data azurerm_locations #23324

Merged
merged 4 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/labeler-issue-triage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ service/stream-analytics:
- '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_stream_analytics_((.|\n)*)###'

service/subscription:
- '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_(extended_locations|subscription\W+|subscriptions)((.|\n)*)###'
- '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_(extended_locations|location|subscription\W+|subscriptions)((.|\n)*)###'

service/synapse:
- '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_synapse_((.|\n)*)###'
Expand Down
6 changes: 5 additions & 1 deletion internal/clients/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,11 @@ func (client *Client) Build(ctx context.Context, o *common.ClientOptions) error
return fmt.Errorf("building Storage for StorageMover: %+v", err)
}
client.StreamAnalytics = streamAnalytics.NewClient(o)
client.Subscription = subscription.NewClient(o)

if client.Subscription, err = subscription.NewClient(o); err != nil {
return fmt.Errorf("building clients for Subscription: %+v", err)
}

client.Synapse = synapse.NewClient(o)
if client.TrafficManager, err = trafficManager.NewClient(o); err != nil {
return fmt.Errorf("building clients for Traffic Manager: %+v", err)
Expand Down
1 change: 1 addition & 0 deletions internal/provider/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ func SupportedTypedServices() []sdk.TypedServiceRegistration {
storage.Registration{},
storagemover.Registration{},
signalr.Registration{},
subscription.Registration{},
orbital.Registration{},
streamanalytics.Registration{},
search.Registration{},
Expand Down
25 changes: 18 additions & 7 deletions internal/services/subscription/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,36 @@
package client

import (
"github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2021-01-01/subscriptions" // nolint: staticcheck
"fmt"

"github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2021-01-01/subscriptions" // nolint: staticcheck
resourceManager "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-12-01/subscriptions"
subscriptionAliasPandora "github.com/hashicorp/go-azure-sdk/resource-manager/subscription/2021-10-01/subscriptions" // nolint: staticcheck
"github.com/hashicorp/terraform-provider-azurerm/internal/common"
)

type Client struct {
Client *subscriptions.Client
AliasClient *subscriptionAliasPandora.SubscriptionsClient
Client *subscriptions.Client
AliasClient *subscriptionAliasPandora.SubscriptionsClient
SubscriptionsClient *resourceManager.SubscriptionsClient
}

func NewClient(o *common.ClientOptions) *Client {
func NewClient(o *common.ClientOptions) (*Client, error) {
client := subscriptions.NewClientWithBaseURI(o.ResourceManagerEndpoint)
o.ConfigureClient(&client.Client, o.ResourceManagerAuthorizer)

aliasClient := subscriptionAliasPandora.NewSubscriptionsClientWithBaseURI(o.ResourceManagerEndpoint)
o.ConfigureClient(&aliasClient.Client, o.ResourceManagerAuthorizer)

return &Client{
AliasClient: &aliasClient,
Client: &client,
subscriptionsClient, err := resourceManager.NewSubscriptionsClientWithBaseURI(o.Environment.ResourceManager)
if err != nil {
return nil, fmt.Errorf("building Subscriptions client: %+v", err)
}
o.Configure(subscriptionsClient.Client, o.Authorizers.ResourceManager)

return &Client{
AliasClient: &aliasClient,
Client: &client,
SubscriptionsClient: subscriptionsClient,
}, nil
}
152 changes: 152 additions & 0 deletions internal/services/subscription/location_data_source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package subscription

import (
"context"
"fmt"
"strings"
"time"

"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonids"
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema"
"github.com/hashicorp/go-azure-helpers/resourcemanager/location"
resourcesSubscription "github.com/hashicorp/go-azure-sdk/resource-manager/resources/2022-12-01/subscriptions"
"github.com/hashicorp/terraform-provider-azurerm/internal/sdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
)

var _ sdk.DataSource = LocationDataSource{}

type LocationDataSource struct{}

type LocationDataSourceModel struct {
Location string `tfschema:"location"`
DisplayName string `tfschema:"display_name"`
ZoneMappings []LocationZoneMapping `tfschema:"zone_mappings"`
}

type LocationZoneMapping struct {
LogicalZone string `tfschema:"logical_zone"`
PhysicalZone string `tfschema:"physical_zone"`
}

func (r LocationDataSource) ResourceType() string {
return "azurerm_location"
}

func (r LocationDataSource) ModelObject() interface{} {
return &LocationDataSourceModel{}
}

func (r LocationDataSource) Arguments() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
"location": commonschema.LocationWithoutForceNew(),
}
}

func (r LocationDataSource) Attributes() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
"display_name": {
Type: pluginsdk.TypeString,
Computed: true,
},
"zone_mappings": {
Type: pluginsdk.TypeList,
Computed: true,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"logical_zone": {
Type: pluginsdk.TypeString,
Computed: true,
},
"physical_zone": {
Type: pluginsdk.TypeString,
Computed: true,
},
},
},
},
}
}

func (r LocationDataSource) Read() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 5 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.Subscription.SubscriptionsClient
subscriptionId := metadata.Client.Account.SubscriptionId

id := commonids.NewSubscriptionID(subscriptionId)
resp, err := client.ListLocations(ctx, id, resourcesSubscription.DefaultListLocationsOperationOptions())
if err != nil {
if response.WasNotFound(resp.HttpResponse) {
return fmt.Errorf("%s was not found", id)
}

return fmt.Errorf("retrieving %s: %+v", id, err)
}

if resp.Model == nil {
return fmt.Errorf("retrieving %s: model was nil", id)
}

if resp.Model.Value == nil {
return fmt.Errorf("retrieving %s: model value was nil", id)
}

var model LocationDataSourceModel
if err := metadata.Decode(&model); err != nil {
return fmt.Errorf("decoding: %+v", err)
}

normalizedLocation := location.Normalize(model.Location)

locationValue, err := getLocation(normalizedLocation, resp.Model.Value)
if err != nil {
return err
}

var state LocationDataSourceModel
state.ZoneMappings = flattenZonesMapping(locationValue)
state.DisplayName = pointer.From(locationValue.DisplayName)
state.Location = normalizedLocation

metadata.ResourceData.SetId(fmt.Sprintf("%s/locations/%s", id.ID(), normalizedLocation))

return metadata.Encode(&state)
},
}
}

func getLocation(location string, input *[]resourcesSubscription.Location) (*resourcesSubscription.Location, error) {
for _, item := range *input {
if pointer.From(item.Name) == location && strings.EqualFold(string(pointer.From(item.Metadata.RegionType)), "Physical") {
return &item, nil
}
}

return nil, fmt.Errorf("no location was found for %q", location)
}

func flattenZonesMapping(location *resourcesSubscription.Location) (zoneMappings []LocationZoneMapping) {
zoneMappings = make([]LocationZoneMapping, 0)

if location == nil || location.AvailabilityZoneMappings == nil {
return zoneMappings
}

for _, zoneMapping := range *location.AvailabilityZoneMappings {
locationZoneMapping := LocationZoneMapping{
LogicalZone: pointer.From(zoneMapping.LogicalZone),
PhysicalZone: pointer.From(zoneMapping.PhysicalZone),
}

zoneMappings = append(zoneMappings, locationZoneMapping)
}

return zoneMappings
}
57 changes: 57 additions & 0 deletions internal/services/subscription/location_data_source_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package subscription_test

import (
"fmt"
"regexp"
"testing"

"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance"
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check"
)

type LocationsDataSource struct{}

func TestAccLocationDataSource_NonExistingRegion(t *testing.T) {
data := acceptance.BuildTestData(t, "data.azurerm_location", "test")

data.DataSourceTest(t, []acceptance.TestStep{
{
Config: LocationsDataSource{}.basic("not-existing-region"),
ExpectError: regexp.MustCompile("\"not-existing-region\" was not found in the list of supported Azure Locations"),
},
})
}

func TestAccLocationDataSource_westUS(t *testing.T) {
data := acceptance.BuildTestData(t, "data.azurerm_location", "test")

data.DataSourceTest(t, []acceptance.TestStep{
{
Config: LocationsDataSource{}.basic("westus"),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).Key("display_name").HasValue("West US"),
check.That(data.ResourceName).Key("zone_mappings.0.logical_zone").HasValue("1"),
check.That(data.ResourceName).Key("zone_mappings.1.logical_zone").HasValue("2"),
check.That(data.ResourceName).Key("zone_mappings.2.logical_zone").HasValue("3"),
check.That(data.ResourceName).Key("zone_mappings.0.physical_zone").IsNotEmpty(),
check.That(data.ResourceName).Key("zone_mappings.1.physical_zone").IsNotEmpty(),
check.That(data.ResourceName).Key("zone_mappings.2.physical_zone").IsNotEmpty(),
),
},
})
}

func (d LocationsDataSource) basic(location string) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}

data "azurerm_location" "test" {
location = "%s"
}
`, location)
}
15 changes: 14 additions & 1 deletion internal/services/subscription/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import (

type Registration struct{}

var _ sdk.UntypedServiceRegistrationWithAGitHubLabel = Registration{}
var (
_ sdk.TypedServiceRegistrationWithAGitHubLabel = Registration{}
_ sdk.UntypedServiceRegistrationWithAGitHubLabel = Registration{}
)

func (r Registration) AssociatedGitHubLabel() string {
return "service/subscription"
Expand Down Expand Up @@ -43,3 +46,13 @@ func (r Registration) SupportedResources() map[string]*pluginsdk.Resource {
"azurerm_subscription": resourceSubscription(),
}
}

func (r Registration) DataSources() []sdk.DataSource {
return []sdk.DataSource{
LocationDataSource{},
}
}

func (r Registration) Resources() []sdk.Resource {
return []sdk.Resource{}
}
Loading
Loading