"Because bad logs are the blue pill."
Imagine a security camera system that records everything β except when you need the footage, you discover it was never turned on. That's what happens when AWS CloudTrail is misconfigured.
What breaks when CloudTrail is misconfigured:
-
You can't prove who did what. When a breach happens, investigators need immutable audit logs to trace the attacker's path. Without proper log file validation, those logs can be modified after the fact β making them useless as evidence.
-
You fail audits. SOC 2 and ISO 27001 require evidence of operational security. If your CloudTrail isn't multi-region, isn't encrypted at rest, or doesn't send logs to CloudWatch, you'll face finding after finding.
-
You have no alert when something goes wrong. A misconfigured trail is like a smoke detector with no batteries. You only discover the fire when it's already too late.
-
Your insurance claim gets denied. Cyber insurance policies increasingly require proof of logging and monitoring. Misconfigured CloudTrail = denied claims.
For non-technical stakeholders: TrailWarden answers the question "Are our cloud audit logs actually working?" β and provides the evidence to prove it.
I built TrailWarden after watching compliance teams spend 3-5 days manually clicking through AWS console, screenshotting configurations, and building spreadsheets that were outdated the moment they were created. I realized these teams were doing manually in days what code could do in seconds.
The final straw was watching an auditor ask for "evidence of CloudTrail log file integrity" β and the team scrambling to explain that AWS has this feature, but nobody had ever enabled it. They had no way to prove their logs hadn't been tampered with. The answer lived in a checkbox buried in the console, and nobody knew it was off.
So I built the tool I wanted to exist: one that scans all your CloudTrail trails across all regions, runs the 10 critical compliance checks, classifies risk, and produces audit-ready reports β with ready-to-run AWS CLI commands to fix whatever it finds.
This isn't a compliance tool that makes you fill out forms. It's a GRC engineering tool that treats compliance as code β version-controlled, repeatable, and automatically verified. Every run produces evidence. Every finding includes remediation. Every report is ready for an auditor.
TrailWarden demonstrates that I don't just understand compliance frameworks β I can operationalize them. I can take something that takes days of manual work and turn it into a single command that runs in seconds. That's what I'm building my next role around.
| Situation | Without TrailWarden | With TrailWarden |
|---|---|---|
| Audit preparation time | 3-5 days of manual console clicking | 30 seconds of code execution |
| Evidence collection | Screenshots, spreadsheets, hopes and prayers | Auto-generated JSON/CSV reports with timestamps |
| Risk visibility | Discover issues only during audits | Continuous visibility with HIGH/MEDIUM/LOW classification |
| Remediation speed | Research each issue, write custom scripts | Ready-to-run AWS CLI commands included with every finding |
| Human error risk | Manual review misses critical misconfigurations | Automated checks never miss a trail |
| Compliance coverage | Spot-check a few regions manually | Scans ALL regions automatically |
| Reporting format | Scattered screenshots and notes | Single source of truth: JSON, CSV, HTML, or XLSX |
flowchart TB
subgraph CLI["CLI Entry Point"]
A["python cloudtrail_compliance_validator.py"] --> B["--dry-run / --profile / --region"]
end
subgraph Auth["AWS Authentication"]
B --> C{Use Mock Data?}
C -->|Yes| D["Dry-Run Mode"]
C -->|No| E["boto3 Session"]
E --> F["sts:GetCallerIdentity"]
F --> G["Validate Credentials"]
end
subgraph Discovery["Region Discovery"]
G --> H["ec2:DescribeRegions"]
end
subgraph Fetch["Trail Fetcher"]
H --> I["cloudtrail:DescribeTrails"]
I --> J["For Each Trail..."]
end
subgraph Validation["Compliance Validation"]
J --> K["Run 10 Checks in Parallel"]
K --> L1["Multi-Region Coverage"]
K --> L2["Log File Validation"]
K --> L3["S3 Encryption"]
K --> L4["KMS Encryption"]
K --> L5["CloudWatch Logs"]
K --> L6["Trail Status"]
K --> L7["S3 Access Logging"]
K --> L8["S3 Public Access"]
K --> L9["MFA Delete"]
K --> L10["Management Events"]
L1 --> M{"HIGH Risk?"}
L2 --> M
L3 --> M
M -->|Yes| N["HIGH Risk Path"]
M -->|No| O{"MEDIUM Risk?"}
O -->|Yes| P["MEDIUM Risk Path"]
O -->|No| Q{"LOW Risk?"}
Q -->|Yes| R["LOW Risk Path"]
Q -->|No| S["COMPLIANT Path"]
style N fill:#fee2e2,stroke:#f87171,stroke-width:2px
style P fill:#fef3c7,stroke:#fbbf24,stroke-width:2px
style R fill:#f3e8ff,stroke:#a78bfa,stroke-width:2px
style S fill:#dcfce7,stroke:#4ade80,stroke-width:2px
end
subgraph Risk["Risk Classifier"]
M --> T["classify_trail_risk()"]
T --> U["HIGH / MEDIUM / LOW / COMPLIANT"]
end
subgraph Reports["Report Generator"]
U --> V["Generate JSON Report"]
U --> W["Generate CSV Report"]
U --> X["Generate XLSX Report"]
U --> Y["Generate HTML Report"]
end
subgraph Output["Console Summary"]
V --> Z["β Report: cloudtrail_compliance_report.json"]
W --> AA["β Report: cloudtrail_compliance_report.csv"]
X --> AB["β Report: cloudtrail_compliance_report.xlsx"]
Y --> AC["β Report: cloudtrail_compliance_report.html"]
end
D --> J
SOC 2 (Service Organization Control 2) is a widely recognized audit framework that demonstrates a company has appropriate controls for protecting customer data. NIST 800-53 is the US federal government's security control framework, widely adopted in regulated industries.
| Check Name | SOC 2 Control | NIST 800-53 Control | Audit Question Answered |
|---|---|---|---|
| Multi-Region Coverage | CC7.2 | AU-2 | "Can you prove activity from all geographic regions is being captured?" |
| Log File Integrity Validation | CC7.2 | AU-9(3), SI-7(5) | "Can you prove no one tampered with your log files?" |
| S3 Bucket Encryption | CC7.2 | AU-9 | "Are your log files encrypted at rest in storage?" |
| KMS Key Encryption | CC7.2 | AU-9 | "Do you use customer-managed encryption keys, not AWS defaults?" |
| CloudWatch Logs Integration | CC7.2 | AU-12 | "Do you have real-time alerting when something goes wrong?" |
| Trail Active Status | CC7.2 | AU-2 | "Can you prove logging was active on the day of the incident?" |
| S3 Server Access Logging | CC7.2 | AU-9 | "Can you see who accessed your log files?" |
| S3 Public Access Block | CC7.2 | AU-9 | "Could your log bucket accidentally become public?" |
| MFA Delete on S3 Bucket | CC7.2 | AU-9 | "Can someone delete your logs without multi-factor authentication?" |
| Management Events Scope | CC7.2 | AU-3 | "Are you capturing administrative actions, not just API calls?" |
TrailWarden uses a hierarchical risk classification system that ensures the most critical issues bubble to the top:
Plain English explanation: If any HIGH-risk check fails, the entire trail is classified as HIGH risk. If no HIGH risks exist but a MEDIUM risk fails, it's MEDIUM. If only LOW risks fail, it's LOW. If everything passes, it's COMPLIANT.
flowchart TD
A["Start: Evaluate All Checks"] --> B{"Any HIGH-risk checks<br/>with FAIL status?"}
B -->|Yes| C["Trail = HIGH Risk"]
B -->|No| D{"Any MEDIUM-risk checks<br/>with FAIL status?"}
D -->|Yes| E["Trail = MEDIUM Risk"]
D -->|No| F{"Any LOW-risk checks<br/>with FAIL status?"}
F -->|Yes| G["Trail = LOW Risk"]
F -->|No| H["Trail = COMPLIANT"]
C --> I["π¨ Urgent: Fix immediately<br/>Audit finding likely"]
E --> J["β οΈ Important: Plan remediation<br/>Document risk acceptance if needed"]
G --> K["π Low: Schedule improvement<br/>Not urgent but worth fixing"]
H --> L["β
Clean: All checks passing<br/>Evidence ready for auditors"]
style C fill:#fee2e2,stroke:#f87171,stroke-width:3px
style E fill:#fef3c7,stroke:#fbbf24,stroke-width:3px
style G fill:#f3e8ff,stroke:#a78bfa,stroke-width:3px
style H fill:#dcfce7,stroke:#4ade80,stroke-width:3px
{
"scan_metadata": {
"scan_date": "2026-03-23T16:09:30+00:00",
"aws_account_id": "123456789012",
"total_trails_scanned": 1,
"compliant_trails": 0,
"high_risk_trails": 0,
"medium_risk_trails": 1,
"low_risk_trails": 0
},
"trails": [
{
"trail_name": "legacy-migration-trail",
"trail_arn": "arn:aws:cloudtrail:us-west-2:123456789012:trail/legacy-migration-trail",
"home_region": "us-west-2",
"overall_risk": "MEDIUM",
"checks": [
{
"check_name": "Multi-Region Coverage",
"status": "FAIL",
"risk_level": "HIGH",
"compliance_mappings": ["SOC2_CC7.2", "NIST_AU-2"],
"details": "IsMultiRegionTrail: False, IncludeGlobalServiceEvents: False",
"remediation_cli": "aws cloudtrail update-trail --name legacy-migration-trail --is-multi-region-trail --include-global-service-events"
},
{
"check_name": "KMS Encryption",
"status": "FAIL",
"risk_level": "MEDIUM",
"compliance_mappings": ["NIST_AU-9"],
"details": "KMSKeyId: Not Set",
"remediation_cli": "aws cloudtrail update-trail --name legacy-migration-trail --kms-key-id <your-kms-key-arn>"
},
{
"check_name": "Log File Validation",
"status": "PASS",
"risk_level": "HIGH",
"compliance_mappings": ["NIST_AU-9(3)", "NIST_SI-7(5)"],
"details": "LogFileValidationEnabled: True",
"remediation_cli": ""
}
]
}
]
}legacy-migration-trail,arn:aws:cloudtrail:us-west-2:123456789012:trail/legacy-migration-trail,us-west-2,MEDIUM,Multi-Region Coverage,FAIL,HIGH,SOC2_CC7.2|NIST_AU-2,IsMultiRegionTrail: False, IncludeGlobalServiceEvents: False,aws cloudtrail update-trail --name legacy-migration-trail --is-multi-region-trail --include-global-service-events
- Python 3.9 or later β Check with
python --version - AWS CLI configured β Run
aws configureor use IAM role/SSO - IAM permissions β See the Security section below for the exact policy
# Clone the repository
git clone https://github.com/webber/trailwarden.git
cd trailwarden
# Install dependencies
pip install -r requirements.txt# Example 1: Basic scan using the default AWS profile and region
# No flags needed β uses your default credentials and scans all regions
python cloudtrail_compliance_validator.py
# Example 2: Scan a specific region with verbose output
# --region limits scan to one region; --verbose prints each check as it runs
python cloudtrail_compliance_validator.py --region us-east-1 --verbose
# Example 3: Use a specific AWS profile and output only JSON to a custom directory
# --profile uses a named AWS credential profile; --output-dir sets where reports go
python cloudtrail_compliance_validator.py --profile prod-admin --format json --output-dir ./audit-evidence
# Example 4: Dry-run mode (no AWS credentials required)
# Perfect for testing or demonstrations β uses detailed mock data
python cloudtrail_compliance_validator.py --dry-run --format both --verboseWhy read-only matters: Junior developers often forget that security tools need write permissions "just in case." But auditors trust tools that can't modify their environment. By using only read-only IAM permissions, TrailWarden signals to security teams that it's designed for assessment, not operation β the same mindset you'd want in a penetration tester or compliance auditor.
The principle: A compliance tool should never be able to change what it's measuring. If it could, the measurement becomes unreliable.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "TrailWardenReadOnly",
"Effect": "Allow",
"Action": [
"cloudtrail:DescribeTrails",
"cloudtrail:GetTrailStatus",
"cloudtrail:GetEventSelectors",
"cloudtrail:ListTrails",
"s3:GetBucketAcl",
"s3:GetBucketEncryption",
"s3:GetBucketLogging",
"s3:GetBucketPolicy",
"s3:GetBucketPublicAccessBlock",
"s3:GetBucketVersioning",
"s3:ListBucket",
"ec2:DescribeRegions",
"sts:GetCallerIdentity"
],
"Resource": "*"
}
]
}Note: This policy contains zero write permissions. It cannot:
- Modify any CloudTrail configuration
- Change any S3 bucket settings
- Enable or disable logging
- Create or delete resources
This is intentional. The tool is designed for assessment only. For production use, attach this policy to an IAM role with least-privilege access.
TrailWarden is designed to run in automated pipelines. Here's a complete GitHub Actions workflow that runs weekly and on-demand:
name: TrailWarden Compliance Scan
on:
schedule:
# Run every Monday at 6 AM UTC
- cron: '0 6 * * 1'
workflow_dispatch:
# Allow manual trigger
pull_request:
branches: [main]
permissions:
id-token: write # Required for OIDC
contents: read
jobs:
audit:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/TrailWardenAuditRole
aws-region: us-east-1
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install -r requirements.txt
- name: Run TrailWarden
run: |
python cloudtrail_compliance_validator.py \
--output-dir ./reports \
--format both \
--verbose
- name: Upload reports
uses: actions/upload-artifact@v4
if: always()
with:
name: compliance-reports
path: ./reports/*
retention-days: 90Why OIDC (OpenID Connect) matters: This workflow uses IAM role assumption instead of static AWS access keys. This is the mature approach because:
- No long-lived credentials stored in GitHub
- Credentials are automatically rotated by GitHub
- You can revoke access instantly by modifying the IAM role
- It's what security-conscious teams expect in 2026
| Feature | Status | Priority | Why It Matters |
|---|---|---|---|
| Multi-account org scanning (AWS Organizations) | π Planned | High | Enterprise customers need to audit hundreds of accounts |
| Slack/Teams alert integration for HIGH risk findings | π Planned | High | Real-time alerting instead of batch reports |
| Terraform module for automated remediation | π Planned | Medium | Enables infrastructure-as-code compliance |
| S3 Data Event logging validation | π Planned | Medium | Captures data-level access patterns |
| VPC Flow Logs correlation | π Planned | Medium | Network-level visibility complements API logging |
| HIPAA + PCI-DSS control mappings | π Planned | High | Expand beyond SOC 2/NIST to regulated industries |
| Web dashboard (React + FastAPI) | π Planned | Medium | Non-technical stakeholders need UI |
| Automated ticketing (Jira/ServiceNow integration) | π Planned | Low | Close the loop between finding and fixing |
Compliance Should Be Engineered, Not Administered
Traditional GRC (Governance, Risk, and Compliance) is stuck in the 2000s. It's manual, reactive, and spreadsheet-driven. Teams spend weeks preparing for audits that take minutes to complete β not because the work is complex, but because it's repetitive and disconnected from actual systems.
I don't think that's acceptable anymore.
GRC engineering is the practice of treating compliance as software:
- Automated: Code runs on schedules or triggers, not human memory
- Proactive: Issues are caught before audits, not during them
- Evidence-first: Every finding includes proof, not just assertions
- Version-controlled: Compliance state is tracked in git, not shared drives
TrailWarden is my proof-of-concept for this philosophy. It's not a product β it's a demonstration that compliance can be operationalized. That you can answer an auditor's question in seconds, not days. That you can show your CISO exactly where the risk is, right now, with evidence.
That's the work I want to do. I'm looking for a team that treats compliance as a technical problem to be solved, not a administrative burden to be tolerated. If that's the kind of engineering culture you're building, I want to be part of it.
- GitHub: github.com/webber
- LinkedIn: linkedin.com/in/webber
"There is no spoon." β The realization that changes everything about what's possible.
TrailWarden β because your audit evidence shouldn't depend on whether someone remembered to click a checkbox in the AWS console.