# Changes to querying AD using LDAPHelper in 8.3

**URL:** <https://forum.inductiveautomation.com/t/changes-to-querying-ad-using-ldaphelper-in-8-3/111518>\
**Category:** Ignition\
**Tags:** ignition83\
**Created:** [October 16, 2025, 2:16pm UTC](https://forum.inductiveautomation.com/t/changes-to-querying-ad-using-ldaphelper-in-8-3/111518 "2025-10-16T14:16:00Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![DNice](https://avatars.discourse-cdn.com/v4/letter/d/858c86/32.png) [@DNice](https://forum.inductiveautomation.com/u/DNice)\
**Post date:** [October 16, 2025, 2:16pm UTC](https://forum.inductiveautomation.com/t/changes-to-querying-ad-using-ldaphelper-in-8-3/111518/1 "2025-10-16T14:16:00Z")

</div>

After upgrading to 8.3, one functionality that broke for me was my LDAP script that enable users to search for users and share resources with them (trends, messages, etc). [This post](https://forum.inductiveautomation.com/t/access-user-source-ldap-instance-for-search/62669/3) helped shape my 8.1 code. The new LDAPHelper requirements are documented in the 8.3 [javadoc](https://files.inductiveautomation.com/sdk/javadoc/ignition83/8.3.0/com/inductiveautomation/ignition/gateway/authentication/impl/LDAPHelper.html#setProfilePassword(com.inductiveautomation.ignition.gateway.secrets.SecretConfig)) - which now requires adding a context to to the LDAPHelper, the password needs to be a [SecreteConfig](https://files.inductiveautomation.com/sdk/javadoc/ignition83/8.3.0/com/inductiveautomation/ignition/gateway/secrets/SecretConfig.Type.html) type, and you need to define getReturnedAttributes function.

I have my 8.1 LDAP script updated to with with 8.3 using a plain text password but ideally this code could be modified to work with the secrets provider. Currently Im getting a type incompatibility between what the secrets library provides; com.inductiveautomation.ignition.common.secrets.PyPlaintext and what LDAPHelper needs: com.inductiveautomation.ignition.gateway.secrets.Plaintext. Any help here would be appreciated 🙂

```python
from com.inductiveautomation.ignition.gateway.authentication.impl import LDAPHelper
from com.inductiveautomation.ignition.common.util import LoggerEx
from com.inductiveautomation.ignition.gateway import IgnitionGateway
from com.inductiveautomation.ignition.gateway.secrets import Plaintext, SecretConfig

class mySearchHandlerClass(LDAPHelper.SearchHandler):
	"""Custom LDAP search handler class used to manage results from LDAP queries in Ignition.
	"""
	def __init__ (self):
	    self.ctx = None
	    self.result = None
	    self.results = []
	    return
	def create(self, ctx, result):
	    self.ctx = ctx
	    self.result = result
	    return self.result  
	def getNoun(self):
	    return 'LDAP'	    
	def getReturnedAttributes(self):
		# New required for 8.3
		# Return None or [] to fetch all, or specify subset like:
		return ["sAMAccountName", "cn", "mail", "givenName", "sn"]
	    
class search(object):
	"""
	LDAP interface class that configures a secure connection and provides helper functions
	to query Active Directory for users, emails, and roles.
	
	Initializes connection settings, credentials, and logging for use with Ignition's LDAPHelper class.
	
	ex: 
		LDAP = LDAP.search() # create LDAP search class instance
		results = LDAP.getUserByName('Test name') # return results given search string
	"""
	builder = LoggerEx.newBuilder()
	search_handler = mySearchHandlerClass()
	logger = builder.build('LDAP_INTEGRATION')
	
	PRIMARY_DOMAIN_CONTROLLER = "SERVERNAME" 
	DC_PORT_PRIMARY = "636" 
	ROLE_ID = "CN"
	ROLE_ATTR = "memberOf"
	USER_ATTR = "sAMAccountName"
	CONTACT_ATTR = ["mail","proxyAddresses","phone"]
	def __init__ (self):

		context = IgnitionGateway.get() # New required for 8.3
		self.instance = LDAPHelper(context, self.logger)
		self.instance.setLdapHost(self.PRIMARY_DOMAIN_CONTROLLER)
		self.instance.setLdapPort(self.DC_PORT_PRIMARY)
		self.instance.setUseSSL(True)			
		self.instance.setProfileUsername("username@Domain.com")
		password = "plain text pass"
		systemEncryptionService = IgnitionGateway.getSystemEncryptionService(context)
		plaintext = Plaintext.fromString(password)
		ciphertext = systemEncryptionService.encryptToJson(plaintext)
		secret_config = SecretConfig.Embedded.embedded(ciphertext)
		self.instance.setProfilePassword(secret_config) # new for 8.3
		self.instance.setReadTimeout(60000)
		self.instance.setPageSize(1000)
	def getReadTimeout(self):
		"""
		Returns the configured LDAP read timeout value.
		
		Returns:
			int: Timeout duration in milliseconds.
		"""
		return self.instance.getReadTimeout()
	    
	def getUserByName(self, searchUser = ''):
		"""
		Searches for a user in Active Directory by matching their name or username.
		Supports inputs in the format of 'lastname,firstname', 'firstname lastname', or a simple username.
		Builds a flexible LDAP query accordingly and executes it.
		
		Args:
			searchUser (str): Input string to search (username or name format).
		
		Returns:
			list: LDAP search results matching the given user input.
		"""
		if ',' in searchUser: #account for "lastname,firstname" format
			names = searchUser.replace(' ','').split(',')
			query = "(&(objectClass=user)(!(objectClass=computer))(&(givenName=" + names[1] + "*)(sn=" + names[0] + "*)))"
		elif ' ' in searchUser: #account for "firstname lastname" format
		    		names = searchUser.split(' ')
	        		query = "(&(objectClass=user)(!(objectClass=computer))(&(givenName=" + names[0] + "*)(sn=" + names[1] + "*)))"
		else: 	
			query = "(&(objectClass=user)(!(objectClass=computer))(|(sAMAccountName=" + searchUser + "*)(givenName=" + searchUser + "*)(sn=" + searchUser + "*)))"
		attrs = []
		base = ['DC=DOMAIN,DC=PATH']
		results = self.instance.search(base, query, attrs,self.search_handler)
			#results2 = self.instance.parseBasePatternString(str(results))
		return results

```

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [October 16, 2025, 5:36pm UTC](https://forum.inductiveautomation.com/t/changes-to-querying-ad-using-ldaphelper-in-8-3/111518/2 "2025-10-16T17:36:06Z")

</div>

Well, you're already well outside the bounds of supported behavior, so what can it hurt...

You can use `FieldUtils` ([as done here](https://forum.inductiveautomation.com/t/easy-way-to-clear-the-serial-buffer/103240/4)) to read the inner `com.inductiveautomation.ignition.gateway.secrets.Plaintext` field from a `PyPlainText`. The field name is `"wrapped"`.

As in, something like this:

```python
        pySecret = system.security.readSecretValue("abc", "xyz")
        javaSecret = FieldUtils.readField(pySecret, "wrapped", True)
        self.instance.setProfilePassword(javaSecret) # new for 8.3

```

---

<div class="post-metadata">

**Author:** ![DNice](https://avatars.discourse-cdn.com/v4/letter/d/858c86/32.png) [@DNice](https://forum.inductiveautomation.com/u/DNice)\
**Post date:** [October 16, 2025, 6:42pm UTC](https://forum.inductiveautomation.com/t/changes-to-querying-ad-using-ldaphelper-in-8-3/111518/3 "2025-10-16T18:42:24Z")

</div>

I used your code but changed the pySecret line to use`system.secrets.readSecretValue()`but still have the same error of:  
TypeError: setProfilePassword(): 1st arg can't be coerced to com.inductiveautomation.ignition.gateway.secrets.SecretConfig

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [October 16, 2025, 9:14pm UTC](https://forum.inductiveautomation.com/t/changes-to-querying-ad-using-ldaphelper-in-8-3/111518/4 "2025-10-16T21:14:17Z")

</div>

Ah, okay, then I think it's something like this:

```python
        pySecret = system.secrets.encrypt(password)
        jsonSecret = TypeUtilities.pyToGson(pySecret)
        javaSecret = SecretConfig.embedded(jsonSecret)
        self.instance.setProfilePassword(javaSecret)

```

(plus `from com.inductiveautomation.ignition.common import TypeUtilities` at the top of the file)

---

<div class="post-metadata">

**Author:** ![DNice](https://avatars.discourse-cdn.com/v4/letter/d/858c86/32.png) [@DNice](https://forum.inductiveautomation.com/u/DNice)\
**Post date:** [October 20, 2025, 11:50am UTC](https://forum.inductiveautomation.com/t/changes-to-querying-ad-using-ldaphelper-in-8-3/111518/5 "2025-10-20T11:50:33Z")

</div>

Not quite. I see why you chose the TypeUtilities but a different but similar error appears about type issue.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [October 20, 2025, 4:09pm UTC](https://forum.inductiveautomation.com/t/changes-to-querying-ad-using-ldaphelper-in-8-3/111518/6 "2025-10-20T16:09:19Z")

</div>

Well, what's the exact error?

---

<div class="post-metadata">

**Author:** ![DNice](https://avatars.discourse-cdn.com/v4/letter/d/858c86/32.png) [@DNice](https://forum.inductiveautomation.com/u/DNice)\
**Post date:** [October 20, 2025, 7:57pm UTC](https://forum.inductiveautomation.com/t/changes-to-querying-ad-using-ldaphelper-in-8-3/111518/7 "2025-10-20T19:57:43Z")

</div>

Failed making field 'java.lang.ref.Reference#referent' accessible; either increase its visibility or write a custom TypeAdapter for its declaring type.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [October 20, 2025, 9:39pm UTC](https://forum.inductiveautomation.com/t/changes-to-querying-ad-using-ldaphelper-in-8-3/111518/8 "2025-10-20T21:39:48Z")

</div>

It's working for me:

```python
def runAction(self, event):
	from com.inductiveautomation.ignition.gateway.secrets import Plaintext, SecretConfig
	from com.inductiveautomation.ignition.common import TypeUtilities
	
	pySecret = system.secrets.encrypt("abc")
	jsonSecret = TypeUtilities.pyToGson(pySecret)
	javaSecret = SecretConfig.embedded(jsonSecret)
	system.perspective.print(javaSecret)

```

---

<div class="post-metadata">

**Author:** ![DNice](https://avatars.discourse-cdn.com/v4/letter/d/858c86/32.png) [@DNice](https://forum.inductiveautomation.com/u/DNice)\
**Post date:** [October 20, 2025, 11:37pm UTC](https://forum.inductiveautomation.com/t/changes-to-querying-ad-using-ldaphelper-in-8-3/111518/9 "2025-10-20T23:37:03Z")

</div>

Ahh yes, you are passing in a standard string as the password. My original script already does this 🙂 . Per the description of the TypeUtilities it suggest it might be able to convert py-types but it was not successful in converting the return from the gateway.secrets com.inductiveautomation.ignition.common.secrets.PyPlaintext type to what the LDAPHelper needs: com.inductiveautomation.ignition.gateway.secrets.Plaintext.

Thats the dream for me.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [October 21, 2025, 6:48pm UTC](https://forum.inductiveautomation.com/t/changes-to-querying-ad-using-ldaphelper-in-8-3/111518/10 "2025-10-21T18:48:16Z")

</div>

I'm confused or you are.  
LDAPHelper accepts a `SecretConfig`, which it _internally_ reads as a `Plaintext`. You don't need to pass one in to LDAPHelper.

EDIT:  
Okay, I was confused, but now I understand.

You don't need `system.secrets` at all, because ultimately you want to create a `SecretConfig` that's a _reference_ to an existing secret manager, instead of an embedded one. `system.secrets` is all about consuming secrets directly within scripting, it's not designed for "passing" secrets in to other (Java) APIs.

But the good news is what you want is actually extremely simple:

```python
javaSecret = SecretConfig.referenced(provider, secretName)
self.instance.setProfilePassword(javaSecret)

```

---

<div class="post-metadata">

**Author:** ![Jesse\_Van\_Hill](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/jesse_van_hill/32/56876_2.png) [@Jesse\_Van\_Hill](https://forum.inductiveautomation.com/u/Jesse_Van_Hill)\
**Post date:** [October 21, 2025, 8:10pm UTC](https://forum.inductiveautomation.com/t/changes-to-querying-ad-using-ldaphelper-in-8-3/111518/11 "2025-10-21T20:10:19Z")

</div>

Paul’s response is the answer. Use the internal secret provider to store your LDAP bind password. This will help reduce password sprawl from your scripts. For instance, if your secret provider name is `MySecretProvider`, and the LDAP password is stored as the named secret `LDAP_PASS` the following could be used:

```python
self.instance.setProfilePassword(SecretConfig.referenced("MySecretProvider", "LDAP_PASS"))

```

I changed the `setProfilePassword` method in 8.3 to take a `SecretConfig` instance to keep the password from being passed around unprotected as long as possible.

---

<div class="post-metadata">

**Author:** ![DNice](https://avatars.discourse-cdn.com/v4/letter/d/858c86/32.png) [@DNice](https://forum.inductiveautomation.com/u/DNice)\
**Post date:** [October 22, 2025, 2:54pm UTC](https://forum.inductiveautomation.com/t/changes-to-querying-ad-using-ldaphelper-in-8-3/111518/12 "2025-10-22T14:54:52Z")

</div>

Thats it! Thank you guys for the help. No more plain text password 😃
