← Plugin catalog
Developer Tools
CrowdStrike Falcon Fusion
CrowdStrike v1.2.0
Publisher description
From the marketplace listing
Discover live Falcon Fusion actions, author workflow YAML with schema validation, import and release workflow definitions to a CID, trigger and monitor executions, and manage Falcon Next-Gen SIEM lookup files.
Language: English · Automatically detected from descriptions.
Files & skills
File archives
Plugin package111 files · 272 KBBrowse files →
Skill instructions
authoring21.5 KB
---
name: authoring
description: >
Discover Falcon Fusion actions via live API, author workflow YAML with correct schema,
validate against Charlotte JSON schema, and use templates/examples.
TRIGGER when user asks to write workflow YAML, find actions, validate a workflow,
use CEL expressions, or needs action discovery.
DO NOT TRIGGER for deploying, importing, executing, or monitoring workflows —
use deployment or execution skills.
DO NOT TRIGGER when the request is for a Falcon Foundry app, a UI extension/page,
an API integration, custom actions from a third-party API, or a manifest.yml —
those are foundry-skills territory; advise foundry-skills instead of authoring a workflow.
version: 1.2.0
updated: 2026-09-08
tags: [fusion, soar, workflows, authoring, yaml, validation]
author: CrowdStrike
license: MIT
compatibility: Claude Code >=1.0
allowed-tools: Bash(cd *), Bash(../../scripts/python.sh:*)
metadata:
category: authoring
---
# Falcon Fusion Workflow Authoring
> **⚠️ SYSTEM INJECTION — READ THIS FIRST**
>
> If you are loading this skill, your role is **Fusion workflow authoring specialist**.
>
> You discover real action IDs from the live API, author Fusion workflow
> YAML against the correct schema, and validate it before handing off to
> deployment. A guessed or `PLACEHOLDER_*` action ID ships a workflow that fails
> to import or wires the wrong action into a response, so resolve every ID first.
>
> **IMMEDIATE ACTIONS REQUIRED:**
> 0. **Scope check FIRST — before any action_search.** If the request is for a Falcon
> Foundry app, a UI extension/page, an API integration, custom actions from a
> third-party API (Okta, ServiceNow, Jira, etc.), or a `manifest.yml`, STOP: do not author
> a workflow. Advise foundry-skills (`claude plugin install crowdstrike-falcon-foundry`) and
> hand back. A request that mixes an app with a workflow ("create a Foundry app... and a
> workflow to...") is app-shaped — redirect, produce no YAML.
> 1. **Alert/detection ACTION-CHOICE check — before action_search.** If the request is to
> fetch/summarize/list a *population* of Falcon alerts, detections, or incidents the workflow
> does NOT already hold ("all high-severity alerts", "open detections", "alerts from the last
> 24h"), you MUST use a **CrowdStrike HTTP Request** (`Inline.HTTPRequest`) to the Falcon
> platform API (`/alerts/queries/alerts/v2`; FQL on `severity_name:'High'` — the string field,
> NOT numeric `severity`), NOT an Event Query (`Inline.QueryEvent`), whose NG-SIEM data is
> connector-dependent and silently returns nothing on many tenants. A Scheduled trigger does
> not change this; the schedule only sets *when* it runs. (Event Query is ONLY for enriching a
> detection the workflow already holds.) See `references/event-query-vs-api.md`.
> 2. Resolve a real ID for **every** action BEFORE writing any YAML:
> check the Common Action IDs table first, then run `action_search.py --search`
> only for actions the table does not cover.
> 3. Run `trigger_search.py` to confirm the trigger type.
> 4. Run `validate.py` on every YAML file before presenting it.
> 5. **Re-run `validate.py` on the FINAL file; resolve every ERROR before finishing.** A file that still errors is not done. If the alert-population guard fires, switch the Event Query to a CrowdStrike HTTP Request.
>
> **MUST NOT:**
> - Author a workflow for a Foundry-app-shaped request (see action 0) — redirect to foundry-skills.
> - Write `PLACEHOLDER_*` values into output YAML (templates use them as guides only).
> - Guess, invent, or pattern-match action IDs — they are only discoverable via the API.
> - Invent a `config_id` or emit a stand-in — an all-zeros UUID (`0000...`) is still a placeholder. Discover or ask (AskUserQuestion).
> - Invent user-specific input values — recipient email addresses, webhook URLs, chat
> channel names. **Ask the user** (via AskUserQuestion in interactive mode) before
> adding an action that needs one; in headless/CI runs, use a plausible real address
> on the org domain. (Send email only delivers to Falcon users and approved domains,
> so `user@example.com` fails at runtime.)
> - Skip validation, or defer it to deploy time.
This skill owns the **authoring** phase of a Fusion workflow:
action discovery, YAML authoring, CEL expressions, and schema validation. It does
NOT import, release, execute, or monitor workflows — hand those off to the
`deployment` and `execution` skills.
---
> **Running the scripts.** Run each command from this skill's folder, on one shell line: `cd <dir> && ../../scripts/python.sh scripts/<name>.py`. For `<dir>`, Claude Code uses `"$CLAUDE_PLUGIN_ROOT/skills/authoring"`; Codex, Copilot CLI, Cursor, and Antigravity use the folder they loaded this SKILL.md from (e.g. `~/.agents/skills/authoring`). The wrapper bootstraps its own Python venv.
## Prerequisites
- **Python 3.13+** with the `falconpy` SDK and `pyyaml` installed.
- **CrowdStrike API credentials** (never hardcoded) — `common/scripts/auth.py` resolves them from `FALCON_CLIENT_ID`/`FALCON_CLIENT_SECRET` (plus optional `FALCON_BASE_URL`) or a `~/.cache/crowdstrike-falcon-fusion/credentials.toml` profile (chosen by `FALCON_PROFILE` or the file's `default` key). Run `/crowdstrike-falcon-fusion:setup` to configure interactively.
- **Workflow** API scope on the API client, with read access to
the activities catalog and import (validate) permission.
- Fusion access in the target CID.
Test credentials before authoring:
```bash
../../scripts/python.sh ../../common/scripts/auth.py
```
---
## Core Workflow
Follow these steps in order — do not skip discovery (steps 1–2).
### 1. Resolve action IDs (MANDATORY)
Every action needs a real `id` from the catalog before you write any YAML. Resolve
them **table-first**: check the Common Action IDs table below, and only run
`action_search.py` for the actions it does not cover. Guessing an ID or shipping
a `PLACEHOLDER_*` is never acceptable — but a verified ID from the table is
already resolved, so searching for it again just wastes a round-trip.
#### Common Action IDs — check here first
These actions show up in almost every workflow, with IDs verified against the
live catalog. If an action is in this table, use the row directly and do **not**
search for it.
| Action | `id` | `class` | `version_constraint` |
|--------|------|---------|----------------------|
| Event Query | `cdf5c3e0d69f156eaaf56c1f5d3f1b66` | `Inline.QueryEvent` | `~1` |
| HTTP Request | `1ba474f407d9228fc8fa02cdce8ae8ef` | `Inline.HTTPRequest` | `~1` |
| Python Script | `7fb9eb10b23943efaf1e6082b0ac0338` | `Inline.Python` | `~1` |
| Send email | `07413ef9ba7c47bf5a242799f59902cc` | — | `~1` |
| Charlotte AI - LLM Completion | `bdfecafafdb44919a458fcf51d6b93a7_98dec86072334d24b37dd798098cfd63` | — | `~0` |
| Contain device | `bec9fbeb4999d207937854fd56088107` | — | `~0` |
| VirusTotal File Hash Lookup | `668bf0d0b832510e21d7c00386d277ea` | — | `~1` |
| VirusTotal IP Request | `ce0386aacfb64bc5a3a6a4a85c07217b` | — | `~0` |
| DomainTools Iris Investigate | `b2087ff84aa1471ea209076fd4852c25` | — | `~0` |
These IDs are stable across the commercial clouds (us-1/us-2/eu-1); GovCloud may
differ. If an import ever rejects one of these `version_constraint` values,
confirm the current value with `action_search.py --search "<name>"` — the
platform occasionally bumps an action's major version.
#### Search for anything not in the table
For the remaining actions, resolve them in one batch — list every action the
workflow needs and run one `--search` per distinct name. **Fire the independent
lookups concurrently: put every `action_search.py --search` and the
`trigger_search.py` call in one message so they run in parallel.** Never
re-search an ID you already have — rediscovering an action mid-pass is the
biggest time sink.
```bash
# Search by name across all vendors (note the --search flag; a bare term errors)
../../scripts/python.sh scripts/action_search.py --search "contain"
# Search within a specific vendor
../../scripts/python.sh scripts/action_search.py --vendor "Okta" --search "revoke"
# Full schema for one action (input fields, class, plugin info)
../../scripts/python.sh scripts/action_search.py --details <action_id>
```
For each action you discover, record: `id` (an opaque catalog identifier), `name`, input
fields/types, its `version_constraint` (nearly all have one), `class` if any,
and whether it is a plugin action (needs a `config_id`).
> If a long-lived local cache might be hiding newly shipped actions, refresh it:
> `../../scripts/python.sh scripts/action_search.py --clear-cache`. The cache also auto-refreshes
> once it is older than 1 hour.
### 2. Choose a trigger type
```bash
../../scripts/python.sh scripts/trigger_search.py --list
../../scripts/python.sh scripts/trigger_search.py --type "On demand"
../../scripts/python.sh scripts/trigger_search.py --events detection # Signal event: values
../../scripts/python.sh scripts/trigger_search.py --fields Investigatable/EPP # payload field paths
```
Valid trigger types: **On demand**, **Signal**, **Scheduled**, **SubModel**.
For most automation, use **On demand** (callable via API and the Falcon UI).
A **Signal** trigger MUST carry an `event:` field (the trigger category, e.g.
`Investigatable/NGSIEM`) — without it, import fails with `code 2003: "unknown
trigger event named "`. Find the value with `trigger_search.py --events` and do
NOT add a hex `id` to the trigger. For a Signal trigger, discover the exact
payload field paths (the `${data['Trigger....']}` references you can read
downstream) with `trigger_search.py --fields <category>` — do NOT guess them.
See `references/trigger-types.md`.
### 3. Author the YAML from a template
Pick the template matching the pattern, then substitute real values:
| Pattern | Template |
|---------|----------|
| Single action | `assets/single-action.yaml` |
| Loop over a list | `assets/loop.yaml` |
| Conditional branching | `assets/conditional.yaml` |
| Loop + conditional | `assets/loop-conditional.yaml` |
Add the header comment on line 1, then write `name`, `trigger`, and `actions`
using the resolved action IDs. Templates contain `PLACEHOLDER_*` markers — they show
the YAML shape, never the values. Substitute every one with a real value.
### 4. Add CEL expressions
Reference trigger inputs and prior outputs with `${data['...']}` expressions:
| Syntax | Meaning |
|--------|---------|
| `${data['param_name']}` | On-demand trigger parameter (no prefix) |
| `${data['ActionLabel.OutputField']}` | A prior action's output |
| `${data['array_param.#']}` | Current loop item |
| `${data[?'key'].orValue("default")}` | Null-safe optional access (preferred) |
See `references/cel-expressions.md` for operators, CrowdStrike extensions
(`cs.json.decode()`, `cs.ip.valid()`, `cs.timestamp.now()`), and YAML quoting.
### 5. Validate
```bash
# Pre-flight + structural + API dry-run
../../scripts/python.sh scripts/validate.py workflow.yaml
# Pre-flight + structural only (no API call)
../../scripts/python.sh scripts/validate.py --preflight-only workflow.yaml
```
Fix every error before handing the file to the `deployment` skill.
---
## Script Reference
All scripts live in `scripts/` and import auth from `common/scripts/auth.py`
via the shared `sys.path` pattern.
| Script | Purpose | Key flags |
|--------|---------|-----------|
| `action_search.py` | Discover actions and vendors | `--search`, `--details`, `--list`, `--vendors`, `--vendor`, `--use-case`, `--limit`, `--offset`, `--json`, `--clear-cache` |
| `trigger_search.py` | List/describe trigger types; list Signal `event:` values; list a trigger's payload field paths | `--list`, `--type`, `--events`, `--fields`, `--json` |
| `validate.py` | Validate workflow YAML | `--preflight-only`, multiple files |
**`action_search.py` cache:** Full-catalog scans (`--vendors`, `--use-case`) are
cached locally in `.action_cache.json` (gitignored, per-user). The cache uses a
**1-hour TTL based on file mtime**: a cache at or past 1 hour is treated as stale
and auto-refreshes from the API (with a printed notice). Use `--clear-cache` to
force an immediate refresh so newly shipped action types are never hidden.
---
## Common Pitfalls / Counter-Rationalizations
| Thought | Reality |
|---------|---------|
| "I'll write the YAML, then fill in action IDs later." | STOP. Resolve every ID first — from the Common Action IDs table, or `action_search.py`. "Later" never happens — placeholders ship. |
| "I'll search for the Event Query / HTTP / Send email / Charlotte AI action." | DON'T. Those are in the Common Action IDs table — use the row directly. |
| "I'll run `action_search.py \"event query\"` to search." | WRONG FLAG. A bare term prints usage and finds nothing. Use `action_search.py --search \"event query\"`. |
| "I can guess the action ID format." | WRONG. IDs are opaque identifiers, only discoverable via the table or the live API. |
| "The template has `PLACEHOLDER_RAN_006`, I'll copy it." | NEVER. Templates are structural guides. Substitute a real value before saving. |
| "Validation can wait until deploy." | NO. Validate after authoring — `validate.py` catches PLACEHOLDERs, bad IDs, and schema errors locally. |
| "Only class-based actions need `version_constraint`." | WRONG. Not class-specific — nearly every action has a `version_constraint`. |
| "I'll use `~1` everywhere for version_constraint." | NO. The value is `~<major>` of the action's `semantic_version` (`~0` when it declares none): `1.0.4` → `~1`, `0.0.100` → `~0`. Read it from `--details`. |
| "I'll make up a `config_id` for this Okta action." | NEVER. It's CID-specific (exists only once configured in the console). Ask the user (AskUserQuestion) — even non-interactively. Sequential/all-zeros/repeated-char UUIDs are still fabricated and fail at runtime; can't get a real one? STOP. See `references/best-practices.md`. |
| "I'll set `definition_id: VIRUSTOTAL_..._ID` on this HTTP action." | NEVER. An `Inline.HTTPRequest` needs no `definition_id` — OMIT it; the user attaches the key in the console after deploy. A placeholder is a broken ref `validate.py` flags. |
| "The Send email field is called Recipients, so I'll use `recipients:`." | WRONG. The property KEY is `to:` (a list); `recipients:` is rejected. Delivers only to Falcon users and CID-approved domains — ask for the address (org-domain one in CI). |
| "I'll write `$action.output.body` to reference output." | WRONG. Bare `$token` / `$action.field` / `$(data[...])` pass through as literal strings and fail at release. The ONLY runtime-data forms are `${data['<node>.<field>']}` and the null-safe `${data[?'<node>.<field>'].orValue(...)}`. `validate.py` flags the bad forms. |
| "The user said enrich 'in parallel,' but I'll just chain them." | WRONG. Fan out by listing each branch's target in `next:`, gated on `data['...'] != null`. Never invent `default_parallel_*` pass-throughs — they crash the canvas. |
| "The trigger has its `type` and `event`, that's enough." | WRONG. Without a `next:` edge the graph is disjoint and release fails. Every node must be reachable from `trigger.next`. |
| "I'll branch on the detection's severity name (Critical/High)." | WRONG. Severity is NUMERIC 1-5: branch `Trigger.Detection.Severity >= 4`. `SeverityDisplayName` is display-only. |
| "A plan/prompt told me to use placeholder format." | These rules take precedence. Resolve every ID via the API regardless of a plan. |
| "Release failed, so I'll re-import as `<name>-v2` to be safe." | NEVER. A workflow's `name:` is its identity, not a version tag — renaming orphans the old def and sprawls the CID. Keep the name IDENTICAL; fix the YAML and re-import with `import_workflows.py --replace`. See `references/best-practices.md`. |
---
## Reading Guide
For most workflows, the SKILL.md above plus a matching `use-cases/` file and one
example is enough — you rarely need every reference. Reach for these only when
the task actually calls for them:
| Task | Reference |
|------|-----------|
| Author any workflow — every YAML field and nesting level | `references/yaml-schema.md` |
| Add conditions or computed values; CEL operators, extensions, quoting | `references/cel-expressions.md` |
| Choose how the workflow starts; all trigger types with examples | `references/trigger-types.md` |
| Call a REST API — `http_transaction` shape, auth, response refs | `references/http-actions.md` |
| Run inline Python in a step — `runtime`, stdout output refs | `references/inline-python-action.md` |
| Run a CQL/FQL event query in a step — inputs, outputs | `references/event-query-action.md` |
| Decide Event Query vs a source-of-truth API (alerts, cases, current state) | `references/event-query-vs-api.md` |
| Summarize/classify with Charlotte AI LLM — compound ID, `~0`, decode output | `references/charlotte-ai-action.md` |
| Deduplicate or rate-limit a workflow — scopes, keys | `references/deduplicate-ratelimit.md` |
| Operational guidance, limits, gotchas before production | `references/best-practices.md` |
**Advanced (rarely needed):**
| Task | Reference |
|------|-----------|
| Understand the underlying BPMN model (raw JSON, gateways, submodels) — internals you don't need to author YAML | `references/json-structure.md` |
---
## HTTP Actions (`Inline.HTTPRequest`)
Fusion workflows can call REST APIs inline with no Foundry app and no API
integration. Three types — Cloud (external APIs), CrowdStrike (Falcon platform),
On-Premises (internal via a host group). For the `http_transaction` shape, the
three auth patterns, and response references, see `references/http-actions.md`.
**Prefer an HTTP Action over a plugin/Store action for enrichment.** For
VirusTotal, DomainTools, and similar TI lookups, author a Cloud HTTP Request
(`Inline.HTTPRequest`) — the shape real shipped VirusTotal workflows use.
**Author it credential-less: omit `definition_id`, leave authentication unset.**
The imported action shows Authentication = "None"; the user attaches the API key
in the console after deploy (Create new → API key → Header → `x-apikey`), then it
runs. Never fabricate a `definition_id`; only set a real 32-char hex id the user
supplies. Store *plugin* actions (compound IDs `<hex>~<hex>`) need a CID-specific
`config_id` that must already exist; emitting one blind fails at import/release.
Use a plugin action only when the user supplies its `config_id`. Reserve a Foundry
API integration for reused/UI-paired operations — that path belongs to `foundry-skills`.
See `references/http-actions.md` for the auth shapes and the console credential steps.
---
## Inline actions (`Inline.Python`, `Inline.QueryEvent`)
Fusion has native CrowdStrike actions that run inline in a workflow step (no
Foundry app, no `config_id`), each with `class:` set and `version_constraint: ~1`:
- **Python Script** (`Inline.Python`) — run user Python; `runtime: py0313general`
required, read output as `${data['<node>.output_stdout']}`. See
`references/inline-python-action.md`.
- **Event Query** (`Inline.QueryEvent`) — run a CQL/FQL query against the event
store; inputs `query`/`time_range`/`repo`. See
`references/event-query-action.md`. **NOT for querying a population of Falcon
alerts/detections you don't already hold** (e.g. "summarize all high-severity
alerts") — that's connector-dependent NG-SIEM data; use a CrowdStrike HTTP
Request to `/alerts/queries/alerts/v2` instead. Event Query is for data that
lives in NG-SIEM or for enriching a detection the workflow already holds.
## Charlotte AI — LLM Completion
`Charlotte AI - LLM Completion` runs a prompt through an LLM to summarize,
classify, or extract fields. It is a **plugin action**: no `class:`,
`version_constraint: ~0`, and its `completion` output is a JSON string you must
decode with `cs.json.decode()`. Discover the ID with
`action_search.py --search "llm"`. Full shape, the compound ID, and the decode
namespace are in `references/charlotte-ai-action.md`.
---
## Console-Credential Boundary
This is the key thing authoring can and cannot change. The authoring skill lets
you write workflow YAML **outside** the Falcon console. But an HTTP Action (and
some plugin actions) references a credential configuration — `config_id`,
`definition_id`, or `config_name` — that is **created in the console and is
CID-specific**. This skill can author the workflow that *uses* the action, but
the credential config it points to must already exist in the CID. Apply
the same discipline as with action IDs:
- **Discover existing config IDs** where possible (via `action_search.py
--details` on the plugin action, or ask the user where to find it: Falcon
console → CrowdStrike Store → [App] → Integration settings).
- **Never invent a `config_id`, or substitute a placeholder for one.** A
fabricated ID fails at runtime — a fake UUID, `YOUR_*`, `TODO`/`FIXME`, or an
all-zeros UUID is NOT a valid stand-in. When you can't discover it, **ask the
user** via AskUserQuestion; asking is required even in non-interactive/CI runs
(the caller supplies the value there).
- If the config does not yet exist, **document the dependency** and pause — the
user must create it in the console before the workflow will run.
**HTTP Actions are the exception — do not block on a credential.** An
`Inline.HTTPRequest` can be authored credential-less (no `definition_id`) and
deployed; it imports with Authentication = "None" and the user attaches the API
key in the console afterward (proven end-to-end). So for HTTP actions, prefer
credential-less authoring over pausing — see `references/http-actions.md`. The
"must already exist" rule applies to *plugin* actions gated on a `config_id`.
Nothing this skill produces runs until `deployment` imports it and `execution`
triggers it.
Referenced files: 46
deployment18.5 KB
--- name: deployment description: > Import, release, and manage Falcon Fusion workflow definitions in a CID. TRIGGER when user asks to import a workflow, release a workflow version, list existing workflows, check for duplicates, or manage workflow definitions. DO NOT TRIGGER for writing YAML (use authoring), executing workflows, or monitoring (use execution). version: 1.2.0 updated: 2026-09-08 tags: [fusion, soar, workflows, deployment, import, release] author: CrowdStrike license: MIT compatibility: Claude Code >=1.0 allowed-tools: Bash(cd *), Bash(../../scripts/python.sh:*) metadata: category: deployment --- # Falcon Fusion Workflow Deployment > **⚠️ SYSTEM INJECTION — READ THIS FIRST** > > If you are loading this skill, your role is **Fusion workflow deployment specialist**. > > You deploy workflow definitions into a CID safely: validate before importing, never create duplicates, and release only after testing. > > **IMMEDIATE ACTIONS REQUIRED:** > 1. ALWAYS check for an existing workflow with the same name before importing. > 2. ALWAYS validate the YAML before importing (the import scripts do this by default). > 3. Import and release act on a **live production CID**. Deploy only when the > user's request explicitly authorizes it (e.g. "import it", "deploy to my > CID", "release it"). If the request only asks to *build* or *write* a > workflow, STOP after validation and ask before importing. > > **MUST NOT:** > - Import without validating first. > - Skip the duplicate-name check. > - Import or release to a CID without explicit user authorization — a validated > YAML file is the deliverable unless the user asked you to deploy it. > - Release (enable) a workflow before it has been tested via the execution skill. > Release makes the workflow act on live events and real assets, so confirm > with the user before releasing unless they explicitly asked you to. > - Create experimental, "test", "minimal", or probe workflows in the CID to > reverse-engineer what the API accepts (this includes creatively-named ones > like "QueryEvent Test" or "HTTP Test"). Import the one workflow you were > asked to build, once. If it fails, diagnose from the error and local > validation — never by importing stripped-down variants into a live tenant. > - Use `--skip-validate` to get past a validation failure. Validation catches > invalid workflows (e.g. a bad `trigger.type`) that otherwise fail at the API > as an opaque 500. Fix the workflow instead of skipping the check. > - Retry an import that returns a 500 / Internal Server Error more than once. > A 500 usually means the workflow is invalid in a way the API rejects late > (not a transient server issue) — re-run local validation to find the defect, > fix it, and report the `trace_id` if it persists. Do not loop re-importing. > - Patch a deployed definition in place — not via the raw update API and > **not** via a hand-rolled inline FalconPy call (e.g. `update_definition`) to > edit a deployed copy. The only supported *update* path is: fix the source > YAML, then re-import. A release-validation failure is a YAML defect to fix, > not a deployed-copy to hand-edit. > - Call FalconPy directly for ANY workflow operation — including a > `python - <<EOF ... delete_definition(...)` snippet to clean up a failed > import attempt. Deleting is fine, but it MUST go through `delete_workflow.py` > (or `scripts/cleanup_workflows.py`), which wrap the supported endpoints. Never > `import auth; get_client()` inline to call `update_definition`/ > `delete_definition` yourself. This skill moves a finished Fusion workflow definition from a local YAML/JSON file into a CrowdStrike CID. Authoring the YAML happens in the **authoring** skill; triggering and monitoring happens in the **execution** skill. Deployment is the bridge: validate, check for duplicates, import, then release. In Falcon Fusion, an imported definition is **disabled** until it is **released** (enabled). Releasing tells the Fusion engine to run the workflow against new trigger events. Keep the workflow disabled until you have tested it. > **Running the scripts.** Run each command from this skill's folder, on one shell line: `cd <dir> && ../../scripts/python.sh scripts/<name>.py` (a sibling skill's script is `../<skill>/scripts/<name>.py`). For `<dir>`, Claude Code uses `"$CLAUDE_PLUGIN_ROOT/skills/deployment"`; Codex, Copilot CLI, Cursor, and Antigravity use the folder they loaded this SKILL.md from (e.g. `~/.agents/skills/deployment`). The wrapper bootstraps its own Python venv. ## Prerequisites - **Python 3.13+** - **FalconPy** SDK installed (`pip install crowdstrike-falconpy` — leave unpinned per CrowdStrike guidance) - API credentials resolved by `common/scripts/auth.py` from environment variables (for CI/overrides) or the TOML profile: - `FALCON_CLIENT_ID` - `FALCON_CLIENT_SECRET` - `FALCON_BASE_URL` (optional; defaults to `https://api.crowdstrike.com`) Run `/crowdstrike-falcon-fusion:setup` to configure credentials interactively (writes the TOML profile). - An API client with the **Workflow** API scope (read + write) - Verify auth before deploying: ```bash ../../scripts/python.sh ../../common/scripts/auth.py ``` ## Core Workflow Deployment is a four-step pipeline. Do not skip steps 1 and 2. ### 1. Validate the YAML first Validation is owned by the **authoring** skill's `validate.py`. The import script calls it automatically, but run it manually first when iterating: ```bash ../../scripts/python.sh ../authoring/scripts/validate.py workflows/my-workflow.yaml ``` Fix every structural error before continuing. A definition that fails validation will be rejected by the API. ### 2. Check for an existing workflow with the same name Workflow names must be unique within the tenant. Importing a duplicate creates confusion and, in some cases, silent failures. Check first: ```bash # Exact-name check (exit 0 if it exists, 1 if not) ../../scripts/python.sh scripts/query_workflows.py --check-name "My Workflow" # Or extract the name straight from the YAML and check ../../scripts/python.sh scripts/query_workflows.py --check-yaml workflows/my-workflow.yaml ``` If a duplicate is found, this is almost always your own earlier attempt at the same workflow. Iterate in place with `import_workflows.py --replace` (it deletes the existing same-name definition, then re-imports). **Do not rename the workflow to `<name> v2` to get past the check** — a renamed copy leaves the old definition orphaned in the CID, and every retry sprawls another dead workflow. Keep the name stable across attempts; use `--replace` (or delete the old definition explicitly) instead. ### 3. Import the definition ```bash # Single file — validates and checks duplicates by default ../../scripts/python.sh scripts/import_workflows.py workflows/my-workflow.yaml # A whole directory of definitions (all *.yaml/*.yml) ../../scripts/python.sh scripts/import_workflows.py workflows/ ``` On success the script prints the new **definition ID**. Capture it — you need it to release and to execute the workflow. **Post-import: configure HTTP-Action credentials in the console.** If the workflow contains a credential-less HTTP Action (authored without a `definition_id`), it imports with Authentication = "None". Tell the user to attach the API key in the console before the action will succeed: open the Cloud HTTP Request action → Authentication → **Create new** → API key → secret key → location **Header** → header name (e.g. `x-apikey`) → **Test** → Save (or **Use existing** if a matching credential already exists). A `401`/`403` at runtime almost always means this step is pending. See `../authoring/references/http-actions.md`. **If the import fails, stop — do not loop.** Some import failures are *not* fixable by editing the YAML, and retrying wastes time and tokens. Read the error and route accordingly: | Error from the API / import script | What it means | What to do | |------------------------------------|---------------|------------| | `no definition ID (workflow not created)` | The API accepted the call but created nothing | Stop. Report it — this is usually a missing plugin config or a server-side issue, not a YAML defect. Do not re-edit and retry. | | `API returned status 500` / "Internal Server Error" | Server-side error (a `trace_id` is included) | Stop. Report the `trace_id` to the user; a 500 is not something YAML edits fix. Retry at most once. | | Missing / unknown `config_id` for a plugin action (VirusTotal, DomainTools, Charlotte AI, Slack, Zscaler) | The integration is not installed/configured in this CID | Stop. Tell the user which action needs a console-created `config_id`; do not invent one or loop editing. | | Structural / validation error | A real YAML defect | Fix the YAML, then re-validate and re-import (this one *is* worth iterating on). | Only the last row justifies editing and retrying. For the others, surface the error to the user and stop — repeatedly re-importing against a 500 or a missing config will not succeed. **Never debug by importing probe workflows.** When an import fails, do not build "Test QueryEvent", "Minimal trigger", or other stripped-down workflows in the CID to isolate what the API accepts. That litters the tenant with disabled junk and burns time without fixing the real workflow. Diagnose from the error message and `validate.py` output instead, and if the blocker is a missing plugin `config_id`, report it — that is a console/CID setup step the user must do, not something more imports will resolve. ### 4. Release (enable) the workflow Releasing enables the definition so the Fusion engine runs it against trigger events. Do this only after testing (see the execution skill): ```bash ../../scripts/python.sh scripts/release_workflow.py --id <definition_id> ``` **If release reports validation errors, stop — do not patch the deployed definition.** A workflow can import successfully yet fail validation at release (the server validates more strictly than import). When that happens, fix the **source YAML**, then re-validate and re-import with `import_workflows.py` — that re-import is the only supported way to update a definition. Do **not** try to repair the deployed copy in place through *any* direct definition-mutation call. That includes the raw workflow update / definition API (`WorkflowDefinitionsUpdate`, `.../entities/definitions/v1`), **and any hand-rolled FalconPy call** — an inline `python - <<'EOF' ... from falconpy` snippet that reaches for `update_definition`, `delete_definition`, or similar is the same forbidden path wearing a disguise. None of those are part of this skill, and looping edits against them returns repeated 500s without ever fixing the workflow. Report the release error (include any `trace_id`) and fix the YAML at the source. A concrete failure mode: the release error `exclusive gateway '<name>' outgoing flow ... has no condition set and is not marked as default` means a condition node has a bare `next:` with neither `default: true` nor a `cel_expression`. Fix it in the **source YAML** (add a `cel_expression` to the gated branch, with its no-match fallthrough in `else:` — `validate.py` now catches this before deploy, including inside nested loops) and re-import. Never fan out with a bare `default: true` pass-through; list the branch targets directly in the source node's `next:`. Do not hand-edit the deployed definition to add the missing flag. **The exact recovery loop (do this, not the escape hatch):** ```bash # 1. Fix the condition in the SOURCE YAML (add cel_expression + else:). # Keep the workflow `name:` IDENTICAL — do NOT bump it to `<name>-v2`. # The name is the workflow's identity; --replace matches on it. # 2. Re-validate — this now catches the release-failing shape pre-deploy: ../../scripts/python.sh ../authoring/scripts/validate.py my-workflow.yaml # 3. Re-import with --replace: deletes the broken same-name definition and # imports the fixed YAML in one step (supported delete + import, not a patch). ../../scripts/python.sh scripts/import_workflows.py --replace my-workflow.yaml ``` `--replace` keeps ONE definition per workflow name instead of leaving a renamed copy per attempt — do NOT rename-and-reimport to dodge the duplicate check, which sprawls the CID with dead definitions. (Note: each `--replace` assigns a new definition ID; true in-place update via the PUT endpoint is not currently usable.) Reaching for `python - <<EOF ... update_definition(...)` to patch the deployed copy is never step 2. It does not fix the source, so the next re-import reintroduces the same defect, and the API returns repeated 500s. Re-import from fixed source is the whole recovery. ## Script Reference All scripts add `common/scripts` to `sys.path` and import `get_client` from the shared `auth` module. Run them from anywhere; paths are anchored to each script's own location. | Script | Purpose | Key flags | |--------|---------|-----------| | `query_workflows.py` | List, search, and check for existing workflows | `--list`, `--search TERM`, `--check-name NAME`, `--check-yaml FILE...`, `--json` | | `import_workflows.py` | Validate, dedupe, and import definitions | `FILE\|DIR...` (positional), `--skip-validate`, `--skip-duplicate-check`, `--replace` | | `release_workflow.py` | Release (enable) a definition by ID | `--id DEF_ID` (required), `--json` | | `delete_workflow.py` | Delete a definition by ID or exact name | `--id DEF_ID`, `--name NAME` (repeatable), `--yes`, `--json` | ### query_workflows.py ```bash ../../scripts/python.sh scripts/query_workflows.py --list # All definitions ../../scripts/python.sh scripts/query_workflows.py --search "contain" # Substring match ../../scripts/python.sh scripts/query_workflows.py --check-name "My Flow" --json ../../scripts/python.sh scripts/query_workflows.py --check-yaml *.yaml # Batch duplicate check ``` `--check-name` and `--check-yaml` exit non-zero when a duplicate exists, so they compose cleanly in shell pipelines and CI gates. ### import_workflows.py ```bash ../../scripts/python.sh scripts/import_workflows.py wf.yaml # Default: validate + dedupe ../../scripts/python.sh scripts/import_workflows.py --skip-validate wf.yaml # AVOID — see pitfall 2 ../../scripts/python.sh scripts/import_workflows.py --skip-duplicate-check wf.yaml ../../scripts/python.sh scripts/import_workflows.py ./workflows/ # Glob a directory ``` Supports YAML and JSON definitions, batch mode (multiple files), and directory expansion (`*.yaml`/`*.yml`). Prints a per-file summary and exits non-zero if any file failed or was a duplicate. ### release_workflow.py ```bash ../../scripts/python.sh scripts/release_workflow.py --id 1a2b3c... # Enable ../../scripts/python.sh scripts/release_workflow.py --id 1a2b3c... --json # Machine-readable ``` Calls the Workflows definition-action endpoint with `action_name="enable"`. The definition ID comes from the import step or from `query_workflows.py`. ### delete_workflow.py ```bash ../../scripts/python.sh scripts/delete_workflow.py --id 1a2b3c... # Delete by ID ../../scripts/python.sh scripts/delete_workflow.py --name "Probe run 1" # Delete by exact name ../../scripts/python.sh scripts/delete_workflow.py --id 1a2b3c... --yes # Skip confirmation (scripted) ``` Deletes a whole definition via the Workflows delete endpoint (FalconPy `delete_definitions`). Use it to remove test, duplicate, or throwaway workflows. Deletion is permanent, so it prompts for confirmation unless `--yes` is passed (or `FUSION_SKILLS_SUPPRESS_CONFIRM=1` is set for test harnesses). This is the supported way to *remove* a workflow — it is **not** a way to *edit* a deployed one: to change a workflow, fix the source YAML and re-import (see pitfall 5). ## Common Pitfalls 1. **Importing a duplicate name.** Names must be unique in the tenant. Always run `query_workflows.py --check-name` (or `--check-yaml`) first. A duplicate import can fail silently or produce an "Unknown error." 2. **Importing without validating.** Skipping validation (`--skip-validate`) pushes broken YAML to the API. Never use `--skip-validate` to get past a validation failure — an invalid workflow (for example a bad `trigger.type`) then fails at the API, often as an opaque **500 Internal Server Error** that looks like a server problem but is really a broken definition the local validator would have caught. Fix the workflow and let validation run. `--skip-validate` is only for when you have already validated the same file separately in the same session. 3. **Releasing before testing.** A freshly imported definition is disabled for a reason. Test it with the execution skill (`trigger_workflow.py`) before calling `release_workflow.py`. Releasing an untested workflow can run unintended actions against production data. 4. **Losing the definition ID.** The import output contains the ID you need for release and execution. Capture it; re-finding it later means a `query_workflows.py --search` round-trip. 5. **Wrong API scope.** Import and release require the Workflow scope with **write** access. A read-only client lists workflows fine but fails on import/release with a permissions error. 6. **Editing the deployed copy, not the source.** Re-importing an edited YAML creates a new definition (or trips the duplicate check). Treat the local YAML as the source of truth; re-import to update, and remember the new definition is disabled again until re-released. Never try to patch a deployed definition in place through the raw workflow update API **or a hand-rolled inline FalconPy call** (`update_definition` in a `python - <<EOF` snippet) — that path is not part of this skill and looping edits against it just returns 500s. (Deleting a whole workflow is fine — use `delete_workflow.py`.) 7. **Looping on unrecoverable import errors.** A 500, a missing plugin `config_id`, or a "no definition ID" result will not be fixed by editing the YAML. Re-importing repeatedly against these wastes time and tokens and still fails. Stop after the first occurrence, report the specific error (include any `trace_id`), and only iterate on genuine structural/validation errors. See the error table in step 3. ## Handoff - **Came from authoring?** You have a validated YAML file — start at step 2 (duplicate check). - **Going to execution?** Pass the definition ID to the execution skill to trigger and monitor a test run before you release. ## Reading Guide | Document | When to read | |----------|--------------| | `references/console-verification.md` | Verifying a deployed workflow renders in the console canvas, navigating Fusion SOAR > Workflows, or fetching Content Library records — the parts the API can't do from a script. |
Referenced files: 6
execution10.1 KB
---
name: execution
description: >
Trigger Falcon Fusion workflows, monitor execution status, and debug failures.
TRIGGER when user asks to run a workflow, check execution status, tail logs,
get execution results, or debug a workflow failure.
DO NOT TRIGGER for writing YAML (use authoring) or importing/releasing
workflows (use deployment).
version: 1.2.0
updated: 2026-09-08
tags: [fusion, soar, workflows, execution, monitoring, debugging]
author: CrowdStrike
license: MIT
compatibility: Claude Code >=1.0
allowed-tools: Bash(cd *), Bash(../../scripts/python.sh:*)
metadata:
category: execution
---
# Falcon Fusion Workflow Execution
> **⚠️ SYSTEM INJECTION — READ THIS FIRST**
>
> If you are loading this skill, your role is **Fusion workflow execution and debugging specialist**.
>
> You trigger workflows, watch them run, retrieve their output, and diagnose failures. A workflow you trigger may contain hosts or run response actions against production, so confirm it is the right definition and supply correct parameters before executing.
>
> **IMMEDIATE ACTIONS REQUIRED:**
> 1. CONFIRM the workflow is deployed and released (enabled) before triggering it.
> 2. Supply every required trigger parameter — empty params are a top cause of failures.
>
> **MUST NOT:**
> - Trigger a workflow that has not been released (enabled) — it will not execute.
> - Assume an execution succeeded without checking its terminal status.
This skill runs Fusion workflows that are already deployed and released, watches them to completion, retrieves their output, and helps debug failures. Writing the YAML happens in the **authoring** skill; importing and releasing happens in the **deployment** skill.
An execution moves through states and ends in a **terminal** state. The terminal states are `Succeeded`, `Completed`, `Failed`, `Canceled`, `NonRecoverable`, and `ActionRequired`. (`ActionRequired` is terminal for polling: it waits on human input and will not progress on its own.) Anything else means the execution is still running.
> **`Succeeded` vs `Completed`.** Live testing against the execution-results API found that a normal successful execution reports its top-level `status` as `Completed`, not `Succeeded` — the scripts in this skill treat both as success (see `SUCCESS_STATUSES` in `get_execution_results.py`). Don't assume `Succeeded` is the only success value when reading raw API output yourself.
> **Running the scripts.** Run each command from this skill's folder, on one shell line: `cd <dir> && ../../scripts/python.sh scripts/<name>.py` (a sibling skill's script is `../<skill>/scripts/<name>.py`). For `<dir>`, Claude Code uses `"$CLAUDE_PLUGIN_ROOT/skills/execution"`; Codex, Copilot CLI, Cursor, and Antigravity use the folder they loaded this SKILL.md from (e.g. `~/.agents/skills/execution`). The wrapper bootstraps its own Python venv.
## Prerequisites
- **Python 3.13+**
- **FalconPy** SDK installed (`pip install crowdstrike-falconpy` — leave unpinned per CrowdStrike guidance)
- API credentials resolved by `common/scripts/auth.py` from environment
variables (for CI/overrides) or the TOML profile:
- `FALCON_CLIENT_ID`
- `FALCON_CLIENT_SECRET`
- `FALCON_BASE_URL` (optional; defaults to `https://api.crowdstrike.com`)
Run `/crowdstrike-falcon-fusion:setup` to configure credentials interactively (writes the TOML profile).
- An API client with the **Workflow** API scope
- The **definition ID** of the workflow to run (from the deployment skill's import output, or `query_workflows.py --search`)
- Verify auth before running:
```bash
../../scripts/python.sh ../../common/scripts/auth.py
```
## Core Workflow
### 1. Verify the workflow is deployed and released
A workflow must be enabled before it will execute. Confirm it exists and is enabled:
```bash
../../scripts/python.sh ../deployment/scripts/query_workflows.py --search "my workflow"
```
Look for `Status: enabled` in the output. If it shows `disabled`, release it first with the deployment skill (`release_workflow.py --id <id>`).
### 2. Trigger the workflow with a payload
```bash
# Pass parameters inline as JSON
../../scripts/python.sh scripts/trigger_workflow.py --id <definition_id> --params '{"device_id":"abc123"}'
# Or let the script prompt you interactively from the workflow's parameter schema
../../scripts/python.sh scripts/trigger_workflow.py --id <definition_id>
```
On success the script prints an **execution ID**. Capture it — you need it to monitor and to fetch results.
### 3. Monitor the execution
Poll until the execution reaches a terminal state:
```bash
../../scripts/python.sh scripts/monitor_execution.py --execution-id <execution_id>
# Tune the cadence for long-running workflows
../../scripts/python.sh scripts/monitor_execution.py --execution-id <execution_id> --interval 10 --timeout 600
```
Status updates go to stderr; the final result goes to stdout, so you can pipe the result while still watching progress.
> **Shortcut:** `trigger_workflow.py --wait` triggers and polls in one step. Use `monitor_execution.py` directly when you triggered earlier, from the console, or from another tool.
### 4. Get the results
```bash
../../scripts/python.sh scripts/get_execution_results.py --execution-id <execution_id>
../../scripts/python.sh scripts/get_execution_results.py --execution-id <execution_id> --json
```
This is a single fetch — use it after `monitor_execution.py` reports a terminal state, or any time you want the current status and output without polling.
### 5. Debug failures
When an execution ends in `Failed` or `NonRecoverable`:
- Pull the full record with `--json` to see the `output` and any error detail:
```bash
../../scripts/python.sh scripts/get_execution_results.py --execution-id <execution_id> --json
```
- Check the inputs you sent. Missing or empty required parameters are the most common cause.
- Re-run with corrected parameters. For workflows that support resume, the Fusion console can resume a failed execution; these scripts trigger fresh executions.
- **Analyzing failures across many executions** (success rates, top failing workflows, error-code breakdowns, find-by-value) is a job for CQL over the `fusion` execution-log repo, not the per-execution scripts. See `references/execution-log-queries.md`.
- **A workflow that looks stuck "in progress"** may be **throttled**, not failed — Fusion paces an action when its execution volume exceeds a limit, queuing and auto-retrying it (up to 6 hours). This is not an error. See `references/throttling.md`.
## Script Reference
All scripts add `common/scripts` to `sys.path` and import from the shared `auth` module. `monitor_execution.py` and `trigger_workflow.py` reuse `fetch_results` and the terminal-status set from `get_execution_results.py`, so they stay in sync on the API response shape.
| Script | Purpose | Key flags |
|--------|---------|-----------|
| `trigger_workflow.py` | Execute a workflow with params; optionally wait | `--id DEF_ID` (required), `--params JSON`, `--wait`, `--timeout SECS`, `--json` |
| `monitor_execution.py` | Poll an execution until terminal/timeout | `--execution-id ID` (required), `--interval SECS`, `--timeout SECS`, `--json` |
| `get_execution_results.py` | Fetch one execution's status and output | `--execution-id ID` (required), `--json` |
### trigger_workflow.py
```bash
../../scripts/python.sh scripts/trigger_workflow.py --id <def_id> --params '{"k":"v"}'
../../scripts/python.sh scripts/trigger_workflow.py --id <def_id> # Interactive prompts
../../scripts/python.sh scripts/trigger_workflow.py --id <def_id> --params '{}' --wait --timeout 120
```
Parameters come from `--params` (a JSON string) or interactive prompts derived from the workflow's parameter schema, with type coercion for integers, booleans, arrays, and objects. The execute endpoint returns the execution ID as a bare string or an object; the script handles both shapes.
### monitor_execution.py
```bash
../../scripts/python.sh scripts/monitor_execution.py --execution-id <exec_id>
../../scripts/python.sh scripts/monitor_execution.py --execution-id <exec_id> --interval 10 --timeout 600 --json
```
Defaults: `--interval 5`, `--timeout 300`. Prints status updates to stderr and the final result to stdout. Exits `0` only on a successful terminal state (`Succeeded` or `Completed`); non-zero on any other terminal state or timeout, so CI can react.
### get_execution_results.py
```bash
../../scripts/python.sh scripts/get_execution_results.py --execution-id <exec_id>
../../scripts/python.sh scripts/get_execution_results.py --execution-id <exec_id> --json
```
Single fetch. Reads `resources[0]` from the API envelope for the execution's `status` and `output`.
## Common Pitfalls
1. **Triggering an unreleased workflow.** A disabled definition will not execute. Confirm `Status: enabled` (step 1) before triggering, and release it via the deployment skill if needed.
2. **Missing required parameters.** When triggered via API (not the console UI), parameters are not prompted by the platform. Pass every required field in `--params`. Empty params are the most common failure cause.
3. **Assuming success without checking status.** A returned execution ID means the run started, not that it succeeded. Always confirm the terminal status with `monitor_execution.py` or `get_execution_results.py`.
4. **Too-short timeout.** Long workflows (loops over many devices, paginated API calls) can exceed the 120s/300s defaults. Raise `--timeout` and `--interval` for these — a timeout does not cancel the execution; it just stops polling.
5. **Malformed `--params` JSON.** The value must be valid JSON (double-quoted keys/strings). `{'k':'v'}` is not valid JSON and will raise a parse error before the workflow is triggered.
6. **Treating `ActionRequired` as still-running.** `ActionRequired` is terminal for polling — the workflow is paused for human input and will not advance on its own. Resolve the input request in the Fusion console; it will not clear from these scripts.
## Handoff
- **Came from deployment?** You have a definition ID and a released workflow — start at step 2 (trigger).
- **Execution failed?** If the YAML logic is wrong, return to the **authoring** skill to fix it, then re-import and re-release via **deployment** before triggering again.
Referenced files: 5
foundry-redirect2.84 KB
--- name: foundry-redirect description: > TRIGGER when the user asks to "build a Foundry app", "create a Foundry app", mentions manifest.yml, or needs a UI page/extension, serverless function, collection, or a custom API integration from a third-party API (Okta, ServiceNow, Jira, etc.) built. DO NOT TRIGGER for a standalone Fusion workflow that only wires together existing actions. This skill declines Foundry-app requests and points to the crowdstrike-falcon-foundry plugin, so the redirect works even without Claude Code hooks; it yields to the real Foundry plugin when that plugin is also installed. version: 1.2.0 updated: 2026-09-08 tags: [fusion, foundry, redirect, routing] author: CrowdStrike license: MIT compatibility: Claude Code >=1.0 metadata: category: routing --- # Falcon Foundry Redirect If this skill triggered, the request is a **Falcon Foundry app**, not a standalone Falcon Fusion workflow. It belongs to the sibling Falcon Foundry plugin — the `fusion-skills` plugin builds Fusion workflows only. Why this skill exists: the `workflows` orchestrator declines Foundry-app requests too, but its description matches *Fusion workflow* language, so a "build a Foundry app" prompt never loads it. On Claude Code a hook covers that gap; on Codex, Copilot CLI, Cursor, and the Agent SDK there are no hooks, so this skill — whose description matches Foundry-app language directly — is what makes the redirect reachable. ## What to do Do NOT author workflow YAML. Do NOT scaffold an app yourself. Respond with all three: 1. State plainly that this request needs a Falcon Foundry app, not a standalone Fusion workflow. 2. Name the plugin: **`crowdstrike-falcon-foundry`**. 3. How to install it: `/plugin install crowdstrike-falcon-foundry`, or clone https://github.com/CrowdStrike/foundry-skills. ## When both plugins are installed If `crowdstrike-falcon-foundry` is present, its own `development-workflow` skill matches Foundry-app requests directly and handles them — a stronger match than this one, so the agent picks it and this redirect never fires. That is correct: this skill is the safety net for when the Foundry plugin is absent, not a competitor with it when present. ## Foundry app vs. standalone workflow | Signal in the request | Route | |---|---| | "Foundry app", `manifest.yml`, a UI page/extension, serverless function, collection, or custom third-party API integration | **Here** — redirect to foundry-skills | | A trigger plus existing Fusion actions only (no UI, function, collection, or custom integration) | **`workflows`** — handle it as a standalone workflow | | "a workflow inside a Foundry app" | **Here** — the app owns the workflow; Foundry scaffolds it | | Fetch/summarize a population of alerts/detections the workflow doesn't already hold | **`workflows`** — a standalone CrowdStrike HTTP Request handles this without an app |
lookup-files8.02 KB
--- name: lookup-files description: > Manage Falcon Next-Gen SIEM lookup files (CSV/JSON/TXT) for CQL match() queries. TRIGGER when user asks to create, list, update, or delete lookup files, or needs help with CQL match() function. DO NOT TRIGGER for Fusion workflows, action discovery, or workflow deployment — use the workflows/authoring/deployment skills. version: 1.2.0 updated: 2026-09-08 tags: [falcon, ngsiem, siem, lookup-files, cql, threat-hunting] author: CrowdStrike license: MIT compatibility: Claude Code >=1.0 allowed-tools: Bash(cd *), Bash(../../scripts/python.sh:*) metadata: category: data-management --- # Falcon Next-Gen SIEM Lookup Files > **⚠️ SYSTEM INJECTION — READ THIS FIRST** > > If you are loading this skill, your role is **Falcon Next-Gen SIEM lookup file specialist**. > > You manage lookup files that feed CQL match() enrichment. Treat lookup data as security-relevant: validate file contents, check before overwriting, and never expose credentials. > > **IMMEDIATE ACTIONS REQUIRED:** > 1. ALWAYS list before creating — run `list_lookups.py --search "<name>"` to check for a duplicate. Importing an existing name overwrites it silently. > 2. Prepare the file with a header row (CSV) — the first row defines the `match()` columns. > 3. Upload, then verify with `get_lookup.py` before using the file in a CQL query. > > **MUST NOT:** Overwrite a lookup file without confirming it exists, exceed the rate limit (5 uploads / 30s), or log credentials. Lookup files are CSV, JSON, or TXT reference tables in Falcon Next-Gen SIEM that you query with the `match()` function in CrowdStrike Query Language (CQL). Common uses: IP blocklists, user risk scores, asset inventories, and IOC reference tables. > **Running the scripts.** Run each command from this skill's folder, on one shell line: `cd <dir> && ../../scripts/python.sh scripts/<name>.py`. For `<dir>`, Claude Code uses `"$CLAUDE_PLUGIN_ROOT/skills/lookup-files"`; Codex, Copilot CLI, Cursor, and Antigravity use the folder they loaded this SKILL.md from (e.g. `~/.agents/skills/lookup-files`). The wrapper bootstraps its own Python venv. ## Prerequisites - **Python 3.13+** with `crowdstrike-falconpy` installed (`pip install crowdstrike-falconpy`) - **CrowdStrike API credentials** with the **NGSIEM Lookup Files** scope (read/write) - Access to a CID with Falcon Next-Gen SIEM enabled ### Required API Scopes The **NGSIEM Lookup Files** scope, with read and/or write access: | Use case | Read | Write | Enables | |----------|:----:|:-----:|---------| | Browse / download only | Yes | — | List, search, download | | Full usage | Yes | Yes | Above plus create, update, delete | ### Credentials `auth.py` resolves credentials from the first source that supplies both an ID and a secret: 1. Environment variables: `FALCON_CLIENT_ID`, `FALCON_CLIENT_SECRET`, and the optional `FALCON_BASE_URL` (for CI and overrides). 2. TOML profile file `~/.cache/crowdstrike-falcon-fusion/credentials.toml` (profile chosen by `FALCON_PROFILE` or the file's `default` key). > Run `/crowdstrike-falcon-fusion:setup` to configure credentials interactively (writes the TOML profile). The scripts share auth via `common/scripts/auth.py`, which exposes `get_ngsiem_client()` for Next-Gen SIEM operations. Test credentials: ```bash ../../scripts/python.sh ../../common/scripts/auth.py ``` ## Core Workflow ### Step 1 — List (check for duplicates) ```bash ../../scripts/python.sh scripts/list_lookups.py --list ../../scripts/python.sh scripts/list_lookups.py --search "blocklist" ``` ### Step 2 — Prepare the file CSV needs a header row; the first column is typically the match key: ```csv ip,category,source,added_date 10.0.0.1,c2,threat-intel,2026-01-15 192.168.1.100,scanner,internal-scan,2026-02-01 ``` See `assets/example-ip-blocklist.csv` and `assets/example-user-risk.csv` for reference formats, and `references/lookup-file-formats.md` for the full format rules. ### Step 3 — Upload ```bash ../../scripts/python.sh scripts/create_lookup.py --file blocklist.csv --name "ip-blocklist.csv" ``` ### Step 4 — Verify ```bash ../../scripts/python.sh scripts/get_lookup.py --name "ip-blocklist.csv" ../../scripts/python.sh scripts/list_lookups.py --search "blocklist" --json ``` ### Step 5 — Use in CQL Reference the file with the `match()` function (run in Falcon Next-Gen SIEM): ``` match(file="ip-blocklist.csv", column=ip, field=src_ip, include=category) ``` `column=` is a header **in the lookup CSV** (`ip`); `field=` is the **event field** (`src_ip`). Do not swap them — putting the event field in `column=` matches a non-existent column and returns nothing. See `references/cql-match-function.md` for full syntax and examples. ### Step 6 — Update Replace the content while keeping the same filename: ```bash ../../scripts/python.sh scripts/update_lookup.py --name "ip-blocklist.csv" --file updated-blocklist.csv ``` > **Update is content-only — it does not carry labels.** The PATCH endpoint > (`/ngsiem-content/entities/lookupfiles/v1`, wrapped by both this script and the > Fusion "Update lookup file" workflow action) accepts only `search_domain`, > `filename`, and `file`. It has no `labels` parameter, so any labels a file had in > the Next-Gen SIEM UI are not preserved across an update — the UI manages labels > through a separate API that the REST update path doesn't touch. If a lookup file > needs labels, set them in the console after updating, or keep label-bearing files > out of automated update workflows. ### Step 7 — Delete ```bash ../../scripts/python.sh scripts/delete_lookup.py --name "ip-blocklist.csv" --confirm ``` ## Lookup Files from Workflows A Fusion pattern (the built-in "Introduction to Lookup file actions" playbook) automates lookup file creation from email: a Monitored Mailbox trigger extracts a CSV attachment, "Get lookup file metadata" checks existence, and "Create/Overwrite lookup file" writes it. To build that workflow, use the **workflows** orchestrator skill in this plugin — the lookup file actions surface via `action_search.py --search "lookup"` in the authoring skill. This skill handles the lookup files themselves, not the workflow that drives them. ## Script Reference | Script | Purpose | Key flags | |--------|---------|-----------| | `list_lookups.py` | List and search lookup files | `--list`, `--search`, `--domain`, `--json` | | `get_lookup.py` | Download a lookup file | `--name`, `--output`, `--domain` | | `create_lookup.py` | Upload a new lookup file | `--file`, `--name`, `--json` | | `update_lookup.py` | Replace lookup file content | `--name`, `--file`, `--json` | | `delete_lookup.py` | Delete a lookup file | `--name`, `--domain`, `--confirm`, `--json` | | `verify_lookup.py` | Verify a lookup resolves via CQL `match()` (upload, match a known row, delete) | `--file`, `--name`, `--column`, `--keep`, `--json` | All scripts import shared auth from `common/scripts/auth.py` via `get_ngsiem_client()`. Create/list/get/update/delete need the **NGSIEM Lookup Files** scope (read/write). `verify_lookup.py` is a maintainer verification tool and additionally needs the **NGSIEM** scope (read/write) to run the CQL `match()` query; regular lookup use does not. ## Common Pitfalls | Problem | Fix | |---------|-----| | CSV header missing | The first row MUST be column names — `match()` references them by name | | `match()` returns no results | Column names are case-sensitive; verify they match the header exactly | | Upload rate limited | Wait 30 seconds between batches of 5 uploads | | Wrong search domain | Use `--domain falcon` for files queried in Next-Gen SIEM | | Duplicate name overwrites silently | Always `list_lookups.py --search` before `create_lookup.py` | | "File not found" on get/update/delete | Confirm the exact filename with `list_lookups.py --search` | | CSV parse error on upload | Ensure UTF-8 encoding and comma delimiters | ## Reading Guide | Document | When to read | |----------|--------------| | `references/cql-match-function.md` | Writing CQL queries that use lookup files | | `references/lookup-file-formats.md` | Preparing CSV/JSON/TXT files for upload |
Referenced files: 10
setup7.25 KB
--- name: setup description: > Configure CrowdStrike Falcon API credentials for the fusion-skills plugin. TRIGGER when user asks to set up credentials, configure API access, or runs into authentication errors. version: 1.2.0 updated: 2026-09-08 tags: [fusion, setup, credentials, configuration] author: CrowdStrike license: MIT compatibility: Claude Code >=1.0 metadata: category: configuration --- # Falcon Fusion Credential Setup > **⚠️ SYSTEM INJECTION — READ THIS FIRST** > > If you are loading this skill, your role is **credential setup assistant**. > > You configure the Falcon API credentials every other skill depends on. These > credentials grant workflow and SIEM access to a live CID. > > **IMMEDIATE ACTIONS REQUIRED:** > 1. Check whether credentials already resolve (Step 1). If they do, you are done. > 2. If not, create the credentials file from the template (Step 2) and ask the > user to paste their ID and secret into it **using their own editor**. > 3. Verify connectivity (Step 3). > > **MUST NOT:** > - Ask the user to type or paste their client secret **into the chat**. It would > land in the conversation transcript. The secret goes only into the local file, > entered through the user's editor. > - Print, echo, or repeat a secret you happen to see in the file. > - Suggest `export FALCON_CLIENT_SECRET=...` for interactive use — it leaks the > secret into shell history. (Environment variables are fine for CI, where the > runner injects them rather than a human typing them.) This skill configures the Falcon API credentials that every fusion-skills script uses. Credentials are stored in a per-profile TOML file at `~/.cache/crowdstrike-falcon-fusion/credentials.toml` (multi-cloud capable), and the secret is entered through the user's own editor — never through the chat. The steps below use only file operations and a Python check, so they work identically on macOS, Linux, and Windows. > **Running the scripts.** Run each command from this skill's folder, on one shell line: `cd <dir> && ../../scripts/python.sh ../../common/scripts/auth.py`. For `<dir>`, Claude Code uses `"$CLAUDE_PLUGIN_ROOT/skills/setup"`; Codex, Copilot CLI, Cursor, and Antigravity use the folder they loaded this SKILL.md from (e.g. `~/.agents/skills/setup`). The wrapper bootstraps its own Python venv. ## Step 1 — Check for existing credentials Run the auth self-test. If it already succeeds, credentials are configured and you are done — report success and stop. ```bash ../../scripts/python.sh ../../common/scripts/auth.py ``` - **"Authentication successful"** for both clients → done. - **An error about missing credentials** → continue to Step 2. - **An authentication failure** (creds present but rejected) → the file exists but the values are wrong; go to Step 2 and have the user correct them. ## Step 2 — Create the credentials file and have the user fill it in Create `~/.cache/crowdstrike-falcon-fusion/credentials.toml` **only if it does not already exist** (never overwrite existing profiles). Write this template with the Write tool: ```toml # CrowdStrike Falcon API credentials for fusion-skills. # Fill in client_id and client_secret below, then save this file. # # Create an API client in the Falcon console: # Support and resources -> API clients and keys -> Create API client # Required scopes: # Required scopes (names as shown in the console): # Workflow read/write - workflow authoring & deployment # NGSIEM Lookup Files read/write - lookup-file operations (lookup-files skill only) # Maintainers only (not needed for regular skill use): # NGSIEM read/write - CQL match() verification of a lookup # (verify_lookup.py / verify-workflows.sh --lookup-dir) default = "us-2" [us-2] client_id = "" client_secret = "" base_url = "https://api.us-2.crowdstrike.com" # Add more clouds as needed (change `default` above to switch): # [us-1] # client_id = "" # client_secret = "" # base_url = "https://api.crowdstrike.com" # # [us-3] # client_id = "" # client_secret = "" # base_url = "https://api.us-3.crowdstrike.com" # # [eu-1] # client_id = "" # client_secret = "" # base_url = "https://api.eu-1.crowdstrike.com" # # [us-gov-1] # client_id = "" # client_secret = "" # base_url = "https://api.laggar.gcw.crowdstrike.com" ``` After creating the file, restrict its permissions (skip on Windows, where the user profile directory is already access-controlled): ```bash chmod 700 ~/.cache/crowdstrike-falcon-fusion chmod 600 ~/.cache/crowdstrike-falcon-fusion/credentials.toml ``` Then tell the user, in your own words: > I created your credentials file at > `~/.cache/crowdstrike-falcon-fusion/credentials.toml`. Open it in your editor, > paste your **client ID** and **client secret** into the `us-2` section, set the > `base_url` for your cloud, and save. Then tell me to verify — don't paste the > secret here. **Offer to open the file for them.** Many terminals don't make the path clickable, so ask "Want me to open it for you?" and, if yes, run the opener for their OS: ```bash # macOS open ~/.cache/crowdstrike-falcon-fusion/credentials.toml # Linux xdg-open ~/.cache/crowdstrike-falcon-fusion/credentials.toml # Windows explorer.exe %USERPROFILE%\.cache\crowdstrike-falcon-fusion\credentials.toml ``` Pick the command for the user's platform (check `uname` / the OS if unsure). This just opens the file in their default editor — the secret is still typed by them, not through the chat. Do **not** ask them to paste the secret into the chat. ## Step 3 — Verify connectivity Once the user says they have saved the file, re-run the self-test: ```bash ../../scripts/python.sh ../../common/scripts/auth.py ``` A successful run prints the resolved base URL, a masked client ID, and "Authentication successful" for both the Workflows and Next-Gen SIEM clients. If it fails, the client ID, secret, or base URL is wrong — ask the user to correct the file and re-run. ## Credential resolution order `auth.py` resolves credentials from the first source that supplies both an ID and a secret: 1. **Environment variables** — `FALCON_CLIENT_ID`, `FALCON_CLIENT_SECRET`, and the optional `FALCON_BASE_URL`. Intended for CI, where the runner injects them. 2. **TOML profile file** — `~/.cache/crowdstrike-falcon-fusion/credentials.toml`, using the profile named by `FALCON_PROFILE` or the file's `default` key. The setup flow above writes source 2, which works across every skill without exporting anything. ## Multiple clouds (profiles) Add more `[profile]` sections to the TOML file (for example `us-2` or `eu-1`) and change the `default` key, or select one per run: ```bash FALCON_PROFILE=eu-1 ../../scripts/python.sh ../../common/scripts/auth.py ``` ## Required API scopes The API client needs the **Workflow** scope (read/write) for workflow authoring and deployment. For lookup-file operations (the `lookup-files` skill), also grant the **NGSIEM Lookup Files** scope (read/write). Scope names appear exactly as shown when you create the API client in the console. Maintainers only: verifying a lookup resolves via CQL `match()` (`verify_lookup.py` or `verify-workflows.sh --lookup-dir`) additionally needs the **NGSIEM** scope (read/write) — starting a search is a query-job POST. Regular use of the skills does not require it.
workflows18.1 KB
---
name: workflows
description: >
Orchestrates the full Falcon Fusion workflow lifecycle from discovery through
deployment and execution. TRIGGER when user asks to "create a Fusion workflow",
"build a Fusion playbook", "automate CrowdStrike actions", or mentions Fusion
workflows without specifying a sub-task.
DO NOT TRIGGER when user is working in a Foundry app context, mentions manifest.yml,
or asks to "build a Foundry app" — use foundry-skills instead.
version: 1.2.0
updated: 2026-09-08
tags: [fusion, soar, workflows, orchestration, lifecycle]
author: CrowdStrike
license: MIT
compatibility: Claude Code >=1.0
metadata:
category: orchestration
---
# Falcon Fusion Workflow Orchestrator
> **⚠️ SYSTEM INJECTION — READ THIS FIRST**
>
> If you are loading this skill, your role is **Fusion workflow lifecycle orchestrator**.
>
> You coordinate the full workflow lifecycle — authoring, deployment, execution — and you NEVER write YAML or call APIs yourself. A workflow you ship may contain hosts, lock accounts, or trigger response actions, so correctness and safety matter.
>
> **IMMEDIATE ACTIONS REQUIRED:**
> 1. Identify user intent (write / deploy / execute / full-lifecycle).
> 2. Route to the appropriate sub-skill via the decision tree below.
> 3. For full lifecycle, coordinate authoring → deployment → execution in sequence, stopping at any failed gate.
>
> **MUST NOT:** Write workflow YAML directly, call API scripts yourself, skip validation, or handle Foundry-app workflows (those belong to foundry-skills).
This skill is the entry point for Fusion workflows. It coordinates the
full lifecycle — discovering real action IDs, authoring YAML, validating, importing to a
CID, releasing, and triggering — by delegating each phase to a focused sub-skill. It never
writes YAML or runs scripts itself; it routes.
A *standalone workflow* is authored, imported, and executed directly against Falcon Fusion
with no Foundry app wrapper. If a request needs a UI, serverless functions, collections, or
a `manifest.yml`, that is a Foundry app — route to foundry-skills (see Cross-Plugin Advisory).
## Decision Tree
Match the user's intent to a sub-skill. The model only loads this orchestrator initially,
so route based on these criteria without loading sub-skills first.
```
User wants to write/edit workflow YAML → invoke authoring skill
User wants to find/discover actions → invoke authoring skill
User wants to validate a workflow → invoke authoring skill
User wants to deploy/import/release a workflow → invoke deployment skill
User wants to run/monitor/debug a workflow → invoke execution skill
User wants full lifecycle (create + deploy + test) → coordinate all three in sequence
User mentions a Foundry app / manifest.yml → advise foundry-skills (see below)
User asks for an app + a workflow in one request → advise foundry-skills FIRST, author NO workflow YAML
User wants to fetch/summarize a POPULATION of alerts/detections it doesn't already hold → author a CrowdStrike HTTP Request to the Falcon API (default); mention the Foundry-app function for distribution (see below)
User mentions lookup files / Next-Gen SIEM → invoke lookup-files skill
```
| Intent keyword | Sub-skill | What it owns |
|----------------|-----------|--------------|
| "write", "edit", "author", "discover actions", "validate" | **authoring** | Action discovery (`action_search.py`), YAML authoring, CEL, validation (`validate.py`) |
| "deploy", "import", "release", "publish to CID" | **deployment** | Duplicate check, import, release, version management |
| "run", "execute", "trigger", "monitor", "tail", "debug" | **execution** | Triggering with payloads, monitoring, logs, results |
| "lookup file", "CSV/JSON lookup", "match() query" | **lookup-files** | Next-Gen SIEM lookup file management |
| "Foundry app", "manifest", "UI + workflow", "functions" | **foundry-skills** (sibling plugin) | App lifecycle, manifest coordination |
## Full Lifecycle Coordination
When the user wants an end-to-end workflow ("create, deploy, and test a workflow that…"),
coordinate the three sub-skills in sequence. Do not skip phases.
**Step 1 — Authoring** (invoke authoring skill)
1. Discover real action IDs with `action_search.py` (never guess or use placeholders).
2. Write the workflow YAML against the schema, with `version_constraint` on every action.
3. Validate with `validate.py` (structural) and, if credentials exist, API validation.
**Step 2 — Deployment** (invoke deployment skill)
1. Check for an existing workflow of the same name (`query_workflows.py`) — avoid silent duplicate versions.
2. Import the validated YAML to the CID (`import_workflows.py`).
3. Release the workflow so it becomes executable (`release_workflow.py`).
**Step 3 — Execution** (invoke execution skill)
1. Trigger the workflow with a test payload (`trigger_workflow.py`).
2. Monitor execution status (`monitor_execution.py`).
3. Verify results and surface any failures for debugging (`get_execution_results.py`).
Carry forward the artifacts between phases: authoring produces a validated YAML file,
deployment produces a `definition_id`, execution produces an `execution_id`. Each phase
depends on the previous one's output — do not start deployment before authoring validates,
and do not trigger before the workflow is released.
**Stop conditions between phases:**
- Authoring → Deployment: stop if validation fails or any action ID is unresolved. Fix the
YAML before importing. Never import a workflow that failed structural validation.
- Deployment → Execution: stop if the import errors or the release does not complete.
An unreleased workflow cannot be triggered.
- Execution: if a run fails, surface the error and route back to authoring (logic/field bug)
or to the console (missing credential config), not to a blind retry.
## Delegation Examples
These show how intent maps to routing. Use them as templates for your own dispatch.
**Full lifecycle:**
```
User: "Create a Fusion workflow that contains a host on critical detection, then test it"
→ authoring: action_search.py "contain", write YAML (Signal/EPP trigger), validate
→ deployment: query_workflows.py (dupe check), import, release
→ execution: trigger with a test device_id, monitor, verify result
```
**After authoring + validating, offer to deploy — don't print a command.** When the user asked to
build a workflow (not "just write the YAML"), and it validates, ASK "Deploy this to your CID now?"
and, on yes, run the deploy yourself via the `deployment` skill. Never tell the user to paste
`/crowdstrike-falcon-fusion:deployment` — invoke it for them. If the workflow contains a
credential-less HTTP Action, after a successful import tell the user it imported (disabled until
released) and give the console steps to attach the API key: open the Cloud HTTP Request action →
Authentication → Create new → API key → secret key → location Header → header name (e.g.
`x-apikey`) → Test → Save. See `references/http-actions.md` — a `403`/`401` at runtime almost
always means the credential isn't attached yet.
**Authoring only:**
```
User: "Write the YAML to enrich an IP with VirusTotal"
→ authoring: discover the HTTP Action + update-indicator action, author YAML, validate
→ STOP. Do not deploy unless the user asks.
```
**Redirect to Foundry:**
```
User: "Build a workflow with a custom dashboard UI to review containment approvals"
→ A dashboard UI is an app-only capability. Advise foundry-skills:
"A custom UI requires a Foundry app. Install crowdstrike-falcon-foundry to scaffold
the app, then this plugin can author the standalone workflow it wraps."
```
## Cross-Plugin Advisory
The boundary is **user intent about the deployment target**, not the presence of any file.
Decide whether to proceed standalone or advise the sibling foundry-skills plugin:
| Situation | Action |
|-----------|--------|
| Standalone workflow, no app wrapper | **Proceed** with fusion-skills (this plugin) |
| Only needs HTTP Actions + a credential config | **Proceed** — document the console-credential boundary (the `config_id` must already exist in the target CID) |
| Needs a UI page, extension, or dashboard | **Advise foundry-skills** — app-only capability |
| Needs serverless functions or collections (app-owned) | **Advise foundry-skills** — app-only capability |
| Needs a `manifest.yml` / "build a Foundry app" | **Advise foundry-skills** — app lifecycle |
| Wants custom actions from a third-party API (Okta, ServiceNow, Jira) | **Advise foundry-skills** — requires a Foundry app with an API integration to share operations with Fusion. See the `custom-soar-actions` use-case in foundry-skills: `claude plugin install crowdstrike-falcon-foundry` |
| Wants to fetch/summarize/list a **population** of Falcon alerts, detections, or incidents the workflow does NOT already hold (e.g. "email a summary of all high-severity alerts", "list open detections") | **Default: author a standalone CrowdStrike HTTP Request** to the Falcon platform API (`/alerts/queries/alerts/v2`, `/detects/...`) — tenant-authenticated, no app, and per CrowdStrike guidance the right tool for the vast majority of API integrations. Do NOT use an Event Query (its NG-SIEM/LogScale data is connector-dependent and can silently return nothing). **Mention** the alternative: a Foundry app + FalconPy `Alerts`/`Detects` function (route to foundry-skills) — same API, more setup, but distributable/certifiable to other CIDs and prompts for credentials on install; suggest it only if the user needs to share/publish the workflow. **Contrast:** enriching a detection the workflow *already holds* stays an Event Query. See `../authoring/references/event-query-vs-api.md`. |
| Dependency already exists in the CID (discoverable via `action_search.py`) | **Proceed** — author the workflow referencing its action ID |
| Dependency must be built and requires an app (function/collection/UI) | **Advise foundry-skills first — do NOT author workflow YAML yet.** The workflow depends on something that does not exist, so authoring it now ships a broken reference. Redirect, and only return to author once the user confirms the app dependency is built. |
| Compound request: a Foundry app (API integration / UI extension / functions) **and** a workflow in one ask | **Advise foundry-skills and STOP — produce no workflow YAML.** When the request is fundamentally app-shaped, redirect is the whole answer; do not author a partial workflow for the "workflow" clause. Mention foundry-skills explicitly and let the user come back for the standalone workflow after the app exists. |
When advising the sibling plugin, include the install command:
```bash
# Foundry app lifecycle (UI, functions, collections, manifest)
claude plugin install crowdstrike-falcon-foundry
```
If foundry-skills is not installed, advise installation but proceed with the available
tools. Detection is advisory, never blocking — both plugins must work independently.
**Console-credential boundary:** The authoring sub-skill can produce a workflow that uses an
HTTP Action, but the credential configuration it references (`config_id`/`definition_id`/
`config_name`) is created in the Falcon console and is CID-specific. Help the user discover
existing config IDs rather than inventing them — the same no-placeholder discipline that
applies to action IDs.
## Use-Case Pattern Matching
Before starting, glob `use-cases/*.md` (at the repo root) and scan the `description` field in
each file's frontmatter. If a use case matches the user's request, load it for reference context
(pattern steps, key actions, trigger configuration) before delegating to sub-skills.
**Gather reference context in parallel, and read it directly — do not spawn subagents for it.**
Reading a matched use-case file (or a couple of reference `.md` files) is a plain `Read`; batch
those reads into one message alongside the first-round discovery calls (`action_search.py`,
`trigger_search.py`) so the whole research phase resolves at once. Subagents add spin-up and
summarization overhead that outweighs any benefit for file reads — reserve them for genuinely
independent multi-step investigation, not for reading known files.
Available use cases:
| Use case | Scenario |
|----------|----------|
| `detection-enrichment` | Enrich a detection's indicators with VirusTotal, then comment/tag the case or blocklist |
| `event-queries` | Run a schemaless CQL/FQL query against the event store inside a workflow |
| `http-actions` | Call an external REST API inline with a Cloud HTTP Request (no Foundry app) |
| `api-pagination` | Page through a large REST API result set inside a workflow |
| `lookup-enrichment` | Enrich detections with third-party data via a Next-Gen SIEM lookup table |
| `custom-soar-actions` | Drive a shared Foundry API action (list/deactivate users) from a workflow |
| `export-query-results-csv` | Export Event Query results to CSV and write them to a lookup file |
| `human-in-the-loop-containment` | Gate device containment behind analyst approval on a high-severity detection |
| `detection-deduplication` | Find and close duplicate Next-Gen SIEM detections with an Event Query dedup |
| `case-management` | Query relevant events and attach them to a Next-Gen SIEM Case |
| `identity-detection-response` | Respond to an Identity Protection detection: get user context, then resolve or notify |
| `ngsiem-detection-response` | Respond to an NG-SIEM detection: hydrate it with an Event Query, extract fields, gate on a condition, summarize with an LLM, and email |
| `lookup-file-management` | Create/overwrite/append/update a lookup file from inside a workflow |
| `notifications` | Send a workflow notification to a chat channel (e.g. Slack) |
| `charlotte-agent-invocation` | Automatically invoke a published Charlotte AI (AgentWorks) agent when a detection fires |
A use case names the sub-skills it needs in its `skills:` frontmatter — use that to plan
which phases to coordinate.
## Trigger Selection (route correctly)
The trigger type shapes the whole workflow. Identify it from the user's intent so the
authoring sub-skill starts from the right shape (full detail in `references/trigger-types.md`):
| User intent | Trigger type |
|-------------|--------------|
| "run it manually", "pass in a device ID", "call from a button or API" | **On demand** |
| "when a detection fires", "on critical EPP detection", "on incident" | **Event (Signal)** |
| "every 6 hours", "nightly", "on a schedule" | **Scheduled** |
| "called by another workflow", "modular sub-playbook" | **Workflow execution** |
**The trigger type does NOT determine the data source.** Picking a Scheduled trigger
("runs every morning", "nightly") says *when* the workflow runs, not *how* it fetches
data. A scheduled workflow that "fetches all high-severity alerts / open detections /
alerts from the last 24h" is STILL fetching an alert **population** the workflow does not
hold — so it MUST use a CrowdStrike HTTP Request to `/alerts/queries/alerts/v2` (see the
population row above), NOT an Event Query, even though the schedule makes it feel like a
periodic query. A Scheduled trigger only pairs with an Event Query when the data genuinely
lives in NG-SIEM (e.g. "query the log repo for failed logins nightly").
**Severity is a numeric field (1–5), not a string.** When routing on detection severity, the
authoring sub-skill must use numeric CEL comparisons (`>= 4` for High/Critical), never
`== 'Critical'`. Flag this whenever a use case involves severity-based branching.
## Counter-Rationalizations
These thoughts mean STOP — you are about to skip a step the lifecycle requires:
| Thought | Reality |
|---------|---------|
| "I'll just write the YAML without searching actions" | STOP. Invoke the authoring skill. It runs `action_search.py` first. No exceptions. |
| "I can guess the action ID format" | WRONG. IDs are opaque identifiers, only discoverable via API. |
| "I'll use a placeholder for now" | NEVER. Resolve every ID before writing YAML. No `PLACEHOLDER_*` values. |
| "Validation can wait until deploy" | NO. Authoring validates; deployment validates again as a pre-flight. Both happen. |
| "This is basically a Foundry app" | CHECK. Does it need UI/functions/collections? If not, it's a standalone workflow. |
| "I'll deploy without releasing" | INCOMPLETE. Workflows must be released before they can execute. |
| "I can skip the duplicate check" | RISKY. Importing a duplicate name silently creates a new version. |
| "Release failed — I'll re-import as `<name>-v2`." | NEVER. The name is the workflow's identity, not a version. Renaming orphans the old definition and sprawls the CID. Fix the source YAML, keep the SAME name, re-import with `--replace`. |
| "I'll build the dependency myself" | PAUSE. If it needs a Foundry function/collection, route to foundry-skills. |
| "They want all high-severity alerts — I'll Event Query the alert population." | STOP. Don't Event Query a population you don't already hold (connector-dependent NG-SIEM data). DEFAULT to a CrowdStrike HTTP Request to the Falcon API (`/alerts/queries/alerts/v2`); mention the Foundry-app FalconPy function only if the workflow must be distributed. Enriching a detection the workflow ALREADY holds stays an Event Query. |
| "version_constraint is optional" | WRONG. Every action requires it. `~0` if no `semantic_version`, `~1` if it has one. |
| "I'll trigger before it's released" | NO. Trigger only after deployment releases the workflow. |
## Reading Guide
Reference docs live under `workflows/references/`. Point sub-skills and yourself here when
you need format details:
| Need | File |
|------|------|
| YAML field reference | `workflows/references/yaml-schema.md` |
| JSON internal schema | `workflows/references/json-structure.md` |
| CEL syntax | `workflows/references/cel-expressions.md` |
| Trigger types | `workflows/references/trigger-types.md` |
| Best practices | `workflows/references/best-practices.md` |
## Improving These Skills
If a skill gave incorrect guidance, was missing a pattern, or required extra trial-and-error,
the user can ask you to capture the fix at the end of the session: clone the fusion-skills
repo, create a branch, update the relevant `SKILL.md`, and open a PR. This turns a
one-session fix into a permanent improvement for all users.
Referenced files: 5
Package details
Publisher declarations from the archived package. These are separate from our research and the live service's terms.
- Package license
- MIT
- Package author
- CrowdStrike
- Keywords
- crowdstrike, falcon, fusion, soar, workflow, automation, security
Declared capabilities
- Interactive
- Write
Package observed Oct 2, 2026.
Technical details
- First seen
- Sep 30, 2026 · 22:02 UTC
- Last seen
- Oct 2, 2026 · 00:00 UTC
- Collection status
- Collected
plugins_6a8f7048ed7881918bf5b79011fe2b5e
Download plugin data (JSON)