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.
Prerequisites
Section titled “Prerequisites”- 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.
Creating a function
Section titled “Creating a function”From inside your app directory:
foundry functions createYou’ll be prompted for:
- Function Name — e.g.,
hello - Description — e.g.,
Returns a greeting based on name input - Language —
pythonorgo - 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
Python handler pattern
Section titled “Python handler pattern”The Python FDK uses a decorator pattern with typed Request and Response objects:
from typing import Any, Dict, Optionalfrom logging import Loggerfrom 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()Using FalconPy in functions
Section titled “Using FalconPy in functions”Add crowdstrike-falconpy to your requirements.txt (leave the version unpinned to receive the latest updates):
crowdstrike-foundry-functioncrowdstrike-falconpyImport 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, Optionalfrom logging import Loggerfrom crowdstrike.foundry.function import Function, Request, Response, APIErrorfrom 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()Adding API scopes
Section titled “Adding API scopes”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:readIf 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.
Calling API integrations from functions
Section titled “Calling API integrations from functions”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_idmust be the integration’s exact name as defined in your app. Using the wrong identifier causes a 404 error.
Working with collections
Section titled “Working with collections”Use FalconPy’s CustomStorage service class with PascalCase method names:
import osfrom 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"
# Writeresponse = custom_storage.PutObject( body=json_data, collection_name=collection_name, object_key=event_id)
# Search with FQLquery_response = custom_storage.SearchObjects( filter=f"event_id:'{event_id}'", collection_name=collection_name, limit=5)The
ext_headersparameter handles theX-CS-APP-IDheader that Foundry uses for app-scoped access. Set theAPP_IDenvironment variable for local testing.
Local testing
Section titled “Local testing”With Docker
Section titled “With Docker”If you have Docker installed and running:
foundry functions runThis starts a Docker container with your function. The port is assigned automatically — check the output for the port number:
curl --request POST --url http://localhost:65463/ \ --header 'content-type: application/json' \ --data '{ "body": { "name": "Matt" }, "method": "POST", "url": "/hello" }'Without Docker
Section titled “Without Docker”Run the main.py file directly from the function directory:
cd functions/hellopython -m venv .venvsource .venv/bin/activatepip install -r requirements.txtpython main.pyThis starts the function on port 8081. To test FalconPy API calls locally, set environment variables:
export FALCON_CLIENT_ID=...export FALCON_CLIENT_SECRET=...python main.pyFind your credentials in ~/.config/foundry/configuration.yml.
Unit testing
Section titled “Unit testing”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:
cd functions/hellopytestSharing functions with workflows
Section titled “Sharing functions with workflows”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
Limitations
Section titled “Limitations”| Limit | Value |
|---|---|
| Maximum request payload | 124 KB (JSON portion) |
| Maximum response payload | 120 KB |
| Default execution timeout | 30 seconds (configurable up to 900 seconds) |
| Default memory | 256 MB (configurable up to 1 GB) |
| Maximum concurrent executions | 100 |
| Maximum package size (including dependencies) | 50 MB |
Next steps
Section titled “Next steps”- Functions: UI Editor — Browser-based Python editor
- Functions Overview — Handler structure for all languages
- Collections — Persistent data storage
- Workflow Templates — Use functions in SOAR workflows