- AWS
boto3 client・resource・sessionの違いと認証の優先順位
boto3 の client は「AWS の API をそのまま呼ぶ低レベルの窓口」、resource は「バケットやテーブルをオブジェクトとして扱う高レベルの窓口」、Session は「認証情報とリージョンを持ち、client と resource を作る元」です。 boto3.client() と書いたときも、裏では既定の Session が 1 つ作られています。
この記事では、同じ処理を 3 通りで書いて戻り値の型と出力を並べ、どれを選ぶかを表にします。後半は認証情報の探索順を全段の一覧にし、「いまどの認証情報が使われたか」を session.get_credentials().method と sts get-caller-identity で確かめます。profile_name・region_name の指定、3 つの定番エラーの再現、AssumeRole で別の Session を作る定型コードまで扱います。
確認環境:Ubuntu 24.04、Python 3.11.15、boto3 1.43.103、AWS CLI 1.46.1。AWS の API をローカルで再現するツール(moto 5.2.3)で実行しました。掲載するコードとコマンドは、実際の AWS でそのまま使える形(接続先の指定なし)にしてあります。
client・resource・Sessionの違い 早見表
| 比べる点 | client | resource | Session |
|---|---|---|---|
| 役割 | API を 1 対 1 で呼ぶ | リソースをオブジェクトとして操作する | 設定を持ち、client・resource を作る |
| 作り方 | boto3.client("s3") |
boto3.resource("s3") |
boto3.Session(profile_name=..., region_name=...) |
| 戻り値 | 辞書(dict) | Bucket・Object・Table などのインスタンス | ― |
| 対応サービス | すべて(この環境では 434) | 9 サービスのみ | ― |
| 新機能の追加 | 続く | 追加されない(既存の機能は使い続けられる) | ― |
| ページ分割 | paginator を使う | コレクション(.all() など)が自動で続きを取る |
― |
| スレッド間の共有 | おおむね可(公式に注意点あり) | 共有しない | 共有しない |
| 例外の捕まえ方 | client.exceptions.XXX |
resource.meta.client.exceptions.XXX |
― |
| やりたいこと | 書き方 |
|---|---|
| 既定の設定でそのまま使う | boto3.client("s3") |
| profile を指定する | boto3.Session(profile_name="dev").client("s3") |
| リージョンを指定する | boto3.client("s3", region_name="ap-northeast-1") |
| いまの認証の出どころを見る | session.get_credentials().method |
| 誰として接続しているかを見る | session.client("sts").get_caller_identity()["Arn"] |
| 使える profile の一覧 | boto3.Session().available_profiles |
| resource から client を取り出す | resource.meta.client |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「boto3 client・resource・sessionの違いと認証の優先順位」
同じ処理をclient・resource・Sessionの3通りで書く
S3 のバケット rjc-demo-bucket(オブジェクトが 2 つ入っている)の一覧を、3 通りで取ります。
# three_ways.py
import boto3
BUCKET = "rjc-demo-bucket"
# 1. client:API をそのまま呼ぶ。戻り値は辞書
client = boto3.client("s3")
resp = client.list_objects_v2(Bucket=BUCKET)
print("client :", type(client).__name__, "->", type(resp).__name__)
for obj in resp.get("Contents", []):
print(" ", obj["Key"], obj["Size"])
# 2. resource:オブジェクトとして扱う。戻り値はリソースのインスタンス
resource = boto3.resource("s3")
bucket = resource.Bucket(BUCKET)
print("resource:", type(resource).__name__, "->", type(bucket).__name__)
for obj in bucket.objects.all():
print(" ", obj.key, obj.size, f"({type(obj).__name__})")
# 3. Session:設定(認証情報・リージョン)を持ち、client も resource もここから作る
session = boto3.Session(region_name="ap-northeast-1")
print("session :", type(session).__name__, "region =", session.region_name)
s3 = session.client("s3")
keys = [o["Key"] for o in s3.list_objects_v2(Bucket=BUCKET).get("Contents", [])]
print(" session.client ->", keys)
keys = [o.key for o in session.resource("s3").Bucket(BUCKET).objects.all()]
print(" session.resource ->", keys)
# resource の中には client が入っている
print("resource.meta.client:", type(resource.meta.client).__name__)
# boto3.client() は、既定の Session を作って使っている
print("既定の Session:", boto3.DEFAULT_SESSION)
client : S3 -> dict
a.txt 3
members.csv 8
resource: s3.ServiceResource -> s3.Bucket
a.txt 3 (s3.ObjectSummary)
members.csv 8 (s3.ObjectSummary)
session : Session region = ap-northeast-1
session.client -> ['a.txt', 'members.csv']
session.resource -> ['a.txt', 'members.csv']
resource.meta.client: S3
既定の Session: Session(region_name='ap-northeast-1')
- client は list_objects_v2 という API 名そのままのメソッドを呼び、辞書を受け取ります。Contents というキーを自分でたどります。
- resource は Bucket というオブジェクトを作り、
bucket.objects.all()で ObjectSummary を順に受け取ります。属性はobj.keyのように小文字です。 - Session は、それ自体が API を呼ぶのではなく、client や resource を作る元です。最後の行のとおり、boto3.client() を呼ぶと既定の Session が作られます。
client を作れるサービスと resource を作れるサービスの数は、次のコードで確かめられます。
# count.py
import boto3
session = boto3.Session()
print("client を作れるサービス :", len(session.get_available_services()))
print("resource を作れるサービス:", session.get_available_resources())
client を作れるサービス : 434
resource を作れるサービス: ['cloudformation', 'cloudwatch', 'dynamodb', 'ec2', 'glacier', 'iam', 's3', 'sns', 'sqs']
どれを選ぶかの比較表
公式ドキュメントの resource のページには、「AWS の Python SDK チームは、resource のインターフェースに新機能を追加する予定がない。既存のインターフェースは boto3 のライフサイクルの間は動き続け、新しいサービス機能は client から利用できる」という趣旨の注記があります。これを踏まえた選び方です。
| 状況 | 選ぶもの | 理由 |
|---|---|---|
| これから書くコード | client | 全サービス・全 API に対応し、新機能も届く |
| resource に無いサービス(Lambda、SSM など) | client | resource は 9 サービスのみ |
| S3 や DynamoDB を短く書きたいスクリプト | resource でもよい | bucket.objects.all() や Table の型変換が手軽 |
| resource で書かれた既存のコード | そのまま使える | 既存の機能は動き続ける。足りない操作だけ resource.meta.client で補う |
| profile・リージョン・ロールを切り替える | Session を明示的に作る | 設定を 1 か所に集められる |
| スレッドやプロセスを分ける | スレッドごとに Session を作る | Session と resource は共有しない、と公式にある |
スレッドについて、公式ドキュメントは「client はおおむねスレッドセーフだが、プロセス間では共有できない」「Session と resource はスレッドセーフではないので、スレッドやプロセスごとに作る」と説明しています。
Sessionの役割:profile_nameとregion_nameの指定
Session が持つのは、主に認証情報・リージョン・profile に関する設定です。以降の検証では、次の 2 つのファイルを使います(キーの値は説明用の架空のものです)。
# ~/.aws/credentials
[default]
aws_access_key_id = AKIAFILEDEFAULT00001
aws_secret_access_key = secret-from-credentials-default
[dev]
aws_access_key_id = AKIAFILEDEV000000002
aws_secret_access_key = secret-from-credentials-dev
[both]
aws_access_key_id = AKIAFILEBOTH00000009
aws_secret_access_key = secret-from-credentials-both
# ~/.aws/config
[default]
region = ap-northeast-1
[profile dev]
region = us-west-2
[profile confonly]
aws_access_key_id = AKIACONFIGONLY000003
aws_secret_access_key = secret-from-config
region = eu-west-1
[profile admin]
role_arn = arn:aws:iam::123456789012:role/demo-admin
source_profile = dev
role_session_name = taro-session
region = ap-northeast-1
リージョンが決まる順番
# region.py
import boto3
session = boto3.Session()
print("Session() :", session.region_name)
print("Session(profile_name='dev') :", boto3.Session(profile_name="dev").region_name)
print("Session(region_name='eu-central-1'):", boto3.Session(region_name="eu-central-1").region_name)
s = boto3.Session(profile_name="dev")
print("dev の client :", s.client("s3").meta.region_name)
print("dev の client(region_name=...) :", s.client("s3", region_name="ap-southeast-1").meta.region_name)
環境変数なしで実行した結果と、AWS_DEFAULT_REGION を付けて実行した結果です。
$ python3 region.py
Session() : ap-northeast-1
Session(profile_name='dev') : us-west-2
Session(region_name='eu-central-1'): eu-central-1
dev の client : us-west-2
dev の client(region_name=...) : ap-southeast-1
$ AWS_DEFAULT_REGION=sa-east-1 python3 region.py
Session() : sa-east-1
Session(profile_name='dev') : sa-east-1
Session(region_name='eu-central-1'): eu-central-1
dev の client : sa-east-1
dev の client(region_name=...) : ap-southeast-1
| 優先 | 指定する場所 | 例 |
|---|---|---|
| 1 | client・resource の引数 | session.client("s3", region_name="ap-southeast-1") |
| 2 | Session の引数 | boto3.Session(region_name="eu-central-1") |
| 3 | 環境変数 AWS_DEFAULT_REGION | AWS_DEFAULT_REGION=sa-east-1 |
| 4 | 設定ファイルの profile にある region | [profile dev] の region = us-west-2 |
profile に region が書いてあっても、AWS_DEFAULT_REGION があるとそちらが使われます。 また、この環境の boto3 では、AWS_REGION という環境変数を設定しても session.region_name は変わりませんでした(AWS_DEFAULT_REGION だけが読まれました)。
認証情報の探索順(全段の一覧)
boto3 は、認証情報が見つかるまで決まった順に探し、最初に見つかったものを使います。公式ドキュメント(Credentials)に載っている順番は次の 12 段です。右の列は、その段で見つかったときに get_credentials().method が返した値です(「―」は今回動かしていない段)。
| 順 | 探す場所 | 指定のしかた | method の値 |
|---|---|---|---|
| 1 | client・resource の引数 | boto3.client("s3", aws_access_key_id=..., aws_secret_access_key=..., aws_session_token=...) |
― |
| 2 | Session の引数 | boto3.Session(aws_access_key_id=..., aws_secret_access_key=...) |
explicit |
| 3 | 環境変数 | AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY、AWS_SESSION_TOKEN | env |
| 4 | AssumeRole(profile) | config の role_arn と source_profile(または credential_source) | assume-role |
| 5 | Web ID トークンでの AssumeRole | AWS_ROLE_ARN と AWS_WEB_IDENTITY_TOKEN_FILE、または config の web_identity_token_file | assume-role-with-web-identity |
| 6 | IAM Identity Center | config の sso_start_url、sso_region、sso_account_id、sso_role_name | ― |
| 7 | 共有 credentials ファイル | ~/.aws/credentials | shared-credentials-file |
| 8 | コンソールの認証情報でのログイン | config の login_session | ― |
| 9 | config ファイル | ~/.aws/config の profile に書いたキー | config-file |
| 10 | Boto2 の設定ファイル | /etc/boto.cfg と ~/.boto の Credentials セクション | ― |
| 11 | コンテナの認証情報 | 環境変数 AWS_CONTAINER_CREDENTIALS_RELATIVE_URI・AWS_CONTAINER_CREDENTIALS_FULL_URI が指す先 | container-role |
| 12 | インスタンスメタデータ | IAM ロールを付けた EC2 インスタンス | iam-role |
11 と 12 は、同じ形式の応答を返す検証用のサーバーを環境変数で指して確かめました。このほか、config の credential_process で外部コマンドから受け取る profile では、method は custom-process でした。
いまどの認証情報が使われたかを確かめる
session.get_credentials().method で出どころを見る
# which_cred.py
import sys
import boto3
profile = sys.argv[1] if len(sys.argv) > 1 else None
session = boto3.Session(profile_name=profile) if profile else boto3.Session()
cred = session.get_credentials() # 見つからなければ None
print("profile :", session.profile_name)
print("region :", session.region_name)
print("method :", cred.method if cred else None)
print("access key:", cred.access_key[:8] + "…" if cred else None)
設定を変えながら実行した結果です。
$ python3 which_cred.py
profile : default
region : ap-northeast-1
method : shared-credentials-file
access key: AKIAFILE…
$ python3 which_cred.py confonly
profile : confonly
region : eu-west-1
method : config-file
access key: AKIACONF…
$ AWS_ACCESS_KEY_ID=AKIAENV0000000000007 AWS_SECRET_ACCESS_KEY=x python3 which_cred.py
profile : default
region : ap-northeast-1
method : env
access key: AKIAENV0…
$ python3 which_cred.py admin
profile : admin
region : ap-northeast-1
method : assume-role
access key: ASIARZPU…
環境変数にキーを入れると、共有ファイルの default より先に使われました(3 が 7 より先)。admin は role_arn と source_profile を持つ profile で、boto3 が自動で AssumeRole を呼び、一時的なキー(ASIA で始まる)に変わっています。
環境変数のキーとprofileが両方あるとき
つまずきやすいのが、環境変数のキーと profile の組み合わせです。3 通りを比べます。
$ AWS_ACCESS_KEY_ID=AKIAENV0000000000007 AWS_SECRET_ACCESS_KEY=x AWS_PROFILE=dev python3 which_cred.py
profile : dev
region : us-west-2
method : env
access key: AKIAENV0…
$ AWS_ACCESS_KEY_ID=AKIAENV0000000000007 AWS_SECRET_ACCESS_KEY=x python3 which_cred.py dev
profile : dev
region : us-west-2
method : shared-credentials-file
access key: AKIAFILE…
$ python3 which_cred.py both
profile : both
region : None
method : shared-credentials-file
access key: AKIAFILE…
| 状況 | 使われた認証情報 |
|---|---|
| 環境変数のキー + 環境変数 AWS_PROFILE=dev | 環境変数のキー(region だけ dev のものになる) |
環境変数のキー + コードで Session(profile_name="dev") |
dev の profile のキー |
| 同じ profile 名が credentials と config の両方にある | credentials ファイルのキー |
コードで profile_name を渡した場合は、環境変数のキーよりその profile が使われます。 「profile を指定したのに別のキーで接続している」と感じたら、AWS_PROFILE ではなく Session の引数で渡しているか、環境変数にキーが残っていないかを method で確かめると早く分かります。
Sessionに直接キーを渡した場合
# explicit.py
import boto3
# 環境変数にも共有ファイルにも認証情報がある状態で実行する
session = boto3.Session(aws_access_key_id="AKIAARGS000000000010",
aws_secret_access_key="secret-from-args")
print("Session に引数 :", session.get_credentials().method,
session.get_credentials().access_key[:8] + "…")
plain = boto3.Session()
print("引数なしの Session:", plain.get_credentials().method,
plain.get_credentials().access_key[:8] + "…")
$ AWS_ACCESS_KEY_ID=AKIAENV0000000000007 AWS_SECRET_ACCESS_KEY=x python3 explicit.py
Session に引数 : explicit AKIAARGS…
引数なしの Session: env AKIAENV0…
引数で渡したキーが最優先です。キーをソースコードに直接書くと、リポジトリに残りやすいので、手元では profile、AWS 上ではロール(コンテナやインスタンスに付けるもの)に任せる形が扱いやすくなります。
sts get-caller-identity で「誰として接続しているか」を見る
method は「どこから見つけたか」、get-caller-identity は「AWS から見て誰か」を教えてくれます。
# whoami.py
import sys
import boto3
session = boto3.Session(profile_name=sys.argv[1])
identity = session.client("sts").get_caller_identity()
print("method :", session.get_credentials().method)
print("Account:", identity["Account"])
print("Arn :", identity["Arn"])
$ python3 whoami.py admin
method : assume-role
Account: 123456789012
Arn : arn:aws:sts::123456789012:assumed-role/demo-admin/taro-session
AWS CLI でも同じ profile で確かめられます。
aws sts get-caller-identity --profile admin
{
"UserId": "AROARZPUZDIKAODWUFO6M:taro-session",
"Account": "123456789012",
"Arn": "arn:aws:sts::123456789012:assumed-role/demo-admin/taro-session"
}
Arn が assumed-role/ロール名/セッション名 の形なら、ロールに切り替わっています。
NoRegionError・NoCredentialsError・ProfileNotFoundを再現する
設定ファイルも環境変数も無い状態で、3 つのエラーを順に起こします。
# errors3.py
import boto3
from botocore.exceptions import NoCredentialsError, NoRegionError, ProfileNotFound
# 1. ProfileNotFound:Session を作る時点で起きる
try:
boto3.Session(profile_name="nothing")
except ProfileNotFound as e:
print("ProfileNotFound :", e)
print("使える profile :", boto3.Session().available_profiles)
# 2. NoRegionError:client を作る時点で起きる
try:
boto3.Session().client("dynamodb")
except NoRegionError as e:
print("NoRegionError :", e)
print("region を渡せば作れる:", boto3.Session().client("dynamodb", region_name="ap-northeast-1").meta.region_name)
# 3. NoCredentialsError:client は作れて、API を呼んだ時点で起きる
s3 = boto3.Session().client("s3", region_name="ap-northeast-1")
print("client は作れる :", type(s3).__name__)
try:
s3.list_buckets()
except NoCredentialsError as e:
print("NoCredentialsError :", e)
print("get_credentials() :", boto3.Session().get_credentials())
ProfileNotFound : The config profile (nothing) could not be found
使える profile : []
NoRegionError : You must specify a region.
region を渡せば作れる: ap-northeast-1
client は作れる : S3
NoCredentialsError : Unable to locate credentials
get_credentials() : None
| エラー | 起きる時点 | 原因 | 直し方 |
|---|---|---|---|
| ProfileNotFound | Session を作るとき | profile 名が設定ファイルに無い(綴り、AWS_CONFIG_FILE の向き先、config 側の見出しに「profile 」を付け忘れ) | available_profiles で一覧を確かめ、名前とファイルを合わせる |
| NoRegionError | client・resource を作るとき | リージョンがどこにも無い | region_name を渡す、AWS_DEFAULT_REGION を設定する、profile に region を書く |
| NoCredentialsError | API を呼んだとき | 12 段のどこにも認証情報が無い | 環境変数・profile・ロールのいずれかを用意する。get_credentials() が None かどうかで事前に分かる |
3 つは起きる時点が違います。 認証情報が無くても client は作れて、最初の API 呼び出しで NoCredentialsError になります。起動時に確かめたい場合は、get_credentials() が None でないことを見るか、get_caller_identity を 1 回呼ぶと早い段階で気づけます。
AssumeRoleで別のSessionを作る定型コード
別のロールに切り替えて操作する方法は 2 つあります。
| 方法 | 書き方 | 向いている場面 |
|---|---|---|
| profile に任せる | config に role_arn と source_profile を書き、Session(profile_name="admin") |
手元の作業、切り替え先が決まっている。期限が来ると boto3 が自動で更新する |
| コードで assume_role を呼ぶ | 下の定型コード | 切り替え先を実行時に決める、複数アカウントを順に処理する |
公式ドキュメントによると、profile による AssumeRole で得た一時的な認証情報はディスクには書かれず、1 つの Session の中でメモリに保持され、その Session から作った client が共有します。
コードで切り替える定型です。
# assume_role.py
import boto3
def assumed_session(role_arn, session_name, base_session=None, duration=3600):
"""AssumeRole で得た一時的な認証情報から、別の Session を作る"""
base = base_session or boto3.Session()
resp = base.client("sts").assume_role(
RoleArn=role_arn, RoleSessionName=session_name, DurationSeconds=duration)
cred = resp["Credentials"]
return boto3.Session(
aws_access_key_id=cred["AccessKeyId"],
aws_secret_access_key=cred["SecretAccessKey"],
aws_session_token=cred["SessionToken"],
region_name=base.region_name,
)
base = boto3.Session(profile_name="dev")
admin = assumed_session("arn:aws:iam::123456789012:role/demo-admin", "taro-session", base)
cred = admin.get_credentials()
print("method :", cred.method)
print("token :", "あり" if cred.token else "なし")
print("Arn :", admin.client("sts").get_caller_identity()["Arn"])
# 切り替え先の Session から、いつもどおり client / resource を作る
s3 = admin.client("s3")
print("buckets:", [b["Name"] for b in s3.list_buckets()["Buckets"]])
method : explicit
token : あり
Arn : arn:aws:sts::123456789012:assumed-role/demo-admin/taro-session
buckets: ['rjc-demo-bucket']
ポイントは 3 つです。
- AccessKeyId・SecretAccessKey・SessionToken の 3 つをそろえて渡します。 一時的な認証情報は、トークンが無いと使えません。
- 元の Session と切り替え先の Session を別の変数で持ちます。 boto3.client() の既定の Session は元のままなので、切り替え先の操作は必ず
admin.client(...)から作ります。 - この書き方では method が explicit になり、期限が来ても自動では更新されません。 長く動く処理では、期限(ここでは 3600 秒を指定)の前に assumed_session を呼び直すか、profile に任せる方法を選びます。
複数のロールを順に処理する形にも、そのまま広げられます。
for role_arn in ["arn:aws:iam::123456789012:role/demo-admin"]:
session = assumed_session(role_arn, "batch-session")
print(session.client("sts").get_caller_identity()["Arn"])
arn:aws:sts::123456789012:assumed-role/demo-admin/batch-session
よくある質問
Q. boto3.client() と boto3.Session().client() は何が違いますか? A. boto3.client() は、既定の Session(boto3.DEFAULT_SESSION)を作ってそこから client を作る近道です。profile やリージョンを切り替えない小さなスクリプトなら前者、設定を明示したい・複数の設定を並べて使いたいときは後者が向いています。
Q. resource で書いたコードは書き直したほうがよいですか?
A. 急ぐ必要はありません。公式の注記は「新機能を追加しない」で、既存のインターフェースは動き続けるとされています。新しく書く部分を client にし、足りない操作は resource.meta.client で補う進め方が無理なく続けられます。
Q. AWS CLI と boto3 で、設定ファイルは共通ですか? A. ~/.aws/credentials と ~/.aws/config を共通で読みます。この記事の admin profile も、boto3 と AWS CLI の両方で同じロールに切り替わりました。
Q. コンテナや EC2 インスタンスの上では、キーをどう渡せばよいですか?
A. ロールを付けておけば、探索順の 11 段目(コンテナの認証情報)や 12 段目(インスタンスメタデータ)で自動的に見つかります。コードは boto3.client("s3") のままで動き、キーをファイルや環境変数に置かずに済みます。
まとめ
- client は API を 1 対 1 で呼ぶ窓口、resource はオブジェクトとして扱う窓口、Session は設定を持って両方を作る元です。
- resource は 9 サービスのみで、新機能は追加されません。 これから書くコードは client を基本にします。
- リージョンは「client の引数 → Session の引数 → AWS_DEFAULT_REGION → profile の region」の順に決まります。
- 認証情報は 12 段を上から探し、最初に見つかったものが使われます。 引数 → 環境変数 → AssumeRole 系 → 共有 credentials → config → コンテナ → インスタンスロールの順です。
- session.get_credentials().method で出どころ、get_caller_identity で接続先から見た身元を確かめられます。
- ProfileNotFound は Session 作成時、NoRegionError は client 作成時、NoCredentialsError は API 呼び出し時に起きます。
- AssumeRole は、得た 3 つの値で別の Session を作るのが定型です。自動更新が必要なら profile の role_arn に任せます。
参考・出典
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「boto3 client・resource・sessionの違いと認証の優先順位」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。