Activity

What is an Activity? An Activity in ZervicePoint is a building block when creating a Workflow. Activities are the things that "do the work" and make things happen in your Workflow. An Activity can be either one of the built-in activities shipped with ZervicePoint, Activities made available by buying a Plugin or a custom Activity that you have built yourself.

  • Built-in: Examples are Send Email, Set State, Assign, Approval etc. Part of the core product. Runs on Process System.

activity-workflow-list

  • Inventoried: Examples are New-ADUser, Enable-S4BUser, Get-ZPO365User. Scripts from disk. Can be custom made or from plugins. Runs on Provisioning System.

activity-workflow-list

Custom Activities

Custom Activites are typically written when we want a step in our workflow to perform a CRUD (Create, Read, Update, Delete) action in a target system (e.g Active Directory). You can also use them for business logic to generate names etc from other systems. For business logic that does not require input from another system, we recommend using the built-in Code Activity.

Info

Before writing your own custom activites, you should be familiar with writing your own PowerShell functions.

Parameters

You can add parameters to your activites. You can either bind parameters to variables or enter a static value as input.

Example

SiteName, Owners and Members parameters are bound to variables while TemplateName is set to a static value.

Tip

If you have several activites using the static input value, you can create a variable and give it a default value or use a assign activity to set it and bind the parameter to the variable.

To add a parameter in a powershell function you must include a parameter block.

function New-TeamSite
{
    param(
        [Parameter(Mandatory=$true)]
        [string]$SiteName,

        [string]$TemplateName,

        [string]$Owners,

        [string]$Members
    )

    # Code to add new team site ...

    # output
    return @{}
}

To learn more about powershell functions, follow this link about_functions

What if I add a new parameter to an existing function?

When adding new parameters to an existing activity. You must re-add the item in the workflow. This example show that the previous version of Get-DayOfWeek does not include the new Format parameter.

Example


Additional Parameters

In order to pass parameters created as Additional Parameter, the function must include a parameter called $Parameters. This must be a hashtable and it will include the defined parameters and the additional parameters. The $Parameters parameter should include [parameter(Mandatory = $false, ParameterSetName = "Hidden")] setting.

Example

Example

param(
    [string]$Test,

    [parameter(Mandatory = $false, ParameterSetName = "Hidden")]
    [hashtable]$Parameters
)

# $Parameters will include the following keys if input is the example above.
Name                           Value
----                           -----
myParameter                    hello world
Test                           123

Tip

As the $Parameters parameter will also include defined parameters. You can use the cmdlet Get-PFAdditionalParameter to only return the additional parameters. Get-PFAdditionalParameter is part of the PowerFrame module which is included in all our plugins.

ParameterSetName "Hidden"

Hides a parameter from the workflow editor. $Parameters and $Config should always be part of the 'Hidden' parameter set name in order to prevent passing multiple values into those parameters.

param(
    [parameter(Mandatory = $false, ParameterSetName = "Hidden")]
    [hashtable]$Parameters,

    [parameter(Mandatory = $false, ParameterSetName = "Hidden")]
    [hashtable]$Config
)

Config Parameter

The $config parameter must be a hashtable and it will automatically contain the following data:

Key Value  Source Comment
scriptpath C:\Plugin\Example\Activities XML Location of modules to import

The provider.xml file used to locate which location to load a plugin from and it can include keys within it's configuration block.

The following provider.xml would add the DomainController and SearchBase to the $config hashtable.

<?xml version="1.0" encoding="utf-8"?>
<providers>
<provider name="Example Plugin">
    <assembly type="Zipper.ZervicePoint.ProvisioningSystem.PowerShellProvider.PowerShellEngine" assemblyFile="C:\Program Files\Zipper\ZervicePoint\Web\ProvisioningSystem\Bin\Zipper.ZervicePoint.ProvisioningSystem.PowerShellProvider.dll"/>
    <configuration>
    <add key="scriptpath" value="C:\Plugin\Example\Activities\" />
    <add key="DomainController" value="dc01.example.com" />
    <add key="SearchBase" value="DC=example,DC=com" />
    </configuration>
</provider>
</providers>

To pass the keys from the configuration and into the powershell function, you must include the $config parameter.

[parameter(Mandatory = $false, ParameterSetName = "Hidden")]
[hashtable]$Config

Output

Custom activities should output a hashtable with output details of the activity.

The output hashtable will be compared with the variable list, if there are any matches between the **Key* and VariableName, then the variable will be updated with the value from the activity output.

Example 1 - Return variable called "DayOfWeek" with value from Get-Date cmdlet.

function Get-DayOfWeek {
    @{
        DayOfWeek = (Get-Date).DayOfWeek.ToString()
    }
}

#Output
Get-DayOfWeek

Name                           Value
----                           -----
DayOfWeek                      Monday

Additional reading: about_Hash_Tables

Example 2 - Return output after creating a Active Directory user. We've also added very simple error handling and different output depending on the outcome.

function New-ADUserExample {
    Param(
        [Parameter(Mandatory=$true)]
        [string]$Username,

        [string]$Firstname,

        [string]$Lastname,

        [string]$OUPath
    )

    try {
        $newADUser = New-ADUser -Name $Username -SamAccountName $Username -GivenName $Firstname -Surname $Lastname -Path $OUPath -Passthru:$true -ErrorAction Stop

        if ($null -ne $newADUser) {
            $ht = @{
                State = "Successfully created AD object"
                StatusCode = 0
                ObjectGUID = $newADUser.ObjectGUID
                SamAccountName = $newADUser.SamAccountName
                DistinguishedName = $newADUser.DistinguishedName
            }
        } else {
            $ht = @{
                State = "Failed to create AD object"
                StatusCode = 1
            }
        }
    } catch {
            $ht = @{
                State = "An error occured when creating AD object"
                ExceptionMessage = $_.exception.message
                StatusCode = 2
            }
    }

    $ht

}

# Output case 1
Name                           Value
----                           -----
SamAccountName                 user0002
DistinguishedName              CN=user0002,OU=test,DC=example,DC=com
ObjectGUID                     a0bd9480-f15a-4cb2-943b-a22bfb24d32d
StatusCode                     0
State                          Successfully created AD object


# Output case 2
Name                           Value
----                           -----
State                          Failed to create AD Object
StatusCode                     1

# Output case 3
Name                           Value
----                           -----
ExceptionMessage               The specified account already exists
StatusCode                     2
State                          An error occured when creating AD object

With the output above, we could add logic in our workflow to do different activities, such as reporting an alert when State 1 or 2 occured. In the example below, the workflow will perform different actions depending on the result of the New-ADUserExample function.

Workflow example

Example

Output Data types

Zervicepoint supports the following data types as outputs from an activity.

  • String
  • Boolean
  • Decimal
  • Datetime

Warning

If you return unsupported data types, the activity will fail to return the data to the workflow.


Prefix

When you use multiple custom activities in your workflow, you may come across the issue with activities outputing the same key values as other activities. This can cause an issue with variables being "overwritten" as they are updated by another activity.

This can either be solved by assigning activities to another variable after the output, but as there is not an unlimited number of variables that can be used in a workflow, this is not always recommended.

To solve this you should include a parameter called $Prefix, this could be used to then add a prefix before the output keys.

function Get-ExampleUser
{
    param(
        [string]$Identity,

        [string]$Prefix
    )

    $getADUser = Get-ADUser -Identity $Identity

    $ht = @{
        "$($Prefix)ObjectGUID" = $getADUser.ObjectGUID
        "$($Prefix)SamAccountName" = $getADUser.SamAccountName
    }

    return $ht
}

# Output
Get-ExampleUser -Identity user001 -Prefix test

Name                           Value
----                           -----
testObjectGUID                 c8108ec6-ff33-4cc8-9302-e5a960ca5111
testSamAccountName             user001
Add prefix using Add-PFHashTablePrefix

You can use "Add-PFHashTablePrefix" to easily add a prefix to an existing hashtable. Add-PFHashTablePrefix is part of the PowerFrame module which is included in all our plugins.

$ht = @{
    SamAccountName = "User001"
    ObjectGUID = "c8108ec6-ff33-4cc8-9302-e5a960ca5111"
    Mail = "user001@example.com"
    GivenName = "user"
    Surname = "001"
}

$htprefix = Add-PFHashTablePrefix -InputObject $ht -Prefix "MyPrefix"

$htprefix

#output
Name                           Value
----                           -----
MyPrefixSamAccountName         User001
MyPrefixMail                   user001@example.com
MyPrefixObjectGUID             c8108ec6-ff33-4cc8-9302-e5a960ca5111
MyPrefixGivenName              user
MyPrefixSurname                001

Here's is the function we use for Add-PFHashTablePrefix

function Add-PFHashTablePrefix
{
    <#
    .SYNOPSIS
        Add prefix to an existing hash table

    .DESCRIPTION
        Add-PFHashTablePrefix adds prefix to an existing hash table

    .PARAMETER InputObject
        Specifies the hashtable object to add prefix to

    .PARAMETER Prefix
        Specifies the prefix to add

    .EXAMPLE
        $hashtable = @{
            "ZP" = "ZervicePoint"
        }
        Add-PFHashTable -InputObject $hashtable -Prefix "Magic"

        The example above returns the following hashtable:
        @{
            "MagicZP" = "ZervicePoint"
        }

    .NOTES
        Add-PFHashTablePrefix is an internal function and part of the ZervicePoint.Utility module.

    .LINK
        https://www.zervicepoint.com
    #>
    [CmdLetBinding(DefaultParameterSetName = "InputObject")]
    [OutputType([System.Collections.Hashtable])]

    param (  
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [hashtable]$InputObject,

        [Parameter(Mandatory=$false)]
        [AllowNull()]
        [AllowEmptyString()]
        [string]$Prefix
    )
    process {
        if ($Prefix) {
            $hashTable = @{}
            foreach ($Object in $InputObject.GetEnumerator()) {
                $hashTable[$( $prefix + $Object.Key )] = $Object.Value
            }
            return $hashTable
        } else {
            return $InputObject
        }
    }   
}

Quickstart - How to create a custom activity

Before you begin you must have ProvisioningSystem installed

How to setup ProvisioningSystem role.

In order to create our own activity, we will need to create the following on the provisioningsystem server.

  • A PowerShell Module
  • A plugin xml file (pluginname.provisioningsystem.provider.xml)

Create a zervicepoint plugin in PowerShell

Start PowerShell (run as admin)

Create the following folder structure

C:\Plugin\ZPDocs\Activites\Activity.ZPDocs

To quickly create the structure above you can run the following powershell commands

New-Item -ItemType Directory -Path 'C:\Plugin\ZPDocs\Activities\Activity.ZPDocs'

Create PowerShell Module Manifest

New-ModuleManifest -Path C:\Plugin\ZPDocs\Activities\Activity.ZPDocs\Activity.ZPDocs.psd1 `
-RootModule "Activity.ZPDocs.psm1" `
-FileList @("Activity.ZPDocs.psd1","Activity.ZPDocs.psm1") `
-FunctionsToExport @('*') `
-PowerShellVersion 3.0

Write a function to PowerShell Module

Copy the function and insert it into the .psm1 file

$psm1 = @"
function Get-DayOfWeek {
    @{
        DayOfWeek = (Get-Date).DayOfWeek.ToString()
    }
}
"@

$psm1 | Out-File 'C:\Plugin\ZPDocs\Activities\Activity.ZPDocs\Activity.ZPDocs.psm1' -Encoding UTF8

Create a plugin xml

The provisioningsystem provider xml contain metadata for the plugin, name and scriptpath. It is also possible to extend this file with custom keys that can contain configuration related to the plugin. (e.g URLs etc)

Copy and paste the following code in a powershell prompt to create a plugin XML file.

$pluginxml = @"
<?xml version="1.0" encoding="utf-8"?>
<providers>
<provider name="Learn zervicepoint">
    <assembly type="Zipper.ZervicePoint.ProvisioningSystem.PowerShellProvider.PowerShellEngine" assemblyFile="C:\Program Files\Zipper\ZervicePoint\ProvisioningSystem\Providers\PowerShell\Zipper.ZervicePoint.ProvisioningSystem.PowerShellProvider.dll" />
    <configuration>
    <add key="scriptpath" value="C:\Plugin\ZPDocs\Activities\" />
    </configuration>
</provider>
</providers>
"@

$pluginxml | Out-File 'C:\Program Files\Zipper\ZervicePoint\ProvisioningSystem\ZPDocs.provisioningsystem.providers.xml' -Encoding UTF8

Trigger Inventory

To inventory new activties, you can choose one of the following methods.

Restart-Service -Name ProvisioningSystem

# or

& 'C:\Program Files\Zipper\ZervicePoint\ProvisioningSystem\Zipper.ZervicePoint.ProvisioningSystem.Service.exe' /inventoryonly

After the inventory is complete, it should soon be searchable in the providers page (AdminWeb). If it does not show up, check the zervicepoint event log.

provider-dayofweek

Now that the activity is inventoried, you can use it in your workflow. This activity will return the Key "DayOfWeek" that can be mapped to a string variable called "DayOfWeek".

provider-dayofweek