𝕏 f B! L
案件・求人数 12,345
案件を探す(準備中) エージェントを探す(準備中) お役立ち情報 ログイン
案件・求人数 12,345
AWS Transfer Family完全ガイド|SFTP/FTPSマネージドサービスでファイル転送基盤を構築

AWS Transfer Family完全ガイド|SFTP/FTPSマネージドサービスでファイル転送基盤を構築

AWSTransfer FamilySFTPファイル転送SESエンジニア
目次
⚡ 3秒でわかる!この記事のポイント
  • Transfer FamilyはSFTP/FTPS/FTPをフルマネージドで提供、サーバー運用不要
  • S3/EFSをバックエンドにしたスケーラブルなファイル転送基盤を即構築
  • B2Bファイル連携案件の月単価は70〜100万円、金融・物流で需要急増中

企業間のデータ連携において、**SFTP(SSH File Transfer Protocol)**は2026年現在も最も広く使われているファイル転送プロトコルです。特に金融機関、物流、医療、製造業では、日次バッチ処理やEDI(電子データ交換)でSFTPが必須となっています。

しかし、SFTPサーバーの自前運用はセキュリティパッチ適用、SSH鍵管理、可用性確保、スケーリングと運用負担が大きいのが現実です。AWS Transfer Familyを使えば、これらの運用をすべてAWSに委ね、ファイル転送の自動化に集中できます。

この記事でわかること
  • AWS Transfer Familyの全体像と対応プロトコル
  • SFTP/FTPSサーバーの構築手順
  • S3連携によるファイル自動処理パイプライン
  • 認証方式の選択と設計(SSH鍵、Active Directory、カスタム認証)
  • B2Bファイル連携のセキュリティベストプラクティス

AWS Transfer Familyとは

概要

AWS Transfer Familyは、SFTP、FTPS、FTP、AS2プロトコルに対応したフルマネージドファイル転送サービスです。ファイルの保存先としてAmazon S3またはAmazon EFSを使用できます。

プロトコルポート暗号化主な用途
SFTP22SSHB2B連携、最も一般的
FTPS990TLSレガシーFTPのセキュア化
FTP21なしテスト・社内のみ
AS2443S/MIMEEDI、小売・流通

従来のSFTPサーバー運用との比較

項目EC2自前運用Transfer Family
サーバー管理必要(OS、パッチ)不要
高可用性Multi-AZ構成要自動(99.99% SLA)
スケーリング手動自動
SSH鍵管理authorized_keys手動管理IAM / カスタム認証
ログ自前設定CloudWatch自動連携
料金EC2 + EBS$0.30/時間 + 転送量

ユースケース

  • 金融機関: 日次の取引データ・帳票ファイルの自動連携
  • 物流: 在庫データ・出荷情報のEDI
  • 医療: 検査結果・診療レポートのセキュア転送
  • 製造: サプライチェーンのデータ連携
  • SaaS: 顧客データの一括インポート/エクスポート

SFTPサーバーの構築

Terraformによるインフラ構築

# main.tf

# Transfer Familyサーバー(SFTP)
resource "aws_transfer_server" "sftp" {
  identity_provider_type = "SERVICE_MANAGED"
  protocols              = ["SFTP"]
  endpoint_type          = "PUBLIC"

  logging_role = aws_iam_role.transfer_logging.arn

  structured_log_destinations = [
    "${aws_cloudwatch_log_group.transfer.arn}:*"
  ]

  security_policy_name = "TransferSecurityPolicy-2024-01"

  tags = {
    Name        = "sftp-server"
    Environment = "production"
    Project     = "b2b-integration"
  }
}

# CloudWatch ロググループ
resource "aws_cloudwatch_log_group" "transfer" {
  name              = "/aws/transfer/${aws_transfer_server.sftp.id}"
  retention_in_days = 90
}

# ロギング用IAMロール
resource "aws_iam_role" "transfer_logging" {
  name = "transfer-logging-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action = "sts:AssumeRole"
      Effect = "Allow"
      Principal = {
        Service = "transfer.amazonaws.com"
      }
    }]
  })
}

resource "aws_iam_role_policy_attachment" "transfer_logging" {
  role       = aws_iam_role.transfer_logging.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSTransferLoggingAccess"
}

# S3バケット(ファイル保存先)
resource "aws_s3_bucket" "transfer" {
  bucket = "my-sftp-transfer-bucket"

  tags = {
    Name = "sftp-transfer-files"
  }
}

# S3バケットの暗号化設定
resource "aws_s3_bucket_server_side_encryption_configuration" "transfer" {
  bucket = aws_s3_bucket.transfer.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm     = "aws:kms"
      kms_master_key_id = aws_kms_key.transfer.arn
    }
  }
}

# KMS暗号化キー
resource "aws_kms_key" "transfer" {
  description             = "SFTP Transfer encryption key"
  deletion_window_in_days = 10
  enable_key_rotation     = true
}

# S3バケットのライフサイクルルール
resource "aws_s3_bucket_lifecycle_configuration" "transfer" {
  bucket = aws_s3_bucket.transfer.id

  rule {
    id     = "archive-old-files"
    status = "Enabled"

    transition {
      days          = 30
      storage_class = "STANDARD_IA"
    }

    transition {
      days          = 90
      storage_class = "GLACIER"
    }

    expiration {
      days = 365
    }
  }
}

ユーザーの作成と権限設定

# SFTPユーザー用IAMロール
resource "aws_iam_role" "sftp_user" {
  name = "sftp-user-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action = "sts:AssumeRole"
      Effect = "Allow"
      Principal = {
        Service = "transfer.amazonaws.com"
      }
    }]
  })
}

# S3アクセスポリシー(ユーザーごとのディレクトリ制限)
resource "aws_iam_role_policy" "sftp_user_s3" {
  name = "sftp-user-s3-access"
  role = aws_iam_role.sftp_user.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "AllowListBucket"
        Effect = "Allow"
        Action = ["s3:ListBucket"]
        Resource = [aws_s3_bucket.transfer.arn]
        Condition = {
          StringLike = {
            "s3:prefix" = ["$${transfer:UserName}/*", "$${transfer:UserName}"]
          }
        }
      },
      {
        Sid    = "AllowObjectAccess"
        Effect = "Allow"
        Action = [
          "s3:PutObject",
          "s3:GetObject",
          "s3:DeleteObject",
          "s3:GetObjectVersion"
        ]
        Resource = ["${aws_s3_bucket.transfer.arn}/$${transfer:UserName}/*"]
      }
    ]
  })
}

# SFTPユーザー作成
resource "aws_transfer_user" "partner_a" {
  server_id = aws_transfer_server.sftp.id
  user_name = "partner-a"
  role      = aws_iam_role.sftp_user.arn

  home_directory_type = "LOGICAL"
  home_directory_mappings {
    entry  = "/"
    target = "/${aws_s3_bucket.transfer.id}/partner-a"
  }

  tags = {
    Company = "Partner A Co., Ltd."
  }
}

# SSH公開鍵の登録
resource "aws_transfer_ssh_key" "partner_a" {
  server_id = aws_transfer_server.sftp.id
  user_name = aws_transfer_user.partner_a.user_name
  body      = file("${path.module}/keys/partner-a.pub")
}

ファイル処理の自動化パイプライン

S3イベント → Lambda → 後続処理

ファイルがアップロードされたら自動的に処理するパイプラインを構築します。

# Lambda関数(ファイル処理)
resource "aws_lambda_function" "process_file" {
  filename         = "lambda/process-file.zip"
  function_name    = "sftp-file-processor"
  role             = aws_iam_role.lambda_exec.arn
  handler          = "index.handler"
  runtime          = "nodejs20.x"
  timeout          = 300
  memory_size      = 512

  environment {
    variables = {
      NOTIFICATION_TOPIC_ARN = aws_sns_topic.file_notification.arn
      DDB_TABLE_NAME         = aws_dynamodb_table.file_log.name
    }
  }
}

# S3イベント通知
resource "aws_s3_bucket_notification" "transfer" {
  bucket = aws_s3_bucket.transfer.id

  lambda_function {
    lambda_function_arn = aws_lambda_function.process_file.arn
    events              = ["s3:ObjectCreated:*"]
    filter_suffix       = ".csv"
  }

  lambda_function {
    lambda_function_arn = aws_lambda_function.process_file.arn
    events              = ["s3:ObjectCreated:*"]
    filter_suffix       = ".xml"
  }
}

Lambda関数の実装例:

// lambda/process-file/index.ts
import { S3Event, Context } from 'aws-lambda';
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3';
import { SNSClient, PublishCommand } from '@aws-sdk/client-sns';
import { DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb';
import { parse } from 'csv-parse/sync';

const s3 = new S3Client({});
const sns = new SNSClient({});
const ddb = new DynamoDBClient({});

export async function handler(event: S3Event, context: Context) {
  for (const record of event.Records) {
    const bucket = record.s3.bucket.name;
    const key = decodeURIComponent(record.s3.object.key);
    const size = record.s3.object.size;

    console.log(`ファイル受信: s3://${bucket}/${key} (${size} bytes)`);

    try {
      // ファイル取得
      const obj = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
      const body = await obj.Body!.transformToString('utf-8');

      // パートナー名とファイル種別を抽出
      const [partner, ...rest] = key.split('/');
      const fileName = rest.join('/');

      // CSV処理
      let recordCount = 0;
      if (key.endsWith('.csv')) {
        const records = parse(body, { columns: true, skip_empty_lines: true });
        recordCount = records.length;
        console.log(`CSVレコード数: ${recordCount}`);
        // ここでデータベースに投入する処理など
      }

      // 処理ログをDynamoDBに記録
      await ddb.send(new PutItemCommand({
        TableName: process.env.DDB_TABLE_NAME!,
        Item: {
          pk: { S: `PARTNER#${partner}` },
          sk: { S: `FILE#${new Date().toISOString()}#${fileName}` },
          fileName: { S: fileName },
          fileSize: { N: String(size) },
          recordCount: { N: String(recordCount) },
          status: { S: 'PROCESSED' },
          processedAt: { S: new Date().toISOString() },
        },
      }));

      // 成功通知
      await sns.send(new PublishCommand({
        TopicArn: process.env.NOTIFICATION_TOPIC_ARN!,
        Subject: `[SFTP] ファイル処理完了: ${fileName}`,
        Message: JSON.stringify({
          partner,
          fileName,
          fileSize: size,
          recordCount,
          status: 'SUCCESS',
          processedAt: new Date().toISOString(),
        }),
      }));

    } catch (error) {
      console.error(`ファイル処理エラー: ${key}`, error);

      // エラー通知
      await sns.send(new PublishCommand({
        TopicArn: process.env.NOTIFICATION_TOPIC_ARN!,
        Subject: `[SFTP] ファイル処理エラー: ${key}`,
        Message: JSON.stringify({
          key,
          error: error instanceof Error ? error.message : 'Unknown error',
          timestamp: new Date().toISOString(),
        }),
      }));

      throw error;
    }
  }
}

カスタム認証の実装

API Gateway + Lambda認証

取引先ごとに異なる認証ロジック(例:IPアドレス制限、時間帯制限)を実装する場合、カスタム認証プロバイダーを使用します。

# カスタム認証サーバー
resource "aws_transfer_server" "sftp_custom_auth" {
  identity_provider_type = "API_GATEWAY"
  url                    = aws_api_gateway_deployment.transfer_auth.invoke_url
  invocation_role        = aws_iam_role.transfer_auth_invocation.arn
  protocols              = ["SFTP"]
  endpoint_type          = "PUBLIC"
  logging_role           = aws_iam_role.transfer_logging.arn
}
// lambda/custom-auth/index.ts
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
import { DynamoDBClient, GetItemCommand } from '@aws-sdk/client-dynamodb';

const ddb = new DynamoDBClient({});

export async function handler(event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> {
  const username = event.pathParameters?.username;
  const password = event.headers?.Password;
  const sourceIp = event.requestContext.identity.sourceIp;
  const protocol = event.queryStringParameters?.protocol;
  const serverId = event.queryStringParameters?.serverId;

  console.log(`認証リクエスト: user=${username}, ip=${sourceIp}, protocol=${protocol}`);

  // ユーザー情報を取得
  const userResult = await ddb.send(new GetItemCommand({
    TableName: 'sftp-users',
    Key: { username: { S: username! } },
  }));

  if (!userResult.Item) {
    console.log(`認証失敗: ユーザー不明 (${username})`);
    return { statusCode: 200, body: JSON.stringify({}) };
  }

  const user = userResult.Item;

  // IPアドレス制限チェック
  const allowedIps = user.allowedIps?.SS || [];
  if (allowedIps.length > 0 && !allowedIps.includes(sourceIp!)) {
    console.log(`認証失敗: IP制限 (${sourceIp} not in ${allowedIps})`);
    return { statusCode: 200, body: JSON.stringify({}) };
  }

  // 時間帯制限チェック(JST 9:00-18:00のみ許可の場合)
  if (user.timeRestriction?.BOOL) {
    const jstHour = new Date().getUTCHours() + 9;
    if (jstHour < 9 || jstHour >= 18) {
      console.log(`認証失敗: 時間帯制限 (JST ${jstHour}時)`);
      return { statusCode: 200, body: JSON.stringify({}) };
    }
  }

  // 認証成功
  console.log(`認証成功: ${username}`);
  return {
    statusCode: 200,
    body: JSON.stringify({
      Role: user.role.S,
      HomeDirectoryType: 'LOGICAL',
      HomeDirectoryDetails: JSON.stringify([
        {
          Entry: '/',
          Target: `/${user.bucket.S}/${username}`,
        },
      ]),
    }),
  };
}

セキュリティベストプラクティス

セキュリティポリシーの選択

AWS Transfer Familyは複数のセキュリティポリシーを提供しています:

ポリシー最小TLS暗号スイート推奨用途
TransferSecurityPolicy-2024-01TLS 1.2最新のみ本番環境推奨
TransferSecurityPolicy-2023-05TLS 1.2やや広い互換性重視
TransferSecurityPolicy-2020-06TLS 1.2レガシー含むレガシー対応

VPCエンドポイントでのプライベートアクセス

インターネット経由でなくVPC内部からのみアクセスする場合:

# VPCエンドポイントタイプのSFTPサーバー
resource "aws_transfer_server" "sftp_vpc" {
  identity_provider_type = "SERVICE_MANAGED"
  protocols              = ["SFTP"]
  endpoint_type          = "VPC"

  endpoint_details {
    subnet_ids         = var.private_subnet_ids
    security_group_ids = [aws_security_group.sftp.id]
    vpc_id             = var.vpc_id
  }
}

resource "aws_security_group" "sftp" {
  name_prefix = "sftp-"
  vpc_id      = var.vpc_id

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = var.allowed_cidr_blocks
    description = "SFTP access from trusted networks"
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

監査ログとアラート

# CloudWatch アラート(認証失敗の検知)
resource "aws_cloudwatch_metric_alarm" "sftp_auth_failure" {
  alarm_name          = "sftp-auth-failure-high"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 1
  metric_name         = "FilesIn"
  namespace           = "AWS/Transfer"
  period              = 300
  statistic           = "Sum"
  threshold           = 0
  alarm_description   = "SFTP認証失敗が多発しています"
  alarm_actions       = [aws_sns_topic.alerts.arn]

  dimensions = {
    ServerId = aws_transfer_server.sftp.id
  }
}

運用と監視

CloudWatchダッシュボード

{
  "widgets": [
    {
      "type": "metric",
      "properties": {
        "title": "ファイル転送量",
        "metrics": [
          ["AWS/Transfer", "BytesIn", "ServerId", "s-xxxx"],
          ["AWS/Transfer", "BytesOut", "ServerId", "s-xxxx"]
        ],
        "period": 3600,
        "stat": "Sum"
      }
    },
    {
      "type": "metric",
      "properties": {
        "title": "ファイル数",
        "metrics": [
          ["AWS/Transfer", "FilesIn", "ServerId", "s-xxxx"],
          ["AWS/Transfer", "FilesOut", "ServerId", "s-xxxx"]
        ],
        "period": 3600,
        "stat": "Sum"
      }
    }
  ]
}

SESエンジニアのキャリア戦略

Transfer Familyスキルの市場価値

B2Bファイル連携は「地味だが無くならない」分野であり、安定した需要があります:

ポジション月単価求められるスキル
インフラエンジニア70〜90万円Transfer Family構築、Terraform
インテグレーションエンジニア80〜110万円B2Bデータ連携、Lambda処理
セキュリティエンジニア90〜120万円暗号化設計、監査ログ分析

スキルの掛け合わせ

  • Transfer Family + Step Functions: 複雑なファイル処理ワークフローの構築
  • Transfer Family + EventBridge: イベント駆動のリアルタイム連携
  • Transfer Family + Glue: ETLパイプラインとの統合
  • Transfer Family + KMS: データ暗号化とコンプライアンス対応

AWS Transfer Family SFTP基盤の全体アーキテクチャ

まとめ

AWS Transfer Familyは、SFTPサーバーの運用負担をゼロにしながら、セキュアなB2Bファイル連携基盤を実現するサービスです。

  • フルマネージド: サーバー管理・パッチ適用・高可用性が自動
  • S3統合: ファイル保存先としてS3を使用し、Lambda連携で自動処理
  • 柔軟な認証: SSH鍵、Active Directory、カスタム認証に対応
  • セキュリティ: KMS暗号化、VPCエンドポイント、監査ログ

B2Bファイル連携のスキルは、金融・物流・製造業のSES案件で安定した需要があります。Transfer Familyを使いこなして、キャリアの幅を広げましょう。

💡 SES BASEでAWSインフラ案件を探す

SES BASEでは、AWSインフラ構築・B2Bインテグレーションの高単価案件を多数掲載。Transfer FamilyやTerraformのスキルを活かせる案件をチェックしましょう。

関連記事

SES案件をお探しですか?

SES記事をもっと読む →
🏗️

SES BASE 編集長

SES業界歴10年以上のメンバーが在籍する編集チーム。SES企業での営業・エンジニア経験、フリーランス独立経験を持つメンバーが、業界のリアルな情報をお届けします。

📊 業界データに基づく記事制作 🔍 IPA・経済産業省データ参照 💼 SES実務経験者が執筆・監修