Back to Blog

EC2 Image Builder Internals for the DOP-C02 Exam: Pipelines, Recipes & Semantic Versioning, AWSTOE Components & Action Modules, Build/Test/Distribution Workflows, Cross-Account Distribution, Launch Template & SSM Parameter Outputs, Inspector Scans, Lifecycle Policies and Auto-Disable

A practitioner's guide to EC2 Image Builder for the AWS Certified DevOps Engineer - Professional (DOP-C02) exam: how the six resource types fit together, semantic versioning and the x wildcard rules that make dependency-triggered builds work, image vs container recipes and their limits, AWSTOE component documents (build/validate/test phases, chaining, onFailure, action modules such as UpdateOS and Reboot), infrastructure configuration and the instance profile policies, build/test/distribution workflows with WaitForAction gates, distribution configurations (target accounts, launch permissions, KMS copies, launch template versions, SSM parameter output, Fast Launch, VM export), the cross-account role, Inspector test-instance vs on-demand AMI scans with scan result conditions, lifecycle policies (deprecate/disable/delete), auto-disable policies, EventBridge events and the post-build clean-up script, with a scenario drill and FAQ.

By Sailor Team , October 31, 2026

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.

ResourceWhat it definesMutable?
Image recipe / container recipeBase 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
ComponentA versioned AWSTOE YAML document that customises (build component) or tests (test component) the instanceNo. Same name + version with changed content creates a new build version
Infrastructure configurationWhere 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-failureYes. Changes apply to the next build
Distribution configurationWhere output images go after a successful test: Regions, target accounts, launch permissions, KMS keys, launch template updates, SSM parameters, license configurations, VM export, Fast LaunchYes, but each image keeps a copy of the settings it was built with
WorkflowThe ordered steps of one stage (BUILD, TEST or DISTRIBUTION), as a versioned YAML documentNo. Clone or create a new version
Image pipelineRecipe + infrastructure configuration + distribution configuration + workflows + schedule + scan settings + execution roleYes
Lifecycle policyRules that deprecate, disable or delete images (and their AMIs, snapshots or ECR images) by age or countYes

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:

ReferenceResolves to
x.x.x2.2.4
1.x.x1.7.8
1.6.x1.6.8
x.2.xError
1.x.8Error

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

FeatureImage recipeContainer recipe
OutputAMIDocker image in Amazon ECR
Base image sourcesAMI 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
Dockerfilen/aRequired template using {{{ imagebuilder:parentImage }}}, {{{ imagebuilder:environments }}} and {{{ imagebuilder:components }}}
Target repositoryn/aECR repository that must already exist
StorageBlock device mappings for the output AMI (incl. per-volume KMS encryption)Block device mappings for the build instance only
User dataSupported (overrides the default cloud-init)Not at recipe level
SSM Agent controlsystemsManagerAgent.uninstallAfterBuildn/a
Marketplace / CIS componentsSupportedNot 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 DEPRECATED or DISABLED status 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 /tmp on Linux and macOS and C:/ 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:

StageInstancePhases that runWhat happens after
Build (pre-snapshot)Build instance launched from the base imagebuild, then validateClean-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:

FieldDefaultNotes
timeoutSeconds7,200 (120 min)-1 means no timeout; 0 and other negatives are invalid
onFailureAbortContinue runs remaining steps but marks the document Failed; Ignore marks the step IgnoredFailure and the document SuccessWithIgnoredFailure
maxAttempts1Retries before onFailure applies
disableLoggingfalseSet 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

ModulePlatformWhat to remember
ExecuteBashLinux, macOSInline commands; exit code 194 triggers a reboot, so scripts must be idempotent
ExecutePowerShellWindowscommands or file, not both; exit code 3010 triggers a reboot
ExecuteBinaryAllpath + arguments
ExecuteDocumentAllRuns nested component documents, up to three levels deep, no cycles
AssertAllComparison/logical operators; false marks the step Failed (the natural test-phase action)
S3Download / S3Upload / WebDownloadAllexpectedBucketOwner for bucket-ownership checks; prefix downloads capped at 1,000 objects; WebDownload supports checksums
UpdateOSLinux, WindowsInstalls 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
RebootLinux, WindowsdelaySeconds default 0; no step timeout; retries bounded by the SSM CommandRetryLimit
InstallMSI / UninstallMSIWindowsreboot: Allow|Force|Skip; MSI exit codes 1641/3010 are treated as success
SetRegistryWindowsHKLM/HKCU/… paths; SZ, DWORD, QWORD, BINARY, MULTI_SZ types
File modulesMostly allCreateFile, 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 InsufficientInstanceCapacity failures. 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) and AmazonSSMManagedInstanceCore, plus whatever your components need (for example s3:GetObject on your installers) and s3:PutObject on 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, imagebuilder and 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: false keeps a successful one.
  • IMDS options: set httpTokens: required so 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:

OrderStageWorkflow typeDefault behaviour
1BuildBUILDLaunch build instance, run build components, create AMI or container image
2TestTESTLaunch test instance from the new image, run test components, optionally collect scan findings
3DistributionDISTRIBUTIONCopy 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 uri beyond that). Workflows are immutable; clone or version them.
  • Each step ends COMPLETED, SKIPPED (an if condition was false), FAILED (including timeoutSeconds exceeded), WAITING or CANCELLED. onFailure: Abort (default) fails the workflow and, with rollbackEnabled: true (also the default), rolls completed steps back in reverse order; onFailure: Continue runs the remaining steps with no rollback. Actions with no rollback record NO_ROLLBACK.
  • A WaitForAction step pauses the workflow and emits the EventBridge event EC2 Image Builder Workflow Step Waiting. Something external (a Lambda function, a Step Functions state machine, a human) calls SendWorkflowStepAction with RESUME or STOP. 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:

OutputKey fieldsWhat it does
amiDistributionConfigurationname (with {{ imagebuilder:buildDate }} and {{ imagebuilder:buildVersion }}), description, amiTags, kmsKeyId, targetAccountIds, launchPermissionNames, tags, encrypts, copies and shares the AMI
launchPermissionuserIds (account IDs or all for public), userGroups, organizationArns, organizationalUnitArnsGrants launch rights. The AMI stays in your account and you keep paying for its snapshots
targetAccountIdsAccount IDsCopies the AMI into each target account, which then owns and pays for its copy. Organizations and OUs never receive copies
launchTemplateConfigurationslaunchTemplateId, accountId, setDefaultVersion (default true)Creates a new launch template version with the new AMI ID; up to 5 per Region
ssmParameterConfigurationsparameterName, amiAccountId, dataType (aws:ec2:image default, or text)Writes the output AMI ID to Parameter Store; up to 10 per Region
licenseConfigurationArnsLicense Manager ARNsAssociates self-managed licenses
fastLaunchConfigurationsenabled, snapshotConfiguration.targetResourceCount, maxParallelLaunches (≥ 6), launchTemplateWindows Fast Launch pre-provisioned snapshots; same-account only
s3ExportConfigurationroleName (typically vmimport), diskImageFormat (VMDK, VHD, RAW), s3Bucket, s3PrefixExports 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 targetAccountIds or in launchPermission.userIds for the Region, or the AMI is public. Launch permission granted through an organization or OU ARN does not satisfy this check. Set setDefaultVersion: false when 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:PutParameter on the execution role and, because the default data type aws:ec2:image makes Systems Manager validate the value as an AMI ID, ec2:DescribeImages too. An existing parameter must be String with 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:ListGrants and kms:RevokeGrant;
  • if the distribution also updates launch templates or SSM parameters in the destination, the role needs the additional ec2:CreateLaunchTemplateVersion/ec2:ModifyLaunchTemplate (conditioned on aws:ResourceTag/CreatedBy = EC2 Image Builder) or ssm:PutParameter statements, 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 typeWhat is scannedRequires Inspector activation?Where findings liveCan it fail the image?
TEST_INSTANCE_CVE (default for AMIs)The test instance during the test stageYes, Inspector for EC2 in the account and RegionSnapshot saved with the image; Image Builder Security findings pages; ListImageScanFindingsNo. It reports; the test instance is terminated afterwards
ON_DEMAND_AMI_CVEThe output AMI itselfNoAmazon EC2 console / GetImageFindings; not in Image Builder’s findings pagesYes, via scanResultCondition
Container image scanA copy pushed to ECR (default repo image-builder-image-scanning-repository)Yes, Inspector for ECR; repo must not use MANUAL scan frequencySaved with the imageNo

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:

RuleEffect on the Image Builder imageOptional effect on the AMI / ECR image
Deprecate (by age)Status Deprecated; pipelines still runSet an AMI deprecation date. The AMI remains launchable
Disable (by age)Status Disabled; pipelines stop running for this imageDisable the AMI so no new instances can launch from it
Delete (by age or by count)Image resource removedDeregister 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 typeWhen
EC2 Image Builder Image State ChangeAny 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 DetectedAfter 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 WaitingA WaitForAction step paused; carries workflow-step-execution-id for SendWorkflowStepAction
EC2 Image Builder Image Pipeline Automatically DisabledThe 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

ScenarioAnswerWhy
Weekly scheduled pipeline never builds, though AWS publishes new base AMIsRecipe 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_ONLYDependency gating only works with semantic version filters
Pipeline must rebuild every Sunday even if nothing changedpipelineExecutionStartCondition: EXPRESSION_MATCH_ONLYDefault condition skips unchanged builds
Components fail with S3 access denied on the build instanceAdd s3:GetObject to the instance profile role, keep EC2InstanceProfileForImageBuilder + AmazonSSMManagedInstanceCoreComponents run on the instance, under the instance profile
Build hangs then fails; instance has no public IP, no NATAdd interface endpoints for ssm, ssmmessages, ec2messages, imagebuilder and an S3 gateway endpointImage Builder drives the instance through Systems Manager Run Command
Need to inspect a failed build instanceterminateInstanceOnFailure: false + key pair in the infrastructure configurationInstances are otherwise terminated on failure
AMI copied to account B cannot be launched there; snapshots encryptedUse a customer managed key with a key policy allowing B; add the KMS inline policy to EC2ImageBuilderDistributionCrossAccountRole in B; never the default aws/ebs keyDefault EBS key snapshots cannot be shared; copies are re-encrypted in the destination
Share one AMI with the whole organization at minimal costlaunchPermission.organizationArnsLaunch permission, no copies; snapshot storage stays in the owner account
Each business unit must own its own copy of the AMItargetAccountIds + the cross-account role in each accountCopies are owned and billed by the target
Source-Region AMI is unencrypted although distribution has a KMS keySet encrypted: true + kmsKeyId in the recipe’s block device mappingDistribution encryption only applies to copies to other Regions/accounts
Auto Scaling group must pick up the new AMI with no template editslaunchTemplateConfigurations with setDefaultVersion: true (or false + instance refresh on a chosen version); or ssmParameterConfigurations read by CloudFormationImage Builder creates a new launch template version with the AMI ID
Launch template update to account B is rejectedAdd B to targetAccountIds or launchPermission.userIds for that RegionOU/organization permissions do not satisfy the check
Block distribution of AMIs with high-severity CVEs that have fixesON_DEMAND_AMI_CVE with scanResultCondition: maximumSeverityLabel=MEDIUM, fixAvailable=trueTest-instance scans only report; on-demand scans with a condition fail the image
Findings must appear on Image Builder’s Security findings pagesTEST_INSTANCE_CVE with Inspector activated for EC2On-demand results live in the EC2 console / GetImageFindings
Manual approval before copying to production RegionsCustom distribution workflow with a WaitForAction step; approve via SendWorkflowStepAction RESUME on the Workflow Step Waiting eventWorkflow gates are native; no need for a separate pipeline
Test an existing AMI without rebuilding itPipeline or CreateImage with a test workflow only (omit the build workflow); recipe may have zero componentsStages are independently skippable
Distribution to a new Region failed on a missing role; avoid a 40-minute rebuildFix the role, then RetryImageRetries rerun from the failed test/distribution phase, reusing copies that succeeded
Pipeline keeps failing nightly and pages the teamautoDisablePolicy.failureCount (max 10, default 5) + rule on the Automatically Disabled eventOnly scheduled failures count
Snapshot costs balloon from months of weekly buildsLifecycle 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 daysRules apply Deprecate → Disable → Delete; needs EC2ImageBuilderLifecycleExecutionPolicy role
Old AMIs must stay launchable but be flaggedDeprecate 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 firstImage Builder installs the agent automatically on Linux only
Linux image still contains the build host’s SSH keysUser data override removed the clean-up; create perform_cleanup in the working directory, and don’t create skip_cleanup_ssh_filesClean-up script is skipped when user data is overridden
UpdateOS step fails on Amazon Linux 2023Not supported on AL2023; rebuild from the latest AL2023 base image (x.x.x)Documented limitation
Secrets appear in CloudTrail from a componentComponent parameters are logged in plain text; pass a Secrets Manager/Parameter Store referenceDocumented warning
Need SCPs to constrain what Image Builder does on your behalfCustom execution role with EC2ImageBuilderExecutionPolicy, not the service-linked roleStated reason in the docs

Common Mistakes

  1. Pinning versions and expecting dependency-triggered builds. EXPRESSION_MATCH_AND_DEPENDENCY_UPDATES_AVAILABLE is the default and requires wildcards. Pinned recipes plus the default condition equals a pipeline that never runs.
  2. 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.
  3. Expecting the distribution KMS key to encrypt the source AMI. It does not. Recipe block device mapping encrypts the source; distribution keys encrypt copies.
  4. 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.
  5. Treating a test-instance scan as a gate. Only an on-demand AMI scan with a scanResultCondition fails the image. Test-instance scans report after the fact.
  6. Assuming omitting the distribution workflow skips distribution. It does not; the distribution configuration still runs.
  7. Defining build and test phases in one document and expecting it to run as a test component. A test-stage component must define test and nothing else.
  8. Editing a recipe. You cannot. New version, new ARN; update the pipeline’s recipe ARN (or reference the recipe with x.x.x).
  9. 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.
  10. Confusing timeoutMinutes on 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.

Limited Time Offer: Get 80% off all Mock Exam Bundles | Sale ends in 7 days. Start learning today.

Claim Now