# Exoscale Documentation — IAM

> Identity and Access Management (IAM): define which permissions and actions users and services can perform, via the Portal, CLI, API or integrations.

---

url: https://community.exoscale.com/product/security/iam/

---

# IAM

- [Quick Start - IAM](https://community.exoscale.com/product/security/iam/quick-start/index.md) - Get started quickly with a guided setup to achieve your first successful use of the service, including the required steps and configuration.
- [Overview - IAM](https://community.exoscale.com/product/security/iam/overview/index.md) - Terminology and key features at a glance, learn the core concepts, capabilities, and options of this service before you dive deeper.
- [How-To - IAM](https://community.exoscale.com/product/security/iam/how-to/index.md) - Step-by-step guides to configure the service and use specific features, with clear instructions for common tasks and problem solving.
- [Service Boundaries - IAM](https://community.exoscale.com/product/security/iam/service-boundaries/index.md) - Service boundaries: quotas, limits, and the guaranteed service levels (SLA) for this product, including key constraints to plan and operate reliably.
- [Reference - IAM](https://community.exoscale.com/product/security/iam/reference/index.md) - Reference documentation for API, CLI, and tools, browse comprehensive details, parameters, commands, and integration notes.

---

url: https://community.exoscale.com/product/security/iam/quick-start/

---

# Quick Start - IAM



This is a quick start guide focusing on the creation of new API Keys and Roles. 
For more in-depth knowledge of IAM, please refer to the
[Policy Guide](https://community.exoscale.com/product/security/iam/how-to/policy-guide/index.md) and the


## Prerequisites 
The goal is to have the necessary resources to use IAM outside of the portal too.

1. Navigate to IAM, then ROLES, and add a new Role.
2. In the Role creation form: name your new Role `example-api-role` and leave other settings as default - make sure "Default service strategy" is _ALLOW_ and "Service Classes" is empty. This will create a role with no restrictions.
3. Navigate to IAM, then KEYS, and add a new API Key.
4. In the Key creation form: name your key `example-api-key` and select the Role from the previous step (`example-api-role`).
5. Once created, the API key id `API Key` and secret `API Secret` are displayed. The secret is displayed only once. We will need both for the next step.
6. This documentation shows you 
[how to configure the CLI with the API key you just created](https://community.exoscale.com/tools/command-line-interface/index.md#configuration). If you haven't installed the CLI yet, you can [refer to the Installation section](https://community.exoscale.com/tools/command-line-interface/index.md#installation)


##  Roles and API Keys 
You can create a Role and API keys from the Portal or the [CLI](https://community.exoscale.com/tools/command-line-interface/index.md).
Every role defines a policy in charge of authorization. API keys are attached to a role, they are responsible for authentication.


###  From the CLI
#### Creating a new API key

```bash
exo iam api-key create second-api-key example-api-role
```

Because we specified the same role from the first time setup, this will create another key with **unrestricted** use of all API operations - identical to the one we created previously.

For most use cases however, we recommend that you create a **restricted** Role that can only be used for certain operations. 

#### Creating a new Role

```bash
cat policy.json | exo iam role create example-restricted-role --editable --policy -
```

`policy.json` defines the role's policy. 

For example, here's a policy that allows IAM operations, allows DNS operations, but denies anything else:

```json
{
  "default-service-strategy": "deny",
  "services": {
    "dns": {
      "type": "allow"
    },
    "iam": {
      "type": "allow"
    }
  }
}
```

`"default-service-strategy": "deny"` rejects any request that doesn't satisfy the conditions under `"services"`.

`"services"` are high level classification of operations used to split the authorization flow. Possible keys inside `"services"` are: `compute`, `iam`, `dns`, `dbaas`, `sos`, `block-storage`, `ai`, `kms` and `organization`.

You can find more information about which operations/endpoint belong to which service and more details of command-line usage for IAM in the following references:

- [API-IAM reference](https://community.exoscale.com/reference/api/iam/index.md)
- [CLI-IAM reference](https://community.exoscale.com/reference/cli/exo/iam/index.md)

---

url: https://community.exoscale.com/product/security/iam/overview/

---

# Overview - IAM




Identity and Access Management (IAM) defines which permissions and actions individuals and services can perform on your platform. These rules apply regardless of how you access Exoscale—whether programmatically via the command line, through your preferred programming language, via integrations with third-party tools, or through the user-friendly web portal.

Exoscale provides multiple interaction methods with its platform, including programmatic access through the command line, various programming languages, and integrations with third-party tools, as well as a simple user interface via the web portal; in all instances, users will encounter Identity and Access Management (IAM), which determines permissions and roles for both individuals and services.

## Understand Roles and Policies

IAM is composed by 2 main building blocks:

Roles
: which act as a container for a single Policy and add some options on top of
  it.

Policies
: which are a set of rules describing what can and what cannot be done.


### Authorization Flow
IAM decouples authorization from authentication. Therefore, we can split authorization through several levels or "gateways" for a single request.

![](auth_flow.png "Authorization Layers")

Organization Policy
: the ORG Policy applies to any User or API Key within an organization.

Role Policy
: the ROLE Policy is assigned to one or more API Keys or Users.

A request must be authorized at both levels to be processed by the platform.
The Organization Policy defaults to an empty set of rules, allowing all operations to be performed.

> [!WARNING]
> Beware to not lock yourself out. 
> A wrong Organization Policy might end up denying you access to the entire platform. 
> At any time, an Owner of an Organization can reset the Organization Policy via the web portal.


### Roles
A Role is a way to store a Policy:

```json
{
  "name": "my-new-role",
  "policy": {
    "default-service-strategy": "deny",
    "services": {
      "iam": {
        "type": "allow"
      }
    }
  }
}
```

In the example above, the role called `"my-new-role"` contains a Policy stating that all operations are denied except for IAM services, which are explicitly allowed.

A Role can have one and only one Policy. Roles can then be assigned to one or more API Keys or Users, applying the permissions stated in its Policy.


### Policies
A Policy is composed of

- The **Services** map - a separation of the authorization logic according to which service class the request belongs to.
- The **Default Service Strategy** - a default top-level `allow` or `deny` decision that applies when there is no entry in the services map for the service of the incoming API operation. For example: `list-zones` is an operation that belongs to the `compute` service - if `compute` is not present in the services map, the default service strategy will be applied.

```json
{
  "default-service-strategy": "allow",
  "services": {
    "compute": {...},
    "dbaas": {...}
  }
}
```

Requests are evaluated in the context of the service class they belong to - Compute, SOS, DBaaS, etc - a Policy can define one of the following authorization bodies for each service:

- **Nothing**
  in the absence of a specification, use the default service strategy
- **Allow**
  overrides the default service strategy - allow all requests.
- **Deny**
  overrides the default service strategy - deny all requests.
- **Rule-based**
  a more fine-grained approach to authorization

> [!NOTE]
> Policy updates may __not__ take effect immediately due to some background synchronization. 
> Generally, you should design your usage patterns with re-use of Roles in mind, and if you 
> require frequent rotation, favor rotating API Keys rather than Roles.


## Terminology

Identity
: Represents users or services that have access to the Exoscale platform. These
  can be individuals, applications, or any entity that interacts with Exoscale
  resources.

Access Management
: The process and policies used to control who is authenticated and authorized
  to use resources.

Policies
: Sets of permissions attached to identities that define what actions they can
  perform on which resources.

Roles
: Collections of policies that can be assigned to identities to streamline
  permission assignments that meet specific needs or job functions.

Permissions
: Specific rights that define what actions an identity can perform, such as
  read, write, or delete resources.

Authentication
: The process of verifying an identity when it tries to access resources.

Authorization
: The process of determining whether an authenticated identity has permission
  to perform a requested action on a resource.


## Features

Fine-Grained Access Controls
: Allows administrators to define precise access controls at various levels,
  ensuring that users and services have only the permissions they need.

Policy-Based Permissions
: Provides the ability to write policies that specify who can access which
  resources and what actions they can perform.

Role-Based Access Control (RBAC)
: Supports assigning roles to users and groups, simplifying the management of
  permissions by associating roles with specific job functions.

Multi-Factor Authentication (MFA)
: Enhances security by requiring multiple forms of verification before granting
  access to the platform.

Audit Logging
: Keeps detailed logs of user activities and changes made within the IAM
  system, aiding in compliance and monitoring.

API Access
: Provides programmatic access to manage IAM resources, allowing developers to
  integrate IAM functionality into their workflows.

Customizable Policies
: Allows for the creation of customized policies tailored to specific
  organizational needs, accommodating diverse security requirements.

---

url: https://community.exoscale.com/product/security/iam/how-to/

---

# How-To - IAM

Step-by-step guides to configure the service and use specific features, with clear instructions for common tasks and problem solving.

- [Manage API Keys](https://community.exoscale.com/product/security/iam/how-to/key-mgmt/index.md) - Manage IAM API keys by creating, listing, or deleting them, and recreate legacy access key behavior with roles and custom policies.
- [Manage IAM Roles](https://community.exoscale.com/product/security/iam/how-to/role-mgmt/index.md) - Manage IAM roles with the CLI to view policies, export JSON, copy existing roles, update editable roles, and delete unused roles.
- [Manage Users and API Keys](https://community.exoscale.com/product/security/iam/how-to/users-keys/index.md) - Manage IAM users and API keys by assigning roles and policies, inviting users, controlling access, and auditing actions across your organization.
- [Write IAM Policies](https://community.exoscale.com/product/security/iam/how-to/policy-guide/index.md) - Write IAM policies with rule-based authorization and CEL expressions for Compute, DNS, IAM, SOS, and portal access control scenarios.
- [Policy Examples](https://community.exoscale.com/product/security/iam/how-to/policy-examples/index.md) - Exoscale IAM policy examples for roles, permissions, services, zones, and secure access control best practices.

---

url: https://community.exoscale.com/product/security/iam/how-to/key-mgmt/

---

# Manage API Keys

API Keys can't be updated or reassigned to another role. One can only create/list/delete them.

```bash
exo iam delete api-key EXO1234567890
```

Note that you should rather use the key's ID _EXO..._ instead of its name when deleting.

## Migrating Legacy Access Keys 

If you have been using our services for a while, you might have noticed old keys being labeled `Legacy` on the platform, known as Access Keys.

It is currently not possible to _migrate_ Access Keys, but you can reproduce an existing Access Key's behavior by creating a Role with a specific policy.

### Unrestricted Access Keys

This is the simple scenario: create a Role with default settings and API Keys to go along with it.

### Restricted Access Keys

Start by getting the Access Key's _Operations_.

```bash
exo iam access-key show EXO...
```

Output:

```bash
┼────────────────┼──────────────────────────────────────────────────┼
│ IAM ACCESS KEY │                                                  │
┼────────────────┼──────────────────────────────────────────────────┼
│ Name           │ my-legacy-access-key                             │
│ Type           │ restricted                                       │
│ API Key        │ EXOXXXXXXXXXXXXXXXXXXXXXXXX                      │
│ API Secret     │ *******************************************      │
│ Operations     │ create-load-balancer                             │
│                │ delete-load-balancer                             │
|                | list-dns-domain-records                          |
┼────────────────┼──────────────────────────────────────────────────┼
```

Create an initial `"default-service-strategy": "deny"` policy

```json
{
  "default-service-strategy": "deny",
  "services": {

  }
}
```

Classify operations by their service class by [referring to this document](https://community.exoscale.com/product/security/iam/reference/index.md). Eg. `create-load-balancer` belongs to `compute`, and `list-dns-domain-records` belongs to `dns`.

For each service class, create a key in the policy under `"services"`, then populate it as such:

```json
{
  "default-service-strategy": "deny",
  "services": {
    "compute": {"type": "rules",
                "rules": [{"action": "allow"
                            "expression": "operation in ['create-load-balancer', 'delete-load-balancer']"}]},
    "dns":     {"type": "rules",
                "rules": [{"action": "allow"
                            "expression": "operation in ['list-dns-domain-records']"}]}
  }
}
```

Pass the finished policy to the CLI through STDIN.

```bash
cat policy.json | exo iam role create migrated-access-key-role --policy -
```

Finally, create an API Key referencing the role

```bash
exo iam api-key create migrated-my-legacy-access-key migrated-access-key-role
```

---

url: https://community.exoscale.com/product/security/iam/how-to/role-mgmt/

---

# Manage IAM Roles

To fetch a role with the CLI

```bash
exo iam role show my-role
```

Output:

```bash
┼─────────────┼──────────────────────────────────────┼
│ ID          │ f37adf2f-1c95-4866-8d73-baadeb3e18a0 │
│ Name        │ my-role                              │
│ Description │                                      │
│ Editable    │ true                                 │
│ Labels      │ n/a                                  │
│ Permissions │ n/a                                  │
┼─────────────┼──────────────────────────────────────┼
```

To see it's policy, add the `--policy` flag

```bash
exo iam role show my-role --policy
```

Output:

```bash
┼─────────┼────────────────────────────────┼─────────────┼────────────────────────────────────────────────┼
│ SERVICE │ TYPE (DEFAULT STRATEGY "DENY") │ RULE ACTION │                RULE EXPRESSION                 │
┼─────────┼────────────────────────────────┼─────────────┼────────────────────────────────────────────────┼
│ compute │ rules                          │ deny        │ resources.sks_cluster.name == 'my-sks-cluster' │
│         │                                │ allow       │ true                                           │
│ dns     │ allow                          │             │                                                │
┼─────────┼────────────────────────────────┼─────────────┼────────────────────────────────────────────────┼
```

To see the policy in JSON format, add the `--output-format json` flag

```bash
exo iam role my-role --policy --output-format json | jq .
```

```json
{
  "default-service-strategy": "deny",
  "services": {
    "compute": {
      "type": "rules",
      "rules": [
        {
          "action": "deny",
          "expression": "resources.sks_cluster.name == 'my-sks-cluster'"
        },
        {
          "action": "allow",
          "expression": "true"
        }
      ]
    },
    "dns": {
      "type": "allow"
    }
  }
}

```

### Reusing an existing Role

If you want to copy or slightly modify an existing policy for a new role, you can get the policy directly:

```bash
exo iam role show --policy --output-format json example-restricted-role > role-policy.json
```

Then

```bash
cat role-policy.json | exo iam role create example-restricted-role-copy --policy -
```

### Updating a role

```bash
exo iam role update example-role --description "hello world"
```

> [!NOTE]
> The Role policy can only be updated if the role is _Editable_ (default true). This parameter can't be updated.


```bash
cat role-policy-v2.json | exo iam role update example-restricted-role --policy -
```

### Deleting a role

```bash
exo iam role delete example-role
```

---

url: https://community.exoscale.com/product/security/iam/how-to/users-keys/

---

# Manage Users and API Keys

Identity and Access Management (IAM) regulates which actions can be performed and by whom, whether it's a person accessing the web portal or an API key used by a client like the CLI or Terraform. Once a Role and a Policy are established, you can either create an API key or invite a new user to your Organization.


## Users management

Users of an Organization are people that have access to the UI and can control or verify your cloud infrastructure and platform from the web portal.

[See here for differences between Users and Organizations](https://community.exoscale.com/platform/organization/index.md).

### Owner Role

When creating a new Organization, would that be during registration or at a later point in time, the new Organization will be provisioned with a special default "Owner" IAM Role.

This Role cannot be modified or deleted, and is available to all Organizations. It contains an empty policy, meaning it allows all kind of operations on the platform.

The user that creates a new Organization becomes automatically its Owner: that is, it will be associated to the Owner Role.

Owner roles can also perform special actions, that might be shared with other special default roles, as e.g managing payments and billing.

An Organization can have more than one Owner, but always at least one Owner must be present. You can hence use the Owner role when you invite other people to your Organization, giving them the same privileges you have.

### Billing Role

On top of the default "Owner" Role, any new Organization also gets a special "Billing".

As for the "Owner" Role, the "Billing" role cannot be modified or deleted. With its "deny-all" policy, it's only allowed to manage administrative and billing information. "Billing" role cannot manage any resources deployed on Exoscale, or perform organizational management.

### Inviting new Users

If you want other people to have access to your Organization via the web portal, you can invite them to join it.

If the person you have invited, identified by their email, has already an Exoscale account, they will be simply added to your Organization. Otherwise they will receive an email with instructions on how to create an account on Exoscale, and once the process completed, they will have access to your Organization. No payment is required during this process, and there is no added cost for adding a user.

Generally, all users are subject to the IAM authorization flow. You need to define a proper IAM Role and its related IAM Policy in order to invite a new user in a role that is not Owner.

When inviting users, you will be able to select the target IAM Role for them, including the two default roles "Owner" and "Billing".

It is of course possible to declare a more complex Policy, restricting e.g. IAM management, allowing some users to operate on only specific parts of the UI, or have only read access, and so on. Please refer to the 
[IAM Policy Guide](https://community.exoscale.com/product/security/iam/how-to/policy-guide/index.md#user-management-and-portal-access) for more information.

> [!NOTE]
> If you are familiar with user management at Exoscale prior of IAM, you can refer to that guide in order 
> to see how a generic `Tech` role could be translated.


You can control the status of an invitation at any time, and while the invitation is pending, you can change the Role if you have changed your mind.

Owners, and users with IAM rights, can edit a Policy if the Role has been created as `editable` at any time.

As an Owner, it is possible to change the Role of a user, and to remove the user from your Organization at any time.

> [!NOTE]
> Given the power and granularity of IAM Roles, the web portal does not adapt to what is allowed or not. 
> A Role with a restricted Policy will still be shown a complete navigation menu, but will potentially 
> receive a number of API errors denying requests for resources that have been denied in the Policy.


Actions performed by Users will be clearly identifiable in Audit Trail by the user UUID.

## API Keys

As for users, API Keys can be created associating them a Role with its related Policy.

While the basic principles are the same as for Users, an API Key is intended to be used programmatically, does not give access to the web portal, and cannot be directly associated to a user.

When creating a new API Key, you can define the associated Role.

Contrary to Users, it is not possible to modify the IAM Role of a key.
The key's secret is shown only once at creation time, and cannot be recovered afterwards.

Actions performed by an API Key will be clearly identifiable in Audit Trail by the key itself.

---

url: https://community.exoscale.com/product/security/iam/how-to/policy-guide/

---

# Write IAM Policies

Below is a collection of best practices and examples for creating effective policies to control the authorization of users and API keys at Exoscale.


## Rule Based Authorization (RBA) 101

```json
{
  "default-service-strategy": "deny",
  "services": {
    "compute": {
      "type": "rules",
      "rules": [
        {
          "action": "deny",
          "expression": "resource.sks_nodepool.name in ['important-nodepool', 'foobar']"
        },
        {
          "action": "allow",
          "expression": "true"
        }
      ]
    }
  }
}
```

A rule-based service policy consists of a list of one or more rules:

- Rules are composed of an **Expression** - against which the request is evaluated, and an **Action** - the resulting *decision* should the evaluated expression returns *TRUE*.
- Expressions are written in the [Common Expression Language CEL](https://github.com/google/cel-spec)
- Rules are evaluated in order
- Operator precedence is defined in the [CEL language specification](https://github.com/google/cel-spec/blob/master/doc/langdef.md#syntax)
- If the `expression` of a rule is **valid**, its `action` becomes the authorization output - `allow` or `deny`.
- If the `expression` of a rule is **invalid**, nothing is concluded and we evaluate the **next rule**
- If **no rule is valid**, the request is not authorized to proceed *regardless of the default service strategy*.

The authorization flow will process each rule in order. The first **valid** rule will short-circuit the authorization process, resulting in its action (either **allow** or **deny**) - equivalent to an OR condition between rules. If all rules are exhausted the request is rejected, regardless of the `default service strategy`.

## CEL bindings

A binding refers to the coupling of a variable in a CEL expression and its corresponding value at the time the expression is evaluated.

The top-level bindings are:

- `service` - the service class in scope (ex: “sos”, “instance-pool”, etc)
- `zone` - the zone where the call is attempted (ex: “ch-gva-2”)
- `now` - a CEL timestamp (string, can be coerced to CEL timestamp in expressions via timestamp(now))
- `source_ip` - the ip of the caller
- `api_key` - the exoscale api key of the caller `EXO1234...`
- `operation` - the operation performed (ex: “scale-instance-pool”)
- `identity` - the caller's identity. A map containing the following keys:
    - `identity.key` - the caller's exoscale API key `EXO1234...`
    - `identity.created` - the CEL timestamp of the key's creation
    - `identity.description` - the key's description (name)
    - `identity.org.uuid` - the caller's organization UUID
    - `identity.org.name` - the caller's organization name
- `parameters` - parameters passed to the command (ex: `parameters.size -> 3`)
- `headers` - relevant for SOS; a map containing the following keys, when applicable:
  - `headers['if-match']` - present when an `if-match` conditional request was sent by the client 
  - `headers['if-none-match']` - present when an `if-none-match` conditional request was sent by the client
  - `headers['sse-c-customer-algorithm']` - present when the SOS request is sent with customer encryption keys, the value will be `AES256`
- `resources` - a map of resource type -> resource (varies depending on the call, see 
[IAM References](https://community.exoscale.com/product/security/iam/reference/index.md)

The `resources` and `parameters` bindings are special, as they don't resolve to mere literals, they can contain nested bindings.
The key difference between `parameters` and `resources` is that the latter might contain metadata that is not in the request payload.

For example:

- `parameters.foo == 'bar'` restrict the call if the input `foo` is equal to `bar`.
- `parameters.foo.bar == 'baz'` or `parameters.foo == {'bar':'baz'}` - restrict the call if the input object `foo` has a key `bar` equal to `baz`
- `parameters.foo.exists(k, k.bar == 'baz')` - restrict the call if the input collection `foo` has and object with a key `bar` equals to `baz`
- `resources.elastic_ip.ip == 10.10.10.10` - restrict the call on an `elastic_ip` resource that has the specified *IP* address.
- `resources.instance_pool.id == "d4c1673a-a342-4a0f-b8e7-b2da4091ddfd"` - restrict the call on an `instance_pool` that has the specified *ID*.

The [IAM References](https://community.exoscale.com/product/security/iam/reference/index.md) contains the necessary information to map any API endpoint to its corresponding `operation`, `parameters`, and `resources` bindings.

## CEL Best Practices


### Split expressions into rules

When writing a rule with more than one condition, it becomes more manageable to write several smaller rules.

This can be achieved as long as the conditions are separated by OR operands.

```json
[
  {
    "action": "allow",
    "expression": "operation == 'list-buckets' || resources.bucket.startsWith('public-')"
  }
]
```

is equivalent to

```json
[
  {
    "action": "allow",
    "expression": "operation == 'list-buckets'"
  },
  {
    "action": "allow",
    "expression": "resources.bucket.startsWith('public-')"
  }
]
```

### Write a catch-all rule

Sometimes, we want our service to have a different default behavior than the rest, while still abiding to some rules.

Because rules are evaluated in order, we can achieve this by writing a catch-all rule at the end of the list.

This is also useful to prevent unexpected authorization failures due to no rule matching the request, it is particularly useful when writing organization policies where you may wish to prevent certain calls across the organization (such as deleting an instance with a particular label, or modifying a particular security group):

```json
{
  "default-service-strategy": "allow",
  "services": {
    "compute": {
      "type": "rules",
      "rules": [
        {
          "action": "deny",
          "expression": "'foo' in resource.sks_cluster.addons"
        },
        {
          "action": "allow",
          "expression": "true"
        }
      ]
    }
  }
}
```

### Don't mix resources in a single rule

Every request may load a specific number of resources in order to have enough information to evaluate the rule properly. This means that not every rule will be able to evaluate every request.

For example: the expression `"resource.security-group.name == 'dev-sg'"` assumes the presence of a security group resource has been loaded into the context.

Consider the incoming request is `GET /instance-pool/<id>` (listing instance pools). Both instance pools and security groups belong to the `compute` service, thus the aforementioned CEL expression will be evaluated. However, only the instance pool resource is loaded for `get-instance-pool` so a CEL expression that refers to `resources.instance_pool.id` and `resources.security_group.id` cannot result in a match for the `get-instance-pool` endpoint.

Even if the expression includes an instance pool parameter `"resource.security_group.name == 'dev-sg' && size(resource.instancepool.instances) > 2"`, it will be invalid too.

Therefore we strongly recommended to write separate rules based on the resource types, and to match on the operation or a list of operations when checking resource rules.


### CEL extensions

We currently have the following CEL functions added as extensions:

* `inIpRange(<string IP>, <string Range>)`: returns true if `IP` is within `Range`.

```js
source_ip.inIpRange('127.0.0/24')
```

```js
inIpRange(source_ip, '127.0.0/24')
```

```js
resources.instance.ipv6_address.inIpRange('2001:0db8:85a3:0000:0000:0000:0000:0000/64')
```

## Compute

Allow only requests that load an `instance` resource - eg. [the `resize-instance-disk` operation](https://community.exoscale.com/reference/iam/compute/index.md#resize-instance-disk) - whose labels include `"dev"`.

```json
{
  "default-service-strategy": "allow",
  "services": {
    "compute": {
      "type": "rules",
      "rules": [
        {
          "action": "allow",
          "expression": "!has(resources.instance)"
        },
        {
          "action": "allow",
          "expression": "'dev' in resources.instance.labels"
        }
      ]
    }
  }
}
```

The first rule `!has(resources.instance)` is needed because the second rule will fail for any request that doesn't involve an instance, thus denying authorization.

> [!WARNING]
> A lookup on a non-existing binding will trigger an error within the CEL evaluator and short-circuit to a 
> failed expression. The use of `has(some.binding.foo)` is very useful to avoid this situation.


Allow read only operations:

```json
operation.startsWith('get-') || operation.startsWith('list-')
```

Allow an instance pool to be scaled to a size of 2, 3, or 4.

```json
operation == 'scale-instance-pool' && int(parameters.size) >= 2 && int(parameters.size) <= 4
```


Prevent the deletion of the load balancer with the name `my-nlb`:

```json
operation == 'delete-load-balancer' && resources.load_balancer.name == 'my-nlb'
```


Prevent the deletion of nodepools on the SKS cluster named `my-sks-cluster`:

```json
operation == 'delete-sks-nodepool' && resources.sks_cluster.name == 'my-sks-cluster'
```


Only allow calls to create instance where `public_ip_assignment` has been specified and where the `public_ip_assignment` is set to `none`.

```json
{
  "default-service-strategy": "allow",
  "services": {
    "compute": {
      "type": "rules",
      "rules": [
        {
          "action": "deny",
          "expression": "operation == 'create-instance' && (!parameters.has('public_ip_assignment') || parameters.public_ip_assignment != 'none')"
        },
        {
          "action": "allow",
          "expression": "true"
        }
      ]
    }
  }
}
```


Prevent the creation and modification of resources in the zone `ch-dk-2`, this could also be written with the simpler rule `zone == 'ch-dk-2'` but errors will be visible on some tooling such as the UI which shows resources across all zones - so allowing read only calls can be useful:

```json
{
  "default-service-strategy": "allow",
  "services": {
    "compute": {
      "type": "rules",
      "rules": [
        {
          "expression": "zone == 'ch-dk-2' && !(operation.startsWith('get-') || operation.startsWith('list-'))",
          "action": "deny"
        },
        {
          "expression": "true",
          "action": "allow"
        }
      ]
    }
  }
}
```

Allow/deny calls from a given source IP:

```json
source_ip == '188.61.116.99'
```

Allow/deny calls from a list of source IPs:

```json
source_ip in ['188.61.126.88', '188.61.116.99']
```

Allow only Snapshot creation and export, and no deletion:

```json
{
  "default-service-strategy": "deny",
  "services": {
    "compute": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation in ['create-snapshot', 'export-snapshot']",
          "action": "allow"
        },
        {
          "expression": "operation.startsWith('get-') || operation.startsWith('list-')",
          "action": "allow"
        }
      ]
    }
  }
}
```

Allow creation of [Private Instances](https://community.exoscale.com/product/compute/instances/overview/index.md) only:

```json
{
  "default-service-strategy": "deny",
  "services": {
    "compute": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation in ['create-instance','create-instance-pool'] && (!parameters.has('public_ip_assignment') || parameters.public_ip_assignment != 'none')",
          "action": "deny"
        },
        {
          "expression": "true",
          "action": "allow"
        }
      ]
    }
  }
}
```

Allow reboot only of any Compute Instance:

```json
{
  "default-service-strategy": "deny",
  "services": {
    "compute": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation.startsWith('get-')",
          "action": "allow"
        },
        {
          "expression": "operation.startsWith('list-')",
          "action": "allow"
        },
        {
          "expression": "operation in ['reboot-instance']",
          "action": "allow"
        }
      ]
    }
  }
}
```

Allow read-only operations on any Compute Instance:

```json
{
  "default-service-strategy": "deny",
  "services": {
    "compute": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation.startsWith('get-')",
          "action": "allow"
        },
        {
          "expression": "operation.startsWith('list-')",
          "action": "allow"
        }
      ]
    }
  }
}
```

List/query Audit-Trail Events but restrict any other operation:

```json
{
  "default-service-strategy": "allow",
  "services": {
    "compute": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation == 'list-events'",
          "action": "allow"
        }
      ]
    }
  }
}
```

## DNS

Allow the retrieval of domains and records:

```json
operation in ['list-dns-domains', 'get-dns-domain', 'list-dns-domain-records', 'get-dns-domain-record']
```

Allow the creation of domains that end with `.ch`:

```json
operation == 'create-dns-domain' && parameters.unicode_name.endsWith('.ch')
```

Allow the creation of `TXT` records only (on any domain):

```json
operation == 'create-dns-domain-record' && parameters.type == 'TXT'
```

Allow `TXT` records to be updated and deleted:

```json
operation in ['update-dns-domain-record', 'delete-dns-domain-record'] && resources.dns_domain_record.type == 'TXT'
```

Allow the creation of `A` records on the domain `my-test-domain.ch`:

```json
operation == 'create-dns-domain-record' && resources.dns_domain.unicode_name == 'my-test-domain.ch' && parameters.type == 'A'
```

Only allow TTLs in the range of 600 (10 minutes) to 7200 (2 hours):

```json
operation in ['create-dns-domain-record', 'update-dns-domain-record'] && int(parameters.ttl) < 600 && int(parameters.ttl) > 7200
```


## DBaaS

Prevent the creation of PostgreSQL services that do not specify a single IP filter of `10.20.0.0/16` (note: the order of `deny` rules in a policy is important, as the first rule that matches will either allow or deny the request):

```json
operation == 'create-dbaas-service-pg' && !parameters.ip_filter.exists_one(x, x == '10.20.0.0/16')
```

Allow the creation of PostgreSQL services with the hobbyist-2 or startup-4 plans:

```json
operation == 'create-dbaas-service-pg' && parameters.plan in ['hobbyist-2', 'startup-4']
```

Prevent the creation of dbaas resources in the zone `CH-DK-2`:

```json
operation.startsWith('create-dbaas-') && zone == 'ch-dk-2'
```

Prevent the deletion of dbaas services in the zone `CH-GVA-2`:

```json
operation.startsWith('delete-dbaas-service-') && zone == 'ch-gva-2'
```

Allow the PostgreSQL service `my-service` to be retrieved:

```json
operation == 'get-dbaas-service-pg' && parameters.name == 'my-service'
```

Allow only a specific User or API Key to see his Kafka secrets

```json
operation = 'reveal-dbaas-kafka-user-password' && parameters.username = 'a-user'
```

## IAM

Deny requests to the IAM service. Allow all requests to other services.

```json
{
  "default-service-strategy": "allow",
  "services": {
    "iam": {
      "type": "deny"
    }
  }
}
```

Deny requests to the IAM service for a specific key. Allow all requests to other services regardless of which key is used.

```json
{
  "default-service-strategy": "allow",
  "services": {
    "iam": {
      "type": "rules",
      "rules": [
        {
          "action": "allow",
          "expression": "api_key != 'EXO123456789'"
        }
      ]
    }
  }
}
```

Alternatively this can be written using a deny rule, which is more practical if you want to enforce this within an organization policy:

```json
{
  "default-service-strategy": "allow",
  "services": {
    "iam": {
      "type": "rules",
      "rules": [
        {
          "action": "deny",
          "expression": "api_key == 'EXO123456789'"
        },
        {
          "action": "allow",
          "expression": "true"
        }
      ]
    }
  }
}
```

Ensure keys can only be created for a particular role id:

```json
{
  "default-service-strategy": "allow",
  "services": {
    "iam": {
      "type": "rules",
      "rules": [
        {
          "action": "deny",
          "expression": "operation == 'create-api-key' && parameters.role_id != '<my-role-id uuid>'"
        },
        {
          "action": "allow",
          "expression": "true"
        }
      ]
    }
  }
}
```

Prevent updates and deletion of a role named 'my-role':

```json
operation in ['update-iam-role', 'update-iam-role-policy', 'delete-iam-role'] && resources.iam_role.name == 'my-role'
```

Allow keys to be created, listed, and retrieved:

```json
{
  "default-service-strategy": "allow",
  "services": {
    "iam": {
      "type": "rules",
      "rules": [
        {
          "action": "allow",
          "expression": "operation in ['create-api-key', 'list-api-keys', 'get-api-key']"
        }
      ]
    }
  }
}
```

Create a time-expiring IAM Key limited to the Compute service class (5 minutes in the example):

```json
{
  "default-service-strategy": "deny",
  "services": {
    "compute": {
      "type": "rules",
      "rules": [
        {
          "expression": "timestamp(identity.created) < timestamp(now) - duration('5m')",
          "action": "deny"
        },
        {
          "expression": "true",
          "action": "allow"
        }
      ]
    }
  }
}
```

## Object Storage (SOS)

The following policy allows listing and retrieving objects on the bucket `my-bucket`, no other operations or buckets are allowed meaning that the caller will not be able to list buckets or to look at the properties of a bucket using the `exo storage show` command.

```json
{
  "default-service-strategy": "deny",
  "services": {
    "sos": {
      "type": "rules",
      "rules": [
        {
          "expression": "parameters.bucket == 'my-bucket' && operation in ['list-objects', 'get-object']",
          "action": "allow"
        }
      ]
    }
  }
}
```

A more elaborate policy is provided in the next example:

- Allows listing all buckets in the organization via the Exoscale CLI `exo storage ls` or s3cmd `s3cmd ls`. Other S3 compatible tooling may require additional operations to be added,
- Operations relating to a bucket are restricted, only `my-bucket` and `my-other-bucket` are allowed, attempts to list objects in other buckets will be rejected.
- The last rule in the policy relates to usage via the Exoscale CLI, more operations are needed to support `exo storage show sos://my-bucket`.
- The order of the rules is important due to the way the `deny` rule has been written, in order to allow listing buckets the expression containing `list-buckets` must come before the `deny` expression.

```json
{
  "default-service-strategy": "deny",
  "services": {
    "sos": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation in ['list-sos-buckets-usage', 'list-buckets']",
          "action": "allow"
        },
        {
          "expression": "!(parameters.bucket in ['my-bucket', 'my-other-bucket'])",
          "action": "deny"
        },
        {
          "expression": "operation in ['list-objects', 'get-object']",
          "action": "allow"
        },
        {
          "expression": "operation in ['get-bucket-acl', 'get-bucket-cors', 'get-bucket-ownership-controls']",
          "action": "allow"
        }
      ]
    }
  }
}
```

Allow/deny reading/writing on a specific `public` prefix:

```json
operation in ['get-object', 'put-object'] && parameters.key.startsWith('public')
```

Allow only read operations on a certain bucket:

```json
{
  "default-service-strategy": "deny",
  "services": {
    "sos": {
      "type": "rules",
      "rules": [
        {
          "expression": "resources.bucket != 'YOUR-BUCKET-NAME'",
          "action": "deny"
        },
        {
          "expression": "operation.startsWith('get-')",
          "action": "allow"
        },
        {
          "expression": "operation.startsWith('head-')",
          "action": "allow"
        },
        {
          "expression": "operation.startsWith('list-')",
          "action": "allow"
        }
      ]
    }
  }
}
```

Allow list all organization buckets. Allow list content of bucket `my-bucket` and all sub-folders, allow only read / download in `my-sub-folder`:

```json
{
  "default-service-strategy": "deny",
  "services": {
    "sos": {
      "type": "rules",
      "rules": [
        {
          "expression": "parameters.bucket != 'my-bucket'",
          "action": "deny"
        },
        {
          "expression": "operation.startsWith('get-') && parameters.key.startsWith('my-sub-folder')",
          "action": "allow"
        },
        {
          "expression": "operation.startsWith('head-') && parameters.key.startsWith('my-sub-folder')",
          "action": "allow"
        },
        {
          "expression": "operation.startsWith('list-')",
          "action": "allow"
        }
      ]
    }
  }
}
```

To forbid listing organization buckets. Add this rule as the first rule (in example above):

```json
       {
          "expression": "operation.startsWith('list-') && parameters.bucket == 'my-bucket'",
          "action": "allow"
        },
```

## User management and portal access

Most commonly, you will need an IAM Role giving access to all resources in the web portal excluding:

- user management
- billing management

This policy will be defined as:

```json
{
  "default-service-strategy": "allow",
  "services": {
    "iam": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation in ['create-user', 'delete-user', 'list-users', 'update-user-role']",
          "action": "deny"
        },
        {
          "expression": "true",
          "action": "allow"
        }
      ]
    }
  }
}
```

> [!WARNING]
> In order for this IAM Policy to be effective it's important to mark the role as non-editable.

A more restrictive policy excluding IAM completely:

```json
{
  "default-service-strategy": "allow",
  "services": {
    "iam": {
      "type": "deny"
    }
  }
}
```

A simple policy allowing only the usage of the `Compute` service class:

```json
{
  "default-service-strategy": "deny",
  "services": {
    "compute": {
      "type": "allow"
    }
  }
}
```

For any other case you can refer to the different service class examples in this page.

> [!NOTE]
> If you desire to be more restrictive in your Policy, keep in mind that in order to work properly 
> the web portal and the exo CLI always require access to the following operations:
>
> - [`list-zones`](https://community.exoscale.com/reference/api/general/zone/index.md#list-zones)
> - [`get-operation`](https://community.exoscale.com/reference/api/general/operation/index.md#get-operation)

Without those two operations the web portal cannot function, you should always include them in your Policy if you wish to customize it heavily.

> [!NOTE]
> Including the ones previously mentioned, other calls might be still required for the portal to be able to 
> bootstrap a specific view. The best way to learn about those is to check your web inspector to identify the 
> required calls. Also, the calls might vary over time, as we add more features to the web portal, eventually 
> leading to the need of adapting your policy.


A concrete example of a more restrictive policy giving access only to a single Object Storage Bucket:

```json
{
  "default-service-strategy": "deny",
  "services": {
    "compute": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation in ['list-zones', 'get-operation']",
          "action": "allow"
        },
      ]
    },
    "sos": {
      "type": "rules",
      "rules": [
        {
          "expression": "parameters.bucket == 'my-bucket'",
          "action": "allow"
        }
      ]
    }
  }
}
```

This would let the user operate only on `my-bucket` and nothing else in the web portal. This also means **the user will not be able to "navigate" to the bucket, as we have not allowed other operations as e.g. `list-buckets-usage` that are required for the different views involved in the navigation to be loaded**. You will hence need to share the exact URL to the required view to your user:

`https://portal.exoscale.com/storage/buckets/ch-dk-2/my-bucket/contents/`

## Concrete AI

The following policy allows read-only access to AI resources (models, deployments and API keys).

```json
{
  "default-service-strategy": "deny",
  "services": {
    "ai": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation.startsWith('get-') || operation.startsWith('list-')",
          "action": "allow"
        }
      ]
    }
  }
}
```

A more elaborate policy is provided in the next example. It builds on the previous one by constraining what can be created:

- Read-only access to all AI resources
- Deployment creation is allowed only with at most 2 GPUs and 3 replicas
- Model creation is allowed

```json
{
  "default-service-strategy": "deny",
  "services": {
    "ai": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation.startsWith('get-') || operation.startsWith('list-')",
          "action": "allow"
        },
        {
          "expression": "operation == 'create-deployment' && int(parameters.gpu_count) <= 2 && int(parameters.replicas) <= 3",
          "action": "allow"
        },
        {
          "expression": "operation == 'create-model'",
          "action": "allow"
        }
      ]
    }
  }
}
```

### AI API Keys

AI API Key management operations are `list-ai-api-keys`, `create-ai-api-key`, `get-ai-api-key`, `update-ai-api-key`, `revoke-ai-api-key`. The `ai_api_key` resource is loaded for the operations that take a key ID in the path (`get-ai-api-key`, `update-ai-api-key`, `revoke-ai-api-key`), exposing `resources.ai_api_key.id`, `.name`, `.models`, `.deployments`, `.created_at`, `.updated_at` and `.revoked_at`. `create-ai-api-key` and `list-ai-api-keys` load no resource; use `parameters` there instead.

Restrict AI API Key management to read operations:

```json
{
  "default-service-strategy": "deny",
  "services": {
    "ai": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation in ['list-ai-api-keys', 'get-ai-api-key']",
          "action": "allow"
        }
      ]
    }
  }
}
```

Deny keys with blanket access. `create-ai-api-key` and `update-ai-api-key` with `parameters.all-models` and `parameters.all-deployments`; this policy rejects that while still allowing scoped keys:

```json
{
  "default-service-strategy": "deny",
  "services": {
    "ai": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation.startsWith('get-') || operation.startsWith('list-')",
          "action": "allow"
        },
        {
          "expression": "operation in ['create-ai-api-key', 'update-ai-api-key'] && ((parameters.has('all-models') && parameters.all-models) || (parameters.has('all-deployments') && parameters.deployments))",
          "action": "deny"
        },
        {
          "expression": "operation in ['create-ai-api-key', 'update-ai-api-key', 'revoke-ai-api-key']",
          "action": "allow"
        }
      ]
    }
  }
}
```

Allow management of a single AI API key, matched on its name:

```json
{
  "default-service-strategy": "deny",
  "services": {
    "ai": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation in ['list-ai-api-keys']",
          "action": "allow"
        },
        {
          "expression": "operation in ['get-ai-api-key', 'update-ai-api-key', 'revoke-ai-api-key'] && resources.ai_api_key.name == 'ci-pipeline'",
          "action": "allow"
        }
      ]
    }
  }
}
```

## Key Management Service (KMS)

Allow read-only access to KMS resources (list and get keys and their rotation history):

```json
{
  "default-service-strategy": "deny",
  "services": {
    "kms": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation.startsWith('get-') || operation.startsWith('list-')",
          "action": "allow"
        }
      ]
    }
  }
}
```

Allow all operations on the organization KMS default key:

```json
{
  "default-service-strategy": "deny",
  "services": {
    "kms": {
      "type": "rules",
      "rules": [
        {
          "expression": "has(resources.kms_key) && resources.kms_key.name == 'Default'",
          "action": "allow"
        }
      ]
    }
  }
}
```

Allow cryptographic operations only (`encrypt`, `decrypt`, `re-encrypt`, `generate-data-key`). Useful for application API keys that use keys but must not manage them:

```json
{
  "default-service-strategy": "deny",
  "services": {
    "kms": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation in ['encrypt', 'decrypt', 're-encrypt', 'generate-data-key']",
          "action": "allow"
        }
      ]
    }
  }
}
```

Allow key lifecycle operations only (create, replicate, enable/disable, rotate, schedule/cancel deletion, enable/disable automatic rotation). Useful for key administrators who manage keys but must not perform cryptographic operations:

```json
{
  "default-service-strategy": "deny",
  "services": {
    "kms": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation in ['create-kms-key', 'replicate-kms-key', 'enable-kms-key', 'disable-kms-key', 'enable-kms-key-rotation', 'disable-kms-key-rotation', 'rotate-kms-key', 'schedule-kms-key-deletion', 'cancel-kms-key-deletion']",
          "action": "allow"
        }
      ]
    }
  }
}
```

Restrict key creation and replication to approved zones only (`ch-gva-2` and `de-fra-1`). For `create-kms-key` the `zone` binding reflects the origin zone. For `replicate-kms-key` the target zone is passed as a `zone` request parameter:

```json
{
  "default-service-strategy": "allow",
  "services": {
    "kms": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation == 'create-kms-key' && !(zone in ['ch-gva-2', 'de-fra-1'])",
          "action": "deny"
        },
        {
          "expression": "operation == 'replicate-kms-key' && !(parameters.zone in ['ch-gva-2', 'de-fra-1'])",
          "action": "deny"
        },
        {
          "expression": "true",
          "action": "allow"
        }
      ]
    }
  }
}
```

Allow all KMS operations except disabling keys and scheduling their deletion. Useful for integration with `compute` or other services to make sure that encrypted compute instances will always be able to start.

```json
{
  "default-service-strategy": "allow",
  "services": {
    "kms": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation in ['disable-kms-key', 'schedule-kms-key-deletion']",
          "action": "deny"
        },
        {
          "expression": "true",
          "action": "allow"
        }
      ]
    }
  }
}
```

For `re-encrypt`, rules on `resources.kms_key` apply to both the source and the destination key, evaluated once for each. Because of this, never pin `resources.kms_key` to one side in a `re-encrypt` rule: a condition like `resources.kms_key.id == '<source-uuid>'` is also checked against the destination key, so it rejects any legitimate call whose destination differs from the source. To write a rule on one side only, use `resources.source_kms_key` or `resources.destination_kms_key`, whose values stay fixed across both evaluations. For example, allow a role to re-encrypt data from any key into one migration key, and nothing else. Replace `<migration-key-uuid>` with the ID of the destination key:

```json
{
  "default-service-strategy": "deny",
  "services": {
    "kms": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation == 're-encrypt' && resources.destination_kms_key.id == '<migration-key-uuid>'",
          "action": "allow"
        }
      ]
    }
  }
}
```

## KMS External Key Stores (XKS)

External Key Store operations belong to the `kms` IAM service. They can be combined with KMS Key rules in the same `services.kms` block. The key store targeted by an operation is available as `resources.key_store`, except for `create-key-store` and `list-key-stores`, which do not target an existing key store.

Allow read-only access to External Key Stores (list and get key stores and their proxy health):

```json
{
  "default-service-strategy": "deny",
  "services": {
    "kms": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation in ['list-key-stores', 'get-key-store']",
          "action": "allow"
        }
      ]
    }
  }
}
```

Allow all External Key Store management operations only. Useful for administrators who manage the connection to the customer-managed XKS proxy but must not manage KMS Keys or perform cryptographic operations:

```json
{
  "default-service-strategy": "deny",
  "services": {
    "kms": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation in ['create-key-store', 'update-key-store', 'list-key-stores', 'get-key-store', 'connect-key-store', 'disconnect-key-store', 'delete-key-store']",
          "action": "allow"
        }
      ]
    }
  }
}
```

Allow all operations on a single External Key Store, identified by its zone and name. IAM policies apply across all zones, and key store names are only unique within a zone, so the `zone` condition prevents the rule from also matching a key store with the same name in another zone. To pin one specific key store instead, match on `resources.key_store.id`:

```json
{
  "default-service-strategy": "deny",
  "services": {
    "kms": {
      "type": "rules",
      "rules": [
        {
          "expression": "has(resources.key_store) && zone == 'ch-gva-2' && resources.key_store.name == 'production-xks'",
          "action": "allow"
        }
      ]
    }
  }
}
```

Restrict the XKS proxy endpoint to an approved host when creating or updating a key store. For `update-key-store` the `proxy` parameter is optional, so the rule only applies when a new endpoint is provided:

```json
{
  "default-service-strategy": "allow",
  "services": {
    "kms": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation == 'create-key-store' && !parameters.proxy.endpoint.startsWith('https://xks.example.com/')",
          "action": "deny"
        },
        {
          "expression": "operation == 'update-key-store' && has(parameters.proxy) && has(parameters.proxy.endpoint) && !parameters.proxy.endpoint.startsWith('https://xks.example.com/')",
          "action": "deny"
        },
        {
          "expression": "true",
          "action": "allow"
        }
      ]
    }
  }
}
```

Allow updating the description of a key store, but not its proxy endpoint or credentials:

```json
{
  "default-service-strategy": "allow",
  "services": {
    "kms": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation == 'update-key-store' && has(parameters.proxy)",
          "action": "deny"
        },
        {
          "expression": "true",
          "action": "allow"
        }
      ]
    }
  }
}
```

Allow all KMS operations except disconnecting and deleting External Key Stores. Useful to make sure that KMS Keys backed by an External Key Store always remain usable:

```json
{
  "default-service-strategy": "allow",
  "services": {
    "kms": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation in ['disconnect-key-store', 'delete-key-store']",
          "action": "deny"
        },
        {
          "expression": "true",
          "action": "allow"
        }
      ]
    }
  }
}
```

## Understanding a forbidden API call

Here are possible scenarios when the API returns a 403 Forbidden response.

- `forbidden by [role|org] policy, [compute|sos|dns|iam|ai|kms]:
Unable to find an operation in the list defined by the policy`. The message indicates that on the specific policy (at role or organization level), the performed operation is not specified with either `deny` or `allow` action. As the parser is not able to find a single match - the call is denied. The message also shows a service-class the restriction belongs to.

> [!NOTE]
If the goal is to restrict a key to a specific operation or resource, but at the same time allow any other type of operations - `{"action":"allow" "expression":"true"}` could be added to the end of the rules set.


- `forbidden by [role|org] policy, [compute|sos|dns|iam|ai|kms] - A deny rule matched. Rule index: *INDEX_NUMBER*`
This extended version of the previous message, will appear when a policy has rules whose **action** is `deny` and matches the performed call. The index indicates the sequence number of the rule in the rules set starting at 0 for the first rule.

---

url: https://community.exoscale.com/product/security/iam/how-to/policy-examples/

---

# Policy Examples

Here is an explanation of IAM roles, along with examples of how to set permissions for services.

### Default service strategy

Choose the default strategy that determines whether access to services is `allow` or `deny` by default. To enhance the security of your organization, we recommend setting the default strategy to `deny`, which ensures that access is granted only when explicitly permitted.

**Example**

```json
{
  "default-service-strategy": "deny"
}
```

### Services

After selecting the default strategy, choose which services should be set to `allow` or `deny` based on the selected default strategy.

**Available services**

- Compute
- Concrete AI
- Storage
- DBaaS
- DNS
- IAM

**Example**

```json
{
  "default-service-strategy": "deny",
  "services": {
    "dns": {
      "type": "allow"
    },
    "dbaas": {
      "type": "allow"
    }
  }
}
```

### Services strategy

It is possible to select one of the following strategies for services.

- **Allow**: This strategy will give the users full access on a service.  
- **Deny** : This strategy will block the access to a service.
- **Rules**: This strategy let to set specific permissions on a service.

**Example**

```json
{
  "default-service-strategy": "deny",
  "services": {
    "compute": {
      "type": "rules",
      "rules": [
        {
          "action": "allow",
          "expression": "operation in ['create-instance', 'delete-instance']"
        }
      ]
    }
  }
}
```
This example grants a user specific permissions within the Compute service. The user is allowed only to create and delete instances. This approach can be used when you want to provide a user with limited or partial access on a service instead of granting full permissions.

### Add a zone

Set permissions that allow users to use services within a specific `zone`.

```json
{
  "default-service-strategy": "deny",
  "services": {
    "compute": {
      "type": "rules",
      "rules": [
        {
          "expression": "operation.startsWith('get-') || operation.startsWith('list-') && zone == 'ch-gva-2'",
          "action": "allow"
        },
        {
          "expression": "operation in ['create-instance', 'delete-instance'] && zone == 'ch-gva-2'",
          "action": "allow"
        },
        {
          "expression": "operation in ['create-block-storage-volume', 'delete-block-storage-volume'] && zone == 'ch-gva-2'",
          "action": "allow"
        }
      ]
    }
  }
}
```
This example grants a user permissions to view, create, and delete instances and block storage resources in the `ch-gva-2` zone. The user can access only the resources within this zone and will not be able to view or manage instances, block storage or other resources located in other zones. 

### Limit resources deletion

Limit the deletion of the services/resources to business hours.

```json
{
  "default-service-strategy": "deny",
  "services": {
    "compute": {
      "type": "rules",
      "rules": [
        {
          "action": "allow",
          "expression": "operation.startsWith('get-') || operation.startsWith('list-')"
        },
        {
          "action": "allow",
          "expression": "operation in ['create-instance']"
        },
        {
          "expression": "operation.startsWith('delete-') && timestamp(now).getDayOfWeek('Europe/Zurich') >= 1 && timestamp(now).getDayOfWeek('Europe/Zurich') <= 5 && timestamp(now).getHours('Europe/Zurich') >= 8 && timestamp(now).getHours('Europe/Zurich') < 18",
          "action": "allow"
        }
      ]
    }
  }
}
```

---

> [!NOTE]
> These examples are provided to help you understand how IAM policies work. They should be adapted to comply with your organization's internal requirements and security policies before use in production.

### Role example — DEV team

Users with this role have permissions to manage the `Compute Service`. They can create or delete an instance, create or delete an instance pool, and create or delete a security group.

```json
{
  "default-service-strategy": "deny",
  "services": {
    "compute": {
      "type": "rules",
      "rules": [
        {
          "action": "allow",
          "expression": "operation.startsWith('get-') || operation.startsWith('list-')"
        },
        {
          "action": "allow",
          "expression": "operation in ['create-instance', 'delete-instance']"
        },
        {
          "action": "allow",
          "expression": "operation in ['create-instance-pool', 'delete-instance-pool']"
        },
        {
          "action":"allow",
          "expression": "operation in ['create-security-group', 'delete-security-group-rule']"
        }
      ]
    }
  }
}
```

#### Available options for compute

These are some of the common ones you will use and here is the place in our docs where you can find the exhaustive list.

#### Instance

To configure additional permissions for compute instances, you can use these options.

- 'elastic-ip-instance'
- 'start-instance'
- 'stop-instance'
- 'enable-tmp-instance'
- 'list-instances'
- 'private-network-instance' 
- 'reset-instance' 
- 'reset-password-instance' 
- 'resize-disk-instance' 
- 'reveal-password-instance'
- 'scale-instance'
- 'security-group-instance'
- 'snapshot-instance'
- 'reboot-instance'
- 'update-instance'

#### instance-pool

To configure additional permissions for instance pool, you can use these options.

- 'evict-instance-pool',
- 'list-instance-pool'
- 'scale-instance-pool'
- 'update-instance-pool'

#### SKS

Here are additional options for SKS

- 'create-sks-cluster'
- 'delete-sks-cluster'
- 'upgrade-sks-cluster'
- 'active-nodepool-template-sks-cluster'
- 'get-sks-cluster-authority-cert'
- 'list-sks-clusters'
- 'create-sks-nodepool',
- 'rotate-ccm-credentials-sks-cluster'
- 'rotate-csi-credentials-sks-cluster'
- 'rotate-karpenter-credentials-sks-cluster'
- 'rotate-operators-ca-sks-cluster'
- 'show-sks-cluster'
- 'update-sks-cluster'
- 'upgrade-sks-cluster'
- 'upgrade-service-level-sks-cluster'
- 'version-sks-cluster'

#### Block storage

Here are additional configuration options for storage services.

- 'create-block-storage-volume'
- 'delete-block-storage-volume'
- 'detach-block-storage-volume'
- 'list-block-storage-volume'
- 'show-block-storage-volume'
- 'snapshot-block-storage-volume'
- 'update-block-storage-volume'

#### Templates

Here are more configuration options for templates.

- 'register-instance-template'
- 'delete-instance-template'
- 'list-instance-template'
- 'show-instance-template'

#### Security group

Available options for security group.

- 'list-security-groups',
- 'add-security-group-rule'
- 'source-security-group'

#### Elastic ip

Additional options for elastic ip.

- 'create-elastic-ip'
- 'delete-elastic-ip'
- 'list-elastic-ips'
- 'update-elastic-ip'

#### Load balancer

Here are additional options for load balancer.

- 'create-load-balancer'
- 'delete-load-balancer'
- 'list-load-balancer'
- 'update-load-balancer'
- 'service-load-balancer'

#### Private network 

Set more configuration for private network.

- 'create-private-network'
- 'delete-private-network'
- 'list-private-network'
- 'update-private-network'

#### SSH-KEY

SSH key additional options.

- 'register-ssh-key'
- 'show-ssh-key'

#### Anti affinity group

Here are additional options for anti affinity group.

- 'create-anti-affinity-group'
- 'delete-anti-affinity-group'
- 'list-anti-affinity-group'
- 'show-anti-affinity-group'

---

### Role example — Viewer

In this example, the users have the permission to see everything in `compute`, `dbaas` and `sos` services but they can't update, stop, start or delete any services.

```json
{
  "default-service-strategy": "deny",
  "services": {
    "compute": {
      "type": "rules",
      "rules": [
        {
          "action": "allow",
          "expression": "operation.startsWith('get-') || operation.startsWith('list-')"
        }
      ]
    },
    "dbaas": {
      "type": "rules",
      "rules": [
        {
          "action": "allow",
          "expression": "operation.startsWith('get-') || operation.startsWith('list-')"
        }
      ]
    },
    "sos": {
      "type": "rules",
      "rules": [
        {
          "action": "allow",
          "expression": "operation.startsWith('get-') || operation.startsWith('list-')"
        }
      ]
    }
  }
}
```
---

### Role example — AI team 

Set permission that allow users to access the `Concrete AI` product and `dedicated-inference` service.

```json
{
  "default-service-strategy": "deny",
  "services": {
    "dedicated-inference": {
      "type": "rules",
      "rules": [
        {
          "action": "allow",
          "expression": "operation.startsWith('get-') || operation.startsWith('list-')"
        },
        {
          "action": "allow",
          "expression": "operation in ['create-model', 'delete-model', 'show-model']"
        },
        {
          "action":"allow",
          "expression" :"operation in ['create-deployment', 'delete-deployment']"
        }
      ]
   }
  }
}
```
#### Available options for concrete AI

#### Deployment

Here are additional options for deployment.

- 'instance-type-deployment'
- 'logs-deployment'
- 'reveal-api-key-deployment'
- 'scale-deployment'
- 'update-deployment'

---

### Role example — Backup admin

The owner of this role has full access to `storage` and partial access to the `compute` for backups.

```json
{
  "default-service-strategy": "deny",
  "services": {
    "sos": {
      "type": "rules",
      "rules": [
        {
          "action": "allow",
          "expression": "operation.startsWith('get-') || operation.startsWith('list-')"
        },
        {
          "action": "allow",
          "expression": "operation in ['create-bucket', 'delete-bucket']"
        }
      ]
    },
    "compute": {
      "type": "rules",
      "rules": [
        {
          "action": "allow",
          "expression": "operation.startsWith('get-') || operation.startsWith('list-')"
        },
        {
          "action": "allow",
          "expression": "operation.contains('snapshot')"
        }
      ]
    }
  }
}
```
#### Available options for storage (SOS)

Here are additional options for SOS.

- operation.startsWith('head-')
- operation == 'show-object-cors'
- 'put-object'
- 'delete-object'
- 'delete-object-tagging'
- 'abort-multipart-upload'
- 'put-bucket-acl'
- 'put-bucket-cors'
- 'put-bucket-policy'
- 'put-bucket-versioning'
- 'delete-bucket-cors'
- 'delete-bucket-policy'
- 'delete-bucket-lifecycle'

### Role example — Database admin

In this case, users with this role only have read access to `DBaaS`.

```json
{
  "default-service-strategy": "deny",
  "services": {
    "dbaas": {
      "type": "rules",
      "rules": [
        {
          "action": "allow",
          "expression": "operation.startsWith('get-') || operation.startsWith('list-')"
        }
      ]
    }
  }
}
```
#### Available options for DBaaS

More options for DBaaS.

- 'create-dbaas-service'
- 'update-dbaas-service'
- 'delete-dbaas-service'

---

### Role example — Network admin

Users with role have access to `DNS` and `Compute services`.

```json
{
  "default-service-strategy": "deny",
  "services": {
    "dns": {
      "type": "rules",
      "rules": [
        {
          "action": "allow",
          "expression": "operation.startsWith('get-') || operation.startsWith('list-')"
        },
        {
          "action": "allow",
          "expression": "operation in ['create-dns-domain-record']"
        },
        {
          "action": "allow",
          "expression": "operation in ['create-dns-domain' ]"
        }
      ]
    },
    "compute": {
      "type": "rules",
      "rules": [
        {
          "action": "allow",
          "expression": "operation.startsWith('get-') || operation.startsWith('list-')"
        },
        {
          "action": "allow",
          "expression": "operation in ['create-instance', 'delete-instance']"
        },
        {
          "action": "allow",
          "expression": "operation in ['create-sks-cluster', 'delete-sks-cluster']"
        }
      ]
    }
  }
}
```
#### Available options for DNS

Set more configuration for DNS services.

- 'update-dns-domain-record'
- 'delete-dns-domain-record'
- 'delete-dns-domain'

---

### Role example — Security admin

In this case, the security team/users have full control on `IAM`, `SSH-KEY` and `Security group` across the entire organization. 

**Note**: Users assigned this role cannot invite other users to the organization. Only the owner of an organization has permission to send user invitations.


```json
{
  "default-service-strategy": "deny",
  "services": {
    "iam": {
      "type": "rules",
      "rules": [
        {
          "action": "allow",
          "expression": "operation.startsWith('get-') || operation.startsWith('list-')"
        },
        {
          "action":"allow",
          "expression": "operation in ['create-iam-role']"
        },
        {
          "action":"allow",
          "expression": "operation in ['create-api-key']"
        }
      ]
    },
    "compute": {
      "type": "rules",
      "rules": [
        {
          "action": "allow",
          "expression": "operation.startsWith('get-') || operation.startsWith('list-')"
        },
        {
          "action": "allow",
          "expression": "operation.contains('security-group') || operation.contains('ssh-key')"
        }
      ]
    }
  }
}
```

#### Available options for IAM

Set more configuration for IAM.

- 'delete-iam-role'
- 'update-iam-role'
- 'delete-api-key'
- 'update-org-policy'

---

url: https://community.exoscale.com/product/security/iam/service-boundaries/

---

# Service Boundaries - IAM

- [Limits and Quotas - IAM](https://community.exoscale.com/product/security/iam/service-boundaries/limits-and-quotas/index.md) - IAM Limitations and Quotas
- [SLA - IAM](https://community.exoscale.com/product/security/iam/service-boundaries/sla/index.md) - IAM Service Level Agreement

---

url: https://community.exoscale.com/product/security/iam/service-boundaries/limits-and-quotas/

---

# Limits and Quotas - IAM

## Quotas


| Usage | Quota |
| -------------------------------------------------------- | ------------------------ |
| IAM API keys count | 100 |


## Availability
IAM is available in all zones.

---

url: https://community.exoscale.com/product/security/iam/service-boundaries/sla/

---

# SLA - IAM



Exoscale IAM does not carry its own SLA. Refer to the actual used product for matching API access SLA if applicable.

---

url: https://community.exoscale.com/product/security/iam/reference/

---

# Reference - IAM

- [API IAM](https://community.exoscale.com/reference/api/iam/index.md) - All operations controlable by IAM. (external)
- [CLI IAM](https://community.exoscale.com/reference/cli/exo/iam/index.md) - All commands for IAM. (external)
- [Resources IAM](https://community.exoscale.com/reference/iam/index.md) - All resources list for IAM. (external)
