Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
475b79c
Initial commit
DocEight Nov 1, 2025
1d93070
Add cross-account bucket construct
DocEight Nov 1, 2025
02d081f
Add updater construct
DocEight Nov 1, 2025
4a62a89
Template tests for s3 bucket
DocEight Nov 1, 2025
bb4e6b9
S3 manager unit tests
DocEight Nov 2, 2025
24bc02a
Write s3 policy manager lambda code (enabled testing on LocalStack)
DocEight Nov 2, 2025
dfa6c10
Add KMS module (let's modularize and refactor to reduce copy-pasta an…
DocEight Nov 2, 2025
ca8cc05
Fix permissions access and s3 tests
DocEight Nov 2, 2025
8981873
Move common xa access role creation to parent class
DocEight Nov 2, 2025
a44ba8a
Move xa-manager code to abstract parent, implement in S3 manager
DocEight Nov 4, 2025
4a203fd
Standardize naming, clean up imports
DocEight Nov 4, 2025
6cd6985
Refactor KMS manager to use abstract parent, standardize naming, fix …
DocEight Nov 4, 2025
531d511
Bug fixes in consumption check and accessor registration
DocEight Nov 7, 2025
6d79c05
Improve customResource event pattern matching
DocEight Nov 8, 2025
24daa62
Add XA KMS Key tests
DocEight Nov 8, 2025
e55d3f9
Remove silly comments
DocEight Nov 8, 2025
2a79d31
Add KMS Key manager tests
DocEight Nov 8, 2025
409fc97
Define props interfaces (kwargs > positional = fact probably)
DocEight Nov 8, 2025
e53574d
Use prop interfaces in tests
DocEight Nov 8, 2025
52d01d5
Add multi-stack test
DocEight Nov 8, 2025
fdda69e
Add multi-stack test (SSE:KMS)
DocEight Nov 8, 2025
aa813f2
Test customResource call events in multi-stack tests
DocEight Nov 8, 2025
ff5df6f
Prepare to package
DocEight Nov 8, 2025
e736927
Prepare to package
DocEight Nov 8, 2025
9aa35bc
Include Lambda code in package
DocEight Nov 8, 2025
617cae8
Adjust main and types in package.json, ignore dist
DocEight Nov 8, 2025
5512f85
Adjust main and types in package.json, ignore dist
DocEight Nov 8, 2025
b0d01f2
Cfn IDs should be PascalCase
DocEight Nov 8, 2025
bb1d6ab
Cfn IDs should be PascalCase (reloaded - actually PascalCase this time)
DocEight Nov 8, 2025
3864a51
Cfn IDs should be PascalCase (reloaded again - actually PascalCase th…
DocEight Nov 8, 2025
d6c5747
Cfn IDs should be PascalCase (as many times as it takes)
DocEight Nov 8, 2025
3bdcdd3
Bug fixes with Lambda permissions (and code) after CF->S3(SSE:S3) test
DocEight Nov 8, 2025
627a015
Fix empty bucket policy edge case
DocEight Nov 8, 2025
3cdb9d9
Output keyArn to facilitate importing in other stack
DocEight Nov 9, 2025
1d07f83
Fix template tests
DocEight Nov 9, 2025
1da33b3
Initial module code v1.0.0
DocEight Nov 9, 2025
b655409
Add API reference and usage guide to README
DocEight Nov 9, 2025
2a263dc
Rebase with main and add license section
DocEight Nov 9, 2025
3009a5d
Add installation instructions and edit name in package.json
DocEight Nov 9, 2025
78397d6
Newlines in README
DocEight Nov 9, 2025
da5ee9a
Newlines in README
DocEight Nov 9, 2025
bfd3749
Newlines in README
DocEight Nov 9, 2025
5927b8d
Add CI and husky git hooks
DocEight Nov 9, 2025
48ba460
Merge main
DocEight Nov 9, 2025
6ac9fb6
Update husky version in package-lock.json
DocEight Nov 9, 2025
e9f9a7d
Remove promise to render svg for now
DocEight Nov 9, 2025
782707c
Fix package.json
DocEight Nov 9, 2025
bc9a7a0
v1.0.0
DocEight Nov 9, 2025
79af98f
Let's not publish on PRs (need a break, that's a bad oversight)
DocEight Nov 9, 2025
d5bd644
Fix lambda-code bundling and paths
DocEight Nov 9, 2025
196aa2a
Merge branch 'main' into init
DocEight Nov 9, 2025
1906c11
Set package version to 1.0.0
DocEight Nov 9, 2025
c88db02
Fetch tags in CI
DocEight Nov 9, 2025
13caec4
Add comment
DocEight Nov 9, 2025
c92d6ef
Not enforcing tags for now
DocEight Nov 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Publish

on:
push:
branches:
- main

permissions:
id-token: write # Required for OIDC
contents: read

jobs:
publish:
environment: prd
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5

- uses: actions/setup-node@v6
with:
node-version: 22
registry-url: "https://registry.npmjs.org"

- name: Update npm
run: npm install -g npm@latest

- name: Install dependencies
run: npm ci

- name: Run template tests
run: npm run test

- name: Build module
run: npm run build

- name: Build module
run: npm publish --access public
47 changes: 0 additions & 47 deletions .github/workflows/version-check.yml

This file was deleted.

14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
*.js
!jest.config.js
*.d.ts
node_modules

# CDK asset staging directory
.cdk.staging
cdk.out

**/.venv

dist/

*.tgz
Empty file modified .husky/pre-push
100644 → 100755
Empty file.
6 changes: 6 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*.ts
!*.d.ts

# CDK asset staging directory
.cdk.staging
cdk.out
196 changes: 196 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
# xa-cdk

![npm](https://img.shields.io/npm/v/xa-cdk)
![MIT](https://img.shields.io/badge/license-MIT-green)

## Description

This library contains resources that can be configured to be accessible across AWS accounts.
By default, CDK can't update the resource policies of resources in another account, so when
setting up cross-account access between resources, some manual configuration is required.

BUT NO MORE.
No more shall developers be forced to click through dashboard menus (or type CLI commands,
tedious in its own right) to simply access their S3 buckets (et al) from another account!
FREEDOM!!

## Installation

Run:
`npm install @doceight/xa-cdk`

## Usage

Given accessed AWS account with ID: `000000000000`
and accessor AWS account with ID: `111111111111`,
you can automate IAM policy management across AWS accounts/CDK stacks using the following
pattern:

1. In `000000000000`: Deploy the resource(s) to be accessed

```typescript
const key = new xa.CrossAccountKmsKey(this, "xaKey", {
xaAwsIds: ["111111111111"], // AWS account IDs that need access
});

new xa.CrossAccountS3Bucket(this, "XaBucket", {
bucketName: "xa-bucket",
xaAwsIds: ["111111111111"], // AWS account IDs that need access
encryptionKey: key.key,
});
```

2. In `111111111111`: Register the resources that need access

```typescript
// Import the resources to be accessed
const xaBucket = s3.Bucket.fromBucketName(
this,
"XaBucket",
"xa-bucket",
);
const xaKey = kms.Key.fromKeyArn(
this,
"XaKey",
"arn:aws:kms:ap-northeast-1:000000000000:key/00000000-0000-4000-000-000000000000",
);

// Make the distribution itself
this.distribution = new cloudfront.Distribution(this, "Distribution", {
defaultBehavior: {
origin: origins.S3BucketOrigin.withOriginAccessControl(xaBucket),
},
});

// Register the Cloudfront distribution with the S3 Bucket and KMS key here
xa.CrossAccountS3BucketManager.allowCloudfront({
scope: this,
bucketName: xaBucket.bucketName,
distributionId: this.distribution.distributionId,
});
xa.CrossAccountKmsKeyManager.allowCloudfront({
scope: this,
keyId: xaKey.keyId,
distributionId: this.distribution.distributionId,
});
```

3. In `111111111111`: After all accessors have been registered, create a cross-account resource manager for each resource in the other account

```typescript
...

// Create resource managers to update policies on the resources in the other account
new xa.CrossAccountS3BucketManager(this, "XaBucketMgmt", {
xaBucketName: "xa-bucket",
xaAwsId: "000000000000", // AWS account ID of the S3 Bucket
});
new xa.CrossAccountKmsKeyManager(this, "XaKeyMgmt", {
xaKeyId: "00000000-0000-4000-000-000000000000",
xaAwsId: "000000000000", // AWS account ID of the KMS Key
});

```

**Note:** Attempting to register new accessors after this step will result in an error.

## API Reference

(v1.0.0)

- Managed S3 Bucket:
```typescript
CrossAccountS3Bucket(scope: Construct, id: string, {
xaAwsIds: string[],
...s3.BucketProps
})
```
- Managed KMS key:
```typescript
CrossAccountKmsKey(scope: Construct, id: string, {
xaAwsIds: string[],
...kms.KeyProps
})
```
- S3 Bucket Manager:
```typescript
CrossAccountS3BucketManager(scope: Construct, id: string, {
xaBucketName: string,
xaAwsId: string,
managerTimeout?: number = 30,
callerTimeout?: number = 30
})
```
- Register Cloudfront distribution:
```typescript
static allowCloudfront({
scope: Construct,
distributionId: string,
bucketName: string,
actions?: string[] = ["s3:GetObject"]
})
```
- KMS Key Manager:
```typescript
CrossAccountKmsKeyManager(scope: Construct, id: string, {
xaKeyId: string,
xaAwsId: string,
managerTimeout?: number = 30,
callerTimeout?: number = 30
})
```
- Register Cloudfront distribution:
```typescript
static allowCloudfront({
scope: Construct,
distributionId: string,
keyId: string,
actions?: string[] = [
"kms:Decrypt",
"kms:Encrypt",
"kms:GenerateDataKey*",
"kms:DescribeKey"
]
})
```

## Diagram

```mermaid

architecture-beta
group accessor(logos:aws-cloud)[Accessor]
service cf(logos:aws-cloudfront)[CloudFront] in accessor
service s3-mgmt(logos:aws-lambda)[S3 Updater] in accessor
service kms-mgmt(logos:aws-lambda)[KMS Updater] in accessor
junction to-mgmt in accessor

group accessed(logos:aws-cloud)[Accessed]
group s3(logos:aws-s3)[S3] in accessed
service bucket(logos:aws-s3)[Bucket] in s3
service bucket-role(logos:aws-iam)[Bucket Updater Role] in s3
group kms(logos:aws-kms)[KMS] in accessed
service key(logos:aws-kms)[Key] in kms
service key-role(logos:aws-iam)[Key Updater Role] in kms

cf:R -[on update]- L:to-mgmt

to-mgmt:T -- B:s3-mgmt
to-mgmt:B -- T:kms-mgmt

s3-mgmt:R -[assume role]- L:bucket-role
bucket-role:R -[assume role]-> L:bucket

kms-mgmt:R -[update policy]- L:key-role
key-role:R -[update policy]-> L:key

key:T -[encryption]- B:bucket

```

(One of these days GitHub will support logos:aws-icons in its inline Mermaid diagrams, thus
I'm using them now.)

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
10 changes: 10 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export { CrossAccountS3Bucket, CrossAccountS3BucketManager } from "./lib/s3";
export { CrossAccountKmsKey, CrossAccountKmsKeyManager } from "./lib/kms";
export type {
CrossAccountS3BucketProps,
CrossAccountS3BucketManagerProps,
} from "./lib/s3";
export type {
CrossAccountKmsKeyProps,
CrossAccountKmsKeyManagerProps,
} from "./lib/kms";
8 changes: 8 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
testEnvironment: 'node',
roots: ['<rootDir>/test'],
testMatch: ['**/*.test.ts'],
transform: {
'^.+\\.tsx?$': 'ts-jest'
}
};
Loading