2.6 Access Keys: Creation, Rotation, and Least-Privilege Practices
Right, let’s talk about access keys. This is where the rubber meets the road, or more accurately, where your code meets AWS’s API. An access key is essentially a username and password for your code, comprised of an Access Key ID and a Secret Access Key. The ID is like your username—semi-public, often found in code. The Secret is, well, secret. It’s the password. If it gets out, someone else can pretend to be your application, and you’ll be paying for their crypto-mining adventure before you can say “bill shock.”
I’m going to walk you through creating them, the absolute necessity of rotating them, and how to do it without causing a four-alarm fire in your production environment.
Creating an Access Key (The Right Way)
You don’t just create a key willy-nilly. First, you need a user. And that user should have a policy attached that follows the principle of least privilege. Giving a user—and by extension, its access keys—full administrative power because you’re lazy is like giving your house keys to a dog walker and also the codes to your safe. Just don’t.
Let’s create a user meant for a backup script. It only needs to push files to one specific S3 bucket. This is the way.
# First, create the user. I'm calling it 'backup-bot'
aws iam create-user --user-name backup-bot
# Now, create a policy that allows write access ONLY to the 'my-app-backups' bucket.
# We're crafting this policy by hand because the AWS-managed policies are often too broad.
cat > backup-policy.json << EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:PutObjectAcl"
],
"Resource": "arn:aws:s3:::my-app-backups/*"
}
]
}
EOF
# Create the policy in IAM
aws iam create-policy --policy-name BackupBotS3Write --policy-document file://backup-policy.json
# Attach the policy to our user
aws iam attach-user-policy --user-name backup-bot --policy-arn "arn:aws:iam::123456789012:policy/BackupBotS3Write"
# NOW, and only now, we create the access key for the user
aws iam create-access-key --user-name backup-bot
The output of that last command is precious. It contains your shiny new Access Key ID and Secret Access Key. The Secret Access Key is shown only this one time. AWS will never show it to you again. If you lose it, you delete the key and create a new one. So, store it somewhere secure immediately, like a secrets manager. Do not, I repeat, do not paste it into a Slack channel or commit it to a public GitHub repo. The internet is littered with bots that scan for these, and they will find it.
The Non-Negotiable Practice of Key Rotation
Here’s the thing AWS doesn’t shout loudly enough: access keys don’t expire. They are valid until explicitly deleted. This is a terrifying design choice. A key created today could still be working a decade from now, long after the intern who created it has left the company and the script it was used for has been forgotten.
This is why you must rotate your keys. Regularly. The best practice is every 90 days. It’s a pain, but it’s a fraction of the pain of a credential leak.
The process isn’t just delete and create. That would cause downtime. The smart way is to have two keys active at any given time. You create a new key (Key #2), deploy it everywhere the old key (Key #1) is used, verify it works, and then disable and delete Key #1.
# List the current keys for the user to see the key id of the active one
aws iam list-access-keys --user-name backup-bot
# Create a second access key
aws iam create-access-key --user-name backup-bot
# Now you have two active keys. Update your application/configs to use the new key.
# Wait. Verify everything works with the new key. Seriously, go have a coffee. Come back.
# Once confirmed, deactivate the old key. This is a safety net; if you missed a spot, the app will break but you can quickly re-enable it.
aws iam update-access-key --user-name backup-bot --access-key-id AKIA1234567890OLDKEY --status Inactive
# Monitor for errors for a final time. If all is well, delete the old key.
aws iam delete-access-key --user-name backup-bot --access-key-id AKIA1234567890OLDKEY
Common Pitfalls and the “Why”
The Broad Policy: The most common mistake is using
"Resource": "*"because it’s easy. Your backup bot doesn’t need to list all buckets. It doesn’t need to read from any bucket. It needs to write to one specific directory. The policy we crafted above is tight and specific. This limits the “blast radius” if the key is ever compromised.Hardcoded Keys: Never hardcode keys in your application source code. Use environment variables or, better yet, a dedicated secrets management tool like AWS Secrets Manager. This makes rotation a configuration change instead of a code deployment.
Ignoring Inactive Keys: The
update-access-keycommand to set a key toInactiveis your best friend. It’s a way to test your rotation process without fully committing. If an old, forgotten system suddenly screams, you know you missed a spot and can re-enable the key while you fix it.User vs. Role Keys: Remember, for applications running inside AWS (on EC2, Lambda, ECS), you should almost always be using IAM Roles instead of access keys. Roles automatically rotate their temporary credentials in the background, saving you from this entire rotational headache. Access keys are primarily for external access: CI/CD systems, local development, or scripts running outside the AWS ecosystem.
The goal isn’t to make your life difficult; it’s to make an attacker’s life impossible. A little discipline here saves you from a world of hurt later. Now go rotate those keys. I’ll wait.