---
type: api
title: Create or update roles
description: **All methods and paths for this operation:**
url: https://www.elastic.co/elastic/docs-builder/docs/4641/api/doc/elasticsearch/v8/operation/operation-security-put-role
resource: https://www.elastic.co/elastic/docs-builder/docs/4641/api/doc/elasticsearch/v8/operation/operation-security-put-role
products:
  - Elasticsearch
---

# Create or update roles

Authentication: required

## Paths

- `POST` (also `PUT`) `/_security/role/{name}`

#### Path parameters

- `name` (string) — required
  The name of the role.

## Description

The role management APIs are generally the preferred way to manage roles in the native realm, rather than using file-based role management.
The create or update roles API cannot update roles that are defined in roles files.
File-based role management is not available in Elastic Serverless.

[Read the reference documentation](https://www.elastic.co/guide/en/elasticsearch/reference/8.19/defining-roles.html)

## Required authorization

- Cluster privileges: `manage_security`

## Query string parameters

- `refresh` (enum) — optional
  If `true` (the default) then refresh the affected shards to make this operation visible to search, if `wait_for` then wait for a refresh to make this operation visible to search, if `false` then do nothing with refreshes.
  Values: `true`, `false`, `wait_for`

## Request body

`application/json`

- `applications` (object[]) — optional
  A list of application privilege entries.
  - `application` (string) — required
    The name of the application to which this entry applies.
  - `privileges` (string[]) — required
    A list of strings, where each element is the name of an application privilege or action.
  - `resources` (string[]) — required
    A list resources to which the privileges are applied.
- `cluster` (union anyOf[]) — optional
  A list of cluster privileges. These privileges define the cluster-level actions for users with this role.
  Values: `all`, `cancel_task`, `create_snapshot`, `cross_cluster_replication`, `cross_cluster_search`, `delegate_pki`, `grant_api_key`, `manage`, `manage_api_key`, `manage_autoscaling`, `manage_behavioral_analytics`, `manage_ccr`, `manage_data_frame_transforms`, `manage_data_stream_global_retention`, `manage_enrich`, `manage_ilm`, `manage_index_templates`, `manage_inference`, `manage_ingest_pipelines`, `manage_logstash_pipelines`, `manage_ml`, `manage_oidc`, `manage_own_api_key`, `manage_pipeline`, `manage_rollup`, `manage_saml`, `manage_search_application`, `manage_search_query_rules`, `manage_search_synonyms`, `manage_security`, `manage_service_account`, `manage_slm`, `manage_token`, `manage_transform`, `manage_user_profile`, `manage_watcher`, `monitor`, `monitor_data_frame_transforms`, `monitor_data_stream_global_retention`, `monitor_enrich`, `monitor_inference`, `monitor_ml`, `monitor_rollup`, `monitor_snapshot`, `monitor_stats`, `monitor_text_structure`, `monitor_transform`, `monitor_watcher`, `none`, `post_behavioral_analytics_event`, `read_ccr`, `read_fleet_secrets`, `read_ilm`, `read_pipeline`, `read_security`, `read_slm`, `transport_client`, `write_connector_secrets`, `write_fleet_secrets`
- `global` (map string to object) — optional
  An object defining global privileges. A global privilege is a form of cluster privilege that is request-aware. Support for global privileges is currently limited to the management of application privileges.
- `indices` (object[]) — optional
  A list of indices permissions entries.
  - `field_security` (object) — optional
    The document fields that the owners of the role have read access to.
    - `except` (union oneOf) — optional
      One of: `string[]` or `string`
    - `grant` (union oneOf) — optional
      One of: `string[]` or `string`
  - `names` (string[]) — required
    A list of indices (or index name patterns) to which the permissions in this entry apply.
  - `privileges` (union anyOf[]) — required
    The index level privileges that owners of the role have on the specified indices.
    Values: `all`, `auto_configure`, `create`, `create_doc`, `create_index`, `cross_cluster_replication`, `cross_cluster_replication_internal`, `delete`, `delete_index`, `index`, `maintenance`, `manage`, `manage_data_stream_lifecycle`, `manage_follow_index`, `manage_ilm`, `manage_leader_index`, `monitor`, `none`, `read`, `read_cross_cluster`, `view_index_metadata`, `write`
  - `query` (union oneOf) — optional
    A search query that defines the documents the owners of the role have access to. A document within the specified indices must match this query for it to be accessible by the owners of the role.
    - **string**
    - **[Query](https://www.elastic.co/elastic/docs-builder/docs/4641/api/doc/elasticsearch/v8/types/_types-query_dsl-querycontainer)**
      An Elasticsearch Query DSL (Domain Specific Language) object that defines a query.
    - **RoleTemplateQuery**
      - `template` (object) — optional
        When you create a role, you can specify a query that defines the document level security permissions. You can optionally
use Mustache templates in the role query to insert the username of the current authenticated user into the role.
Like other places in Elasticsearch that support templating or scripting, you can specify inline, stored, or file-based
templates and define custom parameters. You access the details for the current authenticated user through the _user parameter.
        - `source` (union oneOf) — optional
          - **string**
          - **[Query](https://www.elastic.co/elastic/docs-builder/docs/4641/api/doc/elasticsearch/v8/types/_types-query_dsl-querycontainer)**
            An Elasticsearch Query DSL (Domain Specific Language) object that defines a query.
        - `id` (string) — optional
          The `id` for a stored script.
        - `params` (map string to object) — optional
          Specifies any named parameters that are passed into the script as variables.
Use parameters instead of hard-coded values to decrease compile time.
        - `lang` (union anyOf · default: painless) — optional
          Specifies the language the script is written in.

Supported values include:
  - `painless`: Painless scripting language, purpose-built for Elasticsearch.
  - `expression`: Lucene’s expressions language, compiles a JavaScript expression to bytecode.
  - `mustache`: Mustache templated, used for templates.
  - `java`: Expert Java API
          Values: `painless`, `expression`, `mustache`, `java`
        - `options` (map string to string) — optional
  - `allow_restricted_indices` (boolean · default: false) — optional
    Set to `true` if using wildcard or regular expressions for patterns that cover restricted indices. Implicitly, restricted indices have limited privileges that can cause pattern tests to fail. If restricted indices are explicitly included in the `names` list, Elasticsearch checks privileges against these indices regardless of the value set for `allow_restricted_indices`.
- `remote_indices` (object[]) — optional
  A list of remote indices permissions entries.

NOTE: Remote indices are effective for remote clusters configured with the API key based model.
They have no effect for remote clusters configured with the certificate based model.
  - `clusters` (union oneOf) — required
    A list of cluster aliases to which the permissions in this entry apply.
    One of: `string[]` or `string`
  - `field_security` (object) — optional
    The document fields that the owners of the role have read access to.
    - `except` (union oneOf) — optional
      One of: `string[]` or `string`
    - `grant` (union oneOf) — optional
      One of: `string[]` or `string`
  - `names` (string[]) — required
    A list of indices (or index name patterns) to which the permissions in this entry apply.
  - `privileges` (union anyOf[]) — required
    The index level privileges that owners of the role have on the specified indices.
    Values: `all`, `auto_configure`, `create`, `create_doc`, `create_index`, `cross_cluster_replication`, `cross_cluster_replication_internal`, `delete`, `delete_index`, `index`, `maintenance`, `manage`, `manage_data_stream_lifecycle`, `manage_follow_index`, `manage_ilm`, `manage_leader_index`, `monitor`, `none`, `read`, `read_cross_cluster`, `view_index_metadata`, `write`
  - `query` (union oneOf) — optional
    A search query that defines the documents the owners of the role have access to. A document within the specified indices must match this query for it to be accessible by the owners of the role.
    - **string**
    - **[Query](https://www.elastic.co/elastic/docs-builder/docs/4641/api/doc/elasticsearch/v8/types/_types-query_dsl-querycontainer)**
      An Elasticsearch Query DSL (Domain Specific Language) object that defines a query.
    - **RoleTemplateQuery**
      - `template` (object) — optional
        When you create a role, you can specify a query that defines the document level security permissions. You can optionally
use Mustache templates in the role query to insert the username of the current authenticated user into the role.
Like other places in Elasticsearch that support templating or scripting, you can specify inline, stored, or file-based
templates and define custom parameters. You access the details for the current authenticated user through the _user parameter.
        - `source` (union oneOf) — optional
          - **string**
          - **[Query](https://www.elastic.co/elastic/docs-builder/docs/4641/api/doc/elasticsearch/v8/types/_types-query_dsl-querycontainer)**
            An Elasticsearch Query DSL (Domain Specific Language) object that defines a query.
        - `id` (string) — optional
          The `id` for a stored script.
        - `params` (map string to object) — optional
          Specifies any named parameters that are passed into the script as variables.
Use parameters instead of hard-coded values to decrease compile time.
        - `lang` (union anyOf · default: painless) — optional
          Specifies the language the script is written in.

Supported values include:
  - `painless`: Painless scripting language, purpose-built for Elasticsearch.
  - `expression`: Lucene’s expressions language, compiles a JavaScript expression to bytecode.
  - `mustache`: Mustache templated, used for templates.
  - `java`: Expert Java API
          Values: `painless`, `expression`, `mustache`, `java`
        - `options` (map string to string) — optional
  - `allow_restricted_indices` (boolean · default: false) — optional
    Set to `true` if using wildcard or regular expressions for patterns that cover restricted indices. Implicitly, restricted indices have limited privileges that can cause pattern tests to fail. If restricted indices are explicitly included in the `names` list, Elasticsearch checks privileges against these indices regardless of the value set for `allow_restricted_indices`.
- `remote_cluster` (object[]) — optional
  A list of remote cluster permissions entries.
  - `clusters` (union oneOf) — required
    A list of cluster aliases to which the permissions in this entry apply.
    One of: `string[]` or `string`
  - `privileges` (enum[]) — required
    The cluster level privileges that owners of the role have on the remote cluster.
    Values: `monitor_enrich`, `monitor_stats`
- `metadata` (object) — optional
  Optional metadata. Within the metadata object, keys that begin with an underscore (`_`) are reserved for system use.
- `run_as` (string[]) — optional
  A list of users that the owners of this role can impersonate. *Note*: in Serverless, the run-as feature is disabled. For API compatibility, you can still specify an empty `run_as` field, but a non-empty list will be rejected.
- `description` (string) — optional
  Optional description of the role descriptor
- `transient_metadata` (map string to object) — optional
  Indicates roles that might be incompatible with the current cluster license, specifically roles with document and field level security. When the cluster license doesn’t allow certain features for a given role, this parameter is updated dynamically to list the incompatible features. If `enabled` is `false`, the role is ignored, but is still listed in the response from the authenticate API.

## Response

`application/json`

- `role` (object)
  When an existing role is updated, `created` is set to `false`.
  - `created` (boolean)
## Code examples

### Role example 1

#### Console

```console
POST /_security/role/my_admin_role
{
  "description": "Grants full access to all management features within the cluster.",
  "cluster": ["all"],
  "indices": [
    {
      "names": [ "index1", "index2" ],
      "privileges": ["all"],
      "field_security" : { // optional
        "grant" : [ "title", "body" ]
      },
      "query": "{\"match\": {\"title\": \"foo\"}}" // optional
    }
  ],
  "applications": [
    {
      "application": "myapp",
      "privileges": [ "admin", "read" ],
      "resources": [ "*" ]
    }
  ],
  "run_as": [ "other_user" ], // optional
  "metadata" : { // optional
    "version" : 1
  }
}
```

#### JavaScript

```javascript
const response = await client.security.putRole({
  name: "my_admin_role",
  description:
    "Grants full access to all management features within the cluster.",
  cluster: ["all"],
  indices: [
    {
      names: ["index1", "index2"],
      privileges: ["all"],
      field_security: {
        grant: ["title", "body"],
      },
      query: '{"match": {"title": "foo"}}',
    },
  ],
  applications: [
    {
      application: "myapp",
      privileges: ["admin", "read"],
      resources: ["*"],
    },
  ],
  run_as: ["other_user"],
  metadata: {
    version: 1,
  },
});
```

#### Python

```python
resp = client.security.put_role(
    name="my_admin_role",
    description="Grants full access to all management features within the cluster.",
    cluster=[
        "all"
    ],
    indices=[
        {
            "names": [
                "index1",
                "index2"
            ],
            "privileges": [
                "all"
            ],
            "field_security": {
                "grant": [
                    "title",
                    "body"
                ]
            },
            "query": "{\"match\": {\"title\": \"foo\"}}"
        }
    ],
    applications=[
        {
            "application": "myapp",
            "privileges": [
                "admin",
                "read"
            ],
            "resources": [
                "*"
            ]
        }
    ],
    run_as=[
        "other_user"
    ],
    metadata={
        "version": 1
    },
)
```

#### curl

```curl
curl -X POST "$ELASTICSEARCH_URL/_security/role/my_admin_role" \
  -H "Authorization: ApiKey $ELASTIC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "description": "Grants full access to all management features within the cluster.",
    "cluster": [
      "all"
    ],
    "indices": [
      {
        "names": [
          "index1",
          "index2"
        ],
        "privileges": [
          "all"
        ],
        "field_security": {
          "grant": [
            "title",
            "body"
          ]
        },
        "query": "{\u0022match\u0022: {\u0022title\u0022: \u0022foo\u0022}}"
      }
    ],
    "applications": [
      {
        "application": "myapp",
        "privileges": [
          "admin",
          "read"
        ],
        "resources": [
          "*"
        ]
      }
    ],
    "run_as": [
      "other_user"
    ],
    "metadata": {
      "version": 1
    }
  }'
```

#### Java

```java
client.security().putRole(p -> p
    .applications(a -> a
        .application("myapp")
        .privileges(List.of("admin","read"))
        .resources("*")
    )
    .cluster("all")
    .description("Grants full access to all management features within the cluster.")
    .indices(i -> i
        .fieldSecurity(f -> f
            .grant(List.of("title","body"))
        )
        .names(List.of("index1","index2"))
        .privileges("all")
        .query(q -> q
            .match(m -> m
                .field("title")
                .query(FieldValue.of("foo"))
            )
        )
    )
    .metadata("version", JsonData.fromJson("1"))
    .name("my_admin_role")
    .runAs("other_user")
);
```

#### C#

```c#
var response = await client.Security
    .PutRoleAsync(name: "my_admin_role", d1 => d1
        .Applications(d2 => d2
            .Application("myapp")
            .Privileges("admin", "read")
            .Resources("*")
        )
        .Cluster(ClusterPrivilege.All)
        .Description("Grants full access to all management features within the cluster.")
        .Indices(d2 => d2
            .FieldSecurity(d3 => d3
                .Grant(x => x.Title, x => x.Body)
            )
            .Names(["index1", "index2"])
            .Privileges(IndexPrivilege.All)
            .Query("{\"match\": {\"title\": \"foo\"}}")
        )
        .Metadata(new Dictionary<string, object>()
        {
            { "version", 1 }
        })
        .RunAs("other_user")
    );
```

#### PHP

```php
$resp = $client->security()->putRole([
    "name" => "my_admin_role",
    "body" => [
        "description" => "Grants full access to all management features within the cluster.",
        "cluster" => array(
            "all",
        ),
        "indices" => array(
            [
                "names" => array(
                    "index1",
                    "index2",
                ),
                "privileges" => array(
                    "all",
                ),
                "field_security" => [
                    "grant" => array(
                        "title",
                        "body",
                    ),
                ],
                "query" => "{\"match\": {\"title\": \"foo\"}}",
            ],
        ),
        "applications" => array(
            [
                "application" => "myapp",
                "privileges" => array(
                    "admin",
                    "read",
                ),
                "resources" => array(
                    "*",
                ),
            ],
        ),
        "run_as" => array(
            "other_user",
        ),
        "metadata" => [
            "version" => 1,
        ],
    ],
]);
```

#### Ruby

```ruby
response = client.security.put_role(
  name: "my_admin_role",
  body: {
    "description": "Grants full access to all management features within the cluster.",
    "cluster": [
      "all"
    ],
    "indices": [
      {
        "names": [
          "index1",
          "index2"
        ],
        "privileges": [
          "all"
        ],
        "field_security": {
          "grant": [
            "title",
            "body"
          ]
        },
        "query": "{\"match\": {\"title\": \"foo\"}}"
      }
    ],
    "applications": [
      {
        "application": "myapp",
        "privileges": [
          "admin",
          "read"
        ],
        "resources": [
          "*"
        ]
      }
    ],
    "run_as": [
      "other_user"
    ],
    "metadata": {
      "version": 1
    }
  }
)
```

### Role example 2

#### Console

```console
POST /_security/role/cli_or_drivers_minimal
{
  "cluster": ["cluster:monitor/main"],
  "indices": [
    {
      "names": ["test"],
      "privileges": ["read", "indices:admin/get"]
    }
  ]
}
```

#### JavaScript

```javascript
const response = await client.security.putRole({
  name: "cli_or_drivers_minimal",
  cluster: ["cluster:monitor/main"],
  indices: [
    {
      names: ["test"],
      privileges: ["read", "indices:admin/get"],
    },
  ],
});
```

#### Python

```python
resp = client.security.put_role(
    name="cli_or_drivers_minimal",
    cluster=[
        "cluster:monitor/main"
    ],
    indices=[
        {
            "names": [
                "test"
            ],
            "privileges": [
                "read",
                "indices:admin/get"
            ]
        }
    ],
)
```

#### curl

```curl
curl -X POST "$ELASTICSEARCH_URL/_security/role/cli_or_drivers_minimal" \
  -H "Authorization: ApiKey $ELASTIC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "cluster": [
      "cluster:monitor/main"
    ],
    "indices": [
      {
        "names": [
          "test"
        ],
        "privileges": [
          "read",
          "indices:admin/get"
        ]
      }
    ]
  }'
```

#### Java

```java
client.security().putRole(p -> p
    .cluster("cluster:monitor/main")
    .indices(i -> i
        .names("test")
        .privileges(List.of("read","indices:admin/get"))
    )
    .name("cli_or_drivers_minimal")
);
```

#### C#

```c#
var response = await client.Security
    .PutRoleAsync(name: "cli_or_drivers_minimal", d1 => d1
        .Cluster(new ClusterPrivilege("cluster:monitor/main"))
        .Indices(d2 => d2
            .Names(["test"])
            .Privileges(IndexPrivilege.Read, new IndexPrivilege("indices:admin/get"))
        )
    );
```

#### PHP

```php
$resp = $client->security()->putRole([
    "name" => "cli_or_drivers_minimal",
    "body" => [
        "cluster" => array(
            "cluster:monitor/main",
        ),
        "indices" => array(
            [
                "names" => array(
                    "test",
                ),
                "privileges" => array(
                    "read",
                    "indices:admin/get",
                ),
            ],
        ),
    ],
]);
```

#### Ruby

```ruby
response = client.security.put_role(
  name: "cli_or_drivers_minimal",
  body: {
    "cluster": [
      "cluster:monitor/main"
    ],
    "indices": [
      {
        "names": [
          "test"
        ],
        "privileges": [
          "read",
          "indices:admin/get"
        ]
      }
    ]
  }
)
```

### Role example 3

#### Console

```console
POST /_security/role/only_remote_access_role
{
  "remote_indices": [
    {
      "clusters": ["my_remote"], 
      "names": ["logs*"], 
      "privileges": ["read", "read_cross_cluster", "view_index_metadata"] 
    }
  ],
  "remote_cluster": [
    {
      "clusters": ["my_remote"], 
      "privileges": ["monitor_stats"]  
    }
  ]
}
```

#### JavaScript

```javascript
const response = await client.security.putRole({
  name: "only_remote_access_role",
  remote_indices: [
    {
      clusters: ["my_remote"],
      names: ["logs*"],
      privileges: ["read", "read_cross_cluster", "view_index_metadata"],
    },
  ],
  remote_cluster: [
    {
      clusters: ["my_remote"],
      privileges: ["monitor_stats"],
    },
  ],
});
```

#### Python

```python
resp = client.security.put_role(
    name="only_remote_access_role",
    remote_indices=[
        {
            "clusters": [
                "my_remote"
            ],
            "names": [
                "logs*"
            ],
            "privileges": [
                "read",
                "read_cross_cluster",
                "view_index_metadata"
            ]
        }
    ],
    remote_cluster=[
        {
            "clusters": [
                "my_remote"
            ],
            "privileges": [
                "monitor_stats"
            ]
        }
    ],
)
```

#### curl

```curl
curl -X POST "$ELASTICSEARCH_URL/_security/role/only_remote_access_role" \
  -H "Authorization: ApiKey $ELASTIC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "remote_indices": [
      {
        "clusters": [
          "my_remote"
        ],
        "names": [
          "logs*"
        ],
        "privileges": [
          "read",
          "read_cross_cluster",
          "view_index_metadata"
        ]
      }
    ],
    "remote_cluster": [
      {
        "clusters": [
          "my_remote"
        ],
        "privileges": [
          "monitor_stats"
        ]
      }
    ]
  }'
```

#### Java

```java
client.security().putRole(p -> p
    .name("only_remote_access_role")
    .remoteCluster(r -> r
        .clusters("my_remote")
        .privileges(RemoteClusterPrivilege.MonitorStats)
    )
    .remoteIndices(r -> r
        .clusters("my_remote")
        .names("logs*")
        .privileges(List.of("read","read_cross_cluster","view_index_metadata"))
    )
);
```

#### C#

```c#
var response = await client.Security
    .PutRoleAsync(name: "only_remote_access_role", d1 => d1
        .RemoteCluster(d2 => d2
            .Clusters(new[] { "my_remote" })
            .Privileges(RemoteClusterPrivilege.MonitorStats)
        )
        .RemoteIndices(d2 => d2
            .Clusters(new[] { "my_remote" })
            .Names(["logs*"])
            .Privileges(IndexPrivilege.Read, IndexPrivilege.ReadCrossCluster, IndexPrivilege.ViewIndexMetadata)
        )
    );
```

#### PHP

```php
$resp = $client->security()->putRole([
    "name" => "only_remote_access_role",
    "body" => [
        "remote_indices" => array(
            [
                "clusters" => array(
                    "my_remote",
                ),
                "names" => array(
                    "logs*",
                ),
                "privileges" => array(
                    "read",
                    "read_cross_cluster",
                    "view_index_metadata",
                ),
            ],
        ),
        "remote_cluster" => array(
            [
                "clusters" => array(
                    "my_remote",
                ),
                "privileges" => array(
                    "monitor_stats",
                ),
            ],
        ),
    ],
]);
```

#### Ruby

```ruby
response = client.security.put_role(
  name: "only_remote_access_role",
  body: {
    "remote_indices": [
      {
        "clusters": [
          "my_remote"
        ],
        "names": [
          "logs*"
        ],
        "privileges": [
          "read",
          "read_cross_cluster",
          "view_index_metadata"
        ]
      }
    ],
    "remote_cluster": [
      {
        "clusters": [
          "my_remote"
        ],
        "privileges": [
          "monitor_stats"
        ]
      }
    ]
  }
)
```

## Response examples

#### Security put role response example1

`application/json`

A successful response from `POST /_security/role/my_admin_role`.

```json
{
  "role": {
    "created": true 
  }
}
```

