The Problem
When building custom forms in SailPoint IdentityIQ (IIQ), it’s quite common to use dynamic dropdown fields populated by objects from the identity warehouse — for example:
-
ManagedAttributeobjects (groups, permissions, drives, etc.) -
ApplicationorIdentityobjects -
Any other persisted
SailPointObjecttype returned from a script
However, there’s a limitation in the form renderer:
Fields typed as
ManagedAttribute(or any object type) always display their defaultdisplayName/name, and do not respectdisplayProperty/valuePropertyattributes.
This becomes a problem when:
-
You need to show a localized label (
sysDescriptions.de_DE) -
You want to display a description instead of the internal name
-
You want to combine multiple attributes in the label (like
name (code))
So the question is:
How can we create a dropdown that binds to a
ManagedAttribute(or other object) but shows a custom label?
The Concept — Multi-Lingual Allowed-Values Lists
The supported solution is to not use type="ManagedAttribute", but instead:
-
Set
type="string" -
Return a list of pairs
[value, label]from theAllowedValuesDefinitionscript
This is a known pattern in IIQ called multi-lingual allowed-values lists:
result = []
for each object in queryResult:
display = object.description
or object.sysDescriptions['de_DE']
or object.displayName
or object.name
or object.id
subList = [ object.id, display ]
result.add(subList)
return result
-
The first element is the value saved by the form (e.g.
ManagedAttribute.id) -
The second element is the label shown to the user in the dropdown
You can use this approach for any object type (ManagedAttribute, Identity, Application, Role, etc.)
Full Working Example
Below is a real-world snippet:
Dynamic dropdown listing all ManagedAttribute objects classified with both GDrive and the selected location.
It shows description if present, else sysDescriptions.de_DE, else fallback to displayName.
<Field
name="gDriveItem"
displayName="Verfügbare G-Laufwerke"
type="string"
dynamic="true"
postBack="true"
required="true"
dependencies="selectedLocation">
<AllowedValuesDefinition>
<Script>
<Source>
import sailpoint.object.*;
import sailpoint.object.Filter;
import sailpoint.object.QueryOptions;
import org.apache.log4j.Logger;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.Collections;
Logger log = Logger.getLogger("de.ndr.GDrivePermissionsByItem");
if (selectedLocation == null || selectedLocation.trim().isEmpty()) {
return Collections.EMPTY_LIST;
}
QueryOptions qo = new QueryOptions();
Filter mainFilter = Filter.collectionCondition(
"classifications",
Filter.and(
Filter.eq("classification.name", "GDrive"),
Filter.eq("classification.name", selectedLocation)
)
);
qo.addFilter(mainFilter);
List gDrives = context.getObjects(ManagedAttribute.class, qo);
if (gDrives == null || gDrives.isEmpty()) {
return Collections.EMPTY_LIST;
}
List result = new ArrayList();
for (Object o : gDrives) {
ManagedAttribute ma = (ManagedAttribute) o;
String display = null;
Object desc = ma.getAttribute("description");
if (desc != null) display = String.valueOf(desc);
if (display == null) {
Map sys = (Map) ma.getAttribute("sysDescriptions");
if (sys != null && sys.get("de_DE") != null) {
display = String.valueOf(sys.get("de_DE"));
}
}
if (display == null) {
display = (ma.getDisplayName() != null) ? ma.getDisplayName()
: (ma.getName() != null ? ma.getName() : ma.getId());
}
List subList = new ArrayList();
subList.add(ma.getId()); // stored value
subList.add(display); // visible label
result.add(subList);
}
return result.isEmpty() ? Collections.EMPTY_LIST : result;
</Source>
</Script>
</AllowedValuesDefinition>
</Field>
How to Use the Selected Value
Because the field type is string, the form will return the ManagedAttribute.id.
If you need the full object later (e.g. in workflow logic), just resolve it by ID:
ManagedAttribute ma = context.getObjectById(ManagedAttribute.class, form.getField("gDriveItem"));
Benefits of This Pattern
-
Works with any object type (not just ManagedAttribute) -
Full control over label format, language, fallbacks -
Keeps form clean and dynamic (postBack,dependencies, etc.) -
Easy to extend (add counts, concatenated metadata, …)
Pro Tips
-
You can extend the label to show multiple fields, e.g.
ma.getName() + " - " + ma.getAttribute("costCenter"). -
For multi-lingual support, use the
sysDescriptionsmap (it contains translations). -
If you want to support multiple selections, just add
multi="true"to the field and handle aList<String>of IDs.
Summary
You cannot override labels for
type="ManagedAttribute"fields in IIQ forms.
But by switching totype="string"and returning[id, label]pairs,
you get full control over the display text while preserving clean data binding.
This pattern is simple, reusable, and works across all object types — and it’s one of the cleanest ways to build dynamic, user-friendly dropdowns in IdentityIQ forms.