├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bin ├── imagebuilder-components │ ├── docker.yaml │ ├── git.yaml │ └── node.yaml └── imagebuilder.ts ├── cdk.json ├── imagebuilder-sample-arch.drawio.png ├── lib └── imagebuilder-stack.ts ├── package.json └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode,node 2 | # Edit at https://www.gitignore.io/?templates=osx,linux,python,windows,pycharm,visualstudiocode,node 3 | 4 | ### Linux ### 5 | *~ 6 | 7 | # temporary files which can be created if a process still has a handle open of a deleted file 8 | .fuse_hidden* 9 | 10 | # KDE directory preferences 11 | .directory 12 | 13 | # Linux trash folder which might appear on any partition or disk 14 | .Trash-* 15 | 16 | # .nfs files are created when an open file is removed but is still being accessed 17 | .nfs* 18 | 19 | ### Node ### 20 | # Logs 21 | logs 22 | *.log 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | lerna-debug.log* 27 | 28 | # Diagnostic reports (https://nodejs.org/api/report.html) 29 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 30 | 31 | # Runtime data 32 | pids 33 | *.pid 34 | *.seed 35 | *.pid.lock 36 | 37 | # Directory for instrumented libs generated by jscoverage/JSCover 38 | lib-cov 39 | 40 | # Coverage directory used by tools like istanbul 41 | coverage 42 | *.lcov 43 | 44 | # nyc test coverage 45 | .nyc_output 46 | 47 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 48 | .grunt 49 | 50 | # Bower dependency directory (https://bower.io/) 51 | bower_components 52 | 53 | # node-waf configuration 54 | .lock-wscript 55 | 56 | # Compiled binary addons (https://nodejs.org/api/addons.html) 57 | build/Release 58 | 59 | # Dependency directories 60 | node_modules/ 61 | jspm_packages/ 62 | 63 | # TypeScript v1 declaration files 64 | typings/ 65 | 66 | # TypeScript cache 67 | *.tsbuildinfo 68 | 69 | # Optional npm cache directory 70 | .npm 71 | 72 | # Optional eslint cache 73 | .eslintcache 74 | 75 | # Optional REPL history 76 | .node_repl_history 77 | 78 | # Output of 'npm pack' 79 | *.tgz 80 | 81 | # Yarn Integrity file 82 | .yarn-integrity 83 | 84 | # dotenv environment variables file 85 | .env 86 | .env.test 87 | 88 | # parcel-bundler cache (https://parceljs.org/) 89 | .cache 90 | 91 | # next.js build output 92 | .next 93 | 94 | # nuxt.js build output 95 | .nuxt 96 | 97 | # vuepress build output 98 | .vuepress/dist 99 | 100 | # Serverless directories 101 | .serverless/ 102 | 103 | # FuseBox cache 104 | .fusebox/ 105 | 106 | # DynamoDB Local files 107 | .dynamodb/ 108 | 109 | ### OSX ### 110 | # General 111 | .DS_Store 112 | .AppleDouble 113 | .LSOverride 114 | 115 | # Icon must end with two \r 116 | Icon 117 | 118 | # Thumbnails 119 | ._* 120 | 121 | # Files that might appear in the root of a volume 122 | .DocumentRevisions-V100 123 | .fseventsd 124 | .Spotlight-V100 125 | .TemporaryItems 126 | .Trashes 127 | .VolumeIcon.icns 128 | .com.apple.timemachine.donotpresent 129 | 130 | # Directories potentially created on remote AFP share 131 | .AppleDB 132 | .AppleDesktop 133 | Network Trash Folder 134 | Temporary Items 135 | .apdisk 136 | 137 | ### PyCharm ### 138 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 139 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 140 | 141 | # User-specific stuff 142 | .idea/**/workspace.xml 143 | .idea/**/tasks.xml 144 | .idea/**/usage.statistics.xml 145 | .idea/**/dictionaries 146 | .idea/**/shelf 147 | 148 | # Generated files 149 | .idea/**/contentModel.xml 150 | 151 | # Sensitive or high-churn files 152 | .idea/**/dataSources/ 153 | .idea/**/dataSources.ids 154 | .idea/**/dataSources.local.xml 155 | .idea/**/sqlDataSources.xml 156 | .idea/**/dynamic.xml 157 | .idea/**/uiDesigner.xml 158 | .idea/**/dbnavigator.xml 159 | 160 | # Gradle 161 | .idea/**/gradle.xml 162 | .idea/**/libraries 163 | 164 | # Gradle and Maven with auto-import 165 | # When using Gradle or Maven with auto-import, you should exclude module files, 166 | # since they will be recreated, and may cause churn. Uncomment if using 167 | # auto-import. 168 | .idea/*.xml 169 | .idea/*.iml 170 | .idea 171 | # .idea/modules 172 | # *.iml 173 | # *.ipr 174 | 175 | # CMake 176 | cmake-build-*/ 177 | 178 | # Mongo Explorer plugin 179 | .idea/**/mongoSettings.xml 180 | 181 | # File-based project format 182 | *.iws 183 | 184 | # IntelliJ 185 | out/ 186 | 187 | # mpeltonen/sbt-idea plugin 188 | .idea_modules/ 189 | 190 | # JIRA plugin 191 | atlassian-ide-plugin.xml 192 | 193 | # Cursive Clojure plugin 194 | .idea/replstate.xml 195 | 196 | # Crashlytics plugin (for Android Studio and IntelliJ) 197 | com_crashlytics_export_strings.xml 198 | crashlytics.properties 199 | crashlytics-build.properties 200 | fabric.properties 201 | 202 | # Editor-based Rest Client 203 | .idea/httpRequests 204 | 205 | # Android studio 3.1+ serialized cache file 206 | .idea/caches/build_file_checksums.ser 207 | 208 | ### PyCharm Patch ### 209 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 210 | 211 | # *.iml 212 | # modules.xml 213 | # .idea/misc.xml 214 | # *.ipr 215 | 216 | # Sonarlint plugin 217 | .idea/sonarlint 218 | 219 | ### Python ### 220 | # Byte-compiled / optimized / DLL files 221 | __pycache__/ 222 | *.py[cod] 223 | *$py.class 224 | 225 | # C extensions 226 | *.so 227 | 228 | # Distribution / packaging 229 | .Python 230 | build/ 231 | develop-eggs/ 232 | dist/ 233 | downloads/ 234 | eggs/ 235 | .eggs/ 236 | lib64/ 237 | parts/ 238 | sdist/ 239 | var/ 240 | wheels/ 241 | pip-wheel-metadata/ 242 | share/python-wheels/ 243 | *.egg-info/ 244 | .installed.cfg 245 | *.egg 246 | MANIFEST 247 | 248 | # PyInstaller 249 | # Usually these files are written by a python script from a template 250 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 251 | *.manifest 252 | *.spec 253 | 254 | # Installer logs 255 | pip-log.txt 256 | pip-delete-this-directory.txt 257 | 258 | # Unit test / coverage reports 259 | htmlcov/ 260 | .tox/ 261 | .nox/ 262 | .coverage 263 | .coverage.* 264 | nosetests.xml 265 | coverage.xml 266 | *.cover 267 | .hypothesis/ 268 | .pytest_cache/ 269 | 270 | # Translations 271 | *.mo 272 | *.pot 273 | 274 | # Django stuff: 275 | local_settings.py 276 | db.sqlite3 277 | db.sqlite3-journal 278 | 279 | # Flask stuff: 280 | instance/ 281 | .webassets-cache 282 | 283 | # Scrapy stuff: 284 | .scrapy 285 | 286 | # Sphinx documentation 287 | docs/_build/ 288 | 289 | # PyBuilder 290 | target/ 291 | 292 | # Jupyter Notebook 293 | .ipynb_checkpoints 294 | 295 | # IPython 296 | profile_default/ 297 | ipython_config.py 298 | 299 | # pyenv 300 | .python-version 301 | 302 | # pipenv 303 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 304 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 305 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 306 | # install all needed dependencies. 307 | #Pipfile.lock 308 | 309 | # celery beat schedule file 310 | celerybeat-schedule 311 | 312 | # SageMath parsed files 313 | *.sage.py 314 | 315 | # Environments 316 | .venv 317 | env/ 318 | venv/ 319 | ENV/ 320 | env.bak/ 321 | venv.bak/ 322 | 323 | # Spyder project settings 324 | .spyderproject 325 | .spyproject 326 | 327 | # Rope project settings 328 | .ropeproject 329 | 330 | # mkdocs documentation 331 | /site 332 | 333 | # mypy 334 | .mypy_cache/ 335 | .dmypy.json 336 | dmypy.json 337 | 338 | # Pyre type checker 339 | .pyre/ 340 | 341 | ### VisualStudioCode ### 342 | .vscode 343 | 344 | ### VisualStudioCode Patch ### 345 | # Ignore all local history of files 346 | .history 347 | 348 | ### Windows ### 349 | # Windows thumbnail cache files 350 | Thumbs.db 351 | Thumbs.db:encryptable 352 | ehthumbs.db 353 | ehthumbs_vista.db 354 | 355 | # Dump file 356 | *.stackdump 357 | 358 | # Folder config file 359 | [Dd]esktop.ini 360 | 361 | # Recycle Bin used on file shares 362 | $RECYCLE.BIN/ 363 | 364 | # Windows Installer files 365 | *.cab 366 | *.msi 367 | *.msix 368 | *.msm 369 | *.msp 370 | 371 | # Windows shortcuts 372 | *.lnk 373 | 374 | # End of https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode,node 375 | 376 | ### CDK-specific ignores ### 377 | *.swp 378 | cdk.context.json 379 | package-lock.json 380 | yarn.lock 381 | .cdk.staging 382 | cdk.out 383 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT No Attribution 2 | 3 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 13 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 14 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 15 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS CDK ImageBuilder Sample 2 | 3 | 4 | --- 5 | 6 | ![Stability: Stable](https://img.shields.io/badge/stability-Stable-success.svg?style=for-the-badge) 7 | 8 | > **This is a stable example. It should successfully build out of the box** 9 | > 10 | > This example is built on Construct Libraries marked "Stable" and does not have any infrastructure prerequisites to build. 11 | --- 12 | 13 | 14 | ![AWS Sample Architecture showing AWS ImageBuilder service provisioning a build pipeline that reads a base container image (Amazon Linux 2023) and then installs Git, NodeJS and Docker, then saves the resulting image in an Amazon ECR repository.](imagebuilder-sample-arch.drawio.png "AWS Sample Architecture") 15 | 16 | ## Overview 17 | 18 | This AWS Cloud Development Kit (CDK) TypeScript example demonstrates how to create a fully functional ImageBuilder pipeline that builds an Amazon Linux 2023 container image, installing git, docker and nodejs, all the way to pushing the resulting image to an ECR repository. 19 | 20 | ## Real-world Example 21 | 22 | When working in fast-paced development environments, CI/CD (Continuous Integration and Continuous Delivery) pipelines are used to automatically build, test, and deploy golden images across multiple accounts and environments. This allows new features and bug fixes to be tested and deployed quickly to continuously improve the application. 23 | 24 | For example, this pipeline can be used to create the build image that can be used as part of Amazon CodeCatalyst workflow to build applications that require Node version 18 which requires Amazon Linux 2023 and is not currently provided as a built-in image in Amazon CodeCatalyst. Reducing the time needed to bring an Amazon CodeCatalyst workflow up and running. 25 | 26 | ## Structure 27 | 28 | The following resources are created: 29 | - An ECR reposetory to store the built images 30 | - An ImageBuilder recipe that includes the following componenets: 31 | - install git 32 | - install nodejs 33 | - install docker 34 | - instance profile for ImageBuilder build instance 35 | - Infrastructure configuration to tell ImageBuilder which infra to use for the pipeline 36 | - Distribution configuration to tell ImageBuilder to use the ECR repo as the destination for resulting images 37 | - An ImageBuilder pipeline 38 | 39 | ## Deploying 40 | 41 | 1. Authenticate to an AWS account via a Command Line Interface (CLI). 42 | 2. Navigate to the cloned repo's root directory. 43 | 3. `npm ci` to install required dependencies 44 | 4. `cdk deploy` to deploy the stack to the AWS account you're authenticated to. 45 | 46 | ## Additional Commands 47 | 48 | - `cdk synth` to generate and review the CloudFormation template. 49 | - `cdk diff` to compare local changes with what is currently deployed. 50 | 51 | 52 | ## Security 53 | 54 | Since this is a sample (not production ready), customers should keep the following points in mind: 55 | - Control permissions related to modifying the container recipe which specifies the source container image and the target container registery, to prevent any unapproved base images to be used, and resulting images to be stored in approved repositories. 56 | - Enable [AWS CloudTrail](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-user-guide.html#) to be able to monitor management actions taken against the ImageBuilder pipeline, recipes, compoenets, etc. 57 | - Use ImageBuilder history tab to monitor invocations and triggers of the pipeline and see detailed recipe contents, commands and execution outputs. 58 | - The [ECR](https://docs.aws.amazon.com/AmazonECR/latest/userguide/what-is-ecr.html) repository created as part of this sample uses AWS Managed encryption keys to encrypt the resulting container images at rest. You may want to use your own encryption key using [Amazon KMS](https://docs.aws.amazon.com/AmazonECR/latest/userguide/encryption-at-rest.html). 59 | - For container image vulnerability scanning, consider using [Amazon Inspector ECR scanning feature](https://docs.aws.amazon.com/inspector/latest/user/scanning-ecr.html). 60 | - Follow the [principle of least privilage](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege) to ensure authorized access to image builder, recipes, etc. is on a need basis. 61 | 62 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 63 | 64 | ## License 65 | 66 | This library is licensed under the MIT-0 License. See the LICENSE file. 67 | 68 | -------------------------------------------------------------------------------- /bin/imagebuilder-components/docker.yaml: -------------------------------------------------------------------------------- 1 | name: DockerInstallationDocument 2 | description: This document installs docker. 3 | schemaVersion: 1.0 4 | 5 | phases: 6 | - name: build 7 | steps: 8 | - name: InstallDocker 9 | action: ExecuteBash 10 | inputs: 11 | commands: 12 | - yum update -y && yum install -y docker 13 | 14 | - name: validate 15 | steps: 16 | - name: CheckVersion 17 | action: ExecuteBash 18 | inputs: 19 | commands: 20 | - docker --version -------------------------------------------------------------------------------- /bin/imagebuilder-components/git.yaml: -------------------------------------------------------------------------------- 1 | name: GitInstallationDocument 2 | description: This document installs git. 3 | schemaVersion: 1.0 4 | 5 | phases: 6 | - name: build 7 | steps: 8 | - name: InstallGit 9 | action: ExecuteBash 10 | inputs: 11 | commands: 12 | - yum update -y && yum install -y git 13 | 14 | - name: validate 15 | steps: 16 | - name: CheckVersion 17 | action: ExecuteBash 18 | inputs: 19 | commands: 20 | - git version -------------------------------------------------------------------------------- /bin/imagebuilder-components/node.yaml: -------------------------------------------------------------------------------- 1 | name: NodejsV18InstallationDocument 2 | description: This document installs Node.js v18. 3 | schemaVersion: 1.0 4 | 5 | phases: 6 | - name: build 7 | steps: 8 | - name: NodeInstallation 9 | action: ExecuteBash 10 | inputs: 11 | commands: 12 | - yum update -y && yum install -y tar 13 | - touch ~/.bashrc 14 | - curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash 15 | - source ~/.bashrc 16 | - nvm install --default 18.18.2 17 | - nvm alias default 18.18.2 18 | 19 | - name: validate 20 | steps: 21 | - name: NodeValidation 22 | action: ExecuteBash 23 | inputs: 24 | commands: 25 | - source ~/.bashrc 26 | - node -v -------------------------------------------------------------------------------- /bin/imagebuilder.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from 'aws-cdk-lib'; 4 | import { ImagebuilderStack } from '../lib/imagebuilder-stack'; 5 | import { AwsSolutionsChecks } from 'cdk-nag' 6 | import { Aspects } from 'aws-cdk-lib'; 7 | 8 | const app = new cdk.App(); 9 | Aspects.of(app).add(new AwsSolutionsChecks()) 10 | new ImagebuilderStack(app, 'ImagebuilderStack', { 11 | /* If you don't specify 'env', this stack will be environment-agnostic. 12 | * Account/Region-dependent features and context lookups will not work, 13 | * but a single synthesized template can be deployed anywhere. */ 14 | 15 | /* Uncomment the next line to specialize this stack for the AWS Account 16 | * and Region that are implied by the current CLI configuration. */ 17 | // env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, 18 | 19 | /* Uncomment the next line if you know exactly what Account and Region you 20 | * want to deploy the stack to. */ 21 | // env: { account: '123456789012', region: 'us-east-1' }, 22 | 23 | /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ 24 | }); -------------------------------------------------------------------------------- /cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/imagebuilder.ts", 3 | "watch": { 4 | "include": [ 5 | "**" 6 | ], 7 | "exclude": [ 8 | "README.md", 9 | "cdk*.json", 10 | "**/*.d.ts", 11 | "**/*.js", 12 | "tsconfig.json", 13 | "package*.json", 14 | "yarn.lock", 15 | "node_modules", 16 | "test" 17 | ] 18 | }, 19 | "context": { 20 | "@aws-cdk/aws-lambda:recognizeLayerVersion": true, 21 | "@aws-cdk/core:checkSecretUsage": true, 22 | "@aws-cdk/core:target-partitions": [ 23 | "aws", 24 | "aws-cn" 25 | ], 26 | "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, 27 | "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, 28 | "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, 29 | "@aws-cdk/aws-iam:minimizePolicies": true, 30 | "@aws-cdk/core:validateSnapshotRemovalPolicy": true, 31 | "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, 32 | "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, 33 | "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, 34 | "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, 35 | "@aws-cdk/core:enablePartitionLiterals": true, 36 | "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, 37 | "@aws-cdk/aws-iam:standardizedServicePrincipals": true, 38 | "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, 39 | "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, 40 | "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, 41 | "@aws-cdk/aws-route53-patters:useCertificate": true, 42 | "@aws-cdk/customresources:installLatestAwsSdkDefault": false, 43 | "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, 44 | "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, 45 | "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, 46 | "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, 47 | "@aws-cdk/aws-redshift:columnId": true, 48 | "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, 49 | "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, 50 | "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, 51 | "@aws-cdk/aws-kms:aliasNameRef": true, 52 | "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, 53 | "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, 54 | "@aws-cdk/aws-efs:denyAnonymousAccess": true, 55 | "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true, 56 | "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true, 57 | "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true, 58 | "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true, 59 | "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true, 60 | "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true, 61 | "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /imagebuilder-sample-arch.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-cdk-imagebuilder-sample/ea0c42ffefd2c54fa720f73c7cd610e25041c6fd/imagebuilder-sample-arch.drawio.png -------------------------------------------------------------------------------- /lib/imagebuilder-stack.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from 'aws-cdk-lib'; 2 | import fs = require('fs'); 3 | import * as path from 'path' 4 | 5 | import * as imagebuilder from 'aws-cdk-lib/aws-imagebuilder'; 6 | import * as ecr from 'aws-cdk-lib/aws-ecr' 7 | import * as iam from 'aws-cdk-lib/aws-iam' 8 | 9 | import { NagSuppressions } from 'cdk-nag'; 10 | 11 | export class ImagebuilderStack extends cdk.Stack { 12 | constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { 13 | super(scope, id, props); 14 | 15 | // Create ImageBuilder Component that will handle installing git in the base container image 16 | const gitComponent = new imagebuilder.CfnComponent(this, "GitComponent", { 17 | // Prefix component name with stake name for inter-environment uniqueness 18 | name: this.stackName + '-' + "Git", 19 | platform: "Linux", 20 | version: "1.0.0", 21 | data: fs.readFileSync( 22 | path.resolve('bin/imagebuilder-components/git.yaml'), 23 | 'utf8' 24 | ) 25 | }); 26 | 27 | // Create ImageBuilder Component that will handle installing NodeJS in the base container image 28 | const nodejsComponent = new imagebuilder.CfnComponent(this, "NodejsComponent", { 29 | // Prefix component name with stake name for inter-environment uniqueness 30 | name: this.stackName + '-' + "Nodejs", 31 | platform: "Linux", 32 | version: "1.0.0", 33 | data: fs.readFileSync( 34 | path.resolve('bin/imagebuilder-components/node.yaml'), 35 | 'utf8' 36 | ) 37 | }); 38 | 39 | // Create ImageBuilder Component that will handle installing Docker in the base container image 40 | const dockerComponent = new imagebuilder.CfnComponent(this, "DockerComponent", { 41 | // Prefix component name with stake name for inter-environment uniqueness 42 | name: this.stackName + '-' + "Docker", 43 | platform: "Linux", 44 | version: "1.0.0", 45 | data: fs.readFileSync( 46 | path.resolve('bin/imagebuilder-components/docker.yaml'), 47 | 'utf8' 48 | ) 49 | }); 50 | 51 | // Create the Amazon Elastic Container Registry repository that will host the resulting image(s) 52 | const ecrRepoForImageBuilderCodeCatalyst = new ecr.Repository(this, "EcrRepoForImageBuilderCodeCatalyst", { 53 | // Explicitly set encryption as enabled (default) 54 | encryption: ecr.RepositoryEncryption.AES_256, 55 | // Optional: Can set encryption to KMS and select a customer encryption key 56 | // encryptionKey: 57 | }) 58 | 59 | // Create an ImageBuilder recipe that contains the 3 components 60 | const AmazonLinux2023wGitNodeRecipe = new imagebuilder.CfnContainerRecipe(this, "AmazonLinux2023withGitAndNodeRecipe", { 61 | components: [ 62 | { 63 | componentArn : gitComponent.attrArn, 64 | }, 65 | { 66 | componentArn : nodejsComponent.attrArn, 67 | }, 68 | { 69 | componentArn : dockerComponent.attrArn, 70 | } 71 | ], 72 | containerType: "DOCKER", 73 | dockerfileTemplateData: "FROM {{{ imagebuilder:parentImage }}}\n{{{ imagebuilder:environments }}}\n{{{ imagebuilder:components }}}\n", 74 | // Prefix recipe name with stake name for inter-environment uniqueness 75 | name: this.stackName + '-' + "AmazonLinux2023WithGit", 76 | // Use amazon linux 2023 base image with the latest version tag indicated by /x.x.x 77 | parentImage: `arn:aws:imagebuilder:${this.region}:aws:image/amazon-linux-2023-x86-latest/x.x.x`, 78 | // Specify the destination repository as the one created above 79 | targetRepository: { 80 | repositoryName: ecrRepoForImageBuilderCodeCatalyst.repositoryName, 81 | service : "ECR" 82 | }, 83 | version: "1.0.0" 84 | }) 85 | 86 | // Create an IAM role for ImageBuilder EC2 build instances, that has the needed AWS Managed policies 87 | const iamRoleForImageBuilder = new iam.Role(this, 'EC2InstanceProfileForImageBuilder', { 88 | assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'), 89 | managedPolicies: [ 90 | { 91 | managedPolicyArn: "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" 92 | }, 93 | { 94 | managedPolicyArn: "arn:aws:iam::aws:policy/EC2InstanceProfileForImageBuilder" 95 | }, 96 | { 97 | managedPolicyArn: "arn:aws:iam::aws:policy/EC2InstanceProfileForImageBuilderECRContainerBuilds" 98 | } 99 | ] 100 | }) 101 | // Suppress cdk_nag warning on use of managed policies 102 | NagSuppressions.addResourceSuppressions(iamRoleForImageBuilder, [ 103 | { id: 'AwsSolutions-IAM4', reason: 'Managed policies for ImageBuilder are used as the provide a comprehensive set of permissions to allow ImageBuilder to function in this sample' }, 104 | ]); 105 | 106 | // Create an EC2 instance profile that uses the IAM role created above 107 | const instanceProfileForImageBuilder = new iam.InstanceProfile(this, "InstanceProfileForImageBuilder", { 108 | role: iamRoleForImageBuilder 109 | }); 110 | 111 | // Create build infrastructure configuration that uses the instance profile 112 | const infraConfig = new imagebuilder.CfnInfrastructureConfiguration(this, "ImageBuilderInfraConfig", { 113 | // Prefix recipe name with stake name for inter-environment uniqueness 114 | name: this.stackName + '-' + "infra", 115 | instanceProfileName: instanceProfileForImageBuilder.instanceProfileName, 116 | }); 117 | 118 | // Create a distribution config to specify where the resulting image(s) should be stored 119 | const distConfig = new imagebuilder.CfnDistributionConfiguration(this, "ImageBuilderDistConfig", { 120 | // Prefix recipe name with stake name for inter-environment uniqueness 121 | name: this.stackName + '-' + "dist", 122 | distributions: [ 123 | { 124 | // Set the target region to the same region where the current stack is deployed 125 | region: this.region!, 126 | containerDistributionConfiguration: { 127 | "TargetRepository" : { 128 | // Set the repository to the one created above 129 | "RepositoryName" : ecrRepoForImageBuilderCodeCatalyst.repositoryName, 130 | "Service" : "ECR" 131 | } 132 | } 133 | } 134 | ] 135 | }); 136 | 137 | // Create the ImageBuilder pipeline using the infrastructure, distribution, and container recipe above 138 | const imageBuilderPipeline = new imagebuilder.CfnImagePipeline(this, "AmazonLinux2023WithGitPipeline", { 139 | // Prefix recipe name with stake name for inter-environment uniqueness 140 | name: this.stackName + '-' + "AmazonLinux23WithGitPipeline", 141 | infrastructureConfigurationArn: infraConfig.attrArn, 142 | distributionConfigurationArn: distConfig.attrArn, 143 | containerRecipeArn: AmazonLinux2023wGitNodeRecipe.attrArn, 144 | status: "ENABLED", 145 | }); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "imagebuilder", 3 | "version": "0.1.0", 4 | "bin": { 5 | "imagebuilder": "bin/imagebuilder.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "cdk": "cdk" 11 | }, 12 | "devDependencies": { 13 | "@types/jest": "29.5.11", 14 | "@types/node": "20.11.5", 15 | "aws-cdk": "2.122.0", 16 | "ts-node": "10.9.2", 17 | "typescript": "5.3.3" 18 | }, 19 | "dependencies": { 20 | "aws-cdk-lib": "2.122.0", 21 | "cdk-nag": "2.28.27", 22 | "constructs": "10.3.0", 23 | "source-map-support": "0.5.21" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "commonjs", 5 | "lib": [ 6 | "es2020", 7 | "dom" 8 | ], 9 | "declaration": true, 10 | "strict": true, 11 | "noImplicitAny": true, 12 | "strictNullChecks": true, 13 | "noImplicitThis": true, 14 | "alwaysStrict": true, 15 | "noUnusedLocals": false, 16 | "noUnusedParameters": false, 17 | "noImplicitReturns": true, 18 | "noFallthroughCasesInSwitch": false, 19 | "inlineSourceMap": true, 20 | "inlineSources": true, 21 | "experimentalDecorators": true, 22 | "strictPropertyInitialization": false, 23 | "typeRoots": [ 24 | "./node_modules/@types" 25 | ] 26 | }, 27 | "exclude": [ 28 | "node_modules", 29 | "cdk.out" 30 | ] 31 | } 32 | --------------------------------------------------------------------------------