BreakGlass

BreakGlass

Securing Google Workspace: How to Set Up a $0 Emergency “Break-Glass” Admin

Google Workspace Break-Glass Security

In a well-secured Google Workspace environment, you should never have more Super Admins than absolutely necessary. But what happens if you lose access to your primary account? This guide walks you through setting up a “Break-Glass” account that costs nothing, requires no extra licenses, and includes automated audit reporting to ensure it is never used without your knowledge.


Phase 1: Create the $0 Admin Account

To avoid paying for an extra Workspace license, we leverage Google Cloud Identity Free. This provides core user management features without the cost of a full Workspace license, perfect for an admin account that won’t use Gmail, Drive, or other apps.

  1. Enable Cloud Identity Free:

    • Log in to your Google Admin Console.

    • Navigate to Billing > Buy or upgrade.

    • In the left sidebar, click Cloud Identity.

    • Find Cloud Identity Free and click Get Started. Complete the $0 signup process.
      Cloud your Identity options

  1. Turn off Automatic Licensing (Crucial):

    • Go to Billing > License settings.

    • Select your Google Workspace subscription (e.g., Business Standard).

    • Set Automatic licensing to OFF and click Save. This prevents your new emergency-admin account from automatically consuming a paid Workspace license.
      Automatic licenssing OFF

  1. Create the Emergency User:

    • Go to Directory > Users > Add new user.

    • Create the account (e.g., emergency-admin@yourdomain.com).

    • After creation, click on the user’s name. In the Licenses section, verify that Cloud Identity Free is “On” and your paid Workspace license is “Off.”
      License Assignment

  1. Assign Super Admin Role:

    • While still on the user’s profile, scroll down to Admin roles and privileges.

    • Click Assign Roles.

    • Toggle the Super Admin role to Assigned. Click Save.
      Assigned Roles


Phase 2: Set Up the “Silent Alarm” (Instant Login Alert)

Since this account should only be used in true emergencies, you need to be notified immediately the moment it’s accessed.

  1. Navigate to Rules:

    • In the Admin Console, go to Rules in the left sidebar.

    • Click Create rule and select Activity.
      Create new Activity Rule

  1. Configure Conditions:

    • Rule Name: Emergency Admin Login Alert

    • Description: Alert when the break-glass account logs in.

    • Click Next: View Conditions.

    • Data source: Login log events.

    • Condition 1 (The User):

      • Attribute: Actor (or User email)

      • Operator: Is

      • Value: emergency-admin@yourdomain.com

    • Condition 2 (The Event):

      • Attribute: Event

      • Operator: Is

      • Value: Login (specifically Login success if available).
        Rule Conditions

  1. Set Up Notifications:

    • Click Next: Add Actions.

    • Check Alert Center and set the severity to High.

    • Check Email notifications and Add email recipients for your primary admin email address (or an email-to-SMS gateway for text alerts).

    • Click Next: Review and then Create Rule.
      Rule Recipients


Phase 3: Automated Monthly Security Audit (Apps Script)

To maintain long-term oversight and ensure no unauthorized changes were made by any admin, we’ll deploy a Google Apps Script that emails you a summary of high-risk admin actions on the 1st of every month. This protects against audit log expiration and provides proactive security.

1. Prepare the Apps Script Project

  1. Go to script.google.com and create a + New Project. Name it Monthly Admin Audit Report.

  2. Enable Admin SDK Service:

    • In the left sidebar, click Services (+ icon).

    • Select Admin SDK API.

    • Crucially: Set the Version to reports_v1 and the Identifier to AdminReports. Click Add.
      Admin SDK Identifier

  1. Configure Project Manifest (appsscript.json):

    • Go to Project Settings (gear icon ⚙️) on the left.

    • Check the box: “Show ‘appsscript.json’ manifest file in editor”.

    • Go back to the Editor (< > icon) and click the appsscript.json file.

    • Replace its entire content with this exact JSON, ensuring the oauthScopes are included:

    JSON

    {
      "timeZone": "GMT",
      "dependencies": {
        "enabledAdvancedServices": [
          {
            "userSymbol": "AdminReports",
            "serviceId": "admin",
            "version": "reports_v1"
          }
        ]
      },
      "exceptionLogging": "STACKDRIVER",
      "runtimeVersion": "V8",
      "oauthScopes": [
        "https://www.googleapis.com/auth/admin.reports.audit.readonly",
        "https://www.googleapis.com/auth/script.send_mail",
        "https://www.googleapis.com/auth/userinfo.email"
      ]
    }
    

appscript.json contents

2. Deploy the Audit Script (Code.gs)

  1. Go back to your Code.gs file in the Apps Script editor.

  2. Delete any existing code and paste the following. This script filters for high-risk events for a cleaner, more actionable report.

    JavaScript

    function sendMonthlyAdminReport() {
      const recipientEmail = Session.getEffectiveUser().getEmail(); // Sends to YOU automatically
      const now = new Date();
      const thirtyDaysAgo = new Date(now.getTime() - (30 * 24 * 60 * 60 * 1000));
      const startTime = thirtyDaysAgo.toISOString();
    
      // Define actions we actually care about
      const highRiskEvents = [
        'CHANGE_PASSWORD', 'ADD_PRIVILEGE', 'ASSIGN_ROLE', 'DELETE_USER', 
        'SUSPEND_USER', 'CREATE_USER', 'UPDATE_GROUP', 'CHANGE_APPLICATION_SETTING',
        'DELETE_GROUP', 'ADD_USER_TO_GROUP', 'REMOVE_USER_FROM_GROUP',
        'CREATE_ORGANIZATIONAL_UNIT', 'UPDATE_ORGANIZATIONAL_UNIT', 'DELETE_ORGANIZATIONAL_UNIT',
        'UPDATE_USER_LICENSES'
      ];
    
      console.log("Fetching logs from: " + startTime);
    
      try {
        const report = AdminReports.Activities.list('all', 'admin', {
          startTime: startTime,
          maxResults: 100 // Limit to 100 for a cleaner email
        });
    
        const activities = report.items;
        let emailBody = "<h2 style='font-family: sans-serif; color: #d93025;'>Security Audit: High-Risk Admin Actions</h2>";
        emailBody += `<p style='font-family: sans-serif;'>Reviewing actions from <b>${thirtyDaysAgo.toLocaleDateString()}</b></p>`;
    
        if (!activities || activities.length === 0) {
          emailBody += "<p style='color: #666;'>No admin activity recorded in the last 30 days. This is great for security!</p>";
        } else {
          emailBody += "<table border='1' style='border-collapse: collapse; width: 100%; font-family: sans-serif;'>";
          emailBody += "<tr style='background-color: #fce8e6;'>";
          emailBody += "<th style='padding: 10px; text-align: left;'>Date</th>";
          emailBody += "<th style='padding: 10px; text-align: left;'>Admin</th>";
          emailBody += "<th style='padding: 10px; text-align: left;'>Sensitive Action</th>";
          emailBody += "</tr>";
    
          activities.forEach(activity => {
            const eventName = activity.events[0].name;
    
            // Only add to email if it's in our high-risk list
            if (highRiskEvents.includes(eventName)) {
              const date = new Date(activity.id.time).toLocaleDateString() + " " + new Date(activity.id.time).toLocaleTimeString();
              const admin = activity.actor.email;
    
              emailBody += `<tr>
                <td style='padding: 8px; border-bottom: 1px solid #ddd;'>${date}</td>
                <td style='padding: 8px; border-bottom: 1px solid #ddd;'><b>${admin}</b></td>
                <td style='padding: 8px; border-bottom: 1px solid #ddd; color: #d93025;'><b>${eventName}</b></td>
              </tr>`;
            }
          });
          emailBody += "</table>";
        }
    
        MailApp.sendEmail({
          to: recipientEmail,
          subject: "⚠️ Monthly High-Risk Admin Report",
          htmlBody: emailBody
        });
    
        console.log("Success: Filtered report sent to " + recipientEmail);
    
      } catch (err) {
        console.error("Critical Error: " + err.message);
        MailApp.sendEmail(recipientEmail, "Script Failure: Monthly Admin Report", "The Admin Audit script failed with error: " + err.message);
      }
    }
    

code.gs content

3. Run and Authorize the Script

  1. Click Save (floppy disk icon).

  2. Click Run (the play button).

  3. A “Permission required” dialog will appear. Click Review Permissions.

  4. Select your Google Workspace account (yourname@yourdomain.com).

  5. On the “Google hasn’t verified this app” screen, click Advanced > Go to Monthly Admin Audit Report (unsafe).

  6. Review the requested permissions (e.g., “View audit reports,” “Send email,” “View your email address”) and click Allow.


Phase 4: Whitelist the Internal App in Admin Console

Your Google Workspace domain might have API access controls that block custom scripts by default. You need to explicitly “Trust” your script.

  1. Get your Project Number:

    • In the Apps Script Editor, go to Project Settings (gear icon ⚙️) on the left.

    • Scroll down to Google Cloud Platform (GCP) Project.

    • Copy the Project number (a long string of digits). [Screenshot 12: Apps Script Project Settings showing the Project number]

  2. Trust the App in Admin Console:

    • Log in to the Google Admin Console.

    • Navigate to Security > Access and data control > API controls.

    • Under the App access control card, click Manage App Access. [Screenshot 13: Admin Console > Security > API controls > Manage App Access button]

    • Click Configure new app (or Add app) > OAuth App Name Or Client ID.

    • Paste the Project number you copied in the search box and click Search.

    • Select your script (“Monthly Admin Audit Report”).

    • Choose Trusted: Can access all Google services and click Configure. [Screenshot 14: Admin Console > App access control > Configure new app > Paste Project Number > Set to Trusted]


Final Step: Schedule the Automation

Now that the script is authorized and trusted, set it to run automatically every month.

  1. In the Apps Script editor, click the Triggers icon (clock icon) on the left sidebar.

  2. Click + Add Trigger.

  3. Set the following options:

    • Choose which function to run: sendMonthlyAdminReport

    • Select event source: Time-driven

    • Select type of time based trigger: Month timer

    • Select day of month: 1st

    • Select time of day: Choose a low-activity time (e.g., Midnight - 1am).

  4. Click Save. [Screenshot 15: Apps Script Triggers setup window with Month timer, 1st of month, and time of day configured]


Conclusion

Your Google Workspace emergency access is now configured, instantly monitored, and thoroughly audited every month—all at zero additional cost to your organization. This setup significantly enhances your security posture, providing peace of mind even in unexpected scenarios.


Troubleshooting Common Apps Script Errors

Here are solutions to errors you might encounter while setting up the script.

1. ReferenceError: AdminReports is not defined or TypeError: Cannot read properties of undefined (reading 'list')

  • Cause: The Admin SDK API service isn’t correctly added or its “Identifier” doesn’t match the code.

  • Solution:

    1. In Apps Script, go to Services (left sidebar).

    2. Remove any existing “Admin SDK API” service.

    3. Click + Add Service. Select Admin SDK API.

    4. Crucially: Set the Version to reports_v1 and the Identifier to AdminReports. Click Add.

2. Access blocked: Your institution’s admin needs to review Monthly Admin Audit Report (Error 400: access_not_configured)

  • Cause: Your Google Workspace API controls are blocking the script, or the wrong Project Number was whitelisted.

  • Solution:

    1. In Apps Script, go to Project Settings (gear icon ⚙️) and copy the Project number.

    2. In the Admin Console, go to Security > Access and data control > API controls > Manage App Access.

    3. Click Configure new app > OAuth App Name Or Client ID. Paste your Project Number, search, select your app, and set it to Trusted.

    4. Verify that “Trust internal apps” is also checked under API controls > Settings > Internal apps.

3. Error: You do not have permission to call reports.activities.list. (Required permissions: https://www.googleapis.com/auth/admin.reports.audit.readonly)

  • Cause: The script hasn’t been granted explicit permission to read audit logs, or the appsscript.json manifest file is missing the required OAuth scope.

  • Solution:

    1. Ensure your appsscript.json file contains the exact oauthScopes as provided in Phase 3, Step 1.

    2. Force Re-authorization:

      • Go to myaccount.google.com/permissions.

      • Find “Monthly Admin Audit Report” and click Remove Access.

      • Go back to your script, click Save, then Run. This will force the “Permission required” popup to reappear. Grant the permissions.

    3. Verify the account running the script is a Super Admin.

4. ErrorException: Specified permissions are not sufficient to call Session.getEffectiveUser. (Required permissions: https://www.googleapis.com/auth/userinfo.email)

  • Cause: Similar to the previous error, the appsscript.json manifest file is missing the scope required to get the current user’s email.

  • Solution:

    1. Ensure your appsscript.json file includes https://www.googleapis.com/auth/userinfo.email in the oauthScopes array, as shown in Phase 3, Step 1.

    2. Force a re-authorization (as described in troubleshooting point 3.2).

5. No popup appears, “Execution completed” (but no email)

  • Cause: Often caused by browser conflicts (multiple Google logins), cached permissions, or silent internal blocking.

  • Solution:

    1. Use an Incognito/Private window: Log in only with your marco@yourdomain.com Super Admin account and try running the script.

    2. Force Re-authorization: Go to myaccount.google.com/permissions, Remove Access for “Monthly Admin Audit Report”, then return to the script and Run it. This is the most reliable way to force the permission popup.

Comments

Popular posts from this blog

Connecting your Pi to the Personal Hotspot on an iPhone