<a id="flink-rbac"></a>

# Grant Role-Based Access in Confluent Cloud for Apache Flink

When deploying Flink SQL statements in production, you must configure
appropriate access controls for different types of users and workloads.
Confluent Cloud for Apache Flink® supports
[Role-based Access Control (RBAC)](../../security/access-control/rbac/overview.md#cloud-rbac). Flink does not
support ACLs.

The Flink-specific RBAC roles are:

- [FlinkDeveloper](../../security/access-control/rbac/predefined-rbac-roles.md#flinkdeveloper-role): Enables creating and running statements and
  managing workspaces.
- [FlinkAdmin](../../security/access-control/rbac/predefined-rbac-roles.md#flinkadmin-role): Enables creating and managing compute pools manually
  for workload isolation or cost control.
- [FlinkFunctionDeveloper](../../security/access-control/rbac/predefined-rbac-roles.md#flinkfunctiondeveloper-role): Enables managing user-defined function (UDF)
  artifacts and external connectivity.
- [Operator](../../security/access-control/rbac/predefined-rbac-roles.md#operator-role): Enables metadata access to Flink tables, databases, and catalogs.
- [Assigner](../../security/access-control/rbac/predefined-rbac-roles.md#assigner-role): Enables delegation of statement execution to service
  accounts.

  If you use the service account delegation pattern, the Assigner role is
  required for OAuth identity pool integration with Flink. In this pattern, the
  identity pool authenticates the request while a service account executes the
  statement. The identity pool must have the Assigner role on the service
  account. For details and alternative patterns, see [OAuth authentication](flink-rest-api.md#flink-rest-api-oauth).

**Layered permission model:** Flink permissions follow a layered approach.
Start with base permissions required for all Flink operations, then add
layers based on what users need to accomplish.

**Operational considerations:** Use service accounts for production workloads
and apply least-privilege principles by granting only the permissions needed
for each use case.

For complete role definitions, see
[Predefined RBAC Roles](../../security/access-control/rbac/predefined-rbac-roles.md#cloud-rbac-roles).

- [Permission layers](#flink-rbac-permission-layers)
- [Common user scenarios](#flink-rbac-common-user-scenarios)
- [Production best practices](#flink-rbac-production-best-practices)
- [Audit log events](#flink-rbac-audit-log-events)
- [Reference](#flink-rbac-reference)

<a id="flink-rbac-permission-layers"></a>

## Permission layers

Flink permissions follow a layered approach, in which each layer builds upon the
previous one. This design enables you to grant only the permissions needed for
each use case, following the principle of least privilege.

These are the permission layers:

- [Base/required layer](#flink-rbac-base-required-layer): Fundamental permissions needed for all
  Flink operations
- [Data access layer](#flink-rbac-data-access-layer): Read and write access to specific tables
  and topics
- [Table management layer](#flink-rbac-table-management-layer): Create, alter, and delete tables
- [Administrative layer](#flink-rbac-administrative-layer): Manage compute pools and infrastructure
- [Logging permissions layer](#flink-rbac-udf-logging-layer): Access to UDF logs and audit
  events

Start with the base layer and add layers as needed for your specific
use cases.

<a id="flink-rbac-base-required-layer"></a>

### Base/required layer

All Flink user accounts need these permissions.

#### Flink access

The FlinkDeveloper role enables users to create and run statements, manage
artifacts, and manage their own workspaces without additional configuration.

Additional Flink roles are needed only for specific use cases:

- **FlinkDeveloper** (compute-pool level): Restricts access to specific
  compute pools only. Use this to limit a user’s access to certain pools
  while removing their environment-level access.
- **FlinkFunctionDeveloper**: Enables managing user-defined function (UDF)
  artifacts or external connectivity.
- **FlinkAdmin**: All FlinkDeveloper capabilities plus the ability to create
  and manage compute pools manually for workload isolation or cost control.

Run the following commands only if you need to grant these specialized roles.

```bash
# To restrict a user to specific compute pools only
# First remove environment-level access, then grant pool-level access
confluent iam rbac role-binding delete \
  --environment ${ENV_ID} \
  --principal User:${USER_ID} \
  --role FlinkDeveloper

confluent iam rbac role-binding create \
  --principal User:${USER_ID} \
  --role FlinkDeveloper \
  --environment ${ENV_ID} \
  --flink-region ${FLINK_REGION} \
  --resource ComputePool:${COMPUTE_POOL_ID}

# For users who need to manage UDF artifacts or external connectivity
confluent iam rbac role-binding create \
  --environment ${ENV_ID} \
  --principal User:${USER_ID} \
  --role FlinkFunctionDeveloper

# For users who need to create and manage compute pools manually
confluent iam rbac role-binding create \
  --environment ${ENV_ID} \
  --principal User:${USER_ID} \
  --role FlinkAdmin
```

#### Restrict existing permissions

To narrow down environment-level or organization-level permissions for an
existing FlinkDeveloper grant, run the following command to delete the role
binding:

```bash
confluent iam rbac role-binding delete \
  --environment ${ENV_ID} \
  --principal User:${USER_ID} \
  --role FlinkDeveloper
```

Now grant the FlinkDeveloper role to the principals that need to access the
compute pool. Use the Confluent CLI, or navigate to the compute pool in
the Confluent Cloud Console and add the principals.

#### Kafka Transactional-Id permissions

Flink uses Kafka transactions to ensure exactly-once processing semantics. All
Flink statements require:

- **DeveloperRead** on Transactional-Id `_confluent-flink_*` (to read
  transaction state)
- **DeveloperWrite** on Transactional-Id `_confluent-flink_*` (to create and
  manage transactions)

Run the following commands to grant the necessary permissions.

```bash
# Read transaction state
confluent iam rbac role-binding create \
  --role DeveloperRead \
  --principal User:${USER_ID} \
  --environment ${ENV_ID} \
  --cloud-cluster ${KAFKA_ID} \
  --kafka-cluster ${KAFKA_ID} \
  --resource Transactional-Id:_confluent-flink_ \
  --prefix

# Create and manage transactions
confluent iam rbac role-binding create \
  --role DeveloperWrite \
  --principal User:${USER_ID} \
  --environment ${ENV_ID} \
  --cloud-cluster ${KAFKA_ID} \
  --kafka-cluster ${KAFKA_ID} \
  --resource Transactional-Id:_confluent-flink_ \
  --prefix
```

##### Transaction ID structure and uniqueness

Flink generates unique transaction IDs for each statement to ensure exactly-once
processing. Transaction IDs follow this structure:

`_confluent-flink_{statement-name}_{statement-uid}_{jobgraph-node-id}_{subtask-index}_{transaction-index}`

For example:

`_confluent-flink_calls_11df0820-7d78-497c-88df-4fb620ad5661_8688_0_1`

Components:

- `_confluent-flink_` - Hard-coded prefix
- `calls` - Statement name
- `11df0820-7d78-497c-88df-4fb620ad5661` - Statement UID (UUID)
- `8688` - Jobgraph node ID
- `0` - Subtask index
- `1` - Transaction index

Flink provides transaction ID uniqueness through the statement UID,
a universally unique identifier (UUID) generated for each statement.
This makes it extremely unlikely for transaction IDs to clash, even when
statements have the same name or run in the same environment. The combination
of statement UID, jobgraph node ID, subtask index, and transaction index
provides each transaction with a distinct identifier across all Flink statements
and clusters.

<a id="flink-rbac-data-access-layer"></a>

### Data access layer

The data access layer provides permissions for reading from and writing to
existing tables in your Flink statements. This layer builds on the base layer
and adds specific access to Kafka topics and Schema Registry subjects that your statements
need to interact with.

#### Read from existing tables

When your Flink SQL statements read from tables, for example,
by using a `SELECT * FROM my_table` statement, you need these roles:

- **DeveloperRead** on the Kafka topic
- **DeveloperRead** on the Schema Registry subject

Run the following commands to grant the necessary permissions.

```bash
# Kafka topic read permission
confluent iam rbac role-binding create \
  --role DeveloperRead \
  --principal User:${USER_ID} \
  --environment ${ENV_ID} \
  --cloud-cluster ${KAFKA_ID} \
  --kafka-cluster ${KAFKA_ID} \
  --resource Topic:${TOPIC_NAME}

# Schema Registry subject read permission
confluent iam rbac role-binding create \
  --role DeveloperRead \
  --principal User:${USER_ID} \
  --environment ${ENV_ID} \
  --cloud-cluster ${SR_ID} \
  --schema-registry-cluster ${SR_ID} \
  --resource Subject:${SUBJECT_NAME}
```

#### Write to existing tables

When your Flink SQL statements write to tables, for example, by using an
`INSERT INTO my_sink_table` statement, you need the following roles:

- **DeveloperWrite** on the Kafka topic
- **DeveloperRead** on the Schema Registry subject, to validate data format

Run the following commands to grant the necessary permissions.

```bash
# Kafka topic write permission
confluent iam rbac role-binding create \
  --role DeveloperWrite \
  --principal User:${USER_ID} \
  --environment ${ENV_ID} \
  --cloud-cluster ${KAFKA_ID} \
  --kafka-cluster ${KAFKA_ID} \
  --resource Topic:${TOPIC_NAME}

# Schema Registry subject read permission, to validate data format
confluent iam rbac role-binding create \
  --role DeveloperRead \
  --principal User:${USER_ID} \
  --environment ${ENV_ID} \
  --cloud-cluster ${SR_ID} \
  --schema-registry-cluster ${SR_ID} \
  --resource Subject:${SUBJECT_NAME}
```

#### Access encryption keys for CSFLE and CSPE

When your Flink SQL statements process data encrypted with
[Client-Side Field Level Encryption](../../security/encrypt/csfle/overview.md#csfle-overview) or
[Client-Side Payload Encryption](../../security/encrypt/cspe.md#use-client-side-payload-encryption),
you need access to the Key Encryption Keys used to encrypt the data.

If you have DeveloperRead permission on the Key Encryption Keys, Flink
transparently decrypts the encrypted fields for processing. Without this
permission, the behavior depends on the encryption type:

- **CSFLE**: Encrypted fields remain encrypted and pass through without
  decryption
- **CSPE**: The statement fails because Flink cannot decrypt the payload

Run the following commands to grant access to encryption keys. You need
DeveloperWrite for the first write to generate the Data Encryption Key.
After the first write, DeveloperRead is sufficient for both reading and
writing encrypted data.

```bash
# Write access to Key Encryption Keys for initial DEK generation
confluent iam rbac role-binding create \
  --role DeveloperWrite \
  --principal User:${USER_ID} \
  --environment ${ENV_ID} \
  --cloud-cluster ${SR_ID} \
  --schema-registry-cluster ${SR_ID} \
  --resource Kek:${KEK_NAME}

# Read access to Key Encryption Keys for CSFLE/CSPE decryption
# (sufficient for subsequent reads and writes after DEK generation)
confluent iam rbac role-binding create \
  --role DeveloperRead \
  --principal User:${USER_ID} \
  --environment ${ENV_ID} \
  --cloud-cluster ${SR_ID} \
  --schema-registry-cluster ${SR_ID} \
  --resource Kek:${KEK_NAME}
```

For more information about using CSFLE and CSPE with Flink, see
[Process Encrypted Data with Confluent Cloud for Apache Flink](../../security/encrypt/csfle/flink-integration.md#csfle-flink-integration).

<a id="flink-rbac-table-management-layer"></a>

### Table management layer

The table management layer provides permissions for creating and modifying
tables in your Flink statements. This layer builds on the data access layer and
adds specific access to Kafka topics and Schema Registry subjects that your statements
need to interact with.

#### Create new tables

When your Flink SQL statements create new tables, for example, by using a
`CREATE TABLE` or `CREATE TABLE AS SELECT` statement, you need the following
roles:

- **DeveloperManage** on Kafka topics, to create topics
- **DeveloperWrite** on Schema Registry subjects, to create schemas

Run the following commands to grant the necessary permissions.

```bash
# Kafka topic create/manage permission
confluent iam rbac role-binding create \
  --role DeveloperManage \
  --principal User:${USER_ID} \
  --environment ${ENV_ID} \
  --cloud-cluster ${KAFKA_ID} \
  --kafka-cluster ${KAFKA_ID} \
  --resource Topic:${TABLE_PREFIX} \
  --prefix

# Schema Registry subject create/write permission
confluent iam rbac role-binding create \
  --role DeveloperWrite \
  --principal User:${USER_ID} \
  --environment ${ENV_ID} \
  --cloud-cluster ${SR_ID} \
  --schema-registry-cluster ${SR_ID} \
  --resource Subject:${TABLE_PREFIX} \
  --prefix
```

#### Modify existing tables

When your Flink SQL statements modify table structures, for example, by using
an `ALTER TABLE` statement for watermarks, computed columns, or column type
changes, you need the following roles:

- **DeveloperManage** on the Kafka topic, for table structure changes
- **DeveloperWrite** on the Schema Registry subject, for schema evolution

#### NOTE
If your Kafka cluster enforces ACLs, you also need the
`ALTER_CONFIGS` ACL on the topic to run `ALTER TABLE` statements.

Run the following commands to grant the necessary permissions:

```bash
# Kafka topic manage permission, for table structure changes
confluent iam rbac role-binding create \
  --role DeveloperManage \
  --principal User:${USER_ID} \
  --environment ${ENV_ID} \
  --cloud-cluster ${KAFKA_ID} \
  --kafka-cluster ${KAFKA_ID} \
  --resource Topic:${TABLE_NAME}

# Schema Registry subject write permission, for schema evolution
confluent iam rbac role-binding create \
  --role DeveloperWrite \
  --principal User:${USER_ID} \
  --environment ${ENV_ID} \
  --cloud-cluster ${SR_ID} \
  --schema-registry-cluster ${SR_ID} \
  --resource Subject:${TABLE_NAME}
```

<a id="flink-rbac-dlq-permissions"></a>

#### Dead Letter Queue permissions

Configuring a DLQ requires two sets of permissions: setup permissions for
running the `ALTER TABLE` statement that enables the DLQ, and runtime
permissions for statements that read from the source and write errors to the DLQ.

**Setup permissions** (for running `ALTER TABLE` to configure the DLQ):

Configuring a DLQ by using `ALTER TABLE` is an `ALTER TABLE` operation on the
source table, so it requires the same permissions as
[modifying existing tables](#flink-rbac-table-management-layer), plus
permissions to create the DLQ topic and schema:

- **DeveloperManage** on the source Kafka topic
- **DeveloperWrite** on the source Schema Registry subject
- **DeveloperWrite** (or **DeveloperManage**) on the DLQ Kafka topic, for topic
  creation
- **DeveloperWrite** on the DLQ Schema Registry subject, for schema registration

#### NOTE
If your Kafka cluster enforces ACLs, you also need the
`ALTER_CONFIGS` ACL on the source topic to run the `ALTER TABLE`
statement.

**Runtime permissions** (for statements reading from the source with DLQ
enabled):

- **DeveloperRead** on the source Kafka topic
- **DeveloperRead** on the source Schema Registry subject
- **DeveloperWrite** on the DLQ Kafka topic
- **DeveloperWrite** on the DLQ Schema Registry subject

Run the following commands to grant the setup permissions:

```bash
# Source topic manage permission (required for ALTER TABLE)
confluent iam rbac role-binding create \
  --role DeveloperManage \
  --principal User:${USER_ID} \
  --environment ${ENV_ID} \
  --cloud-cluster ${KAFKA_ID} \
  --kafka-cluster ${KAFKA_ID} \
  --resource Topic:${SOURCE_TOPIC_NAME}

# Source Schema Registry subject write permission
confluent iam rbac role-binding create \
  --role DeveloperWrite \
  --principal User:${USER_ID} \
  --environment ${ENV_ID} \
  --cloud-cluster ${SR_ID} \
  --schema-registry-cluster ${SR_ID} \
  --resource Subject:${SOURCE_SUBJECT_NAME}

# DLQ topic write permission
confluent iam rbac role-binding create \
  --role DeveloperWrite \
  --principal User:${USER_ID} \
  --environment ${ENV_ID} \
  --cloud-cluster ${KAFKA_ID} \
  --kafka-cluster ${KAFKA_ID} \
  --resource Topic:${DLQ_TOPIC_NAME}

# DLQ Schema Registry subject write permission
confluent iam rbac role-binding create \
  --role DeveloperWrite \
  --principal User:${USER_ID} \
  --environment ${ENV_ID} \
  --cloud-cluster ${SR_ID} \
  --schema-registry-cluster ${SR_ID} \
  --resource Subject:${DLQ_SUBJECT_NAME}
```

Run the following commands to grant the runtime permissions:

```bash
# Source topic read permission
confluent iam rbac role-binding create \
  --role DeveloperRead \
  --principal User:${USER_ID} \
  --environment ${ENV_ID} \
  --cloud-cluster ${KAFKA_ID} \
  --kafka-cluster ${KAFKA_ID} \
  --resource Topic:${SOURCE_TOPIC_NAME}

# Source Schema Registry subject read permission
confluent iam rbac role-binding create \
  --role DeveloperRead \
  --principal User:${USER_ID} \
  --environment ${ENV_ID} \
  --cloud-cluster ${SR_ID} \
  --schema-registry-cluster ${SR_ID} \
  --resource Subject:${SOURCE_SUBJECT_NAME}

# DLQ topic write permission
confluent iam rbac role-binding create \
  --role DeveloperWrite \
  --principal User:${USER_ID} \
  --environment ${ENV_ID} \
  --cloud-cluster ${KAFKA_ID} \
  --kafka-cluster ${KAFKA_ID} \
  --resource Topic:${DLQ_TOPIC_NAME}

# DLQ Schema Registry subject write permission
confluent iam rbac role-binding create \
  --role DeveloperWrite \
  --principal User:${USER_ID} \
  --environment ${ENV_ID} \
  --cloud-cluster ${SR_ID} \
  --schema-registry-cluster ${SR_ID} \
  --resource Subject:${DLQ_SUBJECT_NAME}
```

#### NOTE
- If your Kafka cluster enforces ACLs, you also need a `READ` ACL on
  the source topic and a `WRITE` ACL on the DLQ topic for runtime
  permissions.
- If the DLQ table and topic do not exist, Flink creates them automatically.
  If you pre-create the DLQ topic and schema, ensure the schema matches the
  expected format. For the required schema definitions, see
  [Configure a Dead Letter Queue](../how-to-guides/configure-dlq.md#flink-sql-configure-dlq).

For detailed DLQ configuration options and best practices, see
[Configure a Dead Letter Queue](../how-to-guides/configure-dlq.md#flink-sql-configure-dlq).

<a id="flink-rbac-administrative-layer"></a>

### Administrative layer

The administrative layer provides permissions for managing user-created Flink
compute pools. This layer is optional because Flink automatically creates and
manages default compute pools for all users.

You need administrative permissions only if you want to create compute
pools manually for workload isolation or cost control. See
[Compute Pools in Confluent Cloud for Apache Flink](../concepts/compute-pools.md#flink-sql-compute-pools) for more information about when to create
compute pools manually.

The following roles can manage user-created compute pools.

FlinkAdmin
: This role is for Flink-specific administrative access. It provides these
  capabilities:
  <br/>
  - Create, delete, and modify compute pools
  - All FlinkDeveloper capabilities (statements, workspaces, and artifacts)
  - Most common choice for users who need to manage explicit pools

EnvironmentAdmin
: This role provides environment-wide administrative access. It provides these
  capabilities:
  <br/>
  - All Flink administrative capabilities plus broader environment management
  - Typically assigned for other reasons (managing multiple services in an environment)

OrganizationAdmin
: This role provides organization-wide administrative access. It provides these
  capabilities:
  <br/>
  - All Flink administrative capabilities plus organization-wide management
  - Modify the default compute pool maximum CFU limit (50 CFUs by default)
  - Disable default compute pools at the organization level, requiring all users
    to create explicit pools
  - Typically assigned for other reasons, like managing the entire organization

Use FlinkAdmin for users who need to create and manage compute pools manually
for workload isolation or cost control.

Users with EnvironmentAdmin or OrganizationAdmin roles already have the
necessary Flink administrative capabilities.

For complete role definitions and capabilities, see [Predefined RBAC Roles in Confluent Cloud](../../security/access-control/rbac/predefined-rbac-roles.md#cloud-rbac-roles).

<a id="flink-rbac-udf-logging-layer"></a>

### Logging permissions layer

The logging permissions layer provides permissions for accessing
[Operational logs](monitor-statements.md#flink-sql-monitor-statements-event-logging),
[UDF logs](../how-to-guides/enable-udf-logging.md#flink-sql-enable-udf-logging) and
[audit events](../../monitoring/audit-logging/event-methods/flink.md#event-methods-flink) related to your Flink statements.
This layer builds on the administrative layer and adds specific access to Kafka
topics that your statements need to interact with.

To access Flink operational logs or UDF logs, you need these roles:

- **FlinkAdmin** or **FlinkDeveloper** role: provides describe access to UDF
  logs (or EnvironmentAdmin, or OrganizationAdmin).

<a id="flink-rbac-permission-model"></a>

## How Flink permissions work

Understanding the Flink permission model helps you make informed decisions about
access control and troubleshoot permission issues effectively.

### Principal-based access control

Flink uses a *principal-based* permission model, in which statements inherit
all permissions from the principal that runs them. The principal can be a
user or a service account.

The following key concepts help you understand how Flink permissions work.

- **Statements are not principals** - A Flink SQL statement doesn’t have
  its own permissions. It uses the permissions of the principal that runs
  it.
- **Flexible principal assignment** - You can run statements under your
  user account, which is suitable for ad hoc queries, or under a service
  account, which is suitable for production workloads.
- **Permission inheritance** - A statement can access any data that the
  principal has permissions to access in that region, even across
  environments.

For example, if a service account has DeveloperRead on topics in multiple
environments, any statement running under this service account can read from
topics in all of these environments, when in the same region.

### Separation of control plane and data plane access

Flink separates access into two distinct planes: the control plane and the data
plane. Understanding this separation is key to configuring permissions correctly.

#### Control plane access (infrastructure: Flink statements in this case)

This is managed by Flink-specific roles and governs what actions you can
perform within the Flink service.

Control plane access has these characteristics:

- Controls who can create statements, manage compute pools, and other Flink
  resources
- Managed by using FlinkAdmin and FlinkDeveloper roles.

#### Data plane access (data)

This is managed by Kafka and Schema Registry roles and governs which data that your Flink
statements can interact with.

Data plane access has these characteristics:

- Controls which data your statements can read from and write to
- Managed by using DeveloperRead, DeveloperWrite, and DeveloperManage roles
- Resource-scoped permissions (topics, subjects)

Data plane access is important because a user needs permissions on both planes
to execute a Flink SQL statement successfully. For example, a user might have
the FlinkDeveloper role (control plane access to create a statement), but if
they lack DeveloperRead on a source topic (data plane access), the statement
fails at runtime. In contrast, a principal with extensive data access but no
Flink role can’t create statements in the first place.

### Compute pools as shared infrastructure

It’s important to understand that compute pools are resources, not principals.

- A compute pool provides the computational infrastructure for running statements.
- Compute pools don’t have their own permissions or identity.
- Multiple users can share the same compute pool if they have appropriate Flink
  roles.
- The *principal running the statement* determines data access, not the compute
  pool.

For example, users Alice and Bob both have the FlinkDeveloper role and can use
the same compute pool. Alice’s statements access data based on Alice’s
permissions, while Bob’s statements use Bob’s permissions, even when running on
the same compute pool.

### Cross-environment data access

Flink statements can access data across environment boundaries based on the
principal’s permissions.

For example, a statement in Environment A can read from topics in Environment B
if the principal has:

- FlinkDeveloper role in Environment A, to create the statement
- DeveloperRead role on the topics in Environment B, to access the data

Cross-environment data access is important in these use cases:

- Cross-environment analytics and reporting
- Data pipeline orchestration across multiple environments
- Centralized processing with distributed data sources

#### IMPORTANT
Grant cross-environment permissions carefully, because a statement has broad
access based on its principal’s permissions.

<a id="flink-rbac-common-user-scenarios"></a>

## Common user scenarios

This section describes common user scenarios and the required permission
configurations for each. These scenarios follow the layered permission model,
starting with base permissions and adding layers as needed.

Choose the scenario that best matches your use case, then follow the
corresponding permission setup instructions.

### Developers

Assign the following permissions to developer accounts:

- [Base/Required Layer](#flink-rbac-base-required-layer) (Flink Developer
  role + Transactional-Id permissions)
- [Data Access Layer](#flink-rbac-data-access-layer) (read/write access to
  existing tables)
- [Table Management Layer](#flink-rbac-table-management-layer) (for
  creating and modifying tables)

Run the commands shown in the previous sections to grant the necessary
permissions.

<a id="flink-rbac-grant-sa-and-user-permission-for-sql-statements"></a>

### Production workloads (service accounts)

For automated deployments and long-running statements, use service
accounts to ensure stable identity that user-account changes don’t affect.

#### Setup options

Broad-access approach
: 1. Create a service account and grant the EnvironmentAdmin role.
  2. Grant a user the Assigner role on the service account.
  3. Deploy statements using the service account.

Least-privilege approach
: 1. Create a service account and grant
     [base/required layer](#flink-rbac-base-required-layer) permissions.
  2. Grant specific [Data Access Layer](#flink-rbac-data-access-layer) and
     [Table Management Layer](#flink-rbac-table-management-layer)
     permissions as needed.
  3. Grant a user account the Assigner role on the service account.

Run the following commands to grant the necessary permissions.

```bash
# Create service account
confluent iam service-account create ${SA_NAME} \
  --description "${SA_DESCRIPTION}"

# Broad access: Grant EnvironmentAdmin role
confluent iam rbac role-binding create \
  --environment ${ENV_ID} \
  --principal User:${SERVICE_ACCOUNT_ID} \
  --role EnvironmentAdmin

# Grant user Assigner role (for both approaches)
confluent iam rbac role-binding create \
  --principal User:${USER_ID} \
  --resource service-account:${SERVICE_ACCOUNT_ID} \
  --role Assigner
```

For the least-privilege approach, run the commands in the previous layer
sections, using the service account as the principal instead of a user account.

<a id="flink-rbac-administrators"></a>

### Administrators (user-created compute pool management)

Administrative roles are needed only if you want to create compute pools
manually for workload isolation or cost control. Flink automatically provides
default compute pools for all users, so most deployments don’t need
administrative roles.

If you need to create compute pools manually, grant an
[administrative layer](#flink-rbac-administrative-layer) role.

These are the administrative roles that can manage user-created compute pools:

- **FlinkAdmin**: Most common choice for creating and managing user-created pools
- **EnvironmentAdmin**: If they already manage other services in the environment
- **OrganizationAdmin**: If they already manage the entire organization

<a id="flink-rbac-production-best-practices"></a>

## Production best practices

Grant permissions incrementally, starting with base permissions and adding
additional layers as needed for your production use cases.

1. **Start with base/required layer** - Grant fundamental Flink and Kafka
   permissions.
2. **Add data access** - Grant read/write access to existing tables as needed.
3. **Add capabilities** - Table management, administrative access as required.
4. **Validate each layer** - Test functionality after adding each permission
   layer.

### Service account delegation pattern

For automated deployments, run the following command to grant the Assigner
role on production service accounts.

```bash
# CI/CD service account with Assigner role on production service accounts
confluent iam rbac role-binding create \
  --principal User:${CICD_SA_ID} \
  --resource service-account:${PROD_SA_ID} \
  --role Assigner
```

<a id="flink-rbac-audit-log-events"></a>

## Audit log events

Auditable event methods for the `FLINK_WORKSPACE` and `STATEMENT`
resource types are triggered by operations on a Flink workspace and
generate event messages that are sent to the audit log cluster, where
they are stored as event records in a Kafka topic.

For more information, see [Auditable Event Methods](../../monitoring/audit-logging/event-methods/flink.md#event-methods-flink).

<a id="flink-rbac-reference"></a>

## Reference

### Permission summary by layer

| Layer            | Kafka               | Schema Registry                                                           | Flink                                                                      |
|------------------|---------------------|---------------------------------------------------------------------------|----------------------------------------------------------------------------|
| Base/Required    | Transactional-Id    | –                                                                         | FlinkDeveloper OR FlinkAdmin                                               |
| Data Access      | DeveloperRead/Write | DeveloperRead/Write (subjects), DeveloperRead/Write (KEKs for CSFLE/CSPE) | –                                                                          |
| Table Management | DeveloperManage     | DeveloperWrite                                                            | –                                                                          |
| Administrative   | –                   | –                                                                         | FlinkAdmin, FlinkFunctionDeveloper, EnvironmentAdmin, or OrganizationAdmin |
| Logging          | –                   | –                                                                         | FlinkDeveloper                                                             |

### Access to Flink resources

The following table shows which Flink resources the RBAC roles can access.
“CRUD” stands for “Create, Read, Update, Delete”.

| Scope                                       | Statements                | Workspaces   | Compute pools   | Artifacts   | User-defined functions    | AI inference models   | Kafka clusters   | Kafka Topics   |
|---------------------------------------------|---------------------------|--------------|-----------------|-------------|---------------------------|-----------------------|------------------|----------------|
| OrganizationAdmin                           | CRUD                      | CRUD         | CRUD            | CRUD        | CRUD                      | CRUD                  | CRUD             | CRUD           |
| EnvironmentAdmin                            | CRUD                      | CRUD         | CRUD            | CRUD        | CRUD                      | CRUD                  | CRUD             | CRUD           |
| FlinkAdmin                                  | CRUD                      | CRUD         | CRUD            | CRUD        | CRUD                      | –                     | –                | –              |
| FlinkDeveloper bound to Env level or higher | CRUD                      | CRUD         | -R–             | CRUD        | CRUD <sup>[1](#id4)</sup> | –                     | –                | –              |
| FlinkDeveloper bound to Compute Pool level  | CRUD <sup>[2](#id5)</sup> | CRUD         | -R–             | –           | –                         | –                     | –                | –              |
| FlinkFunctionDeveloper                      | –                         | –            | –               | CRUD        | CRUD <sup>[1](#id4)</sup> | –                     | –                | –              |
* <a id='id4'>**[1]**</a> Requires access to cluster.
* <a id='id5'>**[2]**</a> FlinkDeveloper at compute-pool scope can only CRUD statements on pools they have access to. In some cases, this results in different behavior between the compute-pool-scoped role and the environment-scoped role. For example, deleting a statement that does not exist results in a 404 (does not exist) error with the environment-scoped role, but returns a 403 (access denied) error with the compute-pool-scoped roles, because a compute-pool user doesn’t have permissions to see objects outside the pool.

## Related content

- [Auditable Event Methods](../../monitoring/audit-logging/event-methods/flink.md#event-methods-flink)
- [DDL Statements](../concepts/statements.md#flink-sql-statements)
- [Manage RBAC Role Bindings](../../security/access-control/rbac/manage-role-bindings.md#manage-rbac-role-bindings)
- [Role-based Access Control (RBAC)](../../security/access-control/rbac/overview.md#cloud-rbac)
- [Service Accounts](../../security/authenticate/workload-identities/service-accounts/overview.md#service-accounts)

#### NOTE
This website includes content developed at the [Apache Software Foundation](https://www.apache.org/)
under the terms of the [Apache License v2](https://www.apache.org/licenses/LICENSE-2.0.html).
