File: ContainerAppJobContext.cs
Web Access
Project: src\src\Aspire.Hosting.Azure.AppContainers\Aspire.Hosting.Azure.AppContainers.csproj (Aspire.Hosting.Azure.AppContainers)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
#pragma warning disable ASPIREAZURE002 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
 
using Aspire.Hosting.ApplicationModel;
using Azure.Provisioning;
using Azure.Provisioning.AppContainers;
using Azure.Provisioning.Expressions;
using Azure.Provisioning.Resources;
 
namespace Aspire.Hosting.Azure;
 
internal sealed class ContainerAppJobContext(IResource resource, ContainerAppEnvironmentContext containerAppEnvironmentContext)
    : BaseContainerAppContext(resource, containerAppEnvironmentContext)
{
    public override void BuildContainerApp(AzureResourceInfrastructure infra)
    {
        _infrastructure = infra;
        // Write a fake parameter for the container app environment
        // so azd knows the Dashboard URL - see https://github.com/dotnet/aspire/issues/8449.
        // This is temporary until a real fix can be made in azd.
        AllocateParameter(_containerAppEnvironmentContext.Environment.ContainerAppDomain);
 
        var containerAppIdParam = AllocateParameter(_containerAppEnvironmentContext.Environment.ContainerAppEnvironmentId);
 
        ProvisioningParameter? containerImageParam = null;
 
        if (!TryGetContainerImageName(Resource, out var containerImageName))
        {
            AllocateContainerRegistryParameters();
 
            containerImageParam = AllocateContainerImageParameter();
        }
 
        var containerAppResource = CreateContainerAppJob();
 
        BicepValue<string>? containerAppIdentityId = null;
 
        if (Resource.TryGetLastAnnotation<AppIdentityAnnotation>(out var appIdentityAnnotation))
        {
            var appIdentityResource = appIdentityAnnotation.IdentityResource;
 
            containerAppIdentityId = appIdentityResource.Id.AsProvisioningParameter(infra);
 
            var id = BicepFunction.Interpolate($"{containerAppIdentityId}").Compile().ToString();
 
            containerAppResource.Identity.ManagedServiceIdentityType = ManagedServiceIdentityType.UserAssigned;
            containerAppResource.Identity.UserAssignedIdentities[id] = new UserAssignedIdentityDetails();
        }
 
        AddContainerRegistryManagedIdentity(containerAppResource.Identity);
 
        containerAppResource.EnvironmentId = containerAppIdParam;
 
        var configuration = containerAppResource.Configuration;
 
        AddContainerRegistryParameters(reg => configuration.Registries = reg);
 
        var template = new ContainerAppJobTemplate();
        containerAppResource.Template = template;
 
        var containerAppContainer = new ContainerAppContainer();
        template.Containers = [containerAppContainer];
 
        containerAppContainer.Image = containerImageParam is null ? containerImageName! : containerImageParam;
        containerAppContainer.Name = NormalizedContainerAppName;
 
        SetEntryPoint(containerAppContainer);
        AddEnvironmentVariablesAndCommandLineArgs(
            containerAppContainer,
            () => configuration.Secrets ??= [],
            containerAppIdentityId);
        AddAzureClientId(appIdentityAnnotation?.IdentityResource, containerAppContainer.Env);
        AddVolumes(template.Volumes, containerAppContainer);
 
        infra.Add(containerAppResource);
 
        if (Resource.TryGetAnnotationsOfType<AzureContainerAppJobCustomizationAnnotation>(out var annotations))
        {
            foreach (var a in annotations)
            {
                a.Configure(infra, containerAppResource);
            }
        }
    }
 
    private ContainerAppJob CreateContainerAppJob()
    {
        var containerApp = new ContainerAppJob(Infrastructure.NormalizeBicepIdentifier(Resource.Name))
        {
            Name = NormalizedContainerAppName
        };
 
        var configuration = new ContainerAppJobConfiguration()
        {
            ReplicaTimeout = 1800,
            TriggerType = ContainerAppJobTriggerType.Manual,
        };
        containerApp.Configuration = configuration;
 
        if (Resource.HasAnnotationOfType<AzureFunctionsAnnotation>())
        {
            throw new NotSupportedException("Azure Container App Jobs are not supported with Azure Functions.");
        }
 
        return containerApp;
    }
}