Playbook automation API
Splunk Phantom's Automation API allows security operations teams to develop detailed and precise automation strategies. Playbooks can serve many purposes, ranging from automating minimal investigative tasks that can speed up analysis to large-scale response to a security breach. The following APIs are supported to leverage the capabilities of the platform using Playbooks.
act
phantom.act(action, parameters=[], assets=None, tags=None, callback=None, reviewer=None, handle=None, start_time=None, name=None, asset_type=None, app=None)
phantom.act() call can be called from on_start() or any callback of any phantom.act() call. If multiple phantom.act() calls are called within the same function they will execute actions in parallel. Each action can take indefinite amount of time depending on what the action is. If the action is being executed on an asset that has primary approvers assigned then the action will generate approval requests and untill they are approved, the action will not be executed. Similarly if reviewer is specified, the action will not be executed unless the reviewer approves the action. Or the action can simply take a long time like detonation on a sandbox.
Parameter | Required? | Description |
---|---|---|
action | Required | The name of the action that the user intends to execute. These are actions like 'block ip', 'list vms', 'file reputation', etc. that are supported by the Apps installed on the Platform. The Platform can be used to carry out these actions generically across all Assets with the compatible Apps. Since Apps and Assets can be dynamically added to the system, the actions can automatically extend to these new Assets. The behavior can be more specific if the user specifies on which specific asset the action should be executed. |
parameters | Optional | A list of dictionaries that contain the parameters expected by the action. The name of the keys are specific to the action being taken and can be found in the Phantom App Reference Guide section of this documentation. |
assets | Optional | A list of Assets on which the action is to be executed. If the user intends to take the action on a specific asset, it must be specified in this parameter. This is a list of Asset IDs, as specified when an Asset is configured. If the assets have been configured with primary and secondary owners, they will be required to approve an action before it can be executed. Asset owners have the option to approve the action, alter the action parameters, delegate to other users, or deny the action.
If the asset is not specified, the action will be executed on ALL possible Assets on which the action can be executed. This is useful in case of the scenario where the user wants to take an action broadly across all applicable Assets, for example, if the user intends to 'block ip' across all firewall Assets, then the user can skip specifying this parameter and Phantom platform will block the IP on all firewalls. Also, Playbooks may be designed, tested and deployed at any point in time. Later on, if new Assets and Apps are added that support the same action, and if the action did not specify the Assets, actions will automatically be executed across the newly added Assets as well. If there are multiple Apps providing the same action for the same product, the system automatically uses the latest installed App. While not deliberately specifying Assets is very powerful, it introduces an obvious risk. If new Assets or new Apps are added to the Phantom Platform, they may execute actions that you as the Playbook author had not intended. For example, if you begin your Phantom deployment with a simple network-based topology and configure a perimeter Firewall that supports block ip, and then add an Active Directory server which has an associated App that also understands block ip, then that action will be executed on both the firewall and AD server. Setting appropriate Approvals on Assets can help to minimize this risk. |- | tags || Optional || A list of asset tags which can help specify certain assets to be used for executing the action. Assets can be assigned a 'tag' when they are configured. For example if the asset is tagged critical and the Action is block ip, the action will only be executed on Assets that have been tagged as critical. If tags and assets are both specified then the action is only executed on assets tagged with the matching tag. |
callback | Optional | The parameter where the user can specify a callback function (defined in the same Playbook) to be called upon completion of the action. Here the user has an opportunity to evaluate the outcome of one action and then take more actions. The facility can be used to either serialize actions where the user intends to execute the action one after the other or the subsequent action is dependent on the outcome or results of this one. The prototype of the callback function is specified below. |
reviewer | Optional | A username, email address or group that will receive an approval request to review the action before it can be executed. The user receives an approval request with all of the details of the action and its parameters. If provided a comma separated list, or group, only one approval by any member of the list is required. SLA escalation settings affect how long the action will be held for approval. The approval request when using this parameter precedes the approvals generated by the Asset's configured primary and secondary approvers. For example: A response analyst reviews actions prior to the Asset owners. |
handle | Optional | A string object that when specified is passed on to the callback. Users can save any Python object like a list, or dictionary that the user needs to access in the context of the callback from the action he had called. Handle is always saved with the action and passed to the callback.
We recommend using handles to pass objects from action to callbacks instead of using global variables. Global variables may or may not be accessible as actions can take an arbitrary period of time and may not be present across system or platform component restarts Size of 'handle' object is limited to 4k. For objects bigger than 4k, please use save_data() and get_data() APIs instead. |
start_time | Optional | The time in the future when the action should be scheduled for execution. For example 10 mins from now or at a specific time like Jan 01, 2020, 10:00 AM. This value is a datetime object. 'act_scheduled' playbook in the api category shows the usage of this parameter.
params=[] params.append({'ip':'1.1.1.1'}) # schedule 'geolocate ip' 60 seconds from now when = datetime.now()+timedelta(seconds=60) phantom.act("geolocate ip", parameters=params, start_time=when) |
name | Optional | A name the user can give to a instance of an action that gets executed. |
asset_type | Optional | can be specified to limit the action on assets of the specified type. This can be a string or a list of strings. For example, asset_type = 'endpoint' or asset_type=['firewall','endpoint']. |
app | Optional | Can be specified as the specific app which should be used to execute the action. App should be specified as a Python dictionary as {"name":"some_app_name", "version":"x.x.x"}. "name" is case insensitive. The names and versions of the app can be found on the Apps page of the product.
app_data={} app_data['name']='MaxMind' app_data['version']='1.1.0' phantom.act('geolocate ip', parameters=[{ "ip" : "1.1.1.1" }], assets=["maxmind"], callback=geolocate_ip_cb, app=app_data) or phantom.act('geolocate ip', parameters=[{ "ip" : "1.1.1.1" }], assets=["maxmind"], callback=geolocate_ip_cb, app={'name':'MaxMind', 'version':'1.1.0' }) |
Here is a sample playbook that uses the phantom.act() API
import phantom.rules as phantom import json def geolocate_ip_cb(action, success, container, results, handle): phantom.debug(results) if not success: phantom.debug('Action '+json.dumps(action)+' : FAILED') return return def on_start(container): ips = set(phantom.collect(container, 'artifact:*.cef.sourceAddress')) parameters = [] for ip in ips: parameters.append({ "ip" : ip }) if parameters: phantom.act('geolocate ip', parameters=parameters, assets=["maxmind"], callback=geolocate_ip_cb) return def on_finish(container, summary): return
callback
Callback functions are specified as parameters in phantom.act():
phantom.act('geolocate ip', parameters=parameters, assets=["maxmind"], callback=geolocate_ip_cb)
NOTE: Callback functions are called when the phantom.act() action has completed, irrespective of action success or failure.
The phantom.act API takes a callback function, which should accept the following keyword arguments: action, success, container, results, and handle. Each of these parameters is discussed in greater detail in the table below.
The simplest no-op callback would look like this:
def do_nothing(action=None, success=None, container=None, results=None, handle=None): pass
Such a callback would be functionally equivalent to not providing a callback parameter to phantom.act at all.
Parameter | Description |
---|---|
action | This is a JSON object that specifies the action name and the action run id. The action run id, uniquely identifies that instance of action execution and can be used to fetch the details of the action execution any time from anywhere. Here is an example of action parameter from a callback:
{ "action_run_id": 205, "action_name": "whois ip", "action" : "whois ip", "name": "my_whois_ip_action" } The 'action' field replaces the 'action_name' field. The 'action_name' field will be removed in a future release. success Either true or false. An action is considered failed only if the action has failed on all assets and with all specified parameters. If any part of the action succeeds, it is not considered failed. Use the utility functions like parse_errors() or parse_success() to get a flat listing of all errors and success. These utility functions simply parse the results to give the user a different view of the overall results. |
container | This is the container JSON object. Here is an example of container parameter from a callback:
{ "sensitivity": "amber", "create_time": "2016-01-14 18:25:55.921199+00", "owner": "admin", "id": 7, "close_time": "", "severity": "medium", "label": "incident", "due_time": "2016-01-15 06:24:00+00", "version": "1", "current_rule_run_id": 1, "status": "open", "owner_name": "", "hash": "093d1d4d22cab1c5931bbfb1b16ce12c", "description": "this is my test incident", "tags": ["red-network"], "start_time": "2016-01-14 18:25:55.926468+00", "asset_name": "", "artifact_update_time": "2016-01-14 18:26:33.55643+00", "container_update_time": "2016-01-14 18:28:43.859814+00", "kill_chain": "", "name": "test", "ingest_app_id": "", "source_data_identifier": "48e4ab9c-2ec1-44a5-9d05-4e83bec05f87", "end_time": "", "artifact_count": 1 } |
results | This is a JSON object that has the full details and status of the complete action on all assets for each parameter. Here is an example where the 'geolocate ip' action is being executed:
parameters = [] parameters.append({ "ip" : '1.1.1.1' }) phantom.act('geolocate ip', parameters=parameters, assets=["maxmind"], callback=geolocate_ip_cb, name='my_geolocate_action') This simple action can result in various execution strategies and outcomes, depending on how the system is configured. In the most simplistic form, there is one app that supports the geolocate ip action and there is one 'Maxmind' asset that is configured which results in one ip being queried once on one asset. In a more complex example, if there are 2 Apps, both of which support file reputation (for example VirusTotal and ReversingLabs, and 2 Assets (one for VirusTotal and one for ReversingLabs), then this one simple action will result in a file hash being queried on both the assets. The single action file reputation will result in the hash being queried twice, once on each asset. The two queries still constitute one action, therefore the callback 'file_reputation_cb' will be called only once when both the queries have completed. The parameters as seen in the above example is a list of dictionaries and contains one ip. However, in one action call the user can specify many different ips. These parameters are the original input parameters. For actions like prompt, tasks, approvals, and reviews these parameters include and therefore allow a user to see who the intended user was for the action. The Here are the results of the above geolocate ip action (Single parameter, single asset, single app): [ { "asset_id": 63, "status": "success", "name": "geolocate_ip_1", "app": "MaxMind", "action_results": [ { "status": "success", "data": [ { "state_name": "Victoria", "latitude": -37.7, "country_iso_code": "AU", "time_zone": "Australia/Melbourne", "longitude": 145.1833, "state_iso_code": "VIC", "city_name": "Research", "country_name": "Australia", "continent_name": "Oceania", "postal_code": "3095" } ], "message": "City: Research, State: VIC, Country: Australia", "parameter": { "ip": "1.1.1.1", "context": { "guid": "f42fd73f-...-8194aaa9bc11", "artifact_id": 0, "parent_action_run": [] } }, "summary": { "city": "Research", "state": "VIC", "country": "Australia" } } ], "app_id": 83, "summary": { "total_objects": 1, "total_objects_successful": 1 }, "asset": "maxmind", "action": "geolocate ip", "message": "'geolocate_ip_1' on asset 'maxmind': 1 action succeeded. (1)For Parameter: {\"ip\":\"1.1.1.1\"} Message: \"City: Research, State: VIC, Country: Australia\"", "app_run_id": 51, "action_run_id": 11 } ] |
handle | A string object that was specified in the action phantom.act() call for passing data between action and callbacks. Please see the description of this parameter in the phantom.act() call. |
condition
phantom.condition(container=None, action_results=None, conditions=[], logical_operator='or', scope='new', filtered_artifacts=[], filtered_results=[], limit=100, name=None, trace=False, case_sensitive=True)
This API implements the decision block in the visual playbook editor. It evaluates expressions and returns matching artifacts and actions results that evaluate as true. The code for this API is autogenerated via the use of condition blocks on the new 2.0 Playbook editor.
Parameter | Required? | Description |
---|---|---|
container | Required | This is the container dictionary object that is passed into the calling function. |
action_results | Required | These are the action results passed into any callback function or a subset of action results that had been filtered from a phantom.condition() call. |
conditions | Required | Conditions are a list of one or more 'and' or 'or' expressions that are to be evaulated. Matching artifacts or matching action results are returned.
Expression format: [ [ LHS, OPERATOR, RHS ], [ LHS, OPERATOR, RHS ], ..] OPERATOR can be any one of the following: ==, !=, <, >, <=, >=, in, not in LHS/RHS values can be a value, artifact datapath, or action result datapath. Please see below for the various types and examples of datapath that can be used. |
logical_operator | Optional | Valid logical operators are 'and' or 'or' and expresses the relationship between conditions. Defaults to 'or'. |
scope | Optional | Please refer to the phantom.collect() API documentation. |
filtered_artifacts | Optional | Filtered artifacts that were returned from an immediately preceding phantom.condition() block. |
filtered_results | Optional | Filtered results that were returned from an immediately preceding phantom.condition() block. |
limit | Optional | Please refer to phantom.collect() API documentation. |
name | Optional | Specify a unique name to save the filtered results (filtered action results and filtered artifacts) which can then be later retrieved using either collect2() API or phantom.get_filtered_data() API. |
trace | Optional | Set this parameter to 'True' for verbose debugging of the API call. |
case_sensitive | Optional | Default is 'True'. Set to 'False' for evaluating conditions in a case-insensitive manner. |
phantom.condition() returns a list of artifact ids and a list of action result objects. These are the artifacts and actions results that matched the conditions expressed. If there is no filter statement specified that is about action results there will be no filtered action results returned and the playbook editor UI will not show that as a selectable option in subsequent blocks.
When using the Playbook Editor, users can select to connect various UI blocks. Each of these blocks implements a function in the auto generated Python code. These functions have various parameters like container, results, filtered_artifacts and filtered_results. The expressions used for the conditions can be either a constant or a datapath that allows the users to specify what they need to fetch and operate on. These datapaths could point to either a field in the artifact, action_result, filtered-artifacts, filtered results or a constant.
Here are the various examples of the datapaths
Datapath | Description |
---|---|
artifact: | This datapath type refers to a field in the artifact. These datapaths begin with "artifact:" For example: "artifact:*.cef.bytesIn" |
action-name:artifact: | This datapath type refers to a field in the artifact whose one or more CEF fields were used as an input parameter of an action. "geolocate_ip_1:artifact:*.cef.bytesIn" In the phantom.act() API, when specifying parameters, the auto-generated code (or users) uses context which is a hidden parameter that helps identify which artifact was used to execute the action such that users can access it from the results and this access fields of this artifact for subsequent actions or decision making. context is a means to attach a "reference" to the parameter that can then later be retrieved from the results. Each instance of a context has a unique GUID and optionally parent_action that refers to the action that launched this action. Hence "geolocate_ip_1:artifact" implies the artifact_id whose field was used as an input parameter to a particular action. |
filtered-artifact: | This datapath type refers to a field in the artifact that was filtered from a previous decision block. Example: "filtered-artifact:*.cef.bytesIn" |
action_result. | This datapath type refers to a field in the action result object. Example: "action_result.data.*.latitude". When this type of datapath is used, the condition API expects the action_results parameter to provide the action result list object. This is the same object that is passed into the function via the "results" parameter or obtained via the "phantom.get_action_results()" API. |
action_name:action_result. | This datapath type refers to a field in the action result object of an action called with a name parameter. This can be any of the actions that may have been executed in the path that leads to this action. For example: "geolocate_ip_1:action_result.data.*.latitude" thus the datapath "action_result.data.*.latitude" is to be used to extract data from the action named "geolocate_ip_1" |
action_name:filtered-action_result. | This datapath type refers to a field in the action result object of a named action that has been filtered. As mentioned earlier, phantom.condition() statement returns those action_result objects or artifact ids that match the condition. When this type of datapath is used it requires that the filtered results be provided as the parameter. For example: "geolocate_ip_1:filtered-action_result.data.*.latitude" thus the datapath "action_result.data.*.latitude" is to be used to extract data from the previous conditions filtered named "geolocate_ip_1"s filtered results |
Generally, CEF fields that are passed into phantom.condition, if they have commas in the value, will cause the value to be treated as a list. One specific exception that has been defined is that if the CEF field is "toEmail" or "fromEmail", commas will not trigger the list behavior. This is because commas frequently appear in the displayname portion of an email address.
If two strings which can be converted to a numeric type are being compared with one of the following operators, they will be converted to numeric types before the comparison occurs.
==, !=, <, >, <=, >=
format
phantom.format(container=None, template=None, parameters=None, scope='new', name=None, trace=False, separator=None, drop_none=False):
This is an API supported for the purposes of formatting text with values that are extracted using datapaths for other complex objects like artifacts or action results.
Parameter | Required? | Description |
---|---|---|
container | The container object passed into the action callback or on_start or any other method. | |
template | The format string where positional arguments are substituted with their values. The arguments are expressed and passed as list of datapaths in the 'parameters' argument. If the datapath returns a 'list' of items, the positional argument is replaced by a comma separated value of the items. The format string uses positional arguments same as Python string formatting. {0} for first argument, {1} for second etc. | |
parameters | This is a list of datapaths with a datapath for each positional format argument used in the template string. | |
scope | Optional | For this parameter, refer to phantom.collect() API. The value of this API can be 'all' or 'new'. Default value is 'new'. |
name | Optional | This parameter is the name used to save the resulting formatted data, to be later retrieved via get_format_data() API. Internally this API uses save_run_data() API to save the formatted data in the context of playbook run. If this parameter is not specified, the data is not saved. |
trace | Optional | This boolean parameter is used for detailed debugging. Default value is False. |
separator | Optional | If a datapath response contains a list (ie, strings or numbers, but not python objects), the default output separator is ', '. Specify an alternate separator via this parameter. |
drop_none | Optional | Default value for this boolean parameter is False. By default None values are included in the resulting string, but the user can filter NoneType values in the resulting string via this parameter. |
Example:
def on_start(container): template = "Host '{0}' transferred in '{1}' bytes" datapaths = ['artifact:*.cef.sourceAddress','artifact:*.cef.bytesIn'] formatted_data = phantom.format(container=container, template=template, parameters=datapaths, name='formatted_1') phantom.debug("Formatted data is: {}".format(formatted_data)) # use the same name that was provided for key in phantom.format() formatted_data = phantom.get_format_data(name="format_1") phantom.debug("Retrieved formatted data is: {}".format(formatted_data)) return
Sample result:
Starting playbook 'format_test' on 'incident' id '13' with playbook run id '7'. calling on_start() on incident 'test_incident'(id: 13). phantom.collect2(): called for datapath['artifact:*.cef.sourceAddress'] phantom.collect2(): called for datapath['artifact:*.cef.bytesIn'] save_run_data() called Formatted data is: Host '1.1.1.1' transferred in '999' bytes get_run_data() called Retrieved formatted data is: Host '1.1.1.1' transferred in '999' bytes No actions were executed calling on_finish() Playbook 'format_test' (playbook id: 6) executed (playbook run id: 7) on incident 'test_incident'(container id: 13). Playbook execution status is 'success' Total actions executed: 0 {"message":"No actions were executed","playbook_run_id":7,"result":[], "status":"success"}
In the above example, if there are multiple artifacts artifact:*.cef.sourceAddress effectively is referencing a list of IPs. If you use this same format block as above, and there are multiple artifacts in the container, the output would look something like this:
Retrieved formatted data is: Host '1.1.1.1', '8.8.8.8', '8.8.4.4' transferred in '999', '888', '777' bytes
It may be more desirirable for each pair of sourceAddress and bytesIn to have their own line in this output. By wrapping sections of the format text in '%%' you can accomplish this behavior. We could change the above Python code to the following:
def on_start(container): template = """\ %% Host '{0}' transferred in '{1}' bytes" %%""" datapaths = ['artifact:*.cef.sourceAddress','artifact:*.cef.bytesIn'] formatted_data = phantom.format(container=container, template=template, parameters=datapaths, name='formatted_1') phantom.debug("Formatted data is: {}".format(formatted_data)) # use the same name that was provided for key in phantom.format() formatted_data = phantom.get_format_data(name="format_1") phantom.debug("Retrieved formatted data is: {}".format(formatted_data)) return
This will create the following string:
Retrieved formatted data is: Host '1.1.1.1' transferred in '999' bytes Host '8.8.8.8' transferred in '888' bytes Host '8.8.4.4' transferred in '777' bytes
When creating this string, each section will be saved into a list. When you are using the VPE, you can get this list for the format block, instead of the string, and then each item in this list will be passed a parameter to the action.
def on_start(container): template = """\ %% Host '{0}' transferred in '{1}' bytes" %%""" datapaths = ['artifact:*.cef.sourceAddress','artifact:*.cef.bytesIn'] # formatted_data will always be a string formatted_data = phantom.format(container=container, template=template, parameters=datapaths, name='formatted_1') # Add __as_list to the end of the name to retrieve the list instead of the string formatted_data_list = phantom.get_format_data(name="format_1__as_list") return
render_template
phantom.render_template(template, context)
This API accepts a Django 1.11 template and fills the variable fields with contexual information from a provided dictionary. Common uses of the template are for user prompts or case management updates. Additional information about Django 1.11 templates can be found on the Django Project homepage at https://docs.djangoproject.com/en/1.11/ref/templates/language/
Parameter | Required? | Description |
---|---|---|
template | Required | The Django 1.11 template. |
context | Required | Dictionary of values used to populate variable fields in the Django template. |
The following is an example code snippet that demonstrates the addition and population of a template in a playbook.
phantom.render_template( "<html> <head> <title>Report for {{ report_name }}</title> </head> <body>Hi {{ subject }}, here are a list of IPs you should look at! <ul>{% for ip in ip_list %} <li>{{ ip }}</li> {% endfor %} </ul> </body> </html>", { 'report_name': 'Task for {}: {}'.format(container['id'], container['name']), 'subject': container['owner_name'], 'ip_list': ips_affected } )
playbook
phantom.playbook(playbook=None, container=None, handle=None, show_debug=False, callback=None, inherit_scope=True, name=None)
This is an API that allows the users to call another playbook from within the current playbook. If there are 2 or more playbooks by the same name from different repos, the call will fail. In such cases, the playbook name should be very specific i.e. repo_name/playbook_name.
It returns the playbook_run_id that can be used to query corresponding playbook execution details / report.
Parameter | Required? | Description |
---|---|---|
playbook | Required | The playbook name that needs to be executed. It should follow the format "repo name/playbook name" to avoid ambiguity of which playbook to run from which repo. |
container | Required | The container json object that needs to be passed to execute the playbook on. This is the same container JSON object that you get in on_start() or any other callback function. |
handle | Optional | This is a object that you can pass to the API which will be passed back to the callback when the playbook finishes execution. |
show_debug | Optional | This parameter is defaulted to False, however if you set this to True, the debug messages of the launched/callee playbook will be shown in the debug window when you debug the parent/caller playbook. |
callback | Optional | If this parameter is specified, the playbook is launched in a synchronous fashion. When the callee (child) playbook finishes, the specified callback function is called with playbook execution results. When callee(child) playbooks are launched synchronously, the caller (parent) playbook is not considered completed till the called child playbook has finished executing. If this parameter is specified, you must also specify the name parameter.
|
inherit_scope | Optional | Default is True. This implies that the child playbook will inherit the scope settings from the parent when called. If set to false, the child playbook will run with default playbook scope i.e. 'new'. |
name | Required | This is an optional parameter unless the callback parameter is specified. This parameter uniquely identifies the execution instance of the called playbook. If the code for calling the child playbook is auto generated, the name of the function is the recommended value to be used for this parameter. |
Example:
""" This sample playbook shows calling a playbook from a playbook """ import json import phantom.rules as phantom def on_start(container): playbook_run_id = phantom.playbook(playbook='local/test1', container=container) return def on_finish(container, summary): return
prompt
phantom.prompt(user=None, message='', respond_in_mins=30, callback=None, name=None, options=None, parameters=None, container=None, scope='new', trace=False, separator=None, drop_none=False)
Parameter | Required? | Description |
---|---|---|
container | Required | The container object that is associated with the current playbook execution. This object is available to all action callbacks and other playbook execution functions like on_start(). |
user | Required | The recipient in the form of a user email address, username, or a role. Must be a valid user/role on Phantom. |
message | Required | The message content to send. |
respond_in_mins | Optional | The time the user is given to respond. If the user does not respond in the specified time, the prompt fails and the 'failed' status is sent to the callback. Default is 30 mins. |
callback | Optional | This has the same prototype as action callbacks. Status indicates success when the user has responded to the action and is failure only when the user does not respond in the specified time. The results JSON has the same format as any action results. Handle is not used and hence is an empty object. |
name | Optional | The name of the 'prompt' action to be able to distinguish various prompt blocks if there are more than one prompts used in the playbook. |
options | Optional | This parameter is a JSON dictionary. This allows the user response to display with programmed choices. See Option Parameter Examples below. |
parameters | Optional | A list of datapaths whose values are used to format the message. Recognized datapaths will be used to retrieve data, and the data will be used to populate the curly brackets in the message. The first parameter will replace {0}, the second will replace {1} and so on. |
scope | Optional | Scope can either be "new" or "all". Default value is 'new'. Please refer to phantom.collect() API for more details. |
trace | Optional | This parameter is used for detailed debugging. Default value is False. |
separator | Optional | If a datapath response contains a list (ie, strings or numbers, but not python objects), the default output separator is ', '. Specify an alternate separator via this parameter. |
drop_none | Optional | By default None values are included in the resulting string, but user can choose to not have None be included in the resulting string via this parameter. |
Using phantom.prompt() results in a message sent to the specified user or users.
- A notification is sent to the notifications bell icon in the upper right corner of the screen.
- If Splunk Phantom has been configured with an SMTP asset, and the specified user or user have valid email addresses in their account settings, the user or users are sent an email.
An email notification sent using phantom.prompt or phantom.prompt2 cannot be disabled by users by disabling notifications in their account settings.
Pending notifications can be accessed by clicking the bell icon in the top right corner of the UI. Delegation for approvals is possible. Either the user or delegated user can complete the task. When the task is completed, the prompt() callback is called with the final response in the action results.
The structure of the callback function and all the parameters is consistent with an action callback. Using prompt only allows the user to complete a task. The playbook can contain a callback function and use the prompt response, found in the result object in the callback, to change playbook behavior.
Example:
""" This sample playbook shows calling a phantom.prompt() from a playbook """ import phantom.rules as phantom import json def on_start(container): phantom.prompt(container=container, user="user@company.com", message="proceed with blocking these ips on FW?", respond_in_mins=10, callback=prompt_cb, options={ 'type': 'list', 'choices': ['yes', 'no', 'maybe'] }, name="prompt_to_block_ips") return def prompt_cb(action, success, container, results, handle): phantom.debug(results) return def on_finish(container, summary): return
The output of the playbook is as shown below:
Fri Apr 29 2016 19:38:25 GMT-0700 (PDT): Starting playbook 'manual_action' testing on 'incident' id: '215'... Fri Apr 29 2016 19:38:25 GMT-0700 (PDT): calling on_start(): on incident 'test', id: 215. Fri Apr 29 2016 19:38:25 GMT-0700 (PDT): phantom.act(): Warning: For action 'prompt' no assets were specified. The action shall execute on all assets the app (supporting the action) can be executed on Fri Apr 29 2016 19:38:25 GMT-0700 (PDT): phantom.act(): action 'prompt' shall be executed with parameters: '[{"to": "user@company.com", "message": "please make sure xyz is ok ...", "mins_to_act": 10}]', assets: '', callback function: 'prompt_cb', with no action approver, no delay to execute the action, no user provided name for the action, no tags, no asset type Fri Apr 29 2016 19:38:25 GMT-0700 (PDT): Request sent for action'automated action 'prompt' of 'manual_action' playbook' Fri Apr 29 2016 19:38:50 GMT-0700 (PDT): Manual action was completed by the user. User message: yes I am OK.. Fri Apr 29 2016 19:38:50 GMT-0700 (PDT): calling action callback function: prompt_cb Fri Apr 29 2016 19:38:50 GMT-0700 (PDT): [ { "asset_id": 0, "status": "success", "name": "prompt_to_block_ips", "app": "", "action_results": [ { "status": "success", "data": [ { "response": "maybe" } ], "message": "proceed with blocking these ips on FW?", "parameter": { "message": "proceed with blocking these ips on FW?" }, "summary": { "response": "maybe" } } ], "app_id": 0, "app_run_id": 0, "asset": "", "action": "prompt", "message": "1 action succeeded", "summary": {}, "action_run_id": 57 } ] Fri Apr 29 2016 19:38:50 GMT-0700 (PDT): successfully called action callback 'prompt_cb()' in rule: manual_action(id:182) Fri Apr 29 2016 19:38:50 GMT-0700 (PDT): calling on_finish() Fri Apr 29 2016 19:38:50 GMT-0700 (PDT): Playbook 'manual_action (id: 182)' executed (playbook_run_id: 195) on incident 'test'(id: 215). Playbook execution status is:'success' No actions were executed for this playbook and 'incident' Fri Apr 29 2016 19:38:50 GMT-0700 (PDT): *** Playbook execution has completed with status: SUCCESS *** Fri Apr 29 2016 19:38:51 GMT-0700 (PDT): Playbook execution report: {"message":"","playbook_run_id":195,"result":[{"action":"prompt","app_runs":null,"close_time":"2016-04-30T02:38:50.844839+00:00","create_time":"2016-04-30T02:38:25.731+00:00","id":156,"message":"yes I am OK.. ","name":"automated action 'prompt' of 'manual_action' playbook","status":"success","type":"manual"}],"status":"success"}
Option Parameter Examples
Type: list Shows the items in choices as the availabe responses. { 'type': 'list', 'choices': ['High', 'Medium', 'Low'] } Type: range Shows an input that requires a response within the given range of integers, i.e. 1-10. { 'type': 'range', 'min': 1, 'max': 100 } Type: message Shows a text area input in which a free form response can be entered. { 'type': 'message'}
prompt2
phantom.prompt2(user=None, message='', respond_in_mins=30, response_types=None, callback=None, name=None, parameters=None, container=None, scope='new', trace=False, separator=None, drop_none=False)
This API is similar to phantom.prompt, with some changes made to more easily allow creating a prompt with multiple user input fields. Notice the "options" parameter has been replaced by "response_types". The other parameters behave the same as phantom.prompt.
Parameter | Required? | Description |
---|---|---|
container | Required | The container object that is associated with the current playbook execution. This object is available to all action callbacks and other playbook execution functions like on_start(). |
user | Required | The recipient in the form of a user email address, username, or a role. Must be a valid user/role on Phantom.
message [required] the message content to send. |
response_types | Required | The list of JSON dictionaries describing each input field in the prompt which will be created. |
respond_in_mins | Optional | The time the user is given to respond. If the user does not respond in the specified time, the prompt fails and the 'failed' status is sent to the callback. Default is 30 mins. |
callback | Optional | This has the same prototype as action callbacks. Status indicates success when the user has responded to the action and is failure only when the user does not respond in the specified time. The results JSON has the same format as any action results. Handle is not used and hence is an empty object. |
name | Optional | This parameter allows you to name the 'prompt' action to be able to distinguish various prompt blocks if there are more than one prompts used in the playbook. |
parameters | Optional | A list of datapaths whose values are used to format the message. Recognized datapaths will be used to retrieve data, and the data will be used to populate the curly brackets in the message. The first parameter will replace {0}, the second will replace {1} and so on. |
scope | Optional | Scope can either be "new" or "all". Default value is 'new'. Please refer to phantom.collect() API for more details. |
trace | Optional | This parameter is used for detailed debugging. Default value is False. |
separator | Optional | If a datapath response contains a list (ie, strings or numbers, but not python objects), the default output separator is ', '. Specify an alternate separator via this parameter. |
drop_none | Optional | By default None values are included in the resulting string, but user can choose to not have None be included in the resulting string via this parameter. |
Response Types Example:
Response types is a list of JSON objects. Each object represents one input field in the prompt which will be created. Each object will need to have a value with the key 'prompt', and has an optional key, 'options'. 'prompt' is a message for that input field. 'options' is a dictionary with the same structure as a dictionary for the 'options' in phantom.prompt. No options being specified will result in a normal message type prompt.
[ {'prompt': 'Select a number in this range', 'options': {'type': 'range', 'min': 1, 'max': 50}}, {'prompt': 'Describe the event'} ]
The 'message' parameter is still used in phantom.prompt2. The message will be displayed at the top of the created prompt, above the input fields.
task
phantom.task(user=None, message=None, respond_in_mins=0, callback=None, name=None)
This API is a specialization of a manual action that can be used to ask a user or a role to do some work in the course of a response workflow or playbook
Parameter | Required? | Description |
---|---|---|
user | Required | The person or a role to whom the task is assigned |
message | Required | The text that has the information or details of the task to be performed. |
respond_in_mins | Required | The time given to the user to be able to perform the task, after which the task fails and the status is expressed in the callback if callback was specified. |
callback | Optional | A callback function to be called when the task completes i.e. succeeds, fails or expires. |
name | Required | A unique name for the user to be able to identify and distinguish this action from other actions |
error
phantom.error(message)
This is an API supported for the purposes of letting the author debug or print log messages as the Playbook is being executed with logging disabled. This is an equivalent of a print() statement. The parameter for the call is a 'string' type object and the contents are shown in the playbook debug console in red text color so that the user can distinguish his/her text from the system text.
Example:
def on_start(container): phantom.error('in on_start() of playbook') return
Response:
2016-02-13T01:32:52.583000+00:00: calling on_start(): on incident 'test', id: 107. 2016-02-13T01:32:52.608695+00:00: in on_start() of playbook
debug
phantom.debug(message)
This is an API supported for the purposes of letting the author debug as the Playbook is being developed and tested, when logging is enabled. This is an equivalent of a print() statement. The parameter for the call is a 'string' type object and the contents are shown in the debug console in cyan text color so that the user can distinguish his/her text from the system text.
Example:
def on_start(container): phantom.debug('in on_start() of playbook') return
Response:
2016-02-13T01:32:52.583000+00:00: calling on_start(): on incident 'test', id: 107. 2016-02-13T01:32:52.608695+00:00: in on_start() of playbook
discontinue
phantom.discontinue()
This is an API that allows the users to stop executing any more active playbooks when a container is being processed against "Active" playbooks.
Example:
def on_start(container): phantom.discontinue()
completed
phantom.completed(action_names=None, playbook_names=None, trace=False)
This API checks to see if all of the actions and child playbooks (synchronously launched) in the the given list of action_names and playbook names have finished executing or not. Succeeded or Failed implies 'Done'. Returns True if and only if all the actions and playbooks are done or else returns False. This API is used primarily in the join function where certain blocks are being executed in parallel but the next block has to be called only when all the joining blocks have completed executing.
Parameter | Required? | Description |
---|---|---|
action_names | Required | A list of names given to an action via phantom.act() API in the parameter 'name'. |
playbook_names | Required | A list of names given to a playbook execution via phantom.playbook() API in the parameter 'name'. |
Example:
def join_add_tag_1(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None): # this is a "join" function. It is a callback for 2 separate actions # check if all connected incoming actions are done i.e. have succeeded or failed # only invoke add_tag_1 when both have completed if phantom.completed(action_names=[ 'whois_ip_1'], playbook_names=['playbook_local_child_basic_1']): # call connected block "add_tag_1" add_tag_1(container=container, handle=handle) return
PREVIOUS Understanding apps and assets |
NEXT Container automation API |
This documentation applies to the following versions of Splunk® Phantom (Legacy): 4.8
Feedback submitted, thanks!