BMC Discovery
BMC Discovery is a SaaS-based, cloud-native discovery and dependency modeling system that provides instant visibility into hardware, software, and service dependencies across multi-cloud, hybrid, and on-premises environments.
Endpoint · BMC Discovery
Details
| ID | BMC Discovery |
|---|---|
| Provider | BMC Software Inc |
| Category | Endpoint |
| From Version | 6.0.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix XSIAM |
README
BMC Discovery is a SaaS-based, cloud-native discovery and dependency modeling system that provides instant visibility into hardware, software, and service dependencies across multi-cloud, hybrid, and on-premises environments.
This integration was integrated and tested with BMC Discovery v.22.1.
Configure BMC Discovery Integration in Cortex
| Parameter | Description | Required |
|---|---|---|
| Server URL | BMC Discovery URL | True |
| API Token | BMC Discovery user API token* | True |
| Use system proxy settings | False | |
| Trust any certificate (not secure) | False |
- The BMC Discovery user must have the following permissions: admin, api-access, discovery, system
Commands
You can execute these commands from the 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.
bmc-discovery-scan-status-list
Get status of all currently running scans (runs) or a specific scan (run)
Base Command
bmc-discovery-scan-status-list
Input
| Argument Name | Description | Required |
|---|---|---|
| run_id | ID of a specific scan (run) | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| BmcDiscovery.Scan.Status.uuid | String | Scan UUID |
| BmcDiscovery.Scan.Status.blocked | Boolean | Is blocked |
| BmcDiscovery.Scan.Status.done | Number | Number of scanned hosts |
| BmcDiscovery.Scan.Status.total | Number | Number of hosts to scan |
| BmcDiscovery.Scan.Status.finished | Boolean | Is finished |
| BmcDiscovery.Scan.Status.label | String | Scan name |
| BmcDiscovery.Scan.Status.scan_kind | String | IP/Cloud/API |
| BmcDiscovery.Scan.Status.scan_level | String | Full discovery/Sweep scan |
| BmcDiscovery.Scan.Status.scan_type | String | Snapshot/scheduled |
| BmcDiscovery.Scan.Status.user | String | Initiating user |
| BmcDiscovery.Scan.Status.valid_ranges | String | IP ranges to scan |
| BmcDiscovery.Scan.Status.cancelled | String | Is canceled |
| BmcDiscovery.Scan.Status.key | String | Scan key |
| BmcDiscovery.Scan.Status.outpost_id | String | Outpost id |
| BmcDiscovery.Scan.Status.scope | String | Scan scope |
| BmcDiscovery.Scan.Status.scan_params.provider | String | Scan provider |
| BmcDiscovery.Scan.Status.scan_options.NO_PING | Boolean | Is NO-PING option set |
| BmcDiscovery.Scan.Status.scan_options.SESSION_LOGGING | Boolean | Is SESSION_LOGGING option set |
| BmcDiscovery.Scan.Status.scan_options.SKIP_IMPLICIT_SCANS | Boolean | Is SKIP_IMPLICIT_SCANS option set |
| BmcDiscovery.Scan.Status.scan_options.MAX_START_SSM_SESSIONS | Boolean | Is MAX_START_SSM_SESSIONS option set |
| BmcDiscovery.Scan.Status.scan_options.MAX_ACTIVE_SSM_SESSIONS | Boolean | Is MAX_ACTIVE_SSM_SESSIONS option set |
| BmcDiscovery.Scan.Status.scanning | Number | Number of entities in scanning status |
| BmcDiscovery.Scan.Status.pre_scanning | Number | Number of entities in pre_scanning status |
| BmcDiscovery.Scan.Status.starttime | Date | Scan start time |
| BmcDiscovery.Scan.Status.waiting | Number | Number of entities in waiting status |
| BmcDiscovery.Scan.Status.uri | String | Scan URI |
| BmcDiscovery.Scan.Status.inferred | String | Scan inferred URI |
| BmcDiscovery.Scan.Status.results | String | Scan results URI |
| BmcDiscovery.Scan.Status.consolidating | Boolean | Is consolidating |
| BmcDiscovery.Scan.Status.consolidation_source | String | Consolidation source |
bmc-discovery-scan-create
Create a new snapshot scan (run)
Base Command
bmc-discovery-scan-create
Input
| Argument Name | Description | Required |
|---|---|---|
| label | Scan (run) name | Yes |
| ranges | IP range | Yes |
Note: The following run parameters are predefined:
- Kind = IP
- Level = Full Discovery
- Type = Snapshot
Context Output
| Path | Type | Description |
|---|---|---|
| BmcDiscovery.Scan.Create.url | String | New scan URI |
| BmcDiscovery.Scan.Create.uuid | String | New scan UUID |
bmc-discovery-scan-summary
Retrieve scan (run) results summary
Base Command
bmc-discovery-scan-summary
Input
| Argument Name | Description | Required |
|---|---|---|
| run_id | An ID of the scan | Yes |
Note: The following run parameters are predefined:
- Kind = IP
- Level = Full Discovery
- Type = Snapshot
Context Output
| Path | Type | Description |
|---|---|---|
| BmcDiscovery.Scan.Summary.Success | Number | Number of successfully scanned entities |
| BmcDiscovery.Scan.Summary.Skipped | Number | Number of skipped entities |
| BmcDiscovery.Scan.Summary.NoAccess | Number | Number of entities with no access |
| BmcDiscovery.Scan.Summary.NoResponse | Number | Number of entities with no response |
| BmcDiscovery.Scan.Summary.Error | Number | Number of entities in error |
| BmcDiscovery.Scan.Summary.Dropped | Number | Number of dropped entities |
bmc-discovery-scan-stop
Cancel a currently running scan
Base Command
bmc-discovery-scan-stop
Input
| Argument Name | Description | Required |
|---|---|---|
| run_id | An ID of the scan | Yes |
Note: The following run parameters are predefined:
- Kind = IP
- Level = Full Discovery
- Type = Snapshot
Context Output
| Path | Type | Description |
|---|---|---|
| BmcDiscovery.Scan.Stop.cancelled | Boolean | Scan cancel status |
bmc-discovery-scan-results-list
Get a list of hosts by specific result type
Base Command
bmc-discovery-scan-results-list
Input
| Argument Name | Description | Required |
|---|---|---|
| run_id | Yes | |
| result_type | “Available options: Success, Skipped, NoAccess, NoResponse, Error, Dropped” | Yes |
| offset | Search results offset | Optional |
| limit | Search results limit | Optional |
| results_id | Search results id | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| BmcDiscovery.Scan.Result.count | Number | Number of hosts of the result type |
| BmcDiscovery.Scan.Result.kind | String | Result kind type |
| BmcDiscovery.Scan.Result.next_offset | Number | Next offset to be used |
| BmcDiscovery.Scan.Result.offset | Number | Current offset value |
| BmcDiscovery.Scan.Result.results_id | String | Current result id |
| BmcDiscovery.Scan.Result.results | Unknown | The actual scan result data |
bmc-discovery-search
Search for a node by IP address or hostname
Base Command
bmc-discovery-search
Input
| Argument name | Description | Required |
|---|---|---|
| ip | IP address | Optional |
| hostname | Hostname | Optional |
| kind | “Node kind (Host, NetWorkDevice, SNMPManagedDevice)” | Single Select |
| name | Search name | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| BmcDiscovery.Search.count | Number | Number of returned results |
| BmcDiscovery.Search.data | Unknown | Search results |
| BmcDiscovery.Search.name | String | Name of the search |
bmc-discovery-search-custom
Run a user defined query
Base Command
bmc-discovery-search-custom
Input
| Argument name | Description | Required |
|---|---|---|
| query | Full search query* | Yes |
| offset | Search results offset | Optional |
| limit | Search resuluts limit | Optional |
| results_id | Search results id | Optional |
*See the documentation for reference
Context Output
| Path | Type | Description |
|---|---|---|
| BmcDiscovery.Search.count | Number | Number of returned results |
| BmcDiscovery.Search.data | Unknown | Search results |
Configuration parameters
url— Server URL (required)api_token— (required)proxy— Use system proxy settingsinsecure— Trust any certificate (not secure)
Commands (7)
-
bmc-discovery-scan-createCreate a new Discovery scan (run) with pre-defined parameters (Kind: IP; Level: Full Discovery; Type: Snapshot)
-
bmc-discovery-scan-results-listGet detailed scan (run) results
-
bmc-discovery-scan-status-listGet status of all currently running scans (no arguments) or a specific scan (provide a run_id argument)
-
bmc-discovery-scan-stopStop a Discovery scan (run)
-
bmc-discovery-scan-summaryGet scan (run) results summary
-
bmc-discovery-searchSearch for a node by hostname or IP address. If not found, repeat the search with a different node kind.
-
bmc-discovery-search-customRun a user-defined search
import json import os import sys import demistomock as demisto import pytest from BMCDiscovery import ( Client, discovery_process_status_command, discovery_scan_create_command, discovery_scan_results_list_command, discovery_scan_status_list_command, discovery_scan_stop_command, discovery_scan_summary_command, discovery_search_command, discovery_search_custom_command, ) from CommonServerPython import * from pytest import raises # noqa: PT013 p = os.path.abspath(".") sys.path.insert(1, p) INTEGRATION_NAME = "BMCDiscovery" VALID_TOKEN = "NDpYU09BUjo6OnRMbmxsZXVGMkVIcjh6THJPMlRvZnMwZ3RTcVg1c21YZ0dFQjJjMHNEM2xYYVk0QS9aUjVJZzowLTcyNDgwYTNkZGNhYmY1YWYzZjQxZGQxZjkwMjg0NmQwNmU4ZDZjZGFhZjE1MWNkODc5YWIyMTc0OGIwZTY2YjM=" # noqa: E501 INVALID_TOKEN = "95884261de2415f969ab47a06e486f7374" URL = "http://fake-bmc-api.com" def util_load_json(path): with open(path, encoding="utf-8") as f: return json.loads(f.read()) @pytest.fixture(autouse=True) def handle_calling_context(mocker): mocker.patch.object(demisto, "callingContext", {"context": {"IntegrationName": "BMC Discovery"}}) def mock_client(mocker, http_request_result=None): client = Client(url=URL, verify=False, proxy=False, api_token=VALID_TOKEN) if http_request_result: mocker.patch.object(client, "_http_request", return_value=http_request_result) return client client = mock_client(Client) def test_discovery_process_status_command(mocker): mocker.patch.object(client, "discovery_process_status", return_value=util_load_json("test_data/discovery_process_resp.json")) results = discovery_process_status_command(client) assert results.raw_response.get("status") == "running" def test_discovery_search_custom_command(mocker): mocker.patch.object( client, "discovery_search_custom", return_value=util_load_json("test_data/discovery_search_custom_resp.json") ) query = "SEARCH FLAGS(no_segment) DeviceInfo WHERE #DiscoveryResult:DiscoveryAccessResult:DiscoveryAccess:DiscoveryAccess._last_marker TRAVERSE Primary:Inference:InferredElement: SHOW kind(#), name, #InferredElement:Inference:Associate:DiscoveryAccess.endpoint" # noqa: E501 results = discovery_search_custom_command(client, query=query) assert results.outputs.get("data")[0].get("count") == 326 def test_discovery_scan_status_list_command_by_id(mocker): mocker.patch.object( client, "discovery_scan_status_list", return_value=util_load_json("test_data/discovery_scan_status_list_id_resp.json") ) run_id = "f5ac176243062b2b14c1a75a6e446973636f7665727952756e" results = discovery_scan_status_list_command(client, run_id=run_id) assert results.outputs[0].get("label") == "Sample_run_16" def test_discovery_scan_status_list_command_single(mocker): mocker.patch.object( client, "discovery_scan_status_list", return_value=util_load_json("test_data/discovery_scan_status_list_single_resp.json") ) results = discovery_scan_status_list_command(client) assert results.outputs[0].get("valid_ranges") == "192.168.0.0/16" def test_discovery_scan_status_list_command_multiple(mocker): mocker.patch.object( client, "discovery_scan_status_list", return_value=util_load_json("test_data/discovery_scan_status_list_multi_resp.json") ) results = discovery_scan_status_list_command(client) assert len(results.outputs) == 2 def test_discovery_scan_create_command(mocker): mocker.patch.object(client, "discovery_scan_create", return_value=util_load_json("test_data/discovery_scan_create_resp.json")) label = "Sample_XSOAR_scan" ranges = "10.11.6.0/24" results = discovery_scan_create_command(client, label=label, ranges=ranges) assert results.outputs.get("uuid") == "7e1c02625b07bf307ca81c796e446973636f7665727952756e" def test_discovery_scan_stop_command(mocker): mocker.patch.object(client, "discovery_scan_stop", return_value="true") run_id = "f5ac176243062b2b14c1a75a6e446973636f7665727952756e" results = discovery_scan_stop_command(client, run_id=run_id) assert results.outputs == "true" def test_discovery_scan_summary_command_single(mocker): mocker.patch.object( client, "discovery_scan_summary", return_value=util_load_json("test_data/discovery_scan_summary_single_resp.json") ) run_id = "f5ac176243062b2b14c1a75a6e446973636f7665727952756e" results = discovery_scan_summary_command(client, run_id=run_id) assert results.outputs["Dropped"] == 26879 def test_discovery_scan_summary_command_multiple(mocker): mocker.patch.object( client, "discovery_scan_summary", return_value=util_load_json("test_data/discovery_scan_summary_multi_resp.json") ) run_id = "f5ac176243062b2b14c1a7a46e446973636f7665727952756e" results = discovery_scan_summary_command(client, run_id=run_id) assert results.outputs["Success"] == 13 assert results.outputs["Dropped"] == 1318643 def test_discovery_scan_results_list_command_success(mocker): mocker.patch.object( client, "discovery_scan_results_list", return_value=util_load_json("test_data/discovery_scan_results_list_success_resp.json"), ) run_id = "f5ac176243062b2b14c1a75a6e446973636f7665727952756e" result_type = "Success" results = discovery_scan_results_list_command(client, run_id=run_id, result_type=result_type) assert results.outputs[0].get("kind") == "DiscoveryAccess" def test_discovery_scan_results_list_command_noresponse(mocker): mocker.patch.object( client, "discovery_scan_results_list", return_value=util_load_json("test_data/discovery_scan_results_list_empty_resp.json"), ) run_id = "f5ac176243062b2b14c1a75a6e446973636f7665727952756e" result_type = "NoResponse" results = discovery_scan_results_list_command(client, run_id=run_id, result_type=result_type) assert results.outputs[0].get("count") == 0 def test_discovery_scan_results_list_command_dropped(mocker): mocker.patch.object( client, "discovery_scan_results_list", return_value=util_load_json("test_data/discovery_scan_results_list_dropped_resp.json"), ) run_id = "f5ac176243062b2b14c1a75a6e446973636f7665727952756e" result_type = "Dropped" limit = 5 results = discovery_scan_results_list_command(client, run_id=run_id, result_type=result_type, limit=limit) assert results.outputs[0].get("next_offset") == 5 def test_discovery_search_command_ip_success(mocker): mocker.patch.object( client, "discovery_search", return_value=util_load_json("test_data/discovery_search_single_success_192.168.11.1_resp.json"), ) kind = "Host" ip = "192.168.11.1" results = discovery_search_command(client, kind=kind, ip=ip) assert results.outputs.get("data")[0].get("hostname") == "agent-id-pcfdev-0" def test_discovery_search_command_ip_fail(mocker): mocker.patch.object( client, "discovery_search", return_value=util_load_json("test_data/discovery_search_fail_192.168.11.1_resp.json") ) kind = "SNMPManagedDevice" ip = "192.168.11.1" results = discovery_search_command(client, kind=kind, ip=ip) assert results.outputs.get("count") == 0 def test_discovery_search_command_hostname_multiple(mocker): mocker.patch.object( client, "discovery_search", return_value=util_load_json("test_data/discovery_search_success_multi_ais-blade_resp.json") ) kind = "Host" hostname = "ais-blade" results = discovery_search_command(client, kind=kind, hostname=hostname) assert results.outputs["data"][1]["local_fqdn"] == "ais-bladedr-02.calbro.com" def test_discovery_search_custom_command_empty_query_exception(): with raises(DemistoException, match="Please specify query parameter"): discovery_search_custom_command(client) def test_discovery_search_command_no_ip_exception(): with raises(DemistoException, match="Please specify ip or hostname parameter"): discovery_search_command(client) def test_discovery_scan_results_list_command_no_id_exception(): with raises(DemistoException, match="Please specify run_id parameter"): discovery_scan_results_list_command(client) def test_discovery_search_custom_command_no_response(mocker): mocker.patch.object(client, "discovery_search_custom", return_value=None) with raises(DemistoException, match="Search command failed"): discovery_search_custom_command(client) def test_discovery_search_command_no_response(mocker): mocker.patch.object(client, "discovery_search", return_value=None) with raises(DemistoException, match="Search command failed"): discovery_search_command(client) def test_discovery_process_status_command_no_response(mocker): mocker.patch.object(client, "discovery_process_status", return_value=None) with raises(DemistoException, match="Get status failed"): discovery_process_status_command(client) def test_discovery_scan_status_list_command_no_response(mocker): mocker.patch.object(client, "discovery_scan_status_list", return_value=None) with raises(DemistoException, match="Get runs failed"): discovery_scan_status_list_command(client) def test_discovery_scan_summary_command_no_response(mocker): mocker.patch.object(client, "discovery_scan_summary", return_value=None) with raises(DemistoException, match="Failed to get scan summary"): discovery_scan_summary_command(client) def test_discovery_scan_results_list_command_no_response(mocker): mocker.patch.object(client, "discovery_scan_results_list", return_value=None) with raises(DemistoException, match="Failed to get scan results"): discovery_scan_results_list_command(client) def test_discovery_search_command_ip_host_exception(): with raises(DemistoException, match="ip and hostname are mutually exclusive. Please specify just one parameter"): discovery_search_command(client, ip="192.168.1.1", hostname="host.acme.com") def test_discovery_search_command_invalid_ip_exception(): with raises(DemistoException, match="Specified ip address doesn't look valid"): discovery_search_command(client, ip="host.acme.com") def test_discovery_search_custom_command_invalid_offset_exception(): with raises(DemistoException, match='"offset" cannot be specified without "results_id"'): discovery_search_custom_command(client, query="SEARCH Host show *", offset=100)