Skip to content
Merged
92 changes: 89 additions & 3 deletions src/azure-cli/azure/cli/command_modules/resource/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -1958,7 +1958,7 @@

helps['tag'] = """
type: group
short-summary: Manage resource tags.
short-summary: Tag Management on a resource.
"""

helps['tag add-value'] = """
Expand All @@ -1972,18 +1972,104 @@

helps['tag create'] = """
type: command
short-summary: Create a tag in the subscription.
short-summary: Create tags on a specific resource.
long-summary: >
The az tag create command with an id creates or updates the entire set of tags on a resource, resource group or subscription.
This operation allows adding or replacing the entire set of tags on the specified resource, resource group or subscription.
The specified entity can have a maximum of 50 tags.
parameters:
- name: --name -n
short-summary: The name of the tag to create.
- name: --subscription
short-summary: Name or ID of subscription. You can configure the default subscription using az account set -s NAME_OR_ID.
- name: --resource-id
short-summary: The resource identifier for the entity being tagged. A resource, a resource group or a subscription may be tagged.
- name: --tags
short-summary: The tags to be applied on the resource.
examples:
- name: Create a tag in the subscription.
text: >
az tag create --name MyTag
- name: Create or update the entire set of tags on a subscription.
text: >
az tag create --resource-id /subscriptions/{subId} --tags Dept=Finance Status=Normal
- name: Create or update the entire set of tags on a resource group.
text: >
az tag create --resource-id /subscriptions/{sub-id}/resourcegroups/{rg} --tags Dept=Finance Status=Normal
- name: Create or update the entire set of tags on a resource.
text: >
az tag create --resource-id /subscriptions/{sub-id}/resourcegroups/{rg}/providers/Microsoft.Compute/virtualMachines/{vmName} --tags Dept=Finance Status=Normal
"""

helps['tag delete'] = """
type: command
short-summary: Delete a tag in the subscription.
short-summary: Delete tags on a specific resource.
long-summary:
The az tag delete command with an id deletes the entire set of tags on a resource, resource group or subscription.
parameters:
- name: --name -n
short-summary: The name of the tag to be deleted.
- name: --resource-id
short-summary: The resource identifier for the entity being tagged. A resource, a resource group or a subscription may be tagged.
examples:
- name: Delete a tag from the subscription.
text: >
az tag delete --name MyTag
- name: Delete the entire set of tags on a subscription.
text: >
az tag delete --resource-id /subscriptions/{sub-id}
- name: Delete the entire set of tags on a resource group.
text: >
az tag delete --resource-id /subscriptions/{sub-id}/resourcegroups/{rg}
- name: Delete the entire set of tags on a resource.
text: >
az tag delete --resource-id /subscriptions/{sub-id}/resourcegroups/{rg}/providers/Microsoft.Compute/virtualMachines/{vmName}
"""

helps['tag list'] = """
type: command
short-summary: List the entire set of tags on a specific resource.
long-summary: The az tag list command with an id lists the entire set of tags on a resource, resource group or subscription.
parameters:
- name: --resource-id
short-summary: The resource identifier for the entity being tagged. A resource, a resource group or a subscription may be tagged.
examples:
- name: List the entire set of tags on a subscription.
text: >
az tag list --resource-id /subscriptions/{sub-id}
- name: List the entire set of tags on a resource group.
text: >
az tag list --resource-id /subscriptions/{sub-id}/resourcegroups/{rg}
- name: List the entire set of tags on a resource.
text: >
az tag list --resource-id /subscriptions/{sub-id}/resourcegroups/{rg}/providers/Microsoft.Compute/virtualMachines/{vmName}
"""

helps['tag update'] = """
type: command
short-summary: Selectively update the set of tags on a specific resource.
long-summary: >
The az tag update command with an id selectively updates the set of tags on a resource, resource group or subscription.
This operation allows replacing, merging or selectively deleting tags on the specified resource, resource group or subscription.
The specified entity can have a maximum of 50 tags at the end of the operation.
The 'replace' option replaces the entire set of existing tags with a new set.
The 'merge' option allows adding tags with new names and updating the values of tags with existing names.
The 'delete' option allows selectively deleting tags based on given names or name/value pairs.
parameters:
- name: --resource-id
short-summary: The resource identifier for the entity being tagged. A resource, a resource group or a subscription may be tagged.
- name: --operation
short-summary: The update operation. Options are Merge, Replace and Delete.
- name: --tags
short-summary: The tags to be updated on the resource.
examples:
- name: Selectively update the set of tags on a subscription with "merge" Operation.
text: >
az tag update --resource-id /subscriptions/{sub-id} --operation merge --tags key1=value1 key3=value3
- name: Selectively update the set of tags on a resource group with "replace" Operation.
text: >
az tag update --resource-id /subscriptions/{sub-id}/resourcegroups/{rg} --operation replace --tags key1=value1 key3=value3
- name: Selectively update the set of tags on a resource with "delete" Operation.
text: >
az tag update --resource-id /subscriptions/{sub-id}/resourcegroups/{rg}/providers/Microsoft.Compute/virtualMachines/{vmName} --operation delete --tags key1=value1
"""
14 changes: 12 additions & 2 deletions src/azure-cli/azure/cli/command_modules/resource/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def load_arguments(self, _):
from azure.cli.command_modules.resource._validators import (
validate_lock_parameters, validate_resource_lock, validate_group_lock, validate_subscription_lock, validate_metadata, RollbackAction,
validate_msi)
from azure.cli.command_modules.resource.parameters import TagUpdateOperation

DeploymentMode, WhatIfResultFormat, ChangeType = self.get_models('DeploymentMode', 'WhatIfResultFormat', 'ChangeType')

Expand Down Expand Up @@ -68,6 +69,11 @@ def load_arguments(self, _):
arg_type=get_enum_type(ChangeType),
help='Space-separated list of resource change types to be excluded from What-If results.',
is_preview=True, min_api='2019-07-01')
tag_name_type = CLIArgumentType(options_list=['--name', '-n'], help='The tag name.')
tag_value_type = CLIArgumentType(options_list='--value', help='The tag value.')
tag_resource_id_type = CLIArgumentType(options_list='--resource-id',
help='The resource identifier for the tagged entity. A resource, a resource group or a subscription may be tagged.',
min_api='2019-10-01')

_PROVIDER_HELP_TEXT = 'the resource namespace, aka \'provider\''

Expand Down Expand Up @@ -397,8 +403,12 @@ def load_arguments(self, _):
options_list=['--name', '-n', '--resource-group', '-g'], local_context_attribute=None)

with self.argument_context('tag') as c:
c.argument('tag_name', options_list=['--name', '-n'])
c.argument('tag_value', options_list='--value')
c.argument('tag_name', tag_name_type)
c.argument('tag_value', tag_value_type)
c.argument('resource_id', tag_resource_id_type)
c.argument('tags', tags_type)
c.argument('operation', arg_type=get_enum_type([item.value for item in list(TagUpdateOperation)]),
help='The update operation: options include Merge, Replace and Delete.')

with self.argument_context('lock') as c:
c.argument('lock_name', options_list=['--name', '-n'], validator=validate_lock_parameters)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,10 +221,11 @@ def load_command_table(self, _):
g.custom_command('unregister', 'unregister_feature')

# Tag commands
with self.command_group('tag', resource_tag_sdk) as g:
g.command('list', 'list')
g.command('create', 'create_or_update')
g.command('delete', 'delete')
with self.command_group('tag', resource_tag_sdk, resource_type=ResourceType.MGMT_RESOURCE_RESOURCES) as g:
g.custom_command('list', 'get_tag_at_scope')
g.custom_command('create', 'create_or_update_tag_at_scope')
g.custom_command('delete', 'delete_tag_at_scope', confirmation=True)
g.custom_command('update', 'update_tag_at_scope', min_api='2019-10-01')
g.command('add-value', 'create_or_update_value')
g.command('remove-value', 'delete_value')

Expand Down
39 changes: 39 additions & 0 deletions src/azure-cli/azure/cli/command_modules/resource/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -2537,6 +2537,45 @@ def list_resource_links(cmd, scope=None, filter_string=None):
# endregion


# region tags
def get_tag_at_scope(cmd, resource_id=None):
rcf = _resource_client_factory(cmd.cli_ctx)
if resource_id is not None:
return rcf.tags.get_at_scope(scope=resource_id)

return rcf.tags.list()


def create_or_update_tag_at_scope(cmd, resource_id=None, tags=None, tag_name=None):
rcf = _resource_client_factory(cmd.cli_ctx)
if resource_id is not None:
if not tags:
raise IncorrectUsageError("Tags could not be empty.")
Tags = cmd.get_models('Tags')
tag_obj = Tags(tags=tags)
return rcf.tags.create_or_update_at_scope(scope=resource_id, properties=tag_obj)

return rcf.tags.create_or_update(tag_name=tag_name)


def delete_tag_at_scope(cmd, resource_id=None, tag_name=None):
rcf = _resource_client_factory(cmd.cli_ctx)
if resource_id is not None:
return rcf.tags.delete_at_scope(scope=resource_id)

return rcf.tags.delete(tag_name=tag_name)


def update_tag_at_scope(cmd, resource_id, tags, operation):
rcf = _resource_client_factory(cmd.cli_ctx)
if not tags:
raise IncorrectUsageError("Tags could not be empty.")
Tags = cmd.get_models('Tags')
tag_obj = Tags(tags=tags)
return rcf.tags.update_at_scope(scope=resource_id, properties=tag_obj, operation=operation)
# endregion


class _ResourceUtils: # pylint: disable=too-many-instance-attributes
def __init__(self, cli_ctx,
resource_group_name=None, resource_provider_namespace=None,
Expand Down
13 changes: 13 additions & 0 deletions src/azure-cli/azure/cli/command_modules/resource/parameters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------


from enum import Enum


class TagUpdateOperation(str, Enum):
merge = "Merge"
replace = "Replace"
delete = "Delete"
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ def test_tag_scenario(self):
tag_values = self.cmd('tag list --query "[?tagName == \'{tag}\'].values[].tagValue"').get_output_in_json()
for tag_value in tag_values:
self.cmd('tag remove-value --value {} -n {{tag}}'.format(tag_value))
self.cmd('tag delete -n {tag}')
self.cmd('tag delete -n {tag} -y')

self.cmd('tag list --query "[?tagName == \'{tag}\']"', checks=self.is_empty())
self.cmd('tag create -n {tag}', checks=[
Expand All @@ -204,7 +204,7 @@ def test_tag_scenario(self):
self.cmd('tag remove-value -n {tag} --value test2')
self.cmd('tag list --query "[?tagName == \'{tag}\']"',
checks=self.check('[].values[].tagValue', []))
self.cmd('tag delete -n {tag}')
self.cmd('tag delete -n {tag} -y')
self.cmd('tag list --query "[?tagName == \'{tag}\']"',
checks=self.is_empty())

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ def test_tag_scenario(self):
tag_values = self.cmd('tag list --query "[?tagName == \'{tag}\'].values[].tagValue"').get_output_in_json()
for tag_value in tag_values:
self.cmd('tag remove-value --value {} -n {{tag}}'.format(tag_value))
self.cmd('tag delete -n {tag}')
self.cmd('tag delete -n {tag} -y')

self.cmd('tag list --query "[?tagName == \'{tag}\']"', checks=self.is_empty())
self.cmd('tag create -n {tag}', checks=[
Expand All @@ -261,7 +261,7 @@ def test_tag_scenario(self):
self.cmd('tag remove-value -n {tag} --value test2')
self.cmd('tag list --query "[?tagName == \'{tag}\']"',
checks=self.check('[].values[].tagValue', []))
self.cmd('tag delete -n {tag}')
self.cmd('tag delete -n {tag} -y')
self.cmd('tag list --query "[?tagName == \'{tag}\']"',
checks=self.is_empty())

Expand Down
Loading