Skip to content

Filter Region list by provider/service availability being used in project #5424

@spboyer

Description

@spboyer

Findings are showing that there are unsuccessful provision attempts due to services/providers not available in the region selected for a service that is in the project.

We are already filtering the available regions by subscription, we can add the services in the project as an additional filter to further reduce the list.

There are some of the templates that attempt to do this via the allowed parameter in the bicep files, but this is a manual list and has room for error.

Proposed solution -

Extend Existing Method with Optional Filtering
A backwards-compatible approach would be to add optional parameters:

// ...existing code...

// LocationFilterOptions provides filtering options for location queries
type LocationFilterOptions struct {
    ResourceProviders []string // Filter by resource provider availability
    ServiceTypes      []string // Filter by specific service types
    SkipQuotaCheck    bool     // Skip quota availability checks
}

// ListSubscriptionLocationsWithFilter lists physical locations with optional filtering
func (s *SubscriptionsService) ListSubscriptionLocationsWithFilter(
    ctx context.Context, 
    subscriptionId string, 
    tenantId string,
    options *LocationFilterOptions,
) ([]Location, error) {
    client, err := s.createSubscriptionsClient(ctx, tenantId)
    if err != nil {
        return nil, err
    }

    locations := []Location{}
    pager := client.NewListLocationsPager(subscriptionId, nil)

    for pager.More() {
        page, err := pager.NextPage(ctx)
        if err != nil {
            return nil, fmt.Errorf("failed getting next page of locations: %w", err)
        }

        for _, location := range page.LocationListResult.Value {
            // Only include physical locations
            if *location.Metadata.RegionType == "Physical" &&
                !compare.PtrValueEquals(location.Metadata.PhysicalLocation, "") {
                
                locationName := *location.Name
                
                // Apply filters if specified
                if options != nil {
                    if len(options.ResourceProviders) > 0 {
                        supported, err := s.checkProviderAvailability(ctx, subscriptionId, tenantId, locationName, options.ResourceProviders)
                        if err != nil || !supported {
                            continue
                        }
                    }
                    
                    if len(options.ServiceTypes) > 0 {
                        supported, err := s.checkServiceAvailability(ctx, subscriptionId, tenantId, locationName, options.ServiceTypes)
                        if err != nil || !supported {
                            continue
                        }
                    }
                }

                displayName := convert.ToValueWithDefault(location.DisplayName, locationName)
                regionalDisplayName := convert.ToValueWithDefault(location.RegionalDisplayName, displayName)

                locations = append(locations, Location{
                    Name:                locationName,
                    DisplayName:         displayName,
                    RegionalDisplayName: regionalDisplayName,
                })
            }
        }
    }

    sort.Slice(locations, func(i, j int) bool {
        return locations[i].RegionalDisplayName < locations[j].RegionalDisplayName
    })

    return locations, nil
}

Integration with Existing Prompt System
You would also need to update the prompt system to use the filtered locations. This would involve modifying pkg/azureutil/location.go

// Add new function that accepts service filters
func PromptLocationWithServiceFilter(
    ctx context.Context,
    subscriptionId string,
    message string,
    help string,
    console input.Console,
    accountManager account.Manager,
    shouldDisplay func(account.Location) bool,
    defaultSelectedLocation *string,
    serviceTypes []string, // New parameter for service filtering
) (string, error) {
    // Get filtered locations instead of all locations
    allLocations, err := accountManager.GetLocationsForServices(ctx, subscriptionId, serviceTypes)
    if err != nil {
        return "", fmt.Errorf("listing locations: %w", err)
    }

    // Rest of the function remains the same...
}

We would have to parse the bicep files for the services.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions