Using Ansible to set mappers when setting up LDAP federation
TL;DR
Look up the Keycloak's API JavaDoc, search for "MapperConfig" to find your mapper's config class, lowercase all fields and replace the underscores by commas (e.g. LDAP_FULL_NAME_ATTRIBUTE becomes ldap.full.name.attribute). For the group mapper the two following classes are needed: GroupMapperConfig and LdapMapCommonGroupMapperConfig. This is because GroupMapperConfig inherits from LdapMapCommonGroupMapperConfig.
Here is a full working example:
community.general.keycloak_user_federation:
auth_keycloak_url: "https://keycloak.example.com"
auth_client_id: admin-cli
auth_realm: master
auth_username: admin
auth_password: not_a_great_password
realm: master
name: LDAPFederation
provider_id: ldap
config:
bindCredential: the_ldap_password
bindDn: uid=keycloak,cn=accounts, dc=ldap,dc=example,dc=com
connectionUrl: ldap://ldap.example.com
editMode: "READ_ONLY"
usernameLDAPAttribute: uid
userObjectClasses: "inetuser"
usersDn: "cn=users,cn=accounts,dc=ldap,dc=example,dc=com"
vendor: other
mappers:
- name: group
providerId: group-ldap-mapper
providerType: "org.keycloak.storage.ldap.mappers.LDAPStorageMapper"
config:
memberof.ldap.attribute: memberOf
membership.attribute.type: DN
membership.ldap.attribute: member
membership.user.ldap.attribute: uid
mode: READ_ONLY
user.roles.retrieve.strategy: GET_GROUPS_BY_MEMBEROF_ATTRIBUTE
default.ldap.groups.path: /
groups.dn: cn=groups,cn=accounts,dc=ldap,dc=example,dc=com
As of today, if you take a look at the Ansible documentation for the community.general.keycloak_user_federation module, you will find a rather elusive description of how mappers should be defined. In particular, the config
field's description says this:
A list of dicts defining mappers associated with this Identity Provider.
Not a great start. The documentation does not tell you how to find how to set fields. In search for answers, I looked at the examples and found this:
- name: Create LDAP user federation
community.general.keycloak_user_federation:
auth_keycloak_url: https://keycloak.example.com/auth
auth_realm: master
auth_username: admin
auth_password: password
realm: my-realm
name: my-ldap
...
mappers:
- name: "full name"
providerId: "full-name-ldap-mapper"
providerType: "org.keycloak.storage.ldap.mappers.LDAPStorageMapper"
config:
ldap.full.name.attribute: cn
read.only: true
write.only: false
This describes how to configure a full name mapper, one of the mappers that Keycloak supports and probably one of the most straightforward. The example works, but it does not really explain where the config fields come from. The documentation does say this though:
The names of module options are snake_cased versions of the camelCase ones found in the Keycloak API and its documentation at https://www.keycloak.org/docs-api/8.0/rest-api/index.html
So I went to check the Keycloak's API JavaDoc and found the specific page that pertains to the LDAP group mapper.
The getConfig
method tells us that the config type is CommonLDAPGroupMapperConfig, and there bingo! The fields match some of the ones we have in the UI. But not all. Fortunately, a quick look at the direct known subclasses tells us that the GroupMapperConfig inherits from the GroupMapperClass. With the fields from this new class in addition, we have everything we need.
A look at the FullNameLDAPStorageMapper class tells us how to convert the attributes from the JavaDoc to something that the Ansible module will process. LDAP_FULL_NAME_ATTRIBUTE becomes ldap.full.name.attribute, READ_ONLY becomes read.only, etc.
This is not camelCase, so the above quotation does not apply and I don't think this is actually explained anywhere in the documentation.
The process I described can be applied for any group mapper, but here is a working example for the group mapper.
- name: Create LDAP user federation
community.general.keycloak_user_federation:
auth_keycloak_url: https://keycloak.example.com/auth
auth_realm: master
auth_username: admin
auth_password: password
realm: my-realm
name: my-ldap
...
mappers:
- name: "full name"
providerId: "full-name-ldap-mapper"
providerType: "org.keycloak.storage.ldap.mappers.LDAPStorageMapper"
config:
ldap.full.name.attribute: cn
read.only: true
write.only: false