mirror of
https://github.com/Sea-Haven-Industries/seahaven-org-baseline.git
synced 2026-09-30 04:33:15 +00:00
Add AWS Backup with offsite vault (audit C-7) (#3)
* Add AWS Backup with offsite vault (audit C-7)
The account had zero AWS Backup vaults/plans, so 22 of 23 data stores
had no immutable, cross-region recovery path (audit finding C-7). One
ransomware event or rogue delete would erase primary plus same-region
snapshots/PITR.
Phase 1 ("critical data first") protects the seven highest-risk stores
with no offsite leg today (2 RDS, 2 DynamoDB, 3 S3) via a daily plan in
a new us-east-1 vault, copied cross-region into a governance-locked
us-west-2 vault. Governance (not compliance) mode first so the plan can
be validated before committing to irreversible immutability.
The backup service role is backup-only (no restore policies) to stay
least-privilege; restores get a separate audited path later. Resources
are selected by explicit ARN to avoid drifting the stacks that own them.
Deploys via the shared cdk deploy --all alongside the C-1 CloudTrail
stack. See the README pre-deploy gates (S3 versioning, database-1
unencrypted copy smoke-test, DynamoDB PITR) before the first run.
* Grant AWS Backup service use of vault CMKs
The L2 BackupVault does not grant the backup service principal use of a
customer-managed key; the synthesized key policy only delegated to
account IAM. Cross-region copy of encrypted RDS/EBS recovery points uses
KMS grants on the destination key, so without an explicit grant those
copy jobs fail — and silently, since the account has no CloudTrail yet.
Add backup.amazonaws.com crypto + CreateGrant statements to both vault
keys, scoped by aws:SourceAccount (cross-review BLOCK 2; mirrors the
discipline used on the C-1 CloudTrail key). Same class of bug the C-1
cross-review caught on the CloudTrail CMK.
This commit is contained in:
parent
dc079edb93
commit
64ef25dc5b
4 changed files with 360 additions and 5 deletions
74
README.md
74
README.md
|
|
@ -1,10 +1,19 @@
|
|||
# seahaven-account-baseline
|
||||
|
||||
Account-level security and governance baseline for Sea Haven Industries
|
||||
(AWS account **328440206208**, region **us-east-1**), managed as a single CDK
|
||||
TypeScript app. This is where account-wide detective controls live, so they are
|
||||
(AWS account **328440206208**), managed as a single CDK TypeScript app. Most
|
||||
resources are in **us-east-1**; the offsite backup vault is in **us-west-2**.
|
||||
This is where account-wide detective and recovery controls live, so they are
|
||||
versioned, reviewed, and drift-checked like any other stack.
|
||||
|
||||
Stacks (all deployed by `cdk deploy --all` / the CD workflow):
|
||||
|
||||
| Stack | Region | Purpose |
|
||||
|---|---|---|
|
||||
| `seahaven-account-baseline` | us-east-1 | CloudTrail + future detective controls (C-1) |
|
||||
| `seahaven-backup` | us-east-1 | Primary AWS Backup vault + plan + role (C-7) |
|
||||
| `seahaven-backup-offsite` | us-west-2 | Governance-locked offsite copy vault (C-7) |
|
||||
|
||||
## What it deploys
|
||||
|
||||
### CloudTrail (audit finding C-1)
|
||||
|
|
@ -36,11 +45,52 @@ finding H-1 (metric filters + alarms now have a log group to target).
|
|||
- **RETAIN** on the bucket and KMS key so a stack teardown never destroys the
|
||||
audit trail.
|
||||
|
||||
### AWS Backup (audit finding C-7)
|
||||
|
||||
Phase 1 ("critical data first") of fixing the account's complete lack of AWS
|
||||
Backup. Protects the data stores with no offsite leg today and copies each
|
||||
recovery point cross-region into a governance-locked vault.
|
||||
|
||||
| Resource | Logical ID | Notes |
|
||||
|---|---|---|
|
||||
| Primary vault | `seahaven-primary` (us-east-1) | KMS-CMK encrypted, unlocked (working copy), RETAIN |
|
||||
| Offsite vault | `seahaven-offsite` (us-west-2) | KMS-CMK encrypted, **Vault Lock GOVERNANCE** (min-retention 30d, no cooling-off window), RETAIN |
|
||||
| Backup plan | `seahaven-critical-daily` | Daily 06:00 UTC, delete-after 35d, **cross-region CopyAction → offsite** (retain 90d) |
|
||||
| Service role | `seahaven-backup-service-role` | **Backup-only** (Backup + S3-Backup managed policies); restore perms intentionally deferred |
|
||||
|
||||
**Phase-1 scope** (selected by explicit ARN, not tags, to avoid drifting other
|
||||
stacks): RDS `database-1`, RDS `proposal-system-db`, DynamoDB `PaymentsDashboard`,
|
||||
DynamoDB `purchase-orders`, S3 `accounting.seahaven.com`,
|
||||
`seahaven-payments-csv-328440206208`, `google-workspace-seahavenind.com`.
|
||||
|
||||
**Coexists with** existing EBS DLM snapshots and DynamoDB PITR — it supplements
|
||||
them with the missing offsite + immutable leg; it does not replace them.
|
||||
|
||||
**Design decisions:**
|
||||
|
||||
- **Governance lock first, not compliance.** Recovery points can't be silently
|
||||
deleted, but a principal with explicit permission can still intervene while
|
||||
we validate. Graduate to COMPLIANCE (irreversible) later by adding
|
||||
`changeableFor` to the offsite vault lock + redeploy.
|
||||
- **Backup-only role.** Restore policies and `allowRestores` are not granted;
|
||||
restores get a separate audited path once a restore-test process exists.
|
||||
|
||||
**Pre-deploy gates** (must clear before the first scheduled run):
|
||||
|
||||
1. Enable S3 versioning on `seahaven-payments-csv-328440206208` and
|
||||
`google-workspace-seahavenind.com` (`accounting.seahaven.com` already has it,
|
||||
audit C-9), or their jobs fail silently (folds in H-21).
|
||||
2. `database-1` is unencrypted (H-19): smoke-test an on-demand backup + copy of
|
||||
it to us-west-2 first; if the copy fails, encrypt it or drop it from the copy.
|
||||
3. Enable DynamoDB PITR (H-7) on the two tables for between-window recovery.
|
||||
|
||||
## Roadmap (same stack)
|
||||
|
||||
Account-level detective controls with no current home, to be added here:
|
||||
AWS Config (H-2), GuardDuty (H-3), Security Hub (H-4), IAM Access Analyzer
|
||||
(M-5), Inspector2 (M-6).
|
||||
Account-level detective controls with no current home, to be added to the
|
||||
`account-baseline` stack: AWS Config (H-2), GuardDuty (H-3), Security Hub (H-4),
|
||||
IAM Access Analyzer (M-5), Inspector2 (M-6). Backup phase 2: expand past the
|
||||
phase-1 set via tag-based selection and graduate the offsite vault to compliance
|
||||
mode.
|
||||
|
||||
## Deploy
|
||||
|
||||
|
|
@ -59,3 +109,17 @@ aws cloudtrail get-trail-status --name seahaven-org-trail # IsLogging: tr
|
|||
aws cloudtrail describe-trails --trail-name-list seahaven-org-trail
|
||||
aws cloudtrail validate-logs --trail-arn <arn> --start-time <t> # digest integrity
|
||||
```
|
||||
|
||||
AWS Backup (C-7):
|
||||
|
||||
```
|
||||
aws backup list-backup-vaults # seahaven-primary
|
||||
aws backup list-backup-vaults --region us-west-2 # seahaven-offsite
|
||||
aws backup describe-backup-vault --backup-vault-name seahaven-offsite --region us-west-2 # Locked, MinRetentionDays
|
||||
aws backup get-backup-plan --backup-plan-id <id> # daily rule + CopyAction
|
||||
# Smoke test: on-demand backup of one resource, then confirm the cross-region copy lands
|
||||
aws backup start-backup-job --backup-vault-name seahaven-primary \
|
||||
--resource-arn arn:aws:rds:us-east-1:328440206208:db:database-1 \
|
||||
--iam-role-arn arn:aws:iam::328440206208:role/seahaven-backup-service-role
|
||||
aws backup list-copy-jobs --region us-west-2 # copy to offsite present + COMPLETED
|
||||
```
|
||||
|
|
|
|||
16
bin/app.ts
16
bin/app.ts
|
|
@ -2,6 +2,8 @@
|
|||
import "source-map-support/register";
|
||||
import * as cdk from "aws-cdk-lib";
|
||||
import { AccountBaselineStack } from "../lib/account-baseline-stack";
|
||||
import { BackupOffsiteStack } from "../lib/backup-offsite-stack";
|
||||
import { BackupStack } from "../lib/backup-stack";
|
||||
|
||||
const app = new cdk.App();
|
||||
|
||||
|
|
@ -9,3 +11,17 @@ new AccountBaselineStack(app, "account-baseline", {
|
|||
stackName: "seahaven-account-baseline",
|
||||
env: { account: "328440206208", region: "us-east-1" },
|
||||
});
|
||||
|
||||
// AWS Backup (audit C-7). Offsite vault (us-west-2) must exist before the
|
||||
// primary plan that copies to it, hence the explicit dependency.
|
||||
const backupOffsite = new BackupOffsiteStack(app, "backup-offsite", {
|
||||
stackName: "seahaven-backup-offsite",
|
||||
env: { account: "328440206208", region: "us-west-2" },
|
||||
});
|
||||
|
||||
const backupPrimary = new BackupStack(app, "backup", {
|
||||
stackName: "seahaven-backup",
|
||||
env: { account: "328440206208", region: "us-east-1" },
|
||||
});
|
||||
|
||||
backupPrimary.addDependency(backupOffsite);
|
||||
|
|
|
|||
87
lib/backup-offsite-stack.ts
Normal file
87
lib/backup-offsite-stack.ts
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
import * as cdk from "aws-cdk-lib";
|
||||
import * as kms from "aws-cdk-lib/aws-kms";
|
||||
import * as iam from "aws-cdk-lib/aws-iam";
|
||||
import * as backup from "aws-cdk-lib/aws-backup";
|
||||
import { Construct } from "constructs";
|
||||
|
||||
/**
|
||||
* Offsite AWS Backup vault for Sea Haven (account 328440206208), in us-west-2.
|
||||
*
|
||||
* This is the Copy3 / offsite leg of the 3-2-1 strategy and the only immutable
|
||||
* recovery path in the account. The primary plan (see backup-stack.ts, us-east-1)
|
||||
* copies recovery points here cross-region. Closes audit finding C-7 together
|
||||
* with backup-stack.
|
||||
*
|
||||
* Vault Lock is GOVERNANCE mode for now (minRetention only, no `changeableFor`):
|
||||
* recovery points cannot be silently deleted, but a principal with explicit
|
||||
* `backup:DeleteRecoveryPoint` / `backup:DeleteBackupVaultLockConfiguration`
|
||||
* permission can still intervene while we validate the plan. Graduate to
|
||||
* COMPLIANCE mode later by adding `changeableFor` (irreversible after the
|
||||
* cooling-off window) — a one-line change + redeploy.
|
||||
*/
|
||||
export class BackupOffsiteStack extends cdk.Stack {
|
||||
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
|
||||
super(scope, id, props);
|
||||
|
||||
// CMK encrypting offsite recovery points (rotation on; RETAIN so a stack
|
||||
// teardown never strands/destroys the only immutable copy).
|
||||
const vaultKey = new kms.Key(this, "OffsiteVaultKey", {
|
||||
alias: "backup-offsite-vault",
|
||||
description: "Encrypts offsite AWS Backup recovery points (us-west-2)",
|
||||
enableKeyRotation: true,
|
||||
removalPolicy: cdk.RemovalPolicy.RETAIN,
|
||||
});
|
||||
|
||||
// The L2 BackupVault does NOT grant the backup service use of a customer
|
||||
// CMK — without this, cross-region COPY jobs of encrypted RDS/EBS recovery
|
||||
// points fail (and silently, with no CloudTrail). Grant backup.amazonaws.com
|
||||
// the minimum KMS actions on this destination key, incl. CreateGrant.
|
||||
vaultKey.addToResourcePolicy(
|
||||
new iam.PolicyStatement({
|
||||
sid: "AllowAwsBackupUseOfTheKey",
|
||||
principals: [new iam.ServicePrincipal("backup.amazonaws.com")],
|
||||
// Action set matches AWS's documented Backup vault-key policy; scoped
|
||||
// to this account so only this account's Backup service can use it.
|
||||
actions: [
|
||||
"kms:Decrypt",
|
||||
"kms:GenerateDataKey",
|
||||
"kms:GenerateDataKeyWithoutPlaintext",
|
||||
"kms:ReEncrypt*",
|
||||
"kms:DescribeKey",
|
||||
],
|
||||
resources: ["*"],
|
||||
conditions: { StringEquals: { "aws:SourceAccount": this.account } },
|
||||
})
|
||||
);
|
||||
vaultKey.addToResourcePolicy(
|
||||
new iam.PolicyStatement({
|
||||
sid: "AllowAwsBackupCreateGrant",
|
||||
principals: [new iam.ServicePrincipal("backup.amazonaws.com")],
|
||||
actions: ["kms:CreateGrant"],
|
||||
resources: ["*"],
|
||||
conditions: {
|
||||
Bool: { "kms:GrantIsForAWSResource": "true" },
|
||||
StringEquals: { "aws:SourceAccount": this.account },
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
new backup.BackupVault(this, "OffsiteVault", {
|
||||
backupVaultName: "seahaven-offsite",
|
||||
encryptionKey: vaultKey,
|
||||
removalPolicy: cdk.RemovalPolicy.RETAIN,
|
||||
// Governance-mode Vault Lock: no `changeableFor`, so it stays adjustable.
|
||||
lockConfiguration: {
|
||||
minRetention: cdk.Duration.days(30),
|
||||
},
|
||||
});
|
||||
|
||||
cdk.Tags.of(this).add("Project", "account-baseline");
|
||||
cdk.Tags.of(this).add("Owner", "adam@seahavenind.com");
|
||||
cdk.Tags.of(this).add("Environment", "prod");
|
||||
cdk.Tags.of(this).add("ManagedBy", "cdk");
|
||||
|
||||
new cdk.CfnOutput(this, "OffsiteVaultName", { value: "seahaven-offsite" });
|
||||
new cdk.CfnOutput(this, "OffsiteVaultKmsKeyArn", { value: vaultKey.keyArn });
|
||||
}
|
||||
}
|
||||
188
lib/backup-stack.ts
Normal file
188
lib/backup-stack.ts
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
import * as cdk from "aws-cdk-lib";
|
||||
import * as kms from "aws-cdk-lib/aws-kms";
|
||||
import * as iam from "aws-cdk-lib/aws-iam";
|
||||
import * as events from "aws-cdk-lib/aws-events";
|
||||
import * as backup from "aws-cdk-lib/aws-backup";
|
||||
import { Construct } from "constructs";
|
||||
|
||||
/**
|
||||
* Primary AWS Backup vault + plan for Sea Haven (account 328440206208), us-east-1.
|
||||
*
|
||||
* Closes audit finding C-7 (AWS Backup entirely unused) together with
|
||||
* backup-offsite-stack. Phase 1 ("critical data first"): protect the data
|
||||
* stores with no offsite leg today and copy each recovery point cross-region
|
||||
* to the GOVERNANCE-locked `seahaven-offsite` vault (us-west-2).
|
||||
*
|
||||
* Coexistence: this SUPPLEMENTS the existing EBS DLM snapshots and DynamoDB
|
||||
* PITR — it does not replace them. It adds the missing Copy3 (offsite) +
|
||||
* immutability leg. The DLM/PITR overlap is rationalized in a later phase.
|
||||
*
|
||||
* Selection is by explicit ARN (not tag-based) so we don't have to tag — and
|
||||
* drift — resources owned by other stacks (proposal-system, payments-dashboard).
|
||||
* Switch to tag-based selection when expanding past the phase-1 set.
|
||||
*
|
||||
* PRE-DEPLOY GATES (validate before the first scheduled run):
|
||||
* - S3 backup requires bucket versioning. `accounting.seahaven.com` already
|
||||
* has it (audit C-9); `seahaven-payments-csv-328440206208` and
|
||||
* `google-workspace-seahavenind.com` must have versioning enabled first or
|
||||
* their jobs fail silently (folds in audit H-21).
|
||||
* - `database-1` is unencrypted (audit H-19). Cross-region copy of an
|
||||
* unencrypted RDS recovery point may fail or land unencrypted. Smoke-test
|
||||
* an on-demand backup of `database-1` FIRST and confirm the copy job to
|
||||
* us-west-2 succeeds; if not, encrypt database-1 (H-19) or drop it from the
|
||||
* copy until then.
|
||||
* - DynamoDB PITR (H-7) is independent of this plan; enable it on the two
|
||||
* tables for between-window point-in-time recovery.
|
||||
*/
|
||||
export class BackupStack extends cdk.Stack {
|
||||
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
|
||||
super(scope, id, props);
|
||||
|
||||
// CMK encrypting the primary (operational) vault. RETAIN + rotation.
|
||||
const vaultKey = new kms.Key(this, "PrimaryVaultKey", {
|
||||
alias: "backup-primary-vault",
|
||||
description: "Encrypts primary AWS Backup recovery points (us-east-1)",
|
||||
enableKeyRotation: true,
|
||||
removalPolicy: cdk.RemovalPolicy.RETAIN,
|
||||
});
|
||||
|
||||
// The L2 BackupVault does NOT grant the backup service use of a customer
|
||||
// CMK; the default key policy only delegates to account IAM. Grant
|
||||
// backup.amazonaws.com the minimum KMS actions (incl. CreateGrant for
|
||||
// RDS/EBS recovery points) so backup jobs can write to this vault.
|
||||
vaultKey.addToResourcePolicy(
|
||||
new iam.PolicyStatement({
|
||||
sid: "AllowAwsBackupUseOfTheKey",
|
||||
principals: [new iam.ServicePrincipal("backup.amazonaws.com")],
|
||||
// Action set matches AWS's documented Backup vault-key policy; scoped
|
||||
// to this account so only this account's Backup service can use it.
|
||||
actions: [
|
||||
"kms:Decrypt",
|
||||
"kms:GenerateDataKey",
|
||||
"kms:GenerateDataKeyWithoutPlaintext",
|
||||
"kms:ReEncrypt*",
|
||||
"kms:DescribeKey",
|
||||
],
|
||||
resources: ["*"],
|
||||
conditions: { StringEquals: { "aws:SourceAccount": this.account } },
|
||||
})
|
||||
);
|
||||
vaultKey.addToResourcePolicy(
|
||||
new iam.PolicyStatement({
|
||||
sid: "AllowAwsBackupCreateGrant",
|
||||
principals: [new iam.ServicePrincipal("backup.amazonaws.com")],
|
||||
actions: ["kms:CreateGrant"],
|
||||
resources: ["*"],
|
||||
conditions: {
|
||||
Bool: { "kms:GrantIsForAWSResource": "true" },
|
||||
StringEquals: { "aws:SourceAccount": this.account },
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
// Primary vault is intentionally NOT locked — it is the working copy; the
|
||||
// offsite vault carries the immutability guarantee.
|
||||
const primaryVault = new backup.BackupVault(this, "PrimaryVault", {
|
||||
backupVaultName: "seahaven-primary",
|
||||
encryptionKey: vaultKey,
|
||||
removalPolicy: cdk.RemovalPolicy.RETAIN,
|
||||
});
|
||||
|
||||
// Cross-region copy destination, referenced by literal ARN (the offsite
|
||||
// stack is in another region; a literal ARN avoids crossRegionReferences /
|
||||
// SSM exports). Stack ordering is enforced via addDependency in bin/app.ts.
|
||||
const offsiteVault = backup.BackupVault.fromBackupVaultArn(
|
||||
this,
|
||||
"OffsiteVaultRef",
|
||||
`arn:aws:backup:us-west-2:${this.account}:backup-vault:seahaven-offsite`
|
||||
);
|
||||
|
||||
// AWS Backup service role. Explicit (not auto-generated) because S3 backup
|
||||
// needs the S3-specific managed policy on top of the standard backup one.
|
||||
// Least-privilege: BACKUP + S3-backup only. Restore policies
|
||||
// (AWSBackupServiceRolePolicyForRestores / ...ForS3Restore) and
|
||||
// BackupSelection allowRestores are intentionally NOT granted — restores
|
||||
// are a deliberate, audited action and will get their own scoped role/path
|
||||
// once a restore-test process exists (cross-review F-1/F-2). A known role
|
||||
// name lets the deploy role's iam:PassRole be scoped to this exact ARN.
|
||||
// NOTE: creating this role is an IAM change → Sea Haven cross-review gate.
|
||||
const backupRole = new iam.Role(this, "BackupRole", {
|
||||
roleName: "seahaven-backup-service-role",
|
||||
assumedBy: new iam.ServicePrincipal("backup.amazonaws.com"),
|
||||
description: "AWS Backup service role (backup-only) for seahaven-primary",
|
||||
managedPolicies: [
|
||||
iam.ManagedPolicy.fromAwsManagedPolicyName(
|
||||
"service-role/AWSBackupServiceRolePolicyForBackup"
|
||||
),
|
||||
iam.ManagedPolicy.fromAwsManagedPolicyName(
|
||||
"AWSBackupServiceRolePolicyForS3Backup"
|
||||
),
|
||||
],
|
||||
});
|
||||
|
||||
// Daily backup → primary vault (35d), cross-region copy → offsite (90d).
|
||||
const plan = new backup.BackupPlan(this, "Plan", {
|
||||
backupPlanName: "seahaven-critical-daily",
|
||||
backupVault: primaryVault,
|
||||
backupPlanRules: [
|
||||
new backup.BackupPlanRule({
|
||||
ruleName: "daily-crr-offsite",
|
||||
backupVault: primaryVault,
|
||||
// 06:00 UTC — offset from the file-share DLM run.
|
||||
scheduleExpression: events.Schedule.cron({ hour: "6", minute: "0" }),
|
||||
startWindow: cdk.Duration.hours(1),
|
||||
completionWindow: cdk.Duration.hours(6),
|
||||
deleteAfter: cdk.Duration.days(35),
|
||||
copyActions: [
|
||||
{
|
||||
destinationBackupVault: offsiteVault,
|
||||
deleteAfter: cdk.Duration.days(90),
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
// Phase-1 critical set, by explicit ARN (identifiers verified against the
|
||||
// live account 2026-05-29).
|
||||
plan.addSelection("CriticalResources", {
|
||||
backupSelectionName: "critical-data",
|
||||
role: backupRole,
|
||||
// allowRestores omitted (defaults false) — backup-only, see role comment.
|
||||
resources: [
|
||||
// RDS
|
||||
backup.BackupResource.fromArn(
|
||||
`arn:aws:rds:us-east-1:${this.account}:db:database-1`
|
||||
),
|
||||
backup.BackupResource.fromArn(
|
||||
`arn:aws:rds:us-east-1:${this.account}:db:proposal-system-db`
|
||||
),
|
||||
// DynamoDB (financial)
|
||||
backup.BackupResource.fromArn(
|
||||
`arn:aws:dynamodb:us-east-1:${this.account}:table/PaymentsDashboard`
|
||||
),
|
||||
backup.BackupResource.fromArn(
|
||||
`arn:aws:dynamodb:us-east-1:${this.account}:table/purchase-orders`
|
||||
),
|
||||
// S3 (single-copy critical buckets) — versioning required (see header)
|
||||
backup.BackupResource.fromArn("arn:aws:s3:::accounting.seahaven.com"),
|
||||
backup.BackupResource.fromArn(
|
||||
"arn:aws:s3:::seahaven-payments-csv-328440206208"
|
||||
),
|
||||
backup.BackupResource.fromArn(
|
||||
"arn:aws:s3:::google-workspace-seahavenind.com"
|
||||
),
|
||||
],
|
||||
});
|
||||
|
||||
cdk.Tags.of(this).add("Project", "account-baseline");
|
||||
cdk.Tags.of(this).add("Owner", "adam@seahavenind.com");
|
||||
cdk.Tags.of(this).add("Environment", "prod");
|
||||
cdk.Tags.of(this).add("ManagedBy", "cdk");
|
||||
|
||||
new cdk.CfnOutput(this, "PrimaryVaultName", { value: "seahaven-primary" });
|
||||
new cdk.CfnOutput(this, "PrimaryVaultKmsKeyArn", { value: vaultKey.keyArn });
|
||||
new cdk.CfnOutput(this, "BackupPlanId", { value: plan.backupPlanId });
|
||||
new cdk.CfnOutput(this, "BackupRoleArn", { value: backupRole.roleArn });
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue