Skip to content
Closed
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
5 changes: 5 additions & 0 deletions src/containerapp/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ Release History
===============
upcoming
++++++
* 'az containerapp function show/list': New command group to list and show functions in container apps.
* 'az containerapp function list-keys': List function keys for a specific function in a container app
* 'az containerapp function update-keys': Update specific function key for a specific function in a container app
* 'az containerapp function list-hostkeys': List host keys for a container app
* 'az containerapp function update-hostkeys': Update specific host key for a container app
* 'az containerapp update/up': Disallow changing `--revisions-mode` to Labels.
* 'az containerapp session code-interpreter': Fix `--path` in examples
* 'az containerapp sessionpool create/update': Support `--lifecycle-type` and `--max-alive-period`
Expand Down
148 changes: 148 additions & 0 deletions src/containerapp/azext_containerapp/_clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,78 @@ def list(cls, cmd, resource_group_name, container_app_name):
return policy_list


class ContainerAppFunctionsPreviewClient():
api_version = PREVIEW_API_VERSION

@classmethod
def list_functions_by_revision(cls, cmd, resource_group_name, container_app_name, revision_name):
"""List all functions for a specific revision"""
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager
sub_id = get_subscription_id(cmd.cli_ctx)
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/containerApps/{}/revisions/{}/functions?api-version={}"
request_url = url_fmt.format(
management_hostname.strip('/'),
sub_id,
resource_group_name,
container_app_name,
revision_name,
cls.api_version)

r = send_raw_request(cmd.cli_ctx, "GET", request_url)
return r.json()

@classmethod
def get_function_by_revision(cls, cmd, resource_group_name, container_app_name, revision_name, function_name):
"""Get a specific function for a specific revision"""
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager
sub_id = get_subscription_id(cmd.cli_ctx)
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/containerApps/{}/revisions/{}/functions/{}?api-version={}"
request_url = url_fmt.format(
management_hostname.strip('/'),
sub_id,
resource_group_name,
container_app_name,
revision_name,
function_name,
cls.api_version)

r = send_raw_request(cmd.cli_ctx, "GET", request_url)
return r.json()

@classmethod
def list_functions(cls, cmd, resource_group_name, container_app_name):
"""List all functions for a container app in single revision mode"""
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager
sub_id = get_subscription_id(cmd.cli_ctx)
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/containerApps/{}/functions?api-version={}"
request_url = url_fmt.format(
management_hostname.strip('/'),
sub_id,
resource_group_name,
container_app_name,
cls.api_version)

r = send_raw_request(cmd.cli_ctx, "GET", request_url)
return r.json()

@classmethod
def get_function(cls, cmd, resource_group_name, container_app_name, function_name):
"""Get a specific function for a container app in single revision mode"""
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager
sub_id = get_subscription_id(cmd.cli_ctx)
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/containerApps/{}/functions/{}?api-version={}"
request_url = url_fmt.format(
management_hostname.strip('/'),
sub_id,
resource_group_name,
container_app_name,
function_name,
cls.api_version)

r = send_raw_request(cmd.cli_ctx, "GET", request_url)
return r.json()


class DaprComponentResiliencyPreviewClient():
api_version = PREVIEW_API_VERSION

Expand Down Expand Up @@ -1698,3 +1770,79 @@ def remove(cls, cmd, resource_group_name, environment_name):
if r.status_code == 202:
operation_url = r.headers.get(HEADER_LOCATION)
poll_results(cmd, operation_url)

class ContainerAppFunctionsPreviewClient:
api_version = PREVIEW_API_VERSION
Copy link

Copilot AI Sep 19, 2025

Choose a reason for hiding this comment

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

Missing indentation. This line should be indented to be part of the class definition.

Suggested change
api_version = PREVIEW_API_VERSION
api_version = PREVIEW_API_VERSION

Copilot uses AI. Check for mistakes.

@classmethod
def list_function_keys(cls, cmd, resource_group_name, name, function_name):
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager
sub_id = get_subscription_id(cmd.cli_ctx)
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/containerapps/{}/functions/{}/listkeys?api-version={}"
request_url = url_fmt.format(
management_hostname.strip('/'),
sub_id,
resource_group_name,
name,
function_name,
cls.api_version)

r = send_raw_request(cmd.cli_ctx, "POST", request_url)
return r.json()

@classmethod
def update_function_keys(cls, cmd, resource_group_name, name, function_name, key_name, key_value=None):
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager
sub_id = get_subscription_id(cmd.cli_ctx)
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/containerapps/{}/functions/{}/keys/{}?api-version={}"
request_url = url_fmt.format(
management_hostname.strip('/'),
sub_id,
resource_group_name,
name,
function_name,
key_name,
cls.api_version)

body = {}
if key_value:
body["value"] = key_value

r = send_raw_request(cmd.cli_ctx, "PUT", request_url, body=json.dumps(body))
return r.json()

@classmethod
def list_host_keys(cls, cmd, resource_group_name, name):
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager
sub_id = get_subscription_id(cmd.cli_ctx)
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/containerapps/{}/host/default/listkeys?api-version={}"
request_url = url_fmt.format(
management_hostname.strip('/'),
sub_id,
resource_group_name,
name,
cls.api_version)

r = send_raw_request(cmd.cli_ctx, "POST", request_url)
return r.json()

@classmethod
def update_host_keys(cls, cmd, resource_group_name, name, key_type, key_name, key_value=None):
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager
sub_id = get_subscription_id(cmd.cli_ctx)
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/containerapps/{}/host/default/{}/{}?api-version={}"
request_url = url_fmt.format(
management_hostname.strip('/'),
sub_id,
resource_group_name,
name,
key_type,
key_name,
cls.api_version)

body = {}
if key_value:
body["value"] = key_value

r = send_raw_request(cmd.cli_ctx, "PUT", request_url, body=json.dumps(body))
return r.json()
Comment on lines +1774 to +1848
Copy link

Copilot AI Sep 19, 2025

Choose a reason for hiding this comment

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

Duplicate class definition. There's already a ContainerAppFunctionsPreviewClient class defined at line 306. This creates a naming conflict and the second definition will override the first one.

Suggested change
class ContainerAppFunctionsPreviewClient:
api_version = PREVIEW_API_VERSION
@classmethod
def list_function_keys(cls, cmd, resource_group_name, name, function_name):
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager
sub_id = get_subscription_id(cmd.cli_ctx)
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/containerapps/{}/functions/{}/listkeys?api-version={}"
request_url = url_fmt.format(
management_hostname.strip('/'),
sub_id,
resource_group_name,
name,
function_name,
cls.api_version)
r = send_raw_request(cmd.cli_ctx, "POST", request_url)
return r.json()
@classmethod
def update_function_keys(cls, cmd, resource_group_name, name, function_name, key_name, key_value=None):
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager
sub_id = get_subscription_id(cmd.cli_ctx)
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/containerapps/{}/functions/{}/keys/{}?api-version={}"
request_url = url_fmt.format(
management_hostname.strip('/'),
sub_id,
resource_group_name,
name,
function_name,
key_name,
cls.api_version)
body = {}
if key_value:
body["value"] = key_value
r = send_raw_request(cmd.cli_ctx, "PUT", request_url, body=json.dumps(body))
return r.json()
@classmethod
def list_host_keys(cls, cmd, resource_group_name, name):
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager
sub_id = get_subscription_id(cmd.cli_ctx)
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/containerapps/{}/host/default/listkeys?api-version={}"
request_url = url_fmt.format(
management_hostname.strip('/'),
sub_id,
resource_group_name,
name,
cls.api_version)
r = send_raw_request(cmd.cli_ctx, "POST", request_url)
return r.json()
@classmethod
def update_host_keys(cls, cmd, resource_group_name, name, key_type, key_name, key_value=None):
management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager
sub_id = get_subscription_id(cmd.cli_ctx)
url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/containerapps/{}/host/default/{}/{}?api-version={}"
request_url = url_fmt.format(
management_hostname.strip('/'),
sub_id,
resource_group_name,
name,
key_type,
key_name,
cls.api_version)
body = {}
if key_value:
body["value"] = key_value
r = send_raw_request(cmd.cli_ctx, "PUT", request_url, body=json.dumps(body))
return r.json()
# Duplicate definition of ContainerAppFunctionsPreviewClient removed to resolve naming conflict.

Copilot uses AI. Check for mistakes.
78 changes: 77 additions & 1 deletion src/containerapp/azext_containerapp/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,6 @@
az containerapp up -n my-containerapp --image my-app:v1.0 --kind functionapp
"""


helps['containerapp replica count'] = """
type: command
short-summary: Count of a container app's replica(s)
Expand All @@ -179,6 +178,83 @@
az containerapp replica count -n my-containerapp -g MyResourceGroup
"""

helps['containerapp function'] = """
type: group
short-summary: Commands related to Azure Function on Azure Container Apps.
"""

helps['containerapp function list'] = """
type: command
short-summary: List all functions in a container app or a specific revision. (pass --revisionName parameter)
long-summary: |
revisionName is required only if Container App active Revision Mode is setup in Multiple Revision Mode. (Default: Single Revision Mode)
Run to check activerevisionmode: az containerapp show -n my-containerapp -g MyResourceGroup --query properties.configuration.activeRevisionsMode
examples:
- name: List all functions in a container app. (single active revision mode)
text: |
az containerapp function list -n my-containerapp -g MyResourceGroup
- name: List all functions for a specific revision
text: |
az containerapp function list -n my-containerapp -g MyResourceGroup --revision-name MyRevision
"""

helps['containerapp function show'] = """
type: command
short-summary: Show details of a specific function in a container app or a specific revision within app. (pass --revisionName parameter)
long-summary: |
revisionName is required only if Container App active Revision Mode is setup in Multiple Revision Mode. (Default: Single Revision Mode)
Run to check activerevisionmode: az containerapp show -n my-containerapp -g MyResourceGroup --query properties.configuration.activeRevisionsMode
examples:
- name: Show details of a function in a container app. (single active revision mode)
text: |
az containerapp function show -n my-containerapp -g MyResourceGroup --function-name MyFunction
- name: Show details of a function for a specific revision
text: |
az containerapp function show -n my-containerapp -g MyResourceGroup --function-name MyFunction --revision-name MyRevision
"""

helps['containerapp function list-keys'] = """
type: command
short-summary: List function keys for a specific function in a container app.
examples:
- name: List function keys for a specific function
text: |
az containerapp function list-keys -n my-containerapp -g MyResourceGroup --revision MyContainerappRevision --function-name MyFunctionName
"""

helps['containerapp function update-keys'] = """
type: command
short-summary: Update function keys for a specific function in a container app.
examples:
- name: Update a function key for a specific function
text: |
az containerapp function update-keys -n my-containerapp -g MyResourceGroup --revision MyContainerappRevision --function-name MyFunctionName --key-name MyKeyName --key-value MyKeyValue
"""

helps['containerapp function list-hostkeys'] = """
type: command
short-summary: List host keys for a container app.
examples:
- name: List host keys for a container app
text: |
az containerapp function list-hostkeys -n my-containerapp -g MyResourceGroup --revision MyContainerappRevision
"""

helps['containerapp function update-hostkeys'] = """
type: command
short-summary: Update host keys for a container app.
examples:
- name: Update a host key for a container app with function key type
text: |
az containerapp function update-hostkeys -n my-containerapp -g MyResourceGroup --revision MyContainerappRevision --key-name MyKeyName --key-value MyKeyValue --key-type functionKeys
- name: Update a host key for a container app with master key type
text: |
az containerapp function update-hostkeys -n my-containerapp -g MyResourceGroup --revision MyContainerappRevision --key-name MyKeyName --key-value MyKeyValue --key-type masterKey
- name: Update a host key for a container app with system key type
text: |
az containerapp function update-hostkeys -n my-containerapp -g MyResourceGroup --revision MyContainerappRevision --key-name MyKeyName --key-value MyKeyValue --key-type systemKeys
"""

# Environment Commands
helps['containerapp env'] = """
type: group
Expand Down
30 changes: 30 additions & 0 deletions src/containerapp/azext_containerapp/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -530,3 +530,33 @@ def load_arguments(self, _):
c.argument('termination_grace_period', options_list=['--termination-grace-period', '-t'], type=int, help="Time in seconds to drain requests during ingress shutdown. Default 500, minimum 0, maximum 3600.")
c.argument('request_idle_timeout', options_list=['--request-idle-timeout'], type=int, help="Timeout in minutes for idle requests. Default 4, minimum 4, maximum 30.")
c.argument('header_count_limit', options_list=['--header-count-limit'], type=int, help="Limit of http headers per request. Default 100, minimum 1.")

with self.argument_context('containerapp function') as c:
c.argument('resource_group_name', arg_type=resource_group_name_type, id_part=None)
c.argument('name', options_list=['--name', '-n'], help="The name of the Container App.")

with self.argument_context('containerapp function list') as c:
c.argument('revision_name', options_list=['--revision-name', '-r'], help="The name of the revision to list functions from. It is required if container app is running in multiple active revision mode.")

with self.argument_context('containerapp function show') as c:
c.argument('function_name', options_list=['--function-name', '-f'], help="The name of the function to show details for.")
c.argument('revision_name', options_list=['--revision-name', '-r'], help="The name of the revision to get the function from. It is required if container app is running in multiple active revision mode.")

with self.argument_context('containerapp function list-keys') as c:
c.argument('revision', options_list=['--revision'], help="The name of the container app revision.")
c.argument('function_name', options_list=['--function-name'], help="The name of the function.")

with self.argument_context('containerapp function update-keys') as c:
c.argument('revision', options_list=['--revision'], help="The name of the container app revision.")
c.argument('function_name', options_list=['--function-name'], help="The name of the function.")
c.argument('key_name', options_list=['--key-name'], help="The name of the key to update.")
c.argument('key_value', options_list=['--key-value'], help="The value of the key to update.")

with self.argument_context('containerapp function list-hostkeys') as c:
c.argument('revision', options_list=['--revision'], help="The name of the container app revision.")

with self.argument_context('containerapp function update-hostkeys') as c:
c.argument('revision', options_list=['--revision'], help="The name of the container app revision.")
c.argument('key_name', options_list=['--key-name'], help="The name of the host key to update.")
c.argument('key_value', options_list=['--key-value'], help="The value of the host key to update.")
c.argument('key_type', options_list=['--key-type'], arg_type=get_enum_type(['functionKeys', 'masterKey', 'systemKeys']), help="The type of the host key.")
9 changes: 7 additions & 2 deletions src/containerapp/azext_containerapp/_ssh_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,28 @@
SSH_DEFAULT_ENCODING, read_ssh
from azure.cli.core.commands.client_factory import get_subscription_id

import urllib
from knack.log import get_logger

logger = get_logger(__name__)


class DebugWebSocketConnection(WebSocketConnection):
def __init__(self, cmd, resource_group_name, name, revision, replica, container):
def __init__(self, cmd, resource_group_name, name, revision, replica, container, command):
super(DebugWebSocketConnection, self).__init__(cmd, resource_group_name, name, revision, replica, container, "")
self.command = urllib.parse.quote_plus(command) if command else None

def _get_url(self, cmd, resource_group_name, name, revision, replica, container, startup_command):
sub = get_subscription_id(cmd.cli_ctx)
base_url = self._logstream_endpoint
proxy_api_url = base_url[:base_url.index("/subscriptions/")].replace("https://", "")

return (f"wss://{proxy_api_url}/subscriptions/{sub}/resourceGroups/{resource_group_name}/containerApps/{name}"
debug_url = (f"wss://{proxy_api_url}/subscriptions/{sub}/resourceGroups/{resource_group_name}/containerApps/{name}"
f"/revisions/{revision}/replicas/{replica}/debug"
f"?targetContainer={container}")
if self.command:
debug_url += f"&command={self.command}"
return debug_url


def read_debug_ssh(connection: WebSocketConnection, response_encodings):
Expand Down
10 changes: 10 additions & 0 deletions src/containerapp/azext_containerapp/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ def load_command_table(self, args):
g.custom_command('list', 'list_replicas')
g.custom_command('count', 'count_replicas', is_preview=True)

with self.command_group('containerapp function', is_preview=True) as g:
g.custom_command('list', 'list_containerapp_functions')
g.custom_show_command('show', 'show_containerapp_function')

with self.command_group('containerapp env') as g:
g.custom_show_command('show', 'show_managed_environment')
g.custom_command('list', 'list_managed_environments')
Expand Down Expand Up @@ -297,3 +301,9 @@ def load_command_table(self, args):
g.custom_command('add', 'add_environment_premium_ingress')
g.custom_command('update', 'update_environment_premium_ingress')
g.custom_command('remove', 'remove_environment_premium_ingress', confirmation=True)

with self.command_group('containerapp function') as g:
g.custom_command('list-keys', 'list_containerapp_function_keys')
g.custom_command('update-keys', 'update_containerapp_function_keys')
g.custom_command('list-hostkeys', 'list_containerapp_function_hostkeys')
g.custom_command('update-hostkeys', 'update_containerapp_function_hostkeys')
Comment on lines +305 to +309
Copy link

Copilot AI Sep 19, 2025

Choose a reason for hiding this comment

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

Duplicate command group definition. The 'containerapp function' command group is already defined at lines 35-37. This will cause conflicts between the two command group definitions.

Copilot uses AI. Check for mistakes.
Loading
Loading