Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
32 changes: 23 additions & 9 deletions src/stackset-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@ import {
Names,
Lazy,
FileAssetPackaging,
App,
Resource,
Annotations,
Fn,
} from 'aws-cdk-lib';
import { IBucket } from 'aws-cdk-lib/aws-s3';
import { Bucket, IBucket } from 'aws-cdk-lib/aws-s3';
import { BucketDeployment, Source } from 'aws-cdk-lib/aws-s3-deployment';
import { Construct } from 'constructs';

Expand Down Expand Up @@ -52,6 +51,7 @@ export class StackSetStackSynthesizer extends StackSynthesizer {
*/
readonly assetBucketPrefix?: string;
private bucketDeployments: { [key: string]: AssetBucketDeploymentProperties };
private parentAssetBucket?: IBucket;

/**
* Creates a new StackSetStackSynthesizer.
Expand Down Expand Up @@ -82,16 +82,28 @@ export class StackSetStackSynthesizer extends StackSynthesizer {
throw new Error('Asset filename is undefined');
}

const outdir = App.of(this.boundStack)?.outdir ?? 'cdk.out';
const assetPath = `${outdir}/${asset.fileName}`;
const parentStack = (this.boundStack as StackSetStack)._getParentStack();

// Delegate to the parent stack's synthesizer to handle asset staging
const parentLocation = parentStack.synthesizer.addFileAsset(asset);

// Create a reference to the parent's asset bucket (lazily, once)
if (!this.parentAssetBucket) {
this.parentAssetBucket = Bucket.fromBucketName(
this.boundStack,
'ParentAssetBucket',
parentLocation.bucketName,
);
}

// Use Source.bucket() to reference the asset from S3 (avoids timing issue with local files)
const source = Source.bucket(this.parentAssetBucket, parentLocation.objectKey);

for (const assetBucket of this.assetBuckets) {
const index = this.assetBuckets.indexOf(assetBucket);
const assetDeployment = this.bucketDeployments[assetBucket.bucketName];

if (!assetDeployment.bucketDeployment) {
const parentStack = (this.boundStack as StackSetStack)._getParentStack();

if (!Resource.isOwnedResource(assetDeployment.assetBucket)) {
Annotations.of(parentStack).addWarning('[WARNING] Bucket Policy Permissions cannot be added to' +
' referenced Bucket. Please make sure your bucket has the correct permissions');
Expand All @@ -105,7 +117,7 @@ export class StackSetStackSynthesizer extends StackSynthesizer {
parentStack,
bucketDeploymentConstructName,
{
sources: [Source.asset(assetPath)],
sources: [source],
destinationBucket: assetDeployment.assetBucket,
extract: false,
prune: false,
Expand All @@ -114,11 +126,13 @@ export class StackSetStackSynthesizer extends StackSynthesizer {

assetDeployment.bucketDeployment = bucketDeployment;
} else {
assetDeployment.bucketDeployment.addSource(Source.asset(assetPath));
assetDeployment.bucketDeployment.addSource(source);
}
}

const bucketName = Fn.join('-', [this.assetBucketPrefix, this.boundStack.region]);
// Use Fn.ref('AWS::Region') so the bucket name resolves dynamically at deployment time
// in each target region, not at synthesis time (which would hardcode the parent stack's region)
const bucketName = Fn.join('-', [this.assetBucketPrefix, Fn.ref('AWS::Region')]);

const assetFileBaseName = path.basename(asset.fileName);
const s3Filename = assetFileBaseName.split('.')[1] + '.zip';
Expand Down
11 changes: 9 additions & 2 deletions src/stackset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Lazy,
Names,
Resource,
Stack,
} from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { StackSetStack, fileAssetResourceNames } from './stackset-stack';
Expand Down Expand Up @@ -696,7 +697,13 @@ export class StackSet extends Resource implements IStackSet {
effect: iam.Effect.ALLOW,
actions: ['sts:AssumeRole'],
resources: [
`arn:aws:iam::*:role/${deploymentTypeConfig.executionRoleName ?? 'AWSCloudFormationStackSetExecutionRole'}`,
Stack.of(this).formatArn({
service: 'iam',
region: '',
account: '*',
resource: 'role',
resourceName: deploymentTypeConfig.executionRoleName ?? 'AWSCloudFormationStackSetExecutionRole',
}),
],
}));
}
Expand Down Expand Up @@ -768,7 +775,7 @@ export class StackSet extends Resource implements IStackSet {
const disabledPrincipals: iam.IPrincipal[] = [];
targetConfig.regions.forEach(region => {
if (!ENABLED_REGIONS.includes(region)) {
disabledPrincipals.push(new iam.ServicePrincipal(`cloudformation.${region}.amazonaws.com`));
disabledPrincipals.push(new iam.ServicePrincipal(`cloudformation.${region}.${Stack.of(this).urlSuffix}`));
}
});
if (disabledPrincipals.length > 0) {
Expand Down
2 changes: 1 addition & 1 deletion test/integ.stack-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const app = new App({
postCliContext: {
// I don't know why this is needed, but If I don't have it I get
// an error about undefined not being a list
'@aws-cdk/core:target-partitions': ['aws'],
'@aws-cdk/core:target-partitions': ['aws', 'aws-us-gov'],
},
});

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
{
"version": "35.0.0",
"files": {
"3fb6287214999ddeafa7cd0e3e58bc5144c8678bb720f3b5e45e8fd32f333eb3": {
"e56263bd51a9cda3a5920a2b978d8827ae857776a6807cbe4ac9b2115dfed690": {
"source": {
"path": "asset.3fb6287214999ddeafa7cd0e3e58bc5144c8678bb720f3b5e45e8fd32f333eb3.zip",
"packaging": "file"
"path": "asset.e56263bd51a9cda3a5920a2b978d8827ae857776a6807cbe4ac9b2115dfed690",
"packaging": "zip"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "3fb6287214999ddeafa7cd0e3e58bc5144c8678bb720f3b5e45e8fd32f333eb3.zip",
"objectKey": "e56263bd51a9cda3a5920a2b978d8827ae857776a6807cbe4ac9b2115dfed690.zip",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
},
"9eb41a5505d37607ac419321497a4f8c21cf0ee1f9b4a6b29aa04301aea5c7fd": {
"3fb6287214999ddeafa7cd0e3e58bc5144c8678bb720f3b5e45e8fd32f333eb3": {
"source": {
"path": "asset.9eb41a5505d37607ac419321497a4f8c21cf0ee1f9b4a6b29aa04301aea5c7fd",
"packaging": "zip"
"path": "asset.3fb6287214999ddeafa7cd0e3e58bc5144c8678bb720f3b5e45e8fd32f333eb3.zip",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "9eb41a5505d37607ac419321497a4f8c21cf0ee1f9b4a6b29aa04301aea5c7fd.zip",
"objectKey": "3fb6287214999ddeafa7cd0e3e58bc5144c8678bb720f3b5e45e8fd32f333eb3.zip",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
},
"e56263bd51a9cda3a5920a2b978d8827ae857776a6807cbe4ac9b2115dfed690": {
"9eb41a5505d37607ac419321497a4f8c21cf0ee1f9b4a6b29aa04301aea5c7fd": {
"source": {
"path": "asset.e56263bd51a9cda3a5920a2b978d8827ae857776a6807cbe4ac9b2115dfed690.e56263bd51a9cda3a5920a2b978d8827ae857776a6807cbe4ac9b2115dfed690",
"path": "asset.9eb41a5505d37607ac419321497a4f8c21cf0ee1f9b4a6b29aa04301aea5c7fd",
"packaging": "zip"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "e56263bd51a9cda3a5920a2b978d8827ae857776a6807cbe4ac9b2115dfed690.zip",
"objectKey": "9eb41a5505d37607ac419321497a4f8c21cf0ee1f9b4a6b29aa04301aea5c7fd.zip",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
Expand All @@ -53,15 +53,15 @@
}
}
},
"a042f98f25fe2c784f1eb1f27c3963138d49d3e1913feb936f1a12f20a62349e": {
"304cac1154e09b2add5e34bdfc7918f471e54461f7bb556588868eded3c09498": {
"source": {
"path": "integ-stackset-asset-test.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "a042f98f25fe2c784f1eb1f27c3963138d49d3e1913feb936f1a12f20a62349e.json",
"objectKey": "304cac1154e09b2add5e34bdfc7918f471e54461f7bb556588868eded3c09498.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
Expand Down
Loading