Skip to content

Instantly share code, notes, and snippets.

@MrHassanMurtaza
Last active December 15, 2020 09:32
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MrHassanMurtaza/3f7e74c26d0d4c066bf8670cd612ce04 to your computer and use it in GitHub Desktop.
Save MrHassanMurtaza/3f7e74c26d0d4c066bf8670cd612ce04 to your computer and use it in GitHub Desktop.
Automate Analyzing your IAM Permissions using IAM Access Advisor
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))
@solotvun
Copy link

solotvun commented Apr 8, 2020

Thanks for suck a great script! How would you print out the roles which were never excessed with value "None".

Thanks

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