<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic Re: How to insert ESCU detections via REST API into Splunk ESS? in Splunk Enterprise Security</title>
    <link>https://community.splunk.com/t5/Splunk-Enterprise-Security/How-to-insert-ESCU-detections-via-REST-API-into-Splunk-ESS/m-p/745845#M12514</link>
    <description>&lt;P&gt;Hi&amp;nbsp;&lt;a href="https://community.splunk.com/t5/user/viewprofilepage/user-id/221196"&gt;@koshyk&lt;/a&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;I did a PoC with a customer around this a few months ago and we had great success using the regular &lt;A href="https://docs.splunk.com/Documentation/Splunk/9.4.2/RESTREF/RESTsearch#saved.2Fsearches" target="_self"&gt;saved/search endpoint&lt;/A&gt;&amp;nbsp;to create searches from a custom app created with contentctl.&amp;nbsp;&lt;/P&gt;&lt;P&gt;We used a custom app which we uploaded the searches into, this way we are following the same pattern as creating a custom app from contentctl which would be uploaded.&amp;nbsp;&lt;/P&gt;&lt;P&gt;ES rules are essentially scheduled searches, with modular alerts, they just show in ES because of some of the settings in them, from memory these might be&amp;nbsp;action.correlationsearch.* and&amp;nbsp;action.notable.param.* settings.&amp;nbsp;&lt;/P&gt;&lt;P&gt;I think the main gotcha that caught is out is that in the savedsearches.conf we use "enabledSched = 1" to schedule a search, however the API expects "is_scheduled =1" (WHY?!?)&lt;/P&gt;&lt;P&gt;Check my response below to the following post which has a sample python code to upload a savedsearch if it helps -&amp;nbsp;&lt;A href="https://community.splunk.com/t5/All-Apps-and-Add-ons/Custom-app-in-cloud/m-p/745079" target="_blank" rel="noopener"&gt;https://community.splunk.com/t5/All-Apps-and-Add-ons/Custom-app-in-cloud/m-p/745079&lt;/A&gt;&lt;/P&gt;&lt;BLOCKQUOTE&gt;&lt;HR /&gt;&lt;a href="https://community.splunk.com/t5/user/viewprofilepage/user-id/170906"&gt;@livehybrid&lt;/a&gt;&amp;nbsp;wrote:&lt;BR /&gt;&lt;P&gt;Hi&amp;nbsp;&lt;a href="https://community.splunk.com/t5/user/viewprofilepage/user-id/258309"&gt;@hazardoom&lt;/a&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;If you arent wanting to go down the app route then another thing you could look at it is using the REST API.&amp;nbsp;&lt;/P&gt;&lt;P&gt;Ive used this with clients in the past, here is an example to get your started if this is something you wanted to explore:&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;LI-CODE lang="markup"&gt;import configparser
import requests
from urllib.parse import quote

# ======= USER CONFIGURATION =========
SPLUNK_HOST = 'https://yoursplunkhost:8089'  # e.g., https://localhost:8089
SPLUNK_TOKEN = 'your-token-here'  # just the token string, no 'Splunk ' prefix
APP = 'search'  # target app for the saved searches
CONF_FILE = 'savedsearches.conf'  # path to your savedsearches.conf file
VERIFY_SSL = True  # set to False if using self-signed certs
USERNAME = 'yourUsername'  # API requires this as a path component
# ====================================

# Map conf fields to REST API fields
def convert_field_name(field, value):
    """Map .conf fields to API fields and perform value translations."""
    if field == "enableSched":
        return "is_scheduled", "1" if value.strip().lower() in ("1", "true", "yes", "on") else "0"
    return field, value

def load_savedsearches(conf_path):
    cp = configparser.ConfigParser(strict=False, delimiters=['='])
    cp.optionxform = str  # preserve case and case sensitivity
    cp.read(conf_path)
    return cp

def upload_savedsearches(cp):
    headers = {'Authorization': f'Splunk {SPLUNK_TOKEN}'}
    base_url = f"{SPLUNK_HOST}/servicesNS/{USERNAME}/{APP}/saved/searches"

    for savedsearch_name in cp.sections():
        data = {'name': savedsearch_name}
        for field, value in cp[savedsearch_name].items():
            api_field, api_value = convert_field_name(field, value)
            data[api_field] = api_value

        search_url = f"{base_url}/{quote(savedsearch_name)}"
        
        # Check if the saved search exists (GET request)
        check = requests.get(search_url, headers=headers, verify=VERIFY_SSL)
        if check.status_code == 200:
            print(f"Updating existing savedsearch: {savedsearch_name}")
            r = requests.post(search_url, data=data, headers=headers, verify=VERIFY_SSL)
        else:
            print(f"Creating new savedsearch: {savedsearch_name}")
            r = requests.post(base_url, data=data, headers=headers, verify=VERIFY_SSL)

        if r.status_code not in (200, 201):
            print(f"Failed for {savedsearch_name}: {r.status_code} {r.text}")
        else:
            print(f"Success: {savedsearch_name}")

def main():
    cp = load_savedsearches(CONF_FILE)
    upload_savedsearches(cp)

if __name__ == "__main__":
    main()&lt;/LI-CODE&gt;&lt;P&gt;We use this approach to upload file direct from Git pipelines which is especially useful if you arent an admin on the platform so cannot upload an app - however may also work well for your usecase. Note: you could use the Splunk Python SDK too, which basically does the same thing.&lt;/P&gt;&lt;P&gt;&lt;span class="lia-unicode-emoji" title=":glowing_star:"&gt;🌟&lt;/span&gt;&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;STRONG&gt;Did this answer help you?&lt;/STRONG&gt;&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;If so, please consider:&lt;/P&gt;&lt;UL&gt;&lt;LI&gt;Adding karma to show it was useful&lt;/LI&gt;&lt;LI&gt;Marking it as the solution if it resolved your issue&lt;/LI&gt;&lt;LI&gt;Commenting if you need any clarification&lt;/LI&gt;&lt;/UL&gt;&lt;P&gt;Your feedback encourages the volunteers in this community to continue contributing&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;HR /&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
    <pubDate>Fri, 09 May 2025 14:33:07 GMT</pubDate>
    <dc:creator>livehybrid</dc:creator>
    <dc:date>2025-05-09T14:33:07Z</dc:date>
    <item>
      <title>How to insert ESCU detections via REST API into Splunk ESS?</title>
      <link>https://community.splunk.com/t5/Splunk-Enterprise-Security/How-to-insert-ESCU-detections-via-REST-API-into-Splunk-ESS/m-p/745822#M12513</link>
      <description>&lt;P data-unlink="true"&gt;We have automation to insert&amp;nbsp; &lt;EM&gt;/saved/searches&lt;/EM&gt; endpoint and all is good.&amp;nbsp; Also current have quite lot of custom Splunk Enterprise Security (ESS) event-based detections handcrafted via the GUI in splunk cloud. (So can't directly put into savedsearches.conf)&lt;/P&gt;&lt;P data-unlink="true"&gt;We have to automate these as they are not pure 'savedsearches'. We are following the &lt;A href="https://github.com/splunk/security_content/blob/develop/detections/web/access_to_vulnerable_ivanti_connect_secure_bookmark_endpoint.yml" target="_self"&gt;ESCU&lt;/A&gt;&amp;nbsp; standards and use contentctl validate. All good till this stage&lt;/P&gt;&lt;P data-unlink="true"&gt;But how to insert the ESCU detections into Splunk ESS? Which app to insert into? (SplunkEnterpriseSecuritySuite or DA-ESS-* type apps or can it be inserted into our own custom app itself?)&lt;/P&gt;&lt;P data-unlink="true"&gt;Any API based automation into Splunk ESS is deeply appreciated&lt;/P&gt;&lt;P data-unlink="true"&gt;thanks in advance&lt;/P&gt;</description>
      <pubDate>Fri, 09 May 2025 05:30:32 GMT</pubDate>
      <guid>https://community.splunk.com/t5/Splunk-Enterprise-Security/How-to-insert-ESCU-detections-via-REST-API-into-Splunk-ESS/m-p/745822#M12513</guid>
      <dc:creator>koshyk</dc:creator>
      <dc:date>2025-05-09T05:30:32Z</dc:date>
    </item>
    <item>
      <title>Re: How to insert ESCU detections via REST API into Splunk ESS?</title>
      <link>https://community.splunk.com/t5/Splunk-Enterprise-Security/How-to-insert-ESCU-detections-via-REST-API-into-Splunk-ESS/m-p/745845#M12514</link>
      <description>&lt;P&gt;Hi&amp;nbsp;&lt;a href="https://community.splunk.com/t5/user/viewprofilepage/user-id/221196"&gt;@koshyk&lt;/a&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;I did a PoC with a customer around this a few months ago and we had great success using the regular &lt;A href="https://docs.splunk.com/Documentation/Splunk/9.4.2/RESTREF/RESTsearch#saved.2Fsearches" target="_self"&gt;saved/search endpoint&lt;/A&gt;&amp;nbsp;to create searches from a custom app created with contentctl.&amp;nbsp;&lt;/P&gt;&lt;P&gt;We used a custom app which we uploaded the searches into, this way we are following the same pattern as creating a custom app from contentctl which would be uploaded.&amp;nbsp;&lt;/P&gt;&lt;P&gt;ES rules are essentially scheduled searches, with modular alerts, they just show in ES because of some of the settings in them, from memory these might be&amp;nbsp;action.correlationsearch.* and&amp;nbsp;action.notable.param.* settings.&amp;nbsp;&lt;/P&gt;&lt;P&gt;I think the main gotcha that caught is out is that in the savedsearches.conf we use "enabledSched = 1" to schedule a search, however the API expects "is_scheduled =1" (WHY?!?)&lt;/P&gt;&lt;P&gt;Check my response below to the following post which has a sample python code to upload a savedsearch if it helps -&amp;nbsp;&lt;A href="https://community.splunk.com/t5/All-Apps-and-Add-ons/Custom-app-in-cloud/m-p/745079" target="_blank" rel="noopener"&gt;https://community.splunk.com/t5/All-Apps-and-Add-ons/Custom-app-in-cloud/m-p/745079&lt;/A&gt;&lt;/P&gt;&lt;BLOCKQUOTE&gt;&lt;HR /&gt;&lt;a href="https://community.splunk.com/t5/user/viewprofilepage/user-id/170906"&gt;@livehybrid&lt;/a&gt;&amp;nbsp;wrote:&lt;BR /&gt;&lt;P&gt;Hi&amp;nbsp;&lt;a href="https://community.splunk.com/t5/user/viewprofilepage/user-id/258309"&gt;@hazardoom&lt;/a&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;If you arent wanting to go down the app route then another thing you could look at it is using the REST API.&amp;nbsp;&lt;/P&gt;&lt;P&gt;Ive used this with clients in the past, here is an example to get your started if this is something you wanted to explore:&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;LI-CODE lang="markup"&gt;import configparser
import requests
from urllib.parse import quote

# ======= USER CONFIGURATION =========
SPLUNK_HOST = 'https://yoursplunkhost:8089'  # e.g., https://localhost:8089
SPLUNK_TOKEN = 'your-token-here'  # just the token string, no 'Splunk ' prefix
APP = 'search'  # target app for the saved searches
CONF_FILE = 'savedsearches.conf'  # path to your savedsearches.conf file
VERIFY_SSL = True  # set to False if using self-signed certs
USERNAME = 'yourUsername'  # API requires this as a path component
# ====================================

# Map conf fields to REST API fields
def convert_field_name(field, value):
    """Map .conf fields to API fields and perform value translations."""
    if field == "enableSched":
        return "is_scheduled", "1" if value.strip().lower() in ("1", "true", "yes", "on") else "0"
    return field, value

def load_savedsearches(conf_path):
    cp = configparser.ConfigParser(strict=False, delimiters=['='])
    cp.optionxform = str  # preserve case and case sensitivity
    cp.read(conf_path)
    return cp

def upload_savedsearches(cp):
    headers = {'Authorization': f'Splunk {SPLUNK_TOKEN}'}
    base_url = f"{SPLUNK_HOST}/servicesNS/{USERNAME}/{APP}/saved/searches"

    for savedsearch_name in cp.sections():
        data = {'name': savedsearch_name}
        for field, value in cp[savedsearch_name].items():
            api_field, api_value = convert_field_name(field, value)
            data[api_field] = api_value

        search_url = f"{base_url}/{quote(savedsearch_name)}"
        
        # Check if the saved search exists (GET request)
        check = requests.get(search_url, headers=headers, verify=VERIFY_SSL)
        if check.status_code == 200:
            print(f"Updating existing savedsearch: {savedsearch_name}")
            r = requests.post(search_url, data=data, headers=headers, verify=VERIFY_SSL)
        else:
            print(f"Creating new savedsearch: {savedsearch_name}")
            r = requests.post(base_url, data=data, headers=headers, verify=VERIFY_SSL)

        if r.status_code not in (200, 201):
            print(f"Failed for {savedsearch_name}: {r.status_code} {r.text}")
        else:
            print(f"Success: {savedsearch_name}")

def main():
    cp = load_savedsearches(CONF_FILE)
    upload_savedsearches(cp)

if __name__ == "__main__":
    main()&lt;/LI-CODE&gt;&lt;P&gt;We use this approach to upload file direct from Git pipelines which is especially useful if you arent an admin on the platform so cannot upload an app - however may also work well for your usecase. Note: you could use the Splunk Python SDK too, which basically does the same thing.&lt;/P&gt;&lt;P&gt;&lt;span class="lia-unicode-emoji" title=":glowing_star:"&gt;🌟&lt;/span&gt;&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;STRONG&gt;Did this answer help you?&lt;/STRONG&gt;&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;If so, please consider:&lt;/P&gt;&lt;UL&gt;&lt;LI&gt;Adding karma to show it was useful&lt;/LI&gt;&lt;LI&gt;Marking it as the solution if it resolved your issue&lt;/LI&gt;&lt;LI&gt;Commenting if you need any clarification&lt;/LI&gt;&lt;/UL&gt;&lt;P&gt;Your feedback encourages the volunteers in this community to continue contributing&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;HR /&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Fri, 09 May 2025 14:33:07 GMT</pubDate>
      <guid>https://community.splunk.com/t5/Splunk-Enterprise-Security/How-to-insert-ESCU-detections-via-REST-API-into-Splunk-ESS/m-p/745845#M12514</guid>
      <dc:creator>livehybrid</dc:creator>
      <dc:date>2025-05-09T14:33:07Z</dc:date>
    </item>
    <item>
      <title>Re: How to insert ESCU detections via REST API into Splunk ESS?</title>
      <link>https://community.splunk.com/t5/Splunk-Enterprise-Security/How-to-insert-ESCU-detections-via-REST-API-into-Splunk-ESS/m-p/745877#M12515</link>
      <description>&lt;P&gt;thanks &lt;A href="https://community.splunk.com/t5/user/viewprofilepage/user-id/170906" target="_blank" rel="noopener"&gt;@livehybrid&lt;/A&gt; . Upvoted.&lt;/P&gt;&lt;P&gt;I almost figured it out, but in a slightly different manner. I'm got an ansible setup for URL interaction and automation. The 'contentctl build' will produce artefact similar to a Splunk app with `savedsearches.conf` and other things like `analyticsstories.conf`&lt;/P&gt;&lt;LI-CODE lang="markup"&gt;contentctl build --path content --app.title MY_DETECT --app.appid DA-ESS-MY_DETECT --app.prefix MY --app.label MY&lt;/LI-CODE&gt;&lt;P&gt;Then i'm using the ansible automation which interacts with saved/searches and other endpoints to insert it back.&lt;/P&gt;&lt;P&gt;Two things i'm still figuring out is&lt;/P&gt;&lt;UL&gt;&lt;LI&gt;it is slow once the savedsearches have 50+ searches as it runs one by one&lt;/LI&gt;&lt;LI&gt;contentctl new : this option doesn't accept ALL parameters like search, name which means a user input is required&lt;/LI&gt;&lt;LI&gt;Any chance for automation can detect if a savedsearch is Changed, then only insert&lt;/LI&gt;&lt;/UL&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;Update:&amp;nbsp; Able to insert into system after contentctl using REST api "saved/searches".&amp;nbsp; Though the type is specified as 'ebd' (event-based detection), while it is inserted into Splunk, it becomes a 'saved search' type !!&lt;/P&gt;&lt;P&gt;any solutions/recommendations for this?&lt;/P&gt;</description>
      <pubDate>Mon, 12 May 2025 08:55:17 GMT</pubDate>
      <guid>https://community.splunk.com/t5/Splunk-Enterprise-Security/How-to-insert-ESCU-detections-via-REST-API-into-Splunk-ESS/m-p/745877#M12515</guid>
      <dc:creator>koshyk</dc:creator>
      <dc:date>2025-05-12T08:55:17Z</dc:date>
    </item>
    <item>
      <title>Re: How to insert ESCU detections via REST API into Splunk ESS?</title>
      <link>https://community.splunk.com/t5/Splunk-Enterprise-Security/How-to-insert-ESCU-detections-via-REST-API-into-Splunk-ESS/m-p/754553#M12728</link>
      <description>&lt;DIV&gt;&amp;nbsp;&lt;/DIV&gt;&lt;DIV&gt;&lt;SPAN&gt;Thanks!&lt;/SPAN&gt;&lt;/DIV&gt;&lt;DIV&gt;&lt;P&gt;is_scheduled=1 helped me solve one of two missing pieces trying to create an ITSI Entity Import saved search.&lt;BR /&gt;&lt;BR /&gt;The other part of the puzzle is that this line in savedsearches.conf&lt;/P&gt;&lt;P&gt;&lt;SPAN&gt;action.itsi_import_objects = 1&lt;/SPAN&gt;&lt;/P&gt;&lt;P&gt;&lt;SPAN&gt;translates to this for the REST API call:&lt;/SPAN&gt;&lt;/P&gt;&lt;DIV&gt;-d actions="itsi_import_objects"&lt;BR /&gt;&lt;BR /&gt;&lt;/DIV&gt;&lt;DIV&gt;&lt;DIV&gt;As you said... WHY?!? &amp;nbsp;(what else was I going to do with the last 4 hours)&lt;/DIV&gt;&lt;/DIV&gt;&lt;/DIV&gt;</description>
      <pubDate>Tue, 21 Oct 2025 23:24:49 GMT</pubDate>
      <guid>https://community.splunk.com/t5/Splunk-Enterprise-Security/How-to-insert-ESCU-detections-via-REST-API-into-Splunk-ESS/m-p/754553#M12728</guid>
      <dc:creator>stuartidelta01</dc:creator>
      <dc:date>2025-10-21T23:24:49Z</dc:date>
    </item>
    <item>
      <title>Re: How to insert ESCU detections via REST API into Splunk ESS?</title>
      <link>https://community.splunk.com/t5/Splunk-Enterprise-Security/How-to-insert-ESCU-detections-via-REST-API-into-Splunk-ESS/m-p/754556#M12731</link>
      <description>&lt;P&gt;&lt;a href="https://community.splunk.com/t5/user/viewprofilepage/user-id/129308"&gt;@stuartidelta01&lt;/a&gt;&amp;nbsp;ahh yes, I had this recently with something else too!&amp;nbsp;&lt;/P&gt;&lt;P&gt;It is in the docs, of course, but easy to miss!&lt;/P&gt;&lt;P&gt;&lt;A href="https://help.splunk.com/en/splunk-enterprise/leverage-rest-apis/rest-api-reference/9.0/search-endpoints/search-endpoint-descriptions#:~:text=A%20comma%2Dseparated%20list%20of%20actions%20to%20enable.%20For%20example%3A%20rss%2Cemail" target="_blank"&gt;https://help.splunk.com/en/splunk-enterprise/leverage-rest-apis/rest-api-reference/9.0/search-endpoints/search-endpoint-descriptions#:~:text=A%20comma%2Dseparated%20list%20of%20actions%20to%20enable.%20For%20example%3A%20rss%2Cemail&lt;/A&gt;&lt;/P&gt;&lt;P&gt;Basically to enable an action, as you say you need to do actions=&amp;lt;action1&amp;gt;,&amp;lt;action2&amp;gt; not action.&amp;lt;action1&amp;gt;=1 etc&lt;/P&gt;&lt;P&gt;&lt;span class="lia-unicode-emoji" title=":glowing_star:"&gt;🌟&lt;/span&gt;&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;STRONG&gt;Did this answer help you?&lt;/STRONG&gt;&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;If so, please consider:&lt;/P&gt;&lt;UL&gt;&lt;LI&gt;Adding karma to show it was useful&lt;/LI&gt;&lt;LI&gt;Marking it as the solution if it resolved your issue&lt;/LI&gt;&lt;LI&gt;Commenting if you need any clarification&lt;/LI&gt;&lt;/UL&gt;&lt;P&gt;Your feedback encourages the volunteers in this community to continue contributing&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Tue, 21 Oct 2025 23:31:33 GMT</pubDate>
      <guid>https://community.splunk.com/t5/Splunk-Enterprise-Security/How-to-insert-ESCU-detections-via-REST-API-into-Splunk-ESS/m-p/754556#M12731</guid>
      <dc:creator>livehybrid</dc:creator>
      <dc:date>2025-10-21T23:31:33Z</dc:date>
    </item>
  </channel>
</rss>

