HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Creating Keycloak Realm via Ansible

Submitted by: @import:stackexchange-devops··
0
Viewed 0 times
creatingkeycloakviarealmansible

Problem

Currently there is no module to create a Keycloak realm with Ansible. There is a PR but it is stucked since half a year or so. So I though, there is a Keycloak REST API (https://www.keycloak.org/docs-api/9.0/rest-api/index.html#_realms_admin_resource) and I could call it with uri to create a Token and then send a JSON RealmRepresentation via REST. So I tried this:

- name: "Create Token for service Keycloak"
  uri:
    url: "https://keycloak-server/auth/realms/master/protocol/openid-connect/token"
    method: POST
    body_format: form-urlencoded
    body:
      username: "admin"
      password: "password"
      grant_type: "password"
      client_id: "admin-cli"
  register: keycloak_token

- name: "Create Realm for service Keycloak"
  uri:
    url: "https://keycloak-server/auth/"
    method: POST
    src: "realm.json"
    remote_src: "no"
    headers:
      Content-type: "application/json"
      Accept: "application/json"
      Authorization: "Bearer {{ keycloak_token.json.access_token }}"
  register: keycloak_realm_create


The token is available within keycloak_token.json.access_token and it is correct.

As realm.json I tested a minimal JSON (because all attributes in RealmRepresentation are marked as optional):

{
  "id": "myrealm",
  "realm": "myrealm",
  "displayName": "My Realm",
  "enabled": true,
  "sslRequired": "external",
  "registrationAllowed": false,
  "loginWithEmailAllowed": true,
  "duplicateEmailsAllowed": false,
  "resetPasswordAllowed": false,
  "editUsernameAllowed": false,
  "bruteForceProtected": true
}


or a complete JSON export from Keycloak via WebUI (export realm).

But in any case the following error happens:

```
fatal: [localhost]: FAILED! => {
"changed": false,
"connection": "close",
"content": "{\"error\":\"RESTEASY003065: Cannot consume content type\"}",
"content_length": "55",
"content_type": "application/json",
"elapsed": 0,
"json": {"error": "RESTEASY003065: Cannot consume content type"},

Solution

As @JSapkota mentioned the URL was wrong. It must be https://keycloak-server/auth/admin/realms.

- name: "Create Realm for service Keycloak"
  uri:
    url: "https://keycloak-server/auth/admin/realms"
    method: POST
    src: "realm.json"
    remote_src: "no"
    status_code:
     - 201
    headers:
      Content-type: "application/json"
      Accept: "application/json"
      Authorization: "Bearer {{ keycloak_token.json.access_token }}"
  register: keycloak_realm_create


Take care about the return status code. It is "201 created" and the uri module must know, that this is fine.

This works one time. Because in the next run the REST API responses with 409 the uri module fails.
To be idempotent, you need to check first, if the realm exists. This can be done via GET /auth/admin/realms/{{ keycloak_realm_name }} and check the return code, if it is 404 or 200. If it is 404, then you make a "POST" otherwise a "PUT". So, this a complete example:

- name: "Set facts"
  set_fact:
    keycloak_admin_user: "admin"
    keycloak_admin_pass: "password"
    keycloak_base_url: "https://keycloak.server"
    keycloak_realm_name: "myrealm"
    keycloak_realm_data_file: "realm.json"

- name: "Create Token for service Keycloak"
  uri:
    url: "{{ keycloak_base_url }}/auth/realms/master/protocol/openid-connect/token"
    method: POST
    body_format: form-urlencoded
    body:
      username: "{{ keycloak_admin_user }}"
      password: "{{ keycloak_admin_pass }}"
      grant_type: "password"
      client_id: "admin-cli"
  register: keycloak_token

- name: "Find out, if Realm {{ keycloak_realm_name }} for service Keycloak exists"
  uri:
    url: "{{ keycloak_base_url }}/auth/admin/realms/{{ keycloak_realm_name }}"
    method: GET
    status_code:
     - 200
     - 404
    headers:
      Accept: "application/json"
      Authorization: "Bearer {{ keycloak_token.json.access_token }}"
  register: keycloak_realm_exists

- name: "Create Realm {{ keycloak_realm_name }} for service Keycloak"
  uri:
    url: "{{ keycloak_base_url }}/auth/admin/realms"
    method: POST
    src: "{{ keycloak_realm_data_file }}"
    remote_src: "no"
    status_code:
     - 201
    headers:
      Content-type: "application/json"
      Accept: "application/json"
      Authorization: "Bearer {{ keycloak_token.json.access_token }}"
  register: keycloak_realm_create
  when: "keycloak_realm_exists.status == 404"

- name: "Update Realm {{ keycloak_realm_name }} for service Keycloak"
  uri:
    url: "{{ keycloak_base_url }}/auth/admin/realms/{{ keycloak_realm_name }}"
    method: PUT
    src: "{{ keycloak_realm_data_file }}"
    remote_src: "no"
    status_code:
     - 204
    headers:
      Content-type: "application/json"
      Accept: "application/json"
      Authorization: "Bearer {{ keycloak_token.json.access_token }}"
  register: keycloak_realm_create
  when: "keycloak_realm_exists.status == 200"


Also I had the problem, that after every request the Keycloak server responses with a 403 when I don't refresh the token before the next call to Keycloak. But I think, this depends on Realm token settings of the admin realm.

Code Snippets

- name: "Create Realm for service Keycloak"
  uri:
    url: "https://keycloak-server/auth/admin/realms"
    method: POST
    src: "realm.json"
    remote_src: "no"
    status_code:
     - 201
    headers:
      Content-type: "application/json"
      Accept: "application/json"
      Authorization: "Bearer {{ keycloak_token.json.access_token }}"
  register: keycloak_realm_create
- name: "Set facts"
  set_fact:
    keycloak_admin_user: "admin"
    keycloak_admin_pass: "password"
    keycloak_base_url: "https://keycloak.server"
    keycloak_realm_name: "myrealm"
    keycloak_realm_data_file: "realm.json"

- name: "Create Token for service Keycloak"
  uri:
    url: "{{ keycloak_base_url }}/auth/realms/master/protocol/openid-connect/token"
    method: POST
    body_format: form-urlencoded
    body:
      username: "{{ keycloak_admin_user }}"
      password: "{{ keycloak_admin_pass }}"
      grant_type: "password"
      client_id: "admin-cli"
  register: keycloak_token

- name: "Find out, if Realm {{ keycloak_realm_name }} for service Keycloak exists"
  uri:
    url: "{{ keycloak_base_url }}/auth/admin/realms/{{ keycloak_realm_name }}"
    method: GET
    status_code:
     - 200
     - 404
    headers:
      Accept: "application/json"
      Authorization: "Bearer {{ keycloak_token.json.access_token }}"
  register: keycloak_realm_exists

- name: "Create Realm {{ keycloak_realm_name }} for service Keycloak"
  uri:
    url: "{{ keycloak_base_url }}/auth/admin/realms"
    method: POST
    src: "{{ keycloak_realm_data_file }}"
    remote_src: "no"
    status_code:
     - 201
    headers:
      Content-type: "application/json"
      Accept: "application/json"
      Authorization: "Bearer {{ keycloak_token.json.access_token }}"
  register: keycloak_realm_create
  when: "keycloak_realm_exists.status == 404"

- name: "Update Realm {{ keycloak_realm_name }} for service Keycloak"
  uri:
    url: "{{ keycloak_base_url }}/auth/admin/realms/{{ keycloak_realm_name }}"
    method: PUT
    src: "{{ keycloak_realm_data_file }}"
    remote_src: "no"
    status_code:
     - 204
    headers:
      Content-type: "application/json"
      Accept: "application/json"
      Authorization: "Bearer {{ keycloak_token.json.access_token }}"
  register: keycloak_realm_create
  when: "keycloak_realm_exists.status == 200"

Context

StackExchange DevOps Q#11078, answer score: 4

Revisions (0)

No revisions yet.