Register a raw disk image as an AMI through the EBS direct APIs - no S3 bucket and no vmimport role.
Overview · Features · Installation · Usage · Configuration · Requirements · License
# amazon-import uploads through S3 and needs the vmimport service role:
# post-processor "amazon-import" {
# s3_bucket_name = "my-vmimport-bucket" # create it, manage it, pay for it
# role_name = "vmimport" # set up its trust policy first
# ...
# }
# ebsdirect writes the snapshot straight from the raw image:
post-processor "ebsdirect" {
ami_name = "my-image"
}ebsdirect is a Packer post-processor that takes a raw disk image from a previous build step and turns it into an AMI without going through S3. It uploads the image straight into an EBS snapshot with the EBS direct APIs (StartSnapshot / PutSnapshotBlock / CompleteSnapshot), writing 512 KiB blocks in parallel and skipping all-zero ones, then registers that snapshot as an AMI with RegisterImage. Credentials come from the default AWS SDK chain.
This drops the two things amazon-import requires that have nothing to do with the image itself: the S3 bucket you upload through, and the vmimport IAM role with its trust policy.
Available on the Packer integrations registry.
- Uploads via the EBS direct APIs - no S3 bucket and no
vmimportrole to set up. - Skips all-zero 512 KiB blocks, so a 10 GiB image holding 2 GiB of data uploads about 2 GiB.
- Parallel block upload (64 workers) with retry on throttling.
- Per-block SHA-256 plus a LINEAR aggregate checksum that AWS validates at
CompleteSnapshot. - Registers a modern HVM AMI: ENA enabled, gp3 root volume (16 TiB ceiling),
x86_64orarm64, boot modelegacy-bios/uefi/uefi-preferred. - Optional snapshot encryption (
ami_encrypt/ami_kms_key) with the account default or a customer-managed KMS key. - Optional
imds_support = v2.0so the AMI defaults to requiring IMDSv2 on instances launched from it. - Share the AMI's launch permission with accounts, organizations, OUs, or publicly (
ami_users/ami_org_arns/ami_ou_arns/ami_groups).
Declare the plugin in your template and run packer init:
packer {
required_plugins {
ebsdirect = {
version = ">= 0.1.0"
source = "github.com/ivoronin/ebsdirect"
}
}
}packer init .Prebuilt binaries are on the releases page.
Chain the post-processor after any builder that produces a raw image. With the core file builder pointing at an image you built elsewhere:
source "file" "image" {
source = "disk.raw" # raw format, a whole number of GiB
target = "disk.raw"
}
build {
sources = ["source.file.image"]
post-processor "ebsdirect" {
ami_name = "my-image"
architecture = "x86_64" # or arm64
boot_mode = "legacy-bios" # or uefi / uefi-preferred
tags = { project = "demo" }
snapshot_tags = { project = "demo" }
}
}The same shape works after a qemu build that outputs a raw disk. The post-processor produces an AMI artifact with id <region>:<ami-id>; destroying that artifact deregisters the AMI and deletes its snapshot.
| Field | Required | Default | Description |
|---|---|---|---|
ami_name |
yes | - | Name of the registered AMI. |
ami_description |
no | "" |
AMI description. |
architecture |
no | x86_64 |
x86_64 or arm64 (arm64 requires uefi). |
boot_mode |
no | legacy-bios |
legacy-bios, uefi, or uefi-preferred. |
root_device_name |
no | /dev/xvda |
Root device name on the AMI. |
region |
no | from the SDK chain | Target region; otherwise AWS_REGION or the active profile. |
tags |
no | - | Tags applied to the AMI. |
snapshot_tags |
no | - | Tags applied to the snapshot. |
ami_encrypt |
no | false |
Request encryption of the snapshot (and the resulting AMI). An account/region with EBS encryption-by-default still produces an encrypted snapshot regardless. |
ami_kms_key |
no | account default EBS key | KMS key ARN for the encrypted snapshot. Requires ami_encrypt = true; ignored otherwise. |
imds_support |
no | "" |
Set to v2.0 so the AMI defaults to requiring IMDSv2 on launched instances. Image default, not a hard lock (overridable at RunInstances). |
ami_users |
no | - | Account IDs granted launch permission on the AMI. |
ami_groups |
no | - | Launch-permission groups; only all (makes the AMI public) is supported. |
ami_org_arns |
no | - | Organization ARNs granted launch permission. |
ami_ou_arns |
no | - | Organizational-unit ARNs granted launch permission. |
Credentials are read from the default AWS SDK chain (environment, AWS_PROFILE / shared config, SSO, instance role). There are no credential fields in the template.
- Packer 1.7 or later (for
packer init). - A raw disk image whose size is a whole number of GiB. Use
qemu-img convert -O rawto convert andqemu-img resizeto round up if needed. - IAM permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["ebs:StartSnapshot", "ebs:PutSnapshotBlock", "ebs:CompleteSnapshot"],
"Resource": "arn:aws:ec2:*::snapshot/*"
},
{
"Effect": "Allow",
"Action": ["ec2:RegisterImage", "ec2:DescribeSnapshots", "ec2:DeregisterImage", "ec2:DeleteSnapshot", "ec2:CreateTags", "ec2:ModifyImageAttribute"],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": ["kms:DescribeKey", "kms:GenerateDataKeyWithoutPlaintext", "kms:CreateGrant", "kms:ReEncrypt*", "kms:Decrypt"],
"Resource": "*"
}
]
}The KMS actions are only needed when ami_encrypt is used with a customer-managed key; the account default EBS key is already usable by account principals.
Sharing does not share the underlying snapshot: AWS gives recipients snapshot access for launch. Sharing an encrypted AMI works only when it is backed by a customer-managed KMS key (not the default aws/ebs key), and the recipient accounts must be granted access to that key.