Introduction: findings nobody was looking at
MOOBON runs several AWS accounts grouped under AWS Organizations. GuardDuty (threat detection) and Inspector (vulnerability detection) are both services that run per account and per region. The more accounts you have, the more places you need to check: which account, and which region's console?
In practice, nobody keeps switching accounts and opening consoles one by one. Findings are detected, but nobody ever sees them. That is the worst possible state.
So in August 2026, we added an account used only for security, moobon-security, to our organization, collected the organization's findings there, and set things up so that only findings needing action reach us by email and Slack.
However, while reviewing the setup to write this article, we realized that our first alert rule only caught findings in the Tokyo region. This article covers that pitfall and how we fixed it.
Account IDs, email addresses, organization IDs and similar values in this article have all been replaced with placeholders such as
<SECURITY_ACCOUNT_ID>. The commands are arranged to convey the shape of the setup and the thinking behind it.1. Why a dedicated security account?
If all you want is to see every finding in one place, you could use the organization's management account. We still chose to create a dedicated account, for three reasons.
Stay out of the management account
The management account controls billing and SCPs (the organization-wide guardrails); it's the most powerful account in the organization. You want as few people as possible logging into it, with as few permissions as possible. AWS also recommends designating an account other than the management account as the delegated administrator for security services.
Separate the watcher from the watched
If an account running your services is compromised and its findings live in that same account, an attacker could wipe out the detection setup along with them. Put the monitoring in a separate account, and the records and alerts survive no matter what happens on the workload side.
Clean permission boundaries
When you want someone to be able to see findings but not touch production, a separate account means you just grant access to moobon-security. The account itself becomes the permission boundary.
2. The big picture: three services and a delegated administrator
We use three services, each with a different role.
| Service | What it finds | Examples |
|---|---|---|
| GuardDuty | Suspicious activity happening now (threats) | API calls from unfamiliar countries, crypto mining on EC2, use of leaked credentials |
| Inspector | Weaknesses that invite attacks if left alone (vulnerabilities) | Known vulnerabilities (CVEs) in packages on EC2, container images and Lambda |
| Security Hub | Where the two above are gathered into one list | Findings from every account and region, on one screen, sorted by severity |
The key to running these across the organization is the delegated administrator. Once the management account says "moobon-security manages this service for the whole organization," moobon-security can enable the service in every member account and view their findings.
[ Management account ] … only designates the delegated admin (rarely touched after)
│
▼ delegates
[ moobon-security ] … delegated admin for GuardDuty / Inspector / Security Hub
│ ▲
│ │ findings flow in automatically (org accounts × all regions)
│ │
│ ├── [ Management account ]
│ └── [ Accounts for our own services ]
│
▼ Security Hub (all regions aggregated into Tokyo)
▼ GuardDuty findings of MEDIUM and above only
EventBridge → SNS → email / Slack (Amazon Q Developer)In December 2025, Security Hub was revamped, and the original Security Hub was renamed Security Hub CSPM. The new Security Hub is a higher-level service that correlates findings from GuardDuty, Inspector and CSPM to show risk. The commands and alert rules in this article use the Security Hub CSPM API (
aws securityhub) and its finding format (ASFF), the long-established foundation for aggregating findings. MOOBON also started by building aggregation and alerting on CSPM. Since the new Security Hub builds on findings from CSPM, GuardDuty and Inspector, you can add it on top of this setup later. The delegated administrator is shared by both.3. Creating the moobon-security account
Sections 3 and 4 are run in the management account. First, create a new account in the organization.
# run in the management account aws organizations create-account \ --email aws-security@example.com \ --account-name moobon-security # check progress (done when State is SUCCEEDED) aws organizations list-create-account-status --states SUCCEEDED IN_PROGRESS
- Use a shared email address, not a personal one. This address becomes the account's root user. Use a group address that still works when people change roles.
- Move it into a Security OU. Keeping it in a separate OU from workload accounts makes it easier to apply different SCPs later.
# create a Security OU and move moobon-security into it aws organizations create-organizational-unit \ --parent-id <ROOT_ID> --name Security aws organizations move-account \ --account-id <SECURITY_ACCOUNT_ID> \ --source-parent-id <ROOT_ID> \ --destination-parent-id <SECURITY_OU_ID>
Sign-in is through IAM Identity Center (SSO), by assigning a permission set for moobon-security. The root user gets MFA and is not used day to day. We cover the SSO setup in detail in our related article Ditching IAM Users in AI-Driven Development: AWS SSO + AssumeRole.
# add a profile for moobon-security to ~/.aws/config [profile moobon-security] sso_session = moobon-sso sso_account_id = <SECURITY_ACCOUNT_ID> sso_role_name = AdministratorAccess region = ap-northeast-1
4. Designating the delegated administrator
This is essentially all the work done in the management account. For each of the three services, designate moobon-security as the delegated administrator.
# run in the management account
SEC=<SECURITY_ACCOUNT_ID>
# Security Hub (once, in the aggregation region = ap-northeast-1)
aws securityhub enable-organization-admin-account \
--admin-account-id $SEC --region ap-northeast-1
# GuardDuty and Inspector must be designated per region
for r in $(aws ec2 describe-regions --query 'Regions[].RegionName' --output text); do
aws guardduty enable-organization-admin-account \
--admin-account-id $SEC --region $r
aws inspector2 enable-delegated-admin-account \
--delegated-admin-account-id $SEC --region $r
doneThis is the easiest thing to miss. Designating the delegated administrator in Tokyo does nothing for other regions. Regions you don't use are exactly where attackers tend to launch EC2 instances for mining, so designate it in every region (see also section 9).
5. Enabling GuardDuty across the organization
From here on, commands run in moobon-security. Once it is designated as the delegated administrator, GuardDuty is enabled in moobon-security itself. All that's left is to add the organization's accounts as members.
# run in moobon-security (per region)
DET=$(aws guardduty list-detectors --query 'DetectorIds[0]' --output text \
--profile moobon-security)
# add organization accounts as members (no invitation email needed inside the org)
aws guardduty create-members \
--detector-id $DET \
--account-details AccountId=<ACCOUNT_ID_1> AccountId=<ACCOUNT_ID_2> \
--profile moobon-security
# check the members (OK when RelationshipStatus is Enabled)
aws guardduty list-members --detector-id $DET --profile moobon-security \
--query 'Members[].[AccountId,RelationshipStatus]' --output tableMOOBON has only a handful of accounts including the management account, so we add members individually. If your organization adds accounts often, set --auto-enable-organization-members ALL with update-organization-configuration, and both existing accounts and any account that joins later are covered automatically.
GuardDuty membership is also managed per region. The console may show every member as Enabled in Tokyo while they were never added in other regions. Loop over the regions with
for to check.6. Enabling Inspector across the organization
In Inspector, the setting that automatically enables accounts that join the organization later is separate from the operation that enables existing accounts. The auto-enable setting alone does not scan existing accounts.
# run in moobon-security # 1) automatically enable accounts that join later aws inspector2 update-organization-configuration \ --auto-enable ec2=true,ecr=true,lambda=true \ --profile moobon-security # 2) make existing accounts members and enable scanning for acct in <ACCOUNT_ID_1> <ACCOUNT_ID_2>; do aws inspector2 associate-member --account-id $acct --profile moobon-security done aws inspector2 enable \ --account-ids <ACCOUNT_ID_1> <ACCOUNT_ID_2> \ --resource-types EC2 ECR LAMBDA \ --profile moobon-security
In the console, "Activate all accounts" on Inspector's account management page does the same thing.
When you first enable Inspector, it scans OS packages on EC2 and even old container images left in ECR, and lists vulnerabilities in numbers far beyond what you expected. That happened to us too. The realistic approach is to clean up old images (ECR lifecycle policies) and work through the resources that are actually running first. Sending all of that to alerts is not an option, which is why section 8 leaves Inspector out of the alerts.
7. Aggregating in Security Hub
In accounts where Security Hub is enabled, GuardDuty and Inspector findings are sent to Security Hub automatically. Two things remain: (1) gather the results from all regions into one region, and (2) make the organization's accounts members.
(1) Gather all regions into Tokyo
# run in moobon-security, ap-northeast-1 aws securityhub create-finding-aggregator \ --region-linking-mode ALL_REGIONS \ --region ap-northeast-1 --profile moobon-security
Tokyo is now the aggregation region, and findings from other regions are collected there. This aggregation turns out to be the key to fixing the alerts in section 8.
(2) Make the organization's accounts members
aws securityhub create-members \
--account-details '[{"AccountId":"<ACCOUNT_ID_1>"},{"AccountId":"<ACCOUNT_ID_2>"}]' \
--profile moobon-security
aws securityhub list-members --only-associated --profile moobon-security \
--query 'Members[].[AccountId,MemberStatus]' --output tableFor configuration checks, we enabled AWS Foundational Security Best Practices (FSBP) and the CIS AWS Foundations Benchmark. In an organization with many accounts, central configuration lets you define which standards to use as a policy and associate it with the organization root or an OU, so new accounts get the same settings automatically.
8. Alerts: we were only catching Tokyo
Even with everything on one screen, you won't notice anything unless you open it. The last step is alerts that send findings needing action to email and Slack. We narrowed the condition to "GuardDuty findings of MEDIUM (4) severity or higher." Inspector and configuration checks come in large numbers, so we handle them in the console instead of alerting.
Our first alert: catching GuardDuty events directly
At first, we followed the commonly described approach and created a rule in the Tokyo region that catches GuardDuty events directly with EventBridge.
{
"source": ["aws.guardduty"],
"detail-type": ["GuardDuty Finding"],
"detail": { "severity": [{ "numeric": [">=", 4] }] }
}It sends to two SNS topics. One is for email, with the message body assembled in Japanese by EventBridge's input transformer. The other is for Slack, connected to Amazon Q Developer in chat applications (formerly AWS Chatbot), which posts to our #aws-security channel.
When we generated a sample finding in Tokyo, both the email and the Slack message arrived, and we thought we were done.
The pitfall: findings outside Tokyo went nowhere
We realized it while reviewing the setup for this article. GuardDuty events are delivered only to EventBridge in the region where the finding occurred. With a single rule in Tokyo, a finding in, say, Oregon (us-west-2) is never alerted on anywhere.
When credentials leak, a classic attacker move is to launch EC2 instances for crypto mining in regions nobody watches. Even with GuardDuty enabled in every region, if alerts only cover Tokyo, those are exactly the findings that never arrive. Unless someone went and opened the Security Hub console, nobody would have noticed.
The fix: switch the alert source to Security Hub
You could create a rule in every region and forward events to Tokyo, but in section 7 we had already aggregated findings from all regions into Security Hub in Tokyo. So catching the events for findings that Security Hub imports, in Tokyo, lets a single rule catch every region. Save the following as pattern.json.
{
"source": ["aws.securityhub"],
"detail-type": ["Security Hub Findings - Imported"],
"detail": {
"findings": {
"ProductName": ["GuardDuty"],
"Severity": { "Label": ["MEDIUM", "HIGH", "CRITICAL"] },
"Workflow": { "Status": ["NEW"] },
"RecordState": ["ACTIVE"]
}
}
}# run in moobon-security, ap-northeast-1 (the aggregation region) aws events put-rule \ --name securityhub-guardduty-medium-plus \ --event-pattern file://pattern.json \ --profile moobon-security aws events put-targets \ --rule securityhub-guardduty-medium-plus \ --targets file://targets.json \ --profile moobon-security
Security Hub findings use a common format called ASFF, so the paths referenced by the input transformer that builds the email body change too. The finding sits under detail.findings[0].
// targets.json (the input transformer part of the email target)
"InputPathsMap": {
"acct": "$.detail.findings[0].AwsAccountId",
"region": "$.detail.findings[0].Region",
"sev": "$.detail.findings[0].Severity.Product",
"label": "$.detail.findings[0].Severity.Label",
"type": "$.detail.findings[0].Types[0]",
"title": "$.detail.findings[0].Title",
"desc": "$.detail.findings[0].Description",
"first": "$.detail.findings[0].FirstObservedAt",
"last": "$.detail.findings[0].LastObservedAt"
},
"InputTemplate": "\"[GuardDuty] Severity <sev> (<label>)\"\n\"Account: <acct> Region: <region>\"\n..."The email also includes a link that signs in to moobon-security through SSO and opens GuardDuty in the relevant region directly. Appending #/console?account_id=…&role_name=…&destination=… to the IAM Identity Center access portal URL takes you straight to that console page after sign-in. The less effort it takes to get from an alert to the console, the less likely the check gets put off.
For Slack, we simply pass the raw event to SNS. Amazon Q Developer formats Security Hub events into a readable post, so no extra setup is needed.
Verifying: generating a sample in Oregon
# generate a single MEDIUM sample finding in a region other than Tokyo
DET=$(aws guardduty list-detectors --region us-west-2 \
--query 'DetectorIds[0]' --output text --profile moobon-security)
aws guardduty create-sample-findings --region us-west-2 \
--detector-id $DET --finding-types "Recon:EC2/Portscan" \
--profile moobon-securityThe result: within about a minute, the rule in Tokyo fired and the alert reached both email and Slack. At the same time, the old rule (wired directly to GuardDuty) did not fire even once, confirming in practice that it had been missing findings outside Tokyo. Once verified, disable the old rule (otherwise Tokyo findings arrive twice) and archive the sample finding.
If your SNS topic's access policy limits publishing from EventBridge to a specific rule (
aws:SourceArn), alerts won't arrive until you add the new rule's ARN. Creating the rule in the console adds this policy automatically, but with the CLI you have to add it yourself."Imported" events are sent not only for new findings but also when an existing finding is updated. GuardDuty updates a finding when the same suspicious activity continues, so the same content can arrive repeatedly. Once you start handling it, set the workflow status in Security Hub to NOTIFIED or RESOLVED; the finding then no longer matches the rule (NEW) and the alerts stop.
9. Costs and pitfalls
Switching the alert source costs almost nothing
Security Hub was already enabled, and aggregating all regions has no extra charge. Receiving events sent by AWS services in EventBridge is free, SNS email notifications have a free tier of 1,000 per month, and Amazon Q Developer in chat applications itself is free. Cost is no reason to put off this fix.
Check the services' own costs during the free trial
GuardDuty and Security Hub CSPM have a 30-day free trial, and Inspector has a 15-day one. The console shows an estimate of what you'd pay going forward even during the trial, so the safe order is to enable first, then narrow protection plans or scan targets based on the estimate. Costs depend on log and resource volume, so unused regions cost almost nothing.
"Enabled" is not the same as "alerts arrive"
This was our biggest lesson. GuardDuty was enabled in every region and our alert test passed in Tokyo, so we assumed everything was covered. Test your alerts in regions you don't normally use, too. With sample findings, it takes a minute.
Close unused regions with an SCP
Alongside watching every region, denying any activity in unused regions with an SCP shrinks what you have to protect. Note that global services such as IAM and CloudFront use us-east-1, so they need to be excluded from the deny.
Use one account as the delegated administrator
If GuardDuty and Security Hub have different delegated administrator accounts, findings may not aggregate properly. AWS also recommends using the same account. Handing every security service to moobon-security is the simplest setup.
Afterword
We think of security services as only half done once they're enabled. Findings gathered in one place, only the ones that matter delivered, and someone who reads them when they arrive: only when all of that is connected does it mean anything. This time, the review we did to write this article showed us that part of that chain was broken. We learned firsthand that whether enabled settings and passing alert tests really cover everything, you only know once you check from the outside.
Separating out a dedicated security account means fewer reasons to enter the management account, and findings and alerts are now managed in one place: Tokyo in moobon-security. Closing gaps through the system rather than relying on people being careful is the same thinking behind our switch to SSO + AssumeRole. MOOBON holds ISMS (ISO/IEC 27001) certification, and we will keep this setup running as part of managing it.
For a review of your AWS security settings, you can consult us through our free security assessment. We also help with multi-account design and with operations that include running GuardDuty and Security Hub, through our AWS Operations Service.
References
- AWS Security Hub User Guide: Central configuration
- Amazon GuardDuty User Guide: Managing accounts with AWS Organizations
- Amazon Inspector User Guide: Managing multiple accounts with AWS Organizations
- AWS What's New: AWS Security Hub is now generally available with near real-time risk analytics (December 2025)
- AWS Security Reference Architecture (AWS SRA)
Frequently Asked Questions
QWhy not just look at GuardDuty and Security Hub in the management account?
It works, but we don't recommend it. The management account controls billing and SCPs for the whole organization, so it's the place where you want the fewest people logging in and the least permissions in daily use. AWS also recommends designating an account other than the management account as the delegated administrator for security services. Moving day-to-day security work into a dedicated account reduces how often anyone has to enter the management account at all.
QDo I need to enable GuardDuty in regions I don't use?
It's safer to. When credentials leak, attackers often launch large numbers of EC2 instances in regions nobody is watching. If you only watch the regions you use, those become your blind spots. Costs depend on data volume, so unused regions cost almost nothing. Blocking unused regions entirely with an SCP is also effective.
QHow much does it cost?
It varies widely with the size of your accounts and the volume of logs, so there's no single answer. GuardDuty and Security Hub CSPM come with a 30-day free trial and Inspector with a 15-day one, and the console shows an estimate of what you'd pay going forward even during the trial. A practical approach is to enable everything, check the estimate during the trial, and then narrow the protection plans if needed.
QWon't we get so many alerts that we stop reading them?
That's the biggest trap. At MOOBON, alerts cover only GuardDuty findings of MEDIUM severity or higher that nobody has acted on yet. Inspector vulnerabilities and configuration checks come in far larger numbers, so instead of alerting on them, we work through them in order of severity in the Security Hub console. The key is keeping alerts at a volume where you can honestly say you read every one.
QWe set up alerts, so why don't findings from outside Tokyo arrive?
Because GuardDuty events are delivered only to EventBridge in the region where the finding occurred. A single rule in Tokyo can't catch findings from other regions. That was exactly our situation at first. If Security Hub already aggregates findings from all regions into Tokyo, switching the alert source to Security Hub lets one rule in Tokyo catch every region (see section 8).
