Validations 101
When we build the V2 templates we’ve tried to structure the code in a logical manner. Quite literally from A-Z. We believe that, structuring the code like a book, helps understanding the code and all its processes. Additionally, having uniformity in code structure across all connectors also provides a sense of familiarity. You instinctively know where a specific piece of code can be found.
Validations form an integral part of this structure. Based on their outcome, different actions are invoked.
In this post, we’ll talk about the fundamentals of validations within the create lifecycle stage and how validations -and structure- relate to HelloID.
Table of contents
create
lifecycle stage
Here’s an example of the create
lifecycle stage code.
try {
# Validation
try {
$splatParams = @{
Uri = "https://api.example.enyoi/api/v1/users/$($actionContext.Data.ExternalId)"
Method = 'GET'
}
$correlatedAccount = Invoke-RestMethod @splatParams
} catch {
if ($exception.Response.StatusCode -eq 'NotFound'){
$correlatedAccount = $null
} else {
throw
}
}
# Determine actions
if ($null -eq $correlatedAccount) {
$action = 'CreateAccount'
} else {
$action = 'CorrelateAccount'
}
# Preview actions
if ($actionContext.DryRun -eq $true) {
Write-Information "$action account will be executed during enforcement"
}
# Enforce actions
if (-not($actionContext.DryRun -eq $true)) {
switch ($action) {
'CreateAccount' {
$splatParams = @{
Uri = 'https://api.example.enyoi/api/v1/users'
Method = 'POST'
Body = $actionContext.Data | ConvertTo-Json
}
$createdAccount = Invoke-RestMethod @splatParams
$outputContext.AccountReference = $createdAccount.Id
}
'CorrelateAccount' {
$outputContext.AccountReference = $correlatedAccount.Id
}
}
$outputContext.success = $true
}
} catch {
# Main error handling
}
Validation
Every create
story begins with validations.
At its simplest, we have to determine whether an account needs to be created or correlated. This is what we refer to as a validation. In the code example above, we validate if an account for a specific person exists.
TIP
Initially, the internal database Id
is not known to us. Therefore, we prefer to validate this using the EmployeeId
or ExternalId
since this is unique and available within HelloID.
try {
$splatParams = @{
Uri = "https://api.example.enyoi/api/v1/users/$($actionContext.Data.ExternalId)"
Method = 'GET'
}
$correlatedAccount = Invoke-RestMethod @splatParams
} catch {
if ($exception.Response.StatusCode -eq 'NotFound'){
$correlatedAccount = $null
} else {
throw
}
}
TIP
Notice that we wrapped this in a nested try/catch
block because we don’t want the lifecycle stage to fail in case we get back a 404-NotFound
.
Actions
The outcome of a validation always results in an action. Within the create
lifecycle stage, we identify two actions.
- CreateAccount
- CorrelateAccount
if ($null -eq $correlatedAccount) {
$action = 'CreateAccount'
} else {
$action = 'CorrelateAccount'}
If $correlatedAccount
equals to $null
we set the $action
variable to CreateAccount. If $correlatedAccount
not equals to $null
we set $action
to CorrelateAccount.
Technically its not necessary to have a separate action for CorrelateAccount. The reason its there is because we believe that, having a separate action, maintains readability.
Mode
Depending on which mode you’re running in, different code blocks are executed.
Preview
In preview mode, you will see what happens with a particular person during enforcement. This functionality is somewhat comparable to the -WhatIf
switch in PowerShell.
TIP
All the preview logic will need to be encapsulated within the if ($actionContext.DryRun -eq $true)
block.
if ($actionContext.DryRun -eq $true) {
Write-Information "$action account will be executed during enforcement"
}
Enforcement
Commonly referred to as production mode. Enforcement is the process where HelloID actually performs a grant, revoke or modification of an account or entitlement.
TIP
All the enforcement logic is encapsulated within the if (-not($actionContext.DryRun -eq $true)
block.
if (-not($actionContext.DryRun -eq $true)) {
switch ($action) {
'CreateAccount' {
$splatParams = @{
Uri = 'https://api.example.enyoi/api/v1/users'
Method = 'POST'
Body = $actionContext.Data | ConvertTo-Json
}
$createdAccount = Invoke-RestMethod @splatParams
$outputContext.AccountReference = $createdAccount.Id
}
'CorrelateAccount' {
$outputContext.AccountReference = $correlatedAccount.Id
}
}
$outputContext.success = $true
}
If the account is returned in our validation and $correlatedAccount
does contain the actual account object, we extract the Id
and assign it to the $outputContext.AccountReference
variable for correlation.
try/catch
Notice we’ve wrapped all our logic into one main try/catch
block. This ensures that error handling is easy to follow and that we always provide HelloID with clean error messages in case something unfortunate happens.
WARNING
We do not encourage the use of nested try/catch
blocks. This approach can make error handling more difficult to follow and is unnecessary in most cases.
What if there are more validations
There are many occasions where we need to validate more then just the account. For example, an account may require manager and departmental information.
Keep in mind that, in preview mode, we always want to see the actions that will be executed during enforcement. This includes the outcome of each validation. If, for example, the manager needs to be updated, we expect to see that preview mode.
The following code example demonstrates how multiple validations can be used.
# Validate account
try {
$splatParams = @{
Uri = "https://api.example.enyoi/api/v1/users/$($actionContext.Data.ExternalId)"
Method = 'GET'
}
$correlatedAccount = Invoke-RestMethod @splatParams
} catch {
if ($exception.Response.StatusCode -eq 'NotFound'){
$correlatedAccount = $null
} else {
throw
}
}
# Determine actions
if ($null -eq $correlatedAccount) {
$parentAction = 'CreateAccount'
$dryRunMessage = "CreateAccount will be executed during enforcement. Manager ExternalId set to: [$personContext.Person.PrimaryManager.ExternalId]. DepartmentDisplayName set to: [$personContext.PrimaryContract.Department.DisplayName)]"
} else {
$parentAction = 'CorrelateAccount'
$outputContext.AccountReference = $correlatedAccount.id
$dryRunMessage = "CorrelateAccount will be executed during enforcement.`n"
# Define empty list that will contain our childActions.
$childActions = [System.Collections.Generic.List[object]]::new()
# Validate manager
if ($correlatedAccount.Manager.ExternalId -ne ($personContext.Person.PrimaryManager.ExternalId)){
$dryRunMessage += "Manager ExternalId set to: [$personContext.Person.PrimaryManager.ExternalId]`n"
$childActions.Add('UpdateManager')
}
# Validate department
if ($correlatedAccount.Department.DisplayName -ne ($personContext.PrimaryContract.Department.DisplayName)){
$dryRunMessage += "DepartmentDisplayName set to: [$personContext.PrimaryContract.Department.DisplayName)]"
$childActions.Add('UpdateDepartment')
}
}
# Preview actions
if ($actionContext.DryRun -eq $true) {
Write-Information $dryRunMessage
}
# Enforce actions
if (-not($actionContext.DryRun -eq $true)) {
switch ($parentAction) {
'CreateAccount' {
# CreateAccount logic
}
'CorrelateAccount' {
# CorrelateAccount logic
}
}
# childActions
foreach ($childAction in $childActions) {
switch ($childActions) {
'UpdateManager' {
# UpdateManager logic
}
'UpdateDepartment' {
# UpdateDepartment logic
}
}
}
$outputContext.success = $true
}
Wrapping Up
We’ve covered the fundamentals of validations within the create
lifecycle action. Hopefully we could provide some guidance on how to structure your connector.
What’s next
Developing a connector requires extensive knowledge of PowerShell and HelloID provisioning. If you don’t know where to go from here, there are plenty of resources that can help you.
Samples
We have a huge collection of connectors available on the Tools4ever GitHub repository you can adapt from.
Example API
We’ve also created an example API that may provide insight into what is expected for HelloID.
Documentation
Make sure to explore our documentation.
Forum
Lastly, if you find yourself stuck and require assistance, make sure to visit our forum. Here, you can ask questions and connect with our consultants and developers.