diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index e09ee26..cafc1eb 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -9,7 +9,8 @@ jobs:
   release:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v2
+      - name: Checkout code
+        uses: actions/checkout@v2
       - name: Set up Python
         uses: actions/setup-python@v1
         with:
@@ -35,4 +36,18 @@ jobs:
         env:
           ANSIBLE_GALAXY_API_KEY: ${{ secrets.ANSIBLE_GALAXY_API_KEY }}
         run: |
-          ansible-galaxy collection publish *.tar.gz --api-key $ANSIBLE_GALAXY_API_KEY
\ No newline at end of file
+          ansible-galaxy collection publish *.tar.gz --api-key $ANSIBLE_GALAXY_API_KEY
+  dispatch:
+    needs: release
+    strategy:
+      matrix:
+        repo: ['ansible-middleware/cross-dc-rhsso-demo', 'ansible-middleware/flange-demo']
+    runs-on: ubuntu-latest
+    steps:
+      - name: Repository Dispatch
+        uses: peter-evans/repository-dispatch@v1
+        with:
+          token: ${{ secrets.TRIGGERING_PAT }}
+          repository: ${{ matrix.repo }}
+          event-type: "Dependency released - Keycloak"
+          client-payload: '{ "github": ${{toJson(github)}} }'
diff --git a/.gitignore b/.gitignore
index c32b546..c50fe52 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
-*.tar.gz
\ No newline at end of file
+*.tar.gz
+*.zip
diff --git a/README.md b/README.md
index 93e41eb..caafef7 100644
--- a/README.md
+++ b/README.md
@@ -29,60 +29,87 @@ collections:
   - name: middleware_automation.keycloak
 ```
 
+The keycloak collection also depends on the following python packages to be present on the controller host:
+
+* netaddr
+
+A requirement file is provided to install:
+
+    pip install -r requirements.txt
+
+
+### Included roles
+
+* [`keycloak`](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak/README.md): role for installing the service.
+* [`keycloak_realm`](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak_realm/README.md): role for configuring a realm, user federation(s), clients and users, in an installed service.
+
+
+## Usage
+
+
 ### Install Playbook
 
-`playbooks/keycloak.yml` installs the upstream(Keycloak) based on the defined variables.
-`playbooks/rhsso.yml` installs Red Hat Single Sign-On(RHSSO) based on defined variables.
+* [`playbooks/keycloak.yml`](playbooks/keycloak.yml) installs the upstream(Keycloak) based on the defined variables.
+* [`playbooks/rhsso.yml`](playbooks/rhsso.yml) installs Red Hat Single Sign-On(RHSSO) based on defined variables.
 
-### Choosing between upstream(Keycloak) project and Red Hat Single Sign-On(RHSSO)
+Both playbooks include the `keycloak` role, with different settings, as described in the following sections.
 
-The roles supports installing upstream(Keycloak) or Red Hat Single Sign-On in the following ways
+For service configuration details, refer to the [keycloak role README](roles/keycloak/README.md).
 
-#### Install upstream(Keycloak) from remote source
 
-This is default approach, there is one required variable
+### Choosing between upstream project (Keycloak) and Red Hat Single Sign-On (RHSSO)
 
-```
-keycloak_admin_password: "<changeme>"
-```
+The general flag `keycloak_rhsso_enable` controls what to install between upstream(Keycloak, when `False`) or Red Hat Single Sign-On (when `True`).
+The default value for the flag if `True` when Red Hat Network credentials are defined, `False` otherwise.
 
-#### Install upstream(Keycloak) from local source when the following variable is defined
 
-```
-keycloak_admin_password: "<changeme>"
-zip_file_local_path: <keycloak zip file on Ansible control node local path>
-```
+#### Install upstream (Keycloak) from keycloak releases
 
-#### Install RHSSO from the Red Hat Customer Support Portal, when the following variables are defined
+This is the default approach when RHN credentials are not defined. Keycloak is downloaded from keycloak builds (hosted on github.com) locally, and distributed to target nodes.
 
-```
-keycloak_admin_password: "<changeme>"
+
+#### Install RHSSO from the Red Hat Customer Support Portal
+
+Define the credentials as follows, and the default behaviour is to download a fresh archive of RHSSO on the controller node, then distribute to target nodes.
+
+```yaml
 rhn_username: '<customer_portal_username>'
 rhn_password: '<customer_portal_password>'
-rhsso_rhn_id: '<sso_product_id>'
+# (keycloak_rhsso_enable defaults to True)
 ```
 
-where `sso_product_id` is the ID for the specific Red Hat Single Sign-On version, ie. _101971_ will install version _7.5_)
 
-#### Install RHSSO from remote sources like Nexus etc, when the following variables are defined
+#### Install from controller node (local source)
 
+Making the keycloak zip archive (or the RHSSO zip archive), available to the playbook repository root directory, and setting `keycloak_offline_install` to `True`, allows to skip
+the download tasks. The local path for the archive matches the downloaded archive path, so it is also used as a cache when multiple hosts are provisioned in a cluster.
+
+```yaml
+keycloak_offline_install: True
 ```
-keycloak_admin_password: "<changeme>"
+
+And depending on `keycloak_rhsso_enable`:
+
+* `True`: install RHSSO using file rh-sso-x.y.z-server-dist.zip
+* `False`: install keycloak using file keycloak-x.y.zip
+
+
+#### Install from alternate sources (like corporate Nexus, artifactory, proxy, etc)
+
+For RHSSO:
+
+```yaml
 keycloak_rhsso_enable: True
-rhsso_source_download_url: '<url to download RHSSO zip file>'
+keycloak_rhsso_download_url: "https://<internal-nexus.private.net>/<path>/<to>/rh-sso-x.y.z-server-dist.zip"
 ```
 
-#### Install RHSSO from local source when the following variable is defined
+For keycloak:
 
-```
-keycloak_admin_password: "<changeme>"
-keycloak_rhsso_enable: True
-zip_file_local_path: <rhsso zip file on Ansible control node local path>
+```yaml
+keycloak_rhsso_enable: False
+keycloak_download_url: "https://<internal-nexus.private.net>/<path>/<to>/keycloak-x.y.zip"
 ```
 
-### Install role
-
-* [`keycloak`](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak/README.md): role for installing the service. _Requires: python3-netaddr_
 
 ### Example installation command
 
@@ -100,21 +127,20 @@ ansible-playbook -i <ansible_hosts> -e @rhn-creds.yml playbooks/keycloak.yml -e
   localhost ansible_connection=local
   ```
 
+
 ## Configuration
 
+
 ### Config Playbook
 
-`playbooks/keycloak-realm.yml` creates provided realm, user federation(s), client(s), client role(s) and client user(s) if they don't exist.
+[`playbooks/keycloak-realm.yml`](playbooks/keycloak-realm.yml) creates provided realm, user federation(s), client(s), client role(s) and client user(s) if they don't exist.
 
-### Config role
-
-* [`keycloak_realm`](https://github.com/ansible-middleware/keycloak/blob/main/roles/keycloak_realm/README.md): role for configuring a realm, user federation(s), clients and users, in an installed service.
 
 ### Example configuration command
 
 Execute the following command from the source root directory
 
-```
+```bash
 ansible-playbook -i <ansible_hosts> playbooks/keycloak-realm.yml -e keycloak_admin_password=<changeme> -e keycloak_realm=test
 ```
 
@@ -127,9 +153,12 @@ ansible-playbook -i <ansible_hosts> playbooks/keycloak-realm.yml -e keycloak_adm
   localhost ansible_connection=local
   ```
 
+For configuration details, refer to the [keycloak_realm role README](roles/keycloak_realm/README.md).
+
+
 ## License
 
 Apache License v2.0 or later
 
-See [LICENCE](LICENSE) to view the full text.
+See [LICENSE](LICENSE) to view the full text.
 
diff --git a/galaxy.yml b/galaxy.yml
index ecf8d7e..5266e28 100644
--- a/galaxy.yml
+++ b/galaxy.yml
@@ -1,6 +1,6 @@
 namespace: middleware_automation
 name: keycloak
-version: "0.1.9"
+version: "0.2.0"
 readme: README.md
 authors:
   - Romain Pelisse <rpelisse@redhat.com>
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..b2366a5
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,6 @@
+#################################################
+# python dependencies required to be installed 
+# on the controller host with:
+# pip install -r requirements.txt
+#
+netaddr
\ No newline at end of file
diff --git a/roles/keycloak/README.md b/roles/keycloak/README.md
index 93a1a8f..3dffb47 100644
--- a/roles/keycloak/README.md
+++ b/roles/keycloak/README.md
@@ -11,6 +11,16 @@ This role requires the `python3-netaddr` library installed on the controller nod
 
 * to install via yum/dnf: `dnf install python3-netaddr`
 * or via pip: `pip install netaddr==0.8.0`
+* or via the collection: `pip install -r requirements.txt`
+
+
+Dependencies
+------------
+
+The roles depends on:
+
+* the `redhat_csp_download` role from [middleware_automation.redhat_csp_download](https://github.com/ansible-middleware/redhat-csp-download) collection if Red Hat Single Sign-on zip have to be downloaded from RHN.
+* the `wildfly_driver` role from [middleware_automation.wildfly](https://github.com/ansible-middleware/wildfly) collection
 
 
 Versions
@@ -24,9 +34,10 @@ Versions
 Role Defaults
 -------------
 
+* Service configuration
+
 | Variable | Description | Default |
 |:---------|:------------|:---------|
-|`keycloak_rhsso_enable`| Enable Red Hat Single Sign-on installation  | `False` |
 |`keycloak_ha_enabled`| Enable auto configuration for database backend, clustering and remote caches on infinispan | `False` |
 |`keycloak_db_enabled`| Enable auto configuration for database backend | `True` if `keycloak_ha_enabled` is True, else `False` |
 |`keycloak_admin_user`| Administration console user account | `admin` |
@@ -34,13 +45,32 @@ Role Defaults
 |`keycloak_host`| hostname | `localhost` |
 |`keycloak_http_port`| HTTP port | `8080` |
 |`keycloak_https_port`| TLS HTTP port | `8443` |
+|`keycloak_ajp_port`| AJP port | `8009` |
+|`keycloak_jgroups_port`| jgroups cluster tcp port | `7600` |
 |`keycloak_management_http_port`| Management port | `9990` |
 |`keycloak_management_https_port`| TLS management port | `9993` |
 |`keycloak_java_opts`| Additional JVM options | `-Xms1024m -Xmx2048m` |
 |`keycloak_prefer_ipv4`| Prefer IPv4 stack and addresses for port binding | `True` |
+|`keycloak_config_standalone_xml`| filename for configuration | `keycloak.xml` |
+|`keycloak_service_user`| posix account username | `keycloak` |
+|`keycloak_service_group`| posix account group | `keycloak` |
+|`keycloak_service_pidfile`| pid file path for service | `/run/keycloak.pid` |
 |`jvm_package`| RHEL java package runtime | `java-1.8.0-openjdk-devel` |
 
 
+* Install options
+
+| Variable | Description | Default |
+|:---------|:------------|:---------|
+|`keycloak_rhsso_enable`| Enable Red Hat Single Sign-on installation  | `False` |
+|`keycloak_offline_install` | perform an offline install | `False`|
+|`keycloak_download_url`| Download URL for keycloak | `https://github.com/keycloak/keycloak/releases/download/<version>/<archive>`| 
+|`keycloak_rhsso_download_url`| Download URL for RHSSO | `https://access.redhat.com/jbossnetwork/restricted/softwareDownload.html?softwareId=<productID>`|
+|`keycloak_version`| keycloak.org package version | `15.0.2` |
+|`keycloak_rhsso_version`| RHSSO version | `7.5.0` |
+|`keycloak_dest`| Installation root path | `/opt/keycloak` |
+
+
 Role Variables
 --------------
 
@@ -76,29 +106,14 @@ The following variables are _required_ only when `keycloak_db_enabled` is True:
 |`keycloak_db_user` | username for connecting to postgres | `keycloak-user` |
 |`keycloak_db_pass` | password for connecting to postgres | `keycloak-pass` |
 
-The following variable can be used to install Keycloak or Red Hat Single Sign-On from local path:
-| Variable | Description | Example |
-|:---------|:------------|:---------|
-|`zip_file_local_path` | Full local path of upstream(Keycloak) or Red Hat Single Sign-On zip file on Ansible control plane | `tmp/rhsso/rh-sso-7.5-server-dist.zip` |
 
-The following variable can be used to install Red Hat Single Sign-On from source via url, auth support is not added right now.
-| Variable | Description | Example |
-|:---------|:------------|:---------|
-|`rhsso_source_download_url` | URL to download Red Hat Single Sign-On zip file from source | `http://localhost:8081/nexus/rhsso/rh-sso-7.5-server-dist.zip` |
+Example Playbooks
+-----------------
 
-Dependencies
-------------
-
-The roles depends on:
-
-* the redhat_csp_download role from [middleware_automation.redhat_csp_download](https://github.com/ansible-middleware/redhat-csp-download) collection if Red Hat Single Sign-on zip have to be downloaded from RHN.
-* the wildfly_driver role from [middleware_automation.wildfly](https://github.com/ansible-middleware/wildfly) collection
+_NOTE_: use ansible vaults or other security systems for storing credentials.
 
 
-Example Playbook
-----------------
-
-The following is an example playbook that makes use of the role to install keycloak from remote
+* The following is an example playbook that makes use of the role to install keycloak from remote:
 
 ```yaml
 ---
@@ -113,23 +128,7 @@ The following is an example playbook that makes use of the role to install keycl
             keycloak_admin_password: "changeme"
 ```
 
-The following is an example playbook that makes use of the role to install keycloak from local path on Ansible node
-
-```yaml
----
-- hosts: ...
-      collections:
-        - middleware_automation.keycloak
-      tasks:
-        - name: Include keycloak role
-          include_role:
-            name: keycloak
-          vars:
-            keycloak_admin_password: "changeme"
-            zip_file_local_path: "/tmp/keycloak/keycloak-16.1.0.zip"  # This should be local path on Ansible node of upstream(keycloak) zip file
-```
-
-The following is an example playbook that makes use of the role to install Red Hat Single Sign-On from RHN
+* The following is an example playbook that makes use of the role to install Red Hat Single Sign-On from RHN:
 
 ```yaml
 ---
@@ -146,9 +145,30 @@ The following is an example playbook that makes use of the role to install Red H
       vars:
         keycloak_admin_password: "changeme"
         keycloak_rhsso_enable: True
+        rhn_username: '<customer portal username>'
+        rhn_password: '<customer portal password>'
 ```
 
-The following is an example playbook that makes use of the role to install Red Hat Single Sign-On from source url
+
+* The following example playbook makes use of the role to install keycloak from the controller node:
+
+```yaml
+---
+- hosts: ...
+      collections:
+        - middleware_automation.keycloak
+      tasks:
+        - name: Include keycloak role
+          include_role:
+            name: keycloak
+          vars:
+            keycloak_admin_password: "changeme"
+            keycloak_offline_install: True
+            # This should be the filename of keycloak archive on Ansible node: keycloak-16.1.0.zip
+```
+
+
+* This playbook installs Red Hat Single Sign-On from an alternate url:
 
 ```yaml
 ---
@@ -162,10 +182,12 @@ The following is an example playbook that makes use of the role to install Red H
       vars:
         keycloak_admin_password: "changeme"
         keycloak_rhsso_enable: True
-        rhsso_source_download_url: "<REPLACE with - Source download url>" # This should be the full of remote source rhsso zip file
+        keycloak_rhsso_download_url: "<REPLACE with download url>"
+        # This should be the full of remote source rhsso zip file and can contain basic authentication credentials
 ```
 
-The following is an example playbook that makes use of the role to install Red Hat Single Sign-On from local path on Ansible node
+
+* The following is an example playbook that makes use of the role to install Red Hat Single Sign-On from the controller node:
 
 ```yaml
 ---
@@ -179,7 +201,8 @@ The following is an example playbook that makes use of the role to install Red H
       vars:
         keycloak_admin_password: "changeme"
         keycloak_rhsso_enable: True
-        zip_file_local_path: "/tmp/rhsso/rh-sso-7.5-server-dist.zip"  # This should be local path on Ansible node of rhsso zip file
+        keycloak_offline_install: True
+        # This should be the filename of rhsso zip file on Ansible node: rh-sso-7.5-server-dist.zip
 ```
 
 License
diff --git a/roles/keycloak/defaults/main.yml b/roles/keycloak/defaults/main.yml
index de619ac..a4af3fe 100644
--- a/roles/keycloak/defaults/main.yml
+++ b/roles/keycloak/defaults/main.yml
@@ -7,31 +7,36 @@ keycloak_download_url_9x: "https://downloads.jboss.org/keycloak/{{ keycloak_vers
 keycloak_installdir: "{{ keycloak_dest }}/keycloak-{{ keycloak_version }}"
 
 ### Configuration specific to Red Hat Single Sing-On
-keycloak_rhsso_enable: False
-keycloak_rhsso_version: 7.5
+keycloak_rhsso_version: 7.5.0
+rhsso_rhn_id: "{{ rhsso_rhn_ids[keycloak_rhsso_version] }}"
 keycloak_rhsso_archive: "rh-sso-{{ keycloak_rhsso_version }}-server-dist.zip"
-keycloak_rhsso_installdir: "{{ keycloak_dest }}/rh-sso-{{ keycloak_rhsso_version }}"
-keycloak_rhsso_base_url: 'https://access.redhat.com/jbossnetwork/restricted/softwareDownload.html?softwareId='
+keycloak_rhsso_installdir: "{{ keycloak_dest }}/rh-sso-{{ keycloak_rhsso_version | regex_replace('^([0-9])\\.([0-9]*).*', '\\1.\\2') }}"
+keycloak_rhn_url: 'https://access.redhat.com/jbossnetwork/restricted/softwareDownload.html?softwareId='
+keycloak_rhsso_download_url: "{{ keycloak_rhn_url }}{{ rhsso_rhn_id }}"
+
+### keycloak/rhsso choice: by default install rhsso if rhn credentials are defined
+keycloak_rhsso_enable: "{{ True if rhsso_rhn_id is defined and rhn_username is defined and rhn_password is defined else False }}"
+# whether to install from local archive; filename must be keycloak_archive or keycloak_rhsso_archive depending on keycloak_rhsso_enable
+keycloak_offline_install: False
 
 ### Install location and service settings
 jvm_package: java-1.8.0-openjdk-devel
 keycloak_dest: /opt/keycloak
 keycloak_jboss_home: "{{ keycloak_rhsso_installdir if keycloak_rhsso_enable else keycloak_installdir }}"
 keycloak_config_dir: "{{ keycloak_jboss_home }}/standalone/configuration"
-
 keycloak_config_standalone_xml: "keycloak.xml"
 keycloak_config_path_to_standalone_xml: "{{ keycloak_jboss_home }}/standalone/configuration/{{ keycloak_config_standalone_xml }}"
-
 keycloak_service_user: keycloak
 keycloak_service_group: keycloak
 keycloak_service_pidfile: "/run/keycloak.pid"
-keycloak_service_logfile: "{{ keycloak_dest }}/keycloak.log"
 
-### Keycloak configuration settings
+### Common configuration settings
 keycloak_bind_address: 0.0.0.0
 keycloak_host: localhost
 keycloak_http_port: 8080
 keycloak_https_port: 8443
+keycloak_ajp_port: 8009
+keycloak_jgroups_port: 7600
 keycloak_management_http_port: 9990
 keycloak_management_https_port: 9993
 keycloak_java_opts: "-Xms1024m -Xmx2048m"
diff --git a/roles/keycloak/tasks/firewalld.yml b/roles/keycloak/tasks/firewalld.yml
index 8757678..e05c58f 100644
--- a/roles/keycloak/tasks/firewalld.yml
+++ b/roles/keycloak/tasks/firewalld.yml
@@ -24,5 +24,5 @@
     - "{{ keycloak_https_port }}/tcp"
     - "{{ keycloak_management_http_port }}/tcp"
     - "{{ keycloak_management_https_port }}/tcp"
-    - "7600/tcp"
-    - "8009/tcp"
+    - "{{ keycloak_jgroups_port }}/tcp"
+    - "{{ keycloak_ajp_port }}/tcp"
diff --git a/roles/keycloak/tasks/get_rhsso.yml b/roles/keycloak/tasks/get_rhsso.yml
deleted file mode 100644
index fa3fc2b..0000000
--- a/roles/keycloak/tasks/get_rhsso.yml
+++ /dev/null
@@ -1,104 +0,0 @@
----
-- assert:
-    that:
-      - zipfile_dest is defined
-      - keycloak_rhsso_enable
-    quiet: true
-
-- set_fact:
-    rhn_download_url: "{{ keycloak_rhsso_base_url }}{{ rhsso_rhn_id }}"
-  when:
-    - rhsso_rhn_id is defined
-
-- name: "Check zipfile dest directory {{ zipfile_dest }}"
-  stat:
-    path: "{{ zipfile_dest }}"
-  register: archive_path
-
-- name: "Download zipfile from RHN: {{ rhn_download_url }}"
-  redhat_csp_download:
-    url: "{{ rhn_download_url }}"
-    dest: "{{ zipfile_dest }}"
-    username: "{{ rhn_username }}"
-    password: "{{ rhn_password }}"
-  no_log: "{{ omit_rhn_output | default(true) }}"
-  when:
-    - archive_path is defined
-    - archive_path.stat is defined
-    - not archive_path.stat.exists
-    - rhn_username is defined
-    - rhn_password is defined
-    - rhsso_rhn_id is defined
-
-- name: "Copy zipfile from source like Nexus etc : {{ rhsso_source_download_url }}"
-  get_url:
-    url: "{{ rhsso_source_download_url }}"
-    dest: "{{ zipfile_dest }}"
-    owner: "{{ keycloak_service_user }}"
-    group: "{{ keycloak_service_group }}"
-    mode: 0750
-  when:
-    - archive_path is defined
-    - archive_path.stat is defined
-    - not archive_path.stat.exists
-    - rhsso_source_download_url is defined
-
-- name: "Copy zipfile from local source: {{ zip_file_local_path }}"
-  ansible.builtin.copy:
-    src: "{{ zip_file_local_path }}"
-    dest: "{{ zipfile_dest }}"
-    owner: "{{ keycloak_service_user }}"
-    group: "{{ keycloak_service_group }}"
-    mode: 0750
-  when:
-    - archive_path is defined
-    - archive_path.stat is defined
-    - not archive_path.stat.exists
-    - zip_file_local_path is defined
-    
-- name: "Check zipfile dest directory {{ zipfile_dest }}"
-  stat:
-    path: "{{ zipfile_dest }}"
-  register: path_to_downloaded_artifact
-
-- block:
-    - file:
-        path: "{{ work_dir }}"
-        state: directory
-        owner: "{{ keycloak_service_user }}"
-        group: "{{ keycloak_service_group }}"
-        mode: 0750
-
-    - name: "Check directory {{ target_dir }}"
-      stat:
-        path: "{{ target_dir }}"
-      register: target_dir_state
-
-    - assert:
-        that:
-          - target_dir_state is defined
-          - target_dir_state.stat is defined
-        fail_msg: "Directory layout for {{ target_dir }} is invalid."
-        quiet: true
-
-    - name: "Decompress {{ zipfile_dest }} into {{ work_dir }} (results in {{ target_dir }}."
-      unarchive:
-        src: "{{ zipfile_dest }}"
-        dest: "{{ work_dir }}"
-        owner: "{{ keycloak_service_user }}"
-        group: "{{ keycloak_service_user }}"
-        remote_src: yes
-        creates: "{{ target_dir }}"
-      when:
-        - not target_dir_state.stat.exists
-
-    - debug:
-        msg: "{{ target_dir }} already exists, skipping decompressing {{ zipfile_dest }}"
-      when:
-        - target_dir_state.stat.exists
-  when:
-    - path_to_downloaded_artifact is defined
-    - path_to_downloaded_artifact.stat is defined
-    - path_to_downloaded_artifact.stat.exists
-    - target_dir is defined
-    - work_dir is defined
diff --git a/roles/keycloak/tasks/install.yml b/roles/keycloak/tasks/install.yml
index a346da5..af7e022 100644
--- a/roles/keycloak/tasks/install.yml
+++ b/roles/keycloak/tasks/install.yml
@@ -1,5 +1,6 @@
 ---
-- assert:
+- name: Validate parameters
+  assert:
     that:
       - keycloak_jboss_home is defined
       - keycloak_service_user is defined
@@ -9,25 +10,20 @@
       - keycloak_version is defined
     quiet: true
 
-- set_fact:
-    keycloak_service_group: "{{ keycloak_service_user }}"
-  when:
-    - not keycloak_service_group is defined
-
-- name: check for an existing deployment
+- name: Check for an existing deployment
   become: yes
   stat:
     path: "{{ keycloak_jboss_home }}"
   register: existing_deploy
 
 - block:
-    - name: stop the old keycloak service
+    - name: Stop the old keycloak service
       become: yes
       ignore_errors: yes
       systemd:
         name: keycloak
         state: stopped
-    - name: remove the old Keycloak deployment
+    - name: Remove the old Keycloak deployment
       become: yes
       file:
         path: "{{ keycloak_jboss_home }}"
@@ -56,75 +52,111 @@
     group: "{{ keycloak_service_group }}"
     mode: 0750
 
-- block:
-    - set_fact:
-        archive: "{{ keycloak_dest }}/{{ keycloak_archive }}"
-    - name: "Check archive directory {{ archive }}"
-      stat:
-        path: "{{ archive }}"
-      register: archive_path
+## check remote archive
+- name: Set download archive path
+  set_fact:
+    archive: "{{ keycloak_dest }}/{{ keycloak.bundle }}"
 
-    - name: download Keycloak archive to target
-      get_url:
-        url: "{{ keycloak_download_url }}"
-        dest: "{{ keycloak_dest }}"
-        owner: "{{ keycloak_service_user }}"
-        group: "{{ keycloak_service_group }}"
-      when:
-        - archive_path is defined
-        - archive_path.stat is defined
-        - not archive_path.stat.exists
-        - not keycloak_rhsso_enable and not zip_file_local_path is defined
+- name: Check download archive path
+  stat:
+    path: "{{ archive }}"
+  register: archive_path
 
-    - name: "Copy zipfile from local source: {{ zip_file_local_path }}"
-      ansible.builtin.copy:
-        src: "{{ zip_file_local_path }}"
-        dest: "{{ keycloak_dest }}"
-        owner: "{{ keycloak_service_user }}"
-        group: "{{ keycloak_service_group }}"
-        mode: 0750
-      when:
-        - archive_path is defined
-        - archive_path.stat is defined
-        - not archive_path.stat.exists
-        - not keycloak_rhsso_enable and zip_file_local_path is defined
+## download to controller
+- name: Check load download archive path
+  stat:
+    path: "{{ lookup('env', 'PWD') }}"
+  register: local_path
+  delegate_to: localhost
 
-    - name: extract Keycloak archive on target
-      unarchive:
-        remote_src: yes
-        src: "{{ archive }}"
-        dest: "{{ keycloak_dest }}"
-        creates: "{{ keycloak_jboss_home }}"
-        owner: "{{ keycloak_service_user }}"
-        group: "{{ keycloak_service_group }}"
-      notify:
-        - restart keycloak
+- name: Download keycloak archive
+  get_url:
+    url: "{{ keycloak_download_url }}"
+    dest: "{{ local_path.stat.path }}/{{ keycloak.bundle }}"
+  delegate_to: localhost
+  when:
+    - archive_path is defined
+    - archive_path.stat is defined
+    - not archive_path.stat.exists
+    - not keycloak_rhsso_enable
+    - not keycloak_offline_install
+
+- name: Performing download from RHN
+  redhat_csp_download:
+    url: "{{ keycloak_rhsso_download_url }}"
+    dest: "{{ local_path.stat.path }}/{{ keycloak.bundle }}"
+    username: "{{ rhn_username }}"
+    password: "{{ rhn_password }}"
+  no_log: "{{ omit_rhn_output | default(true) }}"
+  delegate_to: localhost
+  when:
+    - archive_path is defined
+    - archive_path.stat is defined
+    - not archive_path.stat.exists
+    - keycloak_rhsso_enable
+    - not keycloak_offline_install
+    - keycloak_rhn_url in keycloak_rhsso_download_url
+
+- name: Download rhsso archive from alternate location
+  get_url:
+    url: "{{ keycloak_rhsso_download_url }}"
+    dest: "{{ local_path.stat.path }}/{{ keycloak.bundle }}"
+  delegate_to: localhost
+  when:
+    - archive_path is defined
+    - archive_path.stat is defined
+    - not archive_path.stat.exists
+    - keycloak_rhsso_enable
+    - not keycloak_offline_install
+    - not keycloak_rhn_url in keycloak_rhsso_download_url
+
+## copy and unpack
+- name: Copy archive to target nodes
+  copy:
+    src: "{{ local_path.stat.path }}/{{ keycloak.bundle }}"
+    dest: "{{ archive }}"
+    owner: "{{ keycloak_service_user }}"
+    group: "{{ keycloak_service_group }}"
+    mode: 0750
+  register: new_version_downloaded
   become: yes
-  when: not keycloak_rhsso_enable
 
-- block:
-    - assert:
-        that:
-          - rhsso_rhn_id is defined or zip_file_local_path is defined
-        quiet: true
-        fail_msg: "Can't install RHSSO without either RHN ID or RHSSO zip file located on Ansible node"
-
-    - name: create download directory
-      file:
-        path: /opt/apps
-        state: directory
-        owner: "{{ keycloak_service_user }}"
-        group: "{{ keycloak_service_group }}"
-        mode: 0750
-
-    - include_tasks: get_rhsso.yml
-      vars:
-        zipfile_dest: "{{ keycloak_dest }}/{{ keycloak_rhsso_archive }}"
-        work_dir: "{{ keycloak_dest }}"
-        target_dir: "{{ keycloak_jboss_home }}"
+- name: "Check target directory: {{ keycloak.home }}"
+  stat:
+    path: "{{ keycloak.home }}"
+  register: path_to_workdir
   become: yes
-  when: keycloak_rhsso_enable
 
+- name: "Extract {{ 'Red Hat Single Sign-On' if keycloak_rhsso_enable else 'Keycloak' }} archive on target"
+  unarchive:
+    remote_src: yes
+    src: "{{ archive }}"
+    dest: "{{ keycloak_dest }}"
+    creates: "{{ keycloak.home }}"
+    owner: "{{ keycloak_service_user }}"
+    group: "{{ keycloak_service_group }}"
+  become: yes
+  when:
+    - new_version_downloaded.changed or not path_to_workdir.stat.exists
+  notify:
+    - restart keycloak
+
+- name: Inform decompression was not executed
+  debug:
+    msg: "{{ keycloak.home }} already exists and version unchanged, skipping decompression"
+  when:
+    - not new_version_downloaded.changed and path_to_workdir.stat.exists
+
+- name: "Reown installation directory to {{ keycloak_service_user }}"
+  file:
+    path: "{{ keycloak.home }}"
+    owner: "{{ keycloak_service_user }}"
+    group: "{{ keycloak_service_group }}"
+    recurse: true
+  become: yes
+  changed_when: false
+
+# driver and configuration
 - name: "Install {{ keycloak_jdbc_engine }} driver"
   include_role:
     name: wildfly_driver
@@ -139,7 +171,7 @@
       jdbc_driver_module_name: "{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_name }}"
   when: keycloak_jdbc[keycloak_jdbc_engine].enabled
 
-- name: "Deploy Keycloak's standalone.xml"
+- name: "Deploy {{ keycloak.service_name }} config to {{ keycloak_config_path_to_standalone_xml }}"
   become: yes
   template:
     src: templates/standalone.xml.j2
@@ -151,7 +183,7 @@
     - restart keycloak
   when: not keycloak_remotecache.enabled
 
-- name: "Deploy Keycloak's standalone.xml with remote cache store"
+- name: "Deploy {{ keycloak.service_name }} config with remote cache store to {{ keycloak_config_path_to_standalone_xml }}"
   become: yes
   template:
     src: templates/standalone-infinispan.xml.j2
diff --git a/roles/keycloak/tasks/main.yml b/roles/keycloak/tasks/main.yml
index bcf0c06..6ee9041 100644
--- a/roles/keycloak/tasks/main.yml
+++ b/roles/keycloak/tasks/main.yml
@@ -15,7 +15,7 @@
 - name: Link default logs directory
   file:
     state: link
-    src: "{{keycloak_jboss_home}}/standalone/log"
+    src: "{{ keycloak_jboss_home }}/standalone/log"
     dest: /var/log/keycloak
 
 - block:
@@ -30,7 +30,7 @@
       retries: 2
       delay: 2
   rescue:
-    - name: create Keycloak admin user
+    - name: "Create {{ keycloak.service_name }} admin user"
       command:
       args:
         argv:
@@ -39,11 +39,11 @@
           - "-u{{ keycloak_admin_user }}"
           - "-p{{ keycloak_admin_password }}"
       become: yes
-    - name: restart keycloak
+    - name: "Restart {{ keycloak.service_name }}"
       include_tasks: tasks/restart_keycloak.yml
-    - name: "Wait until Keycloak becomes active {{ health_url }}"
+    - name: "Wait until {{ keycloak.service_name }} becomes active {{ keycloak.health_url }}"
       uri:
-        url: "{{ health_url }}"
+        url: "{{ keycloak.health_url }}"
       register: keycloak_status
       until: keycloak_status.status == 200
       retries: 25
diff --git a/roles/keycloak/tasks/prereqs.yml b/roles/keycloak/tasks/prereqs.yml
index 51c2ee8..bb1c44b 100644
--- a/roles/keycloak/tasks/prereqs.yml
+++ b/roles/keycloak/tasks/prereqs.yml
@@ -10,11 +10,11 @@
 - name: Validate credentials
   assert:
     that:
-      - (rhn_username is defined and rhsso_rhn_id is defined) or rhsso_rhn_id is not defined
-      - (rhn_password is defined and rhsso_rhn_id is defined) or rhsso_rhn_id is not defined
+      - (rhn_username is defined and keycloak_rhsso_enable) or not keycloak_rhsso_enable or keycloak_offline_install
+      - (rhn_password is defined and keycloak_rhsso_enable) or not keycloak_rhsso_enable or keycloak_offline_install
     quiet: True
     fail_msg: "Cannot install Red Hat SSO without RHN credentials. Check rhn_username and rhn_password are defined"
-    success_msg: "{{ 'Installing Red Hat Single Sign-On' if rhsso_rhn_id is defined else 'Installing keycloak.org' }}"
+    success_msg: "{{ 'Installing Red Hat Single Sign-On' if keycloak_rhsso_enable else 'Installing keycloak.org' }}"
 
 - name: Set required packages facts
   set_fact:
diff --git a/roles/keycloak/tasks/systemd.yml b/roles/keycloak/tasks/systemd.yml
index 858f5d7..ca63491 100644
--- a/roles/keycloak/tasks/systemd.yml
+++ b/roles/keycloak/tasks/systemd.yml
@@ -38,9 +38,6 @@
     daemon_reload: yes
   when: systemdunit.changed
 
-- set_fact:
-    health_url: "{{ keycloak_management_url }}/health"
-
 - name: start keycloak
   systemd:
     name: keycloak
@@ -48,20 +45,22 @@
     state: started
   become: yes
 
-- command: "systemctl status keycloak"
+- name: Check service status
+  command: "systemctl status keycloak"
   register: keycloak_service_status
   changed_when: False
 
-- assert:
+- name: Verify service status
+  assert:
     that:
       - keycloak_service_status is defined
       - keycloak_service_status.stdout is defined
 
 - meta: flush_handlers
 
-- name: "Wait until Keycloak becomes active {{ health_url }}"
+- name: "Wait until Keycloak becomes active {{ keycloak.health_url }}"
   uri:
-    url: "{{ health_url }}"
+    url: "{{ keycloak.health_url }}"
   register: keycloak_status
   until: keycloak_status.status == 200
   retries: 25
diff --git a/roles/keycloak/templates/keycloak-service.sh.j2 b/roles/keycloak/templates/keycloak-service.sh.j2
index 82e3a21..2281b17 100755
--- a/roles/keycloak/templates/keycloak-service.sh.j2
+++ b/roles/keycloak/templates/keycloak-service.sh.j2
@@ -1,4 +1,5 @@
 #!/bin/bash -eu
+# {{ ansible_managed }}
 
 set +u -o pipefail
 
@@ -22,7 +23,6 @@ readonly KEYCLOAK_HTTP_PORT=${KEYCLOAK_HTTP_PORT}
 readonly KEYCLOAK_HTTPS_PORT=${KEYCLOAK_HTTPS_PORT}
 readonly KEYCLOAK_MANAGEMENT_HTTP_PORT=${KEYCLOAK_MANAGEMENT_HTTP_PORT}
 readonly KEYCLOAK_MANAGEMENT_HTTPS_PORT=${KEYCLOAK_MANAGEMENT_HTTPS_PORT}
-readonly KEYCLOAK_LOGFILE={{ keycloak_service_logfile }}
 readonly KEYCLOAK_PIDFILE={{ keycloak_service_pidfile }}
 
 set -u
@@ -70,7 +70,6 @@ startKeycloak() {
   checkEnvVar "${KEYCLOAK_HTTPS_PORT}"   'KEYCLOAK_HTTPS_PORT not provided' 5
   checkEnvVar "${KEYCLOAK_MANAGEMENT_HTTP_PORT}" 'KEYCLOAK_MANAGEMENT_HTTP_PORT not provided' 6
   checkEnvVar "${KEYCLOAK_MANAGEMENT_HTTPS_PORT}" 'KEYCLOAK_MANAGEMENT_HTTPS_PORT not provided' 7
-  checkEnvVar "${KEYCLOAK_LOGFILE}" 'KEYCLOAK_LOGFILE not provided' 8
 
   if [ "$(isKeyCloakRunning)" -eq 1 ]; then
     statusKeycloak
diff --git a/roles/keycloak/templates/keycloak-sysconfig.j2 b/roles/keycloak/templates/keycloak-sysconfig.j2
index f2eda03..15b777c 100644
--- a/roles/keycloak/templates/keycloak-sysconfig.j2
+++ b/roles/keycloak/templates/keycloak-sysconfig.j2
@@ -1,3 +1,4 @@
+# {{ ansible_managed }}
 JAVA_OPTS='{{ keycloak_java_opts }}'
 JBOSS_HOME={{ keycloak_jboss_home }}
 KEYCLOAK_BIND_ADDRESS={{ keycloak_bind_address }}
diff --git a/roles/keycloak/templates/keycloak.service.j2 b/roles/keycloak/templates/keycloak.service.j2
index 5816af0..e7233f2 100644
--- a/roles/keycloak/templates/keycloak.service.j2
+++ b/roles/keycloak/templates/keycloak.service.j2
@@ -1,3 +1,4 @@
+# {{ ansible_managed }}
 [Unit]
 Description=Keycloak Server
 After=network.target
diff --git a/roles/keycloak/templates/standalone-infinispan.xml.j2 b/roles/keycloak/templates/standalone-infinispan.xml.j2
index cceed6e..1097047 100644
--- a/roles/keycloak/templates/standalone-infinispan.xml.j2
+++ b/roles/keycloak/templates/standalone-infinispan.xml.j2
@@ -1,5 +1,5 @@
 <?xml version='1.0' encoding='UTF-8'?>
-
+<!-- {{ ansible_managed }} -->
 <server xmlns="urn:jboss:domain:16.0">
     <extensions>
         <extension module="org.jboss.as.clustering.infinispan"/>
@@ -738,12 +738,12 @@
         </interface>
     </interfaces>
     <socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
-        <socket-binding name="ajp" port="${jboss.ajp.port:8009}"/>
-        <socket-binding name="http" port="${jboss.http.port:8080}"/>
-        <socket-binding name="https" port="${jboss.https.port:8443}"/>
-        <socket-binding name="management-http" interface="management" port="${jboss.management.http.port:9990}"/>
-        <socket-binding name="management-https" interface="management" port="${jboss.management.https.port:9993}"/>
-        <socket-binding name="jgroups-tcp" interface="jgroups" port="7600"/>
+        <socket-binding name="ajp" port="{{ keycloak_ajp_port }}"/>
+        <socket-binding name="http" port="{{ keycloak_http_port }}"/>
+        <socket-binding name="https" port="{{ keycloak_https_port }}"/>
+        <socket-binding name="management-http" interface="management" port="{{ keycloak_management_http_port }}"/>
+        <socket-binding name="management-https" interface="management" port="{{ keycloak_management_https_port }}"/>
+        <socket-binding name="jgroups-tcp" interface="jgroups" port="{{ keycloak_jgroups_port }}"/>
         <socket-binding name="modcluster" multicast-address="${jboss.modcluster.multicast.address:224.0.1.105}" multicast-port="23364"/>
         <socket-binding name="txn-recovery-environment" port="4712"/>
         <socket-binding name="txn-status-manager" port="4713"/>
diff --git a/roles/keycloak/templates/standalone.xml.j2 b/roles/keycloak/templates/standalone.xml.j2
index 14fd3fb..5b57e09 100644
--- a/roles/keycloak/templates/standalone.xml.j2
+++ b/roles/keycloak/templates/standalone.xml.j2
@@ -1,5 +1,5 @@
 <?xml version='1.0' encoding='UTF-8'?>
-
+<!-- {{ ansible_managed }} -->
 <server xmlns="urn:jboss:domain:16.0">
     <extensions>
         <extension module="org.jboss.as.clustering.infinispan"/>
@@ -139,14 +139,32 @@
                     </security>
                 </datasource>
                 <datasource jndi-name="java:jboss/datasources/KeycloakDS" pool-name="KeycloakDS" enabled="true" use-java-context="true" statistics-enabled="${wildfly.datasources.statistics-enabled:${wildfly.statistics-enabled:false}}">
+{% if keycloak_jdbc[keycloak_jdbc_engine].enabled %}
+                    <connection-url>{{ keycloak_jdbc[keycloak_jdbc_engine].connection_url }}</connection-url>
+                    <driver>{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_name }}</driver>
+                    <pool>
+                       <max-pool-size>20</max-pool-size>
+                    </pool>
+                    <security>
+                        <user-name>{{ keycloak_jdbc[keycloak_jdbc_engine].db_user }}</user-name>
+                        <password>{{ keycloak_jdbc[keycloak_jdbc_engine].db_password }}</password>
+                    </security>
+{% else %}
                     <connection-url>jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE</connection-url>
                     <driver>h2</driver>
                     <security>
                         <user-name>sa</user-name>
                         <password>sa</password>
                     </security>
+{% endif %}
                 </datasource>
                 <drivers>
+{% if keycloak_jdbc[keycloak_jdbc_engine].enabled %}
+                    <driver name="{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_name }}" module="{{ keycloak_jdbc[keycloak_jdbc_engine].driver_module_name }}">
+                        <driver-class>{{ keycloak_jdbc[keycloak_jdbc_engine].driver_class }}</driver-class>
+                        <xa-datasource-class>{{ keycloak_jdbc[keycloak_jdbc_engine].xa_datasource_class }}</xa-datasource-class>
+                    </driver>
+{% endif %}
                     <driver name="h2" module="com.h2database.h2">
                         <xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
                     </driver>
@@ -621,11 +639,11 @@
         </interface>
     </interfaces>
     <socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
-        <socket-binding name="ajp" port="${jboss.ajp.port:8009}"/>
-        <socket-binding name="http" port="${jboss.http.port:8080}"/>
-        <socket-binding name="https" port="${jboss.https.port:8443}"/>
-        <socket-binding name="management-http" interface="management" port="${jboss.management.http.port:9990}"/>
-        <socket-binding name="management-https" interface="management" port="${jboss.management.https.port:9993}"/>
+        <socket-binding name="ajp" port="{{ keycloak_ajp_port }}"/>
+        <socket-binding name="http" port="{{ keycloak_http_port }}"/>
+        <socket-binding name="https" port="{{ keycloak_https_port }}"/>
+        <socket-binding name="management-http" interface="management" port="{{ keycloak_management_http_port }}"/>
+        <socket-binding name="management-https" interface="management" port="{{ keycloak_management_https_port }}"/>
         <socket-binding name="modcluster" multicast-address="${jboss.modcluster.multicast.address:224.0.1.105}" multicast-port="23364"/>
         <socket-binding name="txn-recovery-environment" port="4712"/>
         <socket-binding name="txn-status-manager" port="4713"/>
diff --git a/roles/keycloak/vars/main.yml b/roles/keycloak/vars/main.yml
index cf1d6cc..43ece24 100644
--- a/roles/keycloak/vars/main.yml
+++ b/roles/keycloak/vars/main.yml
@@ -4,11 +4,22 @@
 keycloak_admin_password:
 
 # internal variables below
+rhsso_rhn_ids:
+  '7.5.0': '101971'
+  '7.5.1': '103836'
 
 # locations
 keycloak_url: "http://{{ keycloak_host }}:{{ keycloak_http_port }}"
 keycloak_management_url: "http://{{ keycloak_host }}:{{ keycloak_management_http_port }}"
 
+
+keycloak:
+  home: "{{ keycloak_jboss_home }}"
+  config_dir: "{{ keycloak_config_dir }}"
+  bundle: "{{ keycloak_rhsso_archive if keycloak_rhsso_enable else keycloak_archive }}"
+  service_name: "{{ 'rhsso' if keycloak_rhsso_enable else 'keycloak' }}"
+  health_url: "{{ keycloak_management_url }}/health"
+
 # database
 keycloak_jdbc:
   postgres: