Access VBA を JavaScript / Power Fx / Apex に変換する完全ガイド:Claude Code 半自動変換手法
目次 クリックで開く
Access VBA をモダン言語へ変換する完全ガイド ── 5言語マッピング・実コード25パターン・Claude Code自動化
執筆: Koki Soga(Aurant Technologies アーキテクト・コンサルタント) 最終更新: 2026-05-15
Access の刷新プロジェクトで最後まで残る最大の壁が VBA コードの移行です。フォームやテーブルは設計図に近いため、移行先のスキーマに写し直せば動きます。一方 VBA は業務ロジックそのものが書き込まれており、機械的な変換だけでは仕様の取りこぼしが必ず発生します。
本記事では、VBA から kintone JavaScript / Power Fx / Apex / Python / C#(.NET) の 5 言語への変換を、25 個の Before/After 実コード片で示します。後半では Claude Code を使って変換作業の大半を機械化する手順と、変換特化のプロンプト 5 本を公開します。
1. VBA の特性と「そのまま移行」が失敗する3つの理由
VBA は単なる「古い言語」ではありません。Access フォームと密結合した独自の実行モデルを持っており、機械的なシンタックス変換だけでは動かない箇所が必ず残ります。失敗の典型は次の3つです。
理由1:イベント駆動の暗黙コンテキストに依存している Form_BeforeUpdate のように、フォームの現在のレコード・コントロールが暗黙的に参照されます。これは Power Fx や JavaScript ではイベントオブジェクト経由、Apex や C# では明示的なバインドに置き換える必要があります。
理由2:データ操作と画面操作が同じプロシージャに混在している VBA では DLookup や db.Execute のような DB アクセスと MsgBox のような画面操作が 1 つの Sub に混ざりがちです。モダン環境ではレイヤー分離(UI・業務ロジック・データ)が前提のため、構造ごと書き直す必要があります。
理由3:エラー処理のセマンティクスが独特 On Error Resume Next と On Error GoTo の混在は、現代的な try/catch とは挙動が異なります。エラーを無視して継続するコードは、移行後に「動くけれど結果が違う」最も発見しにくいバグになります。
これらは「自動変換ツール」では拾えません。後述の Claude Code 活用と段階的アプローチが必要になります。
2. 業務特性から選ぶ移行先言語:1分判定フローチャート
「どの言語に移すか」は、技術選定ではなく業務特性で決まります。判定軸は次の3つです。
| 軸 | 判定 | 推奨言語 |
|---|---|---|
| 業務の中心がどこにあるか | 申請・承認・案件管理など『情報の流通』 | kintone JavaScript |
| 部門内の業務ツール(Excel併用が多い) | Power Fx(Power Apps) | |
| 営業・顧客管理が中心 | Apex(Salesforce) | |
| データ分析・バッチ処理が中心 | Python | |
| 既存 .NET 資産・Windows 認証連携あり | C#(.NET) | |
| 同時利用人数 | 〜30名 | kintone / Power Fx |
| 30〜200名 | Apex / .NET | |
| 200名超 | Python(API化)/ .NET | |
| VBA 行数 | 〜500行 | どの言語でも可 |
| 500〜3,000行 | 段階移行推奨 | |
| 3,000行超 | レイヤー分離からの再設計が必須 |
この表で2軸が一致する言語があれば、原則そこから検討を始めれば外しません。複数候補が並んだ場合は、社内に保守できる人材がいる言語を優先します。
3. VBA → kintone JavaScript:マッピング表と実コード5パターン
マッピング表
| VBA | kintone JavaScript |
|---|---|
Form_BeforeUpdate |
app.record.create.submit / app.record.edit.submit |
If Me.FieldA = "" |
if (event.record.fieldA.value === "") |
DLookup |
kintone.api('/k/v1/records', 'GET', ...) |
db.Execute UPDATE |
kintone.api('/k/v1/record', 'PUT', ...) |
MsgBox |
alert() または kintone.events.on で event.error を返す |
実コード5パターン
パターン1:BeforeUpdate(入力バリデーション)
' VBA
Private Sub Form_BeforeUpdate(Cancel As Integer)
If IsNull(Me.CustomerCode) Or Len(Me.CustomerCode) <> 6 Then
MsgBox "顧客コードは6桁で入力してください", vbCritical
Cancel = True
End If
End Sub
// kintone JavaScript
kintone.events.on(['app.record.create.submit', 'app.record.edit.submit'], (event) => {
const code = event.record.customerCode.value;
if (!code || code.length !== 6) {
event.error = '顧客コードは6桁で入力してください';
}
return event;
});
パターン2:DLookup(参照値取得)
const resp = await kintone.api('/k/v1/records', 'GET', {
app: CUSTOMER_APP_ID,
query: `customerCode = "${code}" limit 1`,
fields: ['customerName']
});
const name = resp.records[0]?.customerName.value || '';
if (!name) alert('該当する顧客が見つかりません');
パターン3:Recordset ループ(一括更新) kintone は 1 件ずつではなく PUT で一括処理します。
const target = await kintone.api('/k/v1/records', 'GET', {
app: ORDER_APP_ID, query: 'status = "Pending" limit 500'
});
const records = target.records.map(r => ({
id: r.$id.value,
record: { status: { value: 'Approved' }, approvedDate: { value: new Date().toISOString() } }
}));
await kintone.api('/k/v1/records', 'PUT', { app: ORDER_APP_ID, records });
パターン4:トランザクション kintone はマルチアプリのアトミック更新を持たないため、補償トランザクション(失敗時の取消処理)を実装します。
try {
const orderRes = await kintone.api('/k/v1/record', 'POST', { app: ORDER_APP_ID, record: {...} });
try {
await kintone.api('/k/v1/record', 'PUT', { app: CUSTOMER_APP_ID, ... });
} catch (e) {
// 補償:作ったオーダーを削除
await kintone.api('/k/v1/records', 'DELETE', { app: ORDER_APP_ID, ids: [orderRes.id] });
throw e;
}
} catch (e) { alert('処理失敗:' + e.message); }
パターン5:エラーハンドリング
try {
await kintone.api(/* ... */);
} catch (err) {
console.error(err);
// VBA の Resume Next 相当は基本使わない。エラーは握りつぶさない。
}
詳細は kintone 移行記事(Access vs kintone 完全比較)も参照。
4. VBA → Power Fx:マッピング表と実コード5パターン
Power Fx は Excel 数式の延長で書ける宣言型言語です。VBA の手続き的なロジックとは思想が異なるため、「ループを Patch + ForAll に置き換える」発想が要点です。
マッピング表
| VBA | Power Fx |
|---|---|
Form_BeforeUpdate |
OnSave プロパティ |
If…Then…Else |
If(condition, then, else) |
DLookup |
LookUp(Table, condition, Column) |
db.Execute UPDATE |
Patch(Table, record, updates) または UpdateIf |
MsgBox |
Notify("message", NotificationType.Error) |
For Each ループ |
ForAll(Table, ...) |
実コード5パターン
パターン1:BeforeUpdate
// Power Fx (Form の OnSave プロパティ)
If(
IsBlank(CustomerCodeInput.Text) || Len(CustomerCodeInput.Text) <> 6,
Notify("顧客コードは6桁で入力してください", NotificationType.Error);
SubmitForm(EditForm1) // を呼ばずに止める
)
パターン2:DLookup
Set(varCustomerName, LookUp(Customers, customerCode = CustomerCodeInput.Text, customerName));
If(IsBlank(varCustomerName), Notify("該当顧客なし", NotificationType.Warning))
パターン3:一括更新
UpdateIf(
Orders,
status = "Pending",
{ status: "Approved", approvedDate: Now() }
)
パターン4:トランザクション Power Fx に明示的トランザクションはありません。Dataverse のステップ間でロールバックが必要なら Power Automate のフロー(スコープと失敗時実行)に切り出します。
// Power Fx 側は Patch を順に
Set(varOrder, Patch(Orders, Defaults(Orders), { customerCode: "A001", amount: 50000 }));
Patch(Customers, LookUp(Customers, customerCode = "A001"),
{ totalPurchase: LookUp(Customers, customerCode = "A001").totalPurchase + 50000 });
// 失敗ハンドリングは IfError で
パターン5:エラーハンドリング
IfError(
Patch(Orders, Defaults(Orders), { /* ... */ }),
Notify("登録失敗: " & FirstError.Message, NotificationType.Error)
)
実装の前提と費用試算は Access → Power Apps 移行ガイド を参照。
5. VBA → Apex(Salesforce):マッピング表と実コード5パターン
マッピング表
| VBA | Apex |
|---|---|
Sub MyMethod() |
public void myMethod() { } |
Dim x As Integer |
Integer x; |
For i = 1 To 10 |
for (Integer i = 1; i <= 10; i++) |
db.Execute INSERT |
insert new MyObject__c(...); |
SELECT クエリ |
SOQL: [SELECT ... FROM ...] |
Form_BeforeUpdate |
before insert/update トリガ |
実コード5パターン
パターン1:BeforeUpdate → Trigger
trigger CustomerValidation on Customer__c (before insert, before update) {
for (Customer__c c : Trigger.new) {
if (String.isBlank(c.CustomerCode__c) || c.CustomerCode__c.length() != 6) {
c.addError('顧客コードは6桁で入力してください');
}
}
}
パターン2:DLookup → SOQL
List<Customer__c> customers = [
SELECT CustomerName__c FROM Customer__c
WHERE CustomerCode__c = :code LIMIT 1
];
String name = customers.isEmpty() ? '' : customers[0].CustomerName__c;
パターン3:一括更新
List<Order__c> pending = [SELECT Id, Status__c FROM Order__c WHERE Status__c = 'Pending'];
for (Order__c o : pending) {
o.Status__c = 'Approved';
o.ApprovedDate__c = System.now();
}
update pending; // ガバナ制限を考慮、200件超は分割
パターン4:トランザクション
Savepoint sp = Database.setSavepoint();
try {
insert new Order__c(CustomerCode__c = 'A001', Amount__c = 50000);
Customer__c cust = [SELECT Id, TotalPurchase__c FROM Customer__c WHERE CustomerCode__c = 'A001' LIMIT 1];
cust.TotalPurchase__c += 50000;
update cust;
} catch (Exception e) {
Database.rollback(sp);
throw e;
}
パターン5:エラーハンドリング
try {
insert obj;
} catch (DmlException e) {
System.debug(e.getMessage());
// VBA の Resume Next 相当を使う場合でも、ログには必ず残す
}
詳細は Access → Salesforce 移行ガイド を参照。
6. VBA → Python:マッピング表と実コード5パターン
Python 移行は、Access を「業務アプリ」ではなく「データ処理パイプライン」として捉え直すケースに向いています。Web UI が必要なら FastAPI、デスクトップなら PySide6 と組み合わせます。
マッピング表
| VBA | Python |
|---|---|
Dim x As Integer |
x: int |
If…Then |
if ...: |
For Each |
for x in items: |
DLookup |
SQLAlchemy session.query(...).filter(...).first() |
db.Execute |
session.execute(text(...)) または ORM |
MsgBox |
Web なら HTTP レスポンス、デスクトップなら QMessageBox |
実コード5パターン
パターン1:バリデーション(FastAPI + Pydantic)
from pydantic import BaseModel, validator
class CustomerInput(BaseModel):
customer_code: str
@validator('customer_code')
def check_code(cls, v):
if not v or len(v) != 6:
raise ValueError('顧客コードは6桁で入力してください')
return v
パターン2:DLookup
from sqlalchemy.orm import Session
def get_customer_name(db: Session, code: str) -> str:
row = db.query(Customer).filter(Customer.code == code).first()
return row.name if row else ''
パターン3:一括更新
db.query(Order).filter(Order.status == 'Pending').update(
{'status': 'Approved', 'approved_date': datetime.now()},
synchronize_session=False
)
db.commit()
パターン4:トランザクション
try:
with db.begin():
db.add(Order(customer_code='A001', amount=50000))
cust = db.query(Customer).filter(Customer.code == 'A001').one()
cust.total_purchase += 50000
# with ブロック終了時にコミット、例外なら自動ロールバック
except SQLAlchemyError as e:
logger.exception('トランザクション失敗')
raise
パターン5:エラーハンドリング
try:
do_something()
except SpecificError as e:
logger.warning(f'復旧可能: {e}')
# VBA の Resume Next 相当。ログは必ず残す
except Exception:
logger.exception('予期しないエラー')
raise
7. VBA → C#(.NET):マッピング表と実コード5パターン
既存 .NET 資産がある、Windows 認証や Active Directory との連携が前提、というケースで第一候補になります。Blazor で Web 化、WinUI でデスクトップ化の両方に進めます。
マッピング表
| VBA | C#(.NET 8) |
|---|---|
Dim x As Integer |
int x; |
Sub MyMethod() |
public void MyMethod() |
If…Then |
if (...) { } |
For Each |
foreach (var x in items) |
DLookup |
EF Core db.Customers.FirstOrDefault(...) |
db.Execute |
db.Database.ExecuteSqlRaw(...) または LINQ |
実コード5パターン
パターン1:バリデーション(DataAnnotations)
public class CustomerInput
{
[Required(ErrorMessage = "顧客コードは必須です")]
[StringLength(6, MinimumLength = 6, ErrorMessage = "顧客コードは6桁で入力してください")]
public string CustomerCode { get; set; }
}
パターン2:DLookup
var name = await db.Customers
.Where(c => c.CustomerCode == code)
.Select(c => c.CustomerName)
.FirstOrDefaultAsync() ?? "";
パターン3:一括更新
await db.Orders
.Where(o => o.Status == "Pending")
.ExecuteUpdateAsync(s => s
.SetProperty(o => o.Status, "Approved")
.SetProperty(o => o.ApprovedDate, DateTime.Now));
パターン4:トランザクション
using var tx = await db.Database.BeginTransactionAsync();
try {
db.Orders.Add(new Order { CustomerCode = "A001", Amount = 50000 });
var cust = await db.Customers.SingleAsync(c => c.CustomerCode == "A001");
cust.TotalPurchase += 50000;
await db.SaveChangesAsync();
await tx.CommitAsync();
} catch {
await tx.RollbackAsync();
throw;
}
パターン5:エラーハンドリング
try {
await DoSomethingAsync();
} catch (DbUpdateException ex) {
logger.LogWarning(ex, "更新失敗、リトライ対象");
// VBA の Resume Next 相当でも、ログを必ず残す
} catch (Exception ex) {
logger.LogError(ex, "予期しないエラー");
throw;
}
Azure SQL を併用するハイブリッド構成は Access バックエンドを Azure SQL に切り出す を参照。
8. Claude Code を使った半自動変換:プロンプト5本付き
5 言語のマッピングを並べると、機械的に変換できる部分が想像以上に多いことが分かります。Claude Code を使えば、3,000〜5,000行規模の VBA でも、初期変換の主要部分(実測では7割前後)を1〜2日で生成できます。残りは仕様の解釈と検証が必要なため、人手で詰めます。
推奨手順
- 1. VBA コードの抽出:Access の
Application.SaveAsTextか、Claude Code に書かせた pywin32 スクリプトで.bas形式に書き出します。 - 2. モジュール単位のドキュメント化:実装前に意図を日本語で要約させ、Git に commit します。これが後工程の最大のセーフティネットになります。
- 3. マッピング表の事前合意:本記事 3〜7 章の表を
mapping.mdとして渡し、変換ルールの統一を強制します。 - 4. コード生成とテスト生成をセット:必ず pytest / Apex Test / Power Fx Test をセットで作らせます。
- 5. 業務担当者との突合:生成コードの仕様確認は、必ず元 VBA の業務責任者と実施します。
Claude Code 変換プロンプト5本
プロンプト1:VBA モジュール意図の日本語要約
{module}.bas を読み、各プロシージャの ①入力 ②出力 ③副作用(DB更新・画面表示・ファイルI/O)
④呼び出し元 ⑤業務目的(推測でよい、根拠も書く)を vba_doc/{module}.md に表形式で出力。
推測がつかない箇所は「要確認」フラグを立てる。コードの修正は行わない。
プロンプト2:マッピング表に従った変換
{module}.bas を mapping.md のルールに従って {target_lang} に変換し、
{module}.{ext} として出力。以下を厳守:
- 元のコメントは日本語のまま残し、変換に関する補足は // VBA: ... の形式で付ける
- DLookup は LookUp / SOQL / SQLAlchemy など mapping.md 指定の形式に置換
- On Error Resume Next は安易に try/except: pass にしない、必ず log で残す
- 変換できなかった箇所は TODO コメントで明示し、元 VBA を残す
プロンプト3:テストコード自動生成
{module}.{ext} に対するテストを {test_framework} で生成。test_{module}.{ext} に出力。
カバレッジは ①正常系1ケース ②境界値2ケース ③異常系2ケース を最低限含める。
DB 接続は fake / mock で切り離し、純粋関数として呼び出せる形に。
プロンプト4:仕様乖離の検出
元の {module}.bas と、変換後の {module}.{ext} を読み比べ、
①明らかな仕様乖離 ②未実装の分岐 ③エラーハンドリングの差異 ④暗黙的型変換の差異
を outputs/diff_{module}.md に整理。重要度(高/中/低)を付け、対応案も併記。
プロンプト5:未変換 VBA の切り分け(VBA 残置判定)
本記事 9 章の「移行しない選択肢」3 ケースを基準に、{module}.bas のうち
①特定ハードウェア依存 ②Office アドイン依存 ③法定様式の固定印字 に該当する
プロシージャを残置候補として outputs/keep_vba.md に列挙。代替案も併記。
このプロセスを 1 モジュール = 半日〜1日 のリズムで回します。実プロジェクトでは、3,000行規模で 2 週間(人手だけだと 2 ヶ月)に収まります。
9. 変換プロジェクトの進め方とテスト戦略
VBA → モダン言語への変換は、5 フェーズで進めます。
| Phase | 内容 | 期間目安 | 影響度 |
|---|---|---|---|
| 1 | 既存 VBA の抽出と意図ドキュメント化(Git管理) | 2〜5日 | なし |
| 2 | スナップショットテスト整備(直近6〜12ヶ月の本番データで) | 3〜7日 | なし |
| 3 | マッピング合意・小規模モジュールから変換 | 2週間〜 | 軽 |
| 4 | 業務ロジックの段階移行(並行稼働期間あり) | 1〜3ヶ月 | 中 |
| 5 | 旧 VBA 環境のロールアウト停止 | 1ヶ月 | 大 |
Phase 1 と 2 は影響度ゼロです。改修着手前に必ず通します。ここを飛ばすと、変換後に「数字が合わない」問題が出た時、原因がコードか元データかを切り分けられなくなります。
テスト戦略の核は、新環境と旧環境を同じ入力で並行稼働させ、出力差分を機械的に検証することです。月次バッチなら、月次集計の数字を 3 〜 6 ヶ月分突合してから本切替に進みます。
属人化した VBA の解読は、退職リスクと表裏一体です。担当者が在籍中にドキュメント化を完了させることが重要で、進め方は Access 担当者退職の引継ぎ計画 に分けて整理しています。
10. 移行しない選択肢:VBA を残すべき3ケース
すべての VBA を変換する必要はありません。次の3ケースは VBA を残すか、切り離して別系統で運用する方が結果的に低コストです。
ケース1:特定ハードウェアと密結合している バーコードリーダー、ICカードリーダー、ラインの製造装置と COM 経由で通信している処理は、変換よりも該当業務を切り出して別系統で残す方が安全です。
ケース2:Office アドインと深く統合している Excel と Outlook を VBA でブリッジしているケースでは、対象業務が Microsoft 365 内に閉じている限り、Power Automate に部分置換するだけで十分なことがあります。
ケース3:法定様式の固定印字 税務署提出書類、行政提出書類などレイアウトが固定された印字処理は、Access レポートを残し、データ供給だけを新環境から行うのが現実的です。
これらの判断軸は、ピラー記事 Access移行 完全マスターガイド の「移行しない選択肢:4つの戦略」と合わせて検討してください。
まとめ
VBA から 5 つのモダン言語への変換は、マッピング表と実コード片を揃え、Claude Code でドラフト生成を機械化すれば、想定の半分以下の工数で進められます。鍵は次の3点です。
- 言語選定は技術ではなく業務特性で決める(本記事 2 章のフローチャート)
- 変換前に「VBA の意図ドキュメント化」と「スナップショットテスト」を必ず通す
- Claude Code は変換器ではなく「ドラフト生成と差分検出の支援者」として位置づける
Aurant Technologies では Access 移行プロジェクトで本手順を運用しており、ご相談は無料相談窓口から受け付けています。VBA の規模感(行数)と現状の業務を共有いただければ、本記事のフローチャートに沿った第一案を返信します。
関連記事
- Access移行 完全マスターガイド 2026(ピラー)
- Access vs kintone 完全比較
- Access → Power Apps 移行ガイド
- Access → Salesforce 移行ガイド
- Access バックエンドを Azure SQL に
- Access 担当者退職の引継ぎ計画
生成AIの法人導入・セキュリティ設計のご相談
ChatGPTやClaudeなど生成AIのプラン選定・セキュアな全社導入・権限/ログ設計を、貴社の体制に合わせて整理します。すでに導入済みの環境について『この設計で問題ないか』を確認したい、という導入前後のセカンドオピニオンにも対応しています。
AI・業務自動化
ChatGPT・Claude APIを活用したAIエージェント開発、n8n・Difyによるワークフロー自動化で繰り返し業務を削減します。まずはどの業務をAI化できるか診断します。