1# -------------------------------------------------------------------------------------------- 2# Copyright (c) Microsoft Corporation. All rights reserved. 3# Licensed under the MIT License. See License.txt in the project root for license information. 4# -------------------------------------------------------------------------------------------- 5# pylint: disable=unused-argument 6from azure.mgmt.synapse.models import BlobAuditingPolicyState 7from azure.cli.core.commands.client_factory import get_mgmt_service_client 8from azure.cli.core.profiles import ResourceType 9from azure.cli.command_modules.monitor.operations.diagnostics_settings import create_diagnostics_settings 10from azure.cli.command_modules.monitor._client_factory import cf_monitor 11from knack.util import CLIError 12from knack.log import get_logger 13 14 15logger = get_logger(__name__) 16 17 18def sqlpool_blob_auditing_policy_update( 19 cmd, 20 instance, 21 workspace_name, 22 resource_group_name, 23 sql_pool_name, 24 state=None, 25 blob_storage_target_state=None, 26 storage_account=None, 27 storage_endpoint=None, 28 storage_account_access_key=None, 29 storage_account_subscription_id=None, 30 is_storage_secondary_key_in_use=None, 31 retention_days=None, 32 audit_actions_and_groups=None, 33 log_analytics_target_state=None, 34 log_analytics_workspace_resource_id=None, 35 event_hub_target_state=None, 36 event_hub_authorization_rule_id=None, 37 event_hub=None, 38 is_azure_monitor_target_enabled=None, 39 blob_auditing_policy_name=None): 40 """ 41 Updates a sql pool blob auditing policy. Custom update function to apply parameters to instance. 42 """ 43 44 _audit_policy_update( 45 cmd=cmd, 46 instance=instance, 47 workspace_name=workspace_name, 48 resource_group_name=resource_group_name, 49 sql_pool_name=sql_pool_name, 50 state=state, 51 blob_storage_target_state=blob_storage_target_state, 52 storage_account=storage_account, 53 storage_endpoint=storage_endpoint, 54 storage_account_access_key=storage_account_access_key, 55 storage_account_subscription_id=storage_account_subscription_id, 56 is_storage_secondary_key_in_use=is_storage_secondary_key_in_use, 57 retention_days=retention_days, 58 category_name='SQLSecurityAuditEvents', 59 log_analytics_target_state=log_analytics_target_state, 60 log_analytics_workspace_resource_id=log_analytics_workspace_resource_id, 61 event_hub_target_state=event_hub_target_state, 62 event_hub_authorization_rule_id=event_hub_authorization_rule_id, 63 event_hub_name=event_hub, 64 audit_actions_and_groups=audit_actions_and_groups, 65 is_azure_monitor_target_enabled=is_azure_monitor_target_enabled) 66 return instance 67 68 69def sqlserver_blob_auditing_policy_update( 70 cmd, 71 instance, 72 workspace_name, 73 resource_group_name, 74 state=None, 75 blob_storage_target_state=None, 76 storage_account=None, 77 storage_endpoint=None, 78 storage_account_access_key=None, 79 retention_days=None, 80 audit_actions_and_groups=None, 81 log_analytics_target_state=None, 82 log_analytics_workspace_resource_id=None, 83 event_hub_target_state=None, 84 event_hub_authorization_rule_id=None, 85 event_hub=None, 86 storage_account_subscription_id=None, 87 is_storage_secondary_key_in_use=None, 88 is_azure_monitor_target_enabled=None, 89 queue_delay_milliseconds=None, 90 blob_auditing_policy_name=None): 91 _audit_policy_update( 92 cmd=cmd, 93 instance=instance, 94 state=state, 95 workspace_name=workspace_name, 96 resource_group_name=resource_group_name, 97 blob_storage_target_state=blob_storage_target_state, 98 storage_account=storage_account, 99 storage_endpoint=storage_endpoint, 100 storage_account_access_key=storage_account_access_key, 101 audit_actions_and_groups=audit_actions_and_groups, 102 retention_days=retention_days, 103 category_name='SQLSecurityAuditEvents', 104 log_analytics_target_state=log_analytics_target_state, 105 log_analytics_workspace_resource_id=log_analytics_workspace_resource_id, 106 event_hub_target_state=event_hub_target_state, 107 event_hub_authorization_rule_id=event_hub_authorization_rule_id, 108 storage_account_subscription_id=storage_account_subscription_id, 109 event_hub_name=event_hub, 110 is_storage_secondary_key_in_use=is_storage_secondary_key_in_use, 111 is_azure_monitor_target_enabled=is_azure_monitor_target_enabled) 112 113 # this property is only for ServerBlobAuditingPolicy 114 if queue_delay_milliseconds is not None: 115 instance.queue_delay_ms = queue_delay_milliseconds 116 117 return instance 118 119 120def _audit_policy_update( 121 cmd, 122 instance, 123 workspace_name, 124 resource_group_name, 125 sql_pool_name=None, 126 state=None, 127 blob_storage_target_state=None, 128 storage_account=None, 129 storage_endpoint=None, 130 storage_account_access_key=None, 131 storage_account_subscription_id=None, 132 is_storage_secondary_key_in_use=None, 133 retention_days=None, 134 category_name=None, 135 log_analytics_target_state=None, 136 log_analytics_workspace_resource_id=None, 137 event_hub_target_state=None, 138 event_hub_authorization_rule_id=None, 139 event_hub_name=None, 140 audit_actions_and_groups=None, 141 is_azure_monitor_target_enabled=None): 142 143 # Arguments validation 144 _audit_policy_validate_arguments( 145 state=state, 146 blob_storage_target_state=blob_storage_target_state, 147 storage_account=storage_account, 148 storage_endpoint=storage_endpoint, 149 storage_account_access_key=storage_account_access_key, 150 retention_days=retention_days, 151 log_analytics_target_state=log_analytics_target_state, 152 log_analytics_workspace_resource_id=log_analytics_workspace_resource_id, 153 event_hub_target_state=event_hub_target_state, 154 event_hub_authorization_rule_id=event_hub_authorization_rule_id, 155 event_hub_name=event_hub_name) 156 157 # Get diagnostic settings only if log_analytics_target_state or event_hub_target_state is provided 158 if log_analytics_target_state is not None or event_hub_target_state is not None: 159 diagnostic_settings = _get_diagnostic_settings( 160 cmd=cmd, 161 resource_group_name=resource_group_name, 162 workspace_name=workspace_name, 163 sql_pool_name=sql_pool_name) 164 165 # Update diagnostic settings 166 rollback_data = _audit_policy_update_diagnostic_settings( 167 cmd=cmd, 168 workspace_name=workspace_name, 169 resource_group_name=resource_group_name, 170 sql_pool_name=sql_pool_name, 171 diagnostic_settings=diagnostic_settings, 172 category_name=category_name, 173 log_analytics_target_state=log_analytics_target_state, 174 log_analytics_workspace_resource_id=log_analytics_workspace_resource_id, 175 event_hub_target_state=event_hub_target_state, 176 event_hub_authorization_rule_id=event_hub_authorization_rule_id, 177 event_hub_name=event_hub_name) 178 else: 179 diagnostic_settings = None 180 rollback_data = None 181 182 try: 183 # Apply State 184 if state is not None: 185 instance.state = BlobAuditingPolicyState[state.lower()] 186 187 # Apply additional command line arguments only if policy's state is enabled 188 if _is_audit_policy_state_enabled(instance.state): 189 if is_storage_secondary_key_in_use is not None: 190 instance.is_storage_secondary_key_in_use = is_storage_secondary_key_in_use 191 if is_azure_monitor_target_enabled is not None: 192 instance.is_azure_monitor_target_enabled = is_azure_monitor_target_enabled 193 194 # handle storage related parameters 195 _audit_policy_update_apply_blob_storage_details( 196 cmd=cmd, 197 instance=instance, 198 blob_storage_target_state=blob_storage_target_state, 199 storage_account=storage_account, 200 storage_endpoint=storage_endpoint, 201 storage_account_access_key=storage_account_access_key, 202 retention_days=retention_days, 203 storage_account_subscription_id=storage_account_subscription_id) 204 205 if audit_actions_and_groups is not None: 206 instance.audit_actions_and_groups = audit_actions_and_groups 207 208 if not instance.audit_actions_and_groups or instance.audit_actions_and_groups == []: 209 instance.audit_actions_and_groups = [ 210 "SUCCESSFUL_DATABASE_AUTHENTICATION_GROUP", 211 "FAILED_DATABASE_AUTHENTICATION_GROUP", 212 "BATCH_COMPLETED_GROUP"] 213 214 # Apply is_azure_monitor_target_enabled 215 _audit_policy_update_apply_azure_monitor_target_enabled( 216 instance=instance, 217 diagnostic_settings=diagnostic_settings, 218 category_name=category_name, 219 log_analytics_target_state=log_analytics_target_state, 220 event_hub_target_state=event_hub_target_state) 221 return instance 222 except Exception as err: 223 logger.debug(err) 224 225 if rollback_data is not None: 226 _audit_policy_update_rollback( 227 cmd=cmd, 228 workspace_name=workspace_name, 229 resource_group_name=resource_group_name, 230 sql_pool_name=sql_pool_name, 231 rollback_data=rollback_data) 232 233 # Reraise the original exception 234 raise err 235 236 237def _audit_policy_update_apply_blob_storage_details( 238 cmd, 239 instance, 240 blob_storage_target_state, 241 retention_days, 242 storage_account, 243 storage_account_access_key, 244 storage_endpoint, 245 storage_account_subscription_id): 246 ''' 247 Apply blob storage details on policy update 248 ''' 249 if hasattr(instance, 'is_storage_secondary_key_in_use'): 250 is_storage_secondary_key_in_use = instance.is_storage_secondary_key_in_use 251 else: 252 is_storage_secondary_key_in_use = False 253 if blob_storage_target_state is None: 254 # Original audit policy has no storage_endpoint 255 if not instance.storage_endpoint: 256 instance.storage_endpoint = None 257 instance.storage_account_access_key = None 258 else: 259 # Resolve storage_account_access_key based on original storage_endpoint 260 storage_account = _get_storage_account_name(instance.storage_endpoint) 261 storage_resource_group = _find_storage_account_resource_group(cmd.cli_ctx, storage_account) 262 263 instance.storage_account_access_key = _get_storage_key( 264 cli_ctx=cmd.cli_ctx, 265 storage_account=storage_account, 266 resource_group_name=storage_resource_group, 267 use_secondary_key=is_storage_secondary_key_in_use) 268 elif _is_audit_policy_state_enabled(blob_storage_target_state): 269 if storage_account is not None: 270 storage_resource_group = _find_storage_account_resource_group(cmd.cli_ctx, storage_account) 271 storage_endpoint = _get_storage_endpoint(cmd.cli_ctx, storage_account, storage_resource_group) 272 storage_account_subscription_id = _find_storage_account_subscription_id(cmd.cli_ctx, storage_account) 273 274 if storage_endpoint is not None: 275 instance.storage_endpoint = storage_endpoint 276 277 if storage_account_subscription_id is not None: 278 instance.storage_account_subscription_id = storage_account_subscription_id 279 280 if storage_account_access_key is not None: 281 instance.storage_account_access_key = storage_account_access_key 282 elif storage_endpoint is not None: 283 # Resolve storage_account if not provided 284 if storage_account is None: 285 storage_account = _get_storage_account_name(storage_endpoint) 286 storage_resource_group = _find_storage_account_resource_group(cmd.cli_ctx, storage_account) 287 288 # Resolve storage_account_access_key based on storage_account 289 instance.storage_account_access_key = _get_storage_key( 290 cli_ctx=cmd.cli_ctx, 291 storage_account=storage_account, 292 resource_group_name=storage_resource_group, 293 use_secondary_key=instance.is_storage_secondary_key_in_use) 294 295 if retention_days is not None: 296 instance.retention_days = retention_days 297 298 else: 299 instance.storage_endpoint = None 300 instance.storage_account_access_key = None 301 302 303def _find_storage_account_resource_id(cli_ctx, name): 304 ''' 305 Finds a storage account's resource group by querying ARM resource cache. 306 307 Why do we have to do this: so we know the resource group in order to later query the storage API 308 to determine the account's keys and endpoint. Why isn't this just a command line parameter: 309 because if it was a command line parameter then the customer would need to specify storage 310 resource group just to update some unrelated property, which is annoying and makes no sense to 311 the customer. 312 ''' 313 314 storage_type = 'Microsoft.Storage/storageAccounts' 315 classic_storage_type = 'Microsoft.ClassicStorage/storageAccounts' 316 317 query = "name eq '{}' and (resourceType eq '{}' or resourceType eq '{}')".format( 318 name, storage_type, classic_storage_type) 319 320 client = get_mgmt_service_client(cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES) 321 resources = list(client.resources.list(filter=query)) 322 323 if not resources: 324 raise CLIError("No storage account with name '{}' was found.".format(name)) 325 326 if len(resources) > 1: 327 raise CLIError("Multiple storage accounts with name '{}' were found.".format(name)) 328 329 if resources[0].type == classic_storage_type: 330 raise CLIError("The storage account with name '{}' is a classic storage account which is" 331 " not supported by this command. Use a non-classic storage account or" 332 " specify storage endpoint and key instead.".format(name)) 333 334 return resources[0].id 335 336 337def _find_storage_account_resource_group(cli_ctx, name): 338 """ 339 Finds a storage account's resource group by querying ARM resource cache. 340 341 Why do we have to do this: so we know the resource group in order to later query the storage API 342 to determine the account's keys and endpoint. Why isn't this just a command line parameter: 343 because if it was a command line parameter then the customer would need to specify storage 344 resource group just to update some unrelated property, which is annoying and makes no sense to 345 the customer. 346 """ 347 resource_id = _find_storage_account_resource_id(cli_ctx, name) 348 # Split the uri and return just the resource group 349 return resource_id.split('/')[4] 350 351 352def _find_storage_account_subscription_id(cli_ctx, name): 353 """ 354 Finds a storage account's resource group by querying ARM resource cache. 355 356 Why do we have to do this: so we know the resource group in order to later query the storage API 357 to determine the account's keys and endpoint. Why isn't this just a command line parameter: 358 because if it was a command line parameter then the customer would need to specify storage 359 resource group just to update some unrelated property, which is annoying and makes no sense to 360 the customer. 361 """ 362 resource_id = _find_storage_account_resource_id(cli_ctx, name) 363 # Split the uri and return just the resource group 364 return resource_id.split('/')[2] 365 366 367def _get_storage_account_name(storage_endpoint): 368 """ 369 Determines storage account name from endpoint url string. 370 e.g. 'https://mystorage.blob.core.windows.net' -> 'mystorage' 371 """ 372 # url parse package has different names in Python 2 and 3. 'six' package works cross-version. 373 from six.moves.urllib.parse import urlparse # pylint: disable=import-error 374 375 return urlparse(storage_endpoint).netloc.split('.')[0] 376 377 378def _get_storage_endpoint( 379 cli_ctx, 380 storage_account, 381 resource_group_name): 382 """ 383 Gets storage account endpoint by querying storage ARM API. 384 """ 385 from azure.mgmt.storage import StorageManagementClient 386 387 # Get storage account 388 client = get_mgmt_service_client(cli_ctx, StorageManagementClient) 389 account = client.storage_accounts.get_properties( 390 resource_group_name=resource_group_name, 391 account_name=storage_account) 392 393 # Get endpoint 394 # pylint: disable=no-member 395 endpoints = account.primary_endpoints 396 try: 397 return endpoints.blob 398 except AttributeError: 399 raise CLIError("The storage account with name '{}' (id '{}') has no blob endpoint. Use a" 400 " different storage account.".format(account.name, account.id)) 401 402 403def _get_storage_key( 404 cli_ctx, 405 storage_account, 406 resource_group_name, 407 use_secondary_key): 408 """ 409 Gets storage account key by querying storage ARM API. 410 """ 411 from azure.mgmt.storage import StorageManagementClient 412 413 # Get storage keys 414 client = get_mgmt_service_client(cli_ctx, StorageManagementClient) 415 keys = client.storage_accounts.list_keys( 416 resource_group_name=resource_group_name, 417 account_name=storage_account) 418 419 # Choose storage key 420 index = 1 if use_secondary_key else 0 421 return keys.keys[index].value # pylint: disable=no-member 422 423 424def _check_audit_policy_state( 425 state, 426 value): 427 return state is not None and state.lower() == value.lower() 428 429 430def _is_audit_policy_state_enabled(state): 431 return _check_audit_policy_state(state, BlobAuditingPolicyState.enabled.value) 432 433 434def _is_audit_policy_state_none_or_disabled(state): 435 return state is None or _check_audit_policy_state(state, BlobAuditingPolicyState.disabled.value) 436 437 438def _fetch_first_audit_diagnostic_setting(diagnostic_settings, category_name): 439 return next((ds for ds in diagnostic_settings if hasattr(ds, 'logs') and 440 next((log for log in ds.logs if log.enabled and 441 log.category == category_name), None) is not None), None) 442 443 444def _fetch_all_audit_diagnostic_settings(diagnostic_settings, category_name): 445 return [ds for ds in diagnostic_settings if hasattr(ds, 'logs') and 446 next((log for log in ds.logs if log.enabled and 447 log.category == category_name), None) is not None] 448 449 450def _get_diagnostic_settings( 451 cmd, 452 resource_group_name, 453 workspace_name, 454 sql_pool_name=None): 455 ''' 456 Common code to get workspace or sqlpool diagnostic settings 457 ''' 458 459 diagnostic_settings_url = _get_diagnostic_settings_url( 460 cmd=cmd, resource_group_name=resource_group_name, 461 workspace_name=workspace_name, sql_pool_name=sql_pool_name) 462 azure_monitor_client = cf_monitor(cmd.cli_ctx) 463 return azure_monitor_client.diagnostic_settings.list(diagnostic_settings_url) 464 465 466def _get_diagnostic_settings_url( 467 cmd, 468 resource_group_name, 469 workspace_name, 470 sql_pool_name=None): 471 472 from azure.cli.core.commands.client_factory import get_subscription_id 473 474 diagnostic_settings_url = '/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Synapse/workspaces/{}'.format( 475 get_subscription_id(cmd.cli_ctx), resource_group_name, workspace_name) 476 if sql_pool_name is not None: 477 return diagnostic_settings_url + '/sqlpools/{}'.format(sql_pool_name) 478 return diagnostic_settings_url 479 480 481def _audit_policy_update_rollback( 482 cmd, 483 workspace_name, 484 resource_group_name, 485 sql_pool_name, 486 rollback_data): 487 ''' 488 Rollback diagnostic settings change 489 ''' 490 491 diagnostic_settings_url = _get_diagnostic_settings_url( 492 cmd=cmd, 493 resource_group_name=resource_group_name, 494 workspace_name=workspace_name, 495 sql_pool_name=sql_pool_name) 496 497 azure_monitor_client = cf_monitor(cmd.cli_ctx) 498 499 for rd in rollback_data: 500 rollback_diagnostic_setting = rd[1] 501 502 if rd[0] == "create" or rd[0] == "update": 503 create_diagnostics_settings( 504 client=azure_monitor_client.diagnostic_settings, 505 name=rollback_diagnostic_setting.name, 506 resource_uri=diagnostic_settings_url, 507 logs=rollback_diagnostic_setting.logs, 508 metrics=rollback_diagnostic_setting.metrics, 509 event_hub=rollback_diagnostic_setting.event_hub_name, 510 event_hub_rule=rollback_diagnostic_setting.event_hub_authorization_rule_id, 511 storage_account=rollback_diagnostic_setting.storage_account_id, 512 workspace=rollback_diagnostic_setting.workspace_id) 513 else: # delete 514 azure_monitor_client.diagnostic_settings.delete(diagnostic_settings_url, rollback_diagnostic_setting.name) 515 516 517def _audit_policy_update_apply_azure_monitor_target_enabled( 518 instance, 519 diagnostic_settings, 520 category_name, 521 log_analytics_target_state, 522 event_hub_target_state): 523 ''' 524 Apply value of is_azure_monitor_target_enabled on policy update 525 ''' 526 527 # If log_analytics_target_state and event_hub_target_state are None there is nothing to do 528 if log_analytics_target_state is None and event_hub_target_state is None: 529 return 530 531 if _is_audit_policy_state_enabled(log_analytics_target_state) or\ 532 _is_audit_policy_state_enabled(event_hub_target_state): 533 instance.is_azure_monitor_target_enabled = True 534 else: 535 # Sort received diagnostic settings by name and get first element to ensure consistency 536 # between command executions 537 diagnostic_settings.value.sort(key=lambda d: d.name) 538 audit_diagnostic_setting = _fetch_first_audit_diagnostic_setting(diagnostic_settings.value, category_name) 539 540 # Determine value of is_azure_monitor_target_enabled 541 if audit_diagnostic_setting is None: 542 updated_log_analytics_workspace_id = None 543 updated_event_hub_authorization_rule_id = None 544 else: 545 updated_log_analytics_workspace_id = audit_diagnostic_setting.workspace_id 546 updated_event_hub_authorization_rule_id = audit_diagnostic_setting.event_hub_authorization_rule_id 547 548 if _is_audit_policy_state_disabled(log_analytics_target_state): 549 updated_log_analytics_workspace_id = None 550 551 if _is_audit_policy_state_disabled(event_hub_target_state): 552 updated_event_hub_authorization_rule_id = None 553 554 instance.is_azure_monitor_target_enabled = updated_log_analytics_workspace_id is not None or\ 555 updated_event_hub_authorization_rule_id is not None 556 557 558def _is_audit_policy_state_disabled(state): 559 return _check_audit_policy_state(state, BlobAuditingPolicyState.disabled.value) 560 561 562def _audit_policy_update_diagnostic_settings( 563 cmd, 564 workspace_name, 565 resource_group_name, 566 sql_pool_name=None, 567 diagnostic_settings=None, 568 category_name=None, 569 log_analytics_target_state=None, 570 log_analytics_workspace_resource_id=None, 571 event_hub_target_state=None, 572 event_hub_authorization_rule_id=None, 573 event_hub_name=None): 574 ''' 575 Update audit policy's diagnostic settings 576 ''' 577 578 # Fetch all audit diagnostic settings 579 audit_diagnostic_settings = _fetch_all_audit_diagnostic_settings(diagnostic_settings.value, category_name) 580 num_of_audit_diagnostic_settings = len(audit_diagnostic_settings) 581 582 # If more than 1 audit diagnostic settings found then throw error 583 if num_of_audit_diagnostic_settings > 1: 584 raise CLIError('Multiple audit diagnostics settings are already enabled') 585 586 diagnostic_settings_url = _get_diagnostic_settings_url( 587 cmd=cmd, 588 resource_group_name=resource_group_name, 589 workspace_name=workspace_name, 590 sql_pool_name=sql_pool_name) 591 592 azure_monitor_client = cf_monitor(cmd.cli_ctx) 593 594 # If no audit diagnostic settings found then create one if azure monitor is enabled 595 if num_of_audit_diagnostic_settings == 0: 596 if _is_audit_policy_state_enabled(log_analytics_target_state) or\ 597 _is_audit_policy_state_enabled(event_hub_target_state): 598 created_diagnostic_setting = _audit_policy_create_diagnostic_setting( 599 cmd=cmd, 600 resource_group_name=resource_group_name, 601 workspace_name=workspace_name, 602 sql_pool_name=sql_pool_name, 603 category_name=category_name, 604 log_analytics_target_state=log_analytics_target_state, 605 log_analytics_workspace_resource_id=log_analytics_workspace_resource_id, 606 event_hub_target_state=event_hub_target_state, 607 event_hub_authorization_rule_id=event_hub_authorization_rule_id, 608 event_hub_name=event_hub_name) 609 610 # Return rollback data tuple 611 return [("delete", created_diagnostic_setting)] 612 613 # azure monitor is disabled - there is nothing to do 614 return None 615 616 # This leaves us with case when num_of_audit_diagnostic_settings is 1 617 audit_diagnostic_setting = audit_diagnostic_settings[0] 618 619 # Initialize actually updated azure monitor fields 620 if log_analytics_target_state is None: 621 log_analytics_workspace_resource_id = audit_diagnostic_setting.workspace_id 622 elif _is_audit_policy_state_disabled(log_analytics_target_state): 623 log_analytics_workspace_resource_id = None 624 625 if event_hub_target_state is None: 626 event_hub_authorization_rule_id = audit_diagnostic_setting.event_hub_authorization_rule_id 627 event_hub_name = audit_diagnostic_setting.event_hub_name 628 elif _is_audit_policy_state_disabled(event_hub_target_state): 629 event_hub_authorization_rule_id = None 630 event_hub_name = None 631 632 is_azure_monitor_target_enabled = log_analytics_workspace_resource_id is not None or\ 633 event_hub_authorization_rule_id is not None 634 635 has_other_categories = next((log for log in audit_diagnostic_setting.logs 636 if log.enabled and log.category != category_name), None) is not None 637 638 # If there is no other categories except SQLSecurityAuditEvents\DevOpsOperationsAudit update or delete 639 # the existing single diagnostic settings 640 if not has_other_categories: 641 # If azure monitor is enabled then update existing single audit diagnostic setting 642 if is_azure_monitor_target_enabled: 643 create_diagnostics_settings( 644 client=azure_monitor_client.diagnostic_settings, 645 name=audit_diagnostic_setting.name, 646 resource_uri=diagnostic_settings_url, 647 logs=audit_diagnostic_setting.logs, 648 metrics=audit_diagnostic_setting.metrics, 649 event_hub=event_hub_name, 650 event_hub_rule=event_hub_authorization_rule_id, 651 storage_account=audit_diagnostic_setting.storage_account_id, 652 workspace=log_analytics_workspace_resource_id) 653 654 # Return rollback data tuple 655 return [("update", audit_diagnostic_setting)] 656 657 # Azure monitor is disabled, delete existing single audit diagnostic setting 658 azure_monitor_client.diagnostic_settings.delete(diagnostic_settings_url, audit_diagnostic_setting.name) 659 660 # Return rollback data tuple 661 return [("create", audit_diagnostic_setting)] 662 663 # In case there are other categories in the existing single audit diagnostic setting a "split" must be performed: 664 # 1. Disable SQLSecurityAuditEvents\DevOpsOperationsAudit category in found audit diagnostic setting 665 # 2. Create new diagnostic setting with SQLSecurityAuditEvents\DevOpsOperationsAudit category, 666 # i.e. audit diagnostic setting 667 668 # Build updated logs list with disabled SQLSecurityAuditEvents\DevOpsOperationsAudit category 669 updated_logs = [] 670 671 LogSettings = cmd.get_models( 672 'LogSettings', 673 resource_type=ResourceType.MGMT_MONITOR, 674 operation_group='diagnostic_settings') 675 676 RetentionPolicy = cmd.get_models( 677 'RetentionPolicy', 678 resource_type=ResourceType.MGMT_MONITOR, 679 operation_group='diagnostic_settings') 680 681 for log in audit_diagnostic_setting.logs: 682 if log.category == category_name: 683 updated_logs.append(LogSettings(category=log.category, enabled=False, 684 retention_policy=RetentionPolicy(enabled=False, days=0))) 685 else: 686 updated_logs.append(log) 687 688 # Update existing diagnostic settings 689 create_diagnostics_settings( 690 client=azure_monitor_client.diagnostic_settings, 691 name=audit_diagnostic_setting.name, 692 resource_uri=diagnostic_settings_url, 693 logs=updated_logs, 694 metrics=audit_diagnostic_setting.metrics, 695 event_hub=audit_diagnostic_setting.event_hub_name, 696 event_hub_rule=audit_diagnostic_setting.event_hub_authorization_rule_id, 697 storage_account=audit_diagnostic_setting.storage_account_id, 698 workspace=audit_diagnostic_setting.workspace_id) 699 700 # Add original 'audit_diagnostic_settings' to rollback_data list 701 rollback_data = [("update", audit_diagnostic_setting)] 702 703 # Create new diagnostic settings with enabled SQLSecurityAuditEvents\DevOpsOperationsAudit category 704 # only if azure monitor is enabled 705 if is_azure_monitor_target_enabled: 706 created_diagnostic_setting = _audit_policy_create_diagnostic_setting( 707 cmd=cmd, 708 resource_group_name=resource_group_name, 709 workspace_name=workspace_name, 710 sql_pool_name=sql_pool_name, 711 category_name=category_name, 712 log_analytics_target_state=log_analytics_target_state, 713 log_analytics_workspace_resource_id=log_analytics_workspace_resource_id, 714 event_hub_target_state=event_hub_target_state, 715 event_hub_authorization_rule_id=event_hub_authorization_rule_id, 716 event_hub_name=event_hub_name) 717 718 # Add 'created_diagnostic_settings' to rollback_data list in reverse order 719 rollback_data.insert(0, ("delete", created_diagnostic_setting)) 720 721 return rollback_data 722 723 724def _audit_policy_create_diagnostic_setting( 725 cmd, 726 resource_group_name, 727 workspace_name, 728 sql_pool_name=None, 729 category_name=None, 730 log_analytics_target_state=None, 731 log_analytics_workspace_resource_id=None, 732 event_hub_target_state=None, 733 event_hub_authorization_rule_id=None, 734 event_hub_name=None): 735 ''' 736 Create audit diagnostic setting, i.e. containing single category - SQLSecurityAuditEvents or DevOpsOperationsAudit 737 ''' 738 739 # Generate diagnostic settings name to be created 740 name = category_name 741 742 import inspect 743 test_methods = ["test_sql_ws_audit_policy_logentry_eventhub", "test_sql_pool_audit_policy_logentry_eventhub"] 744 test_mode = next((e for e in inspect.stack() if e.function in test_methods), None) is not None 745 746 # For test environment the name should be constant, i.e. match the name written in recorded yaml file 747 if test_mode: 748 name += '_LogAnalytics' if log_analytics_target_state is not None else '' 749 name += '_EventHub' if event_hub_target_state is not None else '' 750 else: 751 import uuid 752 name += '_' + str(uuid.uuid4()) 753 754 diagnostic_settings_url = _get_diagnostic_settings_url( 755 cmd=cmd, 756 resource_group_name=resource_group_name, 757 workspace_name=workspace_name, 758 sql_pool_name=sql_pool_name) 759 760 azure_monitor_client = cf_monitor(cmd.cli_ctx) 761 762 LogSettings = cmd.get_models( 763 'LogSettings', 764 resource_type=ResourceType.MGMT_MONITOR, 765 operation_group='diagnostic_settings') 766 767 RetentionPolicy = cmd.get_models( 768 'RetentionPolicy', 769 resource_type=ResourceType.MGMT_MONITOR, 770 operation_group='diagnostic_settings') 771 772 return create_diagnostics_settings( 773 client=azure_monitor_client.diagnostic_settings, 774 name=name, 775 resource_uri=diagnostic_settings_url, 776 logs=[LogSettings(category=category_name, enabled=True, 777 retention_policy=RetentionPolicy(enabled=False, days=0))], 778 metrics=None, 779 event_hub=event_hub_name, 780 event_hub_rule=event_hub_authorization_rule_id, 781 storage_account=None, 782 workspace=log_analytics_workspace_resource_id) 783 784 785def _audit_policy_validate_arguments( 786 state=None, 787 blob_storage_target_state=None, 788 storage_account=None, 789 storage_endpoint=None, 790 storage_account_access_key=None, 791 retention_days=None, 792 log_analytics_target_state=None, 793 log_analytics_workspace_resource_id=None, 794 event_hub_target_state=None, 795 event_hub_authorization_rule_id=None, 796 event_hub_name=None): 797 ''' 798 Validate input agruments 799 ''' 800 801 blob_storage_arguments_provided = blob_storage_target_state is not None or\ 802 storage_account is not None or storage_endpoint is not None or\ 803 storage_account_access_key is not None or\ 804 retention_days is not None 805 806 log_analytics_arguments_provided = log_analytics_target_state is not None or\ 807 log_analytics_workspace_resource_id is not None 808 809 event_hub_arguments_provided = event_hub_target_state is not None or\ 810 event_hub_authorization_rule_id is not None or\ 811 event_hub_name is not None 812 813 if not state and not blob_storage_arguments_provided and\ 814 not log_analytics_arguments_provided and not event_hub_arguments_provided: 815 raise CLIError('Either state or blob storage or log analytics or event hub arguments are missing') 816 817 if _is_audit_policy_state_enabled(state) and\ 818 blob_storage_target_state is None and log_analytics_target_state is None and event_hub_target_state is None: 819 raise CLIError('One of the following arguments must be enabled:' 820 ' blob-storage-target-state, log-analytics-target-state, event-hub-target-state') 821 822 if _is_audit_policy_state_disabled(state) and\ 823 (blob_storage_arguments_provided or 824 log_analytics_arguments_provided or 825 event_hub_name): 826 raise CLIError('No additional arguments should be provided once state is disabled') 827 828 if (_is_audit_policy_state_none_or_disabled(blob_storage_target_state)) and\ 829 (storage_account is not None or storage_endpoint is not None or 830 storage_account_access_key is not None): 831 raise CLIError('Blob storage account arguments cannot be specified' 832 ' if blob-storage-target-state is not provided or disabled') 833 834 if _is_audit_policy_state_enabled(blob_storage_target_state): 835 if storage_account is not None and storage_endpoint is not None: 836 raise CLIError('storage-account and storage-endpoint cannot be provided at the same time') 837 838 if storage_account is None and storage_endpoint is None: 839 raise CLIError('Either storage-account or storage-endpoint must be provided') 840 841 # Server upper limit 842 max_retention_days = 3285 843 844 if retention_days is not None and\ 845 (int(retention_days) <= 0 or int(retention_days) >= max_retention_days): 846 raise CLIError('retention-days must be a positive number greater than zero and lower than {}' 847 .format(max_retention_days)) 848 849 if _is_audit_policy_state_none_or_disabled(log_analytics_target_state) and\ 850 log_analytics_workspace_resource_id is not None: 851 raise CLIError('Log analytics workspace resource id cannot be specified' 852 ' if log-analytics-target-state is not provided or disabled') 853 854 if _is_audit_policy_state_enabled(log_analytics_target_state) and\ 855 log_analytics_workspace_resource_id is None: 856 raise CLIError('Log analytics workspace resource id must be specified' 857 ' if log-analytics-target-state is enabled') 858 859 if _is_audit_policy_state_none_or_disabled(event_hub_target_state) and\ 860 (event_hub_authorization_rule_id is not None or event_hub_name is not None): 861 raise CLIError('Event hub arguments cannot be specified if event-hub-target-state is not provided or disabled') 862 863 if _is_audit_policy_state_enabled(event_hub_target_state) and event_hub_authorization_rule_id is None: 864 raise CLIError('event-hub-authorization-rule-id must be specified if event-hub-target-state is enabled') 865 866 867def _get_diagnostic_settings_url( 868 cmd, 869 resource_group_name, 870 workspace_name, 871 sql_pool_name=None): 872 873 from azure.cli.core.commands.client_factory import get_subscription_id 874 875 diag_settings = '/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Synapse/workspaces/{}'.format( 876 get_subscription_id(cmd.cli_ctx), 877 resource_group_name, workspace_name) 878 879 if sql_pool_name is not None: 880 diag_settings = diag_settings + '/sqlpools/{}'.format(sql_pool_name) 881 882 return diag_settings 883 884 885def _get_diagnostic_settings( 886 cmd, 887 resource_group_name, 888 workspace_name, 889 sql_pool_name=None): 890 ''' 891 Common code to get server or database diagnostic settings 892 ''' 893 894 diagnostic_settings_url = _get_diagnostic_settings_url( 895 cmd=cmd, resource_group_name=resource_group_name, 896 workspace_name=workspace_name, sql_pool_name=sql_pool_name) 897 azure_monitor_client = cf_monitor(cmd.cli_ctx) 898 899 return azure_monitor_client.diagnostic_settings.list(diagnostic_settings_url) 900 901 902def workspace_audit_policy_show( 903 cmd, 904 client, 905 workspace_name, 906 resource_group_name, 907 blob_auditing_policy_name=None): 908 ''' 909 Show workspace audit policy 910 ''' 911 912 return _audit_policy_show( 913 cmd=cmd, 914 client=client, 915 resource_group_name=resource_group_name, 916 workspace_name=workspace_name, 917 blob_auditing_policy_name=blob_auditing_policy_name, 918 category_name='SQLSecurityAuditEvents') 919 920 921def sqlpool_audit_policy_show( 922 cmd, 923 client, 924 workspace_name, 925 resource_group_name, 926 sql_pool_name, 927 blob_auditing_policy_name=None): 928 ''' 929 Show sql pool audit policy 930 ''' 931 932 return _audit_policy_show( 933 cmd=cmd, 934 client=client, 935 resource_group_name=resource_group_name, 936 workspace_name=workspace_name, 937 sql_pool_name=sql_pool_name, 938 blob_auditing_policy_name=blob_auditing_policy_name, 939 category_name='SQLSecurityAuditEvents') 940 941 942def _audit_policy_show( 943 cmd, 944 client, 945 resource_group_name, 946 workspace_name, 947 sql_pool_name=None, 948 category_name=None, 949 blob_auditing_policy_name=None): 950 ''' 951 Common code to get workspace or sqlpool audit policy including diagnostic settings 952 ''' 953 954 # Request audit policy 955 if sql_pool_name is None: 956 audit_policy = client.get( 957 resource_group_name=resource_group_name, 958 workspace_name=workspace_name, 959 blob_auditing_policy_name=blob_auditing_policy_name) 960 else: 961 audit_policy = client.get( 962 resource_group_name=resource_group_name, 963 workspace_name=workspace_name, 964 sql_pool_name=sql_pool_name) 965 966 audit_policy.blob_storage_target_state = BlobAuditingPolicyState.disabled 967 audit_policy.event_hub_target_state = BlobAuditingPolicyState.disabled 968 audit_policy.log_analytics_target_state = BlobAuditingPolicyState.disabled 969 970 # If audit policy's state is disabled there is nothing to do 971 if _is_audit_policy_state_disabled(audit_policy.state): 972 return audit_policy 973 974 if not audit_policy.storage_endpoint: 975 audit_policy.blob_storage_target_state = BlobAuditingPolicyState.disabled 976 else: 977 audit_policy.blob_storage_target_state = BlobAuditingPolicyState.enabled 978 979 # If 'is_azure_monitor_target_enabled' is false there is no reason to request diagnostic settings 980 if not audit_policy.is_azure_monitor_target_enabled: 981 return audit_policy 982 983 # Request diagnostic settings 984 diagnostic_settings = _get_diagnostic_settings( 985 cmd=cmd, resource_group_name=resource_group_name, 986 workspace_name=workspace_name, sql_pool_name=sql_pool_name) 987 988 # Sort received diagnostic settings by name and get first element to ensure consistency between command executions 989 diagnostic_settings.value.sort(key=lambda d: d.name) 990 audit_diagnostic_setting = _fetch_first_audit_diagnostic_setting(diagnostic_settings.value, category_name) 991 992 # Initialize azure monitor properties 993 if audit_diagnostic_setting is not None: 994 if audit_diagnostic_setting.workspace_id is not None: 995 audit_policy.log_analytics_target_state = BlobAuditingPolicyState.enabled 996 audit_policy.log_analytics_workspace_resource_id = audit_diagnostic_setting.workspace_id 997 998 if audit_diagnostic_setting.event_hub_authorization_rule_id is not None: 999 audit_policy.event_hub_target_state = BlobAuditingPolicyState.enabled 1000 audit_policy.event_hub_authorization_rule_id = audit_diagnostic_setting.event_hub_authorization_rule_id 1001 audit_policy.event_hub_name = audit_diagnostic_setting.event_hub_name 1002 1003 return audit_policy 1004