Azure SQL Management

Microsoft Azure SQL Management Integration manages the Auditing and Threat Policies for Azure SQL.

IT Services · Azure SQL Management

Details

IDAzure SQL Management
ProviderMicrosoft
CategoryIT Services
From Version5.0.0
Docker Imagedemisto/crypto:1.0.0.10120494
Supported ModulesAgentix XSIAM

README

Microsoft Azure SQL Management Integration manages the Auditing and Threat Policies for Azure SQL.
This integration was integrated and tested with version 2021-11-01 of Azure SQL Management

In order to connect to the AzureKubernetesServices using either Cortex XSOAR Azure App or the Self-Deployed Azure App, use one of the following methods:

  • Authorization Code Flow (Recommended).
  • Device Code Flow.
  • Azure Managed Identities
  • Client Credentials Flow.

Self-Deployed Application

To use a self-configured Azure application, you need to add a new Azure App Registration in the Azure Portal.

The application must have user_impersonation permission and must allow public client flows (found under the Authentication section of the app). And must allow public client flows (found under the Authentication section of the app) for Device-code based authentications.

Authentication Using the User-Authentication Flow (recommended)

Follow these steps for a self-deployed configuration:

  1. To use a self-configured Azure application, you need to add a new Azure App Registration in the Azure Portal. To add the registration, refer to the following Microsoft article steps 1-8.
  2. choose the ‘User Auth’ option in the Authentication Type parameter.
  3. Enter your Client/Application ID in the Application ID parameter.
  4. Enter your Client Secret in the Client Secret parameter.
  5. Enter your Tenant ID in the Tenant ID parameter.
  6. Enter your Application redirect URI in the Application redirect URI parameter.
  7. Save the instance.
  8. Run the !azure-sql-generate-login-url command in the War Room and follow the instruction.
  9. Run the !azure-sql-auth-test command - a ‘Success’ message should be printed to the War Room.

Authentication Using the Device Code Flow

Use the device code flow
to link Azure SQL Management with Cortex XSOAR.

Follow these steps for a self-deployed configuration:

  1. Fill in the required parameters.
  2. choose the ‘Device’ option in the user_auth_flow parameter.
  3. Run the !azure-sql-auth-start command.
  4. Follow the instructions that appear.
  5. Run the !azure-sql-auth-complete command.

Cortex XSOAR Azure App

In order to use the Cortex XSOAR Azure application, use the default application ID (8f9010bb-4efe-4cfa-a197-98a2694b7e0c).

You only need to fill in your subscription ID and resource group name. You can find your resource group and
subscription ID in the Azure Portal. For a more detailed explanation, visit this page.

Client Credentials Flow Authentication

Assign Azure roles using the Azure portal Microsoft article
Note: In the Select members section, assign the application you created earlier.
To configure a Microsoft integration that uses this authorization flow with a self-deployed Azure application:

  1. In the Authentication Type field, select the Client Credentials option.
  2. In the Application ID field, enter your Client/Application ID.
  3. In the Tenant ID field, enter your Tenant ID .
  4. In the Client Secret field, enter your Client Secret.
  5. Click Test to validate the URLs, token, and connection
  6. Save the instance.

Testing authentication and connectivity

If you are using Device Code Flow or Authorization Code Flow, for testing your authentication and connectivity to the Azure SQL Management service run the !azure-sql-auth-test command.
If you are using Client Credentials Flow, click Test when you are configuring the instance.

Configure Azure SQL Management on Cortex XSOAR

In both options below, the device authorization grant flow is used.

In order to connect to the Azure SQL Management using either Cortex XSOAR Azure App or the Self-Deployed Azure App:

  1. Fill in the required parameters.

    Parameter Description Required
    Application ID   False
    Default Subscription ID Test button won’t work if not populated. This value can be overridden on a command basis. False
    Default Resource Group Name Test button won’t work if not populated. This value can be overridden on a command basis. False
    Authentication Type Type of authentication - can be Authorization Code Flow (recommended), Device Code Flow, Azure Managed Identities or Client Credentials Flow. True
    Tenant ID For authorization code mode or client credentials. False
    Client Secret For authorization code mode or client credentials. False
    Client Secret For authorization code mode or client credentials. False
    Application redirect URI For authorization code mode. False
    Authorization code for user-auth mode - received from the authorization step. False
    Azure Managed Identities Client ID The Managed Identities client ID for authentication - relevant only if the integration is running on Azure VM. False
    Azure AD endpoint Azure AD endpoint associated with a national cloud. False
    Trust any certificate (not secure)   False
    Use system proxy settings   False

At the end of the process you’ll see a message that you’ve logged in successfully.

Commands

You can execute these commands from the Cortex XSOAR CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

azure-sql-auth-start


Run this command to start the authorization process and follow the instructions in the command results.

Base Command

azure-sql-auth-start

Input

There are no input arguments for this command.

Context Output

There is no context output for this command.

Command example


#### Human Readable Output

>### Authorization instructions
>
>1. To sign in, use a web browser to open the page [https://microsoft.com/devicelogin](https://microsoft.com/devicelogin)
>and enter the code **CODECODE** to authenticate.
>2. Run the **!azure-sql-auth-complete** command in the War Room.

### azure-sql-auth-complete

***
Run this command to complete the authorization process. Should be used after running the azure-sql-auth-start command.

#### Base Command

`azure-sql-auth-complete`

#### Input

There are no input arguments for this command.

#### Context Output

There is no context output for this command.

#### Command Example

```!azure-nsg-auth-complete```

#### Human Readable Output

>✅ Authorization completed successfully.

### azure-sql-auth-reset

***
Run this command if for some reason you need to rerun the authentication process.

#### Base Command

`azure-sql-auth-reset`

#### Input

There are no input arguments for this command.

#### Context Output

There is no context output for this command.

#### Command example

```!azure-sql-auth-reset```

#### Human Readable Output

>Authorization was reset successfully. You can now run ***!azure-sql-auth-start*** and ***!azure-sql-auth-complete***.

### azure-sql-auth-test

***
Tests the connectivity to the Azure SQL Management.

#### Base Command

`azure-sql-auth-test`

#### Input

There are no input arguments for this command.

#### Context Output

There is no context output for this command.

#### Command Example

```!azure-sql-auth-test```

#### Human Readable Output

>✅ Success!

### azure-sql-servers-list

***
Lists all the servers.

#### Base Command

`azure-sql-servers-list`

#### Input

| **Argument Name** | **Description**                                                                                                                                      | **Required** |
| --- |------------------------------------------------------------------------------------------------------------------------------------------------------| --- |
| limit | (Int) The maximum number of servers returned to the War Room. Default is 50.                                                                         | Optional |
| offset | Offset in the data set. Default is 0.                                                                                                                | Optional |
| subscription_id | Subscription ID. This value will override the instance param 'subscription_id'.                                                                      | Optional |
| resource_group_name | A comma-separated list of resource group names. This value will override the instance param 'resource_group_name'.                                   | Optional |
| list_by_resource_group | If true, returns the server lists that matches the given resource groups. Else, returns a list of all the servers. Possible values are: true, false. | Optional |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| AzureSQL.Server | Unknown | Server list. |
| AzureSQL.Server.kind | String | Kind of server. |
| AzureSQL.Server.location | String | Server location. |
| AzureSQL.Server.tags | Unknown | Server tags. |
| AzureSQL.Server.id | String | Server ID. |
| AzureSQL.Server.name | String | Server name. |
| AzureSQL.Server.type | String | Server type. |
| AzureSQL.Server.administratorLogin | String | Username of the server administrator. |
| AzureSQL.Server.version | String | Server version. |
| AzureSQL.Server.state | String | Server state. |
| AzureSQL.Server.fullyQualifiedDomainName | Unknown | Fully qualified domain name of the server. |
| AzureSQL.Server.privateEndpointConnections | Unknown | List of private endpoint connections of the server. |
| AzureSQL.Server.publicNetworkAccess | Boolean | Whether the public endpoint access of the server is enabled. |
| AzureSQL.Server.restrictOutboundNetworkAccess | Unknown | Whether or not to restrict outbound network access for this server. Value is optional but if passed in, must be 'Enabled' or 'Disabled'. |

#### Command example

```!azure-sql-servers-list```

#### Context Example

```json
{
    "AzureSQL": {
        "Server": {
            "administratorLogin": "xsoaradmin",
            "fullyQualifiedDomainName": "sqlintegration.database.windows.net",
            "id": "/subscriptions/0123456789/resourceGroups/sql-integration/providers/Microsoft.Sql/servers/sqlintegration",
            "kind": "v12.0",
            "location": "eastus",
            "name": "sqlintegration",
            "privateEndpointConnections": [],
            "publicNetworkAccess": "Enabled",
            "restrictOutboundNetworkAccess": "Disabled",
            "state": "Ready",
            "tags": {},
            "type": "Microsoft.Sql/servers",
            "version": "12.0"
        }
    }
}

Human Readable Output

Servers List

Administrator Login Fully Qualified Domain Name Id Kind Location Name Public Network Access State Type Version
xsoaradmin sqlintegration.database.windows.net /subscriptions/0123456789/resourceGroups/sql-integration/providers/Microsoft.Sql/servers/sqlintegration v12.0 eastus sqlintegration Enabled Ready Microsoft.Sql/servers 12.0

azure-sql-db-list


Lists all of the databases for the server.

Base Command

azure-sql-db-list

Input

Argument Name Description Required
server_name Server name. Required
limit The maximum number of databases returned to the War Room. Default is 50. Optional
offset Offset in the data set. Default is 0. Optional
subscription_id Subscription ID. This value will override the instance param ‘subscription_id’. Optional
resource_group_name Resource group name. This value will override the instance param ‘resource_group_name’. Optional

Context Output

Path Type Description
AzureSQL.DB Unknown All databases related to the server.
AzureSQL.DB.kind String Kind of database.
AzureSQL.DB.location String Database location.
AzureSQL.DB.id String Database ID.
AzureSQL.DB.name String Database name.
AzureSQL.DB.type String Database type.
AzureSQL.DB.managedBy String Resource that manages the database.
AzureSQL.DB.sku Unknown Database SKU.
AzureSQL.DB.catalogCollation String Collation of the catalog for the database.
AzureSQL.DB.collation String Database collation.
AzureSQL.DB.creationDate String Creation date of the database in ISO format.
AzureSQL.DB.currentServiceObjectiveName String Current service level objective name of the database.
AzureSQL.DB.currentSku Unknown Name, tier, and capacity of the SKU.
AzureSQL.DB.databaseID String Database ID.
AzureSQL.DB.defaultSecondaryLocation String Default secondary location of the database.
AzureSQL.DB.maxSizeBytes Number The maximum size of the database in bytes.
AzureSQL.DB.readReplicaCount Number The number of read-only secondary replicas of the database.
AzureSQL.DB.readScale String The read-only routing state. “Enabled” or “Disabled”.
AzureSQL.DB.requestedServiceObjectiveName String The requested service objective name of the database.
AzureSQL.DB.status String Database status.
AzureSQL.DB.storageAccountType String Database storage account type.
AzureSQL.DB.zoneRedundant Boolean Whether the database zone is redundant.
AzureSQL.DB.currentBackupStorageRedundancy Unknown The storage account type used to store backups for this database.
AzureSQL.DB.requestedBackupStorageRedundancy Unknown The storage account type to be used to store backups for this database.
AzureSQL.DB.maintenanceConfigurationId String Maintenance configuration ID assigned to the database. This configuration defines the period when the maintenance updates will occur.
AzureSQL.DB.isLedgerOn Boolean Whether or not this database is a ledger database, which means all tables in the database are ledger tables. The value of this property cannot be changed after the database has been created.
AzureSQL.DB.isInfraEncryptionEnabled Boolean Whether infra encryption is enabled for this database.

Command Example

!azure-sql-db-list server_name=sqlintegration

Context Example

{
    "AzureSQL": {
        "DB": [
            {
                "catalogCollation": "SQL_Latin1_General_CP1_CI_AS",
                "collation": "SQL_Latin1_General_CP1_CI_AS",
                "creationDate": "2020-12-15T14:31:06.663Z",
                "currentBackupStorageRedundancy": "currentBackupStorageRedundancy",
                "currentServiceObjectiveName": "S0",
                "currentSku": {
                    "capacity": 10,
                    "name": "Standard",
                    "tier": "Standard"
                },
                "databaseId": "databaseId",
                "defaultSecondaryLocation": "westus",
                "earliestRestoreDate": "2023-05-04T13:34:04.2874123Z",
                "id": "id",
                "isInfraEncryptionEnabled": false,
                "isLedgerOn": false,
                "kind": "v12.0,user",
                "location": "eastus",
                "maintenanceConfigurationId": "maintenanceConfigurationId",
                "maxSizeBytes": 268435456000,
                "name": "sql-integration-db",
                "readScale": "Disabled",
                "requestedBackupStorageRedundancy": "requestedBackupStorageRedundancy",
                "requestedServiceObjectiveName": "S0",
                "sku": {
                    "capacity": 10,
                    "name": "Standard",
                    "tier": "Standard"
                },
                "status": "Online",
                "tags": {},
                "type": "Microsoft.Sql/servers/databases",
                "zoneRedundant": false
            }
        ]
    }
}

Human Readable Output

Database List

Name Location Status Managed By
sql-integration-db eastus Online  

azure-sql-db-audit-policy-list


Gets the audit settings of the specified database.

Base Command

azure-sql-db-audit-policy-list

Input

Argument Name Description Required
server_name Server name. Required
db_name Database name. Required
limit The maximum number of database audit policies returned to the War Room. Default is 50. Optional
offset Offset in the data set. Default is 0. Optional
subscription_id Subscription ID. This value will override the instance param ‘subscription_id’. Optional
resource_group_name A comma-separated list of resource group names. This value will override the instance param ‘resource_group_name’. Optional

Context Output

Path Type Description
AzureSQL.DBAuditPolicy Unknown List of all database audit settings.
AzureSQL.DBAuditPolicy.kind String Kind of audit policy.
AzureSQL.DBAuditPolicy.id String Audit policy ID.
AzureSQL.DBAuditPolicy.name String Audit policy name.
AzureSQL.DBAuditPolicy.type String Resource type.
AzureSQL.DBAuditPolicy.isAzureMonitorTargetEnabled Boolean Whether audit events are sent to Azure Monitor. Possible values: “True” (Enabled) or “False” (Disabled).
AzureSQL.DBAuditPolicy.retentionDays Number Number of days to keep in the audit logs in the storage account.
AzureSQL.DBAuditPolicy.state String Policy state.
AzureSQL.DBAuditPolicy.storageAccountSubscriptionId String Storage subscription ID.
AzureSQL.DBAuditPolicy.databaseName String The name of the database that the audit policy is related to.
AzureSQL.DBAuditPolicy.serverName String The name of the server that the audit policy is related to.
AzureSQL.DBAuditPolicy.isManagedIdentityInUse Boolean Specifies whether Managed Identity is used to access blob storage.

Command Example

!azure-sql-db-audit-policy-list server_name=sqlintegration db_name=sql-integration-db

Context Example

{
    "AzureSQL": {
        "DBAuditPolicy": {
            "auditActionsAndGroups": [
                "SUCCESSFUL_DATABASE_AUTHENTICATION_GROUP",
                "FAILED_DATABASE_AUTHENTICATION_GROUP",
                "BATCH_COMPLETED_GROUP"
            ],
            "databaseName": "sql-db",
            "id": "/subscriptions/0123456789/resourceGroups/sql-integration/providers/Microsoft.Sql/servers/sqlintegration/databases/sql-integration-db/auditingSettings/Default",
            "isAzureMonitorTargetEnabled": true,
            "isManagedIdentityInUse": false,
            "name": "Default",
            "retentionDays": 3,
            "serverName": "sql",
            "state": "Enabled",
            "storageAccountSubscriptionId": "00000000-0000-0000-0000-000000000000",
            "storageEndpoint": "",
            "type": "Microsoft.Sql/servers/databases/auditingSettings"
        }
    }
}

Human Readable Output

Database Audit Settings for resource_group_name=’sql-integration’

Audit Actions And Groups Database Name Id Is Azure Monitor Target Enabled Is Managed Identity In Use Name Retention Days Server Name State Storage Account Subscription Id Type
SUCCESSFUL_DATABASE_AUTHENTICATION_GROUP,
FAILED_DATABASE_AUTHENTICATION_GROUP,
BATCH_COMPLETED_GROUP
sql-integration-db /subscriptions/0123456789/resourceGroups/sql-integration/providers/Microsoft.Sql/servers/sqlintegration/databases/sql-integration-db/auditingSettings/Default true false Default 3 sqlintegration Enabled 00000000-0000-0000-0000-000000000000 Microsoft.Sql/servers/databases/auditingSettings

azure-sql-db-threat-policy-get


Gets the threat detection policy of the specified database.

Base Command

azure-sql-db-threat-policy-get

Input

Argument Name Description Required
server_name Server name. Required
db_name Database name. Required
subscription_id Subscription ID. This value will override the instance param ‘subscription_id’. Optional
resource_group_name Resource group name. This value will override the instance param ‘resource_group_name’. Optional

Context Output

Path Type Description
AzureSQL.DBThreatPolicy Unknown All threat policies related to the database.
AzureSQL.DBThreatPolicy.kind String Kind of threat policy.
AzureSQL.DBThreatPolicy.location String Threat policy location.
AzureSQL.DBThreatPolicy.id String Threat policy ID.
AzureSQL.DBThreatPolicy.name String Threat policy name.
AzureSQL.DBThreatPolicy.type String Threat policy type.
AzureSQL.DBThreatPolicy.state String Threat policy state.
AzureSQL.DBThreatPolicy.creationTime String Threat policy creation time.
AzureSQL.DBThreatPolicy.retentionDays Number Number of days to keep in the Threat Detection audit logs.
AzureSQL.DBThreatPolicy.storageAccountAccessKey String The identifier key of the Threat Detection audit storage account.
AzureSQL.DBThreatPolicy.storageEndpoint String Threat Detection audit storage account.
AzureSQL.DBThreatPolicy.emailAccountAdmins Boolean Email account administrators who the alert is sent to.
AzureSQL.DBThreatPolicy.emailAddresses String List of email addresses to which the alert is sent.
AzureSQL.DBThreatPolicy.disabledAlerts String List of alerts that are disabled, or an empty string if no alerts are disabled.
AzureSQL.DBThreatPolicy.useServerDefault Unknown Whether to use the default server policy.
AzureSQL.DBThreatPolicy.databaseName String The name of the database that the threat policy is related to.
AzureSQL.DBThreatPolicy.serverName String The name of server that the threat policy is related to.

Command example

!azure-sql-db-threat-policy-get server_name=sqlintegration db_name=sql-integration-db

Context Example

{
    "AzureSQL": {
        "DBThreatPolicy": {
            "creationTime": "2021-01-04T08:05:32.05Z",
            "databaseName": "sql-db",
            "disabledAlerts": [
                ""
            ],
            "emailAccountAdmins": false,
            "emailAddresses": [
                ""
            ],
            "id": "/subscriptions/0123456789/resourceGroups/sql-integration/providers/Microsoft.Sql/servers/sqlintegration/databases/sql-integration-db/securityAlertPolicies/Default",
            "name": "Default",
            "retentionDays": 0,
            "serverName": "sql",
            "state": "Enabled",
            "storageAccountAccessKey": "",
            "storageEndpoint": "",
            "type": "Microsoft.Sql/servers/databases/securityAlertPolicies"
        }
    }
}

Human Readable Output

Database Threat Detection Policies

Creation Time Database Name Disabled Alerts Email Account Admins Email Addresses Id Name Retention Days Server Name State Type
2021-01-04T08:05:32.05Z sql-db   false   /subscriptions/0123456789/resourceGroups/sql/providers/Microsoft.Sql/servers/sqlintegration/databases/sql-integration-db/securityAlertPolicies/Default Default 0 sqlintegration Enabled Microsoft.Sql/servers/databases/securityAlertPolicies

azure-sql-db-audit-policy-create-update


Creates or updates the database’s auditing policy.

Base Command

azure-sql-db-audit-policy-create-update

Input

Argument Name Description Required
server_name Server name. Required
db_name Database name. Required
state Set the state of the policy. Possible values: “Enable” or “Disable”. When state is enabled, storage_endpoint or is_azure_monitor_target_enabled are required. Required
audit_actions_groups Comma-separated list of actions groups and actions to audit. For all possible values, see the integration documentation at https://docs.microsoft.com/en-us/sql/relational-databases/security/auditing/sql-server-audit-action-groups-and-actions?view=sql-server-ver15. Optional
is_azure_monitor_target_enabled Whether audit events are sent to the Azure Monitor. Possible values: “true” and “false”. Optional
is_storage_secondary_key_in_use Whether the storage Account Access Key value is the storage’s secondary key. Possible values: “true” and “false”. Optional
queue_delay_ms Time in milliseconds that can elapse before audit actions are forced to be processed. The default minimum value is 1000 (1 second). Optional
retention_days Number of days to keep the policy in the audit logs. Optional
storage_account_access_key Identifier key of the auditing storage account. Optional
storage_account_subscription_id Storage subscription ID. Optional
storage_endpoint Storage endpoint. If the value for the state argument is enabled, the value for the storage_endpoint or is_azure_monitor_target_enabled argument is required. Optional
subscription_id Subscription ID. This value will override the instance param ‘subscription_id’. Optional
resource_group_name A comma-separated list of resource group names. This value will override the instance param ‘resource_group_name’. Optional
is_managed_identity_in_use Specifies whether Managed Identity is used to access blob storage. Possible values are: true, false. Optional

Context Output

Path Type Description
AzureSQL.DBAuditPolicy.kind String Kind of audit policy.
AzureSQL.DBAuditPolicy.id String Audit policy ID.
AzureSQL.DBAuditPolicy.name String Audit policy name.
AzureSQL.DBAuditPolicy.type String Resource type.
AzureSQL.DBAuditPolicy.isAzureMonitorTargetEnabled Boolean Whether audit events are sent to the Azure Monitor. The value is “True” (Enabled) or “False” (Disabled).
AzureSQL.DBAuditPolicy.retentionDays Number Number of days to keep in the audit logs in the storage account.
AzureSQL.DBAuditPolicy.state String Policy state.
AzureSQL.DBAuditPolicy.storageAccountSubscriptionId String Storage subscription ID.
AzureSQL.DBAuditPolicy.auditActionsAndGroups Unknown Audit actions and groups to audit.
AzureSQL.DBAuditPolicy.isStorageSecondaryKeyInUse Boolean Whether the storage_account_access_key value is the storage’s secondary key.
AzureSQL.DBAuditPolicy.queueDelayMs String Time in milliseconds that can elapse before audit actions are forced to be processed.
AzureSQL.DBAuditPolicy.storageAccountAccessKey String Identifier key of the auditing storage account.
AzureSQL.DBAuditPolicy.storageEndpoint String Storage endpoint.
AzureSQL.DBAuditPolicy.databaseName String The name of the database that the audit policy is related to.
AzureSQL.DBAuditPolicy.serverName String The name of server that the audit policy is related to.
AzureSQL.DBAuditPolicy.isManagedIdentityInUse Boolean Specifies whether Managed Identity is used to access blob storage.

Command example

!azure-sql-db-audit-policy-create-update server_name=sqlintegration db_name=sql-integration-db state=Enabled is_azure_monitor_target_enabled=true retention_days=3 queue_delay_ms=123

Context Example

{
    "AzureSQL": {
        "DBAuditPolicy": {
            "auditActionsAndGroups": [
                "SUCCESSFUL_DATABASE_AUTHENTICATION_GROUP",
                "FAILED_DATABASE_AUTHENTICATION_GROUP",
                "BATCH_COMPLETED_GROUP"
            ],
            "databaseName": "sql-db",
            "id": "/subscriptions/0123456789/resourceGroups/sql-integration/providers/Microsoft.Sql/servers/sqlintegration/databases/sql-integration-db/auditingSettings/Default",
            "isAzureMonitorTargetEnabled": true,
            "isManagedIdentityInUse": false,
            "name": "Default",
            "queueDelayMs": 123,
            "retentionDays": 3,
            "serverName": "sql",
            "state": "Enabled",
            "storageAccountSubscriptionId": "00000000-0000-0000-0000-000000000000",
            "type": "Microsoft.Sql/servers/databases/auditingSettings"
        }
    }
}

Human Readable Output

Create Or Update Database Auditing Settings for resource_group_name=’sql-integration’

Audit Actions And Groups Database Name Id Is Azure Monitor Target Enabled Is Managed Identity In Use Name Queue Delay Ms Retention Days Server Name State Storage Account Subscription Id Type
SUCCESSFUL_DATABASE_AUTHENTICATION_GROUP,
FAILED_DATABASE_AUTHENTICATION_GROUP,
BATCH_COMPLETED_GROUP
sql-db /subscriptions/0123456789/resourceGroups/sql/providers/Microsoft.Sql/servers/sqlintegration/databases/sql-integration-db/auditingSettings/Default true false Default 123 3 sqlintegration Enabled 00000000-0000-0000-0000-000000000000 Microsoft.Sql/servers/databases/auditingSettings

azure-sql-db-threat-policy-create-update


Creates or updates the database’s threat detection policy.

Base Command

azure-sql-db-threat-policy-create-update

Input

Argument Name Description Required
server_name Server name. Required
db_name Database name. Required
state The state of the policy. Possible values: “Enabled” and “Disabled”. Required
retention_days Number of days to keep the policy in the audit logs. Optional
storage_account_access_key The identifier key of the threat detection audit storage account. Optional
storage_endpoint The blob storage endpoint. This blob storage will hold all Threat Detection audit logs. Optional
disabled_alerts Comma-separated list of alerts that are disabled. Possible values: “None”, “Sql_Injection”, “Sql_Injection_Vulnerability”, “Access_Anomaly”, “Data_Exfiltration”, and “Unsafe_Action”. Optional
email_addresses Comma-separated list of email addresses to which the alert is sent. Optional
email_account_admins Whether the alert is sent to the account administrators. Possible values: “true” and “false”. Optional
use_server_default Whether to use the default server policy. Possible values: “Enabled” and “Disabled”. Optional
subscription_id Subscription ID. This value will override the instance param ‘subscription_id’. Optional
resource_group_name A comma-separated list of resource group names. This value will override the instance param ‘resource_group_name’. Optional

Context Output

Path Type Description
AzureSQL.DBThreatPolicy.kind String Kind of threat policy.
AzureSQL.DBThreatPolicy.location String Threat policy location.
AzureSQL.DBThreatPolicy.id String Threat policy ID.
AzureSQL.DBThreatPolicy.name String Threat policy name.
AzureSQL.DBThreatPolicy.type String Threat policy type.
AzureSQL.DBThreatPolicy.state String Threat policy state.
AzureSQL.DBThreatPolicy.creationTime String Threat policy creation time.
AzureSQL.DBThreatPolicy.retentionDays Number Number of days to keep in the Threat Detection audit logs.
AzureSQL.DBThreatPolicy.storageAccountAccessKey String The identifier key of the Threat Detection audit storage account.
AzureSQL.DBThreatPolicy.storageEndpoint String Threat Detection audit storage account.
AzureSQL.DBThreatPolicy.emailAccountAdmins Boolean Email accounts administrators who the alert is sent to.
AzureSQL.DBThreatPolicy.emailAddresses String List of email addresses to which the alert is sent.
AzureSQL.DBThreatPolicy.disabledAlerts String List of alerts that are disabled, or an empty string if no alerts are disabled.
AzureSQL.DBThreatPolicy.useServerDefault Unknown Whether to use the default server policy.
AzureSQL.DBThreatPolicy.databaseName String The name of the database that the threat policy is related to.
AzureSQL.DBThreatPolicy.serverName String The name of the server that the threat policy is related to.

Command example

!azure-sql-db-threat-policy-create-update server_name=sqlintegration db_name=sql-integration-db state=Enabled disabled_alerts="Sql_Injection,Sql_Injection_Vulnerability" retention_days=5

Context Example

{
    "AzureSQL": {
        "DBThreatPolicy": {
            "creationTime": "0001-01-01T00:00:00Z",
            "databaseName": "sql-integration-db",
            "disabledAlerts": [
                "Sql_Injection",
                "Sql_Injection_Vulnerability"
            ],
            "emailAccountAdmins": false,
            "emailAddresses": [],
            "id": "/subscriptions/0123456789/resourceGroups/sql-integration/providers/Microsoft.Sql/servers/sqlintegration/databases/sql-integration-db/securityAlertPolicies/Default",
            "name": "Default",
            "retentionDays": 5,
            "serverName": "sqlintegration",
            "state": "Enabled",
            "storageAccountAccessKey": "",
            "type": "Microsoft.Sql/servers/databases/securityAlertPolicies"
        }
    }
}

Human Readable Output

Create Or Update Database Threat Detection Policies for resource_group_name=’sql-integration’

Creation Time Database Name Disabled Alerts Email Account Admins Id Name Retention Days Server Name State Type
0001-01-01T00:00:00Z sql-integration-db Sql_Injection,
Sql_Injection_Vulnerability
false /subscriptions/0123456789/resourceGroups/sql-integration/providers/Microsoft.Sql/servers/sqlintegration/databases/sql-integration-db/securityAlertPolicies/Default Default 5 sqlintegration Enabled Microsoft.Sql/servers/databases/securityAlertPolicies

azure-sql-generate-login-url


Generate the login url used for Authorization code flow.

Base Command

azure-sql-generate-login-url

Input

There are no input arguments for this command.

Context Output

There is no context output for this command.

Command Example

Human Readable Output

Subscription List

Subscription Id Name Tenant Id State
subscriptionId displayName tenantId Enabled

azure-sql-resource-group-list


Gets all the resource groups for a subscription.

Base Command

azure-sql-resource-group-list

Input

Argument Name Description Required
subscription_id A comma-separated list of subscription IDs. This value will override the instance param ‘subscription_id’. Optional
tag The tag and value that is attached to the resource group. Input should be “Tag Name:Tag Value”. Optional
limit The number of results to return. Default value is 50. Optional

Context Output

Path Type Description
AzureSQL.ResourceGroup.id String The ID of the resource group.
AzureSQL.ResourceGroup.name String The name of the resource group.
AzureSQL.ResourceGroup.type String The type of the resource group.
AzureSQL.ResourceGroup.location String The location of the resource group. It cannot be changed after the resource group has been created. It must be one of the supported Azure locations.
AzureSQL.ResourceGroup.properties String The resource group properties.
AzureSQL.ResourceGroup.tags String The tags attached to the resource group.

Command Example

azure-sql-resource-group-list tag="Name:name"

Context Example

{
    "AzureSQL": {
        "ResourceGroup":{
            "id": "id",
            "name": "name",
            "type": "Microsoft.Resources/resourceGroups",
            "location": "location",
            "tags": {
                "Name": "name"
            },
            "properties": {
                "provisioningState": "Succeeded"
            }
        }
    }
}

Human Readable Output

Resource Group List for subscriptionID

Name Location Tags Provisioning State
name location Name: name Succeeded

azure-sql-firewall-rule-list


Retrieves a list of firewall rules or a specific firewall rule. Required Permissions: Microsoft.Sql/servers/firewallRules/read.

Base Command

azure-sql-firewall-rule-list

Input

Argument Name Description Required
server_name The name of the server. Required
firewall_rule_name The name of the firewall rule. If not provided, all firewall rules for the server are returned. Optional
subscription_id The subscription ID. This value will override the instance parameter ‘subscription_id’. Optional
resource_group_name A comma-separated list of resource group names. This value will override the instance parameter ‘resource_group_name’. Optional
limit The maximum number of items to return. Applies only when listing all firewall rules (ignored when firewall_rule_name is provided). Default is 50. Optional
offset The offset in the data set. Applies only when listing all firewall rules (ignored when firewall_rule_name is provided). Default is 0. Optional

Context Output

Path Type Description
AzureSQL.FirewallRule.id String The resource ID.
AzureSQL.FirewallRule.name String The resource name.
AzureSQL.FirewallRule.type String The resource type.
AzureSQL.FirewallRule.properties.startIpAddress String The start IP address of the firewall rule.
AzureSQL.FirewallRule.properties.endIpAddress String The end IP address of the firewall rule.

azure-sql-firewall-rule-create-update


Creates or updates a firewall rule. Required Permissions: Microsoft.Sql/servers/firewallRules/write.

Base Command

azure-sql-firewall-rule-create-update

Input

Argument Name Description Required
server_name The name of the server. Required
firewall_rule_name The name of the firewall rule. Required
start_ip_address The start IP address of the firewall rule. Must be IPv4 format. Use value ‘0.0.0.0’ for all Azure-internal IP addresses. Required
end_ip_address The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress. Use value ‘0.0.0.0’ for all Azure-internal IP addresses. Required
subscription_id The subscription ID. This value will override the instance parameter ‘subscription_id’. Optional
resource_group_name A comma-separated list of resource group names. This value will override the instance parameter ‘resource_group_name’. Optional

Context Output

Path Type Description
AzureSQL.FirewallRule.id String The resource ID.
AzureSQL.FirewallRule.name String The resource name.
AzureSQL.FirewallRule.type String The resource type.
AzureSQL.FirewallRule.properties.startIpAddress String The start IP address of the firewall rule.
AzureSQL.FirewallRule.properties.endIpAddress String The end IP address of the firewall rule.

azure-sql-firewall-rule-delete


Deletes a firewall rule. Required Permissions: Microsoft.Sql/servers/firewallRules/delete.

Base Command

azure-sql-firewall-rule-delete

Input

Argument Name Description Required
server_name The name of the server. Required
firewall_rule_name The name of the firewall rule. Required
subscription_id The subscription ID. This value will override the instance parameter ‘subscription_id’. Optional
resource_group_name A comma-separated list of resource group names. This value will override the instance parameter ‘resource_group_name’. Optional

Context Output

There is no context output for this command.

azure-sql-firewall-rule-replace


Replaces all firewall rules on the server. Required Permissions: Microsoft.Sql/servers/firewallRules/write.

Base Command

azure-sql-firewall-rule-replace

Input

Argument Name Description Required
server_name The name of the server. Required
firewall_rule_name The name of the firewall rule. Required unless ‘entry_id’ is provided. Optional
start_ip_address The start IP address of the firewall rule. Must be IPv4 format. Use value ‘0.0.0.0’ for all Azure-internal IP addresses. Required unless ‘entry_id’ is provided. Optional
end_ip_address The end IP address of the firewall rule. Must be IPv4 format. Must be greater than or equal to startIpAddress. Use value ‘0.0.0.0’ for all Azure-internal IP addresses. Required unless ‘entry_id’ is provided. Optional
entry_id The entry ID of a file containing the full request JSON (in the FirewallRuleList format). If provided, the ‘firewall_rule_name’, ‘start_ip_address’, and ‘end_ip_address’ arguments are ignored and must not be set. Optional
subscription_id The subscription ID. This value will override the instance parameter ‘subscription_id’. Optional
resource_group_name A comma-separated list of resource group names. This value will override the instance parameter ‘resource_group_name’. Optional

Context Output

Path Type Description
AzureSQL.FirewallRule.id String The resource ID.
AzureSQL.FirewallRule.name String The resource name.
AzureSQL.FirewallRule.type String The resource type.
AzureSQL.FirewallRule.properties.startIpAddress String The start IP address of the firewall rule.
AzureSQL.FirewallRule.properties.endIpAddress String The end IP address of the firewall rule.

Configuration parameters

  • app_id — Application ID
  • subscription_id — Default Subscription ID
  • resource_group_name — Default Resource Group Name
  • auth_type — Authentication Type (required)
  • tenant_id — Tenant ID
  • credentials — Client Secret
  • redirect_uri — Application redirect URI (for authorization code mode)
  • auth_code — Authorization code
  • managed_identities_client_id
  • azure_ad_endpoint — Azure AD endpoint
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (17)

  • azure-sql-auth-complete

    Run this command to complete the authorization process. Should be used after running the azure-sql-auth-start command.

  • azure-sql-auth-reset

    Run this command if for some reason you need to rerun the authentication process.

  • azure-sql-auth-start

    Run this command to start the authorization process and follow the instructions in the command results.

  • azure-sql-auth-test

    Tests the connectivity to the Azure SQL Management.

  • azure-sql-db-audit-policy-create-update

    Creates or updates the database's auditing policy.

  • azure-sql-db-audit-policy-list

    Gets the audit settings of the specified database.

  • azure-sql-db-list

    Lists all of the databases for the server.

  • azure-sql-db-threat-policy-create-update

    Creates or updates the database's threat detection policy.

  • azure-sql-db-threat-policy-get

    Gets the threat detection policy of the specified database.

  • azure-sql-firewall-rule-create-update

    Creates or updates a firewall rule. Required Permissions: Microsoft.Sql/servers/firewallRules/write.

  • azure-sql-firewall-rule-delete

    Deletes a firewall rule. Required Permissions: Microsoft.Sql/servers/firewallRules/delete.

  • azure-sql-firewall-rule-list

    Retrieves a list of firewall rules or a specific firewall rule. Required Permissions: Microsoft.Sql/servers/firewallRules/read.

  • azure-sql-firewall-rule-replace

    Replaces all firewall rules on the server. Required Permissions: Microsoft.Sql/servers/firewallRules/write.

  • azure-sql-generate-login-url

    Generate the login url used for Authorization code flow.

  • azure-sql-resource-group-list

    Gets all the resource groups for a subscription.

  • azure-sql-servers-list

    Lists all the servers.

  • azure-sql-subscriptions-list

    Gets all subscriptions for a tenant.

import copy

import demistomock as demisto  # noqa: F401
import urllib3
from CommonServerPython import *  # noqa: F401
from MicrosoftApiModule import *  # noqa: E402

from CommonServerUserPython import *

# Disable insecure warnings
urllib3.disable_warnings()

""" CONSTANTS """
DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
API_VERSION = "2021-11-01"
FIREWALL_API_VERSION = "2023-08-01"
""" CLIENT CLASS """


class Client:
    """Client class to interact with the service API"""

    @logger
    def __init__(
        self,
        app_id,
        subscription_id,
        resource_group_name,
        verify,
        proxy,
        auth_type,
        tenant_id=None,
        enc_key=None,
        auth_code=None,
        redirect_uri=None,
        azure_ad_endpoint="https://login.microsoftonline.com",
        managed_identities_client_id=None,
    ):
        self.resource_group_name = resource_group_name
        AUTH_TYPES_DICT: dict = {
            "Authorization Code": {
                "grant_type": AUTHORIZATION_CODE,
                "resource": None,
                "scope": "https://management.azure.com/.default",
            },
            "Device Code": {
                "grant_type": DEVICE_CODE,
                "resource": "https://management.core.windows.net",
                "scope": "https://management.azure.com/user_impersonation offline_access user.read",
            },
            "Client Credentials": {
                "grant_type": CLIENT_CREDENTIALS,
                "resource": None,
                "scope": "https://management.azure.com/.default",
            },
        }
        if "@" in app_id:
            app_id, refresh_token = app_id.split("@")
            integration_context = get_integration_context()
            integration_context.update(current_refresh_token=refresh_token)
            set_integration_context(integration_context)
        base_url = f"https://management.azure.com/subscriptions/{subscription_id}"
        client_args = assign_params(
            self_deployed=True,  # We always set the self_deployed key as True because when not using a self
            # deployed machine, the DEVICE_CODE flow should behave somewhat like a self deployed
            # flow and most of the same arguments should be set, as we're !not! using OProxy.
            auth_id=app_id,
            token_retrieval_url="https://login.microsoftonline.com/organizations/oauth2/v2.0/token"
            if "Device Code" in auth_type
            else None,
            grant_type=AUTH_TYPES_DICT.get(auth_type, {}).get("grant_type"),  # disable-secrets-detection
            base_url=base_url,
            verify=verify,
            proxy=proxy,
            resource=AUTH_TYPES_DICT.get(auth_type, {}).get("resource"),  # disable-secrets-detection
            scope=AUTH_TYPES_DICT.get(auth_type, {}).get("scope"),
            ok_codes=(200, 201, 202, 204),
            redirect_uri=redirect_uri,
            auth_code=auth_code,
            azure_ad_endpoint=azure_ad_endpoint,
            tenant_id=tenant_id,
            enc_key=enc_key,
            managed_identities_client_id=managed_identities_client_id,
            managed_identities_resource_uri=Resources.management_azure,
            command_prefix="azure-sql",
        )
        self.ms_client = MicrosoftClient(**client_args)

    @logger
    def http_request(
        self,
        method: str,
        url_suffix: str = None,
        full_url: str = None,
        params: dict = {},
        data: dict = None,
        resp_type: str = "json",
    ) -> requests.Response:
        if not full_url and not params.get("api-version"):
            params["api-version"] = API_VERSION

        try:
            return self.ms_client.http_request(
                method=method, url_suffix=url_suffix, full_url=full_url, json_data=data, params=params, resp_type=resp_type
            )
        except DemistoException as e:
            if "Error in API call [404] - Not Found" not in e.message:
                raise DemistoException(e)
            message = e.message.split('"message":')[1].split('"')[1]
            return message

    @logger
    def azure_sql_servers_list(self, resource_group_name: str = None):
        if resource_group_name:
            return self.http_request("GET", f"/resourceGroups/{resource_group_name}/providers/Microsoft.Sql/servers")
        return self.http_request("GET", "/providers/Microsoft.Sql/servers")

    @logger
    def azure_sql_db_list(self, server_name: str):
        return self.http_request(
            "GET", f"resourceGroups/{self.resource_group_name}/providers/Microsoft.Sql/servers/{server_name}/databases"
        )

    @logger
    def azure_sql_db_audit_policy_list(self, server_name: str, db_name: str, resource_group_name: str):
        return self.http_request(
            "GET",
            f"resourceGroups/{resource_group_name}/providers/Microsoft.Sql/servers/"
            f"{server_name}/databases/{db_name}/auditingSettings",
        )

    @logger
    def azure_sql_db_threat_policy_get(self, server_name: str, db_name: str):
        return self.http_request(
            "GET",
            f"resourceGroups/{self.resource_group_name}/providers/Microsoft.Sql/servers/"
            f"{server_name}/databases/{db_name}/securityAlertPolicies/default",
        )

    @logger
    def azure_sql_db_audit_policy_create_update(
        self,
        server_name: str,
        db_name: str,
        state: str,
        audit_actions_groups: List[str],
        is_azure_monitor_target_enabled: bool,
        is_storage_secondary_key_in_use: bool,
        queue_delay_ms: str,
        retention_days: str,
        storage_account_access_key: str,
        storage_account_subscription_id: str,
        storage_endpoint: str,
        is_managed_identity_in_use: bool,
        resource_group_name: str,
    ):
        properties = assign_params(
            state=state,
            auditActionsAndGroups=audit_actions_groups,
            isAzureMonitorTargetEnabled=is_azure_monitor_target_enabled,
            isStorageSecondaryKeyInUse=is_storage_secondary_key_in_use,
            queueDelayMs=queue_delay_ms,
            retentionDays=retention_days,
            storageAccountAccessKey=storage_account_access_key,
            storageAccountSubscriptionId=storage_account_subscription_id,
            storageEndpoint=storage_endpoint,
            isManagedIdentityInUse=is_managed_identity_in_use,
        )

        request_body = {"properties": properties} if properties else {}

        return self.http_request(
            method="PUT",
            url_suffix=f"resourceGroups/{resource_group_name}/providers"
            f"/Microsoft.Sql/servers/{server_name}/databases/"
            f"{db_name}/auditingSettings/default",
            data=request_body,
        )

    def azure_sql_db_threat_policy_create_update(
        self,
        server_name: str,
        db_name: str,
        state: str,
        disabled_alerts: List[str],
        email_account_admins: str,
        email_addresses: List[str],
        retention_days: str,
        storage_account_access_key: str,
        use_server_default: str,
        storage_endpoint: str,
        resource_group_name: str,
    ):
        properties = assign_params(
            state=state,
            retentionDays=retention_days,
            storageAccountAccessKey=storage_account_access_key,
            storageEndpoint=storage_endpoint,
            disabledAlerts=disabled_alerts,
            emailAccountAdmins=email_account_admins,
            emailAddresses=email_addresses,
            useServerDefault=use_server_default,
        )

        request_body = {"properties": properties} if properties else {}

        return self.http_request(
            method="PUT",
            url_suffix=f"resourceGroups/{resource_group_name}/providers"
            f"/Microsoft.Sql/servers/{server_name}/databases/"
            f"{db_name}/securityAlertPolicies/default",
            data=request_body,
        )

    def azure_sql_firewall_rule_list(self, server_name: str, resource_group_name: str, firewall_rule_name: str = None):
        """Gets a list of firewall rules or a specific firewall rule.
        Args:
            server_name: The name of the server.
            resource_group_name: The name of the resource group.
            firewall_rule_name: The name of the firewall rule. If provided, gets a specific rule.
        Returns:
            The API response.
        """
        url_suffix = f"resourceGroups/{resource_group_name}/providers/Microsoft.Sql/servers/{server_name}/firewallRules"
        if firewall_rule_name:
            url_suffix += f"/{firewall_rule_name}"
        return self.http_request("GET", url_suffix, params={"api-version": FIREWALL_API_VERSION})

    def azure_sql_firewall_rule_create_update(
        self,
        server_name: str,
        resource_group_name: str,
        firewall_rule_name: str,
        start_ip_address: str = None,
        end_ip_address: str = None,
    ):
        """Creates or updates a firewall rule.
        Args:
            server_name: The name of the server.
            resource_group_name: The name of the resource group.
            firewall_rule_name: The name of the firewall rule.
            start_ip_address: The start IP address of the firewall rule.
            end_ip_address: The end IP address of the firewall rule.
        Returns:
            The API response.
        """
        request_body = {
            "properties": {
                "startIpAddress": start_ip_address,
                "endIpAddress": end_ip_address,
            }
        }
        url_suffix = (
            f"resourceGroups/{resource_group_name}/providers/Microsoft.Sql/servers/"
            f"{server_name}/firewallRules/{firewall_rule_name}"
        )
        return self.http_request("PUT", url_suffix, params={"api-version": FIREWALL_API_VERSION}, data=request_body)

    def azure_sql_firewall_rule_delete(self, server_name: str, resource_group_name: str, firewall_rule_name: str):
        """Deletes a firewall rule.
        Args:
            server_name: The name of the server.
            resource_group_name: The name of the resource group.
            firewall_rule_name: The name of the firewall rule.
        Returns:
            The API response.
        """
        url_suffix = (
            f"resourceGroups/{resource_group_name}/providers/Microsoft.Sql/servers/"
            f"{server_name}/firewallRules/{firewall_rule_name}"
        )
        return self.http_request("DELETE", url_suffix, params={"api-version": FIREWALL_API_VERSION}, resp_type="response")

    def azure_sql_firewall_rule_replace(
        self,
        server_name: str,
        resource_group_name: str,
        firewall_rule_name: str = None,
        start_ip_address: str = None,
        end_ip_address: str = None,
        request_body: dict = None,
    ):
        """Replaces all firewall rules on the server.
        Args:
            server_name: The name of the server.
            resource_group_name: The name of the resource group.
            firewall_rule_name: The name of the firewall rule.
            start_ip_address: The start IP address of the firewall rule.
            end_ip_address: The end IP address of the firewall rule.
            request_body: A pre-built request body. If provided, it is used as-is and the
                individual rule arguments are ignored.
        Returns:
            The API response.
        """
        if request_body is None:
            properties = assign_params(
                startIpAddress=start_ip_address,
                endIpAddress=end_ip_address,
            )
            rule: dict = {"name": firewall_rule_name}
            if properties:
                rule["properties"] = properties
            request_body = {"values": [rule]}
        url_suffix = f"resourceGroups/{resource_group_name}/providers/Microsoft.Sql/servers/{server_name}/firewallRules"
        return self.http_request("PUT", url_suffix, params={"api-version": FIREWALL_API_VERSION}, data=request_body)

    def subscriptions_list_request(self):
        """Gets all subscriptions for a tenant.e.
        Returns:
            A dictionary that contains the list of subscription.
        """
        return self.http_request(method="GET", full_url="https://management.azure.com/subscriptions?api-version=2020-01-01")

    def resource_group_list_request(self, sub_id: str, tag: str, limit: int):
        """Gets all the resource groups for a subscription.
        Args:
            sub_id: str - A subscription id.
            tag: str - The tag and value that attached to the resource group.
            limit: int - The number of results to return.
        Returns:
            A dictionary that contains the list of resource groups for the given subscription id.
        """
        full_url = f"https://management.azure.com/subscriptions/{sub_id}/resourcegroups?api-version=2021-04-01"
        if tag:
            tag_split = tag.split(":")
            tag_name = tag_split[0]
            tag_value = tag_split[1]
            demisto.debug(f"{tag=}, {tag_split}, {tag_name=}, {tag_value=}")
            full_url = f"{full_url}&$filter=tagName eq '{tag_name}' and tagValue eq '{tag_value}'"
        if limit:
            full_url = f"{full_url}&$top={limit}"
        return self.http_request(method="GET", full_url=full_url)


@logger
def azure_sql_servers_list_command(client: Client, args: Dict[str, str], resource_group_name: str) -> CommandResults:
    """azure-sql-servers-list command returns a list of all servers

    Args:
        client: AzureSQLManagement Client to use
        limit: The maximum number of servers returned to the War Room. Default is 50.
        offset: Offset in the data set. Default is 0.
        resource_group_name: str - The name of the resource group that contains the resource.
    Returns:
        A ``CommandResults`` object that is then passed to ``return_results``,
        that contains a list of all servers
    """
    offset_int = int(args.get("offset", "0"))
    limit_int = int(args.get("limit", "50"))
    list_by_resource_group = argToBoolean(args.get("list_by_resource_group", False))

    if list_by_resource_group:
        server_list_raw = client.azure_sql_servers_list(resource_group_name)
        name = f"The list of servers in the resource group: {resource_group_name}"
    else:
        server_list_raw = client.azure_sql_servers_list()
        name = "Servers List"

    if isinstance(server_list_raw, str):  # if there is 404, an error message will return
        return CommandResults(readable_output=server_list_raw)

    server_list_fixed = copy.deepcopy(server_list_raw.get("value", "")[offset_int : (offset_int + limit_int)])
    for server in server_list_fixed:
        if properties := server.get("properties", {}):
            server.update(properties)
            del server["properties"]

    human_readable = tableToMarkdown(name=name, t=server_list_fixed, headerTransform=pascalToSpace, removeNull=True)

    return CommandResults(
        readable_output=human_readable,
        outputs_prefix="AzureSQL.Server",
        outputs_key_field="id",
        outputs=server_list_fixed,
        raw_response=server_list_raw,
    )


@logger
def azure_sql_db_list_command(client: Client, args: Dict[str, str]) -> CommandResults:
    """azure-sql-db-list command returns a list of all databases for server

    Args:
        client: AzureSQLManagement Client to use
        server_name: server name for which we want to receive list of databases
        limit: The maximum number of databases returned to the War Room. Default
        is 50.
        offset: Offset in the data set. Default is 0.

    Returns:
        A ``CommandResults`` object that is then passed to ``return_results``,
        that contains a list of all databases for server
    """

    offset_int = int(args.get("offset", "0"))
    limit_int = int(args.get("limit", "50"))

    database_list_raw = client.azure_sql_db_list(args.get("server_name"))

    if isinstance(database_list_raw, str):  # if there is 404, an error message will return
        return CommandResults(readable_output=database_list_raw)

    database_list_fixed = copy.deepcopy(database_list_raw.get("value", "")[offset_int : (offset_int + limit_int)])

    for db in database_list_fixed:
        properties = db.get("properties", {})
        if properties:
            db.update(properties)
            del db["properties"]

    human_readable = tableToMarkdown(
        name="Database List",
        t=database_list_fixed,
        headers=["name", "location", "status", "managedBy"],
        headerTransform=pascalToSpace,
        removeNull=True,
    )

    return CommandResults(
        readable_output=human_readable,
        outputs_prefix="AzureSQL.DB",
        outputs_key_field="id",
        outputs=database_list_fixed,
        raw_response=database_list_raw,
    )


@logger
def azure_sql_db_audit_policy_list_command(client: Client, args: Dict[str, str], resource_group_name: str) -> CommandResults:
    """azure_sql_db_audit_policy_list command returns a list of auditing settings of a database

    Args:
        client: AzureSQLManagement Client to use
        server_name: server name for which we want to receive list of auditing settings
        db_name: database for which we want to receive list of auditing settings
        limit: The maximum number of audit policies returned to the War Room. Default
        is 50.
        offset: Offset in the data set. Default is 0.
        resource_group_name: The name of the resource group that contains the resource.

    Returns:
        A ``CommandResults`` object that is then passed to ``return_results``,
        that contains a list of auditing settings of a database

    """
    server_name = args.get("server_name")
    db_name = args.get("db_name")
    offset_int = int(args.get("offset", "0"))
    limit_int = int(args.get("limit", "50"))

    audit_list_raw = client.azure_sql_db_audit_policy_list(server_name, db_name, resource_group_name)

    if isinstance(audit_list_raw, str):  # if there is 404 then, error message will return
        return CommandResults(readable_output=audit_list_raw)

    audit_list_fixed = copy.deepcopy(audit_list_raw.get("value", "")[offset_int : (offset_int + limit_int)])
    for db in audit_list_fixed:
        db["serverName"] = server_name
        db["databaseName"] = db_name
        if properties := db.get("properties", {}):
            db.update(properties)
            del db["properties"]

    human_readable = tableToMarkdown(
        name=f"Database Audit Settings for {resource_group_name=}",
        t=audit_list_fixed,
        headerTransform=pascalToSpace,
        removeNull=True,
    )

    return CommandResults(
        readable_output=human_readable,
        outputs_prefix="AzureSQL.DBAuditPolicy",
        outputs_key_field="id",
        outputs=audit_list_fixed,
        raw_response=audit_list_raw,
    )


@logger
def azure_sql_db_audit_policy_create_update_command(
    client: Client, args: Dict[str, str], resource_group_name: str
) -> CommandResults:
    """azure_sql_db_audit_policy_create_update command upadates and creates audit policies related to the server
    and database

    Args:
        client: AzureSQLManagement Client to use
        server_name: server name for which we want to create or update auditing settings
        db_name: database for which we want to create or update auditing settings
        state: state of the policy
        audit_actions_groups: Comma-separated Actions-Groups and Actions to audit.
        is_azure_monitor_target_enabled: Is audit events are sent to Azure Monitor
        is_storage_secondary_key_in_use: Is storageAccountAccessKey value is the storage's secondary key
        queue_delay_ms: Time in milliseconds that can elapse before audit actions are forced
        to be processed.
        retention_days: Number of days to keep the policy in the audit logs.
        storage_account_access_key: identifier key of the auditing storage account
        storage_account_subscription_id: storage subscription Id
        storage_endpoint: Storage endpoint.
        resource_group_name: The name of the resource group that contains the resource.

    Returns:
        A ``CommandResults`` object that is then passed to ``return_results``,
        that contains an updated audit policy

    """

    server_name = args.get("server_name")
    db_name = args.get("db_name")
    state = args.get("state")
    audit_actions_groups = argToList(args.get("audit_actions_groups", ""))
    is_azure_monitor_target_enabled = args.get("is_azure_monitor_target_enabled", "")
    is_storage_secondary_key_in_use = args.get("is_storage_secondary_key_in_use", "")
    queue_delay_ms = args.get("queue_delay_ms", "")
    retention_days = args.get("retention_days", "")
    storage_account_access_key = args.get("storage_account_access_key", "")
    storage_account_subscription_id = args.get("storage_account_subscription_id", "")
    storage_endpoint = args.get("storage_endpoint", "")
    is_managed_identity_in_use = args.get("is_managed_identity_in_use", "")

    raw_response = client.azure_sql_db_audit_policy_create_update(
        server_name=server_name,
        db_name=db_name,
        state=state,
        audit_actions_groups=audit_actions_groups,
        is_azure_monitor_target_enabled=is_azure_monitor_target_enabled,
        is_storage_secondary_key_in_use=is_storage_secondary_key_in_use,
        queue_delay_ms=queue_delay_ms,
        retention_days=retention_days,
        storage_account_access_key=storage_account_access_key,
        storage_account_subscription_id=storage_account_subscription_id,
        storage_endpoint=storage_endpoint,
        is_managed_identity_in_use=is_managed_identity_in_use,
        resource_group_name=resource_group_name,
    )

    if isinstance(raw_response, str):  # if there is 404, an error message will return
        return CommandResults(readable_output=raw_response)

    fixed_response = copy.deepcopy(raw_response)
    if properties := fixed_response.get("properties", {}):
        fixed_response["serverName"] = server_name
        fixed_response["databaseName"] = db_name
        fixed_response.update(properties)
        del fixed_response["properties"]

    human_readable = tableToMarkdown(
        name=f"Create Or Update Database Auditing Settings for {resource_group_name=}",
        t=fixed_response,
        headerTransform=pascalToSpace,
        removeNull=True,
    )

    return CommandResults(
        readable_output=human_readable,
        outputs_prefix="AzureSQL.DBAuditPolicy",
        outputs_key_field="id",
        outputs=fixed_response,
        raw_response=raw_response,
    )


@logger
def azure_sql_db_threat_policy_get_command(client: Client, args: Dict[str, str]) -> CommandResults:
    """azure_sql_db_threat_policy_get command returns a threat detection policy of a database

    Args:
        client: AzureSQLManagement Client to use
        server_name: server name for which we want to receive threat detection policies
        db_name: database for which we want to receive threat detection policies

        A ``CommandResults`` object that is then passed to ``return_results``,
        that contains a threat detection policy of a database
    """
    server_name = args.get("server_name")
    db_name = args.get("db_name")
    threat_raw = client.azure_sql_db_threat_policy_get(server_name, db_name)

    if isinstance(threat_raw, str):  # if there is 404, an error message will return
        return CommandResults(readable_output=threat_raw)

    threat_fixed = copy.deepcopy(threat_raw)

    if properties := threat_fixed.get("properties", {}):
        threat_fixed["serverName"] = server_name
        threat_fixed["databaseName"] = db_name
        threat_fixed.update(properties)
        del threat_fixed["properties"]

    human_readable = tableToMarkdown(
        name="Database Threat Detection Policies", t=threat_fixed, headerTransform=pascalToSpace, removeNull=True
    )

    return CommandResults(
        readable_output=human_readable,
        outputs_prefix="AzureSQL.DBThreatPolicy",
        outputs_key_field="id",
        outputs=threat_fixed,
        raw_response=threat_raw,
    )


@logger
def azure_sql_db_threat_policy_create_update_command(
    client: Client, args: Dict[str, str], resource_group_name: str
) -> CommandResults:
    """azure_sql_db_audit_policy_create_update command upadates and creates threat policy related to the server
    and database

    Args:
        client: AzureSQLManagement Client to use
        server_name: server name for which we want to create or update auditing settings
        db_name: database for which we want to create or update auditing settings
        state: satate of the policy
        disabled_alerts: Comma-separated list of alerts that are disabled, or "none" to
        disable no alerts.
        email_account_admins: The alert is sent to the account administrators.
        email_addresses: Comma-separated list of e-mail addresses to which the alert is
        sent.
        retention_days: Number of days to keep the policy in the audit logs.
        storage_account_access_key: identifier key of the auditing storage account
        use_server_default: Whether to use the default server policy or not.
        storage_endpoint: Storage endpoint.
        resource_group_name: The name of the resource group that contains the resource.

    Returns:
    A ``CommandResults`` object that is then passed to ``return_results``,
    that contains an updated threat policy

    """
    server_name = args["server_name"]
    db_name = args["db_name"]
    state = args["state"]
    retention_days = args.get("retention_days", "")
    email_account_admins = args.get("email_account_admins", "")
    email_addresses = argToList(args.get("email_addresses", ""))
    storage_account_access_key = args.get("storage_account_access_key", "")
    use_server_default = args.get("use_server_default", "")
    storage_endpoint = args.get("storage_endpoint", "")
    disabled_alerts = [""] if "None" in argToList(args.get("disabled_alerts", "")) else argToList(args.get("disabled_alerts", ""))

    raw_response = client.azure_sql_db_threat_policy_create_update(
        server_name=server_name,
        db_name=db_name,
        state=state,
        retention_days=retention_days,
        disabled_alerts=disabled_alerts,
        email_account_admins=email_account_admins,
        email_addresses=email_addresses,
        storage_account_access_key=storage_account_access_key,
        use_server_default=use_server_default,
        storage_endpoint=storage_endpoint,
        resource_group_name=resource_group_name,
    )

    if isinstance(raw_response, str):  # if there is 404, an error message will return
        return CommandResults(readable_output=raw_response)

    fixed_response = copy.deepcopy(raw_response)
    if properties := fixed_response.get("properties", {}):
        fixed_response["serverName"] = server_name
        fixed_response["databaseName"] = db_name
        fixed_response.update(properties)
        del fixed_response["properties"]

    human_readable = tableToMarkdown(
        name=f"Create Or Update Database Threat Detection Policies for {resource_group_name=}",
        t=fixed_response,
        headerTransform=pascalToSpace,
        removeNull=True,
    )

    return CommandResults(
        readable_output=human_readable,
        outputs_prefix="AzureSQL.DBThreatPolicy",
        outputs_key_field="id",
        outputs=fixed_response,
        raw_response=raw_response,
    )


def azure_sql_firewall_rule_list_command(client: Client, args: Dict[str, str], resource_group_name: str) -> CommandResults:
    """azure-sql-firewall-rule-list command returns a list of firewall rules or a specific firewall rule.

    Args:
        client: AzureSQLManagement Client to use.
        args: Command arguments.
        resource_group_name: The name of the resource group.

    Returns:
        A ``CommandResults`` object that is then passed to ``return_results``.
    """
    server_name = args["server_name"]
    firewall_rule_name = args.get("firewall_rule_name")
    offset_int = arg_to_number(args.get("offset")) or 0
    limit_int = arg_to_number(args.get("limit")) or 50

    raw_response = client.azure_sql_firewall_rule_list(server_name, resource_group_name, firewall_rule_name)

    if isinstance(raw_response, str):
        return CommandResults(readable_output=raw_response)

    if firewall_rule_name:
        # Single rule response - wrap in list for consistent processing
        rules = [raw_response]
    else:
        rules = raw_response.get("value", [])[offset_int : (offset_int + limit_int)]

    if not rules:
        return CommandResults(readable_output="No firewall rules were found.")

    human_readable = tableToMarkdown(
        name="Firewall Rules",
        t=rules,
        headers=["id", "name", "type"],
        headerTransform=pascalToSpace,
        removeNull=True,
    )

    return CommandResults(
        readable_output=human_readable,
        outputs_prefix="AzureSQL.FirewallRule",
        outputs_key_field="id",
        outputs=rules,
        raw_response=rules,
    )


def azure_sql_firewall_rule_create_update_command(
    client: Client, args: Dict[str, str], resource_group_name: str
) -> CommandResults:
    """azure-sql-firewall-rule-create-update command creates or updates a firewall rule.

    Args:
        client: AzureSQLManagement Client to use.
        args: Command arguments.
        resource_group_name: The name of the resource group.

    Returns:
        A ``CommandResults`` object that is then passed to ``return_results``.
    """
    server_name = args["server_name"]
    firewall_rule_name = args["firewall_rule_name"]
    start_ip_address = args["start_ip_address"]
    end_ip_address = args["end_ip_address"]

    raw_response = client.azure_sql_firewall_rule_create_update(
        server_name=server_name,
        resource_group_name=resource_group_name,
        firewall_rule_name=firewall_rule_name,
        start_ip_address=start_ip_address,
        end_ip_address=end_ip_address,
    )

    if isinstance(raw_response, str):
        return CommandResults(readable_output=raw_response)

    return CommandResults(
        readable_output=f"Successfully updated the firewall rule {firewall_rule_name}",
        outputs_prefix="AzureSQL.FirewallRule",
        outputs_key_field="id",
        outputs=raw_response,
        raw_response=raw_response,
    )


def azure_sql_firewall_rule_delete_command(client: Client, args: Dict[str, str], resource_group_name: str) -> CommandResults:
    """azure-sql-firewall-rule-delete command deletes a firewall rule.

    Args:
        client: AzureSQLManagement Client to use.
        args: Command arguments.
        resource_group_name: The name of the resource group.

    Returns:
        A ``CommandResults`` object that is then passed to ``return_results``.
    """
    server_name = args["server_name"]
    firewall_rule_name = args["firewall_rule_name"]

    response = client.azure_sql_firewall_rule_delete(
        server_name=server_name,
        resource_group_name=resource_group_name,
        firewall_rule_name=firewall_rule_name,
    )

    if isinstance(response, str):  # if there is a 404, an error message will return
        return CommandResults(readable_output=response)

    return CommandResults(
        readable_output=f"The firewall rule {firewall_rule_name} has been successfully deleted.",
    )


def azure_sql_firewall_rule_replace_command(client: Client, args: Dict[str, str], resource_group_name: str) -> CommandResults:
    """azure-sql-firewall-rule-replace command replaces all firewall rules on the server.

    Args:
        client: AzureSQLManagement Client to use.
        args: Command arguments.
        resource_group_name: The name of the resource group.

    Returns:
        A ``CommandResults`` object that is then passed to ``return_results``.
    """
    server_name = args["server_name"]
    entry_id = args.get("entry_id")
    firewall_rule_name = args.get("firewall_rule_name")
    start_ip_address = args.get("start_ip_address")
    end_ip_address = args.get("end_ip_address")

    request_body = None
    if entry_id:
        if firewall_rule_name or start_ip_address or end_ip_address:
            raise DemistoException(
                "When 'entry_id' is provided, the 'firewall_rule_name', 'start_ip_address', and "
                "'end_ip_address' arguments must not be set, as the request body is taken entirely from the file."
            )
        file_path = demisto.getFilePath(entry_id).get("path")
        with open(file_path) as f:
            try:
                request_body = json.loads(f.read())
            except json.JSONDecodeError as e:
                raise DemistoException(f"Failed to parse the JSON file in entry_id '{entry_id}': {e}")
    else:
        if not (firewall_rule_name and start_ip_address and end_ip_address):
            raise DemistoException(
                "Either 'entry_id' must be provided, or all of 'firewall_rule_name', 'start_ip_address', "
                "and 'end_ip_address' must be provided."
            )

    raw_response = client.azure_sql_firewall_rule_replace(
        server_name=server_name,
        resource_group_name=resource_group_name,
        firewall_rule_name=firewall_rule_name,
        start_ip_address=start_ip_address,
        end_ip_address=end_ip_address,
        request_body=request_body,
    )

    if isinstance(raw_response, str):
        return CommandResults(readable_output=raw_response)

    return CommandResults(
        readable_output="Successfully updated the firewall rule",
        outputs_prefix="AzureSQL.FirewallRule",
        outputs_key_field="id",
        outputs=raw_response,
        raw_response=raw_response,
    )


def subscriptions_list_command(client: Client) -> CommandResults:
    """Gets all subscriptions for a tenant.
    Args:
        client: AzureSQLManagement Client to use.
    Returns:
    A ``CommandResults`` object that is then passed to ``return_results``,
    that contains all the subscriptions for a tenant.
    """
    response = client.subscriptions_list_request()

    if isinstance(response, str):  # if there is 404, an error message will return
        return CommandResults(readable_output=response)

    response = response.get("value", [{}])
    readable_output_table = []
    for result in response:
        d = {
            "Subscription Id": result.get("subscriptionId"),
            "Tenant Id": result.get("tenantId"),
            "State": result.get("state"),
            "Name": result.get("displayName"),
        }
        readable_output_table.append(d)
    headers = ["Subscription Id", "Name", "Tenant Id", "State"]
    human_readable = tableToMarkdown(name="Subscription List", t=readable_output_table, removeNull=True, headers=headers)
    return CommandResults(
        readable_output=human_readable,
        outputs_prefix="AzureSQL.Subscription",
        outputs=response,
        raw_response=response,
        outputs_key_field="subscriptionId",
    )


def resource_group_list_command(client: Client, args: Dict, subscriptions_id: List) -> List:
    """Gets all subscriptions for a tenant.
    Args:
        client: Client - AzureSQLManagement Client to use.
        args: Dict - The command arguments.
        subscriptions_id: List - A list of subscription ids.
    Returns:
    A ``CommandResults`` object that is then passed to ``return_results``,
    that contains all the subscriptions for a tenant.
    """
    tag = args.get("tag", "")
    limit = arg_to_number(args.get("limit")) or 50

    results = []
    for sub_id in subscriptions_id:
        response = client.resource_group_list_request(sub_id, tag, limit)
        demisto.debug(f"{response=}")

        if isinstance(response, str):  # if there is 404, an error message will return
            result_message = CommandResults(readable_output=response)
            results.append(result_message)

        else:
            response = response.get("value", [{}])
            readable_output_table = []
            for result in response:
                d = {
                    "Name": result.get("name"),
                    "Location": result.get("location"),
                    "Tags": result.get("tags"),
                    "Provisioning State": result.get("properties", {}).get("provisioningState"),
                }
                readable_output_table.append(d)
            headers = ["Name", "Location", "Tags", "Provisioning State"]
            human_readable = tableToMarkdown(
                name=f"Resource Group List for {sub_id}", t=readable_output_table, removeNull=True, headers=headers
            )
            command_result = CommandResults(
                readable_output=human_readable,
                outputs_prefix="AzureSQL.ResourceGroup",
                outputs=response,
                raw_response=response,
                outputs_key_field="id",
            )
            results.append(command_result)
    return results


@logger
def test_connection(client: Client) -> CommandResults:
    if demisto.params().get("auth_type") == "Device Code":
        client.ms_client.get_access_token()  # If fails, MicrosoftApiModule returns an error
    else:
        client.ms_client.get_access_token()  # If fails, MicrosoftApiModule returns an error
    return CommandResults(readable_output="✅ Success!")


@logger
def start_auth(client: Client) -> CommandResults:  # pragma: no cover
    result = client.ms_client.start_auth("!azure-sql-auth-complete")
    return CommandResults(readable_output=result)


@logger
def complete_auth(client: Client) -> CommandResults:  # pragma: no cover
    client.ms_client.get_access_token()
    return CommandResults(readable_output="✅ Authorization completed successfully.")


@logger
def test_module(client):
    """
    Performs basic GET request to check if the API is reachable and authentication is successful.
    Returns ok if successful.
    """
    params = demisto.params()
    if params.get("auth_type") == "Device Code":
        raise Exception(
            "When using device code flow configuration, "
            "Please enable the integration and run `!azure-sql-auth-start` and `!azure-sql-auth-complete` to "
            "log in. You can validate the connection by running `!azure-sql-auth-test`\n"
            "For more details press the (?) button."
        )

    elif params.get("auth_type") == "Authorization Code":
        raise Exception(
            "When using user auth flow configuration, "
            "Please enable the integration and run the !azure-sql-auth-test command in order to test it"
        )
    elif params.get("auth_type") == "Azure Managed Identities" or params.get("auth_type") == "Client Credentials":
        client.ms_client.get_access_token()
        return "ok"
    return None


# Maps commands that support multiple resource_group_name values to their handler functions.
# This dict is the single source of truth: its keys drive routing in main() and its values drive dispatch here.
COMMANDS_WITH_MULTIPLE_RESOURCE_GROUP_NAME = {
    "azure-sql-db-audit-policy-create-update": azure_sql_db_audit_policy_create_update_command,
    "azure-sql-servers-list": azure_sql_servers_list_command,
    "azure-sql-db-threat-policy-create-update": azure_sql_db_threat_policy_create_update_command,
    "azure-sql-db-audit-policy-list": azure_sql_db_audit_policy_list_command,
    "azure-sql-firewall-rule-list": azure_sql_firewall_rule_list_command,
    "azure-sql-firewall-rule-create-update": azure_sql_firewall_rule_create_update_command,
    "azure-sql-firewall-rule-delete": azure_sql_firewall_rule_delete_command,
    "azure-sql-firewall-rule-replace": azure_sql_firewall_rule_replace_command,
}


def command_with_multiple_resource_group_name(client: Client, args: Dict, command: str, resource_group_name: List) -> List:
    """Manage commands that can have multiple resource_group_name.
    Args:
        client: Client - Azure SQL management client.
        args: Dict - The arguments to the command.
        command: str - the name of the command
        resource_group_name: List - A list of the resource group names

    Returns:
        A list of CommandResults objects that is then passed to ``return_results``,
        that contains the results of the relevant command.
    """
    command_fn = COMMANDS_WITH_MULTIPLE_RESOURCE_GROUP_NAME[command]
    results = []
    for name in resource_group_name:
        result = command_fn(client, args, name)
        results.append(result)
    return results


""" MAIN FUNCTION """


def main() -> None:
    """main function, parses params and runs command functions"""
    params = demisto.params()
    command = demisto.command()
    args = demisto.args()

    demisto.debug(f"Command being called is {command}")
    try:
        subscription_id = argToList(args.get("subscription_id")) or [params.get("subscription_id", "")]
        resource_group_name = argToList(args.get("resource_group_name")) or [params.get("resource_group_name", "")]
        client = Client(
            tenant_id=params.get("tenant_id", ""),
            auth_type=params.get("auth_type", "Device Code"),
            auth_code=params.get("auth_code", {}).get("password", ""),
            redirect_uri=params.get("redirect_uri", ""),
            enc_key=params.get("credentials", {}).get("password", ""),
            app_id=params.get("app_id", ""),
            subscription_id=subscription_id[0],
            resource_group_name=resource_group_name[0],
            verify=not params.get("insecure", False),
            proxy=params.get("proxy", False),
            azure_ad_endpoint=params.get("azure_ad_endpoint", "https://login.microsoftonline.com")
            or "https://login.microsoftonline.com",
            managed_identities_client_id=get_azure_managed_identities_client_id(params),
        )
        if command == "test-module":
            return_results(test_module(client))

        elif command in COMMANDS_WITH_MULTIPLE_RESOURCE_GROUP_NAME:
            return_results(command_with_multiple_resource_group_name(client, args, command, resource_group_name))

        elif command == "azure-sql-db-list":
            return_results(azure_sql_db_list_command(client, args))

        elif command == "azure-sql-db-threat-policy-get":
            return_results(azure_sql_db_threat_policy_get_command(client, args))

        elif command == "azure-sql-auth-start":
            return_results(start_auth(client))

        elif command == "azure-sql-auth-complete":
            return_results(complete_auth(client))

        elif command == "azure-sql-auth-reset":
            return_results(reset_auth())

        elif command == "azure-sql-auth-test":
            return_results(test_connection(client))

        elif command == "azure-sql-generate-login-url":
            return_results(generate_login_url(client.ms_client))

        elif command == "azure-sql-subscriptions-list":
            return_results(subscriptions_list_command(client))

        elif command == "azure-sql-resource-group-list":
            return_results(resource_group_list_command(client, args, subscription_id))

    # Log exceptions and return errors
    except Exception as e:
        return_error(f"Failed to execute {demisto.command()} command.\nError:\n{e!s}")


""" ENTRY POINT """

if __name__ in ("__main__", "__builtin__", "builtins"):
    main()