From 1de51eea9678da84ac9b0ba3cc446094e7b2d07e Mon Sep 17 00:00:00 2001 From: Haroon Feisal <38823870+haroonf@users.noreply.github.com> Date: Thu, 7 Apr 2022 17:57:21 -0400 Subject: [PATCH 1/9] Marchp1s and add back Identity (#57) * Skeleton code * az containerapp env show * List kube/managed environments * Create kube environment, wait doesn't work yet * Update containerapp stubs (check if it is supported now) * Containerapp env delete, polling not working yet * Added polling for create and delete * Use Microsoft.App RP for show, list, delete command * Create containerapp env using Microsoft.App RP * Add optional containerapp env create arguments * Remove old kube environment code, naming fixes * Containerapp create almost done * Done containerapp create, except for --yaml. Need to test * Containerapp show, list * Fix helptext * Containerapp delete * Containerapp update. Needs secrets api to be implemented, and testing * Add scale command * Various validations, small fixes * listSecrets API for updates, autogen log analytics for env * Use space delimiter for secrets and env variables * Verify sub is registered to Microsoft.ContainerRegistration if creating vnet enabled env, remove logs-type parameter * Containerapp create --yaml * Fix updating registry to do create or update * Fix containerapp update command. Add image-name parameter to support multi container updates. Fix updating registries, containers and secrets * started update with --yaml. Need to do create or update for when an attribute is a list of items * use space delimiter for startup_command and args, instead of comma delimiter * Traffic weights * List and show revisions * az containerapp revision restart, activate, deactivate * Add ability for users to clear args/command in az containerapp update * Various fixes, traffic weights fixes * Verify subnet subscription is registered to Microsoft.ContainerServices * GitHub Actions Update (#17) * Added models. Finished transferring Calvin's previous work. * Updated wrong models. * Updated models in custom.py, added githubactionclient. * Updated envelope to be correct. * Small bug fixes. * Updated error handling. Fixed bugs. Initial working state. * Added better error handling. * Added error messages for tokens with inappropriate access rights. * Added back get_acr_cred. * Fixed problems from merge conflict. * Updated names of imports from ._models.py to fix pylance erros. * Removed random imports. Co-authored-by: Haroon Feisal * Remove --location since location must be same as managed env * Add options for flag names: --env-vars and --registry-srever * Empty string to clear env_vars * Default revisions_mode to single * Infer acr credentials if it is acr and credentials are not provided * fix help msg * if image is hosted on acr, and no registry server is supplied, infer the registry server * Added subgroups (Ingress, Registry, Secret) and updated revisions (#18) * Added ingress subgroup. * Added help for ingress. * Fixed ingress traffic help. * Added registry commands. * Updated registry remove util to clear secrets if none remaining. Added warning when updating existing registry. Added registry help. * Changed registry delete to remove. * Added error message if user tries to remove non assigned registry. * Changed registry add back to registry set. * Added secret subgroup commands. * Removed yaml support from secret set. * Changed secret add to secret set. Updated consistency between secret set and secret delete. Added secret help. Require at least one secret passed with --secrets for secret commands. * Changed param name for secret delete from --secrets to --secret-names. Updated help. * Changed registry remove to registry delete. * Fixed bug in registry delete. * Added revision mode set and revision copy. * Modified update_containerapp_yaml to support updating from non-current revision. Authored-by: Haroon Feisal * More p0 fixes (#20) * Remove --registry-login-server, only allow --registry-server * Rename --environment-variables to --env-vars * If no image is supplied, use default quickstart image * Update help text (#21) * Update help text * Update punctuation * master -> main * New 1.0.1 version * Added identity commands + --assign-identity flag to containerapp create (#8) * Added identity show and assign. * Finisheed identity remove. * Added helps, updated identity remove to work with identity names instead of requiring identity resource ids. * Moved helper function to utils. * Require --identities flag when removing identities. * Added message for assign identity with no specified identity. * Added --assign-identity flag to containerapp create. * Moved assign-identity flag to containerapp create. * Fixed small logic error on remove identities when passing duplicate identities. Added warnings for certain edge cases. * Updated param definition for identity assign --identity default. * Added identity examples in help. * Made sure secrets were not removed when assigning identities. Added tolerance for [system] passed with capital letters. * Fixed error from merge. Co-authored-by: Haroon Feisal * Dapr Commands (#23) * Added ingress subgroup. * Added help for ingress. * Fixed ingress traffic help. * Added registry commands. * Updated registry remove util to clear secrets if none remaining. Added warning when updating existing registry. Added registry help. * Changed registry delete to remove. * Added error message if user tries to remove non assigned registry. * Changed registry add back to registry set. * Added secret subgroup commands. * Removed yaml support from secret set. * Changed secret add to secret set. Updated consistency between secret set and secret delete. Added secret help. Require at least one secret passed with --secrets for secret commands. * Changed param name for secret delete from --secrets to --secret-names. Updated help. * Changed registry remove to registry delete. * Fixed bug in registry delete. * Added revision mode set and revision copy. * Added dapr enable and dapr disable. Need to test more. * Added list, show, set dapr component. Added dapr enable, disable. * Added delete dapr delete. * Added helps and param text. * Changed dapr delete to dapr remove to match with dapr set. * Commented out managed identity for whl file. * Uncommented. Co-authored-by: Haroon Feisal * Rename --image-name to --container-name * Remove allowInsecure since it was messing with the api parsing * Fix for env var being empty string * Rename to --dapr-instrumentation-key, only infer ACR credentials if --registry-server is provided * Remove az containerapp scale * Fix delete containerapp errors * Remove ingress, dapr flags from az containerapp update/revision copy * Fix revision list -o table * Help text fix * Bump extension to 0.1.2 * Update managed identities and Dapr help text (#25) * Update managed identities and Dapr help text * Update Dapr flags * Add secretref note * Env var options + various bug fixes (#26) * Moved dapr arguments to env as a subgroup. * Added env variable options. * Changed revision mode set to revision set-mode. * Added env var options to revision copy. * Fixed revision copy bug related to env secret refs. * Changed registry and secret delete to remove. Added registry param helps. Removed replica from table output and added trafficWeight. * Updating warning text. * Updated warning text once more. * Made name optional for revision copy if from-revision flag is passed. Co-authored-by: Haroon Feisal * Fixed style issues, various bug fixes (#27) * Moved dapr arguments to env as a subgroup. * Added env variable options. * Changed revision mode set to revision set-mode. * Added env var options to revision copy. * Fixed revision copy bug related to env secret refs. * Changed registry and secret delete to remove. Added registry param helps. Removed replica from table output and added trafficWeight. * Updating warning text. * Updated warning text once more. * Made name optional for revision copy if from-revision flag is passed. * Fixed whitespace style issues. * Styled clients and utils to pass pylint. * Finished client.py pylint fixes. * Fixed pylint issues. * Fixed flake8 commands and custom. * Fixed flake issues in src. * Added license header to _sdk_models. * Added confirmation for containerapp delete. Co-authored-by: Haroon Feisal * Update src/containerapp/azext_containerapp/tests/latest/test_containerapp_scenario.py Co-authored-by: Xing Zhou * Specific Error Types + Bugfixes (Help, remove app-subnet-resource-id, removed env-var alias, added help text for --name) (#28) * Moved dapr arguments to env as a subgroup. * Added env variable options. * Changed revision mode set to revision set-mode. * Added env var options to revision copy. * Fixed revision copy bug related to env secret refs. * Changed registry and secret delete to remove. Added registry param helps. Removed replica from table output and added trafficWeight. * Updating warning text. * Updated warning text once more. * Made name optional for revision copy if from-revision flag is passed. * Fixed whitespace style issues. * Styled clients and utils to pass pylint. * Finished client.py pylint fixes. * Fixed pylint issues. * Fixed flake8 commands and custom. * Fixed flake issues in src. * Added license header to _sdk_models. * Added confirmation for containerapp delete. * Update helps for identity, revision. Removed env-var alias for set-env-vars. Added name param help. * Removed app-subnet-resource-id. * Updated infrastructure subnet param help. * Check if containerapp resource exists before attempting to delete. * Added check before deleting managed env. * Changed error types to be more specific. * Removed check before deletion. Removed comments. Co-authored-by: Haroon Feisal * Reset to 0.1.0 version, remove unneeded options-list * Update min cli core version * Fixed style issues. (#30) Co-authored-by: Haroon Feisal * Fix linter issues * Use custom-show-command * Removed --ids from revision, secret, registry list. * Add linter exclusions * Fix polling on delete containerapp * Fix error handling * Add Container App Service * Fix flake linter * Fix help text * Mark extension as preview * Add python 3.9 and 3.10 as supported * Remove registries and secrets from az containerapp update, in favor of registry and secret subgroup * Fix YAML not working * Move import to inside deserialize function * Ingress enable --transport default. Secret list returns empty array. Secret update prints message saying user needs to restart their apps. Added show-values flag to secret list. Fixed yaml datetime field issues, replaced x00 values that also came up during testing. * Fixed dapr in create. * Revert "Ingress enable --transport default. Secret list returns empty array. Secret update prints message saying user needs to restart their apps. Added show-values flag to secret list. Fixed yaml datetime field issues, replaced x00 values that also came up during testing." This reverts commit 51bc543338f82584b6c74ff5296453b76e6749aa. * Revert "Fixed dapr in create." This reverts commit 37030adc542ab3e38264b29a01b284e29479f716. * Ingress enable --transport default. Secret list returns empty array. Secret update prints message saying user needs to restart their apps. Added show-values flag to secret list. Fixed yaml datetime field issues, replaced x00 values that also came up during testing. * Skeleton code * az containerapp env show * List kube/managed environments * Create kube environment, wait doesn't work yet * Update containerapp stubs (check if it is supported now) * Containerapp env delete, polling not working yet * Added polling for create and delete * Use Microsoft.App RP for show, list, delete command * Create containerapp env using Microsoft.App RP * Add optional containerapp env create arguments * Remove old kube environment code, naming fixes * Containerapp create almost done * Done containerapp create, except for --yaml. Need to test * Containerapp show, list * Fix helptext * Containerapp delete * Containerapp update. Needs secrets api to be implemented, and testing * Add scale command * Various validations, small fixes * listSecrets API for updates, autogen log analytics for env * Use space delimiter for secrets and env variables * Verify sub is registered to Microsoft.ContainerRegistration if creating vnet enabled env, remove logs-type parameter * Containerapp create --yaml * Fix updating registry to do create or update * Fix containerapp update command. Add image-name parameter to support multi container updates. Fix updating registries, containers and secrets * started update with --yaml. Need to do create or update for when an attribute is a list of items * use space delimiter for startup_command and args, instead of comma delimiter * Traffic weights * List and show revisions * az containerapp revision restart, activate, deactivate * Add ability for users to clear args/command in az containerapp update * Various fixes, traffic weights fixes * Verify subnet subscription is registered to Microsoft.ContainerServices * GitHub Actions Update (#17) * Added models. Finished transferring Calvin's previous work. * Updated wrong models. * Updated models in custom.py, added githubactionclient. * Updated envelope to be correct. * Small bug fixes. * Updated error handling. Fixed bugs. Initial working state. * Added better error handling. * Added error messages for tokens with inappropriate access rights. * Added back get_acr_cred. * Fixed problems from merge conflict. * Updated names of imports from ._models.py to fix pylance erros. * Removed random imports. Co-authored-by: Haroon Feisal * Remove --location since location must be same as managed env * Add options for flag names: --env-vars and --registry-srever * Empty string to clear env_vars * Default revisions_mode to single * Infer acr credentials if it is acr and credentials are not provided * fix help msg * if image is hosted on acr, and no registry server is supplied, infer the registry server * Added subgroups (Ingress, Registry, Secret) and updated revisions (#18) * Added ingress subgroup. * Added help for ingress. * Fixed ingress traffic help. * Added registry commands. * Updated registry remove util to clear secrets if none remaining. Added warning when updating existing registry. Added registry help. * Changed registry delete to remove. * Added error message if user tries to remove non assigned registry. * Changed registry add back to registry set. * Added secret subgroup commands. * Removed yaml support from secret set. * Changed secret add to secret set. Updated consistency between secret set and secret delete. Added secret help. Require at least one secret passed with --secrets for secret commands. * Changed param name for secret delete from --secrets to --secret-names. Updated help. * Changed registry remove to registry delete. * Fixed bug in registry delete. * Added revision mode set and revision copy. * Modified update_containerapp_yaml to support updating from non-current revision. Authored-by: Haroon Feisal * More p0 fixes (#20) * Remove --registry-login-server, only allow --registry-server * Rename --environment-variables to --env-vars * If no image is supplied, use default quickstart image * Update help text (#21) * Update help text * Update punctuation * master -> main * New 1.0.1 version * Added identity commands + --assign-identity flag to containerapp create (#8) * Added identity show and assign. * Finisheed identity remove. * Added helps, updated identity remove to work with identity names instead of requiring identity resource ids. * Moved helper function to utils. * Require --identities flag when removing identities. * Added message for assign identity with no specified identity. * Added --assign-identity flag to containerapp create. * Moved assign-identity flag to containerapp create. * Fixed small logic error on remove identities when passing duplicate identities. Added warnings for certain edge cases. * Updated param definition for identity assign --identity default. * Added identity examples in help. * Made sure secrets were not removed when assigning identities. Added tolerance for [system] passed with capital letters. * Fixed error from merge. Co-authored-by: Haroon Feisal * Dapr Commands (#23) * Added ingress subgroup. * Added help for ingress. * Fixed ingress traffic help. * Added registry commands. * Updated registry remove util to clear secrets if none remaining. Added warning when updating existing registry. Added registry help. * Changed registry delete to remove. * Added error message if user tries to remove non assigned registry. * Changed registry add back to registry set. * Added secret subgroup commands. * Removed yaml support from secret set. * Changed secret add to secret set. Updated consistency between secret set and secret delete. Added secret help. Require at least one secret passed with --secrets for secret commands. * Changed param name for secret delete from --secrets to --secret-names. Updated help. * Changed registry remove to registry delete. * Fixed bug in registry delete. * Added revision mode set and revision copy. * Added dapr enable and dapr disable. Need to test more. * Added list, show, set dapr component. Added dapr enable, disable. * Added delete dapr delete. * Added helps and param text. * Changed dapr delete to dapr remove to match with dapr set. * Commented out managed identity for whl file. * Uncommented. Co-authored-by: Haroon Feisal * Rename --image-name to --container-name * Remove allowInsecure since it was messing with the api parsing * Fix for env var being empty string * Rename to --dapr-instrumentation-key, only infer ACR credentials if --registry-server is provided * Remove az containerapp scale * Fix delete containerapp errors * Remove ingress, dapr flags from az containerapp update/revision copy * Fix revision list -o table * Help text fix * Bump extension to 0.1.2 * Update managed identities and Dapr help text (#25) * Update managed identities and Dapr help text * Update Dapr flags * Add secretref note * Env var options + various bug fixes (#26) * Moved dapr arguments to env as a subgroup. * Added env variable options. * Changed revision mode set to revision set-mode. * Added env var options to revision copy. * Fixed revision copy bug related to env secret refs. * Changed registry and secret delete to remove. Added registry param helps. Removed replica from table output and added trafficWeight. * Updating warning text. * Updated warning text once more. * Made name optional for revision copy if from-revision flag is passed. Co-authored-by: Haroon Feisal * Fixed style issues, various bug fixes (#27) * Moved dapr arguments to env as a subgroup. * Added env variable options. * Changed revision mode set to revision set-mode. * Added env var options to revision copy. * Fixed revision copy bug related to env secret refs. * Changed registry and secret delete to remove. Added registry param helps. Removed replica from table output and added trafficWeight. * Updating warning text. * Updated warning text once more. * Made name optional for revision copy if from-revision flag is passed. * Fixed whitespace style issues. * Styled clients and utils to pass pylint. * Finished client.py pylint fixes. * Fixed pylint issues. * Fixed flake8 commands and custom. * Fixed flake issues in src. * Added license header to _sdk_models. * Added confirmation for containerapp delete. Co-authored-by: Haroon Feisal * Update src/containerapp/azext_containerapp/tests/latest/test_containerapp_scenario.py Co-authored-by: Xing Zhou * Specific Error Types + Bugfixes (Help, remove app-subnet-resource-id, removed env-var alias, added help text for --name) (#28) * Moved dapr arguments to env as a subgroup. * Added env variable options. * Changed revision mode set to revision set-mode. * Added env var options to revision copy. * Fixed revision copy bug related to env secret refs. * Changed registry and secret delete to remove. Added registry param helps. Removed replica from table output and added trafficWeight. * Updating warning text. * Updated warning text once more. * Made name optional for revision copy if from-revision flag is passed. * Fixed whitespace style issues. * Styled clients and utils to pass pylint. * Finished client.py pylint fixes. * Fixed pylint issues. * Fixed flake8 commands and custom. * Fixed flake issues in src. * Added license header to _sdk_models. * Added confirmation for containerapp delete. * Update helps for identity, revision. Removed env-var alias for set-env-vars. Added name param help. * Removed app-subnet-resource-id. * Updated infrastructure subnet param help. * Check if containerapp resource exists before attempting to delete. * Added check before deleting managed env. * Changed error types to be more specific. * Removed check before deletion. Removed comments. Co-authored-by: Haroon Feisal * Reset to 0.1.0 version, remove unneeded options-list * Update min cli core version * Fixed style issues. (#30) Co-authored-by: Haroon Feisal * Fix linter issues * Use custom-show-command * Removed --ids from revision, secret, registry list. * Add linter exclusions * Fix polling on delete containerapp * Fix error handling * Add Container App Service * Fix flake linter * Fix help text * Mark extension as preview * Add python 3.9 and 3.10 as supported * Remove registries and secrets from az containerapp update, in favor of registry and secret subgroup * Fix YAML not working * Move import to inside deserialize function * Dapr moved from Template to Configuration * Use aka.ms link for containerapps yaml * Updated dapr enable/disable to current spec. * Fixed oversight. * Remove revisions-mode from containerapp update * Fixed dapr enable property names. (#47) Co-authored-by: Haroon Feisal * Fix exceptions with using --yaml in containerapp create/update * Rename history msg * Include fqdn in containerapp table output * Added ingress messages. * Revert history msg * Reduced redundant code between revision copy and containerapp update. * Fixed merge issues. * Fixed merge conflicts, moved helper function Co-authored-by: Calvin Chan Co-authored-by: Haroon Feisal Co-authored-by: Anthony Chu Co-authored-by: Xing Zhou --- src/containerapp/azext_containerapp/_help.py | 49 ++- .../azext_containerapp/_params.py | 7 + .../azext_containerapp/commands.py | 5 + src/containerapp/azext_containerapp/custom.py | 330 +++++++----------- 4 files changed, 193 insertions(+), 198 deletions(-) diff --git a/src/containerapp/azext_containerapp/_help.py b/src/containerapp/azext_containerapp/_help.py index 5e75c334d82..c4e500caf4a 100644 --- a/src/containerapp/azext_containerapp/_help.py +++ b/src/containerapp/azext_containerapp/_help.py @@ -162,6 +162,14 @@ """ +helps['containerapp revision copy'] = """ + type: command + short-summary: Create a revision based on a previous revision. + examples: + - name: Create a revision based on a previous revision. + text: | + az containerapp revision copy -n MyContainerapp -g MyResourceGroup --cpu 0.75 --memory 1.5Gi +""" # Environment Commands helps['containerapp env'] = """ @@ -256,6 +264,45 @@ az containerapp env dapr-component remove -g MyResourceGroup --dapr-component-name MyDaprComponentName --name MyEnvironment """ +# Identity Commands +helps['containerapp identity'] = """ + type: group + short-summary: Commands to manage managed identities. +""" + +helps['containerapp identity assign'] = """ + type: command + short-summary: Assign managed identity to a container app. + long-summary: Managed identities can be user-assigned or system-assigned. + examples: + - name: Assign system identity. + text: | + az containerapp identity assign + - name: Assign user identity. + text: | + az containerapp identity assign --identities myAssignedId + - name: Assign system and user identity. + text: | + az containerapp identity assign --identities [system] myAssignedId +""" + +helps['containerapp identity remove'] = """ + type: command + short-summary: Remove a managed identity from a container app. + examples: + - name: Remove system identity. + text: | + az containerapp identity remove --identities [system] + - name: Remove system and user identity. + text: | + az containerapp identity remove --identities [system] myAssignedId +""" + +helps['containerapp identity show'] = """ + type: command + short-summary: Show managed identities of a container app. +""" + # Ingress Commands helps['containerapp ingress'] = """ type: group @@ -470,7 +517,7 @@ helps['containerapp dapr enable'] = """ type: command - short-summary: Enable Dapr for a container app. + short-summary: Enable Dapr for a container app. Updates existing values. examples: - name: Enable Dapr for a container app. text: | diff --git a/src/containerapp/azext_containerapp/_params.py b/src/containerapp/azext_containerapp/_params.py index 6e0ee6918d4..c70de7f12ac 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -75,6 +75,7 @@ def load_arguments(self, _): c.argument('transport', arg_type=get_enum_type(['auto', 'http', 'http2']), help="The transport protocol used for ingress traffic.") with self.argument_context('containerapp create') as c: + c.argument('assign_identity', nargs='+', help="Space-separated identities. Use '[system]' to refer to the system assigned identity.") c.argument('traffic_weights', nargs='*', options_list=['--traffic-weight'], help="A list of revision weight(s) for the container app. Space-separated values in 'revision_name=weight' format. For latest revision, use 'latest=weight'") with self.argument_context('containerapp scale') as c: @@ -112,6 +113,12 @@ def load_arguments(self, _): with self.argument_context('containerapp env show') as c: c.argument('name', name_type, help='Name of the Container Apps Environment.') + with self.argument_context('containerapp identity') as c: + c.argument('identities', nargs='+', help="Space-separated identities. Use '[system]' to refer to the system assigned identity.") + + with self.argument_context('containerapp identity assign') as c: + c.argument('identities', nargs='+', help="Space-separated identities. Use '[system]' to refer to the system assigned identity. Default is '[system]'.") + with self.argument_context('containerapp github-action add') as c: c.argument('repo_url', help='The GitHub repository to which the workflow file will be added. In the format: https://github.com//') c.argument('token', help='A Personal Access Token with write access to the specified repository. For more information: https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line') diff --git a/src/containerapp/azext_containerapp/commands.py b/src/containerapp/azext_containerapp/commands.py index c5b924287f8..dd6f2d067dc 100644 --- a/src/containerapp/azext_containerapp/commands.py +++ b/src/containerapp/azext_containerapp/commands.py @@ -62,6 +62,11 @@ def load_command_table(self, _): g.custom_command('set', 'create_or_update_dapr_component') g.custom_command('remove', 'remove_dapr_component') + with self.command_group('containerapp identity') as g: + g.custom_command('assign', 'assign_managed_identity', supports_no_wait=True, exception_handler=ex_handler_factory()) + g.custom_command('remove', 'remove_managed_identity', supports_no_wait=True, exception_handler=ex_handler_factory()) + g.custom_show_command('show', 'show_managed_identity') + with self.command_group('containerapp github-action') as g: g.custom_command('add', 'create_or_update_github_action', exception_handler=ex_handler_factory()) g.custom_show_command('show', 'show_github_action', exception_handler=ex_handler_factory()) diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index 15cea81f0ff..52620d08e1d 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -292,7 +292,8 @@ def create_containerapp(cmd, startup_command=None, args=None, tags=None, - no_wait=False): + no_wait=False, + assign_identity=None): _validate_subscription_registered(cmd, "Microsoft.App") if yaml: @@ -309,6 +310,9 @@ def create_containerapp(cmd, if managed_env is None: raise RequiredArgumentMissingError('Usage error: --environment is required if not using --yaml') + if assign_identity is None: + assign_identity = [] + # Validate managed environment parsed_managed_env = parse_resource_id(managed_env) managed_env_name = parsed_managed_env['name'] @@ -374,6 +378,28 @@ def create_containerapp(cmd, config_def["registries"] = [registries_def] if registries_def is not None else None config_def["dapr"] = dapr_def + # Identity actions + identity_def = ManagedServiceIdentityModel + identity_def["type"] = "None" + + assign_system_identity = '[system]' in assign_identity + assign_user_identities = [x for x in assign_identity if x != '[system]'] + + if assign_system_identity and assign_user_identities: + identity_def["type"] = "SystemAssigned, UserAssigned" + elif assign_system_identity: + identity_def["type"] = "SystemAssigned" + elif assign_user_identities: + identity_def["type"] = "UserAssigned" + + if assign_user_identities: + identity_def["userAssignedIdentities"] = {} + subscription_id = get_subscription_id(cmd.cli_ctx) + + for r in assign_user_identities: + r = _ensure_identity_resource_id(subscription_id, resource_group_name, r) + identity_def["userAssignedIdentities"][r] = {} # pylint: disable=unsupported-assignment-operation + scale_def = None if min_replicas is not None or max_replicas is not None: scale_def = ScaleModel @@ -407,6 +433,7 @@ def create_containerapp(cmd, containerapp_def = ContainerAppModel containerapp_def["location"] = location + containerapp_def["identity"] = identity_def containerapp_def["properties"]["managedEnvironmentId"] = managed_env containerapp_def["properties"]["configuration"] = config_def containerapp_def["properties"]["template"] = template_def @@ -429,25 +456,26 @@ def create_containerapp(cmd, handle_raw_exception(e) -def update_containerapp(cmd, - name, - resource_group_name, - yaml=None, - image=None, - container_name=None, - min_replicas=None, - max_replicas=None, - set_env_vars=None, - remove_env_vars=None, - replace_env_vars=None, - remove_all_env_vars=False, - cpu=None, - memory=None, - revision_suffix=None, - startup_command=None, - args=None, - tags=None, - no_wait=False): +def update_containerapp_logic(cmd, + name, + resource_group_name, + yaml=None, + image=None, + container_name=None, + min_replicas=None, + max_replicas=None, + set_env_vars=None, + remove_env_vars=None, + replace_env_vars=None, + remove_all_env_vars=False, + cpu=None, + memory=None, + revision_suffix=None, + startup_command=None, + args=None, + tags=None, + no_wait=False, + from_revision=None): _validate_subscription_registered(cmd, "Microsoft.App") if yaml: @@ -455,7 +483,7 @@ def update_containerapp(cmd, set_env_vars or remove_env_vars or replace_env_vars or remove_all_env_vars or cpu or memory or\ startup_command or args or tags: logger.warning('Additional flags were passed along with --yaml. These flags will be ignored, and the configuration defined in the yaml will be used instead') - return update_containerapp_yaml(cmd=cmd, name=name, resource_group_name=resource_group_name, file_name=yaml, no_wait=no_wait) + return update_containerapp_yaml(cmd=cmd, name=name, resource_group_name=resource_group_name, file_name=yaml, no_wait=no_wait, from_revision=from_revision) containerapp_def = None try: @@ -466,6 +494,16 @@ def update_containerapp(cmd, if not containerapp_def: raise ResourceNotFoundError("The containerapp '{}' does not exist".format(name)) + if from_revision: + try: + r = ContainerAppClient.show_revision(cmd=cmd, resource_group_name=resource_group_name, container_app_name=name, name=from_revision) + except CLIError as e: + # Error handle the case where revision not found? + handle_raw_exception(e) + + _update_revision_env_secretrefs(r["properties"]["template"]["containers"], name) + containerapp_def["properties"]["template"] = r["properties"]["template"] + # Doing this while API has bug. If env var is an empty string, API doesn't return "value" even though the "value" should be an empty string if "properties" in containerapp_def and "template" in containerapp_def["properties"] and "containers" in containerapp_def["properties"]["template"]: for container in containerapp_def["properties"]["template"]["containers"]: @@ -613,6 +651,48 @@ def update_containerapp(cmd, handle_raw_exception(e) +def update_containerapp(cmd, + name, + resource_group_name, + yaml=None, + image=None, + container_name=None, + min_replicas=None, + max_replicas=None, + set_env_vars=None, + remove_env_vars=None, + replace_env_vars=None, + remove_all_env_vars=False, + cpu=None, + memory=None, + revision_suffix=None, + startup_command=None, + args=None, + tags=None, + no_wait=False): + _validate_subscription_registered(cmd, "Microsoft.App") + + return update_containerapp_logic(cmd, + name, + resource_group_name, + yaml, + image, + container_name, + min_replicas, + max_replicas, + set_env_vars, + remove_env_vars, + replace_env_vars, + remove_all_env_vars, + cpu, + memory, + revision_suffix, + startup_command, + args, + tags, + no_wait) + + def show_containerapp(cmd, name, resource_group_name): _validate_subscription_registered(cmd, "Microsoft.App") @@ -1195,177 +1275,26 @@ def copy_revision(cmd, if not name: name = _get_app_from_revision(from_revision) - if yaml: - if image or min_replicas or max_replicas or\ - set_env_vars or replace_env_vars or remove_env_vars or \ - remove_all_env_vars or cpu or memory or \ - startup_command or args or tags: - logger.warning('Additional flags were passed along with --yaml. These flags will be ignored, and the configuration defined in the yaml will be used instead') - return update_containerapp_yaml(cmd=cmd, name=name, resource_group_name=resource_group_name, file_name=yaml, from_revision=from_revision, no_wait=no_wait) - - containerapp_def = None - try: - containerapp_def = ContainerAppClient.show(cmd=cmd, resource_group_name=resource_group_name, name=name) - except: - pass - - if not containerapp_def: - raise ResourceNotFoundError("The containerapp '{}' does not exist".format(name)) - - if from_revision: - try: - r = ContainerAppClient.show_revision(cmd=cmd, resource_group_name=resource_group_name, container_app_name=name, name=from_revision) - except CLIError as e: - # Error handle the case where revision not found? - handle_raw_exception(e) - - _update_revision_env_secretrefs(r["properties"]["template"]["containers"], name) - containerapp_def["properties"]["template"] = r["properties"]["template"] - - # Doing this while API has bug. If env var is an empty string, API doesn't return "value" even though the "value" should be an empty string - if "properties" in containerapp_def and "template" in containerapp_def["properties"] and "containers" in containerapp_def["properties"]["template"]: - for container in containerapp_def["properties"]["template"]["containers"]: - if "env" in container: - for e in container["env"]: - if "value" not in e: - e["value"] = "" - - update_map = {} - update_map['scale'] = min_replicas or max_replicas - update_map['container'] = image or container_name or set_env_vars or replace_env_vars or remove_env_vars or remove_all_env_vars or cpu or memory or startup_command is not None or args is not None - - if tags: - _add_or_update_tags(containerapp_def, tags) - - if revision_suffix is not None: - containerapp_def["properties"]["template"]["revisionSuffix"] = revision_suffix - - # Containers - if update_map["container"]: - if not container_name: - if len(containerapp_def["properties"]["template"]["containers"]) == 1: - container_name = containerapp_def["properties"]["template"]["containers"][0]["name"] - else: - raise ValidationError("Usage error: --image-name is required when adding or updating a container") - - # Check if updating existing container - updating_existing_container = False - for c in containerapp_def["properties"]["template"]["containers"]: - if c["name"].lower() == container_name.lower(): - updating_existing_container = True - - if image is not None: - c["image"] = image - - if set_env_vars is not None: - if "env" not in c or not c["env"]: - c["env"] = [] - # env vars - _add_or_update_env_vars(c["env"], parse_env_var_flags(set_env_vars), is_add=True) - - if replace_env_vars is not None: - if "env" not in c or not c["env"]: - c["env"] = [] - # env vars - _add_or_update_env_vars(c["env"], parse_env_var_flags(replace_env_vars)) - - if remove_env_vars is not None: - if "env" not in c or not c["env"]: - c["env"] = [] - # env vars - _remove_env_vars(c["env"], remove_env_vars) - - if remove_all_env_vars: - c["env"] = [] - - if startup_command is not None: - if isinstance(startup_command, list) and not startup_command: - c["command"] = None - else: - c["command"] = startup_command - if args is not None: - if isinstance(args, list) and not args: - c["args"] = None - else: - c["args"] = args - if cpu is not None or memory is not None: - if "resources" in c and c["resources"]: - if cpu is not None: - c["resources"]["cpu"] = cpu - if memory is not None: - c["resources"]["memory"] = memory - else: - c["resources"] = { - "cpu": cpu, - "memory": memory - } - - # If not updating existing container, add as new container - if not updating_existing_container: - if image is None: - raise ValidationError("Usage error: --image is required when adding a new container") - - resources_def = None - if cpu is not None or memory is not None: - resources_def = ContainerResourcesModel - resources_def["cpu"] = cpu - resources_def["memory"] = memory - - container_def = ContainerModel - container_def["name"] = container_name - container_def["image"] = image - - if set_env_vars is not None: - # env vars - _add_or_update_env_vars(container_def["env"], parse_env_var_flags(set_env_vars), is_add=True) - - if replace_env_vars is not None: - # env vars - _add_or_update_env_vars(container_def["env"], parse_env_var_flags(replace_env_vars)) - - if remove_env_vars is not None: - # env vars - _remove_env_vars(container_def["env"], remove_env_vars) - - if remove_all_env_vars: - container_def["env"] = [] - - if startup_command is not None: - if isinstance(startup_command, list) and not startup_command: - container_def["command"] = None - else: - container_def["command"] = startup_command - if args is not None: - if isinstance(args, list) and not args: - container_def["args"] = None - else: - container_def["args"] = args - if resources_def is not None: - container_def["resources"] = resources_def - - containerapp_def["properties"]["template"]["containers"].append(container_def) - - # Scale - if update_map["scale"]: - if "scale" not in containerapp_def["properties"]["template"]: - containerapp_def["properties"]["template"]["scale"] = {} - if min_replicas is not None: - containerapp_def["properties"]["template"]["scale"]["minReplicas"] = min_replicas - if max_replicas is not None: - containerapp_def["properties"]["template"]["scale"]["maxReplicas"] = max_replicas - - _get_existing_secrets(cmd, resource_group_name, name, containerapp_def) - - try: - r = ContainerAppClient.create_or_update( - cmd=cmd, resource_group_name=resource_group_name, name=name, container_app_envelope=containerapp_def, no_wait=no_wait) - - if "properties" in r and "provisioningState" in r["properties"] and r["properties"]["provisioningState"].lower() == "waiting" and not no_wait: - logger.warning('Containerapp update in progress. Please monitor the update using `az containerapp show -n {} -g {}`'.format(name, resource_group_name)) - - return r - except Exception as e: - handle_raw_exception(e) + return update_containerapp_logic(cmd, + name, + resource_group_name, + yaml, + image, + container_name, + min_replicas, + max_replicas, + set_env_vars, + remove_env_vars, + replace_env_vars, + remove_all_env_vars, + cpu, + memory, + revision_suffix, + startup_command, + args, + tags, + no_wait, + from_revision) def set_revision_mode(cmd, resource_group_name, name, mode, no_wait=False): @@ -1410,7 +1339,7 @@ def show_ingress(cmd, name, resource_group_name): raise ValidationError("The containerapp '{}' does not have ingress enabled.".format(name)) from e -def enable_ingress(cmd, name, resource_group_name, type, target_port, transport, allow_insecure=False, no_wait=False): # pylint: disable=redefined-builtin +def enable_ingress(cmd, name, resource_group_name, type, target_port, transport="auto", allow_insecure=False, no_wait=False): # pylint: disable=redefined-builtin _validate_subscription_registered(cmd, "Microsoft.App") containerapp_def = None @@ -1688,22 +1617,28 @@ def remove_registry(cmd, name, resource_group_name, server, no_wait=False): pass -def list_secrets(cmd, name, resource_group_name): +def list_secrets(cmd, name, resource_group_name, show_values=False): _validate_subscription_registered(cmd, "Microsoft.App") containerapp_def = None try: - containerapp_def = ContainerAppClient.show(cmd=cmd, resource_group_name=resource_group_name, name=name) + r = containerapp_def = ContainerAppClient.show(cmd=cmd, resource_group_name=resource_group_name, name=name) except: pass if not containerapp_def: raise ResourceNotFoundError("The containerapp '{}' does not exist".format(name)) + if not show_values: + try: + return r["properties"]["configuration"]["secrets"] + except: + return [] try: return ContainerAppClient.list_secrets(cmd=cmd, resource_group_name=resource_group_name, name=name)["value"] - except Exception as e: - raise ValidationError("The containerapp {} has no assigned secrets.".format(name)) from e + except Exception: + return [] + # raise ValidationError("The containerapp {} has no assigned secrets.".format(name)) from e def show_secret(cmd, name, resource_group_name, secret_name): @@ -1796,6 +1731,7 @@ def set_secrets(cmd, name, resource_group_name, secrets, try: r = ContainerAppClient.create_or_update( cmd=cmd, resource_group_name=resource_group_name, name=name, container_app_envelope=containerapp_def, no_wait=no_wait) + logger.warning("Containerapp must be restarted in order for secret changes to take effect.") return r["properties"]["configuration"]["secrets"] except Exception as e: handle_raw_exception(e) From 7735595c26665c6ce1cd697df716053b849c1865 Mon Sep 17 00:00:00 2001 From: Calvin Chan Date: Fri, 8 Apr 2022 20:41:58 -0700 Subject: [PATCH 2/9] Fix help for linter --- src/containerapp/azext_containerapp/_params.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/containerapp/azext_containerapp/_params.py b/src/containerapp/azext_containerapp/_params.py index c70de7f12ac..b1a89f2c8d0 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -151,14 +151,11 @@ def load_arguments(self, _): with self.argument_context('containerapp ingress traffic') as c: c.argument('traffic_weights', nargs='*', options_list=['--traffic-weight'], help="A list of revision weight(s) for the container app. Space-separated values in 'revision_name=weight' format. For latest revision, use 'latest=weight'") - with self.argument_context('containerapp secret set') as c: + with self.argument_context('containerapp secret') as c: c.argument('secrets', nargs='+', options_list=['--secrets', '-s'], help="A list of secret(s) for the container app. Space-separated values in 'key=value' format.") - - with self.argument_context('containerapp secret show') as c: c.argument('secret_name', help="The name of the secret to show.") - - with self.argument_context('containerapp secret remove') as c: c.argument('secret_names', nargs='+', help="A list of secret(s) for the container app. Space-separated secret values names.") + c.argument('show_values', help='Show the secret values.') with self.argument_context('containerapp env dapr-component') as c: c.argument('dapr_app_id', help="The Dapr app ID.") @@ -178,8 +175,5 @@ def load_arguments(self, _): with self.argument_context('containerapp registry list') as c: c.argument('name', id_part=None) - with self.argument_context('containerapp secret list') as c: - c.argument('name', id_part=None) - with self.argument_context('containerapp revision list') as c: c.argument('name', id_part=None) From 2e1be89822dbbbb311dac2ab51cb519f61e8678a Mon Sep 17 00:00:00 2001 From: Calvin Date: Mon, 11 Apr 2022 11:32:14 -0700 Subject: [PATCH 3/9] various fixes, helptext (#59) --- src/containerapp/azext_containerapp/_help.py | 4 ++-- src/containerapp/azext_containerapp/_params.py | 7 +++++-- src/containerapp/azext_containerapp/_utils.py | 2 +- src/containerapp/azext_containerapp/_validators.py | 11 ++++++----- src/containerapp/azext_containerapp/custom.py | 4 ++-- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/containerapp/azext_containerapp/_help.py b/src/containerapp/azext_containerapp/_help.py index c4e500caf4a..c6809690a73 100644 --- a/src/containerapp/azext_containerapp/_help.py +++ b/src/containerapp/azext_containerapp/_help.py @@ -184,13 +184,13 @@ - name: Create an environment with an auto-generated Log Analytics workspace. text: | az containerapp env create -n MyContainerappEnvironment -g MyResourceGroup \\ - --location "Canada Central" + --location eastus2 - name: Create an environment with an existing Log Analytics workspace. text: | az containerapp env create -n MyContainerappEnvironment -g MyResourceGroup \\ --logs-workspace-id myLogsWorkspaceID \\ --logs-workspace-key myLogsWorkspaceKey \\ - --location "Canada Central" + --location eastus2 """ diff --git a/src/containerapp/azext_containerapp/_params.py b/src/containerapp/azext_containerapp/_params.py index b1a89f2c8d0..af77982345f 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -34,8 +34,8 @@ def load_arguments(self, _): with self.argument_context('containerapp', arg_group='Container') as c: c.argument('image', type=str, options_list=['--image', '-i'], help="Container image, e.g. publisher/image-name:tag.") c.argument('container_name', type=str, help="Name of the container.") - c.argument('cpu', type=float, validator=validate_cpu, help="Required CPU in cores, e.g. 0.5") - c.argument('memory', type=str, validator=validate_memory, help="Required memory, e.g. 1.0Gi") + c.argument('cpu', type=float, validator=validate_cpu, help="Required CPU in cores from 0.25 - 2.0, e.g. 0.5") + c.argument('memory', type=str, validator=validate_memory, help="Required memory from 0.5 - 4.0 ending with \"Gi\", e.g. 1.0Gi") c.argument('env_vars', nargs='*', help="A list of environment variable(s) for the container. Space-separated values in 'key=value' format. Empty string to clear existing values. Prefix value with 'secretref:' to reference a secret.") c.argument('startup_command', nargs='*', options_list=['--command'], help="A list of supported commands on the container that will executed during startup. Space-separated values e.g. \"/bin/queue\" \"mycommand\". Empty string to clear existing values") c.argument('args', nargs='*', help="A list of container startup command argument(s). Space-separated values e.g. \"-c\" \"mycommand\". Empty string to clear existing values") @@ -175,5 +175,8 @@ def load_arguments(self, _): with self.argument_context('containerapp registry list') as c: c.argument('name', id_part=None) + with self.argument_context('containerapp secret list') as c: + c.argument('name', id_part=None) + with self.argument_context('containerapp revision list') as c: c.argument('name', id_part=None) diff --git a/src/containerapp/azext_containerapp/_utils.py b/src/containerapp/azext_containerapp/_utils.py index 39ccef52633..0be73b8e767 100644 --- a/src/containerapp/azext_containerapp/_utils.py +++ b/src/containerapp/azext_containerapp/_utils.py @@ -293,7 +293,7 @@ def _generate_log_analytics_if_not_provided(cmd, logs_customer_id, logs_key, loc def _get_existing_secrets(cmd, resource_group_name, name, containerapp_def): - if "secrets" not in containerapp_def["properties"]["configuration"]: + if "secrets" not in containerapp_def["properties"]["configuration"] or not containerapp_def["properties"]["configuration"]["secrets"]: containerapp_def["properties"]["configuration"]["secrets"] = [] else: secrets = [] diff --git a/src/containerapp/azext_containerapp/_validators.py b/src/containerapp/azext_containerapp/_validators.py index e7fe0435a11..861a7f049b6 100644 --- a/src/containerapp/azext_containerapp/_validators.py +++ b/src/containerapp/azext_containerapp/_validators.py @@ -16,13 +16,14 @@ def _is_number(s): def validate_memory(namespace): - memory = namespace.memory - - if memory is not None: + if namespace.memory is not None: valid = False - if memory.endswith("Gi"): - valid = _is_number(memory[:-2]) + if not namespace.memory.endswith("Gi"): + namespace.memory = namespace.memory.rstrip() + namespace.memory += "Gi" + + valid = _is_number(namespace.memory[:-2]) if not valid: raise ValidationError("Usage error: --memory must be a number ending with \"Gi\"") diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index 52620d08e1d..8333e1ae026 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -528,7 +528,7 @@ def update_containerapp_logic(cmd, if len(containerapp_def["properties"]["template"]["containers"]) == 1: container_name = containerapp_def["properties"]["template"]["containers"][0]["name"] else: - raise ValidationError("Usage error: --image-name is required when adding or updating a container") + raise ValidationError("Usage error: --container-name is required when adding or updating a container") # Check if updating existing container updating_existing_container = False @@ -1731,7 +1731,7 @@ def set_secrets(cmd, name, resource_group_name, secrets, try: r = ContainerAppClient.create_or_update( cmd=cmd, resource_group_name=resource_group_name, name=name, container_app_envelope=containerapp_def, no_wait=no_wait) - logger.warning("Containerapp must be restarted in order for secret changes to take effect.") + logger.warning("Containerapp '{}' must be restarted in order for secret changes to take effect.".format(name)) return r["properties"]["configuration"]["secrets"] except Exception as e: handle_raw_exception(e) From 5fcd7857c5389053309d0007aa31d2e4bea200f2 Mon Sep 17 00:00:00 2001 From: Calvin Date: Mon, 11 Apr 2022 11:46:16 -0700 Subject: [PATCH 4/9] Fixes (#60) --- src/containerapp/azext_containerapp/_help.py | 6 ------ src/containerapp/azext_containerapp/_utils.py | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/containerapp/azext_containerapp/_help.py b/src/containerapp/azext_containerapp/_help.py index c6809690a73..0d6a2ae001f 100644 --- a/src/containerapp/azext_containerapp/_help.py +++ b/src/containerapp/azext_containerapp/_help.py @@ -29,12 +29,6 @@ --image nginx --environment MyContainerappEnv \\ --cpu 0.5 --memory 1.0Gi \\ --min-replicas 4 --max-replicas 8 - - name: Create a container app with secrets and environment variables. - text: | - az containerapp create -n MyContainerapp -g MyResourceGroup \\ - --image my-app:v1.0 --environment MyContainerappEnv \\ - --secrets mysecret=secretvalue1 anothersecret="secret value 2" \\ - --env-vars GREETING="Hello, world" SECRETENV=secretref:anothersecret - name: Create a container app using a YAML configuration. Example YAML configuration - https://aka.ms/azure-container-apps-yaml text: | az containerapp create -n MyContainerapp -g MyResourceGroup \\ diff --git a/src/containerapp/azext_containerapp/_utils.py b/src/containerapp/azext_containerapp/_utils.py index 0be73b8e767..39ccef52633 100644 --- a/src/containerapp/azext_containerapp/_utils.py +++ b/src/containerapp/azext_containerapp/_utils.py @@ -293,7 +293,7 @@ def _generate_log_analytics_if_not_provided(cmd, logs_customer_id, logs_key, loc def _get_existing_secrets(cmd, resource_group_name, name, containerapp_def): - if "secrets" not in containerapp_def["properties"]["configuration"] or not containerapp_def["properties"]["configuration"]["secrets"]: + if "secrets" not in containerapp_def["properties"]["configuration"]: containerapp_def["properties"]["configuration"]["secrets"] = [] else: secrets = [] From a1929c87e2b7b337d1b830be7ea657d9cd97af4c Mon Sep 17 00:00:00 2001 From: Haroon Feisal <38823870+haroonf@users.noreply.github.com> Date: Mon, 11 Apr 2022 15:25:17 -0400 Subject: [PATCH 5/9] Updated managed identity + help. (#61) Co-authored-by: Haroon Feisal --- src/containerapp/azext_containerapp/_help.py | 22 ++++++-- .../azext_containerapp/_params.py | 7 ++- src/containerapp/azext_containerapp/custom.py | 55 +++++++++++-------- 3 files changed, 52 insertions(+), 32 deletions(-) diff --git a/src/containerapp/azext_containerapp/_help.py b/src/containerapp/azext_containerapp/_help.py index 0d6a2ae001f..c02d04b49bc 100644 --- a/src/containerapp/azext_containerapp/_help.py +++ b/src/containerapp/azext_containerapp/_help.py @@ -271,13 +271,13 @@ examples: - name: Assign system identity. text: | - az containerapp identity assign + az containerapp identity assign -n myContainerapp -g MyResourceGroup --system-assigned - name: Assign user identity. text: | - az containerapp identity assign --identities myAssignedId + az containerapp identity assign -n myContainerapp -g MyResourceGroup --user-assigned myAssignedId - name: Assign system and user identity. text: | - az containerapp identity assign --identities [system] myAssignedId + az containerapp identity assign -n myContainerapp -g MyResourceGroup --system-assigned --user-assigned myAssignedId """ helps['containerapp identity remove'] = """ @@ -286,15 +286,25 @@ examples: - name: Remove system identity. text: | - az containerapp identity remove --identities [system] + az containerapp identity remove -n myContainerapp -g MyResourceGroup --system-assigned - name: Remove system and user identity. text: | - az containerapp identity remove --identities [system] myAssignedId + az containerapp identity remove -n myContainerapp -g MyResourceGroup --system-assigned --user-assigned myAssignedId + - name: Remove all user identities. + text: | + az containerapp identity remove -n myContainerapp -g MyResourceGroup --user-assigned + - name: Remove system identity and all user identities. + text: | + az containerapp identity remove -n myContainerapp -g MyResourceGroup --system-assigned --user-assigned """ helps['containerapp identity show'] = """ type: command short-summary: Show managed identities of a container app. + examples: + - name: Show managed identities. + text: | + az containerapp identity show -n myContainerapp -g MyResourceGroup """ # Ingress Commands @@ -520,7 +530,7 @@ helps['containerapp dapr disable'] = """ type: command - short-summary: Disable Dapr for a container app. + short-summary: Disable Dapr for a container app. Removes existing values. examples: - name: Disable Dapr for a container app. text: | diff --git a/src/containerapp/azext_containerapp/_params.py b/src/containerapp/azext_containerapp/_params.py index af77982345f..b2d778e08ae 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -114,10 +114,11 @@ def load_arguments(self, _): c.argument('name', name_type, help='Name of the Container Apps Environment.') with self.argument_context('containerapp identity') as c: - c.argument('identities', nargs='+', help="Space-separated identities. Use '[system]' to refer to the system assigned identity.") + c.argument('user_assigned', nargs='+', help="Space-separated user identities.") + c.argument('system_assigned', help="System-assigned identity.") - with self.argument_context('containerapp identity assign') as c: - c.argument('identities', nargs='+', help="Space-separated identities. Use '[system]' to refer to the system assigned identity. Default is '[system]'.") + with self.argument_context('containerapp identity remove') as c: + c.argument('user_assigned', nargs='*', help="Space-separated user identities. If no user identities are specified, all user identities will be removed.") with self.argument_context('containerapp github-action add') as c: c.argument('repo_url', help='The GitHub repository to which the workflow file will be added. In the format: https://github.com//') diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index 8333e1ae026..c09a4d1b395 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -843,17 +843,13 @@ def delete_managed_environment(cmd, name, resource_group_name, no_wait=False): handle_raw_exception(e) -def assign_managed_identity(cmd, name, resource_group_name, identities=None, no_wait=False): +def assign_managed_identity(cmd, name, resource_group_name, system_assigned=False, user_assigned=None, no_wait=False): _validate_subscription_registered(cmd, "Microsoft.App") - # if no identities, then assign system by default - if not identities: - identities = ['[system]'] - logger.warning('Identities not specified. Assigning managed system identity.') - - identities = [x.lower() for x in identities] - assign_system_identity = '[system]' in identities - assign_user_identities = [x for x in identities if x != '[system]'] + assign_system_identity = system_assigned + if not user_assigned: + user_assigned = [] + assign_user_identities = [x.lower() for x in user_assigned] containerapp_def = None @@ -906,12 +902,16 @@ def assign_managed_identity(cmd, name, resource_group_name, identities=None, no_ subscription_id = get_subscription_id(cmd.cli_ctx) for r in assign_user_identities: - old_id = r r = _ensure_identity_resource_id(subscription_id, resource_group_name, r).replace("resourceGroup", "resourcegroup") - try: - containerapp_def["identity"]["userAssignedIdentities"][r] - logger.warning("User identity {} is already assigned to containerapp".format(old_id)) - except: + isExisting = False + + for old_user_identity in containerapp_def["identity"]["userAssignedIdentities"]: + if old_user_identity.lower() == r.lower(): + isExisting = True + logger.warning("User identity {} is already assigned to containerapp".format(old_user_identity)) + break + + if not isExisting: containerapp_def["identity"]["userAssignedIdentities"][r] = {} try: @@ -923,18 +923,19 @@ def assign_managed_identity(cmd, name, resource_group_name, identities=None, no_ handle_raw_exception(e) -def remove_managed_identity(cmd, name, resource_group_name, identities, no_wait=False): +def remove_managed_identity(cmd, name, resource_group_name, system_assigned=False, user_assigned=None, no_wait=False): _validate_subscription_registered(cmd, "Microsoft.App") - identities = [x.lower() for x in identities] - remove_system_identity = '[system]' in identities - remove_user_identities = [x for x in identities if x != '[system]'] - remove_id_size = len(remove_user_identities) + remove_system_identity = system_assigned + remove_user_identities = user_assigned - # Remove duplicate identities that are passed and notify - remove_user_identities = list(set(remove_user_identities)) - if remove_id_size != len(remove_user_identities): - logger.warning("At least one identity was passed twice.") + if user_assigned: + remove_id_size = len(remove_user_identities) + + # Remove duplicate identities that are passed and notify + remove_user_identities = list(set(remove_user_identities)) + if remove_id_size != len(remove_user_identities): + logger.warning("At least one identity was passed twice.") containerapp_def = None # Get containerapp properties of CA we are updating @@ -964,6 +965,14 @@ def remove_managed_identity(cmd, name, resource_group_name, identities, no_wait= raise InvalidArgumentValueError("The containerapp {} has no system assigned identities.".format(name)) containerapp_def["identity"]["type"] = ("None" if containerapp_def["identity"]["type"] == "SystemAssigned" else "UserAssigned") + if isinstance(user_assigned, list) and not user_assigned: + containerapp_def["identity"]["userAssignedIdentities"] = {} + remove_user_identities = [] + + if containerapp_def["identity"]["userAssignedIdentities"] == {}: + containerapp_def["identity"]["userAssignedIdentities"] = None + containerapp_def["identity"]["type"] = ("None" if containerapp_def["identity"]["type"] == "UserAssigned" else "SystemAssigned") + if remove_user_identities: subscription_id = get_subscription_id(cmd.cli_ctx) try: From df5cc993a0128c71824e63b9667c1fb0ba0e6c08 Mon Sep 17 00:00:00 2001 From: Haroon Feisal <38823870+haroonf@users.noreply.github.com> Date: Mon, 11 Apr 2022 16:00:36 -0400 Subject: [PATCH 6/9] Added user-assigned and system-assigned to containerapp create. (#62) Co-authored-by: Haroon Feisal --- src/containerapp/azext_containerapp/_params.py | 7 +++++-- src/containerapp/azext_containerapp/custom.py | 13 +++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/containerapp/azext_containerapp/_params.py b/src/containerapp/azext_containerapp/_params.py index b2d778e08ae..6ed1709f19e 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -75,9 +75,12 @@ def load_arguments(self, _): c.argument('transport', arg_type=get_enum_type(['auto', 'http', 'http2']), help="The transport protocol used for ingress traffic.") with self.argument_context('containerapp create') as c: - c.argument('assign_identity', nargs='+', help="Space-separated identities. Use '[system]' to refer to the system assigned identity.") c.argument('traffic_weights', nargs='*', options_list=['--traffic-weight'], help="A list of revision weight(s) for the container app. Space-separated values in 'revision_name=weight' format. For latest revision, use 'latest=weight'") + with self.argument_context('containerapp create', arg_group='Identity') as c: + 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.") + with self.argument_context('containerapp scale') as c: c.argument('min_replicas', type=int, help="The minimum number of replicas.") c.argument('max_replicas', type=int, help="The maximum number of replicas.") @@ -115,7 +118,7 @@ def load_arguments(self, _): with self.argument_context('containerapp identity') as c: c.argument('user_assigned', nargs='+', help="Space-separated user identities.") - c.argument('system_assigned', help="System-assigned identity.") + c.argument('system_assigned', help="Boolean indicating whether to assign system-assigned identity.") with self.argument_context('containerapp identity remove') as c: c.argument('user_assigned', nargs='*', help="Space-separated user identities. If no user identities are specified, all user identities will be removed.") diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index c09a4d1b395..06ce16ad26d 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -293,7 +293,8 @@ def create_containerapp(cmd, args=None, tags=None, no_wait=False, - assign_identity=None): + system_assigned=False, + user_assigned=None): _validate_subscription_registered(cmd, "Microsoft.App") if yaml: @@ -310,9 +311,6 @@ def create_containerapp(cmd, if managed_env is None: raise RequiredArgumentMissingError('Usage error: --environment is required if not using --yaml') - if assign_identity is None: - assign_identity = [] - # Validate managed environment parsed_managed_env = parse_resource_id(managed_env) managed_env_name = parsed_managed_env['name'] @@ -382,8 +380,11 @@ def create_containerapp(cmd, identity_def = ManagedServiceIdentityModel identity_def["type"] = "None" - assign_system_identity = '[system]' in assign_identity - assign_user_identities = [x for x in assign_identity if x != '[system]'] + assign_system_identity = system_assigned + if user_assigned: + assign_user_identities = [x.lower() for x in user_assigned] + else: + assign_user_identities = [] if assign_system_identity and assign_user_identities: identity_def["type"] = "SystemAssigned, UserAssigned" From 0dedf194f5f75e00e5b94281cbcf029c90aa2803 Mon Sep 17 00:00:00 2001 From: Calvin Date: Mon, 11 Apr 2022 14:53:08 -0700 Subject: [PATCH 7/9] Bump version to 0.1.1 (#63) --- src/containerapp/HISTORY.rst | 4 ++++ src/containerapp/azext_containerapp/_help.py | 6 ++++++ src/containerapp/azext_containerapp/_params.py | 2 +- src/containerapp/setup.py | 2 +- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/containerapp/HISTORY.rst b/src/containerapp/HISTORY.rst index b66465a832a..7f2c67553a4 100644 --- a/src/containerapp/HISTORY.rst +++ b/src/containerapp/HISTORY.rst @@ -3,6 +3,10 @@ Release History =============== +0.1.1 +++++++ +* Subgroup commands for managed identities: az containerapp identity + 0.1.0 ++++++ * Initial release for Container App support with Microsoft.App RP. diff --git a/src/containerapp/azext_containerapp/_help.py b/src/containerapp/azext_containerapp/_help.py index c02d04b49bc..708bee843e3 100644 --- a/src/containerapp/azext_containerapp/_help.py +++ b/src/containerapp/azext_containerapp/_help.py @@ -29,6 +29,12 @@ --image nginx --environment MyContainerappEnv \\ --cpu 0.5 --memory 1.0Gi \\ --min-replicas 4 --max-replicas 8 + - name: Create a container app with secrets and environment variables. + text: | + az containerapp create -n MyContainerapp -g MyResourceGroup \\ + --image my-app:v1.0 --environment MyContainerappEnv \\ + --secrets mysecret=secretvalue1 anothersecret="secret value 2" \\ + --env-vars GREETING="Hello, world" SECRETENV=secretref:anothersecret - name: Create a container app using a YAML configuration. Example YAML configuration - https://aka.ms/azure-container-apps-yaml text: | az containerapp create -n MyContainerapp -g MyResourceGroup \\ diff --git a/src/containerapp/azext_containerapp/_params.py b/src/containerapp/azext_containerapp/_params.py index 6ed1709f19e..4c958cd0077 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -88,7 +88,7 @@ def load_arguments(self, _): with self.argument_context('containerapp env') as c: c.argument('name', name_type, help='Name of the Container Apps environment.') c.argument('resource_group_name', arg_type=resource_group_name_type) - c.argument('location', arg_type=get_location_type(self.cli_ctx), help='Location of resource. Examples: Canada Central, North Europe') + c.argument('location', arg_type=get_location_type(self.cli_ctx), help='Location of resource. Examples: eastus2, northeurope') c.argument('tags', arg_type=tags_type) with self.argument_context('containerapp env', arg_group='Log Analytics') as c: diff --git a/src/containerapp/setup.py b/src/containerapp/setup.py index e23b0011367..104b3e22417 100644 --- a/src/containerapp/setup.py +++ b/src/containerapp/setup.py @@ -16,7 +16,7 @@ # TODO: Confirm this is the right version number you want and it matches your # HISTORY.rst entry. -VERSION = '0.1.0' +VERSION = '0.1.1' # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers From 1474d693b47c6bd93cc0f8b1d92fcb23388d70f7 Mon Sep 17 00:00:00 2001 From: Haroon Feisal <38823870+haroonf@users.noreply.github.com> Date: Mon, 11 Apr 2022 18:25:26 -0400 Subject: [PATCH 8/9] Added more specific MSI help text. (#64) * Added more specific MSI help text. * Updated help text. Co-authored-by: Haroon Feisal --- src/containerapp/azext_containerapp/_help.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/containerapp/azext_containerapp/_help.py b/src/containerapp/azext_containerapp/_help.py index 708bee843e3..895c769a2dc 100644 --- a/src/containerapp/azext_containerapp/_help.py +++ b/src/containerapp/azext_containerapp/_help.py @@ -280,10 +280,13 @@ az containerapp identity assign -n myContainerapp -g MyResourceGroup --system-assigned - name: Assign user identity. text: | - az containerapp identity assign -n myContainerapp -g MyResourceGroup --user-assigned myAssignedId + az containerapp identity assign -n myContainerapp -g MyResourceGroup --user-assigned myUserIdentityName + - name: Assign user identity (from a different resource group than the containerapp). + text: | + az containerapp identity assign -n myContainerapp -g MyResourceGroup --user-assigned myUserIdentityResourceId - name: Assign system and user identity. text: | - az containerapp identity assign -n myContainerapp -g MyResourceGroup --system-assigned --user-assigned myAssignedId + az containerapp identity assign -n myContainerapp -g MyResourceGroup --system-assigned --user-assigned myUserIdentityResourceId """ helps['containerapp identity remove'] = """ @@ -293,9 +296,12 @@ - name: Remove system identity. text: | az containerapp identity remove -n myContainerapp -g MyResourceGroup --system-assigned - - name: Remove system and user identity. + - name: Remove user identity. + text: | + az containerapp identity remove -n myContainerapp -g MyResourceGroup --user-assigned myUserIdentityName + - name: Remove system and user identity (from a different resource group than the containerapp). text: | - az containerapp identity remove -n myContainerapp -g MyResourceGroup --system-assigned --user-assigned myAssignedId + az containerapp identity remove -n myContainerapp -g MyResourceGroup --system-assigned --user-assigned myUserIdentityResourceId - name: Remove all user identities. text: | az containerapp identity remove -n myContainerapp -g MyResourceGroup --user-assigned From 5afc2b1c3c98b6d83c28173050f5734a8227f702 Mon Sep 17 00:00:00 2001 From: Calvin Date: Mon, 11 Apr 2022 15:26:40 -0700 Subject: [PATCH 9/9] Bump to 0.3.0 (#65) --- src/containerapp/HISTORY.rst | 2 +- src/containerapp/setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/containerapp/HISTORY.rst b/src/containerapp/HISTORY.rst index 7f2c67553a4..2abbd24f08a 100644 --- a/src/containerapp/HISTORY.rst +++ b/src/containerapp/HISTORY.rst @@ -3,7 +3,7 @@ Release History =============== -0.1.1 +0.3.0 ++++++ * Subgroup commands for managed identities: az containerapp identity diff --git a/src/containerapp/setup.py b/src/containerapp/setup.py index 104b3e22417..490525870da 100644 --- a/src/containerapp/setup.py +++ b/src/containerapp/setup.py @@ -16,7 +16,7 @@ # TODO: Confirm this is the right version number you want and it matches your # HISTORY.rst entry. -VERSION = '0.1.1' +VERSION = '0.3.0' # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers