IAMアクセスキーのローテーションとは、「新しいキーを作る → アプリを新しいキーに切り替える → 以前のキーを無効化する → 使われていないことを確かめて削除する」という順で、キーを入れ替えることです。アクセスキーは、アクセスキー ID とシークレットアクセスキーの2つからなる、プログラム用の認証情報です。

この記事では、この入れ替えを AWS CLI だけで行う手順を、コマンドと JSON 出力つきで1本につなげて紹介します。後半では、全ユーザーのキーを作成日・状態・最終使用日つきで一覧にし、作成から 90 日を超えたものだけを抜き出すワンライナーと boto3 スクリプトも載せます。

確認環境:Ubuntu 24.04、AWS CLI 1.46.1、Python 3.11、boto3 1.43。AWS の API をローカルで再現するツール(moto 5.2.3)で実行しました。掲載するコマンドは、接続先の指定を外して、AWS でそのまま使える形にしています。出力のシークレットアクセスキーは途中から省略しています。

アクセスキー操作の早見表

やりたいこと コマンド
キーを一覧する aws iam list-access-keys --user-name app-batch
キーを作る aws iam create-access-key --user-name app-batch
最終使用日を見る aws iam get-access-key-last-used --access-key-id AKIA...
無効化する aws iam update-access-key --user-name app-batch --access-key-id AKIA... --status Inactive
有効に戻す aws iam update-access-key --user-name app-batch --access-key-id AKIA... --status Active
削除する aws iam delete-access-key --user-name app-batch --access-key-id AKIA...
いま誰として接続しているか aws sts get-caller-identity
自分のキーを一覧する aws iam list-access-keys(--user-name を省く)
覚えておきたい決まり 内容
キーの本数 1ユーザーにつき2本まで(無効化したキーも数に入る)
シークレットアクセスキー 見られるのは作成時の1回だけ。控えそこねたら、そのキーを削除して作り直す
無効化と削除 無効化は Active に戻せる。削除したキーは一覧にも最終使用日の照会にも出なくなる
入れ替えの目安 AWS Security Hub のコントロール IAM.3 は 90 日以内の入れ替えを推奨

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「IAMアクセスキーの作成・確認・ローテーション手順 CLI版」

ローテーションの全体像

2本まで持てるという決まりを使い、新旧2本が並んで使える期間を作ってから切り替えるのがポイントです。こうすると、アプリを止めずに入れ替えられます。

flowchart LR
  A[1. 一覧で現状確認<br>list-access-keys] --> B[2. 新しいキーを作成<br>create-access-key]
  B --> C[3. アプリを切り替え<br>get-caller-identity で確認]
  C --> D[4. 最終使用日を確認<br>get-access-key-last-used]
  D --> E[5. 以前のキーを無効化<br>update-access-key]
  E --> F[6. 削除<br>delete-access-key]

以降では、app-batch というユーザーのキーを入れ替えます。コマンドを打つのは、IAM を操作できる管理用のユーザーです。

手順1:いまのキーを確認する(list-access-keys)

aws iam list-access-keys --user-name app-batch
{
    "AccessKeyMetadata": [
        {
            "UserName": "app-batch",
            "AccessKeyId": "AKIARZPUZDIKATDFQ5FO",
            "Status": "Active",
            "CreateDate": "2026-03-19T06:12:47Z"
        }
    ]
}

キーは1本で、作成日は 2026-03-19 です。一覧に出るのはアクセスキー ID・状態・作成日だけで、シークレットアクセスキーは出ません。

--user-name を省くと、コマンドを打っている本人のキーが返ります。

手順2:新しいキーを作る(create-access-key)

aws iam create-access-key --user-name app-batch
{
    "AccessKey": {
        "UserName": "app-batch",
        "AccessKeyId": "AKIARZPUZDIKH2DHJWI4",
        "Status": "Active",
        "SecretAccessKey": "SzkK9QBawnDa…(省略)",
        "CreateDate": "2026-10-05T06:12:52Z"
    }
}

SecretAccessKey が表示されるのは、このときだけです。画面に流したままにせず、シークレットを保管するしくみ(AWS Secrets Manager、Systems Manager パラメータストア、CI のシークレット機能など)へすぐ登録します。

ファイルに受けてから取り出す形にしておくと、端末の履歴や画面に残りにくくなります。

aws iam create-access-key --user-name app-batch > newkey.json
NEW_ID=$(python3 -c "import json;print(json.load(open('newkey.json'))['AccessKey']['AccessKeyId'])")
NEW_SEC=$(python3 -c "import json;print(json.load(open('newkey.json'))['AccessKey']['SecretAccessKey'])")

登録が済んだら newkey.json は削除します。

手順3:アプリを新しいキーに切り替えて確かめる

切り替え先はアプリによって異なりますが、ここでは AWS CLI の profile に新しいキーを書き込み、そのキーで誰として接続できるかを確かめます。

aws configure set aws_access_key_id "$NEW_ID" --profile app-batch
aws configure set aws_secret_access_key "$NEW_SEC" --profile app-batch

aws sts get-caller-identity --profile app-batch
{
    "UserId": "o3lc9r2j5h3ufxrl8tge",
    "Account": "123456789012",
    "Arn": "arn:aws:iam::123456789012:user/app-batch"
}

Arn が app-batch になっていれば、新しいキーで接続できています。get-caller-identity は権限を付けていなくても呼べるので、キーの確認に向いています。

手順4:最終使用日を確かめる(get-access-key-last-used)

新しいキーの最終使用日を見ます。

aws iam get-access-key-last-used --access-key-id AKIARZPUZDIKH2DHJWI4
{
    "UserName": "app-batch",
    "AccessKeyLastUsed": {
        "LastUsedDate": "2026-10-05T06:12:54Z",
        "ServiceName": "sts",
        "Region": "ap-northeast-1"
    }
}

手順3 の呼び出し(sts)が記録されました。続けて、以前のキーも見ます。

aws iam get-access-key-last-used --access-key-id AKIARZPUZDIKATDFQ5FO
{
    "UserName": "app-batch",
    "AccessKeyLastUsed": {
        "LastUsedDate": "2026-10-02T03:12:47Z",
        "ServiceName": "s3",
        "Region": "ap-northeast-1"
    }
}

以前のキーは、3日前に S3 で使われたのが最後です。切り替えのあと、以前のキーの LastUsedDate が進まなくなれば、すべての利用元が新しいキーへ移ったと判断できます。AWS のユーザーガイドでも、数日待ってから以前のキーの使用状況を確かめる方法が紹介されています。

ServiceName と Region が分かるので、日付が進んでしまったときは「どのサービスを、どのリージョンで呼んでいる処理が残っているか」を手がかりに、切り替え漏れを探せます。

一度も使われていないキーの表示

{
    "UserName": "report-job",
    "AccessKeyLastUsed": {
        "ServiceName": "N/A",
        "Region": "N/A"
    }
}
項目 一度も使われていないとき
LastUsedDate 項目そのものが出ない(--output text では None)
ServiceName N/A
Region N/A

手順5:以前のキーを無効化する(update-access-key)

いきなり削除せず、まず無効化(Inactive)にします。無効化は元に戻せるので、切り替え漏れが見つかったときにすぐ対応できます。

aws iam update-access-key --user-name app-batch \
  --access-key-id AKIARZPUZDIKATDFQ5FO --status Inactive

うまくいくと何も表示されません。一覧で状態を確かめます。

aws iam list-access-keys --user-name app-batch \
  --query "AccessKeyMetadata[].[AccessKeyId,Status,CreateDate]" --output text
AKIARZPUZDIKATDFQ5FO    Inactive    2026-03-19T06:12:47Z
AKIARZPUZDIKH2DHJWI4    Active  2026-10-05T06:12:52Z

切り替え漏れが見つかったら Active に戻す

aws iam update-access-key --user-name app-batch \
  --access-key-id AKIARZPUZDIKATDFQ5FO --status Active

aws iam list-access-keys --user-name app-batch \
  --query "AccessKeyMetadata[].[AccessKeyId,Status]" --output text
AKIARZPUZDIKATDFQ5FO    Active
AKIARZPUZDIKH2DHJWI4    Active

残っていた利用元を新しいキーに切り替えたら、もう一度 Inactive にして様子を見ます。

手順6:以前のキーを削除する(delete-access-key)

無効化したまましばらく置き、アプリが新しいキーだけで動いていることを確かめてから削除します。

aws iam delete-access-key --user-name app-batch --access-key-id AKIARZPUZDIKATDFQ5FO

aws iam list-access-keys --user-name app-batch \
  --query "AccessKeyMetadata[].[AccessKeyId,Status,CreateDate]" --output text
AKIARZPUZDIKH2DHJWI4    Active  2026-10-05T06:12:52Z

新しいキー1本だけになりました。削除したキーの最終使用日を照会すると、次のエラーになります。

aws iam get-access-key-last-used --access-key-id AKIARZPUZDIKATDFQ5FO
An error occurred (NoSuchEntity) when calling the GetAccessKeyLastUsed operation: The Access Key with id AKIARZPUZDIKATDFQ5FO cannot be found

最終使用日を記録に残したいときは、削除の前に控えておきます。

LimitExceeded:キーは2本までという上限

すでに2本のキーを持つユーザーに、3本目を作ろうとするとエラーになります。

aws iam list-access-keys --user-name report-job \
  --query "AccessKeyMetadata[].[AccessKeyId,Status,CreateDate]" --output text
AKIARZPUZDIKLDUC7KGH    Active  2026-06-07T06:12:47Z
AKIARZPUZDIKCOIYS2S4    Active  2026-09-25T06:12:47Z
aws iam create-access-key --user-name report-job
An error occurred (LimitExceeded) when calling the CreateAccessKey operation: Cannot exceed quota for AccessKeysPerUser: 2

無効化しただけのキーも1本として数えられます。手順5 のあと(Inactive が1本、Active が1本)の状態で create-access-key を打ったときも、同じエラーになりました。

状況 対処
2本とも使っている どちらが以前のキーかを作成日で確かめ、利用元を1本に寄せてから、もう一方を無効化 → 削除
1本が Inactive のまま残っている get-access-key-last-used で使われていないことを確かめて削除
どちらも使われていない 両方を無効化 → 削除し、必要になったときに作り直す

ローテーションでは一時的に2本になるので、入れ替えが終わったら1本に戻しておくと、次の入れ替えのときに枠が空いています。

全ユーザーのキーを一覧にする(CLI)

list-access-keys はユーザーごと、get-access-key-last-used はキーごとの照会なので、ループで組み合わせます。次の内容を list_keys.sh として保存します。

for u in $(aws iam list-users --query "Users[].UserName" --output text); do
  aws iam list-access-keys --user-name "$u" \
    --query "AccessKeyMetadata[].[UserName,AccessKeyId,Status,CreateDate]" --output text |
  while read -r name id status created; do
    used=$(aws iam get-access-key-last-used --access-key-id "$id" \
      --query "AccessKeyLastUsed.[LastUsedDate,ServiceName]" --output text)
    printf '%s\t%s\t%s\t%s\t%s\n' "$name" "$id" "$status" "$created" "$used"
  done
done

確認用に、作成日の異なるキーを持つユーザーを5人用意して実行しました。列は、ユーザー名・アクセスキー ID・状態・作成日・最終使用日・最後に使ったサービスです。

bash list_keys.sh
admin   AKIARZPUZDIKJZGLDUQH    Active  2026-09-15T06:12:47Z    2026-10-05T06:13:06Z    iam
app-batch   AKIARZPUZDIKH2DHJWI4    Active  2026-10-05T06:12:52Z    2026-10-05T06:12:54Z    sts
report-job  AKIARZPUZDIKLDUC7KGH    Active  2026-06-07T06:12:47Z    None    N/A
report-job  AKIARZPUZDIKCOIYS2S4    Active  2026-09-25T06:12:47Z    2026-10-04T03:12:47Z    sqs
deploy-bot  AKIARZPUZDIKHHKDI4KD    Active  2026-09-05T06:12:47Z    2026-10-04T03:12:47Z    cloudformation
legacy-tool AKIARZPUZDIKAT7H4YZE    Inactive    2025-08-31T06:12:47Z    2025-09-20T03:12:47Z    ec2

作成から 90 日を超えたキーだけを抜き出す

作成日は ISO 8601 形式なので、文字列としての大小がそのまま日付の前後になります。90 日前の日付を date コマンドで作り、4列目と比べます(GNU date の書き方です)。

bash list_keys.sh | awk -v d="$(date -u -d '90 days ago' +%Y-%m-%d)" '$4 < d'
report-job  AKIARZPUZDIKLDUC7KGH    Active  2026-06-07T06:12:47Z    None    N/A
legacy-tool AKIARZPUZDIKAT7H4YZE    Inactive    2025-08-31T06:12:47Z    2025-09-20T03:12:47Z    ec2

実行日は 2026-10-05 で、90 日前は 2026-07-07 です。それより前に作られた2本が残りました。report-job の1本は作成から一度も使われておらず、legacy-tool の1本は無効化されたまま残っていることも、同じ行から読み取れます。

1ユーザーだけを調べるなら、--query の中で日付を比べる形でも書けます。

D=$(date -u -d '90 days ago' +%Y-%m-%d)
aws iam list-access-keys --user-name legacy-tool --output text \
  --query "AccessKeyMetadata[?CreateDate<='$D'].[UserName,AccessKeyId,CreateDate]"
legacy-tool AKIARZPUZDIKAT7H4YZE    2025-08-31T06:12:47Z

全ユーザーのキーを一覧にする(boto3)

定期的に調べるなら、Python のスクリプトにしておくと、日数の計算や通知への組み込みがしやすくなります。key_report.py として保存します。

"""全ユーザーのアクセスキーを一覧にし、作成から MAX_AGE_DAYS 日を超えたものを抜き出す"""
from datetime import datetime, timezone

import boto3

MAX_AGE_DAYS = 90

iam = boto3.client("iam")
now = datetime.now(timezone.utc)
rows = []

for page in iam.get_paginator("list_users").paginate():
    for user in page["Users"]:
        name = user["UserName"]
        for kpage in iam.get_paginator("list_access_keys").paginate(UserName=name):
            for key in kpage["AccessKeyMetadata"]:
                used = iam.get_access_key_last_used(AccessKeyId=key["AccessKeyId"])["AccessKeyLastUsed"]
                last = used.get("LastUsedDate")  # 一度も使われていないキーには無い
                rows.append({
                    "user": name,
                    "key": key["AccessKeyId"],
                    "status": key["Status"],
                    "age": (now - key["CreateDate"]).days,
                    "idle": (now - last).days if last else None,
                    "service": used["ServiceName"],
                })

def show(title, items):
    print(f"== {title}({len(items)} 件)")
    print(f"{'user':<12} {'key':<21} {'status':<9} {'age':>4} {'idle':>5}  service")
    for r in items:
        idle = "-" if r["idle"] is None else r["idle"]
        print(f"{r['user']:<12} {r['key']:<21} {r['status']:<9} {r['age']:>4} {idle:>5}  {r['service']}")

show("すべてのキー", rows)
show(f"作成から {MAX_AGE_DAYS} 日超", [r for r in rows if r["age"] > MAX_AGE_DAYS])
python3 key_report.py
== すべてのキー(6 件)
user         key                   status     age  idle  service
admin        AKIARZPUZDIKJZGLDUQH  Active      20     0  iam
app-batch    AKIARZPUZDIKH2DHJWI4  Active       0     0  sts
report-job   AKIARZPUZDIKLDUC7KGH  Active     120     -  N/A
report-job   AKIARZPUZDIKCOIYS2S4  Active      10     1  sqs
deploy-bot   AKIARZPUZDIKHHKDI4KD  Active      30     1  cloudformation
legacy-tool  AKIARZPUZDIKAT7H4YZE  Inactive   400   380  ec2
== 作成から 90 日超(2 件)
user         key                   status     age  idle  service
report-job   AKIARZPUZDIKLDUC7KGH  Active     120     -  N/A
legacy-tool  AKIARZPUZDIKAT7H4YZE  Inactive   400   380  ec2

age は作成からの日数、idle は最後に使われてからの日数です。スクリプトのポイントは3つあります。

ポイント 理由
paginator を使う ユーザーやキーが多いときも、続きのページまで取りこぼさない
used.get("LastUsedDate") で受ける 一度も使われていないキーには、この項目が無い
datetime.now(timezone.utc) と比べる boto3 が返す日時はタイムゾーンつきなので、同じ形にそろえて引き算する

一覧から読み取れること

見えた状態 次の一手
age が 90 を超え、idle が小さい いまも使われているキー。この記事の手順でローテーションする
age が大きく、idle が「-」 作られてから使われていない。持ち主に確かめて無効化 → 削除
status が Inactive のまま日数がたっている 削除の候補。枠を1本ふさいでいる
1ユーザーに Active が2本 入れ替えの途中で止まっていないか確かめる

AWS Security Hub のコントロールでは、IAM.3 が「アクセスキーは 90 日以内に入れ替える」、IAM.22 が「45 日以上使われていない認証情報は取り除く」ことを確かめる内容になっています。上のスクリプトの MAX_AGE_DAYS や、idle の値を判定に使えば、同じ観点で手元でも確認できます。

AKIA と ASIA:接頭辞で種類を見分ける

アクセスキー ID の先頭4文字は、種類を表します。

接頭辞 種類 一緒に必要なもの 期限
AKIA IAM ユーザーなどの長期のアクセスキー シークレットアクセスキー 削除するまで有効(だからローテーションする)
ASIA AWS STS が発行する一時的なアクセスキー シークレットアクセスキーとセッショントークン 期限つき

この記事で作成したキーはすべて AKIA で始まっています。一方、ロールを引き受けたとき(sts assume-role)に返るキーは ASIA で始まり、セッショントークンが付いてきます。

aws sts assume-role --role-arn arn:aws:iam::123456789012:role/tmp-role \
  --role-session-name check \
  --query "Credentials.[AccessKeyId,SessionToken!=null]" --output text
ASIARZPUZDIKGC3GV2TT    True

ASIA で始まるキーを使うときは、セッショントークン(環境変数なら AWS_SESSION_TOKEN)も一緒に設定します。ログや設定ファイルにキー ID を見つけたときも、先頭が AKIA か ASIA かで、長期のキーか一時的なキーかを切り分けられます。

IAM のユーザーガイドには、ほかの ID の接頭辞も載っています。

接頭辞 種類
AIDA IAM ユーザー(get-caller-identity の UserId など)
AROA ロール
AGPA ユーザーグループ
ANPA 管理ポリシー
AIPA EC2 インスタンスプロファイル

ローテーションのチェックリスト

確認すること コマンド・目安
入れ替え前に、キーが1本であること list-access-keys(2本あるなら先に整理)
新しいキーのシークレットを、その場で保管先へ登録したか 表示は作成時の1回だけ
新しいキーで接続できたか sts get-caller-identity
以前のキーの最終使用日が進んでいないか get-access-key-last-used を数日おいて確認
削除の前に無効化を挟んだか update-access-key --status Inactive
入れ替え後に1本へ戻したか delete-access-key
作業用のファイル(newkey.json など)を消したか シークレットを残さない

よくある質問

Q. シークレットアクセスキーを控えそこねました。もう一度表示できますか? A. 表示できるのは作成時だけです。そのキーを削除し、新しいキーを作成します。

Q. 無効化と削除は、どう使い分けますか? A. 無効化は Active に戻せるので、切り替え漏れがないかを見る期間に使います。問題がないと分かったら削除します。無効化したキーも2本の枠に数えられるため、残したままにせず、最後は削除まで進めます。

Q. どのくらいの間隔で入れ替えればよいですか? A. AWS Security Hub のコントロール IAM.3 は、90 日以内の入れ替えを確かめる内容です。この記事のワンライナーやスクリプトで、90 日を超えたキーを定期的に洗い出すと管理しやすくなります。

Q. そもそもアクセスキーを使わない方法はありますか? A. あります。AWS のユーザーガイドは、EC2 などの AWS 上で動くアプリには IAM ロールを使い、一時的な認証情報を優先することを勧めています。一時的なキー(ASIA で始まるもの)は期限つきなので、手作業のローテーションが要りません。

まとめ

  • ローテーションは 作成 → 切り替え → 無効化 → 最終使用日の確認 → 削除 の順で進める
  • シークレットアクセスキーが見られるのは作成時だけ。その場で保管先へ登録する
  • 切り替えの確認は sts get-caller-identity、使われていないことの確認は get-access-key-last-used
  • いきなり削除せず、無効化を挟む。Active に戻せるので安心して進められる
  • キーは 1ユーザー2本まで。無効化したキーも数に入り、3本目は LimitExceeded になる
  • 全ユーザーの棚卸しは、list-users と list-access-keys のループか boto3 の paginator で行う
  • AKIA は長期のキー、ASIA は一時的なキー。ASIA にはセッショントークンが必要

参考・出典

  • AWS IAM ユーザーガイド「アクセスキーの更新(id-credentials-access-keys-update)」「IAM ユーザーのアクセスキーの管理(id_credentials_access-keys)」「アクセスキーを保護する(securing_access-keys)」「IAM 識別子(reference_identifiers)」
  • AWS CLI Command Reference「iam get-access-key-last-used」
  • AWS Security Hub ユーザーガイド「IAM のコントロール(IAM.3、IAM.22)」

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「IAMアクセスキーの作成・確認・ローテーション手順 CLI版」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?