diff --git a/src/azure-cli/azure/cli/command_modules/sql/_help.py b/src/azure-cli/azure/cli/command_modules/sql/_help.py index be720297a4a..69604702916 100644 --- a/src/azure-cli/azure/cli/command_modules/sql/_help.py +++ b/src/azure-cli/azure/cli/command_modules/sql/_help.py @@ -73,6 +73,11 @@ text: | az sql db audit-policy update -g mygroup -s myserver -n mydb --state Enabled \\ --lats Enabled --lawri myworkspaceresourceid + - name: Set the fields included in audit events sent to Azure Monitor. + text: | + az sql db audit-policy update -g mygroup -s myserver -n mydb --state Enabled \\ + --lats Enabled --lawri myworkspaceresourceid \\ + --required-fields event_time action_id statement - name: Disable a log analytics auditing policy. text: | az sql db audit-policy update -g mygroup -s myserver -n mydb @@ -1490,6 +1495,11 @@ text: | az sql server audit-policy update -g mygroup -n myserver --state Enabled \\ --lats Enabled --lawri myworkspaceresourceid + - name: Set the fields included in audit events sent to Azure Monitor. + text: | + az sql server audit-policy update -g mygroup -n myserver --state Enabled \\ + --lats Enabled --lawri myworkspaceresourceid \\ + --required-fields event_time action_id statement - name: Disable a log analytics auditing policy. text: | az sql server audit-policy update -g mygroup -n myserver diff --git a/src/azure-cli/azure/cli/command_modules/sql/_params.py b/src/azure-cli/azure/cli/command_modules/sql/_params.py index cc2445cd5a8..ffda2371122 100644 --- a/src/azure-cli/azure/cli/command_modules/sql/_params.py +++ b/src/azure-cli/azure/cli/command_modules/sql/_params.py @@ -1203,6 +1203,12 @@ def _configure_security_policy_storage_params(arg_ctx): 'Example: --actions FAILED_DATABASE_AUTHENTICATION_GROUP BATCH_COMPLETED_GROUP', nargs='+') + c.argument('required_fields', + arg_group=policy_arg_group, + help='List of fields to include in audit events. Can only be specified when the Azure Monitor ' + 'target is enabled.', + nargs='+') + c.argument('retention_days', arg_group=policy_arg_group, help='The number of days to retain audit logs.') @@ -2056,6 +2062,12 @@ def _configure_security_policy_storage_params(arg_ctx): 'Example: --actions FAILED_DATABASE_AUTHENTICATION_GROUP BATCH_COMPLETED_GROUP', nargs='+') + c.argument('required_fields', + arg_group=policy_arg_group, + help='List of fields to include in audit events. Can only be specified when the Azure Monitor ' + 'target is enabled.', + nargs='+') + c.argument('retention_days', arg_group=policy_arg_group, help='The number of days to retain audit logs.') diff --git a/src/azure-cli/azure/cli/command_modules/sql/_validators.py b/src/azure-cli/azure/cli/command_modules/sql/_validators.py index 334d8e0c939..80bd752344b 100644 --- a/src/azure-cli/azure/cli/command_modules/sql/_validators.py +++ b/src/azure-cli/azure/cli/command_modules/sql/_validators.py @@ -44,7 +44,7 @@ def _expansion_validator_impl(namespace): for name in arguments: # Get the validation map from the model type in order to determine # whether the argument should be required - validation = model_type._validation.get(name, None) # pylint: disable=protected-access + validation = getattr(model_type, '_validation', {}).get(name, None) # pylint: disable=protected-access required = validation.get('required', False) if validation else False # Generate the command line argument name from the property name diff --git a/src/azure-cli/azure/cli/command_modules/sql/commands.py b/src/azure-cli/azure/cli/command_modules/sql/commands.py index c92f56b1e8a..2214d2dc744 100644 --- a/src/azure-cli/azure/cli/command_modules/sql/commands.py +++ b/src/azure-cli/azure/cli/command_modules/sql/commands.py @@ -229,7 +229,7 @@ def load_command_table(self, _): g.custom_show_command('show', 'transparent_data_encryptions_get') database_encryption_protector_operations = CliCommandType( - operations_tmpl='azure.mgmt.sql.operations#DatabaseEncryptionProtectorOperations.{}', + operations_tmpl='azure.mgmt.sql.operations#DatabaseEncryptionProtectorsOperations.{}', client_factory=get_sql_database_encryption_protector_operations) with self.command_group('sql db tde key', diff --git a/src/azure-cli/azure/cli/command_modules/sql/custom.py b/src/azure-cli/azure/cli/command_modules/sql/custom.py index 0bcdd69f980..ede2c7ff1df 100644 --- a/src/azure-cli/azure/cli/command_modules/sql/custom.py +++ b/src/azure-cli/azure/cli/command_modules/sql/custom.py @@ -2470,6 +2470,7 @@ def _audit_policy_validate_arguments( storage_endpoint=None, storage_account_access_key=None, retention_days=None, + required_fields=None, log_analytics_target_state=None, log_analytics_workspace_resource_id=None, event_hub_target_state=None, @@ -2492,7 +2493,7 @@ def _audit_policy_validate_arguments( event_hub_name is not None if not state and not blob_storage_arguments_provided and\ - not log_analytics_arguments_provided and not event_hub_arguments_provided: + not log_analytics_arguments_provided and not event_hub_arguments_provided and required_fields is None: raise CLIError('Either state or blob storage or log analytics or event hub arguments are missing') if _is_audit_policy_state_enabled(state) and\ @@ -2503,7 +2504,8 @@ def _audit_policy_validate_arguments( if _is_audit_policy_state_disabled(state) and\ (blob_storage_arguments_provided or log_analytics_arguments_provided or - event_hub_name): + event_hub_name or + required_fields is not None): raise CLIError('No additional arguments should be provided once state is disabled') if (_is_audit_policy_state_none_or_disabled(blob_storage_target_state)) and\ @@ -2883,6 +2885,7 @@ def _audit_policy_update_global_settings( storage_endpoint=None, storage_account_access_key=None, audit_actions_and_groups=None, + required_fields=None, retention_days=None, log_analytics_target_state=None, event_hub_target_state=None): @@ -2925,6 +2928,12 @@ def _audit_policy_update_global_settings( log_analytics_target_state=log_analytics_target_state, event_hub_target_state=event_hub_target_state) + if required_fields is not None: + if not _is_audit_policy_state_enabled(instance.state) or not instance.is_azure_monitor_target_enabled: + raise ValidationError( + 'required-fields can only be specified when auditing and the Azure Monitor target are enabled') + instance.required_fields = required_fields + def _audit_policy_update_rollback( cmd, @@ -2974,6 +2983,7 @@ def _audit_policy_update( storage_endpoint=None, storage_account_access_key=None, audit_actions_and_groups=None, + required_fields=None, retention_days=None, category_name=None, log_analytics_target_state=None, @@ -2990,6 +3000,7 @@ def _audit_policy_update( storage_endpoint=storage_endpoint, storage_account_access_key=storage_account_access_key, retention_days=retention_days, + required_fields=required_fields, log_analytics_target_state=log_analytics_target_state, log_analytics_workspace_resource_id=log_analytics_workspace_resource_id, event_hub_target_state=event_hub_target_state, @@ -3034,6 +3045,7 @@ def _audit_policy_update( storage_endpoint=storage_endpoint, storage_account_access_key=storage_account_access_key, audit_actions_and_groups=audit_actions_and_groups, + required_fields=required_fields, retention_days=retention_days, log_analytics_target_state=log_analytics_target_state, event_hub_target_state=event_hub_target_state) @@ -3065,6 +3077,7 @@ def server_audit_policy_update( storage_endpoint=None, storage_account_access_key=None, audit_actions_and_groups=None, + required_fields=None, retention_days=None, log_analytics_target_state=None, log_analytics_workspace_resource_id=None, @@ -3087,6 +3100,7 @@ def server_audit_policy_update( storage_endpoint=storage_endpoint, storage_account_access_key=storage_account_access_key, audit_actions_and_groups=audit_actions_and_groups, + required_fields=required_fields, retention_days=retention_days, category_name='SQLSecurityAuditEvents', log_analytics_target_state=log_analytics_target_state, @@ -3108,6 +3122,7 @@ def db_audit_policy_update( storage_endpoint=None, storage_account_access_key=None, audit_actions_and_groups=None, + required_fields=None, retention_days=None, log_analytics_target_state=None, log_analytics_workspace_resource_id=None, @@ -3130,6 +3145,7 @@ def db_audit_policy_update( storage_endpoint=storage_endpoint, storage_account_access_key=storage_account_access_key, audit_actions_and_groups=audit_actions_and_groups, + required_fields=required_fields, retention_days=retention_days, category_name='SQLSecurityAuditEvents', log_analytics_target_state=log_analytics_target_state, @@ -4502,7 +4518,7 @@ def server_create( if minimal_tls_version is not None: kwargs['minimal_tls_version'] = minimal_tls_version else: - kwargs['minimal_tls_version'] = SqlServerMinimalTlsVersionType.tls_1_2 + kwargs['minimal_tls_version'] = SqlServerMinimalTlsVersionType.tls_1_2.value kwargs['key_id'] = key_id kwargs['federated_client_id'] = federated_client_id diff --git a/src/azure-cli/azure/cli/command_modules/sql/tests/latest/test_sql_audit_policy.py b/src/azure-cli/azure/cli/command_modules/sql/tests/latest/test_sql_audit_policy.py new file mode 100644 index 00000000000..77f8d42f829 --- /dev/null +++ b/src/azure-cli/azure/cli/command_modules/sql/tests/latest/test_sql_audit_policy.py @@ -0,0 +1,71 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import unittest +from types import SimpleNamespace +from unittest.mock import MagicMock, patch + +from azure.cli.core.azclierror import ValidationError +from azure.cli.core.util import CLIError +from azure.cli.command_modules.sql import custom + + +class SqlAuditPolicyRequiredFieldsTest(unittest.TestCase): + + def _enabled_instance(self, azure_monitor_enabled): + return SimpleNamespace( + state='Enabled', + storage_endpoint=None, + storage_account_access_key=None, + is_azure_monitor_target_enabled=azure_monitor_enabled, + audit_actions_and_groups=['BATCH_COMPLETED_GROUP']) + + @patch('azure.cli.command_modules.sql.custom._audit_policy_update_apply_azure_monitor_target_enabled') + @patch('azure.cli.command_modules.sql.custom._audit_policy_update_apply_blob_storage_details') + def test_required_fields_applied_when_azure_monitor_enabled(self, _, apply_azure_monitor): + instance = self._enabled_instance(azure_monitor_enabled=True) + + custom._audit_policy_update_global_settings( + cmd=MagicMock(), instance=instance, required_fields=['event_time', 'statement']) + + self.assertEqual(['event_time', 'statement'], instance.required_fields) + apply_azure_monitor.assert_called_once() + + @patch('azure.cli.command_modules.sql.custom._audit_policy_update_apply_azure_monitor_target_enabled') + @patch('azure.cli.command_modules.sql.custom._audit_policy_update_apply_blob_storage_details') + def test_required_fields_rejected_without_azure_monitor(self, _, apply_azure_monitor): + instance = self._enabled_instance(azure_monitor_enabled=False) + + with self.assertRaisesRegex(ValidationError, 'Azure Monitor target'): + custom._audit_policy_update_global_settings( + cmd=MagicMock(), instance=instance, required_fields=['event_time']) + + apply_azure_monitor.assert_called_once() + + def test_required_fields_alone_satisfies_argument_validation(self): + custom._audit_policy_validate_arguments(required_fields=['event_time']) + + def test_no_arguments_still_fails_argument_validation(self): + with self.assertRaises(CLIError): + custom._audit_policy_validate_arguments() + + def test_required_fields_rejected_when_state_disabled(self): + with self.assertRaisesRegex(CLIError, 'state is disabled'): + custom._audit_policy_validate_arguments(state='Disabled', required_fields=['event_time']) + + def test_show_returns_required_fields_from_sdk(self): + required_fields = ['event_time', 'action_id'] + client = MagicMock() + client.get.return_value = SimpleNamespace(state='Disabled', required_fields=required_fields) + + server_result = custom.server_audit_policy_show(MagicMock(), client, 'server', 'resource-group') + self.assertEqual(required_fields, server_result.required_fields) + + database_result = custom.db_audit_policy_show(MagicMock(), client, 'server', 'resource-group', 'database') + self.assertEqual(required_fields, database_result.required_fields) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/azure-cli/pyproject.toml b/src/azure-cli/pyproject.toml index 8d91fe93b03..b0cb12c8dd3 100644 --- a/src/azure-cli/pyproject.toml +++ b/src/azure-cli/pyproject.toml @@ -98,7 +98,7 @@ dependencies = [ 'azure-mgmt-servicefabric~=2.1.0', 'azure-mgmt-signalr==2.0.0b2', 'azure-mgmt-sqlvirtualmachine==1.0.0b5', - 'azure-mgmt-sql==4.0.0b22', + 'azure-mgmt-sql==5.0.0b2', 'azure-mgmt-storage==25.0.0', 'azure-mgmt-synapse==2.1.0b5', 'azure-mgmt-trafficmanager~=1.0.0', diff --git a/src/azure-cli/requirements.py3.Darwin.txt b/src/azure-cli/requirements.py3.Darwin.txt index 3c7345ebdbe..a45436a2359 100644 --- a/src/azure-cli/requirements.py3.Darwin.txt +++ b/src/azure-cli/requirements.py3.Darwin.txt @@ -78,7 +78,7 @@ azure-mgmt-servicebus==10.0.0b1 azure-mgmt-servicefabric==2.1.0 azure-mgmt-servicefabricmanagedclusters==2.1.0b3 azure-mgmt-servicelinker==1.2.0b3 -azure-mgmt-sql==4.0.0b22 +azure-mgmt-sql==5.0.0b2 azure-mgmt-signalr==2.0.0b2 azure-mgmt-sqlvirtualmachine==1.0.0b5 azure-mgmt-storage==25.0.0 diff --git a/src/azure-cli/requirements.py3.Linux.txt b/src/azure-cli/requirements.py3.Linux.txt index bebbc6ac92f..ccc94f7992c 100644 --- a/src/azure-cli/requirements.py3.Linux.txt +++ b/src/azure-cli/requirements.py3.Linux.txt @@ -78,7 +78,7 @@ azure-mgmt-servicebus==10.0.0b1 azure-mgmt-servicefabric==2.1.0 azure-mgmt-servicefabricmanagedclusters==2.1.0b3 azure-mgmt-servicelinker==1.2.0b3 -azure-mgmt-sql==4.0.0b22 +azure-mgmt-sql==5.0.0b2 azure-mgmt-signalr==2.0.0b2 azure-mgmt-sqlvirtualmachine==1.0.0b5 azure-mgmt-storage==25.0.0 diff --git a/src/azure-cli/requirements.py3.windows.txt b/src/azure-cli/requirements.py3.windows.txt index 87bc8487587..13ba3b3b829 100644 --- a/src/azure-cli/requirements.py3.windows.txt +++ b/src/azure-cli/requirements.py3.windows.txt @@ -78,7 +78,7 @@ azure-mgmt-servicebus==10.0.0b1 azure-mgmt-servicefabric==2.1.0 azure-mgmt-servicefabricmanagedclusters==2.1.0b3 azure-mgmt-servicelinker==1.2.0b3 -azure-mgmt-sql==4.0.0b22 +azure-mgmt-sql==5.0.0b2 azure-mgmt-signalr==2.0.0b2 azure-mgmt-sqlvirtualmachine==1.0.0b5 azure-mgmt-storage==25.0.0