Compare commits

..

17 Commits
3.0.10 ... main

Author SHA1 Message Date
Harsha Cherukuri
9e5946a2a4 Merge pull request #373 from RanabirChakraborty/AMW-594
AMW-594 Upstream release is failing due to recent rules change
2026-07-28 13:21:08 -04:00
Ranabir Chakraborty
ef2f24e9fc AMW-594 Upstream release is failing due to recent rules change 2026-07-28 21:28:35 +05:30
Harsha Cherukuri
866c5e2255 Merge pull request #371 from RISE-GmbH/368-avoid_pruning_of_undefined_realm_attributes
368 keycloak_realm: Control whether undefined realm attributes are pruned
2026-07-28 09:10:49 -04:00
Harsha Cherukuri
a0b924b593 Merge pull request #372 from hcherukuri/main
Add community.general.keycloak_realm_users_info into keycloak collection
2026-07-28 08:46:31 -04:00
Thomas Bargetz
95c6fe1b50 386 add integration tests 2026-07-27 11:01:35 +02:00
Harsha Cherukuri
9bb680b8e6 Update plugins/modules/keycloak_realm_users_info.py
Co-authored-by: Felix Fontein <felix@fontein.de>
2026-07-26 21:02:08 -04:00
Thomas Bargetz
1f0a7c289f 368 introduce the parameter prune_undefined_realm_attributes to control whether undefined realm attributes are pruned 2026-07-24 10:20:17 +02:00
Harsha Cherukuri
ff51c4098a Add community.general.keycloak_realm_users_info into keycloak collection 2026-07-23 22:31:32 -04:00
Ranabir Chakraborty
f2792e32ba Merge pull request #364 from cihlamar/AMW-299
Fix health check URL to include HTTP relative path
2026-07-22 19:49:44 +05:30
Martin Cihlar
4f62617f2c Fix health check URL to include HTTP relative path
- Include keycloak_quarkus_http_relative_path in health_url construction
- Use rstrip('/') to handle both default '/' and custom paths like '/auth'
- Replace hardcoded 'master' realm with keycloak_quarkus_master_realm variable
- Add keycloak_quarkus_validate_certs option for SSL certificate validation
- Update documentation in meta and README

Fixes #299
2026-07-22 11:24:57 +02:00
Harsha Cherukuri
edb38ebb3d Merge pull request #366 from RanabirChakraborty/AMW-592
AMW-592 AMW Github Actions are failing because of latest molecule
2026-07-20 15:38:03 -04:00
Ranabir Chakraborty
d25bf46a6a AMW-592 AMW Github Actions are failing because of latest molecule 2026-07-21 00:27:35 +05:30
Helmut Wolf
74ca1e05ef Merge pull request #363 from world-direct/fix/no_log-provider-secrets
fix(keycloak_quarkus): avoid leaking maven provider password in logs
2026-07-01 09:38:16 +02:00
Helmut Wolf
b9181abb24 fix(keycloak_quarkus): avoid leaking maven provider password in logs
The Validate providers / Download custom providers via http / Copy local
providers tasks loop over all providers and print the item (including
maven.password) in cleartext, even for skipped iterations. Add the same
conditional no_log the maven download/copy tasks already use so entries
carrying a secret are censored while url/local providers stay visible.

Refs ansible-middleware/keycloak#362

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 09:23:48 +02:00
Helmut Wolf
d4eafe1b30 Merge pull request #361 from world-direct/fix/360
fix: fact renaming regression (store_items), closes #360
2026-06-30 08:35:09 +02:00
Helmut Wolf
19bd41a86a fix: fact renaming regression (store_items), closes #360 2026-06-30 08:15:48 +02:00
ansible-middleware-core
b44d754b8c Bump version to 3.0.11 2026-06-25 12:29:55 +00:00
42 changed files with 340 additions and 66 deletions

31
.github/workflows/publish.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
---
name: Publish collection
on:
workflow_dispatch:
jobs:
publish:
uses: ansible-middleware/github-actions/.github/workflows/publish.yml@main
with:
collection_fqcn: 'middleware_automation.keycloak'
downstream_name: 'rhbk'
secrets:
galaxy_token: ${{ secrets.ANSIBLE_GALAXY_API_KEY }}
jira_webhook: ${{ secrets.JIRA_WEBHOOK_CREATE_VERSION }}
push_token: ${{ secrets.TRIGGERING_PAT }}
dispatch:
needs: publish
if: needs.publish.result == 'success'
strategy:
matrix:
repo: ['ansible-middleware/ansible-middleware-ee']
runs-on: ubuntu-latest
steps:
- name: Repository Dispatch
uses: peter-evans/repository-dispatch@v4
with:
token: ${{ secrets.TRIGGERING_PAT }}
repository: ${{ matrix.repo }}
event-type: "Dependency released - Keycloak v${{ needs.publish.outputs.tag_version }}"
client-payload: '{ "github": ${{toJson(github)}} }'

View File

@@ -17,18 +17,4 @@ jobs:
secrets:
galaxy_token: ${{ secrets.ANSIBLE_GALAXY_API_KEY }}
jira_webhook: ${{ secrets.JIRA_WEBHOOK_CREATE_VERSION }}
dispatch:
needs: release
strategy:
matrix:
repo: ['ansible-middleware/ansible-middleware-ee']
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 v${{ needs.release.outputs.tag_version }}"
client-payload: '{ "github": ${{toJson(github)}} }'
push_token: ${{ secrets.TRIGGERING_PAT }}

View File

@@ -85,6 +85,7 @@ All Keycloak administration modules from `community.general` are provided in thi
* `keycloak_realm_keys_metadata_info`: retrieve realm keys metadata.
* `keycloak_realm_localization`: manage realm localization texts.
* `keycloak_realm_rolemapping`: manage realm role mappings for users and groups.
* `keycloak_realm_users_info`: retrieve users from a realm.
* `keycloak_role`: manage realm and client roles.
* `keycloak_user`: manage users (create/update/delete).
* `keycloak_user_execute_actions_email`: trigger execute-actions emails for users.

View File

@@ -1,7 +1,7 @@
---
namespace: middleware_automation
name: keycloak
version: "3.0.10"
version: "3.0.11"
readme: README.md
authors:
- Romain Pelisse <rpelisse@redhat.com>

View File

@@ -29,6 +29,7 @@ action_groups:
- keycloak_realm_keys_metadata_info
- keycloak_realm_localization
- keycloak_realm_rolemapping
- keycloak_realm_users_info
- keycloak_role
- keycloak_user
- keycloak_user_federation

View File

@@ -6,7 +6,7 @@ platforms:
image: ghcr.io/hspaans/molecule-containers:debian-13
pre_build_image: true
privileged: true
port_bindings:
exposed_ports:
- "8080/tcp"
- "8443/tcp"
- "8009/tcp"

View File

@@ -7,7 +7,7 @@ platforms:
pre_build_image: true
privileged: true
command: "/usr/sbin/init"
port_bindings:
exposed_ports:
- "8080/tcp"
- "8443/tcp"
- "8009/tcp"

View File

@@ -1,4 +1,9 @@
---
keycloak_download_validate_certs: false
keycloak_validate_certs: false
keycloak_quarkus_download_validate_certs: false
keycloak_quarkus_validate_certs: false
keycloak_realm_validate_certs: false
keycloak_quarkus_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_systemd_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"
keycloak_quarkus_install_require_privilege_escalation: "{{ keycloak_install_requires_become | default(true) }}"

View File

@@ -7,12 +7,11 @@
keycloak_quarkus_show_deprecation_warnings: false
keycloak_quarkus_bootstrap_admin_password: "remembertochangeme"
keycloak_quarkus_bootstrap_admin_user: "remembertochangeme"
keycloak_quarkus_hostname: https://proxy
keycloak_quarkus_hostname: "http://proxy:{{ keycloak_quarkus_http_port }}"
keycloak_quarkus_log: file
keycloak_quarkus_http_enabled: True
keycloak_quarkus_http_port: 8080
keycloak_quarkus_proxy_mode: edge
keycloak_quarkus_http_relative_path: /
keycloak_quarkus_health_check_url: http://proxy:8080/realms/master/.well-known/openid-configuration
roles:
- role: keycloak_quarkus

View File

@@ -9,7 +9,7 @@ platforms:
command: "/usr/sbin/init"
networks:
- name: keycloak
port_bindings:
exposed_ports:
- "8080/tcp"
published_ports:
- 0.0.0.0:8080:8080/tcp
@@ -20,7 +20,7 @@ platforms:
command: "/usr/sbin/init"
networks:
- name: keycloak
port_bindings:
exposed_ports:
- "443/tcp"
published_ports:
- 0.0.0.0:443:443/tcp

View File

@@ -7,7 +7,7 @@ platforms:
pre_build_image: true
privileged: true
command: "/usr/sbin/init"
port_bindings:
exposed_ports:
- "8080/tcp"
- "8443/tcp"
- "8009/tcp"

View File

@@ -52,6 +52,7 @@
- keycloak_realm_keys_metadata_info
- keycloak_realm_localization
- keycloak_realm_rolemapping
- keycloak_realm_users_info
- keycloak_role
- keycloak_user
- keycloak_user_execute_actions_email
@@ -90,6 +91,7 @@
auth_realm: "{{ auth_realm }}"
auth_username: "{{ auth_username }}"
auth_password: "{{ auth_password }}"
validate_certs: false
block:
- name: Reset fixtures from a previous verify run
middleware_automation.keycloak.keycloak_user:
@@ -168,6 +170,7 @@
middleware_automation.keycloak.keycloak_realm_info:
auth_keycloak_url: "{{ auth_keycloak_url }}"
realm: "{{ target_realm }}"
validate_certs: false
- name: Exercise migrated modules against running Keycloak
module_defaults:
@@ -176,6 +179,7 @@
auth_realm: "{{ auth_realm }}"
auth_username: "{{ auth_username }}"
auth_password: "{{ auth_password }}"
validate_certs: false
block:
- name: keycloak_realm_keys_metadata_info — query realm keys
middleware_automation.keycloak.keycloak_realm_keys_metadata_info:
@@ -185,6 +189,18 @@
middleware_automation.keycloak.keycloak_component_info:
realm: "{{ target_realm }}"
- name: keycloak_realm_users_info — list users in realm
middleware_automation.keycloak.keycloak_realm_users_info:
realm: "{{ target_realm }}"
register: realm_users_info_result
- name: Assert keycloak_realm_users_info returns the molecule user
ansible.builtin.assert:
that:
- realm_users_info_result is not changed
- realm_users_info_result.users | length > 0
- realm_users_info_result.users | selectattr('username', 'equalto', user) | list | length == 1
- name: keycloak_realm — create ephemeral realm
middleware_automation.keycloak.keycloak_realm:
id: "{{ ephemeral_realm }}"
@@ -192,6 +208,62 @@
enabled: true
state: present
- name: keycloak_realm — modify realm attributes without pruning undefined attributes. Step 1 - Introduce custom realm attributes
middleware_automation.keycloak.keycloak_realm:
id: "{{ ephemeral_realm }}"
realm: "{{ ephemeral_realm }}"
enabled: true
state: present
prune_undefined_realm_attributes: false
attributes:
custom-realm-attribute-1: 123
custom-realm-attribute-2: true
register: custom_realm_attributes_result
- name: Assert realm attribute without pruning undefined data result (Step 1)
ansible.builtin.assert:
that:
- custom_realm_attributes_result is changed
- custom_realm_attributes_result.end_state.attributes["custom-realm-attribute-1"] == "123"
- custom_realm_attributes_result.end_state.attributes["custom-realm-attribute-2"] == "true"
- name: keycloak_realm — modify realm attributes without pruning undefined attributes. Step 2 - Verify unchanged custom realm attributes
middleware_automation.keycloak.keycloak_realm:
id: "{{ ephemeral_realm }}"
realm: "{{ ephemeral_realm }}"
enabled: true
state: present
prune_undefined_realm_attributes: false
attributes:
custom-realm-attribute-2: false
register: custom_realm_attributes_result
- name: Assert realm attribute without pruning undefined data result (Step 2)
ansible.builtin.assert:
that:
- custom_realm_attributes_result is changed
- custom_realm_attributes_result.end_state.attributes["custom-realm-attribute-1"] == "123" # should still be 123
- custom_realm_attributes_result.end_state.attributes["custom-realm-attribute-2"] == "false"
- name: keycloak_realm — modify realm attributes with pruning undefined attributes
middleware_automation.keycloak.keycloak_realm:
id: "{{ ephemeral_realm }}"
realm: "{{ ephemeral_realm }}"
enabled: true
state: present
prune_undefined_realm_attributes: true
attributes:
custom-realm-attribute-3: custom-attribute-3
register: custom_realm_attributes_result
- name: Assert realm attribute with pruning undefined data result
ansible.builtin.assert:
that:
- custom_realm_attributes_result is changed
- custom_realm_attributes_result.end_state.attributes["custom-realm-attribute-1"] is not defined
- custom_realm_attributes_result.end_state.attributes["custom-realm-attribute-2"] is not defined
- custom_realm_attributes_result.end_state.attributes["custom-realm-attribute-3"] == "custom-attribute-3"
- name: keycloak_realm_localization — set locale override
middleware_automation.keycloak.keycloak_realm_localization:
parent_id: "{{ target_realm }}"
@@ -536,6 +608,7 @@
auth_realm: "{{ auth_realm }}"
auth_username: "{{ auth_username }}"
auth_password: "{{ auth_password }}"
validate_certs: false
block:
- name: keycloak_authz_custom_policy — remove custom policy if created
middleware_automation.keycloak.keycloak_authz_custom_policy:

View File

@@ -7,7 +7,7 @@ platforms:
pre_build_image: true
privileged: true
command: "/usr/sbin/init"
port_bindings:
exposed_ports:
- "8080/tcp"
- "8443/tcp"
- "8009/tcp"

View File

@@ -7,7 +7,7 @@ platforms:
pre_build_image: true
privileged: true
command: "/usr/sbin/init"
port_bindings:
exposed_ports:
- "8080/tcp"
- "8443/tcp"
- "8009/tcp"

View File

@@ -7,7 +7,7 @@ platforms:
pre_build_image: true
privileged: true
command: "/usr/sbin/init"
port_bindings:
exposed_ports:
- "8080/tcp"
- "8009/tcp"
- "9000/tcp"

View File

@@ -11,7 +11,7 @@ platforms:
- keycloak
networks:
- name: rhbk
port_bindings:
exposed_ports:
- "8080/tcp"
- "8443/tcp"
- "9000/tcp"
@@ -24,7 +24,7 @@ platforms:
- keycloak
networks:
- name: rhbk
port_bindings:
exposed_ports:
- "8080/tcp"
- "8443/tcp"
- "9000/tcp"
@@ -37,7 +37,7 @@ platforms:
- database
networks:
- name: rhbk
port_bindings:
exposed_ports:
- "5432/tcp"
mounts:
- type: bind

View File

@@ -11,7 +11,7 @@ platforms:
- keycloak
networks:
- name: rhbk
port_bindings:
exposed_ports:
- "8080/tcp"
- "8443/tcp"
- "9000/tcp"
@@ -24,7 +24,7 @@ platforms:
- keycloak
networks:
- name: rhbk
port_bindings:
exposed_ports:
- "8080/tcp"
- "8443/tcp"
- "9000/tcp"
@@ -37,7 +37,7 @@ platforms:
- database
networks:
- name: rhbk
port_bindings:
exposed_ports:
- "5432/tcp"
mounts:
- type: bind

View File

@@ -11,7 +11,7 @@ platforms:
- keycloak
networks:
- name: rhbk
port_bindings:
exposed_ports:
- "8080/tcp"
- "8443/tcp"
- "9000/tcp"
@@ -24,7 +24,7 @@ platforms:
- infinispan
networks:
- name: rhbk
port_bindings:
exposed_ports:
- "11222/tcp"
- name: postgres
image: ubuntu/postgres:14-22.04_beta
@@ -35,7 +35,7 @@ platforms:
- database
networks:
- name: rhbk
port_bindings:
exposed_ports:
- "5432/tcp"
mounts:
- type: bind

View File

@@ -11,8 +11,8 @@ platforms:
command: "/usr/sbin/init"
pre_build_image: true
privileged: true
port_bindings:
- 8080:8080
exposed_ports:
- "8080/tcp"
- "9000/tcp"
published_ports:
- 0.0.0.0:8080:8080/TCP

View File

@@ -1108,6 +1108,22 @@ class KeycloakAPI:
except Exception as e:
self.fail_request(e, msg=f"Could not obtain the user for realm {realm} and username {username}: {e}")
def get_realm_users(self, realm: str = "master") -> list[dict[str, t.Any]]:
"""Obtain list of users from the realm
:param realm: realm id
:return: list of user representations
"""
users_url = URL_USERS.format(url=self.baseurl, realm=realm)
try:
return self._request_and_deserialize(users_url, method="GET")
except ValueError as e:
self.module.fail_json(
msg=f"API returned incorrect JSON when trying to obtain the users for realm {realm}: {e}"
)
except Exception as e:
self.fail_request(e, msg=f"Could not obtain the users for realm {realm}: {e}")
def get_service_account_user_by_client_id(self, client_id, realm: str = "master"):
"""Fetch a keycloak service account user within a realm based on its client_id.

View File

@@ -424,6 +424,13 @@ options:
aliases:
- permanentLockout
type: bool
prune_undefined_realm_attributes:
description:
- If True all realm attributes which are not defined in the attributes dict will be deleted
aliases:
- pruneUndefinedRealmAttributes
type: bool
default: true
quick_login_check_milli_seconds:
description:
- The realm quick login check in milliseconds.
@@ -799,6 +806,9 @@ from ansible_collections.middleware_automation.keycloak.plugins.module_utils.ide
get_token,
keycloak_argument_spec,
)
from ansible_collections.middleware_automation.keycloak.plugins.module_utils.identity.keycloak._keycloak_utils import (
merge_settings_without_absent_nulls,
)
def normalise_cr(realmrep):
@@ -920,6 +930,7 @@ def main():
password_policy=dict(type="str", aliases=["passwordPolicy"], no_log=False),
organizations_enabled=dict(type="bool", aliases=["organizationsEnabled"]),
permanent_lockout=dict(type="bool", aliases=["permanentLockout"]),
prune_undefined_realm_attributes=dict(type="bool", aliases=["pruneUndefinedRealmAttributes"], default=True),
quick_login_check_milli_seconds=dict(type="int", aliases=["quickLoginCheckMilliSeconds"]),
refresh_token_max_reuse=dict(type="int", aliases=["refreshTokenMaxReuse"], no_log=False),
registration_allowed=dict(type="bool", aliases=["registrationAllowed"]),
@@ -1031,9 +1042,10 @@ def main():
realm = module.params.get("realm")
state = module.params.get("state")
prune_undefined_realm_attributes = module.params.get("prune_undefined_realm_attributes")
# convert module parameters to realm representation parameters (if they belong in there)
params_to_ignore = list(keycloak_argument_spec().keys()) + ["state"]
params_to_ignore = list(keycloak_argument_spec().keys()) + ["state", "prune_undefined_realm_attributes"]
# Filter and map the parameters names that apply to the role
realm_params = [x for x in module.params if x not in params_to_ignore and module.params.get(x) is not None]
@@ -1049,6 +1061,9 @@ def main():
for realm_param in realm_params:
new_param_value = module.params.get(realm_param)
if camel(realm_param) == "attributes" and not prune_undefined_realm_attributes:
new_param_value = merge_settings_without_absent_nulls(before_realm.get("attributes"), new_param_value)
changeset[camel(realm_param)] = new_param_value
# Prepare the desired values using the existing values (non-existence results in a dict that is save to use as a basis)

View File

@@ -0,0 +1,123 @@
#!/usr/bin/python
# Copyright (c) Ansible project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import (absolute_import, division, print_function)
from __future__ import annotations
__metaclass__ = type
DOCUMENTATION = r"""
module: keycloak_realm_users_info
short_description: Retrieve users from a Keycloak realm using the Keycloak API
# Originally added in community.general 13.1.0
version_added: "3.1.0"
description:
- This module retrieves all users from a specified Keycloak realm using the Keycloak REST API.
- Access to the REST API is performed via OpenID Connect. The user and client used must have the necessary permissions.
- Authentication can be performed either with username/password or with a token.
- The names of module options are snake_case versions of the camelCase ones found in the Keycloak API and its documentation
at U(https://www.keycloak.org/docs-api/latest/rest-api/index.html).
options:
realm:
type: str
description:
- The Keycloak realm from which users should be retrieved.
default: 'master'
extends_documentation_fragment:
- middleware_automation.keycloak.keycloak
- middleware_automation.keycloak.actiongroup_keycloak
- middleware_automation.keycloak.attributes
- middleware_automation.keycloak.attributes.info_module
author:
- Felix Grzelka (@felix-grzelka)
"""
EXAMPLES = r"""
- name: List all users in the "MyCustomRealm" realm using username/password authentication
middleware_automation.keycloak.keycloak_realm_users_info:
realm: MyCustomRealm
auth_client_id: admin-cli
auth_keycloak_url: https://auth.example.com
auth_realm: master
auth_username: USERNAME
auth_password: PASSWORD
delegate_to: localhost
- name: List all users in the "MyCustomRealm" realm using a token
middleware_automation.keycloak.keycloak_realm_users_info:
realm: MyCustomRealm
auth_client_id: admin-cli
auth_keycloak_url: https://auth.example.com
token: TOKEN
delegate_to: localhost
"""
RETURN = r"""
users:
description: List of users in the specified realm.
returned: always
type: list
elements: dict
sample:
- id: "1234-5678-90"
username: "user1"
email: "user1@example.com"
- id: "2345-6789-01"
username: "user2"
email: "user2@example.com"
"""
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.middleware_automation.keycloak.plugins.module_utils.identity.keycloak.keycloak import (
KeycloakAPI,
KeycloakError,
get_token,
keycloak_argument_spec,
)
def main():
argument_spec = keycloak_argument_spec()
meta_args = dict(
realm=dict(default="master"),
)
argument_spec.update(meta_args)
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
required_one_of=(
[["token", "auth_realm", "auth_username", "auth_password", "auth_client_id", "auth_client_secret"]]
),
required_together=([["auth_username", "auth_password"]]),
required_by={"refresh_token": "auth_realm"},
)
result = dict(changed=False, msg="", users="")
# Obtain access token, initialize API
try:
connection_header = get_token(module.params)
except KeycloakError as e:
module.fail_json(msg=str(e))
kc = KeycloakAPI(module, connection_header)
realm = module.params.get("realm")
result["users"] = kc.get_realm_users(realm=realm)
module.exit_json(**result)
if __name__ == "__main__":
main()

View File

@@ -87,7 +87,7 @@
mode: '0644'
url_username: "{{ keycloak_binary_download_user | default(omit) }}"
url_password: "{{ keycloak_binary_download_pass | default(omit) }}"
validate_certs: false
validate_certs: "{{ keycloak_download_validate_certs | default(omit) }}"
delegate_to: localhost
run_once: true
when:
@@ -114,7 +114,7 @@
product_type: DISTRIBUTION
product_version: "{{ sso_version.split('.')[:2] | join('.') }}"
product_category: "{{ sso_product_category }}"
validate_certs: false
validate_certs: "{{ keycloak_download_validate_certs | default(omit) }}"
register: keycloak_rhn_products
no_log: "{{ omit_rhn_output | default(true) }}"
delegate_to: localhost
@@ -135,7 +135,7 @@
client_secret: "{{ rhn_password }}"
product_id: "{{ (keycloak_rhn_filtered_products | first).id }}"
dest: "{{ keycloak_local_path.stat.path }}/{{ keycloak_archive }}"
validate_certs: false
validate_certs: "{{ keycloak_download_validate_certs | default(omit) }}"
no_log: "{{ omit_rhn_output | default(true) }}"
delegate_to: localhost
run_once: true
@@ -145,7 +145,7 @@
url: "{{ keycloak_rhsso_download_url }}"
dest: "{{ keycloak_local_path.stat.path }}/{{ keycloak_archive }}"
mode: '0644'
validate_certs: false
validate_certs: "{{ keycloak_download_validate_certs | default(omit) }}"
delegate_to: localhost
run_once: true
when:

View File

@@ -60,7 +60,7 @@
url: "{{ keycloak_url }}/auth/realms/master/protocol/openid-connect/token"
method: POST
body: "client_id={{ keycloak_auth_client }}&username={{ keycloak_admin_user }}&password={{ keycloak_admin_password }}&grant_type=password"
validate_certs: false
validate_certs: "{{ keycloak_validate_certs | default(omit) }}"
register: keycloak_auth_response
until: keycloak_auth_response.status == 200
retries: 2
@@ -81,6 +81,7 @@
- name: "Wait until service becomes active: {{ keycloak_config.service_name }}"
ansible.builtin.uri:
url: "{{ keycloak_config.health_url }}"
validate_certs: "{{ keycloak_validate_certs | default(omit) }}"
register: keycloak_status
until: keycloak_status.status == 200
retries: 25

View File

@@ -12,6 +12,7 @@
- name: "Wait until service becomes active: {{ keycloak_config.service_name }}"
ansible.builtin.uri:
url: "{{ keycloak_config.health_url }}"
validate_certs: "{{ keycloak_validate_certs | default(omit) }}"
register: keycloak_status
until: keycloak_status.status == 200
delegate_to: "{{ ansible_play_hosts | first }}"

View File

@@ -29,7 +29,7 @@
product_type: BUGFIX
product_version: "{{ sso_version.split('.')[:2] | join('.') }}"
product_category: "{{ sso_product_category }}"
validate_certs: false
validate_certs: "{{ keycloak_download_validate_certs | default(omit) }}"
register: keycloak_rhn_products
no_log: "{{ omit_rhn_output | default(true) }}"
delegate_to: localhost
@@ -75,7 +75,7 @@
client_secret: "{{ rhn_password }}"
product_id: "{{ (keycloak_rhn_filtered_products | sort | last).id }}"
dest: "{{ keycloak_local_path.stat.path }}/{{ keycloak_patch_bundle }}"
validate_certs: false
validate_certs: "{{ keycloak_download_validate_certs | default(omit) }}"
no_log: "{{ omit_rhn_output | default(true) }}"
delegate_to: localhost
run_once: true
@@ -143,6 +143,7 @@
- name: "Wait until service becomes active: {{ keycloak_config.service_name }}"
ansible.builtin.uri:
url: "{{ keycloak_config.health_url }}"
validate_certs: "{{ keycloak_validate_certs | default(omit) }}"
register: keycloak_status
until: keycloak_status.status == 200
retries: 25

View File

@@ -10,6 +10,7 @@
- name: "Wait until service becomes active: {{ keycloak_config.service_name }}"
ansible.builtin.uri:
url: "{{ keycloak_config.health_url }}"
validate_certs: "{{ keycloak_validate_certs | default(omit) }}"
register: keycloak_status
until: keycloak_status.status == 200
retries: "{{ keycloak_service_start_retries }}"

View File

@@ -63,8 +63,8 @@ Role Defaults
|`keycloak_quarkus_additional_env_vars` | List of additional env variables of { key: str, value: str} to be put in sysconfig file, see https://www.keycloak.org/server/all-config | `[]` |
|`keycloak_quarkus_frontend_url`| Deprecated, use `keycloak_quarkus_hostname` instead. | |
|`keycloak_quarkus_admin_url`| Deprecated, use `keycloak_quarkus_hostname_admin` instead. | |
|`keycloak_quarkus_health_check_url`| Full URL (including scheme, host, path, fragment etc.) used for health check endpoint; keycloak_quarkus_hostname will NOT be prepended; helpful when health checks should happen against http port, but keycloak_quarkus_hostname uses https scheme per default | `` |
|`keycloak_quarkus_health_check_url_path`| Path to the health check endpoint; keycloak_quarkus_hostname will be prepended automatically; Note that keycloak_quarkus_health_check_url takes precedence over this property | `realms/master/.well-known/openid-configuration` |
|`keycloak_quarkus_health_check_url`| Full URL (including scheme, host, path, fragment etc.) used for health check endpoint; keycloak_quarkus_hostname will NOT be prepended; helpful when health checks should happen against http port, but keycloak_quarkus_hostname uses https scheme per default | `{{ keycloak_quarkus_hostname ~ keycloak_quarkus_http_relative_path.rstrip('/') ~ '/' ~ keycloak_quarkus_health_check_url_path }}` |
|`keycloak_quarkus_health_check_url_path`| Path to the health check endpoint; keycloak_quarkus_hostname will be prepended automatically; Note that keycloak_quarkus_health_check_url takes precedence over this property | `realms/{{ keycloak_quarkus_master_realm }}/.well-known/openid-configuration` |
|`keycloak_quarkus_proxy_headers`| Parse reverse proxy headers (`forwarded` or `xforwarded`) | `""` |
|`keycloak_quarkus_config_key_store_file`| Path to the configuration key store; only used if `keycloak_quarkus_config_key_store_password` is not empty | `{{ keycloak.home }}/conf/conf_store.p12` if `keycloak_quarkus_config_key_store_password != ''`, else `''` |
|`keycloak_quarkus_config_key_store_password`| Password of the configuration keystore; if non-empty, `keycloak_quarkus_db_pass` will be saved to the keystore at `keycloak_quarkus_config_key_store_file` instead of being written to the configuration file in clear text | `""` |
@@ -90,6 +90,7 @@ Role Defaults
|`keycloak_quarkus_restart_health_check`| Whether to wait for successful health check after restart | `true` |
|`keycloak_quarkus_restart_health_check_delay`| Seconds to let pass before starting healch checks | `10` |
|`keycloak_quarkus_restart_health_check_retries`| Number of attempts for successful health check before failing | `25` |
|`keycloak_quarkus_validate_certs`| Whether to validate SSL certificates when performing health checks | `true` |
|`keycloak_quarkus_restart_pause`| Seconds to wait between restarts in HA strategy | `15` |

View File

@@ -186,6 +186,9 @@ keycloak_quarkus_restart_strategy: restart/serial.yml
keycloak_quarkus_restart_health_check: true
keycloak_quarkus_restart_health_check_delay: 10
keycloak_quarkus_restart_health_check_retries: 25
keycloak_quarkus_validate_certs: true
keycloak_quarkus_health_check_url_path: "realms/{{ keycloak_quarkus_master_realm }}/.well-known/openid-configuration"
keycloak_quarkus_health_check_url: "{{ keycloak_quarkus_hostname ~ keycloak_quarkus_http_relative_path.rstrip('/') ~ '/' ~ keycloak_quarkus_health_check_url_path }}"
keycloak_quarkus_restart_pause: 15
keycloak_quarkus_force_install: false

View File

@@ -115,10 +115,11 @@ argument_specs:
description: "HTTP port"
type: "int"
keycloak_quarkus_health_check_url:
default: "{{ keycloak_quarkus_hostname ~ keycloak_quarkus_http_relative_path.rstrip('/') ~ '/' ~ keycloak_quarkus_health_check_url_path }}"
description: "Full URL (including scheme, host, path, fragment etc.) used for health check endpoint; keycloak_quarkus_hostname will NOT be prepended; helpful when health checks should happen against http port, but keycloak_quarkus_hostname uses https scheme per default"
type: "str"
keycloak_quarkus_health_check_url_path:
default: "realms/master/.well-known/openid-configuration"
default: "realms/{{ keycloak_quarkus_master_realm }}/.well-known/openid-configuration"
description: "Path to the health check endpoint; keycloak_quarkus_hostname will be prepended automatically; Note that keycloak_quarkus_health_check_url takes precedence over this property"
type: "str"
keycloak_quarkus_https_key_file_enabled:
@@ -488,6 +489,10 @@ argument_specs:
description: "Number of attempts for successful health check before failing"
default: 25
type: 'int'
keycloak_quarkus_validate_certs:
description: "Whether to validate SSL certificates when performing health checks"
default: true
type: "bool"
keycloak_quarkus_show_deprecation_warnings:
default: true
description: "Whether or not deprecation warnings should be shown"

View File

@@ -36,7 +36,7 @@
fi
echo {{ item.value | quote }} | keytool -noprompt -importpass -alias {{ item.key | quote }} -keystore {{ keycloak_quarkus_config_key_store_file | quote }} -storepass {{ keycloak_quarkus_config_key_store_password | quote }} -storetype PKCS12
loop: "{{ store_items }}"
loop: "{{ keycloak_quarkus_store_items }}"
no_log: true
become: "{{ keycloak_quarkus_config_store_require_privilege_escalation | default(true) }}"
changed_when: true

View File

@@ -81,7 +81,7 @@
mode: '0640'
url_username: "{{ keycloak_quarkus_binary_download_user | default(omit) }}"
url_password: "{{ keycloak_quarkus_binary_download_pass | default(omit) }}"
validate_certs: false
validate_certs: "{{ keycloak_quarkus_download_validate_certs | default(omit) }}"
delegate_to: localhost
become: false
run_once: true
@@ -110,7 +110,7 @@
product_type: DISTRIBUTION
product_version: "{{ rhbk_version }}"
product_category: "{{ rhbk_product_category }}"
validate_certs: false
validate_certs: "{{ keycloak_quarkus_download_validate_certs | default(omit) }}"
register: keycloak_quarkus_rhn_products
no_log: "{{ omit_rhn_output | default(true) }}"
delegate_to: localhost
@@ -129,7 +129,7 @@
product_id: "{{ (keycloak_quarkus_rhn_filtered_products | first).id }}"
dest: "{{ keycloak_quarkus_local_path.stat.path }}/{{ keycloak.bundle }}"
mode: '0640'
validate_certs: false
validate_certs: "{{ keycloak_quarkus_download_validate_certs | default(omit) }}"
no_log: "{{ omit_rhn_output | default(true) }}"
delegate_to: localhost
run_once: true
@@ -243,9 +243,11 @@
group: "{{ keycloak.service_group }}"
mode: '0640'
checksum: "{{ item.checksum | default(omit) }}"
validate_certs: "{{ keycloak_quarkus_download_validate_certs | default(omit) }}"
become: "{{ keycloak_quarkus_install_require_privilege_escalation | default(true) }}"
loop: "{{ keycloak_quarkus_providers }}"
when: item.url is defined and item.url | length > 0
no_log: "{{ item.maven.password is defined and item.maven.password | length > 0 | default(false) }}"
notify: "{{ ['invalidate keycloak theme cache', 'rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or item.restart else [] }}"
# this requires the `lxml` package to be installed; we redirect this step to localhost such that we do need to install it on the remote hosts
@@ -258,6 +260,7 @@
username: "{{ item.maven.username | default(omit) }}"
password: "{{ item.maven.password | default(omit) }}"
dest: "{{ keycloak_quarkus_local_path.stat.path }}/{{ item.id }}.jar"
validate_certs: "{{ keycloak_quarkus_download_validate_certs | default(omit) }}"
delegate_to: "localhost"
run_once: true
loop: "{{ keycloak_quarkus_providers }}"
@@ -289,6 +292,7 @@
become: "{{ keycloak_quarkus_install_require_privilege_escalation | default(true) }}"
loop: "{{ keycloak_quarkus_providers }}"
when: item.local_path is defined
no_log: "{{ item.maven.password is defined and item.maven.password | length > 0 | default(false) }}"
notify: "{{ ['invalidate keycloak theme cache', 'rebuild keycloak config', 'restart keycloak'] if not item.restart is defined or item.restart else [] }}"
- name: Ensure required folder structure for policies exists
@@ -308,6 +312,7 @@
owner: "{{ keycloak.service_user }}"
group: "{{ keycloak.service_group }}"
mode: '0640'
validate_certs: "{{ keycloak_quarkus_download_validate_certs | default(omit) }}"
become: "{{ keycloak_quarkus_install_require_privilege_escalation | default(true) }}"
loop: "{{ keycloak_quarkus_policies }}"
when: item.url is defined and item.url | length > 0

View File

@@ -123,6 +123,7 @@
fail_msg: >
Providers definition incorrect; `id` and one of `spi`, `url`, `local_path`, or `maven` are mandatory. `key` and `value` are mandatory for each property
loop: "{{ keycloak_quarkus_providers }}"
no_log: "{{ item.maven.password is defined and item.maven.password | length > 0 | default(false) }}"
- name: "Validate policies"
ansible.builtin.assert:

View File

@@ -10,6 +10,7 @@
- name: "Wait until service becomes active: {{ keycloak.service_name }}"
ansible.builtin.uri:
url: "{{ keycloak.health_url }}"
validate_certs: "{{ keycloak_quarkus_validate_certs }}"
register: keycloak_quarkus_status
until: keycloak_quarkus_status.status == 200
retries: "{{ keycloak_quarkus_restart_health_check_retries }}"

View File

@@ -10,6 +10,7 @@
- name: "Wait until service becomes active: {{ keycloak.service_name }}"
ansible.builtin.uri:
url: "{{ keycloak.health_url }}"
validate_certs: "{{ keycloak_quarkus_validate_certs }}"
register: keycloak_quarkus_status
until: keycloak_quarkus_status.status == 200
retries: 25

View File

@@ -4,7 +4,7 @@ keycloak: # noqa var-naming this is an internal dict of interpolated values
config_dir: "{{ keycloak_quarkus_config_dir }}"
bundle: "{{ keycloak_quarkus_archive }}"
service_name: "keycloak"
health_url: "{{ keycloak_quarkus_health_check_url | default(keycloak_quarkus_hostname ~ '/' ~ (keycloak_quarkus_health_check_url_path | default('realms/master/.well-known/openid-configuration'))) }}"
health_url: "{{ keycloak_quarkus_health_check_url }}"
cli_path: "{{ keycloak_quarkus_home }}/bin/kcadm.sh"
service_user: "{{ keycloak_quarkus_service_user }}"
service_group: "{{ keycloak_quarkus_service_group }}"

View File

@@ -4,7 +4,7 @@
url: "{{ keycloak_realm_url }}{{ keycloak_realm_context }}/realms/master/protocol/openid-connect/token"
method: POST
body: "client_id={{ keycloak_realm_auth_client }}&username={{ keycloak_realm_admin_user }}&password={{ keycloak_realm_admin_password }}&grant_type=password"
validate_certs: false
validate_certs: "{{ keycloak_realm_validate_certs | default(omit) }}"
no_log: "{{ keycloak_realm_no_log | default('True') }}"
register: keycloak_realm_auth_response
until: keycloak_realm_auth_response.status == 200
@@ -15,7 +15,7 @@
ansible.builtin.uri:
url: "{{ keycloak_realm_url }}{{ keycloak_realm_context }}/admin/realms/{{ keycloak_realm_realm }}"
method: GET
validate_certs: false
validate_certs: "{{ keycloak_realm_validate_certs | default(omit) }}"
status_code:
- 200
- 404
@@ -29,7 +29,7 @@
url: "{{ keycloak_realm_url }}{{ keycloak_realm_context }}/admin/realms"
method: POST
body: "{{ lookup('template', 'realm.json.j2') }}"
validate_certs: false
validate_certs: "{{ keycloak_realm_validate_certs | default(omit) }}"
body_format: json
headers:
Authorization: "Bearer {{ keycloak_realm_auth_response.json.access_token }}"
@@ -42,6 +42,7 @@
auth_realm: "{{ keycloak_realm_auth_realm }}"
auth_username: "{{ keycloak_realm_admin_user }}"
auth_password: "{{ keycloak_realm_admin_password }}"
validate_certs: "{{ keycloak_realm_validate_certs | default(omit) }}"
realm: "{{ item.realm | default(keycloak_realm_realm) }}"
name: "{{ item.name }}"
state: present
@@ -72,6 +73,7 @@
auth_realm: "{{ keycloak_realm_auth_realm }}"
auth_username: "{{ keycloak_realm_admin_user }}"
auth_password: "{{ keycloak_realm_admin_password }}"
validate_certs: "{{ keycloak_realm_validate_certs | default(omit) }}"
realm: "{{ item.realm | default(keycloak_realm_realm) }}"
default_roles: "{{ item.roles | default(omit) }}"
client_id: "{{ item.client_id | default(omit) }}"

View File

@@ -8,6 +8,7 @@
auth_realm: "{{ keycloak_realm_auth_realm }}"
auth_username: "{{ keycloak_realm_admin_user }}"
auth_password: "{{ keycloak_realm_admin_password }}"
validate_certs: "{{ keycloak_realm_validate_certs | default(omit) }}"
state: present
loop: "{{ client.roles | flatten }}"
no_log: "{{ keycloak_realm_no_log | default('True') }}"

View File

@@ -5,7 +5,7 @@
method: PUT
body:
accessTokenLifespan: 300
validate_certs: false
validate_certs: "{{ keycloak_realm_validate_certs | default(omit) }}"
body_format: json
status_code:
- 200

View File

@@ -2,7 +2,7 @@
- name: "Check if User Already Exists"
ansible.builtin.uri:
url: "{{ keycloak_realm_url }}{{ keycloak_realm_context }}/admin/realms/{{ keycloak_realm_realm }}/users?username={{ user.username }}"
validate_certs: false
validate_certs: "{{ keycloak_realm_validate_certs | default(omit) }}"
headers:
Authorization: "Bearer {{ keycloak_realm_auth_response.json.access_token }}"
register: keycloak_realm_user_search_result
@@ -18,7 +18,7 @@
email: "{{ user.email | default(omit) }}"
firstName: "{{ user.firstName | default(omit) }}"
lastName: "{{ user.lastName | default(omit) }}"
validate_certs: false
validate_certs: "{{ keycloak_realm_validate_certs | default(omit) }}"
body_format: json
headers:
Authorization: "Bearer {{ keycloak_realm_auth_response.json.access_token }}"
@@ -28,7 +28,7 @@
- name: "Get User"
ansible.builtin.uri:
url: "{{ keycloak_realm_url }}{{ keycloak_realm_context }}/admin/realms/{{ keycloak_realm_realm }}/users?username={{ user.username }}"
validate_certs: false
validate_certs: "{{ keycloak_realm_validate_certs | default(omit) }}"
headers:
Authorization: "Bearer {{ keycloak_realm_auth_response.json.access_token }}"
register: keycloak_realm_user
@@ -41,7 +41,7 @@
type: password
temporary: false
value: "{{ user.password }}"
validate_certs: false
validate_certs: "{{ keycloak_realm_validate_certs | default(omit) }}"
body_format: json
status_code:
- 200

View File

@@ -3,7 +3,7 @@
ansible.builtin.uri:
url: "{{ keycloak_realm_url }}{{ keycloak_realm_context }}/admin/realms/{{ client_role.realm | default(keycloak_realm_realm) }}"
method: GET
validate_certs: false
validate_certs: "{{ keycloak_realm_validate_certs | default(omit) }}"
status_code:
- 200
headers:
@@ -17,7 +17,7 @@
default(keycloak_realm_realm) }}/users/{{ (keycloak_realm_user.json | first).id }}/role-mappings/clients/{{ (keycloak_realm_create_client_result.results | \
selectattr('end_state.clientId', 'equalto', client_role.client) | list | first).end_state.id }}/available"
method: GET
validate_certs: false
validate_certs: "{{ keycloak_realm_validate_certs | default(omit) }}"
status_code:
- 200
headers:
@@ -37,7 +37,7 @@
containerId: "{{ item.containerId }}"
name: "{{ item.name }}"
composite: "{{ item.composite }}"
validate_certs: false
validate_certs: "{{ keycloak_realm_validate_certs | default(omit) }}"
body_format: json
headers:
Authorization: "Bearer {{ keycloak_realm_auth_response.json.access_token }}"

View File

@@ -2,8 +2,8 @@
- name: "Get User {{ user.username }}"
ansible.builtin.uri:
url: "{{ keycloak_realm_url }}{{ keycloak_realm_context }}/admin/realms/{{ keycloak_realm_realm }}/users?username={{ user.username }}"
validate_certs: "{{ keycloak_realm_validate_certs | default(omit) }}"
headers:
validate_certs: false
Authorization: "Bearer {{ keycloak_realm_auth_response.json.access_token }}"
register: keycloak_realm_user
@@ -12,7 +12,7 @@
url: "{{ keycloak_realm_url }}{{ keycloak_realm_context }}/realms/master/protocol/openid-connect/token"
method: POST
body: "client_id={{ keycloak_realm_auth_client }}&username={{ keycloak_realm_admin_user }}&password={{ keycloak_realm_admin_password }}&grant_type=password"
validate_certs: false
validate_certs: "{{ keycloak_realm_validate_certs | default(omit) }}"
register: keycloak_realm_auth_response
no_log: "{{ keycloak_realm_no_log | default('True') }}"
until: keycloak_realm_auth_response.status == 200