2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ファイル処理を自動化して業務を変える――AWS Lambda で作る実践的 Excel 統合システム

Last updated at Posted at 2025-12-01

あこちらの記事は Excel業務をDX化したい。あなたならどうする? by MESCIUS Advent Calendar 2025 の2日目の記事です。


Excel DX(デジタルトランスフォーメーション)とは、「人手によるExcel作業を、システムが裏で自動化し、最終的に“必要なときだけ開くExcel”に変える」取り組みです。本記事では、AWSを活用して“複数ファイルを解析し、1つのExcelに統合する処理系”を、実践的な視点で解説します。


はじめに:Excel DX が必要な理由

多くの現場では、CSVやPDF、写真付き報告書など「バラバラの形式のデータ」を最終的にExcelへまとめています。しかし、この統合作業は担当者の手作業に頼りがちで、以下のような問題を抱えています。

  • フォーマットの差異調整に時間がかかる
  • コピペミス・変換ミスが頻発する
  • 更新履歴が追えない

この記事では、これらの課題を「サーバーレス+自動化」で根本から解決する方法を紹介します。


具体例:Excel DX の現場での活用

ここでは、実際の業務でよくある2つのケースを例に、Excel DX の効果と処理系の全体像を解説します。

① 退勤表を自動集計する(S3 → Lambda → DynamoDB/RDS → Excel)

ユースケース:部署ごとの「退勤表.xlsx」「打刻履歴.csv」を毎月アップロードすると、全社集計Excelが自動で出力される。

処理フロー(例)

S3 プレフィックス設計

  • inbound/attendance/{yyyyMM}/{dept}/退勤表_*.csv
  • export/attendance/attendance_{yyyyMM}.xlsx
  • logs/attendance/{run_id}.json(検出エラー・件数など)

データモデル(どちらか)

  • DynamoDB: PK=EMP#{employee_id}, SK=DATE#{yyyymmdd}、属性:work_minutes, overtime_minutes, source_bucket, source_key, row_hash

  • RDS(PostgreSQL):

    CREATE TABLE attendance (
      employee_id TEXT NOT NULL,
      work_date DATE NOT NULL,
      clock_in TIME, clock_out TIME,
      work_minutes INT, overtime_minutes INT,
      row_hash TEXT NOT NULL,
      source_bucket TEXT, source_key TEXT,
      created_at TIMESTAMPTZ DEFAULT now(),
      updated_at TIMESTAMPTZ DEFAULT now(),
      PRIMARY KEY (employee_id, work_date)
    );
    

バリデーション例

  • 必須:employee_id, work_date, clock_in, clock_out
  • ルール:clock_out >= clock_inwork_minutes <= 24*60
  • 行スキップ/隔離:違反行は quarantine/attendance/{run_id}.csv に退避

Excel出力の設計

  • シート:RawData(全件)/ Summary(社員別合計)/ OvertimeTop20
  • 書式:ヘッダ固定、部署名フィルタ、残業閾値で条件付き書式
  • ファイル名:attendance_{yyyyMM}.xlsx(バージョン管理したい場合は _run{run_id} 付与)

IAM の最小権限の目安

  • L1 (Lambda: attendance_ingest): s3:GetObject(inbound), dynamodb:BatchWriteItem or rds-data:ExecuteStatement
  • L2 (Lambda: attendance_export): s3:PutObject(export), 読み取り系のDB権限

運用ノート

  • 退勤表テンプレートを共通配布して列名揺れを抑止(Lambda側の COLUMN_MAP と二重対策)
  • 部署追加時はマスタに部署コードを追加 → Excelのドロップダウンも自動反映

② インターネットバンキングの取引履歴を統合(S3 → Step Functions → Lambda → DB → Excel)

ユースケース:複数銀行のCSVを取り込み、収支台帳Excelを月次で自動生成する。

ワークフロー(例)

S3 プレフィックス設計

  • inbound/banking/{bank}/{yyyyMM}/{account}/transactions_*.csv
  • export/banking/{account}_book_{yyyyMM}.xlsx

データモデルの例(DynamoDB 版)

銀行取引明細は、「口座ごと」「日付ごと」に複数の取引が記録される構造です。
そのため、DynamoDBでは以下のように複合キー設計を採用します。

🔹 テーブル設計

項目名 説明
PK(パーティションキー) ACCT#0012345678 口座IDを表します。すべての取引はこの口座の下に紐づきます。
SK(ソートキー) DATE#20251105#TS#0001 取引日+連番(または時刻)を連結。日付順で自動ソートされます。
desc "給与振込 ABC株式会社" 取引内容(摘要)。
debit 50000 出金額(マイナス側)。
credit 300000 入金額(プラス側)。
balance 1450000 残高。
row_hash sha256(...) 同一取引の重複登録を防ぐためのハッシュ。
source_bucket / source_key S3のパス情報 どのCSVファイルから取り込まれたかのトレース情報。

🔹 GSI(グローバルセカンダリインデックス)

日別・月別の収支を照会しやすくするため、以下のような GSI を追加します。

GSI名 パーティションキー ソートキー 用途
GSI1 ACCT#0012345678 DATE#20251105 特定の口座の特定日付に属するすべての取引を一括取得。

これにより、

  • 「口座ごとに日付順で全取引を表示」
  • 「指定日付の入出金を集計」
    といったクエリが高速に実行できます。

🔹 クエリ例(Python boto3)

# 特定口座の2025年11月5日の全取引を取得
resp = ddb.query(
    TableName="bank_tx",
    IndexName="GSI1",
    KeyConditionExpression="GSI1PK = :acct AND begins_with(GSI1SK, :date)",
    ExpressionAttributeValues={
        ":acct": {"S": "ACCT#0012345678"},
        ":date": {"S": "DATE#20251105"}
    }
)

🔹 モデル構造のポイント

  • 階層構造を文字列で表現することで、日付・時刻で自動的に並び替え可能。
  • 口座単位でのクエリ最適化:1口座=1パーティションとなり、アクセス分離が明確。
  • 冪等性(idempotency)row_hash で同一取引の重複登録を防止。
  • トレース性source_bucket / source_key で、元CSVファイルを特定可能。

Excel出力の設計(銀行取引履歴の自動帳票)

出力されるExcelは、現場でそのまま集計・印刷・共有できることを目的に設計します。
会計担当者や経理スタッフが、従来の「入出金帳簿」をそのまま置き換えられるような構成です。

🔹 シート構成

シート名 役割 内容例
Transactions(明細) すべての取引履歴を時系列に表示 日付・摘要・出金・入金・残高など。フィルタ付きで並べ替え・絞り込み可能。
DailyNet(日別収支) 日ごとの合計入金・出金・差引を集計 グラフの元データとしても利用。
MonthlySummary(月次集計) 月単位での合計収支や平均残高を集計 年間推移を確認するための要約。

💡 ポイント:
銀行ごと・月ごとにファイルを分けて出力することで、Excel の 100 万行制限にかからず、保守性も高まります。


🔹 書式設定

  1. 金額列はカンマ区切り(#,##0) で見やすく整形。
  2. 負数(出金額)は赤文字表示Conditional Formatting で自動適用。
  3. 取引日・摘要にオートフィルターを設定し、日付範囲やキーワードで即検索可能。
  4. 残高列は右寄せ&太字 にし、帳簿らしい見た目に。
  5. ヘッダー行(1行目)は固定してスクロール時も見失わないように設定。
# openpyxlでの書式適用イメージ
from openpyxl.styles import Font, Alignment, PatternFill, numbers
ws["D:D"].number_format = "#,##0"      # 金額列のカンマ区切り
ws["D:D"].font = Font(color="000000")  # 通常黒
# 負数を赤にする条件付き書式も設定可能

🔹 グラフ構成

  • 折れ線グラフ(DailyNetシート)
    日ごとの入金・出金の推移を折れ線で可視化。
    「今月の資金の増減トレンド」がひと目で分かるようになります。

  • 円グラフ(カテゴリ別)
    摘要(desc)列をもとに「給与」「家賃」「光熱費」「その他」などに分類して円グラフを生成。
    出費の割合を視覚的に把握できます。

(摘要のカテゴリ化は辞書定義 category_map.json を用意して自動分類しておくと便利です)


🔹 出力ファイルの例

exports/banking/
 ├── MUFG_book_202510.xlsx
 ├── JA_book_202510.xlsx
 └── JP_book_202510.xlsx
  • ファイル名には銀行コードと年月を付与({bank}_book_{yyyymm}.xlsx
  • すべてのファイルは S3 に保存し、署名付きURL で共有。
  • ダウンロード後は即印刷・会議資料として使える状態。

セキュリティとガバナンス

取引履歴のような金融データを扱う場合、セキュリティ対策と権限設計は必須です。
ここをおろそかにすると、漏えいや誤操作のリスクが高まります。

  1. アップロード権限の分離
    各担当者は自分の銀行・口座フォルダにしかアップロードできないよう、
    S3バケットポリシーと IAM ポリシーで プレフィックス単位のアクセス制御 を設定します。
    例:

    inbound/banking/mufg/ → MUFG担当者のみ
    inbound/banking/ja/   → JA担当者のみ
    

    これにより、他銀行のデータを誤って上書きするリスクを防げます。

  2. データソースの健全性
    銀行サイトのスクレイピングや非公式API利用は厳禁です。必ず各行が提供する 正規のダウンロードCSV を使用します。スクレイピングは利用規約違反や法令リスクを伴うため、公式CSV以外のデータは自動処理対象外とするのが安全です。

  3. 署名付きURLの有効期限管理
    Excel出力を共有する際は 署名付きURL(presigned URL) を発行しますが、URLの有効期限は短めに設定(1〜24時間以内が目安)。必要な期間を過ぎたURLは自動的に無効化されるため、閲覧可能期間を最小化して情報漏えいリスクを下げられます。


障害発生時のハンドリング

自動処理で取り込みエラーが発生した場合も、安全に止まり、再実行できる仕組みが重要です。

  1. 異常データの隔離(Quarantine)
    CSVファイルに列抜け・金額の不正値・フォーマット崩れなどがあった場合、Lambda はその行をスキップし、該当行を

    quarantine/banking/{run_id}.csv
    

    に退避します。こうしておくと、後で担当者がファイルを確認・修正し、再アップロードするだけで再処理が可能になります。

  2. 重複登録の防止(Idempotency)
    各取引行には内容に基づく row_hash(SHA256)が付与されています。これにより、同じファイルや同じ取引を再度取り込んでも、既存データとのハッシュ一致でスキップされ、重複登録が起こりません。手動再処理やリトライを行っても安全です。


どっちを選ぶ? Lambda vs その他基盤 早見表

要件 / 特徴 Lambdaで十分 ECS / Batch / Fargate が適す
ファイルサイズ / 件数 数十MB、数万行 数百MB〜GB、数百万行
処理時間 15分以内(1回のLambda上限) 長時間(30分〜数時間)
依存ライブラリ 軽量 (pandas, openpyxl) 重いネイティブ依存(LibreOffice, OCR等)
処理タイミング S3イベントや定時実行 複雑な依存関係のあるワークフロー
同時実行数 数十〜数百関数並列 大量並列ジョブ(1000以上)
目的 定期帳票・軽量レポート 大規模バッチ・複雑なデータパイプライン
コスト管理 実行時間課金で安価 常時稼働・大量データで安定動作

👉 目安:月次退勤表・取引履歴・日報集計のような中小規模業務なら Lambda が最適。大規模分析や長時間処理には ECS/Fargate などを選びましょう。

判断の流れ図(選定フローチャート)


おすすめの始め方(ステップバイステップ導入ガイド)

  1. S3 にファイルを集める仕組みを作る — 命名規則とアップロード手順を統一する。
  2. Lambda で解析ロジックを試作 — まずは1ファイル単位でJSON出力を確認。
  3. DynamoDB/RDS へ登録 — 正規化して履歴と監査列を持たせる。
  4. Excel 出力のテンプレート化 — 既存フォーマットをテンプレートにして出力を自動化。
  5. 自動実行と監視を整える — EventBridge+CloudWatch+DLQで安心運用。

導入後に得られる効果(定量的・定性的メリット)

項目 Before After
ファイル統合作業 1〜2時間/回 数分で自動化
グラフ作成 手動コピー 自動テンプレート生成
月次レポート 1〜2人日 数十分以内
エラー 頻発 自動検証でゼロに近づく

定性的な効果

  • データの信頼性が高まり、属人化が解消。
  • 担当者が「集計」から「分析・判断」へシフト。
  • 組織全体でデータ活用文化が育つ。

付録:テンプレート設計・スケジュール・Step Functions・SAM スニペット

A. 部署共通テンプレート template.xlsx の中身(最低限)

  • シート構成
    • RawData:出力専用(データ直書き)
    • Summary:集計表(ピボット or 関数)
    • Masters:ドロップダウン用マスタ(部署名・活動項目など)
  • 名前定義DeptList=Masters!$A$2:$A$100 のように範囲名を定義
  • 列幅/体裁:見出し1行を太字・背景薄灰、A1:Z1 にフィルター、A2 でフリーズ
  • 印刷:余白=狭い、横向き、1ページに収める、ヘッダにファイル名/日付
  • 条件付き書式例:残業閾値 > 480 分で背景色

Python(openpyxl)でのデータ検証ドロップダウン追加例

from openpyxl import load_workbook
from openpyxl.worksheet.datavalidation import DataValidation

wb = load_workbook("template.xlsx")
ws = wb["RawData"]
# DeptList は Names に登録済み(MastersシートA2:A100想定)
dv = DataValidation(type="list", formula1="=DeptList", allow_blank=True)
ws.add_data_validation(dv)
dv.add("B2:B10000")  # B列に部署ドロップダウン
wb.save("template.xlsx")

B. スケジュール(EventBridge)例:日次・月次(JST)

  • 日次 03:00(JST)cron(0 18 * * ? *)
    ※EventBridgeはUTC。JST 03:00 = UTC 18:00(前日)。
  • 毎月1日 06:30(JST)cron(30 21 28-31 * ? *) + ルール内で「翌月1日かチェック」ロジック or Step Functions で判定
  • rate式(簡易):rate(1 day)

SAM での定義断片

AttendanceExportRule:
  Type: AWS::Events::Rule
  Properties:
    ScheduleExpression: "cron(0 18 * * ? *)"  # JST 03:00
    Targets:
      - Arn: !GetAtt AttendanceExportFunction.Arn
        Id: AttendanceExport

C. Step Functions(ASL)定義スニペット(銀行取引の並列処理)

{
  "Comment": "Banking Ingest",
  "StartAt": "ListFiles",
  "States": {
    "ListFiles": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Parameters": {"FunctionName": "list_s3_keys", "Payload.$": "$"},
      "ResultPath": "$.keys",
      "Next": "ForEach"
    },
    "ForEach": {
      "Type": "Map",
      "ItemsPath": "$.keys",
      "MaxConcurrency": 10,
      "Iterator": {
        "StartAt": "DetectBank",
        "States": {
          "DetectBank": {
            "Type": "Task",
            "Resource": "arn:aws:states:::lambda:invoke",
            "Parameters": {"FunctionName": "detect_bank", "Payload.$": "$"},
            "ResultPath": "$.bank",
            "Next": "Normalize"
          },
          "Normalize": {
            "Type": "Task",
            "Resource": "arn:aws:states:::lambda:invoke",
            "Parameters": {"FunctionName": "normalize_bank", "Payload.$": "$"},
            "ResultPath": "$.normalized",
            "Next": "UpsertDB"
          },
          "UpsertDB": {
            "Type": "Task",
            "Resource": "arn:aws:states:::lambda:invoke",
            "Parameters": {"FunctionName": "upsert_tx", "Payload.$": "$"},
            "End": true
          }
        }
      },
      "Catch": [{"ErrorEquals": ["States.ALL"], "ResultPath": "$.error", "Next": "NotifyDLQ"}],
      "Next": "ExportExcel"
    },
    "ExportExcel": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Parameters": {"FunctionName": "export_month_book", "Payload.$": "$"},
      "End": true
    },
    "NotifyDLQ": {
      "Type": "Task",
      "Resource": "arn:aws:states:::sns:publish",
      "Parameters": {"TopicArn": "arn:aws:sns:...:dlq-notify", "Message.$": "$.error"},
      "End": true
    }
  }
}

D. SAM テンプレート最小例(Function / Role / Layer)

Globals:
  Function:
    Runtime: python3.12
    Timeout: 900
    MemorySize: 2048
    Tracing: Active

Resources:
  PandasLayer:
    Type: AWS::Serverless::LayerVersion
    Properties:
      ContentUri: layers/pandas_openpyxl/
      CompatibleRuntimes: [python3.12]

  AttendanceIngestFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/attendance_ingest/
      Handler: app.handler
      Layers: [!Ref PandasLayer]
      Policies:
        - S3ReadPolicy:
            BucketName: !Ref InboundBucket
        - DynamoDBCrudPolicy:
            TableName: !Ref AttendanceTable
      Events:
        S3Put:
          Type: S3
          Properties:
            Bucket: !Ref InboundBucket
            Events: s3:ObjectCreated:*
            Filter:
              S3Key:
                Rules:
                  - Name: prefix
                    Value: inbound/attendance/

  AttendanceExportFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/attendance_export/
      Handler: app.handler
      Layers: [!Ref PandasLayer]
      Policies:
        - S3WritePolicy:
            BucketName: !Ref ExportBucket
        - DynamoDBReadPolicy:
            TableName: !Ref AttendanceTable

  InboundBucket:
    Type: AWS::S3::Bucket
  ExportBucket:
    Type: AWS::S3::Bucket
  AttendanceTable:
    Type: AWS::DynamoDB::Table
    Properties:
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
        - AttributeName: pk
          AttributeType: S
        - AttributeName: sk
          AttributeType: S
      KeySchema:
        - AttributeName: pk
          KeyType: HASH
        - AttributeName: sk
          KeyType: RANGE

IAM 最小権限の考え方

  • 取り込み関数:s3:GetObject(接頭辞で制限), dynamodb:BatchWriteItem, dynamodb:PutItem
  • 出力関数:s3:PutObject(export配下に限定), dynamodb:Query/Scan

E. 運用ランブック(抜粋)

  1. 失敗の検知:CloudWatch アラーム(Errors > 0 / Throttles > 0)→ SNS 通知
  2. 再処理手順quarantine/yyyymmdd/*.csvstaging/ へ再配置 → 再実行Lambda起動
  3. スキーマ変更parser_version を上げ、過去データは移行スクリプトで補正
  4. パフォーマンス調整:Lambdaメモリ増でCPU向上、Step FunctionsのMaxConcurrencyで並列度を制御

まとめ:Excel DX は「脱・手作業」から「知の自動化」へ

Excelは日本の現場で最も使われている“情報の出口”です。しかし、その出口を自動化すると、組織全体の時間と集中力が一気に解放されます。

人がExcelを作る時代から、Excelが人の仕事を支える時代へ。

ファイル解析 → データ正規化 → Excel出力の自動化は、単なる効率化ではなく、「業務を見える化し、再現性ある知識に変える」ための第一歩です。あなたのチームでも今日から Excel DX を始めてみませんか?

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?