Keycloak | CLI
The web UI of Keycloak works well for manual setup—but what about infrastructure-as-code workflows or CI/CD pipelines? Enter the Admin CLI (kcadm.sh), Keycloak's powerful command-line interface that maps directly to the Admin REST API.
In this article, we'll build a complete working setup from scratch using only the CLI and show how easy it is to manage a realm, client, role, group, and user. We'll also request an access token to verify that the setup works.
We'll be using Keycloak version 26.7.1. If you are using a different version, verify that the command syntax has not changed.
You can find the complete Keycloak Admin CLI documentation here.
Let's get started!
Prerequisites
To follow along with this article, please ensure that you have some containerization tool (Docker, Podman, etc.) installed on your machine.
Important notes
Note 1: We will use two terminals: one to run the Keycloak Docker container and another for the CLI.
Note 2. Some comments are labeled [Optional]. These optional commands are included to demonstrate additional Keycloak endpoints for managing resources.
Start Keycloak Docker container
In one terminal, let's run the following command to start a Keycloak Docker container:
docker run --rm \
--name keycloak \
-p 8080:8080 \
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
-e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
quay.io/keycloak/keycloak:26.7.1 start-devWait for it to start before proceeding.
Download Keycloak Zip
Go ahead and download the Keycloak ZIP file from the official Keycloak downloads page. Then, unzip it in your workspace.
Keycloak includes the Admin CLI in its server distribution, along with execution scripts located in the bin directory. We will use the kcadm.sh script.
Open a terminal and navigate to the Keycloak root folder you just unzipped. From now on, we will use this terminal to run the Admin CLI.
Exploring the commands
Run the following help command to check the available commands:
./bin/kcadm.sh --helpIt should return:
Keycloak Admin CLI
Use 'kcadm.sh config credentials' command with username and password to start a session against a specific
server and realm.
For example:
$ kcadm.sh config credentials --server http://localhost:8080 --realm master --user admin
Enter password:
Logging into http://localhost:8080 as user admin of realm master
Any configured username can be used for login, but to perform admin operations the user
needs proper roles, otherwise operations will fail.
Usage: kcadm.sh COMMAND [ARGUMENTS]
Global options:
-x Print full stack trace when exiting with error
--help Print help for specific command
--config Path to the config file (~/.keycloak/kcadm.config by default)
Commands:
config Set up credentials, and other configuration settings using the config file
create Create new resource
get Get a resource
update Update a resource
delete Delete a resource
get-roles List roles for a user or a group
add-roles Add role to a user or a group
remove-roles Remove role from a user or a group
set-password Re-set password for a user
help This help
Use 'kcadm.sh help <command>' for more information about a given command.The Admin CLI performs CRUD operations against the Admin REST API, wrapping endpoints with simplified commands:
./bin/kcadm.sh create ENDPOINT [ARGUMENTS] # POST
./bin/kcadm.sh get ENDPOINT [ARGUMENTS] # GET
./bin/kcadm.sh update ENDPOINT [ARGUMENTS] # PUT
./bin/kcadm.sh delete ENDPOINT [ARGUMENTS] # DELETELog in with the admin CLI (master realm)
Let's authenticate as the admin user from the "master" realm using direct username/password credentials. We need to do it once per terminal:
./bin/kcadm.sh config credentials \
--server http://localhost:8080 \
--realm master \
--user admin \
--password adminYou should get
Logging into http://localhost:8080 as user admin of realm masterManage a Realm
Creating the realm "my-realm"
Let's create a new realm with the name "my-realm" and with the property enabled set to true to indicate that the realm is active. Here is the command:
./bin/kcadm.sh create realms \
-s realm=my-realm \
-s enabled=trueIt should return:
Created new realm with id 'my-realm'Note: If you're unsure about any command (like
createabove), just run:./bin/kcadm.sh create --help. It should return the explanation and usage examples.
Updating the realm "my-realm"
Let's update the realm "my-realm" by setting the registrationAllowed property to true, using the following command:
./bin/kcadm.sh update realms/my-realm \
-s registrationAllowed=true[Optional] Retrieving all realms
To retrieve all realms, use the following command:
./bin/kcadm.sh get realms --fields realmIt should return:
[ {
"realm" : "master"
}, {
"realm" : "my-realm"
} ]Note: Adding the
--fieldsflag lets you select which fields you want in the response, instead of retrieving everything.
[Optional] Retrieving the realm "my-realm"
To retrieve a specific realm, in this case "my-realm," use the following command:
./bin/kcadm.sh get realms/my-realm --fields realm,registrationAllowedIt should return:
{
"realm" : "my-realm",
"registrationAllowed" : false
}[Optional] Deleting the realm "my-realm"
To delete the realm "my-realm," use the following command:
./bin/kcadm.sh delete realms/my-realmManage a Client
Assuming you followed the previous step to create the realm "my-realm."
Creating the client "my-client"
Let's create a new client with the name "my-client" and with the property redirectUris set to ["http://localhost:9080/*"]. Here is the command:
./bin/kcadm.sh create clients \
-r my-realm \
-s clientId=my-client \
-s 'redirectUris=["http://localhost:9080/*"]'The response should be something like:
Created new client with id '<my-client-id-generated-by-keycloak>'The "my-client" ID (highlighted in bold) is present in the response. Let's copy it and set the MY_CLIENT_ID environment variable:
MY_CLIENT_ID=<my-client-id-generated-by-keycloak>Updating the client "my-client"
Let's update "my-client" by setting the property directAccessGrantsEnabled to true, using the command below:
./bin/kcadm.sh update clients/$MY_CLIENT_ID \
-r my-realm \
-s directAccessGrantsEnabled=true[Optional] Retrieving all clients
To retrieve all clients, use the following command:
./bin/kcadm.sh get clients --fields id,clientId[Optional] Retrieving the client "my-client"
To retrieve a specific client, in this case "my-client," use the following command:
./bin/kcadm.sh get clients/$MY_CLIENT_ID \
-r my-realm \
--fields id,clientId,directAccessGrantsEnabled[Optional] Deleting the client "my-client"
To delete the client named "my-client," use the following command:
./bin/kcadm.sh delete clients/$MY_CLIENT_ID \
-r my-realmManage a Client Role
Assuming you followed the previous steps to create the realm "my-realm" and the client "my-client."
Creating the client role "MY_ROLE" for the client "my-client"
Let's create a new client role named "MY_ROLE" for "my-client." Here is the command:
./bin/kcadm.sh create clients/$MY_CLIENT_ID/roles \
-r my-realm \
-s name=MY_ROLEThe response should be like this:
Created new role with id 'MY_ROLE'[Optional] Retrieving all roles of the client "my-client"
To retrieve all "my-client" roles, use the following command:
./bin/kcadm.sh get clients/$MY_CLIENT_ID/roles \
-r my-realm[Optional] Retrieving the role "MY_ROLE" of the client "my-client"
To retrieve a specific client role, in this case "MY_ROLE," use the following command:
./bin/kcadm.sh get clients/$MY_CLIENT_ID/roles/MY_ROLE \
-r my-realm[Optional] Deleting the role "MY_ROLE" of the client "my-client"
To delete the client role "MY_ROLE" of the client "my-client," use the following command:
./bin/kcadm.sh delete clients/$MY_CLIENT_ID/roles/MY_ROLE \
-r my-realmManage a Group
Assuming you followed the previous steps to create the realm "my-realm", the client "my-client" and the client role "MY_ROLE."
Creating the group "MY_GROUP"
Let's create a group named "MY_GROUP." Here is the command:
./bin/kcadm.sh create groups \
-r my-realm \
-s name=MY_GROUPIt should return something like:
Created new group with id '<my-group-id-generated-by-keycloak>'The "MY_GROUP" ID (highlighted in bold) is present in the response. Let's copy it and set the MY_GROUP_ID environment variable:
MY_GROUP_ID=<my-group-id-generated-by-keycloak>Assigning the client role "MY_ROLE" to group "MY_GROUP"
First, let's retrieve the client role information:
./bin/kcadm.sh get clients/$MY_CLIENT_ID/roles/MY_ROLE \
-r my-realmWe should get something like:
{ "id" : "<my-role-id-generated-by-keycloak>", "name" : "MY_ROLE", ... }Let's copy the value in the id field and set it to the MY_ROLE_ID environment variable:
MY_ROLE_ID=<my-role-id-generated-by-keycloak>Then, run the command below to perform the assignment:
./bin/kcadm.sh add-roles \
-r my-realm \
--gname MY_GROUP \
--cclientid my-client \
--rolename MY_ROLE[Optional] Retrieving all groups
To retrieve all groups, use the following command:
./bin/kcadm.sh get groups \
-r my-realm[Optional] Retrieving the group "MY_GROUP"
To retrieve a specific group, in this case "MY_GROUP," use the following command:
./bin/kcadm.sh get groups/$MY_GROUP_ID \
-r my-realm[Optional] Deleting the group "MY_GROUP"
To delete the group "MY_GROUP," use the following command:
./bin/kcadm.sh delete groups/$MY_GROUP_ID \
-r my-realmManage a User
Assuming you followed the previous steps to create the realm "my-realm", the client "my-client", the client role "MY_ROLE" and the group "MY_GROUP."
Creating the user "my-user"
Let's create a new user whose username is "my-user." Here is the command:
./bin/kcadm.sh create users \
-r my-realm \
-s username=my-user \
-s enabled=trueIt should return something like:
Created new user with id '<my-user-id-generated-by-keycloak>'The "my-user" ID (highlighted in bold) is present in the response. Let's copy it and set the MY_USER_ID environment variable:
MY_USER_ID=<my-user-id-generated-by-keycloak>Setting a password to the user "my-user"
We can set the password "123" for "my-user" using the following command:
./bin/kcadm.sh set-password \
-r my-realm \
--userid $MY_USER_ID \
--new-password 123 \
--temporary=falseUpdating the user "my-user"
We can update the user "my-user" by setting, for instance, the email property using the command below:
./bin/kcadm.sh update users/$MY_USER_ID \
-r my-realm \
-s email=my-user@test.com[Optional] Retrieving all users
To retrieve all users, use the following command:
./bin/kcadm.sh get users -r my-realm \
--fields id,username,email,emailVerified[Optional] Retrieving the user "my-user"
To retrieve a specific user, in this case "my-user," use the following command:
./bin/kcadm.sh get users/$MY_USER_ID \
-r my-realmAssigning the group "MY_GROUP" to user "my-user"
Let's assign the group "MY_GROUP" to the user "my-user" using the command below:
./bin/kcadm.sh update users/$MY_USER_ID/groups/$MY_GROUP_ID \
-r my-realm[Optional] Getting "my-user" groups
To retrieve all groups "my-user" belongs to, use the following command:
./bin/kcadm.sh get users/$MY_USER_ID/groups \
-r my-realm[Optional] Deleting the user "my-user"
To delete the user named "my-user," use the following command:
./bin/kcadm.sh delete users/$MY_USER_ID \
-r my-realmGet user Access Token
Assuming you followed the previous step to create the realm "my-realm", the client "my-client", the client role "MY_ROLE", the group "MY_GROUP" and the user "my-user."
Getting the secret of the client "my-client"
To obtain the "my-client" secret, use the following command:
./bin/kcadm.sh get clients/$MY_CLIENT_ID/client-secret \
-r my-realmIt should return something like:
{
"type" : "secret",
"value" : "cVNxRvhbqC3D23UxnbiEdMOZbMA4..."
}Let's copy the secret present in the value field to the MY_CLIENT_SECRET environment variable:
MY_CLIENT_SECRET=...Disabling the required action Verify Profile
For testing purposes, let's disable the required action Verify Profile. Otherwise, we won't be able to retrieve a user access token (discussed in the next step):
./bin/kcadm.sh update authentication/required-actions/VERIFY_PROFILE \
-r my-realm \
-s enabled=false \
-s defaultAction=false \
-s priority=90Getting "my-user" access token for the client "my-client"
Let's obtain a "my-user" access token for the client "my-client" using the following command:
curl -i -X POST "http://localhost:8080/realms/my-realm/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=my-user" \
-d "password=123" \
-d "grant_type=password" \
-d "client_secret=$MY_CLIENT_SECRET" \
-d "client_id=my-client"It should return something like:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI...",
"expires_in": 300,
"refresh_expires_in": 1800,
"refresh_token": "eyJhbGciOiJIUzUxMiIsInR5cCI...",
"token_type": "Bearer",
"not-before-policy": 0,
"session_state": "eQzWuP4_PBfxZqOFSqkAuAMM",
"scope": "profile email"
}We can inspect the access token using Skycloak's jwt-token-analyzer. After decoding it, we get the following header and payload:
Header
{
"alg": "RS256",
"typ": "JWT",
"kid": "KZC4n_xILRzWfIxoDRjyqIUeX0UBn4_Llm31EqfCloI"
}Payload
{
"exp": 1786896077,
"iat": 1786895777,
"jti": "onrtro:db86ebb5-a307-2aa0-0beb-fe75a8af5ff9",
"iss": "http://localhost:8080/realms/my-realm",
"aud": "account",
"sub": "cdf5838e-c87f-4bfe-aa74-d5cc4a4bcb15",
"typ": "Bearer",
"azp": "my-client",
"sid": "eQzWuP4_PBfxZqOFSqkAuAMM",
"acr": "1",
"allowed-origins": [
"http://localhost:9080"
],
"realm_access": {
"roles": [
"offline_access",
"uma_authorization",
"default-roles-my-realm"
]
},
"resource_access": {
"my-client": {
"roles": [
"MY_ROLE"
]
},
"account": {
"roles": [
"manage-account",
"manage-account-links",
"view-profile"
]
}
},
"scope": "profile email",
"email_verified": false,
"preferred_username": "my-user",
"email": "my-user@test.com"
}Shutdown
In the terminal where Keycloak is running, press Ctrl+C to stop the application.
Conclusion
By using Keycloak's Admin CLI, we've seen how simple it is to automate Keycloak configuration and integrate it into your CI/CD pipelines. With just a few commands, you can manage realms, clients, roles, groups, and users — all without touching the web UI. This approach keeps your identity management consistent, repeatable, and ready for version control.
Thanks for Reading
If you found this article useful, here are a few ways you can support my work:
- 👏 Clap and highlight sections you found valuable — it helps this article reach more readers.
- 💬 Join the conversation by leaving a response. I enjoy discussing ideas and answering questions.
- 🔔 Follow me on Medium | LinkedIn | X | GitHub.
- ✉️ Subscribe to my newsletter to get my latest posts and insights directly in your inbox.
- ☕ Support my writing: If you'd like to support the time and effort behind these articles, you can
