Build a Drive permissions auditor
Flag files shared publicly or with external addresses for a Northwind compliance review.
Published Jul 24, 2025
Sharing settings drift quietly. Someone flips a client brief to “anyone with the link” for a quick review, a freelancer is added as an editor for one project, and six months later nobody remembers either. At Northwind, that accumulated drift is exactly what a compliance review needs to surface — but clicking through every file’s share dialog by hand is nobody’s idea of a good afternoon.
This script walks a folder tree and writes a tidy report of every file that is
either publicly accessible or shared with an editor outside the
@northwind.studio domain. The result is a single sheet a reviewer can scan,
fix, and sign off — instead of a Drive full of unknowns.
What you’ll need
- A root Drive folder to audit — the script recurses into every subfolder.
- A Google Sheet to hold the report. The script clears the first tab and rewrites it on every run, so use a dedicated sheet.
- Edit access to both the folder tree and the report sheet.
- Your internal domain — anything not ending in it counts as external.
The script
// The internal domain. Editors whose address does not end in this
// are treated as external.
const INTERNAL_DOMAIN = '@northwind.studio';
/**
* Audits a Drive folder tree and writes every risky file to a sheet.
* "Risky" means publicly shared, or shared with an external editor.
*
* @param {string} rootFolderId ID of the folder to audit.
* @param {string} sheetId ID of the spreadsheet for the report.
*/
function auditPermissions(rootFolderId, sheetId) {
// 1. Walk the tree, collecting issues into a flat array of rows.
const issues = [];
walk(DriveApp.getFolderById(rootFolderId), '', issues);
// 2. Open the report sheet and reset it.
const sheet = SpreadsheetApp.openById(sheetId).getSheets()[0];
sheet.clear();
sheet.getRange(1, 1, 1, 4)
.setValues([['Path', 'Issue', 'Editors', 'Link']]);
// 3. Write the findings, if there are any.
if (issues.length) {
sheet.getRange(2, 1, issues.length, 4).setValues(issues);
}
Logger.log('Found ' + issues.length + ' permission issue(s).');
}
/**
* Recursively scans a folder, pushing one row per issue into `out`.
* Each row is [path, issue type, editors, file URL].
*
* @param {Folder} folder The folder to scan.
* @param {string} path Path of the parent, for building a readable trail.
* @param {Array} out The accumulator array of issue rows.
*/
function walk(folder, path, out) {
// Build a human-readable path like "Clients/Acme/Brief".
const full = path ? `${path}/${folder.getName()}` : folder.getName();
// Check every file in this folder.
const files = folder.getFiles();
while (files.hasNext()) {
const f = files.next();
// Flag files anyone can open via a link.
const access = f.getSharingAccess();
if (access === DriveApp.Access.ANYONE ||
access === DriveApp.Access.ANYONE_WITH_LINK) {
out.push([full, 'public link', '', f.getUrl()]);
}
// Flag editors whose address is outside the internal domain.
const external = f.getEditors()
.filter((e) => !e.getEmail().endsWith(INTERNAL_DOMAIN));
if (external.length) {
out.push([
full,
'external editor',
external.map((e) => e.getEmail()).join(','),
f.getUrl(),
]);
}
}
// Recurse into every subfolder.
const subs = folder.getFolders();
while (subs.hasNext()) walk(subs.next(), full, out);
}
How it works
auditPermissionsopens the root folder and callswalkto scan the whole tree, collecting issue rows into a singleissuesarray.- It opens the report spreadsheet, clears the first tab, and writes a header
row of
Path,Issue,Editors, andLink. - If
walkfound anything, it writes every issue row in one bulksetValuescall, then logs the total count. walkbuilds a readable path string so a reviewer can see exactly where each file lives without opening it.- For each file it checks
getSharingAccess—ANYONEorANYONE_WITH_LINKmeans a public link, which gets its own row. - It also reads the file’s editors and keeps any whose address does not end in
INTERNAL_DOMAIN, recording them as an external-editor issue. - Finally it recurses into every subfolder, so a single call covers an entire folder hierarchy.
Example run
Point the script at a Clients folder. The report sheet fills with one row per
finding:
| Path | Issue | Editors | Link |
|---|---|---|---|
| Clients/Acme/Brief | public link | https://docs.google.com/… | |
| Clients/Acme/Assets | external editor | [email protected] | https://drive.google.com/… |
| Clients/Beta/Contract | external editor | [email protected] | https://docs.google.com/… |
A reviewer works straight down the list: open each link, decide whether the sharing is intentional, and tighten it if not.
Run it
This is an on-demand audit, so run it by hand when a review is due:
- Paste the script into the Apps Script editor.
- From a temporary function, call
auditPermissionswith your folder and sheet IDs, or fill the IDs into a small wrapper:
function runAudit() {
auditPermissions('YOUR_ROOT_FOLDER_ID', 'YOUR_REPORT_SHEET_ID');
}
- Select
runAudit, click Run, and approve the authorisation prompt. - Open the report sheet to see the findings.
To run it on a schedule instead, add a time-driven trigger on runAudit from
the Triggers panel.
Watch out for
- A public file shows up only once even if it also has external editors — the public-link row is added first. That is usually fine; the file needs fixing either way.
getEditorsdoes not include people who only have view access. This audit is about who can change files; widen the check togetViewersif you also care about read access.- Large trees take time.
walktouches every file, and eachgetSharingAccesscall is a Drive request — a folder of thousands of files can approach the six-minute execution limit. Audit subtrees separately if you hit it. - The script reports; it does not fix. It deliberately never changes sharing
settings, so a reviewer always makes the call. Add
setSharingonly if you are certain you want automatic remediation. - Files in shared drives have different ownership rules.
DriveAppcan still read them, but the “owner” concept differs — treat shared-drive results with extra care.
Related
Detect and report broken file shortcuts
Find Drive shortcuts in Northwind folders pointing at deleted or inaccessible files.
Updated Dec 3, 2025
Build a Drive cleanup recommendation report
Suggest what Northwind can delete or archive — large, stale, duplicate, or untouched files.
Updated Nov 21, 2025
Generate a folder-level changelog
Track additions and deletions in a Northwind folder over time — a written history.
Updated Nov 5, 2025
Track contract expiry from Drive files
Read expiry dates out of Northwind contract Docs and warn before renewals.
Updated Oct 28, 2025
Build a Drive quota early-warning system
Alert Northwind before storage runs out — email when usage crosses 80%.
Updated Oct 20, 2025