diff --git a/local-app/python-tools/aws-service-linked-roles/aws-slr-generate.py b/local-app/python-tools/aws-service-linked-roles/aws-slr-generate.py new file mode 100755 index 00000000..914769d5 --- /dev/null +++ b/local-app/python-tools/aws-service-linked-roles/aws-slr-generate.py @@ -0,0 +1,92 @@ +#!/bin/env python + +import boto3 +import argparse +import sys +from botocore.exceptions import ClientError, ProfileNotFound + +VERSION = "1.0.1" + +def get_account_id(session): + sts = session.client('sts') + return sts.get_caller_identity()['Account'] + +def generate_slr_terraform(): + parser = argparse.ArgumentParser(description="Audit AWS Service Linked Roles and generate Terraform import/vars files.") + parser.add_argument("--profile", help="AWS CLI profile to use") + parser.add_argument("--region", help="AWS region (e.g., us-gov-west-1)") + parser.add_argument("--import-file", default="import.tf", help="Output filename for import blocks") + parser.add_argument("--variables-file", default="variables.service-linked-roles.auto.tfvars", help="Output filename for tfvars") + parser.add_argument("--version", action="version", version=f"%(prog)s {VERSION}") + + args = parser.parse_args() + + try: + # Initialize Session + session = boto3.Session(profile_name=args.profile, region_name=args.region) + iam = session.client('iam') + account_id = get_account_id(session) + + service_names = [] + slr_count = 0 + + # Paginate through IAM roles with the SLR path + paginator = iam.get_paginator('list_roles') + for page in paginator.paginate(PathPrefix='/aws-service-role/'): + for role in page['Roles']: + # Extract the service principal from the Trust Policy + trust_policy = role.get('AssumeRolePolicyDocument', {}) + for statement in trust_policy.get('Statement', []): + principal = statement.get('Principal', {}).get('Service') + if principal: + slr_count += 1 + if isinstance(principal, list): + service_names.extend(principal) + else: + service_names.append(principal) + + # De-duplicate and sort + unique_services = sorted(list(set(service_names))) + + # 1. Generate Import File + with open(args.import_file, 'w') as f: + f.write(f"# Generated by SLR-to-TF v{VERSION}\n") + for service in unique_services: + f.write(f'import {{\n') + f.write(f' to = aws_iam_service_linked_role.roles["{service}"]\n') + f.write(f' id = "arn:aws:iam::{account_id}:role/aws-service-role/{service}"\n') + f.write(f'}}\n\n') + + # 2. Generate Variables File + with open(args.variables_file, 'w') as f: + f.write(f"# Generated by SLR-to-TF v{VERSION}\n") + f.write('service_linked_roles = [\n') + for service in unique_services: + f.write(f' "{service}",\n') + f.write(']\n') + + # Summary Output + print("-" * 40) + print(f"SLR-to-TF Generator v{VERSION}") + print("-" * 40) + print(f"AWS Account: {account_id}") + print(f"Region: {session.region_name}") + print(f"Roles Found: {slr_count}") + print(f"Unique Services: {len(unique_services)}") + print(f"Import File: {args.import_file}") + print(f"Variables File: {args.variables_file}") + print("-" * 40) + print("Success: Files generated. Run 'terraform plan' to verify imports.") + + except ProfileNotFound: + print(f"Error: The profile '{args.profile}' was not found.") + sys.exit(1) + except ClientError as e: + print(f"AWS Error: {e}") + sys.exit(1) + except Exception as e: + print(f"An unexpected error occurred: {e}") + sys.exit(1) + +if __name__ == "__main__": + generate_slr_terraform()