Last active
December 15, 2020 09:32
-
-
Save MrHassanMurtaza/3f7e74c26d0d4c066bf8670cd612ce04 to your computer and use it in GitHub Desktop.
Automate Analyzing your IAM Permissions using IAM Access Advisor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import boto3 | |
from botocore.exceptions import ClientError | |
from datetime import datetime, timezone | |
import traceback | |
from time import sleep | |
RECIPIENTS = ["recipient_user@abc.com"] | |
SENDER = "John Doe <johndoe@abc.com>" | |
LAST_ACCESS_THRESHOLD = 90 | |
def get_iam_client(): | |
""" | |
Get identity and access management client | |
""" | |
return boto3.client( | |
'iam' | |
) | |
def get_ses_client(): | |
""" | |
Get simple email service client | |
""" | |
return boto3.client( | |
'ses' | |
) | |
def get_older_iam_roles(client): | |
""" | |
Get dictionary of iam roles with servics older than 90 days | |
:param client: iam client to list roles | |
""" | |
Services_Access = {} | |
# listing arns | |
list_roles_arn = client.list_roles() | |
if not list_roles_arn: | |
raise Exception("No roles found in account") | |
# iterating over arns | |
for single_arn in list_roles_arn['Roles']: | |
TODAYS_DAY = datetime.now(timezone.utc) | |
ARNS = single_arn['Arn'] | |
jobid_response = client.generate_service_last_accessed_details( | |
Arn=ARNS | |
) | |
role_jobid = jobid_response['JobId'] | |
service_response = client.get_service_last_accessed_details( | |
JobId=role_jobid | |
) | |
# checking if job is completed else wait and retry until job is completed | |
while service_response['JobStatus'] != 'COMPLETED': | |
# print(f'pending : {service_response}') | |
service_response = client.get_service_last_accessed_details( | |
JobId=role_jobid | |
) | |
sleep(1) | |
# getting last access services | |
last_accessed_services = service_response['ServicesLastAccessed'] | |
# iterating over servies to get the services with last access date greater than 90 | |
for last_accessed in last_accessed_services: | |
try: | |
# print(last_accessed) | |
role_lastaccess_day = last_accessed['LastAuthenticated'] | |
# difference between today and last access date | |
days_difference = TODAYS_DAY - role_lastaccess_day | |
# checking if difference is greater than 90 | |
check_difference = days_difference.days > LAST_ACCESS_THRESHOLD | |
if check_difference: | |
Services_Access[ARNS] = {} | |
Services_Access[ARNS][last_accessed['ServiceName']] = days_difference.days | |
except Exception as e: | |
continue | |
# returning dictionary containing iam roles for services with no access > 90 days | |
return Services_Access | |
def send_email_iam_services(client, old_iams, sender, receipients): | |
""" | |
Send email for all the iam roles with services access more than 90 days | |
:param client: simple emailing service client | |
:param old_iams: iam role arns dictionary | |
:param sender: email sender | |
:param receipients: list of receipients e.g. ['abc@gmail.com', 'abc123@gmail.com'] etc. | |
""" | |
CHARSET = "UTF-8" | |
SUBJECT = "List of IAM Roles" | |
TABLE_START="""<table style="width:100%"> | |
<tr> | |
<th>Role ARN</th> | |
<th>Service</th> | |
<th>Days</th> | |
</tr> """ | |
TABLE_BODY = """ """ | |
TABLE_END = """</table>""" | |
for role, services in old_iams.items(): | |
TABLE_BODY += "<tr>" | |
for service in services.items(): | |
TABLE_BODY += "<td>" + str(role) + "</td>" +"<td>" + str(service[0]) + "</td>" + "<td>" + str(service[1]) + "</td>" | |
TABLE_BODY += "</tr>" | |
TOTAL_TABLE = TABLE_START + TABLE_BODY + TABLE_END | |
# Try to send the email. | |
try: | |
#Provide the contents of the email. | |
response = client.send_email( | |
Destination={ | |
'ToAddresses': receipients, | |
}, | |
Message={ | |
'Body': { | |
'Html': { | |
'Charset': CHARSET, | |
'Data': TOTAL_TABLE, | |
} | |
}, | |
'Subject': { | |
'Charset': CHARSET, | |
'Data': SUBJECT, | |
}, | |
}, | |
Source=sender, | |
# If you are not using a configuration set, comment or delete the | |
# following line | |
) | |
# Display an error if something goes wrong. | |
except ClientError as e: | |
print(e.response['Error']['Message']) | |
raise Exception(e.response['Error']['Message']) | |
else: | |
print("Email sent for older IAM Roles! Message ID:"), | |
print(response['MessageId']) | |
def lambda_handler(event, context): | |
try: | |
# getting iam client to use | |
iam_client = get_iam_client() | |
# getting ses client to use | |
ses_client = get_ses_client() | |
# getting dictionary of all iam roles with last access > 90 | |
old_iams = get_older_iam_roles(iam_client) | |
# sending email for iam roles we got in last step | |
send_email_iam_services(ses_client, old_iams, SENDER, RECIPIENTS) | |
except Exception as e: | |
traceback.print_exc() | |
raise Exception(str(e)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for suck a great script! How would you print out the roles which were never excessed with value "None".
Thanks