ICT CLI Specification#
Table of Contents#
Overview#
image-composer-tool is a command-line tool for generating custom images for
different operating systems, including
Azure Linux, Wind River eLxr, RCD, and
Edge Microvisor Toolkit.
The tool provides a flexible approach to creating and configuring
production-ready OS images with precise customization.
ICT uses a single CLI with subcommands to deliver a consistent user experience while maintaining flexibility. The tool’s architecture is built around the following files:
A global configuration file that defines system-wide settings like cache locations and provider configurations
Image template files in YAML format that define per-image build requirements
The tool follows a staged build process to support package caching, image caching, and various customization options that speed up development cycles and ensure reproducible builds.
CLI Flow#
The following diagram illustrates the high-level flow of the ICT
CLI, the commands of which begin with image-composer-tool:
flowchart TD
Start([ict]) --> Config[Load Configuration]
Config --> Commands{Commands}
Commands -->|build| Build[Build OS Image]
Build --> ReadTemplate[Read YAML Template]
ReadTemplate --> BuildProcess[Run Build Pipeline]
BuildProcess --> SaveImage[Save Output Image]
Commands -->|validate| Validate[Validate Template File]
Commands -->|config| ConfigCmd[Manage Configuration]
ConfigCmd --> ConfigOps[init/show]
Commands -->|cache| Cache[Manage Cache]
Cache --> CacheOps[Clean Cache]
Commands -->|version| Version[Show Version Info]
Commands -->|install-completion| Completion[Install Shell Completion]
%% Styling
classDef command fill:#b5e2fa,stroke:#0077b6,stroke-width:2px;
classDef process fill:#f8edeb,stroke:#333,stroke-width:1px;
class Start command;
class Build,Validate,ConfigCmd,Cache,Version,Completion command;
class ReadTemplate,BuildProcess,SaveImage,ConfigOps,CacheOps process;
The primary workflow is through the build command, which reads an image
template file and runs the build pipeline to create a new image.
See also:
Build Stages for the stages of the build pipeline
Usage#
image-composer-tool [global options] command [command options] [arguments...]
Global Options#
The ICT command-line utility uses a layered configuration approach, with command-line options taking priority over the configuration file settings:
Option |
Description |
|---|---|
|
Global configuration file. This file contains system-wide settings that apply to all image builds. If not specified, the tool searches for configuration files in standard locations. |
|
Log level: debug, info, warn, error (overrides config). Use debug for troubleshooting build issues. |
|
Tee logs to a specific file path (overrides |
|
Show help for any command or subcommand. |
|
Show |
Commands#
Build Command#
Build an OS image from an image template file. This is the primary command for creating custom OS images according to your requirements.
image-composer-tool build [flags] TEMPLATE_FILE
Arguments:
TEMPLATE_FILE- Path to the YAML image template file (required)
Flags:
Flag |
Description |
|---|---|
|
Number of concurrent download workers (overrides config). |
|
Package cache directory (overrides config). Proper caching significantly improves build times. |
|
Working directory for builds (overrides config). This directory is where images are constructed before being finalized. |
|
Enable verbose output (equivalent to –log-level debug). Displays detailed information about each step of the build process. |
|
Generate a dot file for the merged template dependency graph (user + defaults with resolved packages). |
|
When paired with |
|
Build from scratch: create fresh, unique cache and workspace directories (ignoring any existing caches), then remove them once the build finishes. The final image is copied into the configured |
|
Run a post-build inspection of the emitted overlay image and write the report to an artifact file (default: off). The report covers the partition layout, filesystem, bootloader, and SBOM of the finished image. It is written next to the emitted image in the build artifacts directory as |
|
Enable CVE analysis of the built image. Not yet implemented — passing this flag currently returns an error. |
|
Override |
Example:
# Build an image with default settings
sudo -E image-composer-tool build my-image-template.yml
# Build with custom workers and cache directory
sudo -E image-composer-tool build --workers 16 --cache-dir /tmp/cache my-image-template.yml
# Build with verbose output
sudo -E image-composer-tool build --verbose my-image-template.yml
# Build and generate dependency graphs
sudo -E image-composer-tool build --dotfile deps.dot my-image-template.yml
# Limit the graph to SystemConfig.Packages roots
sudo -E image-composer-tool build --dotfile system.dot --system-packages-only my-image-template.yml
# Overlay build with a post-build image inspection report written to
# <image-name>-<version>.inspect.txt in the build artifacts directory
sudo -E image-composer-tool build --inspect overlay-template.yml
# Overlay build overriding the baseline image path
sudo -E image-composer-tool build --baseline-image /images/base.raw overlay-template.yml
Note: The build command typically requires sudo privileges for operations like creating loopback devices and mounting filesystems.
Baseline image formats (overlay mode): baseline.source.format accepts raw (default), qcow2, vhd, and vhdx. Non-RAW baselines are converted to RAW with qemu-img before mounting, so qemu-img must be installed on the host when a non-RAW format is used (the build fails clearly if it is missing). The declared format is verified against the image’s actual format; a mismatch aborts the build. The user-supplied baseline is never modified — conversion writes into the build workspace.
Cancellation semantics: Sending SIGINT (Ctrl+C) or SIGTERM to a running build triggers cooperative cleanup before the tool exits:
The signal cancels an ambient
context.Contextthat is bound to every subsequent shell subprocess. Because those subprocesses are spawned into their own process group, a singleSIGTERMto the negative process-group id reapsbash,sudo, and the real tool (mmdebstrap,apt,mksquashfs,losetup,mkfs.*,xorriso,dracut,ukify,sbsign,qemu-img, …) together. Every ICT-spawned tool terminates onSIGTERM, so the group exits cooperatively. If the leader (bashacting as the group’scmd.Process) has not exited within 5 s, the Go runtime’sWaitDelayescalation sendsSIGKILLto that leader specifically —WaitDelaydoes not broadcastSIGKILLto the whole process group. The group is expected to be gone by then via the earlierSIGTERM; the leader-levelSIGKILLis a backstop for a wedged pipe on the leader itself, not a group-wide kill.Pure-Go HTTP work (DEB/RPM package downloads and repository metadata fetches) observes the same ambient context:
http.Client.Docalls carry the ctx viahttp.NewRequestWithContext, in-flight requests cancel as soon as it fires, and the retry backoff aborts within one delay quantum instead of running to completion. This coverspkgfetcher.FetchPackages,rpmutils.fetchURLWithRetry, anddebutils.checkFileExists.Registered teardowns run in reverse acquisition order (loop-device detach first, then chroot unmount + gpg-agent stop) under a fresh 30 s per-entry budget so cleanup itself is not aborted by the signal that fired. Each cleanup callback rebinds the ambient shell context to the per-entry budget so its own shell calls (unmount,
losetup -d,swapoff) run under the cleanup deadline rather than the already-cancelled parent.PostProcess(which is itself part of the build’s cleanup) runs under a detached 2-minute context so an already-cancelled parent does not defeat the umount escalations inside it.Any resource that could not be reaped (unmount stuck,
losetup -drefused because a partition is still busy) is reported at ERROR level with the label and error text so the operator canmount | grep <work-dir>andlosetup -lto reclaim it.Exit code is
130(128 + SIGINT) only for user-initiated cancellation (a signal). Internal timeouts — such as the 2-minute PostProcess cleanup budget being exceeded — surface as exit1so scripts can distinguish “user aborted” from “internal cleanup timed out”. A second signal during cleanup skips the remaining teardown and exits with130immediately — use this if a residual umount is hanging.
See also:
Build Stages in Detail for information about each build stage
Build Performance Optimization for tips to improve build speed
Validate Command#
Validate an image template file without building it. This allows checking for errors in your template before committing to a full build process.
image-composer-tool validate TEMPLATE_FILE
Arguments:
TEMPLATE_FILE- Path to the YAML image template file to validate (required)
Description:
The validate command performs the following checks:
YAML syntax validation
Schema validation against the image template JSON schema
Required fields verification
Type checking for all fields
Example:
# Validate a template file
image-composer-tool validate my-image-template.yml
# Validate with verbose output
image-composer-tool --log-level debug validate my-image-template.yml
See also:
Template Loading and Validation for details on the validation process
Resolve Command#
Resolve a template and print the merged YAML to stdout for debugging and traceability. Resolve does not build anything and never writes to disk; the merged output is computed on every invocation and is not cached.
image-composer-tool resolve [flags] TEMPLATE_FILE
Arguments:
Argument |
Description |
|---|---|
|
Path to the image template YAML file (required, positional) |
Flags:
Flag |
Description |
|---|---|
|
Include OS defaults in the output, showing exactly what will be built |
Description:
By default, the resolve command walks the template’s extends: chain (leaf
towards root) and prints the chain-merged YAML without OS defaults. If the
template does not use extends:, the command prints
No extends used in template, nothing to resolve and exits successfully.
When --full is passed, resolve additionally folds the extends chain on top of
the OS default configuration — OS defaults are the base layer and the extends
chain overrides them (leaf wins). This is the same merge the build command
runs, so the output shows exactly what the tool would build. --full
suppresses the “nothing to resolve” short-circuit, so it also works for
templates that do not use extends:.
Sensitive fields are always redacted in the output:
systemConfig.users[*].passwordsystemConfig.users[*].hash_algosystemConfig.immutability.secureBootDBKeysystemConfig.immutability.secureBootDBCrtsystemConfig.immutability.secureBootDBCer
The output is safe to paste into an issue or a code review.
Example:
# Show the extends-chain-merged YAML for a template that inherits from a parent
image-composer-tool resolve image-templates/ubuntu24/ubuntu24-x86_64-extends-example-raw.yml
# Show the full build-time template (extends chain + OS defaults)
image-composer-tool resolve image-templates/azl3/azl3-x86_64-edge-raw.yml --full
# Pipe the merged template into a file for offline review
image-composer-tool resolve my-template.yml --full > merged.yml
See also:
Validate Command — checks template validity without emitting YAML.
Inspect Command#
Inspects a raw image and outputs comprehensive details about the image including partition table layout, partition identity and attributes, filesystem information, bootloader details, and layout diagnostics.
image-composer-tool inspect [flags] IMAGE_FILE
Arguments:
IMAGE_FILE- Path to the RAW image file to inspect (required)
Flags:
Flag |
Description |
|---|---|
|
Output format: |
|
Pretty-print JSON output (only for |
|
Extracts SBOM and saves the output in FILE, default filename is used if FILE is not specified |
Description:
The inspect command extracts and analyzes the following from a disk image:
Partition Table:
Type (GPT/MBR) and sector sizes
For GPT: disk GUID and protective MBR status
Layout diagnostics: largest unallocated free span and misaligned partitions (detected against physical sector size and 1 MiB alignment)
Partitions:
Index, name, type/GUID, start/end LBA, and size
For GPT: partition GUID and decoded attribute bits (required, legacy BIOS bootable, read-only)
Filesystem type, label, and UUID
EFI/UKI evidence (if present on ESP/VFAT partitions)
Bootloader & Secure Boot:
EFI binaries: kind, architecture, signature status, SBAT
UKI payloads: kernel/initrd/OS-release hashes and metadata
Output Formats:
text: Human-readable summary with tables and structured sectionsjson: Complete structured data suitable for automation and comparisonyaml: YAML representation of the image summary
Example:
# Inspect a raw image and output text (default)
image-composer-tool inspect my-image.raw
# Inspect and output pretty JSON
image-composer-tool inspect --format=json --pretty my-image.raw
# Inspect and output YAML
image-composer-tool inspect --format=yaml my-image.raw
# Inspect and extract SPDX data from an IMAGE
image-composer-tool inspect my-image.raw --extract-sbom my-sbom.json
Compare Command#
Compares two disk images and outputs detailed differences in partition layout, filesystems, bootloaders, and EFI/UKI payloads.
image-composer-tool compare [flags] IMAGE_FILE1 IMAGE_FILE2
Arguments:
IMAGE_FILE1- Path to the first RAW image file (required)IMAGE_FILE2- Path to the second RAW image file (required)SPDX_FILE1- Path to the first SPDX JSON file (required if--mode=spdx)SPDX_FILE2- Path to the second SPDX JSON file (required if--mode=spdx)
Flags:
Flag |
Description |
|---|---|
|
Output format: |
|
Compare mode: |
|
Pretty-print JSON output (only for |
|
Perform image hashing for verifying binary identical image (default |
Description:
The compare command performs a deep structural comparison of two images and reports:
Partition Table Changes:
Disk GUID changes (GPT)
Partition table type or sector size changes
Free space layout changes (largest unallocated extent)
Misaligned partition detection changes
Partition Changes:
Added/removed partitions (detected by GUID for GPT, by LBA range for MBR)
Modified partitions: changes to name, GUID, LBA range, size, or GPT attribute bits
Filesystem changes: type, label, UUID modifications
Per-partition EFI binary changes (path, kind, architecture, signature status)
Global EFI/UKI Changes:
Added/removed EFI binaries across all partitions
Modified EFI binaries: SHA256, signature status, bootloader kind
UKI payload changes: kernel, initrd, OS-release, and section SHA256s
Compare Modes:
diff: Detailed changes (partitions, filesystems, EFI binaries)summary: High-level counts (added, removed, modified counts)full: Complete image metadata plus all diffsspdx: Compares two SPDX JSON files
Output:
Text format provides human-readable sections with tables and field-by-field diffs
JSON format includes complete structured data for scripting and tooling
Exit code is 0 if images are equal, 1 if differences found
Example:
# Compare two images and show detailed text diff
image-composer-tool compare image-v1.raw image-v2.raw
# Show only a summary of changes
image-composer-tool compare --mode=summary image-v1.raw image-v2.raw
# Compare and output pretty JSON with full metadata
image-composer-tool compare --format=json --mode=full --pretty image-v1.raw image-v2.raw
# Compact JSON diff suitable for CI/CD automation
image-composer-tool compare --format=json --mode=diff image-v1.raw image-v2.raw
# Perform comparison with image hashing enabled with details text diff
image-composer-tool compare --hash-images=true image-v1.raw image-v2.raw
# Perform SPDX comparison
image-composer-tool compare --format=json --mode=spdx spdx-file1.json spdx-file2.json
Comparing overlay build outputs:
The compare command works directly against
overlay build artifacts — the
baseline RAW, the overlay RAW, and the emitted *.complete.spdx.json /
*.delta.spdx.json SBOM sidecars. Compare the baseline RAW against the overlay
RAW for a structural/binary diff, and use --mode=spdx with the complete SBOM
for an accurate package-level (added/removed/upgraded) diff. For a full
walkthrough with sample commands and expected output, see
Comparing Overlay Outputs
in the Usage Guide.
In --mode=spdx, either argument may be a standalone SPDX JSON file or an OS
image — an image’s embedded SBOM (at /usr/share/sbom) is extracted
automatically. In the default image-compare modes (diff/summary/full) the
SBOM diff reflects metadata only (package count, canonical hash); use
--mode=spdx for a per-package breakdown.
Cache Command#
Manage cached artifacts created during the build process.
image-composer-tool cache SUBCOMMAND
cache clean#
Remove cached packages or workspace chroot data.
image-composer-tool cache clean [flags]
Flags:
Flag |
Description |
|---|---|
|
Remove cached packages (default when no scope flags are provided). |
|
Remove cached chroot environments and chroot tarballs under the workspace directory. |
|
Enable both package and workspace cleanup in a single invocation. |
|
Restrict cleanup to a specific provider (format: |
|
Show what would be removed without deleting anything. |
Examples:
# Remove all cached packages
image-composer-tool cache clean
# Remove chroot caches for a single provider
image-composer-tool cache clean --workspace --provider-id azure-linux-azl3-x86_64
# Preview everything that would be deleted
image-composer-tool cache clean --all --dry-run
When no scope flag is supplied, the command defaults to --packages.
Config Command#
Manage the global configuration file. The config command provides subcommands for initializing and viewing configuration.
image-composer-tool config SUBCOMMAND
Subcommands:
config init#
Initialize a new configuration file with default values.
image-composer-tool config init [CONFIG_FILE]
Arguments:
CONFIG_FILE- Path where the configuration file should be created (optional). If not specified, creates the configuration in a standard location.
Example:
# Initialize configuration in current directory
image-composer-tool config init image-composer-tool.yml
# Initialize in default location
image-composer-tool config init
config show#
Show the current configuration settings.
image-composer-tool config show
Example:
# Show current configuration
image-composer-tool config show
# Show configuration from specific file
image-composer-tool --config /path/to/config.yml config show
Version Command#
Display the tool’s version information, including build date, Git commit SHA, and organization.
image-composer-tool version
Example:
image-composer-tool version
Output includes:
Version number
Build date
Git commit SHA
Organization
Install-Completion Command#
Install shell completion for the image-composer-tool command. Supports bash, zsh, fish, and PowerShell.
image-composer-tool install-completion [flags]
Flags:
Flag |
Description |
|---|---|
|
Shell type (bash, zsh, fish, powershell). If not specified, auto-detects current shell. |
|
Force overwrite existing completion files. |
Example:
# Auto-detect shell and install completion
image-composer-tool install-completion
# Install completion for specific shell
image-composer-tool install-completion --shell bash
# Force reinstall
image-composer-tool install-completion --force
Post-Installation Steps:
After installing completion, you need to reload your shell configuration:
Bash:
echo "source ~/.bash_completion.d/ict.bash" >> ~/.bashrc
source ~/.bashrc
Zsh:
echo 'fpath=(~/.zsh/completion $fpath)' >> ~/.zshrc
source ~/.zshrc
Fish: Fish automatically loads completions from the standard location. Just restart your terminal.
PowerShell:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
. $PROFILE
Examples#
Building an Image#
# Build an image with default settings
sudo -E image-composer-tool build image-templates/azl3/azl3-x86_64-edge-raw.yml
# Build with custom configuration
sudo -E image-composer-tool --config=/path/to/config.yaml build image-templates/azl3/azl3-x86_64-edge-raw.yml
# Build with custom workers and cache
sudo -E image-composer-tool build --workers 12 --cache-dir ./package-cache image-templates/azl3/azl3-x86_64-edge-raw.yml
# Build with debug logging
sudo -E image-composer-tool --log-level debug build image-templates/azl3/azl3-x86_64-edge-raw.yml
Managing Configuration#
# Initialize a new configuration file
image-composer-tool config init my-config.yml
# Show current configuration
image-composer-tool config show
# Use a specific configuration file
image-composer-tool --config /etc/image-composer-tool/config.yml build template.yml
Managing Cache#
# Remove all cached packages
image-composer-tool cache clean
# Remove workspace chroot caches for a specific provider
image-composer-tool cache clean --workspace --provider-id azure-linux-azl3-x86_64
# Preview both package and workspace cleanup without deleting files
image-composer-tool cache clean --all --dry-run
Inspecting and Comparing Images#
# Inspect an image in text format
image-composer-tool inspect my-image.raw
# Inspect and output JSON (suitable for tooling/CI)
image-composer-tool inspect --format=json --pretty my-image.raw
# Compare two images with detailed diff
image-composer-tool compare image-v1.raw image-v2.raw
# Compare with JSON output for parsing
image-composer-tool compare --format=json --mode=diff image-v1.raw image-v2.raw
Validating Templates#
# Validate a template
image-composer-tool validate image-templates/azl3/azl3-x86_64-edge-raw.yml
# Validate with debug output
image-composer-tool --log-level debug validate image-templates/azl3/azl3-x86_64-edge-raw.yml
Resolving Templates#
# Print the extends-chain-merged YAML (without OS defaults) for a template
# that inherits from a parent
image-composer-tool resolve image-templates/ubuntu24/ubuntu24-x86_64-extends-example-raw.yml
# Print the fully merged, build-ready YAML including OS defaults
image-composer-tool resolve image-templates/azl3/azl3-x86_64-edge-raw.yml --full
# Save the merged template to a file for offline review
image-composer-tool resolve my-template.yml --full > merged.yml
Configuration Files#
Global Configuration File#
The global configuration file (YAML format) defines system-wide settings that apply to all image builds. The tool searches for configuration files in the following locations (in order):
Path specified with
--configflagimage-composer-tool.ymlin current directory.image-composer-tool.ymlin current directoryimage-composer-tool.yamlin current directory.image-composer-tool.yamlin current directory~/.image-composer-tool/config.yml~/.ict/config.yaml~/.config/image-composer-tool/config.yml~/.config/image-composer-tool/config.yaml/etc/image-composer-tool/config.yml/etc/image-composer-tool/config.yaml
Example Configuration:
# Number of concurrent workers for package downloads
workers: 8
# Directory for caching downloaded packages
cache_dir: "./cache"
# Working directory for build process
work_dir: "./workspace"
# Configuration files directory
config_dir: "./config"
# Temporary directory
temp_dir: "/tmp"
# Logging configuration
logging:
level: "info" # debug, info, warn, error
Configuration Fields:
Field |
Type |
Description |
|---|---|---|
|
integer |
Number of concurrent download workers (1-100). Default: 8 |
|
string |
Directory for package cache. Default: “./cache” |
|
string |
Working directory for builds. Default: “./workspace” |
|
string |
Directory for configuration files. Default: “./config” |
|
string |
Temporary directory. Default: system temp directory |
|
string |
Log level (debug/info/warn/error). Default: “info” |
Image Template File#
The image template file (YAML) defines everything that goes into a custom OS
image. A minimal template requires only image and target; the remaining
sections are merged from the OS-specific default template at build time.
Minimal example (only the two required sections):
image:
name: edge-device-image
version: "1.2.0"
target:
os: azure-linux
dist: azl3
arch: x86_64
imageType: raw
Top-level sections:
Section |
Required |
Description |
|---|---|---|
|
No |
AI-searchable discovery metadata (ignored by build engine) |
|
Yes |
Image name and version |
|
Yes |
OS, distribution, architecture, image type |
|
No |
Disk size, partitions, output artifact formats |
|
No |
Additional package repositories |
|
No (user) / Yes (merged) |
Packages, kernel, users, bootloader, immutability, build commands |
For the complete field-by-field reference including all nested fields, valid values, validation rules, and merge behavior, see the Image Template Reference.
Exit Codes#
The tool provides consistent exit codes that can be used in scripting and automation:
Code |
Description |
|---|---|
0 |
Success: The command completed successfully. |
1 |
General error: An unspecified error occurred during execution. |
130 |
Cancelled by signal: A |
Troubleshooting#
Common Issues#
Disk Space: Building images requires significant temporary disk space.
# Check free space in workspace directory df -h ./workspace # Check free space in cache directory df -h ./cache
Permissions: The build command requires sudo privileges.
# Run with sudo and preserve environment sudo -E image-composer-tool build template.yml
Configuration Issues: Verify configuration is valid.
# Show current configuration image-composer-tool config show # Initialize with defaults image-composer-tool config init
Template Validation Errors: Validate templates before building.
# Validate template image-composer-tool validate template.yml
Logging#
Use the --log-level flag or --verbose flag to get more detailed output:
# Debug logging
image-composer-tool --log-level debug build template.yml
# Verbose output (same as debug)
image-composer-tool build --verbose template.yml
# Error logging only
image-composer-tool --log-level error build template.yml