Skip to main content
Redhat Developers  Logo
  • Products

    Featured

    • Red Hat Enterprise Linux
      Red Hat Enterprise Linux Icon
    • Red Hat OpenShift AI
      Red Hat OpenShift AI
    • Red Hat Enterprise Linux AI
      Linux icon inside of a brain
    • Image mode for Red Hat Enterprise Linux
      RHEL image mode
    • Red Hat OpenShift
      Openshift icon
    • Red Hat Ansible Automation Platform
      Ansible icon
    • Red Hat Developer Hub
      Developer Hub
    • View All Red Hat Products
    • Linux

      • Red Hat Enterprise Linux
      • Image mode for Red Hat Enterprise Linux
      • Red Hat Universal Base Images (UBI)
    • Java runtimes & frameworks

      • JBoss Enterprise Application Platform
      • Red Hat build of OpenJDK
    • Kubernetes

      • Red Hat OpenShift
      • Microsoft Azure Red Hat OpenShift
      • Red Hat OpenShift Virtualization
      • Red Hat OpenShift Lightspeed
    • Integration & App Connectivity

      • Red Hat Build of Apache Camel
      • Red Hat Service Interconnect
      • Red Hat Connectivity Link
    • AI/ML

      • Red Hat OpenShift AI
      • Red Hat Enterprise Linux AI
    • Automation

      • Red Hat Ansible Automation Platform
      • Red Hat Ansible Lightspeed
    • Developer tools

      • Red Hat Trusted Software Supply Chain
      • Podman Desktop
      • Red Hat OpenShift Dev Spaces
    • Developer Sandbox

      Developer Sandbox
      Try Red Hat products and technologies without setup or configuration fees for 30 days with this shared Openshift and Kubernetes cluster.
    • Try at no cost
  • Technologies

    Featured

    • AI/ML
      AI/ML Icon
    • Linux
      Linux Icon
    • Kubernetes
      Cloud icon
    • Automation
      Automation Icon showing arrows moving in a circle around a gear
    • View All Technologies
    • Programming Languages & Frameworks

      • Java
      • Python
      • JavaScript
    • System Design & Architecture

      • Red Hat architecture and design patterns
      • Microservices
      • Event-Driven Architecture
      • Databases
    • Developer Productivity

      • Developer productivity
      • Developer Tools
      • GitOps
    • Secure Development & Architectures

      • Security
      • Secure coding
    • Platform Engineering

      • DevOps
      • DevSecOps
      • Ansible automation for applications and services
    • Automated Data Processing

      • AI/ML
      • Data Science
      • Apache Kafka on Kubernetes
      • View All Technologies
    • Start exploring in the Developer Sandbox for free

      sandbox graphic
      Try Red Hat's products and technologies without setup or configuration.
    • Try at no cost
  • Learn

    Featured

    • Kubernetes & Cloud Native
      Openshift icon
    • Linux
      Rhel icon
    • Automation
      Ansible cloud icon
    • Java
      Java icon
    • AI/ML
      AI/ML Icon
    • View All Learning Resources

    E-Books

    • GitOps Cookbook
    • Podman in Action
    • Kubernetes Operators
    • The Path to GitOps
    • View All E-books

    Cheat Sheets

    • Linux Commands
    • Bash Commands
    • Git
    • systemd Commands
    • View All Cheat Sheets

    Documentation

    • API Catalog
    • Product Documentation
    • Legacy Documentation
    • Red Hat Learning

      Learning image
      Boost your technical skills to expert-level with the help of interactive lessons offered by various Red Hat Learning programs.
    • Explore Red Hat Learning
  • Developer Sandbox

    Developer Sandbox

    • Access Red Hat’s products and technologies without setup or configuration, and start developing quicker than ever before with our new, no-cost sandbox environments.
    • Explore Developer Sandbox

    Featured Developer Sandbox activities

    • Get started with your Developer Sandbox
    • OpenShift virtualization and application modernization using the Developer Sandbox
    • Explore all Developer Sandbox activities

    Ready to start developing apps?

    • Try at no cost
  • Blog
  • Events
  • Videos

Automate your SSO with Ansible and Keycloak

February 20, 2023
Romain Pelisse
Related topics:
Automation and management
Related products:
Red Hat Ansible Automation Platform

Share:

    The article Deploy Keycloak single sign-on with Ansible discussed how to automate the deployment of Keycloak. In this follow-up article, we’ll use that as a baseline and explore how to automate the configuration of the Keycloak single sign-on (SSO) server, including setting up users, specifying LDAP connection details, and so on.

    Here again, to facilitate our automation, we will leverage an Ansible collection named middleware_automation.keycloak, specifically designed for this endeavor. 

    Install Keycloak with Ansible

    In the previous article, we saw in detail how to automate the installation of Keycloak. For this new installment, we’ll start from there using the following playbook:

    ---
    - name: Playbook for Keycloak Hosts
      hosts: keycloak
      vars:
    	keycloak_admin_password: "remembertochangeme"
      collections:
    	- middleware_automation.keycloak
      roles:
    	- keycloak
    

    This short playbook will take care of the installation of the single sign-on server itself, which already includes quite a few tasks to perform on the target system, including:

    • Creating appropriate operating system user and group accounts (the name is keycloak for both)
    • Downloading the installation archive from the Keycloak website
    • Unarchiving the content while ensuring that all the files are associated with the appropriate user and groups along with the correct privileges
    • Ensuring that the required version of the Java Virtual Machine (JVM) is installed
    • Integrating the software into the host service management system (in our case, the Linux systemd daemon).

    However, prior to running the playbook, we are going to enhance it even further to perform day two configurations of the Keycloak server, including the configuration of the SSO realm, clients, and users.

    Configure single sign-on

    The Ansible collection for Keycloak allows defining the realm, client, and users without adding a single, extra task. All that is needed is to define a few extra variables. Of course, those variables are quite structured and need to be formatted correctly for Ansible to be able to configure Keycloak appropriately. The following is a complete, working example of such a configuration:

    ---
    - name: Playbook for Keycloak Hosts
      hosts: all
      vars:
        keycloak_admin_password: "remembertochangeme"
        keycloak_realm: TestRealm
      collections:
        - middleware_automation.keycloak
      roles:
        - keycloak
      tasks:
     - name: Keycloak Realm Role
      	ansible.builtin.include_role:
        	name: keycloak_realm
      	vars:
        	keycloak_client_default_roles:
          	- TestRoleAdmin
          	- TestRoleUser
        	keycloak_client_users:
          	- username: TestUser
            	password: password
            	client_roles:
              	- client: TestClient
                	role: TestRoleUser
                	realm: "{{ keycloak_realm }}"
          	- username: TestAdmin
            	password: password
            	client_roles:
              	- client: TestClient
                	role: TestRoleUser
                	realm: "{{ keycloak_realm }}"
              	- client: TestClient
                	role: TestRoleAdmin
                	realm: "{{ keycloak_realm }}"
        	keycloak_realm: TestRealm
        	keycloak_clients:
          	- name: TestClient
            	roles: "{{ keycloak_client_default_roles }}"
            	realm: "{{ keycloak_realm }}"
            	public_client: "{{ keycloak_client_public }}"
            	web_origins: "{{ keycloak_client_web_origins }}"
            	users: "{{ keycloak_client_users }}"
            	client_id: TestClient

    Note that this example, purposely, does not rely on any external sources (such as an LDAP server) so that it can be used easily, to test the collection without requiring the setup of any extra resources.

    Because the SSO configuration is quite dense, we are going to break down each portion to not only provide additional insight, but to illustrate its significance in the SSO configuration.

    Define the realm

    The very first step is to define a realm, which, for the purpose of this article, contains the desired user and role details, but other capabilities provided by Keycloak that will be explored throughout the article. To create the realm, we just need to add one variable to our playbook:

    … 
         	 	- client: TestClient
           		 role: TestRoleAdmin
           		 realm: "{{ keycloak_realm }}"
       	 keycloak_realm: TestRealm
       	 keycloak_clients:
    … 
    

    Configure roles and users

    The next portion of the variables provided populates the realm with the appropriate details related to users and roles. For the demonstration of this article, we added two users (and two roles) to the realm we are defining:

    • TestAdmin: An admin user who can connect to the SSO server and configure the realm. This user belongs to both roles we defined above.
    • TestClient: A user belonging to the realm and thus belongs only in the TestRoleUser.
    …
        	keycloak_client_default_roles:
     		 - TestRoleAdmin
     		 - TestRoleUser
       	 keycloak_client_users:
     		 - username: TestUser
       		 password: password
       		 client_roles:
         		 - client: TestClient
           		 role: TestRoleUser
           		 realm: "{{ keycloak_realm }}"
     		 - username: TestAdmin
       		 password: password
       		 client_roles:
         		 - client: TestClient
           		 role: TestRoleUser
           		 realm: "{{ keycloak_realm }}"
         		 - client: TestClient
           		 role: TestRoleAdmin
           		 realm: "{{ keycloak_realm }}"
    …
    

    Define Keycloak clients

    The last portion of the variables defines the client associated with the roles so that their users can use the SSO service:

    …
        	keycloak_clients:
          	- name: TestClient
            	roles: "{{ keycloak_client_default_roles }}"
            	realm: "{{ keycloak_realm }}"
            	public_client: "{{ keycloak_client_public }}"
            	web_origins: "{{ keycloak_client_web_origins }}"
            	users: "{{ keycloak_client_users }}"
    
    …
    

    Run the playbook

    That’s it! With these details provided, we can now run the playbook to deploy Keycloak and fully configure our SSO instance (based on the user's information inside the TestRealm). Execute the following command to execute the automation:

    # ansible-playbook -i inventory keycloak.yml
    
    PLAY [Playbook for Keycloak Hosts] *********************************************
    
    TASK [Gathering Facts] *********************************************************
    ok: [localhost]
    
    TASK [keycloak : Validating arguments against arg spec 'main'] *****************
    ok: [localhost]
    
    TASK [keycloak : Check prerequisites] ******************************************
    included: /work/roles/keycloak/tasks/prereqs.yml for localhost
    
    TASK [keycloak : Validate admin console password] ******************************
    ok: [localhost]
    
    TASK [keycloak : Validate configuration] ***************************************
    ok: [localhost]
    
    TASK [keycloak : Validate credentials] *****************************************
    ok: [localhost]
    
    TASK [keycloak : Validate persistence configuration] ***************************
    skipping: [localhost]
    
    TASK [keycloak : Ensure required packages are installed] ***********************
    included: /work/roles/keycloak/tasks/fastpackages.yml for localhost
    
    TASK [keycloak : Check if packages are already installed] **********************
    fatal: [localhost]: FAILED! => {"changed": true, "cmd": ["rpm", "-q", "java-11-openjdk-headless", "unzip", "procps-ng", "initscripts"], "delta": "0:00:00.006828", "end": "2022-12-28 14:22:44.352750", "msg": "non-zero return code", "rc": 3, "start": "2022-12-28 14:22:44.345922", "stderr": "", "stderr_lines": [], "stdout": "package java-11-openjdk-headless is not installed\npackage unzip is not installed\nprocps-ng-3.3.15-6.el8.x86_64\npackage initscripts is not installed", "stdout_lines": ["package java-11-openjdk-headless is not installed", "package unzip is not installed", "procps-ng-3.3.15-6.el8.x86_64", "package initscripts is not installed"]}
    
    TASK [keycloak : Add missing packages to the yum install list] *****************
    ok: [localhost]
    
    TASK [keycloak : Install packages: ['java-11-openjdk-headless', 'unzip', 'initscripts']] ***
    changed: [localhost]
    
    TASK [keycloak : Include firewall config tasks] ********************************
    skipping: [localhost]
    
    TASK [keycloak : Include install tasks] ****************************************
    included: /work/roles/keycloak/tasks/install.yml for localhost
    
    TASK [keycloak : Validate parameters] ******************************************
    ok: [localhost]
    
    TASK [keycloak : Check for an existing deployment] *****************************
    ok: [localhost]
    
    TASK [keycloak : Stop the old keycloak service] ********************************
    skipping: [localhost]
    
    TASK [keycloak : Remove the old keycloak deployment] ***************************
    skipping: [localhost]
    
    TASK [keycloak : Check for an existing deployment after possible forced removal] ***
    ok: [localhost]
    
    TASK [keycloak : Create keycloak service user/group] ***************************
    changed: [localhost]
    
    TASK [keycloak : Create keycloak install location] *****************************
    changed: [localhost]
    
    TASK [keycloak : Set download archive path] ************************************
    ok: [localhost]
    
    TASK [keycloak : Check download archive path] **********************************
    ok: [localhost]
    
    TASK [keycloak : Check local download archive path] ****************************
    ok: [localhost]
    
    TASK [keycloak : Download keycloak archive] ************************************
    ok: [localhost]
    
    TASK [keycloak : Perform download from RHN] ************************************
    skipping: [localhost]
    
    TASK [keycloak : Download rhsso archive from alternate location] ***************
    skipping: [localhost]
    
    TASK [keycloak : Check downloaded archive] *************************************
    ok: [localhost]
    
    TASK [keycloak : Copy archive to target nodes] *********************************
    changed: [localhost]
    
    TASK [keycloak : Check target directory: /opt/keycloak/keycloak-18.0.2] ********
    ok: [localhost]
    
    TASK [keycloak : Extract Keycloak archive on target] ***************************
    changed: [localhost]
    
    TASK [keycloak : Inform decompression was not executed] ************************
    skipping: [localhost]
    
    TASK [keycloak : Reown installation directory to keycloak] *********************
    ok: [localhost]
    
    TASK [keycloak : Install postgres driver] **************************************
    skipping: [localhost]
    
    TASK [keycloak : Deploy keycloak config to /opt/keycloak/keycloak-18.0.2/standalone/configuration/keycloak.xml from standalone.xml.j2] ***
    changed: [localhost]
    
    TASK [keycloak : Deploy keycloak config with remote cache store to /opt/keycloak/keycloak-18.0.2/standalone/configuration/keycloak.xml] ***
    skipping: [localhost]
    
    TASK [keycloak : Include systemd tasks] ****************************************
    included: /work/roles/keycloak/tasks/systemd.yml for localhost
    
    TASK [keycloak : Configure keycloak service script wrapper] ********************
    changed: [localhost]
    
    TASK [keycloak : Determine JAVA_HOME for selected JVM RPM] *********************
    ok: [localhost]
    
    TASK [keycloak : Configure sysconfig file for keycloak service] ****************
    changed: [localhost]
    
    TASK [keycloak : Configure systemd unit file for keycloak service] *************
    changed: [localhost]
    
    TASK [keycloak : Reload systemd] ***********************************************
    ok: [localhost]
    
    TASK [keycloak : Start and wait for keycloak service (first node db)] **********
    skipping: [localhost]
    
    TASK [keycloak : Start and wait for keycloak service (remaining nodes)] ********
    included: /work/roles/keycloak/tasks/start_keycloak.yml for localhost
    
    TASK [keycloak : Start keycloak service] ***************************************
    changed: [localhost]
    
    TASK [keycloak : Wait until keycloak becomes active http://localhost:9990/health] ***
    FAILED - RETRYING: [localhost]: Wait until keycloak becomes active http://localhost:9990/health (25 retries left).
    ok: [localhost]
    
    TASK [keycloak : Check service status] *****************************************
    ok: [localhost]
    
    TASK [keycloak : Verify service status] ****************************************
    ok: [localhost] => {
    	"changed": false,
    	"msg": "All assertions passed"
    }
    
    TASK [keycloak : Flush handlers] ***********************************************
    
    RUNNING HANDLER [keycloak : Restart handler] ***********************************
    included: /work/roles/keycloak/tasks/restart_keycloak.yml for localhost
    
    RUNNING HANDLER [keycloak : Restart and enable {{ keycloak.service_name }} service] ***
    changed: [localhost]
    
    RUNNING HANDLER [keycloak : Wait until {{ keycloak.service_name }} becomes active {{ keycloak.health_url }}] ***
    FAILED - RETRYING: [localhost]: Wait until keycloak becomes active http://localhost:9990/health (25 retries left).
    ok: [localhost]
    
    RUNNING HANDLER [keycloak : Restart and enable {{ keycloak.service_name }} service] ***
    skipping: [localhost]
    
    TASK [keycloak : Include patch install tasks] **********************************
    skipping: [localhost]
    
    TASK [keycloak : Link default logs directory] **********************************
    changed: [localhost]
    
    TASK [keycloak : Check admin credentials by generating a token (supposed to fail on first installation)] ***
    FAILED - RETRYING: [localhost]: Check admin credentials by generating a token (supposed to fail on first installation) (2 retries left).
    FAILED - RETRYING: [localhost]: Check admin credentials by generating a token (supposed to fail on first installation) (1 retries left).
    fatal: [localhost]: FAILED! => {"attempts": 2, "cache_control": "no-store", "changed": false, "connection": "close", "content_length": "72", "content_type": "application/json", "date": "Wed, 28 Dec 2022 14:24:25 GMT", "elapsed": 0, "json": {"error": "invalid_grant", "error_description": "Invalid user credentials"}, "msg": "Status code was 401 and not [200]: HTTP Error 401: Unauthorized", "pragma": "no-cache", "redirected": false, "referrer_policy": "no-referrer", "status": 401, "strict_transport_security": "max-age=31536000; includeSubDomains", "url": "http://localhost:8080/auth/realms/master/protocol/openid-connect/token", "x_content_type_options": "nosniff", "x_frame_options": "SAMEORIGIN", "x_xss_protection": "1; mode=block"}
    
    TASK [keycloak : Create keycloak admin user] ***********************************
    changed: [localhost]
    
    TASK [keycloak : Restart keycloak] *********************************************
    included: /work/roles/keycloak/tasks/restart_keycloak.yml for localhost
    
    TASK [keycloak : Restart and enable keycloak service] **************************
    changed: [localhost]
    
    TASK [keycloak : Wait until keycloak becomes active http://localhost:9990/health] ***
    FAILED - RETRYING: [localhost]: Wait until keycloak becomes active http://localhost:9990/health (25 retries left).
    ok: [localhost]
    
    TASK [keycloak : Restart and enable keycloak service] **************************
    skipping: [localhost]
    
    TASK [keycloak : Wait until keycloak becomes active http://localhost:9990/health] ***
    ok: [localhost]
    
    TASK [Keycloak Realm Role] *****************************************************
    
    TASK [keycloak_realm : Validating arguments against arg spec 'main'] ***********
    ok: [localhost]
    
    TASK [keycloak_realm : Generate keycloak auth token] ***************************
    ok: [localhost]
    
    TASK [keycloak_realm : Determine if realm exists] ******************************
    ok: [localhost]
    
    TASK [keycloak_realm : Create Realm] *******************************************
    ok: [localhost]
    
    TASK [keycloak_realm : Create user federation] *********************************
    
    TASK [keycloak_realm : Validate Keycloak clients] ******************************
    ok: [localhost] => (item=TestClient)
    
    TASK [keycloak_realm : Create or update a Keycloak client] *********************
    changed: [localhost] => (item=None)
    changed: [localhost]
    
    TASK [keycloak_realm : Create client roles] ************************************
    included: /work/roles/keycloak_realm/tasks/manage_client_roles.yml for localhost => (item={'name': 'TestClient', 'roles': ['TestRoleAdmin', 'TestRoleUser'], 'realm': 'TestRealm', 'public_client': True, 'web_origins': '+', 'users': [{'username': 'TestUser', 'password': 'password', 'client_roles': [{'client': 'TestClient', 'role': 'TestRoleUser', 'realm': 'TestRealm'}]}, {'username': 'TestAdmin', 'password': 'password', 'client_roles': [{'client': 'TestClient', 'role': 'TestRoleUser', 'realm': 'TestRealm'}, {'client': 'TestClient', 'role': 'TestRoleAdmin', 'realm': 'TestRealm'}]}], 'client_id': 'TestClient'})
    
    TASK [keycloak_realm : Create client roles] ************************************
    changed: [localhost] => (item=None)
    changed: [localhost] => (item=None)
    changed: [localhost]
    
    TASK [keycloak_realm : Create client users] ************************************
    included: /work/roles/keycloak_realm/tasks/manage_client_users.yml for localhost => (item={'name': 'TestClient', 'roles': ['TestRoleAdmin', 'TestRoleUser'], 'realm': 'TestRealm', 'public_client': True, 'web_origins': '+', 'users': [{'username': 'TestUser', 'password': 'password', 'client_roles': [{'client': 'TestClient', 'role': 'TestRoleUser', 'realm': 'TestRealm'}]}, {'username': 'TestAdmin', 'password': 'password', 'client_roles': [{'client': 'TestClient', 'role': 'TestRoleUser', 'realm': 'TestRealm'}, {'client': 'TestClient', 'role': 'TestRoleAdmin', 'realm': 'TestRealm'}]}], 'client_id': 'TestClient'})
    
    TASK [keycloak_realm : Manage Users] *******************************************
    included: /work/roles/keycloak_realm/tasks/manage_user.yml for localhost => (item={'username': 'TestUser', 'password': 'password', 'client_roles': [{'client': 'TestClient', 'role': 'TestRoleUser', 'realm': 'TestRealm'}]})
    included: /work/roles/keycloak_realm/tasks/manage_user.yml for localhost => (item={'username': 'TestAdmin', 'password': 'password', 'client_roles': [{'client': 'TestClient', 'role': 'TestRoleUser', 'realm': 'TestRealm'}, {'client': 'TestClient', 'role': 'TestRoleAdmin', 'realm': 'TestRealm'}]})
    
    TASK [keycloak_realm : Check if User Already Exists] ***************************
    ok: [localhost]
    
    TASK [keycloak_realm : Create User] ********************************************
    ok: [localhost]
    
    TASK [keycloak_realm : Get User] ***********************************************
    ok: [localhost]
    
    TASK [keycloak_realm : Update User Password] ***********************************
    ok: [localhost]
    
    TASK [keycloak_realm : Check if User Already Exists] ***************************
    ok: [localhost]
    
    TASK [keycloak_realm : Create User] ********************************************
    ok: [localhost]
    
    TASK [keycloak_realm : Get User] ***********************************************
    ok: [localhost]
    
    TASK [keycloak_realm : Update User Password] ***********************************
    ok: [localhost]
    
    TASK [keycloak_realm : Manage User Roles] **************************************
    included: /work/roles/keycloak_realm/tasks/manage_user_roles.yml for localhost => (item={'username': 'TestUser', 'password': 'password', 'client_roles': [{'client': 'TestClient', 'role': 'TestRoleUser', 'realm': 'TestRealm'}]})
    included: /work/roles/keycloak_realm/tasks/manage_user_roles.yml for localhost => (item={'username': 'TestAdmin', 'password': 'password', 'client_roles': [{'client': 'TestClient', 'role': 'TestRoleUser', 'realm': 'TestRealm'}, {'client': 'TestClient', 'role': 'TestRoleAdmin', 'realm': 'TestRealm'}]})
    
    TASK [keycloak_realm : Get User TestUser] **************************************
    ok: [localhost]
    
    TASK [keycloak_realm : Refresh keycloak auth token] ****************************
    ok: [localhost]
    
    TASK [keycloak_realm : Manage Client Role Mapping for TestUser] ****************
    included: /work/roles/keycloak_realm/tasks/manage_user_client_roles.yml for localhost => (item={'client': 'TestClient', 'role': 'TestRoleUser', 'realm': 'TestRealm'})
    
    TASK [keycloak_realm : Get Realm for role] *************************************
    ok: [localhost]
    
    TASK [keycloak_realm : Check if Mapping is available] **************************
    ok: [localhost]
    
    TASK [keycloak_realm : Create Role Mapping] ************************************
    ok: [localhost] => (item={'id': '5cdd02f6-8341-4cd0-ba31-46631a847cdf', 'name': 'TestRoleUser', 'composite': False, 'clientRole': True, 'containerId': 'f084b840-c30d-4c93-933e-18f8be1ed19a'})
    skipping: [localhost] => (item={'id': '88579cbc-9d5d-462f-8816-635901b6a12e', 'name': 'TestRoleAdmin', 'composite': False, 'clientRole': True, 'containerId': 'f084b840-c30d-4c93-933e-18f8be1ed19a'})
    
    TASK [keycloak_realm : Get User TestAdmin] *************************************
    ok: [localhost]
    
    TASK [keycloak_realm : Refresh keycloak auth token] ****************************
    ok: [localhost]
    
    TASK [keycloak_realm : Manage Client Role Mapping for TestAdmin] ***************
    included: /work/roles/keycloak_realm/tasks/manage_user_client_roles.yml for localhost => (item={'client': 'TestClient', 'role': 'TestRoleUser', 'realm': 'TestRealm'})
    included: /work/roles/keycloak_realm/tasks/manage_user_client_roles.yml for localhost => (item={'client': 'TestClient', 'role': 'TestRoleAdmin', 'realm': 'TestRealm'})
    
    TASK [keycloak_realm : Get Realm for role] *************************************
    ok: [localhost]
    
    TASK [keycloak_realm : Check if Mapping is available] **************************
    ok: [localhost]
    
    TASK [keycloak_realm : Create Role Mapping] ************************************
    ok: [localhost] => (item={'id': '5cdd02f6-8341-4cd0-ba31-46631a847cdf', 'name': 'TestRoleUser', 'composite': False, 'clientRole': True, 'containerId': 'f084b840-c30d-4c93-933e-18f8be1ed19a'})
    skipping: [localhost] => (item={'id': '88579cbc-9d5d-462f-8816-635901b6a12e', 'name': 'TestRoleAdmin', 'composite': False, 'clientRole': True, 'containerId': 'f084b840-c30d-4c93-933e-18f8be1ed19a'})
    
    TASK [keycloak_realm : Get Realm for role] *************************************
    ok: [localhost]
    
    TASK [keycloak_realm : Check if Mapping is available] **************************
    ok: [localhost]
    
    TASK [keycloak_realm : Create Role Mapping] ************************************
    ok: [localhost] => (item={'id': '88579cbc-9d5d-462f-8816-635901b6a12e', 'name': 'TestRoleAdmin', 'composite': False, 'clientRole': True, 'containerId': 'f084b840-c30d-4c93-933e-18f8be1ed19a'})
    
    PLAY RECAP *********************************************************************
    localhost              	: ok=82   changed=16   unreachable=0	failed=0	skipped=14   rescued=2	ignored=0

    Once the playbook has run successfully, you can verify on the target instance that the SSO server is running (as a systemd service) using the following command:

    
    # systemctl status keycloak
    ● keycloak.service - keycloak Server
       Loaded: loaded (/etc/systemd/system/keycloak.service; enabled; vendor preset: disabled)
       Active: active (running) since Wed 2022-12-28 14:24:28 UTC; 26min ago
      Process: 1607 ExecStop=/opt/keycloak/keycloak-service.sh stop (code=exited, status=0/SUCCESS)
      Process: 1627 ExecStart=/opt/keycloak/keycloak-service.sh start (code=exited, status=0/SUCCESS)
     Main PID: 1742 (java)
       CGroup: /system.slice/keycloak.service
           	├─1630 /bin/sh /opt/keycloak/keycloak-18.0.2/bin/standalone.sh -Djboss.bind.address=0.0.0.0 -Djboss.http.port=8080 -Djboss.https.port=8443 -Djboss.management.http.port=9990 -Djbo>
           	└─1742 /usr/lib/jvm/java-11-openjdk-11.0.17.0.8-2.el8_6.x86_64/bin/java -D[Standalone] -server -Xms1024m -Xmx2048m --add-exports=java.desktop/sun.awt=ALL-UNNAMED --add-exports=ja>
    
    Dec 28 14:24:35 515ef9b313a5 keycloak-service.sh[1742]: 14:24:35,360 INFO  [org.jboss.resteasy.resteasy_jaxrs.i18n] (ServerService Thread Pool -- 59) RESTEASY002220: Adding singleton resour>
    Dec 28 14:24:35 515ef9b313a5 keycloak-service.sh[1742]: 14:24:35,360 INFO  [org.jboss.resteasy.resteasy_jaxrs.i18n] (ServerService Thread Pool -- 59) RESTEASY002220: Adding singleton resour>
    Dec 28 14:24:35 515ef9b313a5 keycloak-service.sh[1742]: 14:24:35,360 INFO  [org.jboss.resteasy.resteasy_jaxrs.i18n] (ServerService Thread Pool -- 59) RESTEASY002220: Adding singleton resour>
    Dec 28 14:24:35 515ef9b313a5 keycloak-service.sh[1742]: 14:24:35,360 INFO  [org.jboss.resteasy.resteasy_jaxrs.i18n] (ServerService Thread Pool -- 59) RESTEASY002210: Adding provider singlet>
    Dec 28 14:24:35 515ef9b313a5 keycloak-service.sh[1742]: 14:24:35,428 INFO  [org.wildfly.extension.undertow] (ServerService Thread Pool -- 59) WFLYUT0021: Registered web context: '/auth' for>
    Dec 28 14:24:35 515ef9b313a5 keycloak-service.sh[1742]: 14:24:35,487 INFO  [org.jboss.as.server] (ServerService Thread Pool -- 42) WFLYSRV0010: Deployed "keycloak-server.war" (runtime-name >
    Dec 28 14:24:35 515ef9b313a5 keycloak-service.sh[1742]: 14:24:35,522 INFO  [org.jboss.as.server] (Controller Boot Thread) WFLYSRV0212: Resuming server
    Dec 28 14:24:35 515ef9b313a5 keycloak-service.sh[1742]: 14:24:35,524 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: Keycloak 18.0.2 (WildFly Core 18.1.1.Final) started in 8112ms>
    Dec 28 14:24:35 515ef9b313a5 keycloak-service.sh[1742]: 14:24:35,526 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:9990/>
    Dec 28 14:24:35 515ef9b313a5 keycloak-service.sh[1742]: 14:24:35,527 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:9990

    To go even further, we can add a check to our playbook that will use the Keycloak admin credentials to get a token from the SSO server. This emulates what will happen when a user tries to access an application using the SSO service. Thus, if it works fine, it confirms the service is functional:

    - name: Verify token api call
      ansible.builtin.uri:
        url: "{{ keycloak_port }}/auth/realms/master/protocol/openid-connect/token"
        method: POST
        body: "client_id=admin-cli&username=admin&password={{ keycloak_admin_password }}&grant_type=password"
        validate_certs: no
        register: keycloak_auth_response
        until: keycloak_auth_response.status == 200
        retries: 2
        delay: 2
    
    

    Conclusion

    On top of deploying the Keycloak server, we have fully automated the configuration of our SSO. We can deploy a fully functional instance, in any environment, without any manual intervention. Most importantly, it is accomplished in a secure and repeatable fashion. With just this playbook, you can set up the entire infrastructure for SSO in a matter of minutes using the tooling provided by the Ansible Middleware project.

    Last updated: August 14, 2023

    Related Posts

    • Deploy Keycloak single sign-on with Ansible

    • How to install Red Hat Ansible Automation Platform on RHEL 9

    • API login and JWT token generation using Keycloak

    Recent Posts

    • Storage considerations for OpenShift Virtualization

    • Upgrade from OpenShift Service Mesh 2.6 to 3.0 with Kiali

    • EE Builder with Ansible Automation Platform on OpenShift

    • How to debug confidential containers securely

    • Announcing self-service access to Red Hat Enterprise Linux for Business Developers

    What’s up next?

    Choosing an Automation Tool feature image

    Get an overview of Ansible, Helm, and Kubernetes Operators, including how they work and the benefits of each tool.

     

     

    Read the e-book
    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Products

    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform

    Build

    • Developer Sandbox
    • Developer Tools
    • Interactive Tutorials
    • API Catalog

    Quicklinks

    • Learning Resources
    • E-books
    • Cheat Sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site Status Dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit
    © 2025 Red Hat

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Report a website issue