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

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:

  1. 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.
  2. 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.
  3. 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:

  1. Create a new access key.
  2. (Optional) Replace the old access key with the new one everywhere.
  3. Deactivate the old access key.
  4. (Optional) Test the applications using the new key.
  5. 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.

Add user page showing dummy1 and dummy2 users being created and Access key - Programmatic Access is checked as credential type.

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.

Create group page showing the group name (rotate-key-demo) and policy )reset-own-credentials) selected int he list. Also there is Create group button at the bottom right.

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.

Add user page after the users have been created. It shows the user names, access key ids and secret access keys (masked)

Update your credentials file with these new accounts:

AWS credentials file showing the newly created users' access key ids and secret access keys
Make sure to keep your actual credentials safe somewhere so that you can put them back in your credentials file.

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:

Get-AWSCredential output showing the profilename, storetypename (SharedCredentialsFile) and profile location

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:

Script output showing the access keys have been replaced.

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.

Categories: awsiam