aws stepfunctions start-execution --state-machine-arn <state_machine_arn>
The execution history in the Step Functions console shows each account’s collect step and its
error, if any. A scheduled run is identical; the EventBridge rule just calls StartExecution.
Security Hub report ready for Hubbsters (with failures)
Accounts: 298 succeeded, 2 failed of 300
Findings: 41873
S3 path: s3://example-logging-bucket/SecurityHubReports/2026/09/15/Hubbsters-Security-Hub-Findings.xlsx
Download: aws s3 cp s3://example-logging-bucket/SecurityHubReports/2026/09/15/Hubbsters-Security-Hub-Findings.xlsx .
Failed accounts: 111111111111, 222222222222
ok, so a partial account (some regions
read, some not) is counted as failed and listed. Its findings are still in the workbook.Security Hub report FAILED for <cust_name> with the state machine error instead.| Symptom | Where to look | Usual cause |
|---|---|---|
Account error with assume_role: ... AccessDenied |
Summary sheet Errors column | SecurityHubReadOnlyCrossAccount is missing in that account or trusts the wrong collector role ARN. Redeploy the CloudFormation template there. |
Account partial with <region>: ... InvalidAccessException |
Summary sheet Errors column | Security Hub is not enabled in that region for that account. Enable it or drop the region from report_regions. |
Account missing |
Summary sheet | collect crashed outright (timeout, out of memory). Check the collect log group for the account ID and the Step Functions execution for CollectCrashed. |
Account crashed in the execution history |
Step Functions console | Same as missing, seen from the state machine side. |
Failure notification Environment variable X is required but not set |
discover/aggregate logs | A tfvars value is empty. Every env var is rendered from variables in lambda.tf. |
Failure notification with No accounts to report on |
discover logs | account_ids is empty and the deploying account is not the management or delegated admin account, or has no ACTIVE accounts. |
| Report present but no Slack/Teams message | aggregate logs | Webhook rejected the payload; the log line has the HTTP status. SNS is still published. |
| aggregate runs out of memory | aggregate logs | Raise memory_size for aggregate in lambda.tf. 1024 MB handles several hundred thousand rows. |
The part file for any account is at
s3://<out_bucket>/SecurityHubReports/YYYY/MM/DD/parts/<HHMMSS>/<account>.json and contains
the same status, errors and findings the Summary sheet was built from.
Log groups are /aws/lambda/<cust_name>-security-hub-report-<function> with log_retention_days
retention.
Nothing is alarmed by design (cost). Two signals are worth watching if you want to add alarms later:
ExecutionsFailed on the state machine. Any run that ends in RunFailed
also sends a failure notification, so the notification channel is the first alarm.Errors on the collect function, which should be zero: collect handles account
failures internally, so an invocation error means a timeout or memory problem.Findings sheet is truncated at the Excel row limit (1,048,575 rows) and the run
statistics report truncated = true. Narrow report_regions or the account list, or
resolve findings, if you hit it.parts/ are kept for troubleshooting. Add an S3 lifecycle rule on the
SecurityHubReports/*/parts/ prefix if you want them expired.