5

I have multiple projects and apis in google cloud platform like maps api, php app engines, sql and so on.

Google changed the way billing is managed and now it's a real possibility that billing can skyrocket in any reason (bug, hacker etc.)

how can I make it so that every billing is disabled when limit is reached? not just email notification, that is not enough! I cannot just shut down my app engine instances, because the api credentials to maps and other places can still make expenses!

John Hanley
  • 74,467
  • 6
  • 95
  • 159
gray_15
  • 411
  • 1
  • 7
  • 13

1 Answers1

3

The Set budgets and budget alerts documentation. You have 2 strategies to deal with this.

The first one is to set an API spending limit either wuota based or limiting the calls per user, so if one fo your services is making an excesive amount of calls to an specific API you just block this API so your entire service/project can keep serving.

The other approach is to automate the disabling of billing for the entire project. This is more risky because it will block the entire project and can cause lost of data.

For this you will deploy a Cloud Function like the one in the documentation triggered by the Pub/Sub topic that your budget is set to use:

const {google} = require('googleapis');
const {GoogleAuth} = require('google-auth-library');

const PROJECT_ID = process.env.GOOGLE_CLOUD_PROJECT;
const PROJECT_NAME = `projects/${PROJECT_ID}`;
const billing = google.cloudbilling('v1').projects;

exports.stopBilling = async pubsubEvent => {
  const pubsubData = JSON.parse(
    Buffer.from(pubsubEvent.data, 'base64').toString()
  );
  if (pubsubData.costAmount <= pubsubData.budgetAmount) {
    return `No action necessary. (Current cost: ${pubsubData.costAmount})`;
  }

  if (!PROJECT_ID) {
    return 'No project specified';
  }

  _setAuthCredential();
  const billingEnabled = await _isBillingEnabled(PROJECT_NAME);
  if (billingEnabled) {
    return _disableBillingForProject(PROJECT_NAME);
  } else {
    return 'Billing already disabled';
  }
};

/**
 * @return {Promise} Credentials set globally
 */
const _setAuthCredential = () => {
  const client = new GoogleAuth({
    scopes: [
      'https://www.googleapis.com/auth/cloud-billing',
      'https://www.googleapis.com/auth/cloud-platform',
    ],
  });

  // Set credential globally for all requests
  google.options({
    auth: client,
  });
};

/**
 * Determine whether billing is enabled for a project
 * @param {string} projectName Name of project to check if billing is enabled
 * @return {bool} Whether project has billing enabled or not
 */
const _isBillingEnabled = async projectName => {
  try {
    const res = await billing.getBillingInfo({name: projectName});
    return res.data.billingEnabled;
  } catch (e) {
    console.log(
      'Unable to determine if billing is enabled on specified project, assuming billing is enabled'
    );
    return true;
  }
};

/**
 * Disable billing for a project by removing its billing account
 * @param {string} projectName Name of project disable billing on
 * @return {string} Text containing response from disabling billing
 */
const _disableBillingForProject = async projectName => {
  const res = await billing.updateBillingInfo({
    name: projectName,
    resource: {billingAccountName: ''}, // Disable billing
  });
  return `Billing disabled: ${JSON.stringify(res.data)}`;
};

Dependencies:

{
 "name": "cloud-functions-billing",
 "version": "0.0.1",
 "dependencies": {
   "google-auth-library": "^2.0.0",
   "googleapis": "^52.0.0"
 }
}

Then you grant the Billing Admin permission to the service account of this Cloud Function

If you want to use this approach I would suggest you in either case to have different projects per service if possible so you can turn off just parts of your service.

Chris32
  • 4,716
  • 2
  • 18
  • 30
  • 1
    In other words, there is no good and easy way to prevent excessive billing like there used to be... – new name Jan 31 '21 at 22:04
  • 1) Disabling billing for a project stops automatic payment. Bills might still occur e.g. services are not immediately terminated. 2) If you disable billing for a project, some of your Google Cloud resources might be removed and become non-recoverable. 3) You cannot disable an API that has services deployed using that API. 4) Technically your answer is good, but has harmful side effects. – John Hanley Jan 31 '21 at 22:44
  • 1
    I agree with John, but there is not longer (or atleast i'm not aware of) a good method to safely stop the charges to a project – Chris32 Feb 01 '21 at 09:09