# Function instances for Firewall

A function instance binds one function to one [firewall](/en/documentation/platform/firewall/) and holds the arguments that function receives on that firewall. The instance runs only when a rule in [Rules Engine for Firewall](/en/documentation/platform/firewall/rules-engine/) names it in a *Run Function* behavior. For example, one firewall can hold two instances of [Bot Manager Lite](/en/documentation/platform/firewall/bot-manager/bot-manager-lite/) with different `threshold` values, and each rule runs the instance it names.

---

## Instance fields

Each firewall holds its own function instances. Azion Console lists them in the firewall's **Functions Instances** tab, where the **Function** button opens a drawer with three sections: **General**, **Function**, and **Arguments**. The API serves the same records under `/v4/workspace/firewalls/<firewall-id>/functions`, and Azion CLI under the `firewall-instance` noun. An instance carries nine fields: five that a request sets, and four that the platform sets and returns.

| Field           | Type                                      | Required  | Description                                                                                                                                                                                                              |
| --------------- | ----------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `id`            | integer                                   | Read-only | The instance ID. A *Run Function* behavior sends it in `value`, and every call on one instance takes it in the path                                                                                                      |
| `name`          | string, 1 to 100 characters               | Yes       | The instance name. The Console renders it as the **Name** field and asks for a unique, descriptive name. Past 100 characters, the API answers `Ensure this field has no more than 100 characters.`                       |
| `function`      | integer                                   | Yes       | The ID of the function the instance binds, which is not the instance ID. The function must declare the `firewall` execution environment. The Console renders it as the **Function** selector                             |
| `args`          | JSON object or array, up to 100,000 bytes | No        | The arguments the function receives, stored exactly as sent and never checked against the function. The Console renders them as the **Arguments** editor. Their defaults and their validation are described in Arguments |
| `azion_form`    | JSON object                               | No        | A JSON Schema that renders the arguments as a form. When a request sends none, the platform stores `{}`                                                                                                                  |
| `active`        | boolean                                   | No        | Whether the instance is active. Only an active instance appears in the Console list of a *Run Function* behavior. The Console sends `true` when it creates an instance and offers no control to change it                |
| `last_editor`   | string                                    | Read-only | The account that last changed the instance, shown in the **Last Editor** column                                                                                                                                          |
| `last_modified` | date-time                                 | Read-only | When the instance last changed, shown in the **Last Modified** column                                                                                                                                                    |
| `created_at`    | date-time                                 | Read-only | When the instance was created                                                                                                                                                                                            |

The **Functions Instances** list shows the columns **Name**, **Function**, **Last Editor**, and **Last Modified**. It has no status column, and **Delete** is its only row action.

The **Function** selector lists only the functions in the account whose `execution_environment` is `firewall`, and its **Create Function** footer creates one. The API refuses a function with any other environment, with `Invalid edge function runtime. You should use a function designed for Edge Firewall.` Functions from Azion Marketplace, such as Bot Manager Lite, Secure Token, and JWT, are firewall functions as well. A Marketplace function reaches the account through Azion Console, because neither Azion CLI nor the API has an install operation. Its record does not expose its source code.

An instance never changes the code of its function. It sets the name, the arguments, and whether the instance is active.

The **Functions Instances** tab appears only when [Functions](/en/documentation/platform/functions/) is enabled on the firewall. The switch is **Functions** in the firewall's **Main Settings** tab, and `modules.functions.enabled` in the API. Functions is on when a firewall is created through the API, the CLI, or the Console create page. The Console create drawer is the exception, and starts Functions off.

An application holds the same object under a tab of the same name. An instance on a firewall runs its function before the request reaches the application, so the function can stop the request there. An instance on an application runs its function as part of serving the request. For more information, refer to [Function instances](/en/documentation/platform/applications/functions-instances/).

---

## Arguments

The arguments of an instance are the JSON value in its `args` field, and the function reads them each time the instance runs. They let one function behave differently in each instance: the code stays the same, and only the values change. In Azion Console, the **Arguments** section of the instance drawer edits them. When the selected function carries a non-empty `azion_form`, the section offers two modes: *Form*, the default, which builds a form from that schema, and *JSON*. Otherwise, the JSON editor appears alone.

The help text of the editor says that code reads an argument with `event.args('arg_name')`. For the code side of a firewall function, refer to [Functions for Firewall](/en/documentation/platform/firewall/functions/).

An `args` object that sets four Bot Manager Lite arguments:

```json
{
  "threshold": 30,
  "action": "deny",
  "internal_logs": 2,
  "log_tag": "storefront-bots"
}
```

All four keys are among the arguments Bot Manager Lite ships defaults for. A key the function does not read is saved the same way, and the function ignores it.

### Default arguments

A function record carries `default_args`, the argument values the function ships with, next to its `azion_form`. The command `azion describe function --function-id <function-id>` returns both. When an instance supplies no arguments, the function runs with its `default_args`. A key the instance sets takes the value the instance gives it.

The fallback happens at run time, not in the instance record. The record keeps only what it was sent, so an instance created with `{}` reads back `{}`, not the defaults of its function. For example, Bot Manager Lite ships a `threshold` of `30` in its `default_args`. An instance of it with `args` of `{}` shows no `threshold`, and runs at `30`.

The eight default arguments of Bot Manager Lite are `action`, `bad_fingerprint_list`, `disabled_rules`, `good_fingerprint_list`, `internal_logs`, `log_headers`, `log_tag`, and `threshold`. For what each one does, refer to [Arguments](/en/documentation/platform/firewall/bot-manager/arguments/).

### Argument validation

The platform checks `args` for two things only. Azion Console refuses content that does not parse, with `Invalid JSON`, and the API refuses a value larger than 100,000 bytes. The bound is decimal: a payload of 102,400 bytes is refused with `Value size (in bytes) is too big. Maximum size allowed is 100000 bytes.` For every bound a firewall applies, refer to [Firewall limits](/en/documentation/platform/firewall/limits/).

Nothing checks the keys or the values against the function. The platform stores every key as sent, with its JSON type, including a key the function never reads, and the save succeeds. For example, a Bot Manager Lite instance with `"thresold": 5` saves and reads back exactly as typed, and does not lower the threshold. No interface reports the misspelled key, so compare each key name with the documentation of the function before you save.

---

## Run Function behavior

A function instance runs only when a firewall rule names it in a *Run Function* behavior. When a request matches the criteria of that rule, the platform runs the function with the arguments of the instance. The function runs on Azion's distributed infrastructure, before the request reaches the application. What the client receives then depends on what the function does with the request.

In the API, the behavior is `run_function`, and its `value` attribute holds the `id` of the instance, never the ID of the function:

```json
{ "type": "run_function", "attributes": { "value": <function-instance-id> } }
```

In Azion Console, choosing *Run Function* adds the **Select a Function** list. The list holds the active instances of this firewall rather than the functions of the account, and its **Create Function Instance** footer creates one. Each rule can hold one *Run Function* behavior, so one rule runs one instance.

The behavior requires Functions enabled on the firewall and access to Functions in the account. Without either, the Console shows *Run Function - required Functions* and does not let you select it.

A rule that runs one instance on every request, because `${request_uri}` `starts_with` `/` matches every URI:

```json
{
  "name": "Run Bot Manager on every request",
  "active": true,
  "criteria": [[{ "variable": "${request_uri}", "conditional": "if", "operator": "starts_with", "argument": "/" }]],
  "behaviors": [{ "type": "run_function", "attributes": { "value": <function-instance-id> } }]
}
```

For the order a firewall runs its rules in, refer to [How Firewall works](/en/documentation/platform/firewall/how-it-works/).

---

## API

Instance operations sit under `https://api.azion.com/v4/workspace/firewalls/<firewall-id>/functions`. Each call authenticates with a personal token in the header `Authorization: Token [TOKEN VALUE]`, and a call with a body adds `Content-Type: application/json`.

| Operation                        | Method and path                   |
| -------------------------------- | --------------------------------- |
| List the instances of a firewall | `GET /functions`                  |
| Create an instance               | `POST /functions`                 |
| Retrieve an instance             | `GET /functions/{function_id}`    |
| Replace an instance              | `PUT /functions/{function_id}`    |
| Update part of an instance       | `PATCH /functions/{function_id}`  |
| Delete an instance               | `DELETE /functions/{function_id}` |

In these paths, `{function_id}` takes the ID of the instance, not the ID of the function. Creating or deleting an instance answers `202`. Reading one answers `200`, with the record in `data` and no `state` key.

Create an instance of Bot Manager Lite with four arguments set, where `<function-id>` is the ID of the Bot Manager Lite function in your account:

```bash
curl --request POST \
  --url https://api.azion.com/v4/workspace/firewalls/<firewall-id>/functions \
  --header 'Accept: application/json' \
  --header 'Authorization: Token [TOKEN VALUE]' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "bot-manager-lite",
  "function": <function-id>,
  "active": true,
  "args": { "threshold": 30, "action": "deny", "internal_logs": 2, "log_tag": "storefront-bots" }
}'
```

The response answers `202`, with a `state` of `pending` and the instance in `data`. This excerpt keeps `id` and the fields the call sent:

```json
{
  "state": "pending",
  "data": {
    "id": <function-instance-id>,
    "name": "bot-manager-lite",
    "args": { "threshold": 30, "action": "deny", "internal_logs": 2, "log_tag": "storefront-bots" },
    "azion_form": {},
    "function": <function-id>,
    "active": true
  }
}
```

The platform stores `azion_form` as `{}` because the call sent none. The `id` in `data` is the value a *Run Function* behavior sends.

---

## CLI

The `firewall-instance` noun of Azion CLI creates, lists, and reads instances.

| Command                            | What it does                                                                                      |
| ---------------------------------- | ------------------------------------------------------------------------------------------------- |
| `azion create firewall-instance`   | Creates an instance, with `--name`, `--firewall-id`, `--function-id`, `--args`, and `--active`    |
| `azion list firewall-instance`     | Lists the instances of a firewall, with `--firewall-id`                                           |
| `azion describe firewall-instance` | Returns one instance, with `--firewall-id` and `--instance-id`. `--format json` prints the record |

The `--args` flag takes the path of a JSON file, not inline JSON. A file that sets three arguments:

```json
{"threshold": 10, "action": "deny", "log_tag": "bm-probe"}
```

Save it as `args.json`, then create the instance:

```bash
azion create firewall-instance --name my-instance --firewall-id <firewall-id> \
  --function-id <function-id> --args args.json --active true
```

```text
Created Firewall Function Instance with ID <function-instance-id>
```

Read the instance back:

```bash
azion describe firewall-instance --firewall-id <firewall-id> --instance-id <function-instance-id> --format json
```

The arguments return with the keys, values, and JSON types the file sent. An excerpt of the record:

```json
{
 "active": true,
 "args": { "action": "deny", "log_tag": "bm-probe", "threshold": 10 },
 "azion_form": {},
 "function": <function-id>,
 "id": <function-instance-id>,
 "name": "my-instance"
}
```

---

## Errors

Azion CLI prints the message of the platform inside `Error: failed to create the Firewall Function Instance: [...]`, or inside `Error: failed to create the Firewall Rule: [...]` for a rule. The table lists each message as it appears, with what causes it.

| Message                                                                                                                  | What causes it                                                                               | What to do                                                         |
| ------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| `Ensure this field has no more than 100 characters.`                                                                     | A `name` longer than 100 characters                                                          | Send a name of 100 characters or fewer                             |
| `Value size (in bytes) is too big. Maximum size allowed is 100000 bytes.`                                                | An `args` value larger than 100,000 bytes, such as 102,400 bytes                             | Reduce `args` to 100,000 bytes or fewer                            |
| `Invalid edge function runtime. You should use a function designed for Edge Firewall.`                                   | A `function` whose `execution_environment` is `application`                                  | Instantiate a function whose `execution_environment` is `firewall` |
| `The function was not found.`                                                                                            | A `function` ID that names no function in the account, such as `99999999`                    | Send the ID of an existing firewall function                       |
| `Function Instance '99999999' not found.`                                                                                | A rule whose `run_function` behavior sends a `value` that names no instance, here `99999999` | Send the `id` of an existing instance, not the ID of its function  |
| `Error: failed to read args file: open {"threshold":10,"action":"deny","log_tag":"bm-probe"}: no such file or directory` | `azion create firewall-instance` with inline JSON in `--args`                                | Save the arguments to a file and pass its path                     |
| `Invalid JSON`                                                                                                           | Content in the Console **Arguments** editor that does not parse as JSON                      | Correct the JSON, then save                                        |

---

## Related resources

- [Instantiate a function on a firewall](/en/documentation/guides/application-security/firewall-and-waf/instantiate-functions.md): The procedure that creates an instance from Azion Console or the API.
- [Create a firewall rule](/en/documentation/guides/application-security/firewall-and-waf/work-with-rules-engine.md): The procedure that builds a firewall rule in Azion Console.
- [How Firewall works](/en/documentation/platform/firewall/how-it-works.md): Where a Run Function behavior sits among the rules a firewall runs on a request.
- [Functions limits](/en/documentation/platform/functions/limits.md): The ceilings that bound one run of the function an instance binds.
