Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 13, 2026

Users were selecting regions where project services aren't available, causing provision failures (e.g., Static Web Apps unavailable in France Central).

Implementation

Resource Type Extraction (pkg/azure/arm_template.go)

  • Parse compiled ARM templates to extract resource types from resources[] array
  • Returns deduplicated list like ["Microsoft.App/containerApps", "Microsoft.DBforPostgreSQL/flexibleServers"]

Provider Availability Check (pkg/account/subscriptions.go)

  • Query Azure Provider APIs per location to verify resource type registration and availability
  • Filter locations to those supporting ALL required resource types
  • Best-effort with error logging - API failures don't block user

Integration (pkg/infra/provisioning/bicep/)

  • Extract resource types during EnsureEnv() after Bicep compilation
  • Pass to location prompt via new PromptLocationWithResourceTypes() method
  • Works alongside existing allowedValues and quota filtering

Interface Extensions

  • Manager.GetLocationsWithFilter(resourceTypes []string) - filtered location retrieval
  • Prompter.PromptLocationWithResourceTypes(resourceTypes []string) - filtered prompting
  • EnsureSubscriptionAndLocationOptions.ResourceTypes - provisioning options

Example

Project using Container Apps + PostgreSQL:

// Bicep compiles to ARM template
resourceTypes := azure.ExtractResourceTypes(armTemplate)
// ["Microsoft.App/containerApps", "Microsoft.DBforPostgreSQL/flexibleServers"]

// Only ~30 compatible locations shown instead of 60+ total
locations := manager.GetLocationsWithFilter(ctx, subId, resourceTypes)

Notes

  • Backward compatible: when resourceTypes is nil/empty, no filtering occurs
  • Bicepparam mode skipped (requires subscription/location before compilation)
  • Invalid resource types logged and skipped
  • Provider API errors logged but don't fail operation

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • code.cloudfoundry.org
    • Triggering command: /update-job-proxy /update-job-proxy DROP bin/bash test -e cef98f5.0 /opt/hostedtoolc-e /bin/test rvice-availabili/usr/bin/git -buildtags /home/REDACTED/.do--global test -e oot_CA_2022.pem git est 1db302536c17e6f4/usr/sbin/iptables les ache/go/1.25.5/x-t test (dns block)
  • dario.cat
    • Triggering command: /update-job-proxy /update-job-proxy DROP bin/bash test -e cef98f5.0 /opt/hostedtoolc-e /bin/test rvice-availabili/usr/bin/git -buildtags /home/REDACTED/.do--global test -e oot_CA_2022.pem git est 1db302536c17e6f4/usr/sbin/iptables les ache/go/1.25.5/x-t test (dns block)
  • go.googlesource.com
    • Triggering command: /update-job-proxy /update-job-proxy DROP bin/bash test -e cef98f5.0 /opt/hostedtoolc-e /bin/test rvice-availabili/usr/bin/git -buildtags /home/REDACTED/.do--global test -e oot_CA_2022.pem git est 1db302536c17e6f4/usr/sbin/iptables les ache/go/1.25.5/x-t test (dns block)
  • go.opentelemetry.io
    • Triggering command: /update-job-proxy /update-job-proxy DROP bin/bash test -e cef98f5.0 /opt/hostedtoolc-e /bin/test rvice-availabili/usr/bin/git -buildtags /home/REDACTED/.do--global test -e oot_CA_2022.pem git est 1db302536c17e6f4/usr/sbin/iptables les ache/go/1.25.5/x-t test (dns block)
  • go.uber.org
    • Triggering command: /update-job-proxy /update-job-proxy DROP bin/bash test -e cef98f5.0 /opt/hostedtoolc-e /bin/test rvice-availabili/usr/bin/git -buildtags /home/REDACTED/.do--global test -e oot_CA_2022.pem git est 1db302536c17e6f4/usr/sbin/iptables les ache/go/1.25.5/x-t test (dns block)
  • google.golang.org
    • Triggering command: /update-job-proxy /update-job-proxy DROP bin/bash test -e cef98f5.0 /opt/hostedtoolc-e /bin/test rvice-availabili/usr/bin/git -buildtags /home/REDACTED/.do--global test -e oot_CA_2022.pem git est 1db302536c17e6f4/usr/sbin/iptables les ache/go/1.25.5/x-t test (dns block)
  • gopkg.in
    • Triggering command: /update-job-proxy /update-job-proxy DROP bin/bash test -e cef98f5.0 /opt/hostedtoolc-e /bin/test rvice-availabili/usr/bin/git -buildtags /home/REDACTED/.do--global test -e oot_CA_2022.pem git est 1db302536c17e6f4/usr/sbin/iptables les ache/go/1.25.5/x-t test (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>Filter Region list by provider/service availability being used in project</issue_title>
<issue_description>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.</issue_description>

Comments on the Issue (you are @copilot in this section)

@weikanglim @spboyer Do you have a couple of a quick examples in mind?

I suspect what this ends up looking, in the case of a single location being used across multiple resources, could look like:

@metadata{
  azd:{
    // these resources use this same location variable
    resour...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

- Fixes Azure/azure-dev#5424

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

Copilot AI and others added 4 commits January 13, 2026 16:18
Co-authored-by: spboyer <7681382+spboyer@users.noreply.github.com>
Co-authored-by: spboyer <7681382+spboyer@users.noreply.github.com>
Co-authored-by: spboyer <7681382+spboyer@users.noreply.github.com>
Co-authored-by: spboyer <7681382+spboyer@users.noreply.github.com>
Copilot AI changed the title [WIP] Filter region list by service availability in project Filter location prompts by resource provider availability Jan 13, 2026
Copilot AI requested a review from spboyer January 13, 2026 16:33
@spboyer spboyer marked this pull request as ready for review January 14, 2026 17:03
Copilot AI review requested due to automatic review settings January 14, 2026 17:03
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements location filtering based on Azure resource provider availability to prevent provisioning failures when users select regions where required services are unavailable. The implementation extracts resource types from compiled ARM templates and filters location prompts to only show regions that support all required resource types.

Changes:

  • Adds ARM template parsing to extract resource types from compiled Bicep templates
  • Implements Azure Provider API integration to check resource type availability per location
  • Updates location prompting flow to filter by resource availability alongside existing quota filtering
  • Maintains backward compatibility by only applying filters when resource types are provided

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
cli/azd/pkg/azure/arm_template.go Adds ExtractResourceTypes function to parse ARM templates and extract unique resource types
cli/azd/pkg/azure/arm_template_test.go Comprehensive unit tests for ARM template resource type extraction with various scenarios
cli/azd/pkg/account/subscriptions.go Implements resource type availability checking via Azure Provider APIs with location filtering
cli/azd/pkg/account/subscriptions_manager.go Adds ListLocationsWithFilter method to manager layer
cli/azd/pkg/account/manager.go Extends Manager interface with GetLocationsWithFilter method
cli/azd/pkg/infra/provisioning/manager.go Adds ResourceTypes field to options and integrates filtered location prompting
cli/azd/pkg/infra/provisioning/bicep/bicep_provider.go Extracts resource types during EnsureEnv and stores for parameter prompting
cli/azd/pkg/infra/provisioning/bicep/prompt.go Updates location prompts to use resource type filtering
cli/azd/pkg/prompt/prompter.go Adds PromptLocationWithResourceTypes method to Prompter interface
cli/azd/pkg/azureutil/location.go Implements PromptLocationWithResourceTypeFilter with internal routing logic
cli/azd/test/mocks/mockaccount/mock_manager.go Updates mock to implement new GetLocationsWithFilter method
cli/azd/extensions/azure.ai.finetune/internal/utils/environment.go Minor whitespace formatting fix
cli/azd/extensions/azure.ai.finetune/go.mod Moves github.com/sethvargo/go-retry from indirect to direct dependency
Comments suppressed due to low confidence (2)

cli/azd/pkg/infra/provisioning/bicep/bicep_provider.go:148

  • In bicepparam mode, location prompting happens before compilation (line 144-147), but resource types are only extracted after compilation (line 156-162). This means bicepparam mode users won't benefit from resource type filtering since p.extractedResourceTypes will be empty when EnsureSubscriptionAndLocation is called. The PR description notes this limitation, but the implementation should either: 1) Pass p.extractedResourceTypes to EnsureSubscriptionAndLocation options for bicepparam mode (it will be empty but makes intent clear), or 2) Add a TODO comment explaining why bicepparam mode doesn't get filtering.
	if p.mode == bicepparamMode {
		if err := provisioning.EnsureSubscriptionAndLocation(
			ctx, p.envManager, p.env, p.prompters, provisioning.EnsureSubscriptionAndLocationOptions{}); err != nil {
			return err
		}
	}

cli/azd/pkg/account/subscriptions.go:214

  • Direct fmt.Printf calls bypass the structured logging system. Use the log package or a structured logger instead. This is inconsistent with the telemetry and logging infrastructure used elsewhere in azd.
			fmt.Printf(
				"warning: skipping invalid resource type format '%s' (expected 'Provider/Type')\n",
				resourceType)

if err != nil {
// Log error but continue with other locations to provide best-effort filtering.
// If all locations fail, an empty list will be returned, prompting the user to check permissions.
fmt.Printf("warning: failed to check resource availability for location %s: %v\n", location.Name, err)
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Direct fmt.Printf calls bypass the structured logging system. Use the log package or a structured logger instead. This is inconsistent with the telemetry and logging infrastructure used elsewhere in azd.

This issue also appears in the following locations of the same file:

  • line 212

Copilot uses AI. Check for mistakes.
}

// Check resource type availability for each location
filteredLocations := []Location{}
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the capacity is unknown but likely to be less than len(allLocations), consider using make([]Location, 0, len(allLocations)) to pre-allocate the slice with capacity to avoid reallocations during append operations.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants