def main(): argspec = dict( state=dict( required=False, choices=["present", "absent", "enabled", "disable"], default="present", type="str", ), connection_host=dict(required=False, choices=["ip", "dns", "none"], default="ip", type="str"), host=dict(required=False, type="str", default=None), index=dict(required=False, type="str", default=None), name=dict(required=True, type="str"), protocol=dict(required=True, type="str", choices=["tcp", "udp"]), queue=dict( required=False, type="str", choices=["parsingQueue", "indexQueue"], default="parsingQueue", ), rawTcpDoneTimeout=dict(required=False, type="int", default=10), restrictToHost=dict(required=False, type="str", default=None), ssl=dict(required=False, type="bool", default=None), source=dict(required=False, type="str", default=None), sourcetype=dict(required=False, type="str", default=None), datatype=dict(required=False, choices=["cooked", "raw"], default="raw"), ) module = AnsibleModule(argument_spec=argspec, supports_check_mode=True) splunk_request = SplunkRequest( module, headers={"Content-Type": "application/x-www-form-urlencoded"}, not_rest_data_keys=["state", "datatype", "protocol"], ) # This is where the splunk_* args are processed request_data = splunk_request.get_data() query_dict = splunk_request.get_by_path( "servicesNS/nobody/search/data/inputs/{0}/{1}/{2}".format( quote_plus(module.params["protocol"]), quote_plus(module.params["datatype"]), quote_plus(module.params["name"]), )) if module.params["state"] in ["present", "enabled", "disabled"]: _data = splunk_request.get_data() if module.params["state"] in ["present", "enabled"]: _data["disabled"] = False else: _data["disabled"] = True if query_dict: needs_change = False for arg in request_data: if arg in query_dict["entry"][0]["content"]: if to_text( query_dict["entry"][0]["content"][arg]) != to_text( request_data[arg]): needs_change = True if not needs_change: module.exit_json(changed=False, msg="Nothing to do.", splunk_data=query_dict) if module.check_mode and needs_change: module.exit_json( changed=True, msg="A change would have been made if not in check mode.", splunk_data=query_dict, ) if needs_change: splunk_data = splunk_request.create_update( "servicesNS/nobody/search/data/inputs/{0}/{1}/{2}".format( quote_plus(module.params["protocol"]), quote_plus(module.params["datatype"]), quote_plus(module.params["name"]), data=urlencode(_data), )) if module.params["state"] in ["present", "enabled"]: module.exit_json(changed=True, msg="{0} updated.", splunk_data=splunk_data) else: module.exit_json(changed=True, msg="{0} disabled.", splunk_data=splunk_data) else: # Create it splunk_data = splunk_request.create_update( "servicesNS/nobody/search/data/inputs/{0}/{1}".format( quote_plus(module.params["protocol"]), quote_plus(module.params["datatype"]), ), data=urlencode(_data), ) module.exit_json(changed=True, msg="{0} created.", splunk_data=splunk_data) elif module.params["state"] == "absent": if query_dict: splunk_data = splunk_request.delete_by_path( "servicesNS/nobody/search/data/inputs/{0}/{1}/{2}".format( quote_plus(module.params["protocol"]), quote_plus(module.params["datatype"]), quote_plus(module.params["name"]), )) module.exit_json( changed=True, msg="Deleted {0}.".format(module.params["name"]), splunk_data=splunk_data, ) module.exit_json(changed=False, msg="Nothing to do.", splunk_data={})
def main(): argspec = dict( name=dict(required=True, type="str"), state=dict(choices=["present", "absent"], required=True), blacklist=dict(required=False, type="str", default=None), check_index=dict(required=False, type="bool", default=False), check_path=dict(required=False, type="bool", default=None), crc_salt=dict(required=False, type="str", default=None), disabled=dict(required=False, type="bool", default=False), followTail=dict(required=False, type="bool", default=False), host=dict(required=False, type="str", default=None), host_segment=dict(required=False, type="int", default=None), host_regex=dict(required=False, type="str", default=None), ignore_older_than=dict(required=False, type="str", default=None), index=dict(required=False, type="str", default=None), recursive=dict(required=False, type="bool", default=False), rename_source=dict(required=False, type="str", default=None), sourcetype=dict(required=False, type="str", default=None), time_before_close=dict(required=False, type="int", default=None), whitelist=dict(required=False, type="str", default=None), ) module = AnsibleModule(argument_spec=argspec, supports_check_mode=True) # map of keys for the splunk REST API that aren't pythonic so we have to # handle the substitutes keymap = { "check_index": "check-index", "check_path": "check-path", "crc_salt": "crc-salt", "ignore_older_than": "ignore-older-than", "rename_source": "rename-source", "time_before_close": "time-before-close", } splunk_request = SplunkRequest( module, headers={"Content-Type": "application/x-www-form-urlencoded"}, keymap=keymap, not_rest_data_keys=["state"], ) # This is where the splunk_* args are processed request_data = splunk_request.get_data() query_dict = splunk_request.get_by_path( "servicesNS/nobody/search/data/inputs/monitor/{0}".format( quote_plus(module.params["name"]))) if module.params["state"] == "present": if query_dict: needs_change = False for arg in request_data: if arg in query_dict["entry"][0]["content"]: if to_text( query_dict["entry"][0]["content"][arg]) != to_text( request_data[arg]): needs_change = True if not needs_change: module.exit_json(changed=False, msg="Nothing to do.", splunk_data=query_dict) if module.check_mode and needs_change: module.exit_json( changed=True, msg="A change would have been made if not in check mode.", splunk_data=query_dict, ) if needs_change: splunk_data = splunk_request.create_update( "servicesNS/nobody/search/data/inputs/monitor/{0}".format( quote_plus(module.params["name"]))) module.exit_json(changed=True, msg="{0} updated.", splunk_data=splunk_data) else: # Create it _data = splunk_request.get_data() _data["name"] = module.params["name"] splunk_data = splunk_request.create_update( "servicesNS/nobody/search/data/inputs/monitor", data=urlencode(_data)) module.exit_json(changed=True, msg="{0} created.", splunk_data=splunk_data) if module.params["state"] == "absent": if query_dict: splunk_data = splunk_request.delete_by_path( "servicesNS/nobody/search/data/inputs/monitor/{0}".format( quote_plus(module.params["name"]))) module.exit_json( changed=True, msg="Deleted {0}.".format(module.params["name"]), splunk_data=splunk_data, ) module.exit_json(changed=False, msg="Nothing to do.", splunk_data=query_dict)
def main(): argspec = dict( name=dict(required=True, type="str"), correlation_search_name=dict(required=True, type="str"), description=dict(required=True, type="str"), state=dict(choices=["present", "absent"], required=True), security_domain=dict( choices=["access", "endpoint", "network", "threat", "identity", "audit"], required=False, default="threat", ), severity=dict( choices=["informational", "low", "medium", "high", "critical", "unknown"], required=False, default="high", ), default_owner=dict(required=False, type="str"), default_status=dict( choices=[ "unassigned", "new", "in progress", "pending", "resolved", "closed", ], required=False, default="", ), drill_down_name=dict(required=False, type="str"), drill_down_search=dict(required=False, type="str"), drill_down_earliest_offset=dict( required=False, type="str", default="$info_min_time$" ), drill_down_latest_offset=dict( required=False, type="str", default="$info_max_time$" ), investigation_profiles=dict(required=False, type="str"), next_steps=dict(required=False, type="list", default=[]), recommended_actions=dict(required=False, type="list", default=[]), asset_extraction=dict( required=False, type="list", default=["src", "dest", "dvc", "orig_host"], choices=["src", "dest", "dvc", "orig_host"], ), identity_extraction=dict( required=False, type="list", default=["user", "src_user"], choices=["user", "src_user"], ), ) module = AnsibleModule(argument_spec=argspec, supports_check_mode=True) splunk_request = SplunkRequest( module, headers={"Content-Type": "application/x-www-form-urlencoded"}, not_rest_data_keys=["state"], ) query_dict = splunk_request.get_by_path( "servicesNS/nobody/SplunkEnterpriseSecuritySuite/saved/searches/{0}".format( quote_plus(module.params["correlation_search_name"]) ) ) # Have to custom craft the data here because they overload the saved searches # endpoint in the rest api and we want to hide the nuance from the user request_post_data = {} # FIXME need to figure out how to properly support these, the possible values appear to # be dynamically created based on what the search is indexing # request_post_data['action.notable.param.extract_assets'] = '[\"src\",\"dest\",\"dvc\",\"orig_host\"]' # request_post_data['action.notable.param.extract_identities'] = [\"src_user\",\"user\"] if module.params["next_steps"]: if len(module.params["next_steps"]) == 1: next_steps = "[[action|{0}]]".format(module.params["next_steps"][0]) else: next_steps = "" for next_step in module.params["next_steps"]: if next_steps: next_steps += "\n[[action|{0}]]".format(next_step) else: next_steps = "[[action|{0}]]".format(next_step) # NOTE: version:1 appears to be hard coded when you create this via the splunk web UI # but I don't know what it is/means because there's no docs on it next_steps_dict = {"version": 1, "data": next_steps} request_post_data["action.notable.param.next_steps"] = json.dumps( next_steps_dict ) if module.params["recommended_actions"]: if len(module.params["recommended_actions"]) == 1: request_post_data[ "action.notable.param.recommended_actions" ] = module.params["recommended_actions"][0] else: request_post_data["action.notable.param.recommended_actions"] = ",".join( module.params["recommended_actions"] ) request_post_data["action.notable.param.rule_description"] = module.params[ "description" ] request_post_data["action.notable.param.rule_title"] = module.params["name"] request_post_data["action.notable.param.security_domain"] = module.params[ "security_domain" ] request_post_data["action.notable.param.severity"] = module.params["severity"] request_post_data["action.notable.param.asset_extraction"] = module.params[ "asset_extraction" ] request_post_data["action.notable.param.identity_extraction"] = module.params[ "identity_extraction" ] # NOTE: this field appears to be hard coded when you create this via the splunk web UI # but I don't know what it is/means because there's no docs on it request_post_data["action.notable.param.verbose"] = "0" if module.params["default_owner"]: request_post_data["action.notable.param.default_owner"] = module.params[ "default_owner" ] if module.params["default_status"]: request_post_data["action.notable.param.default_status"] = module.params[ "default_status" ] if query_dict: request_post_data["search"] = query_dict["entry"][0]["content"]["search"] if "actions" in query_dict["entry"][0]["content"]: if query_dict["entry"][0]["content"]["actions"] == "notable": pass elif ( len(query_dict["entry"][0]["content"]["actions"].split(",")) > 0 and "notable" not in query_dict["entry"][0]["content"]["actions"] ): request_post_data["actions"] = ( query_dict["entry"][0]["content"]["actions"] + ", notable" ) else: request_post_data["actions"] = "notable" else: module.fail_json( msg="Unable to find correlation search: {0}", splunk_data=splunk_data ) if module.params["state"] == "present": needs_change = False for arg in request_post_data: if arg in query_dict["entry"][0]["content"]: if to_text(query_dict["entry"][0]["content"][arg]) != to_text( request_post_data[arg] ): needs_change = True if not needs_change: module.exit_json( changed=False, msg="Nothing to do.", splunk_data=query_dict ) if module.check_mode and needs_change: module.exit_json( changed=True, msg="A change would have been made if not in check mode.", splunk_data=query_dict, ) if needs_change: splunk_data = splunk_request.create_update( "servicesNS/nobody/SplunkEnterpriseSecuritySuite/saved/searches/{0}".format( quote_plus(module.params["correlation_search_name"]) ), data=urlencode(request_post_data), ) module.exit_json( changed=True, msg="{0} updated.".format(module.params["correlation_search_name"]), splunk_data=splunk_data, ) if module.params["state"] == "absent": # FIXME - need to figure out how to clear the action.notable.param fields from the api endpoint module.exit_json( changed=True, msg="Deleted {0}.".format(module.params["name"]), splunk_data=splunk_data, ) for arg in request_post_data: if arg in query_dict["entry"][0]["content"]: needs_change = True del query_dict["entry"][0]["content"][arg] if not needs_change: module.exit_json( changed=False, msg="Nothing to do.", splunk_data=query_dict ) if module.check_mode and needs_change: module.exit_json( changed=True, msg="A change would have been made if not in check mode.", splunk_data=query_dict, ) if needs_change: splunk_data = splunk_request.create_update( "servicesNS/nobody/SplunkEnterpriseSecuritySuite/saved/searches/{0}".format( quote_plus(module.params["correlation_search_name"]) ), data=urlencode(request_post_data), ) module.exit_json( changed=True, msg="{0} updated.".format(module.params["correlation_search_name"]), splunk_data=splunk_data, ) module.exit_json(changed=False, msg="Nothing to do.", splunk_data=query_dict)
def main(): argspec = dict( name=dict(required=True, type="str"), description=dict(required=True, type="str"), state=dict(choices=["present", "absent", "enabled", "disabled"], required=True), search=dict(required=True, type="str"), app=dict(type="str", required=False, default="SplunkEnterpriseSecuritySuite"), ui_dispatch_context=dict(type="str", required=False), time_earliest=dict(type="str", required=False, default="-24h"), time_latest=dict(type="str", required=False, default="now"), cron_schedule=dict(type="str", required=False, default="*/5 * * * *"), scheduling=dict( type="str", required=False, default="real-time", choices=["real-time", "continuous"], ), schedule_window=dict(type="str", required=False, default="0"), schedule_priority=dict( type="str", required=False, default="Default", choices=["Default", "Higher", "Highest"], ), trigger_alert_when=dict( type="str", required=False, default="number of events", choices=[ "number of events", "number of results", "number of hosts", "number of sources", ], ), trigger_alert_when_condition=dict( type="str", required=False, default="greater than", choices=[ "greater than", "less than", "equal to", "not equal to", "drops by", "rises by", ], ), trigger_alert_when_value=dict(type="str", required=False, default="10"), throttle_window_duration=dict(type="str", required=False), throttle_fields_to_group_by=dict(type="str", required=False), suppress_alerts=dict(type="bool", required=False, default=False), ) module = AnsibleModule(argument_spec=argspec, supports_check_mode=True) if module.params["state"] in ["present", "enabled"]: module_disabled_state = False else: module_disabled_state = True splunk_request = SplunkRequest( module, headers={"Content-Type": "application/x-www-form-urlencoded"}, not_rest_data_keys=["state"], ) try: query_dict = splunk_request.get_by_path( "servicesNS/nobody/SplunkEnterpriseSecuritySuite/saved/searches/{0}".format( quote_plus(module.params["name"]) ) ) except HTTPError as e: # the data monitor doesn't exist query_dict = {} # Have to custom craft the data here because they overload the saved searches # endpoint in the rest api and we want to hide the nuance from the user request_post_data = {} request_post_data["name"] = module.params["name"] request_post_data["action.correlationsearch.enabled"] = "1" request_post_data["is_scheduled"] = True request_post_data["dispatch.rt_backfill"] = True request_post_data["action.correlationsearch.label"] = module.params["name"] request_post_data["description"] = module.params["description"] request_post_data["search"] = module.params["search"] request_post_data["request.ui_dispatch_app"] = module.params["app"] if module.params["ui_dispatch_context"]: request_post_data["request.ui_dispatch_context"] = module.params[ "ui_dispatch_context" ] request_post_data["dispatch.earliest_time"] = module.params["time_earliest"] request_post_data["dispatch.latest_time"] = module.params["time_latest"] request_post_data["cron_schedule"] = module.params["cron_schedule"] if module.params["scheduling"] == "real-time": request_post_data["realtime_schedule"] = True else: request_post_data["realtime_schedule"] = False request_post_data["schedule_window"] = module.params["schedule_window"] request_post_data["schedule_priority"] = module.params["schedule_priority"].lower() request_post_data["alert_type"] = module.params["trigger_alert_when"] request_post_data["alert_comparator"] = module.params[ "trigger_alert_when_condition" ] request_post_data["alert_threshold"] = module.params["trigger_alert_when_value"] request_post_data["alert.suppress"] = module.params["suppress_alerts"] request_post_data["disabled"] = module_disabled_state if module.params["state"] in ["present", "enabled", "disabled"]: if query_dict: needs_change = False for arg in request_post_data: if arg in query_dict["entry"][0]["content"]: if to_text(query_dict["entry"][0]["content"][arg]) != to_text( request_post_data[arg] ): needs_change = True if not needs_change: module.exit_json( changed=False, msg="Nothing to do.", splunk_data=query_dict ) if module.check_mode and needs_change: module.exit_json( changed=True, msg="A change would have been made if not in check mode.", splunk_data=query_dict, ) if needs_change: # FIXME - need to find a reasonable way to deal with action.correlationsearch.enabled del request_post_data[ "name" ] # If this is present, splunk assumes we're trying to create a new one wit the same name splunk_data = splunk_request.create_update( "servicesNS/nobody/SplunkEnterpriseSecuritySuite/saved/searches/{0}".format( quote_plus(module.params["name"]) ), data=urlencode(request_post_data), ) module.exit_json( changed=True, msg="{0} updated.", splunk_data=splunk_data ) else: # Create it splunk_data = splunk_request.create_update( "servicesNS/nobody/SplunkEnterpriseSecuritySuite/saved/searches", data=urlencode(request_post_data), ) module.exit_json(changed=True, msg="{0} created.", splunk_data=splunk_data) elif module.params["state"] == "absent": if query_dict: splunk_data = splunk_request.delete_by_path( "services/saved/searches/{0}".format(quote_plus(module.params["name"])) ) module.exit_json( changed=True, msg="Deleted {0}.".format(module.params["name"]), splunk_data=splunk_data, ) module.exit_json(changed=False, msg="Nothing to do.", splunk_data=query_dict)