Tutorial: Chain a series of actions in Splunk SOAR (On-premises)
This topic demonstrates how to execute a series of actions to bind together many security operations tasks into a single playbook in Splunk SOAR (On-premises). You execute a series of actions by incorporating new actions into the callback of another action.
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 ***