概要
aws-sdk/client-s3 の v3 を使って s3 にアップロードをするにあたって、特に s3 との認証周りで少し苦労したので、ここに方法を書き残しておきます。
動作確認環境
- TypeScript 5.3.2
- ts-node 10.9.1
- aws-sdk/client-s3 3.461.0
- aws-sdk/credential-providers 3.461.0
前提条件
AWS に接続先の s3 バケットを用意していること
s3 を操作できる iam を用意していること
コード例
最終的にこんな感じでアップロードできました。(環境変数の読み込みは next.js のお作法に則っています)
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import { fromIni, fromContainerMetadata } from "@aws-sdk/credential-providers";
class MyS3Client {
private client: S3Client;
constructor() {
this.client = new S3Client({
region: "ap-northeast-1",
credentials: this.createCredentials(),
});
}
async putObject() {
const params = {
Bucket: process.env.BUCKET_NAME,
Key: "hello.txt",
Body: "hello from mys3client",
};
await this.client.send(new PutObjectCommand(params));
}
private createCredentials() {
if (process.env.NEXT_PUBLIC_APP_ENV === "local") {
return fromIni({
profile: "your-aws-profile",
});
} else {
return fromContainerMetadata();
}
}
}
const s3Client = new MyS3Client();
s3Client.putObject();
解説
こちらの S3Client の初期化については公式ドキュメントにあるように region や credentials を設定してあげなくても、デフォルトの profile を使って接続をしてくれます。
this.client = new S3Client({
// region: "ap-northeast-1",
// credentials: this.createCredentials(),
});
ですが、profile を指定したい場合や、本番環境では別の認証方法にしたい場合には特別に credentials も指定してあげる必要があります。
そのために、aws-sdk/credential-providers を使って credentials 情報を付与します。
今回はローカルの環境では profile を使い、デプロイ先では ECS のタスクロールの権限を利用したかったため、分岐させています。
private createCredentials() {
if (process.env.NEXT_PUBLIC_APP_ENV === "local") {
return fromIni({
profile: "your-aws-profile",
});
} else {
return fromContainerMetadata();
}
}
環境変数を認証に利用する場合やデプロイ先が EC2 の場合などにはユースケースに応じて利用するメソッドが異なります。
こちらにそれぞれのユースケースがまとめられています。
苦労した箇所
一番よく出していたエラーは
AccessDenied: Access Denied
でした。なぜ拒否されるのかのフィードバックが少なくて困っていました。
私の場合このエラーは 「credential の設定自体は読み込めているものの、接続しようとしているバケットにオブジェクトをアップロードする権限がない。」
という理由で起きていました。
なのでこのエラーの解消において確認すべきはコード側ではなく、iam の権限設定や s3 バケットのバケットポリシーです。
よくある間違いとしては iam 側の設定でアクセスするバケットのオブジェクトの指定が s3 バケットの arn のみが書かれているパターンではないでしょうか。
# よくない例
arn:aws:s3:::your-bucket-name
このままではそのバケット配下にオブジェクトを入れることができません。
末尾に/*
をつけるようにしてください。
# よい例
arn:aws:s3:::your-bucket-name/*
まとめ
v2 から v3 になって credentials 情報をコードの中になるべく書かずに実装できるようになりました。
これまで環境変数を使って割り当てていたりしましたが、(v3 でもその方法はあります)credential-providers を使うと何かと便利でした。
どなたかの助けになれば幸いです。