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
4 changes: 4 additions & 0 deletions src/containerapp/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ Release History
===============
upcoming
++++++
* '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
Comment on lines +7 to +10

Choose a reason for hiding this comment

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

please review these commands from Nitesh/Deep as well.

Comment on lines +7 to +10
Copy link

@krishnajaju krishnajaju Sep 16, 2025

Choose a reason for hiding this comment

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

we also have master key and system keys. Instead of adding separate commands for each type of keys, can we follow the same approach as that of az functionapp keys and az functionapp function keys

My suggestion is to have only one command that handles all kinds of function keys, following the az functionapp conventions.

az containerapp function keys list/delete/set --key-type {functionKeys, hostKey, masterKey, systemKeys} [--function-name] {} [--key-name] {} [--key-value] {}

  • if keyType is functionKeys, --function-name becomes mandatory parameter, else it is optional.
  • keyType is mandatory parameter
  • list --> to view all keys with given keyType
  • delete --> delete a key with given keyType and keyName (keyName required only when keyType is functionKeys. Is it possible to delete master key, host key in case of other Functions SKUs using az functionapp keys command?
  • set --> add new key or update existing key. (Only update operation supported in host and master key. Add or update supported in functionKeys)
  • function-name, key-name and key-value parameters will be applicable only in certain combination of commands.

Choose a reason for hiding this comment

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

* '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
76 changes: 76 additions & 0 deletions src/containerapp/azext_containerapp/_clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -1698,3 +1698,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

@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()
48 changes: 47 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,53 @@
az containerapp replica count -n my-containerapp -g MyResourceGroup
"""

helps['containerapp function'] = """
type: group
short-summary: Commands to manage function keys in a container app.
"""

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
27 changes: 27 additions & 0 deletions src/containerapp/azext_containerapp/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -529,3 +529,30 @@ 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 list-keys') 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.")
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('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.")
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('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.")
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('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.")
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.")
6 changes: 6 additions & 0 deletions src/containerapp/azext_containerapp/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,3 +297,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')
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# coding=utf-8
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
# pylint: disable=line-too-long, broad-except, logging-format-interpolation
from knack.log import get_logger
from typing import Any, Dict

from azure.cli.core.commands import AzCliCommand
from azure.cli.core.azclierror import ValidationError
from azure.cli.command_modules.containerapp.base_resource import BaseResource

from ._clients import ContainerAppFunctionsPreviewClient
from ._client_factory import handle_raw_exception

logger = get_logger(__name__)


class ContainerAppFunctionKeysDecorator(BaseResource):
"""Base decorator for Container App Function Keys operations"""

def __init__(self, cmd: AzCliCommand, client: Any, raw_parameters: Dict, models: str):
super().__init__(cmd, client, raw_parameters, models)

def get_argument_function_name(self):
return self.get_param("function_name")

def get_argument_revision_name(self):
return self.get_param("revision_name")

def validate_common_arguments(self):
"""Validate common arguments required for all function operations"""
resource_group_name = self.get_argument_resource_group_name()
name = self.get_argument_name()
revision_name = self.get_argument_revision_name()

if not resource_group_name:
raise ValidationError("Resource group name is required.")

if not name:
raise ValidationError("Container app name is required.")

if not revision_name:
raise ValidationError("Revision name is required.")

return resource_group_name, name, revision_name

def validate_function_arguments(self):
"""Validate arguments required for function-specific operations"""
resource_group_name, name, revision_name = self.validate_common_arguments()
function_name = self.get_argument_function_name()

if not function_name:
raise ValidationError("Function name is required.")

return resource_group_name, name, revision_name, function_name


class ContainerAppFunctionKeysListDecorator(ContainerAppFunctionKeysDecorator):
"""Decorator for listing function keys"""

def __init__(self, cmd: AzCliCommand, client: Any, raw_parameters: Dict, models: str):
super().__init__(cmd, client, raw_parameters, models)

def list_keys(self):
"""List keys for a specific function"""
try:
resource_group_name, name, revision_name, function_name = self.validate_function_arguments()

return self.client.list_function_keys(
cmd=self.cmd,
resource_group_name=resource_group_name,
name=name,
function_name=function_name
)
except Exception as e:
handle_raw_exception(e)


class ContainerAppFunctionKeysUpdateDecorator(ContainerAppFunctionKeysDecorator):
"""Decorator for updating function keys"""

def __init__(self, cmd: AzCliCommand, client: Any, raw_parameters: Dict, models: str):
super().__init__(cmd, client, raw_parameters, models)

def get_argument_key_name(self):
return self.get_param("key_name")

def get_argument_key_value(self):
return self.get_param("key_value")

def validate_update_arguments(self):
"""Validate arguments required for updating function keys"""
resource_group_name, name, revision_name, function_name = self.validate_function_arguments()
key_name = self.get_argument_key_name()

if not key_name:
raise ValidationError("Key name is required.")

return resource_group_name, name, revision_name, function_name, key_name

def update_keys(self):
"""Update keys for a specific function"""
try:
resource_group_name, name, revision_name, function_name, key_name = self.validate_update_arguments()
key_value = self.get_argument_key_value()

return self.client.update_function_keys(
cmd=self.cmd,
resource_group_name=resource_group_name,
name=name,
function_name=function_name,
key_name=key_name,
key_value=key_value
)
except Exception as e:
handle_raw_exception(e)


class ContainerAppFunctionHostKeysListDecorator(ContainerAppFunctionKeysDecorator):
"""Decorator for listing host keys"""

def __init__(self, cmd: AzCliCommand, client: Any, raw_parameters: Dict, models: str):
super().__init__(cmd, client, raw_parameters, models)

def list_host_keys(self):
"""List host keys for the container app function host"""
try:
resource_group_name, name, revision_name = self.validate_common_arguments()

return self.client.list_host_keys(
cmd=self.cmd,
resource_group_name=resource_group_name,
name=name
)
except Exception as e:
handle_raw_exception(e)


class ContainerAppFunctionHostKeysUpdateDecorator(ContainerAppFunctionKeysDecorator):
"""Decorator for updating host keys"""

def __init__(self, cmd: AzCliCommand, client: Any, raw_parameters: Dict, models: str):
super().__init__(cmd, client, raw_parameters, models)

def get_argument_key_type(self):
return self.get_param("key_type")

def get_argument_key_name(self):
return self.get_param("key_name")

def get_argument_key_value(self):
return self.get_param("key_value")

def validate_host_key_arguments(self):
"""Validate arguments required for updating host keys"""
resource_group_name, name, revision_name = self.validate_common_arguments()
key_type = self.get_argument_key_type()
key_name = self.get_argument_key_name()

if not key_type:
raise ValidationError("Key type is required.")

if not key_name:
raise ValidationError("Key name is required.")

return resource_group_name, name, revision_name, key_type, key_name

def update_host_keys(self):
"""Update host keys for the container app function host"""
try:
resource_group_name, name, revision_name, key_type, key_name = self.validate_host_key_arguments()
key_value = self.get_argument_key_value()

return self.client.update_host_keys(
cmd=self.cmd,
resource_group_name=resource_group_name,
name=name,
key_type=key_type,
key_name=key_name,
key_value=key_value
)
except Exception as e:
handle_raw_exception(e)
Loading
Loading