Home Resume
Homeβ€Ί Blogβ€Ί Week 11 - Security Hub + GuardDuty: Findings That …
AWS Weekly Lab AWS Terraform

Security Hub + GuardDuty: Findings That Fix Themselves

A security finding is worthless if it sits in a console nobody watches. This week wires Security Hub and GuardDuty to EventBridge and Lambda so the clear-cut, dangerous misconfigurations β€” a management port open to the whole internet, a public S3 bucket β€” get fixed automatically in seconds, while active-threat findings that need human judgement get escalated to a person instead of being touched. The whole thing is tag-gated so it can never surprise a resource you meant to leave alone.

AWS Platform Engineering Lab Β· Week 11

Why β€” The Problem This Solves

The dangerous cloud misconfigurations are also the most common and the most mechanical: a security group left open to 0.0.0.0/0 on SSH or RDP, an S3 bucket with public access enabled, encryption switched off. Tools like GuardDuty and Security Hub are very good at finding these. The gap is what happens next β€” the finding lands in a console, and then it sits there.

Mean-time-to-remediate for these boring-but-critical issues is measured in hours or days, which is exactly the window an attacker needs. And teams don't already auto-remediate because it's frightening: one bad rule can revoke a legitimate security group or break production, so the finding gets a "we'll review it" that never comes.

This week closes the loop. For a defined set of clear-cut findings the fix happens automatically in seconds and the finding is marked resolved β€” while the judgement calls still reach a human. The scary part (blast radius) is handled by a single, boring guardrail: nothing is ever mutated unless it carries an explicit opt-in tag.

What You Need to Know β€” Skills & Tools

AWS Security Hub (CSPM) Amazon GuardDuty Amazon EventBridge AWS Lambda (Python 3.13) Amazon SNS Amazon SQS (DLQ) Amazon CloudWatch AWS Config Terraform (HCP) ASFF

The one decision that shaped everything: classic CSPM, not the new unified Security Hub

At re:Invent in December 2025 AWS released a re-launched, unified "AWS Security Hub" (v2 APIs) that correlates signals from GuardDuty, Inspector, Macie and the classic Security Hub into one detection-and-response view. It's a genuinely nice evolution β€” but as of mid-2026 the Terraform AWS provider does not yet stably support it (the aws_securityhub_account_v2 resource is still an open feature request).

That's fine, because the thing we're building β€” findings β†’ automation rules β†’ EventBridge β†’ Lambda β†’ writeback β€” is entirely Security Hub CSPM (the classic service, now named that), which the provider fully supports. v2 is a correlation and analytics layer sitting above the finding stream; it doesn't change how you react to findings. So this build uses classic CSPM in Terraform, and you can enable v2 alongside it in the console for the richer dashboards.

Prerequisite: AWS Config must be recording

The Security Hub controls this stack remediates (EC2.13/14/19, S3.2/3/8) are backed by AWS Config. Without a configuration recorder actively recording security groups and S3 buckets, those controls report NO_DATA, no findings are generated, and nothing is remediated β€” the pipeline deploys cleanly but does nothing. This stack does not provision Config (an account typically has one centrally-managed recorder; adding a second conflicts). Confirm one is recording before you deploy: aws configservice describe-configuration-recorder-status should return recording=True, lastStatus=SUCCESS.

Why Lambda β€” and not Step Functions or ECS

Each remediation is one to a few API calls, runs in seconds, is stateless, event-triggered, and has near-zero concurrency: revoke a security-group rule, or set Block Public Access on a bucket. That is precisely the shape Lambda exists for. Step Functions would earn its place only if a remediation needed multi-step orchestration, waits, or a human-approval timeout; ECS/Fargate only if a remediation were long-running or had heavy dependencies. Neither applies here, so bringing either in would be weight without benefit.

Two more choices worth stating plainly. EventBridge, not a direct Security Hub β†’ Lambda call, because Security Hub has no native Lambda target β€” EventBridge is the integration point, and it also gives content-based filtering (each Lambda only ever receives the finding class it fixes) and parallel fan-out to SNS. And both GuardDuty and Security Hub, not one, because they answer different questions: CSPM asks "is this bucket configured to be public?" while GuardDuty asks "is someone exfiltrating from it right now?" GuardDuty's findings flow into Security Hub too, so it stays one stream to react to.

Architecture β€” How It Fits Together

GuardDuty and Security Hub CSPM produce findings; a Security Hub automation rule enriches them before anything is routed; EventBridge matches specific finding types and fans them out; three Lambdas do the work β€” two fix, one notifies; and every automated action is written back to the finding so Security Hub reflects reality. Failures land in a dead-letter queue that alarms.

Security Hub + GuardDuty Auto-RemediationAmazon GuardDutyactive-threat detectionSecurity Hub CSPMFSBP standard + aggregationfindings ingestedautomation rule: prod β†’ CRITICAL (before routing)Amazon EventBridgecontent-based routingFindings Imported (NEW)GuardDuty Findingsg_remediatorrevoke world-open ingresss3_remediatorapply Block Public Accessthreat_notifierescalate β€” no mutationEC2.13/14/19S3.2/3/8aws.guarddutyRevoke 0.0.0.0/0 β†’ :22 / :3389S3 Block Public Access onSNS notify for human triagetag-gated: auto-remediate=truetag-gated: auto-remediate=truewriteback: BatchUpdateFindings β†’ RESOLVEDAmazon SNSemail alertsSQS DLQ14-dayCW alarmDLQ depthon failurealarm β†’ SNSnotify email β†’ katta… (subscribed)
1

Detect

GuardDuty analyses CloudTrail, VPC Flow Logs and DNS for active threats (Extended Threat Detection is auto-enabled at no cost). Security Hub CSPM runs the AWS Foundational Security Best Practices standard and ingests GuardDuty's findings β€” all normalized to one schema, the AWS Security Finding Format (ASFF).

2

Enrich (before routing)

A Security Hub automation rule runs inside Security Hub, before any EventBridge rule fires. It escalates any failed control on a resource tagged environment=production to CRITICAL β€” so downstream paging is smarter without touching the Lambdas.

3

Route

EventBridge rules match on detail.findings.Compliance.SecurityControlId (EC2.13/14/19 β†’ the SG remediator, S3.2/3/8 β†’ the S3 remediator) and on the native aws.guardduty event (β†’ the threat notifier). Crucially, they match only Workflow.Status = NEW β€” more on why that word matters in Challenges.

4

Act β€” but only on opt-in

The SG remediator revokes just the world-open ingress on high-risk ports; the S3 remediator applies all four Block Public Access settings. Both act only if the resource carries auto-remediate=true β€” enforced in code and in IAM. Anything untagged is left untouched and a human is notified instead. The threat notifier never mutates: active threats are judgement calls.

5

Write back & be safe

Every action is written back via BatchUpdateFindings (workflow status β†’ RESOLVED or NOTIFIED) so the console reflects reality, and results go to SNS. Any failed asynchronous invocation lands in a 14-day SQS dead-letter queue that raises a CloudWatch alarm β€” a dropped security action is worse than a slow one.

How We Built It β€” Step by Step

The stack is 30 Terraform resources across three modules (securityhub, guardduty, remediation), deployed through HCP Terraform. The walkthrough below follows the order things actually come up and get tested.

Step 1 β€” Enable Security Hub CSPM and the FSBP standard

Enable the account, subscribe the AWS Foundational Security Best Practices standard (the source of the config findings we remediate), add a cross-region finding aggregator, and set control_finding_generator = "SECURITY_CONTROL" so findings carry a populated Compliance.SecurityControlId β€” the exact field the EventBridge rules key on.

Security Hub CSPM Security standards page showing AWS Foundational Security Best Practices v1.0.0 enabled with an 80 percent security score
FSBP enabled and scoring β€” the source of the config findings the pipeline remediates.
Security Hub CSPM summary showing an 80 percent security score, 292 passed and 72 failed FSBP controls, and assets with the most findings
The summary view: 292 passed / 72 failed. Those failed controls are what the automation reacts to.

Step 2 β€” Enable GuardDuty

A single detector turns on foundational threat detection (CloudTrail, VPC Flow Logs, DNS) with no extra data-source cost; Extended Threat Detection comes along for free. Findings publish to EventBridge every 15 minutes.

GuardDuty settings page showing the detector enabled with a detector ID and findings exported to EventBridge every 15 minutes
GuardDuty enabled β€” foundational sources only, Extended Threat Detection on, 15-minute export cadence.

Step 3 β€” Deploy the remediation backbone

The remediation module uses a for_each over a map of three Lambda definitions, so each function gets its own least-privilege role, log group, EventBridge rule and invoke permission from one block β€” plus a shared SNS topic, a shared SQS dead-letter queue, and a CloudWatch alarm on DLQ depth.

AWS Lambda functions list showing the three week11 remediation functions with their descriptions
The three actors: two tag-gated remediators and one notify-only threat handler.
Amazon EventBridge rules list showing the three week11 rules enabled, each routing a finding class to its Lambda
One EventBridge rule per finding class β€” content-based routing by control ID and event source.
Amazon SNS topic detail page showing the alerts topic with one confirmed email subscription
The SNS topic carrying remediation results and threat alerts, with a confirmed email subscription.
CloudWatch alarm for the remediation dead-letter queue in the OK state
The safety net: an alarm on DLQ depth. If a remediation ever fails, this pages a human.

Step 4 β€” Watch the security-group remediation, end to end

To prove the loop, a small script deliberately creates an offending security group β€” SSH (22) and RDP (3389) open to 0.0.0.0/0 β€” tagged auto-remediate=true.

EC2 security group detail page showing two inbound rules for SSH port 22 and RDP port 3389
Before: two management ports open to the entire internet. Config evaluates the new group within minutes.

Security Hub raises the FSBP finding; EventBridge routes it to sg_remediator, which confirms the tag, revokes exactly the world-open rules on the high-risk ports, and writes the finding back to RESOLVED. Start to finish, this took about 90 seconds.

Security Hub CSPM findings console showing findings from both Security Hub and GuardDuty in a single pane with account IDs redacted
One pane: Security Hub's config findings and GuardDuty's threat findings side by side, all in ASFF.
The same EC2 security group now showing zero inbound rules after remediation
After: the world-open ingress is gone. The legitimate rules (had there been any) would have been left in place.
SNS email with subject RESOLVED Closed open security group, describing the revoked rules
And the receipt: the remediator reports exactly what it did, on the same SNS channel.

Step 5 β€” And the S3 remediation

Same shape for a deliberately public bucket, also tagged for opt-in: the finding fires, s3_remediator applies all four Block Public Access settings, and marks the finding RESOLVED.

S3 bucket permissions page showing Block all public access set to Off
Before: Block Public Access is off.
S3 bucket permissions page showing Block all public access set to On after remediation
After: Block Public Access is on β€” even though a public bucket policy is still present, it is now overridden.
SNS email with subject RESOLVED Blocked public access on S3 bucket
The S3 remediation receipt.

Step 6 β€” The threat path: notify, don't touch

GuardDuty sample findings (a credential-exfiltration and a crypto-mining finding) exercise the third Lambda. threat_notifier formats the finding and publishes it to SNS for a human β€” and deliberately does not remediate. Killing the wrong instance or key mid-incident can be worse than the threat itself.

GuardDuty findings list showing two high-severity sample threat findings and several informational findings, account IDs redacted
GuardDuty's high-severity threat findings β€” the ones that get escalated, not auto-actioned.
SNS email with subject GuardDuty HIGH describing a credential-exfiltration finding and stating it was not auto-remediated
The threat alert a human receives: full context, and an explicit note that nothing was auto-remediated.

Step 7 β€” The guardrail, proven

This is the most important screenshot in the post. When Security Hub flagged a real, pre-existing public bucket in the account that was not tagged for opt-in, the remediator did exactly what it should: nothing. It sent a review notification and left the bucket alone.

SNS email with subject REVIEW Public S3 bucket not auto-remediated, explaining the bucket was not tagged and no change was made
The tag-gate working in the wild: an untagged bucket is reported, never modified. This is what makes auto-remediation safe to run.

Challenges β€” What Actually Went Wrong

Three problems were worth the scar tissue. The last one only surfaced because the pipeline was tested against the account's real findings, not just synthetic ones β€” and it's the most important lesson of the week.

1The FSBP standard subscription timed out on first enablement

The very first apply errored on aws_securityhub_standards_subscription: "timeout while waiting for state to become READY, INCOMPLETE (last state: PENDING, timeout: 3m0s)". The FSBP standard has roughly 300 controls, and on a cold account it sits in PENDING for longer than the provider's 3-minute default before it finishes initializing. Worse, each failed apply disabled and re-enabled the standard, resetting the clock β€” a loop.

Fix

Raise the create timeout on the resource: timeouts { create = "15m" }. Subscribing a Security Hub standard from a fresh account is not instantaneous β€” budget for it, or the first apply looks broken when it isn't.

2The redeploy silently re-ran the old code

After pushing the timeout fix, I queued a run through the HCP API β€” and it timed out at 3 minutes again, with the 15-minute setting nowhere in sight. The cause: a plain run created via the HCP API reuses the workspace's latest already-ingressed configuration version; it does not fetch the new commit. Both runs had pinned to the pre-fix code.

Fix

Bind the run explicitly to the new commit's configuration version (or let the VCS webhook ingress it first and confirm the run's version matches the commit you intend). With HCP VCS workspaces, "queue a run via the API" is not the same as "deploy my latest commit" β€” verify which one you're actually applying.

3The remediator got stuck in an infinite feedback loop

Live testing surfaced the real one. The S3 remediator started firing about eight times a minute, indefinitely, against a pre-existing untagged bucket β€” each invocation sending a notification email. The EventBridge rule matched Workflow.Status of both NEW and NOTIFIED. So on the notify path the Lambda called BatchUpdateFindings to set the status to NOTIFIED β€” and that update was re-emitted as a "Findings - Imported" event, which re-matched the rule, which re-invoked the Lambda, which set NOTIFIED again. A handler was triggering itself off its own writeback.

Fix

Match Workflow.Status = ["NEW"] only. A handler that writes back to the same finding store it is triggered by must keep its writeback status outside its own trigger pattern β€” trigger on NEW, and make RESOLVED/NOTIFIED fall outside the match. Security Hub resets a recurring finding back to NEW, so genuine recurrences are still caught. This is the single most important design rule for any find-and-act automation, and it's the kind of bug that only a real end-to-end run reveals.

Security β€” Guardrails on the Automation

The whole point of auto-remediation is that it acts without a human in the loop β€” so the safety has to be structural, not procedural.

Tag-scoped opt-in, enforced twice

Mutating remediations only touch resources tagged auto-remediate=true. The SG remediator enforces this in two independent places: the code checks the tag, and its IAM policy adds an aws:ResourceTag condition so the RevokeSecurityGroupIngress API itself refuses to act on anything untagged.

Least privilege per function

Each Lambda has its own role scoped to exactly the actions it needs. The threat notifier has no mutating permissions at all β€” it can publish to SNS and nothing else.

Surgical, idempotent changes

The SG remediator strips only the 0.0.0.0/0 and ::/0 ingress on high-risk ports, leaving legitimate scoped rules untouched, and treats an already-removed rule as success. Re-running it is a no-op.

No silent failures

Every failed asynchronous invocation goes to a 14-day dead-letter queue that alarms to SNS. Threat findings are never auto-mutated. And every automated action is written back to the finding, so there's always an audit trail of what changed and why.

Cost

For a single, low-traffic account in us-east-1. Prices as of July 2026 β€” verify at the Security Hub and GuardDuty pricing pages.

ItemEstimate
GuardDuty foundational (CloudTrail $4/M events, VPC/DNS ~$1/GB)~$2–5 / month
Security Hub CSPM checks ($0.0010/check; first 10k findings/mo free)~$3–15 / month
EventBridge + Lambda + SNS + SQSpennies (well within free tier)
30-day free trial (both Security Hub and GuardDuty)covers the lab window
Destroyed$0

Cleanup

Queue a destroy plan on the week-11-dev workspace in HCP β€” it disables Security Hub CSPM and the GuardDuty detector and removes the Lambdas, rules, SNS topic and DLQ. Destroying is inexpensive to reverse: one apply rebuilds the whole stack in about ten minutes. Any deliberately-insecure test resources are torn down with a small cleanup script.

Key Takeaways

  • The loop-break is the whole game. A find-and-act automation whose action updates the thing that triggered it will feed itself forever unless the writeback status falls outside the trigger pattern. Trigger on NEW only.
  • Make auto-remediation opt-in, not opt-out. Tag-scoping (enforced in both code and IAM) is what turns "terrifying" into "safe to run in production" β€” an untagged resource is reported, never touched.
  • Fix the mechanical, escalate the judgement calls. Config misconfigurations get auto-closed; active threats get a human. Same backbone, different verbs.
  • Know your prerequisites. Security Hub controls need AWS Config recording, and standards take minutes to initialize. Neither is obvious until the pipeline silently does nothing.
  • Real end-to-end tests find what unit tests can't. The feedback loop was invisible to synthetic tests β€” it only appeared when the pipeline met the account's actual findings.

Comments

How was your experience?
Your feedback helps improve this site.
PoorExcellent