Skip to main content

EC2 AMI Public

Overview

This check examines Amazon Machine Images (AMIs) owned by your AWS account and flags any that are publicly accessible. An AMI is considered public when its launch permissions include "all" (Group=all), meaning anyone with an AWS account can launch instances from your image.

Risk

A publicly shared AMI creates serious security vulnerabilities:

  • Secrets exposure: Embedded credentials, API keys, SSH keys, or configuration files in the AMI become accessible to anyone
  • Data leakage: Sensitive data stored on EBS snapshots referenced by the AMI can be accessed by external parties
  • Infrastructure fingerprinting: Attackers can analyze your AMI to understand your software stack, configurations, and potential vulnerabilities
  • Supply chain attacks: Malicious actors could clone your AMI, inject malware, and redistribute it to unsuspecting users
  • Compliance violations: Public AMIs may violate data protection regulations (GDPR, HIPAA, PCI-DSS)

This check is rated Critical severity because public AMIs directly expose your infrastructure to the internet.

Remediation Steps

Prerequisites

  • AWS account access with permissions to modify EC2 AMI launch permissions
  • The AMI ID of the image that needs to be made private
Required IAM permissions

You will need the following permissions:

  • ec2:DescribeImages - View AMI details and launch permissions
  • ec2:ModifyImageAttribute - Change AMI launch permissions from public to private
  • ec2:DescribeImageAttribute - Verify the launch permissions after modification

AWS Console Method

Step 1: Find your public AMIs

  1. Sign in to the AWS Management Console
  2. Navigate to EC2 > AMIs (under "Images" in the left sidebar)
  3. Make sure you are in the correct region (e.g., us-east-1)
  4. In the filter dropdown, select Owned by me
  5. Look for AMIs where the Visibility column shows Public

Step 2: Make the AMI private

  1. Select the public AMI by clicking its checkbox
  2. Click Actions > Edit AMI permissions
  3. Under AMI availability, select Private
  4. Click Save changes

Step 3: (Optional) Share with specific accounts

If you need to share this AMI with specific AWS accounts (instead of making it fully public):

  1. With the AMI still selected, click Actions > Edit AMI permissions
  2. Ensure AMI availability is set to Private
  3. Under Shared accounts, click Add account ID
  4. Enter the AWS account ID(s) that should have access
  5. Click Save changes
AWS CLI (optional)

List your public AMIs:

aws ec2 describe-images \
--owners self \
--filters "Name=is-public,Values=true" \
--query 'Images[*].[ImageId,Name,Public]' \
--output table \
--region us-east-1

Make an AMI private:

aws ec2 modify-image-attribute \
--image-id <your-ami-id> \
--launch-permission "Remove=[{Group=all}]" \
--region us-east-1

Replace <your-ami-id> with the actual AMI ID (e.g., ami-0123456789abcdef0).

Share with specific AWS accounts (optional):

aws ec2 modify-image-attribute \
--image-id <your-ami-id> \
--launch-permission "Add=[{UserId=123456789012},{UserId=987654321098}]" \
--region us-east-1

Replace the account IDs with the actual AWS account IDs you want to share with.

Enable block public access for AMIs (account-wide protection):

aws ec2 enable-image-block-public-access \
--image-block-public-access-state block-new-sharing \
--region us-east-1

This prevents any AMIs in your account from being made public in the future.

CloudFormation (optional)

CloudFormation does not directly support modifying existing AMI permissions. However, you can use a Custom Resource with a Lambda function to enforce private AMIs.

For new AMIs created through EC2 Image Builder, you can control sharing settings:

AWSTemplateFormatVersion: '2010-09-09'
Description: EC2 Image Builder pipeline with private AMI output

Parameters:
BaseImageArn:
Type: String
Description: ARN of the base image to build from
Default: arn:aws:imagebuilder:us-east-1:aws:image/amazon-linux-2023-x86/x.x.x

Resources:
ImageBuilderComponent:
Type: AWS::ImageBuilder::Component
Properties:
Name: sample-component
Platform: Linux
Version: '1.0.0'
Description: Sample component for private AMI
Data: |
name: SampleComponent
schemaVersion: 1.0
phases:
- name: build
steps:
- name: UpdateOS
action: ExecuteBash
inputs:
commands:
- yum update -y

ImageBuilderRecipe:
Type: AWS::ImageBuilder::ImageRecipe
Properties:
Name: private-ami-recipe
Version: '1.0.0'
ParentImage: !Ref BaseImageArn
Components:
- ComponentArn: !Ref ImageBuilderComponent

ImageBuilderInfraConfig:
Type: AWS::ImageBuilder::InfrastructureConfiguration
Properties:
Name: private-ami-infrastructure
InstanceProfileName: !Ref ImageBuilderInstanceProfile

ImageBuilderInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref ImageBuilderRole

ImageBuilderRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
- arn:aws:iam::aws:policy/EC2InstanceProfileForImageBuilder

# Distribution configuration - AMI stays private by default
ImageBuilderDistribution:
Type: AWS::ImageBuilder::DistributionConfiguration
Properties:
Name: private-ami-distribution
Distributions:
- Region: us-east-1
AmiDistributionConfiguration:
Name: 'private-ami-{{imagebuilder:buildDate}}'
Description: Private AMI built by Image Builder
# No LaunchPermissions means the AMI stays private
AmiTags:
Environment: Production
ManagedBy: ImageBuilder

ImageBuilderPipeline:
Type: AWS::ImageBuilder::ImagePipeline
Properties:
Name: private-ami-pipeline
ImageRecipeArn: !Ref ImageBuilderRecipe
InfrastructureConfigurationArn: !Ref ImageBuilderInfraConfig
DistributionConfigurationArn: !Ref ImageBuilderDistribution

Outputs:
PipelineArn:
Description: ARN of the Image Builder pipeline
Value: !Ref ImageBuilderPipeline

Important: By default, AMIs created by Image Builder are private. Only add LaunchPermissions in the AmiDistributionConfiguration if you need to share with specific accounts.

Terraform (optional)

Terraform can manage AMI launch permissions using the aws_ami_launch_permission resource.

Remove public access from an existing AMI:

provider "aws" {
region = "us-east-1"
}

# This resource manages sharing with specific accounts
# To make an AMI private, simply don't create any launch permission resources for it

# Example: Share AMI with specific accounts only (not public)
resource "aws_ami_launch_permission" "trusted_account" {
image_id = "ami-0123456789abcdef0" # Replace with your AMI ID
account_id = "123456789012" # Replace with trusted account ID
}

# Enable block public access for AMIs (account-wide protection)
resource "aws_ec2_image_block_public_access" "block_public" {
state = "block-new-sharing"
}

Create a private AMI from an instance:

provider "aws" {
region = "us-east-1"
}

# Create an AMI from an existing instance
resource "aws_ami_from_instance" "private_ami" {
name = "my-private-ami"
source_instance_id = "i-0123456789abcdef0" # Replace with your instance ID

# AMIs are private by default - no launch permission resources needed

tags = {
Name = "my-private-ami"
Environment = "Production"
Public = "false"
}
}

# Only share with specific accounts if needed
resource "aws_ami_launch_permission" "share_with_partner" {
image_id = aws_ami_from_instance.private_ami.id
account_id = "987654321098" # Partner account ID
}

# Block public AMI sharing at the account level
resource "aws_ec2_image_block_public_access" "block_public" {
state = "block-new-sharing"
}

Important: AMIs in Terraform are private by default. Only add aws_ami_launch_permission resources for specific accounts you want to share with. Never use group = "all" as it makes the AMI public.

Verification

After completing the remediation:

  1. Go to EC2 > AMIs in the AWS Console
  2. Select your AMI and check the Visibility column - it should show Private
  3. Click Actions > Edit AMI permissions to verify no public sharing is enabled
  4. Re-run the Prowler check to confirm the issue is resolved
CLI verification commands

Verify the AMI is no longer public:

aws ec2 describe-images \
--image-ids <your-ami-id> \
--query 'Images[0].[ImageId,Public]' \
--output table \
--region us-east-1

The Public field should show False.

Check the launch permissions:

aws ec2 describe-image-attribute \
--image-id <your-ami-id> \
--attribute launchPermission \
--region us-east-1

The output should not include "Group": "all". It should either be empty or list only specific account IDs.

Verify no public AMIs remain:

aws ec2 describe-images \
--owners self \
--filters "Name=is-public,Values=true" \
--query 'Images[*].ImageId' \
--output text \
--region us-east-1

This command should return no results if all your AMIs are private.

Additional Resources

Notes

  • Sanitize before sharing: Even when sharing with specific accounts, ensure AMIs do not contain secrets, credentials, or sensitive data. Use tools like EC2 Image Builder to create clean, standardized images.
  • Encrypt EBS snapshots: AMIs reference EBS snapshots. Ensure these snapshots are encrypted with KMS keys, and control access to the KMS keys to prevent unauthorized access.
  • Enable block public access: Use the account-level "Block public access for AMIs" setting to prevent accidental public sharing of any AMI in your account.
  • Lifecycle management: Implement a process to regularly review and deregister unused AMIs. Old AMIs may contain outdated software with known vulnerabilities.
  • Audit sharing: Periodically review which accounts have access to your AMIs and revoke access for accounts that no longer need it.
  • Cross-region considerations: AMI permissions are region-specific. If you have copied AMIs to multiple regions, you must update permissions in each region separately.