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:
-
ManagedAttribute
objects (groups, permissions, drives, etc.) -
Application
orIdentity
objects -
Any other persisted
SailPointObject
type 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
/valueProperty
attributes.
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 theAllowedValuesDefinition
script
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
sysDescriptions
map (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.