Skip to content
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
1 change: 1 addition & 0 deletions src/containerapp/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ upcoming
* 'az containerapp auth update': Support authenticating blob storage token store using managed identity with `--blob-container-uri` and `--blob-container-identity`.
* 'az containerapp env create': Set identity only when `--mi-system-assigned` or `--mi-user-assigned` is specified.
* 'az containerapp env create': Set identity only when `--system-assigned` or `--user-assigned` is specified.
* 'az containerapp up': Support deploying Azure AI Foundry model to Container App with `--model-registry`, `--model-name`, `--model-version`.

1.1.0b4
++++++
Expand Down
3 changes: 3 additions & 0 deletions src/containerapp/azext_containerapp/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@
- name: Create a container app from an image in a registry on a connected environment
text: |
az containerapp up -n my-containerapp --image myregistry.azurecr.io/myImage:myTag --environment MyConnectedEnvironmentId
- name: Create a container app and deploy a model from Azure AI Foundry
text: |
az containerapp up -n my-containerapp -l westus3 --model-registry azureml --model-name Phi-4 --model-version 7
"""


Expand Down
5 changes: 5 additions & 0 deletions src/containerapp/azext_containerapp/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,11 @@ def load_arguments(self, _):
c.argument('user_assigned', nargs='+', help="Space-separated user identities to be assigned.")
c.argument('system_assigned', help="Boolean indicating whether to assign system-assigned identity.", action='store_true')

with self.argument_context('containerapp up', arg_group='Deploy an Azure AI Foundry Model', is_preview=True) as c:
c.argument('model_registry', help="The name of the Azure AI Foundry model registry.", is_preview=True)
c.argument('model_name', help="The name of the Azure AI Foundry model.", is_preview=True)
c.argument('model_version', help="The version of the Azure AI Foundry model.", is_preview=True)

with self.argument_context('containerapp auth') as c:
c.argument('blob_container_uri', help='The URI of the blob storage containing the tokens. Should not be used along with sas_url_secret and sas_url_secret_name.', is_preview=True)
c.argument('blob_container_identity', options_list=['--blob-container-identity', '--bci'],
Expand Down
231 changes: 226 additions & 5 deletions src/containerapp/azext_containerapp/_up_utils.py

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,20 @@ def set_up_workload_profiles(self):
"maximumCount": 1
}
workload_profiles.append(gpu_profile)
if self.is_env_for_azml_app() and not self.get_argument_enable_dedicated_gpu():
wp_type = self.get_argument_workload_profile_type()
if wp_type is None or wp_type.lower() == "consumption-gpu-nc24-a100":
serverless_a100_profile = {
"workloadProfileType": "Consumption-GPU-NC24-A100",
"name": self.get_argument_workload_profile_name() if self.get_argument_workload_profile_name() else "serverless-A100",
}
workload_profiles.append(serverless_a100_profile)
else:
serverless_gpu_profile = {
"workloadProfileType": wp_type,
"name": self.get_argument_workload_profile_name() if self.get_argument_workload_profile_name() else "serverless-gpu",
}
workload_profiles.append(serverless_gpu_profile)
self.managed_env_def["properties"]["workloadProfiles"] = workload_profiles

def set_up_custom_domain_configuration(self):
Expand Down Expand Up @@ -182,6 +196,15 @@ def get_argument_certificate_key_vault_url(self):
def get_argument_public_network_access(self):
return self.get_param("public_network_access")

def is_env_for_azml_app(self):
return self.get_param("is_env_for_azml_app")

def get_argument_workload_profile_type(self):
return self.get_param("workload_profile_type")

def get_argument_workload_profile_name(self):
return self.get_param("workload_profile_name")


class ContainerappEnvPreviewUpdateDecorator(ContainerAppEnvUpdateDecorator):
def validate_arguments(self):
Expand Down
104 changes: 99 additions & 5 deletions src/containerapp/azext_containerapp/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,79 @@ def create_managed_environment(cmd,
system_assigned=False,
user_assigned=None,
public_network_access=None):
return create_managed_environment_logic(
cmd=cmd,
name=name,
resource_group_name=resource_group_name,
logs_destination=logs_destination,
storage_account=storage_account,
logs_customer_id=logs_customer_id,
logs_key=logs_key,
location=location,
instrumentation_key=instrumentation_key,
dapr_connection_string=dapr_connection_string,
infrastructure_subnet_resource_id=infrastructure_subnet_resource_id,
infrastructure_resource_group=infrastructure_resource_group,
docker_bridge_cidr=docker_bridge_cidr,
platform_reserved_cidr=platform_reserved_cidr,
platform_reserved_dns_ip=platform_reserved_dns_ip,
internal_only=internal_only,
tags=tags,
disable_warnings=disable_warnings,
zone_redundant=zone_redundant,
hostname=hostname,
certificate_file=certificate_file,
certificate_password=certificate_password,
certificate_identity=certificate_identity,
certificate_key_vault_url=certificate_key_vault_url,
enable_workload_profiles=enable_workload_profiles,
mtls_enabled=mtls_enabled,
p2p_encryption_enabled=p2p_encryption_enabled,
enable_dedicated_gpu=enable_dedicated_gpu,
no_wait=no_wait,
logs_dynamic_json_columns=logs_dynamic_json_columns,
system_assigned=system_assigned,
user_assigned=user_assigned,
public_network_access=public_network_access
)


def create_managed_environment_logic(cmd,
name,
resource_group_name,
logs_destination="log-analytics",
storage_account=None,
logs_customer_id=None,
logs_key=None,
location=None,
instrumentation_key=None,
dapr_connection_string=None,
infrastructure_subnet_resource_id=None,
infrastructure_resource_group=None,
docker_bridge_cidr=None,
platform_reserved_cidr=None,
platform_reserved_dns_ip=None,
internal_only=False,
tags=None,
disable_warnings=False,
zone_redundant=False,
hostname=None,
certificate_file=None,
certificate_password=None,
certificate_identity=None,
certificate_key_vault_url=None,
enable_workload_profiles=True,
mtls_enabled=None,
p2p_encryption_enabled=None,
enable_dedicated_gpu=False,
no_wait=False,
logs_dynamic_json_columns=False,
system_assigned=False,
user_assigned=None,
public_network_access=None,
workload_profile_type=None,
workload_profile_name=None,
is_env_for_azml_app=False):
raw_parameters = locals()
containerapp_env_create_decorator = ContainerappEnvPreviewCreateDecorator(
cmd=cmd,
Expand Down Expand Up @@ -1244,16 +1317,27 @@ def containerapp_up(cmd,
user_assigned=None,
system_assigned=None,
custom_location_id=None,
connected_cluster_id=None):
connected_cluster_id=None,
model_registry=None,
model_name=None,
model_version=None):
from ._up_utils import (_validate_up_args, _validate_custom_location_connected_cluster_args, _reformat_image, _get_dockerfile_content, _get_ingress_and_target_port,
ResourceGroup, Extension, CustomLocation, ContainerAppEnvironment, ContainerApp, _get_registry_from_app,
_get_registry_details, _get_registry_details_without_get_creds, _create_github_action, _set_up_defaults, up_output,
check_env_name_on_rg, get_token, _has_dockerfile)
check_env_name_on_rg, get_token, _has_dockerfile, _validate_azml_args, _is_azml_app, _validate_azml_env_and_create_if_needed, _set_azml_env_vars)
from azure.cli.command_modules.containerapp._github_oauth import cache_github_token
HELLOWORLD = "mcr.microsoft.com/k8se/quickstart"
ACA_AZML_MCR = "mcr.microsoft.com/k8se/aca-foundry-model-host"
dockerfile = "Dockerfile" # for now the dockerfile name must be "Dockerfile" (until GH actions API is updated)

register_provider_if_needed(cmd, CONTAINER_APPS_RP)
is_azureml_app = _is_azml_app(model_registry, model_name, model_version)
model_asset_id = None
model_reference_endpoint = None
if is_azureml_app:
model_asset_id, model_reference_endpoint, model_type, model_load_class, image = _validate_azml_args(cmd, ACA_AZML_MCR, image, model_registry, model_name, model_version)
env_vars = _set_azml_env_vars(cmd, env_vars, model_asset_id, model_reference_endpoint, model_type, model_load_class, is_azml_mcr_app=image.lower() == ACA_AZML_MCR.lower())

_validate_up_args(cmd, source, artifact, build_env_vars, image, repo, registry_server)
validate_create(registry_identity=registry_identity,
registry_pass=registry_pass,
Expand All @@ -1280,6 +1364,10 @@ def containerapp_up(cmd,
ingress = "external" if not ingress else ingress
target_port = 80 if not target_port else target_port

if image and ACA_AZML_MCR in image.lower():
ingress = "external" if not ingress else ingress
target_port = 8000 if not target_port else target_port

if image:
if ingress and not target_port and target_port != 0:
target_port = 0
Expand All @@ -1294,7 +1382,7 @@ def containerapp_up(cmd,
resource_group = ResourceGroup(cmd, name=resource_group_name, location=location)
custom_location = CustomLocation(cmd, name=custom_location_id, resource_group_name=resource_group_name, connected_cluster_id=connected_cluster_id)
extension = Extension(cmd, logs_rg=resource_group_name, logs_location=location, logs_share_key=logs_key, logs_customer_id=logs_customer_id, connected_cluster_id=connected_cluster_id)
env = ContainerAppEnvironment(cmd, environment, resource_group, location=location, logs_key=logs_key, logs_customer_id=logs_customer_id, custom_location_id=custom_location_id, connected_cluster_id=connected_cluster_id)
env = ContainerAppEnvironment(cmd, environment, resource_group, location=location, logs_key=logs_key, logs_customer_id=logs_customer_id, custom_location_id=custom_location_id, connected_cluster_id=connected_cluster_id, is_env_for_azml_app=is_azureml_app)
app = ContainerApp(cmd, name, resource_group, None, image, env, target_port, registry_server, registry_user, registry_pass, env_vars, workload_profile_name, ingress, registry_identity=registry_identity, user_assigned=user_assigned, system_assigned=system_assigned, revisions_mode=revisions_mode, target_label=target_label)

# Check and see if registry (username and passwords) or registry-identity are specified. If so, set is_registry_server_params_set to True to use those creds.
Expand All @@ -1305,6 +1393,12 @@ def containerapp_up(cmd,
if app.get()["properties"]["provisioningState"] == "InProgress":
raise ValidationError("Containerapp has an existing provisioning in progress. Please wait until provisioning has completed and rerun the command.")

if is_azureml_app:
env, workload_profile_name, app_cpu_limit, app_memory_limit = _validate_azml_env_and_create_if_needed(cmd, app, env, environment, resource_group, resource_group_name, workload_profile_name)
app.workload_profile_name = workload_profile_name
app.cpu = app_cpu_limit
app.memory = app_memory_limit

resource_group.create_if_needed()
extension.create_if_needed()
custom_location.create_if_needed()
Expand Down Expand Up @@ -1337,7 +1431,7 @@ def containerapp_up(cmd,
up_output(app, no_dockerfile=(source and not _has_dockerfile(source, dockerfile)))


def containerapp_up_logic(cmd, resource_group_name, name, managed_env, image, env_vars, ingress, target_port, registry_server, registry_user, workload_profile_name, registry_pass, environment_type=None, force_single_container_updates=False, registry_identity=None, system_assigned=None, user_assigned=None, revisions_mode=None, target_label=None):
def containerapp_up_logic(cmd, resource_group_name, name, managed_env, image, env_vars, ingress, target_port, registry_server, registry_user, workload_profile_name, registry_pass, environment_type=None, force_single_container_updates=False, registry_identity=None, system_assigned=None, user_assigned=None, revisions_mode=None, target_label=None, cpu=None, memory=None):
containerapp_def = None
try:
containerapp_def = ContainerAppPreviewClient.show(cmd=cmd, resource_group_name=resource_group_name, name=name)
Expand All @@ -1349,7 +1443,7 @@ def containerapp_up_logic(cmd, resource_group_name, name, managed_env, image, en
registry_server=registry_server, registry_user=registry_user, registry_pass=registry_pass, workload_profile_name=workload_profile_name, container_name=name, force_single_container_updates=force_single_container_updates,
registry_identity=registry_identity, system_assigned=system_assigned, user_assigned=user_assigned, revisions_mode=revisions_mode, target_label=target_label)
return create_containerapp(cmd=cmd, name=name, resource_group_name=resource_group_name, managed_env=managed_env, image=image, env_vars=env_vars, ingress=ingress, target_port=target_port, registry_server=registry_server, registry_user=registry_user, registry_pass=registry_pass, workload_profile_name=workload_profile_name, environment_type=environment_type,
registry_identity=registry_identity, system_assigned=system_assigned, user_assigned=user_assigned, revisions_mode=revisions_mode, target_label=target_label)
registry_identity=registry_identity, system_assigned=system_assigned, user_assigned=user_assigned, revisions_mode=revisions_mode, target_label=target_label, cpu=cpu, memory=memory)


def list_certificates(cmd, name, resource_group_name, location=None, certificate=None, thumbprint=None, managed_certificates_only=False, private_key_certificates_only=False):
Expand Down
Loading
Loading