AWS システム開発関連 パソコン関連

[AWS Lambda] 給与明細のPDFをS3に置いたら、自動でパスワード解除する仕組みを作ってみた

パスワード付きの給与明細PDFを、S3とLambda・qpdfで自動復号する流れを表したイラスト

給与明細が、毎月メールに添付されたパスワード付きPDFで送られてきます。開くたびにパスワードを入力するのも少し面倒ですし、いずれは明細の内容を家計簿にも取り込めるようにしたいところ。
せっかくなので、AWS Lambdaの学習がてら、PDFをS3にアップロードすると、自動でパスワードを解除して保存してくれる仕組みを作ってみました。今回は、作ったものと構築中に学んだことをまとめます。

⚠ 注意事項

設定内容やプログラムの内容は、用途・環境に応じて適切なものが変わります。

各種設定値や環境情報についてよく理解を深め、壊れてもよい環境で十分に検証してください。

なるべく正確に書くよう心掛けていますが、本投稿内容を実施される際には自己責任の下でお願いいたします。

まずは「S3に置いた後」を自動化する

最終的にはメールが届いたところから自動化したいのですが、いきなり全部をつなぐと確認することが増えそうです。
まずは、メールの添付PDFを手動でS3へアップロードし、その先の処理をLambdaに任せるところまでを作りました。

給与明細のメールが届く
  ↓ 添付PDFを保存し、手動でアップロード
S3:incoming/給与明細.pdf
  ↓ ファイル作成イベント
Lambda:qpdfで復号
  ↓ 自動で保存
S3:decrypted/給与明細.pdf

ここでいうパスワード解除は、自分が受け取った給与明細を、手元で分かっているパスワードを使って復号する処理です。
現在は1種類の固定パスワードを設定して使う構成にしています。

使ったもの

  • Amazon S3:元のPDFと復号後のPDFの保存先
  • AWS Lambda:S3へのアップロードをきっかけに処理を実行
  • Python 3.13/boto3:S3からのダウンロードとアップロード
  • qpdf:PDFの復号
  • Docker/Amazon ECR:qpdfを含む実行環境の作成と保管
  • IAM/CloudWatch Logs:実行権限の設定とログの確認

構築作業はWindows上のWSL2とDebianで進めました。
今回はPythonから外部コマンドのqpdfを実行するので、qpdfも一緒に入れたコンテナイメージをLambdaで動かす形にしています。

各サービスの連携図

各サービスの関係を、PDFを処理するときの流れと、実行環境をデプロイするときの流れに分けると、次のようになります。

PDFをアップロードしてからの流れ

[メール:給与明細PDFを添付]
          |
          | (1) PDFを保存して手動アップロード
          v
+--------------------------+
| Amazon S3                |
| incoming/給与明細.pdf     |
+--------------------------+
          |
          | (2) ファイル作成イベント
          v
+----------------------------------+
| AWS Lambda                       |
|                                  |
| Python / boto3                    |
|   (3) incoming/から取得(GetObject)    |
|          |                       |
|          v                       |
|   /tmp:入力PDF                |
|          |                       |
|          v                       |
|   qpdf + PDF_PASSWORD            |
|          |                       |
|          v                       |
|   /tmp:復号済みPDF            |
+----------------------------------+
          |                |
          | (4) 保存(PutObject)  | ログ
          v                v
+---------------------+  +-----------------+
| Amazon S3           |  | CloudWatch Logs |
| decrypted/          |  | 実行ログ        |
|   給与明細.pdf      |  +-----------------+
+---------------------+

図の上下にあるS3は同じバケットです。イベント通知でPDFそのものが渡されるわけではなく、通知を受けたLambdaがboto3で元のPDFを取得します。PDF_PASSWORDはLambdaの環境変数で、出力先のdecrypted/は今回の通知対象に含めていません。

コンテナイメージのデプロイと権限の関係

[Windows/WSL2/Debian]
          |
          | Dockerイメージをビルド
          v
[コンテナイメージ:Python + qpdf + ハンドラー]
          |
          | コンテナイメージを登録
          v
[Amazon ECR]
          |
          | イメージを指定して関数を作成・更新
          v
[AWS Lambda] --------------------> [S3/CloudWatch Logs]
          ^     実行ロールを使ってアクセス
          |
          | 実行ロール
         [IAM]

[S3イベント] -------------------> [AWS Lambda]
            呼び出し許可:
            Lambdaのリソースベースポリシー

ECRは実行用イメージの保管先で、給与明細PDFの保存先はS3です。LambdaがS3やCloudWatch Logsへアクセスするための実行ロールと、S3からLambdaを呼び出すための許可は、図のように別々に設定しています。

qpdfを入れたコンテナをLambdaで動かす

Dockerfileはかなり短く、Lambda用のPythonベースイメージにqpdfをインストールし、処理用のPythonファイルをコピーしています。

FROM public.ecr.aws/lambda/python:3.13

RUN dnf install -y qpdf \
    && dnf clean all

COPY lambda_function.py ${LAMBDA_TASK_ROOT}

CMD ["lambda_function.lambda_handler"]

最初はPDFの処理まで入れず、qpdfのバージョンを返すだけの関数で動作確認しました。
コンテナの中で動くことを確かめてからECRへ登録し、Lambda上でも実行できることを確認。
その後、S3からPDFを取得する処理を追加しています。


Lambdaでコンテナイメージを利用する流れは、AWS公式ドキュメントにもまとまっています。
イメージをECRに置き、そのイメージをLambdaの実行環境として使う構成です。

Lambdaの中でやっていること

処理そのものはシンプルです。S3イベントからバケット名とファイルのキーを取得し、incoming/配下のPDFだけを対象にします。そのPDFをLambdaの/tmpへダウンロードし、qpdfで復号。
できたファイルを同じバケットのdecrypted/へアップロードします。

パスワードはコードへ直接書かず、Lambdaの環境変数PDF_PASSWORDから取得しています。
復号にはqpdfの--password--decryptを使い、Pythonのsubprocess.run()で実行しています。

アップロード元のPDFはそのまま残るので、S3にはパスワード付きの元ファイルと復号済みファイルがそれぞれ保存されます。

コード以外の設定も勉強になった

今回、整理する必要があったのがそれぞれのサービスが利用する権限の内容です。
「LambdaがS3を読み書きする権限」と「S3がLambdaを呼び出す権限」をそれぞれ設定します。
関数を作って通知先に指定するだけではなく、サービス同士をどうつなぐのかも含めて、よい練習になりました。

もう一つは、S3の通知対象を絞ること。
今回は入力をincoming/、出力をdecrypted/に分け、incoming/配下の.pdfだけでLambdaを起動するようにしています。
出力したファイルでもLambda関数が起動すると処理が繰り返され無限ループになってしまうため結構重要です。
権限と通知の考え方は、S3イベントとLambdaについてのAWS公式ドキュメントを参照しながら作業しました。

S3にアップロードしたら、復号済みPDFができた

構築時には、パスワード付きPDFをincoming/にアップロードし、Lambdaが起動してdecrypted/にファイルが生成されるところまで確認できました。
処理の途中経過はCloudWatch Logsで確認しています。

まだメールから添付ファイルを取り出すところは手動ですが、「S3に置くと、その先は勝手に処理される」という形にはなりました。
身近な作業を題材にすると、Lambdaをどこで使うのかがイメージしやすくなりますね。

料金はどのくらいかかる?

今回の用途は、基本的に月1回、給与明細のPDFを処理する程度です。常時サーバを動かす構成ではないので実行回数は少ないのですが、Lambdaの実行料だけでなく、PDF・コンテナイメージ・ログを保存する料金も考える必要があります。

サービス主な課金対象今回、見ておきたいところ
AWS Lambdaリクエスト数、割り当てメモリ量と実行時間月1回の処理に加え、構築中のテストや再実行も利用量に含まれる
Amazon S3保存容量、PUT・GETなどのリクエスト、条件に応じたデータ転送元のPDFと復号済みPDFの両方を残すので、保存期間が長いほど容量が増える
Amazon ECRコンテナイメージの保存容量、条件に応じたデータ転送Lambdaを実行しない月でも、イメージを保管していれば保存料の対象になる
CloudWatch Logsログの取り込み、保存、Logs Insightsなどでの分析ログの量と保持期間。調査でログを分析する場合は、その利用量も確認する

月1回の処理を仮定してみる

今回の実測値ではありませんが、Lambdaのメモリを512MBに設定し、課金対象の実行時間が1回10秒、月1回の実行だったとすると、使用量は0.5GB × 10秒 × 1回 = 5GB秒/月です。

2026年9月3日に確認したLambdaの公式料金ページでは、月100万リクエストと40万GB秒の無料枠が案内されています。
現状他にLambdaを動かしているわけではないので、無料枠の範囲内で動かせそうです。

一方、ECRのプライベートイメージの保存料は、公式料金ページ1GBあたり月0.10米ドルと案内されています。
仮に課金対象の保存容量が1GBで、それを1か月保管すると、無料枠やクレジットを差し引く前の保存料は0.10米ドルです。
これはECRの保存料だけの例で、S3やログなどを含めた合計額ではありません。

ここで使う容量は、ローカルのDockerで表示されるイメージサイズではなく、ECR側の保存量で確認します。
今回のように実行頻度が低い用途では、処理そのものの料金に加えて、イメージを置き続ける費用を見ておくとよさそうです。

使い続けるなら、保存するものも整理する

費用を抑えるためにも、不要になったECRの古いイメージを整理し、S3のPDFとCloudWatch Logsの保持期間を決めておきたいところです。
特に、構築中に何度もビルド・テストした月は、普段の月1回運用とは利用量が変わります。
上の内容は2026年9月3日時点の公式情報をもとにした目安で、実際の請求実績ではありません。
メール取得やPDFの読み取り、家計簿アプリとの連携を追加する際には、その部分の費用も別途見積もるつもりです。

今の実装で残っていること

まず動くところを優先したので、継続して使うために整えたい点もあります。
例えば、出力先には元のファイル名だけを使っているため、同じ名前のPDFをアップロードすると復号済みファイルが上書きされます。
年月を含めた保存先にするなど、給与明細を蓄積する前に見直したいところです。

また、/tmpの一時ファイルの削除、処理に失敗したときの通知、パスワードの管理方法も今後の改善項目です。
給与明細を扱うので、S3のアクセス範囲と、復号したPDFをどのくらい残すかも含めて運用を整えていきたいと思います。

最終的には、メール受信から家計簿への連携まで

やりたいのは、給与明細のメールが届いたら、自動で添付PDFを取り出し、パスワードを解除して、必要な項目を家計簿アプリへ渡すところまでです。

【今後作りたい流れ】
給与明細のメールを受信
  ↓
対象メールから添付PDFを取得
  ↓
S3へ自動アップロード
  ↓
Lambdaで復号(今回作成した部分)
  ↓
PDFから支給額・控除額などを抽出
  ↓
内容を確認して家計簿アプリへ連携

メールの取得方法や家計簿アプリへの連携方法はこれから検討します。
PDFの内容を読み取る処理もまだ作っていません。
まずは抽出した金額を確認できるようにして、同じ明細を二重に登録しない仕組みも考えながら、少しずつつないでいくつもりです。

毎月のパスワード入力を少し楽にしたい、というところから始めましたが、メールと家計簿までつながると、なかなか便利になりそうです。
今回はその最初の一歩として、S3とLambdaでPDFを処理する部分を作ってみました。

ソースコード

今回のコードはGitHubで公開しています。LambdaのPythonコード、Dockerfile、AWS設定用のJSONひな形を置いています。
GitHub:k636174/pdf-decryppt-lambda

-AWS, システム開発関連, パソコン関連
-, , ,