5 minute read

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.