Skip to content

Functions: Foundry CLI

The Foundry CLI supports function development in Python and Go with local testing and debugging capabilities.

It’s best to use functions when you’ve exhausted all the other possibilities in Falcon Foundry. If you can perform the work with an API integration and actions in a workflow, try that first. That said, if you’re a developer fluent in Python or Go, you may be able to iterate faster with functions.

  • Foundry CLI 1.4.3 or above (install guide)
  • Docker (required for foundry functions run)
  • Python 3.x (for Python functions)
  • Go (for Go functions)

The Python FDK is called crowdstrike-foundry-function. Add it to your function’s requirements.txt.

From inside your app directory:

Terminal window
foundry functions create

You’ll be prompted for:

  • Function Name — e.g., hello
  • Description — e.g., Returns a greeting based on name input
  • Languagepython or go
  • Handler Name — e.g., hello
  • Handler Method — e.g., POST
  • Handler Path — e.g., /hello
  • Input/Output schemas — generate defaults or provide JSON Schema files
  • Workflow share settings — share with app workflows, Fusion SOAR, or keep private

The Python FDK uses a decorator pattern with typed Request and Response objects:

from typing import Any, Dict, Optional
from logging import Logger
from crowdstrike.foundry.function import Function, Request, Response, APIError
func = Function.instance()
@func.handler(method='POST', path='/hello')
def on_post(request: Request, _config: Optional[Dict[str, Any]], logger: Logger) -> Response:
if 'name' not in request.body:
return Response(
code=400,
errors=[APIError(code=400, message='missing name from request body')]
)
return Response(
body={'greeting': f'Hello {request.body["name"]}! It is nice to see you.'},
code=200,
)
if __name__ == '__main__':
func.run()

Add crowdstrike-falconpy to your requirements.txt (leave the version unpinned to receive the latest updates):

crowdstrike-foundry-function
crowdstrike-falconpy

Import the service collection you need. When running in the cloud, FalconPy uses context authentication — no credentials needed in your code:

from typing import Any, Dict, Optional
from logging import Logger
from crowdstrike.foundry.function import Function, Request, Response, APIError
from falconpy import Hosts
FUNC = Function.instance()
@FUNC.handler(method="POST", path="/host-details")
def on_post(request: Request, _config: Optional[Dict[str, Any]], logger: Logger) -> Response:
if "host_id" not in request.body:
return Response(
code=400,
errors=[APIError(code=400, message="missing host_id from request body")]
)
falcon = Hosts()
response = falcon.get_device_details(ids=request.body["host_id"])
if response["status_code"] != 200:
return Response(
code=response["status_code"],
errors=[APIError(code=response["status_code"],
message=f"Error retrieving host: {response['body']}")],
)
return Response(
body={"host_details": response["body"]["resources"][0]},
code=200,
)
if __name__ == "__main__":
FUNC.run()

Use foundry auth scopes add to add the required scopes for your FalconPy imports. For example, search for “Hosts” and select Hosts read. This adds the scope to your manifest.yml:

auth:
scopes:
- devices:read

If you add API integrations and collections to your app, additional scopes (api-integrations, custom-storage) are added automatically by Foundry when you deploy. You don’t need to reference them in your manifest.

Use FalconPy’s APIIntegrations service class to call integrations defined in your app:

from falconpy import APIIntegrations
api = APIIntegrations()
response = api.execute_command_proxy(
definition_id="ServiceNow",
operation_id="POST__api_now_table_tablename",
params={
"path": {"tableName": "incident"}
},
request={
"json": payload,
"headers": {
"Accept": "application/json",
"Content-Type": "application/json"
}
}
)

Important: The definition_id must be the integration’s exact name as defined in your app. Using the wrong identifier causes a 404 error.

Use FalconPy’s CustomStorage service class with PascalCase method names:

import os
from falconpy import CustomStorage
def _app_headers() -> dict:
"""Build app headers for CustomStorage construction."""
app_id = os.environ.get("APP_ID")
if app_id:
return {"X-CS-APP-ID": app_id}
return {}
custom_storage = CustomStorage(ext_headers=_app_headers())
collection_name = "event_logs"
# Write
response = custom_storage.PutObject(
body=json_data,
collection_name=collection_name,
object_key=event_id
)
# Search with FQL
query_response = custom_storage.SearchObjects(
filter=f"event_id:'{event_id}'",
collection_name=collection_name,
limit=5
)

The ext_headers parameter handles the X-CS-APP-ID header that Foundry uses for app-scoped access. Set the APP_ID environment variable for local testing.

If you have Docker installed and running:

Terminal window
foundry functions run

This starts a Docker container with your function. The port is assigned automatically — check the output for the port number:

Terminal window
curl --request POST --url http://localhost:65463/ \
--header 'content-type: application/json' \
--data '{ "body": { "name": "Matt" }, "method": "POST", "url": "/hello" }'

Run the main.py file directly from the function directory:

Terminal window
cd functions/hello
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python main.py

This starts the function on port 8081. To test FalconPy API calls locally, set environment variables:

Terminal window
export FALCON_CLIENT_ID=...
export FALCON_CLIENT_SECRET=...
python main.py

Find your credentials in ~/.config/foundry/configuration.yml.

Create test files in your function directory (e.g., test_main.py):

import main
def test_hello_missing_name():
"""Test that missing name returns 400."""
request = main.Request(body={}, method="POST", url="/hello")
response = main.on_post(request)
assert response.code == 400
def test_hello_success():
"""Test successful greeting."""
request = main.Request(body={"name": "Test"}, method="POST", url="/hello")
response = main.on_post(request)
assert response.code == 200
assert "Test" in response.body["greeting"]

Run with pytest:

Terminal window
cd functions/hello
pytest

When creating a function with foundry functions create, you’re prompted for Workflow share settings:

  • Share function with app workflows and Fusion SOAR — available as a SOAR action
  • Share function with app workflows only — only callable from your app’s workflows
  • Don’t share — only callable from UI extensions
LimitValue
Maximum request payload124 KB (JSON portion)
Maximum response payload120 KB
Default execution timeout30 seconds (configurable up to 900 seconds)
Default memory256 MB (configurable up to 1 GB)
Maximum concurrent executions100
Maximum package size (including dependencies)50 MB