---
title: "Convert ACLs in one command"
description: "Use convert to turn an ACL dump into Confluent RBAC role bindings as a shell script, without a run directory or MDS access."
url: https://monedula.dev/flock/docs/acl-rbac-converter/tutorials/one-shot-conversion/
---
Turn a saved `kafka-acls.sh --list` output into a script of Confluent CLI role-binding commands with one `convert` call. No cluster or MDS access is needed: `convert` only writes the script.

## What you need

- `monedula-acl-rbac` on your `PATH`. [Install monedula-acl-rbac](/flock/docs/acl-rbac-converter/how-to/install/) shows how.
- Git, to get the example files.

## 1. Get the example files

```bash
git clone https://github.com/monedula-dev/monedula-acl-rbac-converter
cd monedula-acl-rbac-converter/examples/text-dump
```

The [text-dump](https://github.com/monedula-dev/monedula-acl-rbac-converter/tree/main/examples/text-dump) example holds the ACLs of a small microservices platform in `acls.txt`, and a `scopes.yaml`. Run the rest of the commands from this directory.

## 2. Look at the ACLs

`acls.txt` is in the format that `kafka-acls.sh --list` prints: the ACLs grouped by resource, one line per principal and operation.

```text
Current ACLs for resource `ResourcePattern(resourceType=TOPIC, name=orders, patternType=LITERAL)`:
 	(principal=User:svc-orders-consumer, host=*, operation=READ, permissionType=ALLOW)
 	(principal=User:svc-orders-consumer, host=*, operation=DESCRIBE, permissionType=ALLOW)
 	(principal=User:svc-orders-owner, host=*, operation=ALL, permissionType=ALLOW)
 	(principal=User:svc-orders-relay, host=*, operation=READ, permissionType=ALLOW)
 	(principal=User:svc-orders-relay, host=*, operation=WRITE, permissionType=ALLOW)
 	(principal=User:svc-orders-relay, host=*, operation=DESCRIBE, permissionType=ALLOW)

Current ACLs for resource `ResourcePattern(resourceType=GROUP, name=orders-consumers, patternType=LITERAL)`:
 	(principal=User:svc-orders-consumer, host=*, operation=READ, permissionType=ALLOW)
 	(principal=User:svc-orders-consumer, host=*, operation=DESCRIBE, permissionType=ALLOW)

Current ACLs for resource `ResourcePattern(resourceType=TOPIC, name=payments, patternType=LITERAL)`:
 	(principal=User:svc-payments-producer, host=*, operation=WRITE, permissionType=ALLOW)
 	(principal=User:svc-payments-producer, host=*, operation=DESCRIBE, permissionType=ALLOW)

Current ACLs for resource `ResourcePattern(resourceType=TOPIC, name=events., patternType=PREFIXED)`:
 	(principal=User:svc-analytics, host=*, operation=READ, permissionType=ALLOW)
 	(principal=User:svc-analytics, host=*, operation=DESCRIBE, permissionType=ALLOW)

Current ACLs for resource `ResourcePattern(resourceType=TOPIC, name=pii-events, patternType=LITERAL)`:
 	(principal=User:legacy-importer, host=*, operation=READ, permissionType=DENY)
```

On your own cluster, save the same format with:

```bash
kafka-acls.sh --list --bootstrap-server $BROKER > acls.txt
```

## 3. Set the cluster ID

Every role binding names the Kafka cluster it applies to. The example's `scopes.yaml` sets it:

```yaml
kafka_cluster: lkc-demo-1
```

`lkc-demo-1` is a placeholder, which is fine here because nothing is applied. For bindings you want to use, the value must be the ID that MDS knows your cluster by; [Find the cluster IDs for scopes.yaml](/flock/docs/acl-rbac-converter/how-to/find-cluster-ids/) shows where to get it.

## 4. Convert

```bash
monedula-acl-rbac convert --from text --input acls.txt --scopes scopes.yaml > bindings.sh
```

`convert` plans the role bindings with the built-in mapping rules and prints them as a shell script. `--from text` names the input format, and no run directory is created.

## 5. Read the script

```bash
cat bindings.sh
```

The script starts with a header that counts the bindings, followed by one `confluent iam rbac role-binding create` command per binding. These are its first lines:

```bash
#!/usr/bin/env bash
set -euo pipefail

# Generated by monedula-acl-rbac emit --format script
# Bindings: 7 to create, 0 to skip (already exist).

confluent iam rbac role-binding create \
  --principal 'User:svc-payments-producer' \
  --role 'DeveloperWrite' \
  --kafka-cluster 'lkc-demo-1' \
  --resource 'Topic:payments'  # binding rb-3713a985833c (source_acl_ids=[9 10])
```

The trailing comment carries the binding's ID and the `source_acl_ids` of the ACLs it replaces, numbered in the order they appear in `acls.txt`. The prefixed ACL becomes a prefixed binding, marked with `--prefix`:

```bash
confluent iam rbac role-binding create \
  --principal 'User:svc-analytics' \
  --role 'DeveloperRead' \
  --kafka-cluster 'lkc-demo-1' \
  --resource 'Topic:events.' --prefix  # binding rb-b6fac2ae9aa0 (source_acl_ids=[11 12])
```

The example's [expected/script.sh](https://github.com/monedula-dev/monedula-acl-rbac-converter/blob/main/examples/text-dump/expected/script.sh) holds the full result; your `bindings.sh` has the same commands. It lacks only the `# Plan:` header line, because `convert` keeps the plan in memory instead of writing `plan.json`.

## 6. See how the ACLs mapped

Thirteen ACLs became seven bindings:

| Principal | ACLs | Role binding |
| --- | --- | --- |
| `User:svc-orders-consumer` | Read and Describe on topic `orders` | `DeveloperRead` on topic `orders` |
| `User:svc-orders-consumer` | Read and Describe on group `orders-consumers` | `DeveloperRead` on group `orders-consumers` |
| `User:svc-payments-producer` | Write and Describe on topic `payments` | `DeveloperWrite` on topic `payments` |
| `User:svc-analytics` | Read and Describe on prefix `events.` | `DeveloperRead` on prefix `events.` |
| `User:svc-orders-owner` | All on topic `orders` | `ResourceOwner` on topic `orders` |
| `User:svc-orders-relay` | Read, Write and Describe on topic `orders` | `DeveloperRead` and `DeveloperWrite` on topic `orders` |
| `User:legacy-importer` | Deny Read on topic `pii-events` | none |

The relay gets two bindings and the DENY none; [How ACLs map to RBAC roles](/flock/docs/acl-rbac-converter/concepts/how-acls-map-to-rbac/) explains both. `convert` prints no report, so the rejected DENY appears nowhere in its output. The example's `./run.sh` runs the step-by-step pipeline, `extract`, `plan` and `emit`, whose [report.txt](https://github.com/monedula-dev/monedula-acl-rbac-converter/blob/main/examples/text-dump/expected/report.txt) lists it.

## 7. Render the bindings for Kubernetes

The same plan can come out in another shape. Ask for Confluent for Kubernetes manifests instead of a script:

```bash
monedula-acl-rbac convert --from text --input acls.txt --scopes scopes.yaml --format cfk > bindings.yaml
```

`bindings.yaml` holds one `ConfluentRolebinding` resource per binding, named after the binding's ID; `--format mds-curl` gives `curl` commands against the MDS REST API instead.

## Next steps

- [Your first ACL to RBAC migration](/flock/docs/acl-rbac-converter/tutorials/first-migration/) runs the full pipeline, including apply, effective-access verification and ACL deletion, against a local Confluent Platform.
- [Hand bindings to a CD pipeline](/flock/docs/acl-rbac-converter/how-to/emit-for-a-cd-pipeline/) covers the three output formats in a production run.
- The [convert command reference](/flock/docs/acl-rbac-converter/reference/cli/convert/) lists every flag.