Assistant de synchronisation Google Calendar

VérifiéSûr

Guide la configuration d'un compte de service Google Cloud pour synchroniser des événements avec Google Calendar. Utilise l'interface gcloud pour automatiser la plupart des étapes, avec des interventions manuelles pour l'authentification navigateur et la Console Admin. Utile lors de la mise en place d'une synchronisation de calendrier, de comptes de service ou d'une délégation à l'échelle du domaine.

Spar Skills Guide Bot
DeveloppementIntermédiaire
17002/06/2026
Claude Code
#google-calendar#calendar-sync#service-account#gcloud#api-integration

Recommandé pour

Notre avis

Ce guide configure l'intégration de l'API Google Calendar avec un compte de service via une procédure pas à pas, en utilisant gcloud pour automatiser la plupart des étapes.

Points forts

  • Automatisation des commandes gcloud pour réduire les erreurs manuelles.
  • Détection de l'état actuel pour reprendre à l'étape appropriée.
  • Séparation claire entre actions automatiques et actions humaines requises.

Limites

  • Nécessite des interventions manuelles dans la console Google Cloud et l'Admin Console Google Workspace.
  • Dépend de l'installation de gcloud CLI.
  • Ne gère pas la synchronisation bidirectionnelle en temps réel.
Quand l'utiliser

Utilisez cette compétence pour mettre en place une synchronisation d'événements avec Google Calendar via un compte de service et une délégation à l'échelle du domaine.

Quand l'éviter

Ne l'utilisez pas si vous avez besoin d'une synchronisation bidirectionnelle en temps réel ou si vous ne disposez pas des accès administrateur Google Workspace nécessaires.

Analyse de sécurité

Sûr
Score qualité90/100

The skill uses only safe bash commands for system state checks and guides the user through standard Google Cloud CLI operations. No destructive actions, data exfiltration, or obfuscation are present.

Aucun point d'attention détecté

Exemples

Set up Google Calendar sync
Set up Google Calendar API integration with a service account for my application.
Configure service account
Start the Google Calendar sync wizard to create a service account and enable calendar API.
Resume calendar setup
I need to finish setting up Google Calendar sync. Check the current state and continue from where I left off.

name: google-calendar-sync description: Wizard to set up Google Calendar API integration with service account authentication. Use when setting up calendar sync, Google Calendar API, service accounts, or domain-wide delegation. allowed-tools:

  • Bash
  • Read
  • Write
  • Edit
  • Glob
  • Grep
  • AskUserQuestion

Google Calendar Sync Setup Wizard

This skill guides you through configuring a Google Cloud service account to sync events with Google Calendar. It uses gcloud CLI to automate most Google Cloud steps.

How This Works

This is a guided wizard with two types of steps:

  • AGENT ACTION - Steps you perform automatically via gcloud CLI or code
  • HUMAN ACTION REQUIRED - Steps requiring manual intervention (browser auth, Admin Console)

When you hit a HUMAN ACTION REQUIRED step, use the AskUserQuestion tool to pause and wait for confirmation before continuing.

State Detection

Run this first to determine current progress:

echo "=== GOOGLE CALENDAR SYNC STATE CHECK ==="

# Check 1: gcloud installed?
if command -v gcloud &> /dev/null; then
  echo "gcloud: INSTALLED"
else
  echo "gcloud: MISSING -> Start at Step 0.1"
fi

# Check 2: gcloud authenticated?
if gcloud auth list --filter="status:ACTIVE" --format="value(account)" 2>/dev/null | grep -q "@"; then
  echo "gcloud_auth: AUTHENTICATED as $(gcloud auth list --filter='status:ACTIVE' --format='value(account)' | head -1)"
else
  echo "gcloud_auth: NOT_AUTHENTICATED -> Complete Step 0.2"
fi

# Check 3: Project configured?
PROJECT=$(gcloud config get-value project 2>/dev/null)
if [ -n "$PROJECT" ] && [ "$PROJECT" != "(unset)" ]; then
  echo "project: CONFIGURED as $PROJECT"
else
  echo "project: NOT_SET -> Start at Phase 2"
fi

# Check 4: Calendar API enabled?
if [ -n "$PROJECT" ] && [ "$PROJECT" != "(unset)" ]; then
  if gcloud services list --enabled --filter="name:calendar" --format="value(name)" 2>/dev/null | grep -q "calendar"; then
    echo "calendar_api: ENABLED"
  else
    echo "calendar_api: NOT_ENABLED -> Complete Step 2.2"
  fi
fi

# Check 5: Service account exists?
if [ -n "$PROJECT" ] && [ "$PROJECT" != "(unset)" ]; then
  SA_EMAIL="calendar-sync@${PROJECT}.iam.gserviceaccount.com"
  if gcloud iam service-accounts describe "$SA_EMAIL" &>/dev/null; then
    echo "service_account: EXISTS ($SA_EMAIL)"
  else
    echo "service_account: NOT_FOUND -> Complete Step 2.3"
  fi
fi

# Check 6: Credentials file exists?
if [ -f "google-credentials.json" ]; then
  echo "credentials_file: EXISTS (needs base64 encoding)"
elif [ -f "google-credentials.b64" ]; then
  echo "credentials_b64: EXISTS"
else
  echo "credentials: NOT_FOUND -> Complete Step 2.5"
fi

# Check 7: Environment variables?
echo "=== ENV VAR CHECK ==="
if [ -f ".env.local" ]; then
  grep -q "GOOG_CREDENTIALS_JSON" .env.local 2>/dev/null && echo "GOOG_CREDENTIALS_JSON: SET in .env.local" || echo "GOOG_CREDENTIALS_JSON: MISSING"
  grep -q "GOOG_CALENDAR_IMPERSONATE_USER" .env.local 2>/dev/null && echo "GOOG_CALENDAR_IMPERSONATE_USER: SET in .env.local" || echo "GOOG_CALENDAR_IMPERSONATE_USER: MISSING"
  grep -q "GOOG_CALENDAR_ID" .env.local 2>/dev/null && echo "GOOG_CALENDAR_ID: SET in .env.local" || echo "GOOG_CALENDAR_ID: MISSING"
elif [ -f ".env" ]; then
  grep -q "GOOG_CREDENTIALS_JSON" .env 2>/dev/null && echo "GOOG_CREDENTIALS_JSON: SET in .env" || echo "GOOG_CREDENTIALS_JSON: MISSING"
  grep -q "GOOG_CALENDAR_IMPERSONATE_USER" .env 2>/dev/null && echo "GOOG_CALENDAR_IMPERSONATE_USER: SET in .env" || echo "GOOG_CALENDAR_IMPERSONATE_USER: MISSING"
  grep -q "GOOG_CALENDAR_ID" .env 2>/dev/null && echo "GOOG_CALENDAR_ID: SET in .env" || echo "GOOG_CALENDAR_ID: MISSING"
else
  echo "env_file: NOT_FOUND -> Complete Phase 5"
fi

# Check 8: googleapis package?
if [ -f "package.json" ]; then
  grep -q "googleapis" package.json && echo "googleapis: INSTALLED" || echo "googleapis: NOT_INSTALLED -> Complete Step 5.4"
fi

echo "=== END STATE CHECK ==="

Based on the state check output, skip to the appropriate phase.


Prerequisites (Agent Checks + Human Installs)

Check for required tools and guide the user through installing anything missing.

Step 0.1: Check for gcloud CLI

if command -v gcloud &> /dev/null; then
  echo "INSTALLED: gcloud $(gcloud --version 2>/dev/null | head -1)"
else
  echo "NOT_INSTALLED: gcloud"
fi

If NOT_INSTALLED, use AskUserQuestion to prompt installation:

Install the Google Cloud CLI:

macOS (Homebrew): brew install google-cloud-sdk

macOS (Manual):

curl https://sdk.cloud.google.com | bash
exec -l $SHELL

Linux (Debian/Ubuntu):

sudo apt-get install apt-transport-https ca-certificates gnupg curl
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list
sudo apt-get update && sudo apt-get install google-cloud-cli

Linux (Other):

curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-linux-x86_64.tar.gz
tar -xf google-cloud-cli-linux-x86_64.tar.gz
./google-cloud-sdk/install.sh

Windows: Download from https://cloud.google.com/sdk/docs/install

Step 0.2: Check gcloud authentication

if gcloud auth list --filter="status:ACTIVE" --format="value(account)" 2>/dev/null | head -1 | grep -q "@"; then
  echo "AUTHENTICATED: $(gcloud auth list --filter='status:ACTIVE' --format='value(account)' | head -1)"
else
  echo "NOT_AUTHENTICATED"
fi

If NOT_AUTHENTICATED, prompt the user to run:

gcloud auth login

This opens a browser for Google account authentication. Wait for confirmation.

Step 0.3: Check for Node.js

if command -v node &> /dev/null; then
  echo "INSTALLED: node $(node --version)"
else
  echo "NOT_INSTALLED: node"
fi

If NOT_INSTALLED, prompt installation:

  • macOS: brew install node
  • Linux: curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - && sudo apt-get install -y nodejs
  • Or use nvm: https://github.com/nvm-sh/nvm

Step 0.4: Check for package manager

if command -v pnpm &> /dev/null; then
  echo "INSTALLED: pnpm $(pnpm --version)"
elif command -v yarn &> /dev/null; then
  echo "INSTALLED: yarn $(yarn --version)"
elif command -v npm &> /dev/null; then
  echo "INSTALLED: npm $(npm --version)"
else
  echo "NOT_INSTALLED: package manager"
fi

Phase 1: Project Discovery

Step 1.1: Check existing Google credentials

grep -r "GOOG_" .env* 2>/dev/null || echo "No existing Google config found"
grep -r "GOOGLE_" .env* 2>/dev/null || echo "No existing Google config found"

Step 1.2: List existing gcloud projects

gcloud projects list --format="table(projectId,name,createTime)"

Use AskUserQuestion: Use an existing project or create a new one?


Phase 2: Google Cloud Setup (Mostly Automated)

Step 2.1: Create or select project

Create new project:

PROJECT_ID="calendar-sync-$(date +%s | tail -c 7)"
PROJECT_NAME="Calendar Sync"

gcloud projects create "$PROJECT_ID" --name="$PROJECT_NAME"
gcloud config set project "$PROJECT_ID"

Or select existing:

gcloud config set project YOUR_PROJECT_ID

Step 2.2: Enable Google Calendar API

gcloud services enable calendar-json.googleapis.com

Verify:

gcloud services list --enabled --filter="name:calendar"

Step 2.3: Create service account

SERVICE_ACCOUNT_NAME="calendar-sync"
PROJECT_ID=$(gcloud config get-value project)

gcloud iam service-accounts create "$SERVICE_ACCOUNT_NAME" \
  --display-name="Calendar Sync Service" \
  --description="Manages calendar events for the application"

Step 2.4: Grant permissions

PROJECT_ID=$(gcloud config get-value project)
SERVICE_ACCOUNT_EMAIL="calendar-sync@${PROJECT_ID}.iam.gserviceaccount.com"

gcloud projects add-iam-policy-binding "$PROJECT_ID" \
  --member="serviceAccount:${SERVICE_ACCOUNT_EMAIL}" \
  --role="roles/owner"

Step 2.5: Create service account key

PROJECT_ID=$(gcloud config get-value project)
SERVICE_ACCOUNT_EMAIL="calendar-sync@${PROJECT_ID}.iam.gserviceaccount.com"

gcloud iam service-accounts keys create ./google-credentials.json \
  --iam-account="$SERVICE_ACCOUNT_EMAIL"

If this fails with "Key creation is not allowed", prompt user:

Disable the org policy constraint. Run (requires org admin):

gcloud org-policies reset iam.disableServiceAccountKeyCreation --project="$PROJECT_ID"

Or via Console: Organization Policies > search iam.disableServiceAccountKeyCreation > Override > Not enforced.

Step 2.6: Base64 encode credentials

cat google-credentials.json | base64 -w 0 > google-credentials.b64
echo "Credentials encoded to google-credentials.b64"
cat google-credentials.b64

Step 2.7: Get Client ID for domain-wide delegation

PROJECT_ID=$(gcloud config get-value project)
SERVICE_ACCOUNT_EMAIL="calendar-sync@${PROJECT_ID}.iam.gserviceaccount.com"

gcloud iam service-accounts describe "$SERVICE_ACCOUNT_EMAIL" \
  --format="value(uniqueId)"

Save this Client ID for Phase 3.


Phase 3: Domain-Wide Delegation (Human Action Required)

Note: Only required for Google Workspace. Skip if using personal Gmail with calendar sharing.

Step 3.1: Configure Domain-Wide Delegation

HUMAN ACTION REQUIRED - Use AskUserQuestion to guide:

  1. Go to Google Admin Console
  2. Navigate to Security > Access and data control > API controls
  3. Click Manage Domain Wide Delegation
  4. Click Add new
  5. Enter the Client ID from Step 2.7
  6. Add OAuth scope: https://www.googleapis.com/auth/calendar.events
  7. Click Authorize

Wait for "Delegation configured" confirmation.

Step 3.2: Identify Impersonation User

HUMAN ACTION REQUIRED

Ask for the email of a Google Workspace user who has access to the target calendar. This becomes GOOG_CALENDAR_IMPERSONATE_USER.


Phase 4: Get Calendar ID

Step 4.1: Find the calendar ID

Option A - Primary calendar: The calendar ID is the user's email address.

Option B - Shared/secondary calendar:

HUMAN ACTION REQUIRED - Use AskUserQuestion:

  1. Open Google Calendar
  2. Find your calendar in the left sidebar
  3. Click the three dots > Settings and sharing
  4. Scroll to Integrate calendar
  5. Copy the Calendar ID (looks like abc123@group.calendar.google.com)

Phase 5: Environment Configuration

Step 5.1: Add environment variables

After getting the base64 credentials, impersonation user, and calendar ID from the user:

CREDS_B64=$(cat google-credentials.b64)
IMPERSONATE_USER="user@yourdomain.com"  # Get from user
CALENDAR_ID="calendar-id@group.calendar.google.com"  # Get from user

cat >> .env.local << EOF

# Google Calendar Integration
GOOG_CREDENTIALS_JSON="$CREDS_B64"
GOOG_CALENDAR_IMPERSONATE_USER="$IMPERSONATE_USER"
GOOG_CALENDAR_ID="$CALENDAR_ID"
EOF

Step 5.2: Add to .env.example

cat >> .env.example << 'EOF'

# Google Calendar Integration
# See: .claude/skills/google-calendar-sync/SKILL.md
GOOG_CREDENTIALS_JSON=
GOOG_CALENDAR_IMPERSONATE_USER=
GOOG_CALENDAR_ID=
EOF

Step 5.3: Update .gitignore

echo "google-credentials.json" >> .gitignore
echo "google-credentials.b64" >> .gitignore

Step 5.4: Install googleapis package

if command -v pnpm &> /dev/null; then
  pnpm add googleapis
elif command -v yarn &> /dev/null; then
  yarn add googleapis
else
  npm install googleapis
fi

Step 5.5: Create calendar utility module

Create lib/google-calendar.ts with the following content:

import { google } from 'googleapis'

function getCalendarClient() {
  const credentials = JSON.parse(
    Buffer.from(process.env.GOOG_CREDENTIALS_JSON!, 'base64').toString()
  )

  const auth = new google.auth.GoogleAuth({
    credentials,
    scopes: ['https://www.googleapis.com/auth/calendar.events'],
    clientOptions: {
      subject: process.env.GOOG_CALENDAR_IMPERSONATE_USER,
    },
  })

  return google.calendar({ version: 'v3', auth })
}

export async function createCalendarEvent(event: {
  summary: string
  description?: string
  start: Date
  end: Date
  location?: string
}) {
  const calendar = getCalendarClient()
  const calendarId = process.env.GOOG_CALENDAR_ID!

  return calendar.events.insert({
    calendarId,
    requestBody: {
      summary: event.summary,
      description: event.description,
      location: event.location,
      start: {
        dateTime: event.start.toISOString(),
        timeZone: 'UTC',
      },
      end: {
        dateTime: event.end.toISOString(),
        timeZone: 'UTC',
      },
    },
  })
}

export async function updateCalendarEvent(
  eventId: string,
  event: {
    summary?: string
    description?: string
    start?: Date
    end?: Date
    location?: string
  }
) {
  const calendar = getCalendarClient()
  const calendarId = process.env.GOOG_CALENDAR_ID!

  return calendar.events.patch({
    calendarId,
    eventId,
    requestBody: {
      ...(event.summary && { summary: event.summary }),
      ...(event.description && { description: event.description }),
      ...(event.location && { location: event.location }),
      ...(event.start && {
        start: { dateTime: event.start.toISOString(), timeZone: 'UTC' },
      }),
      ...(event.end && {
        end: { dateTime: event.end.toISOString(), timeZone: 'UTC' },
      }),
    },
  })
}

export async function deleteCalendarEvent(eventId: string) {
  const calendar = getCalendarClient()
  const calendarId = process.env.GOOG_CALENDAR_ID!

  return calendar.events.delete({
    calendarId,
    eventId,
  })
}

export async function listCalendarEvents(options?: {
  timeMin?: Date
  timeMax?: Date
  maxResults?: number
}) {
  const calendar = getCalendarClient()
  const calendarId = process.env.GOOG_CALENDAR_ID!

  return calendar.events.list({
    calendarId,
    timeMin: options?.timeMin?.toISOString(),
    timeMax: options?.timeMax?.toISOString(),
    maxResults: options?.maxResults ?? 10,
    singleEvents: true,
    orderBy: 'startTime',
  })
}

Phase 6: Verification

Step 6.1: Verify environment variables

echo "=== VERIFYING ENV VARS ==="

if [ -f ".env.local" ]; then
  source .env.local 2>/dev/null
elif [ -f ".env" ]; then
  source .env 2>/dev/null
fi

MISSING=""
[ -z "$GOOG_CREDENTIALS_JSON" ] && MISSING="$MISSING GOOG_CREDENTIALS_JSON"
[ -z "$GOOG_CALENDAR_IMPERSONATE_USER" ] && MISSING="$MISSING GOOG_CALENDAR_IMPERSONATE_USER"
[ -z "$GOOG_CALENDAR_ID" ] && MISSING="$MISSING GOOG_CALENDAR_ID"

if [ -n "$MISSING" ]; then
  echo "MISSING ENV VARS:$MISSING"
  echo "Please complete Phase 5 before testing."
  exit 1
else
  echo "All required env vars are set!"
fi

Step 6.2: Test connection

if [ -f ".env.local" ]; then
  export $(grep -v '^#' .env.local | xargs)
elif [ -f ".env" ]; then
  export $(grep -v '^#' .env | xargs)
fi

node -e "
const { google } = require('googleapis');

if (!process.env.GOOG_CREDENTIALS_JSON) {
  console.error('ERROR: GOOG_CREDENTIALS_JSON not set');
  process.exit(1);
}

const creds = JSON.parse(Buffer.from(process.env.GOOG_CREDENTIALS_JSON, 'base64').toString());
const auth = new google.auth.GoogleAuth({
  credentials: creds,
  scopes: ['https://www.googleapis.com/auth/calendar.events'],
  clientOptions: { subject: process.env.GOOG_CALENDAR_IMPERSONATE_USER }
});
const calendar = google.calendar({ version: 'v3', auth });

console.log('Testing connection to calendar:', process.env.GOOG_CALENDAR_ID);
console.log('Impersonating user:', process.env.GOOG_CALENDAR_IMPERSONATE_USER);

calendar.events.list({
  calendarId: process.env.GOOG_CALENDAR_ID,
  maxResults: 5,
  timeMin: new Date().toISOString(),
  singleEvents: true,
  orderBy: 'startTime'
}).then(r => {
  console.log('SUCCESS! Connection working.');
  console.log('Found', r.data.items?.length || 0, 'upcoming events');
  if (r.data.items?.length > 0) {
    console.log('Next event:', r.data.items[0].summary);
  }
}).catch(e => {
  console.error('ERROR:', e.message);
  if (e.message.includes('insufficient')) {
    console.error('-> Check domain-wide delegation is configured correctly');
  }
  if (e.message.includes('not found')) {
    console.error('-> Check GOOG_CALENDAR_ID is correct');
  }
  process.exit(1);
});
"

Step 6.3: Cleanup sensitive files

rm -f google-credentials.json google-credentials.b64
echo "Sensitive files cleaned up"

Troubleshooting

"Insufficient Permission" errors

  • Verify domain-wide delegation uses correct Client ID
  • Scope must match exactly: https://www.googleapis.com/auth/calendar.events
  • Impersonation user must have calendar access

"Key creation is not allowed"

gcloud org-policies reset iam.disableServiceAccountKeyCreation --project="$(gcloud config get-value project)"

Find calendar ID via API

node -e "
const { google } = require('googleapis');
const creds = JSON.parse(Buffer.from(process.env.GOOG_CREDENTIALS_JSON, 'base64').toString());
const auth = new google.auth.GoogleAuth({
  credentials: creds,
  scopes: ['https://www.googleapis.com/auth/calendar.readonly'],
  clientOptions: { subject: process.env.GOOG_CALENDAR_IMPERSONATE_USER }
});
const calendar = google.calendar({ version: 'v3', auth });
calendar.calendarList.list().then(r => {
  r.data.items?.forEach(c => console.log(c.id, '-', c.summary));
}).catch(e => console.error('Error:', e.message));
"

Checklist

  • [ ] gcloud CLI installed and authenticated
  • [ ] Google Cloud project created/selected
  • [ ] Google Calendar API enabled
  • [ ] Service account created
  • [ ] Service account key generated and base64-encoded
  • [ ] Domain-wide delegation configured (Workspace only)
  • [ ] Environment variables added to .env
  • [ ] googleapis package installed
  • [ ] Calendar utility module created
  • [ ] Connection tested
  • [ ] Sensitive files cleaned up
Skills similaires