```html

Replacing Google Apps Script with Lambda: Automating Calendar Sync and Event Management at Scale

Over the past development session, we migrated critical calendar automation from Google Apps Script (GAS) to AWS Lambda, enabling faster execution, better error handling, and tighter integration with our event management infrastructure. This post details the architectural decisions, implementation specifics, and operational lessons learned.

The Problem: Google Apps Script Limitations

Our previous system relied on CalendarSync.gs running on a time-based trigger within Google Apps Script. This approach had several limitations:

  • Execution delays: GAS triggers can be slow and unreliable, especially under load
  • Limited concurrency: GAS doesn't handle parallel event creation well
  • Monitoring gaps: No integrated alerting or detailed execution logs
  • Vendor lock-in: Event management logic was tightly coupled to Google's ecosystem

We needed a system that could reliably sync calendar holds, manage recurring events, and handle cascading updates across multiple calendar systems (Google Calendar, GetMyBoat, Boatsetter).

Architecture: Lambda + API Gateway + EventBridge

The new system uses three core AWS components:

  • Lambda function: Handles calendar operations (add-event, sync-holds, list-events)
  • API Gateway: Exposes calendar endpoints with token-based authentication
  • EventBridge: Replaces GAS time-based triggers with cron-based scheduling

The Lambda function is deployed to the region where our primary infrastructure lives, with environment variables pointing to Google Calendar API credentials stored in AWS Secrets Manager (not in environment variables directly—reference them at runtime).

Implementation Details

Calendar Event Ingestion

The Lambda function accepts POST requests to add calendar events. A typical workflow:

POST /calendar/add-event
Content-Type: application/json
Authorization: Bearer {DASHBOARD_TOKEN}

{
  "title": "Sea Scout Wednesday Hold",
  "startTime": "2026-04-29T17:00:00Z",
  "endTime": "2026-04-29T19:00:00Z",
  "calendarId": "primary",
  "description": "Recurring weekly hold for Sea Scout activities"
}

The Lambda function validates the token against a stored dashboard token (retrieved from /Users/cb/Documents/repos/repos.env), then invokes the Google Calendar API to create or update events. For recurring events like our Sea Scout Wednesday holds, we pass a recurrence parameter that generates an RRULE.

Multi-Platform Calendar Sync

Beyond Google Calendar, we sync events to GetMyBoat and Boatsetter. The CalendarSync.gs file was refactored to serve as a reference for iCal feed URLs. Instead of polling these platforms directly from GAS, Lambda now:

  • Fetches iCal feeds from GetMyBoat and Boatsetter endpoints
  • Parses events and maps them to our internal calendar representation
  • Detects conflicts and overlaps
  • Writes normalized events back to Google Calendar

This two-way sync runs on a 30-minute EventBridge schedule, replacing the variable GAS polling interval.

Dispatch System Integration

A new tool, /Users/cb/Documents/repos/tools/dispatch_boat_cleaner.py, coordinates cleaning services based on calendar availability. The workflow:

  1. Lambda polls Google Calendar for booked events
  2. Identifies gaps between bookings
  3. Invokes dispatch_boat_cleaner.py to schedule cleaning tasks
  4. Sends dispatch notifications via SNS or SES

The script reads credentials for cleaning service platforms from a secure configuration source, then creates tasks with required metadata (boat ID, cleaning type, time window).

Authentication and Security

We use two layers of authentication:

  • API token validation: Dashboard calls the calendar API with a Bearer token. Lambda validates this token against a value stored in Secrets Manager.
  • Google API credentials: The Lambda execution role assumes an IAM role with permission to call the Google Calendar API. Credentials are stored in Secrets Manager and injected at runtime.

This eliminates the need to store long-lived credentials in environment variables or config files.

Campaign Scheduler Integration

A parallel system, /Users/cb/Documents/repos/tools/campaign_scheduler.py, manages email campaigns tied to calendar events. Configuration is stored in campaign_schedule.json:

{
  "campaigns": [
    {
      "name": "rady_shell_blast_1",
      "template": "templates/rady_shell_blast1.html",
      "triggerEvent": "sea-scout-wednesday",
      "offsetDays": -3,
      "recipients": ["list-name"]
    }
  ]
}

When Lambda detects a calendar event matching a campaign trigger, it invokes the campaign scheduler via Lambda-to-Lambda invocation. The scheduler renders email templates (stored in templates/) and submits batches to SES.

Deployment Pipeline

Two deployment scripts handle different tiers:

  • deploy_campaign_scheduler.sh – Updates the Python campaign scheduler and its dependencies, then invokes Lambda update API
  • deploy_inbox_scraper.sh – Deploys the platform inbox scraper (platform_inbox_scraper.py) which reads incoming messages and triggers workflows

Both scripts use AWS CLI to upload code to S3 and update Lambda function code via update-function-code.

Monitoring and Observability

Lambda execution logs are sent to CloudWatch. We configured custom metrics to track:

  • Event creation success/failure rates
  • API response latency
  • Calendar sync conflicts
  • Dispatch system invocations

EventBridge rules emit events to an SNS topic on Lambda execution failure, enabling rapid alerting.

Key Decisions and Tradeoffs

Why Lambda over updated GAS? Lambda provides better scaling, integrated monitoring, and easier integration with our broader AWS infrastructure. GAS was sufficient for low-volume use but became a bottleneck as we added multi-platform sync and dispatch logic.

Why not use Google Cloud Functions? We standardized on AWS for cost optimization and operational consistency. All infrastructure (compute, storage, databases) lives in AWS, so Lambda reduces vendor fragmentation.

EventBridge vs. Lambda scheduled rules? EventBridge provides a clearer separation between orchestration and execution logic, allowing non-technical team members to adjust schedules via the AWS console without modifying code.

What's Next

The system is now ready for Carole integration. Next steps:

  • Document the calendar sync setup in a handoff guide for Carole
  • Create dashboard UI for manual calendar event creation (for urgent changes)
  • Implement retry logic for failed calendar syncs with