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 では DLookupdb.Execute のような DB アクセスと MsgBox のような画面操作が 1 つの Sub に混ざりがちです。モダン環境ではレイヤー分離(UI・業務ロジック・データ)が前提のため、構造ごと書き直す必要があります。

理由3:エラー処理のセマンティクスが独特 On Error Resume NextOn 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.onevent.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 移行ガイド を参照。


Access VBAのJS・Apex変換、Claude Codeで半自動化できていますか?Aurant のAI・業務自動化支援は、ChatGPT・Claude・n8n・Dify などを使った自動化フローの設計から実装、運用定着までを一貫して支援します。✓ 自動化フローの設計・実装✓ ChatGPT・Claude・n8nの活用✓ 運用定着まで伴走AI・業務自動化支援を見る →繰り返し業務をAIに渡す繰り返し業務自動化支援本来の仕事設計・実装・運用定着まで伴走

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. 1. VBA コードの抽出:Access の Application.SaveAsText か、Claude Code に書かせた pywin32 スクリプトで .bas 形式に書き出します。
  2. 2. モジュール単位のドキュメント化:実装前に意図を日本語で要約させ、Git に commit します。これが後工程の最大のセーフティネットになります。
  3. 3. マッピング表の事前合意:本記事 3〜7 章の表を mapping.md として渡し、変換ルールの統一を強制します。
  4. 4. コード生成とテスト生成をセット:必ず pytest / Apex Test / Power Fx Test をセットで作らせます。
  5. 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 の規模感(行数)と現状の業務を共有いただければ、本記事のフローチャートに沿った第一案を返信します。


関連記事

生成AIの法人導入・セキュリティ設計のご相談

ChatGPTやClaudeなど生成AIのプラン選定・セキュアな全社導入・権限/ログ設計を、貴社の体制に合わせて整理します。すでに導入済みの環境について『この設計で問題ないか』を確認したい、という導入前後のセカンドオピニオンにも対応しています。

生成AI導入・セキュリティ支援を見る → セキュリティ設計の支援を見る →

AI・業務自動化

ChatGPT・Claude APIを活用したAIエージェント開発、n8n・Difyによるワークフロー自動化で繰り返し業務を削減します。まずはどの業務をAI化できるか診断します。

AT
aurant technologies 編集

上場企業からスタートアップまで、数多くのデータ分析基盤構築・AI導入プロジェクトを主導。単なる技術提供にとどまらず、MA/CRM(Salesforce, Hubspot, kintone, LINE)導入によるマーケティング最適化やバックオフィス業務の自動化など、常に「事業数値(売上・利益)」に直結する改善実績多数。

この記事が役に立ったらシェア: