Tempkey Blog
Serverless Security: Managing Contractor Access to Google Cloud Functions Without Over-Privileging
Learn how to give contractors time-boxed access to Google Cloud Functions, scope IAM roles so they can deploy without owning your project, and prove access was removed when the engagement ends.
Effective contractor access management for google cloud functions requires locking down granular IAM permissions to specific serverless resources, eliminating long-lived service account keys, and establishing a provable offboarding workflow. When you bring an outside developer in to build or patch a serverless microservice, giving them broad project-level roles creates security blind spots that persist long after their contract wraps up.
Serverless architectures decouple compute from infrastructure, but they do not automatically isolate permissions. Operations managers, technical founders, and IT leads often default to assigning project-wide roles to get external talent working quickly. However, securing serverless functions demands a disciplined approach: scope the role to the exact function, bind the identity to dedicated deployment pipelines or service accounts, time-box the engagement, and verify that all bindings and access keys are destroyed when work completes.
Why Google Cloud Functions Access Is Easy to Over-Grant
In Google Cloud Platform (GCP), permissions operate hierarchically. Policies applied at the organization, folder, or project level flow downward to all nested resources. If you grant a freelance developer an administrative role at the project root so they can deploy a single Cloud Function, they inadvertently inherit visibility and control over Cloud Storage buckets, compute instances, database instances, and networking layers across that entire project.
According to Google Cloud documentation on using IAM securely, granting the least privilege and avoiding basic roles like Editor and Owner drastically reduces the blast radius of compromised credentials.
Three specific predefined roles dictate most day-to-day operations for Cloud Functions:
- Cloud Functions Developer (
roles/cloudfunctions.developer): Grants full control over functions, allowing the user to create, update, deploy, and delete function code and configurations. - Cloud Functions Viewer (
roles/cloudfunctions.viewer): Allows the user to list and inspect function properties, code metadata, and configuration without giving edit or execution rights. - Cloud Run Invoker ( roles/run.invoker ): Cloud Functions (2nd gen) run directly on Google Cloud Run infrastructure.
The biggest hidden vulnerability in contractor workflows is the uncontrolled creation of service account keys. If a contractor is granted the ability to create service accounts or generate keys (iam.serviceAccountKeys.create), they can export a private JSON key file to a personal laptop. Even if you remove the contractor's Google account from your IAM policy at the end of the sprint, that exported JSON key retains working access to your cloud resources indefinitely. As outlined in the Google Cloud documentation on managing service account keys, user-managed service account keys pose significant risk because they lack automatic rotation and can bypass standard user offboarding if left active.
Consider a realistic scenario: you hire an external specialist on a two-week sprint to build a webhook handler that ingests Stripe events and stores payloads. They need to deploy the function, test invocations with sample payloads, inspect execution logs, and resolve runtime errors. If you grant them Editor access at the project root to keep onboarding simple, that contractor can inspect customer data in your production databases, read private secrets in Google Secret Manager, and download storage assets. When the sprint concludes, you need that contractor completely removed—not left with lingering privileges, hidden service keys, or persistent tokens.
Map the Access a Contractor Actually Needs Before You Grant Anything
Before touching the Google Cloud Console or running a single gcloud CLI command, write down the contractor's exact deliverable and translate each requirement into the smallest feasible IAM role. Rather than relying on generalized job titles, map access directly to required functional deliverables.
To maintain operational control, build a resource-to-role map using this simple framework:
| Task Deliverable | Minimum Predefined Role | Resource Scope | Target Expiry |
|---|---|---|---|
| Attach execution identity | roles/iam.serviceAccountUser |
Dedicated runtime Service Account | Sprint completion date |
| Debug runtime exceptions | roles/logging.viewer |
Project or log bucket filter | Sprint completion date |
| Fetch database credentials | roles/secretmanager.secretAccessor |
Specific Secret resource | Sprint completion date |
Wherever the GCP resource model supports it, prefer resource-level IAM bindings over project-level bindings. For instance, rather than granting the developer role across the entire project, bind the role directly on the target function resource. This ensures the contractor cannot modify or overwrite existing production functions deployed alongside their work.
Next, decide whether to issue the contractor a company-managed Google Workspace identity (e.g., contractor@yourcompany.com) or grant access directly to their external email (e.g., dev@freelanceagency.io). Issuing a company-managed account lets your IT team enforce multi-factor authentication (MFA) standards and suspend login sessions globally. However, if you suspend the Workspace account without stripping the cloud bindings, the underlying permissions remain mapped to the user ID. Conversely, inviting an external identity directly to GCP saves a Workspace seat, but makes your revocation path entirely dependent on rigorous IAM cleanup. For teams handling wide toolsets, reviewing our guide on contractor access management checklists helps standardize these account provisioning decisions.
Scoping IAM Roles for Freelancers: A Practical Role Map
Precision is the core principle of IAM role scoping for freelancers. In GCP, granting a developer permission to deploy a function is insufficient on its own; a Cloud Function requires a runtime service account identity to execute its logic. If you do not explicitly assign an execution identity, GCP attempts to attach the default App Engine or Compute Engine service account, which often possesses broad editor privileges by default.
To establish safe boundaries, create a dedicated, low-privilege service account specifically for the function's runtime, such as sa-stripe-webhook-runner@project.iam.gserviceaccount.com. Then, grant the freelancer the roles/iam.serviceAccountUser role strictly on that specific service account:
# Grant the contractor permission to attach the runtime service account
gcloud iam service-accounts add-iam-policy-binding \
sa-stripe-webhook-runner@my-project-id.iam.gserviceaccount.com \
--member="user:contractor@example.com" \
--role="roles/iam.serviceAccountUser"
Combine this with the Developer role scoped to the target function:
# Grant function-scoped developer access
gcloud functions add-iam-policy-binding stripe-webhook-handler \
--region=us-central1 \
--member="user:contractor@example.com" \
--role="roles/cloudfunctions.developer"
Why You Must Avoid Basic Roles
As documented in the Google Cloud documentation on understanding roles, teams should avoid assigning basic roles such as roles/editor or roles/owner to temporary workers. While basic roles eliminate permission errors during development, they introduce significant operational liabilities:
- Full Data Read/Write:
roles/editorgrants access to read and overwrite every Cloud Storage bucket, Firestore collection, and BigQuery dataset in the project. - IAM Mutation:
roles/ownerpermits the contractor to grant privileges to other arbitrary accounts, create permanent backdoor service accounts, or disable administrative audit logging.
If your serverless function needs to access third-party API keys or database connection strings, manage those values using Google Secret Manager rather than raw environment variables. Do not grant the contractor project-wide secret manager roles. Instead, assign roles/secretmanager.secretAccessor directly to the function's runtime service account, or grant it to the contractor only on the specific development secret:
# Scope secret access to a single resource
gcloud secrets add-iam-policy-binding stripe-api-key \
--member="user:contractor@example.com" \
--role="roles/secretmanager.secretAccessor"
For contractors charged solely with testing endpoints or integrating external webhook triggers, do not grant development roles at all. Assign roles/run.invoker on the underlying 2nd-gen Cloud Run service.
Keep in mind that IAM changes in Google Cloud do not always take effect instantaneously. Google Cloud IAM relies on distributed caching mechanisms, meaning policy modifications propagate quickly but can take a brief window to reflect across all edge locations. Account for this slight delay during onboarding test handoffs rather than granting broader permissions in an attempt to troubleshoot propagation lag. For teams operating across multi-cloud ecosystems, the principles outlined in our guide to managing contractor access to Google Cloud IAM roles provide a structured approach to scoping down privileges.
Time-Boxing Access: Expiry, Conditions, and What GCP Does Not Do For You
Limiting the functional scope of an IAM role addresses what a contractor can touch, but operational security requires limiting how long they can touch it. Without rigid time-boxing, contractors retain privileges long after deliverables are approved, leaving inactive access vectors scattered across your projects.
Google Cloud IAM offers conditional role bindings using Common Expression Language (CEL). You can configure an IAM condition that invalidates a role binding once a specific calendar timestamp passes:
# Add an IAM binding with an absolute time-based expiration condition
gcloud projects add-iam-policy-binding my-project-id \
--member="user:contractor@example.com" \
--role="roles/cloudfunctions.developer" \
--condition='expression=request.time < timestamp("2026-10-15T00:00:00Z"),title=contractor_expiry,description=Expires on Oct 15 2026'
While CEL conditions work well for basic bindings, they are not a universal panacea:
- Not Globally Supported: IAM conditions are supported on many core services, but not every resource-level policy supports conditional CEL statements.
- Static and Silent: When a CEL condition expires, the binding ceases to function, but the stale policy statement remains in your IAM policy document indefinitely until an administrator manually cleans it up. This creates audit clutter.
- Zero Key Invalidation: An IAM condition on a user account does nothing to revoke or expire static credentials, such as service account JSON keys or third-party API tokens generated while the binding was active.
To eliminate long-lived credential risks for automated workflows, consult the Google Cloud documentation on best practices for using service accounts, which warns that long-lived keys represent a substantial attack surface and advises utilizing short-lived credentials instead.
When external contractors need to run automated CI/CD deployments from external environments like GitHub Actions or GitLab, leverage workload federation. As detailed in the Google Cloud documentation on Workload Identity Federation, external platforms can securely authenticate directly to Google Cloud using short-lived OpenID Connect (OIDC) tokens, eliminating the need to store long-lived service account keys in third-party CI secrets.
Where native GCP tooling leaves off, an automated governance layer becomes necessary. Native cloud tools do not proactively manage your operational offboarding schedule, alert cross-functional managers when contractor contracts wrap up, or clean up corresponding SaaS tools like GitHub, Slack, and project tracking boards. You must define a fixed operational duration, enforce the end date across all access vectors, and log every lifecycle state change.
Offboarding a Contractor From Cloud Functions: A Step-by-Step Checklist
When an engagement concludes, manual offboarding must be meticulous. Do not assume that deleting a single IAM binding covers all entry paths. Follow this operational checklist in order:
- Audit All Touched Resources: Review Cloud Logging to identify every service account, storage bucket, secret, and Cloud Function modified or accessed by the contractor's identity during their sprint.
-
Strip Resource and Project Bindings: Remove the contractor's email address from all project-level and resource-level IAM policies. Start at the narrowest scope (function-level, secret-level) and work outward to the project root:
gcloud functions remove-iam-policy-binding stripe-webhook-handler \ --region=us-central1 \ --member="user:contractor@example.com" \ --role="roles/cloudfunctions.developer" -
Locate and Destroy Service Account Keys: Enumerate all user-managed service account keys across the project. Identify any keys created during the contractor's tenure and delete them:
# List keys for a service account gcloud iam service-accounts keys list \ --iam-account=sa-stripe-webhook-runner@my-project-id.iam.gserviceaccount.com # Delete compromised or unneeded key gcloud iam service-accounts keys delete KEY_ID \ --iam-account=sa-stripe-webhook-runner@my-project-id.iam.gserviceaccount.com - Revoke Workspace and Identity Sessions: If the user was issued an internal account, suspend the Google Workspace profile and invalidate all active OAuth refresh tokens and active web sessions.
- Read Back Provider Policies to Confirm State: Programmatically retrieve the project and resource IAM policies using the CLI or API to prove that the identity no longer appears in any active bindings.
- Archive the Offboarding Evidence: Save the read-back policy outputs alongside contract closeout paperwork to ensure an auditable paper trail.
Teams managing broad infrastructure footprints can streamline this sequence by applying systematic approaches, similar to those outlined in our deep dive on automated offboarding vs. manual cleanup.
Verifying Revocation Instead of Trusting It
In IT operations, an unverified removal is merely an assumption. A human operator may run a removal command, encounter an unnoticed syntax error or network timeout, and assume the identity was deleted when it was not. True security requires automated verification: query provider state back directly from the source API and confirm the binding no longer exists.
Run a direct read-back verification against the project's IAM policy:
# Query IAM policy directly to confirm the user has no remaining roles
gcloud projects get-iam-policy my-project-id \
--flatten="bindings[].members" \
--format="table(bindings.role)" \
--filter="bindings.members:contractor@example.com"
If this command returns any output, access is still active.
Beyond direct user bindings, inspect secondary avenues through which a contractor might retain unauthorized control:
- Service Account Impersonation: Check if another service account the contractor still accesses holds the
roles/iam.serviceAccountTokenCreatorrole on administrative service accounts. - Cloud Scheduler Jobs: Contractors debugging serverless routines often configure automated Cron triggers. Verify that no lingering Cloud Scheduler jobs are firing functions using privileged execution contexts.
- Pub/Sub Subscriptions: Inspect Google Cloud Pub/Sub push subscriptions pointing to Cloud Function URLs to ensure no back-channel data ingestion paths remain open. Review our operational guide on managing contractor access to Google Cloud Pub/Sub for dedicated mitigation strategies.
Tempkey executes revocation and reads provider state back to confirm it. Because revocation depends on third-party provider APIs, Tempkey does not guarantee removal within any specific time and surfaces failed or unenforceable revokes in the audit log.
Keeping a Record You Can Show an Auditor or Client
When enterprise clients, external security assessors, or prospective buyers review your security posture, they invariably ask to see contractor lifecycle logs. Simply telling an auditor that you remove contractors when their work wraps up is not sufficient; you must present clear documentation of the grant lifecycle.
A defensible access record must capture seven structural data points:
- Principal Identity: The specific verified account or email granted access.
- Assigned Role: The exact predefined or custom IAM permissions provisioned.
- Resource Target: The granular Cloud Function, Secret, or project ID scoped.
- Grant Timestamp: The exact date, time, and administrative identity approving access.
- Scheduled Expiration: The agreed-upon operational expiration milestone.
- Revocation Timestamp: The exact time access was systematically removed.
- Verification Evidence: The API confirmation proving the removal succeeded.
Tempkey gives you an exportable, append-only audit trail to support your own compliance and offboarding records. Tempkey does not currently hold SOC 2, ISO 27001, HIPAA, or PCI certification. Source: Tempkey source.
Pairing this exported trail with internal project deliverables, client contracts, and change management tickets creates a transparent, unbroken operational record that satisfies both internal stakeholders and external clients.
Where a Grant-and-Expire Tool Fits Alongside Google Cloud IAM
Google Cloud IAM is an exceptional access control engine: it allows granular policy definitions, robust condition checking, and deep service integration. However, GCP IAM is an infrastructure policy layer, not an automated offboarding manager for small operations teams. It does not track contractor contract dates, send proactive offboarding warnings to project managers, or coordinate credentials across external SaaS stacks.
Tempkey sits directly alongside infrastructure providers to enforce time boundaries on temporary access:
- Tempkey natively enforces access on 10 providers — Slack, Google Workspace, Microsoft 365, GitHub, GitLab, Zoom, AWS IAM, Figma, Dropbox, and Asana. Notion and Trello are limited-native (tracked, not fully enforced) and Zapier/Make are best-effort webhook bridges without automated verification.
- Automatic Slack deactivation uses Slack's SCIM API, which Slack offers on its Business+ and Enterprise plans. On other Slack plans, Tempkey verifies that access was removed rather than removing it.
For Google Cloud Functions specifically, the operational workflow is straightforward: configure the minimal IAM role directly within the GCP Console or CLI, register the contractor's grant and scheduled duration within Tempkey, and export the audit record when the engagement concludes.
Provider admin tokens are write-only in the browser and encrypted at rest using AWS KMS in production; they are never displayed again after submission. Sign-in is passwordless — magic links plus WebAuthn/passkeys. Tempkey does not offer SSO/SAML today. Furthermore, Tempkey is a hosted cloud service; there is no self-hosted or on-premise deployment option.
For teams looking to integrate access provisioning with automated workflows, Tempkey has a public REST API covering grants, extension, revocation with read-back verification, integrations, the audit trail, and API-key management. Keys are bearer tokens with read/write scopes; an OpenAPI 3 spec is published at api.tempkey.io/openapi.json and human docs at tempkey.io/docs/api.
Plans are month-to-month (Free / $39 Team / $99 Business) with active-grant limits of 2 / 10 / 30. Business includes extended audit-history retention. Enterprise IT suites (e.g. Rippling, Okta, JumpCloud) bundle contractor offboarding inside larger, per-employee-priced products; their pricing changes often and is frequently quote-gated. Tempkey prices per active contractor grant.
Common Mistakes That Leave a Contractor With Standing Access
Security issues in small and mid-market teams rarely stem from sophisticated cryptographic exploits; they happen because simple operational oversights leave standing access open. Avoid these five frequent mistakes:
-
Granting
roles/editor"Just for Now": Teams often assign Editor access during project kickoff to avoid troubleshooting deployment configurations, intending to restrict it later. That cleanup rarely happens, leaving developers with excessive standing access for months. -
Ignoring Service Account Keys: Stripping a developer's identity from a GCP project while leaving a service account JSON key on their local device leaves your environment exposed. Block key creation privileges at the organizational policy level using
constraints/iam.disableServiceAccountKeyCreation. -
Removing Individual Accounts While Leaving Group Grants: If a contractor is assigned permissions via an internal Google Workspace group (such as
contractors@company.com), removing their direct IAM binding does nothing if their Workspace identity remains in that group. - Assuming Google Workspace Suspension Cleans Up Cloud Resources: Suspending a corporate email address stops web console logins, but any standalone personal identities, OAuth application tokens, or external API keys generated during the contract continue working until systematically revoked.
- Failing to Record an Expiry Date at Provisioning: If an expiration date is not recorded on day one, access persists indefinitely. Every temporary grant requires an explicit operational end date.
Frequently Asked Questions
What is the minimum IAM role a contractor needs to deploy a Google Cloud Function?
To deploy and update a Cloud Function, a contractor needs the Cloud Functions Developer role (roles/cloudfunctions.developer) scoped to the specific function, combined with the Service Account User role (roles/iam.serviceAccountUser) assigned specifically to the function's designated runtime service account. If the developer also needs to view build logs to debug failed deployments, add the Logs Viewer role (roles/logging.viewer) scoped to the project or relevant log bucket.
Can IAM conditions in Google Cloud automatically revoke contractor access?
IAM conditions using Common Expression Language (CEL) can expire access based on a calendar timestamp (request.time < timestamp(...)). Once the specified timestamp passes, the binding stops granting permissions. However, the condition statement remains in your IAM policy document until manually removed, and IAM conditions do not invalidate service account JSON keys or external tool access.
How can an operations team verify that contractor access was actually removed?
Do not rely on the assumption that a removal command executed cleanly. Query the resource's IAM policy directly using gcloud functions get-iam-policy FUNCTION_NAME or gcloud projects get-iam-policy PROJECT_ID with a filter on the contractor's identity. If the command returns no bindings for that principal, access is confirmed removed at the policy level.