Google Cloud Setup — Automate Workshop Reflection Forms
Overview
Purpose: Create all MHP DE Workshop reflection Google Forms from
google-forms-manifest.yaml using the official
Related:
- google-forms-reflection-design.md — question design
- google-forms-reflection-design.html — design (HTML)
- Script: create-forms.mjs
What you get after setup
| Output | Location |
|---|---|
| 10 Google Forms (Mod 0–7 + optional 8–9) | Your Google Drive |
| Form URLs (edit + responder) | workshop-2026-v2/trainer/google-forms/forms-output.json |
| OAuth token (reused on later runs) | workshop-2026-v2/trainer/google-forms/token.json *(gitignored)* |
You still link each form to Google Sheets manually (one click per form in the Form UI) unless you add a follow-up Drive API script.
Prerequisites
| Requirement | Notes |
|---|---|
| Google account | Gmail or Google Workspace |
| Node.js 18+ | node --version |
| npm | Bundled with Node |
| Browser | For one-time OAuth consent |
| ~20 minutes | First-time GCP + OAuth setup |
> Cost: Creating a GCP project, enabling the Forms API, and running this script for a one-time workshop rollout is free under standard Forms API quotas. You do not need a billing account for typical use. Google may require billing only if you exceed elevated quotas later in 2026.
Step 1 — Create a Google Cloud project
- Open Google Cloud Console.
- Click the project picker → New Project.
- Name:
mhp-de-workshop-forms(any name is fine). - Click Create and select the new project.
No billing account is required for this workflow.
Step 2 — Enable APIs (Forms + Drive)
Enable both in the same GCP project as your OAuth client (credentials.json).
Google Forms API (required)
- APIs & Services → Library.
- Search Google Forms API → Enable.
Google Drive API (required for --replace delete + banner)
- APIs & Services → Library.
- Search Google Drive API → Enable.
Direct link (replace project ID if needed):
https://console.cloud.google.com/apis/library/drive.googleapis.com
Or use the link from the error message, e.g.
https://console.developers.google.com/apis/api/drive.googleapis.com/overview?project=YOUR_PROJECT_ID
After enabling, wait 2–5 minutes, then re-run the script. No new credentials.json needed.
Step 3 — Configure the OAuth consent screen
- Go to APIs & Services → OAuth consent screen.
- User type:
- Internal — if you use Google Workspace and only colleagues will run the script.
- External — if you use a personal Gmail account (add yourself as a test user).
- Fill required fields:
- App name:
MHP DE Workshop Forms - User support email: your email
- Developer contact: your email
- Scopes → Add or remove scopes → add:
https://www.googleapis.com/auth/forms.bodyhttps://www.googleapis.com/auth/drive*(banner upload +--replacedelete;drive.filealone often returns Insufficient Permission)*
- Test users (External only) — required for personal Gmail:
- Click + ADD USERS
- Add every Google account that will run
create-forms.mjsor open the OAuth link - Example:
juewei.jin@gmail.commust be listed if you sign in with that address - Click Save
- On the consent screen summary, confirm Publishing status shows Testing (that is OK).
- Save through the summary screen.
While the app is in "Testing", refresh tokens expire after 7 days unless the app is published or the user type is Internal. For a one-day workshop setup, run the script once before class; re-auth if needed.
> Do not click Publish app unless you are ready for Google’s full app verification (weeks; not needed for this workshop script).
Step 4 — Create OAuth client credentials
Where to find it in Google Cloud Console (2025+ UI)
Google moved OAuth settings. Use one of these paths:
| Path | Steps |
|---|---|
| A — Direct link | Open Google Auth Platform → Clients (pick your project at the top if prompted) |
| B — Left menu | ☰ Menu → Google Auth platform → Clients |
| C — Legacy menu | ☰ Menu → APIs & Services → Credentials → under OAuth 2.0 Client IDs, click your client name |
If you do not see Google Auth platform, use path C or the direct link in A.
Create the client
- On the Clients page, click + Create client (or Create Credentials → OAuth client ID on the legacy Credentials page).
- Application type: Desktop app (not “Web application”).
- Name:
MHP Forms CLI. - Click Create → Download JSON (download immediately—secrets may not be shown again).
Redirect URIs — Desktop vs Web (read this)
| Client type | “Authorized redirect URIs” in console? | What you do |
|---|---|---|
| Desktop app | Usually not shown — Google’s docs say the console does not require redirect URIs for desktop apps | Nothing to add. Loopback (http://127.0.0.1:PORT) is allowed automatically. Our script listens on port 53187. |
| Web application | Yes — you will see Authorized redirect URIs | Add http://127.0.0.1:53187/oauth2callback or recreate the client as Desktop app (recommended). |
If you cannot find Authorized redirect URIs, your client is probably Desktop—that is normal. Skip redirect URI setup and go to Step 6.
If the client type column says Web application, either add the redirect URI on that client’s edit page or create a new Desktop app client and download a new credentials.json.
- Save the JSON file as:
textworkshop-2026-v2/trainer/google-forms/credentials.json
Never commit credentials.json or token.json to git. They are listed in .gitignore.
Step 5 — Install script dependencies
From the repository root:
powershellcd /workshop-2026-v2/trainer/google-forms
npm install
This installs googleapis and js-yaml.
Step 6 — First run (OAuth + create forms)
powershellnode create-forms.mjs
First run only:
- The script prints a URL and tries to open your browser (keep the terminal running).
- Sign in with the same Gmail address you added under Test users (e.g.
juewei.jin@gmail.com). - Click Allow (scope: edit Forms). If you see Access blocked / Error 403: access_denied, see Fix: Access blocked (403) below.
- After Allow, Google redirects to
127.0.0.1:53187— you should see Authorization successful in the browser (not *connection refused*). - Return to the terminal; the script saves
token.jsonand creates all forms.
If the browser shows This site can’t be reached / localhost refused to connect, see Fix: localhost refused to connect below.
Later runs reuse token.json — no browser step unless the token expired.
Dry run (no API calls)
powershellnode create-forms.mjs --dry-run
Prints how many questions would be created per form.
Single module only
powershellnode create-forms.mjs --module 4
node create-forms.mjs --module final
Replace previous forms (recommended after manifest changes)
powershellnode create-forms.mjs --replace
Deletes every form listed in forms-output.json, then recreates from the manifest (quiz keys, banner, Drive folder).
Move existing forms into one Drive folder
powershellnode create-forms.mjs --organize-folder
Uses drive_folder from google-forms-manifest.yaml. Does not create new forms.
Regenerate trainer answer key (Markdown)
powershellnode sync-grading-artifacts.mjs
Reads google-forms-grading.yaml → updates google-forms-reflection-answer-key.md.
Step 7 — Review output
Open forms-output.json (includes driveFolder.folderUrl — all forms are moved into that Drive folder):
json{
"generatedAt": "2026-05-31T12:00:00.000Z",
"forms": [
{
"module": 0,
"title": "MHP DE Workshop — Mod 0 Reflection",
"formId": "1FAIpQLS...",
"editUrl": "https://docs.google.com/forms/d/1FAIpQLS.../edit",
"responderUrl": "https://docs.google.com/forms/d/1FAIpQLS.../viewform"
}
]
}
| URL | Use in class |
|---|---|
editUrl | Trainer edits questions or settings |
responderUrl | Short link / QR for trainees |
Share responder URLs after each module reflection (see module-delivery-pattern.md).
Step 8 — Link responses to Google Sheets (manual)
For each form:
- Open
editUrlfromforms-output.json. - Responses tab → Link to Sheets → create or select a spreadsheet.
- Suggested Sheet name:
MHP-DE-2026-Mod{NN}-Responses.
Repeat for Mod 0–7 (and 8–9 if created).
Step 9 — Publish forms for trainees
In each form Settings:
- Turn off Restrict to users in your organization if trainees use personal Gmail.
- Choose whether to collect email addresses (or rely on the "Full name" question in the manifest).
- Confirm Accepting responses is on.
Generate QR codes from each responderUrl (any QR generator).
Updating forms after manifest changes
- Edit google-forms-manifest.yaml.
- Option A (recommended) — Replace via script (uses
forms-output.jsonfrom your last successful run):
powershell node create-forms.mjs --replace --dry-run # preview deletes
node create-forms.mjs --replace # delete + recreate all
node create-forms.mjs --module 4 --replace # one module only
Deletes go to Google Drive trash (Forms file). Linked Sheets keep historical responses; point Sheets at the new forms if needed.
- Option B —
node create-forms.mjs --delete-onlythen create without--replace. - Option C — Manually edit existing forms in the Google Forms UI.
The script does not update existing forms in place; without --replace it adds new forms each run.
Quiz mode and auto-grading (Mod 0–9)
Reflection forms are created as quizzes using google-forms-grading.yaml:
- Correct answers and point values (radio = 1 pt, checkbox = 2 pt by default)
- Correct/incorrect feedback (
when_right/when_wrong) shown after submit - Scales, grids, Module 0.7 risk question, and final survey are not auto-graded
Regenerate trainer answer key after editing grading:
powershellnode sync-grading-artifacts.mjs
In Google Forms → Settings → Quizzes, set Release grade (e.g. immediately after submission).
Fix: localhost refused to connect
After you click Continue, the browser may show:
> *This site can’t be reached* — *localhost refused to connect*
That means Google redirected to localhost but the script was not running a local server yet (or the terminal was closed).
Fix (Desktop client — most users)
- Confirm OAuth client type is Desktop app on Clients — you do not need to add redirect URIs.
- Delete old token: remove
trainer/google-forms/token.json. - Run the script and keep the terminal open:
powershellcd /workshop-2026-v2/trainer/google-forms
node create-forms.mjs
- Wait until the terminal prints:
OAuth: waiting for browser callback on 127.0.0.1:53187 ... - Complete sign-in in the browser → you should see Authorization successful (not connection refused).
Fix (only if your client type is Web application)
- Clients → click the Web client → Authorized redirect URIs → add:
text http://127.0.0.1:53187/oauth2callback
- Save, delete
token.json, runnode create-forms.mjsagain.
Still failing?
| Cause | Action |
|---|---|
| Terminal closed before Allow | Script must stay running until browser shows success |
| Wrong client type | Use Desktop app + new credentials.json |
redirect_uri_mismatch | Web client: add URI above; Desktop: use Desktop credentials only |
| Firewall / VPN | Allow local 127.0.0.1 |
Fix: Access blocked (403 access_denied)
If the browser shows:
> *Access blocked: … has not completed the Google verification process*
> *Error 403: access_denied*
the OAuth app is in Testing mode and your Google account is not on the Test users list (or you signed in with a different account).
Fix in Google Cloud Console (2 minutes)
- Open Google Cloud Console → select project mhp-de-workshop-forms (or your project name).
- APIs & Services → OAuth consent screen.
- Scroll to Test users → + ADD USERS.
- Enter the exact email you use in the browser, e.g.
juewei.jin@gmail.com→ Save. - Wait 1–2 minutes, then run
node create-forms.mjsagain and open a new OAuth URL (or incognito window). - Sign in only as that test user—not a work account alias unless that alias is also added.
Checklist
| Check | Action |
|---|---|
| Correct GCP project | OAuth client and consent screen must be in the same project as credentials.json |
| External + Testing | Normal for Gmail; you must use Test users, not “Publish app” |
| Email matches | Login email = one of the Test users (character-for-character) |
| Stale browser session | Try incognito or sign out of other Google accounts first |
| Workspace org | If your company blocks unverified apps, use Internal user type on Workspace, or create forms manually |
If you have Google Workspace (company account)
On OAuth consent screen, if Internal is available:
- Switch user type to Internal (org-only; no Test users list).
- Only accounts in your organization can authorize—use your
@company.comaccount consistently.
What not to do
- Publish app to production without verification—you will hit Google’s review process; unnecessary for a trainer script.
- Add scopes beyond
forms.bodyunless you need them—extra scopes slow consent and verification.
Troubleshooting
| Symptom | Fix |
|---|---|
credentials.json not found | Download OAuth Desktop JSON to trainer/google-forms/credentials.json |
| localhost refused to connect | Fix section above—run script first; redirect URI only for Web clients |
| Cannot find redirect URIs | Normal for Desktop app—use Clients and verify type is Desktop |
Access blocked / 403 access_denied | Fix section above—add juewei.jin@gmail.com (or your login) under Test users |
access_denied on consent screen | Same as above; email must be on Test users for External apps |
Google Forms API has not been used... | Enable Google Forms API in the correct GCP project |
Google Drive API has not been used... / it is disabled | Enable Google Drive API in the same project → wait a few minutes → re-run (no new credentials) |
invalid_grant / token expired | Delete token.json and run node create-forms.mjs again |
Insufficient Permission (delete or banner) | Add scope drive (not only drive.file) on consent screen → delete token.json → run node create-forms.mjs again and complete OAuth |
Failed to fetch image from source_uri | Delete banner-drive-cache.json, re-run (script picks a Forms-compatible Drive URL). Or set banner_source_uri in manifest to any public image URL. Or add banner manually in Form editor |
| Banner missing on forms | Same as above; or add image manually in each Form editor |
--replace delete failed | Old forms still in Drive — trash them at Google Drive (filter: Google Forms); new URLs are in forms-output.json |
| Enterprise blocks third-party apps | Ask IT to allow the OAuth client or use manual Form creation |
| Grid questions missing options | Check manifest GRID_RADIO has rows and columns arrays |
Security checklist
credentials.jsonandtoken.jsonare not committedforms-output.jsoncontains only public form IDs (safe to share with co-trainers)- Workshop forms owned by a team Google account if possible
- Revoke access later: Google Account → Third-party access
File layout
textworkshop-2026-v2/trainer/google-forms/
├── google-forms-manifest.yaml ← source of truth for questions
├── google-forms-grading.yaml ← quiz keys + feedback (Mod 0–9)
├── google-forms-reflection-design.md
├── google-forms-reflection-design.html
├── google-forms-reflection-answer-key.md
├── google-forms-gcp-setup-guide.md ← this document
├── google-forms-gcp-setup-guide.html
├── create-forms.mjs ← automation entry point
├── sync-grading-artifacts.mjs ← answer-key.md from grading YAML
├── package.json
├── credentials.json ← you download (gitignored)
├── credentials.json.example
├── token.json ← created on first auth (gitignored)
└── forms-output.json ← URLs after successful runQuick reference commands
powershellcd /workshop-2026-v2/trainer/google-forms
npm install
node create-forms.mjs --dry-run
node create-forms.mjs
node create-forms.mjs --replace
node create-forms.mjs --module 7 --replace
node create-forms.mjs --delete-only
node create-forms.mjs --organize-folder
node sync-grading-artifacts.mjs
Regenerate HTML companion for this guide:
powershellpython d:\GitRepo\MHPDataEngineerWorkshop\workshop-2026-v2\trainer\build_trainer_html.py google-forms/google-forms-gcp-setup-guide.mdDocument history
| Date | Change |
|---|---|
| 2026-05-31 | Initial GCP + Forms API automation setup guide |
| 2026-05-31 | HTML companion: google-forms-gcp-setup-guide.html |
| 2026-05-31 | Consolidated under trainer/google-forms/ |
| 2026-05-31 | Added 403 access_denied / Test users fix section |
| 2026-05-31 | Local OAuth callback on 127.0.0.1:53187; localhost troubleshooting |
| 2026-05-31 | Console navigation: Google Auth Platform → Clients; Desktop has no redirect URI field |