Repository: pulumi/pulumi-cdk
Pull request #257 (2024-11-26 13:57:35)

Reference assets directly
Previously we were generating a physical name for the staging resources (S3 Bucket / ECR Repository) because it was mistakenly assumed that we needed those to be known values for CDK to work. Turns out that we can use `asString` on the Output values and CDK handles it correctly. So this PR does a couple of things:

1. Removes the hard coded resource names and allows Pulumi to generate an autoname.
2. Removes the dependency each stack had on the staging stack. Because we are using Output values to reference the assets, each resource can depend on the individual asset directly. This allows for more resources to be provisioned in parallel. On the `eventbridge-sns` test this had a 20% decrease in deployment time.


Patch:
diff --git a/api-docs/Namespace.synthesizer.md b/api-docs/Namespace.synthesizer.md
index 1891f02..773f95b 100644
--- a/api-docs/Namespace.synthesizer.md
+++ b/api-docs/Namespace.synthesizer.md
@@ -54,15 +54,15 @@ resources and deploy the assets themselves.
 
 ###### Defined in
 
-[synthesizer.ts:231](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L231)
+[synthesizer.ts:217](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L217)
 
 #### Properties
 
 | Property | Modifier | Type | Default value | Description | Overrides | Defined in |
 | ------ | ------ | ------ | ------ | ------ | ------ | ------ |
-| `stagingBucket?` | `public` | `BucketV2` | `undefined` | The app-scoped, environment-keyed staging bucket. | - | [synthesizer.ts:158](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L158) |
-| `stagingRepos` | `readonly` | `Record`\<`string`, `Repository`\> | `{}` | The app-scoped, environment-keyed ecr repositories associated with this app. | - | [synthesizer.ts:163](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L163) |
-| `stagingStack` | `readonly` | `CdkConstruct` | `undefined` | The Pulumi ComponentResource wrapper which contains all of the staging resources. This can be added to the `dependsOn` of the main stack to ensure the staging assets are created first | [`PulumiSynthesizerBase`](Namespace.synthesizer.md#pulumisynthesizerbase).`stagingStack` | [synthesizer.ts:153](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L153) |
+| `stagingBucket?` | `public` | `BucketV2` | `undefined` | The app-scoped, environment-keyed staging bucket. | - | [synthesizer.ts:159](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L159) |
+| `stagingRepos` | `readonly` | `Record`\<`string`, `Repository`\> | `{}` | The app-scoped, environment-keyed ecr repositories associated with this app. | - | [synthesizer.ts:164](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L164) |
+| `stagingStack` | `readonly` | `CdkConstruct` | `undefined` | The Pulumi ComponentResource wrapper which contains all of the staging resources. This can be added to the `dependsOn` of the main stack to ensure the staging assets are created first | [`PulumiSynthesizerBase`](Namespace.synthesizer.md#pulumisynthesizerbase).`stagingStack` | [synthesizer.ts:154](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L154) |
 
 #### Methods
 
@@ -82,7 +82,7 @@ Returns the S3 key prefix that will be used for deploy time assets.
 
 ###### Defined in
 
-[synthesizer.ts:114](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L114)
+[synthesizer.ts:115](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L115)
 
 ##### getStagingBucket()
 
@@ -101,7 +101,7 @@ and custom resource responses.
 
 ###### Defined in
 
-[synthesizer.ts:437](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L437)
+[synthesizer.ts:416](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L416)
 
 ***
 
@@ -136,7 +136,7 @@ creates Pulumi resources then you should extend this class.
 
 | Property | Modifier | Type | Description | Defined in |
 | ------ | ------ | ------ | ------ | ------ |
-| `stagingStack` | `abstract` | `CdkConstruct` | The Pulumi ComponentResource wrapper which contains all of the staging resources. This can be added to the `dependsOn` of the main stack to ensure the staging assets are created first | [synthesizer.ts:103](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L103) |
+| `stagingStack` | `abstract` | `CdkConstruct` | The Pulumi ComponentResource wrapper which contains all of the staging resources. This can be added to the `dependsOn` of the main stack to ensure the staging assets are created first | [synthesizer.ts:104](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L104) |
 
 #### Methods
 
@@ -152,7 +152,7 @@ Returns the S3 key prefix that will be used for deploy time assets.
 
 ###### Defined in
 
-[synthesizer.ts:114](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L114)
+[synthesizer.ts:115](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L115)
 
 ##### getStagingBucket()
 
@@ -167,7 +167,7 @@ and custom resource responses.
 
 ###### Defined in
 
-[synthesizer.ts:109](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L109)
+[synthesizer.ts:110](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L110)
 
 ## Interfaces
 
@@ -177,10 +177,10 @@ and custom resource responses.
 
 | Property | Modifier | Type | Description | Defined in |
 | ------ | ------ | ------ | ------ | ------ |
-| `appId` | `readonly` | `string` | A unique identifier for the application that the staging stack belongs to. This identifier will be used in the name of staging resources created for this application, and should be unique across CDK apps. The identifier should include lowercase characters, numbers, periods (.) and dashes ('-') only and have a maximum of 17 characters. | [synthesizer.ts:28](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L28) |
-| `autoDeleteStagingAssets?` | `readonly` | `boolean` | Auto deletes objects in the staging S3 bucket and images in the staging ECR repositories. This will also delete the S3 buckets and ECR repositories themselves when all objects / images are removed. **Default** `true` | [synthesizer.ts:73](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L73) |
-| `deployTimeFileAssetLifetime?` | `readonly` | `Duration` | The lifetime for deploy time file assets. Assets that are only necessary at deployment time (for instance, CloudFormation templates and Lambda source code bundles) will be automatically deleted after this many days. Assets that may be read from the staging bucket during your application's run time will not be deleted. Set this to the length of time you wish to be able to roll back to previous versions of your application without having to do a new `cdk synth` and re-upload of assets. **Default** `- Duration.days(30)` | [synthesizer.ts:52](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L52) |
-| `imageAssetVersionCount?` | `readonly` | `number` | The maximum number of image versions to store in a repository. Previous versions of an image can be stored for rollback purposes. Once a repository has more than 3 image versions stored, the oldest version will be discarded. This allows for sensible garbage collection while maintaining a few previous versions for rollback scenarios. **Default** `- up to 3 versions stored` | [synthesizer.ts:85](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L85) |
-| `parent?` | `readonly` | `Resource` | The parent resource for any Pulumi resources created by the Synthesizer | [synthesizer.ts:90](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L90) |
-| `stagingBucketName?` | `readonly` | `string` | Explicit name for the staging bucket **Default** `- a well-known name unique to this app/env.` | [synthesizer.ts:35](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L35) |
-| `stagingStackNamePrefix?` | `readonly` | `string` | Specify a custom prefix to be used as the staging stack name and construct ID. The prefix will be appended before the appId, which is required to be part of the stack name and construct ID to ensure uniqueness. **Default** `'staging-stack'` | [synthesizer.ts:62](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L62) |
+| `appId` | `readonly` | `string` | A unique identifier for the application that the staging stack belongs to. This identifier will be used in the name of staging resources created for this application, and should be unique across CDK apps. The identifier should include lowercase characters, numbers, periods (.) and dashes ('-') only and have a maximum of 17 characters. | [synthesizer.ts:29](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L29) |
+| `autoDeleteStagingAssets?` | `readonly` | `boolean` | Auto deletes objects in the staging S3 bucket and images in the staging ECR repositories. This will also delete the S3 buckets and ECR repositories themselves when all objects / images are removed. **Default** `true` | [synthesizer.ts:74](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L74) |
+| `deployTimeFileAssetLifetime?` | `readonly` | `Duration` | The lifetime for deploy time file assets. Assets that are only necessary at deployment time (for instance, CloudFormation templates and Lambda source code bundles) will be automatically deleted after this many days. Assets that may be read from the staging bucket during your application's run time will not be deleted. Set this to the length of time you wish to be able to roll back to previous versions of your application without having to do a new `cdk synth` and re-upload of assets. **Default** `- Duration.days(30)` | [synthesizer.ts:53](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L53) |
+| `imageAssetVersionCount?` | `readonly` | `number` | The maximum number of image versions to store in a repository. Previous versions of an image can be stored for rollback purposes. Once a repository has more than 3 image versions stored, the oldest version will be discarded. This allows for sensible garbage collection while maintaining a few previous versions for rollback scenarios. **Default** `- up to 3 versions stored` | [synthesizer.ts:86](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L86) |
+| `parent?` | `readonly` | `Resource` | The parent resource for any Pulumi resources created by the Synthesizer | [synthesizer.ts:91](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L91) |
+| `stagingBucketName?` | `readonly` | `string` | Explicit name for the staging bucket **Default** `- a well-known name unique to this app/env.` | [synthesizer.ts:36](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L36) |
+| `stagingStackNamePrefix?` | `readonly` | `string` | Specify a custom prefix to be used as the staging stack name and construct ID. The prefix will be appended before the appId, which is required to be part of the stack name and construct ID to ensure uniqueness. **Default** `'staging-stack'` | [synthesizer.ts:63](https://github.com/pulumi/pulumi-cdk/blob/main/src/synthesizer.ts#L63) |
diff --git a/examples/s3-object-lambda/src/s3-object-lambda-stack.ts b/examples/s3-object-lambda/src/s3-object-lambda-stack.ts
index c4585af..ae38392 100644
--- a/examples/s3-object-lambda/src/s3-object-lambda-stack.ts
+++ b/examples/s3-object-lambda/src/s3-object-lambda-stack.ts
@@ -8,7 +8,7 @@ import * as s3 from 'aws-cdk-lib/aws-s3';
 import * as s3ObjectLambda from 'aws-cdk-lib/aws-s3objectlambda';
 
 // configurable variables
-const S3_ACCESS_POINT_NAME = 'example-test-ap';
+const S3_ACCESS_POINT_NAME = 'cdk-example-test-ap';
 const OBJECT_LAMBDA_ACCESS_POINT_NAME = 's3-object-lambda-ap';
 
 export class S3ObjectLambdaStack extends pulumicdk.Stack {
diff --git a/src/converters/app-converter.ts b/src/converters/app-converter.ts
index f774a66..696f32f 100644
--- a/src/converters/app-converter.ts
+++ b/src/converters/app-converter.ts
@@ -238,7 +238,6 @@ export class StackConverter extends ArtifactConverter implements intrinsics.Intr
 
     private stackDependsOn(dependencies: Set<ArtifactConverter>): pulumi.Resource[] {
         const dependsOn: pulumi.Resource[] = [];
-        dependsOn.push(...this.app.dependencies);
         for (const d of dependencies) {
             if (d instanceof StackConverter) {
                 dependsOn.push(d.stackResource);
diff --git a/src/synthesizer.ts b/src/synthesizer.ts
index 787b18b..11ee337 100644
--- a/src/synthesizer.ts
+++ b/src/synthesizer.ts
@@ -2,6 +2,7 @@ import * as path from 'path';
 import * as docker from '@pulumi/docker-build';
 import { NetworkMode } from '@pulumi/docker-build';
 import * as pulumi from '@pulumi/pulumi';
+import * as ccapi from '@pulumi/aws-native';
 import * as cdk from 'aws-cdk-lib/core';
 import { translateCfnTokenToAssetToken } from 'aws-cdk-lib/core/lib/helpers-internal';
 import * as aws from '@pulumi/aws';
@@ -171,15 +172,10 @@ export class PulumiSynthesizer extends PulumiSynthesizerBase implements cdk.IReu
     private readonly autoDeleteStagingAssets: boolean;
     private readonly imageAssetVersionCount: number;
 
-    /**
-     * The final CDK name of the asset S3 bucket. This may contain CDK tokens
-     */
-    private cdkBucketName?: string;
-
     /**
      * The final Pulumi name of the asset S3 bucket
      */
-    private pulumiBucketName?: string | pulumi.Output<string>;
+    private pulumiBucketLogicalId: string;
 
     /**
      * The region from the pulumi provider
@@ -187,19 +183,9 @@ export class PulumiSynthesizer extends PulumiSynthesizerBase implements cdk.IReu
     private readonly pulumiRegion: pulumi.Output<string>;
 
     /**
-     * The account id from the pulumi provider
-     */
-    private readonly pulumiAccount: pulumi.Output<string>;
-
-    /**
-     * The accountId which may contain CDK tokens
+     * The url suffix for the S3 bucket URL
      */
-    private cdkAccount?: string;
-
-    /**
-     * The region that may contain CDK tokens
-     */
-    private cdkRegion?: string;
+    private readonly urlSuffix: pulumi.Output<string>;
 
     /**
      * The resources that any file assets need to depend on
@@ -219,7 +205,7 @@ export class PulumiSynthesizer extends PulumiSynthesizerBase implements cdk.IReu
      * The same asset could be registered multiple times, but we
      * only want to upload it a single time
      */
-    private readonly seenFileAssets = new Set<string>();
+    private readonly seenFileAssets = new Map<string, aws.s3.BucketObjectv2>();
 
     /**
      * Map of `${assetName}:${assetHash}` to docker.Image that have already been created.
@@ -236,10 +222,10 @@ export class PulumiSynthesizer extends PulumiSynthesizerBase implements cdk.IReu
         this.appId = this.validateAppId(props.appId);
         this.imageAssetVersionCount = props.imageAssetVersionCount ?? 3;
 
-        const account = aws.getCallerIdentity({}, { parent: props.parent }).then((id) => id.accountId);
-        this.pulumiAccount = pulumi.output(account);
-        const region = aws.getRegion({}, { parent: props.parent }).then((r) => r.name);
-        this.pulumiRegion = pulumi.output(region);
+        this.pulumiRegion = aws.getRegionOutput({}, { parent: props.parent }).name;
+        this.urlSuffix = ccapi.getUrlSuffixOutput({ parent: props.parent }).urlSuffix;
+        this.pulumiBucketLogicalId = this._stagingBucketName ?? `pulumi-cdk-${this.appId}-staging`;
+
         const id = `${stackPrefix}-${this.appId}`;
         // create a wrapper component resource that we can depend on
         this.stagingStack = new CdkConstruct(id, 'StagingStack', { parent: props.parent });
@@ -285,7 +271,6 @@ export class PulumiSynthesizer extends PulumiSynthesizerBase implements cdk.IReu
             const repo = new aws.ecr.Repository(
                 repoName,
                 {
-                    name: repoName,
                     // prevents you from pushing an image with a tag that already exists in the repository
                     // @see https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-tag-mutability.html
                     imageTagMutability: 'IMMUTABLE',
@@ -330,16 +315,10 @@ export class PulumiSynthesizer extends PulumiSynthesizerBase implements cdk.IReu
      * CDK application.
      */
     private getCreateBucket(): aws.s3.BucketV2 {
-        // The pulumi resources can use the actual output values for account/region
-        this.pulumiBucketName =
-            this._stagingBucketName ??
-            pulumi.interpolate`pulumi-cdk-${this.appId}-staging-${this.pulumiAccount}-${this.pulumiRegion}`;
-
         if (!this.stagingBucket) {
             this.stagingBucket = new aws.s3.BucketV2(
-                'pulumi-cdk-staging-bucket',
+                this.pulumiBucketLogicalId,
                 {
-                    bucket: this.pulumiBucketName,
                     forceDestroy: this.autoDeleteStagingAssets,
                 },
                 {
@@ -349,7 +328,7 @@ export class PulumiSynthesizer extends PulumiSynthesizerBase implements cdk.IReu
             );
 
             const encryption = new aws.s3.BucketServerSideEncryptionConfigurationV2(
-                'staging-bucket-encryption',
+                `${this.pulumiBucketLogicalId}-encryption`,
                 {
                     bucket: this.stagingBucket.bucket,
                     rules: [
@@ -365,7 +344,7 @@ export class PulumiSynthesizer extends PulumiSynthesizerBase implements cdk.IReu
 
             // Many AWS account safety checkers will complain when buckets aren't versioned
             const versioning = new aws.s3.BucketVersioningV2(
-                'staging-bucket-versioning',
+                `${this.pulumiBucketLogicalId}-versioning`,
                 {
                     bucket: this.stagingBucket.bucket,
                     versioningConfiguration: {
@@ -376,7 +355,7 @@ export class PulumiSynthesizer extends PulumiSynthesizerBase implements cdk.IReu
             );
 
             const lifecycle = new aws.s3.BucketLifecycleConfigurationV2(
-                'staging-bucket-lifecycle',
+                `${this.pulumiBucketLogicalId}-lifecycle`,
                 {
                     bucket: this.stagingBucket.bucket,
                     rules: [
@@ -422,7 +401,7 @@ export class PulumiSynthesizer extends PulumiSynthesizerBase implements cdk.IReu
                 ],
             });
             const policy = new aws.s3.BucketPolicy(
-                'staging-bucket-policy',
+                `${this.pulumiBucketLogicalId}-policy`,
                 {
                     bucket: this.stagingBucket.bucket,
                     policy: policyDoc,
@@ -477,8 +456,6 @@ export class PulumiSynthesizer extends PulumiSynthesizerBase implements cdk.IReu
             throw new Error(`Stack ${stack.stackName} must be created within an App`);
         }
         this.outdir = app.assetOutdir;
-        this.cdkRegion = stack.region;
-        this.cdkAccount = stack.account;
     }
 
     /**
@@ -490,15 +467,12 @@ export class PulumiSynthesizer extends PulumiSynthesizerBase implements cdk.IReu
      * @hidden
      */
     public addFileAsset(asset: cdk.FileAssetSource): cdk.FileAssetLocation {
-        assertBound(this.cdkAccount);
-        assertBound(this.cdkRegion);
-        // The name that CDK uses needs to include CDK intrinsics so we use the CDK account/region
-        this.cdkBucketName =
-            this._stagingBucketName ?? `pulumi-cdk-${this.appId}-staging-${this.cdkAccount}-${this.cdkRegion}`;
         if (asset.fileName === this.boundStack.templateFile) {
+            // This isn't going to be used so the actual bucketName doesn't need to be correct
+            // We won't be uploading the template to S3
             return this.cloudFormationLocationFromFileAsset(
                 this.assetManifest.defaultAddFileAsset(this.boundStack, asset, {
-                    bucketName: translateCfnTokenToAssetToken(this.cdkBucketName),
+                    bucketName: translateCfnTokenToAssetToken(this.pulumiBucketLogicalId),
                     bucketPrefix: asset.deployTime ? DEPLOY_TIME_PREFIX : undefined,
                 }),
             );
@@ -511,35 +485,56 @@ export class PulumiSynthesizer extends PulumiSynthesizerBase implements cdk.IReu
         }
 
         const location = this.assetManifest.defaultAddFileAsset(this.boundStack, asset, {
-            bucketName: translateCfnTokenToAssetToken(this.cdkBucketName),
+            // this can't contain Output values so just use the LogicalId. This Information
+            // is just for debugging purposes so the correct value isn't necessary
+            bucketName: this.pulumiBucketLogicalId,
             bucketPrefix: asset.deployTime ? DEPLOY_TIME_PREFIX : undefined,
         });
 
         // Assets can be registered multiple times, but we should only create the resource once
         if (this.seenFileAssets.has(asset.sourceHash)) {
-            return this.cloudFormationLocationFromFileAsset(location);
-        }
-        this.seenFileAssets.add(asset.sourceHash);
-
-        // Don't upload the CloudFormation template
-        if (asset.fileName !== this.boundStack.templateFile) {
-            const assetFile = path.join(this.outdir, asset.fileName);
-            const outputPath =
-                asset.packaging === cdk.FileAssetPackaging.ZIP_DIRECTORY
-                    ? zipDirectory(assetFile, assetFile + '.zip')
-                    : assetFile;
-
-            new aws.s3.BucketObjectv2(
-                `${this.stagingStack.name}/${asset.sourceHash}`,
-                {
-                    source: outputPath,
-                    bucket: stagingBucket.bucket,
-                    key: location.objectKey,
-                },
-                { parent: this.stagingStack, dependsOn: this.fileDependencies },
-            );
+            return this.locationFromFileAsset(asset.sourceHash);
         }
-        return this.cloudFormationLocationFromFileAsset(location);
+
+        const assetFile = path.join(this.outdir, asset.fileName);
+        const outputPath =
+            asset.packaging === cdk.FileAssetPackaging.ZIP_DIRECTORY
+                ? zipDirectory(assetFile, assetFile + '.zip')
+                : assetFile;
+
+        const object = new aws.s3.BucketObjectv2(
+            `${this.stagingStack.name}/${asset.sourceHash}`,
+            {
+                source: outputPath,
+                bucket: stagingBucket.bucket,
+                key: location.objectKey,
+            },
+            {
+                parent: this.stagingStack,
+                dependsOn: this.fileDependencies,
+                // We have lifecycle policies on the bucket to handle
+                // object deletion. If the asset hash changes and we upload
+                // a new object we don't want to necessarily delete the old one
+                retainOnDelete: true,
+            },
+        );
+        this.seenFileAssets.set(asset.sourceHash, object);
+        return this.locationFromFileAsset(asset.sourceHash);
+    }
+
+    private locationFromFileAsset(assetSourceHash: string): cdk.FileAssetLocation {
+        assertBound(this.stagingBucket);
+        const fileAsset = this.seenFileAssets.get(assetSourceHash)!;
+        const httpUrl = asString(
+            pulumi.interpolate`https://s3.${this.pulumiRegion}.${this.urlSuffix}/${this.stagingBucket.bucket}/${fileAsset.key}`,
+        );
+        const s3ObjectUrl = asString(pulumi.interpolate`s3://${this.stagingBucket.bucket}/${fileAsset.key}`);
+        return {
+            bucketName: asString(this.stagingBucket.bucket),
+            objectKey: asString(fileAsset.key),
+            s3ObjectUrl: s3ObjectUrl,
+            httpUrl,
+        };
     }
 
     /**
@@ -596,7 +591,7 @@ export class PulumiSynthesizer extends PulumiSynthesizerBase implements cdk.IReu
         // Assets can be registered multiple times, but we should only create the resource once
         if (this.seenImageAssets.has(assetKey)) {
             return {
-                repositoryName: repoName,
+                repositoryName: asString(repo.name),
                 imageUri: asString(this.seenImageAssets.get(assetKey)!.ref),
             };
         }
@@ -651,7 +646,7 @@ export class PulumiSynthesizer extends PulumiSynthesizerBase implements cdk.IReu
         this.seenImageAssets.set(assetKey, image);
 
         return {
-            repositoryName: repoName,
+            repositoryName: asString(repo.name),
             imageUri: asString(image.ref),
         };
     }


Test patch:
diff --git a/tests/mocks.ts b/tests/mocks.ts
index eb590d7..56f1a19 100644
--- a/tests/mocks.ts
+++ b/tests/mocks.ts
@@ -158,16 +158,17 @@ export function setMocks(resources?: MockResourceArgs[]) {
                             ref: args.inputs.tags[0] + '@sha256:abcdefghijk1023',
                         },
                     };
-                case 'aws:ecr/repository:Repository':
+                case 'aws:ecr/repository:Repository': {
                     resources?.push(args);
                     return {
-                        id: args.inputs.name + '_id',
+                        id: args.name + '_id',
                         state: {
                             ...args.inputs,
                             arn: args.name + '_arn',
-                            repositoryUrl: '12345678910.dkr.ecr.us-east-1.amazonaws.com/' + args.inputs.name,
+                            repositoryUrl: '12345678910.dkr.ecr.us-east-1.amazonaws.com/' + args.name,
                         },
                     };
+                }
                 case 'aws-native:cloudformation:CustomResourceEmulator':
                     resources?.push(args);
                     return {
diff --git a/tests/synthesizer.test.ts b/tests/synthesizer.test.ts
index 4539068..bd9da1a 100644
--- a/tests/synthesizer.test.ts
+++ b/tests/synthesizer.test.ts
@@ -64,23 +64,23 @@ describe('Synthesizer File Assets', () => {
                     type: 'cdk:construct:StagingStack',
                 }),
                 expect.objectContaining({
-                    name: 'pulumi-cdk-staging-bucket',
+                    name: 'pulumi-cdk-project-stack-staging',
                     type: 'aws:s3/bucketV2:BucketV2',
                 }),
                 expect.objectContaining({
-                    name: 'staging-bucket-versioning',
+                    name: 'pulumi-cdk-project-stack-staging-versioning',
                     type: 'aws:s3/bucketVersioningV2:BucketVersioningV2',
                 }),
                 expect.objectContaining({
-                    name: 'staging-bucket-encryption',
+                    name: 'pulumi-cdk-project-stack-staging-encryption',
                     type: 'aws:s3/bucketServerSideEncryptionConfigurationV2:BucketServerSideEncryptionConfigurationV2',
                 }),
                 expect.objectContaining({
-                    name: 'staging-bucket-policy',
+                    name: 'pulumi-cdk-project-stack-staging-policy',
                     type: 'aws:s3/bucketPolicy:BucketPolicy',
                 }),
                 expect.objectContaining({
-                    name: 'staging-bucket-lifecycle',
+                    name: 'pulumi-cdk-project-stack-staging-lifecycle',
                     type: 'aws:s3/bucketLifecycleConfigurationV2:BucketLifecycleConfigurationV2',
                 }),
                 expect.objectContaining({