Complex Transform - First Valid or Conditional

Hey everyone!

We recently purchased SailPoint ISC, and I have started working on some of the transformations we will need to properly build out our identity profiles. I have had success with some of the easier transforms like lookups, but I am having some difficulty with a transform that requires slightly more complex logic.

I have 2 attributes coming from a source called NERM_msft and NERM_login, which are being mapped to identity attributes respectively. I need to create a transform which looks at the values of those 2 attributes, either coming from the source, or once on the identity (Not sure if using identity attributes or source attributes is easier for this) and creates a new identity attribute based on which values are found in the NERM_msft and NERM_login.

Table of conditional statements I want to resolve to

DeviceLogin IG + MSFT selected → Premium :white_check_mark:
DeviceLogin IG + no MSFT → Basic :white_check_mark:
DeviceLogin Client + MSFT Suite → PremiumAlt :white_check_mark:
DeviceLogin Client + Teams + Outlook → Mobile Comms :white_check_mark:
DeviceLogin Client + only Teams → Mobile Comms :white_check_mark:
DeviceLogin Client + only Outlook → Mail Only :white_check_mark:
Else fallback → ID Only :white_check_mark:

I am struggling to decide which would be the best type to perform something like this. Right now I was attempting to use the conditional type, with something like this:

{
  "name": "EmployeeSubtype_Expression",
  "type": "conditional",
  "attributes": {
    "expression": "($nermDevicelogin eq 'IG') and (($nermMsft co 'Teams') or ($nermMsft co 'MSFT Suite') or ($nermMsft co 'Outlook (Email)'))",
    "positiveCondition": "$premium",
    "negativeCondition": {
      "type": "conditional",
      "attributes": {
        "expression": "($nermDevicelogin eq 'IG') and (($nermMsft eq '') or ($nermMsft eq null))",
        "positiveCondition": "$basic",
        "negativeCondition": {
          "type": "conditional",
          "attributes": {
            "expression": "($nermDevicelogin eq 'Client') and ($nermMsft co 'MSFT Suite')",
            "positiveCondition": "$premiumAlt",
            "negativeCondition": {
              "type": "conditional",
              "attributes": {
                "expression": "($nermDevicelogin eq 'Client') and (($nermMsft co 'Teams') and ($nermMsft co 'Outlook (Email)'))",
                "positiveCondition": "$mobileComms",
                "negativeCondition": {
                  "type": "conditional",
                  "attributes": {
                    "expression": "($nermDevicelogin eq 'Client') and ($nermMsft co 'Teams')",
                    "positiveCondition": "$mobileComms",
                    "negativeCondition": {
                      "type": "conditional",
                      "attributes": {
                        "expression": "($nermDevicelogin eq 'Client') and ($nermMsft co 'Outlook (Email)')",
                        "positiveCondition": "$mailOnly",
                        "negativeCondition": "$idOnly"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "nermDevicelogin": {
      "type": "identityAttribute",
      "attributes": {
        "name": "nermDevicelogin"
      }
    },
    "nermMsft": {
      "type": "identityAttribute",
      "attributes": {
        "name": "nermMsft"
      }
    },
    "premium": {
      "type": "static",
      "attributes": {
        "value": "Premium"
      }
    },
    "basic": {
      "type": "static",
      "attributes": {
        "value": "Basic"
      }
    },
    "premiumAlt": {
      "type": "static",
      "attributes": {
        "value": "PremiumAlt"
      }
    },
    "mobileComms": {
      "type": "static",
      "attributes": {
        "value": "Mobile Communications"
      }
    },
    "mailOnly": {
      "type": "static",
      "attributes": {
        "value": "Mail Only"
      }
    },
    "idOnly": {
      "type": "static",
      "attributes": {
        "value": "ID Only"
      }
    }
  }
}

However I am not getting the values I expect from the conditions. I have a feeling something in my code is being written wrong but I am not completely sure what it is. Above code was written with help from CHATGPT so I am sure something is amiss. Any help that can be given or guidance would be greatly appreciated!

Thank you!

Welcome to the community!!

A lot of this depends on your preference.

My preference is to use a static transform and velocity script to handle complex conditions.

Why? The JSON structure in representing the conditions is good, and easier to read then Apache Velocity Script. But if you need to make a modification you have to fumble a little bit with the JSON structure. Where with a static transform your modification is on “one line” in the transform and the structure doesn’t need to be considered, unless you are needing to modify or add additional attributes from various sources. You copy that one line to an editor, format it to a more readable structure and include a much more robust statements as needed to transition your data. I haven’t worked with documenting the logic with velocity script, but the transforms currently don’t have a way to include comments as to what the transform is doing. I believe Velocity script will allow comments in the logic.

Example transform with Velocity Script. Note, this is a rough draft, I haven’t double checked it for structure or syntax.

{
  "name": "EmployeeSubtype_Expression",
  "type": "static",
  "attributes": {
    "nermDevicelogin": {
      "type": "identityAttribute",
      "attributes": {
        "name": "nermDevicelogin"
      }
    },
    "nermMsft": {
      "type": "identityAttribute",
      "attributes": {
        "name": "nermMsft"
      }
    },
    "premium": {
      "type": "static",
      "attributes": {
        "value": "Premium"
      }
    },
    "basic": {
      "type": "static",
      "attributes": {
        "value": "Basic"
      }
    },
    "premiumAlt": {
      "type": "static",
      "attributes": {
        "value": "PremiumAlt"
      }
    },
    "mobileComms": {
      "type": "static",
      "attributes": {
        "value": "Mobile Communications"
      }
    },
    "mailOnly": {
      "type": "static",
      "attributes": {
        "value": "Mail Only"
      }
    },
    "idOnly": {
      "type": "static",
      "attributes": {
        "value": "ID Only"
      }
    },
	"value": "#if($nermDevicelogin && $nermMsft)#if($nermDevicelogin == "IG" && ($nermMsft.contains("Teams") || $nermMsft.contains("MSFT Suite") || $nermMsft.contains("Outlook (Email)")))Premium#elseif($nermDevicelogin == "IG" && ($nermMsft == "" || $nermMsft == "null"))Basic#elseif($nermDevicelogin == "Client" && $nermMsft.contains("MSFT Suite"))PremiumAlt#elseif($nermDevicelogin == "Client" && $nermMsft.contains("Teams") && $nermMsft.contains("Outlook (Email)"))Mobile Communications#elseif($nermDevicelogin == "Client" && $nermMsft.contains("Teams"))Mobile Communications#elseif($nermDevicelogin == "Client" && $nermMsft.contains("Outlook (Email)"))Mail Only#elseID Only#end#elseID Only#end"
  }
}

I also prefer this method when troubleshooting complex logic. A velocity script allows you to display values being evaluated to help determine where your transform is failing. The display would be showing in the Identity Profile Preview.

Apache Velocity Engine - User Guide

As a note, there is a vscode velocity transform extension that will assist in formatting and syntax.

This is amazing feedback. VS code is where I live so I will grab that extension and take a look.

Appreciate the prompt reply and effort into helping me! Look forward to contributing to the community myself as I learn more and more with this tool.

1 Like

An awesome community at that.

Just a followup to Fred’s excellent piece of work. Velocity scripting will fail if ANY of the attributes are blank, so you should make each one a firstValid with a default value (preferably different) so that you can error check

I was just about to post that I was able to take Fred’s great suggestion, create a transform and get values I was desiring in the new subtype attribute! Thank you!

However as you just stated Phil, those attributes being passed from NERM, are not required attributes, and therefore sometimes come over blank. This results in the identity not even populating those attributes.

I was attempting to do a NULL check on the values, but since the attributes don’t exist AT ALL on the identity, I keep getting processing errors. I assume it’s because of that reason.

I was going to ask if FirstValid would be the best way to overcome this, or if I should instead set up my form in NERM to have this field be required and add a “Not Required” choice and work that into my logic.

Thanks again team.