Expand AWS Backup to remaining DDB + EBS (audit Day 4 phase-2) (#8)

Add a second BackupSelection 'phase2-offsite-everything' on the existing
seahaven-critical-daily plan covering the 15 remaining DynamoDB tables and
all 9 in-use EBS volumes, with the same daily backup + cross-region copy to
the GOVERNANCE-locked seahaven-offsite vault ('offsite for everything').

Reuses seahaven-backup-service-role (AWSBackupServiceRolePolicyForBackup
already grants DDB/RDS/EBS) - no IAM change. Explicit-ARN (not tag-based) to
avoid drifting the standalone file-share volumes and stack-owned tables, same
as phase-1. The 4 deprecated ledgerflow delete-targets are excluded.

Cross-reviewed (no BLOCK). Follow-up: migrate EBS to tag-based selection with
tags codified in owning stacks for resilience to volume replacement.

Deployed to seahaven-backup before merge; selection verified live (24 resources).
This commit is contained in:
Adam Moussa 2026-06-03 11:18:53 -04:00 • committed by GitHub
parent 60e8b0e9ed
commit f2a0cc40d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -175,6 +175,69 @@ export class BackupStack extends cdk.Stack {
],
});
// Phase-2 expansion (audit Day 4): bring the remaining DynamoDB tables and
// all in-use EBS volumes under the same daily plan + cross-region copy to
// the locked offsite vault ("offsite for everything"). Same role and rule
// as phase-1; a separate selection keeps the phase-1 critical set readable.
//
// Still EXPLICIT-ARN (not tag-based) on purpose: the file-share volumes are
// standalone CDK-managed (RETAIN) and the DynamoDB tables are owned by other
// stacks, so tagging them here would drift those stacks — the same reason
// phase-1 avoided tags. Tradeoff: if a volume is replaced (new vol-id) it
// silently drops from this selection; scheduled drift detection + the audit
// re-run are the backstop. Identifiers verified against the live account
// 2026-06-03.
//
// Excluded by intent: the 4 deprecated ledgerflow delete-targets
// (ledgerflow-edi-transactions/-sessions/-invoices/-settings) — being
// retired, not protected. database-1 is in the phase-1 selection above and
// must be removed there when it is deleted (audit H-19).
plan.addSelection("Phase2Resources", {
backupSelectionName: "phase2-offsite-everything",
role: backupRole,
resources: [
// DynamoDB — all remaining tables (15)
...[
"SiteAssignments",
"VendorReplies",
"WorkOrderComments",
"WorkOrders",
"afterhours-shifts",
"exec-aide",
"front-sla-alerts",
"internal-portal-data",
"last-war-bot",
"ledgerflow-pos",
"meal-order-manager-orders",
"pending-site-review",
"seahaven-conversations",
"seahaven-unanswered-questions",
"verified-sites",
].map((t) =>
backup.BackupResource.fromArn(
`arn:aws:dynamodb:us-east-1:${this.account}:table/${t}`
)
),
// EBS — all 9 in-use volumes (unencrypted sources land encrypted at the
// vault CMK, as the C-7 database-1 smoke-test confirmed)
...[
"vol-05cb0eb5c145d799b", // SeaHavenIndustries-dev
"vol-054cf918f227d88f6", // file-share (20 GiB)
"vol-00f05a5a809697ce5", // forgejo
"vol-04d951cccacc435b5", // file-share NAS (500 GiB)
"vol-07094902194638fff", // syslog-server
"vol-0488e0bad1f9afbfb", // apm-wo-analysis grafana
"vol-0c2cbe9e71517a517", // Mutual Aid Data
"vol-0fe224f13812f47e7", // jump box
"vol-0f0c167f3d7f85542", // last-war-rankings
].map((v) =>
backup.BackupResource.fromArn(
`arn:aws:ec2:us-east-1:${this.account}:volume/${v}`
)
),
],
});
cdk.Tags.of(this).add("Project", "account-baseline");
cdk.Tags.of(this).add("Owner", "adam@seahavenind.com");
cdk.Tags.of(this).add("Environment", "prod");