Import from Keycloak
--from keycloak reads a realm directly from a running Keycloak instance through its Admin REST API and recreates it in FerrisKey.
ferris-ctl realm import --from keycloak \
--source-url https://keycloak.example.com \
--source-realm acme \
--source-client-id ferriskey-import \
--source-client-secret "$KC_SECRET" \
--target-realm acme
Required flags
| Flag | Description |
|---|---|
--source-url | Base URL of the Keycloak instance |
--source-realm | Realm to read from Keycloak |
Authentication
Two options:
- Client credentials: pass
--source-client-idand--source-client-secret. The CLI runs a client-credentials grant against the token endpoint of the source realm, so the client has to be registered in that realm, with a service account that may read the realm through the Admin REST API. - A ready token: pass
--source-tokenwhen you already have a bearer token in hand.
When --source-token is set, the client id and secret are ignored.
ferris-ctl source add kc-prod --kind keycloak \
--url https://keycloak.example.com --realm acme \
--client-id ferriskey-import --client-secret "$KC_SECRET"
ferris-ctl realm import --source-ref kc-prod --target-realm acme
What gets imported
The importer reads the realm representation and then clients, roles, and users under /admin/realms/{realm}, and maps them like this:
| Keycloak | FerrisKey |
|---|---|
| Realm | A realm named like the source realm, or --target-realm |
Realm settings accessTokenLifespan, registrationAllowed, resetPasswordAllowed, rememberMe, verifyEmail | access_token_lifetime, user_registration_enabled, forgot_password_enabled, remember_me_enabled, email_verification_enabled |
| Realm roles | Realm roles, with name and description |
| Clients | Clients with clientId, name, enabled, protocol, serviceAccountsEnabled, directAccessGrantsEnabled, and redirectUris. A client with publicClient set is public, any other is confidential |
| Client roles | Roles scoped to their client, with name and description |
| Users | Users with username, email, first name, last name, and the email-verified flag |
Only those five realm settings are mapped; every other Keycloak setting keeps the FerrisKey default. Users are read 100 at a time until the list runs out.
What does not come across
- Passwords. Keycloak never exports password hashes, so users arrive without credentials.
- Role assignments. The roles are created, but who holds them is not carried over.
- Permissions. Roles are created with none, so a role that meant something in Keycloak grants nothing until you set its permissions in FerrisKey.
- The
enabledflag of a user. It is not read, so every user is created with FerrisKey’s default. - Per-client OAuth details: post-logout redirect URIs, web origins, the PKCE requirement, token lifetimes, and the device authorization grant. Clients arrive with FerrisKey’s defaults for these and need finishing by hand.
- Client secrets. A confidential client gets a new secret from FerrisKey, listed in the import report; the applications that used the Keycloak secret need the new one.
- Everything the importer does not request, such as groups, identity providers, client scopes, and mappers.
A client is never created as system.
A failed read of one client’s roles is swallowed without a message: that client simply arrives with no roles. Compare the role count in the report with what you expect.
Go through the imported realm afterwards and re-establish credentials, permissions, and role assignments. Run --dry-run -o yaml first to see precisely what would be created.