#!/usr/bin/python3 from datetime import datetime import boto3 import jmespath import time import re # dump user/group/role last activity def generateLastAccessed(myclient: boto3.client, arn: str, myAccountId: str) -> list[str]: response = myclient.generate_service_last_accessed_details( Arn=arn, Granularity='SERVICE_LEVEL') jobId = response.get("JobId") accessDetails = client.get_service_last_accessed_details(JobId=jobId, MaxItems=20) while True: time.sleep(2) accessDetails = client.get_service_last_accessed_details(JobId=jobId, MaxItems=20) if accessDetails.get("JobStatus") != "COMPLETED": continue else: break r2 = client.list_policies_granting_service_access( Arn=arn, ServiceNamespaces=jmespath.search("ServicesLastAccessed[*].ServiceNamespace", accessDetails) ) returnString = [] for p in jmespath.search("PoliciesGrantingServiceAccess[*].Policies[]", r2): if p.get("PolicyType") == "INLINE": returnString.append("INLINE:" + p.get("PolicyName")) else: if myAccountId in p.get("PolicyArn"): returnString.append(p.get("PolicyArn")) return list(dict.fromkeys(returnString)) def formatDate(myTime: time) -> str: if myTime is None: return "Never" else: return myTime.date() def getPolicyUpdateTime(myClient: boto3.client, arn: str) -> str: resp = myClient.get_policy(PolicyArn=arn) return resp.get("Policy").get("UpdateDate").date() def heading(title: str) -> None: print("=" * 40) print("**", title, "**") print("=" * 40) sts = boto3.client('sts') accountId = sts.get_caller_identity()["Account"] client = boto3.client('iam') heading("Users") entity = client.list_users() for u in jmespath.search("Users[*]", entity): accessKeyQuery = client.list_access_keys(UserName=u.get('UserName')) keys = accessKeyQuery.get("AccessKeyMetadata") print("UserName", u.get("UserName"), sep=": ") print("CreateDate", formatDate(u.get("CreateDate")), sep=": ") print("PasswordLastUsed", formatDate(u.get("PasswordLastUsed")), sep=": ") doPolicyLastUsed = False if u.get("PasswordLastUsed") is None else True for k in accessKeyQuery.get("AccessKeyMetadata"): print("AccessKeyId", k.get("AccessKeyId"), sep=": ") print("AccessKeyStatus", k.get("Status"), sep=": ") if k.get("Status") == "Inactive": doPolicyLastUsed = False print("AccessKeyCreateDate", formatDate(k.get("CreateDate")), sep=": ") akLastUsedQuery = client.get_access_key_last_used(AccessKeyId=k.get("AccessKeyId")) print("AccessKeyLastUsed", formatDate(akLastUsedQuery.get("AccessKeyLastUsed").get("LastUsedDate")), sep=": ") if doPolicyLastUsed: lastAccessed = generateLastAccessed(client, u.get("Arn"), accountId) if len(lastAccessed) > 0: print("CustomerPolicyLastUsed and PolicyLastModified:") for p in lastAccessed: if "INLINE" not in p: print(p, getPolicyUpdateTime(client, p), sep=", ") else: print(p) print("-" * 10) heading("Groups") entity = client.list_groups() print("GroupName", "CreateDate", sep=", ") for g in jmespath.search("Groups[*]", entity): print(g.get("GroupName"), formatDate(g.get("CreateDate")), sep=", ") heading("Roles") entity = client.list_roles() for r in jmespath.search("Roles[*]", entity): if re.match("^/.*service-role/.*", r.get("Path")) is not None or re.match("^/aws-reserved/", r.get("Path")) is not None: continue getRoleQuery = client.get_role(RoleName=r.get("RoleName")) r1 = getRoleQuery.get("Role") print("RoleName", r1.get("RoleName"), sep=": ") print("CreateDate", formatDate(r1.get("CreateDate")), sep=": ") print("RoleLastUsed", formatDate(jmespath.search("RoleLastUsed.LastUsedDate", r1)), sep=": ") if jmespath.search("RoleLastUsed.LastUsedDate", r1) is not None: lastAccessed = generateLastAccessed(client, r1.get("Arn"), accountId) if len(lastAccessed) > 0: print("CustomerPolicyLastUsed and PolicyLastModified:") for p in lastAccessed: if "INLINE" not in p: print(p, getPolicyUpdateTime(client, p), sep=", ") else: print(p) print("-" * 10)