From d2a338f57d099bb3838d42031ecc88cdb4ad0317 Mon Sep 17 00:00:00 2001 From: Jeffrey Chen Date: Wed, 10 Dec 2025 20:56:25 +0000 Subject: [PATCH] Implement TargetResourceResolver for containerAppTarget instead of using `SupportsDelayedProvisioning()` --- cli/azd/pkg/project/service_target.go | 12 +++--- .../project/service_target_containerapp.go | 41 +++++++++++++++++++ 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/cli/azd/pkg/project/service_target.go b/cli/azd/pkg/project/service_target.go index 2ed744f289d..5ce1feae9cd 100644 --- a/cli/azd/pkg/project/service_target.go +++ b/cli/azd/pkg/project/service_target.go @@ -147,13 +147,15 @@ func (st ServiceTargetKind) IgnoreFile() string { } } -// SupportsDelayedProvisioning returns true if the service target kind -// supports delayed provisioning resources at deployment time, otherwise false. +// SupportsDelayedProvisioning returns true if the service target supports provisioning +// resources during deployment when they don't exist yet. // -// As an example, ContainerAppTarget is able to provision the container app as part of deployment, -// and thus returns true. +// Deprecated: Use TargetResourceResolver interface for custom resource resolution instead. +// Container Apps supports delayed provisioning but implements TargetResourceResolver to +// provide better error messages when resources are missing in non-bicep deployment scenarios. +// Retained only for AKS backward compatibility. func (st ServiceTargetKind) SupportsDelayedProvisioning() bool { - return st == AksTarget || st == ContainerAppTarget + return st == AksTarget } func checkResourceType(resource *environment.TargetResource, expectedResourceType azapi.AzureResourceType) error { diff --git a/cli/azd/pkg/project/service_target_containerapp.go b/cli/azd/pkg/project/service_target_containerapp.go index 3529bc3e3bf..b598b0177a7 100644 --- a/cli/azd/pkg/project/service_target_containerapp.go +++ b/cli/azd/pkg/project/service_target_containerapp.go @@ -6,6 +6,7 @@ package project import ( "context" "encoding/json" + "errors" "fmt" "log" "os" @@ -16,6 +17,7 @@ import ( "github.com/azure/azure-dev/cli/azd/pkg/async" "github.com/azure/azure-dev/cli/azd/pkg/azapi" "github.com/azure/azure-dev/cli/azd/pkg/azure" + "github.com/azure/azure-dev/cli/azd/pkg/azureutil" "github.com/azure/azure-dev/cli/azd/pkg/containerapps" "github.com/azure/azure-dev/cli/azd/pkg/environment" "github.com/azure/azure-dev/cli/azd/pkg/exec" @@ -395,6 +397,45 @@ func (at *containerAppTarget) validateTargetResource( return nil } +// ResolveTargetResource implements TargetResourceResolver for containerAppTarget. +// It attempts to find an existing container app resource. If not found, it returns a partial +// target resource (with resource group but no resource name) to support bicep-based deployments +// that provision the container app on-demand. +func (at *containerAppTarget) ResolveTargetResource( + ctx context.Context, + subscriptionId string, + serviceConfig *ServiceConfig, + defaultResolver func() (*environment.TargetResource, error), +) (*environment.TargetResource, error) { + targetResource, err := defaultResolver() + if err == nil { + return targetResource, nil + } + + var resourceNotFoundError *azureutil.ResourceNotFoundError + if errors.As(err, &resourceNotFoundError) { + resourceGroupTemplate := serviceConfig.ResourceGroupName + if resourceGroupTemplate.Empty() { + resourceGroupTemplate = serviceConfig.Project.ResourceGroupName + } + + resourceGroupName, rgErr := at.resourceManager.GetResourceGroupName(ctx, subscriptionId, resourceGroupTemplate) + if rgErr != nil { + return nil, err + } + + // Return partial target resource to enable bicep-based deployments + return environment.NewTargetResource( + subscriptionId, + resourceGroupName, + "", + string(azapi.AzureResourceTypeContainerApp), + ), nil + } + + return nil, err +} + func (at *containerAppTarget) addPreProvisionChecks(ctx context.Context, serviceConfig *ServiceConfig) error { // Attempt to retrieve the target resource for the current service // This allows the resource deployment to detect whether or not to pull existing container image during