identity access management

Implementing Google Workspace Admin Security

Implements comprehensive Google Workspace security hardening including admin console configuration, phishing-resistant MFA enforcement, DLP policies, email authentication (SPF/DKIM/DMARC), OAuth app control, and external sharing restrictions. Activates for requests involving Google Workspace hardening, G Suite security configuration, or cloud office security administration.

admin-securitycloud-securitydlpdmarcgoogle-workspacemfaoauth
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

When to Use

  • Deploying or hardening a Google Workspace environment for enterprise use
  • CIS benchmark compliance assessment for Google Workspace configuration
  • Protecting against business email compromise (BEC) and phishing attacks targeting Google accounts
  • Implementing Data Loss Prevention controls for Gmail and Google Drive
  • Restricting OAuth application access and third-party integrations
  • Configuring admin account security with Advanced Protection Program enrollment

Do not use for Microsoft 365 environments; Google Workspace has distinct admin console settings and API configurations that differ from Azure AD/Entra ID controls.

Prerequisites

  • Google Workspace Business Plus, Enterprise Standard, or Enterprise Plus license
  • Super Admin access to the Google Admin Console (admin.google.com)
  • DNS management access for SPF, DKIM, and DMARC record configuration
  • Google Cloud Identity or Cloud Identity Premium for advanced security features
  • FIDO2 security keys for super admin accounts (YubiKey 5 Series recommended)

Workflow

Step 1: Harden Super Admin Accounts

Secure the highest-privilege accounts in the Google Workspace tenant:

# Google Workspace Admin SDK - configure admin account security
# Using gam (Google Apps Manager) CLI tool
 
# List all super admin accounts for audit
gam print admins role "Super Admin" > super_admins.csv
echo "Review and minimize super admin count (recommended: 2-3 maximum)"
 
# Enforce Advanced Protection Program for super admins
# APP provides strongest account protections:
# - Requires FIDO2 security key for sign-in
# - Blocks third-party app access to Gmail and Drive
# - Enhanced account recovery verification
gam update user superadmin@corp.com \
    advanced_protection true
 
# Create dedicated break-glass admin account
gam create user breakglass-admin@corp.com \
    firstname "Break" lastname "Glass Admin" \
    password "$(openssl rand -base64 32)" \
    changepassword true \
    org "/Emergency Accounts"
 
# Assign super admin role to break-glass account
gam create admin breakglass-admin@corp.com "Super Admin"
 
# Configure admin activity alerts
# Alert Center API - create alert for admin actions
cat > admin_alert_policy.json << 'EOF'
{
  "alertPolicies": [
    {
      "name": "Super Admin Sign-In Alert",
      "conditions": {
        "eventType": "login",
        "filterCriteria": "actor.adminRole=SUPER_ADMIN"
      },
      "notifications": {
        "email": ["security-team@corp.com"],
        "webhook": "https://siem.corp.com/webhook/google-admin"
      }
    },
    {
      "name": "Admin Role Change Alert",
      "conditions": {
        "eventType": "admin_role_change"
      },
      "notifications": {
        "email": ["security-team@corp.com"]
      }
    }
  ]
}
EOF

Step 2: Enforce Phishing-Resistant Multi-Factor Authentication

Configure MFA policies that eliminate phishable authentication factors:

# Enforce 2-Step Verification for all organizational units
# Using Admin SDK Directory API
 
# Enable 2SV enforcement for the entire organization
gam update org "/" settings \
    2sv_enforcement true \
    2sv_enrollment_grace_period 14 \
    2sv_new_user_enrollment_period 1
 
# Configure allowed 2SV methods - restrict to phishing-resistant only
# For high-security OUs: Security keys only
gam update org "/Executive" settings \
    2sv_allowed_methods "SECURITY_KEY_ONLY"
 
# For general staff: Security keys or phone prompts (no SMS/voice)
gam update org "/" settings \
    2sv_allowed_methods "SECURITY_KEY,PHONE_PROMPT" \
    2sv_disallowed_methods "SMS,VOICE_CALL,BACKUP_CODES"
 
# Bulk check 2SV enrollment status
gam print users \
    fields primaryEmail,isEnrolledIn2Sv,isEnforcedIn2Sv \
    query "isEnrolledIn2Sv=false" > users_without_2sv.csv
 
# Count users without 2SV
echo "Users without 2SV enrolled:"
wc -l < users_without_2sv.csv
 
# Configure context-aware access policies
# Require 2SV + managed device for sensitive apps
cat > context_aware_policy.json << 'EOF'
{
  "accessLevels": [
    {
      "name": "Managed Device Required",
      "conditions": {
        "devicePolicy": {
          "requireScreenLock": true,
          "requireAdminApproval": true,
          "allowedEncryptionStatuses": ["ENCRYPTED"],
          "requireCorpOwned": false
        },
        "requiredAccessLevels": ["VERIFIED_2SV"]
      }
    }
  ],
  "applicationPolicies": [
    {
      "applications": ["Google Drive", "Gmail", "Admin Console"],
      "accessLevel": "Managed Device Required"
    }
  ]
}
EOF

Step 3: Configure Email Authentication and Anti-Phishing

Set up SPF, DKIM, DMARC and advanced phishing protections:

# Step 3a: Configure SPF record
# Add to DNS TXT record for corp.com
echo 'DNS TXT Record for SPF:'
echo 'corp.com TXT "v=spf1 include:_spf.google.com ~all"'
echo ''
echo 'After testing, change ~all to -all (hard fail) for enforcement'
 
# Step 3b: Generate and configure DKIM signing
# Generate 2048-bit DKIM key via Admin Console or API
gam create dkim domain corp.com selector google bitlength 2048
 
echo 'Add DKIM DNS TXT record:'
echo 'google._domainkey.corp.com TXT "v=DKIM1; k=rsa; p=<public_key_from_admin_console>"'
 
# Verify DKIM is working
gam info dkim domain corp.com
 
# Step 3c: Configure DMARC policy
echo 'DNS TXT Record for DMARC (start with monitoring):'
echo '_dmarc.corp.com TXT "v=DMARC1; p=none; rua=mailto:dmarc-reports@corp.com; ruf=mailto:dmarc-forensics@corp.com; pct=100; adkim=s; aspf=s"'
echo ''
echo 'After 30 days monitoring, escalate to quarantine then reject:'
echo '_dmarc.corp.com TXT "v=DMARC1; p=reject; rua=mailto:dmarc-reports@corp.com; pct=100; adkim=s; aspf=s"'
 
# Step 3d: Enable advanced phishing and malware protection
# Configure in Admin Console > Security > Email Safety
gam update settings email_safety \
    protect_against_domain_spoofing true \
    protect_against_employee_spoofing true \
    protect_against_inbound_spoofing true \
    protect_unauthenticated_email true \
    identify_spoofed_groups true \
    auto_move_suspicious_to_spam true
 
# Configure attachment security
gam update settings email_safety \
    protect_encrypted_attachments true \
    protect_anomalous_attachment_types true \
    protect_scripts_from_untrusted true \
    whitelist_sender_domains "" \
    apply_future_recommended_settings true

Step 4: Implement Data Loss Prevention (DLP)

Configure DLP rules to prevent sensitive data exfiltration:

# Create DLP rules for Gmail and Drive
# Using Google Workspace DLP API
 
cat > dlp_rules.json << 'EOF'
{
  "dlpRules": [
    {
      "name": "PII Detection - SSN",
      "description": "Detect Social Security Numbers in outbound email and Drive sharing",
      "trigger": {
        "contentMatchers": [
          {
            "infoType": "US_SOCIAL_SECURITY_NUMBER",
            "likelihood": "LIKELY",
            "minMatchCount": 1
          }
        ],
        "scope": ["GMAIL_OUTBOUND", "DRIVE_EXTERNAL_SHARE"]
      },
      "action": {
        "blockAction": "QUARANTINE",
        "notifyAdmin": true,
        "notifyUser": true,
        "userMessage": "This message contains a Social Security Number and has been quarantined for review.",
        "auditLog": true
      }
    },
    {
      "name": "Credit Card Number Detection",
      "description": "Block credit card numbers in outbound communications",
      "trigger": {
        "contentMatchers": [
          {
            "infoType": "CREDIT_CARD_NUMBER",
            "likelihood": "LIKELY",
            "minMatchCount": 1
          }
        ],
        "scope": ["GMAIL_OUTBOUND", "DRIVE_EXTERNAL_SHARE", "CHAT"]
      },
      "action": {
        "blockAction": "BLOCK",
        "notifyAdmin": true,
        "notifyUser": true,
        "auditLog": true
      }
    },
    {
      "name": "Confidential Document Detection",
      "description": "Detect documents marked as Confidential or Internal Only",
      "trigger": {
        "contentMatchers": [
          {
            "customRegex": "(?i)(CONFIDENTIAL|INTERNAL ONLY|DO NOT DISTRIBUTE|RESTRICTED)",
            "minMatchCount": 2
          }
        ],
        "metadataMatchers": [
          {
            "driveLabels": ["Confidential", "Restricted"]
          }
        ],
        "scope": ["DRIVE_EXTERNAL_SHARE"]
      },
      "action": {
        "blockAction": "WARN",
        "requireJustification": true,
        "auditLog": true
      }
    }
  ]
}
EOF
 
echo "Apply DLP rules via Admin Console > Security > Data Protection"
echo "Or use the Google Workspace DLP API for programmatic deployment"

Step 5: Control OAuth Applications and Third-Party Access

Restrict which third-party applications can access organizational data:

# Configure OAuth app access control
# Admin Console > Security > API Controls > App Access Control
 
# Block all third-party apps by default, then allowlist approved ones
gam update org "/" settings \
    third_party_app_access "BLOCKED" \
    allow_users_to_install_apps false
 
# Allowlist approved applications
cat > approved_apps.json << 'EOF'
{
  "allowedApps": [
    {
      "appId": "slack-app-id",
      "name": "Slack",
      "scopes": ["gmail.readonly", "calendar.readonly"],
      "approvedBy": "security-team",
      "reviewDate": "2026-01-15"
    },
    {
      "appId": "zoom-app-id",
      "name": "Zoom",
      "scopes": ["calendar.events"],
      "approvedBy": "security-team",
      "reviewDate": "2026-01-15"
    },
    {
      "appId": "salesforce-app-id",
      "name": "Salesforce",
      "scopes": ["gmail.send", "contacts.readonly"],
      "approvedBy": "security-team",
      "reviewDate": "2026-01-15"
    }
  ]
}
EOF
 
# Audit current OAuth tokens granted by users
gam all users print tokens > oauth_tokens_audit.csv
echo "Review oauth_tokens_audit.csv for unauthorized third-party access"
 
# Revoke tokens for unapproved applications
gam all users deprovision tokens \
    clientid "unapproved-app-client-id"
 
# Configure API scopes restriction
# Limit which API scopes third-party apps can request
gam update org "/" settings \
    api_access_restricted true \
    allowed_api_scopes "gmail.readonly,calendar.readonly,drive.readonly"

Step 6: Configure External Sharing and Drive Security

Lock down data sharing controls:

# Configure Google Drive sharing restrictions
gam update org "/" settings \
    drive_sharing_outside_domain "WHITELISTED_DOMAINS" \
    drive_sharing_whitelisted_domains "partner1.com,partner2.com" \
    drive_allow_file_requests false \
    drive_shared_drive_creation "ADMIN_ONLY" \
    drive_default_link_sharing "RESTRICTED"
 
# Configure sharing alerts
gam create alert \
    name "External Sharing Alert" \
    type "drive_external_share" \
    condition "shared_outside_domain=true AND file_type IN ('spreadsheet','document','presentation')" \
    action "notify_admin security-team@corp.com"
 
# Audit current external shares
gam all users print filelist \
    fields id,name,owners,permissions \
    query "visibility='anyoneWithLink' or visibility='anyoneCanFind'" \
    > external_shares_audit.csv
 
echo "External shares requiring review:"
wc -l < external_shares_audit.csv
 
# Configure Google Groups security
gam update org "/" settings \
    groups_external_members false \
    groups_external_posting false \
    groups_creation "ADMIN_ONLY" \
    groups_allow_external_invitations false

Key Concepts

Term Definition
Advanced Protection Program (APP) Google's strongest account security requiring FIDO2 security keys, blocking third-party app access, and enhanced identity verification for account recovery
Context-Aware Access Security policy framework that evaluates device posture, location, and user identity before granting access to Google Workspace applications
DMARC Domain-based Message Authentication, Reporting and Conformance protocol that prevents email domain spoofing by validating SPF and DKIM alignment
DLP Rule Data Loss Prevention policy that scans content in Gmail, Drive, and Chat for sensitive data patterns and triggers block, quarantine, or warn actions
OAuth App Allowlisting Admin control restricting which third-party applications can access organizational data through Google OAuth API scopes
2-Step Verification (2SV) Google's multi-factor authentication implementation supporting security keys, phone prompts, TOTP, and backup codes as second factors

Tools & Systems

  • Google Admin Console: Web-based administration portal for managing all Google Workspace security settings, users, and organizational units
  • GAM (Google Apps Manager): Open-source command-line tool for bulk Google Workspace administration and automation
  • Google Workspace Alert Center: Centralized dashboard for security alerts including suspicious login activity, DLP violations, and device compromise
  • Google BeyondCorp Enterprise: Zero-trust access solution integrated with Google Workspace for context-aware access policies

Common Scenarios

Scenario: Securing a Newly Acquired Google Workspace Tenant

Context: Post-acquisition security audit reveals the acquired company's Google Workspace has no MFA enforcement, open external sharing, no DLP policies, and multiple unauthorized OAuth applications accessing user data.

Approach:

  1. Immediately enforce 2SV for all super admin accounts using FIDO2 security keys
  2. Reduce super admin count to 3 (primary, secondary, break-glass)
  3. Deploy SPF, DKIM, and DMARC starting with monitoring mode (p=none)
  4. Enable all anti-phishing and anti-spoofing settings in Email Safety
  5. Audit and revoke all unauthorized OAuth application tokens
  6. Set third-party app access to blocked with allowlist of approved applications
  7. Restrict external Drive sharing to approved partner domains only
  8. Deploy DLP rules for PII, financial data, and confidential documents
  9. Enable context-aware access requiring managed devices for sensitive applications
  10. Configure security alerts and SIEM integration for ongoing monitoring

Pitfalls:

  • Enforcing MFA without enrollment grace period locks users out of accounts
  • Setting DMARC to reject before monitoring period causes legitimate email delivery failures
  • Blocking all OAuth apps without identifying business-critical integrations disrupts workflows
  • Not auditing existing external shares before restricting sharing leaves data exposed

Output Format

GOOGLE WORKSPACE SECURITY ASSESSMENT REPORT
=============================================
Tenant:            corp.com
License:           Enterprise Plus
Total Users:       3,847
Organizational Units: 12
 
AUTHENTICATION SECURITY
2SV Enforced:           YES (all OUs)
2SV Enrollment:         3,712 / 3,847 (96.5%)
Security Keys Only:     Executive OU (47 users)
Advanced Protection:    3 super admin accounts
Super Admin Count:      3 (within recommended limit)
 
EMAIL AUTHENTICATION
SPF:                    CONFIGURED (hard fail: -all)
DKIM:                   CONFIGURED (2048-bit, selector: google)
DMARC:                  ENFORCED (p=reject, 100%)
Anti-Phishing:          ALL PROTECTIONS ENABLED
Anti-Spoofing:          ENABLED (domain + employee name)
 
DATA PROTECTION
DLP Rules Active:       7
  PII Detection:        SSN, Credit Card, Passport
  Content Labels:       Confidential, Restricted
  Custom Patterns:      3 organization-specific rules
DLP Violations (30d):   89 (67 blocked, 22 warned)
 
APPLICATION CONTROL
Third-Party App Policy: BLOCKED (allowlist mode)
Approved Apps:          12
Unauthorized Tokens:    0 (all revoked)
API Scope Restrictions: ENABLED
 
SHARING CONTROLS
External Sharing:       RESTRICTED (allowlisted domains only)
Public Link Sharing:    DISABLED
External Group Members: DISABLED
Shared Drive Creation:  ADMIN ONLY
Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 1

api-reference.md2.2 KB

API Reference: Implementing Google Workspace Admin Security

Libraries

google-api-python-client + google-auth

Admin SDK Directory API

Method Description
users().list(domain, projection="full") List users with full profile
users().get(userKey) Get specific user details
users().update(userKey, body) Update user settings
users().list(query="isAdmin=true") List admin users
orgunits().list(customerId) List organizational units
roles().list(customer) List admin roles
roleAssignments().list(customer) List role assignments

Reports API (Audit Logs)

Method Description
activities().list(userKey, applicationName) Get audit events
Application names: login, admin, drive, token, mobile

Key User Fields for Security

Field Description
isEnrolledIn2Sv User enrolled in 2-Step Verification
isEnforcedIn2Sv 2SV enforcement applied
isAdmin Super admin status
isDelegatedAdmin Delegated admin status
lastLoginTime Last login timestamp
recoveryEmail Recovery email (risk if external)
recoveryPhone Recovery phone number
isSuspended Account suspended

OAuth Scopes Required

  • admin.directory.user -- User management
  • admin.directory.domain -- Domain settings
  • admin.reports.audit.readonly -- Audit log access
  • admin.directory.orgunit -- Org unit management

Login Event Names

  • login_success -- Successful login
  • login_failure -- Failed login attempt
  • login_challenge -- 2FA challenge issued
  • suspicious_login -- Flagged by Google
  • account_disabled_password_leak -- Compromised password

External References

Scripts 1

agent.py8.3 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Google Workspace admin security hardening agent using Admin SDK."""

import json
import sys
import argparse
from datetime import datetime

try:
    from google.oauth2 import service_account
    from googleapiclient.discovery import build
    from googleapiclient.errors import HttpError
except ImportError:
    print("Install: pip install google-api-python-client google-auth")
    sys.exit(1)


SCOPES = [
    "https://www.googleapis.com/auth/admin.directory.user",
    "https://www.googleapis.com/auth/admin.directory.domain",
    "https://www.googleapis.com/auth/admin.reports.audit.readonly",
    "https://www.googleapis.com/auth/admin.directory.orgunit",
]


def get_admin_service(credentials_file, admin_email, api="admin", version="directory_v1"):
    """Build Google Admin SDK service with domain-wide delegation."""
    creds = service_account.Credentials.from_service_account_file(
        credentials_file, scopes=SCOPES, subject=admin_email)
    return build(api, version, credentials=creds)


def get_reports_service(credentials_file, admin_email):
    """Build Reports API service."""
    creds = service_account.Credentials.from_service_account_file(
        credentials_file, scopes=SCOPES, subject=admin_email)
    return build("admin", "reports_v1", credentials=creds)


def list_users_without_2fa(service, domain):
    """List users who have not enrolled in 2-Step Verification."""
    users_without_2fa = []
    request = service.users().list(domain=domain, maxResults=500,
                                    projection="full", orderBy="email")
    while request:
        response = request.execute()
        for user in response.get("users", []):
            is_enrolled = user.get("isEnrolledIn2Sv", False)
            is_enforced = user.get("isEnforcedIn2Sv", False)
            if not is_enrolled:
                users_without_2fa.append({
                    "email": user["primaryEmail"],
                    "name": user.get("name", {}).get("fullName", ""),
                    "is_admin": user.get("isAdmin", False),
                    "is_2sv_enrolled": is_enrolled,
                    "is_2sv_enforced": is_enforced,
                    "last_login": user.get("lastLoginTime", "never"),
                })
        request = service.users().list_next(request, response)
    return users_without_2fa


def list_admin_users(service, domain):
    """List all admin users and their admin roles."""
    admins = []
    request = service.users().list(domain=domain, maxResults=500,
                                    projection="full", query="isAdmin=true")
    response = request.execute()
    for user in response.get("users", []):
        admins.append({
            "email": user["primaryEmail"],
            "name": user.get("name", {}).get("fullName", ""),
            "is_super_admin": user.get("isAdmin", False),
            "is_delegated_admin": user.get("isDelegatedAdmin", False),
            "is_2sv_enrolled": user.get("isEnrolledIn2Sv", False),
            "last_login": user.get("lastLoginTime", "never"),
            "creation_time": user.get("creationTime", ""),
        })
    return admins


def get_login_audit_events(reports_service, user_email=None, days=7):
    """Get login audit events to detect suspicious activity."""
    events = []
    try:
        params = {"userKey": "all", "applicationName": "login", "maxResults": 200}
        if user_email:
            params["userKey"] = user_email
        request = reports_service.activities().list(**params)
        response = request.execute()
        for activity in response.get("items", []):
            for event in activity.get("events", []):
                event_data = {
                    "user": activity.get("actor", {}).get("email", ""),
                    "event_name": event.get("name", ""),
                    "time": activity.get("id", {}).get("time", ""),
                    "ip_address": activity.get("ipAddress", ""),
                }
                for param in event.get("parameters", []):
                    event_data[param["name"]] = param.get("value", param.get("boolValue", ""))
                events.append(event_data)
    except HttpError as e:
        events.append({"error": str(e)})
    return events


def check_suspended_users(service, domain):
    """List suspended users that may still have active sessions."""
    suspended = []
    request = service.users().list(domain=domain, maxResults=500,
                                    query="isSuspended=true")
    response = request.execute()
    for user in response.get("users", []):
        suspended.append({
            "email": user["primaryEmail"],
            "suspension_reason": user.get("suspensionReason", "manual"),
            "last_login": user.get("lastLoginTime", "never"),
        })
    return suspended


def check_recovery_settings(service, domain):
    """Audit users with recovery email/phone that could be used for account takeover."""
    findings = []
    request = service.users().list(domain=domain, maxResults=500, projection="full")
    response = request.execute()
    for user in response.get("users", []):
        recovery_email = user.get("recoveryEmail", "")
        recovery_phone = user.get("recoveryPhone", "")
        if user.get("isAdmin") and (recovery_email or recovery_phone):
            if recovery_email and not recovery_email.endswith(f"@{domain}"):
                findings.append({
                    "email": user["primaryEmail"],
                    "issue": "Admin has external recovery email",
                    "recovery_email": recovery_email,
                    "severity": "HIGH",
                })
    return findings


def run_workspace_audit(service, reports_service, domain):
    """Run comprehensive Google Workspace security audit."""
    print(f"\n{'='*60}")
    print(f"  GOOGLE WORKSPACE SECURITY AUDIT")
    print(f"  Domain: {domain}")
    print(f"  Generated: {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')} UTC")
    print(f"{'='*60}\n")

    admins = list_admin_users(service, domain)
    print(f"--- ADMIN ACCOUNTS ({len(admins)}) ---")
    for a in admins:
        mfa_status = "2FA ON" if a["is_2sv_enrolled"] else "2FA OFF"
        print(f"  [{mfa_status}] {a['email']} (Super: {a['is_super_admin']})")

    no_2fa = list_users_without_2fa(service, domain)
    print(f"\n--- USERS WITHOUT 2FA ({len(no_2fa)}) ---")
    admin_no_2fa = [u for u in no_2fa if u["is_admin"]]
    if admin_no_2fa:
        print(f"  CRITICAL: {len(admin_no_2fa)} admin(s) without 2FA!")
        for u in admin_no_2fa:
            print(f"    {u['email']}")
    print(f"  Total users without 2FA: {len(no_2fa)}")

    recovery = check_recovery_settings(service, domain)
    print(f"\n--- RECOVERY SETTINGS ISSUES ({len(recovery)}) ---")
    for r in recovery:
        print(f"  [{r['severity']}] {r['email']}: {r['issue']}")

    suspended = check_suspended_users(service, domain)
    print(f"\n--- SUSPENDED USERS ({len(suspended)}) ---")
    for s in suspended[:5]:
        print(f"  {s['email']} (Reason: {s['suspension_reason']})")

    events = get_login_audit_events(reports_service)
    suspicious = [e for e in events if e.get("event_name") == "login_failure"]
    print(f"\n--- RECENT LOGIN EVENTS ---")
    print(f"  Total events: {len(events)}")
    print(f"  Failed logins: {len(suspicious)}")

    print(f"\n{'='*60}\n")
    return {"admins": len(admins), "no_2fa": len(no_2fa),
            "admin_no_2fa": len(admin_no_2fa), "suspended": len(suspended)}


def main():
    parser = argparse.ArgumentParser(description="Google Workspace Admin Security Agent")
    parser.add_argument("--credentials", required=True, help="Service account JSON key file")
    parser.add_argument("--admin-email", required=True, help="Admin email for delegation")
    parser.add_argument("--domain", required=True, help="Google Workspace domain")
    parser.add_argument("--audit", action="store_true", help="Run full security audit")
    parser.add_argument("--output", help="Save report to JSON")
    args = parser.parse_args()

    if args.audit:
        service = get_admin_service(args.credentials, args.admin_email)
        reports = get_reports_service(args.credentials, args.admin_email)
        report = run_workspace_audit(service, reports, args.domain)
        if args.output:
            with open(args.output, "w") as f:
                json.dump(report, f, indent=2, default=str)
    else:
        parser.print_help()


if __name__ == "__main__":
    main()
Keep exploring