Security Hub Without Automation Is Just a Dashboard
Security Hub aggregates findings from GuardDuty, Config, Inspector, Macie, and dozens of partner integrations into a single pane. For organizations managing multiple accounts and services, this aggregation is genuinely valuable. But Security Hub without automation is just a dashboard — a dashboard that generates thousands of findings that require human attention.
The power of Security Hub comes from treating it as a findings pipeline, not a reporting tool. Automation rules triage and enrich findings automatically. EventBridge routes findings to the right workflows. Lambda functions trigger remediation actions. The goal is a system where the common cases are handled automatically and humans only see the findings that require judgment.
Security Hub Automation Rules
Security Hub Automation Rules let you define criteria-based transformations that run automatically when findings match. They're evaluated before findings appear in your dashboard, so they can suppress noise, enrich context, and adjust severity before you ever see the finding.
Suppressing Known False Positives
Every environment has findings that are technically correct but not actionable in context. Suppress them with automation rules:
aws securityhub create-automation-rule --rule-name "SuppressDevAccountFindings" --rule-order 1 --criteria '{
"AwsAccountId": [{"Value": "DEV_ACCOUNT_ID", "Comparison": "EQUALS"}],
"ProductName": [{"Value": "Security Hub", "Comparison": "EQUALS"}],
"ComplianceStatus": [{"Value": "FAILED", "Comparison": "EQUALS"}]
}' --actions '[{
"Type": "FINDING_FIELDS_UPDATE",
"FindingFieldsUpdate": {
"Workflow": {"Status": "SUPPRESSED"},
"Note": {
"Text": "Dev account findings suppressed - not subject to production controls",
"UpdatedBy": "AutomationRule"
}
}
}]'
Severity Normalization
Different services report findings with different severity scales. Normalize them for your context:
# Downgrade Config findings for non-production resources tagged as 'dev'
aws securityhub create-automation-rule --rule-name "DowngradeDevResourceFindings" --criteria '{
"ResourceTags": [{"Key": "Environment", "Value": "dev", "Comparison": "EQUALS"}],
"SeverityLabel": [{"Value": "HIGH", "Comparison": "EQUALS"}]
}' --actions '[{
"Type": "FINDING_FIELDS_UPDATE",
"FindingFieldsUpdate": {
"Severity": {"Label": "MEDIUM"},
"Note": {"Text": "Severity downgraded: dev environment", "UpdatedBy": "AutomationRule"}
}
}]'
Automatic Owner Assignment
Finding findings without owners creates accountability gaps. Use automation rules to assign owners based on resource tags or finding type:
# Assign S3 findings to the data team
aws securityhub create-automation-rule --rule-name "AssignS3FindingsToDataTeam" --criteria '{
"ResourceType": [{"Value": "AwsS3Bucket", "Comparison": "EQUALS"}],
"WorkflowStatus": [{"Value": "NEW", "Comparison": "EQUALS"}]
}' --actions '[{
"Type": "FINDING_FIELDS_UPDATE",
"FindingFieldsUpdate": {
"UserDefinedFields": {"AssignedTeam": "data-engineering", "AssignedSlackChannel": "#data-security"}
}
}]'
EventBridge Integration: Routing Findings to Workflows
Security Hub publishes findings to EventBridge as Security Hub Findings - Imported events. EventBridge rules can route these to Lambda functions, SQS queues, SNS topics, or other targets for further processing.
High-Severity Finding Alert
resource "aws_cloudwatch_event_rule" "securityhub_high" {
name = "securityhub-high-severity"
event_pattern = jsonencode({
source = ["aws.securityhub"]
detail-type = ["Security Hub Findings - Imported"]
detail = {
findings = {
Severity = { Label = ["CRITICAL", "HIGH"] }
Workflow = { Status = ["NEW"] }
RecordState = ["ACTIVE"]
}
}
})
}
Finding Routing Lambda
import boto3
import json
import os
import urllib.request
def route_finding(event, context):
for finding in event['detail']['findings']:
severity = finding['Severity']['Label']
resource_type = finding.get('Resources', [{}])[0].get('Type', 'Unknown')
title = finding['Title']
account = finding['AwsAccountId']
region = finding['Region']
if severity in ['CRITICAL', 'HIGH']:
# Page via PagerDuty
trigger_pagerduty_alert(finding)
else:
# Post to Slack
slack_message = {
'text': f'*[{severity}]* {title}
Account: {account} | Region: {region} | Resource: {resource_type}'
}
req = urllib.request.Request(
os.environ['SLACK_WEBHOOK_URL'],
data=json.dumps(slack_message).encode('utf-8'),
headers={'Content-Type': 'application/json'},
method='POST'
)
urllib.request.urlopen(req)
Automated Remediation Workflows
For high-confidence, well-understood findings, automated remediation eliminates the human bottleneck. The key principle: automate remediations that are safe (low risk of breaking things), high-frequency (worth the investment), and deterministic (the right action is clear).
Auto-Remediate Public S3 Buckets
A Security Hub finding for a public S3 bucket triggers automatic remediation:
def remediate_public_s3(finding):
bucket_name = finding['Resources'][0]['Id'].split(':::')[1]
s3 = boto3.client('s3')
# Restore block public access
s3.put_public_access_block(
Bucket=bucket_name,
PublicAccessBlockConfiguration={
'BlockPublicAcls': True,
'IgnorePublicAcls': True,
'BlockPublicPolicy': True,
'RestrictPublicBuckets': True
}
)
# Update the finding workflow to RESOLVED
securityhub = boto3.client('securityhub')
securityhub.batch_update_findings(
FindingIdentifiers=[{
'Id': finding['Id'],
'ProductArn': finding['ProductArn']
}],
Workflow={'Status': 'RESOLVED'},
Note={
'Text': f'Auto-remediated: Block Public Access restored on {bucket_name}',
'UpdatedBy': 'AutoRemediation'
}
)
For the full S3 auto-remediation pattern, see our S3 public access detection guide and Config auto-remediation guide.
Auto-Remediate Open Security Groups
Security groups with 0.0.0.0/0 ingress on sensitive ports can be automatically remediated:
def remediate_open_security_group(finding):
sg_id = finding['Resources'][0]['Id'].split('/')[-1]
ec2 = boto3.client('ec2')
# Get the security group
sg = ec2.describe_security_groups(GroupIds=[sg_id])['SecurityGroups'][0]
# Remove any ingress rules with 0.0.0.0/0 on risky ports (22, 3389, 3306)
risky_ports = [22, 3389, 3306, 5432]
for rule in sg['IpPermissions']:
for ip_range in rule.get('IpRanges', []):
if ip_range['CidrIp'] == '0.0.0.0/0':
port = rule.get('FromPort', -1)
if port in risky_ports:
ec2.revoke_security_group_ingress(
GroupId=sg_id,
IpPermissions=[rule]
)
Finding Deduplication
Multiple services can report the same underlying issue. Security Hub normalizes findings into the ASFF (Amazon Security Finding Format), and the GeneratorId and Types fields identify duplicate findings. Set up deduplication logic to avoid notifying about the same issue multiple times:
def is_duplicate_finding(finding, seen_findings):
"""Simple deduplication by resource + finding type"""
key = f"{finding['Resources'][0]['Id']}:{finding['GeneratorId']}"
if key in seen_findings:
return True
seen_findings.add(key)
return False
Multi-Account Finding Aggregation
For organizations with multiple AWS accounts, Security Hub's finding aggregation feature collects findings from all accounts into a central aggregation region:
aws securityhub create-finding-aggregator --region-linking-mode ALL_REGIONS
Combined with an EventBridge rule in the aggregation region, you can process findings from all accounts in a single workflow. This is essential for the monitoring architecture described in our AWS Organizations guide. For MSPs managing customer accounts, see our MSP account management guide.
Tracking Remediation SLAs
Compliance frameworks often require evidence that findings are remediated within defined timeframes (e.g., critical findings within 24 hours, high findings within 7 days). Track this with a Lambda that runs daily:
def check_sla_violations(event, context):
securityhub = boto3.client('securityhub')
sns = boto3.client('sns')
sla_by_severity = {'CRITICAL': 1, 'HIGH': 7, 'MEDIUM': 30}
response = securityhub.get_findings(
Filters={
'WorkflowStatus': [{'Value': 'NEW', 'Comparison': 'EQUALS'}],
'RecordState': [{'Value': 'ACTIVE', 'Comparison': 'EQUALS'}]
}
)
violations = []
for finding in response['Findings']:
severity = finding['Severity']['Label']
created = datetime.fromisoformat(finding['CreatedAt'].replace('Z', '+00:00'))
age_days = (datetime.now(timezone.utc) - created).days
sla = sla_by_severity.get(severity, 30)
if age_days > sla:
violations.append({
'title': finding['Title'],
'severity': severity,
'age_days': age_days,
'sla_days': sla
})
if violations:
sns.publish(
TopicArn=os.environ['ALERT_TOPIC_ARN'],
Subject=f'Security Hub SLA Violations: {len(violations)} findings',
Message=json.dumps(violations, indent=2)
)
FAQ
How do I avoid automating remediations that break production?
Start automation in "notify only" mode: the Lambda identifies the right remediation action, logs it, and notifies a human — but doesn't execute. After reviewing the logged actions for 2-4 weeks and confirming they're correct, enable execution. Keep humans in the loop for any remediation that modifies production resources.
Can Security Hub automation rules replace manual finding review?
For well-understood, repeating finding types, yes. Use automation rules to suppress known false positives, normalize severity, and route findings to the right owners. This dramatically reduces manual review volume. You'll still want regular reviews of all active findings to catch novel issues that don't fit existing patterns.
How many automation rules should I have?
Start small: 5-10 rules for your highest-volume false positives and most common routing needs. Add rules incrementally as you identify patterns. More than 25-30 rules becomes difficult to maintain and reason about.
Protect your AWS accounts before it's too late
Vigilare monitors your AWS accounts for suspension risks — billing anomalies, IAM issues, GuardDuty findings, and more — and alerts you before AWS takes action.
Written by Vigilare Engineering
Platform Team