"""Application aws iam users script.""" import argparse import csv import json import logging import os from datetime import datetime from email.mime.application import MIMEApplication from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText import boto3 import botocore from botocore.exceptions import ClientError import requests config = botocore.config.Config( region_name='us-east-1', retries=dict(max_attempts=10, mode='adaptive') ) client = boto3.client('iam', config=config) now = datetime.now() dt_string = now.strftime('%d-%m-%Y-%H%M%S') file_name = '../spreadsheets/aws-iam-user-audit_{date_time}.csv'.format(date_time=dt_string) # noqa: E501 key = 'aws-iam-user-audit/aws-iam-user-audit_{date_time}.csv'.format(date_time=dt_string) # noqa: E501 bucket = os.getenv('BUCKET') recipient = os.getenv('RECIPIENT') source = os.getenv('SOURCE') LINK_TO_POST = os.getenv('LINK_TO_POST') def get_iam_user_list(): """Get iam users. Returns: list: List of all iam users """ iam_users = [] response = client.list_users(MaxItems=700) if 'Users' in response: iam_users = response['Users'] return iam_users def update_iam_user_list(iam_users): """Update iam users list. Args: iam_users: List of all iam users Returns: list: Updated list of all iam users """ delete_unnecessary_user_info(iam_users) add_groups_to_iam_user(iam_users) add_inline_and_direct_policies_to_users(iam_users) add_iam_console_link(iam_users) def delete_unnecessary_user_info(iam_users): """Delete unnecessary info from user list. Args: iam_users: List of all iam users Returns: list: Updated list of all iam users """ for each_user in iam_users: del each_user['Path'] del each_user['Arn'] del each_user['UserId'] def add_groups_to_iam_user(iam_users): """Add iam groups lists to iam users. Args: iam_users: List of all iam users Returns: list: Updated list of all iam users """ i = 0 while i < len(iam_users): user_groups = [] iam_user = iam_users[i] response = client.list_groups_for_user(UserName=iam_user['UserName']) if 'Groups' in response: user_groups = response['Groups'] j = 0 updated_user_groups = [] while j < len(user_groups): user_group = user_groups[j] updated_user_groups.append(user_group['GroupName']) j += 1 if len(updated_user_groups) == 0: updated_user_groups = 'None' iam_user['Groups'] = updated_user_groups i += 1 def add_inline_and_direct_policies_to_users(iam_users): """Get inline and direct policies to users from the list. Args: iam_users: List of all iam users Returns: list: Updated list of all iam users """ i = 0 while i < len(iam_users): iam_user = iam_users[i] inline_policies = client.list_user_policies( UserName=iam_user['UserName']) updated_inline_policies = inline_policies['PolicyNames'] directly_attached_policies = client.list_attached_user_policies( UserName=iam_user['UserName']) updated_directly_attached_policies = [] j = 0 while j < len(directly_attached_policies['AttachedPolicies']): policy = directly_attached_policies['AttachedPolicies'][j] updated_directly_attached_policies.append(policy['PolicyName']) j += 1 amount_of_policies = str(len(updated_inline_policies) + len(updated_directly_attached_policies)) iam_user['Directly Attached Policies'] = '=HYPERLINK("https://us-east-1.console.aws.amazon.com/iam/home?region=us-east-1#/users/{iam_user}"; "{amount_of_policies}")'.format(iam_user=iam_user['UserName'], amount_of_policies=amount_of_policies) # noqa: E501 iam_user['Inline Policies'] = len(updated_inline_policies) i += 1 def add_iam_console_link(iam_users): """Get iam console link to users from the list. Args: iam_users: List of all iam users Returns: list: Updated list of all iam users """ i = 0 while i < len(iam_users): iam_user = iam_users[i] iam_user['IAM Console link'] = 'https://us-east-1.console.aws.amazon.com/iam/home?region=us-east-1#/users/{iam_user}?section=groups'.format(iam_user=iam_user['UserName']) # noqa: E501 iam_user['AWS Username'] = iam_user['UserName'] del iam_user['UserName'] i += 1 def add_access_keys(iam_users): """Get users' access keys and add them to user object. Args: iam_users: List of all iam users """ for each_user in iam_users: each_user['Access Key First'] = 'No Access Key' each_user['Access Key Second'] = 'No Access Key' response = client.list_access_keys( UserName=each_user['UserName'], ) if len(response['AccessKeyMetadata']) >= 1: response_first = client.get_access_key_last_used( AccessKeyId=response['AccessKeyMetadata'][0]['AccessKeyId']) if 'LastUsedDate' in response_first['AccessKeyLastUsed']: each_user['Access Key First'] = \ response_first['AccessKeyLastUsed']['LastUsedDate'] else: each_user['Access Key First'] = 'Never used' if 'Status' in response['AccessKeyMetadata'][0]: if response['AccessKeyMetadata'][0]['Status'] \ == 'Inactive': each_user['Access Key First'] = 'Inactive' if len(response['AccessKeyMetadata']) == 2: response_second = client.get_access_key_last_used( AccessKeyId=response['AccessKeyMetadata'][1]['AccessKeyId'] ) if 'LastUsedDate' in response_second['AccessKeyLastUsed']: each_user['Access Key Second'] = \ response_second['AccessKeyLastUsed']['LastUsedDate'] else: each_user['Access Key Second'] = 'Never used' if 'Status' in response['AccessKeyMetadata'][1]: if response['AccessKeyMetadata'][1]['Status'] \ == 'Inactive': each_user['Access Key Second'] = 'Inactive' def add_user_tags(iam_users): """Get users' metadata and add it to user object. Args: iam_users: List of all iam users """ for each_user in iam_users: response = client.list_user_tags( UserName=each_user['UserName'] ) if len(response['Tags']) > 0: for each_tag in response['Tags']: if each_tag['Key'] == 'email': each_user['Email'] = each_tag['Value'] if each_tag['Key'] == 'name': each_user['Name'] = each_tag['Value'] if each_tag['Key'] == 'role': each_user['Role'] = each_tag['Value'] if each_tag['Key'] == 'manager': each_user['Manager'] = each_tag['Value'] if each_tag['Key'] == 'company': each_user['Company'] = each_tag['Value'] if each_tag['Key'] == 'Deactivated': each_user['Deactivated Date'] = each_tag['Value'] def update_password_last_used(iam_users): """Update PasswordLastUsed in user object. Args: iam_users: List of all iam users """ for each_user in iam_users: if 'PasswordLastUsed' not in each_user: each_user['PasswordLastUsed'] = 'Never used' def add_console_access(iam_users): """Check if the user object has console access. Args: iam_users: List of all iam users """ for each_user in iam_users: try: client.get_login_profile( UserName=each_user['UserName'] ) each_user['Console Access'] = 'Y' except client.exceptions.NoSuchEntityException: each_user['Console Access'] = 'N' def check_if_deactivated(iam_users): """Check if the user is deactivated. Args: iam_users: List of all iam users """ for each_user in iam_users: if each_user['Console Access'] == 'Y' or (each_user['Access Key First'] != 'Inactive' and each_user['Access Key First'] != 'No Access Key') or (each_user['Access Key Second'] != 'Inactive' and each_user['Access Key Second'] != 'No Access Key'): # noqa: E501 each_user['Deactivated?'] = 'N' else: each_user['Deactivated?'] = 'Y' def get_last_user_activity(iam_users): """Get last user activity days count. Args: iam_users: List of all iam users """ for each_user in iam_users: l_date = datetime.now().date() f_date = datetime(2000, 1, 1,).date() if each_user['PasswordLastUsed'] != 'Never used': f_date = each_user['PasswordLastUsed'].date() if each_user['Access Key First'] != 'Inactive' and each_user['Access Key First'] != 'No Access Key' and each_user['Access Key First'] != 'Never used': # noqa: E501 if f_date < each_user['Access Key First'].date(): f_date = each_user['Access Key First'].date() if each_user['Access Key Second'] != 'Inactive' and each_user['Access Key Second'] != 'No Access Key' and each_user['Access Key Second'] != 'Never used': # noqa: E501 if f_date < each_user['Access Key Second'].date(): f_date = each_user['Access Key Second'].date() if f_date != datetime(2000, 1, 1,).date(): delta = l_date - f_date each_user['Last Activity'] = str(delta.days) else: each_user['Last Activity'] = 'Never' def filter_service_users(iam_users): """Filter out service users from iam_users list. Args: iam_users: List of all iam users """ filtered = [] for each_user in iam_users: if 'Role' in each_user and each_user['Role'] != 'service-user': filtered.append(each_user) if 'Role' not in each_user: filtered.append(each_user) return filtered def filter_out_deactivated_users(iam_users): """Filter out deactivated users from iam_users list. Args: iam_users: List of all iam users """ filtered = [] for each_user in iam_users: if each_user['Deactivated?'] == 'N': filtered.append(each_user) return filtered def create_csv_user_spreadsheet(iam_users): """Create aws_users csv file. Args: iam_users: List of all iam users """ user_info = ['AWS Username', 'Name', 'Email', 'Role', 'Company', 'Manager', 'Manager Approval', 'IAM Console link', 'Groups', 'Deactivated?', 'Deactivated Date', 'CreateDate', 'Last Activity', 'Console Access', 'PasswordLastUsed', 'Directly Attached Policies', 'Inline Policies', 'Access Key First', 'Access Key Second'] os.makedirs(os.path.dirname(file_name), exist_ok=True) with open(file_name, 'w') as csvfile: writer = csv.DictWriter(csvfile, fieldnames=user_info) writer.writeheader() writer.writerows(iam_users) def upload_to_s3(): """Upload file to s3 bucket.""" s3_client = boto3.client('s3', config=config) try: s3_client.upload_file(file_name, bucket, key) except ClientError as e: logging.error(e) print('Uploading {} to s3://{}/{} failed'.format(file_name, bucket, key)) return False print('Uploading {} to s3://{}/{} was successful'.format(file_name, bucket, key)) return True def send_email(): """Send email with the aws-user-audit attachment.""" client_ses = boto3.client('ses', config=config) msg = MIMEMultipart() msg['Subject'] = 'AWS User Audit' body = MIMEText('AWS User Audit spreadsheet', 'plain') msg.attach(body) with open(file_name, 'rb') as attachment: part = MIMEApplication(attachment.read()) part.add_header('Content-Disposition', 'attachment', filename=file_name) msg.attach(part) client_ses.send_raw_email( Destinations=[recipient, ], RawMessage={'Data': msg.as_string()}, Source=source, ) def post_to_slack(): """Post to slack.""" payload = { 'blocks': [ { 'type': 'section', 'text': { 'type': 'mrkdwn', 'text': 'New AWS audit was generated and sent to your email' # noqa: E501 } } ] } response = requests.post(LINK_TO_POST, data=json.dumps(payload), headers={'Content-Type': 'application/json'}) print(response.text) def main(*args): """Start script.""" iam_users = get_iam_user_list() add_user_tags(iam_users) add_console_access(iam_users) update_password_last_used(iam_users) add_access_keys(iam_users) get_last_user_activity(iam_users) check_if_deactivated(iam_users) update_iam_user_list(iam_users) parser = argparse.ArgumentParser() parser.add_argument( '--upload', help='Upload to s3 bucket', default=False, action='store_true') parser.add_argument( '--email', help='Email spreadsheet to managers', default=False, action='store_true') parser.add_argument( '--no_service_users', help='Filter out service users', default=False, action='store_true') parser.add_argument( '--active_only', help='Filter out deactivated users', default=False, action='store_true') args = parser.parse_args(args) if args else parser.parse_args() if args.no_service_users: filtered_users = filter_service_users(iam_users) iam_users = filtered_users if args.active_only: filtered_users = filter_out_deactivated_users(iam_users) iam_users = filtered_users create_csv_user_spreadsheet(iam_users) if args.upload: upload_to_s3() if args.email: send_email() post_to_slack() if __name__ == '__main__': main()