<!-- Copyright (c) 2026 defconxt. All rights reserved. -->
<!-- Licensed under AGPL-3.0 — see LICENSE file for details. -->
---
name: building-security-workflow-orchestration
description: >-
  Build multi-stage security workflow orchestration with conditional branching,
  parallel execution, approval gates, SLA tracking, and cross-tool
  coordination for complex incident and compliance processes.
domain: cybersecurity
subdomain: security-operations-automation
tags:
  - workflow
  - orchestration
  - dag
  - approval-gates
  - sla
  - automation
version: "1.0"
author: defconxt
license: AGPL-3.0
metadata:
  mitre-attack: []
---

# Building Security Workflow Orchestration

## Overview

Complex security processes (incident escalation, compliance audits, access
reviews) require multi-stage workflows with conditional logic, parallel
task execution, approval gates, and SLA enforcement. This technique covers
building a DAG-based workflow engine that coordinates actions across security
tools with state persistence and audit trails.

Mode: `[MODE: ARCHITECT]` — Workflow design and orchestration.

## Prerequisites

| Requirement | Details |
|---|---|
| Python 3.10+ with `requests`, `pyyaml` | Required |
| State persistence backend (Redis, PostgreSQL, or filesystem) | Required |
| Notification service (Slack, PagerDuty, email) | Required |
| Cross-tool API credentials for orchestrated actions | Required |

## Key Concepts

### Workflow DAG Definition

```python
from dataclasses import dataclass, field
from typing import Any


@dataclass
class WorkflowStep:
    """A single step in a security workflow DAG."""

    step_id: str
    name: str
    action: str
    depends_on: list[str] = field(default_factory=list)
    condition: str | None = None
    timeout_minutes: int = 30
    on_failure: str = "halt"
    params: dict[str, Any] = field(default_factory=dict)


@dataclass
class Workflow:
    """DAG-based security workflow definition."""

    workflow_id: str
    name: str
    trigger: str
    steps: list[WorkflowStep] = field(default_factory=list)
    sla_minutes: int = 60
    owner: str = ""


INCIDENT_WORKFLOW = Workflow(
    workflow_id="wf-incident-response",
    name="Incident Response Workflow",
    trigger="siem_alert",
    sla_minutes=60,
    owner="soc-team",
    steps=[
        WorkflowStep("triage", "Alert Triage", "auto_triage"),
        WorkflowStep("enrich", "IOC Enrichment", "enrich_iocs", ["triage"]),
        WorkflowStep("severity", "Severity Assessment", "assess_severity", ["enrich"]),
        WorkflowStep(
            "contain", "Containment", "isolate_host",
            ["severity"], condition="severity >= high",
        ),
        WorkflowStep("approve", "Manager Approval", "request_approval", ["contain"]),
        WorkflowStep("remediate", "Remediation", "execute_remediation", ["approve"]),
        WorkflowStep("close", "Close Incident", "close_ticket", ["remediate"]),
    ],
)
```

### DAG Executor

```python
from datetime import datetime, timezone


def get_ready_steps(
    workflow: Workflow,
    completed: set[str],
) -> list[WorkflowStep]:
    """Find steps whose dependencies are all satisfied."""
    ready: list[WorkflowStep] = []
    for step in workflow.steps:
        if step.step_id in completed:
            continue
        if all(dep in completed for dep in step.depends_on):
            ready.append(step)
    return ready


def execute_workflow(workflow: Workflow, context: dict[str, Any]) -> dict[str, Any]:
    """Execute a workflow DAG with dependency resolution."""
    completed: set[str] = set()
    results: list[dict[str, Any]] = []
    start_time = datetime.now(timezone.utc)

    while len(completed) < len(workflow.steps):
        ready = get_ready_steps(workflow, completed)
        if not ready:
            break
        for step in ready:
            result = {
                "step_id": step.step_id,
                "name": step.name,
                "status": "completed",
                "timestamp": datetime.now(timezone.utc).isoformat(),
            }
            results.append(result)
            completed.add(step.step_id)

    elapsed = (datetime.now(timezone.utc) - start_time).total_seconds()
    return {
        "workflow_id": workflow.workflow_id,
        "steps_completed": len(completed),
        "total_steps": len(workflow.steps),
        "elapsed_seconds": elapsed,
        "sla_met": elapsed <= workflow.sla_minutes * 60,
        "results": results,
    }
```

## Workflow

### Step 1: List Workflows

```bash
node scripts/agent.js --action list-workflows
```

### Step 2: Execute Workflow

```bash
node scripts/agent.js --action execute --workflow wf-incident-response
```

### Step 3: Check SLA Status

```bash
node scripts/agent.js --action sla-status --workflow wf-incident-response
```

## Verification

- [ ] Workflow steps execute in dependency order
- [ ] Parallel-ready steps identified correctly
- [ ] Approval gates block execution until approved
- [ ] SLA tracking reports time compliance

## References

- [Apache Airflow DAG Concepts](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dags.html)
- [NIST SP 800-61r2 — Incident Response Lifecycle](https://csrc.nist.gov/publications/detail/sp/800-61/rev-2/final)
- [Tines Security Automation](https://www.tines.com/)
