AWS Config料金についてリソースタイプ毎に日時記録と連続記録どちらが良いのかを試算してみた
こんにちは。クラウド事業本部の木村です。
AWS Configの料金が想定より高くなっており、原因を調査する機会がありました。高騰の原因となっているリソースタイプの記録を停止すれば料金は抑えられますが、Security Hubの検出対象から外れてしまうなどの課題があります。
そこで今回は同じような悩みを抱えている方向けに記録方式の変更によってコストを最適化する方法と、その調査手順をまとめます。
Configの記録方法について
まずConfigの記録方法について簡単に確認しておきます。
記録方法については以下の2つの種類があります。
- 連続記録
- 変更が発生するたびにリアルタイムで設定変更を記録します。
- 日時記録
- 過去24時間のリソースの最新状態を、前回記録した設定項目(CI)と異なる場合のみ記録します。
この際、作成 → 削除が1日の記録の範囲で行われたリソースについては日時記録では記録が行われないことから、エフェメラルリソースとして試算時には日時記録の対象外にしています。
それぞれ記録の回数によって課金が発生し、単価は以下です。(2026/4/19時点、東京リージョン)
- 連続記録 : 0.003USD/1回
- 日時記録 : 0.012USD/1回
デフォルトの設定は連続記録で、リソースの変更毎に構成変更を記録します。
変更の度に記録されるため、頻繁にリソースを変更する環境ではコストが高騰してしまいがちです。
そこで今回は頻繁に記録が行われるリソースに対して、リソースタイプについて日時記録に変更することでコストの最適化を目指します。
日時記録への切り替え目安について
次に日時記録へと切り替える目安について考えていきたいと思います。単価が日時記録の方が高いので全てのリソースタイプを対象に切り替えてしまうとコストが上がってしまうケースがあります。ですので記録が多いリソースタイプに絞って設定を変更する必要があります。
またこの際大事なのが日時記録への切り替えが有利かどうかは、リソースタイプ全体の記録数の多寡ではなく1リソースあたりの平均更新回数で判断するという点です。
例として、リソースタイプにて合計100回/日の更新が発生しているケースを考えます。
環境A:1個のリソースに100回の更新がある場合
連続記録コスト:100回 × 0.003USD = 0.30USD/日
日時記録コスト:1回 × 0.012USD = 0.012USD/日
日時記録に変更すると1/25まで安くなります。
環境B:100個のリソースに1回ずつ更新が分散している場合
連続記録コスト:100回 × 0.003USD = $0.30/日
日時記録コスト:100回 × 0.012USD = $1.20/日
日時記録の方が4倍高くなります。
例のように1リソースに対して1日での変更数が少ないものに対して、日時記録へと変更を行ってしまうと単価が4倍高い分逆にコスト増になってしまいます。
上記のパターンのように同じ記録回数であっても日時記録への変更が適切なケースとそうでないケースがございます。
きちんと見極めずに記録回数が多いからということで様々なリソースタイプを日時記録に移行してしまうと全体では安くなるかもしれませんが、環境Bのような無駄になっている部分が紛れ込んでしまうかもしれません。
調査の一歩目として対象を絞りこもう
とはいえ全てを対象に分析を行っていくのはかなり大変な作業になるので、対象の絞り込みを行うことが重要です。
なのでまずリソースタイプ全体で記録数が多いところを確認しましょう。
記録数が多いところ = 料金が多く発生しているところ
ということになります。なので削減がうまくいった時にその効果も大きくなりやすいです。
私はいつも以下ブログを参照してリソースタイプ毎の記録回数を確認しております。詳細はリンク先でご確認ください。
対象のリソースタイプを絞りこめたら、それぞれのリソースタイプで調査を行っていきます。
対象のリソースタイプを日時記録に変更すべきか確認する
ではここから各リソースタイプを日時記録に変更すべきかを確認していきます。
Configのコンソールから各リソースの変更の記録を確認することができるのですが、様々な要素を考慮しつつリソースを確認していると何時間あっても足りなくなってしまうので要件をまとめてClaude Codeと対話しながらでコードを作成しました。
以下になります。
import boto3
import datetime
import argparse
from dateutil.relativedelta import relativedelta
from dateutil import parser as date_parser
JST = datetime.timezone(datetime.timedelta(hours=9))
CONTINUOUS_PRICE = 0.003 # $/CI(連続記録・東京リージョン)
PERIODIC_PRICE = 0.012 # $/CI(日時記録・東京リージョン)
def _to_dt(ts):
"""タイムスタンプを timezone-aware な datetime に変換する"""
if isinstance(ts, str):
ts = date_parser.parse(ts)
if ts.tzinfo is None:
ts = ts.replace(tzinfo=datetime.timezone.utc)
return ts
def analyze_recording_cost(region, resource_type, days_to_analyze=30, limit=None):
"""
指定したリソースタイプについて、連続記録と日時記録のコストを比較します。
エフェメラルリソース(同一JST日付内に全CIが収まるリソース)は
日時記録では記録されないため、日時記録コストの算出対象から除外します。
Args:
region (str): 分析対象のAWSリージョン
resource_type (str): 分析対象のリソースタイプ
days_to_analyze (int): 分析する過去の日数
limit (int): 取得するリソース数の上限(テスト用)
"""
config_client = boto3.client('config', region_name=region)
end_time = datetime.datetime.now(datetime.timezone.utc)
start_time = end_time - relativedelta(days=days_to_analyze)
print(f"分析期間: {start_time.astimezone(JST).strftime('%Y-%m-%d')} 〜 "
f"{end_time.astimezone(JST).strftime('%Y-%m-%d')} ({days_to_analyze}日間) ※JST基準")
print(f"対象リソースタイプ: {resource_type}")
print()
# リソース取得(削除済み含む)
resource_identifiers = []
paginator = config_client.get_paginator('list_discovered_resources')
print("リソースを取得中(削除済み含む)...")
try:
for page in paginator.paginate(
resourceType=resource_type,
includeDeletedResources=True
):
for resource in page.get('resourceIdentifiers', []):
resource_identifiers.append(resource['resourceId'])
if limit and len(resource_identifiers) >= limit:
break
if limit and len(resource_identifiers) >= limit:
print(f" 上限 {limit} 件に達したため取得を打ち切りました。")
break
except Exception as e:
print(f"リソースの取得中にエラーが発生しました: {str(e)}")
return
if not resource_identifiers:
print(f"{resource_type} リソースが見つかりませんでした。")
return
print(f"合計 {len(resource_identifiers)} 個のリソースを取得しました。")
print("各リソースの設定履歴を分析中...")
print()
total_ci = 0
ephemeral_resources = 0
ephemeral_ci = 0
non_ephemeral_resources = 0
non_ephemeral_ci = 0
non_ephemeral_change_days = 0 # 非エフェメラルリソースの(リソース×変更日)の総数
skipped = 0
for i, resource_id in enumerate(resource_identifiers):
if i > 0 and i % 10 == 0:
print(f" 進捗: {i}/{len(resource_identifiers)} 処理済み")
try:
all_items = []
response = config_client.get_resource_config_history(
resourceType=resource_type,
resourceId=resource_id,
earlierTime=start_time,
laterTime=end_time
)
all_items.extend(response.get('configurationItems', []))
while 'nextToken' in response:
response = config_client.get_resource_config_history(
resourceType=resource_type,
resourceId=resource_id,
earlierTime=start_time,
laterTime=end_time,
nextToken=response['nextToken']
)
all_items.extend(response.get('configurationItems', []))
if not all_items:
skipped += 1
continue
count = len(all_items)
total_ci += count
# CIのJST日付を収集
dates_jst = set()
for item in all_items:
dates_jst.add(_to_dt(item['configurationItemCaptureTime']).astimezone(JST).date())
# 最後のCIのステータスを確認(削除済みかどうか)
last_item = max(all_items, key=lambda x: _to_dt(x['configurationItemCaptureTime']))
last_status = last_item.get('configurationItemStatus', '')
is_deleted = last_status in ('ResourceDeleted', 'ResourceDeletedNotRecorded')
if len(dates_jst) == 1 and is_deleted:
# 真のエフェメラルリソース:同一日内に作成・削除
# 日時記録のチェック時点でリソースが存在しないため記録されない
ephemeral_resources += 1
ephemeral_ci += count
else:
# 複数日にまたがるリソース、または削除されていないリソース
# 日時記録のチェック時点でリソースが存在するため記録される
non_ephemeral_resources += 1
non_ephemeral_ci += count
non_ephemeral_change_days += len(dates_jst)
except Exception as e:
print(f" リソース {resource_id} の処理中にエラーが発生しました: {str(e)}")
# ===== コスト計算 =====
# 連続記録:全CIに対して課金(エフェメラル含む)
continuous_cost = total_ci * CONTINUOUS_PRICE
# 日時記録:非エフェメラルのみ、変更があった日に1CIとして課金
periodic_cost = non_ephemeral_change_days * PERIODIC_PRICE
saving = continuous_cost - periodic_cost
saving_pct = saving / continuous_cost * 100 if continuous_cost > 0 else 0
# 月換算(分析期間から外挿)
continuous_monthly = continuous_cost / days_to_analyze * 30
periodic_monthly = periodic_cost / days_to_analyze * 30
saving_monthly = saving / days_to_analyze * 30
# 変更日あたりの平均CI数(削減効果の説明に使用)
avg_ci_per_change_day = (
non_ephemeral_ci / non_ephemeral_change_days
if non_ephemeral_change_days > 0 else 0
)
# ===== 結果出力 =====
analyzed = ephemeral_resources + non_ephemeral_resources
print()
print("=" * 68)
print(f" 分析結果: {resource_type}")
print("=" * 68)
print(f" 取得リソース数 : {analyzed + skipped} 個")
if skipped > 0:
print(f" 履歴なし(スキップ): {skipped} 個")
print(f" 分析対象リソース数: {analyzed} 個")
print(f" 総CI数 : {total_ci:,} 件")
print()
print("【エフェメラルリソース(同日中に消滅)】")
print(f" リソース数: {ephemeral_resources} 個 / CI数: {ephemeral_ci:,} 件")
print(f" ※ 日時記録では記録されないためコスト算出対象外")
print()
print("【複数日にまたがるリソース】")
print(f" リソース数 : {non_ephemeral_resources} 個")
print(f" CI数 : {non_ephemeral_ci:,} 件")
print(f" 延べ変更日数 : {non_ephemeral_change_days} 日")
if non_ephemeral_change_days > 0:
print(f" 変更日あたり平均CI: {avg_ci_per_change_day:,.1f} 件")
print()
# 算出根拠の文字列
continuous_basis = f"{total_ci:,}CI x ${CONTINUOUS_PRICE}"
periodic_basis = f"{non_ephemeral_change_days}日 x ${PERIODIC_PRICE}"
print("【コスト試算(東京リージョン)】")
print(f" 連続記録: ${CONTINUOUS_PRICE}/CI、日時記録: ${PERIODIC_PRICE}/CI(変更があった日に1CI)")
print()
print(f" {'':22} {'算出根拠':22} {'分析期間':>10} {'月換算':>10}")
print(f" {'連続記録(現状)':22} {continuous_basis:22} ${continuous_cost:>9,.2f} ${continuous_monthly:>9,.2f}")
print(f" {'日時記録(変更後)':22} {periodic_basis:22} ${periodic_cost:>9,.2f} ${periodic_monthly:>9,.2f}")
print(f" {'削減見込み':22} {'':22} ${saving:>9,.2f} ${saving_monthly:>9,.2f} ({saving_pct:.1f}%)")
if saving < 0:
print()
print(" ⚠ 日時記録に切り替えるとコストが増加します。")
print(" (変更頻度が低く、連続記録の方が有利な構成です)")
print("=" * 68)
print(f" ※ 月換算は分析期間({days_to_analyze}日間)の実績から30日換算した推計値です")
print(" ※ 東京リージョンの標準料金で試算しており、実際の額は契約条件により異なります")
print("=" * 68)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="AWS Config 連続記録 vs 日時記録 コスト比較ツール"
)
parser.add_argument('--region', type=str, required=True,
help='分析対象のAWSリージョン(例: ap-northeast-1)')
parser.add_argument('--resource-type', type=str, required=True,
help='分析対象のリソースタイプ(例: AWS::EC2::NetworkInterface)')
parser.add_argument('--days', type=int, default=30,
help='分析する過去の日数(デフォルト: 30)')
parser.add_argument('--limit', type=int, default=None,
help='取得するリソース数の上限(テスト用)')
args = parser.parse_args()
analyze_recording_cost(args.region, args.resource_type, args.days, args.limit)
以下のように引数を渡して実行してください。
python config_cost_comparison.py \
--region ap-northeast-1 \
--resource-type AWS::EC2::VPC \
--days 7
またAWS::EC2::NetworkInterfaceのような記録タイプではリソース数が多すぎて実行に時間がかかり過ぎてしまう場合には以下のようにリミットを設定して実行してください。
python config_cost_comparison.py \
--region ap-northeast-1 \
--resource-type AWS::EC2::NetworkInterface \
--days 7 \
--limit 100
今回このコードでは処理時間等の関係上以下の点までは考慮できていないので、あくまで概算である点をご了承ください。
- 実際の日時記録で1日の範囲とコード内での1日の基準のズレ
- 1日の間で状態A → 状態B → 状態Aに戻り実際の日時記録では記録されないものの記録数への計上
- 軽微な記録数のズレ(私が確認したところ基本的には1%以内の誤差でした)
試してみる
では実際に環境で動かして検証してみます。
まず AWS::EC2::VPC で確認してみます。
以下の実行結果になりました。
====================================================================
分析結果: AWS::EC2::VPC
====================================================================
取得リソース数 : 3 個
履歴なし(スキップ): 1 個
分析対象リソース数: 2 個
総CI数 : 1,424 件
【エフェメラルリソース(同日中に消滅)】
リソース数: 0 個 / CI数: 0 件
※ 日時記録では記録されないためコスト算出対象外
【複数日にまたがるリソース】
リソース数 : 2 個
CI数 : 1,424 件
延べ変更日数 : 16 日
変更日あたり平均CI: 89.0 件
【コスト試算(東京リージョン)】
連続記録: $0.003/CI、日時記録: $0.012/CI(変更があった日に1CI)
算出根拠 分析期間 月換算
連続記録(現状) 1,424CI x $0.003 $ 4.27 $ 18.31
日時記録(変更後) 16日 x $0.012 $ 0.19 $ 0.82
削減見込み $ 4.08 $ 17.49 (95.5%)
====================================================================
※ 月換算は分析期間(7日間)の実績から30日換算した推計値です
※ 東京リージョンの標準料金で試算しており、実際の額は契約条件により異なります
====================================================================
こちらは日時記録に変更することで大きな削減効果が期待できそうです。
続いて AWS::EC2::NetworkInterfaceを確認してみます。
数が多すぎるので100件をリミットに参考値を取得しています。
以下の実行結果になりました。
====================================================================
分析結果: AWS::EC2::NetworkInterface
====================================================================
取得リソース数 : 100 個
履歴なし(スキップ): 86 個
分析対象リソース数: 14 個
総CI数 : 15 件
【エフェメラルリソース(同日中に消滅)】
リソース数: 0 個 / CI数: 0 件
※ 日時記録では記録されないためコスト算出対象外
【複数日にまたがるリソース】
リソース数 : 14 個
CI数 : 15 件
延べ変更日数 : 14 日
変更日あたり平均CI: 1.1 件
【コスト試算(東京リージョン)】
連続記録: $0.003/CI、日時記録: $0.012/CI(変更があった日に1CI)
算出根拠 分析期間 月換算
連続記録(現状) 15CI x $0.003 $ 0.04 $ 0.19
日時記録(変更後) 14日 x $0.012 $ 0.17 $ 0.72
削減見込み $ -0.12 $ -0.53 (-273.3%)
⚠ 日時記録に切り替えるとコストが増加します。
(変更頻度が低く、連続記録の方が有利な構成です)
====================================================================
※ 月換算は分析期間(7日間)の実績から30日換算した推計値です
※ 東京リージョンの標準料金で試算しており、実際の額は契約条件により異なります
====================================================================
取得範囲を絞っているのであくまで参考ですが、こちらは移行することでコストが上がってしまいそうなので現状維持で連続記録とした方が良さそうということが確認できました。
今回はサンプルで取得しているため100件と少ない数値で計測しましたが、実際の環境で調査する際はなるべく多い数で集計すると良いかと思います。
このようにタイプ別に確認を行って移行すべきか否かを概算ではありますが、判定させることができます。
まとめ
今回はConfigについてのコスト最適化のための調査方法についてまとめてみました。
検証環境などで詳細な記録が不要かつ変更が多くなりがちでコストがかかっているところでは今回の分析が役に立つのではないかと思います。
この記事が参考になれば幸いです。
以上、クラウド事業本部の木村がお届けしました。








