Back to blog
AWSBest PracticesCloud SecurityCompute & ContainersStorage

AMI Is Publicly Accessible: Why It Matters and How to Fix It

A public AMI leaks secrets, credentials, and source code to every AWS account. Learn how to detect, remediate, and block publicly accessible Amazon Machine Images.

TL;DR

This check flags any Amazon Machine Image (AMI) that has public launch permissions, meaning anyone with an AWS account can find and launch it. AMIs often bake in secrets, credentials, and proprietary code, so a public one leaks data and IP. Fix it by removing the public launch permission with aws ec2 modify-image-attribute.

An AMI is the template AWS uses to spin up an EC2 instance. It captures the root volume, attached EBS snapshots, and all the configuration that came with it. That convenience is also the problem: whatever was on disk at the moment you created the image is now part of the image, and if you set that image to public, you are sharing all of it with every AWS account on the planet.

This check, ebs_publicami in the ebs_checks module, looks for AMIs you own that have a launch permission granting access to all. When that permission is present, the image is listed in the public community AMI catalog and any AWS user can launch an instance from it.


What this check detects

Every AMI has a launchPermission attribute. By default an AMI is private and only the owning account can use it. You can explicitly share an AMI with specific account IDs, or you can share it with everyone by adding the special all group. Lensix flags the last case.

You can confirm the current state of any image yourself:

aws ec2 describe-image-attribute \
  --image-id ami-0123456789abcdef0 \
  --attribute launchPermission

If the response contains a Group set to all, the image is public:

{
  "ImageId": "ami-0123456789abcdef0",
  "LaunchPermissions": [
    { "Group": "all" }
  ]
}

Note: Making an AMI public does not make the underlying EBS snapshots public on its own, but in practice AWS requires the backing snapshots to be shared for the AMI to be usable by others. A public AMI almost always means publicly readable snapshot data, so treat the two as a single exposure.


Why it matters

The real risk is not that someone launches your image. It is what they can read out of it before they ever boot it. AMIs are snapshots of a running system, and engineers leave things on disk that were never meant to leave the account.

Common items that end up baked into an image:

  • SSH private keys and authorized_keys files
  • Hardcoded database passwords and API tokens in app config
  • .aws/credentials files with long-lived access keys
  • Application source code and proprietary binaries
  • Cached IAM instance role credentials and tokens
  • Customer data left in logs, temp directories, or database files

An attacker does not need to crack anything. They mount the public snapshot in their own account, browse the filesystem at their leisure, and harvest whatever they find. If your AMI contains a valid access key, they now have a foothold in your environment that has nothing to do with the EC2 instance itself.

Danger: Treat a publicly exposed AMI as a credential leak, not just a misconfiguration. Any secret that was on disk when the image was created should be considered compromised. Rotate every credential, key, and token that could have been present, even after you make the AMI private again.

This has happened to real organizations. Researchers regularly scan the public AMI catalog and have found images containing production secrets, internal tooling, and customer records. Because public AMIs are indexed and discoverable, you do not get to rely on obscurity. The window between "made public" and "found by a scanner" can be measured in hours.


How to fix it

The fix is to remove the public launch permission. This is fast and non-disruptive to your own account, since you retain full ownership of the image.

1. Make the AMI private

aws ec2 modify-image-attribute \
  --image-id ami-0123456789abcdef0 \
  --launch-permission "Remove=[{Group=all}]"

Verify the change took effect:

aws ec2 describe-image-attribute \
  --image-id ami-0123456789abcdef0 \
  --attribute launchPermission

An empty LaunchPermissions array means the image is private again.

2. Lock down the backing snapshots

Find the snapshots associated with the AMI and confirm they are not public either:

# List the snapshot IDs behind the AMI
aws ec2 describe-images \
  --image-ids ami-0123456789abcdef0 \
  --query 'Images[].BlockDeviceMappings[].Ebs.SnapshotId' \
  --output text

For each snapshot, remove public access:

aws ec2 modify-snapshot-attribute \
  --snapshot-id snap-0123456789abcdef0 \
  --attribute createVolumePermission \
  --operation-type remove \
  --group-names all

3. Console steps

If you prefer the console:

  1. Open the EC2 console and go to Images > AMIs.
  2. Select the AMI, then choose Actions > Edit AMI permissions.
  3. Set the visibility to Private and remove any account IDs you do not recognize.
  4. Save, then repeat the snapshot cleanup under Elastic Block Store > Snapshots.

4. Rotate exposed credentials

Do not skip this step. If the image was public for any length of time, assume the contents were copied.

  • Rotate IAM access keys that may have been in the image.
  • Reset database and application passwords.
  • Invalidate and reissue SSH key pairs.
  • Revoke any long-lived API tokens.

Warning: If you genuinely intended to publish this AMI (for example, a vendor distributing a product), rebuild it from a clean, secret-free base before sharing. Never make an image public that was created from a live production instance.


How to prevent it from happening again

One-off fixes do not scale. Put guardrails in place so a public AMI cannot quietly appear and stay.

Block public sharing at the account level

AWS provides an account-wide setting that blocks public sharing of AMIs entirely. Enable it in every account and region unless you have a deliberate reason not to.

aws ec2 enable-image-block-public-access \
  --image-block-public-access-state block-new-sharing

Once enabled, any attempt to make an AMI public fails, and existing private AMIs stay private. Confirm the state with:

aws ec2 get-image-block-public-access-state

Tip: Roll block-new-sharing out across your whole organization using a stack set or your IaC pipeline. It is the single most effective control for this check, and it is free. Make it a default for every new account in your AWS Organizations setup.

Enforce it with an SCP

For organizations using AWS Organizations, a service control policy stops anyone from disabling the block:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyDisableImageBlockPublicAccess",
      "Effect": "Deny",
      "Action": "ec2:DisableImageBlockPublicAccess",
      "Resource": "*"
    },
    {
      "Sid": "DenyPublicAmiSharing",
      "Effect": "Deny",
      "Action": "ec2:ModifyImageAttribute",
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "ec2:Add/group": "all"
        }
      }
    }
  ]
}

Catch it in CI/CD with policy-as-code

If you build AMIs with Packer or Terraform, scan the resulting templates before they ship. A simple example with OPA Conftest against a Terraform plan:

# Render plan to JSON
terraform plan -out=tfplan.binary
terraform show -json tfplan.binary > tfplan.json

# Evaluate policy
conftest test tfplan.json --policy ami-policy.rego

A Rego rule that fails any AMI launch permission set to the public group:

package main

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "aws_ami_launch_permission"
  resource.change.after.group == "all"
  msg := sprintf("AMI %s grants public launch permission", [resource.address])
}

Continuous detection

Block-level controls can drift, especially in accounts created before you standardized. Keep a continuous check running so you find anything that slips through. This is exactly what the Lensix ebs_publicami check does, sweeping your accounts on a schedule and surfacing any AMI that becomes public, regardless of how it got that way.


Best practices

  • Build images from clean bases. Use a hardened, secret-free golden image as your starting point and inject secrets at runtime through SSM Parameter Store or Secrets Manager, never baked into the image.
  • Keep secrets out of the filesystem. Pull credentials at boot via instance roles rather than storing static keys on disk.
  • Share narrowly when you must share. If another account needs an AMI, share with that specific account ID, not with all.
  • Encrypt your snapshots. Encrypted EBS snapshots cannot be shared publicly at all, which gives you a hard backstop against accidental exposure.
  • Tag and inventory your AMIs. Know which images are in use, which are stale, and who owns them. Deregister old images so they are not lying around as forgotten attack surface.
  • Audit launch permissions regularly. Run a periodic sweep across all regions, since AMIs and snapshots are regional and a public image in ap-southeast-2 is just as dangerous as one in your primary region.

Note: Remember that AMIs are regional. An audit that only checks us-east-1 will miss public images everywhere else. Always iterate across every enabled region when you scan.

A public AMI is one of those misconfigurations that looks harmless in a console toggle but functions as a full data and credential leak. Block public sharing at the account level, enforce it with policy, and keep a continuous check watching for drift. With those three layers in place, this check should stay green for good.