Skip to content

Conversation

oyiz-michael
Copy link

Custom ECR Repository Support for Docker Image Assets

Issue # (if applicable)

Closes #33228

Reason for this change

Currently, DockerImageAsset only supports using the default ECR repository managed by the CDK synthesizer, and image tags are limited to the asset hash or global synthesizer prefixes. This limitation prevents developers from:

  1. Using existing ECR repositories - Organizations often have pre-existing ECR repositories with specific lifecycle policies, permissions, and governance requirements
  2. Meaningful image tagging - Developers want semantic versioning (e.g., v1.2.3) or branch-based tagging (e.g., main-abc123) instead of just asset hashes
  3. Per-asset tag customization - Different images in the same stack may need different tagging strategies

This has been a highly requested feature with 39+ community upvotes and is marked as P1 priority.

Description of changes

This PR adds three new optional properties to DockerImageAssetOptions:

New Properties:

  • ecrRepository?: ecr.IRepository - Allows specifying a custom ECR repository instead of using the default synthesizer repository
  • imageTag?: string - Enables setting explicit custom tags for Docker images (e.g., 'v1.2.3', 'latest')
  • imageTagPrefix?: string - Allows adding a prefix to the asset hash for image tags (e.g., 'feature-''feature-abc123')

Implementation Details:

1. Three Execution Paths:

  • Custom Repository Path: When ecrRepository is provided, bypasses synthesizer and uses the custom repository directly
  • Custom Tagging Path: When only imageTag or imageTagPrefix is provided, uses default repository with custom tag via helper method
  • Default Path: Maintains existing behavior when no custom properties are specified

2. Tag Precedence Logic:

const finalTag = props.imageTag ?? `${props.imageTagPrefix ?? ''}${assetHash}`;

Priority: imageTag > imageTagPrefix + assetHash > assetHash

3. Enhanced Asset Hash Calculation:

// New properties included in hash for proper cache invalidation
if (props.ecrRepository) { extraHash.ecrRepository = props.ecrRepository.repositoryName; }
if (props.imageTag) { extraHash.imageTag = props.imageTag; }
if (props.imageTagPrefix) { extraHash.imageTagPrefix = props.imageTagPrefix; }

4. Helper Method for Custom Tagging:
addDockerImageAssetWithCustomTag() - Integrates with synthesizer for default repository but overrides tag generation

Code Changes:

Core Implementation (image-asset.ts):

  • Extended DockerImageAssetOptions interface with 3 new optional properties
  • Modified constructor with conditional logic for the three execution paths
  • Added helper method for custom tag synthesis with default repositories
  • Enhanced hash calculation to include new properties

Documentation (README.md):

  • Added comprehensive usage examples for all new features
  • Updated "Publishing images to ECR repositories" section
  • Migration guide from deprecated repositoryName property

Testing:

  • Unit Tests (custom-repository.test.ts): 10 comprehensive tests covering all functionality
  • Integration Tests (integ.custom-repository.ts): Real-world usage examples with CloudFormation outputs
  • Test Fixtures (fixtures/custom-dockerfile/): Complete Docker application for testing

Alternatives Considered:

  1. Global synthesizer configuration - Rejected because it doesn't allow per-asset customization
  2. Separate construct for custom repositories - Rejected because it would break the unified asset experience
  3. Stack-level overrides - Rejected because it's less intuitive than asset-level configuration

Design Decisions:

  1. Optional properties - Ensures 100% backward compatibility
  2. Tag precedence hierarchy - imageTag wins over imageTagPrefix for predictable behavior
  3. Hash invalidation - Include all new properties to ensure proper cache invalidation
  4. Repository responsibility - When using custom repositories, users manage lifecycle and permissions

Describe any new or updated permissions being added

No new IAM permissions are required.

When users specify a custom ECR repository via ecrRepository, they become responsible for ensuring their CDK execution role has appropriate permissions to push to that repository. The implementation uses existing ECR repository methods (repositoryUriForTag) and synthesizer patterns.

Typical permissions needed for custom repositories (user's responsibility):

  • ecr:GetAuthorizationToken
  • ecr:BatchCheckLayerAvailability
  • ecr:GetDownloadUrlForLayer
  • ecr:BatchGetImage
  • ecr:DescribeRepositories
  • ecr:InitiateLayerUpload
  • ecr:UploadLayerPart
  • ecr:CompleteLayerUpload
  • ecr:PutImage

Description of how you validated changes

Unit Testing:

  • 10 comprehensive unit tests in custom-repository.test.ts:
    • Custom ECR repository support
    • Custom image tags with custom repositories
    • Custom tag prefixes with custom repositories
    • Custom tagging with default repositories
    • Asset hash invalidation verification
    • Tag precedence logic (imageTag > imageTagPrefix)
    • Backward compatibility verification

Integration Testing:

  • Real-world integration test (integ.custom-repository.ts):
    • Tests all feature combinations in a realistic CDK app
    • Validates CloudFormation output with exportValue
    • Demonstrates proper repository and tag assignments

Compatibility Testing:

  • Backward compatibility verified:
    • All existing DockerImageAsset usage patterns continue to work
    • No breaking changes to existing interfaces
    • Default behavior unchanged when new properties not used

Logic Testing:

  • Custom JavaScript test suite validated:
    • Tag precedence logic works correctly
    • Hash calculation includes all new properties
    • All execution paths work as intended
    • Edge cases handled properly

Manual Testing:

  • TypeScript compilation - No syntax or type errors
  • Import/export patterns - Match existing AWS CDK conventions
  • Interface compatibility - All existing usage patterns work unchanged

Usage Examples

Basic Custom Repository:

const customRepo = new ecr.Repository(this, 'MyRepo', {
  repositoryName: 'my-app-images'
});

const asset = new DockerImageAsset(this, 'MyAsset', {
  directory: './docker',
  ecrRepository: customRepo,  // Uses custom repository
});
// Result: my-app-images:abc123def

Custom Tags:

const asset = new DockerImageAsset(this, 'MyAsset', {
  directory: './docker',
  imageTag: 'v1.2.3',  // Explicit semantic version
});
// Result: <synthesizer-repo>:v1.2.3

Tag Prefixes:

const asset = new DockerImageAsset(this, 'MyAsset', {
  directory: './docker',
  imageTagPrefix: 'feature-branch-',  // Branch-based tagging
});
// Result: <synthesizer-repo>:feature-branch-abc123def

Combined Usage:

const asset = new DockerImageAsset(this, 'MyAsset', {
  directory: './docker',
  ecrRepository: customRepo,
  imageTag: 'production-v1.2.3',  // Custom repo + explicit tag
});
// Result: my-app-images:production-v1.2.3

Checklist

  • My code adheres to the CONTRIBUTING GUIDE and DESIGN GUIDELINES
  • Added comprehensive unit tests for all new functionality
  • Added integration tests demonstrating real-world usage
  • Updated documentation with usage examples
  • Ensured backward compatibility with existing code
  • Included proper asset hash invalidation
  • Followed existing AWS CDK patterns and conventions
  • No breaking changes introduced

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

…port

This commit implements custom ECR repository support for Docker Image Assets,
addressing issue aws#33228. The implementation adds three new optional properties
to DockerImageAssetOptions:

- ecrRepository: Allows specifying a custom ECR repository instead of using
  the default synthesizer repository
- imageTag: Enables setting explicit custom tags for Docker images
- imageTagPrefix: Allows adding a prefix to the asset hash for image tags

Key Features:
- Full backward compatibility with existing code
- Proper asset hash invalidation when properties change
- Tag precedence: imageTag > imageTagPrefix > assetHash
- Support for custom repositories with custom tagging
- Comprehensive test coverage with unit and integration tests

Implementation Details:
- Three execution paths: custom repository, custom tagging, default behavior
- Helper method for custom tag synthesis with default repositories
- Enhanced hash calculation including new properties
- Updated README with usage examples and migration guide

Tests:
- 10 comprehensive unit tests covering all functionality
- Integration tests with real-world usage examples
- Test fixtures with complete Docker application
- Backward compatibility verification

Resolves: aws#33228
@github-actions github-actions bot added the beginning-contributor [Pilot] contributed between 0-2 PRs to the CDK label Aug 17, 2025
@aws-cdk-automation aws-cdk-automation requested a review from a team August 17, 2025 01:26
@github-actions github-actions bot added effort/medium Medium work item – several days of effort feature-request A feature should be added or improved. p1 labels Aug 17, 2025
Copy link
Collaborator

@aws-cdk-automation aws-cdk-automation left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(This review is outdated)

@oyiz-michael oyiz-michael changed the title feat(aws-ecr-assets): add custom ECR repository and image tagging sup… feat(ecr-assets): add custom ECR repository and image tagging sup… Aug 17, 2025
…port

- Add comprehensive integration test demonstrating custom ECR repository usage
- Include test snapshots for PR linter validation
- Test custom repository, imageTag, and imageTagPrefix features
- Verify precedence handling between imageTag and imageTagPrefix

Addresses aws#33228
@aws-cdk-automation aws-cdk-automation dismissed their stale review August 17, 2025 02:02

✅ Updated pull request passes all PRLinter validations. Dismissing previous PRLinter review.

@Abogical Abogical self-assigned this Sep 2, 2025
Copy link
Member

@Abogical Abogical left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @oyiz-michael , thank you for working on this! There are a few changes needed.

@mergify mergify bot dismissed Abogical’s stale review September 2, 2025 20:05

Pull request has been modified.

@oyiz-michael
Copy link
Author

Hi @oyiz-michael , thank you for working on this! There are a few changes needed.
I have committed the chnages into the branch, are there any other chnages you would like to see?

Co-authored-by: A. Abdel-Rahman <github@abogic.al>
@mergify mergify bot dismissed Abogical’s stale review September 3, 2025 16:00

Pull request has been modified.

- Fix prefer-const ESLint error in image-asset.ts
- Update integration test for custom repository feature
Abogical
Abogical previously approved these changes Sep 4, 2025
Copy link
Member

@Abogical Abogical left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks!

- Remove integ.custom-repository.ts from aws-cdk-lib/aws-ecr-assets/test/
- Remove associated fixtures directory
- Integration test already exists in proper location at @aws-cdk-testing/framework-integ
- Resolves circular dependency: framework-integ -> integ-tests-alpha -> aws-cdk-lib -> integ-tests-alpha
…ion test file

- Rename dependencies-pnpm.ts to dependencies-pnpm.ts.deactivated
- File was commented out but still causing TypeScript parser to detect integ-tests dependency
- Resolves circular dependency: aws-cdk-lib:build -> @aws-cdk/integ-tests-alpha:build -> aws-cdk-lib:build
- Build system now passes without circular dependency errors
@mergify mergify bot dismissed Abogical’s stale review September 6, 2025 10:18

Pull request has been modified.

… values

The integration test snapshot has been updated to reflect the expected behavior
of the custom ECR repository feature. Changes include:
- TaggedCustomAssetTag now shows 'v1.2.3' instead of asset hash
- PrefixedCustomAssetTag shows 'branch-main-{hash}' format
- DefaultRepoCustomTagTag shows 'custom-tag-v2'
- DefaultRepoPrefixedTag shows 'feature-{hash}' format
- PrecedenceTestTag shows 'explicit-wins' (imageTag takes precedence)
- BasicCustomAssetUri updated to reference custom repository

These changes demonstrate that the custom tagging feature is working correctly.
Abogical
Abogical previously approved these changes Oct 9, 2025
When using custom tags with the default repository, the synthesizer returns
imageUri with CDK tokens (like Asset1$1). The previous implementation would
return the base location unchanged when tokens were detected, preventing
custom tags from being applied.

This fix ensures that even when imageUri contains tokens, the custom imageTag
is still properly set in the returned location.
@mergify mergify bot dismissed Abogical’s stale review October 9, 2025 13:08

Pull request has been modified.

Add imageTag and imageTagPrefix properties to DockerImageAssetSource to
enable per-asset custom tags. This allows meaningful Docker image tagging
for security scanning (AWS Inspector) and image management, addressing
the core use case from aws#33228.

The implementation properly extends the CDK synthesizer architecture by:
1. Adding optional imageTag/imageTagPrefix to DockerImageAssetSource interface
2. Updating AssetManifestBuilder to respect per-asset tag overrides
3. Passing custom tags through synthesizer flow for both custom and default repos

This solution follows CDK patterns and avoids breaking changes.
@oyiz-michael oyiz-michael requested a review from a team as a code owner October 9, 2025 13:23
@oyiz-michael oyiz-michael requested a review from Abogical October 9, 2025 14:00
Copy link
Member

@Abogical Abogical left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice refactor editing the manifest builder directly.

Copy link
Member

@Abogical Abogical left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is yet another lint failure:

aws-cdk-lib: /codebuild/output/src2987853504/src/actions-runner/_work/aws-cdk/aws-cdk/packages/aws-cdk-lib/core/lib/stack-synthesizers/asset-manifest-builder.ts
aws-cdk-lib:   66:1  error  Trailing spaces not allowed  no-trailing-spaces

Use yarn lint --fix whenever possible to avoid this. See https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md#linters

@mergify mergify bot dismissed Abogical’s stale review October 9, 2025 16:17

Pull request has been modified.

@oyiz-michael oyiz-michael requested a review from Abogical October 9, 2025 16:26
Copy link
Member

@Abogical Abogical left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lint passes but the integration test doesn't. Following snapshot needs to be updated:

@aws-cdk-testing/framework-integ:   CHANGED    aws-ecr-assets/test/integ.custom-repository 1.021s
@aws-cdk-testing/framework-integ:       Outputs
@aws-cdk-testing/framework-integ:       [~] Output BasicCustomAssetUri: {"Value":{"Fn::Join":["",[{"Fn::Select":[4,{"Fn::Split":[":",{"Fn::GetAtt":["CustomRepo98CFBAD2","Arn"]}]}]},".dkr.ecr.",{"Fn::Select":[3,{"Fn::Split":[":",{"Fn::GetAtt":["CustomRepo98CFBAD2","Arn"]}]}]},".",{"Ref":"AWS::URLSuffix"},"/",{"Ref":"CustomRepo98CFBAD2"},":Asset1$1"]]}} to {"Value":{"Fn::Join":["",[{"Fn::Select":[4,{"Fn::Split":[":",{"Fn::GetAtt":["CustomRepo98CFBAD2","Arn"]}]}]},".dkr.ecr.",{"Fn::Select":[3,{"Fn::Split":[":",{"Fn::GetAtt":["CustomRepo98CFBAD2","Arn"]}]}]},".",{"Ref":"AWS::URLSuffix"},"/",{"Ref":"CustomRepo98CFBAD2"},":2aac91d10652d5f4ee8e2b9135e10afb5c6ca351007e0b0c3109bf754ea46224"]]}}
@aws-cdk-testing/framework-integ:       [~] Output PrefixedCustomAssetTag: {"Value":"branch-main-Asset1$1"} to {"Value":"branch-main-4766dd6ca48737524a7667b729a979f74792c72f39366e460813c9a040303dd3"}
@aws-cdk-testing/framework-integ:       [~] Output DefaultRepoPrefixedTag: {"Value":"feature-Asset1$1"} to {"Value":"feature-Asset2$1"}
@aws-cdk-testing/framework-integ:       
@aws-cdk-testing/framework-integ:

@mergify mergify bot dismissed Abogical’s stale review October 11, 2025 11:40

Pull request has been modified.

@oyiz-michael oyiz-michael requested a review from Abogical October 11, 2025 11:41
…rties

Reverts earlier change that removed ecrRepository, imageTag, and imageTagPrefix
from the asset hash calculation. While these don't directly affect Docker image
content, they need to be included in the hash for consistent asset identification
and to avoid breaking changes in asset naming.
@AdamVD
Copy link
Contributor

AdamVD commented Oct 14, 2025

@oyiz-michael I'll admit I'm asking this without looking much into the code, but how does this work when you're creating the repo in the same application that the image is being built for? My understanding is CDK publishes all assets before the stack deployment even begins, hence the CDK behavior to use a bootstrapped repo that must be there before making any deployments. Again, maybe just a naive view but clearly trying to publish to a repo that hasn't been created yet would be problematic.

@Abogical
Copy link
Member

Abogical commented Oct 15, 2025

@AdamVD

From the PR description, it assumes that there is an ECR repository created outside of the CDK application:

Using existing ECR repositories - Organizations often have pre-existing ECR repositories with specific lifecycle policies, permissions, and governance requirements.

So the use case for this PR is for using an externally created repository before deploying the CDK docker assets.

Copy link
Contributor

@rix0rrr rix0rrr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ECR assets mirror S3 assets by current design.

There is no "push this S3 file to a specific bucket with a specific filename" feature for files; I don't see convincing reasons why ECR assets should be different.

I know the lifetime management concerns are different, and that there are improvements to be made. However, that's a discussion we should have with a good all-around proposal and design first, preferably via RFC, rather than patching in an option here and an option there in PRs.

If your goal is:

  • I want to have an image uploaded a well-known ECR repository with a fixed name (perhaps so that a different app/team can consume it from its well-known location): use cdk-docker-image-deployment
  • I want to clean up resources on a per-app basis: use the app staging synthesizer
  • I want to put images into custom places that already exist and are managed externally to my app: write a custom stack synthesizer
  • I have a good idea for how ECR asset handling should be done (taking into account CLI deployments, pipeline deployments, life cycle management, and repository cleanup): submit an RFC

@AdamVD
Copy link
Contributor

AdamVD commented Oct 15, 2025

@oyiz-michael Thanks. I think the example in the README.md is misleading then if an externally created repo is assumed:

import * as ecr from 'aws-cdk-lib/aws-ecr';

// Custom ECR repository
const customRepo = new ecr.Repository(this, 'MyRepo', {
  repositoryName: 'my-custom-repo'
});

const asset = new DockerImageAsset(this, 'MyAsset', {
  directory: path.join(__dirname, 'my-image'),
  ecrRepository: customRepo,           // Use custom repository
  imageTag: 'v1.2.3',                 // Custom tag
  // OR
  imageTagPrefix: 'feature-branch-',   // Tag prefix + asset hash
});

@rix0rrr I think you pretty much covered the desired use cases, but we should acknowledge:

  • cdk-docker-image-deployment is not first-party CDK (unlike the S3 Deployment construct)
  • app staging synthesizer has remained in alpha

Two reasons I think that contribute to the popularity of the issue referenced from this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

beginning-contributor [Pilot] contributed between 0-2 PRs to the CDK effort/medium Medium work item – several days of effort feature-request A feature should be added or improved. p1

Projects

None yet

Development

Successfully merging this pull request may close these issues.

(aws-ecr-assets): Custom ECR repository, custom name and/or tag

5 participants