---
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.4.0
updated: 2026-10-30
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
> Your role here is **Fusion workflow deployment specialist**.
>
> You deploy workflow definitions into a CID safely: validate before importing, never create duplicates, and release only with the user's go-ahead.
>
> **Required steps:**
> 1. Import with `import_workflows.py`, which validates the YAML and checks for an existing workflow with the same name by default.
> 2. 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.
>
> **Don't:**
> - Import without validating first.
> - Skip the duplicate-name check (`--skip-duplicate-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 without the user's go-ahead. A workflow must be
> released before it can be executed at all, including a test run, and release
> makes it 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, or call FalconPy directly for any
> workflow operation (an inline `update_definition` or `delete_definition`,
> the raw update API). The only update path is: fix the source YAML, then
> re-import. Delete through `delete_workflow.py` (or the repo's
> `../../scripts/cleanup_workflows.py`).
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. A disabled definition can't be executed, not even on demand: the execute API rejects it with `workflow definition ... is disabled`. To test a workflow, release it first, ideally in a test CID.
> **Running the scripts.** Run each command from this skill's folder, on one shell line: `cd
&& ../../scripts/python.sh scripts/.py` (a sibling skill's script is `..//scripts/.py`). For ``, 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; FalconPy discovers US-1, US-2, and EU-1, so set it for any other cloud, such as US-3 or GovCloud)
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. `import_workflows.py` checks this itself before importing (step 3): it skips a file whose name already exists, prints `DUPLICATE` with the existing ID, and exits 1. So you don't need a separate check before importing. Use `query_workflows.py` only when you want to look a name up without importing:
```bash
# Exact-name check (exit 0 if it exists, 1 if not, 2 if the API call failed)
../../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
every existing definition with exactly that name, then re-imports; a name that
differs only in case is left alone and reported as a duplicate). **Do not rename the
workflow to ` 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. It's also required before the execution skill can trigger the workflow, so release before testing, with the user's go-ahead:
```bash
../../scripts/python.sh scripts/release_workflow.py --id
```
**If release reports validation errors, stop and fix the source YAML.** A workflow can import successfully yet fail validation at release, because the server validates more strictly than import. Fix the YAML, re-validate, and re-import with `--replace`; don't repair the deployed copy (see Don't above). Report the release error, including any `trace_id`. A common one, `exclusive gateway '' outgoing flow ... has no condition set and is not marked as default`, is a condition with neither a `cel_expression` nor an `else:` (see the authoring skill's parallel-branches example).
**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 `-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.)
It exports the old definition before deleting it. If the new import then fails
(a server-side error can follow a clean validation), it re-imports that export and
prints `RESTORED previous version (new ID: …)`; if the restore fails too, it prints
the path of the saved export.
Reaching for `python - < Workflows, or fetching Content Library records — the parts the API can't do from a script. |