Tutorial: Chain a series of actions in
This topic demonstrates how to execute a series of actions to bind together many security operations tasks into a single playbook in . You execute a series of actions by incorporating new actions into the callback of another action.
You don't need to use callbacks to run multiple actions in parallel if those actions have no interdependency. Instead, invoke multiple act()
commands sequentially. However, if one action depends on the results of another action, the second action must be called from within the first action's callback.
Prerequisites
Before completing this tutorial, review the following topics to learn about callbacks and extracting data from a container:
Parse results
The results of an action are passed to the callback as the fourth parameter. The results contain an array of results that you can access as a JSON structure. You can run a single action on multiple assets, in which case a result exists for each asset.
More than one parameter might have been provided to the action, meaning there is an additional array of results with one result for each parameter. In that case, the result structure can be three levels deep. If you intend to parse results in your own code, you must iterate through all assets and all parameters to access them.
For example, a simple action that calls disable user with a single user produces the following results:
import phantom.rules as phantom import json def disable_user_cb(action, success, container, results, handle): phantom.debug(results) if not success: return return def on_start(incident): users = [{ "username" : "jason_malware" }] phantom.act('disable user', parameters=users, assets=["domainctrl1"], callback=disable_user_cb) return
[ { 'status': 'success', 'action_results': [ { 'status': 'success', 'data': [ ], 'message': 'Userstatechanged', 'parameter': { 'username': 'jason_malware' }, 'summary': { } } ], 'asset': 'domainctrl1', 'summary': { 'total_objects': 1, 'total_objects_successful': 1 } } ]
Calling this same Action on two users, with one invalid user name, produces an array of results:
import phantom.rules as phantom import json def disable_user_cb(action, success, container, results, handle): phantom.debug(results) if not success: return return def on_start(incident): users = [{ "username" : "jason_malware" }, { "username" : "fred_malware" }] phantom.act('disable user', parameters=users, assets=["domainctrl1"], callback=disable_user_cb) return
{ 'status': 'success', 'action_results': [ { 'status': 'success', 'data': [ ], 'message': 'Userstatesameasrequired', 'parameter': { 'username': 'jason_malware' }, 'summary': { } }, { 'status': 'failed', 'data': [ ], 'message': 'SearchonADforUserDNfailed', 'parameter': { 'username': 'fred_malware' }, 'summary': { } } ], 'asset': 'domainctrl1', 'summary': { 'total_objects': 2, 'total_objects_successful': 1 } }
Chain actions
If any an Action on any one Asset with any single parameter succeeds, then the overall Action is considered to have succeeded. Individual portions of that Action may have, however, failed. If we want to now operate on the results of the disable user call, we can iterate through the result structure and look for successful results, calling enable user on each disabled user:
import phantom.rules as phantom import json def enable_user_cb(action, success, container, results, handle): if not success: return for result in results: for user in result['action_results']: if 'status' in user and user['status'] == 'success': phantom.debug(user['parameter']['username'] + ' re-enabled') def disable_user_cb(action, success, container, results, handle): if not success: return for result in results: for user in result['action_results']: if 'status' in user and user['status'] == 'success': phantom.debug(user['parameter']['username'] + ' disabled') phantom.act('enable user', parameters=[{ "username" : user['parameter']['username'] }], assets=["domainctrl1"], callback=enable_user_cb) return def on_start(incident): users = [{ "username" : "jason_malware" }, { "username" : "fred_malware" }] phantom.act('disable user', parameters=users, assets=["domainctrl1"], callback=disable_user_cb) return
2015-03-21T00:29:59.131000: Processing incident: '4' [2a76c74c-5713-11e4-8a26-9b99986c1e2a] 2015-03-21T00:29:59.137000: act(): Action 'disable user' shall be executed on assets: domainctrl1 2015-03-21T00:29:59.137000: act(): action details: [disable user] parameters: [[{"username": "jason_malware"}, {"username": "fred_malware"}]] assets: [domainctrl1] callback function: [disable_user_cb] and NO user specified for reviewing params 2015-03-21T00:29:59.167000: act(): No action parameter review or asset approval requests generated. 2015-03-21T00:29:59.168000: Starting action 'disable user' on asset '6c9c1b21-c259-4382-88ce-957cf6bb7a8e' 2015-03-21T00:29:59.189000: running: The connector 'LDAP App' started successfully. Execution parameters sent. 2015-03-21T00:29:59.340000: running: Loaded action execution configuration 2015-03-21T00:29:59.367000: running: Ldap module initialized 2015-03-21T00:29:59.541000: running: Got Base DN = 'DC=corp,DC=contoso,DC=com' 2015-03-21T00:29:59.542000: running: Connecting to 10.10.0.42... 2015-03-21T00:29:59.628000: running: Got User Base DN CN=Jason Malware,CN=Users,DC=corp,DC=contoso,DC=com 2015-03-21T00:29:59.674000: running: Disabling user 2015-03-21T00:29:59.729000: running: Ldap module initialized 2015-03-21T00:29:59.883000: running: Got Base DN = 'DC=corp,DC=contoso,DC=com' 2015-03-21T00:29:59.884000: running: Connecting to 10.10.0.42... 2015-03-21T00:29:59.973000: success: 1 of 2 actions succeeded 2015-03-21T00:29:59.991000: Command 'disable user' success. 1 of 2 actions succeeded 2015-03-21T00:29:59.996000: calling action callback function: disable_user_cb 2015-03-21T00:30:00.814000: jason_malware disabled 2015-03-21T00:30:00.823000: act(): Action 'enable user' shall be executed on assets: domainctrl1 2015-03-21T00:30:00.824000: act(): action details: [enable user] parameters: [[{"username": "jason_malware"}]] assets: [domainctrl1] callback function: [enable_user_cb] and NO user specified for reviewing params 2015-03-21T00:30:00.854000: act(): No action parameter review or asset approval requests generated. 2015-03-21T00:30:00.855000: Starting action 'enable user' on asset '6c9c1b21-c259-4382-88ce-957cf6bb7a8e' 2015-03-21T00:30:00.871000: running: The connector 'LDAP App' started successfully. Execution parameters sent. 2015-03-21T00:30:01.014000: running: Loaded action execution configuration 2015-03-21T00:30:01.037000: running: Ldap module initialized 2015-03-21T00:30:01.222000: running: Got Base DN = 'DC=corp,DC=contoso,DC=com' 2015-03-21T00:30:01.224000: running: Connecting to 10.10.0.42... 2015-03-21T00:30:01.328000: running: Got User Base DN CN=Jason Malware,CN=Users,DC=corp,DC=contoso,DC=com 2015-03-21T00:30:01.396000: running: Enabling user 2015-03-21T00:30:01.441000: success: 1 of 1 action succeeded 2015-03-21T00:30:01.468000: Command 'enable user' success. 1 of 1 action succeeded 2015-03-21T00:30:01.473000: calling action callback function: enable_user_cb 2015-03-21T00:30:02.383000: jason_malware re-enabled *** The Rule has completed. Result: success ***
Tutorial: Specify parameters in | Develop, test, and deploy playbooks in |
This documentation applies to the following versions of Splunk® SOAR (On-premises): 5.0.1
Feedback submitted, thanks!