State Machine Workflow Complete Template

@{
    #* A workflow will only work with the following version of 
    #*   kenaflow. After you tested your workflow with a new 
    #*   version of kenaflow you can change the following 
    #*   setting.   
    Version = "4.0";

    #* set it to $true to ENABLE the workflow. ($false DISABLES it)
    Enabled = $false;

    #* Type = SHAREPOINT, EMAIL, POWERSHELL
    Type = "SHAREPOINT";

    #* SubType
    #*   if "SHAREPOINT" then ALL, LASTMODIFIED (alias LIST), 
    #*   QUERY, SITE, STATEMACHINE if "EMAIL" then 
    #*   EXCHANGE2007SP1, EXCHANGE2010, EXCHANGE2010SP1, 
    #*   EXCHANGE2010SP2, EXCHANGE2013, EXCHANGE2013SP1, 
    #*   EXCHANGE, EXCHANGEONLINE, POP3, IMAP if "POWERSHELL" => EMPTY
    SubType = "STATEMACHINE";

    #* set it to a meaningful name
    Name = "<<wfname>>";

    #* what SharePoint do you use? Possible values: "sp2013" or 
    #*   "sp2016" or "sp2019" or "spo" 
    Platform = "";

    #* a unique (!) id (type 'System.Guid') for this workflow. 
    #*   must be unique in YOUR environment. Use 
    #*   "[guid]::newGuid().tostring('d')" to create a GUID in 
    #*   Powershell.   
    Id = "<<wfid>>";

    #* A list of one or more files that are checked during 
    #*   deserialization. The workflow configuration is 
    #*   reserialized if such an additional file was modified 
    #*   since last serialization or is missing. 
    #AdditionalConfigFiles = @();

    #* option. can be used to specify the folder where the 
    #*   workflow scripts are in. the workflow directory (where 
    #*   the _wfconfig.ps1 is in) is used if empty. 
    #ScriptFolder = "";

    #* "Time between execution" => minimal time difference IN 
    #*   SECONDS between two executions of the workflow Make sure 
    #*   you set TBE > 0 to activate time-based execution. First 
    #*   TBE is checked and afterwards CRON if set. 
    TBE = 60;

    #* CRON-style pattern for execution scheduling. Please 
    #*   consult our documentation to get more infos on that. 
    #Cron = "0 * * * *";

    #* During a time based workflow run the same workflow 
    #*   should start also on another kenaflow server (in case of 
    #*   scaling). If your really sure that you want to run 
    #*   multiple instances of the same workflow than you can set 
    #*   this to $FALSE. 
    #Locking = $true;

    #* This can be used to specify one or more custom 
    #*   PowerShell libariy files that will be executed before 
    #*   workflow script execution. 
    #CustomLibrary = @("");

    #* Here you can specify a script for error handling. It's 
    #*   processed in case of exceptions within the workflow 
    #*   script.  Please read 
    #*   https://doc.kenaflow.com/basics/errorhandling   
    #ErrorHandlingScript = "errorHandling.ps1"

    #* This script is executed after the workflow is started. 
    #*   Before any connection, e.g. to SharePoint, is initiated. 
    #PreProcessingScript = "preProcessing.ps1"

    #* This script is executed before the workflow is finished.
    #PostProcessingScript = "postProcessing.ps1"

    #* Can be used to mark this workflow as "in debug" for use 
    #*   with cmdlet Get-KFInDebug -WorkflowDebugState 
    #Debug = $false;

    #* This file will be serialized for performance reasons. 
    #*   You can prevent serialization by setting this option.
    #NoSerializing = $false;

    #* This file will be serialized for performance reasons as 
    #*   long as property 'NoSerializing' is not set to $true. 
    #*   But you can specify a "maximum lifetime" here. if this 
    #*   amount of time elapsed the workflow configuration gets 
    #*   re-serialized. Possible values are TimeSpan (object or 
    #*   string) or amount of seconds (int or string) 
    #MaxLifetimeOfSerialization = "0:5" #5 minutes

    #UserProfileCacheLifetime = 60; #In seconds. If not specified the global setting from kenaflow.conf is used

    #* Force workflow script to run again in case of "version 
    #*   conflict" (parallel item modifications in SharePoint). 
    #*   Default: $false 
    #RetryOnVersionConflict = $false;

    #* If set to $false the workflow will continue with the 
    #*   next item on workflow script errors. Default ist $true 
    #*   ("stop workflow on script errors") 
    #StopWorkflowOnScriptError = $true;


    #* The following two parameters can be used to specify credentials
    #*   however it is possible to store credentials in a 
    #*   protected fashion using the parameters --setusername and 
    #*   --setpassword on kenaflow.exe while running the program 
    #*   inside a workflow folder that contains _wfconfig.ps1. 
    #*   this will create a file _wfconfig-cred.ps1 that contains 
    #*   encrypted credentials. the credentials can only 
    #*   decrypted on the same machine. on other machines the 
    #*   file is worthless. 
    #spUser = "";
    #spPwd = '';

    #* only if platform is 'spo' = SharePoint Online; **Useable 
    #*   IF 'AzureAdApp' is not specified or set to `$true`. 
    #spoAppId = "";
    #* only if platform is 'spo' = SharePoint Online; **Useable 
    #*   IF 'AzureAdApp' is not specified or set to `$true`. 
    #spoAppSecret = '';

    #AzureAdApp = $false
    
    #* To use *App only* authentication with SharePoint Online 
    #*   and Azure Active Directory (App Registration) you can 
    #*   specify `spoCert`, `spoCertPassword` and `spoAppId` To 
    #*   use that you must set `AzureAdApp=$true`!! 
    
    #* The name of a certificate file containing public and 
    #*   private (!) key! (X509Certificate / PFX). 
    #spoCert=""; 
    #* #The password for the cert file.
    #spoCertPassword=''; 
    #* The app ID from Azure AD / App Registration. The tenant 
    #*   ID must come first in the string, separated from the app 
    #*   id with | (pipe character)
    #spoAppId = '<tenant-id>|<app-only-credential-id>';


    #* Starting with kenaflow 3.0 you can execute a SharePoint 
    #*   workflow on multiple lists in multiple webs. The 
    #*   executed workflow script is always the same. All other 
    #*   settings are identical (names of lists such as error 
    #*   list or data list). But the lists can be configured per 
    #*   web. If you configure this property you cannot configure 
    #*   `Web`, `AlternateWebUrls` and `List` in parallel. You 
    #*   can also specify different login / credential 
    #*   information for each connection. If not specified the 
    #*   settings from the workflow is used. 
    <#
    Connection = @(
      @{
        Web = "https://firstweb.sharepoint.farm";
        List = @("List 1", "List 2");
        AlternateWebUrls = @();
      },
      @{
        Web = "https://secondweb.sharepoint.farm";
        List = "List 3";
        AlternateWebUrls = @();
      },
      @{
        Web = "https://thirdweb.sharepoint.farm";
        List = @("List 4", "List 5");
        AlternateWebUrls = @();
        spoAppId         = '';     #optional! 
        spoAppSecret     = '';     #optional! 
        spUser           = '';     #optional! 
        spPassword       = '';     #optional! 
        AzureAdApp       = $false; #optional! 
        spoCert          = '';     #optional! 
        spoCertPassword  = '';     #optional! 
        Environment      = '';     #optional!
      }
    )
    #>

    #* Full URL to the SharePoint web where the workflow list 
    #*   is in 
    Web = "";

    #* Title of the SharePoint list where the workflow operates 
    #*   on. This list is called "workflow list" You can 
    #*   configure multiple list for the workflow by using a 
    #*   PowerShell list object: @("List 1", "List 2")
    List = @("");


    #* A list of alternate URLS, e.g. if a web application has 
    #*   Alternate Access Mappings. Remove Events could be 
    #*   received with such an alternate URL 
    #AlternateWebUrls = @();

    #* a list for global config values
    ConfigList = "Workflow Configuration";

    #* a list that can be used to store data
    DataList = "Workflow Data";

    #* a list that can be used to record log information
    HistoryList = "Workflow History";

    #* Workflow errors will be stored in this SharePoint list
    ErrorList = "Workflow Errors";

    #* Can be uses to define default config entries that will 
    #*   be deployed to the config list if the workflow config 
    #*   list is created by kenaflow (kenaflow.exe) 
    ConfigListDefaults = @{
        "kenaflow" = "cool"
    };

    #* List of mail addresses that will receive mails in case 
    #*   of workflow internal errors. 
    AlertAddresses = @();

    #* can be used to overwrite the global settings.
    #AlertFloodProtection = 3600;

    #* Address of the sender of mails that the workflow sends. 
    #*   If empty it will be taken from the kenaflow global 
    #*   config   
    #MailFrom = "";

    #* Address to which uses can reply to. If empty it will be 
    #*   taken from the kenaflow global config 
    #MailReply = "";

    #* After this amount of time elasped the workflow execution 
    #*   will be stopped; -1 = use default 
    #MaxExecutionTime = -1;

    #* This script is executed before querying
    #PreQueryProcessing = "preQueryProcessing.ps1";

    #* Specified whether the Workflow needs Remote Event 
    #*   Receiver to execute the workflow scripts immediately 
    #*   after a change in SharePoint. 
    RER = $false;

    #* Optional, but must be used if multipe workflow listen to 
    #*   the same list!! 
    #RERHandlerName = "kenaflow_wf1";

    #* Specifies how long a Remote Event is valid before it is 
    #*   skipped forever (if not processed). Default: 
    #*   configuration in global config. There is default: 120s. 
    #MaximumRerLifetime = 120;

    #* Specifies how long a a remote event is postponed in case 
    #*   of errors during processing. Default: configuration in 
    #*   global config. There is default: 5s. 
    #PostponeFailedRer = 5;

    #* Handle lists and libraries "flat" by ignoring folders. 
    #*   Default: $false 
    #IgnoreFoldersInQuery = $false;

    #* lookup field in the workflow list that represents the 
    #*   current item state 
    StateField = "State";

    #* list that holds all possible states
    StateList = "WFStates";

    #* If you only want to process item with changes in certain 
    #*   item fields (columns) you can specify the list of names 
    #*   here. If you want only process items with changes in any 
    #*   field you can set this simply to $true. This global 
    #*   setting is used also for states if it is not overwritten 
    #*   for  a certain state! 
    #ItemChangeHashFields = $true;
    #ItemChangeHashFields = @("Title", "CustomField");

    #* if false the 'last modified' flag file is used
    #LastModifiedBasedOnLastRun = true;

    #* Count of list items queried from SharePoint at one 
    #*   request. -1 = use default 
    #ItemBatchCount = -1;

    #* Amount of time before each item is checked to be 
    #*   processed. -1 = use default; 0 = check immediately 
    #ItemReCheckTime = -1;

    #* The following setting can be used to order the execution 
    #*   of states. It is possibe to mention a state multiple 
    #*   times to execute it multiple times during a workflow 
    #*   run. This could help to execute states faster. 
    #OrderedStates = @( "(empty)", "1.0", "2.0" ); #...as list of strings #or #OrderedStates = "(empty), 1.0, 2.0"; #...as string (if the state keys do not contain characters ',' (comma) and ' ' (space))
    
    #* Here you can specify how often all (enabled) states 
    #*   should be executed again in one workflow run. This is 
    #*   used to execute all (enabled) states multiple times to 
    #*   ensure that all possible state changes and state scripts 
    #*   are executed as fast as possible without waiting for the 
    #*   next workflow run. 
    #RepeatStates = 1;


    States = @{
        "(empty)" = @{
            #* You can define the execution order for states by 
            #*   defining this key. All states that have not 
            #*   definied this setting the number 0 is used. 
            #*   Except for state "(empty)" for which -1 is used 
            #*   by default. So (empty) will be executed first. 
            #*   All scripts with the same "ExecutionOrder" (or 
            #*   none which means "0") are executed by the order 
            #*   of their keys. 
            #ExecutionOrder = 100;

            #* After this run time SharePoint list items will 
            #*   be checked for their corrent state before 
            #*   processing   
            #ItemReCheckTime = -1;

            #* After this amount of time elasped the workflow 
            #*   execution will be stopped; -1 = use default 
            #MaxExecutionTime = -1;

            #* Count of list items queried from SharePoint at 
            #*   one request. -1 = use default 
            #ItemBatchCount = -1;

            #* Can be used to mark this state as "in debug" for 
            #*   use with cmdlet Get-KFInDebug 
            #*   -WorkflowStateDebugState   
            #Debug = $false;

            #LastModifiedBasedOnLastRun = true; #if false the 'last modified' flag file is used
        };
        "1.0" = @{
            #* States can be disabled for processing by setting 
            #*   this to $false. Default is $true. 
            Enabled = $true;

            #* if not set the State key ("1.0") will be used
            Title = "State 1.0";

            #* query, lastmodified, all or (empty)
            Type="lastmodified";

            #* will be translated into CAML
            #Query = "";

            #* If you only want to process item with changes in 
            #*   certain item fields (columns) you can specify 
            #*   the list of names here. If you want only process 
            #*   items with changes in any field you can set this 
            #*   simply to $true. If you want to disable this of 
            #*   the current state and the global setting of the 
            #*   workflow for this property defines a field list 
            #*   or $true, then you can set this to $false 
            #ItemChangeHashFields = $true;
            #ItemChangeHashFields = $false;
            #ItemChangeHashFields = @("Title", "CustomField");

            #* Handle lists and libraries "flat" by ignoring 
            #*   folders. Default: $false 
            #IgnoreFoldersInQuery = $false;

            #* if empty the script must be named as 
            #*   "S_<state><your text or noting>.ps1" 
            #Script = "";

            #* Time Before Execution. Each state remembers the 
            #*   last runtime in a text file. The state script 
            #*   will not execute until this amount of seconds 
            #*   elapsed sind last run. 
            #TBE = 30;

            #* CRON-style pattern for execution scheduling. 
            #*   Please consult our documentation to get more 
            #*   infos on that. 
            #Cron = "0 * * * *";
        };
        "2.0" = @{
            Type="query";
            Query="{{Title}} == string 'Test' & {{Modified}} > datetime [[TODAY]] & {{Editor}} != lookupid [[USERID]]"
            #You can specify CAML instead. {{ }} tokens are processed at runtime
            #Query="<Where><And><And><Eq><FieldRef Name='Title'/><Value Type='Text'>Test</Value></Eq><Gt><FieldRef Name='Modified'/><Value Type='DateTime' IncludeTimeValue='TRUE'><Today/></Value></Gt></And>" +
            #* "<Neq><FieldRef Name='Editor'/><Value Type='Lookup'><UserId/></Value></Neq></And></Where>";

        };
        "3.0" = @{
            Title = "Last State";

            #* This state has a dedicated script name that is 
            #*   used instead the default state script name 
            #*   "S_<state-key>.ps1"   
            Script = "S_laststate.ps1";
        };
        "4.0" = @{
            Enabled = $false;
            Title = "Ignore";
        };
    }


    #* Some item permission related Cmdlets in workflows can 
    #*   simply set "CRUD" +M (Manage) +A (Approve) permissions 
    #*   by specifying some characters from set [C, R, U, D, M, 
    #*   A]. They refer to permission set that the Site 
    #*   Collection administrator has to create. They must also 
    #*   be named here 
    #PermissionSetCreate="kenaflow Create";
    #PermissionSetRead="kenaflow Read";
    #PermissionSetUpdate="kenaflow Update";
    #PermissionSetDelete="kenaflow Delete";
    #PermissionSetManage="kenaflow Manage";
    #PermissionSetApprove="kenaflow Approve";


    #* +++++++++++++++++++
    #*   runtime settings

    #* (only) kenaflow Cmdlets will throw Exceptions on failure 
    #*   ($true) or hide them ($false). In the last case the 
    #*   exception can be queried with Get-KFLastResult. Default 
    #*   is $true 
    #ThrowErrorsPreference = $true;
    #* This is the default for the common PowerShell variable 
    #*   $ErrorActionPreference that controls the default 
    #*   PowerShell error handling. Default is "Stop". This can 
    #*   be overwritten by Invoke-Kenaflow -ErrorActionPreference 
    #*   <value> or in script using $ErrorActionPreference = 
    #*   <value>   
    #ErrorActionPreference = 'Stop';

    #* +++++++++++++++++++
    #*   debug settings

    #* if not specified here the default setting from kenaflow 
    #*   will be used. 
    #writeMailsToDiskDuringDebug = $true;

    #* if not specified here the default setting from kenaflow 
    #*   will be used. 
    #alwaysWriteMailsToDiskInsteadSending = $false;

    #* can be used to redirect all mails to the specified account(s)
    #redirectAllMails = @();

    #* this parameter can be used to force client processes 
    #*   (--exec) to send PowerShell output serialzied to the 
    #*   main proces (--run) Default: $false
    #SendOutputToMainProcess = $false;

    #* this will dump RER file contents to the Log.
    #DumpRerEventDataToLog = $false

    #* It is possible to save processed remote event files. 
    #*   Normally they are deleted. Saving can be enabled in the 
    #*   global configuration but must be also enabled for each 
    #*   workflow itself by setting this property to $TRUE. 
    #*   Default: $FALSE 
    #SaveRerEventsAfterProcessing = $false
}

if(!$kenaflow){import-module "<<directory>>\kenaflow.runtime.dll";Test-KFConfig;exit}

Discussion