AWS Configルールを利用して、IAMアクセスキーが一定期間ローテーションされていない場合の自動無効化前に事前通知する仕組み
はじめに
セキュリティ対策として、長期間ローテーションされていないIAMユーザーのアクセスキーを定期的に無効化することは重要です。
一般的な運用として、例えば90日以上ローテーションされていない場合に無効化するケースが考えられます。しかし、突然無効化されると利用者が困ってしまうため、事前に通知したい場合もあるでしょう。
以下の記事のように、AWS Config ルール(access-keys-rotated)とAWS Systems Manager オートメーションを利用することで、一定期間ローテーションされていないアクセスキーに対して、自動無効化が実装できます。
しかし、突然無効化されると利用者が困ってしまうため、事前に通知したい場合もあるでしょう。
AWS Config ルール(access-keys-rotated)を活用することで、一定期間ローテーションされていないアクセスキーを検知し、通知システムを構築できます。
本記事では、無効化の数日前に利用者や管理者に事前通知するシステムの構築方法を解説します。
システム構成
構成は以下のとおりです。
Config ルール access-keys-rotated は、アクティブなIAMアクセスキーが maxAccessKeyAge
で指定された日数内にローテーション(変更)されるかどうかを確認します。アクセスキーが指定した期間内にローテーションされていない場合、ルールは NON_COMPLIANT
となります。デフォルト値は90日です。
今回は、無効化予定日の7日前と14日前に段階的に通知したいと仮定します。この要件を実現するため、異なる日数パラメータを設定した2つのConfig ルールを作成します。
2つのルールが必要な理由
Amazon EventBridge は、Config ルールが準拠から非準拠への変更時に1回だけトリガーされる仕組みにします。
そのため、各通知タイミング(7日前、14日前)で確実に1回ずつ通知するには、それぞれ専用のConfigルールが必要になります。
ルールが非準拠となった場合、AWS Config のコンプライアンスステータス変更をトリガーに、EventBridge 経由でAWS Step Functions を起動し、Amazon SES でメール通知を行います。
なお、今回Amazon SES は作成済みとします。IDタイプはドメインでセットアップしました。
AWS Configルール作成
AWS Config ルールを作成します。
AWS マネージド型ルールである access-keys-rotated
を利用します。
ルール名を access-keys-rotated-1
にし、パラメータ maxAccessKeyAge
は 83
に設定します。このパラメータは、指定した日数内にローテーションされていないアクティブなアクセスキーがあるかどうかを確認します。つまり、アクセスキーが83日以上ローテーションされていない場合、非準拠となります。
これで1つ目のルールが作成できました。
同様に、パラメータ maxAccessKeyAge
を 76
に設定したルール名 access-keys-rotated-2
を作成します。
これで2つのAWS Config ルールが作成できました。
Step Functions ステートマシンの作成
以下のステートマシンを作成します。
maxUnusedDays
で90日での自動無効化期間を設定し、メール送信にはAmazon SES を利用しています。
toEmailAddress
と fromEmailAddress
は各自の環境に合わせて変更してください。
{
"Comment": "IAMユーザーアクセスキーローテーション通知ステートマシン",
"QueryLanguage": "JSONata",
"StartAt": "Initialize Configuration",
"States": {
"Initialize Configuration": {
"Type": "Pass",
"Comment": "初期設定値を定義:AWSアカウントID、Config Rule名、最大未使用日数、送信者・宛先メールアドレスを設定",
"Assign": {
"awsAccountId": "{% $states.input.detail.awsAccountId %}",
"configRuleName": "{% $states.input.detail.configRuleName %}",
"maxUnusedDays": "{% 90 %}",
"toEmailAddress": "{% 'example1@example.com' %}",
"fromEmailAddress": "{% 'example2@example.com' %}"
},
"Next": "Calculate Days"
},
"Calculate Days": {
"Type": "Pass",
"Comment": "ConfigRule名に基づいて残り日数を計算:access-keys-rotated-1なら7日、access-keys-rotated-2なら14日",
"Assign": {
"remainingDays": "{% $configRuleName = \"access-keys-rotated-1\" ? 7 : $configRuleName = \"access-keys-rotated-2\" ? 14 %}"
},
"Next": "Calculate Disable Deadline"
},
"Calculate Disable Deadline": {
"Type": "Pass",
"Comment": "ローテーション期限日を計算:現在日時に残り日数を加算してYYYY/MM/DD形式の文字列として生成",
"Assign": {
"rotationDeadline": "{% (\n $remainingDays := (\n $configRuleName = \"access-keys-rotated-1\" ? 7 :\n $configRuleName = \"access-keys-rotated-2\" ? 14\n );\n $targetDate := $toMillis($now()) + ($remainingDays * 24 * 60 * 60 * 1000);\n $dateStr := $substring($fromMillis($targetDate), 0, 10);\n $replace($replace($dateStr, \"-\", \"/\"), /^(\\d{4})\\/(\\d{2})\\/(\\d{2})$/, \"$1/$2/$3\")\n) %}"
},
"Next": "ListUsers"
},
"ListUsers": {
"Type": "Task",
"Comment": "IAMサービスから全ユーザー一覧を取得し、resourceIdに該当するユーザー名を特定",
"Arguments": {},
"Assign": {
"userName": "{% $states.result.Users[UserId=$states.input.detail.resourceId].UserName[0] %}"
},
"Resource": "arn:aws:states:::aws-sdk:iam:listUsers",
"Next": "SES Send Email"
},
"SES Send Email": {
"Type": "Task",
"Comment": "SESv2を使用してアクセスキーローテーション通知メールを送信(件名、本文、宛先を設定)",
"Arguments": {
"ConfigurationSetName": "ses-bounce-configuration-set",
"Content": {
"Simple": {
"Body": {
"Text": {
"Charset": "UTF-8",
"Data": "{% '【IAMアクセスキーローテーション通知】\n\nIAMユーザー「' & $userName & '」のアクセスキーローテーション期限が' & $string($remainingDays) & '日後(' & $rotationDeadline & ')に迫っています。\n\n▼ 継続利用される場合の対応方法\n・新しいアクセスキーの発行\n・古いアクセスキーの削除\n・アプリケーションでの新しいアクセスキー設定\n\n※' & $string($maxUnusedDays) & '日間ローテーションされていないアクセスキーは、セキュリティ対策として自動無効化されます。\n\n■ 対象情報\n- AWSアカウントID: ' & $awsAccountId & '\n- IAMユーザー名: ' & $userName & '\n- ローテーション期限: ' & $rotationDeadline & '\n- 残り日数: ' & $string($remainingDays) & '日\n- ルール名: ' & $configRuleName & '\n\n本メールは自動送信されています。' %}"
}
},
"Subject": {
"Charset": "UTF-8",
"Data": "{% '【アクション必要】IAMアクセスキー期限通知 - ' & $userName & ' (残り' & $string($remainingDays) & '日)' %}"
}
}
},
"Destination": {
"ToAddresses": [
"{% $toEmailAddress %}"
]
},
"FromEmailAddress": "{% $fromEmailAddress %}"
},
"Resource": "arn:aws:states:::aws-sdk:sesv2:sendEmail",
"End": true
}
}
}
ステートマシンの動作は以下のとおりです。
- AWS Config ルールからEventBridge 経由で受け取ったイベントから、AWSアカウントIDとConfig ルール名を取得
- Config ルール名に基づいて残り日数を計算(access-keys-rotated-1なら7日、access-keys-rotated-2なら14日)
- ローテーション期限日を計算してYYYY/MM/DD形式で生成
- IAM ListUsers APIを実行してリソースIDからユーザー名を特定
- SES 経由でメール通知を送信
ステートマシン作成時、IAMロールと一部のIAMポリシーも自動作成されます。
自動作成されないIAMポリシーは手動で作成し、IAMロールにアタッチします。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ses:SendEmail",
"ses:SendRawEmail",
"iam:ListUsers"
],
"Resource": "*"
}
]
}
SES設定セットについて
SES では設定セット(ConfigurationSetName:ses-bounce-configuration-set)を設定しています。ConfigurationSetName は必須ではありません。
設定セットは、Amazon SES でメール送信を行った際に発生するバウンス(送信したメールが何らかの原因で相手に届かず、送信者の元に返送されてくる現象)を検知し、管理者に通知する仕組みとして利用します。詳細は以下の記事を参照してください。
EventBridge ルールの作成
EventBridge ルールを作成し、AWS Config のコンプライアンス変更イベントをトリガーとしてステートマシンを起動します。
イベントパターンは以下のように設定します。
{
"detail-type": ["Config Rules Compliance Change"],
"source": ["aws.config"],
"detail": {
"configRuleName": ["access-keys-rotated-1", "access-keys-rotated-2"],
"messageType": ["ComplianceChangeNotification"],
"newEvaluationResult": {
"complianceType": ["NON_COMPLIANT"]
}
}
}
ステータスが準拠から非準拠に変わった場合にトリガーされます。
一度トリガーされた後もConfig ルールで毎日チェックしますが、継続して非準拠の状態が続いても再度トリガーされることはありません。
EventBridge ルールのターゲット先は、先ほど作成したステートマシンに設定します。
リソース作成時の注意点
EventBridge ルールとステートマシンを先に作成し、その後AWS Config ルールを作成すると、76日や83日以上ローテーションされていないアクセスキーを持つIAMユーザーが即座に非準拠となり、通知されてしまいます。
問題例
例えば、アクセスキーが88日経過している場合を考えてみます。
access-keys-rotated-1
(83日で非準拠)→ すでに非準拠状態access-keys-rotated-2
(76日で非準拠)→ すでに非準拠状態
この状況でEventBridge ルールとステートマシンを作成すると、両方のルールから通知が送信されます。しかし、メール内容は以下のようになります。
access-keys-rotated-1
の場合:「7日前」の通知access-keys-rotated-2
の場合:「14日前」の通知
実際には88日経過しているため、90日まで残り2日しかありません。メール内容と実際の残り日数に大きな乖離が生じてしまいます。
推奨する作成順序
以下の順序でリソースを作成することを推奨します。
- AWS Config ルールを先に作成
- 既存のアクセスキーが非準拠となっても、この時点では通知されない
- EventBridge ルールとステートマシンを作成
- EventBridge は既存の非準拠状態に対してはトリガーされない
- 動作確認
- その後、準拠から非準拠に変わったIAMユーザーのみが適切なタイミングで通知される
この順序により、正確な残り日数での通知が実現できます。
動作確認
実際に動作確認を行ったところ、以下のとおり通知されました。
ステートマシンが正常に動作し、期待どおりの通知メールが送信されていることが確認できました。
参考として、ステートマシンに渡されるEventBridge イベントの例は以下のとおりです。
{
"version": "0",
"id": "2c2a2284-0651-1c08-768c-8140299f1f37",
"detail-type": "Config Rules Compliance Change",
"source": "aws.config",
"account": "アカウントID",
"time": "2025-07-02T03:23:19Z",
"region": "ap-northeast-1",
"resources": [],
"detail": {
"resourceId": "AIDATCKAQT2XHRAS7UPNO",
"awsRegion": "ap-northeast-1",
"awsAccountId": "アカウントID",
"configRuleName": "access-keys-rotated-1",
"recordVersion": "1.0",
"configRuleARN": "arn:aws:config:ap-northeast-1:アカウントID:config-rule/config-rule-1xk3vm",
"messageType": "ComplianceChangeNotification",
"newEvaluationResult": {
"evaluationResultIdentifier": {
"evaluationResultQualifier": {
"configRuleName": "access-keys-rotated-1",
"resourceType": "AWS::IAM::User",
"resourceId": "AIDATCKAQT2XHRAS7UPNO",
"evaluationMode": "DETECTIVE"
},
"orderingTimestamp": "2025-07-02T03:22:44.084Z"
},
"complianceType": "NON_COMPLIANT",
"resultRecordedTime": "2025-07-02T03:23:18.689Z",
"configRuleInvokedTime": "2025-07-02T03:22:47.595Z"
},
"oldEvaluationResult": {
"evaluationResultIdentifier": {
"evaluationResultQualifier": {
"configRuleName": "access-keys-rotated-1",
"resourceType": "AWS::IAM::User",
"resourceId": "AIDATCKAQT2XHRAS7UPNO",
"evaluationMode": "DETECTIVE"
},
"orderingTimestamp": "2025-07-01T03:22:44.079Z"
},
"complianceType": "COMPLIANT",
"resultRecordedTime": "2025-07-01T03:23:18.588Z",
"configRuleInvokedTime": "2025-07-01T03:22:47.462Z"
},
"notificationCreationTime": "2025-07-02T03:23:19.371Z",
"resourceType": "AWS::IAM::User"
}
}
最後に
AWS Config ルール、Amazon EventBridge、AWS Step Functions、Amazon SES を組み合わせることで、IAMユーザーのアクセスキーローテーション期限前の事前通知システムを構築できました。
この仕組みにより、利用者や管理者に対して十分な猶予期間を持って通知でき、突然のアクセス停止を防ぐことが可能です。アクセスキーの定期的なローテーションを促進し、セキュリティとユーザビリティの両方を考慮した運用が実現できます。