Creating Playbooks
Guide to writing custom playbooks for your security workflows.
Getting Started
1. Create Playbook File
mkdir -p playbooks
touch playbooks/my_playbook.yaml
2. Define Basic Structure
name: my_playbook
description: Description of what this playbook does
version: "1.0"
triggers:
incident_type: phishing
auto_run: true
steps:
- name: First Step
action: parse_email
output: result
3. Register Playbook
tw-cli playbook add playbooks/my_playbook.yaml
Step Types
Action Step
Execute a registered action:
- name: Parse Email Content
action: parse_email
parameters:
raw_email: "{{ incident.raw_data.raw_email }}"
output: parsed
on_error: continue # or "fail" (default)
Condition Step
Branch based on conditions:
- name: Check if High Risk
condition: "{{ sender_rep.score < 0.3 }}"
then:
- action: quarantine_email
parameters:
message_id: "{{ incident.raw_data.message_id }}"
else:
- action: log_event
parameters:
message: "Low risk, no action needed"
AI Analysis Step
Get AI verdict:
- name: AI Analysis
type: ai_analysis
model: claude-sonnet-4-20250514
context:
- parsed
- auth_results
- reputation
prompt: |
Analyze this email for phishing indicators.
Consider the authentication results and sender reputation.
output: ai_verdict
Notification Step
Send alerts:
- name: Alert Team
action: notify_channel
parameters:
channel: slack
message: |
New {{ incident.severity }} incident detected
ID: {{ incident.id }}
Type: {{ incident.incident_type }}
Error Handling
Per-Step Error Handling
- name: Check Reputation
action: lookup_sender_reputation
parameters:
sender: "{{ parsed.sender }}"
output: reputation
on_error: continue # Don't fail playbook if this fails
default_output: # Use this if step fails
score: 0.5
risk_level: "unknown"
Global Error Handler
on_error:
- action: notify_channel
parameters:
channel: slack
message: "Playbook {{ playbook.name }} failed: {{ error.message }}"
- action: escalate
parameters:
level: analyst
reason: "Automated triage failed"
Variables and Templates
Define Variables
variables:
high_risk_threshold: 0.3
quarantine_enabled: true
notification_channel: "#security-alerts"
Use Variables
- name: Check Risk
condition: "{{ sender_rep.score < variables.high_risk_threshold }}"
then:
- action: quarantine_email
condition: "{{ variables.quarantine_enabled }}"
Template Functions
parameters:
# String manipulation
domain: "{{ parsed.sender | split('@') | last }}"
# Conditionals
priority: "{{ 'critical' if incident.severity == 'critical' else 'high' }}"
# Lists
all_urls: "{{ parsed.urls | join(', ') }}"
url_count: "{{ parsed.urls | length }}"
# Defaults
assignee: "{{ incident.assignee | default('unassigned') }}"
Testing Playbooks
Dry Run
tw-cli playbook test my_playbook \
--incident INC-2024-001 \
--dry-run
With Mock Data
tw-cli playbook test my_playbook \
--data '{"raw_email": "From: [email protected]..."}'
Validate Syntax
tw-cli playbook validate playbooks/my_playbook.yaml
Best Practices
1. Use Descriptive Names
# Good
- name: Check sender domain reputation
# Bad
- name: step1
2. Handle Failures Gracefully
- name: External Lookup
action: lookup_sender_reputation
on_error: continue
default_output:
score: 0.5
3. Add Timeouts
- name: Slow External API
action: custom_lookup
timeout: 30s
4. Log Key Decisions
- name: Log Verdict
action: log_event
parameters:
level: info
message: "Verdict: {{ verdict.classification }} ({{ verdict.confidence }})"
5. Version Your Playbooks
name: phishing_triage
version: "2.1.0"
changelog:
- "2.1.0: Added attachment analysis"
- "2.0.0: Restructured for parallel lookups"
Example: Complete Playbook
name: comprehensive_phishing_triage
description: Full phishing email analysis with all checks
version: "2.0"
triggers:
incident_type: phishing
auto_run: true
variables:
quarantine_threshold: 0.3
block_threshold: 0.2
steps:
# Parse email
- name: Parse Email
action: parse_email
parameters:
raw_email: "{{ incident.raw_data.raw_email }}"
output: parsed
# Parallel enrichment
- name: Enrich Data
parallel:
- action: check_email_authentication
parameters:
headers: "{{ parsed.headers }}"
output: auth
- action: lookup_sender_reputation
parameters:
sender: "{{ parsed.sender }}"
output: sender_rep
- action: lookup_urls
parameters:
urls: "{{ parsed.urls }}"
output: urls
condition: "{{ parsed.urls | length > 0 }}"
- action: lookup_attachments
parameters:
attachments: "{{ parsed.attachments }}"
output: attachments
condition: "{{ parsed.attachments | length > 0 }}"
# AI Analysis
- name: AI Verdict
type: ai_analysis
model: claude-sonnet-4-20250514
context: [parsed, auth, sender_rep, urls, attachments]
output: verdict
# Response actions
- name: Quarantine Malicious
action: quarantine_email
parameters:
message_id: "{{ incident.raw_data.message_id }}"
condition: >
verdict.classification == 'malicious' and
verdict.confidence >= variables.quarantine_threshold
- name: Block Repeat Offender
action: block_sender
parameters:
sender: "{{ parsed.sender }}"
condition: >
sender_rep.score < variables.block_threshold
- name: Create Ticket
action: create_ticket
parameters:
title: "{{ verdict.classification | title }}: {{ parsed.subject | truncate(50) }}"
priority: "{{ incident.severity }}"
condition: "{{ verdict.classification != 'benign' }}"
on_error:
- action: escalate
parameters:
level: analyst
reason: "Playbook execution failed"