How to Rotate AWS Access Keys Without Causing Downtime

Retool's new app builder is where AI-generated code ships safely
Building apps with AI is easy. Getting them to production safely is another story.
AWS access keys are long-lived credentials: they do not expire by default and remain active until explicitly deleted or deactivated. A key that has existed for two years has likely been copied to config files, CI/CD environment variables, developer laptops, and documentation. Before you can rotate it safely, you need to know everywhere it lives.
The correct rotation sequence prevents the two most common failure modes: outages from deleting the old key before all consumers are updated, and key leaks from access keys that persist in git history or shared files after rotation.
Step 1: Audit Where the Key Is Currently Used
Before creating a new key, find every place the old key is used. Rotating without this step leads to missed consumers that break after the old key is deactivated.
Identify the key to rotate:
# List all access keys for an IAM user
aws iam list-access-keys --user-name myuser
# Output shows: AccessKeyId, Status (Active/Inactive), CreateDate, LastUsed
# Check the last-used timestamp for the key
aws iam get-access-key-last-used --access-key-id AKIAIOSFODNN7EXAMPLE
# Output includes: LastUsedDate, ServiceName, Region
CloudTrail: find recent API calls made with this key:
# Search CloudTrail for the access key ID used in the last 90 days
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=AccessKeyId,AttributeValue=AKIAIOSFODNN7EXAMPLE \
--start-time $(date -u -d '90 days ago' +%Y-%m-%dT%H:%M:%SZ) \
--query 'Events[*].{Time:EventTime,Event:EventName,Source:EventSource,User:Username,Region:CloudTrailEvent}' \
--output table | head -50
The CloudTrail results show which services were called and from which source: this tells you whether the key is used by EC2 instances, Lambda functions, a CI/CD pipeline, a developer's local machine, or all of the above.
Common locations to check for hardcoded keys:
# Search git history for the access key ID (run in each relevant repo)
git log --all -p | grep -F 'AKIAIOSFODNN7EXAMPLE'
# Search the local filesystem for the key ID
grep -r 'AKIAIOSFODNN7EXAMPLE' ~/.aws/ /etc/ /home/ /var/app/ 2>/dev/null
# Check CI/CD environment variables
# GitHub Actions: Settings > Secrets and variables > Actions
# Jenkins: Manage Jenkins > Credentials
# GitLab: Settings > CI/CD > Variables
# These must be updated manually after creating the new key
Step 2: Create the New Access Key
# Create a new access key for the user
# Note: this outputs the SecretAccessKey ONCE: capture it immediately
aws iam create-access-key --user-name myuser
Output:
{
"AccessKey": {
"UserName": "myuser",
"AccessKeyId": "AKIANEWKEYEXAMPLE",
"Status": "Active",
"SecretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYNEWKEYEXAMPLE",
"CreateDate": "2026-06-26T10:00:00Z"
}
}
The SecretAccessKey is shown exactly once. If you close the terminal without saving it, you must delete this key and create another.
At this point you have two active keys: the old key still works while you update consumers.
Store the new credentials securely before proceeding:
# Update your local AWS credentials profile
aws configure set aws_access_key_id AKIANEWKEYEXAMPLE --profile rotation-test
aws configure set aws_secret_access_key wJalrXUtnFEMI/K7MDENG/bPxRfiCYNEWKEYEXAMPLE --profile rotation-test
# Verify the new key works before updating any production consumers
aws sts get-caller-identity --profile rotation-test
Briefings like this, every morning before 9am.
Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.
Step 3: Update All Consumers
Update each consumer identified in Step 1 to use the new key. Do not deactivate the old key until all consumers are updated and confirmed working.
Update AWS CLI credentials file:
# ~/.aws/credentials
[default]
aws_access_key_id = AKIANEWKEYEXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYNEWKEYEXAMPLE
Update EC2 instance user data or instance profile: If the key is used directly on EC2 instances, the correct long-term fix is to replace the hardcoded key with an IAM instance profile: the instance assumes an IAM role, no key required. For immediate rotation, update the key in the configuration management system (Ansible, Chef, SSM Parameter Store) that provisions the instance.
Update application secrets stores:
# AWS Secrets Manager: update an existing secret
aws secretsmanager put-secret-value \
--secret-id myapp/aws-credentials \
--secret-string '{"aws_access_key_id":"AKIANEWKEYEXAMPLE","aws_secret_access_key":"wJalr..."}'
# AWS Systems Manager Parameter Store
aws ssm put-parameter \
--name /myapp/aws_access_key_id \
--value AKIANEWKEYEXAMPLE \
--type SecureString \
--overwrite
Update CI/CD pipelines:
- GitHub Actions: Settings > Secrets > AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
- GitLab CI: Settings > CI/CD > Variables
- CircleCI: Project Settings > Environment Variables
- Jenkins: Credentials manager
After updating each consumer, trigger a test run or make a test API call to verify the new key is being used correctly before moving to the next consumer.
Step 4: Verify and Delete the Old Key
After updating all consumers, verify the old key has stopped being used before deactivating it.
Check last-used timestamp for the old key:
aws iam get-access-key-last-used --access-key-id AKIAIOSFODNN7EXAMPLE
If LastUsedDate is not updating beyond the timestamp when you started rotation, all consumers have been migrated. Wait at least 24 hours after the last update before deactivating: some consumers (cron jobs, monthly batch processes) only run periodically.
Deactivate the old key (not delete yet: deactivation is reversible):
aws iam update-access-key \
--user-name myuser \
--access-key-id AKIAIOSFODNN7EXAMPLE \
--status Inactive
Monitor for breakage for 24-48 hours. Any consumer that was missed will start failing after deactivation. Reactivating the old key (--status Active) immediately restores service while you find and update the missed consumer.
After confirming no breakage: delete the old key:
aws iam delete-access-key \
--user-name myuser \
--access-key-id AKIAIOSFODNN7EXAMPLE
# Verify only the new key remains
aws iam list-access-keys --user-name myuser
The long-term fix: eliminate long-lived keys entirely: For most AWS use cases, IAM roles with temporary STS credentials are the right solution instead of long-lived access keys:
- EC2 instances: IAM instance profiles (no keys needed)
- Lambda functions: Lambda execution roles (no keys needed)
- ECS/EKS: task roles and pod identity
- CI/CD: OIDC federation (GitHub Actions, GitLab CI, CircleCI all support AWS OIDC: no stored keys)
Any new system should use roles, not keys. Reserve long-lived keys for the specific cases where role-based auth is not available.
The bottom line
AWS access key rotation requires four steps in strict sequence: audit where the key is used (CloudTrail last-used, grep repos and config files), create the new key (capture the secret immediately: it shows once), update all consumers before deactivating the old key, then deactivate and monitor for 24-48 hours before deleting. The two-key limit makes zero-downtime rotation possible: use it. Long-term, replace access keys with IAM roles wherever possible.
Frequently asked questions
How do I rotate AWS access keys without causing downtime?
Create the new access key first (IAM users can have two active keys simultaneously), update all consumers to use the new key while the old key remains active, verify the old key's last-used timestamp has stopped advancing, deactivate the old key and monitor for 24-48 hours, then delete it. Never delete the old key before confirming all consumers are using the new one.
How do I find all places an AWS access key is being used?
Run 'aws iam get-access-key-last-used --access-key-id KEY_ID' to see the last service used and timestamp. Search CloudTrail for the key ID to see all recent API calls and source IPs. Grep your git repositories and config directories for the key ID. Check CI/CD environment variables manually in each pipeline system.
How do I prevent AWS access keys from being committed to source code?
Three prevention layers: pre-commit hooks using git-secrets or detect-secrets that scan staged files before each commit; GitHub's built-in Secret Scanning (enabled by default on public repositories and available in GitHub Advanced Security for private repos) which scans every push and alerts on over 200 secret types; and regular git repository scanning with tools like truffleHog or gitleaks to find secrets in existing history. For new development, eliminate access keys entirely for EC2, ECS, Lambda, and other AWS compute: use IAM instance profiles or execution roles to authenticate without any static credentials.
What is the AWS IAM access key rotation best practice?
AWS recommends rotating access keys every 90 days maximum. The safe rotation sequence: (1) create a new access key while the old key remains active; (2) update all applications and pipelines to use the new key; (3) verify the old key is no longer generating CloudTrail events (wait 24-48 hours); (4) deactivate the old key (do not delete yet, in case a missed location needs rollback); (5) wait a few days to confirm no disruption; (6) delete the old key. Never delete first without deactivating: deactivation is reversible, deletion is not. Set up AWS Config rule 'access-keys-rotated' to alert when keys exceed 90 days without rotation.
When should I use AWS IAM roles instead of access keys?
Use IAM roles (not access keys) for all compute resources: EC2 instances via instance profiles, Lambda via execution roles, ECS tasks via task roles, and CodeBuild/CodePipeline via service roles. Roles provide automatically rotated temporary credentials via the EC2 metadata service or STS — no manual key management required. Use static access keys only for: human AWS CLI access (and require MFA for these users), third-party integrations that do not support role-based authentication, and on-premises systems. If a third-party service requests permanent IAM user keys, create a dedicated least-privilege IAM user for that integration and rotate the key every 90 days.
What should you do immediately if an AWS access key is leaked in a public repository?
Act within minutes because automated scanners find exposed AWS keys in public repositories in under an hour. First, deactivate the key immediately using 'aws iam update-access-key --access-key-id <KEY_ID> --status Inactive' — do not delete yet, as you need the key ID for CloudTrail investigation. Second, review CloudTrail for all API calls made with that key since the commit timestamp: look for unauthorized EC2 instances launched, S3 buckets accessed or created, IAM users or keys created (privilege escalation), and data exfiltration patterns. Use 'aws cloudtrail lookup-events --lookup-attributes AttributeKey=AccessKeyId,AttributeValue=<KEY_ID>' to retrieve the full activity log. Third, check all AWS regions, not just your primary region: attackers commonly launch resources in regions you do not normally monitor. Fourth, if any unauthorized activity is found, treat this as a security incident: engage your incident response process, preserve CloudTrail logs, and review what data the key had access to. After completing the investigation, rotate to a new key using the standard rotation procedure and delete the old key.
Sources & references
Free resources
Critical CVE Reference Card 2025–2026
25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.
Ransomware Incident Response Playbook
Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.
Get threat intel before your inbox does.
50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.
Unsubscribe anytime. We never sell your data.

Founder & Cybersecurity Evangelist, Decryption Digest
Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.
Win a $2,495 Black Hat pass.
Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.
