How to Inventory a Microsoft Access Application

by DeeDee Walsh, on Jun 8, 2026 2:12:50 PM

MS Access breaks almost every assumption a file-counting tool makes because the entire application lives inside a single binary database. The code, the queries, the macros, and half the business rules are sealed in one file your scanner can only see the outside of. Here's how to take a real inventory anyway.

Of all the legacy stacks people ask us to size, Microsoft Access is the one most reliably under-scoped and it's under-scoped at the very first step, before anyone has even looked at the code. Someone points at a single .accdb file sitting on a network share and calls it "a couple of screens we should probably replace someday." Then you open it and find forty forms, a hundred saved queries, twelve thousand lines of VBA, a startup macro that wires the whole thing together, and a tangle of table relationships enforcing rules that exist nowhere else in the business. The "couple of screens" is a full line-of-business application. It just doesn't look like one from the outside, because the outside is one file.

That's the central problem with inventorying Access, and it's worth being precise about why it's different from inventorying, say, a VB6 project. A normal codebase is a tree of text files. You can walk it, count it, and read it. An Access application is a database. It's a single binary container (.accdb for anything built since Access 2007 on the ACE engine, or the older .mdb for the Jet-era apps that came before it). All the source including every form, every line of VBA, every query definition is stored inside that binary blob. A tool that walks the file system sees one file and a byte count, and the byte count tells you almost nothing, because most of those bytes are data, not code.

So before you can inventory an Access app, you have to understand where the application actually lives. It lives in at least six places, and only some of them are obvious.

The six places logic hides in an Access app

1. VBA modules. The standard and class modules are the part everyone expects; the closest thing Access has to "the source code." If you're lucky, the real logic lives here in named procedures you can read. Often you're not that lucky, because a lot of the logic lives in the next item.

2. Code behind the forms and reports. Every form and report can carry its own VBA: event procedures on buttons, fields, and the form itself. This is where a huge amount of business logic actually sits in older Access apps, scattered across dozens of OnClick and AfterUpdate handlers rather than centralized in modules. A count of standalone modules will badly undercount the real code surface if you don't also pull the code behind every form.

3. Saved queries. Access stores named queries as objects, and they aren't just SELECT statements. They carry business rules including joins, filters, calculated fields, update and append and make-table operations that mutate data on a schedule or a button press. In a lot of apps, the most important logic in the whole system is a stack of saved queries that nobody documented. They are code. Inventory them as code.

4. Macros: the parallel automation system. Access has two automation systems that grew up side by side: VBA and macros. Older and citizen-built apps lean heavily on macros, and the one you must always find is AutoExec: the macro that runs on startup and is, in practice, the application's main(). Newer apps (2007+) also use embedded macros that live inside form and control definitions rather than as standalone objects, which makes them even easier to miss. If your inventory only looks for VBA, an entire control layer is invisible to you.

5. Table-level rules. Validation rules, default values, input masks, required-field constraints, and the relationship/referential-integrity graph between tables are all logic; they're just declarative logic enforced by the engine instead of written in VBA. For many Access apps this is the only place certain rules are expressed, which means they vanish silently in any migration that treats the tables as "just data."

6. The data tier itself which may not even be in the file. Which brings us to the question that reorganizes the whole inventory.

First question: is this one file, or two?

A surprising number of "serious" Access apps are split. The front-end .accdb holds the forms, reports, queries, and code; the back-end holds nothing but tables. The two are connected by linked tables, and the back-end might be another Access file on a share or it might be SQL Server, or an ODBC source, or SharePoint, with Access acting as a thin client over data that lives somewhere else entirely.

This matters enormously for scoping, and you can't see it by counting files. Two .accdb files of similar size can be completely different animals: one a self-contained application, the other a forms-and-code front-end whose data is a SQL Server database you haven't accounted for yet. The first thing to establish for any Access file is whether it's a front-end, a back-end, or a self-contained app and where its linked tables actually point.

Second question: which copy is the real one?

Access apps proliferate the way spreadsheets do. They get copied to local drives "so it runs faster," emailed as attachments, branched into FinalApp_v2_USE_THIS_ONE.accdb, and left on shares long after they're dead. The inventory problem is partly an archaeology problem: out of the eleven copies on the network, which one is live, and which are abandoned forks?

This is where file metadata earns its keep. Check out the last-modified timestamps and folder structure to separate the live regions of the tree from the dead ones nobody has touched since 2014. A scanner that walks the whole share and reports every Access file with its size and dates turns "which one is real" from a guessing game into a sortable list.

The version and format minefield

A few format facts that change what's even possible:

  • .mdb vs .accdb. .mdb is the older Jet-engine format (Access 2003 and earlier); .accdb is the ACE-engine format introduced with Access 2007 and still current in Access 2024. You'll find both in the wild, often in the same organization.
  • .mde / .accde: the worst case. These are compiled versions of an Access app with the VBA source stripped out. If the only artifact anyone can find is an .accde, the source code is gone. This is the Access equivalent of the "the people who wrote it are gone and so is the source" scenario, and it needs to be flagged loudly because it changes the modernization path entirely.
  • The ACE driver situation. The standalone Access Database Engine 2016 redistributable reached end of support in October 2025; the recommended replacement is the Microsoft 365 Access Runtime. If your inventory tooling needs to open these files programmatically, the 32-bit vs 64-bit ACE driver mismatch is a real and recurring source of pain and is worth knowing before you start.

Where ByteInsight fits and the one step to do first

ByteInsight walks a folder tree, recognizes source files by type, counts their lines, and separates real code from blank lines and binary blobs. Point it at a tree of text source: VB6, C#, VB.NET, the languages that live as files and it gives you a clean inventory. Point it at a raw .accdb, and it does exactly what it should: it reports a single binary file with a byte count. It cannot see inside the container, because the container is binary. No file-walking tool can.

So with Access there's one step that comes first, and it's the step that makes everything above actually measurable: you decompose the database to text. Access has a built-in (long undocumented, now documented for Access 2019 through 2024) method for this: Application.SaveAsText exports any form, report, query, macro, or module to a text file, and DoCmd.TransferText exports the table data. Loop it over every object and you get a folder tree: one text file per form, per query, per module, per macro. The community has standardized this into small decompose.vbs / compose.vbs scripts that turn an Access app into a source tree and back again which is, not coincidentally, also how people put Access apps under version control.

Then you scan it. Once the app is a text tree, ByteInsight can do what it's built for: count the real VBA across modules and form code, enumerate the forms, reports, queries, and macros, and give you an inventory that reflects the actual size of the application instead of the size of the file. The decompose step is the difference between "one 40 MB binary" and "412 objects, 11,800 lines of VBA, 103 saved queries, 1 AutoExec macro, 14 linked tables pointing at a SQL Server you didn't know about."

A practical inventory sequence

  1. Find every Access file across the shares. Not just the official one. Walk the whole tree for .mdb, .accdb, .mde, and .accde. Record size and last-modified for each. (This part a scanner does for you.)
  2. Separate front-ends from back-ends. Forms-and-code vs. tables-only vs. self-contained. This reframes everything downstream.
  3. Identify the live copy. Use timestamps and folder context to find the version in real use and set the abandoned forks aside.
  4. Check format and compile state. Flag any .mde/.accde immediately because stripped source changes the whole plan.
  5. Decompose the live app to text. SaveAsText for objects, TransferText for table data. Now you have a source tree.
  6. Inventory the linked tables. Where does the data actually live — in the file, in another Access back-end, or in SQL Server / ODBC / SharePoint? Account for it explicitly.
  7. Scan the text tree. Count VBA lines and object counts across modules, form/report code, queries, and macros — the real code surface, not the byte count.
  8. Map the external dependencies. ODBC DSNs, VBA references, ActiveX/OCX controls embedded on forms, and automation out to Excel, Outlook, or Word. These are the load-bearing dependencies that don't show up as "Access" at all.

What this tells you about modernization

The reason this matters beyond tidiness: the parts of an Access app that are hardest to inventory are the same parts that are hardest to modernize automatically. An automated translator can do a lot with clean VBA in named modules. It can do much less with logic that lives in a startup macro, in the validation rules on a table, or in a stack of update queries because that logic was never written down as code an analyzer can read, and a folder scan can't even see it until you've decomposed the file.

That's the connection to the 70% wall: how much of an Access application lives in places a naive scan can't reach is a leading indicator of how much an automated migrator will be able to cover on its own and how much will need a human. The decompose-and-scan step doesn't just size the app. It tells you, before you commit to anything, roughly where your wall is going to be.

You can't deal with what you have until you know what you have. With Access, knowing what you have starts with admitting it's not one file — it's an application wearing a database costume, and the inventory is the act of taking the costume off.

Want to see the real shape of your Access estate? Download ByteInsight to inventory the files across your shares for free. No source code leaves your machine and once you've decomposed an app to text, our migration engineers will help you read the results. Book a working session.

Topics:.NET.NET 10MS Access

Comments

Subscribe to GAPVelocity AI Modernization Blog

FREE CODE ASSESSMENT TOOL