terraform-aws-security-hub-report

Deployment

Prerequisites

Deploy the module

cp security-hub/core.tfvars security-hub/<env>.tfvars   # edit values
make plan VAR_FILE=<env>.tfvars
make apply VAR_FILE=<env>.tfvars

make build bundles files/*.py plus openpyxl into security-hub/build/ for the arm64 Python 3.13 runtime; plan and apply depend on it. terraform plan fails if that directory is missing because the archive data source zips it, and only make build creates it.

State is local by default. Add a backend block to versions.tf for shared state.

!!! note “Tags” tags is applied to every resource through provider default tags. core.tfvars shows the expected keys: Company, Application, Environment, Owner, CostCenter.

Roll out the cross-account role

Take the collector_role_arn output and deploy cloudformation/reporting-account-role.yaml in every account that should appear in the report, including the reporting account itself if it should be included.

=== “Single account”

```shell
aws cloudformation deploy \
  --stack-name security-hub-report-role \
  --template-file cloudformation/reporting-account-role.yaml \
  --parameter-overrides CollectorRoleArn=<collector_role_arn> \
  --capabilities CAPABILITY_NAMED_IAM
```

=== “StackSet (whole Organization)”

Deploy as a service-managed StackSet from the management account with automatic
deployment enabled so new accounts get the role as they join:

```shell
aws cloudformation create-stack-set \
  --stack-set-name security-hub-report-role \
  --template-body file://cloudformation/reporting-account-role.yaml \
  --parameters ParameterKey=CollectorRoleArn,ParameterValue=<collector_role_arn> \
  --capabilities CAPABILITY_NAMED_IAM \
  --permission-model SERVICE_MANAGED \
  --auto-deployment Enabled=true,RetainStacksOnAccountRemoval=false
aws cloudformation create-stack-instances \
  --stack-set-name security-hub-report-role \
  --deployment-targets OrganizationalUnitIds=<root-or-ou-id> \
  --regions <region>
```

IAM is global, so one region per account is enough.

Notifications

Every channel receives the same summary; see Operations.

Variables

Name Default Purpose
region required Deployment region
cust_name required Resource name prefix and report label
account_ids [] Accounts to report on; empty means the whole Organization
report_regions required Regions to read findings from in each account
out_bucket required Destination bucket
webhook_urls [] Slack/Teams webhooks (sensitive)
create_sns true Create an SNS topic here
sns_topic_arn null Existing topic when create_sns = false
report_schedule cron(0 2 1 * ? *) EventBridge schedule
max_concurrency 10 Accounts collected in parallel
log_retention_days 30 Lambda log retention
tags {} Applied to every resource via provider default tags

Outputs

Name Purpose
state_machine_arn Start an ad-hoc run with aws stepfunctions start-execution
collector_role_arn Pass as CollectorRoleArn to the CloudFormation role template
sns_topic_arn Topic to subscribe to, if one was created
bucket_policy Statement to attach to out_bucket
sns_topic_policy Policy for an SNS topic in another account when create_sns = false