How to scan your AWS account for old access keys using python
Tags: aws, python, security, iam
Published: 2020-03-29 15:59:50

This article was first published on [razcodes.dev](https://razcodes.dev "razcodes.dev")

In AWS, control 1.3 of the [CIS AWS Foundations](https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html "CIS AWS") talks about making sure that keys that are older than 90 days are disabled. You can check this manually of course using the AWS console as explained in the documentation or you can write a script that does those checks for you with python.

Using the terminal, in your projects folder create a new directory for this experiment.

```bash
mkdir iam-key-scanner && cd iam-key-scanner
```

I like having virtual environments for all my projects, so I will create a new one and activate it.

```bash
python3 -m venv venv
source ./venv/bin/activate
```

You will need to install 2 packages to follow along. One is of course boto3 and the other one is python-dotenv for storing the AWS keys used to to the scan.

```bash
pip install boto3 python-dotenv
```

Create a .env file for storing the AWS credentials. If later you decide to convert this into a lambda, you would of course not need this and you would create a role, but for running it from the computer this approach is a good option.

```bash
touch .env
```

Edit the .env file in your favorite editor and add the following 2 lines with your credentials.

```bash
AWS_ACCESS_KEY_ID=your-access-key-id
AWS_SECRET_ACCESS_KEY=your-secret-access-key
```

First thing that we have to do is to generate the report and then after it's generated we can read it and take action. To keep this simple I will break it into the two parts, however they can be part of the same script when you are ready for production.

## Generating the report

Here is the script that will generate the report.

```bash
touch generate-report.py
```

```python
import os
import boto3
from dotenv import load_dotenv

load_dotenv()

AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_ACCESS_KEY')

client = boto3.client(
    'iam',
    aws_access_key_id=AWS_ACCESS_KEY_ID,
    aws_secret_access_key=AWS_SECRET_ACCESS_KEY
)

response = client.generate_credential_report()
```

Now run it.

```python
python generate-report.py
```

The report generation is pretty fast, but if you want to wait for it just look at the response. When the _State_ is 'COMPLETE', the report is ready.

```bash
print(response['State'])
```

## Parsing the report

For parsing the report I decided to use _namedtuple_ so I can access the data more easily.

I am using _timedelta_, so I can calculate the age of a user key. You will see that there are two user keys for each user so we need to make sure we look at both. I am also declaring a variable called _format_ that I am using together with _strptime_ to get the returned date as I want it.

This script will just print out the users, but you can easily create another function that you can call to disable the expired key or do perform key rotation. Also note that the script will show you the users with keys older than 90 days even if the key is inactive. You can add an extra check if you would like to only show the ones with active keys.

```bash
touch parse-user-report.py
```

```python
import os
import boto3
from dotenv import load_dotenv
from collections import namedtuple
from datetime import datetime, timedelta, timezone

retentionDate = datetime.now() - timedelta(days=90)
format = '%Y-%m-%d'

load_dotenv()

User = namedtuple('User', 'user arn user_creation_time password_enabled password_last_used password_last_changed password_next_rotation mfa_active access_key_1_active access_key_1_last_rotated access_key_1_last_used_date access_key_1_last_used_region access_key_1_last_used_service access_key_2_active access_key_2_last_rotated access_key_2_last_used_date access_key_2_last_used_region access_key_2_last_used_service cert_1_active cert_1_last_rotated cert_2_active cert_2_last_rotated')

AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_ACCESS_KEY')

client = boto3.client(
    'iam',
    aws_access_key_id=AWS_ACCESS_KEY_ID,
    aws_secret_access_key=AWS_SECRET_ACCESS_KEY
)

response = client.get_credential_report()
body = response['Content'].decode('utf-8')
lines = body.split('\n')

users = [User(*line.split(',')) for line in lines[1:]]
for user in users:
    if (user.access_key_1_last_rotated != 'N/A' and datetime.strptime(user.access_key_1_last_rotated[:10], format) < retentionDate) or (user.access_key_2_last_rotated != 'N/A' and datetime.strptime(user.access_key_2_last_rotated[:10], format) < retentionDate):
        print(user.user)
```

```python
python parse-user-report.py
```

Please let me know if you have any questions or if you would have done something differently.