[Yandex Cloud documentation](../../index.md) > [Yandex Compute Cloud](../index.md) > [Tutorials](index.md) > Creating a budget trigger that invokes a function to stop a VM

# Creating a budget trigger that invokes a Cloud Functions function to stop VM instances


Create a [budget](../../billing/concepts/budget.md) trigger that invokes a Cloud Functions [function](../../functions/concepts/function.md). The function will stop Compute Cloud [VM instances](../concepts/vm.md#project) when their resource use exceeds the budget thresholds.

To deploy a project:
1. [Download a project](#download).
1. [Create a service account](#create-sa).
1. [Prepare a ZIP archive with the function code](#prepare-zip).
1. [Create a Cloud Functions function](#create-function).
1. [Create a budget](#create-budget).
1. [Create a budget trigger](#create-trigger).
1. [Create Compute Cloud VM instances](#create-vm).
1. [Make sure the trigger stops the VM instances](#test).

If you no longer need the resources you created, [delete them](#clear-out).

## Getting started {#before-you-begin}

Sign up for Yandex Cloud and create a [billing account](../../billing/concepts/billing-account.md):
1. Navigate to the [management console](https://console.yandex.cloud) and log in to Yandex Cloud or create a new account.
1. On the **[Yandex Cloud Billing](https://center.yandex.cloud/billing/accounts)** page, make sure you have a billing account linked and it has the `ACTIVE` or `TRIAL_ACTIVE` [status](../../billing/concepts/billing-account-statuses.md). If you do not have a billing account, [create one](../../billing/quickstart/index.md) and [link](../../billing/operations/pin-cloud.md) a cloud to it.

If you have an active billing account, you can create or select a [folder](../../resource-manager/concepts/resources-hierarchy.md#folder) for your infrastructure on the [cloud page](https://console.yandex.cloud/cloud).

[Learn more about clouds and folders here](../../resource-manager/concepts/resources-hierarchy.md).

### Required paid resources {#paid-resources}

The cost of resources includes:
* Fee for using VM instances (see [Compute Cloud pricing](../pricing.md)).
* Fee for the number of function calls, computing resources allocated to run a function, and outgoing traffic (see [Cloud Functions pricing](../../functions/pricing.md)).

## Download a project {#download}

Clone the repository with the project:

```bash
git clone https://github.com/yandex-cloud-examples/yc-serverless-trigger-budget
```

## Create a service account {#create-sa}

{% list tabs group=instructions %}

- Management console {#console}

    1. In the [management console](https://console.yandex.cloud), select the folder where you want to create a service account.
    1. Navigate to **Identity and Access Management**.
    1. Click **Create service account**.
    1. Enter a name for the service account: `service-account-for-budget`.
    1. Click **Add role** and assign the `compute.admin`, `iam.serviceAccounts.user`, and `functions.functionInvoker` roles to the service account.
    1. Click **Create**.

- CLI {#cli}

    If you do not have the Yandex Cloud CLI yet, [install and initialize it](../../cli/quickstart.md#install).

    The folder used by default is the one specified when [creating](../../cli/operations/profile/profile-create.md) the CLI profile. To change the default folder, use the `yc config set folder-id <folder_ID>` command. You can also specify a different folder for any command using `--folder-name` or `--folder-id`. If you access a resource by its name, the search will be limited to the default folder. If you access a resource by its ID, the search will be global, i.e., through all folders based on access permissions.

    1. Create a service account named `service-account-for-budget`:
        ```bash
        yc iam service-account create --name service-account-for-budget
        ```

        Result:
        ```text
        id: ajersamh4sjq********
        folder_id: b1gc1t4cb638********
        created_at: "2022-12-07T10:36:29.726397755Z"
        name: service-account-for-budget
        ```

    1. Get a list of folders in the cloud:
        ```bash
        yc resource-manager folder list
        ```

        Result:
        ```text
        +----------------------+----------------------+--------+--------+
        |          ID          |         NAME         | LABELS | STATUS |
        +----------------------+----------------------+--------+--------+
        | b1gp7arme3nn******** | my-folder            |        | ACTIVE |
        | b1g0itj57rbj******** | test-folder          |        | ACTIVE |
        +----------------------+----------------------+--------+--------+
        ```

        Save the ID of the folder where you want to create the service account.

    1. Get a list of service accounts in the folder:
        ```bash
        yc iam service-account list
        ```

        Result:
        ```text
        +----------------------+------------------------------+
        |          ID          |             NAME             |
        +----------------------+------------------------------+
        | aje07l4q4vmo******** | test-sa                      |
        | ajersamh4sjq******** | service-account-for-budget   |
        +----------------------+------------------------------+
        ```

        Save the `service-account-for-budget` service account ID.

    1. Assign the `compute.admin`, `iam.serviceAccounts.user`, and `functions.functionInvoker` roles to the service account:
        ```bash
        yc resource-manager folder add-access-binding <folder_ID> \
           --role compute.admin \
           --subject serviceAccount:<service_account_ID>

        yc resource-manager folder add-access-binding <folder_ID> \
           --role iam.serviceAccounts.user \
           --subject serviceAccount:<service_account_ID>

        yc resource-manager folder add-access-binding <folder_ID> \
           --role functions.functionInvoker \
           --subject serviceAccount:<service_account_ID>
        ```

        Result:
        ```text
        done (1s)
        ```

- API {#api}

    To create a service account, use the [create](../../iam/api-ref/ServiceAccount/create.md) method for the [ServiceAccount](../../iam/api-ref/ServiceAccount/index.md) resource.

    To assign the service account the `compute.admin`, `iam.serviceAccounts.user`, and `functions.functionInvoker` roles for the folder, use the [setAccessBindings](../../iam/api-ref/ServiceAccount/setAccessBindings.md) method for the [ServiceAccount](../../iam/api-ref/ServiceAccount/index.md) resource.

{% endlist %}

## Prepare a ZIP archive with the function code {#prepare-zip}

Navigate to the `yc-serverless-trigger-budget/steps/2-create-function/` directory and add files to the `src.zip` archive:
```bash
zip src.zip index.go go.mod
```

## Create a Cloud Functions function {#create-function}

{% list tabs group=instructions %}

- Management console {#console}

    1. In the [management console](https://console.yandex.cloud), select the folder where you created the service account.
    1. Navigate to **Cloud Functions**.
    1. Create a function:
        1. Click **Create function**.
        1. Specify the function name: `function-for-budget`.
        1. Click **Create**.
    1. Create a function version:
        1. Select `golang119` as the runtime environment, disable the **Add files with code examples** option, and click **Continue**.
        1. Specify the **ZIP archive** upload method and select the archive created in the previous step.
        1. Specify the entry point: `index.StopComputeInstances`.
        1. Under **Parameters**, specify:
            * **Timeout**: `5`
            * **Memory**: `512 MB`
            * **Service account**: `service-account-for-budget`
            * **Environment variables**:
                * `FOLDER_ID` : ID of the folder where you want the VMs stopped.
                * `TAG`: `target-for-stop`.
        1. Click **Save changes**.

- CLI {#cli}

    1. Create a function named `function-for-budget`:
        ```bash
        yc serverless function create --name function-for-budget
        ```

        Result:
        ```text
        done (1s)
        id: d4eiqjdbqt7s********
        folder_id: b1gc1t4cb638********
        created_at: "2022-12-07T10:44:13.156Z"
        name: function-for-budget
        log_group_id: ckg6bie2rtgd********
        http_invoke_url: https://functions.yandexcloud.net/d4eiqjdbqt7s********
        status: ACTIVE
        ```

    1. Create a version of the `function-for-budget` function:
        ```bash
        yc serverless function version create \
          --function-name function-for-budget \
          --memory=512m \
          --execution-timeout=5s \
          --runtime=golang119 \
          --entrypoint=index.StopComputeInstances \
          --service-account-id=<service_account_ID> \
          --environment FOLDER_ID=<folder_ID> \
          --environment TAG=target-for-stop \
          --source-path=./src.zip
        ```

        Where:
        * `--function-name`: Name of the function whose version you are creating.
        * `--memory`: Amount of RAM.
        * `--execution-timeout`: Maximum function execution time.
        * `--runtime`: Runtime environment.
        * `--entrypoint`: Entry point.
        * `--service-account-id`: `service-account-for-budget` service account ID.
        * `--environment`: Environment variables; `FOLDER_ID`: ID of the folder where you want the VMs stopped.
        * `--source-path`: Path to the `src.zip` archive.

        Result:
        ```text
        done (2m7s)
        id: d4ev38dddr41********
        function_id: d4eiqjdbqt7s********
        created_at: "2022-12-07T11:14:07.802Z"
        runtime: golang119
        entrypoint: index.StopComputeInstances
        resources:
          memory: "536870912"
        execution_timeout: 5s
        service_account_id: ajersamh4sjq********
        image_size: "19587072"
        status: ACTIVE
        tags:
          - $latest
        log_group_id: ckg6bie2rtgd********
        environment:
          FOLDER_ID: b1gc1t4cb638********
          TAG: target-for-stop
        log_options:
          folder_id: b1gc1t4cb638********
        ```

- API {#api}

    To create a function, use the [create](../../functions/functions/api-ref/Function/create.md) method for the [Function](../../functions/functions/api-ref/Function/index.md) resource.

    To create a function version, use the [createVersion](../../functions/functions/api-ref/Function/createVersion.md) method for the [Function](../../functions/functions/api-ref/Function/index.md) resource.

{% endlist %}

## Create a budget {#create-budget}

The user needs the `editor` [role](../../iam/concepts/access-control/roles.md) to create a budget. To get notifications, the `viewer` role is sufficient.

{% list tabs group=instructions %}

- Yandex Cloud Billing interface {#billing}

   1. Go to [**Yandex Cloud Billing**](https://center.yandex.cloud/billing/accounts).
   1. Select a [billing account](../../billing/concepts/billing-account.md).
   1. In the **General information** section, in the **ID** field, copy the billing account ID. You will need it when creating a [trigger](../../functions/concepts/trigger/index.md) for budgets.
   1. Go to the **Budgets** tab and click **Create budget**.
   1. Under **General information**, specify:
      * **Name**: `vm-budget`.
      * **Type**: `Due and payable`.
      * **Amount**: Amount of consumption expenses, e.g. `₽10`.
      * **Calculation period**: `Monthly`.
      * **Expires**: Budget expiration date.

         The end date is when the budget stops calculating usage and sending notifications. The end date is the last day of the month. It must be within five years of the current date.
      * **Notify**: Select yourself.
   1. Under **Scope**, select your current [folder](../../resource-manager/concepts/resources-hierarchy.md#folder) and the **Compute Cloud** service.
   1. Under **Limits**, set the threshold values as a percentage upon reaching which:
      * Specified users will get notifications.
      * Trigger for budgets will fire.

      For example, you can set two thresholds: `50%` and `100%`.
   1. Click **Create**.
   1. A window will open with the new `vm-budget`. In the **ID** field, copy the ID of the new budget. You will need it later when creating a trigger.

- API {#api}

   To create a budget, use the [create](../../billing/api-ref/Budget/create.md) method for the [Budget](../../billing/api-ref/Budget/index.md) resource or the [BudgetService/Create](../../billing/api-ref/grpc/Budget/create.md) gRPC API call.

{% endlist %}

## Create a budget trigger {#create-trigger}

{% list tabs group=instructions %}

- Management console {#console}

    1. In the [management console](https://console.yandex.cloud), select the folder where you created the service account, function, and budget.

    1. Navigate to **Cloud Functions**.

    1. In the left-hand panel, select ![image](../../_assets/console-icons/gear-play.svg) **Triggers**.

    1. Click **Create trigger**.

    1. Under **Basic settings**:

        * Specify the trigger name: `vm-stop-trigger`.
        * In the **Type** field, select `Budget`.
        * In the **Launched resource** field, select `Function`.

    1. Under **Budget settings**, select your billing account and the `vm-budget` you created in the previous step.

    1. Under **Function settings**, select the `function-for-budget` function and specify the following:

        * [Function version tag](../../functions/concepts/function.md#tag). The default tag is `$latest`.
        * `service-account-for-budget` service account. This is the service account to invoke the function.

    1. Click **Create trigger**.

- CLI {#cli}

    To create a budget trigger that invokes the `function-for-budget` function, run the following command:

    ```bash
    yc serverless trigger create billing-budget \
      --name vm-stop-trigger \
      --billing-account-id <billing_account_ID> \
      --budget-id <budget_ID> \
      --invoke-function-id <function_ID> \
      --invoke-function-service-account-id <service_account_ID>
    ```

    Where:

    * `--name`: Trigger name.
    * `--billing-account-id`: [Billing account](../../billing/concepts/billing-account.md) ID.
    * `--budget-id`: `vm-budget` ID.
    * `--invoke-function-id`: `function-for-budget` ID.
    * `--invoke-function-service-account-id`: `service-account-for-budget` service account ID.

    Result:

    ```text
    id: a1sfe084v4**********
    folder_id: b1g88tflru**********
    created_at: "2022-12-04T08:45:31.131391Z"
    name: vm-stop-trigger
    rule:
      billing-budget:
        billing-account-id: dn2char50j**********
        budget-id: dn2jnshmdlc1********
        invoke_function:
          function_id: d4eofc7n0m03********
          function_tag: $latest
          service_account_id: aje3932acd0c********
    status: ACTIVE
    ```

- API {#api}

    To create a budget trigger, use the [create](../../functions/triggers/api-ref/Trigger/create.md) method for the [Trigger](../../functions/triggers/api-ref/Trigger/index.md) resource.

{% endlist %}

## Create Compute Cloud VMs {#create-vm}

{% list tabs group=instructions %}

- CLI {#cli}

    1. Select a subnet:
        ```bash
        yc vpc subnet list
        ```

    1. Create a VM:
        ```bash
        yc compute instance create \
           --name target-instance-1 \
           --labels target-for-stop=true \
           --zone ru-central1-d \
           --network-interface subnet-name=<subnet_name>,nat-ip-version=ipv4 \
           --create-boot-disk image-folder-id=standard-images,image-family=ubuntu-2004-lts \
           --ssh-key ~/.ssh/<key_name>.pub
        ```

        Where:

        * `--name`: VM name.
        * `--labels`: Label. If the `target-for-stop` label value is `true`, the budget trigger will stop the VM when it exceeds the resources allocated in the budget.
        * `--zone`: Availability zone matching the selected subnet.
        * `subnet-name`: Name of the selected subnet.
        * `nat-ip-version`: Public IP.
        * `image-family`: [Image family](../concepts/image.md#family). This option allows you to install the latest version of the operating system from the specified image family.
        * `--ssh-key`: Public SSH key path. The VM will automatically create a user named `yc-user` for this key. [How to create an SSH key pair](../operations/vm-connect/ssh.md#creating-ssh-keys).

        Result:
        ```text
        done (34s)
        id: fhm8lon8b9a9********
        folder_id: b1gc1t4cb638********
        created_at: "2022-12-07T11:29:39Z"
        name: target-instance-1
        labels:
          target-for-stop: "true"
        zone_id: ru-central1-d
        platform_id: standard-v2
        resources:
          memory: "2147483648"
          cores: "2"
          core_fraction: "100"
        status: RUNNING
        metadata_options:
          gce_http_endpoint: ENABLED
          aws_v1_http_endpoint: ENABLED
          gce_http_token: ENABLED
          aws_v1_http_token: ENABLED
        boot_disk:
          mode: READ_WRITE
          device_name: fhm459pv68ta********
          auto_delete: true
          disk_id: fhm459pv68ta********
        network_interfaces:
          - index: "0"
            mac_address: d0:0d:8a:e2:e8:5a
            subnet_id: e9b3vsm91fl7********
            primary_v4_address:
              address: 10.128.0.4
              one_to_one_nat:
                address: 158.160.47.82
                ip_version: IPV4
        fqdn: fhm8lon8b9a9********.auto.internal
        scheduling_policy: {}
        network_settings:
          type: STANDARD
        placement_policy: {}
        ```

- API {#api}

    To create a VM instance, use the [create](../api-ref/Instance/create.md) method for the [Instance](../api-ref/Instance/index.md) resource.

{% endlist %}

Similarly, create two more VMs: `target-instance-2` and `target-instance-3`. For the latter, set the `target-for-stop` label to `false`.

## Make sure the trigger stops the VM instances {#test}

Wait until the resource use of your VMs reaches the threshold values specified in your budget. Make sure the budget trigger has invoked the function you created, and `target-instance-1` and `target-instance-2` have stopped.

## How to delete the resources you created {#clear-out}

To stop paying for the resources you created:

1. [Delete](../../functions/operations/trigger/trigger-delete.md) the budget trigger.
1. [Delete](../../functions/operations/function/function-delete.md) the function.
1. [Delete](../operations/vm-control/vm-delete.md) the VM instances.
1. [Delete](../../iam/operations/sa/delete.md) the service account.