Build, Encrypt, and Manage AMIs with Python, Boto3, and GitHub Actions


Creating custom Amazon Machine Images (AMIs) is essential for scalable, repeatable, and secure infrastructure in the AWS cloud. This guide will teach us how to build, encrypt, and manage AMIs programmatically using Python, Boto3, and GitHub Actions for automation.


Why Custom AMIs Matter

Custom AMIs allow you to:

  • Pre-configure your operating system, software, and configurations

  • Ensure security compliance and baseline hardening.

  • Reduce deployment time across environments.

  • Manage updates consistently

Combining automation with GitHub Actions ensures every AMI you create is reproducible and version-controlled.


Prerequisites

Before you begin:

  • An AWS account with EC2 permissions

  • Python 3.8+ installed

  • boto3 and botocore libraries

  • GitHub repository with GitHub Actions enabled

Install required Python packages:


pip install boto3 botocore



Step 1: Create and Encrypt AMIs Using Python and Boto3

Below is a sample script to create an AMI from an existing EC2 instance and encrypt it.

Python Script to Create and Encrypt an AMI


import boto3

from datetime import datetime


ec2 = boto3.client('ec2', region_name='us-east-1')


def create_encrypted_ami(instance_id, kms_key_id):

    timestamp = datetime.utcnow().strftime('%Y%m%d-%H%M%S')

    image_name = f'custom-ami-{timestamp}'

    

    # Create Image

    response = ec2.create_image(

        InstanceId=instance_id,

        Name=image_name,

        Description='Encrypted custom AMI',

        NoReboot=True

    )

    

    image_id = response['ImageId']

    print(f"Created AMI: {image_id}, waiting for it to become available...")

    

    waiter = ec2.get_waiter('image_available')

    waiter.wait(ImageIds=[image_id])

    

    # Copy and encrypt the AMI

    encrypted_response = ec2.copy_image(

        SourceImageId=image_id,

        SourceRegion='us-east-1',

        Name=f'{image_name}-encrypted',

        Encrypted=True,

        KmsKeyId=kms_key_id

    )

    

    print(f"Encrypted AMI Created: {encrypted_response['ImageId']}")

    return encrypted_response['ImageId']



Step 2: Automate AMI Creation with GitHub Actions

Add the following to .github/workflows/create-ami.yml in your repository.

GitHub Actions Workflow


name: Build and Encrypt AMI


on:

  workflow_dispatch:


jobs:

  create-ami:

    runs-on: ubuntu-latest

    steps:

      - name: Checkout Code

        uses: actions/checkout@v3


      - name: Set up Python

        uses: actions/setup-python@v4

        with:

          python-version: '3.9'


      - name: Install dependencies

        run: pip install boto3


      - name: Create Encrypted AMI

        env:

          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}

          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

          AWS_DEFAULT_REGION: us-east-1

          INSTANCE_ID: ${{ secrets.INSTANCE_ID }}

          KMS_KEY_ID: ${{ secrets.KMS_KEY_ID }}

        run: |

          python3 create_encrypted_ami.py


Store INSTANCE_ID, KMS_KEY_ID, AWS_ACCESS_KEY_ID, and AWS_SECRET_ACCESS_KEY as encrypted GitHub secrets.


Step 3: Manage and Clean Up AMIs

It's best practice to remove older AMIs to manage cost and clutter.


def delete_old_amis(tag_key, tag_value, keep_latest=3):

    images = ec2.describe_images(Owners=['self'])['Images']

    filtered = [img for img in images if any(

        tag['Key'] == tag_key and tag['Value'] == tag_value for tag in img.get('Tags', []))]


    # Sort by creation date descending

    sorted_images = sorted(filtered, key=lambda x: x['CreationDate'], reverse=True)


    for img in sorted_images[keep_latest:]:

        ec2.deregister_image(ImageId=img['ImageId'])

        print(f"Deregistered AMI: {img['ImageId']}")



Best Practices

  • Tag AMIs with metadata (Environment, App, BuildVersion) for easier tracking.

  • Use KMS keys with tight access controls for encryption.

  • Integrate Slack or email notifications in GitHub Actions for feedback.

  • Combine with Packer if you require more extensive provisioning.


Conclusion

By integrating Python, Boto3, and GitHub Actions, you gain complete control over your AMI lifecycle—build, encrypt, manage, and automate everything with code. This approach boosts your DevOps maturity and enhances security, reproducibility, and deployment speed.

Comments

Popular posts from this blog

HTTP Basic vs API Key Auth: Best Practices for Secure API Development

ECS Deployment Best Practices: Blue/Green with CodePipeline and CodeDeploy

AWS Console Not Loading? Here’s How to Fix It Fast

YouTube Channel