Skip to content

Instantly share code, notes, and snippets.

@RajChowdhury240
Created October 17, 2025 10:58
Show Gist options
  • Select an option

  • Save RajChowdhury240/d356d3279ea5ab4781bd2c4b49276fbe to your computer and use it in GitHub Desktop.

Select an option

Save RajChowdhury240/d356d3279ea5ab4781bd2c4b49276fbe to your computer and use it in GitHub Desktop.
@RajChowdhury240
Copy link
Author

image

@RajChowdhury240
Copy link
Author

image

@RajChowdhury240
Copy link
Author

image

@RajChowdhury240
Copy link
Author

₹₹₹python

#!/usr/bin/env python3
"""
IAM User Key Rotation Utility - Enhanced UI Version
Rotates AWS IAM access keys without storing credentials locally
"""

import boto3
import getpass
from datetime import datetime, timezone
from botocore.exceptions import ClientError
import sys
import os

class Colors:
"""ANSI color codes for terminal output"""
HEADER = '\033[95m'
BLUE = '\033[94m'
CYAN = '\033[96m'
GREEN = '\033[92m'
YELLOW = '\033[93m'
RED = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
WHITE = '\033[97m'
BG_BLUE = '\033[44m'
BG_GREEN = '\033[42m'
BG_RED = '\033[41m'
BG_YELLOW = '\033[43m'

def clear_screen():
"""Clear the terminal screen"""
os.system('cls' if os.name == 'nt' else 'clear')

def print_banner():
"""Print application banner"""
clear_screen()
banner = f"""
{Colors.CYAN}{Colors.BOLD}
╔══════════════════════════════════════════════════════════════════════╗
║ ║
║ 🔐 AWS IAM KEY ROTATION UTILITY 🔐 ║
║ ║
║ Secure Key Management Without Local Storage ║
║ ║
╚══════════════════════════════════════════════════════════════════════╝
{Colors.ENDC}
"""
print(banner)

def print_section_header(title):
"""Print a formatted section header"""
print(f"\n{Colors.BOLD}{Colors.BLUE}{'═' * 72}{Colors.ENDC}")
print(f"{Colors.BOLD}{Colors.WHITE} {title}{Colors.ENDC}")
print(f"{Colors.BOLD}{Colors.BLUE}{'═' * 72}{Colors.ENDC}\n")

def print_success(message):
"""Print success message"""
print(f"{Colors.GREEN}✓ {message}{Colors.ENDC}")

def print_error(message):
"""Print error message"""
print(f"{Colors.RED}✗ {message}{Colors.ENDC}")

def print_warning(message):
"""Print warning message"""
print(f"{Colors.YELLOW}⚠ {message}{Colors.ENDC}")

def print_info(message):
"""Print info message"""
print(f"{Colors.CYAN}ℹ {message}{Colors.ENDC}")

def get_credentials():
"""Prompt user for AWS credentials securely"""
print_banner()
print_section_header("🔑 AWS CREDENTIAL INPUT")

print(f"{Colors.WHITE}Please enter your AWS credentials to begin.{Colors.ENDC}")
print(f"{Colors.YELLOW}Note: Secret key input will be hidden for security.{Colors.ENDC}\n")

access_key = input(f"{Colors.CYAN}Access Key ID: {Colors.ENDC}").strip()
secret_key = getpass.getpass(f"{Colors.CYAN}Secret Access Key: {Colors.ENDC}").strip()

return access_key, secret_key

def create_iam_client(access_key, secret_key):
"""Create IAM client with provided credentials"""
try:
print(f"\n{Colors.YELLOW}Authenticating...{Colors.ENDC}", end='', flush=True)

    client = boto3.client(
        'iam',
        aws_access_key_id=access_key,
        aws_secret_access_key=secret_key
    )
    # Test the credentials with a simple call
    client.get_user()
    print(f"\r{' ' * 50}\r", end='')  # Clear the line
    return client
except ClientError as e:
    print(f"\r{' ' * 50}\r", end='')  # Clear the line
    error_code = e.response['Error']['Code']
    error_msg = e.response['Error']['Message']
    
    if error_code == 'InvalidClientTokenId':
        print_error("Authentication failed: Invalid Access Key ID")
    elif error_code == 'SignatureDoesNotMatch':
        print_error("Authentication failed: Invalid Secret Access Key")
    elif error_code == 'AccessDenied':
        print_error(f"Access Denied: {error_msg}")
        print_info("Your IAM user needs 'iam:GetUser' permission.")
    else:
        print_error(f"Authentication error: {error_msg}")
    return None
except Exception as e:
    print(f"\r{' ' * 50}\r", end='')  # Clear the line
    print_error(f"Error creating IAM client: {str(e)}")
    return None

def get_username(iam_client):
"""Get the current IAM username or detect root account"""
try:
response = iam_client.get_user()

    # Check if this is a root account
    if 'User' in response:
        user_arn = response['User']['Arn']
        if ':root' in user_arn:
            print_warning("You are using ROOT account credentials!")
            print_info("It's strongly recommended to use IAM user credentials instead.")
            return 'root'
        return response['User']['UserName']
    else:
        return 'root'
        
except ClientError as e:
    error_code = e.response['Error']['Code']
    error_msg = e.response['Error']['Message']
    
    if error_code == 'ValidationError':
        print_warning("Detected ROOT account")
        print_info("Using root credentials is not recommended for security!")
        return 'root'
    elif error_code == 'InvalidClientTokenId':
        print_error("Authentication failed: Invalid Access Key ID")
    elif error_code == 'SignatureDoesNotMatch':
        print_error("Authentication failed: Invalid Secret Access Key")
    elif error_code == 'AccessDenied':
        print_error(f"Access Denied: {error_msg}")
        print_info("Your IAM user needs 'iam:GetUser' permission.")
    else:
        print_error(f"Error getting user information: {error_msg}")
    return None
except KeyError as e:
    print_error(f"Unexpected response format: Missing key {str(e)}")
    return None
except Exception as e:
    print_error(f"Unexpected error while authenticating: {str(e)}")
    return None

def list_access_keys(iam_client, username):
"""List all access keys for the user with their age"""
try:
# For root account, don't specify UserName parameter
if username == 'root':
response = iam_client.list_access_keys()
else:
response = iam_client.list_access_keys(UserName=username)

    keys = response['AccessKeyMetadata']
    
    if not keys:
        print_warning("No access keys found for this user.")
        return []
    
    print(f"\n{Colors.BOLD}{Colors.WHITE}📋 Access Keys for: {Colors.CYAN}{username}{Colors.ENDC}\n")
    
    # Table header
    header = f"{Colors.BOLD}{Colors.BLUE}"
    print(f"{header}{'Key ID':<25} {'Status':<12} {'Created':<25} {'Age (days)':<12}{Colors.ENDC}")
    print(f"{Colors.BLUE}{'─' * 78}{Colors.ENDC}")
    
    current_time = datetime.now(timezone.utc)
    key_info = []
    
    for key in keys:
        key_id = key['AccessKeyId']
        status = key['Status']
        created = key['CreateDate']
        age_days = (current_time - created).days
        
        key_info.append({
            'KeyId': key_id,
            'Status': status,
            'Created': created,
            'AgeDays': age_days
        })
        
        # Color code status
        if status == 'Active':
            status_colored = f"{Colors.GREEN}{status}{Colors.ENDC}"
        else:
            status_colored = f"{Colors.RED}{status}{Colors.ENDC}"
        
        # Color code age
        if age_days > 90:
            age_colored = f"{Colors.RED}{age_days}{Colors.ENDC}"
        elif age_days > 60:
            age_colored = f"{Colors.YELLOW}{age_days}{Colors.ENDC}"
        else:
            age_colored = f"{Colors.GREEN}{age_days}{Colors.ENDC}"
        
        print(f"{Colors.WHITE}{key_id:<25}{Colors.ENDC} {status_colored:<20} "
              f"{Colors.WHITE}{created.strftime('%Y-%m-%d %H:%M:%S'):<25}{Colors.ENDC} {age_colored:<20}")
    
    print(f"{Colors.BLUE}{'─' * 78}{Colors.ENDC}")
    
    # Show age legend
    print(f"\n{Colors.BOLD}Age Legend:{Colors.ENDC} "
          f"{Colors.GREEN}< 60 days{Colors.ENDC} | "
          f"{Colors.YELLOW}60-90 days{Colors.ENDC} | "
          f"{Colors.RED}> 90 days{Colors.ENDC}")
    
    return key_info

except ClientError as e:
    print_error(f"Error listing access keys: {e.response['Error']['Message']}")
    return []

def create_new_access_key(iam_client, username):
"""Create a new access key for the user"""
try:
# For root account, don't specify UserName parameter
if username == 'root':
response = iam_client.create_access_key()
else:
response = iam_client.create_access_key(UserName=username)

    new_key = response['AccessKey']
    
    print(f"\n{Colors.GREEN}{Colors.BOLD}{'═' * 72}{Colors.ENDC}")
    print(f"{Colors.GREEN}{Colors.BOLD}✓ NEW ACCESS KEY CREATED SUCCESSFULLY!{Colors.ENDC}")
    print(f"{Colors.GREEN}{Colors.BOLD}{'═' * 72}{Colors.ENDC}\n")
    
    print(f"{Colors.RED}{Colors.BOLD}⚠  CRITICAL: Save these credentials NOW!{Colors.ENDC}")
    print(f"{Colors.RED}   They will never be shown again!{Colors.ENDC}\n")
    
    print(f"{Colors.CYAN}{'─' * 72}{Colors.ENDC}")
    print(f"{Colors.BOLD}Access Key ID:{Colors.ENDC}     {Colors.WHITE}{Colors.BOLD}{new_key['AccessKeyId']}{Colors.ENDC}")
    print(f"{Colors.BOLD}Secret Access Key:{Colors.ENDC} {Colors.WHITE}{Colors.BOLD}{new_key['SecretAccessKey']}{Colors.ENDC}")
    print(f"{Colors.BOLD}Created:{Colors.ENDC}           {Colors.WHITE}{new_key['CreateDate'].strftime('%Y-%m-%d %H:%M:%S UTC')}{Colors.ENDC}")
    print(f"{Colors.CYAN}{'─' * 72}{Colors.ENDC}\n")
    
    return new_key['AccessKeyId']

except ClientError as e:
    error_code = e.response['Error']['Code']
    if error_code == 'LimitExceeded':
        print_error("You already have the maximum number of access keys (2).")
        print_info("Please delete an old key before creating a new one.")
    else:
        print_error(f"Error creating access key: {e.response['Error']['Message']}")
    return None

def delete_access_key(iam_client, username, key_id):
"""Delete an access key"""
try:
# For root account, don't specify UserName parameter
if username == 'root':
iam_client.delete_access_key(AccessKeyId=key_id)
else:
iam_client.delete_access_key(UserName=username, AccessKeyId=key_id)

    print_success(f"Access key {key_id} deleted successfully!")
    return True
except ClientError as e:
    print_error(f"Error deleting access key: {e.response['Error']['Message']}")
    return False

def main_menu():
"""Display main menu and get user choice"""
print(f"\n{Colors.BOLD}{Colors.BLUE}{'═' * 72}{Colors.ENDC}")
print(f"{Colors.BOLD}{Colors.WHITE} MAIN MENU{Colors.ENDC}")
print(f"{Colors.BOLD}{Colors.BLUE}{'═' * 72}{Colors.ENDC}\n")

options = [
    ("1", "📋 List All Access Keys", "View all keys with age information"),
    ("2", "➕ Create New Access Key", "Generate a new access key pair"),
    ("3", "🗑️  Delete Access Key", "Remove an existing access key"),
    ("4", "🔄 Rotate Keys", "Create new key and delete old one"),
    ("5", "🚪 Exit", "Close the application securely"),
]

for num, title, desc in options:
    print(f"  {Colors.BOLD}{Colors.CYAN}{num}.{Colors.ENDC} {Colors.BOLD}{title}{Colors.ENDC}")
    print(f"     {Colors.WHITE}{desc}{Colors.ENDC}\n")

print(f"{Colors.BOLD}{Colors.BLUE}{'─' * 72}{Colors.ENDC}")
choice = input(f"\n{Colors.BOLD}Enter your choice (1-5): {Colors.ENDC}").strip()
return choice

def rotate_keys(iam_client, username, keys_info):
"""Complete key rotation: create new, then delete old"""
print_section_header("🔄 KEY ROTATION PROCESS")

if len(keys_info) >= 2:
    print_warning("You have 2 access keys (maximum allowed).")
    print_info("You must delete one before creating a new key.")
    return

print_info("Starting key rotation process...\n")

# Create new key
new_key_id = create_new_access_key(iam_client, username)

if not new_key_id:
    return

# Ask if user wants to delete old key
if keys_info:
    print(f"\n{Colors.YELLOW}{'─' * 72}{Colors.ENDC}")
    confirm = input(f"\n{Colors.BOLD}Delete the old access key now? (yes/no): {Colors.ENDC}").strip().lower()
    
    if confirm in ['yes', 'y']:
        old_key_id = keys_info[0]['KeyId']
        print(f"\n{Colors.YELLOW}Deleting old key: {old_key_id}{Colors.ENDC}")
        if delete_access_key(iam_client, username, old_key_id):
            print(f"\n{Colors.GREEN}{Colors.BOLD}✓ KEY ROTATION COMPLETED SUCCESSFULLY!{Colors.ENDC}\n")
    else:
        print_warning("Remember to delete your old key after updating your applications!")

def main():
"""Main program flow"""
try:
# Get credentials from user
access_key, secret_key = get_credentials()

    if not access_key or not secret_key:
        print_error("Access key and secret key are required!")
        return
    
    # Create IAM client
    iam_client = create_iam_client(access_key, secret_key)
    if not iam_client:
        return
    
    # Get username
    username = get_username(iam_client)
    if not username:
        return
    
    print_success(f"Successfully authenticated as: {Colors.BOLD}{Colors.CYAN}{username}{Colors.ENDC}")
    input(f"\n{Colors.YELLOW}Press Enter to continue...{Colors.ENDC}")
    
    # Main loop
    while True:
        clear_screen()
        print_banner()
        print(f"{Colors.WHITE}Logged in as: {Colors.BOLD}{Colors.CYAN}{username}{Colors.ENDC}\n")
        
        choice = main_menu()
        
        if choice == '1':
            print_section_header("📋 ACCESS KEY LIST")
            keys_info = list_access_keys(iam_client, username)
            input(f"\n{Colors.YELLOW}Press Enter to continue...{Colors.ENDC}")
        
        elif choice == '2':
            print_section_header("➕ CREATE NEW ACCESS KEY")
            keys_info = list_access_keys(iam_client, username)
            if len(keys_info) >= 2:
                print_error("Cannot create new key: You already have 2 keys (maximum).")
                print_info("Please delete an old key first.")
            else:
                create_new_access_key(iam_client, username)
            input(f"\n{Colors.YELLOW}Press Enter to continue...{Colors.ENDC}")
        
        elif choice == '3':
            print_section_header("🗑️  DELETE ACCESS KEY")
            keys_info = list_access_keys(iam_client, username)
            if keys_info:
                key_id = input(f"\n{Colors.BOLD}Enter the Access Key ID to delete: {Colors.ENDC}").strip()
                
                # Warn if deleting current key
                if key_id == access_key:
                    print_warning("You are about to delete the key you're currently using!")
                    confirm = input(f"{Colors.BOLD}Are you sure? This will end your session. (yes/no): {Colors.ENDC}").strip().lower()
                    if confirm not in ['yes', 'y']:
                        print_info("Deletion cancelled.")
                        input(f"\n{Colors.YELLOW}Press Enter to continue...{Colors.ENDC}")
                        continue
                
                delete_access_key(iam_client, username, key_id)
                
                if key_id == access_key:
                    print_warning("Your current session key was deleted. Exiting...")
                    input(f"\n{Colors.YELLOW}Press Enter to exit...{Colors.ENDC}")
                    break
            input(f"\n{Colors.YELLOW}Press Enter to continue...{Colors.ENDC}")
        
        elif choice == '4':
            keys_info = list_access_keys(iam_client, username)
            rotate_keys(iam_client, username, keys_info)
            input(f"\n{Colors.YELLOW}Press Enter to continue...{Colors.ENDC}")
        
        elif choice == '5':
            clear_screen()
            print_banner()
            print(f"\n{Colors.GREEN}{Colors.BOLD}{'═' * 72}{Colors.ENDC}")
            print(f"{Colors.GREEN}{Colors.BOLD}   Thank you for using IAM Key Rotation Utility!{Colors.ENDC}")
            print(f"{Colors.GREEN}{Colors.BOLD}{'═' * 72}{Colors.ENDC}\n")
            print_success("Session ended securely")
            print_info("No credentials were stored locally")
            print(f"\n{Colors.CYAN}Stay secure! 🔐{Colors.ENDC}\n")
            break
        
        else:
            print_error("Invalid choice. Please enter 1-5.")
            input(f"\n{Colors.YELLOW}Press Enter to continue...{Colors.ENDC}")
    
    # Clear sensitive data
    access_key = None
    secret_key = None
    iam_client = None

except KeyboardInterrupt:
    print(f"\n\n{Colors.YELLOW}Program interrupted by user.{Colors.ENDC}")
    print_info("Exiting securely...")
except Exception as e:
    print_error(f"Unexpected error: {str(e)}")
finally:
    print(f"\n{Colors.GREEN}🔒 Session ended. No credentials stored.{Colors.ENDC}\n")

if name == "main":
main()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment