Most DOP-C02 study material treats EC2 Image Builder as a one-line answer: “use a golden AMI built by EC2 Image Builder”. That is enough for the easy questions. It is not enough for the ones that describe a pipeline that should rebuild when a base image is patched but never does, an AMI that arrives in a target account but cannot be launched, a distribution that updates the wrong launch template version, or a vulnerability scan that must block an image rather than merely report on it. Those questions test how the service’s resources connect, which flags gate which behaviours, and where IAM roles live.
This guide walks EC2 Image Builder the way a practitioner builds and operates it, written for the AWS Certified DevOps Engineer - Professional (DOP-C02) exam and verified against the current EC2 Image Builder documentation. Our configuration management and IaC guide already positions golden AMIs against bootstrapping; this article assumes that decision is made and goes one layer down into the service itself.
Where Image Builder Sits in the DOP-C02 Exam
Image Builder is not a domain of its own. It appears in three places:
- Domain 1, SDLC Automation, Task Statement 1.4 (Build and manage artifacts) names automating EC2 instance and container image builds, with EC2 Image Builder as the example service. An AMI or container image is an artifact, and the exam treats its pipeline like any other build pipeline: triggers, tests, promotion, distribution.
- Domain 2, Configuration Management and IaC, where the golden-AMI pattern competes with user data,
cfn-init, State Manager and Elastic Beanstalk for how instances get configured. - Domain 5, Security and Compliance, where hardened, scanned, regularly rebuilt images are the control, and Inspector findings, STIG components and encrypted cross-account distribution are the details.
The service itself is free. You pay for what it launches and stores on your behalf: EC2 build and test instances, EBS snapshots behind the AMIs, S3 log storage, ECR storage and pushes for container images, and Amazon Inspector scans.
The Six Resource Types and How They Connect
Everything in Image Builder is a reusable, named, Region-scoped resource. A pipeline is nothing more than pointers to the others plus a schedule.
| Resource | What it defines | Mutable? |
|---|---|---|
| Image recipe / container recipe | Base image, ordered list of build and test components, storage, working directory, user data, output AMI tags (image recipe) or Dockerfile template and target ECR repository (container recipe) | No. Recipes are immutable; you create a new version |
| Component | A versioned AWSTOE YAML document that customises (build component) or tests (test component) the instance | No. Same name + version with changed content creates a new build version |
| Infrastructure configuration | Where the build and test instances run: instance types, instance profile, VPC/subnet/security groups, key pair, logging bucket, SNS topic, IMDS options, placement, terminate-on-failure | Yes. Changes apply to the next build |
| Distribution configuration | Where output images go after a successful test: Regions, target accounts, launch permissions, KMS keys, launch template updates, SSM parameters, license configurations, VM export, Fast Launch | Yes, but each image keeps a copy of the settings it was built with |
| Workflow | The ordered steps of one stage (BUILD, TEST or DISTRIBUTION), as a versioned YAML document | No. Clone or create a new version |
| Image pipeline | Recipe + infrastructure configuration + distribution configuration + workflows + schedule + scan settings + execution role | Yes |
| Lifecycle policy | Rules that deprecate, disable or delete images (and their AMIs, snapshots or ECR images) by age or count | Yes |
Two vocabulary traps matter for the exam. First, an Image Builder image is a managed resource (version, platform, metadata, output resources) that points at an AMI or a container image; it is not the AMI itself. Second, resource names are normalised into the ARN: lowercase, with spaces and underscores replaced by hyphens. A component named My Component_v2 has the ARN segment my-component-v2, so my recipe and My_Recipe collide and the second create fails with ResourceAlreadyExistsException.
Components, images, image recipes and container recipes can be shared across accounts, and across an organization, with AWS Resource Access Manager. Infrastructure and distribution configurations cannot.
Semantic Versioning: The Mechanism Behind “Rebuild When Dependencies Change”
Image Builder versions every recipe, component, image and workflow with a four-node scheme:
<major>.<minor>.<patch>/<build>
You assign the first three nodes, each an integer from 0 to 2^30 - 1 (1,073,741,823). Image Builder assigns the fourth, the build version, automatically. A component ARN therefore ends in something like component/update-linux/1.0.2/1, where /1 is the build.
The part the exam cares about is the x wildcard. In a reference to a base image or a component you can replace nodes with x to mean “the latest”, with one rule: once a node is a wildcard, every node to its right must also be a wildcard. Given versions 2.2.4, 1.7.8 and 1.6.8:
| Reference | Resolves to |
|---|---|
x.x.x | 2.2.4 |
1.x.x | 1.7.8 |
1.6.x | 1.6.8 |
x.2.x | Error |
1.x.8 | Error |
Wildcards are also allowed when you create a recipe: a recipe created with semanticVersion: "1.0.x" resolves to 1.0.1, then 1.0.2 on the next create, so your CI/CD never has to track recipe numbers. Create and Get calls return a latestVersionReferences object with four pre-built ARNs (latestVersionArn = x.x.x, latestMajorVersionArn = 1.x.x, latestMinorVersionArn = 1.2.x, latestPatchVersionArn = 1.2.3) so you do not have to splice wildcards into ARNs yourself.
Why this matters: a scheduled pipeline’s pipelineExecutionStartCondition defaults to EXPRESSION_MATCH_AND_DEPENDENCY_UPDATES_AVAILABLE, which the documentation defines as “When you use semantic version filters on the base image or components in your image recipe, EC2 Image Builder builds a new image only when there are new versions of the base image or components in your recipe that match the filter.” If your recipe pins amazon-linux-2023-x86/2024.1.1 and update-linux/1.0.2/1, there is never a dependency update, and the scheduled pipeline silently skips every run. The fix is x.x.x on the base image (in the console, Use latest available OS version) and Use latest available component version on components. A base image referenced through an SSM parameter (ssm:/golden/base-ami) also counts: a change to the parameter’s value is a dependency update.
The alternative, EXPRESSION_MATCH_ONLY, builds every time the schedule fires regardless of changes.
Recipes: Image vs Container
| Feature | Image recipe | Container recipe |
|---|---|---|
| Output | AMI | Docker image in Amazon ECR |
| Base image sources | AMI ID, Image Builder image ARN (with x.x.x), ssm: parameter, AWS Marketplace product ID (prod-… or UUID) | Docker Hub image, ECR image, Amazon-managed image, Image Builder image ARN |
| Dockerfile | n/a | Required template using {{{ imagebuilder:parentImage }}}, {{{ imagebuilder:environments }}} and {{{ imagebuilder:components }}} |
| Target repository | n/a | ECR repository that must already exist |
| Storage | Block device mappings for the output AMI (incl. per-volume KMS encryption) | Block device mappings for the build instance only |
| User data | Supported (overrides the default cloud-init) | Not at recipe level |
| SSM Agent control | systemsManagerAgent.uninstallAfterBuild | n/a |
| Marketplace / CIS components | Supported | Not supported |
Recipe constraints that turn into distractors:
- 20 components per recipe by default (build + test combined), each component at most once regardless of version, cumulative component configuration 25 KB.
- Components must match the platform (Linux, Windows, macOS) and, where metadata exists, the OS version of the base image. Components in
DEPRECATEDorDISABLEDstatus cannot be added to new recipes. - At most nine AWS Marketplace product codes on the output image, counting the base AMI and Marketplace components.
- Components run in recipe order, except CIS hardening components always run last so the benchmark tests run against the finished image.
- The default working directory is
/tmpon Linux and macOS andC:/on Windows; point it at a larger volume when installers need scratch space. - Component parameters are plain text and logged in CloudTrail. Pass a Secrets Manager or Parameter Store reference and resolve it inside the component, never the secret itself.
Two newer recipe-level features are worth knowing. amiTags applies tags to the output AMI during the build (the keys CreatedBy and Ec2ImageBuilderArn are reserved). amiWatermarks attaches one to five lineage labels that propagate automatically to every copy in every Region and account; a watermarked AMI can never be made public, and Image Builder rejects a distribution configuration that tries (InvalidParameterCombinationException).
User data and the clean-up script
For Linux and macOS, Image Builder runs a clean-up script just before it snapshots the build instance: it shreds cloud-init artefacts, SSH host keys and every user’s authorized_keys, instance logs, AWSTOE working files and SSM Agent logs, deletes /var/tmp, random seeds and journal files, and truncates /etc/machine-id so each launched instance gets a unique ID. Windows images go through Sysprep instead.
If you override user data in the recipe, that script does not run unless your user data creates an empty file named perform_cleanup in the working directory. You can skip individual sections by creating marker files such as skip_cleanup_ssh_files (no file extension), but the documentation is blunt that skipping can leave SSH keys and account details in the AMI.
The SSM Agent logic is similarly subtle. On Linux, Image Builder installs the agent if the base image lacks it and removes it before creating the image; on Windows it does not install the agent at all, so your Windows base image must already have it. uninstallAfterBuild: true always removes the agent; false keeps it; and if you omit the setting, leave user data alone, and Image Builder installed the agent itself, it removes it.
Components: AWSTOE Documents, Phases and Stages
Image Builder performs every on-instance action through the AWS Task Orchestrator and Executor (AWSTOE), a standalone application it installs on the build and test instances (or inside the container for container builds). A component is an AWSTOE YAML document:
name: InstallAndVerifyAgent
description: Download an agent from S3, install it, verify the service.
schemaVersion: 1.0
parameters:
- AgentVersion:
type: string
default: "2.4.1"
description: Agent version to install
phases:
- name: build
steps:
- name: Download
action: S3Download
timeoutSeconds: 120
onFailure: Abort
maxAttempts: 3
inputs:
- source: 's3://amzn-s3-demo-bucket/agent-{{ AgentVersion }}.rpm'
destination: '/tmp/agent.rpm'
- name: Install
action: ExecuteBash
inputs:
commands:
- 'sudo yum install -y {{ build.Download.inputs[0].destination }}'
- name: validate
steps:
- name: ServiceEnabled
action: ExecuteBash
inputs:
commands:
- 'systemctl is-enabled my-agent'
- name: test
steps:
- name: ServiceRunning
action: ExecuteBash
inputs:
commands:
- 'systemctl is-active my-agent'
The build process has two stages, and the phases map onto them strictly:
| Stage | Instance | Phases that run | What happens after |
|---|---|---|---|
| Build (pre-snapshot) | Build instance launched from the base image | build, then validate | Clean-up, stop, snapshot → candidate AMI (or container image) |
| Test (post-snapshot) | A new instance launched from the candidate AMI (containers: same instance) | test (plus container-host-test for containers) | Image created and distributed only if every test passes |
The rules the exam likes: a component runs in the build stage only if it defines build or validate; a component runs in the test stage only if it defines test and no other phases (that is what makes it a test component rather than a build component with a test phase). Because the stages run on different instances, chaining expressions cannot cross stages; pass values with parameters instead.
Step-level fields:
| Field | Default | Notes |
|---|---|---|
timeoutSeconds | 7,200 (120 min) | -1 means no timeout; 0 and other negatives are invalid |
onFailure | Abort | Continue runs remaining steps but marks the document Failed; Ignore marks the step IgnoredFailure and the document SuccessWithIgnoredFailure |
maxAttempts | 1 | Retries before onFailure applies |
disableLogging | false | Set at document, phase or step level for steps that handle sensitive data |
Chaining uses {{ phase.step.inputs.variable }} or {{ phase.step.outputs.variable }} (zero-based [index] for lists), only inside inputs, and the expression must be quoted. AWSTOE writes a log folder per run (/var/lib/amazon/toe/TOE_<timestamp>_<id> on Linux and macOS, $env:ProgramFiles\Amazon\TaskOrchestratorAndExecutor on Windows) containing document.yaml, application.log, detailedoutput.json, console.log and chaining.json. If the infrastructure configuration names an S3 bucket, the same logs are copied there.
Action modules you should recognise
| Module | Platform | What to remember |
|---|---|---|
ExecuteBash | Linux, macOS | Inline commands; exit code 194 triggers a reboot, so scripts must be idempotent |
ExecutePowerShell | Windows | commands or file, not both; exit code 3010 triggers a reboot |
ExecuteBinary | All | path + arguments |
ExecuteDocument | All | Runs nested component documents, up to three levels deep, no cycles |
Assert | All | Comparison/logical operators; false marks the step Failed (the natural test-phase action) |
S3Download / S3Upload / WebDownload | All | expectedBucketOwner for bucket-ownership checks; prefix downloads capped at 1,000 objects; WebDownload supports checksums |
UpdateOS | Linux, Windows | Installs all available updates; include/exclude filter the set; uses yum or apt-get; does not support Amazon Linux 2023 (rebuild on the new AL2023 base AMI instead); on Windows, Microsoft Update must be opted into with a prior PowerShell step to pick up non-OS products |
Reboot | Linux, Windows | delaySeconds default 0; no step timeout; retries bounded by the SSM CommandRetryLimit |
InstallMSI / UninstallMSI | Windows | reboot: Allow|Force|Skip; MSI exit codes 1641/3010 are treated as success |
SetRegistry | Windows | HKLM/HKCU/… paths; SZ, DWORD, QWORD, BINARY, MULTI_SZ types |
| File modules | Mostly all | CreateFile, AppendFile, CopyFile, DeleteFile, SetFilePermissions (owner/permission modules are Linux and macOS only) |
Managed components (owner Amazon) include OS update, CloudWatch agent, AWS CLI, reboot, simple boot tests and STIG hardening components at low/medium/high levels for Linux and Windows at no extra charge. CIS Benchmark hardening is a Marketplace subscription component, image recipes only. When a managed component reaches end of support, affected accounts receive about four weeks’ notice through the AWS Health Dashboard listing the recipes that use it.
Infrastructure Configuration: Where the Build Runs
The infrastructure configuration answers where and as whom the temporary build and test instances run. Exam-relevant details:
- Instance types: a list, not a single type. Image Builder launches the first type with available capacity, so listing several reduces transient
InsufficientInstanceCapacityfailures. If none is available in the Region, the build fails. macOS images need Mac instance types on a Dedicated Host you allocated beforehand. - Instance profile: the role on the build and test instances. It needs
EC2InstanceProfileForImageBuilder,EC2InstanceProfileForImageBuilderECRContainerBuilds(container builds) andAmazonSSMManagedInstanceCore, plus whatever your components need (for examples3:GetObjecton your installers) ands3:PutObjecton the logging bucket if S3 logging is configured. This is the single most common “why did my component fail” answer. - Networking: if you specify a subnet you must specify at least one security group. The instance must reach Systems Manager (Image Builder drives every on-instance action through Run Command, which you cannot opt out of) and whatever your components download from. Private subnets work with a NAT gateway or interface endpoints for
ssm,ssmmessages,ec2messages,imagebuilderand S3. Container builds additionally need internet access to pull from Docker Hub and to fetch the AWS CLI. - Troubleshooting:
terminateInstanceOnFailure(default true) and a key pair let you keep and inspect a failed build instance;terminateInstanceOnCompletion: falsekeeps a successful one. - IMDS options: set
httpTokens: requiredso the build and test instances enforce IMDSv2, and raise the hop limit for container builds. - SNS topic: build-status notifications. If the topic is KMS-encrypted, the key must live in the same account as Image Builder; cross-account keys do not work.
- Enhanced image metadata (
enhancedImageMetadataEnabled, default true): installs an SSM Inventory association on the build instance to record OS and package versions.
Changes to an infrastructure configuration take effect on the next build; they never alter images already created, and you cannot delete a configuration while a pipeline references it.
A recent addition is the parent instance option: instead of launching a new instance, Image Builder detaches a random InService, SSM-online instance of the same platform and architecture from an Auto Scaling group you name, tags it CreatedBy: EC2 Image Builder, and builds on it. The group’s desired capacity does not change, so it launches a replacement. The instance keeps the launch template’s instance type, profile, subnet and volumes, so the recipe’s base image, block device mappings and user data are ignored. The pipeline needs an execution role (the service-linked role cannot touch Auto Scaling) with autoscaling:DescribeAutoScalingGroups, autoscaling:DetachInstances and ec2:CreateTags/DeleteTags, and Image Builder waits at most capacityWaitTimeoutSeconds (one hour maximum) with no fallback to launching a fresh instance.
Workflows: Build, Test and Distribution Stages
Image Builder runs the image creation process as three stages, each driven by a workflow of the matching type:
| Order | Stage | Workflow type | Default behaviour |
|---|---|---|---|
| 1 | Build | BUILD | Launch build instance, run build components, create AMI or container image |
| 2 | Test | TEST | Launch test instance from the new image, run test components, optionally collect scan findings |
| 3 | Distribution | DISTRIBUTION | Copy to target Regions/accounts, modify attributes, apply launch templates and license configurations |
Rules that show up as distractors:
- An image or pipeline can have at most one build workflow, one distribution workflow, and one or more test workflows, ten in total.
- The distribution workflow is optional, and omitting it does not skip distribution. Image Builder still runs the attached distribution configuration. To skip distribution entirely you supply a null or empty distribution configuration. To skip build or test, omit a workflow of that type (test-only workflows that validate an existing AMI, or distribution-only workflows, are legitimate designs).
- Workflow documents are limited to 15 steps, 25 parameters, 25 outputs and 16,000 bytes inline (use an S3
uribeyond that). Workflows are immutable; clone or version them. - Each step ends
COMPLETED,SKIPPED(anifcondition was false),FAILED(includingtimeoutSecondsexceeded),WAITINGorCANCELLED.onFailure: Abort(default) fails the workflow and, withrollbackEnabled: true(also the default), rolls completed steps back in reverse order;onFailure: Continueruns the remaining steps with no rollback. Actions with no rollback recordNO_ROLLBACK. - A
WaitForActionstep pauses the workflow and emits the EventBridge eventEC2 Image Builder Workflow Step Waiting. Something external (a Lambda function, a Step Functions state machine, a human) callsSendWorkflowStepActionwithRESUMEorSTOP. This is how you build a manual approval gate between “tested” and “distributed to production Regions”.
Managed workflows come in three families: standard (build-image, test-image, build-container, test-container), express (express-build-image, express-test-image, express-build-container, which wait only for the instance running state instead of full EC2 status checks and skip metadata or scan collection, for faster builds), and scan (test-ami-scan, inserted automatically for on-demand AMI scans).
The execution role
Workflows, distribution extras and the parent-instance feature run under an execution role that Image Builder assumes. AWS explicitly recommends that you do not pass the AWSServiceRoleForImageBuilder service-linked role; instead create a custom role with the EC2ImageBuilderExecutionPolicy managed policy. The stated reason is governance: a custom role keeps your SCPs and RCPs in effect for the operations Image Builder performs on your behalf, and you can add exactly the extra permissions a feature needs (Fast Launch, SSM parameter writes, Auto Scaling).
Distribution: Getting the Image Where It Needs to Go
A distribution configuration is a list of distributions, exactly one per Region, each defining at least one output. The AMI outputs you can combine per Region:
| Output | Key fields | What it does |
|---|---|---|
amiDistributionConfiguration | name (with {{ imagebuilder:buildDate }} and {{ imagebuilder:buildVersion }}), description, amiTags, kmsKeyId, targetAccountIds, launchPermission | Names, tags, encrypts, copies and shares the AMI |
launchPermission | userIds (account IDs or all for public), userGroups, organizationArns, organizationalUnitArns | Grants launch rights. The AMI stays in your account and you keep paying for its snapshots |
targetAccountIds | Account IDs | Copies the AMI into each target account, which then owns and pays for its copy. Organizations and OUs never receive copies |
launchTemplateConfigurations | launchTemplateId, accountId, setDefaultVersion (default true) | Creates a new launch template version with the new AMI ID; up to 5 per Region |
ssmParameterConfigurations | parameterName, amiAccountId, dataType (aws:ec2:image default, or text) | Writes the output AMI ID to Parameter Store; up to 10 per Region |
licenseConfigurationArns | License Manager ARNs | Associates self-managed licenses |
fastLaunchConfigurations | enabled, snapshotConfiguration.targetResourceCount, maxParallelLaunches (≥ 6), launchTemplate | Windows Fast Launch pre-provisioned snapshots; same-account only |
s3ExportConfiguration | roleName (typically vmimport), diskImageFormat (VMDK, VHD, RAW), s3Bucket, s3Prefix | Exports VM disks to S3 for on-premises hypervisors |
Behaviours that decide exam answers:
- Encryption scope. The KMS key in a distribution applies only to copies sent to other Regions or accounts. The AMI that the build creates in your own account in the source Region is encrypted by the recipe’s block device mapping (
encrypted: true,kmsKeyId), not by the distribution configuration. Questions about an “unencrypted source AMI despite a KMS key in distribution settings” are testing this line. - Settings are frozen per image. Image Builder saves a copy of the distribution settings with each image at creation. Editing the distribution configuration affects only images built afterwards; retries of an in-progress image use the saved copy. Removing a Region does not remove AMIs already copied there.
- AMI names are made unique. If you supply a name, Image Builder appends a build timestamp; if you omit it, it uses the recipe name plus timestamp.
- Launch template updates are gated. Image Builder rejects a configuration that updates a launch template in an account unless that account is in
targetAccountIdsor inlaunchPermission.userIdsfor the Region, or the AMI is public. Launch permission granted through an organization or OU ARN does not satisfy this check. SetsetDefaultVersion: falsewhen another process (for example an Auto Scaling instance refresh you trigger deliberately) decides when the new version goes live. The CodeDeploy-style rollout that consumes the new version is covered in our deployment strategies guide. - SSM parameter output needs
ssm:PutParameteron the execution role and, because the default data typeaws:ec2:imagemakes Systems Manager validate the value as an AMI ID,ec2:DescribeImagestoo. An existing parameter must beStringwith a matching data type. For an opt-in Region, the AMI copy can succeed while the parameter write fails unless the source account has enabled that Region. CloudFormation or Terraform that reads the parameter gets the new AMI without a template change, which is the standard “promote the golden AMI without editing IaC” pattern. - Concurrency quota: 50 concurrent AMI copies per destination Region.
Cross-account distribution
To copy an AMI into another account, the destination account must have an IAM role named exactly EC2ImageBuilderDistributionCrossAccountRole with the managed policy Ec2ImageBuilderCrossAccountDistributionAccess attached and a trust policy that allows the source account to assume it. For encrypted AMIs, Image Builder decrypts in the source account and re-encrypts in the destination with the key you designated for that Region, so:
- the source account needs a customer managed key in every Region it builds in and distributes to, with a key policy that allows the destination accounts to use it;
- the destination role needs an inline policy with
kms:Encrypt,kms:Decrypt,kms:ReEncrypt*,kms:GenerateDataKey*,kms:DescribeKey,kms:CreateGrant,kms:ListGrantsandkms:RevokeGrant; - if the distribution also updates launch templates or SSM parameters in the destination, the role needs the additional
ec2:CreateLaunchTemplateVersion/ec2:ModifyLaunchTemplate(conditioned onaws:ResourceTag/CreatedBy = EC2 Image Builder) orssm:PutParameterstatements, because the managed policy does not cover them.
You cannot create an unencrypted copy of an encrypted snapshot, and if you omit a customer managed key Image Builder falls back to the default EBS key, whose snapshots cannot be shared cross-account. If a cross-account distribution fails on a missing role or key policy, fix it and retry the image: the distribution phase reruns without rebuilding.
Distribute and retry without rebuilding
Two operations decouple distribution from the build. DistributeImage runs only the distribution phase against an existing source (an AMI ID, an ssm:/path parameter whose value is an AMI ID, or an Image Builder image ARN, with or without the build number) and takes an execution role at call time. RetryImage reruns an image that is FAILED or CANCELLED in its test or distribution phase from the start of that phase, reusing the original execution role and saved settings; AMI copies that already landed are reused rather than re-copied. Limits: AMI images only, created within the last 30 days, at most 10 retries, and it counts against the concurrent build quota.
Security Scanning with Amazon Inspector
Image Builder offers three scan types, and an AMI pipeline can use only one of the two AMI types:
| Scan type | What is scanned | Requires Inspector activation? | Where findings live | Can it fail the image? |
|---|---|---|---|---|
TEST_INSTANCE_CVE (default for AMIs) | The test instance during the test stage | Yes, Inspector for EC2 in the account and Region | Snapshot saved with the image; Image Builder Security findings pages; ListImageScanFindings | No. It reports; the test instance is terminated afterwards |
ON_DEMAND_AMI_CVE | The output AMI itself | No | Amazon EC2 console / GetImageFindings; not in Image Builder’s findings pages | Yes, via scanResultCondition |
| Container image scan | A copy pushed to ECR (default repo image-builder-image-scanning-repository) | Yes, Inspector for ECR; repo must not use MANUAL scan frequency | Saved with the image | No |
The scan result condition is the DOP-C02 answer to “prevent vulnerable AMIs from being distributed”. It has two fields: maximumSeverityLabel (INFORMATIONAL, LOW, MEDIUM or HIGH; a counted CRITICAL finding always fails the image) and fixAvailable (default true, meaning only findings with an available fix count). With a condition set, Image Builder waits for the Inspector scan (the test-ami-scan managed workflow, WaitForAmiScanTimeoutSeconds up to 43,500 seconds, about 12 hours) and fails the image in the test stage if the threshold is exceeded, so distribution never happens. Without a condition, the scan is started and the build continues without waiting.
Constraints: on-demand scans need imageScanningEnabled: true, imageTestsEnabled: true (otherwise InvalidParameterCombinationException), an image recipe (not a container recipe), an Inspector-supported OS, and, if you skip the build stage, your account must own the parent AMI. Inspector charges per successful on-demand scan.
For test-instance scans, Image Builder polls Inspector for up to 15 minutes by default; ACCESS_DENIED, INTERNAL_ERROR and UNSUPPORTED_OS are terminal, a timeout records UNKNOWN, and a failed scan cannot be repeated for the same image; you build a new one. Because activation is checked only when the pipeline is created or updated, deactivating Inspector later silently produces images with no findings. The broader detect-and-remediate picture (Security Hub, Config, Inspector findings routed to automation) is in our security and compliance automation guide.
Scheduling, Dependency Gating and Auto-Disable
A pipeline runs manually (start-image-pipeline-execution), on a cron expression, or on a rate expression such as rate(1 day). The cron format has six fields, Minute Hour Day Month Day-of-week Year, and either Day or Day-of-week must be ?:
cron(0 10 ? * mon-fri *) # 10:00 every weekday
cron(30 22 ? * tue#2 *) # 22:30 on the second Tuesday of the month
Times are UTC unless you set timezone in IANA format (America/Los_Angeles). The Schedule Builder in the console defaults to weekly.
The schedule is only half the trigger. The other half is pipelineExecutionStartCondition, discussed above: the default EXPRESSION_MATCH_AND_DEPENDENCY_UPDATES_AVAILABLE skips a run when nothing in the recipe has a newer matching version. Note the difference from an EventBridge schedule rule that targets the pipeline: an EventBridge rule always starts a build when it fires, with no dependency gating and no auto-disable, and the service’s own schedule can also be evaluated for dependency updates without EventBridge. You can also run a pipeline in response to an arbitrary event (a new base AMI published to an SSM parameter, a component version created) by making Image Builder an EventBridge target; our EventBridge guide covers the routing side.
autoDisablePolicy protects you from a scheduled pipeline that fails forever. You set failureCount up to 10 (default 5 if the policy is omitted). Only scheduled executions count: a manual run that succeeds resets the counter, a manual run that fails leaves it unchanged, and updating the pipeline resets it. When the limit is exceeded, Image Builder disables the pipeline and emits EC2 Image Builder Image Pipeline Automatically Disabled.
Image tests are on by default (imageTestsEnabled: true). The timeoutMinutes field accepts 60 to 1,440 and stores 720 if omitted, but the documentation states it is not currently active and is ignored, which makes it a tempting but wrong answer to “limit test duration”.
Build and pipeline logs go to CloudWatch Logs in /aws/imagebuilder/<image-name> (stream per version/build) and /aws/imagebuilder/pipeline/<pipeline-name> (stream per day); you can point both at custom log groups if the execution role can create streams and put events.
Image Lifecycle Policies
Without lifecycle management, a weekly pipeline leaves dozens of AMIs and their snapshots behind. A lifecycle policy targets AMI_IMAGE or CONTAINER_IMAGE resources selected by recipes (up to 50 recipe versions, with x.x.x/1.x.x/1.0.x wildcards resolved at execution time) or by tags (up to 50 key-value pairs), and applies up to three rules in a fixed order, Deprecate, then Disable, then Delete:
| Rule | Effect on the Image Builder image | Optional effect on the AMI / ECR image |
|---|---|---|
| Deprecate (by age) | Status Deprecated; pipelines still run | Set an AMI deprecation date. The AMI remains launchable |
| Disable (by age) | Status Disabled; pipelines stop running for this image | Disable the AMI so no new instances can launch from it |
| Delete (by age or by count) | Image resource removed | Deregister AMIs and delete their snapshots, or delete ECR images. “Retain at least one image per recipe” and retain-by-tag lists protect survivors |
Exclusion rules keep public AMIs, AMIs in named Regions, AMIs shared with named accounts, AMIs launched within a recent window, and tagged AMIs out of scope. “By count” keeps the N most recent images per recipe version.
The policy needs an execution role that trusts imagebuilder.amazonaws.com with the managed policy EC2ImageBuilderLifecycleExecutionPolicy; the service-linked role is explicitly not compatible and is filtered out of the console. For AMIs copied into other accounts via targetAccountIds, each destination account needs a role named exactly Ec2ImageBuilderCrossAccountLifecycleAccess with the same managed policy and a trust policy conditioned on aws:SourceAccount. For container images, tag the ECR repositories LifecycleExecutionAccess: EC2 Image Builder. If you only shared AMIs with launch permissions, nothing left your account and no cross-account role is needed.
Observability: Image States and EventBridge Events
Every Image Builder event uses the source aws.imagebuilder on the default event bus and is delivered best-effort, so confirm state with GetImage before acting on something irreversible.
| Detail type | When |
|---|---|
EC2 Image Builder Image State Change | Any image status transition. Statuses: CREATING, IMPORTING, BUILDING, TESTING, DISTRIBUTING, INTEGRATING (post-distribution configurations such as launch templates and license associations; absent if there is no distribution configuration), AVAILABLE, CANCELLED, FAILED (with state.reason) |
EC2 Image Builder CVE Detected | After a CollectImageScanFindings step, even with zero findings; reports critical, high, medium and all counts only (not low/informational). Not emitted for on-demand AMI scans |
EC2 Image Builder Workflow Step Waiting | A WaitForAction step paused; carries workflow-step-execution-id for SendWorkflowStepAction |
EC2 Image Builder Image Pipeline Automatically Disabled | The auto-disable threshold was exceeded; carries consecutive-failures |
A typical DOP-C02 design chains these: Image State Change → AVAILABLE triggers a Lambda function that reads the AMI ID from the event’s resources (or the SSM parameter Image Builder wrote) and starts an Auto Scaling instance refresh, while FAILED posts to the on-call channel. The SNS topic in the infrastructure configuration is the simpler alternative when you only need a notification.
Putting It Together: A Reference Golden-AMI Pipeline
# 1. Component: patch, harden, install the CloudWatch agent (managed components, latest versions)
# plus one custom component created from a YAML document in S3
aws imagebuilder create-component \
--name install-app-agent --semantic-version 1.0.x --platform Linux \
--supported-os-versions "Amazon Linux 2023" \
--uri s3://amzn-s3-demo-bucket/components/install-app-agent.yaml
# 2. Recipe: latest AL2023 managed image, encrypted root volume, wildcards everywhere
aws imagebuilder create-image-recipe --cli-input-json file://recipe.json
# recipe.json (abridged)
# {
# "name": "al2023-golden", "semanticVersion": "1.0.x",
# "parentImage": "arn:aws:imagebuilder:us-east-1:aws:image/amazon-linux-2023-x86/x.x.x",
# "components": [
# {"componentArn": "arn:aws:imagebuilder:us-east-1:aws:component/amazon-cloudwatch-agent-linux/x.x.x"},
# {"componentArn": "arn:aws:imagebuilder:us-east-1:aws:component/stig-build-linux-high/x.x.x"},
# {"componentArn": "arn:aws:imagebuilder:us-east-1:111122223333:component/install-app-agent/x.x.x"},
# {"componentArn": "arn:aws:imagebuilder:us-east-1:aws:component/simple-boot-test-linux/x.x.x"}
# ],
# "blockDeviceMappings": [{"deviceName": "/dev/xvda",
# "ebs": {"volumeSize": 30, "volumeType": "gp3", "encrypted": true,
# "kmsKeyId": "arn:aws:kms:us-east-1:111122223333:key/<key-id>"}}],
# "additionalInstanceConfiguration": {"systemsManagerAgent": {"uninstallAfterBuild": false}},
# "amiTags": {"Environment": "shared", "Owner": "platform"}
# }
# 3. Infrastructure: private subnet, several instance types, IMDSv2, logs to S3
aws imagebuilder create-infrastructure-configuration \
--name golden-infra --instance-profile-name ImageBuilderInstanceProfile \
--instance-types t3.medium t3a.medium m6i.large \
--subnet-id subnet-0123 --security-group-ids sg-0123 \
--instance-metadata-options httpTokens=required,httpPutResponseHopLimit=2 \
--logging s3Logs='{s3BucketName=amzn-s3-demo-logs,s3KeyPrefix=imagebuilder}' \
--terminate-instance-on-failure --sns-topic-arn arn:aws:sns:us-east-1:111122223333:image-builds
# 4. Distribution: copy to two workload accounts in two Regions, publish the AMI ID to SSM,
# create a new launch template version but let instance refresh decide when to use it
aws imagebuilder create-distribution-configuration --cli-input-json file://dist.json
# dist.json (abridged, us-east-1 entry)
# {"region": "us-east-1",
# "amiDistributionConfiguration": {
# "name": "al2023-golden-{{ imagebuilder:buildDate }}",
# "targetAccountIds": ["222233334444", "333344445555"],
# "kmsKeyId": "arn:aws:kms:us-east-1:111122223333:key/<key-id>"},
# "ssmParameterConfigurations": [{"parameterName": "/golden/al2023/latest", "dataType": "aws:ec2:image"}],
# "launchTemplateConfigurations": [{"launchTemplateId": "lt-0abc", "accountId": "222233334444",
# "setDefaultVersion": false}]}
# 5. Pipeline: weekly, dependency-gated, on-demand AMI scan that blocks HIGH+ with a fix,
# auto-disable after 3 consecutive scheduled failures
aws imagebuilder create-image-pipeline \
--name al2023-golden-weekly \
--image-recipe-arn arn:aws:imagebuilder:us-east-1:111122223333:image-recipe/al2023-golden/x.x.x \
--infrastructure-configuration-arn arn:aws:imagebuilder:us-east-1:111122223333:infrastructure-configuration/golden-infra \
--distribution-configuration-arn arn:aws:imagebuilder:us-east-1:111122223333:distribution-configuration/golden-dist \
--execution-role arn:aws:iam::111122223333:role/ImageBuilderExecutionRole \
--schedule 'scheduleExpression=cron(0 3 ? * sun *),timezone=Etc/UTC,pipelineExecutionStartCondition=EXPRESSION_MATCH_AND_DEPENDENCY_UPDATES_AVAILABLE,autoDisablePolicy={failureCount=3}' \
--image-scanning-configuration 'imageScanningEnabled=true,imageScanningTypes=ON_DEMAND_AMI_CVE,scanResultCondition={maximumSeverityLabel=HIGH,fixAvailable=true}' \
--image-tests-configuration imageTestsEnabled=true \
--status ENABLED
Each targetAccountIds entry presupposes the EC2ImageBuilderDistributionCrossAccountRole in that account (with the KMS inline policy, because the AMI is encrypted, and the launch template statements, because we update one there). Multi-account plumbing of this kind is the same pattern as cross-account CodePipeline deployments, covered in our multi-account CI/CD guide.
Scenario Drill
| Scenario | Answer | Why |
|---|---|---|
| Weekly scheduled pipeline never builds, though AWS publishes new base AMIs | Recipe pins exact versions; switch base image and components to x.x.x wildcards (or an ssm: base image) so EXPRESSION_MATCH_AND_DEPENDENCY_UPDATES_AVAILABLE sees updates, or set EXPRESSION_MATCH_ONLY | Dependency gating only works with semantic version filters |
| Pipeline must rebuild every Sunday even if nothing changed | pipelineExecutionStartCondition: EXPRESSION_MATCH_ONLY | Default condition skips unchanged builds |
| Components fail with S3 access denied on the build instance | Add s3:GetObject to the instance profile role, keep EC2InstanceProfileForImageBuilder + AmazonSSMManagedInstanceCore | Components run on the instance, under the instance profile |
| Build hangs then fails; instance has no public IP, no NAT | Add interface endpoints for ssm, ssmmessages, ec2messages, imagebuilder and an S3 gateway endpoint | Image Builder drives the instance through Systems Manager Run Command |
| Need to inspect a failed build instance | terminateInstanceOnFailure: false + key pair in the infrastructure configuration | Instances are otherwise terminated on failure |
| AMI copied to account B cannot be launched there; snapshots encrypted | Use a customer managed key with a key policy allowing B; add the KMS inline policy to EC2ImageBuilderDistributionCrossAccountRole in B; never the default aws/ebs key | Default EBS key snapshots cannot be shared; copies are re-encrypted in the destination |
| Share one AMI with the whole organization at minimal cost | launchPermission.organizationArns | Launch permission, no copies; snapshot storage stays in the owner account |
| Each business unit must own its own copy of the AMI | targetAccountIds + the cross-account role in each account | Copies are owned and billed by the target |
| Source-Region AMI is unencrypted although distribution has a KMS key | Set encrypted: true + kmsKeyId in the recipe’s block device mapping | Distribution encryption only applies to copies to other Regions/accounts |
| Auto Scaling group must pick up the new AMI with no template edits | launchTemplateConfigurations with setDefaultVersion: true (or false + instance refresh on a chosen version); or ssmParameterConfigurations read by CloudFormation | Image Builder creates a new launch template version with the AMI ID |
| Launch template update to account B is rejected | Add B to targetAccountIds or launchPermission.userIds for that Region | OU/organization permissions do not satisfy the check |
| Block distribution of AMIs with high-severity CVEs that have fixes | ON_DEMAND_AMI_CVE with scanResultCondition: maximumSeverityLabel=MEDIUM, fixAvailable=true | Test-instance scans only report; on-demand scans with a condition fail the image |
| Findings must appear on Image Builder’s Security findings pages | TEST_INSTANCE_CVE with Inspector activated for EC2 | On-demand results live in the EC2 console / GetImageFindings |
| Manual approval before copying to production Regions | Custom distribution workflow with a WaitForAction step; approve via SendWorkflowStepAction RESUME on the Workflow Step Waiting event | Workflow gates are native; no need for a separate pipeline |
| Test an existing AMI without rebuilding it | Pipeline or CreateImage with a test workflow only (omit the build workflow); recipe may have zero components | Stages are independently skippable |
| Distribution to a new Region failed on a missing role; avoid a 40-minute rebuild | Fix the role, then RetryImage | Retries rerun from the failed test/distribution phase, reusing copies that succeeded |
| Pipeline keeps failing nightly and pages the team | autoDisablePolicy.failureCount (max 10, default 5) + rule on the Automatically Disabled event | Only scheduled failures count |
| Snapshot costs balloon from months of weekly builds | Lifecycle policy: Deprecate at 30 d, Disable at 60 d, Delete by count keeping 3 with deregister AMIs + delete snapshots, exclude AMIs launched in the last 7 days | Rules apply Deprecate → Disable → Delete; needs EC2ImageBuilderLifecycleExecutionPolicy role |
| Old AMIs must stay launchable but be flagged | Deprecate rule with “Deprecate AMIs” | Deprecated AMIs remain launchable; disabled ones do not |
| Windows build fails: “SSM Agent not found” | Bake the agent into the Windows base image first | Image Builder installs the agent automatically on Linux only |
| Linux image still contains the build host’s SSH keys | User data override removed the clean-up; create perform_cleanup in the working directory, and don’t create skip_cleanup_ssh_files | Clean-up script is skipped when user data is overridden |
UpdateOS step fails on Amazon Linux 2023 | Not supported on AL2023; rebuild from the latest AL2023 base image (x.x.x) | Documented limitation |
| Secrets appear in CloudTrail from a component | Component parameters are logged in plain text; pass a Secrets Manager/Parameter Store reference | Documented warning |
| Need SCPs to constrain what Image Builder does on your behalf | Custom execution role with EC2ImageBuilderExecutionPolicy, not the service-linked role | Stated reason in the docs |
Common Mistakes
- Pinning versions and expecting dependency-triggered builds.
EXPRESSION_MATCH_AND_DEPENDENCY_UPDATES_AVAILABLEis the default and requires wildcards. Pinned recipes plus the default condition equals a pipeline that never runs. - Putting component permissions on the execution role. Components run on the instance under the instance profile; the execution role is for workflow actions, distribution extras and Auto Scaling. Two roles, two jobs.
- Expecting the distribution KMS key to encrypt the source AMI. It does not. Recipe block device mapping encrypts the source; distribution keys encrypt copies.
- Granting launch permission to an OU and then configuring a launch template update in a member account. The OU grant does not meet the launch-template precondition.
- Treating a test-instance scan as a gate. Only an on-demand AMI scan with a
scanResultConditionfails the image. Test-instance scans report after the fact. - Assuming omitting the distribution workflow skips distribution. It does not; the distribution configuration still runs.
- Defining
buildandtestphases in one document and expecting it to run as a test component. A test-stage component must definetestand nothing else. - Editing a recipe. You cannot. New version, new ARN; update the pipeline’s recipe ARN (or reference the recipe with
x.x.x). - Using the service-linked role as the execution role. It works for basic flows but bypasses your SCP/RCP intent and lacks permissions for parent instances, Fast Launch and SSM outputs.
- Confusing
timeoutMinuteson image tests with an enforced limit. The docs say it is stored and ignored.
Practice Image-Pipeline Scenarios Under Exam Conditions
DOP-C02 questions in this space are long, and the discriminating word is usually small: every week, only when patched, own copy, block, without rebuilding, no public IP. The way to get fast and accurate is repetition against distractors written at the same altitude as the real exam, which is what Sailor.sh’s AWS Certified DevOps Engineer - Professional (DOP-C02) Mock Exam Bundle provides: full-length exams whose explanations spell out why the test-instance scan, the OU launch permission or the service-linked role is the wrong choice in a given scenario. Pair it with the DOP-C02 study plan to sequence Domain 1 artifacts, Domain 2 configuration management and Domain 5 security, and with our SDLC automation guide for the pipeline that will eventually consume the image you just built.
Frequently Asked Questions
Does EC2 Image Builder cost anything?
No. The service is free. You pay for the EC2 instances it launches to build and test, the EBS snapshots behind your AMIs, S3 log storage, ECR storage and pushes, and Amazon Inspector scans if enabled.
What is the difference between a build component and a test component?
A build component defines a build and/or validate phase and runs on the build instance before the snapshot. A test component defines only a test phase and runs on a fresh instance launched from the candidate image. A document that defines build plus test is a build component whose test phase also runs in the test stage.
Why does my scheduled pipeline skip runs?
The default start condition only builds when a base image or component with a semantic-version wildcard has a newer matching version. Either use x.x.x references (or an ssm: base image whose value changes) or switch to EXPRESSION_MATCH_ONLY.
How do I distribute an AMI to another account?
Add the account to targetAccountIds for the Region, and in that account create the role EC2ImageBuilderDistributionCrossAccountRole with the Ec2ImageBuilderCrossAccountDistributionAccess policy and a trust policy for your source account. For encrypted AMIs, use a customer managed key shared with the target and add the KMS inline policy to the role.
Launch permission or copy to target accounts, which should I use?
Launch permission (launchPermission) keeps a single AMI in your account that other accounts, OUs or the organization can launch from; you pay for storage. targetAccountIds copies the AMI so each target owns and manages (and can lifecycle) its own copy. Launch template and SSM parameter updates in other accounts require the account to be a target or in userIds.
Can Image Builder stop a vulnerable image from shipping?
Yes, with an on-demand AMI scan (ON_DEMAND_AMI_CVE) and a scanResultCondition. If any counted finding exceeds maximumSeverityLabel, the image fails in the test stage and is not distributed. Test-instance scans and container scans only record findings.
How do I update an Auto Scaling group with the new AMI automatically?
Use launchTemplateConfigurations so Image Builder creates a new launch template version (and optionally makes it the default), then start an instance refresh, typically from a Lambda function on the EC2 Image Builder Image State Change event with status AVAILABLE. Alternatively write the AMI ID to an SSM parameter and reference it from CloudFormation.
What happens to old AMIs?
Nothing, unless you create a lifecycle policy. Policies can deprecate (still launchable), disable (no new launches) and delete (deregister AMIs and delete snapshots) by age or by count, with exclusions for public, recently launched, Region-specific or tagged AMIs.
Can I keep the SSM Agent in the final image?
Yes. Set systemsManagerAgent.uninstallAfterBuild: false in the image recipe. If you omit the setting and Image Builder had to install the agent itself, it removes it before creating the AMI.
Conclusion
EC2 Image Builder rewards a resource-by-resource mental model. The recipe says what the image is and is immutable; semantic version wildcards in that recipe are what let a scheduled pipeline notice new base images and components. The infrastructure configuration decides where the temporary instances run and, through the instance profile, what your components may do. Workflows give you build, test and distribution stages you can skip, extend or gate with WaitForAction. The distribution configuration does the multi-Region, multi-account delivery, with the sharp edges around KMS scope, the cross-account role, launch template preconditions and SSM parameter outputs. Inspector reports unless you use an on-demand AMI scan with a condition, in which case it blocks. Lifecycle policies clean up in Deprecate → Disable → Delete order, and auto-disable stops a broken schedule from paging you forever. Learn which flag lives on which resource and the long DOP-C02 scenarios reduce to matching one constraint to one field.