From 822e2654d4c359af2a5fa99ee70e27904c3dc93e Mon Sep 17 00:00:00 2001 From: Gianluca Salvo Date: Sat, 24 Feb 2024 16:50:04 +0100 Subject: [PATCH 1/7] Added Jinja2 templating support --- changelogs/fragments/templating_support.yml | 2 ++ plugins/inventory/ldap.py | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 changelogs/fragments/templating_support.yml diff --git a/changelogs/fragments/templating_support.yml b/changelogs/fragments/templating_support.yml new file mode 100644 index 0000000..86666c7 --- /dev/null +++ b/changelogs/fragments/templating_support.yml @@ -0,0 +1,2 @@ +minor_changes: + - Added support for Jinja2 templating in ldap inventory. Supported fields are 'username', 'password', 'server', 'tls_mode', 'auth_protocol', 'ca_cert', 'cert_validation', 'certificate', 'certificate_key', 'certificate_key', 'encrypt'. diff --git a/plugins/inventory/ldap.py b/plugins/inventory/ldap.py index c5b34c8..6bbca97 100644 --- a/plugins/inventory/ldap.py +++ b/plugins/inventory/ldap.py @@ -311,8 +311,14 @@ def parse( custom_attributes["inventory_hostname"] = { "inventory_hostname": inventory_hostname } - connection_options = self.get_options() + template_fields = ['username', 'password', 'server', 'tls_mode', 'auth_protocol', 'ca_cert', 'cert_validation', 'certificate', 'certificate_key', 'certificate_key', 'encrypt'] + for t in template_fields: + if t in connection_options.keys(): + if self.templar.is_template(connection_options[t]): + self.display.vvv("detemplating option %s" % t) + connection_options[t] = self.templar.template(variable=connection_options[t], disable_lookups=False) + laps_decryptor = LAPSDecryptor(**connection_options) with create_ldap_connection(**connection_options) as client: schema = LDAPSchema.load_schema(client) From 0dccf9c3419419aba07afa219c5710ec93ac506b Mon Sep 17 00:00:00 2001 From: Gianluca Salvo Date: Sat, 24 Feb 2024 17:29:50 +0100 Subject: [PATCH 2/7] linting --- plugins/inventory/ldap.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/inventory/ldap.py b/plugins/inventory/ldap.py index 6bbca97..ca76f09 100644 --- a/plugins/inventory/ldap.py +++ b/plugins/inventory/ldap.py @@ -312,7 +312,8 @@ def parse( "inventory_hostname": inventory_hostname } connection_options = self.get_options() - template_fields = ['username', 'password', 'server', 'tls_mode', 'auth_protocol', 'ca_cert', 'cert_validation', 'certificate', 'certificate_key', 'certificate_key', 'encrypt'] + template_fields = ['username', 'password', 'server', 'tls_mode', 'auth_protocol', 'ca_cert', + 'cert_validation', 'certificate', 'certificate_key', 'certificate_key', 'encrypt'] for t in template_fields: if t in connection_options.keys(): if self.templar.is_template(connection_options[t]): From 9bfef637b3315cc2a50a7c8d71bfd299a0806c01 Mon Sep 17 00:00:00 2001 From: Gianluca Salvo Date: Sat, 24 Feb 2024 17:45:12 +0100 Subject: [PATCH 3/7] Linting fragment --- changelogs/fragments/templating_support.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/changelogs/fragments/templating_support.yml b/changelogs/fragments/templating_support.yml index 86666c7..ebbf139 100644 --- a/changelogs/fragments/templating_support.yml +++ b/changelogs/fragments/templating_support.yml @@ -1,2 +1,5 @@ minor_changes: - - Added support for Jinja2 templating in ldap inventory. Supported fields are 'username', 'password', 'server', 'tls_mode', 'auth_protocol', 'ca_cert', 'cert_validation', 'certificate', 'certificate_key', 'certificate_key', 'encrypt'. + - >- + Added support for Jinja2 templating in ldap inventory. + Supported fields are 'username', 'password', 'server', 'tls_mode', 'auth_protocol', 'ca_cert', 'cert_validation', 'certificate', + 'certificate_key', 'certificate_key', 'encrypt'. From fb2980b73042752c91c70f0c3d158af7047c1dba Mon Sep 17 00:00:00 2001 From: Gianluca Salvo Date: Sat, 24 Feb 2024 17:48:36 +0100 Subject: [PATCH 4/7] Shortened the line --- changelogs/fragments/templating_support.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/changelogs/fragments/templating_support.yml b/changelogs/fragments/templating_support.yml index ebbf139..4e11ca1 100644 --- a/changelogs/fragments/templating_support.yml +++ b/changelogs/fragments/templating_support.yml @@ -1,5 +1,3 @@ minor_changes: - >- Added support for Jinja2 templating in ldap inventory. - Supported fields are 'username', 'password', 'server', 'tls_mode', 'auth_protocol', 'ca_cert', 'cert_validation', 'certificate', - 'certificate_key', 'certificate_key', 'encrypt'. From 2ea0460bd3decb99a53529df2adaf097711cdbfd Mon Sep 17 00:00:00 2001 From: Gianluca Salvo Date: Sat, 24 Feb 2024 17:53:15 +0100 Subject: [PATCH 5/7] Trailing spacee --- changelogs/fragments/templating_support.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/fragments/templating_support.yml b/changelogs/fragments/templating_support.yml index 4e11ca1..8707554 100644 --- a/changelogs/fragments/templating_support.yml +++ b/changelogs/fragments/templating_support.yml @@ -1,3 +1,3 @@ minor_changes: - >- - Added support for Jinja2 templating in ldap inventory. + Added support for Jinja2 templating in ldap inventory. From bbd6e9c49b8f0bde011add64ba7972707e6df916 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Wed, 20 Mar 2024 05:45:43 +1000 Subject: [PATCH 6/7] Minor changes and extend docs --- plugins/doc_fragments/ldap_connection.py | 13 ++++++++ plugins/inventory/ldap.py | 39 +++++++++++++++++++----- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/plugins/doc_fragments/ldap_connection.py b/plugins/doc_fragments/ldap_connection.py index 9300881..327c1ba 100644 --- a/plugins/doc_fragments/ldap_connection.py +++ b/plugins/doc_fragments/ldap_connection.py @@ -31,6 +31,7 @@ class ModuleDocFragment: installed. - See R(LDAP authentication,ansible_collections.microsoft.ad.docsite.guide_ldap_connection.authentication) for more information. + - This option can be set using a Jinja2 template value. choices: - simple - certificate @@ -47,6 +48,7 @@ class ModuleDocFragment: certificate validation. - If omitted, the default CA store used for validation is dependent on the current Python settings. + - This option can be set using a Jinja2 template value. type: str env: - name: MICROSOFT_AD_LDAP_CA_CERT @@ -60,6 +62,7 @@ class ModuleDocFragment: hostname checks performed by TLS. - See R(Certificate validation,ansible_collections.microsoft.ad.docsite.guide_ldap_connection.cert_validation) for more information. + - This option can be set using a Jinja2 template value. choices: - always - ignore @@ -80,6 +83,7 @@ class ModuleDocFragment: - Use I(certificate_key) if the certificate specified does not contain the key. - Use I(certificate_password) if the key is encrypted with a password. + - This option can be set using a Jinja2 template value. type: str env: - name: MICROSOFT_AD_LDAP_CERTIFICATE @@ -89,6 +93,7 @@ class ModuleDocFragment: - The value can either be a path to a file containing the key in the PEM or DER encoded form, or it can be the string of a PEM encoded key. - Use I(certificate_password) if the key is encrypted with a password. + - This option can be set using a Jinja2 template value. type: str env: - name: MICROSOFT_AD_LDAP_CERTIFICATE_KEY @@ -96,6 +101,7 @@ class ModuleDocFragment: description: - The password used to decrypt the certificate key specified by I(certificate) or I(certificate_key). + - This option can be set using a Jinja2 template value. type: str env: - name: MICROSOFT_AD_LDAP_CERTIFICATE_PASSWORD @@ -103,6 +109,7 @@ class ModuleDocFragment: description: - The timeout in seconds to wait until the connection is established before failing. + - This option can be set using a Jinja2 template value. default: 5 type: int env: @@ -117,6 +124,7 @@ class ModuleDocFragment: - If using C(auth_protocol=simple) over LDAP without TLS then this must be set to C(False). As no encryption is used, all traffic will be in plaintext and should be avoided. + - This option can be set using a Jinja2 template value. default: true type: bool env: @@ -129,6 +137,7 @@ class ModuleDocFragment: - If I(auth_protocol) is C(negotiate), C(kerberos), or C(ntlm) and no password is specified, it will attempt to use the local cached credential specified by I(username) if available. + - This option can be set using a Jinja2 template value. type: str env: - name: MICROSOFT_AD_LDAP_PASSWORD @@ -137,6 +146,7 @@ class ModuleDocFragment: - The LDAP port to use for the connection. - Port 389 is used for LDAP and port 686 is used for LDAPS. - Defaults to port C(636) if C(tls_mode=ldaps) otherwise C(389). + - This option can be set using a Jinja2 template value. type: int env: - name: MICROSOFT_AD_LDAP_PORT @@ -147,6 +157,7 @@ class ModuleDocFragment: C(default_realm) setting and with an SRV DNS lookup. - See R(Server lookup,ansible_collections.microsoft.ad.docsite.guide_ldap_connection.server_lookup) for more information. + - This option can be set using a Jinja2 template value. type: str env: - name: MICROSOFT_AD_LDAP_SERVER @@ -159,6 +170,7 @@ class ModuleDocFragment: operation before the authentication bind. - It is recommended to use C(ldaps) over C(start_tls) if TLS is going to be used. + - This option can be set using a Jinja2 template value. choices: - ldaps - start_tls @@ -173,6 +185,7 @@ class ModuleDocFragment: - If I(auth_protocol) is C(negotiate), C(kerberos), or C(ntlm) and no username is specified, it will attempt to use the local cached credential if available, for example one retrieved by C(kinit). + - This option can be set using a Jinja2 template value. type: str env: - name: MICROSOFT_AD_LDAP_USERNAME diff --git a/plugins/inventory/ldap.py b/plugins/inventory/ldap.py index ca76f09..0a329da 100644 --- a/plugins/inventory/ldap.py +++ b/plugins/inventory/ldap.py @@ -84,6 +84,9 @@ information. - This plugin is a tech preview and the module options are subject to change based on feedback received. +- Unless specified otherwise in the option description, the value specified in + the config file is used as is. Only the LDAP connection options allow using + a Jinja2 template. extends_documentation_fragment: - constructed - microsoft.ad.ldap_connection @@ -125,6 +128,11 @@ tls_mode: ldaps ca_cert: /home/user/certs/ldap.pem +# The username and password can be retrieved using a template with a lookup. +# Other connection options can also be set this way, the option description +# tells you whether it can be set to a template. +username: '{{ lookup("ansible.builtin.env", "LDAP_USERNAME") }}' +password: '{{ lookup("ansible.builtin.env", "LDAP_PASSWORD") }}' ############################################## # Search Options # @@ -312,13 +320,30 @@ def parse( "inventory_hostname": inventory_hostname } connection_options = self.get_options() - template_fields = ['username', 'password', 'server', 'tls_mode', 'auth_protocol', 'ca_cert', - 'cert_validation', 'certificate', 'certificate_key', 'certificate_key', 'encrypt'] - for t in template_fields: - if t in connection_options.keys(): - if self.templar.is_template(connection_options[t]): - self.display.vvv("detemplating option %s" % t) - connection_options[t] = self.templar.template(variable=connection_options[t], disable_lookups=False) + + # These options are in ../doc_fragments/ldap_connection.py + template_fields = { + 'auth_protocol', + 'ca_cert', + 'cert_validation', + 'certificate', + 'certificate_key', + 'certificate_password', + 'connection_timeout', + 'encrypt', + 'password', + 'port', + 'server', + 'tls_mode', + 'username', + } + for option_name, option_value in connection_options.items(): + if option_name in template_fields and self.templar.is_template(option_value): + self.display.vvv(f"Templating option {option_name}") + connection_options[option_name] = self.templar.template( + variable=option_value, + disable_lookups=False, + ) laps_decryptor = LAPSDecryptor(**connection_options) with create_ldap_connection(**connection_options) as client: From df21c7df3918b85018901bfd34d2ca5048a6be17 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Wed, 20 Mar 2024 06:14:03 +1000 Subject: [PATCH 7/7] Add test --- .../inventory_ldap/roles/test/tasks/main.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/integration/targets/inventory_ldap/roles/test/tasks/main.yml b/tests/integration/targets/inventory_ldap/roles/test/tasks/main.yml index a5ebcd4..7e0bb2e 100644 --- a/tests/integration/targets/inventory_ldap/roles/test/tasks/main.yml +++ b/tests/integration/targets/inventory_ldap/roles/test/tasks/main.yml @@ -93,6 +93,23 @@ assert: that: *default-assertion +- import_tasks: invoke.yml + vars: + scenario: LDAP through lookup templates + inventory: + plugin: microsoft.ad.ldap + server: !unsafe '{{ lookup("ansible.builtin.env", "LDAP_SERVER") }}' + username: !unsafe '{{ lookup("ansible.builtin.env", "LDAP_USERNAME") }}' + password: !unsafe '{{ lookup("ansible.builtin.env", "LDAP_PASSWORD") }}' + environment: + LDAP_SERVER: '{{ ldap_server }}' + LDAP_USERNAME: '{{ ldap_user }}' + LDAP_PASSWORD: '{{ ldap_pass }}' + +- name: assert LDAP through lookup templates + assert: + that: *default-assertion + - import_tasks: invoke.yml vars: scenario: LDAPS