diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index fad22f067f4..cc2770cc6cc 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -237,3 +237,5 @@ /src/spring/ @yuwzho /src/containerapp-compose/ @smurawski @jldeen + +/src/change-analysis/ @linglingtong diff --git a/src/change-analysis/HISTORY.rst b/src/change-analysis/HISTORY.rst new file mode 100644 index 00000000000..8c34bccfff8 --- /dev/null +++ b/src/change-analysis/HISTORY.rst @@ -0,0 +1,8 @@ +.. :changelog: + +Release History +=============== + +0.1.0 +++++++ +* Initial release. \ No newline at end of file diff --git a/src/change-analysis/README.md b/src/change-analysis/README.md new file mode 100644 index 00000000000..9207c7c4e8d --- /dev/null +++ b/src/change-analysis/README.md @@ -0,0 +1,28 @@ +# Azure CLI ChangeAnalysis Extension # +This is an extension to Azure CLI to manage ChangeAnalysis resources. + +## How to use ## +Install this extension using the below CLI command +``` +az extension add --name change-analysis +``` +### Included Features +#### List changes for resources: + +##### List the changes of a subscription within the specific time range. + +``` +az change-analysis list --start-time '05/24/2022 8:43:36' --end-time '05/25/2022 9:46:36' +``` + +##### List the changes of a resource group within the specific time range + +``` +az change-analysis list -g [ResourceGroup] --start-time '05/24/2022 8:43:36' --end-time '05/25/2022 9:46:36' +``` + +##### List the changes of a resource within the specified time range + +``` +az change-analysis list-by-resource -r [ResourceId] --start-time '05/24/2022 8:43:36' --end-time '05/25/2022 9:46:36' +``` \ No newline at end of file diff --git a/src/change-analysis/azext_change_analysis/__init__.py b/src/change-analysis/azext_change_analysis/__init__.py new file mode 100644 index 00000000000..c847b6b7e96 --- /dev/null +++ b/src/change-analysis/azext_change_analysis/__init__.py @@ -0,0 +1,42 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# +# Code generated by aaz-dev-tools +# -------------------------------------------------------------------------------------------- + +from azure.cli.core import AzCommandsLoader +from azext_change_analysis._help import helps # pylint: disable=unused-import + + +class ChangeAnalysisCommandsLoader(AzCommandsLoader): + + def __init__(self, cli_ctx=None): + from azure.cli.core.commands import CliCommandType + custom_command_type = CliCommandType( + operations_tmpl='azext_change_analysis.custom#{}') + super().__init__(cli_ctx=cli_ctx, + custom_command_type=custom_command_type) + + def load_command_table(self, args): + from azext_change_analysis.commands import load_command_table + from azure.cli.core.aaz import load_aaz_command_table + try: + from . import aaz + except ImportError: + aaz = None + if aaz: + load_aaz_command_table( + loader=self, + aaz_pkg_name=aaz.__name__, + args=args + ) + load_command_table(self, args) + return self.command_table + + def load_arguments(self, command): + from azext_change_analysis._params import load_arguments + load_arguments(self, command) + + +COMMAND_LOADER_CLS = ChangeAnalysisCommandsLoader diff --git a/src/change-analysis/azext_change_analysis/_help.py b/src/change-analysis/azext_change_analysis/_help.py new file mode 100644 index 00000000000..126d5d00714 --- /dev/null +++ b/src/change-analysis/azext_change_analysis/_help.py @@ -0,0 +1,11 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# +# Code generated by aaz-dev-tools +# -------------------------------------------------------------------------------------------- + +# pylint: disable=line-too-long +# pylint: disable=too-many-lines + +from knack.help_files import helps # pylint: disable=unused-import diff --git a/src/change-analysis/azext_change_analysis/_params.py b/src/change-analysis/azext_change_analysis/_params.py new file mode 100644 index 00000000000..cfcec717c9c --- /dev/null +++ b/src/change-analysis/azext_change_analysis/_params.py @@ -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. +# +# Code generated by aaz-dev-tools +# -------------------------------------------------------------------------------------------- + +# pylint: disable=too-many-lines +# pylint: disable=too-many-statements + + +def load_arguments(self, _): # pylint: disable=unused-argument + pass diff --git a/src/change-analysis/azext_change_analysis/aaz/__init__.py b/src/change-analysis/azext_change_analysis/aaz/__init__.py new file mode 100644 index 00000000000..5757aea3175 --- /dev/null +++ b/src/change-analysis/azext_change_analysis/aaz/__init__.py @@ -0,0 +1,6 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# +# Code generated by aaz-dev-tools +# -------------------------------------------------------------------------------------------- diff --git a/src/change-analysis/azext_change_analysis/aaz/latest/__init__.py b/src/change-analysis/azext_change_analysis/aaz/latest/__init__.py new file mode 100644 index 00000000000..5757aea3175 --- /dev/null +++ b/src/change-analysis/azext_change_analysis/aaz/latest/__init__.py @@ -0,0 +1,6 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# +# Code generated by aaz-dev-tools +# -------------------------------------------------------------------------------------------- diff --git a/src/change-analysis/azext_change_analysis/aaz/latest/change_analysis/__cmd_group.py b/src/change-analysis/azext_change_analysis/aaz/latest/change_analysis/__cmd_group.py new file mode 100644 index 00000000000..19797678fb2 --- /dev/null +++ b/src/change-analysis/azext_change_analysis/aaz/latest/change_analysis/__cmd_group.py @@ -0,0 +1,23 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# +# Code generated by aaz-dev-tools +# -------------------------------------------------------------------------------------------- + +# pylint: skip-file +# flake8: noqa + +from azure.cli.core.aaz import * + + +@register_command_group( + "change-analysis", +) +class __CMDGroup(AAZCommandGroup): + """List changes for resources + """ + pass + + +__all__ = ["__CMDGroup"] diff --git a/src/change-analysis/azext_change_analysis/aaz/latest/change_analysis/__init__.py b/src/change-analysis/azext_change_analysis/aaz/latest/change_analysis/__init__.py new file mode 100644 index 00000000000..4167e108267 --- /dev/null +++ b/src/change-analysis/azext_change_analysis/aaz/latest/change_analysis/__init__.py @@ -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. +# +# Code generated by aaz-dev-tools +# -------------------------------------------------------------------------------------------- + +# pylint: skip-file +# flake8: noqa + +from .__cmd_group import * +from ._list import * +from ._list_by_resource import * diff --git a/src/change-analysis/azext_change_analysis/aaz/latest/change_analysis/_list.py b/src/change-analysis/azext_change_analysis/aaz/latest/change_analysis/_list.py new file mode 100644 index 00000000000..f1b1976f0a1 --- /dev/null +++ b/src/change-analysis/azext_change_analysis/aaz/latest/change_analysis/_list.py @@ -0,0 +1,394 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# +# Code generated by aaz-dev-tools +# -------------------------------------------------------------------------------------------- + +# pylint: skip-file +# flake8: noqa + +from azure.cli.core.aaz import * + + +@register_command( + "change-analysis list", + is_preview=True, +) +class List(AAZCommand): + """List the changes of resources in the subscription within the specified time range. Customer data will always be masked. + + :example: List the changes of a subscription within the specific time range + az change-analysis list --start-time '05/24/2022 8:43:36' --end-time '05/25/2022 9:46:36' + + :example: List the changes of a resource group within the specific time range + az change-analysis list -g [ResourceGroup] --start-time '05/24/2022 8:43:36' --end-time '05/25/2022 9:46:36' + """ + + _aaz_info = { + "version": "2021-04-01", + "resources": [ + ["mgmt-plane", "/subscriptions/{}/providers/microsoft.changeanalysis/changes", "2021-04-01"], + ["mgmt-plane", "/subscriptions/{}/resourcegroups/{}/providers/microsoft.changeanalysis/changes", "2021-04-01"], + ] + } + + def _handler(self, command_args): + super()._handler(command_args) + return self.build_paging(self._execute_operations, self._output) + + _args_schema = None + + @classmethod + def _build_arguments_schema(cls, *args, **kwargs): + if cls._args_schema is not None: + return cls._args_schema + cls._args_schema = super()._build_arguments_schema(*args, **kwargs) + + # define Arg Group "" + + _args_schema = cls._args_schema + _args_schema.resource_group = AAZResourceGroupNameArg() + _args_schema.end_time = AAZStrArg( + options=["--end-time"], + help="Specifies the end time of the changes request.", + required=True, + ) + _args_schema.skip_token = AAZStrArg( + options=["--skip-token"], + help="A skip token is used to continue retrieving items after an operation returns a partial result. If a previous response contains a nextLink element, the value of the nextLink element will include a skipToken parameter that specifies a starting point to use for subsequent calls.", + ) + _args_schema.start_time = AAZStrArg( + options=["--start-time"], + help="Specifies the start time of the changes request.", + required=True, + ) + return cls._args_schema + + def _execute_operations(self): + condition_0 = has_value(self.ctx.args.resource_group) and has_value(self.ctx.subscription_id) and has_value(self.ctx.args.end_time) and has_value(self.ctx.args.start_time) + condition_1 = has_value(self.ctx.subscription_id) and has_value(self.ctx.args.end_time) and has_value(self.ctx.args.start_time) and has_value(self.ctx.args.resource_group) is not True + if condition_0: + self.ChangesListChangesByResourceGroup(ctx=self.ctx)() + if condition_1: + self.ChangesListChangesBySubscription(ctx=self.ctx)() + + def _output(self, *args, **kwargs): + result = self.deserialize_output(self.ctx.vars.instance.value, client_flatten=True) + next_link = self.deserialize_output(self.ctx.vars.instance.next_link) + return result, next_link + + class ChangesListChangesByResourceGroup(AAZHttpOperation): + CLIENT_TYPE = "MgmtClient" + + def __call__(self, *args, **kwargs): + request = self.make_request() + session = self.client.send_request(request=request, stream=False, **kwargs) + if session.http_response.status_code in [200]: + return self.on_200(session) + + return self.on_error(session.http_response) + + @property + def url(self): + return self.client.format_url( + "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ChangeAnalysis/changes", + **self.url_parameters + ) + + @property + def method(self): + return "GET" + + @property + def error_format(self): + return "MgmtErrorFormat" + + @property + def url_parameters(self): + parameters = { + **self.serialize_url_param( + "resourceGroupName", self.ctx.args.resource_group, + required=True, + ), + **self.serialize_url_param( + "subscriptionId", self.ctx.subscription_id, + required=True, + ), + } + return parameters + + @property + def query_parameters(self): + parameters = { + **self.serialize_query_param( + "$endTime", self.ctx.args.end_time, + required=True, + ), + **self.serialize_query_param( + "$skipToken", self.ctx.args.skip_token, + ), + **self.serialize_query_param( + "$startTime", self.ctx.args.start_time, + required=True, + ), + **self.serialize_query_param( + "api-version", "2021-04-01", + required=True, + ), + } + return parameters + + @property + def header_parameters(self): + parameters = { + **self.serialize_header_param( + "Accept", "application/json", + ), + } + return parameters + + def on_200(self, session): + data = self.deserialize_http_content(session) + self.ctx.set_var( + "instance", + data, + schema_builder=self._build_schema_on_200 + ) + + _schema_on_200 = None + + @classmethod + def _build_schema_on_200(cls): + if cls._schema_on_200 is not None: + return cls._schema_on_200 + + cls._schema_on_200 = AAZObjectType() + + _schema_on_200 = cls._schema_on_200 + _schema_on_200.next_link = AAZStrType( + serialized_name="nextLink", + ) + _schema_on_200.value = AAZListType() + + value = cls._schema_on_200.value + value.Element = AAZObjectType() + + _element = cls._schema_on_200.value.Element + _element.id = AAZStrType( + flags={"read_only": True}, + ) + _element.name = AAZStrType( + flags={"read_only": True}, + ) + _element.properties = AAZObjectType() + _element.type = AAZStrType( + flags={"read_only": True}, + ) + + properties = cls._schema_on_200.value.Element.properties + properties.change_type = AAZStrType( + serialized_name="changeType", + ) + properties.initiated_by_list = AAZListType( + serialized_name="initiatedByList", + ) + properties.property_changes = AAZListType( + serialized_name="propertyChanges", + ) + properties.resource_id = AAZStrType( + serialized_name="resourceId", + ) + properties.time_stamp = AAZStrType( + serialized_name="timeStamp", + ) + + initiated_by_list = cls._schema_on_200.value.Element.properties.initiated_by_list + initiated_by_list.Element = AAZStrType() + + property_changes = cls._schema_on_200.value.Element.properties.property_changes + property_changes.Element = AAZObjectType() + + _element = cls._schema_on_200.value.Element.properties.property_changes.Element + _element.change_category = AAZStrType( + serialized_name="changeCategory", + ) + _element.change_type = AAZStrType( + serialized_name="changeType", + ) + _element.description = AAZStrType() + _element.display_name = AAZStrType( + serialized_name="displayName", + ) + _element.is_data_masked = AAZBoolType( + serialized_name="isDataMasked", + ) + _element.json_path = AAZStrType( + serialized_name="jsonPath", + ) + _element.level = AAZStrType() + _element.new_value = AAZStrType( + serialized_name="newValue", + ) + _element.old_value = AAZStrType( + serialized_name="oldValue", + ) + + return cls._schema_on_200 + + class ChangesListChangesBySubscription(AAZHttpOperation): + CLIENT_TYPE = "MgmtClient" + + def __call__(self, *args, **kwargs): + request = self.make_request() + session = self.client.send_request(request=request, stream=False, **kwargs) + if session.http_response.status_code in [200]: + return self.on_200(session) + + return self.on_error(session.http_response) + + @property + def url(self): + return self.client.format_url( + "/subscriptions/{subscriptionId}/providers/Microsoft.ChangeAnalysis/changes", + **self.url_parameters + ) + + @property + def method(self): + return "GET" + + @property + def error_format(self): + return "MgmtErrorFormat" + + @property + def url_parameters(self): + parameters = { + **self.serialize_url_param( + "subscriptionId", self.ctx.subscription_id, + required=True, + ), + } + return parameters + + @property + def query_parameters(self): + parameters = { + **self.serialize_query_param( + "$endTime", self.ctx.args.end_time, + required=True, + ), + **self.serialize_query_param( + "$skipToken", self.ctx.args.skip_token, + ), + **self.serialize_query_param( + "$startTime", self.ctx.args.start_time, + required=True, + ), + **self.serialize_query_param( + "api-version", "2021-04-01", + required=True, + ), + } + return parameters + + @property + def header_parameters(self): + parameters = { + **self.serialize_header_param( + "Accept", "application/json", + ), + } + return parameters + + def on_200(self, session): + data = self.deserialize_http_content(session) + self.ctx.set_var( + "instance", + data, + schema_builder=self._build_schema_on_200 + ) + + _schema_on_200 = None + + @classmethod + def _build_schema_on_200(cls): + if cls._schema_on_200 is not None: + return cls._schema_on_200 + + cls._schema_on_200 = AAZObjectType() + + _schema_on_200 = cls._schema_on_200 + _schema_on_200.next_link = AAZStrType( + serialized_name="nextLink", + ) + _schema_on_200.value = AAZListType() + + value = cls._schema_on_200.value + value.Element = AAZObjectType() + + _element = cls._schema_on_200.value.Element + _element.id = AAZStrType( + flags={"read_only": True}, + ) + _element.name = AAZStrType( + flags={"read_only": True}, + ) + _element.properties = AAZObjectType() + _element.type = AAZStrType( + flags={"read_only": True}, + ) + + properties = cls._schema_on_200.value.Element.properties + properties.change_type = AAZStrType( + serialized_name="changeType", + ) + properties.initiated_by_list = AAZListType( + serialized_name="initiatedByList", + ) + properties.property_changes = AAZListType( + serialized_name="propertyChanges", + ) + properties.resource_id = AAZStrType( + serialized_name="resourceId", + ) + properties.time_stamp = AAZStrType( + serialized_name="timeStamp", + ) + + initiated_by_list = cls._schema_on_200.value.Element.properties.initiated_by_list + initiated_by_list.Element = AAZStrType() + + property_changes = cls._schema_on_200.value.Element.properties.property_changes + property_changes.Element = AAZObjectType() + + _element = cls._schema_on_200.value.Element.properties.property_changes.Element + _element.change_category = AAZStrType( + serialized_name="changeCategory", + ) + _element.change_type = AAZStrType( + serialized_name="changeType", + ) + _element.description = AAZStrType() + _element.display_name = AAZStrType( + serialized_name="displayName", + ) + _element.is_data_masked = AAZBoolType( + serialized_name="isDataMasked", + ) + _element.json_path = AAZStrType( + serialized_name="jsonPath", + ) + _element.level = AAZStrType() + _element.new_value = AAZStrType( + serialized_name="newValue", + ) + _element.old_value = AAZStrType( + serialized_name="oldValue", + ) + + return cls._schema_on_200 + + +__all__ = ["List"] diff --git a/src/change-analysis/azext_change_analysis/aaz/latest/change_analysis/_list_by_resource.py b/src/change-analysis/azext_change_analysis/aaz/latest/change_analysis/_list_by_resource.py new file mode 100644 index 00000000000..5b99a4e8246 --- /dev/null +++ b/src/change-analysis/azext_change_analysis/aaz/latest/change_analysis/_list_by_resource.py @@ -0,0 +1,231 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# +# Code generated by aaz-dev-tools +# -------------------------------------------------------------------------------------------- + +# pylint: skip-file +# flake8: noqa + +from azure.cli.core.aaz import * + + +@register_command( + "change-analysis list-by-resource", + is_preview=True, +) +class ListByResource(AAZCommand): + """List the changes of a resource within the specified time range. Customer data will be masked if the user doesn't have access. + + :example: List the changes of a resource within the specified time range + az change-analysis list-by-resource -r [ResourceId] --start-time '05/24/2022 8:43:36' --end-time '05/25/2022 9:46:36' + """ + + _aaz_info = { + "version": "2021-04-01", + "resources": [ + ["mgmt-plane", "/{resourceid}/providers/microsoft.changeanalysis/resourcechanges", "2021-04-01"], + ] + } + + def _handler(self, command_args): + super()._handler(command_args) + return self.build_paging(self._execute_operations, self._output) + + _args_schema = None + + @classmethod + def _build_arguments_schema(cls, *args, **kwargs): + if cls._args_schema is not None: + return cls._args_schema + cls._args_schema = super()._build_arguments_schema(*args, **kwargs) + + # define Arg Group "" + + _args_schema = cls._args_schema + _args_schema.resource = AAZStrArg( + options=["--resource", "-r"], + help="The identifier of the resource.", + required=True, + ) + _args_schema.end_time = AAZStrArg( + options=["--end-time"], + help="Specifies the end time of the changes request.", + required=True, + ) + _args_schema.skip_token = AAZStrArg( + options=["--skip-token"], + help="A skip token is used to continue retrieving items after an operation returns a partial result. If a previous response contains a nextLink element, the value of the nextLink element will include a skipToken parameter that specifies a starting point to use for subsequent calls.", + ) + _args_schema.start_time = AAZStrArg( + options=["--start-time"], + help="Specifies the start time of the changes request.", + required=True, + ) + return cls._args_schema + + def _execute_operations(self): + self.ResourceChangesList(ctx=self.ctx)() + + def _output(self, *args, **kwargs): + result = self.deserialize_output(self.ctx.vars.instance.value, client_flatten=True) + next_link = self.deserialize_output(self.ctx.vars.instance.next_link) + return result, next_link + + class ResourceChangesList(AAZHttpOperation): + CLIENT_TYPE = "MgmtClient" + + def __call__(self, *args, **kwargs): + request = self.make_request() + session = self.client.send_request(request=request, stream=False, **kwargs) + if session.http_response.status_code in [200]: + return self.on_200(session) + + return self.on_error(session.http_response) + + @property + def url(self): + return self.client.format_url( + "/{resourceId}/providers/Microsoft.ChangeAnalysis/resourceChanges", + **self.url_parameters + ) + + @property + def method(self): + return "POST" + + @property + def error_format(self): + return "MgmtErrorFormat" + + @property + def url_parameters(self): + parameters = { + **self.serialize_url_param( + "resourceId", self.ctx.args.resource, + required=True, + ), + } + return parameters + + @property + def query_parameters(self): + parameters = { + **self.serialize_query_param( + "$endTime", self.ctx.args.end_time, + required=True, + ), + **self.serialize_query_param( + "$skipToken", self.ctx.args.skip_token, + ), + **self.serialize_query_param( + "$startTime", self.ctx.args.start_time, + required=True, + ), + **self.serialize_query_param( + "api-version", "2021-04-01", + required=True, + ), + } + return parameters + + @property + def header_parameters(self): + parameters = { + **self.serialize_header_param( + "Accept", "application/json", + ), + } + return parameters + + def on_200(self, session): + data = self.deserialize_http_content(session) + self.ctx.set_var( + "instance", + data, + schema_builder=self._build_schema_on_200 + ) + + _schema_on_200 = None + + @classmethod + def _build_schema_on_200(cls): + if cls._schema_on_200 is not None: + return cls._schema_on_200 + + cls._schema_on_200 = AAZObjectType() + + _schema_on_200 = cls._schema_on_200 + _schema_on_200.next_link = AAZStrType( + serialized_name="nextLink", + ) + _schema_on_200.value = AAZListType() + + value = cls._schema_on_200.value + value.Element = AAZObjectType() + + _element = cls._schema_on_200.value.Element + _element.id = AAZStrType( + flags={"read_only": True}, + ) + _element.name = AAZStrType( + flags={"read_only": True}, + ) + _element.properties = AAZObjectType() + _element.type = AAZStrType( + flags={"read_only": True}, + ) + + properties = cls._schema_on_200.value.Element.properties + properties.change_type = AAZStrType( + serialized_name="changeType", + ) + properties.initiated_by_list = AAZListType( + serialized_name="initiatedByList", + ) + properties.property_changes = AAZListType( + serialized_name="propertyChanges", + ) + properties.resource_id = AAZStrType( + serialized_name="resourceId", + ) + properties.time_stamp = AAZStrType( + serialized_name="timeStamp", + ) + + initiated_by_list = cls._schema_on_200.value.Element.properties.initiated_by_list + initiated_by_list.Element = AAZStrType() + + property_changes = cls._schema_on_200.value.Element.properties.property_changes + property_changes.Element = AAZObjectType() + + _element = cls._schema_on_200.value.Element.properties.property_changes.Element + _element.change_category = AAZStrType( + serialized_name="changeCategory", + ) + _element.change_type = AAZStrType( + serialized_name="changeType", + ) + _element.description = AAZStrType() + _element.display_name = AAZStrType( + serialized_name="displayName", + ) + _element.is_data_masked = AAZBoolType( + serialized_name="isDataMasked", + ) + _element.json_path = AAZStrType( + serialized_name="jsonPath", + ) + _element.level = AAZStrType() + _element.new_value = AAZStrType( + serialized_name="newValue", + ) + _element.old_value = AAZStrType( + serialized_name="oldValue", + ) + + return cls._schema_on_200 + + +__all__ = ["ListByResource"] diff --git a/src/change-analysis/azext_change_analysis/azext_metadata.json b/src/change-analysis/azext_change_analysis/azext_metadata.json new file mode 100644 index 00000000000..e6fdc728d58 --- /dev/null +++ b/src/change-analysis/azext_change_analysis/azext_metadata.json @@ -0,0 +1,4 @@ +{ + "azext.isPreview": true, + "azext.minCliCoreVersion": "2.37.0" +} \ No newline at end of file diff --git a/src/change-analysis/azext_change_analysis/commands.py b/src/change-analysis/azext_change_analysis/commands.py new file mode 100644 index 00000000000..b0d842e4993 --- /dev/null +++ b/src/change-analysis/azext_change_analysis/commands.py @@ -0,0 +1,15 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# +# Code generated by aaz-dev-tools +# -------------------------------------------------------------------------------------------- + +# pylint: disable=too-many-lines +# pylint: disable=too-many-statements + +# from azure.cli.core.commands import CliCommandType + + +def load_command_table(self, _): # pylint: disable=unused-argument + pass diff --git a/src/change-analysis/azext_change_analysis/custom.py b/src/change-analysis/azext_change_analysis/custom.py new file mode 100644 index 00000000000..86df1e48ef5 --- /dev/null +++ b/src/change-analysis/azext_change_analysis/custom.py @@ -0,0 +1,14 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# +# Code generated by aaz-dev-tools +# -------------------------------------------------------------------------------------------- + +# pylint: disable=too-many-lines +# pylint: disable=too-many-statements + +from knack.log import get_logger + + +logger = get_logger(__name__) diff --git a/src/change-analysis/azext_change_analysis/tests/__init__.py b/src/change-analysis/azext_change_analysis/tests/__init__.py new file mode 100644 index 00000000000..5757aea3175 --- /dev/null +++ b/src/change-analysis/azext_change_analysis/tests/__init__.py @@ -0,0 +1,6 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# +# Code generated by aaz-dev-tools +# -------------------------------------------------------------------------------------------- diff --git a/src/change-analysis/azext_change_analysis/tests/latest/__init__.py b/src/change-analysis/azext_change_analysis/tests/latest/__init__.py new file mode 100644 index 00000000000..5757aea3175 --- /dev/null +++ b/src/change-analysis/azext_change_analysis/tests/latest/__init__.py @@ -0,0 +1,6 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# +# Code generated by aaz-dev-tools +# -------------------------------------------------------------------------------------------- diff --git a/src/change-analysis/azext_change_analysis/tests/latest/test_change_analysis.py b/src/change-analysis/azext_change_analysis/tests/latest/test_change_analysis.py new file mode 100644 index 00000000000..33babfdc31e --- /dev/null +++ b/src/change-analysis/azext_change_analysis/tests/latest/test_change_analysis.py @@ -0,0 +1,72 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# +# Code generated by aaz-dev-tools +# -------------------------------------------------------------------------------------------- + +import sys +import datetime +import time +from azure.cli.testsdk import * + + +class ChangeAnalysisScenario(LiveScenarioTest): + @ResourceGroupPreparer(name_prefix="cli_test_change_analysis_") + def test_change_analysis_list(self, resource_group): + print(resource_group) + start_time = datetime.datetime.utcnow() + str_start_time = start_time.strftime('%Y-%m-%dT%H:%M:%S') + + self.kwargs.update({ + 'vnet1': 'vnet1', + 'vnet2': 'vnet2', + 'subnet1': 'subnet1', + 'location': 'centralus', + 'rg': resource_group, + 'start_time': str_start_time, + }) + print(str_start_time) + + # prepare for some resoruce changes + self.cmd('network vnet create -g {rg} -n {vnet1} --subnet-name {subnet1} -l {location}') + self.cmd('network vnet create -g {rg} -n {vnet2} --subnet-name {subnet1} -l {location}') + + # wait for up to 5 minutes to ensure changes can get to ARG + changes = {} + for x in range(10): + time.sleep(30) + end_time = datetime.datetime.utcnow() + str_end_time = end_time.strftime('%Y-%m-%dT%H:%M:%S') + print(str_end_time) + self.kwargs.update({ + 'end_time': str_end_time, + }) + # list change for subscription + changes = self.cmd('change-analysis list --start-time {start_time} --end-time {end_time}').get_output_in_json() + print(changes) + if len(changes) > 0: + break + + assert len(changes) > 0 + self.assertIsNotNone(changes[0]['id']) + property_changes = changes[0]['properties']['propertyChanges'] + resource_id = changes[0]['properties']['resourceId'] + assert len(property_changes) > 0 + + # list changes for given resoruce group + changes = self.cmd('change-analysis list --start-time {start_time} --end-time {end_time} -g {rg}').get_output_in_json() + print(changes) + assert len(changes) > 0 + for change in changes: + self.assertEqual(change['resourceGroup'], resource_group) + + # list changes for not existed group, should not get any change + changes = self.cmd('change-analysis list --start-time {start_time} --end-time {end_time} -g foo').get_output_in_json() + assert len(changes) == 0 + + # list changes by resource id + changes = self.cmd('change-analysis list-by-resource --start-time {start_time} --end-time {end_time} --r ' + resource_id).get_output_in_json() + assert len(changes) > 0 + for change in changes: + self.assertEqual(change['properties']['resourceId'], resource_id) diff --git a/src/change-analysis/setup.cfg b/src/change-analysis/setup.cfg new file mode 100644 index 00000000000..2fdd96e5d39 --- /dev/null +++ b/src/change-analysis/setup.cfg @@ -0,0 +1 @@ +#setup.cfg \ No newline at end of file diff --git a/src/change-analysis/setup.py b/src/change-analysis/setup.py new file mode 100644 index 00000000000..05651957549 --- /dev/null +++ b/src/change-analysis/setup.py @@ -0,0 +1,49 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# +# Code generated by aaz-dev-tools +# -------------------------------------------------------------------------------------------- + +from codecs import open +from setuptools import setup, find_packages + + +# HISTORY.rst entry. +VERSION = '0.1.0' + +# The full list of classifiers is available at +# https://pypi.python.org/pypi?%3Aaction=list_classifiers +CLASSIFIERS = [ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'Intended Audience :: System Administrators', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'License :: OSI Approved :: MIT License', +] + +DEPENDENCIES = [] + +with open('README.md', 'r', encoding='utf-8') as f: + README = f.read() +with open('HISTORY.rst', 'r', encoding='utf-8') as f: + HISTORY = f.read() + +setup( + name='change-analysis', + version=VERSION, + description='Microsoft Azure Command-Line Tools ChangeAnalysis Extension.', + long_description=README + '\n\n' + HISTORY, + license='MIT', + author='Microsoft Corporation', + author_email='azpycli@microsoft.com', + url='https://github.com/Azure/azure-cli-extensions/tree/main/src/change-analysis', + classifiers=CLASSIFIERS, + packages=find_packages(exclude=["tests"]), + package_data={'azext_change_analysis': ['azext_metadata.json']}, + install_requires=DEPENDENCIES +) diff --git a/src/service_name.json b/src/service_name.json index 70cbfb40669..ffff84dc052 100644 --- a/src/service_name.json +++ b/src/service_name.json @@ -593,5 +593,10 @@ "Command": "az quota", "AzureServiceName": "Reserved VM Instances", "URL": "https://docs.microsoft.com/en-us/rest/api/reserved-vm-instances/quotaapi" + }, + { + "Command": "az change-analysis", + "AzureServiceName": "Change Analysis", + "URL": "https://docs.microsoft.com/en-us/azure/azure-monitor/change/change-analysis" } ]