diff --git a/samples/README.md b/samples/README.md
index ebf383d0..7278add3 100644
--- a/samples/README.md
+++ b/samples/README.md
@@ -95,6 +95,13 @@ The following samples are categorized by CrowdStrike product, and further catego
| [Asset Management (Discover)](#asset-management-samples) | List discovered hosts
Spyglass |
| [Vulnerability Management (Spotlight)](#vulnerability-management-samples) | Find vulnerable hosts by CVE ID
CISA DHS Known Exploited Vulnerabilities
Spotlight Quick Report |
+
+
+### [Fusion and Foundry](#fusion-and-foundry-apis)
+| Topic | Samples |
+| :-- | :-- |
+| [Workflows](#workflows-samples) | Workflow Manager (terminal)
Workflows Manager (GUI) |
+
### [Threat Intelligence](#threat-intelligence-apis)
@@ -1794,6 +1801,84 @@ This sample demonstrates the following CrowdStrike Spotlight Vulnerability API a
+
+
+
+
+Fusion and Foundry
+
+
+
+Workflows
(click to expand)
+The samples in this section focus on the CrowdStrike Falcon Workflows API service collection.
+
+
+- [Workflow Manager (terminal version)](#workflow-manager-terminal-version)
+- [Workflow Manager (gui version)](#workflow-manager-gui-version)
+
+#### Workflow Manager (terminal version)
+
+This sample demonstrates how to leverage the Workflows API to provide the following functionality:
+ - List all workflows
+ - Execute a workflow
+ - List all executions for a workflow
+ - Print the results of a workflow execution
+ - Import a workflow
+ - Export a workflow
+
+[![Falcon Fusion Workflows](https://img.shields.io/badge/Service%20Class-Falcon_Fusion_SOAR_Workflows_Manager_[terminal_version]-silver?style=for-the-badge&labelColor=C30A16&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAAOCAYAAAAi2ky3AAABhWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw1AUhU9TpaIVBzuIOGSoDmJBVEQ3rUIRKoRaoVUHk5f+CE0akhQXR8G14ODPYtXBxVlXB1dBEPwBcXNzUnSREu9LCi1ifPB4H+e9c7jvXkColZhmtY0Cmm6bqURczGRXxNAruhAEMI1hmVnGrCQl4bu+7hHg512MZ/m/+3N1qzmLAQGReIYZpk28Tjy5aRuc94kjrCirxOfEIyYVSPzIdcXjN84FlwWeGTHTqTniCLFYaGGlhVnR1IgniKOqplO+kPFY5bzFWStVWKNO/sNwTl9e4jrtASSwgEVIEKGggg2UYCNGp06KhRTdx338/a5fIpdCrg0wcsyjDA2y6wefwe/eWvnxMS8pHAfaXxznYxAI7QL1quN8HztO/QQIPgNXetNfrgFTn6RXm1r0COjZBi6um5qyB1zuAH1PhmzKrsTnL+TzwPsZjSkL9N4Cnate3xr3OH0A0tSr5A1wcAgMFSh7zeffHa19+/dNo38/hq9yr+iELI0AAAAGYktHRAAAAAAAAPlDu38AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflDAsTByz7Va2cAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAAAYBJREFUKM+lkjFIlVEYht/zn3sFkYYUyUnIRcemhCtCU6JQOLiIU+QeJEQg6BBIm0s4RBCBLjq5OEvgJC1uOniJhivesLx17/97/vO9b4NK4g25157hfHCGB773/cA0HZIEAKiMj+LWiOxljG/i96pnCFP58XHnrWX2+9cj0dYl9Yu2FE9/9rXrcAAgs2eSyiBfOe/XRD503h/CuffOubQVUXL+Jh9BllzBbyJJBgDclVkO4Kukd8zzkXJbeUljIldFTstsmSHM6S81ma2KfPKlFdkGAMY4wzx/bbXapMy21My+YizdKNq5mDzLkrxafSxySFKjSWX2oTmjKzz4vN0r2lOFcL/Q3V0/mX95ILMXTTGYVfaut/aP2+oCMAvnZgCcsF5fcR0dg65YHAdwB+QApADvu0AuOe/ftlJAD7Nsgmm6yBjDtfWORJZlNtFyo/lR5Z7MyheKA5ktSur7sTAHazSG27pehjAiaVfkN8b4XFIJ/wOzbOx07VNRUuHy7w98CzCcGPyWywAAAABJRU5ErkJggg==)](workflows##workflow-manager-terminal-version)
+
+##### Workflows API operations discussed
+This sample demonstrates the following CrowdStrike Workflows API operations:
+
+| Operation | Description |
+| :--- | :--- |
+| [WorkflowDefinitionsCombined](https://falconpy.io/Service-Collections/Workflows.html#workflowdefinitionscombined) | Search workflow definitions based on the provided filter. |
+| [WorkflowDefinitionsExport](https://falconpy.io/Service-Collections/Workflows.html#workflowdefinitionsexport) | Export a workflow definition for the given definition ID. |
+| [WorkflowDefinitionsImport](https://falconpy.io/Service-Collections/Workflows.html#workflowdefinitionsimport) | Import a workflow definition from a file. |
+| [WorkflowExecute](https://falconpy.io/Service-Collections/Workflows.html#workflowexecute) | Execute an on-demand workflow. The response will contain the execution ID. |
+| [WorkflowExecutionsCombined](https://falconpy.io/Service-Collections/Workflows.html#workflowexecutionscombined) | Search workflow executions based on the provided filter. |
+| [WorkflowExecutionsResults](https://falconpy.io/Service-Collections/Workflows.html#workflowexecutionsresults) | Get execution result of a given execution. |
+
+---
+
+#### Workflow Manager (GUI version)
+
+Like the sample above, this sample demonstrates how to leverage the Workflows API to provide the following functionality:
+ - List all workflows
+ - Execute a workflow
+ - List all executions for a workflow
+ - Print the results of a workflow execution
+ - Import a workflow
+ - Export a workflow
+
+Additional functionality provided by this sample include:
+ - Full GUI interface
+ - Activity logging (to a local file)
+ - Exporting list results to CSV
+
+[![Falcon Fusion Workflows](https://img.shields.io/badge/Service%20Class-Falcon_Fusion_SOAR_Workflows_Manager_[GUI_version]-silver?style=for-the-badge&labelColor=C30A16&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAAOCAYAAAAi2ky3AAABhWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw1AUhU9TpaIVBzuIOGSoDmJBVEQ3rUIRKoRaoVUHk5f+CE0akhQXR8G14ODPYtXBxVlXB1dBEPwBcXNzUnSREu9LCi1ifPB4H+e9c7jvXkColZhmtY0Cmm6bqURczGRXxNAruhAEMI1hmVnGrCQl4bu+7hHg512MZ/m/+3N1qzmLAQGReIYZpk28Tjy5aRuc94kjrCirxOfEIyYVSPzIdcXjN84FlwWeGTHTqTniCLFYaGGlhVnR1IgniKOqplO+kPFY5bzFWStVWKNO/sNwTl9e4jrtASSwgEVIEKGggg2UYCNGp06KhRTdx338/a5fIpdCrg0wcsyjDA2y6wefwe/eWvnxMS8pHAfaXxznYxAI7QL1quN8HztO/QQIPgNXetNfrgFTn6RXm1r0COjZBi6um5qyB1zuAH1PhmzKrsTnL+TzwPsZjSkL9N4Cnate3xr3OH0A0tSr5A1wcAgMFSh7zeffHa19+/dNo38/hq9yr+iELI0AAAAGYktHRAAAAAAAAPlDu38AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQflDAsTByz7Va2cAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAAAYBJREFUKM+lkjFIlVEYht/zn3sFkYYUyUnIRcemhCtCU6JQOLiIU+QeJEQg6BBIm0s4RBCBLjq5OEvgJC1uOniJhivesLx17/97/vO9b4NK4g25157hfHCGB773/cA0HZIEAKiMj+LWiOxljG/i96pnCFP58XHnrWX2+9cj0dYl9Yu2FE9/9rXrcAAgs2eSyiBfOe/XRD503h/CuffOubQVUXL+Jh9BllzBbyJJBgDclVkO4Kukd8zzkXJbeUljIldFTstsmSHM6S81ma2KfPKlFdkGAMY4wzx/bbXapMy21My+YizdKNq5mDzLkrxafSxySFKjSWX2oTmjKzz4vN0r2lOFcL/Q3V0/mX95ILMXTTGYVfaut/aP2+oCMAvnZgCcsF5fcR0dg65YHAdwB+QApADvu0AuOe/ftlJAD7Nsgmm6yBjDtfWORJZlNtFyo/lR5Z7MyheKA5ktSur7sTAHazSG27pehjAiaVfkN8b4XFIJ/wOzbOx07VNRUuHy7w98CzCcGPyWywAAAABJRU5ErkJggg==)](workflows/#workflow-manager-gui-version)
+
+##### Workflows API operations discussed
+This sample demonstrates the following CrowdStrike Workflows API operations:
+
+| Operation | Description |
+| :--- | :--- |
+| [WorkflowDefinitionsCombined](https://falconpy.io/Service-Collections/Workflows.html#workflowdefinitionscombined) | Search workflow definitions based on the provided filter. |
+| [WorkflowDefinitionsExport](https://falconpy.io/Service-Collections/Workflows.html#workflowdefinitionsexport) | Export a workflow definition for the given definition ID. |
+| [WorkflowDefinitionsImport](https://falconpy.io/Service-Collections/Workflows.html#workflowdefinitionsimport) | Import a workflow definition from a file. |
+| [WorkflowExecute](https://falconpy.io/Service-Collections/Workflows.html#workflowexecute) | Execute an on-demand workflow. The response will contain the execution ID. |
+| [WorkflowExecutionsCombined](https://falconpy.io/Service-Collections/Workflows.html#workflowexecutionscombined) | Search workflow executions based on the provided filter. |
+| [WorkflowExecutionsResults](https://falconpy.io/Service-Collections/Workflows.html#workflowexecutionsresults) | Get execution result of a given execution. |
+
+
+
+[Back to top](#falconpy-sample-library) | [How to authenticate](#authentication-for-these-examples) | [Table of Contents](#fusion-and-foundry-toc)
+
+---
+
+
+
diff --git a/samples/workflows/README.md b/samples/workflows/README.md
new file mode 100644
index 00000000..afa48e1b
--- /dev/null
+++ b/samples/workflows/README.md
@@ -0,0 +1,794 @@
+![CrowdStrike Falcon](https://raw.githubusercontent.com/CrowdStrike/falconpy/main/docs/asset/cs-logo.png)
+[![CrowdStrike Subreddit](https://img.shields.io/badge/-r%2Fcrowdstrike-white?logo=reddit&labelColor=gray&link=https%3A%2F%2Freddit.com%2Fr%2Fcrowdstrike)](https://reddit.com/r/crowdstrike)
+
+# Workflows examples
+The examples within this folder focus on leveraging CrowdStrike's Falcon Falcon Fusion SOAR API.
+
+- [Workflow Manager (terminal)](#workflow-manager-terminal-version)
+- [Workflow Manager (gui)](#workflow-manager-gui-version)
+
+## Workflow Manager (Terminal version)
+This sample demonstrates how to leverage the Workflows API to provide the following functionality:
+
+- List all workflows
+- Execute a workflow
+- List all executions for a workflow
+- Print the results of a workflow execution
+- Import a workflow
+- Export a workflow
+
+### Running the program
+In order to run this demonstration, you you will need access to CrowdStrike API keys with the following scopes:
+
+| Service Collection | Scope |
+| :---- | :---- |
+| Workflows | __READ__, __WRITE__ |
+
+#### Required packages
+In order to run this sample, you will need to have the [`tabulate`](https://pypi.org/project/tabulate/) and [`termcolor`](https://pypi.org/project/termcolor/) packages installed.
+
+### Execution syntax
+This sample leverages simple command-line arguments to implement functionality.
+
+#### Basic usage
+Execute the default example. This will default to listing all workflows discovered in tabular format.
+
+```shell
+python3 workflow_manager.py -k $FALCON_CLIENT_ID -s $FALCON_CLIENT_SECRET
+```
+
+> [!TIP]
+> This sample supports [Environment Authentication](https://falconpy.io/Usage/Authenticating-to-the-API.html#environment-authentication), meaning you can execute any of the command lines shown below without providing credentials if you have the values `FALCON_CLIENT_ID` and `FALCON_CLIENT_SECRET` defined in your environment.
+
+```shell
+python3 workflow_manager.py
+```
+
+Change the CrowdStrike region with the `-b` argument.
+
+```shell
+python3 workflow_manager.py -b usgov1
+```
+
+Execute a workflow using a custom payload.
+
+```shell
+python3 workflow_manager.py -e -i $WORKFLOW_DEFINITION_ID -p {'key': 'value'}
+```
+
+List all executions of a workflow using the `-le` argument.
+
+```shell
+python3 workflow_manager.py -le -i $WORKFLOW_DEFINITION_ID
+```
+
+Retrieve the results of an execution with the `-g` argument.
+
+```shell
+python3 workflow_manager.py -g -i $WORKFLOW_EXECUTION_ID
+```
+
+Export a workflow to a local YAML file using the `-ex` argument.
+
+```shell
+python3 workflow_manager.py -ex $EXPORT_FILENAME -i $WORKFLOW_DEFINITION_ID
+```
+
+> [!NOTE]
+> Exporting to an existing file will overwrite it's contents.
+
+Import a workflow from a local YAML file using the `-im` argument.
+
+```shell
+python3 workflow_manager.py -im $IMPORT_FILENAME
+```
+
+> [!NOTE]
+> If the workflow name defined within your workflow YAML file exists within your tenant, an error will be thrown.
+> Use the `-n` argument to import this file as a new workflow with a new name.
+
+```shell
+python3 workflow_manager.py -im $IMPORT_FILENAME -n $WORKFLOW_NEW_NAME
+```
+
+> [!TIP]
+> You can validate this workflow import without saving by providing the `-v` argument with the above command.
+
+```shell
+python3 workflow_manager.py -im $IMPORT_FILENAME -n $WORKFLOW_NEW_NAME -v
+```
+
+API debugging can be enabled using the `-d` argument.
+
+```shell
+python3 workflow_manager.py -d
+```
+
+Adjust the output table format using the `-t` argument.
+
+```shell
+python3 workflow_manager.py -l -t fancy_grid
+```
+
+Swap to JSON output with the `-j` argument.
+
+```shell
+python3 workflow_manager.py -l -j
+```
+
+#### Command-line help
+Command-line help is available via the `-h` argument.
+
+```shell
+usage: workflow_manager.py [-h] [-d] [-i ID] [-p PAYLOAD] [-e] [-g] [-l] [-le] [-ex EXPORT_WORKFLOW] [-im IMPORT_WORKFLOW] [-n WORKFLOW_NAME] [-v] [-j] [-t TABLE_FORMAT] [-k CLIENT_ID]
+ [-s CLIENT_SECRET] [-b BASE_URL]
+
+Falcon Fusion SOAR workflow manager.
+
+ _______ __ _______ __ __ __
+| _ .----.-----.--.--.--.--| | _ | |_.----|__| |--.-----.
+|. 1___| _| _ | | | | _ | 1___| _| _| | <| -__|
+|. |___|__| |_____|________|_____|____ |____|__| |__|__|__|_____|
+|: 1 | |: 1 |
+|::.. . | CROWDSTRIKE FALCON |::.. . | FalconPy
+`-------' `-------'
+
+ ██ ██ ██ ████ ██
+░██ ░██ ░██ ░██░ ░██
+░██ █ ░██ ██████ ██████░██ ██ ██████ ░██ ██████ ███ ██
+░██ ███ ░██ ██░░░░██░░██░░█░██ ██ ░░░██░ ░██ ██░░░░██░░██ █ ░██
+░██ ██░██░██░██ ░██ ░██ ░ ░████ ░██ ░██░██ ░██ ░██ ███░██
+░████ ░░████░██ ░██ ░██ ░██░██ ░██ ░██░██ ░██ ░████░████
+░██░ ░░░██░░██████ ░███ ░██░░██ ░██ ███░░██████ ███░ ░░░██
+░░ ░░ ░░░░░░ ░░░ ░░ ░░ ░░ ░░░ ░░░░░░ ░░░ ░░░
+ ████ ████
+░██░██ ██░██ █████
+░██░░██ ██ ░██ ██████ ███████ ██████ ██░░░██ █████ ██████
+░██ ░░███ ░██ ░░░░░░██ ░░██░░░██ ░░░░░░██ ░██ ░██ ██░░░██░░██░░█
+░██ ░░█ ░██ ███████ ░██ ░██ ███████ ░░██████░███████ ░██ ░
+░██ ░ ░██ ██░░░░██ ░██ ░██ ██░░░░██ ░░░░░██░██░░░░ ░██
+░██ ░██░░████████ ███ ░██░░████████ █████ ░░██████░███
+░░ ░░ ░░░░░░░░ ░░░ ░░ ░░░░░░░░ ░░░░░ ░░░░░░ ░░░
+
+This sample demonstrates how to leverage the Workflows API to provide
+the following functionality:
+ - List all workflows (-l or --list-workflows)
+ - Execute a workflow (-e or --execute)
+ - List all executions for a workflow (-le or --list-executions)
+ - Print the results of a workflow execution (-g or --get_result)
+ - Import a workflow (-im {FILENAME} or --import-workflow {FILENAME})
+ - Export a workflow (-ex {FILENAME} or --export-workflow {FILENAME})
+
+Creation date: 11.06.2024 - jlangdev@CrowdStrike
+Modification date: 11.08.2024 - jshcodes@CrowdStrike
+
+This sample requires the following packages:
+- crowdstrike-falconpy >= 1.4.1
+- tabulate
+- termcolor
+
+options:
+ -h, --help show this help message and exit
+ -d, --debug Activate API debugging
+
+workflow arguments:
+ -i, --id ID Workflow definition or execution ID
+ -p, --payload PAYLOAD
+ Workflow execution payload
+
+command arguments:
+ -e, --execute Execute the workflow specified
+ -g, --get-result Retrieve a workflow execution result
+ -l, --list-workflows List all workflows
+ -le, --list-executions
+ List the executions for the workflow specified
+ -ex, --export-workflow EXPORT_WORKFLOW
+ Export a workflow to a local file.
+ Provide a filename for this argument. Example: 'exported.yml'
+ -im, --import-workflow IMPORT_WORKFLOW
+ Import a workflow from a local file.
+ Provide a filename for this argument. Example: 'to_import.yml'
+ -n, --workflow-name WORKFLOW_NAME
+ Name for the imported workflow
+ -v, --validate-only Validate the workflow only, do not save upon import
+
+formatting arguments:
+ -j, --json Display execution results in JSON format
+ -t, --table-format TABLE_FORMAT
+ Tabular display format
+
+authentication arguments (environment authentication supported):
+ -k, --falcon-client-id CLIENT_ID
+ CrowdStrike Falcon API ID
+ -s, --falcon-client-secret CLIENT_SECRET
+ CrowdStrike Falcon API secret
+ -b, --base-url BASE_URL
+ CrowdStrike Region (US1, US2, EU1, USGOV1, USGOV2)
+ Full URL is also supported.
+```
+
+### Example source code
+The source code for this example can be found [here](workflow_manager.py).
+
+---
+---
+---
+
+
+
+## Workflow Manager (GUI version)
+
+[Running the program](#running-the-program-1) || [Authentication](#authentication) || [Basic Usage](#basic-usage-1) ||
+[Advanced Usage](#advanced-usage) || [Command-line help](#command-line-help-1) || [Source Code](#example-source-code-1)
+
+Like the sample above, this sample demonstrates how to leverage the Workflows API to provide the following functionality:
+
+
+
+- List all workflows
+- Execute a workflow
+- List all executions for a workflow
+- Print the results of a workflow execution
+- Import a workflow
+- Export a workflow
+
+Additional functionality provided by this sample include:
+
+- Full GUI interface
+- Activity logging (to a local file)
+- Exporting list results to CSV
+
+### Running the program
+In order to run this demonstration, you you will need access to CrowdStrike API keys with the following scopes:
+
+| Service Collection | Scope |
+| :---- | :---- |
+| Workflows | __READ__, __WRITE__ |
+
+
+This application can be started using a simple command line without arguments.
+
+```shell
+python3 workflow_manager_gui.py
+```
+
+#### Required packages
+In order to run this sample, you will need to have the [`Gooey`](https://pypi.org/project/Gooey/), [`requests`](https://pypi.org/project/requests/) and [`tabulate`](https://pypi.org/project/tabulate/) packages installed.
+
+[Return to Summary](#workflow-manager-gui-toc)
+
+---
+---
+
+### Authentication
+
+
+
+API credentials can be specified on the __Environment__ tab.
+
+These will be pre-populated if provided by the command line or environment.
+
+> [!TIP]
+> These values can be provided on the command line using the `-k` and `-s` arguments or the `--client_id` and `--client_secret` arguments.
+> This sample also supports [Environment Authentication](https://falconpy.io/Usage/Authenticating-to-the-API.html#environment-authentication), meaning these values will be pre-populated for you from the variables `FALCON_CLIENT_ID` and `FALCON_CLIENT_SECRET` if they are present in the execution environment.
+
+[Return to Summary](#workflow-manager-gui-toc)
+
+---
+---
+
+### Basic Usage
+The GUI workflow manager application supports all of the same command functionality provided by the [terminal version](#workflow-manager-terminal-version).
+
+- [Listing workflows](#listing-workflows)
+- [Executing a workflow](#executing-a-workflow)
+- [Retrieving workflow execution IDs](#retrieving-all-executions-for-a-workflow)
+- [Getting an execution result](#getting-the-results-of-a-workflow-execution)
+- [Exporting workflows](#exporting-a-workflow-to-a-yaml-file)
+- [Importing workflows](#importing-a-workflow-from-a-yaml-file)
+
+#### Listing workflows
+
+
+
+To retrieve a list of all workflows within the tenant, select __*list_workflows*__ on the __Command__ tab.
+
+Listing all workflows within the tenant requires no additional parameters (beyond authentication).
+
+> [!NOTE]
+> This is the default command when no command is specified.
+
+
+
+
+
+Results will be shown in a console window.
+
+> [!TIP]
+> Review advanced configuration options below for more detail regarding
+> [table formatting](#formatting-output) and [outputing results to CSV](#exporting-list-results-to-csv).
+
+
+
+---
+
+#### Executing a workflow
+
+
+
+To execute a workflow, first select the `execute` action on the __Command__ tab.
+
+
+
+
+
+Provide the workflow definition ID of the workflow to execute in the __*id*__ field on the __Workflow__ tab.
+
+
+
+
+
+If authentication credentials are provided via the command line or detected within the environment, the application will attempt
+to display a dropdown of all available workflows.
+
+> [!TIP]
+> Bypass this behavior by providing the `-sk` or `--skip_preflight` command line argument when starting the application.
+
+
+
+
+
+The dropdown is editable and will accept custom values.
+
+
+
+
+
+Once your ID has been specified, provide the necessary execution payload for the workflow in the `payload` field.
+
+
+
+
+
+Clicking the __*Start*__ button will execute the options specified and display the results to the console.
+
+
+
+---
+
+#### Retrieving all executions for a workflow
+
+
+
+Select the `list_executions` option on the __Command__ tab to begin.
+
+
+
+
+
+Provide the desired workflow definition ID in the __*id*__ field on the __Workflow__ tab.
+
+
+
+
+
+Clicking the __*Start*__ button will execute the search.
+
+Results are displayed to the console.
+
+
+
+---
+
+#### Getting the results of a workflow execution
+
+
+
+To retrieve results for a specific execution, first select the `get_result` option on the __Command__ tab.
+
+
+
+
+
+Provide the Workflow execution ID in the __*execution_id*__ field on the __Workflow__ tab.
+
+
+
+
+
+To return results in indented JSON format, select the __*json*__ option on the __Environment__ tab.
+
+
+
+
+
+Clicking the __*Start*__ button will execute the request using the specified options and display the results to the console.
+
+> [!NOTE]
+> Device IDs were redacted for this screen shot but are typically shown by the application.
+
+
+
+---
+
+#### Exporting a workflow to a YAML file
+
+
+
+Select the __*workflow_export*__ option on the __Command__ tab.
+
+
+
+
+
+Provide the desired workflow definition ID in the __*id*__ field on the __Workflow__ tab.
+
+
+
+
+
+Use the __*export_workflow*__ field on the __Export__ tab to specify the save file for the export.
+
+
+
+
+
+This file will be saved in YAML format.
+
+
+
+
+
+
+The __*Browse*__ button may be used to specify this value.
+
+
+
+
+
+Clicking the __*Start*__ button will export the selected workflow to the specified file.
+
+If this file already exists, it will be overwritten.
+
+
+
+---
+
+#### Importing a workflow from a YAML file
+
+
+
+Select the __*workflow_import*__ option on the __Command__ tab.
+
+
+
+
+
+
+On the __Import__ tab, provide the location of the workflow template YAML file in the __*import_workflow*__ field.
+
+
+
+
+
+The __*Browse*__ button can be used to search and select the desired workflow template to import.
+
+
+
+
+
+If the workflow name defined within the YAML file already exists in your tenant, an error will be thrown.
+
+You can specify a new name for this workflow using the __*workflow_name*__ field.
+
+
+
+
+
+Use the __*validate*__ checkbox to specify that this workflow will be validated for successful import, but no action will be taken.
+
+
+
+
+
+Clicking the __*Start*__ button will begin the import as specified.
+
+Results will be displayed to the console upon completion.
+
+
+
+[Return to Summary](#workflow-manager-gui-toc)
+
+---
+---
+
+### Advanced Usage
+
+
+There are several advanced options that can be specified on the __Environment__ tab or via the command line.
+
+- [Formatting](#formatting-output)
+- [CSV list exports](#exporting-list-results-to-csv)
+- [Prefilling configuration options](#providing-configuration-via-the-command-line)
+- [Font size](#adjusting-the-console-display-font-size)
+- [Autostarting](#auto-starting-execution-via-the-command-line)
+- [Debugging](#debugging-api-activity)
+- [Logging results](#logging-results)
+
+#### Formatting output
+
+
+
+Specify the __*json*__ option to output results in formatted JSON.
+
+
+
+
+
+Different table formats may be selected using the __*table_format*__ dropdown.
+
+
+
+
+
+Output can compressed in the console to display when running multiple executions with the __*compress_output*__ option.
+
+
+
+---
+
+#### Exporting list results to CSV
+
+
+
+Select the _csv_ option in the __*table_format*__ dropdown field to export list results to CSV for the `list_workflows` and `list_executions` commands.
+
+Results will be saved to _**`workflows.csv`**_ or _**`workflow_executions.csv`**_ depending on the operation selected.
+
+Execution results are still displayed to the console using the _simple_ table format when the __*Start*__ button is pressed.
+
+
+
+---
+
+#### Providing configuration via the command line
+Configuration options may be specified on the command line when starting the application. These values will be pre-populated on the
+configuration form. Command line provided configuration options take precedence over values specified as defaults or detected within
+the running environment.
+
+##### Specifying the list executions command option and a workflow definition ID
+
+```shell
+python3 workflow_manager_gui.py -le -i $WORKFLOW_DEFINITION_ID
+```
+
+##### Importing a workflow using the command line
+
+```shell
+python3 workflow_manager_gui.py -im -iw $PATH_AND_FILENAME -n $NEW_WORKFLOW_NAME
+```
+
+---
+
+#### Adjusting the console display font size
+
+The font point size for the console display can be adjusted using a positional command line argument. This value should be an integer.
+
+```shell
+python3 workflow_manager_gui.py 10
+```
+
+> [!NOTE]
+> Positional command line arguments may be mixed with named arguments.
+
+```shell
+python3 workflow_manager_gui.py 14 -i $WORKFLOW_DEFINITION_ID -p {'HostNames': ['example-hostname']} -e
+```
+
+---
+
+#### Auto-starting execution via the command line
+
+Execution can be triggered at runtime by providing the `go` positional argument.
+
+```shell
+python3 workflow_manager_gui.py go -ex -ew $PATH_AND_FILENAME -i $WORKFLOW_DEFINITION_ID
+```
+
+> [!NOTE]
+> The font size and auto-execution positional arguments can be mixed together along with named arguments.
+> When using font size and auto-execution together, the font size should be specified first.
+
+```shell
+python3 workflow_manager_gui.py 11 go -g -ei $EXECUTION_ID
+```
+
+---
+
+#### Debugging API activity
+
+
+
+Select the __*debug*__ option to enable API debugging.
+
+This will show detailed information regarding interactions performed with the CrowdStrike Falcon API,
+listing endpoints used, payloads provided, and responses received.
+
+
+
+---
+
+#### Logging results
+
+
+
+To keep a seperate log file of all results produced by the application, use the __*logfile*__ field.
+
+
+
+
+
+The __*Browse*__ button can be used to select the log file.
+
+
+
+
+
+> [!WARNING]
+> If this file exists, you may be prompted to replace the existing file.
+>
+> This is a known issue. Log files will not be overwritten. Instead, results will be appended to the bottom of the
+> file regardless of the operating system message.
+
+
+
+[Return to Summary](#workflow-manager-gui-toc)
+
+---
+---
+
+#### Command-line help
+Command-line help is available via the `-h` argument.
+
+```shell
+usage: workflow_manager_gui.py [-h] (-l | -e | -le | -g | -ex | -im) [-i ID] [-ei EXECUTION_ID] [-p PAYLOAD]
+ [-n WORKFLOW_NAME] [-v] [-iw IMPORT_WORKFLOW] [-ew EXPORT_WORKFLOW] [-k CLIENT_ID]
+ [-s CLIENT_SECRET] [-b {auto,us1,us2,eu1,usgov1,usgov2}] [-lf LOGFILE] [-d] [-o] [-sk]
+ (-j |
+ -t {plain,simple,github,grid,simple_grid,rounded_grid,heavy_grid,mixed_grid,
+ double_grid,fancy_grid,outline,simple_outline,rounded_outline,heavy_outline,
+ mixed_outline,double_outline,fancy_outline,pipe,csv,orgtbl,asciidoc,jira,presto,
+ pretty,psql,rst,mediawiki,moinmoin,youtrack,html,unsafehtml,latex,latex_raw,
+ latex_booktabs,latex_longtable,textile,tsv})
+
+Falcon Fusion SOAR workflow manager.
+
+ _______ __ _______ __ __ __
+| _ .----.-----.--.--.--.--| | _ | |_.----|__| |--.-----.
+|. 1___| _| _ | | | | _ | 1___| _| _| | <| -__|
+|. |___|__| |_____|________|_____|____ |____|__| |__|__|__|_____|
+|: 1 | |: 1 |
+|::.. . | CROWDSTRIKE FALCON |::.. . | FalconPy 1.4.1+
+`-------' `-------'
+
+ ██ ██ ██ ████ ██
+░██ ░██ ░██ ░██░ ░██
+░██ █ ░██ ██████ ██████░██ ██ ██████ ░██ ██████ ███ ██
+░██ ███ ░██ ██░░░░██░░██░░█░██ ██ ░░░██░ ░██ ██░░░░██░░██ █ ░██
+░██ ██░██░██░██ ░██ ░██ ░ ░████ ░██ ░██░██ ░██ ░██ ███░██
+░████ ░░████░██ ░██ ░██ ░██░██ ░██ ░██░██ ░██ ░████░████
+░██░ ░░░██░░██████ ░███ ░██░░██ ░██ ███░░██████ ███░ ░░░██
+░░ ░░ ░░░░░░ ░░░ ░░ ░░ ░░ ░░░ ░░░░░░ ░░░ ░░░
+ ████ ████
+░██░██ ██░██ █████
+░██░░██ ██ ░██ ██████ ███████ ██████ ██░░░██ █████ ██████
+░██ ░░███ ░██ ░░░░░░██ ░░██░░░██ ░░░░░░██ ░██ ░██ ██░░░██░░██░░█
+░██ ░░█ ░██ ███████ ░██ ░██ ███████ ░░██████░███████ ░██ ░
+░██ ░ ░██ ██░░░░██ ░██ ░██ ██░░░░██ ░░░░░██░██░░░░ ░██
+░██ ░██░░████████ ███ ░██░░████████ █████ ░░██████░███
+░░ ░░ ░░░░░░░░ ░░░ ░░ ░░░░░░░░ ░░░░░ ░░░░░░ ░░░
+
+This sample demonstrates how to leverage the Workflows API to provide
+the following functionality:
+ - List all workflows
+ - Results can be exported to CSV
+ - Execute a workflow
+ - List all executions for a workflow
+ - Results can be exported to CSV
+ - Print the results of a workflow execution
+ - Import a workflow
+ - Export a workflow
+ - Optional logging of results to a file
+
+This version leverages the Gooey project to implement a simple GUI, command line
+arguments are supported but not required to specify execution configuration.
+
+Creation date: 11.06.2024 - Initial version, jlangdev@CrowdStrike
+Modification date: 11.08.2024 - Refactoring, jshcodes@CrowdStrike
+Modification date: 11.10.2024 - Add graphical interface, jshcodes@CrowdStrike
+
+This sample requires the following packages:
+- crowdstrike-falconpy >= 1.4.1
+- gooey
+- requests
+- tabulate
+
+options:
+ -h, --help show this help message and exit
+
+Command:
+ Workflow command to perform
+
+ -l, --list_workflows List all workflows
+ -e, --execute Execute the workflow specified on the Workflow tab
+ -le, --list_executions
+ List the executions for the workflow specified
+ -g, --get_result Retrieve a workflow execution result
+ -ex, --workflow_export
+ Export a workflow
+ -im, --workflow_import
+ Import a workflow
+
+Workflow:
+ Workflow or execution ID and workflow payload
+
+ -i, --id ID Workflow definition ID
+ -ei, --execution_id EXECUTION_ID
+ Workflow execution ID
+ -p, --payload PAYLOAD
+ Workflow execution payload
+
+Import:
+ Import a workflow from a file
+
+ -n, --workflow_name WORKFLOW_NAME
+ Name for the imported workflow
+ -v, --validate_only Validate the workflow only, do not save upon import
+ -iw, --import_workflow IMPORT_WORKFLOW
+ Location of the YAML workflow file to import
+
+Export:
+ Export a workflow to a file
+
+ -ew, --export_workflow EXPORT_WORKFLOW
+ Location to save the exported workflow (YAML format)
+ Use the Workflow tab to specify the desired workflow ID
+
+Environment:
+ Authentication and program execution options
+
+ -k, --client_id CLIENT_ID
+ CrowdStrike Falcon API ID
+ (pre-filled from environment or command line)
+ -s, --client_secret CLIENT_SECRET
+ CrowdStrike Falcon API secret
+ (pre-filled from environment or command line)
+ -b, --base_url {auto,us1,us2,eu1,usgov1,usgov2}
+ CrowdStrike Region
+ ('auto' not implemented for usgov1 or usgov2)
+ -lf, --logfile LOGFILE
+ Log output results to a local file as well as the console
+ -d, --debug Activate API debugging
+ -o, --compress_output
+ Compress display output
+ -sk, --skip_preflight
+ Skip preflight API lookups
+ -j, --json Display execution results in JSON format
+ -t, --table_format {plain,simple,github,grid,simple_grid,rounded_grid,heavy_grid,mixed_grid,double_grid,fancy_grid,outline,simple_outline,rounded_outline,heavy_outline,mixed_outline,double_outline,fancy_outline,pipe,csv,orgtbl,asciidoc,jira,presto,pretty,psql,rst,mediawiki,moinmoin,youtrack,html,unsafehtml,latex,latex_raw,latex_booktabs,latex_longtable,textile,tsv}
+ Tabular display format
+ Selecting CSV format will output to a file and display a table to the console using simple format
+```
+
+[Return to Summary](#workflow-manager-gui-toc)
+
+---
+---
+
+### Example source code
+The source code for this example can be found [here](workflow_manager_gui.py).
+
+[Return to Summary](#workflow-manager-gui-toc)
+
+---
+---
+---
\ No newline at end of file
diff --git a/samples/workflows/asset/command-execute.png b/samples/workflows/asset/command-execute.png
new file mode 100644
index 00000000..6568b8aa
Binary files /dev/null and b/samples/workflows/asset/command-execute.png differ
diff --git a/samples/workflows/asset/command-get-result.png b/samples/workflows/asset/command-get-result.png
new file mode 100644
index 00000000..ebcd71ec
Binary files /dev/null and b/samples/workflows/asset/command-get-result.png differ
diff --git a/samples/workflows/asset/command-list-executions.png b/samples/workflows/asset/command-list-executions.png
new file mode 100644
index 00000000..90451c77
Binary files /dev/null and b/samples/workflows/asset/command-list-executions.png differ
diff --git a/samples/workflows/asset/command-list-workflows.png b/samples/workflows/asset/command-list-workflows.png
new file mode 100644
index 00000000..ad40109c
Binary files /dev/null and b/samples/workflows/asset/command-list-workflows.png differ
diff --git a/samples/workflows/asset/command-workflow-export.png b/samples/workflows/asset/command-workflow-export.png
new file mode 100644
index 00000000..c5131f18
Binary files /dev/null and b/samples/workflows/asset/command-workflow-export.png differ
diff --git a/samples/workflows/asset/command-workflow-import.png b/samples/workflows/asset/command-workflow-import.png
new file mode 100644
index 00000000..0dca24ee
Binary files /dev/null and b/samples/workflows/asset/command-workflow-import.png differ
diff --git a/samples/workflows/asset/config_icon.png b/samples/workflows/asset/config_icon.png
new file mode 100644
index 00000000..72e2dff1
Binary files /dev/null and b/samples/workflows/asset/config_icon.png differ
diff --git a/samples/workflows/asset/environment-configuration-api-debugging.png b/samples/workflows/asset/environment-configuration-api-debugging.png
new file mode 100644
index 00000000..44b7c432
Binary files /dev/null and b/samples/workflows/asset/environment-configuration-api-debugging.png differ
diff --git a/samples/workflows/asset/environment-configuration-compress-output.png b/samples/workflows/asset/environment-configuration-compress-output.png
new file mode 100644
index 00000000..a374ef5d
Binary files /dev/null and b/samples/workflows/asset/environment-configuration-compress-output.png differ
diff --git a/samples/workflows/asset/environment-configuration-csv-output.png b/samples/workflows/asset/environment-configuration-csv-output.png
new file mode 100644
index 00000000..54dfa1e9
Binary files /dev/null and b/samples/workflows/asset/environment-configuration-csv-output.png differ
diff --git a/samples/workflows/asset/environment-configuration-json-formatting.png b/samples/workflows/asset/environment-configuration-json-formatting.png
new file mode 100644
index 00000000..a9886da9
Binary files /dev/null and b/samples/workflows/asset/environment-configuration-json-formatting.png differ
diff --git a/samples/workflows/asset/environment-configuration-log-file-dialog.png b/samples/workflows/asset/environment-configuration-log-file-dialog.png
new file mode 100644
index 00000000..65637a86
Binary files /dev/null and b/samples/workflows/asset/environment-configuration-log-file-dialog.png differ
diff --git a/samples/workflows/asset/environment-configuration-log-file-overwrite-dialog.png b/samples/workflows/asset/environment-configuration-log-file-overwrite-dialog.png
new file mode 100644
index 00000000..c37b9311
Binary files /dev/null and b/samples/workflows/asset/environment-configuration-log-file-overwrite-dialog.png differ
diff --git a/samples/workflows/asset/environment-configuration-log-file.png b/samples/workflows/asset/environment-configuration-log-file.png
new file mode 100644
index 00000000..d4b44c25
Binary files /dev/null and b/samples/workflows/asset/environment-configuration-log-file.png differ
diff --git a/samples/workflows/asset/environment-configuration-table-formatting.png b/samples/workflows/asset/environment-configuration-table-formatting.png
new file mode 100644
index 00000000..8f1a61e9
Binary files /dev/null and b/samples/workflows/asset/environment-configuration-table-formatting.png differ
diff --git a/samples/workflows/asset/environment-configuration.png b/samples/workflows/asset/environment-configuration.png
new file mode 100644
index 00000000..6b3fceac
Binary files /dev/null and b/samples/workflows/asset/environment-configuration.png differ
diff --git a/samples/workflows/asset/execution-example-execute-workflow.png b/samples/workflows/asset/execution-example-execute-workflow.png
new file mode 100644
index 00000000..fdfa27cb
Binary files /dev/null and b/samples/workflows/asset/execution-example-execute-workflow.png differ
diff --git a/samples/workflows/asset/execution-example-export-workflow.png b/samples/workflows/asset/execution-example-export-workflow.png
new file mode 100644
index 00000000..03e6f2e0
Binary files /dev/null and b/samples/workflows/asset/execution-example-export-workflow.png differ
diff --git a/samples/workflows/asset/execution-example-get-result.png b/samples/workflows/asset/execution-example-get-result.png
new file mode 100644
index 00000000..f7d33acf
Binary files /dev/null and b/samples/workflows/asset/execution-example-get-result.png differ
diff --git a/samples/workflows/asset/execution-example-import-workflow.png b/samples/workflows/asset/execution-example-import-workflow.png
new file mode 100644
index 00000000..a95c708e
Binary files /dev/null and b/samples/workflows/asset/execution-example-import-workflow.png differ
diff --git a/samples/workflows/asset/execution-example-list-executions.png b/samples/workflows/asset/execution-example-list-executions.png
new file mode 100644
index 00000000..61a601d4
Binary files /dev/null and b/samples/workflows/asset/execution-example-list-executions.png differ
diff --git a/samples/workflows/asset/execution-example-list-workflows.png b/samples/workflows/asset/execution-example-list-workflows.png
new file mode 100644
index 00000000..5de75420
Binary files /dev/null and b/samples/workflows/asset/execution-example-list-workflows.png differ
diff --git a/samples/workflows/asset/execution-example-workflow-disabled.png b/samples/workflows/asset/execution-example-workflow-disabled.png
new file mode 100644
index 00000000..3f4f79f5
Binary files /dev/null and b/samples/workflows/asset/execution-example-workflow-disabled.png differ
diff --git a/samples/workflows/asset/exit-program.png b/samples/workflows/asset/exit-program.png
new file mode 100644
index 00000000..2a6b8bd5
Binary files /dev/null and b/samples/workflows/asset/exit-program.png differ
diff --git a/samples/workflows/asset/export-configuration-export-file.png b/samples/workflows/asset/export-configuration-export-file.png
new file mode 100644
index 00000000..2475d5fc
Binary files /dev/null and b/samples/workflows/asset/export-configuration-export-file.png differ
diff --git a/samples/workflows/asset/export-configuration-save-file-dialog.png b/samples/workflows/asset/export-configuration-save-file-dialog.png
new file mode 100644
index 00000000..5465c4a2
Binary files /dev/null and b/samples/workflows/asset/export-configuration-save-file-dialog.png differ
diff --git a/samples/workflows/asset/export-configuration.png b/samples/workflows/asset/export-configuration.png
new file mode 100644
index 00000000..4c8a8753
Binary files /dev/null and b/samples/workflows/asset/export-configuration.png differ
diff --git a/samples/workflows/asset/import-configuration-import-file-dialog.png b/samples/workflows/asset/import-configuration-import-file-dialog.png
new file mode 100644
index 00000000..7c1f8fdf
Binary files /dev/null and b/samples/workflows/asset/import-configuration-import-file-dialog.png differ
diff --git a/samples/workflows/asset/import-configuration-validate-only.png b/samples/workflows/asset/import-configuration-validate-only.png
new file mode 100644
index 00000000..f6a67859
Binary files /dev/null and b/samples/workflows/asset/import-configuration-validate-only.png differ
diff --git a/samples/workflows/asset/import-configuration-workflow-name.png b/samples/workflows/asset/import-configuration-workflow-name.png
new file mode 100644
index 00000000..c346f158
Binary files /dev/null and b/samples/workflows/asset/import-configuration-workflow-name.png differ
diff --git a/samples/workflows/asset/import-configuration.png b/samples/workflows/asset/import-configuration.png
new file mode 100644
index 00000000..b2f7864e
Binary files /dev/null and b/samples/workflows/asset/import-configuration.png differ
diff --git a/samples/workflows/asset/program_icon.png b/samples/workflows/asset/program_icon.png
new file mode 100644
index 00000000..28a27e21
Binary files /dev/null and b/samples/workflows/asset/program_icon.png differ
diff --git a/samples/workflows/asset/running_icon.png b/samples/workflows/asset/running_icon.png
new file mode 100644
index 00000000..3495bd51
Binary files /dev/null and b/samples/workflows/asset/running_icon.png differ
diff --git a/samples/workflows/asset/workflow-configuration-execution-id.png b/samples/workflows/asset/workflow-configuration-execution-id.png
new file mode 100644
index 00000000..30cfd0fd
Binary files /dev/null and b/samples/workflows/asset/workflow-configuration-execution-id.png differ
diff --git a/samples/workflows/asset/workflow-configuration-id-and-payload.png b/samples/workflows/asset/workflow-configuration-id-and-payload.png
new file mode 100644
index 00000000..61073172
Binary files /dev/null and b/samples/workflows/asset/workflow-configuration-id-and-payload.png differ
diff --git a/samples/workflows/asset/workflow-configuration-no-dropdown.png b/samples/workflows/asset/workflow-configuration-no-dropdown.png
new file mode 100644
index 00000000..37a95b23
Binary files /dev/null and b/samples/workflows/asset/workflow-configuration-no-dropdown.png differ
diff --git a/samples/workflows/asset/workflow-configuration-select-option.png b/samples/workflows/asset/workflow-configuration-select-option.png
new file mode 100644
index 00000000..9c57ac01
Binary files /dev/null and b/samples/workflows/asset/workflow-configuration-select-option.png differ
diff --git a/samples/workflows/asset/workflow-configuration-select-workflow-id.png b/samples/workflows/asset/workflow-configuration-select-workflow-id.png
new file mode 100644
index 00000000..bb8513a1
Binary files /dev/null and b/samples/workflows/asset/workflow-configuration-select-workflow-id.png differ
diff --git a/samples/workflows/asset/workflow-configuration-workflow-id-custom-value.png b/samples/workflows/asset/workflow-configuration-workflow-id-custom-value.png
new file mode 100644
index 00000000..4e917726
Binary files /dev/null and b/samples/workflows/asset/workflow-configuration-workflow-id-custom-value.png differ
diff --git a/samples/workflows/asset/workflow-configuration.png b/samples/workflows/asset/workflow-configuration.png
new file mode 100644
index 00000000..12c0b274
Binary files /dev/null and b/samples/workflows/asset/workflow-configuration.png differ
diff --git a/samples/workflows/workflow_manager.py b/samples/workflows/workflow_manager.py
new file mode 100644
index 00000000..9554f494
--- /dev/null
+++ b/samples/workflows/workflow_manager.py
@@ -0,0 +1,404 @@
+"""Falcon Fusion SOAR workflow manager.
+
+ _______ __ _______ __ __ __
+| _ .----.-----.--.--.--.--| | _ | |_.----|__| |--.-----.
+|. 1___| _| _ | | | | _ | 1___| _| _| | <| -__|
+|. |___|__| |_____|________|_____|____ |____|__| |__|__|__|_____|
+|: 1 | |: 1 |
+|::.. . | CROWDSTRIKE FALCON |::.. . | FalconPy
+`-------' `-------'
+
+ ██ ██ ██ ████ ██
+░██ ░██ ░██ ░██░ ░██
+░██ █ ░██ ██████ ██████░██ ██ ██████ ░██ ██████ ███ ██
+░██ ███ ░██ ██░░░░██░░██░░█░██ ██ ░░░██░ ░██ ██░░░░██░░██ █ ░██
+░██ ██░██░██░██ ░██ ░██ ░ ░████ ░██ ░██░██ ░██ ░██ ███░██
+░████ ░░████░██ ░██ ░██ ░██░██ ░██ ░██░██ ░██ ░████░████
+░██░ ░░░██░░██████ ░███ ░██░░██ ░██ ███░░██████ ███░ ░░░██
+░░ ░░ ░░░░░░ ░░░ ░░ ░░ ░░ ░░░ ░░░░░░ ░░░ ░░░
+ ████ ████
+░██░██ ██░██ █████
+░██░░██ ██ ░██ ██████ ███████ ██████ ██░░░██ █████ ██████
+░██ ░░███ ░██ ░░░░░░██ ░░██░░░██ ░░░░░░██ ░██ ░██ ██░░░██░░██░░█
+░██ ░░█ ░██ ███████ ░██ ░██ ███████ ░░██████░███████ ░██ ░
+░██ ░ ░██ ██░░░░██ ░██ ░██ ██░░░░██ ░░░░░██░██░░░░ ░██
+░██ ░██░░████████ ███ ░██░░████████ █████ ░░██████░███
+░░ ░░ ░░░░░░░░ ░░░ ░░ ░░░░░░░░ ░░░░░ ░░░░░░ ░░░
+
+This sample demonstrates how to leverage the Workflows API to provide
+the following functionality:
+ - List all workflows (-l or --list-workflows)
+ - Execute a workflow (-e or --execute)
+ - List all executions for a workflow (-le or --list-executions)
+ - Print the results of a workflow execution (-g or --get_result)
+ - Import a workflow (-im {FILENAME} or --import-workflow {FILENAME})
+ - Export a workflow (-ex {FILENAME} or --export-workflow {FILENAME})
+
+Creation date: 11.06.2024 - jlangdev@CrowdStrike
+Modification date: 11.08.2024 - jshcodes@CrowdStrike
+
+This sample requires the following packages:
+- crowdstrike-falconpy >= 1.4.1
+- tabulate
+- termcolor
+"""
+import logging
+from argparse import ArgumentParser, RawTextHelpFormatter, Namespace
+from json import dumps, loads
+from os import getenv
+from tabulate import tabulate
+from termcolor import colored
+from falconpy import Workflows, Result, APIError
+
+
+def consume_arguments() -> Namespace:
+ """Consume the provided command line."""
+ parser = ArgumentParser(description=__doc__, formatter_class=RawTextHelpFormatter)
+ parser.add_argument("-d", "--debug",
+ help="Activate API debugging",
+ action="store_true",
+ required=False
+ )
+ wflow = parser.add_argument_group("workflow arguments")
+ wflow.add_argument("-i", "--id",
+ help="Workflow definition or execution ID",
+ required=False
+ )
+ wflow.add_argument("-p", "--payload",
+ help="Workflow execution payload",
+ required=False,
+ default="{}"
+ )
+ cmds = parser.add_argument_group("command arguments")
+ cmds.add_argument("-e", "--execute",
+ help="Execute the workflow specified",
+ required=False,
+ action="store_true"
+ )
+ cmds.add_argument("-g", "--get-result",
+ dest="get_result",
+ help="Retrieve a workflow execution result",
+ required=False,
+ action="store_true"
+ )
+ cmds.add_argument("-l", "--list-workflows",
+ dest="list_workflows",
+ help="List all workflows",
+ required=False,
+ action="store_true"
+ )
+ cmds.add_argument("-le", "--list-executions",
+ dest="list_executions",
+ help="List the executions for the workflow specified",
+ required=False,
+ action="store_true"
+ )
+ cmds.add_argument("-ex", "--export-workflow",
+ dest="export_workflow",
+ help="Export a workflow to a local file.\n"
+ "Provide a filename for this argument. Example: 'exported.yml'",
+ required=False
+ )
+ cmds.add_argument("-im", "--import-workflow",
+ dest="import_workflow",
+ help="Import a workflow from a local file.\n"
+ "Provide a filename for this argument. Example: 'to_import.yml'",
+ required=False
+ )
+ cmds.add_argument("-n", "--workflow-name",
+ help="Name for the imported workflow",
+ required=False
+ )
+ cmds.add_argument("-v", "--validate-only",
+ dest="validate",
+ help="Validate the workflow only, do not save upon import",
+ required=False,
+ default=False,
+ action="store_true"
+ )
+ frmt = parser.add_argument_group("formatting arguments")
+ frmt.add_argument("-j", "--json",
+ help="Display execution results in JSON format",
+ required=False,
+ action="store_true"
+ )
+ frmt.add_argument("-t", "--table-format",
+ dest="table_format",
+ help="Tabular display format",
+ required=False,
+ default="simple"
+ )
+ auth = parser.add_argument_group("authentication arguments "
+ "(environment authentication supported)"
+ )
+ auth.add_argument("-k", "--falcon-client-id",
+ dest="client_id",
+ help="CrowdStrike Falcon API ID",
+ required=False,
+ default=getenv("FALCON_CLIENT_ID")
+ )
+ auth.add_argument("-s", "--falcon-client-secret",
+ dest="client_secret",
+ help="CrowdStrike Falcon API secret",
+ required=False,
+ default=getenv("FALCON_CLIENT_SECRET")
+ )
+ auth.add_argument("-b", "--base-url",
+ dest="base_url",
+ help="CrowdStrike Region (US1, US2, EU1, USGOV1, USGOV2) \n"
+ "Full URL is also supported.",
+ required=False,
+ default="auto"
+ )
+
+ return parser.parse_args()
+
+
+def get_workflows(sdk: Workflows):
+ """Print a list of workflows within the tenant to the screen."""
+ workflow_list = []
+ pop_keys = ["trigger", "actions", "description", "conditions", "loops"]
+ for workflow in sdk.search_definitions().data:
+ pop_list = [k for k in pop_keys if k in workflow]
+ workflow["type"] = "Unknown"
+ if "conditions" in workflow:
+ workflow["type"] = "Event"
+ if "trigger" in workflow:
+ if "schedule" in workflow["trigger"]:
+ workflow["type"] = "Scheduled"
+ if "event" not in workflow["trigger"]:
+ workflow["type"] = "On demand"
+ for key in pop_list:
+ workflow.pop(key)
+ workflow_list.append(workflow)
+
+ return workflow_list
+
+
+def get_executions(sdk: Workflows, definition_id: str):
+ """Retrieve all executions for the specified workflow."""
+ execution_list = []
+ pop_keys = ["definition_id", "definition_version", "ancestor_executions",
+ "retryable", "trigger", "activities", "loops"
+ ]
+ if definition_id in [w["id"] for w in get_workflows(sdk)]:
+ for execution in sdk.search_executions(filter=f"definition_id:'{definition_id}'").data:
+ pop_list = [k for k in pop_keys if k in execution]
+ for key in pop_list:
+ execution.pop(key)
+ execution_list.append(execution)
+ else:
+ print("Invalid workflow ID")
+
+ return execution_list
+
+
+def display_executions(sdk: Workflows, definition_id: str, tformat: str, use_json: bool):
+ """Display the execution list to the terminal."""
+ exec_list = get_executions(sdk, definition_id=definition_id)
+ if exec_list:
+ if use_json:
+ print(dumps(exec_list, indent=4))
+ else:
+ display_keys = {"execution_id": "ID",
+ "status": "Status",
+ "start_timestamp": "Start",
+ "end_timestamp": "End"
+ }
+ print(colored(EXECUTION_HEADER, "red", attrs=["bold"]))
+ print(tabulate(exec_list, headers=display_keys, tablefmt=tformat))
+ else:
+ print("No executions found.")
+
+
+def display_workflows(sdk: Workflows, tformat: str, use_json: bool):
+ """Display the workflow list to the terminal."""
+ workflow_list = get_workflows(sdk)
+ if workflow_list:
+ if use_json:
+ print(dumps(workflow_list, indent=4))
+ else:
+ display_keys = {"id": "ID",
+ "name": "Name",
+ "enabled": "Enabled",
+ "type": "Type",
+ "last_modified_timestamp": "Last modified",
+ "version": "Version"
+ }
+ print(colored(WORKFLOW_HEADER, "red", attrs=["bold"]))
+ print(tabulate(workflow_list,
+ headers=display_keys,
+ tablefmt=tformat
+ ))
+ else:
+ print("No workflows found.")
+
+
+def execute_workflow(sdk: Workflows, definition_id: str, payload: str):
+ """Execute the specified workflow."""
+ print(f"Attempting to execute workflow {definition_id}")
+ if definition_id in [w["id"] for w in get_workflows(sdk)]:
+ try:
+ result: Result = sdk.execute(definition_id=definition_id, body=loads(payload))
+ if result.status_code == 200:
+ print(f"Workflow execution {result.data} triggered successfully")
+ else:
+ for err in result.errors:
+ print(f"[{err['code']}] {err['message']}")
+ except APIError as failure:
+ print(failure)
+ else:
+ print("Invalid workflow ID.")
+
+
+def display_execution_results(sdk: Workflows, definition_id: str, use_json: bool):
+ """Display the results for the specified execution."""
+ print(colored(RESULT_HEADER, "red", attrs=["bold"]))
+ try:
+ for result in sdk.execution_results(ids=definition_id).data:
+ for activity in result.get("activities"):
+ output = activity.get("result")
+ if use_json:
+ output = dumps(output, indent=4)
+ print(output)
+ except APIError as err:
+ print(err)
+
+
+def export_workflow(sdk: Workflows, definition_id: str, filename: str):
+ """Export the specified workflow to a YAML file."""
+ print(colored(EXPORT_HEADER, "red", attrs=["bold"]))
+ print(f"Exporting workflow {definition_id} to {filename}...\n")
+ try:
+ with open(filename, "wb") as export_file:
+ export_file.write(sdk.export_definition(id=definition_id).data)
+ except APIError as err:
+ print(err)
+
+
+def import_workflow(sdk: Workflows, name: str, filename: str, validate_only: bool):
+ """Import the specified YAML file as a workflow."""
+ print(colored(IMPORT_HEADER, "red", attrs=["bold"]))
+ stub = "Validat" if validate_only else "Import"
+ nam = "a new workflow" if not name else name
+ print(f"{stub}ing {filename} as {nam}...\n")
+ try:
+ response: Result = sdk.import_definition(name=name,
+ data_file=filename,
+ validate_only=validate_only
+ )
+ if response.status_code == 200:
+ if validate_only:
+ print("Workflow import passes validation.")
+ else:
+ print(f"Workflow imported successfully [{response.data[0].get('id')}].")
+ except APIError as err:
+ print(err)
+
+
+def main():
+ """Execute the main routine."""
+ cmd_line = consume_arguments()
+ _debug = False
+ if cmd_line.debug:
+ # Activate debug logging
+ logging.basicConfig(level=logging.DEBUG)
+ _debug = True
+ # Check for a specified action, if not present, set the default to list all workflows
+ if not max(cmd_line.execute,
+ cmd_line.list_workflows,
+ cmd_line.list_executions,
+ cmd_line.get_result,
+ bool(cmd_line.export_workflow),
+ bool(cmd_line.import_workflow)
+ ):
+ cmd_line.list_workflows = True
+ # Open the Service Collection as a context manager so we log out automatically
+ with Workflows(debug=_debug,
+ pythonic=True,
+ client_id=cmd_line.client_id,
+ client_secret=cmd_line.client_secret,
+ base_url=cmd_line.base_url
+ ) as workflows:
+ if not workflows.token_valid:
+ # Invalid login, show an error and then exit
+ fail_msg = colored("Invalid CrowdStrike API credentials or region specified.",
+ "red",
+ attrs=["bold"]
+ )
+ print(LOGIN_FAIL.format(fail_msg))
+ return
+
+ if cmd_line.execute:
+ execute_workflow(workflows, cmd_line.id, cmd_line.payload)
+
+ if cmd_line.list_executions:
+ display_executions(workflows, cmd_line.id, cmd_line.table_format, cmd_line.json)
+
+ if cmd_line.list_workflows:
+ display_workflows(workflows, cmd_line.table_format, cmd_line.json)
+
+ if cmd_line.get_result:
+ display_execution_results(workflows, cmd_line.id, cmd_line.json)
+
+ if cmd_line.export_workflow:
+ export_workflow(workflows, cmd_line.id, cmd_line.export_workflow)
+
+ if cmd_line.import_workflow:
+ import_workflow(workflows,
+ cmd_line.workflow_name,
+ cmd_line.import_workflow,
+ cmd_line.validate
+ )
+
+
+WORKFLOW_HEADER = r"""
+_ _ _ ____ ____ _ _ ____ _ ____ _ _ _ ____
+| | | | | |__/ |_/ |___ | | | | | | [__
+|_|_| |__| | \ | \_ | |___ |__| |_|_| ___]
+"""
+
+EXECUTION_HEADER = r"""
+_ _ _ ____ ____ _ _ ____ _ ____ _ _ _ ____ _ _ ____ ____ _ _ ___ _ ____ _ _ ____
+| | | | | |__/ |_/ |___ | | | | | | |___ \/ |___ | | | | | | | |\ | [__
+|_|_| |__| | \ | \_ | |___ |__| |_|_| |___ _/\_ |___ |___ |__| | | |__| | \| ___]
+"""
+
+RESULT_HEADER = r"""
+____ _ _ ____ ____ _ _ ___ _ ____ _ _ ____ ____ ____ _ _ _ ___ ____
+|___ \/ |___ | | | | | | | |\ | |__/ |___ [__ | | | | [__
+|___ _/\_ |___ |___ |__| | | |__| | \| | \ |___ ___] |__| |___ | ___]
+"""
+
+IMPORT_HEADER = r"""
+_ _ _ ___ ____ ____ ___
+| |\/| |__] | | |__/ |
+| | | | |__| | \ |
+"""
+
+EXPORT_HEADER = r"""
+____ _ _ ___ ____ ____ ___
+|___ \/ |__] | | |__/ |
+|___ _/\_ | |__| | \ |
+"""
+
+LOGIN_FAIL = r"""
+
+ _\\/|(/_
+ >_ _ /
+ (_)-(_)
+ ) o (
+ \ = / {}
+ |W|
+ | |
+ __| |__
+ / \ u / \
+ | `-' |
+ |__| |__|
+ |||ADM|||
+ ||| |||
+ ||| |||
+"""
+
+if __name__ == "__main__":
+ main()
diff --git a/samples/workflows/workflow_manager_gui.py b/samples/workflows/workflow_manager_gui.py
new file mode 100644
index 00000000..7c000dd1
--- /dev/null
+++ b/samples/workflows/workflow_manager_gui.py
@@ -0,0 +1,798 @@
+"""Falcon Fusion SOAR workflow manager.
+
+ _______ __ _______ __ __ __
+| _ .----.-----.--.--.--.--| | _ | |_.----|__| |--.-----.
+|. 1___| _| _ | | | | _ | 1___| _| _| | <| -__|
+|. |___|__| |_____|________|_____|____ |____|__| |__|__|__|_____|
+|: 1 | |: 1 |
+|::.. . | CROWDSTRIKE FALCON |::.. . | FalconPy 1.4.1+
+`-------' `-------'
+
+ ██ ██ ██ ████ ██
+░██ ░██ ░██ ░██░ ░██
+░██ █ ░██ ██████ ██████░██ ██ ██████ ░██ ██████ ███ ██
+░██ ███ ░██ ██░░░░██░░██░░█░██ ██ ░░░██░ ░██ ██░░░░██░░██ █ ░██
+░██ ██░██░██░██ ░██ ░██ ░ ░████ ░██ ░██░██ ░██ ░██ ███░██
+░████ ░░████░██ ░██ ░██ ░██░██ ░██ ░██░██ ░██ ░████░████
+░██░ ░░░██░░██████ ░███ ░██░░██ ░██ ███░░██████ ███░ ░░░██
+░░ ░░ ░░░░░░ ░░░ ░░ ░░ ░░ ░░░ ░░░░░░ ░░░ ░░░
+ ████ ████
+░██░██ ██░██ █████
+░██░░██ ██ ░██ ██████ ███████ ██████ ██░░░██ █████ ██████
+░██ ░░███ ░██ ░░░░░░██ ░░██░░░██ ░░░░░░██ ░██ ░██ ██░░░██░░██░░█
+░██ ░░█ ░██ ███████ ░██ ░██ ███████ ░░██████░███████ ░██ ░
+░██ ░ ░██ ██░░░░██ ░██ ░██ ██░░░░██ ░░░░░██░██░░░░ ░██
+░██ ░██░░████████ ███ ░██░░████████ █████ ░░██████░███
+░░ ░░ ░░░░░░░░ ░░░ ░░ ░░░░░░░░ ░░░░░ ░░░░░░ ░░░
+
+This sample demonstrates how to leverage the Workflows API to provide
+the following functionality:
+ - List all workflows
+ - Results can be exported to CSV
+ - Execute a workflow
+ - List all executions for a workflow
+ - Results can be exported to CSV
+ - Print the results of a workflow execution
+ - Import a workflow
+ - Export a workflow
+ - Optional logging of results to a file
+
+This version leverages the Gooey project to implement a simple GUI, command line
+arguments are supported but not required to specify execution configuration.
+
+Creation date: 11.06.2024 - Initial version, jlangdev@CrowdStrike
+Modification date: 11.08.2024 - Refactoring, jshcodes@CrowdStrike
+Modification date: 11.10.2024 - Add graphical interface, jshcodes@CrowdStrike
+
+This sample requires the following packages:
+- crowdstrike-falconpy >= 1.4.1
+- gooey
+- requests
+- tabulate
+"""
+import csv
+import os
+import sys
+import logging
+from argparse import RawTextHelpFormatter, Namespace, ArgumentError
+from json import dumps, dump, loads
+from os import getenv
+from typing import List, Union
+import requests
+from tabulate import tabulate
+from gooey import Gooey, GooeyParser
+from falconpy import Workflows, Result, APIError
+
+
+MENU = [
+ {
+ "type": "AboutDialog",
+ "menuTitle": "About",
+ "name": "Falcon Fusion SOAR Workflow Manager",
+ "description": "\n\n\nA graphical application for managing Falcon Fusion SOAR workflows\n"
+ "\n Built using the CrowdStrike FalconPy project",
+ "version": "0.1",
+ "copyright": "2024",
+ "website": "https://github.com/crowdstrike/falconpy"
+ }, {
+ "type": "Link",
+ "menuTitle": "Falcon Fusion SOAR API documentation",
+ "url": "https://www.falconpy.io/Service-Collections/Workflows.html"
+ }, {
+ "type": "Link",
+ "menuTitle": "Falcon Fusion SOAR console documentation",
+ "url": "https://falcon.crowdstrike.com/documentation/page/dc4f8c45/workflows-falcon-fusion"
+ }, {
+ "type": "Link",
+ "menuTitle": "CrowdStrike Developer Center",
+ "url": "https://developer.crowdstrike.com"
+ }, {
+ "type": "Link",
+ "menuTitle": "More FalconPy code samples",
+ "url": "https://github.com/crowdstrike/falconpy/tree/main/samples#falconpy-sample-library"
+ }
+]
+
+
+def get_font_size() -> int:
+ """Return the specified font point size for terminal output.
+
+ Specify this value as the first argument (integer) when executing the application.
+ """
+ returned = None
+ try:
+ if "-" not in sys.argv[1]:
+ returned = int(sys.argv[1])
+ # Pop it out of the list so it doesn't
+ # interfere with argument parsing
+ sys.argv.pop(1)
+ except (IndexError, ValueError):
+ pass
+
+ return returned
+
+
+def check_for_autostart() -> bool:
+ """Check to see if the command line specifies automatic execution."""
+ returned = False
+ try:
+ if "-" not in sys.argv[1]:
+ if sys.argv[1].lower() in ["run", "go", "exec", "force"]:
+ returned = True
+ sys.argv.pop(1)
+ except (IndexError, ValueError):
+ pass
+
+ return returned
+
+
+def get_image_dir():
+ """Identify the current program image directory."""
+ returned = os.path.dirname(os.path.abspath(__file__))
+ if os.path.exists(f"{returned}/asset/program_icon.png"):
+ returned = f"{returned}/asset"
+
+ return returned
+
+
+def gui_image(img: str, local_img: str):
+ """Retrieve GUI program images from GitHub before launching the interface."""
+ curpath = os.path.dirname(os.path.abspath(__file__))
+ loc_array = ["https://raw.githubusercontent.com/CrowdStrike/",
+ "falconpy/refs/heads/main/samples/workflows/asset/"
+ ]
+ loc = "".join(loc_array)
+ if not max(os.path.exists(f"{curpath}/{local_img}"),
+ os.path.exists(f"{curpath}/asset/{local_img}")
+ ):
+ try:
+ result = requests.get(f"{loc}{img}", timeout=5)
+ if result.status_code == 200:
+ img_data = result.content
+ with open(f"{curpath}/{local_img}", "wb") as gui_icon:
+ gui_icon.write(img_data)
+ except (requests.exceptions.Timeout, PermissionError, OSError):
+ pass
+
+
+def prefill_check(what: str = "ID") -> str:
+ """Detect if environment authentication is in use and inform the user."""
+ returned = f"CrowdStrike Falcon API {what}"
+ if getenv(f"FALCON_CLIENT_{what.upper()}"):
+ returned = f"{returned}\n(pre-filled from environment or command line)"
+
+ return returned
+
+
+def attempt_to_prefill_workflow_ids() -> Union[List[str], None]:
+ """Attempt to lookup workflow names and IDs before the GUI loads.
+
+ This functionality only works when environment authentication is in use, or valid
+ credentials are provided on the command line using the -k and -s arguments.
+ """
+ returned: List[str] = []
+ tmp_id = getenv("FALCON_CLIENT_ID")
+ tmp_sec = getenv("FALCON_CLIENT_SECRET")
+ tmp_base = "auto"
+ tmp_debug = False
+ cnt = 0
+ for arg in sys.argv:
+ if arg in ["-k", "--client_id"]:
+ tmp_id = sys.argv[cnt+1]
+ if arg in ["-s", "--client_secret"]:
+ tmp_sec = sys.argv[cnt+1]
+ if arg in ["-b", "--base_url"]:
+ tmp_base = sys.argv[cnt+1]
+ if arg in ["-d", "--debug"]:
+ tmp_debug = True
+ if arg in ["-g", "--get_result", "--skip_preflight", "-sk", "-h"]:
+ tmp_id = None
+ returned = None
+ break
+ cnt += 1
+ if min(bool(tmp_id), bool(tmp_sec)):
+ with Workflows(client_id=tmp_id,
+ client_secret=tmp_sec,
+ base_url=tmp_base,
+ debug=tmp_debug,
+ pythonic=True
+ ) as flows:
+ try:
+ for flow in flows.search_definitions().data:
+ returned.append(f"{flow.get('id')} ({flow.get('name')})")
+ except APIError:
+ pass
+
+ return returned
+
+
+def check_action() -> int:
+ """Check to see if a default action was specified on the command line."""
+ returned = 0
+ arglist = {
+ "-l": 0,
+ "--list_workflows": 0,
+ "-e": 1,
+ "--execute": 1,
+ "-le": 2,
+ "--list-executions": 2,
+ "-g": 3,
+ "--get_result": 3
+ }
+ try:
+ for arg in sys.argv:
+ returned = arglist.get(arg, returned)
+ except (IndexError, ValueError):
+ pass
+ return returned
+
+
+def check_for_json_format() -> int:
+ """Check to see if JSON formatted output was specified."""
+ returned = 1
+ try:
+ if "-j" in sys.argv:
+ returned = 0
+ except (IndexError, ValueError):
+ pass
+
+ return returned
+
+
+def return_true() -> bool:
+ """Return true."""
+ return True
+
+
+@Gooey(advanced=True,
+ program_name="Falcon Fusion SOAR Workflow Manager",
+ program_description="List, execute, review and manage Fusion workflows.",
+ default_size=(1100, 610),
+ terminal_font_family='Courier New',
+ terminal_font_size=get_font_size(),
+ auto_start=check_for_autostart(),
+ show_stop_warning=False,
+ show_success_modal=False,
+ show_restart_button=False,
+ use_cmd_args=True,
+ tabbed_groups=True,
+ image_dir=get_image_dir(),
+ menu=[{"name": "Help", "items": MENU}]
+ )
+def consume_arguments() -> Namespace: # pylint: disable=R0915
+ """Consume the provided command line."""
+ parser = GooeyParser(description=__doc__,
+ formatter_class=RawTextHelpFormatter,
+ exit_on_error=False
+ )
+ cmds = parser.add_argument_group("Command", description="Workflow command to perform")
+ excl = cmds.add_mutually_exclusive_group("Action",
+ gooey_options={"initial_selection": check_action()}
+ )
+ excl.add_argument("-l", "--list_workflows",
+ help="List all workflows",
+ required=False,
+ action="store_true"
+ )
+ excl.add_argument("-e", "--execute",
+ help="Execute the workflow specified on the Workflow tab",
+ required=False,
+ action="store_true"
+ )
+ excl.add_argument("-le", "--list_executions",
+ help="List the executions for the workflow specified",
+ required=False,
+ action="store_true"
+ )
+ excl.add_argument("-g", "--get_result",
+ help="Retrieve a workflow execution result",
+ required=False,
+ action="store_true"
+ )
+ excl.add_argument("-ex", "--workflow_export",
+ help="Export a workflow",
+ required=False,
+ action="store_true"
+ )
+ excl.add_argument("-im", "--workflow_import",
+ help="Import a workflow",
+ required=False,
+ action="store_true"
+ )
+ wflow = parser.add_argument_group("Workflow",
+ description="Workflow or execution ID and workflow payload",
+ gooey_options={"columns": 1}
+ )
+ wflow.add_argument("-i", "--id",
+ help="Workflow definition ID",
+ required=False,
+ choices=attempt_to_prefill_workflow_ids()
+ )
+ wflow.add_argument("-ei", "--execution_id",
+ help="Workflow execution ID",
+ required=False
+ )
+ wflow.add_argument("-p", "--payload",
+ help="Workflow execution payload",
+ required=False,
+ default='{\n "key": "value"\n}',
+ widget="Textarea"
+ )
+ imp = parser.add_argument_group("Import", description="Import a workflow from a file")
+ imp.add_argument("-n", "--workflow_name",
+ help="Name for the imported workflow",
+ required=False
+ )
+ imp.add_argument("-v", "--validate_only",
+ dest="validate",
+ help="Validate the workflow only, do not save upon import",
+ required=False,
+ default=False,
+ widget="BlockCheckbox",
+ action="store_true",
+ gooey_options={"checkbox_label": " Validate only"}
+ )
+ imp.add_argument("-iw", "--import_workflow",
+ help="Location of the YAML workflow file to import",
+ required=False,
+ widget="FileChooser",
+ gooey_options={"default_dir": os.path.dirname(os.path.abspath(__file__)),
+ "wildcard": "YAML files (*.yml)|*.yml",
+ "message": "Select YAML file"
+ }
+ )
+ exp = parser.add_argument_group("Export", description="Export a workflow to a file")
+ exp.add_argument("-ew", "--export_workflow",
+ help="Location to save the exported workflow (YAML format)\n"
+ "Use the Workflow tab to specify the desired workflow ID",
+ required=False,
+ widget="FileSaver",
+ gooey_options={"default_dir": os.path.dirname(os.path.abspath(__file__)),
+ "default_file": "exported.yml", "message": "Specify YAML file"
+ }
+ )
+ auth = parser.add_argument_group("Environment",
+ description="Authentication and program execution options",
+ gooey_options={"columns": 3}
+ )
+ auth.add_argument("-k", "--client_id",
+ help=prefill_check(),
+ required=False,
+ default=getenv("FALCON_CLIENT_ID"),
+ widget="PasswordField"
+ )
+ auth.add_argument("-s", "--client_secret",
+ help=prefill_check("secret"),
+ required=False,
+ default=getenv("FALCON_CLIENT_SECRET"),
+ widget="PasswordField"
+ )
+ auth.add_argument("-b", "--base_url",
+ help="CrowdStrike Region\n('auto' not implemented for usgov1 or usgov2)",
+ required=False,
+ default="auto",
+ choices=["auto", "us1", "us2", "eu1", "usgov1", "usgov2"]
+ )
+ auth.add_argument("-lf", "--logfile",
+ help="Log output results to a local file as well as the console",
+ required=False,
+ widget="FileSaver",
+ gooey_options={"default_dir": os.path.dirname(os.path.abspath(__file__)),
+ "default_file": "workflow-manager.log",
+ "message": "Specify log file",
+ }
+ )
+ auth.add_argument("-d", "--debug",
+ help=" Activate API debugging",
+ action="store_true",
+ required=False
+ )
+
+ auth.add_argument("-o", "--compress_output",
+ help=" Compress display output",
+ action="store_true",
+ default=False
+ )
+ auth.add_argument("-sk", "--skip_preflight",
+ help=" Skip preflight API lookups",
+ default=False,
+ action="store_true",
+ gooey_options={"visible": False}
+ )
+ frmt = auth.add_mutually_exclusive_group("Format",
+ gooey_options={
+ "initial_selection": check_for_json_format(),
+ "show_border": False,
+ "show_underline": True
+ }
+ )
+ frmt.add_argument("-j", "--json",
+ help="Display execution results in JSON format",
+ required=False,
+ action="store_true"
+ )
+ frmt.add_argument("-t", "--table_format",
+ help="Tabular display format\n"
+ "Selecting CSV format will output to a file and display a table "
+ "to the console using simple format",
+ required=False,
+ default="simple",
+ choices=["plain", "simple", "github", "grid", "simple_grid", "rounded_grid",
+ "heavy_grid", "mixed_grid", "double_grid", "fancy_grid", "outline",
+ "simple_outline", "rounded_outline", "heavy_outline",
+ "mixed_outline", "double_outline", "fancy_outline", "pipe", "csv",
+ "orgtbl", "asciidoc", "jira", "presto", "pretty", "psql", "rst",
+ "mediawiki", "moinmoin", "youtrack", "html", "unsafehtml", "latex",
+ "latex_raw", "latex_booktabs", "latex_longtable", "textile", "tsv"
+ ]
+ )
+
+ arglist = sys.argv
+ for arg in arglist:
+ if arg in ["-h", "--help"]:
+ parser.print_help()
+ sys.exit(0)
+
+ hold_id = None
+ try:
+ parsed = parser.parse_args()
+ except ArgumentError as bad_arg:
+ if "invalid choice" in bad_arg.message:
+ cnt = 0
+ arglist = sys.argv
+ for arg in arglist:
+ if arg in ["-i", "--id"]:
+ hold_id = sys.argv[cnt+1]
+ sys.argv.pop(cnt+1)
+ sys.argv.pop(cnt)
+ cnt += 1
+ else:
+ raise bad_arg
+ finally:
+ parsed = parser.parse_args()
+ if hold_id:
+ parsed.id = hold_id
+
+ return parsed
+
+
+def get_workflows(sdk: Workflows):
+ """Print a list of workflows within the tenant to the screen."""
+ workflow_list = []
+ pop_keys = ["trigger", "actions", "description", "conditions", "loops"]
+ for workflow in sdk.search_definitions().data:
+ pop_list = [k for k in pop_keys if k in workflow]
+ workflow["type"] = "Unknown"
+ if "conditions" in workflow:
+ workflow["type"] = "Event"
+ if "trigger" in workflow:
+ if "schedule" in workflow["trigger"]:
+ workflow["type"] = "Scheduled"
+ if "event" not in workflow["trigger"]:
+ workflow["type"] = "On demand"
+ for key in pop_list:
+ workflow.pop(key)
+ workflow_list.append(workflow)
+
+ return workflow_list
+
+
+def get_executions(sdk: Workflows, definition_id: str):
+ """Retrieve all executions for the specified workflow."""
+ execution_list = []
+ pop_keys = ["definition_id", "definition_version", "ancestor_executions",
+ "retryable", "trigger", "activities", "loops"
+ ]
+ if definition_id in [w["id"] for w in get_workflows(sdk)]:
+ for execution in sdk.search_executions(filter=f"definition_id:'{definition_id}'").data:
+ pop_list = [k for k in pop_keys if k in execution]
+ for key in pop_list:
+ execution.pop(key)
+ execution_list.append(execution)
+ else:
+ print("Invalid workflow ID")
+
+ return execution_list
+
+
+def log_write(logging_file: str, json_mode: bool, log_text: str):
+ """Perform the log action."""
+ with open(logging_file, "a", encoding="utf-8") as logfile:
+ logfile.write("\n")
+ if json_mode:
+ dump(log_text, logfile, indent=4)
+ else:
+ logfile.write(log_text)
+
+
+def display_executions(sdk: Workflows,
+ tformat: str,
+ use_json: bool,
+ log: str,
+ definition_id: str = None
+ ):
+ """Display the execution list to the terminal."""
+ if not definition_id:
+ print("Invalid workflow ID specified.")
+ return
+ exec_list = get_executions(sdk, definition_id=definition_id.split(" ", maxsplit=1)[0])
+ if exec_list:
+ if use_json:
+ if log:
+ log_write(log, use_json, exec_list)
+ print(dumps(exec_list, indent=4))
+ else:
+ display_keys = {"execution_id": "ID",
+ "status": "Status",
+ "start_timestamp": "Start",
+ "end_timestamp": "End"
+ }
+ if log:
+ log_write(log, use_json, f"{EXECUTION_HEADER}\n"
+ f"{tabulate(exec_list, headers=display_keys, tablefmt=tformat)}"
+ )
+ if tformat.lower() == "csv":
+ output_file = "workflow_executions.csv"
+ with open(output_file, "w", newline="", encoding="utf-8") as csvfile:
+ writer = csv.DictWriter(csvfile, fieldnames=display_keys.keys())
+ writer.writeheader()
+ writer.writerows(exec_list)
+ print(f"Tabular results shown below have been output to {output_file}")
+ tformat = "simple"
+ print(EXECUTION_HEADER)
+ print(tabulate(exec_list, headers=display_keys, tablefmt=tformat))
+ else:
+ print("No executions found.")
+
+
+def display_workflows(sdk: Workflows, tformat: str, use_json: bool, log: str):
+ """Display the workflow list to the terminal."""
+ workflow_list = get_workflows(sdk)
+ if workflow_list:
+ if use_json:
+ if log:
+ log_write(log, use_json, workflow_list)
+ print(dumps(workflow_list, indent=4))
+ else:
+ display_keys = {"id": "ID",
+ "name": "Name",
+ "enabled": "Enabled",
+ "type": "Type",
+ "last_modified_timestamp": "Last modified",
+ "version": "Version"
+ }
+ if log:
+ log_write(log, use_json, f"{WORKFLOW_HEADER}\n"
+ f"{tabulate(workflow_list, headers=display_keys, tablefmt=tformat)}"
+ )
+ if tformat.lower() == "csv":
+ output_file = "workflows.csv"
+ with open(output_file, "w", newline="", encoding="utf-8") as csvfile:
+ writer = csv.DictWriter(csvfile, fieldnames=display_keys.keys())
+ writer.writeheader()
+ writer.writerows(workflow_list)
+ print(f"Tabular results shown below have been output to {output_file}")
+ tformat = "simple"
+ print(WORKFLOW_HEADER)
+ print(tabulate(workflow_list,
+ headers=display_keys,
+ tablefmt=tformat
+ ))
+ else:
+ print("No workflows found.")
+
+
+def fix_quotes(inbound: str):
+ """Format the payload string and remove disallowed characters."""
+ returned = inbound.replace('‘', '"').replace('’', '"').replace('“', '"').replace('”', '"')
+ return loads(returned)
+
+
+def execute_workflow(sdk: Workflows, payload: str, log: str, definition_id: str = None):
+ """Execute the specified workflow."""
+ def inform(msg: str):
+ """Display and log the output."""
+ if log:
+ log_write(log, False, msg)
+ print(msg)
+
+ if not definition_id:
+ print("Invalid workflow ID specified.")
+ return
+
+ definition_id = definition_id.split(" ", maxsplit=1)[0]
+ inform(f"Attempting to execute workflow {definition_id}")
+ if definition_id in [w["id"] for w in get_workflows(sdk)]:
+ try:
+ result: Result = sdk.execute(definition_id=definition_id, body=fix_quotes(payload))
+ if result.status_code == 200:
+ inform(f"Workflow execution {result.data} triggered successfully")
+ else:
+ for err in result.errors:
+ inform(f"[{err['code']}] {err['message']}")
+ except APIError as failure:
+ output = failure.message
+ if log:
+ log_write(log, False, output)
+ print(output)
+
+ else:
+ inform("Invalid workflow ID specified.")
+
+
+def display_execution_results(sdk: Workflows, execution_id: str, use_json: bool, log: str):
+ """Display the results for the specified execution."""
+ if not execution_id:
+ print("Invalid execution ID specified.")
+ return
+ print(RESULT_HEADER)
+ try:
+ for result in sdk.execution_results(ids=execution_id).data:
+ for activity in result.get("activities"):
+ output = activity.get("result")
+ if log:
+ log_write(log, use_json, output if use_json else str(output))
+ if use_json:
+ output = dumps(output, indent=4)
+ print(output)
+ except APIError as err:
+ print(err)
+
+
+def export_workflow(sdk: Workflows, definition_id: str, filename: str):
+ """Export the specified workflow to a YAML file."""
+ if not definition_id:
+ print("You must specify a workflow definition ID to export.")
+ else:
+ definition_id = definition_id.split(" ", maxsplit=1)[0]
+ print(EXPORT_HEADER)
+ print(f"Exporting workflow {definition_id} to {filename}...\n")
+ try:
+ with open(filename, "wb") as export_file:
+ export_file.write(sdk.export_definition(id=definition_id).data)
+ except APIError as err:
+ print(err)
+
+
+def import_workflow(sdk: Workflows, name: str, filename: str, validate_only: bool):
+ """Import the specified YAML file as a workflow."""
+ print(IMPORT_HEADER)
+ stub = "Validat" if validate_only else "Import"
+ nam = "a new workflow" if not name else name
+ print(f"{stub}ing {filename} as {nam}...\n")
+ try:
+ response: Result = sdk.import_definition(name=name,
+ data_file=filename,
+ validate_only=validate_only
+ )
+ if response.status_code == 200:
+ if validate_only:
+ print("Workflow import passes validation.")
+ else:
+ print(f"Workflow imported successfully [{response.data[0].get('id')}].")
+ except APIError as err:
+ print(err)
+
+
+def main():
+ """Execute the main routine."""
+ cmd_line = consume_arguments()
+ _debug = False
+ if cmd_line.debug:
+ # Activate debug logging
+ logging.basicConfig(level=logging.DEBUG)
+ _debug = True
+ # Check for a specified action, if not present, set the default to list all workflows
+ if not max(cmd_line.execute,
+ cmd_line.list_workflows,
+ cmd_line.list_executions,
+ cmd_line.get_result,
+ cmd_line.workflow_export,
+ cmd_line.workflow_import
+ ):
+ cmd_line.list_workflows = True
+ # Open the Service Collection as a context manager so we log out automatically
+ with Workflows(debug=_debug,
+ pythonic=True,
+ client_id=cmd_line.client_id,
+ client_secret=cmd_line.client_secret,
+ base_url=cmd_line.base_url
+ ) as workflows:
+
+ if not workflows.token_valid:
+ print(LOGIN_FAIL)
+ return
+
+ if cmd_line.execute:
+ execute_workflow(workflows, cmd_line.payload, cmd_line.logfile, cmd_line.id)
+
+ if cmd_line.list_executions:
+ display_executions(workflows,
+ cmd_line.table_format,
+ cmd_line.json,
+ cmd_line.logfile,
+ cmd_line.id
+ )
+
+ if cmd_line.list_workflows:
+ display_workflows(workflows,
+ cmd_line.table_format,
+ cmd_line.json,
+ cmd_line.logfile
+ )
+
+ if cmd_line.get_result:
+ display_execution_results(workflows,
+ cmd_line.execution_id,
+ cmd_line.json,
+ cmd_line.logfile
+ )
+
+ if cmd_line.workflow_export:
+ export_workflow(workflows, cmd_line.id, cmd_line.export_workflow)
+
+ if cmd_line.workflow_import:
+ import_workflow(workflows,
+ cmd_line.workflow_name,
+ cmd_line.import_workflow,
+ cmd_line.validate
+ )
+ if not cmd_line.compress_output:
+ # Divide multiple execution results in the console display with a horizontal rule
+ print(f"\n{'_' * 120}\n")
+
+
+WORKFLOW_HEADER = r"""
+_ _ _ ____ ____ _ _ ____ _ ____ _ _ _ ____
+| | | | | |__/ |_/ |___ | | | | | | [__
+|_|_| |__| | \ | \_ | |___ |__| |_|_| ___]
+"""
+
+EXECUTION_HEADER = r"""
+_ _ _ ____ ____ _ _ ____ _ ____ _ _ _ ____ _ _ ____ ____ _ _ ___ _ ____ _ _ ____
+| | | | | |__/ |_/ |___ | | | | | | |___ \/ |___ | | | | | | | |\ | [__
+|_|_| |__| | \ | \_ | |___ |__| |_|_| |___ _/\_ |___ |___ |__| | | |__| | \| ___]
+"""
+
+RESULT_HEADER = r"""
+____ _ _ ____ ____ _ _ ___ _ ____ _ _ ____ ____ ____ _ _ _ ___ ____
+|___ \/ |___ | | | | | | | |\ | |__/ |___ [__ | | | | [__
+|___ _/\_ |___ |___ |__| | | |__| | \| | \ |___ ___] |__| |___ | ___]
+"""
+
+IMPORT_HEADER = r"""
+_ _ _ ___ ____ ____ ___
+| |\/| |__] | | |__/ |
+| | | | |__| | \ |
+"""
+
+EXPORT_HEADER = r"""
+____ _ _ ___ ____ ____ ___
+|___ \/ |__] | | |__/ |
+|___ _/\_ | |__| | \ |
+"""
+
+LOGIN_FAIL = r"""
+
+ _\\/|(/_
+ >_ _ /
+ (_)-(_)
+ ) o (
+ \ = / Invalid CrowdStrike API credentials or region specified.
+ |W|
+ | |
+ __| |__
+ / \ u / \
+ | `-' |
+ |__| |__|
+ |||ADM|||
+ ||| |||
+ ||| |||
+"""
+
+
+if __name__ == "__main__":
+ gui_image("program_icon.png", "program_icon.png")
+ gui_image("csfalcon.png", "running_icon.png")
+ gui_image("cs-logo.png", "config_icon.png")
+ main()
diff --git a/samples/workflows/workflow_manager_gui_requirements.txt b/samples/workflows/workflow_manager_gui_requirements.txt
new file mode 100644
index 00000000..2771dc98
--- /dev/null
+++ b/samples/workflows/workflow_manager_gui_requirements.txt
@@ -0,0 +1,4 @@
+crowdstrike-falconpy
+gooey
+requests
+tabulate
\ No newline at end of file
diff --git a/samples/workflows/workflow_manager_requirements.txt b/samples/workflows/workflow_manager_requirements.txt
new file mode 100644
index 00000000..0dc177d1
--- /dev/null
+++ b/samples/workflows/workflow_manager_requirements.txt
@@ -0,0 +1,3 @@
+crowdstrike-falconpy
+tabulate
+termcolor
\ No newline at end of file