1. 개요

사용자별 Active key age를 보고 싶다.

2. 코드

파일명.py

import boto3
from datetime import datetime, timezone
 
def utc_to_local(utc_dt):
    return utc_dt.replace(tzinfo=timezone.utc).astimezone(tz=None)
 
def diff_dates(date1, date2):
    return abs(date2 - date1).days
 
resource = boto3.resource('iam')
client = boto3.client("iam")
 
KEY = 'LastUsedDate'
 
print ("{:<30} {:<25} {:<25} {:<10} {:<10}".format('User','Key','LastUsed','AgeOfKey',"Critical"))
 
for user in resource.users.all():
    Metadata = client.list_access_keys(UserName=user.user_name)
 
    if Metadata['AccessKeyMetadata']:
        for key in user.access_keys.all():
 
            AccessId = key.access_key_id
            Status = key.status
            CreatedDate = key.create_date
 
            numOfDays = diff_dates(utc_to_local(datetime.utcnow()), utc_to_local(CreatedDate))
            LastUsed = client.get_access_key_last_used(AccessKeyId=AccessId)
 
            if (Status == "Active"):
                if KEY in LastUsed['AccessKeyLastUsed']:
                    accessKeyLastUsed = LastUsed['AccessKeyLastUsed'][KEY].strftime("%Y-%m-%d %H:%M:%S")
                    print("{:<30} {:<25} {:<25} {:<10} {:<10}".format(user.user_name, AccessId, accessKeyLastUsed, numOfDays, "O" if numOfDays>90 else ""))
                else:
                    print("{:<30} {:<25} {:<25}".format(user.user_name, AccessId, "Active Key but never used"))
            else:
                print("{:<30} {:<25} {:<25}".format(user.user_name, AccessId, "Inactive Key"))
    else:
        print("{:<30} {:<25} {:<25}".format(user.user_name, AccessId, "No Key"))

3. Lambda 코드화

위 기본 Python 코드를 기반이며, 결과를 특정 S3 버킷에 저장하는 람다 코드이다.

3.1. 권한 부여

해당 Lambda 코드 role에 다음 policy를 부여한다.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "iam:GetAccessKeyLastUsed",
                "iam:ListUsers",
                "iam:ListAccessKeys"
            ],
            "Resource": "*"
        }
    ]
}

3.2. 추가 패키지 설치

prettytable import를 위해 패키지를 설치하고 Lambda에 넣어준다.

자세한 방법은 https://sarc.io/index.php/aws/805-aws-lambda-zip-python 을 참고한다.

Lambda 상에서 lambda_function.py 와 같은 레벨로 각 패키지의 디렉토리들이 존재하면 된다.

3.3. 코드

lambda_function.py

import json
import boto3
import traceback
from prettytable import PrettyTable
from datetime import datetime, timezone
 
def utc_to_local(utc_dt):
    return utc_dt.replace(tzinfo=timezone.utc).astimezone(tz=None)
 
def diff_dates(date1, date2):
    return abs(date2 - date1).days
 
def upload_file_s3(bucket, file_name, file):
    s3 = boto3.client('s3')
    try:
        s3.put_object(Bucket=bucket, Key=file_name, Body=str(file))
        return True
    except:
        traceback.print_exc()
        return False
 
def lambda_handler(event, context):
    resource = boto3.resource('iam')
    client = boto3.client("iam")
 
    KEY = 'LastUsedDate'
 
    t = PrettyTable(['User', 'Key', 'LastUsed', 'AgeOfKey', 'Critical'])
 
    print ("{:<30} {:<25} {:<25} {:<10} {:<10}".format('User','Key','LastUsed','AgeOfKey',"Critical"))
 
    for user in resource.users.all():
        Metadata = client.list_access_keys(UserName=user.user_name)
 
        if Metadata['AccessKeyMetadata']:
            for key in user.access_keys.all():
                AccessId = key.access_key_id
                Status = key.status
                CreatedDate = key.create_date
 
                numOfDays = diff_dates(utc_to_local(datetime.utcnow()), utc_to_local(CreatedDate))
                LastUsed = client.get_access_key_last_used(AccessKeyId=AccessId)
 
                if (Status == "Active"):
                    if KEY in LastUsed['AccessKeyLastUsed']:
                        accessKeyLastUsed = LastUsed['AccessKeyLastUsed'][KEY].strftime("%Y-%m-%d %H:%M:%S")
                        t.add_row([user.user_name, AccessId, accessKeyLastUsed, numOfDays, "O" if numOfDays>90 else ""])
                    else:
                        t.add_row([user.user_name, AccessId, "Active Key but never used", "", ""])
                else:
                    t.add_row([user.user_name, AccessId, "Inactive Key", "", ""])
        else:
            #print("{:<30} {:<25} {:<25}".format(user.user_name, AccessId, "No Key"))
            t.add_row([user.user_name, AccessId, "No Key", "", ""])
 
    bucket = '<S3 버킷 이름>'
    file_name = "iam-user-access-key-" + datetime.now().strftime("%Y%m%d-%H%M%S")
    result = upload_file_s3(bucket, file_name + '.txt', t)
 
    if result:
        return {
            'statusCode': 200,
            'body': json.dumps("Upload success")
        }
    else:
        return {
            'statusCode': 400,
            'body': json.dumps("Upload fail")
        }