Skip to content

Conversation

@tarunb12
Copy link
Contributor

@tarunb12 tarunb12 commented Nov 18, 2025

Issue

aws/aws-cdk-rfcs#789

Reason for this change

This change adds a new alpha module for EC2 Image Builder L2 Constructs (@aws-cdk/aws-imagebuilder-alpha), as outlined in aws/aws-cdk-rfcs#789. This PR specifically implements the ImageRecipe construct.

Description of changes

This change implements the ImageRecipe construct, which is a higher-level construct of CfnImageRecipe.

Example

const userData = ec2.UserData.forLinux();
userData.addS3DownloadCommand({
  bucket: s3.Bucket.fromBucketName(this, 'Bucket', `test-bucket-${this.account}`),
  bucketKey: 'test-key',
  localFile: 's3-executable.sh',
});
userData.addExecuteFileCommand({ filePath: 's3-executable.sh' });
userData.addCommands('User Data complete!');

const imageRecipe = new imagebuilder.ImageRecipe(this, 'ImageRecipe', {
  imageRecipeName: 'test-image-recipe',
  imageRecipeVersion: '1.0.0',
  description: 'An Image Recipe',
  // Use an AL2023 base image
  baseImage: imagebuilder.BaseImage.fromSsmParameterName(
    this,
    'MachineImage',
    '/aws/service/ami-amazon-linux-latest/al2023-ami-minimal-kernel-default-x86_64',
  ),
  // Use an AWS-managed component, shared component, self-owned component with parameters, and marketplace component
  components: [
    {
      component: imagebuilder.AwsManagedComponent.updateOS(this, 'UpdateOS', {
        platform: imagebuilder.Platform.Linux,
      }),
    },
    {
      component: imagebuilder.Component.fromComponentArn(
        this,
        'ComplianceTestComponent',
        `arn:${this.partition}:imagebuilder:${this.region}:123456789012:component/compliance-test/2025.x.x.x`,
      ),
    },
    {
      component: imagebuilder.AwsMarketplaceComponent.fromAwsMarketplaceComponentAttributes(
        this,
        'MarketplaceComponent',
        {
          name: 'marketplace-component-name',
          marketplaceProductId: '12345678-1234-1234-1234-123456789012',
        },
      ),
    },
    {
      component: imagebuilder.Component.fromComponentAttributes(this, 'CustomComponent', {
        componentName: 'custom-component',
      }),
      parameters: {
        CUSTOM_PARAMETER_KEY: imagebuilder.ComponentParameterValue.fromString('custom-parameter-value'),
      },
    },
  ],
  workingDirectory: '/var/tmp',
  // Optional - retain the SSM agent after the build, and apply custom userdata
  uninstallSsmAgentAfterBuild: false,
  userDataOverride: userData,
  // Optional - attach additional block device to the build instance
  blockDevices: [
    {
      deviceName: '/dev/sda1',
      mappingEnabled: true,
      volume: ec2.BlockDeviceVolume.ebs(50, {
        deleteOnTermination: true,
        iops: 1000,
        volumeType: ec2.EbsDeviceVolumeType.GP3,
        throughput: 1000,
        encrypted: true,
        kmsKey: kms.Key.fromLookup(this, 'VolumeKey', { aliasName: 'alias/volume-encryption-key' }),
      }),
    },
  ],
  // Optional - specify tags to apply to the output AMI
  amiTags: {
    Environment: 'production',
  },
});

Describe any new or updated permissions being added

N/A - new L2 construct in alpha module

Description of how you validated changes

Validated with unit tests and integration tests. Manually verified generated CFN templates as well.

Checklist


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

@github-actions github-actions bot added beginning-contributor [Pilot] contributed between 0-2 PRs to the CDK p2 labels Nov 18, 2025
@aws-cdk-automation aws-cdk-automation requested a review from a team November 18, 2025 08:21
@aws-cdk-automation aws-cdk-automation added the pr/needs-further-review PR requires additional review from our team specialists due to the scope or complexity of changes. label Nov 18, 2025
@tarunb12 tarunb12 marked this pull request as ready for review November 18, 2025 09:01
@ozelalisen ozelalisen self-assigned this Nov 18, 2025
Copy link
Member

@ozelalisen ozelalisen left a comment

Choose a reason for hiding this comment

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

Thanks for considering patterns from previous PRs, left first round of comments

resource: 'image-recipe',
resourceName: `${this.physicalName}/${imageRecipeVersion}`,
});
this.imageRecipeVersion = cdk.Fn.select(2, cdk.Fn.split('/', imageRecipe.attrArn));
Copy link
Member

Choose a reason for hiding this comment

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

This should not be belong here, it was related to fromImageRecipeAttributes method

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah good call. we can actually use .getAtt('Version') here (for some reason, that is not modeled as .attrVersion on the L1)

*
* @default - this is false if the Systems Manager agent is pre-installed on the base image. Otherwise, this is true.
*/
readonly uninstallSsmAgentAfterBuild?: boolean;
Copy link
Member

Choose a reason for hiding this comment

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

What happens if on my baseimage there is not any ssmagent, and I set this as true?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it behaves the same as the default behavior - the output image will not contain the SSM agent

Comment on lines +14 to +17
public static fromString(value: string): ComponentParameterValue {
return new ComponentParameterValue([value]);
}
Copy link
Member

Choose a reason for hiding this comment

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

There can be multiple values but, only one value can be saved via this method

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The API/CFN input is string list, but only one element is supported for now (to support the simple string data type), so that it can be extended once stringList is supported here

/**
* The parameter value for a component parameter
*/
export class ComponentParameterValue {
Copy link
Member

Choose a reason for hiding this comment

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

I do not see any purpose with this class, because parameter value can only be list of strings, so we do not need create an additional parameter value class, this pattern is useful, when value is any that can be number, string, bool.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah - I created it like this so that when Image Builder components support more parameter value data types, we can extend it easily here, rather than having to deprecate the property if we add support for stringLists or booleans for example

Copy link
Member

Choose a reason for hiding this comment

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

So, it is expected that this might be extended for different types?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct - that was the intent of making this class

* @default - no parameters. if the component contains parameters, their default values will be used. otherwise, any
* required parameters that are not included will result in a build failure
*/
readonly parameters?: { [name: string]: ComponentParameterValue };
Copy link
Member

Choose a reason for hiding this comment

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

This can be simplify as:

Suggested change
readonly parameters?: { [name: string]: ComponentParameterValue };
readonly parameters?: { [name: string]: string[] };

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we should keep this as ComponentParameterValue. As noted in the other comment, a string list is accepted in API/CFN, but only one element is supported since only the string data type is supported. When stringList, boolean, etc. is supported, ComponentParameterValue would be the better option here, following CDK design guideline for union data types

@tarunb12 tarunb12 force-pushed the imagebuilder-image-recipe branch from 56b4365 to f41878c Compare November 19, 2025 03:23
@mergify mergify bot dismissed ozelalisen’s stale review November 19, 2025 03:23

Pull request has been modified.

@tarunb12 tarunb12 force-pushed the imagebuilder-image-recipe branch 2 times, most recently from 336df49 to aef71d7 Compare November 19, 2025 03:36
@aws-cdk-automation aws-cdk-automation added the pr/needs-community-review This PR needs a review from a Trusted Community Member or Core Team Member. label Nov 19, 2025
@tarunb12 tarunb12 force-pushed the imagebuilder-image-recipe branch from aef71d7 to 7ca9300 Compare November 19, 2025 04:23
Comment on lines +66 to +71
/**
* Indicates whether the recipe is an Image Recipe
*
* @internal
*/
_isImageRecipe(): this is IImageRecipe;
Copy link
Member

Choose a reason for hiding this comment

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

What is the purpose of this?

Copy link
Contributor Author

@tarunb12 tarunb12 Nov 19, 2025

Choose a reason for hiding this comment

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

We are gonna use this in Image/ImagePipeline constructs. In those constructs, recipe: IRecipeBase will be accepted which can be either an image or container recipe. This method helps us know which recipe, so that we can map it to the correct parameter in the L1 definition. There will be a similar method for IContainerRecipe.

/**
* The parameter value for a component parameter
*/
export class ComponentParameterValue {
Copy link
Member

Choose a reason for hiding this comment

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

So, it is expected that this might be extended for different types?

Comment on lines +195 to +203
/**
* Indicates whether the recipe is an Image Recipe
*
* @internal
*/
public _isImageRecipe(): this is IImageRecipe {
return true;
}
}
Copy link
Member

Choose a reason for hiding this comment

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

What is purpose of this?

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 p2 pr/needs-community-review This PR needs a review from a Trusted Community Member or Core Team Member. pr/needs-further-review PR requires additional review from our team specialists due to the scope or complexity of changes.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants