【AWS】2026年最新機能「S3 Files」をTerraformで構築する手順と3つのハマりどころ
はじめに
2026年4月にGA(一般提供)となったS3 Filesは、S3バケットをNFSファイルシステムとしてマウントできるサービスです。EC2などのクライアントから通常のディレクトリのように読み書きでき、ファイルサーバー移行の選択肢として注目されています。
従来、ファイルシステムが必要な場合はEFSのような専用のファイルストレージを用意し、S3上のデータを使う場合はオブジェクトとファイルの間でコピー・同期する構成が一般的でした。S3 Filesはデータをオブジェクトのまま保持しつつファイルシステムとしてもアクセスできるため、複製・同期が不要になります。また、バッキングストアがS3であることから、S3の高い耐久性・可用性やストレージクラスによるコスト効率、IAMや暗号化によるセキュリティもそのまま活用できます。
しかし、公式ドキュメントを読みながら実際にTerraformで構築を進めると、IAMの設定やセキュリティグループの要件など、いくつか「詰まりやすい」ポイントが存在しました。
本記事では、実際にTerraformでS3 Filesを構築した経験をもとに、必要な構成リソースとハマりどころを共有します。S3 Filesの導入を検討している方の参考になれば幸いです。
構成の全体像
今回の検証構成は以下のとおりです。
S3バケット(バッキングストア)
S3 Files ファイルシステム(S3バケットと紐づく)
S3 Files マウントターゲット(VPC内のサブネットに作成、AZごと)
EC2インスタンス2台(クライアント、/mnt/s3files にマウント)
IAMロール・ポリシー(EC2用、S3 Files用)
セキュリティグループ(NFS通信の許可)
Terraformはモジュール構成で管理しています。
terraform/
├── main.tf
├── provider.tf
├── variables.tf
└── modules/
├── network/ # VPC, Subnet, NAT Gateway
├── security/ # Security Group
├── storage/ # S3, S3 Files
├── app/ # EC2, IAM
└── alb/ # ALB, Target GroupS3 Files関連リソースの設定
Providerの設定
S3 FilesのTerraformリソースは hashicorp/aws provider v6.40.0 以降で利用できます。
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 6.40.0"
}
}
required_version = ">= 1.0"
}S3バケット
S3 Filesのバッキングストアになる通常のS3バケットを作成します。
バージョニングとサーバーサイド暗号化の有効化がS3 Files ファイルシステム作成の前提条件のため、depends_on で順序を制御します。
※ 検証環境のため force_destroy = true を指定しています。本番環境では誤削除防止の観点から慎重に検討してください。
resource "aws_s3_bucket" "this" {
bucket = "${var.project_name}-data-bucket"
force_destroy = true
}
resource "aws_s3_bucket_versioning" "this" {
bucket = aws_s3_bucket.this.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "this" {
bucket = aws_s3_bucket.this.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}S3 Files用IAMロール
S3 Files自身がS3バケットにアクセスするためのIAMロールです。Principalは elasticfilesystem.amazonaws.com を指定します(S3 FilesはEFSの系譜のためこのサービス名になっています)。
SourceAccountとSourceArnの条件を付けることで、意図しないアカウントやリソースからのAssumeRoleを防ぎます。
resource "aws_iam_role" "s3_files_role" {
name = "${var.project_name}-s3-files-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Service = "elasticfilesystem.amazonaws.com" }
Action = "sts:AssumeRole"
Condition = {
StringEquals = { "aws:SourceAccount" = var.account_id }
ArnLike = { "aws:SourceArn" = "arn:aws:s3files:${var.region}:${var.account_id}:file-system/*" }
}
}]
})
}このロールに付与するポリシーは、S3バケットへの読み書き権限に加えて、S3 Filesが内部で使用するEventBridgeルール(DO-NOT-DELETE-S3-Files*)の管理権限も必要です。
resource "aws_iam_role_policy" "s3_files_policy" {
name = "${var.project_name}-s3-files-policy"
role = aws_iam_role.s3_files_role.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "S3BucketPermissions"
Effect = "Allow"
Action = ["s3:ListBucket", "s3:ListBucketVersions"]
Resource = aws_s3_bucket.this.arn
Condition = {
StringEquals = { "aws:ResourceAccount" = var.account_id }
}
},
{
Sid = "S3ObjectPermissions"
Effect = "Allow"
Action = ["s3:AbortMultipartUpload", "s3:DeleteObject*",
"s3:GetObject*", "s3:List*", "s3:PutObject*"]
Resource = "${aws_s3_bucket.this.arn}/*"
Condition = {
StringEquals = { "aws:ResourceAccount" = var.account_id }
}
},
{
Sid = "EventBridgeManage"
Effect = "Allow"
Action = [
"events:DeleteRule", "events:DisableRule",
"events:EnableRule",
"events:PutRule", "events:PutTargets", "events:RemoveTargets"
]
Condition = {
StringEquals = { "events:ManagedBy" = "elasticfilesystem.amazonaws.com" }
}
Resource =
["arn:aws:events:*:*:rule/DO-NOT-DELETE-S3-Files*"]
},
{
Sid = "EventBridgeRead"
Effect = "Allow"
Action = ["events:DescribeRule", "events:ListRuleNamesByTarget", "events:ListRules", "events:ListTargetsByRule"]
Resource = ["arn:aws:events:*:*:rule/*"]
}
]
})
}S3 Files ファイルシステム(aws_s3files_file_system)
S3バケットのARNと、先ほど作成したIAMロールのARNを指定します。バージョニングと暗号化が有効になってから作成するように depends_on を設定してください。
resource "aws_s3files_file_system" "this" {
bucket = aws_s3_bucket.this.arn
role_arn = aws_iam_role.s3_files_role.arn
depends_on = [
aws_s3_bucket_versioning.this,
aws_s3_bucket_server_side_encryption_configuration.this,
]
}S3 Files マウントターゲット(aws_s3files_mount_target)
ここが最初の詰まりポイントです。
ファイルシステムを作成しただけではEC2からマウントできません。
EC2からマウントするためには、VPC内にマウントターゲットを作成する必要があります。
EFSと同様の考え方で、マウントするEC2が存在するサブネット(AZ)ごとに1つ作成します。
resource "aws_s3files_mount_target" "this" {
count = length(var.private_subnet_ids)
file_system_id = aws_s3files_file_system.this.id
subnet_id = var.private_subnet_ids[count.index]
security_groups = [var.s3_files_mount_target_sg_id]
}マウントターゲットのエンドポイントは <AZ ID>.fs-<ID>.s3files.<リージョン>.on.aws という形式になります。
EC2からこのエンドポイントへのNFS通信(ポート2049)が通っている必要があります。
セキュリティグループ
ここが2つ目の詰まりポイントです。
IAMが正しく設定されていても、セキュリティグループ(以下、SG)でポート2049が開いていないとマウント時にタイムアウトします。
必要なのはマウントターゲット側のインバウンドだけではありません。公式ドキュメントでは、EC2側(マウント元)にもマウントターゲットSGへのポート2049アウトバウンドの許可が要件として明記されています。
セキュリティグループ | ルール | プロトコル | ポート | 送信元/送信先 |
EC2インスタンス | アウトバウンド | TCP | 2049 | マウントターゲットのSG |
マウントターゲット | インバウンド | TCP | 2049 | EC2インスタンスのSG |
マウントターゲット用のセキュリティグループを作成し、EC2のセキュリティグループからのポート2049(TCP)インバウンドを許可します。
resource "aws_security_group" "s3_files_mount_target" {
name = "${var.project_name}-s3-files-mount-target-sg"
vpc_id = var.vpc_id
ingress {
from_port = 2049
to_port = 2049
protocol = "tcp"
security_groups = [aws_security_group.app.id]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}なお、今回のEC2側セキュリティグループ(aws_security_group.app)はegressを 0.0.0.0/0 で全許可しているため、ポート2049のアウトバウンドも暗黙的に許可されています。
最小権限の構成にする場合は、EC2側にもマウントターゲットSGへの2049番ポートを明示したアウトバウンドルールを追加してください。
EC2用IAMロール
ここが3つ目の詰まりポイントです。
IAMはEC2側とS3 Files側の2方向で設定が必要です。またEC2側には AmazonS3FilesClientFullAccess マネージドポリシーのアタッチが必要です。
resource "aws_iam_role" "app_role" {
name = "${var.project_name}-app-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Service = "ec2.amazonaws.com" }
Action = "sts:AssumeRole"
}]
})
}
resource "aws_iam_role_policy_attachment" "s3_files_access" {
role = aws_iam_role.app_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonS3FilesClientFullAccess"
}
resource "aws_iam_role_policy_attachment" "ssm" {
role = aws_iam_role.app_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}さらに、公式ドキュメントではEC2ロールに対して、紐づくS3バケットを直接読み取るためのインラインポリシーもあわせて推奨されています。
これは「Intelligent read routing」と呼ばれる読み取り最適化のための追加ポリシーです。無くてもマウント・アクセス自体は可能ですが、読み取り性能が最適化されません。
resource "aws_iam_role_policy" "s3_read_policy" {
name = "${var.project_name}-s3-read-policy"
role = aws_iam_role.app_role.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "S3ObjectReadAccess"
Effect = "Allow"
Action = ["s3:GetObject", "s3:GetObjectVersion"]
Resource = "${aws_s3_bucket.this.arn}/*"
},
{
Sid = "S3BucketListAccess"
Effect = "Allow"
Action = "s3:ListBucket"
Resource = aws_s3_bucket.this.arn
}
]
})
}EC2のuser-data(マウント設定)
S3 Filesのマウントには amazon-efs-utils を使用します。S3 Filesの利用には amazon-efs-utils バージョン3.0.0以上が必要です。
AL2023の最新パッケージであれば問題ありませんが、古いAMIを使う場合はバージョンを確認してください。
今回は/etc/fstab にファイルシステムIDを記載しておくことで、EC2起動時に自動マウントされます。
resource "aws_instance" "app" {
count = 2
ami = data.aws_ami.al2023.id
instance_type = "t3.micro"
iam_instance_profile = aws_iam_instance_profile.app_profile.name
subnet_id = var.private_subnet_ids[count.index]
vpc_security_group_ids = [var.app_sg_id]
user_data = <<-EOT
#!/bin/bash
yum -y install amazon-efs-utils
mkdir -p /mnt/s3files
echo "${var.s3_file_system_id}:/ /mnt/s3files s3files _netdev 0 0" >> /etc/fstab
mount -a
EOT
}ファイルシステムIDは aws_s3files_file_system リソースの出力から module.storage.s3_file_system_id として渡します。
詰まりポイントまとめ
マウントターゲットがないと名前解決エラーになる
Failed to resolve
"apne1-az4.fs-xxxxxx.s3files.ap-northeast-1.on.aws"
- ensure that the VPC has an S3Files mount target for this file system ID.ファイルシステムだけ作ってマウントするとこのエラーが出ます。マウントターゲットを作成してから再試行してください。
セキュリティグループのポート2049を忘れずに
マウントターゲットへのインバウンドでポート2049(TCP / NFS)を許可しないと、マウント時にタイムアウトします。IAMが正しくても通信が届かなければマウントできません。
公式ドキュメントではEC2側のアウトバウンドも要件として明記されているため、最小権限で構成する場合はEC2側のセキュリティグループにも2049番ポートのアウトバウンド許可が必要です(今回の構成ではEC2側のegressを全許可しているため、結果的に通信は通っています)。
IAMは「EC2 → S3 Files」と「S3 Files → S3」の2方向
どちらか一方が欠けてもマウントやファイルアクセスが失敗します。
方向 | ポリシー | アタッチ先 |
EC2 → S3 Files | AmazonS3FilesClientFullAccess | EC2のIAMロール |
EC2 → S3(読み取り最適化、任意) | カスタムポリシー(s3:GetObject等) | EC2のIAMロール |
S3 Files → S3 | カスタムポリシー(S3操作 + EventBridge) | S3 Files用IAMロール |
まとめ
S3 Filesをゼロから構築する場合、最低限必要なリソースと注意点は以下のとおりです。
リソース | 注意点 |
| バージョニング・暗号化を先に有効化する |
|
|
| AZごとに1つ作成する |
IAMロール(S3 Files用) | Principal は |
IAMロール(EC2用) |
|
セキュリティグループ | マウントターゲットへのインバウンドとEC2側のアウトバウンドの両方でポート2049(TCP)を許可 |
公式ドキュメントにはリソース単体の仕様は記載されていますが、構成全体の繋がりや順序依存関係は試してみないと分かりにくいポイントが多く、今回の検証を通じて解像度が上がりました。
S3 Filesを利用してみたいと考えている方へ、本記事が構築の参考になれば幸いです。
この記事を書いた人
What is BEMA!?
Be Engineer, More Agile


