AWS Security Hub Centralized Findings Management at Scale
Deploying AWS Security Hub across multi-account organizations with custom insights, automated remediation, and finding aggregation strategies

Introduction
AWS Security Hub aggregates security findings from 50+ AWS services and third-party tools into a single pane of glass. In organizations with 100+ accounts, Security Hub processes thousands of findings daily across Inspector, GuardDuty, Config, IAM Access Analyzer, Macie, and partner integrations. The challenge is not collecting findings but building effective workflows that turn findings into action.
This article covers the deployment architecture, finding prioritization frameworks, and automation patterns that make Security Hub operationally effective rather than just another noisy dashboard.
Security Hub Architecture
Finding Sources
| Source | Finding Types | Volume (per 100 accounts) | Default Enabled |
|---|---|---|---|
| AWS Config | Compliance violations | 2,000-5,000/week | Via standards |
| Inspector | Vulnerabilities (CVE) | 3,000-8,000/week | Manual |
| GuardDuty | Threat intelligence | 50-200/week | Manual |
| IAM Access Analyzer | Public/cross-account access | 100-500/week | Manual |
| Macie | Sensitive data exposure | 200-1,000/week | Manual |
| Firewall Manager | Security group violations | 500-2,000/week | Manual |
| Third-party (Prowler, etc.) | Best practice deviations | 1,000-5,000/week | Manual |
Multi-Account Deployment
Organization-Wide Enablement
# Designate delegated administrator
aws securityhub enable-organization-admin-account \
--admin-account-id 111111111111
# From admin account: configure auto-enable for new accounts
aws securityhub update-organization-configuration \
--auto-enable \
--auto-enable-standards
# Enable specific standards across organization
aws securityhub batch-enable-standards \
--standards-subscription-requests '[
{
"StandardsArn": "arn:aws:securityhub:::ruleset/cis-aws-foundations-benchmark/v/3.0.0"
},
{
"StandardsArn": "arn:aws:securityhub:us-east-1::standards/aws-foundational-security-best-practices/v/1.0.0"
}
]'
Cross-Region Aggregation
# Enable finding aggregation from all regions to us-east-1
aws securityhub create-finding-aggregator \
--region us-east-1 \
--region-linking-mode ALL_REGIONS
# Or specify regions explicitly
aws securityhub create-finding-aggregator \
--region us-east-1 \
--region-linking-mode SPECIFIED_REGIONS \
--regions '["eu-west-1", "ap-southeast-1", "us-west-2"]'
Terraform Configuration
# Admin account configuration
resource "aws_securityhub_account" "admin" {}
resource "aws_securityhub_organization_admin_account" "admin" {
admin_account_id = var.security_account_id
}
resource "aws_securityhub_organization_configuration" "org" {
auto_enable = true
auto_enable_standards = "DEFAULT"
depends_on = [aws_securityhub_organization_admin_account.admin]
}
resource "aws_securityhub_finding_aggregator" "all_regions" {
linking_mode = "ALL_REGIONS"
}
# Enable standards
resource "aws_securityhub_standards_subscription" "cis" {
standards_arn = "arn:aws:securityhub:::ruleset/cis-aws-foundations-benchmark/v/3.0.0"
}
resource "aws_securityhub_standards_subscription" "fsbp" {
standards_arn = "arn:aws:securityhub:${var.region}::standards/aws-foundational-security-best-practices/v/1.0.0"
}
Security Standards Comparison
| Standard | Controls | Focus Area | Recommended For |
|---|---|---|---|
| AWS FSBP | 300+ | AWS-specific best practices | All AWS accounts |
| CIS v3.0 | 60+ | Industry baseline | Compliance-driven orgs |
| PCI DSS | 130+ | Payment card security | E-commerce/fintech |
| NIST 800-53 | 200+ | Government/regulated | Federal/defense |
Control Failure Distribution (Typical Enterprise)
| Severity | FSBP Failures | CIS Failures | Combined |
|---|---|---|---|
| Critical | 15-25 | 5-10 | 20-35 |
| High | 80-150 | 30-50 | 110-200 |
| Medium | 200-400 | 50-100 | 250-500 |
| Low | 100-200 | 20-40 | 120-240 |
| Total | 400-775 | 105-200 | 500-975 |
Custom Insights for Prioritization
Custom insights aggregate findings into actionable views:
# Top accounts with critical findings
aws securityhub create-insight \
--name "Critical Findings by Account" \
--filters '{
"SeverityLabel": [{"Value": "CRITICAL", "Comparison": "EQUALS"}],
"WorkflowStatus": [{"Value": "NEW", "Comparison": "EQUALS"}],
"RecordState": [{"Value": "ACTIVE", "Comparison": "EQUALS"}]
}' \
--group-by-attribute "AwsAccountId"
# Unresolved findings older than 30 days
aws securityhub create-insight \
--name "Aging Critical Findings" \
--filters '{
"SeverityLabel": [{"Value": "CRITICAL", "Comparison": "EQUALS"}, {"Value": "HIGH", "Comparison": "EQUALS"}],
"WorkflowStatus": [{"Value": "NEW", "Comparison": "EQUALS"}],
"CreatedAt": [{"DateRange": {"Value": 30, "Unit": "DAYS"}}]
}' \
--group-by-attribute "ResourceType"
# Public-facing resource findings
aws securityhub create-insight \
--name "Public Resource Exposure" \
--filters '{
"Type": [{"Value": "Software and Configuration Checks", "Comparison": "PREFIX"}],
"Title": [
{"Value": "public", "Comparison": "PREFIX"},
{"Value": "unrestricted", "Comparison": "PREFIX"}
],
"RecordState": [{"Value": "ACTIVE", "Comparison": "EQUALS"}]
}' \
--group-by-attribute "AwsAccountId"
Automated Remediation
EventBridge Rule for Critical Findings
{
"source": ["aws.securityhub"],
"detail-type": ["Security Hub Findings - Imported"],
"detail": {
"findings": {
"Severity": {
"Label": ["CRITICAL"]
},
"Workflow": {
"Status": ["NEW"]
},
"Compliance": {
"Status": ["FAILED"]
},
"ProductFields": {
"StandardsControlArn": [{
"prefix": "arn:aws:securityhub"
}]
}
}
}
}
Auto-Remediation Lambda
import boto3
import json
# Map control IDs to remediation functions
REMEDIATION_MAP = {
'S3.2': remediate_s3_public_read,
'S3.3': remediate_s3_public_write,
'EC2.2': remediate_default_sg,
'EC2.19': remediate_sg_unrestricted,
'IAM.3': remediate_iam_key_rotation,
'RDS.2': remediate_rds_public,
}
def handler(event, context):
finding = event['detail']['findings'][0]
control_id = extract_control_id(finding)
if control_id in REMEDIATION_MAP:
remediation_fn = REMEDIATION_MAP[control_id]
result = remediation_fn(finding)
# Update finding workflow status
securityhub = boto3.client('securityhub')
securityhub.batch_update_findings(
FindingIdentifiers=[{
'Id': finding['Id'],
'ProductArn': finding['ProductArn']
}],
Workflow={'Status': 'RESOLVED'},
Note={
'Text': f'Auto-remediated: {result}',
'UpdatedBy': 'security-automation'
}
)
else:
# Create ticket for manual remediation
create_jira_ticket(finding)
def remediate_s3_public_read(finding):
"""Block public access on S3 bucket."""
s3 = boto3.client('s3')
bucket_name = finding['Resources'][0]['Id'].split(':')[-1]
s3.put_public_access_block(
Bucket=bucket_name,
PublicAccessBlockConfiguration={
'BlockPublicAcls': True,
'IgnorePublicAcls': True,
'BlockPublicPolicy': True,
'RestrictPublicBuckets': True
}
)
return f"Blocked public access on {bucket_name}"
def remediate_default_sg(finding):
"""Remove all rules from default security group."""
ec2 = boto3.client('ec2')
sg_id = finding['Resources'][0]['Id'].split('/')[-1]
# Revoke all ingress rules
sg = ec2.describe_security_groups(GroupIds=[sg_id])['SecurityGroups'][0]
if sg['IpPermissions']:
ec2.revoke_security_group_ingress(
GroupId=sg_id,
IpPermissions=sg['IpPermissions']
)
return f"Cleared rules from default SG {sg_id}"
Finding Suppression Strategy
Suppress findings that represent accepted risks to reduce noise:
# Suppress findings for approved exceptions
aws securityhub batch-update-findings \
--finding-identifiers '[
{"Id": "arn:aws:securityhub:us-east-1:123456789012:finding/abc-123", "ProductArn": "arn:aws:securityhub:us-east-1::product/aws/securityhub"}
]' \
--workflow '{"Status": "SUPPRESSED"}' \
--note '{"Text": "Accepted risk: Legacy system migration planned Q3 2026", "UpdatedBy": "security-team"}'
Metrics and KPIs
| KPI | Target | Measurement | Frequency |
|---|---|---|---|
| Critical MTTR | < 24 hours | Finding created to resolved | Daily |
| High MTTR | < 7 days | Finding created to resolved | Weekly |
| Security score | > 85% | Security Hub score | Weekly |
| Auto-remediation rate | > 40% | Auto-resolved / total | Monthly |
| Finding aging (critical) | 0 findings > 72h | Open critical findings | Daily |
| Suppression ratio | < 15% | Suppressed / total active | Monthly |
Key Takeaways
- Enable Security Hub organization-wide with finding aggregation to create a single security view across all accounts and regions.
- Start with AWS FSBP standard as it provides the most comprehensive coverage of AWS-specific security best practices with 300+ controls.
- Build custom insights for actionable prioritization focusing on critical findings by account, aging findings, and public resource exposure.
- Automate remediation for common findings (S3 public access, default security groups, unrestricted SSH) to reduce MTTR from days to minutes.
- Suppress accepted risks systematically with documented justification to reduce alert fatigue while maintaining audit trails.
- Target 85%+ security score as a KPI and track critical finding MTTR under 24 hours for mature security operations.
- Integrate with ticketing systems for findings that require human judgment, ensuring nothing falls through the cracks between automated and manual remediation.
Recommended reading

Per-Team Cost Allocation in Shared Kubernetes Clusters: From Chaos to Clarity
Implementing accurate per-namespace cost allocation in multi-tenant Kubernetes clusters, covering request vs. usage attribution, shared resource amortization, and building showback dashboards that drive accountability.

Measuring and Eliminating Toil: From 40% to 12% of Engineering Time
A systematic approach to identifying, measuring, and automating toil—the repetitive operational work that scales linearly with service growth and prevents engineers from doing creative work.

Serverless Postgres in Production: Branching, Scale-to-Zero, and the End of Database Provisioning
Running Neon serverless Postgres in production for 8 months — covering database branching workflows, scale-to-zero economics, connection pooling, and migration from RDS.

Comments
No comments yet. Be the first to share your thoughts.