If you are working with AWS, it’s considered a good practice to rotate AWS access keys. When working with AWS resources, I try to use role-based access controls, but it’s not always possible to stay in the AWS ecosystem. At the very least, I use my IAM user credentials locally in my dev environment. The ability to replace these keys easily can help increase your AWS account’s security. In this article, you will implement a PowerShell script to rotate your IAM access keys. It works with multiple profiles, so if all your passwords are compromised in a security breach (such as the LastPass incident) you can replace all your IAM keys by simply running your script.
Prerequisites
- An AWS account (You can create a new account for free)
- AWS Tools for PowerShell (Installing the AWS Tools for PowerShell)
Why rotate AWS access keys?
Rotating AWS Access keys is a best practice for managing the security of your AWS accounts. It is a way to periodically update the keys used to access your AWS resources, which helps protect against unauthorised access to your accounts.
There are several reasons why you should rotate your AWS IAM keys:
- Security: One of the main reasons to rotate your IAM keys is to enhance the security of your AWS accounts. Regularly updating your keys can minimize the risk of someone gaining unauthorized access to your accounts.
- Compliance: Many regulatory standards and industry best practices recommend rotating IAM keys on a regular basis as a way to ensure the security and integrity of your systems.
- Convenience: Rotating IAM keys can also be convenient. It allows you to easily revoke access to your resources if a team member leaves your organisation or a key is lost or compromised.
It’s also advised to use IAM roles where possible, but if you have to use access keys, it’s a good idea to rotate them periodically.
How to rotate AWS access keys?
AWS allows an IAM user to have a maximum of 2 access keys. This way, you can rotate your keys without disruption by following the steps below:
- Create a new access key.
- (Optional) Replace the old access key with the new one everywhere.
- Deactivate the old access key.
- (Optional) Test the applications using the new key.
- Delete the old access key.
The script you will build in this article focuses on replacing all the keys in your credentials file (stored locally on your computer) in one fell swoop. It assumes the keys are only used in your AWS credentials file. Therefore, it skips steps 2 and 4.
Set up the Test Environment
Before getting into the script, create the test environment. It’s always a good idea to test your code first on some dummy accounts.
In the IAM console, click Users and then Add users.
In the user details, enter dummy1 as the user name and click Add another user. Enter dummy2 as the second user name.
Tick Access key – programmatic access in the Select AWS credential type section.
Scroll down to the Attach permissions policies section and click Create policy.
Click Next: Permissions.
Click Add users to group (which will make it easier to manage policies attached to the users) and click Create group.
Enter rotate-key-demo as the group name.
Click Create policy (opens a new tab).
At this point, the users you create don’t have any permissions. For a user to be able to reset their own credentials, they need some permissions.
In the Create policy page, switch to the JSON tab and paste the following policy:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "iam:ListUsers", "iam:GetAccountPasswordPolicy" ], "Resource": "*" }, { "Effect": "Allow", "Action": [ "iam:*AccessKey*", "iam:ChangePassword", "iam:GetUser", "iam:*ServiceSpecificCredential*", "iam:*SigningCertificate*" ], "Resource": [ "arn:aws:iam::*:user/${aws:username}" ] } ] }
This policy allows the user to only reset their own password and access keys.
Click Next:Tags and then Next:Review.
Enter reset-own-credentials as the policy name and click Create policy.
Switch to the group creation tab and click Refresh to see the newly created policy. If it’s not visible on the page, you can search by name to find it.
After you’ve located the policy, tick the checkbox next to it and click the Create group button.
You should now be back on to Add user page. Click the Next:Tags button, then click Next:Review.
Finally, click the Create users button to finalize the process.
After the users have been created, AWS allows you to note down the secret access key. When you close this page, you will not see the secret access key again so copy them and put them in your credentials file.
Update your credentials file with these new accounts:
Now that the test environment is ready let’s start looking into the steps to rotate the AWS access keys.
Implement the script
Create a new PowerShell file, such as rotate-keys.ps1 and paste the following code:
param ([switch]$Remove) $awsProfiles = Get-AWSCredential -ListProfileDetail $awsProfiles | %{ Write-Host "Rotating key for profile [$($_.ProfileName)]" try { $newIamAccessKeyResult = New-IAMAccessKey -ProfileName $_.ProfileName Write-Host "Added new access key: $($newIamAccessKeyResult.AccessKeyId)" Set-AWSCredential -AccessKey $newIamAccessKeyResult.AccessKeyId -SecretKey $newIamAccessKeyResult.SecretAccessKey -StoreAs $_.ProfileName Write-Host "Waiting 10 seconds for the IAM key to propagate" Start-Sleep -Seconds 10 Write-Host "Getting the oldest key..." $oldestKey = Get-IAMAccessKey -ProfileName $_.ProfileName | Sort-Object -Property CreateDate | Select-Object -First 1 Write-Host "Got the oldest key: $($oldestKey.AccessKeyId)" if ($Remove) { Write-Host "Removing key $($oldestKey.AccessKeyId)" Remove-IAMAccessKey -AccessKeyId $oldestKey.AccessKeyId -Force -ProfileName $_.ProfileName } else { Write-Host "Deactivating key $($oldestKey.AccessKeyId)" Update-IAMAccessKey -AccessKeyId $oldestKey.AccessKeyId -Status "Inactive" -Force -ProfileName $_.ProfileName } Write-Host "Access key for [$($_.ProfileName)] has been replaced." } catch { Write-Host "An error occured: $PSItem" -ForegroundColor DarkRed } }
Now, investigate the script line by line:
The script accepts a switch type parameter (like a boolean, it evaluates to true when provided and false otherwise). If you run the script with this argument (e.g. ./rotate-keys.ps1 -Remove), it deletes the old key rather than deactivating it. This is useful when you are sure the keys are not used anywhere and you are confident you can replace the old ones immediately.
The first action is to enumerate all the access keys in your credentials file carried out by the Get-AWSCredential -ListProfileDetail
command. If you run this command in a terminal, you should see the following result:
It then loops through all the profiles.
First, it creates a new key and updates the credentials file:
$newIamAccessKeyResult = New-IAMAccessKey -ProfileName $_.ProfileName Write-Host "Added new access key: $($newIamAccessKeyResult.AccessKeyId)" Set-AWSCredential -AccessKey $newIamAccessKeyResult.AccessKeyId -SecretKey $newIamAccessKeyResult.SecretAccessKey -StoreAs $_.ProfileName
In my tests, it takes a few seconds for the new key to propagate, so there is a 10-second delay added to ensure you don’t get an access denied error. You can experiment with this value and see what works best for you.
The next step is to find the oldest key to remove or deactivate. It does this by calling the Get-IAMAccessKey
cmdlet. It sorts the results by CreateDate and gets the first result which is the oldest created key.
Then it either removes it completely or deactivates it so that you can undo it easily:
if ($Remove) { Write-Host "Removing key $($oldestKey.AccessKeyId)" Remove-IAMAccessKey -AccessKeyId $oldestKey.AccessKeyId -Force -ProfileName $_.ProfileName } else { Write-Host "Deactivating key $($oldestKey.AccessKeyId)" Update-IAMAccessKey -AccessKeyId $oldestKey.AccessKeyId -Status "Inactive" -Force -ProfileName $_.ProfileName }
Run the script with the Remove argument, and it should look like this:
Check the IAM console and make sure the active key matches the one in your credentials file and it’s the newest one.
Now you can replace the dummy keys with your actual ones and rotate them easily by running this script whenever you want.
Conclusion
Dealing with security is always hard. Generally, there is always a security vs convenience trade-off. By automating security-related tasks as much as possible, you can reduce this friction and maintain more secure systems easier. This script and tutorial are just an attempt in this direction. Hope it helps.