はじめに
こんにちは。GMOグローバルサイン・ホールディングスのCTO室の吉澤です。
昨今のWeb開発では、GitHub Actionsなどを用いたCI/CDが当たり前になっていますが、レガシーな環境や共用サーバーなど、「デプロイ手段がFTPしかない」というケースに遭遇することも少なくありません。Linux環境であれば .netrcファイルとアクセス権の設定で自動化することもありますが、Windows環境で標準のftp.exeでは通常、パスワードを平文でファイルに保存する必要があり、また暗号化通信(FTPSなど)にも対応しておらず、セキュリティ面に不安が残ります。
そこで今回は、AI(LLM)との対話を通じて構築した、PowerShellによる安全かつセキュアなFTP自動デプロイ環境の構築方法をご紹介します。
安全面への配慮
自動化において最も懸念されるのが「認証情報の管理」です。Linuxの .netrc は非常に便利ですが、パスワードが平文で保存されるため、ファイルが流出した際のリスクがあります。
今回採用したPowerShellの手法では、Windows標準の暗号化技術であるDPAPI(Data Protection API) を活用します。具体的には、Export-Clixmlというコマンドレットを使用し、以下の特性を持たせています。
- ユーザー特定の暗号化: 作成したWindowsユーザーアカウント本人しか復号できません。
- マシン特定の暗号化: ファイルを別のPCにコピーしても、他のPCでは読み取ることができません。
これにより、万が一設定ファイルが外部に漏れたとしても、第三者にパスワードを盗み見られるリスクを極限まで抑えています。
構築手順
下記を実行することで、AIエージェントからFTPサーバーへのアップロードが可能になります。
- PowerShellスクリプトの設置
- 認証情報の暗号化と保存
- 接続先FTPサーバーの設定
- Skillsの生成
PowerShellスクリプトの設置
FTPサーバーへのアップロードを行うPowerShellスクリプトをローカルの任意のディレクトリに設置します。スクリプトは下記Gitリポジトリからダウンロードできます。
FTPアップロードスクリプト
- FTPS用のスクリプト:
ftps_upload.ps1 - FTP用のスクリプト:
ftp_upload.ps1
※PowerShellのスクリプトの文字コードはShift-JISになります。
接続先FTPサーバーの設定
接続先のFTPサーバー情報を設定ファイルftp_settings.jsonに記述します。内容は下記です。ファイルはスクリプトと同じディレクトリに設置してください。
{
"FtpServer": "ftp.example.com"
}
認証情報の暗号化と保存
まず、PowerShellを起動します。powershell とスタートメニューから検索して起動してください。
次に、以下のコマンドを実行してFTPサーバーの認証情報を安全に保存します。Get-Credential | Export-Clixml -Path ".\ftp_cred.xml"
コマンドを実行すると、ユーザー名とパスワードの入力ダイアログが表示されます。FTPの認証情報を入力してください。入力後、同じディレクトリに ftp_cred.xml というファイルが生成されます。ファイルにはパスワードが暗号化された状態で保存されており、セキュリティが確保されています。ファイルはスクリプトと同じディレクトリに設置してください。

※
私の環境だと、ダイアログが表示されない現象が見られました。その時は、Windowsに再ログインすると直りました。
Skillsの生成
Webアプリのプロジェクト内にSkillsを設置します。私はAIエージェントにSkillsを生成・設置してもらいました。その際のプロンプトは下記です。
このプロジェクトのディレクトリに置かれたファイルを、PowerShellを使ってアップロードするスキルを作って欲しいです。コマンド例は下記です。
スキルに、隠しファイルはアップロードしないよう、記述してください。
ドキュメントルートの、`index.html`をアップロードする場合:
powershell -ExecutionPolicy Bypass -File "C:\Users\<YourUsername>\Documents\FTPCoding\ftps_upload.ps1" -RemoteDir "/" -LocalFile ".\index.html"
サブディレクトリ `/assets/img` 内の `sample.jpg` をアップロードする場合:
powershell -ExecutionPolicy Bypass -File "C:\Users\<YourUsername>\Documents\FTPCoding\ftps_upload.ps1" -RemoteDir "/assets/img/" -LocalFile ".\assets\img\sample.jpg"
※参考として、私の環境で生成されたスキルをTips(後述)に記載します。
使い方
「~をアップロードして」 といった指示をAIエージェントに与えると、FTPサーバーへのアップロードが自動で行われます。コンテンツを全てアップロードしたり、更新されたファイルのみアップロードする指示も、私の環境(Github Copilot + Codex5.3)では受け付けてくれました。
注意事項
- アップロードされてはならないファイルが無いか、十分にご注意ください。
- FTPなので、上書きアップロードされます。
- FTPはパスワードが平文で送信されるため、FTPSなどの安全なプロトコルをご利用ください。
Tips
DPAPIについて
特徴
DPAPIの最大の特徴は、暗号化・復号するための「鍵(キー)」をユーザーが用意する必要がないことです。代わりに、以下の2つの情報を組み合わせてWindowsが自動的に鍵を生成します。
- 現在ログインしているWindowsユーザーのアカウント情報
- 実行しているPC本体(システム)の情報
なぜ安全なのか?
- ファイルが盗まれても安全
万が一 ftp_cred.xml をUSBメモリなどで盗み出され、別のPCにコピーされたとしても、PC環境が異なるため復号(パスワードの読み取り)ができません。- ActiveDirectory環境下で移動ユーザープロファイルが有効な場合を除く
- 同じPCの別のユーザーからも安全
同じPCを使っている別のWindowsユーザー(例えば同僚や家族など)がこのXMLファイルを開こうとしても、ユーザーアカウントが異なるため復号できません。 - スクリプト内に「鍵」を書かなくてよい
独自の暗号化方式を使うと「暗号化を解くためのパスワード(鍵)」自体をスクリプト内に書かなければならず、それが漏洩の原因になりがちですが、DPAPIはその弱点を克服しています。
スキルの例(.github/skills/upload-web-files/SKILL.md)
---
name: upload-web-files
description: "Use when: プロジェクトのファイルをPowerShellでアップロードしたい、FTPアップロードを実行したい、ftps_upload.ps1を使ってファイル送信したい"
---
# Web Files Upload (PowerShell)
このスキルは、プロジェクトのファイルを PowerShell スクリプト経由でアップロードするときに使用します。
## 目的
- ディレクトリ内の対象ファイルを選ぶ
- `ftps_upload.ps1` を使ってアップロードする
- 失敗時に原因を特定しやすい形で実行する
## 前提
- PowerShell が利用可能
- アップロードスクリプトが存在する
- `C:\Users\<YourUsername>\Documents\FTPCoding\ftps_upload.ps1`
- 対象ファイルが存在する
## 手順
1. 対象ファイルがあることを確認する。
2. アップロード先ディレクトリ(例: `/`)を決める。
3. 以下の形式でコマンドを実行する。
`powershell -ExecutionPolicy Bypass -File "C:\Users\<YourUsername>\Documents\FTPCoding\ftps_upload.ps1" -RemoteDir "/<dir>/" -LocalFile ".\<dir>\<filename>"`
## 実行例
`powershell -ExecutionPolicy Bypass -File "C:\Users\<YourUsername>\Documents\FTPCoding\ftps_upload.ps1" -RemoteDir "/" -LocalFile ".\index.html"`
`powershell -ExecutionPolicy Bypass -File "C:\Users\<YourUsername>\Documents\FTPCoding\ftps_upload.ps1" -RemoteDir "/assets/img/" -LocalFile ".\assets\img\sample.jpg"`
## 運用ルール
- 既定では `-RemoteDir "/"` を使う。必要時のみ変更する。
- `-LocalFile` は必ず対象ファイルのパスを指定する。
- 隠しファイルはアップロードしない。
- 先頭が `.` のファイル(例: `.env`)を除外する。
- Windows の Hidden 属性を持つファイルを除外する。
- ファイル名に空白がある場合は `-LocalFile` の値を必ず二重引用符で囲む。
- 実行前に対象ファイルの存在確認を行う。
## トラブルシュート
- `Cannot find path` が出る場合:
- カレントディレクトリがプロジェクトルートか確認する。
- 対象ファイルのパスが正しいか確認する。
- `File not found` が出る場合:
- `ftps_upload.ps1` の配置場所を確認する。
- 認証エラーが出る場合:
- `ftps_upload.ps1` 内の接続設定を確認する。