{"id":28409962,"url":"https://github.com/crowdstrike/foundry-fn-python","last_synced_at":"2026-02-24T13:01:32.253Z","repository":{"id":186978578,"uuid":"675832839","full_name":"CrowdStrike/foundry-fn-python","owner":"CrowdStrike","description":null,"archived":false,"fork":false,"pushed_at":"2025-06-12T09:32:07.000Z","size":1235,"stargazers_count":5,"open_issues_count":0,"forks_count":2,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-06-22T23:35:17.707Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CrowdStrike.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":"CITATION.cff","codeowners":null,"security":"SECURITY.md","support":"SUPPORT.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-08-07T20:47:14.000Z","updated_at":"2025-06-11T14:46:40.000Z","dependencies_parsed_at":"2023-10-16T23:22:34.422Z","dependency_job_id":"ca32fca2-9b86-413d-8e57-9f9772736ae4","html_url":"https://github.com/CrowdStrike/foundry-fn-python","commit_stats":null,"previous_names":["crowdstrike/foundry-fn-python"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/CrowdStrike/foundry-fn-python","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CrowdStrike%2Ffoundry-fn-python","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CrowdStrike%2Ffoundry-fn-python/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CrowdStrike%2Ffoundry-fn-python/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CrowdStrike%2Ffoundry-fn-python/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CrowdStrike","download_url":"https://codeload.github.com/CrowdStrike/foundry-fn-python/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CrowdStrike%2Ffoundry-fn-python/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264139007,"owners_count":23563239,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-06-02T11:09:27.148Z","updated_at":"2026-02-24T13:01:27.211Z","avatar_url":"https://github.com/CrowdStrike.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cpicture\u003e\n  \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/CrowdStrike/falconpy/main/docs/asset/cs-logo-red.png\"\u003e\n  \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"https://raw.githubusercontent.com/CrowdStrike/falconpy/main/docs/asset/cs-logo.png\"\u003e\n  \u003cimg alt=\"CrowdStrike Logo.\" src=\"https://raw.githubusercontent.com/CrowdStrike/falconpy/main/docs/asset/cs-logo-red.png\"\u003e\n\u003c/picture\u003e\n\n# Falcon Foundry Function as a Service Python FDK\n\n`foundry-fn-python` is a community-driven, open source project designed to enable the authoring of functions.\nWhile not a formal CrowdStrike product, the `foundry-fn-python` project and the `crowdstrike-foundry-function` FDK package are maintained by CrowdStrike and supported in partnership with the open source developer community.\n\n## Installation ⚙️\n\n### Installation via `pip`\n\nThe FDK can be installed or updated via `pip install`:\n\n```shell\npython3 -m pip install crowdstrike-foundry-function\n```\n\n## Quickstart 💫\n\n### Example Code\n\nAdd the FDK to your project by following the [installation](#installation) instructions above,\nthen create your `main.py` that contains your handler implementation:\n\n```python\nfrom crowdstrike.foundry.function import (\n    APIError,\n    Request,\n    Response,\n    Function,\n)\n\n\nfunc = Function.instance()\n\n# An example POST handler\n@func.handler(method='POST', path='/my-resource')\ndef on_post(request: Request) -\u003e Response:\n\n    # Validate the request body\n    if 'name' not in request.body:\n        # This example expects 'name' field in the request body.\n        # Return an error response (400 - Bad Request) if not provided by the caller\n        return Response(\n            code=400,\n            errors=[APIError(code=400, message='name field is missing from request body')]\n        )\n\n    # Process the request\n    new_resource_id = 1\n    # ...snip...\n\n    # Return a success response\n    return Response(\n        body={\n           'result': f'Resource with name {request.body[\"name\"]} is created.',\n           'id': new_resource_id\n        },\n        code=200,\n    )\n\n\nif __name__ == '__main__':\n    func.run()\n```\n\n### Example breakdown\n\n#### The Function object\nThe `Function` class wraps the Foundry Function implementation. A `Function` instance can consist of one or more handlers, with each corresponding to an endpoint. You should only have one `Function` object defined per function implemented within your Foundry application. Multiple instances will result in unexpected behavior.\n\n```python\nfunc = Function.instance()\n```\n\n#### The function handler decorator\nThe handler decorator defines a Python method as handler for a specific endpoint. This handler must have the `method` and `path` keywords defined. The `method` keyword will correspond to one of the supported HTTP methods (`GET`, `POST`, `PUT`, `PATCH` or `DELETE`). The `path` keyword will define the URL used to trigger this method, and should be unique.\n\n```python\n@func.handler(method='POST', path='/my-resource')\n```\n\n#### Method details - Request\nOur python handler function is decorated with `@func.handler`. The first argument to our method must be a `Request` object which defines the HTTP request payload and metadata.\n\nA `Request` object consists of:\n\n* `body`: The request payload as given in the Function Gateway `body` payload field. This will be deserialized as a dictionary (`dict[str, Any]`).\n* `params`: The request headers (`params.header`) and query string parameters (`params.query`).\n* `url`: The request path relative to the function. This is a string.\n* `method`: The request HTTP method or verb.\n* `access_token`: Caller-supplied access token.\n\nIn this example we've named our method `on_post`, but you may name the method whatever you wish.\n\n```python\ndef on_post(request: Request) -\u003e Response:\n```\n\n#### Method details - Response\nThe return type for our method should be a `Response` object.\n\n##### Successful responses\nA successful response will be a `Response` object containing the fields `body` (a dictionary containing the response returned to the function) and `code` (the HTTP status code returned to the function).\n\n```python\n# Return a success response\nreturn Response(\n    body={\n        'result': f'Resource with name {request.body[\"name\"]} is created.',\n        'id': new_resource_id\n    },\n    code=200,\n)\n```\n\n##### Error responses\nAn unsuccessful response will be a `Response` object containing the fields `errors` (a list of `APIError` objects) and `code` (the HTTP status code returned to the function).\n\nAn `APIError` object will contain a `code` indicating the type of the error and a `message` which should contain the error text.\n\nIf no `code` is provided as part of the `Response` object, this value will be derived from the greatest valid HTTP code present within the `APIError` list.\n\n```python\nreturn Response(\n    code=400,\n    errors=[APIError(code=400, message='id field is missing from request params')]\n)\n```\n\n#### Running the function\nThe runner method is the general starting point for execution of your function and will be executed when your code is called by Foundry. This causes the `Function` to initialize and start execution. This should be the last line of your script as code defined after the `func.run()` statement may not be executed. You may implement code before this statement as necessary.\n\n```python\nif __name__ == '__main__':\n    func.run()\n```\n\n#### Retrieving parameters passed to your Function\nYou may retrieve query string values passed to your function by accessing the `request.params.query` dictionary.\n\n```python\nresource_id = request.params.query.get(\"id\")\n```\n\n\n#### Additional HTTP method examples\nDifferent types of HTTP requests will follow the same pattern demonstrated in our `POST` request above.\n\n##### HTTP GET\n\n```python\nfrom crowdstrike.foundry.function import (\n    APIError,\n    Request,\n    Response,\n    Function,\n)\n\n\nfunc = Function.instance()\n\n# An example GET handler\n@func.handler(method='GET', path='/my-resource')\ndef on_get(request: Request) -\u003e Response:\n\n    # Fetch the requested resources\n    resources = []\n    # ...snip...\n\n    # Return the requested resources\n    return Response(\n        body={'resources': resources},\n        code=200,\n    )\n\n\nif __name__ == '__main__':\n    func.run()\n```\n\n##### HTTP PUT\n\n```python\nfrom crowdstrike.foundry.function import (\n    APIError,\n    Request,\n    Response,\n    Function,\n)\n\n\nfunc = Function.instance()\n\n# An example PUT handler\n@func.handler(method='PUT', path='/my-resource')\ndef on_put(request: Request) -\u003e Response:\n\n    # Obtain the id of the resource to update from the request query parameters\n    resource_id = request.params.query.get('id')\n    if not resource_id:\n        # This example expects 'id' field in the request query parameters.\n        # Returns an error response (400 - Bad Request) if not provided by the caller\n        return Response(\n            code=400,\n            errors=[APIError(code=400, message='id field is missing from request params')]\n        )\n\n    # Get the update data provided in the request body and\n    # Update the resource with the data provided\n    data = request.body.get('data')\n    # ...snip...\n\n    # Return success with the updated resource info\n    return Response(\n        body={\n           'result': f'Resource {resource_id} is updated successfully.',\n           'data': data\n        },\n        code=200,\n    )\n\n\nif __name__ == '__main__':\n    func.run()\n```\n\n##### HTTP DELETE\n\n```python\nfrom crowdstrike.foundry.function import (\n    APIError,\n    Request,\n    Response,\n    Function,\n)\n\n\nfunc = Function.instance()\n\n# An example DELETE handler\n@func.handler(method='DELETE', path='/my-resource')\ndef on_delete(request: Request) -\u003e Response:\n\n    # Obtain the id of the resource to update from the request query parameters\n    resource_id = request.params.query.get('id')\n    if not resource_id:\n        # This example expects 'id' field in the request query parameters.\n        # Returns an error response (400 - Bad Request) if not provided by the caller\n        return Response(\n            code=400,\n            errors=[APIError(code=400, message='id field is missing from request params')]\n        )\n\n    # Delete the requested resource\n    # ...snip...\n\n    # Return success back to the caller\n    return Response(\n        code=200,\n    )\n\n\nif __name__ == '__main__':\n    func.run()\n```\n\n\n### Testing locally\n\nThe FDK provides an out-of-the-box runtime for executing the function.\n\n#### Executing your code\n\u003e [!NOTE]\n\u003e A basic HTTP server will be started to listen on port 8081 when executing your code locally.\n\n```shell\ncd my-project\npython3 main.py\n```\n\nYou can use `curl` or another python application to make requests to the web server that has been started.\n\n##### Example POST request\n\n```shell\n# Test POST /my-resource request\ncurl --location 'http://localhost:8081' \\\n  -H 'Content-Type: application/json' \\\n  --data '{\n    \"body\": {\n        \"name\": \"bar\"\n    },\n    \"method\": \"POST\",\n    \"url\": \"/my-resource\"\n}'\n```\n\n##### Example GET request\n\n```shell\n# Test GET /my-resource request\ncurl --location 'http://localhost:8081' \\\n  -H 'Content-Type: application/json' \\\n  --data '{\n    \"method\": \"GET\",\n    \"url\": \"/my-resource\"\n}'\n```\n\n##### Example PUT request\n\n```shell\n# Test PUT /my-resource request\ncurl --location 'http://localhost:8081' \\\n  -H 'Content-Type: application/json' \\\n  --data '{\n    \"body\": {\n        \"name\": \"bar\",\n    },\n    \"params\": {\n        \"query\": {\n          \"id\": \"12345\"\n        }\n    },\n    \"method\": \"PUT\",\n    \"url\": \"/my-resource\"\n}'\n```\n\n##### Example DELETE request\n\n```shell\n# Test DELETE /my-resource request\ncurl --location 'http://localhost:8081' \\\n  -H 'Content-Type: application/json' \\\n  --data '{\n    \"params\": {\n        \"query\": {\n          \"id\": \"12345\"\n        }\n    },\n    \"method\": \"DELETE\",\n    \"url\": \"/my-resource\"\n}'\n```\n\n#### Executing your code without an HTTP server\n\nIf you prefer to test your function locally without starting an HTTP server, you can provide the request payload in a JSON file on the command line.\n\nFirst, create a JSON file containing your request payload.\nExample `request_payload.json` file:\n```shell\n{\n    \"body\": {\n        \"name\": \"bar\"\n    },\n    \"method\": \"POST\",\n    \"url\": \"/my-resource\"\n}\n```\n\nThen invoke your function handler as follows:\n\n```shell\npython3 main.py --data ./request_payload.json\n```\n\nThis will execute the requested function handler and print the response returned, including the response status code, body and headers.\n\nYou can also provide request headers to your function on the command line:\n```shell\npython3 main.py --data request_payload.json --header \"Content-Type: application/json\" --header \"X-CUSTOM-HEADER: testing\"\n```\n\n## Leveraging the FalconPy SDK to interact with CrowdStrike APIs inside of your Foundry function\nFoundry function authors should include `crowdstrike-falconpy` within their _requirements.txt_ file and then import `falconpy` explicitly in their function code.\n\nYou may use any [FalconPy Service Class](https://falconpy.io/Home.html#service-collections) or the [FalconPy Uber Class](https://falconpy.io/Usage/Basic-Uber-Class-usage.html) within your function.\n\n### General FalconPy usage information\nFalconPy implements [Context Authentication](https://falconpy.io/Usage/Authenticating-to-the-API.html#context-authentication) for use within Foundry Functions, removing the need for developers to provide their `access_token` to the class as this value is provided by context when the function is executed.\n\n\u003e [!TIP]\n\u003e If you are instantiating a FalconPy class within your method, you will need to do this for every method you implement. If you instantiate the FalconPy class outside of your method, but before the `func.run()` statement, this object will be available to all methods defined in your function code.\n\nTo test the function locally without having to adjust your code, you can set the following environment variables in your local environment:\n\n| Variable Name | Purpose |\n| :--- | :--- |\n| `FALCON_CLIENT_ID`     | CrowdStrike Falcon API client ID     |\n| `FALCON_CLIENT_SECRET` | CrowdStrike Falcon API client secret |\n\n\n#### FalconPy usage example\n```python\n\nfrom falconpy import Hosts\nfrom crowdstrike.foundry.function import (\n    Function,\n    Request,\n    Response,\n    APIError\n)\n\n\nfunc = Function.instance()\n\n\n@func.handler(method='POST', path='/hosts-query')\ndef on_hosts_query(request: Request) -\u003e Response:\n\n    # get the requested Host IDs from the request body\n    host_ids = request.body.get(\"ids\")\n    if not host_ids:\n       return Response(\n            code=400,\n            errors=[APIError(code=400, message='Required host ids are not provided')]\n        )\n\n    # Initialize falconpy client for Hosts API\n    # This example uses context authentication\n    hosts_client = Hosts()\n\n    # Call falconpy API to fetch the details of the requested hosts\n    api_result = hosts_client.get_device_details_v1(ids=host_ids)\n    if api_result['status_code'] != 200:\n        # falconpy API returned an error\n        response = Response(\n           code=api_result['status_code'],\n            errors=[\n               APIError(code=api_result['status_code'], message=\"falconpy API call failed\")\n            ]\n        )\n    else:\n       # falconpy API was successful, return the requested data\n        response = Response(\n            body={\n                'hosts': api_result['body']['resources']\n            },\n            code=200\n        )\n\n    return response\n\nif __name__ == '__main__':\n    func.run()\n```\n\n## Using custom configurations and debug logging\nFoundry supports custom configurations and debug logging to support developers with the implementation of their functions.\n\n### Implementing custom configurations\nUsing a custom configuration within a function is optional and may be provided as a JSON file. This functionality is intended to give the developer a location to store custom configuration data, such as API keys and credentials, in a secure manner when the function is deployed on Falcon platform.\n\n\u003e [!NOTE]\n\u003e The configuration is encrypted with a unique key per function in the cloud.\n\nTo utilize a custom configuration within a function, include the `config` keyword argument as shown in the example below.\n\nThe `config` keyword is an optional argument to the handler function and must be the second argument if provided.\n\n### Enabling logging\nLogging for a function is optional but adding log messages to functions can make triage and debugging easier when troubleshooting problems. When a function is deployed on the Falcon platform, the messages logged with the provided `logger` are formatted in a custom manner with fields injected to assist with working within the Falcon logging infrastructure.\n\n\u003e [!NOTE]\n\u003e You may use native [FalconPy logging](https://falconpy.io/Usage/Logging.html) in conjunction with your function logger config by providing the `debug` keyword when you instantiate your FalconPy class.\n\nTo utilize logging in a function, include the `logger` parameter as shown in the example below.\n\n`logger` is an optional parameter to the handler function and must the third parameter if provided.\n\n\n```python\nfrom logging import Logger\nfrom typing import Union, Any\nfrom falconpy import Hosts\nfrom crowdstrike.foundry.function import (\n    Function,\n    Request,\n    Response,\n    APIError\n)\n\n\nfunc = Function.instance()\n\n\n@func.handler(method='POST', path='/hosts-query')\ndef on_hosts_query(request: Request, config: Union[dict[str, Any], None], logger: Logger) -\u003e Response:\n\n    logger.info(\"POST handler for /hosts-query is invoked\")\n\n    # get the requested Host IDs from the request body\n    host_ids = request.body.get(\"ids\")\n    if not host_ids:\n       logger.error(\"ids argument is missing from request parameters\")\n       return Response(\n            code=400,\n            errors=[APIError(code=400, message='Required host ids are not provided')]\n        )\n\n    # Example config provided to the function\n    action = \"Dev resource update\"\n    if config and config.get(\"is_production\", False):\n        action = \"Production resource update\"\n\n    # Initialize falconpy client for Hosts API and enable debugging\n    hosts_client = Hosts(debug=True)\n\n    # Call falconpy API to fetch the details of the requested hosts\n    api_result = hosts_client.get_device_details_v1(ids=host_ids)\n    if api_result['status_code'] != 200:\n        # FalconPy SDK returned an error\n        response = Response(\n           code=api_result['status_code'],\n            errors=[\n               APIError(code=api_result['status_code'], message=\"FalconPy API call failed\")\n            ]\n        )\n    else:\n       # falconpy API was successful, return the requested data\n        response = Response(\n            body={\n                'hosts': api_result['body']['resources'],\n                'action': action\n            },\n            code=200\n        )\n\n    return response\n\nif __name__ == '__main__':\n    func.run()\n```\n\n\n---\n\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/CrowdStrike/foundry-fn-python/main/docs/asset/cs-logo-footer.png\"\u003e\u003cBR/\u003e\u003cimg width=\"250px\" src=\"https://raw.githubusercontent.com/CrowdStrike/foundry-fn-python/main/docs/asset/adversary-red-eyes.png\"\u003e\u003c/P\u003e\n\u003ch3\u003e\u003cP align=\"center\"\u003eWE STOP BREACHES\u003c/P\u003e\u003c/h3\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcrowdstrike%2Ffoundry-fn-python","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcrowdstrike%2Ffoundry-fn-python","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcrowdstrike%2Ffoundry-fn-python/lists"}