Apps Script Cloud Projects for Advanced Automation
Learn how to link Apps Script to a Google Cloud project for Cloud Logging, IAM, and APIs that go far beyond what default projects support.
Every developer who has worked with Google Apps Script has hit the same ceiling. The quota errors, the missing API access, the logging that disappears into a black hole, the OAuth consent screen you cannot fully control. You have built something useful -- a script that processes form submissions, generates reports, or pushes data between Workspace and an external system -- and now you need it to be production-grade. You need proper observability. You need fine-grained access control. You need to call APIs that the default Apps Script project does not expose.
The answer is not to abandon Apps Script for Cloud Functions. It is to connect your script to a standard Google Cloud (GCP) project. When you do, Apps Script transforms from a scrappy automation tool into a first-class cloud-integrated developer platform. You get Cloud Logging, full IAM control, access to the entire GCP API library, and the ability to configure your OAuth consent screen exactly as you need it.
This guide walks through exactly how to make that connection and what it unlocks for teams building serious Workspace automations.
What this guide covers:
- The difference between default and Cloud projects in Apps Script
- How to link an existing script to a GCP project step by step
- Enabling Cloud Logging and querying logs from Cloud Console
- Configuring IAM roles for scripts that interact with GCP services
- Calling advanced Google APIs that require OAuth 2.0 with a configured consent screen
- Annotated code examples you can adapt immediately
Default Apps Script Projects vs Cloud Projects
When you create a new Apps Script file -- whether from script.google.com or from a bound script inside a Sheet, Doc, or Form -- Google automatically provisions a hidden GCP project behind the scenes. This is the default project. You cannot see it in Cloud Console. You cannot attach custom logging sinks to it. You cannot assign IAM roles to it. You get basic script logging via console.log() that surfaces in the Apps Script editor's Executions tab, and that is roughly where the observability story ends.
The default project also limits which Google APIs you can use. The Apps Script editor exposes a curated list of "Advanced Services" (Sheets API, Drive API, Gmail API, etc.) that you can toggle on, and these work without any Cloud Console interaction. But if you want to call Cloud Natural Language API, Vertex AI, Secret Manager, or any other GCP service, you need an OAuth 2.0 token scoped to those services, which requires a configured OAuth consent screen and an API enablement on a real GCP project.
The Cloud project approach replaces the hidden default project with a GCP project you own and control. Everything still runs as Apps Script -- the same .gs files, the same triggers, the same SpreadsheetApp and GmailApp calls -- but now the project management layer is yours. The trade-off is minimal: a few extra setup steps. The gain is substantial.
| Capability | Default Project | Cloud Project |
|---|---|---|
| Cloud Logging | Not available | Full Cloud Logging with sinks, metrics, and alerts |
| IAM roles | Not configurable | Assign roles to service accounts and project members |
| API enablement | Curated list only | Any GCP API |
| OAuth consent screen | Limited configuration | Full branding, scopes, and test/production status |
| Visibility in Cloud Console | Hidden | Full access |
| Secret Manager access | Not available | Native integration |
| Quotas and billing | Shared default quotas | Project-level quota management |
Step 1: Create or Select a GCP Project
If you already have a GCP project for your Workspace organisation, you can use it. If not, create one now.
- Open console.cloud.google.com and sign in with your Google Workspace admin account.
- Click the project dropdown in the top navigation bar, then click New Project.
- Give it a meaningful name (e.g.,
workspace-automation-prod), select your billing account, and set the organisation to your Workspace domain. - Click Create. Note the Project Number -- you will need it in the next step. Find it under IAM & Admin > Settings.
A note on project structure for Australian SMBs: If you run multiple automation scripts for different functions (HR onboarding, finance reporting, client communication), consider whether they belong in a single shared project or separate projects. A single project simplifies billing and API management but blurs access boundaries. Separate projects give you tighter IAM controls and cleaner log isolation. For a team of two to five developers, a single project with well-named log labels is usually sufficient.
Step 2: Link the Apps Script Project to GCP
This is the step most tutorials skip past too quickly.
- Open your Apps Script project at script.google.com.
- In the left sidebar, click the Settings gear icon (Project Settings).
- Scroll to the section labelled Google Cloud Platform (GCP) Project.
- Click Change project.
- Enter the Project Number from the GCP project you created above (not the project ID -- the numeric ID found under IAM & Admin > Settings).
- Click Set project.
Apps Script will validate the connection. If it fails, verify that the account you are signed in to in the Apps Script editor has at least the Editor role on the GCP project. You set this in Cloud Console under IAM & Admin > IAM.
Once linked, your script now uses that GCP project for authentication, API calls, and logging. The connection persists across all versions and deployments of the script.
Step 3: Configure the OAuth Consent Screen
With a Cloud project attached, you can now configure the OAuth consent screen properly. This matters because it controls what users see when they authorise your script, which scopes are available, and whether you can distribute the script outside your organisation.
- In Cloud Console, navigate to APIs & Services > OAuth consent screen.
- Choose Internal if this script will only ever be used within your Google Workspace organisation. This skips the Google review process and lets you add any scopes immediately. Choose External only if you plan to distribute the script publicly.
- Fill in the App name, support email, and developer contact email. These display to users during the authorisation flow.
- Under Scopes, click Add or Remove Scopes and add the specific OAuth scopes your script requires. For example:
https://www.googleapis.com/auth/spreadsheetsfor Sheets accesshttps://www.googleapis.com/auth/logging.writefor Cloud Logginghttps://www.googleapis.com/auth/cloud-platformfor general GCP access
Match the scopes declared here to the @OnlyCurrentDoc or full-scope annotations in your Apps Script manifest (appsscript.json). Mismatched scopes cause authorisation errors that are difficult to debug without proper logging.
Step 4: Enable Required APIs
Any GCP API your script calls must be enabled on the project.
- Navigate to APIs & Services > Library in Cloud Console.
- Search for and enable each API you need. Common ones for Workspace automation:
- Cloud Logging API -- for structured log writes
- Secret Manager API -- for storing API keys and credentials securely
- Cloud Natural Language API -- for text classification in form responses
- Vertex AI API -- for generative AI features
- Admin SDK API -- for programmatic Workspace admin operations
Enabling an API here does not automatically grant access to your script. Access is controlled by the OAuth scopes declared in your consent screen and your script manifest. Both layers must align.
Step 5: Cloud Logging from Apps Script
This is one of the most practical benefits of switching to a Cloud project. Instead of console.log() output that is only visible in the Apps Script editor and disappears after 90 days, you write structured logs to Cloud Logging where they persist, are queryable, and can trigger alerts.
Writing Structured Logs
The Apps Script console object does write to Cloud Logging when a Cloud project is attached. However, for structured logging with custom severity levels and labels, use the Cloud Logging API via UrlFetchApp:
/**
* Writes a structured log entry to Cloud Logging.
* Requires the Cloud Logging API to be enabled on the linked GCP project.
* Requires the OAuth scope: https://www.googleapis.com/auth/logging.write
*
* @param {string} message - Human-readable log message.
* @param {string} severity - One of: DEFAULT, DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL.
* @param {Object} labels - Key-value pairs attached to the log entry for filtering.
*/
function writeStructuredLog(message, severity, labels) {
const projectId = 'your-gcp-project-id'; // Replace with your actual project ID
const logName = 'apps-script-automation';
const logEntry = {
entries: [
{
logName: `projects/${projectId}/logs/${logName}`,
resource: {
type: 'global'
},
severity: severity || 'INFO',
textPayload: message,
labels: labels || {}
}
]
};
const url = 'https://logging.googleapis.com/v2/entries:write';
const token = ScriptApp.getOAuthToken();
const options = {
method: 'post',
contentType: 'application/json',
headers: {
Authorization: `Bearer ${token}`
},
payload: JSON.stringify(logEntry),
muteHttpExceptions: true
};
const response = UrlFetchApp.fetch(url, options);
if (response.getResponseCode() !== 200) {
// Fall back to console.log so the error is visible in the editor
console.error('Cloud Logging write failed: ' + response.getContentText());
}
}
Call it from any function in your script:
function processFormSubmission(e) {
const formData = e.namedValues;
const submitterEmail = formData['Email'][0];
writeStructuredLog(
`Processing form submission from ${submitterEmail}`,
'INFO',
{ submitter: submitterEmail, formId: 'onboarding-form' }
);
try {
// Your processing logic here
createOnboardingChecklist(formData);
writeStructuredLog(
`Onboarding checklist created for ${submitterEmail}`,
'INFO',
{ submitter: submitterEmail, outcome: 'success' }
);
} catch (error) {
writeStructuredLog(
`Failed to create checklist: ${error.message}`,
'ERROR',
{ submitter: submitterEmail, outcome: 'failure', errorStack: error.stack }
);
// Re-throw so the trigger execution is marked as failed
throw error;
}
}
Querying Logs in Cloud Console
Once logs are flowing, query them in Cloud Console under Logging > Log Explorer:
resource.type="global"
logName="projects/your-gcp-project-id/logs/apps-script-automation"
severity>=WARNING
You can also filter by the custom labels you attached:
labels.outcome="failure"
timestamp>="2026-02-01T00:00:00Z"
Set up Log-based Alerts (under Logging > Log-based Alerts) to send email or Pub/Sub notifications when ERROR or CRITICAL severity entries appear. For a production script running on a time-based trigger, this means you are notified of failures within minutes rather than discovering them when a stakeholder complains.
Step 6: IAM for Scripts That Call GCP Services
When your Apps Script calls GCP services beyond the standard Workspace APIs -- Secret Manager, BigQuery, Vertex AI -- you need to ensure the calling identity has the right IAM roles.
Apps Script executes as the authorising user's identity. The OAuth token ScriptApp.getOAuthToken() represents that user, and the GCP services check IAM permissions against their account.
To grant a user account the necessary roles:
- Navigate to IAM & Admin > IAM in Cloud Console.
- Click Grant Access.
- Enter the Google account email (the user who will authorise the script).
- Assign the minimum necessary role. For example:
- Secret Manager Secret Accessor (
roles/secretmanager.secretAccessor) for reading secrets - BigQuery Data Viewer + BigQuery Job User for running queries
- Logs Writer (
roles/logging.logWriter) for Cloud Logging writes
Reading a Secret from Secret Manager
This pattern removes hardcoded API keys and credentials from your script entirely:
/**
* Retrieves a secret value from Secret Manager.
* The calling user must have the roles/secretmanager.secretAccessor role
* on the GCP project.
*
* @param {string} secretName - The name of the secret (not the full resource path).
* @returns {string} The decoded secret value.
*/
function getSecret(secretName) {
const projectId = 'your-gcp-project-id';
const url = `https://secretmanager.googleapis.com/v1/projects/${projectId}/secrets/${secretName}/versions/latest:access`;
const token = ScriptApp.getOAuthToken();
const response = UrlFetchApp.fetch(url, {
method: 'get',
headers: {
Authorization: `Bearer ${token}`
},
muteHttpExceptions: true
});
if (response.getResponseCode() !== 200) {
throw new Error(`Secret Manager error (${response.getResponseCode()}): ${response.getContentText()}`);
}
const payload = JSON.parse(response.getContentText());
// The secret data is base64-encoded
return Utilities.newBlob(Utilities.base64Decode(payload.payload.data)).getDataAsString();
}
// Usage: retrieve an external API key without it ever appearing in code
function callExternalAPI() {
const apiKey = getSecret('external-crm-api-key');
const response = UrlFetchApp.fetch('https://api.yourcrm.com/contacts', {
headers: {
'X-API-Key': apiKey
}
});
return JSON.parse(response.getContentText());
}
This pattern means that rotating credentials requires a single update in Secret Manager rather than a script edit and redeployment.
Step 7: Advanced API Access via the Service Account Pattern
For scripts deployed as Web Apps or Add-ons that run at scale -- particularly those executing as service accounts rather than end users -- you may want to call GCP APIs using a service account rather than the authorising user's identity. This is useful when the script needs consistent permissions regardless of who installed it.
The pattern:
- Create a service account in IAM & Admin > Service Accounts.
- Grant it the appropriate IAM roles.
- Download a JSON key file and store the contents as a Secret Manager secret.
- In Apps Script, use the OAuth 2.0 library (
OAuth2for Apps Script) or manually construct a JWT to obtain a token for the service account.
Full service account impersonation from Apps Script is an advanced topic, but the foundation is:
/**
* Demonstrates the high-level structure for calling a GCP API
* using a service account credential stored in Secret Manager.
* Requires the OAuth2 for Apps Script library to be installed.
* Library ID: 1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF
*/
function getServiceAccountToken(serviceAccountEmail, privateKey, scopes) {
const now = Math.floor(Date.now() / 1000);
const header = {
alg: 'RS256',
typ: 'JWT'
};
const claim = {
iss: serviceAccountEmail,
scope: scopes.join(' '),
aud: 'https://oauth2.googleapis.com/token',
iat: now,
exp: now + 3600
};
// Build and sign the JWT using the service account private key
const signedJwt = signJWT(header, claim, privateKey);
const tokenResponse = UrlFetchApp.fetch('https://oauth2.googleapis.com/token', {
method: 'post',
contentType: 'application/x-www-form-urlencoded',
payload: {
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
assertion: signedJwt
}
});
return JSON.parse(tokenResponse.getContentText()).access_token;
}
For most teams using Apps Script for internal Workspace automations, user-level OAuth is sufficient. The service account pattern becomes necessary when building add-ons distributed to other organisations, or when running background jobs that should not be tied to any individual user's account.
Practical Architecture: A Production-Ready Onboarding Script
Pulling the above components together, here is the architecture for a production-grade employee onboarding automation that a developer at an Australian SMB could deploy today:
Trigger: A Google Form submission captures new hire details (name, email, start date, department, manager).
Script actions:
1. Validates the form data and writes an INFO log to Cloud Logging.
2. Reads a CRM API key from Secret Manager.
3. Creates a contact record in the external CRM via REST API.
4. Provisions a Google Workspace account using the Admin SDK (via the AdminDirectory Advanced Service, enabled in the Apps Script editor).
5. Creates an onboarding document from a template in Drive and shares it with the new hire and their manager.
6. Logs the outcome (success or failure with error details) to Cloud Logging.
7. On failure, an alert fires within five minutes via a log-based alert configured in Cloud Console.
This script has no hardcoded credentials, generates observable logs you can query after the fact, and can have its secrets rotated without touching the code. It is the difference between a script that lives in someone's personal Drive and one that a team can rely on.
Resources and Affiliate Links
These are the tools and platforms relevant to building the workflows described above. Some links below include referral programmes that may provide benefits to you and to CloudGeeks. We only recommend what we use with clients.
Google Workspace
Apps Script is included in every Google Workspace plan at no additional cost. If your business has not yet made the switch, or you are on an older legacy plan, it is worth reviewing the current plan tiers. Business Starter gives you Apps Script with standard quotas; Business Standard and above lift several limits and provide better Vault retention for script audit logs.
Start or upgrade your Google Workspace plan
Google Cloud Platform
The GCP services used in this guide -- Cloud Logging, Secret Manager, IAM -- have generous free tiers. The first 50 GiB of Cloud Logging ingestion per month is free. Secret Manager charges USD $0.06 per 10,000 access operations. For an internal automation script, the monthly GCP cost is typically under USD $1.
Start with a free trial credit and explore the platform at console.cloud.google.com.
Recommended Documentation
- Apps Script + GCP Project Setup -- the official guide, kept current by the Apps Script team.
- Cloud Logging API reference -- full schema for structured log entries.
- Secret Manager quickstart -- getting your first secret stored and accessed in under five minutes.
Conclusion
The gap between a "good enough" Apps Script and one that a development team can rely on in production is not as wide as it seems. Linking your script to a GCP project is a fifteen-minute setup task. What you get in return -- Cloud Logging, IAM, Secret Manager, and full API access -- turns Apps Script from a convenient shortcut into a legitimate automation platform.
For Australian developers and IT teams building on Google Workspace, this pattern is particularly valuable. You do not need to leave the Workspace ecosystem or adopt entirely new infrastructure. You extend what you already have with the parts of Google Cloud that solve the problems default projects cannot.
Start with one script. Link it to a Cloud project. Enable Cloud Logging. Move your hardcoded credentials into Secret Manager. Run it in production and watch the logs. Once you have seen the difference in observability alone, you will not go back to the default project approach.
If you need help architecting or deploying Apps Script automations at scale for your business, get in touch with the CloudGeeks team. We work with Australian businesses across a range of industries to build Workspace automations that run reliably, not just in theory.
Ash Ganda is the founder of CloudGeeks, a Google Workspace consultancy helping Australian SMBs get more from their cloud tools. He writes about practical technology strategies at insights.cloudgeeks.com.au.