こんにちは。サービス開発室の武田です。
motoはPythonのAWS SDKであるBoto3のモックライブラリです。motoを使用したテストを書く際に、マルチアカウントに対する処理がたまに発生します。たとえば次のようなケースが考えられるでしょう。
- 管理アカウントにリソース(e.g. Compute Optimizer)があり、それに依存したメンバーアカウントの処理
- 他のアカウントにあるEventBridgeと連携するような処理
- 他のアカウントにS3バケットがある場合の処理
今回はS3バケットを例にして、マルチアカウントが必要なテストケースを確認してみます。なお moto v4以降 がマルチアカウントをサポートしています。
サンプルコードの準備
実行環境は次のようになっています。
- Python: 3.12
- boto3: 1.34.51
- pytest: 8.0.2
- moto: 5.0.2
Poetryを使用して検証プロジェクトを作成します。
$ mkdir work && cd $_
$ poetry init -n
$ poetry add boto3 pytest moto
テスト対象となるコードは次のようなものを用意しました。
main.py
import boto3
def main():
s3 = boto3.client("s3")
<pre><code>try:
s3.create_bucket(
Bucket="test-bucket",
CreateBucketConfiguration={"LocationConstraint": "ap-northeast-1"},
)
except s3.exceptions.BucketAlreadyOwnedByYou:
return "BucketAlreadyOwnedByYou"
except s3.exceptions.BucketAlreadyExists:
return "BucketAlreadyExists"
</code></pre>
create_bucket
メソッドでS3バケットを作成するわけですが、すでに同じ名前のバケットが存在する場合は作成できません。Pythonでは例外が投げられるわけですが、実はその該当バケットのオーナーが自分なのか他人なのかで投げられる例外が異なります。
- 自分がオーナー
- BucketAlreadyOwnedByYou
- 他人がオーナー
- BucketAlreadyExists
続いてテストケースです。
test_main.py
import boto3
from moto import mock_aws
import main
@mock_aws
def test_main1():
res = main.main()
assert res is None
@mock_aws
def test_main2():
s3 = boto3.client("s3")
s3.create_bucket(
Bucket="test-bucket",
CreateBucketConfiguration={"LocationConstraint": "ap-northeast-1"},
)
<pre><code>res = main.main()
assert res == "BucketAlreadyOwnedByYou"
</code></pre>
@mock_aws
def test_main3():
sts = boto3.client("sts")
res = sts.assume_role(
RoleArn="arn:aws:iam::999999999999:role/test-role",
RoleSessionName="test-session-name",
)
<pre><code>alt_session = boto3.Session(
aws_access_key_id=res["Credentials"]["AccessKeyId"],
aws_secret_access_key=res["Credentials"]["SecretAccessKey"],
aws_session_token=res["Credentials"]["SessionToken"],
)
alt_s3 = alt_session.client("s3")
alt_s3.create_bucket(
Bucket="test-bucket",
CreateBucketConfiguration={"LocationConstraint": "ap-northeast-1"},
)
res = main.main()
assert res == "BucketAlreadyExists"
</code></pre>
ポイントはtest_main3
で、テストケースの中でassume_role
をすることで、別のアカウントのセッションを作成しています。これによってメインとなるアカウントとは別のアカウントで処理を実行できています。
これを実行すると次のような結果となり、問題なくテストできています。
$ poetry run pytest test_main.py
collected 3 items
test_main.py ...
まとめ
複雑な処理からシンプルな処理まで、マルチアカウントが必要となるケースはままあります。motoをうまく使用して、しっかりテストを書いていきましょう。