Answers: <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>
  
Answers: CC BY-SA 4.0
Answers: 2020-03-19T09:27:16.410Z
Answers: 2020-03-19T09:27:16.410Z
Answers: TRW
Body: <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>
  
Comments: CC BY-SA 4.0
Comments: 2020-03-18T01:17:38.993Z
Comments: 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`?
Comments: atokpas
Comments: CC BY-SA 4.0
Comments: 2020-03-19T07:18:42.447Z
Comments: Yes, I can confirm, if I add the `/admin/` between auth and realms, it works. I'll write an answer with a complete example.
Comments: TRW
Tags: <ansible><keycloak>
Title: Creating Keycloak Realm via Ansible
UserDisplayName: TRW
ThreadText: # 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.
  
  
  ---
  
  