Skip to content

Commit 875e0e7

Browse files
authored
feat(imagebuilder-alpha): add support for Container Recipe Construct (#36091)
### 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 ContainerRecipe construct. ### Description of changes This change implements the `ContainerRecipe` construct, which is a higher-level construct of [CfnContainerRecipe](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_imagebuilder.CfnContainerRecipe.html). #### Example ```ts const containerRecipe = new imagebuilder.ContainerRecipe(this, 'ContainerRecipe', { containerRecipeName: 'test-container-recipe', containerRecipeVersion: '1.0.0', description: 'A Container Recipe', baseImage: imagebuilder.BaseImage.fromEcr( this, 'ECRImage', ecr.Repository.fromRepositoryName(this, 'SourceRepository', 'source-repository'), 'latest', ), dockerfile: imagebuilder.DockerfileData.fromInline(` FROM {{{ imagebuilder:parentImage }}} CMD ["echo", "Hello, world!"] {{{ imagebuilder:environments }}} {{{ imagebuilder:components }}} `), targetRepository: imagebuilder.Repository.fromEcr( ecr.Repository.fromRepositoryName(this, 'TargetRepository', 'imagebuilder-repository'), ), // Use an AWS-managed component, shared component, and a self-owned component with parameters components: [ { component: imagebuilder.AwsManagedComponent.fromAwsManagedComponentName( this, 'update-linux-component', 'update-linux', ), }, { component: imagebuilder.Component.fromComponentArn( this, 'ComplianceTestComponent', `arn:${this.partition}:imagebuilder:${this.region}:123456789012:component/compliance-test/2025.x.x.x`, ), }, { component: imagebuilder.Component.fromComponentAttributes(this, 'CustomComponent', { componentName: 'custom-component', }), parameters: { CUSTOM_PARAMETER: imagebuilder.ComponentParameterValue.fromString('custom-parameter-value'), }, }, ], workingDirectory: '/var/tmp', // The image + block devices to use for the EC2 instance used for building the container image instanceImage: imagebuilder.ContainerInstanceImage.fromSsmParameterName( this, 'ContainerInstanceImage', '/aws/service/ecs/optimized-ami/amazon-linux-2023/recommended', ), instanceBlockDevices: [ { 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' }), }), }, ], }); ``` ### 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 - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 59ef00d commit 875e0e7

File tree

48 files changed

+5207
-1
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+5207
-1
lines changed

packages/@aws-cdk/aws-imagebuilder-alpha/README.md

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,181 @@ EC2 Image Builder supports AWS-managed components for common tasks, AWS Marketpl
3636
that you create. Components run during specific workflow phases: build and validate phases during the build stage, and
3737
test phase during the test stage.
3838

39+
### Container Recipe
40+
41+
A container recipe is similar to an image recipe but specifically for container images. It defines the base container
42+
image and components applied to produce the desired configuration for the output container image. Container recipes work
43+
with Docker images from DockerHub, Amazon ECR, or Amazon-managed container images as starting points.
44+
45+
#### Container Recipe Basic Usage
46+
47+
Create a container recipe with the required base image and target repository:
48+
49+
```ts
50+
const containerRecipe = new imagebuilder.ContainerRecipe(this, 'MyContainerRecipe', {
51+
baseImage: imagebuilder.BaseContainerImage.fromDockerHub('amazonlinux', 'latest'),
52+
targetRepository: imagebuilder.Repository.fromEcr(
53+
ecr.Repository.fromRepositoryName(this, 'Repository', 'my-container-repo')
54+
)
55+
});
56+
```
57+
58+
#### Container Recipe Base Images
59+
60+
##### DockerHub Images
61+
62+
Using public Docker Hub images:
63+
64+
```ts
65+
const containerRecipe = new imagebuilder.ContainerRecipe(this, 'DockerHubContainerRecipe', {
66+
baseImage: imagebuilder.BaseContainerImage.fromDockerHub('amazonlinux', 'latest'),
67+
targetRepository: imagebuilder.Repository.fromEcr(
68+
ecr.Repository.fromRepositoryName(this, 'Repository', 'my-container-repo')
69+
)
70+
});
71+
```
72+
73+
##### ECR Images
74+
75+
Using images from your own ECR repositories:
76+
77+
```ts
78+
const sourceRepo = ecr.Repository.fromRepositoryName(this, 'SourceRepo', 'my-base-image');
79+
const targetRepo = ecr.Repository.fromRepositoryName(this, 'TargetRepo', 'my-container-repo');
80+
81+
const containerRecipe = new imagebuilder.ContainerRecipe(this, 'EcrContainerRecipe', {
82+
baseImage: imagebuilder.BaseContainerImage.fromEcr(sourceRepo, '1.0.0'),
83+
targetRepository: imagebuilder.Repository.fromEcr(targetRepo)
84+
});
85+
```
86+
87+
##### ECR Public Images
88+
89+
Using images from Amazon ECR Public:
90+
91+
```ts
92+
const containerRecipe = new imagebuilder.ContainerRecipe(this, 'EcrPublicContainerRecipe', {
93+
baseImage: imagebuilder.BaseContainerImage.fromEcrPublic('amazonlinux', 'amazonlinux', '2023'),
94+
targetRepository: imagebuilder.Repository.fromEcr(
95+
ecr.Repository.fromRepositoryName(this, 'Repository', 'my-container-repo')
96+
)
97+
});
98+
```
99+
100+
#### Container Recipe Components
101+
102+
##### Custom Components in Container Recipes
103+
104+
Add your own components to the container recipe:
105+
106+
```ts
107+
const customComponent = new imagebuilder.Component(this, 'MyComponent', {
108+
platform: imagebuilder.Platform.LINUX,
109+
data: imagebuilder.ComponentData.fromJsonObject({
110+
schemaVersion: imagebuilder.ComponentSchemaVersion.V1_0,
111+
phases: [
112+
{
113+
name: imagebuilder.ComponentPhaseName.BUILD,
114+
steps: [
115+
{
116+
name: 'install-app',
117+
action: imagebuilder.ComponentAction.EXECUTE_BASH,
118+
inputs: {
119+
commands: ['yum install -y my-container-application']
120+
}
121+
}
122+
]
123+
}
124+
]
125+
})
126+
});
127+
128+
const containerRecipe = new imagebuilder.ContainerRecipe(this, 'ComponentContainerRecipe', {
129+
baseImage: imagebuilder.BaseContainerImage.fromDockerHub('amazonlinux', 'latest'),
130+
targetRepository: imagebuilder.Repository.fromEcr(
131+
ecr.Repository.fromRepositoryName(this, 'Repository', 'my-container-repo')
132+
),
133+
components: [
134+
{
135+
component: customComponent
136+
}
137+
]
138+
});
139+
```
140+
141+
##### AWS-Managed Components in Container Recipes
142+
143+
Use pre-built AWS components:
144+
145+
```ts
146+
const containerRecipe = new imagebuilder.ContainerRecipe(this, 'AwsManagedContainerRecipe', {
147+
baseImage: imagebuilder.BaseContainerImage.fromDockerHub('amazonlinux', 'latest'),
148+
targetRepository: imagebuilder.Repository.fromEcr(
149+
ecr.Repository.fromRepositoryName(this, 'Repository', 'my-container-repo')
150+
),
151+
components: [
152+
{
153+
component: imagebuilder.AwsManagedComponent.updateOS(this, 'UpdateOS', {
154+
platform: imagebuilder.Platform.LINUX
155+
})
156+
},
157+
{
158+
component: imagebuilder.AwsManagedComponent.awsCliV2(this, 'AwsCli', {
159+
platform: imagebuilder.Platform.LINUX
160+
})
161+
}
162+
]
163+
});
164+
```
165+
166+
#### Container Recipe Configuration
167+
168+
##### Custom Dockerfile
169+
170+
Provide your own Dockerfile template:
171+
172+
```ts
173+
const containerRecipe = new imagebuilder.ContainerRecipe(this, 'CustomDockerfileContainerRecipe', {
174+
baseImage: imagebuilder.BaseContainerImage.fromDockerHub('amazonlinux', 'latest'),
175+
targetRepository: imagebuilder.Repository.fromEcr(
176+
ecr.Repository.fromRepositoryName(this, 'Repository', 'my-container-repo')
177+
),
178+
dockerfile: imagebuilder.DockerfileData.fromInline(`
179+
FROM {{{ imagebuilder:parentImage }}}
180+
CMD ["echo", "Hello, world!"]
181+
{{{ imagebuilder:environments }}}
182+
{{{ imagebuilder:components }}}
183+
`)
184+
});
185+
```
186+
187+
##### Instance Configuration
188+
189+
Configure the build instance:
190+
191+
```ts
192+
const containerRecipe = new imagebuilder.ContainerRecipe(this, 'InstanceConfigContainerRecipe', {
193+
baseImage: imagebuilder.BaseContainerImage.fromDockerHub('amazonlinux', 'latest'),
194+
targetRepository: imagebuilder.Repository.fromEcr(
195+
ecr.Repository.fromRepositoryName(this, 'Repository', 'my-container-repo')
196+
),
197+
// Custom ECS-optimized AMI for building
198+
instanceImage: imagebuilder.ContainerInstanceImage.fromSsmParameterName(
199+
'/aws/service/ecs/optimized-ami/amazon-linux-2023/recommended/image_id'
200+
),
201+
// Additional storage for build process
202+
instanceBlockDevices: [
203+
{
204+
deviceName: '/dev/xvda',
205+
volume: ec2.BlockDeviceVolume.ebs(50, {
206+
encrypted: true,
207+
volumeType: ec2.EbsDeviceVolumeType.GENERAL_PURPOSE_SSD_GP3
208+
})
209+
}
210+
]
211+
});
212+
```
213+
39214
### Component
40215

41216
A component defines the sequence of steps required to customize an instance during image creation (build component) or
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"exclude": [
3-
"props-no-arn-refs:@aws-cdk/aws-imagebuilder-alpha.InfrastructureConfigurationProps.ec2InstanceHostResourceGroupArn"
3+
"props-no-arn-refs:@aws-cdk/aws-imagebuilder-alpha.InfrastructureConfigurationProps.ec2InstanceHostResourceGroupArn",
4+
"no-unused-type:@aws-cdk/aws-imagebuilder-alpha.ContainerType"
45
]
56
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import * as ecr from 'aws-cdk-lib/aws-ecr';
2+
import * as ssm from 'aws-cdk-lib/aws-ssm';
3+
4+
/**
5+
* Represents a base image that is used to start from in EC2 Image Builder image builds
6+
*/
7+
export class BaseContainerImage {
8+
/**
9+
* The DockerHub image to use as the base image in a container recipe
10+
*
11+
* @param repository The DockerHub repository where the base image resides in
12+
* @param tag The tag of the base image in the DockerHub repository
13+
*/
14+
public static fromDockerHub(repository: string, tag: string): BaseContainerImage {
15+
return new BaseContainerImage(`${repository}:${tag}`);
16+
}
17+
18+
/**
19+
* The ECR container image to use as the base image in a container recipe
20+
*
21+
* @param repository The ECR repository where the base image resides in
22+
* @param tag The tag of the base image in the ECR repository
23+
*/
24+
public static fromEcr(repository: ecr.IRepository, tag: string): BaseContainerImage {
25+
return new BaseContainerImage(repository.repositoryUriForTag(tag));
26+
}
27+
28+
/**
29+
* The ECR public container image to use as the base image in a container recipe
30+
*
31+
* @param registryAlias The alias of the ECR public registry where the base image resides in
32+
* @param repositoryName The name of the ECR public repository, where the base image resides in
33+
* @param tag The tag of the base image in the ECR public repository
34+
*/
35+
public static fromEcrPublic(registryAlias: string, repositoryName: string, tag: string): BaseContainerImage {
36+
return new BaseContainerImage(`public.ecr.aws/${registryAlias}/${repositoryName}:${tag}`);
37+
}
38+
39+
/**
40+
* The string value of the base image to use in a container recipe. This can be an EC2 Image Builder image ARN,
41+
* an ECR or ECR public image, or a container URI sourced from a third-party container registry such as DockerHub.
42+
*
43+
* @param baseContainerImageString The base image as a direct string value
44+
*/
45+
public static fromString(baseContainerImageString: string): BaseContainerImage {
46+
return new BaseContainerImage(baseContainerImageString);
47+
}
48+
49+
/**
50+
* The rendered base image to use
51+
**/
52+
public readonly image: string;
53+
54+
protected constructor(image: string) {
55+
this.image = image;
56+
}
57+
}
58+
59+
/**
60+
* Represents a container instance image that is used to launch the instance used for building the container for an
61+
* EC2 Image Builder container build.
62+
*/
63+
export class ContainerInstanceImage {
64+
/**
65+
* The AMI ID to use to launch the instance for building the container image
66+
*
67+
* @param amiId The AMI ID to use as the container instance image
68+
*/
69+
public static fromAmiId(amiId: string): ContainerInstanceImage {
70+
return new ContainerInstanceImage(amiId);
71+
}
72+
73+
/**
74+
* The SSM parameter to use to launch the instance for building the container image
75+
*
76+
* @param parameter The SSM parameter to use as the container instance image
77+
*/
78+
public static fromSsmParameter(parameter: ssm.IStringParameter): ContainerInstanceImage {
79+
return new ContainerInstanceImage(`ssm:${parameter.parameterArn}`);
80+
}
81+
82+
/**
83+
* The name of the SSM parameter used to launch the instance for building the container image
84+
*
85+
* @param parameterName The name of the SSM parameter used as the container instance image
86+
*/
87+
public static fromSsmParameterName(parameterName: string): ContainerInstanceImage {
88+
return new ContainerInstanceImage(`ssm:${parameterName}`);
89+
}
90+
91+
/**
92+
* The string value of the container instance image to use in a container recipe. This can either be:
93+
* - an SSM parameter reference, prefixed with `ssm:` and followed by the parameter name or ARN
94+
* - an AMI ID
95+
*
96+
* @param containerInstanceImageString The container instance image as a direct string value
97+
*/
98+
public static fromString(containerInstanceImageString: string): ContainerInstanceImage {
99+
return new ContainerInstanceImage(containerInstanceImageString);
100+
}
101+
102+
/**
103+
* The rendered container instance image to use
104+
**/
105+
public readonly image: string;
106+
107+
protected constructor(image: string) {
108+
this.image = image;
109+
}
110+
}

0 commit comments

Comments
 (0)