Désactiver l'utilisation de la facturation avec les notifications

Ce document explique comment désactiver automatiquement la facturation d'un projet lorsque vos coûts atteignent ou dépassent le budget du projet. Lorsque vous désactivez la facturation d'un projet, vous résiliez tous les Google Cloud services du projet, y compris les services de niveau gratuit. Pour obtenir une réponse plus nuancée aux notifications de budget, consultez la section Contrôler l'utilisation des ressources à l'aide de notifications.

Vous pouvez vouloir limiter les coûts si vous avez un montant maximal que vous pouvez dépenser sur Google Cloud. Dans ce cas, lorsque votre limite budgétaire est atteinte, vous pouvez arrêter tous vos Google Cloud services et leur utilisation pour ne plus générer de coûts. Désactiver la facturation sur votre projet est une méthode efficace pour ne plus encourir de frais.

Limites

  • Il existe un délai entre le moment où les coûts sont engagés et celui où les notifications de budget sont reçues. En conséquence, des coûts supplémentaires peuvent être engagés pour une utilisation qui n'est pas encore arrivée au moment où les services sont arrêtés. Le fait de suivre les étapes présentées dans cet exemple n'est en aucun cas une garantie que les dépenses ne dépasseront pas le seuil budgétaire. Si vos fonds sont limités, définissez votre budget maximal en dessous de vos fonds disponibles afin de tenir compte des délais de facturation.

  • Vous ne pouvez pas désactiver la facturation d'un projet associé à un compte de facturation. Pour en savoir plus sur le verrouillage et le déverrouillage de projets, consultez la section Sécuriser l'association entre un projet et son compte de facturation.

Avant de commencer

Avant de commencer, vous devez effectuer les tâches suivantes:

  1. Activer l'API Cloud Billing
  2. Créer un budget limité à un seul projet
  3. Configurer des notifications de budget automatisées

Configurer une fonction Cloud Run

Pour désactiver Cloud Billing pour un projet, créez une fonction Cloud Run et configurez-la pour qu'elle appelle l'API Cloud Billing.

  1. Suivez la procédure décrite dans Créer une fonction Cloud Run. Assurez-vous que le type de déclencheur est défini sur le même sujet Pub/Sub que celui que votre budget utilisera.
  2. Ajoutez les dépendances suivantes :

    Node.js

    Copiez ce qui suit dans votre fichier package.json:

    {
      "name": "cloud-functions-billing",
      "private": "true",
      "version": "0.0.1",
      "description": "Examples of integrating Cloud Functions with billing",
      "main": "index.js",
      "engines": {
        "node": ">=16.0.0"
      },
      "scripts": {
        "compute-test": "c8 mocha -p -j 2 test/periodic.test.js --timeout=600000",
        "test": "c8 mocha -p -j 2 test/index.test.js --timeout=5000 --exit"
      },
      "author": "Ace Nassri <anassri@google.com>",
      "license": "Apache-2.0",
      "dependencies": {
        "@google-cloud/billing": "^4.0.0",
        "@google-cloud/compute": "^4.0.0",
        "google-auth-library": "^9.0.0",
        "googleapis": "^143.0.0",
        "slack": "^11.0.1"
      },
      "devDependencies": {
        "@google-cloud/functions-framework": "^3.0.0",
        "c8": "^10.0.0",
        "gaxios": "^6.0.0",
        "mocha": "^10.0.0",
        "promise-retry": "^2.0.0",
        "proxyquire": "^2.1.0",
        "sinon": "^18.0.0",
        "wait-port": "^1.0.4"
      }
    }
    

    Python

    Copiez ce qui suit dans votre fichier requirements.txt:

    slackclient==2.9.4
    google-api-python-client==2.131.0
    

  3. Copiez le code suivant dans votre fonction Cloud Run:

    Node.js

    const {CloudBillingClient} = require('@google-cloud/billing');
    const {InstancesClient} = require('@google-cloud/compute');
    
    const PROJECT_ID = process.env.GOOGLE_CLOUD_PROJECT;
    const PROJECT_NAME = `projects/${PROJECT_ID}`;
    const billing = new CloudBillingClient();
    
    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';
      }
    
      const billingEnabled = await _isBillingEnabled(PROJECT_NAME);
      if (billingEnabled) {
        return _disableBillingForProject(PROJECT_NAME);
      } else {
        return 'Billing already disabled';
      }
    };
    
    /**
     * 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.getProjectBillingInfo({name: projectName});
        return res.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.updateProjectBillingInfo({
        name: projectName,
        resource: {billingAccountName: ''}, // Disable billing
      });
      return `Billing disabled: ${JSON.stringify(res)}`;
    };

    Python

    import base64
    import json
    import os
    
    from googleapiclient import discovery
    
    PROJECT_ID = os.getenv("GCP_PROJECT")
    PROJECT_NAME = f"projects/{PROJECT_ID}"
    def stop_billing(data, context):
        pubsub_data = base64.b64decode(data["data"]).decode("utf-8")
        pubsub_json = json.loads(pubsub_data)
        cost_amount = pubsub_json["costAmount"]
        budget_amount = pubsub_json["budgetAmount"]
        if cost_amount <= budget_amount:
            print(f"No action necessary. (Current cost: {cost_amount})")
            return
    
        if PROJECT_ID is None:
            print("No project specified with environment variable")
            return
    
        billing = discovery.build(
            "cloudbilling",
            "v1",
            cache_discovery=False,
        )
    
        projects = billing.projects()
    
        billing_enabled = __is_billing_enabled(PROJECT_NAME, projects)
    
        if billing_enabled:
            __disable_billing_for_project(PROJECT_NAME, projects)
        else:
            print("Billing already disabled")
    
    
    def __is_billing_enabled(project_name, projects):
        """
        Determine whether billing is enabled for a project
        @param {string} project_name Name of project to check if billing is enabled
        @return {bool} Whether project has billing enabled or not
        """
        try:
            res = projects.getBillingInfo(name=project_name).execute()
            return res["billingEnabled"]
        except KeyError:
            # If billingEnabled isn't part of the return, billing is not enabled
            return False
        except Exception:
            print(
                "Unable to determine if billing is enabled on specified project, assuming billing is enabled"
            )
            return True
    
    
    def __disable_billing_for_project(project_name, projects):
        """
        Disable billing for a project by removing its billing account
        @param {string} project_name Name of project disable billing on
        """
        body = {"billingAccountName": ""}  # Disable billing
        try:
            res = projects.updateBillingInfo(name=project_name, body=body).execute()
            print(f"Billing disabled: {json.dumps(res)}")
        except Exception:
            print("Failed to disable billing, possibly check permissions")
    
    

  4. Définissez le point d'entrée sur la fonction à exécuter:

    Node.js

    Définissez le champ Point d'entrée sur stopBilling.

    Python

    Définissez le champ Point d'entrée sur stop_billing.

  5. Consultez la liste des variables d'environnement définies automatiquement pour déterminer si vous devez définir manuellement la variable GOOGLE_CLOUD_PROJECT sur le projet pour lequel vous souhaitez désactiver Cloud Billing.

  6. Cliquez sur DÉPLOYER.

Configurer les autorisations du compte de service

Votre fonction Cloud Run s'exécute en tant que compte de service automatiquement créé. Pour désactiver la facturation, vous devez accorder au compte de service des autorisations sur tous les services du projet dont il aura besoin pour apporter des modifications en procédant comme suit:

  1. Identifiez le bon compte de service en consultant les informations de votre fonction Cloud Run. Le compte de service est répertorié au bas de la page.
  2. Accédez à la page IAM de la console Google Cloud pour définir les autorisations appropriées.

    Accéder à la page IAM

  3. Pour modifier les autorisations du compte de facturation, dans la console Google Cloud, accédez à la page Gestion des comptes de facturation, ajoutez le compte de service en tant que principe au compte de facturation Cloud et définissez les autorisations de compte de facturation appropriées.

    Accéder à la page "Gestion des comptes" dans Cloud Billing

Découvrez comment configurer les autorisations pour les comptes de facturation Cloud.

Vérifier que Cloud Billing est désactivé

Lorsque le budget envoie une notification, le projet spécifié n'a plus de compte de facturation Cloud associé. Pour vous assurer que votre fonction fonctionne comme prévu, suivez la procédure décrite dans Tester une fonction Cloud Run.

Si l'opération aboutit, le projet n'est plus visible sous le compte de facturation Cloud et les ressources du projet sont désactivées, y compris la fonction Cloud Run si elle se trouve dans ce projet.

Pour continuer à utiliser les Google Cloud ressources du projet, dans la console Google Cloud, réactivez manuellement Cloud Billing pour votre projet.

Étape suivante

Consultez d'autres exemples de notifications programmatiques pour découvrir comment: