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

<pre><code>- 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
</code></pre>

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

<p>This works one time. Because in the next run the REST API responses with 409 the <code>uri</code> module fails. 
To be idempotent, you need to check first, if the realm exists. This can be done via <code>GET /auth/admin/realms/{{ keycloak_realm_name }}</code> 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:</p>

<pre><code>- 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"
</code></pre>

<p>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.</p>

CC BY-SA 4.0
2020-03-19T09:27:16.410Z
2020-03-19T09:27:16.410Z
TRW
<p>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 (<a href="https://www.keycloak.org/docs-api/9.0/rest-api/index.html#_realms_admin_resource" rel="noreferrer">https://www.keycloak.org/docs-api/9.0/rest-api/index.html#_realms_admin_resource</a>) and I could call it with <code>uri</code> to create a Token and then send a JSON RealmRepresentation via REST. So I tried this:</p>

<pre><code>- 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
</code></pre>

<p>The token is available within <code>keycloak_token.json.access_token</code> and it is correct.</p>

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

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

<p>or a complete JSON export from Keycloak via WebUI (export realm). </p>

<p>But in any case the following error happens:</p>

<pre><code>fatal: [localhost]: FAILED! =&gt; {
  "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"},
  "msg": "Status code was 415 and not [200]: HTTP Error 415: Unsupported Media Type",
  "redirected": false,
  "status": 415,
  "url": "https://keycloak-server/auth/"
}
</code></pre>

<p>The documentation says it must be a <code>POST</code> to <code>/</code> and it consumes <code>application/json</code>. So, I'm not sure - is the URL correct? If I try <code>https://keycloak-server/auth/realms/</code>, I'm getting a 404. If I remove the <code>Content-type</code> - I'm getting a 406 with "Not acceptable". If I remove Accepts too, I'm getting a HTML page.</p>

<p>So - what is wrong?</p>

CC BY-SA 4.0
2020-03-18T01:17:38.993Z
I think the endpoint should be `https://keycloak-server/auth/admin/realms`? What happens if you try with CURL something like this `curl -v -X POST \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d @realm.json \
  $KCHOST/auth/admin/realms`?
atokpas
CC BY-SA 4.0
2020-03-19T07:18:42.447Z
Yes, I can confirm, if I add the `/admin/` between auth and realms, it works. I'll write an answer with a complete example.
TRW
<ansible><keycloak>
Creating Keycloak Realm via Ansible
TRW
# Creating Keycloak Realm via Ansible

(Asked by: username_0 on 2020-03-15)

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"},
  "msg": "Status code was 415 and not [200]: HTTP Error 415: Unsupported Media Type",
  "redirected": false,
  "status": 415,
  "url": "https://keycloak-server/auth/"
}

The documentation says it must be a POST to / and it consumes application/json. So, I'm not sure - is the URL correct? If I try https://keycloak-server/auth/realms/, I'm getting a 404. If I remove the Content-type - I'm getting a 406 with "Not acceptable". If I remove Accepts too, I'm getting a HTML page.
So - what is wrong?

**Tags:** ansible, keycloak

> **Comments:**
> **username_1**: I think the endpoint should be `https://keycloak-server/auth/admin/realms`? What happens if you try with CURL something like this `curl -v -X POST \   -H "Authorization: Bearer $ACCESS_TOKEN" \   -H "Content-Type: application/json" \   -d @realm.json \   $KCHOST/auth/admin/realms`?  
> **username_0**: Yes, I can confirm, if I add the `/admin/` between auth and realms, it works. I'll write an answer with a complete example.  

---

## 1 answers

### Answer by username_0 on 2020-03-19. Score: 4 | Accepted answer

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.


---

