Installing K2 Blackpearl Smartforms Runtime in a separate Active Directory domain


Recently I created an architecture which saw K2 Smartform Runtime components deployed and configured in a separate Active Directory forest from the relevant K2 Blackpearl server.  This architecture aligns with the security and enterprise architecture principals for one of my Government clients.

All of the client’s environments are all designed to implement a network/domain equivalence of air gapping, by implementing separate Active Directory forests for the security perimeter (a DMZ Domain) and for the corporate (“internal”) systems.  External connectivity must route through the DMZ domain for all external functionality – i.e. no exposure of services or applications within the corporate domain directly.

Naturally, K2 Blackpearl resides within the corporate domain, but external users can only authenticate to the DMZ domain – their credentials stored within Active Directory within the DMZ domain.  Both the DMZ and corporate directories implement AD FS 3 (Server 2012 R2), so WS-Federation and claims based authentication (SSO) is possible, and implemented already.

Luckily for us, the K2 Smartforms Runtime supports this authentication scenario.

The following diagram illustrates the configuration and domain design:


Pre-install Requirements

Note: This article assumes you already have installed and configured the K2 Blackpearl server.

Permissions required to execute the install

· Domain Admin in DMZ

· Domain Admin in Internal

· Admin role in K2 Server

· RDP access to K2 server, SQL server (K2 database), DMZ webserver, DMZ Domain Controller and DMZ ADFS

· A bespoke T-SQL script to configure the K2 database (details below)

· Access to K2 install media


The DMZ Web Server(s) needs port 1433 TCP access (one way) to SQL Server to connect to the K2 database – only for installation!

This access can be removed, post-installation.  The Runtime needs the TCP 5555 port to contact the K2 Blackpearl server.  The K2 Blackpearl server may require TCP 389/626 (LDAP) port access to the DMZ AD DS server to validate authenticated identities (being confirmed).


You will require a digital certificate to secure the website for AD FS authentication.  Self-signed certificates would suffice for non-Production environments, although I have personally abandoned that practice many years ago, and insist on the installation of an Enterprise CA now for the purpose of issuing valid certificates for internal testing.  For pre-Production and Production environments, you’d be unprofessional not to use a properly issued certificate from a major issuer, e.g. Verisign etc.

Server Roles/Features


Web Server -> Application Development -> Application Initialization

Web Server -> Performance -> Dynamic Content Compression

Web Server -> Security -> URL Authorization


.NET Framework 3.5 Features -> HTTP Activation

Windows Process Activation Service -> .NET Environment 3.5

1. K2 Core Install

1. Copy installer package to the server (e.g. D:\Installs\)

2. Extract package

3. Authenticate to SQL Server (Internal)

4. Open SSMS and expand Security\Logins

5. Create a SQL Server Authentication Login:

    a. Name: svc_k2_dmz

    b. Password: <Generate>

    c. User Mappings:

    d. K2 Database – db_datareader & db_datawriter


6. Make a record of the server & account details (needed for K2 install)

7. Return to DMZ webserver(s) and run the K2 blackpearl installer (first option)


8. Click through the installer until this screen, and select ‘custom’


9. UNCHECK everything except for the Required Components (at the very bottom)


It should look like this when installing:


10. Once finished, reboot and then proceed to the second installer:


2. K2 Smartforms Install

1. Run the installer

2. Continue until you encounter the following screen:

3. Uncheck everything except K2 smartforms runtime:


4. Enter the correct connection properties to the K2 database on the APPS cluster and test


5. Enter license information (see appendix)


While you are waiting for a license key, check the web site configuration.

Create the new website which will host the actual K2 Smartforms runtime site itself.  It should have a custom App Pool with a local account as the identity, e.g. svc_k2forms  This identity (local account) should also exist on the K2 server, with the same password (not set to expire).  I prefer to create the site manually rather than creating it through the installer.


Resuming the install, paste the license key into the UI and click next:

6. Select the forms website:


7. And the existing app pool


8. Confirm:


Once the install is underway, it’s worth configuring ADFS


Health error seems to be irrelevant…

Site now needs Controls – see K2 Smartforms Control Pack

2a. ADFS Configuration

Navigate to the DMZ ADFS server and open the AD FS Administration console.

If no trust exists, you can easily create one.  Use the endpoint URI of the site, and create a claim rule as so:


Add claim rules – Provider

clip_image032 clip_image034

=> issue(Type = “”, Value = “ADFS”);

Add claim rules – Identity

clip_image036  clip_image038

3. Install the K2 Smartforms Control Pack

1. Return to the Web Splash page and click install of the Control Pack:


2. Click through the installer until this page:


3. Reconfirm the DB connection settings using environment specific values:


Continue to run through the installer.


Next, edit the web.config for the Runtime application. Go to the following location:

C:\Program Files (x86)\K2 blackpearl\K2 smartforms Runtime
and take a backup copy of the Web.config.

Now edit the Web.config and append the following line:

<!– The DefaultSecurityLabel is used to when none is specified. Leave blank or missing to to use the URM default security label –>

<!–<add key=”DefaultSecurityLabel” value=”K2″/>–>

<!– SecurityLabels that are available (Semi-colon separated list). Leave blank or missing to use the all URM security labels–>

<add key=”SecurityLabels” value=”K2FORMS”/>

Perform an iisreset & restart the K2 blackpearl service on the K2 server.

You are finished in the DMZ. I’d suggest a reboot to ensure everything is nice and clean.

Time to configure the internal SQL & K2 server.

4. SQL Server Configuration

A specific T-SQL script needs to be run against the K2 database to insert a provider for the DMZ ADFS STS.  You’ll need to ensure all the <DMZDOMAIN> parts are valid before executing the script.  This should just be references to your LDAP names for the DMZ domain.  We’re using an Organisation Unit (OU) called “External Users” as the location for the user accounts.  Change as required.

   1: -- K2 LDAP User Manager (Forms - Setup).sql

   2: -- sample script for creating a K2 LDAP user manager that uses the SourceCode.Security.Providers.LdapProvider.Forms.Ldap provider



   5: DECLARE @SecurityLabelName NVARCHAR(20) = 'K2FORMS'; --Update as needed

   6: DECLARE @XmlConfig XML = 

   7: '<AuthInit>

   8:   <LdapConnection

   9:     LdapServer="dmz.<DMZDOMAIN>"

  10:     LdapServerPort="389"

  11:     LdapSsl="false"

  12:     LdapAuthTypeConnect="Negotiate"

  13:     LdapAuthTypeAuthenticateUser="Negotiate"

  14:     LdapResolveAuthenticationUserToDistinguishedName="false"

  15:     LdapAutoBind="false"

  16:     LdapScope="Subtree"

  17:     LdapConnectIntegrated="true"

  18:     LdapConnectUserName=""

  19:     LdapConnectUserPassword=""

  20:     LdapTimeout="0"

  21:     LdapProtocolVersion="3"

  22:     LdapServerCertificatePath="" />

  23:   <LdapUserBaseObject>ou=external users,<DMZDOMAIN></LdapUserBaseObject>

  24:   <LdapUserSearchFormatString>(&amp;(objectClass=Person)(objectCategory=User)(samAccountName={0}))</LdapUserSearchFormatString>

  25:   <LdapUserGroupSearchFormatString>(memberOf:1.2.840.113556.1.4.1941:={0})</LdapUserGroupSearchFormatString>

  26:   <LdapUserAttributes>

  27:     <K2LdapMapping K2Name="ID" LdapName="samAccountName" ObjectType="System.String" />

  28:     <K2LdapMapping K2Name="Name" LdapName="samAccountName" ObjectType="System.String" />

  29:     <K2LdapMapping K2Name="Description" Multiline="true" LdapName="description" ObjectType="System.String" />

  30:     <K2LdapMapping K2Name="Email" LdapName="mail" ObjectType="System.String" />

  31:     <K2LdapMapping K2Name="DistinguishedName" LdapName="distinguishedName" ObjectType="System.String" />

  32:     <K2LdapMapping K2Name="ObjectSID" FullOnly="true" LdapName="objectSID" ObjectType="System.String" />

  33:     <K2LdapMapping K2Name="CommonName" LdapName="cn" ObjectType="System.String" />

  34:     <K2LdapMapping K2Name="UserPrincipalName" LdapName="userPrincipalName" ObjectType="System.String" />

  35:     <K2LdapMapping K2Name="Manager" FullOnly="true" LdapName="manager" ObjectType="System.String" SearchQuery="(&amp;(objectClass=Person)(objectCategory=User))" SearchResultProperty="samAccountName" />

  36:     <K2LdapMapping K2Name="SipAccount" LdapName="msRTCSIP-PrimaryUserAddress" ObjectType="System.String" />

  37:     <K2LdapMapping K2Name="DisplayName" LdapName="displayName" ObjectType="System.String" />

  38:     <K2LdapMapping K2Name="TelephoneNumber" LdapName="telephoneNumber" ObjectType="System.String" />

  39:     <K2LdapMapping K2Name="Mobile" LdapName="mobile" ObjectType="System.String" />

  40:     <K2LdapMapping K2Name="HomePage" LdapName="wWWHomePage" ObjectType="System.String" />

  41:     <K2LdapMapping K2Name="FaxNumber" LdapName="facsimileTelephoneNumber" ObjectType="System.String" />

  42:     <K2LdapMapping K2Name="HomePhone" LdapName="homePhone" ObjectType="System.String" />

  43:     <K2LdapMapping K2Name="IPPhone" LdapName="ipPhone" ObjectType="System.String" />

  44:     <K2LdapMapping K2Name="StreetAddress" LdapName="streetAddress" ObjectType="System.String" />

  45:     <K2LdapMapping K2Name="City" LdapName="l" ObjectType="System.String" />

  46:     <K2LdapMapping K2Name="Country" LdapName="c" ObjectType="System.String" />

  47:     <K2LdapMapping K2Name="State" LdapName="st" ObjectType="System.String" />

  48:     <K2LdapMapping K2Name="Title" LdapName="title" ObjectType="System.String" />

  49:     <K2LdapMapping K2Name="Department" LdapName="department" ObjectType="System.String" />

  50:     <K2LdapMapping K2Name="Company" LdapName="company" ObjectType="System.String" />

  51:     <K2LdapMapping K2Name="Office" LdapName="physicalDeliveryOfficeName" ObjectType="System.String" />

  52:     <K2LdapMapping K2Name="ManagedUsers" FullOnly="true" LdapName="managedUsers" SearchQuery="(&amp;(objectClass=Person)(objectCategory=User))" SearchResultProperty="samAccountName" ObjectType="System.Collections.ArrayList" />

  53:     <K2LdapMapping K2Name="Groups" FullOnly="true" LdapName="memberOf" SearchQuery="(objectCategory=Group)" SearchResultProperty="samAccountName" ObjectType="System.Collections.ArrayList" />

  54:   </LdapUserAttributes>

  55:   <LdapGroupBaseObject>ou=external users,dc=dmz,<DMZDOMAIN></LdapGroupBaseObject>

  56:   <LdapGroupSearchFormatString>(&amp;(objectCategory=Group)(samAccountName={0}))</LdapGroupSearchFormatString>

  57:   <LdapGroupMemberSearchFormatString>(member:1.2.840.113556.1.4.1941:={0})</LdapGroupMemberSearchFormatString>

  58:   <LdapGroupAttributes>

  59:     <K2LdapMapping K2Name="ID" LdapName="samAccountName" ObjectType="System.String" />

  60:     <K2LdapMapping K2Name="Name" LdapName="cn" ObjectType="System.String" />

  61:     <K2LdapMapping K2Name="Description" Multiline="true" LdapName="description" ObjectType="System.String" />

  62:     <K2LdapMapping K2Name="Email" LdapName="mail" ObjectType="System.String" />

  63:     <K2LdapMapping K2Name="DistinguishedName" LdapName="distinguishedName" FullOnly="true" ObjectType="System.String" />

  64:     <K2LdapMapping K2Name="ObjectSID" LdapName="objectSID" FullOnly="true" ObjectType="System.String" />

  65:     <K2LdapMapping K2Name="Member" LdapName="member" FullOnly="true" SearchQuery="(&amp;(objectClass=Person)(objectCategory=User))" SearchResultProperty="samAccountName" ObjectType="System.Collections.ArrayList" />

  66:   </LdapGroupAttributes>

  67: </AuthInit>' -- XML configuration for the LDAP provider, see K2 Help for more information on configuration values


  69: DECLARE @AuthSecurityProviderID UNIQUEIDENTIFIER = NEWID(); --Assigning new GUID

  70: --c84080d3-7673-4a87-9c79-42b99d39ce0a

  71: DECLARE @AuthInit XML = @XmlConfig;

  72: DECLARE @RoleSecurityProviderID UNIQUEIDENTIFIER = @AuthSecurityProviderID;

  73: DECLARE @RoleInit XML = @XmlConfig;

  74: DECLARE @DefaultLabel BIT = NULL; --1 = true, NULL and 0 = false

  75: DECLARE @ProviderClassName NVARCHAR(200) = 'SourceCode.Security.Providers.LdapProvider.Forms.Ldap';




  79: DELETE FROM [HostServer].[SecurityProvider] WHERE ProviderClassName = @ProviderClassName;

  80: DELETE FROM [HostServer].[SecurityLabel] WHERE SecurityLabelName = @SecurityLabelName;

  81: INSERT INTO [HostServer].[SecurityProvider] VALUES (@AuthSecurityProviderID, @ProviderClassName);

  82: INSERT INTO [HostServer].[SecurityLabel] VALUES (@SecurityLabelID, @SecurityLabelName, @AuthSecurityProviderID, @AuthInit, @RoleSecurityProviderID, @RoleInit, @DefaultLabel);


  84: SELECT @SPProviderID = [SecurityProviderId] FROM [HostServer].[SecurityProvider] WHERE [ProviderClassName] = N'SourceCode.Security.Providers.SharePoint.SharePointProvider';

  85: IF NOT EXISTS (SELECT 1 FROM [HostServer].[GroupProvider] WHERE [SecurityLabelID] = @SecurityLabelID)

  86: BEGIN

  87:        INSERT INTO [HostServer].[GroupProvider]

  88:        (

  89:               [GroupProviderID]

  90:               ,[SecurityLabelID]

  91:               ,[SecurityProviderID]

  92:               ,[Name]

  93:               ,[Init]

  94:        )

  95:        VALUES

  96:        (

  97:               NEWID()

  98:               ,@SecurityLabelID

  99:               ,@SPProviderID

 100:               ,'*'

 101:               ,'<init><label name="SP" /></init>'

 102:        )

 103: END


Open it within SSMS on the SQL cluster (or someplace handy). You need to have access to the K2 database in the APPS instance.

Alter the following as appropriate for the environment you are configuring (replace <DMZDOMAIN> as appropriate):



<LdapUserBaseObject>ou=external users,dc=<DMZDOMAIN></LdapUserBaseObject>

<LdapGroupBaseObject>ou=external users,dc=<DMZDOMAIN></LdapGroupBaseObject>

Then execute on the K2 database when ready.

You need to restart the K2 blackpearl service on the K2 server once completed.

5. K2 Server Configuration

Ensure the K2 Blackpearl service has been restarted. Navigate to the K2 Studio in Internet Explorer.
Open K2 Designer for the K2 server instance in the environment you are configuring.


Expand the Tree to:

All Items -> System -> Management -> Security -> Forms -> Manage Issuers

clip_image050   clip_image052

And Click “Run” and then click “New”. Add values appropriate to the environment you are configuring.


Where to get the Thumbprint? Authenticate to ADFS in the DMZ. Open a PowerShell console and type Get-ADFSCertificate:


When you’ve entered the thumbprint, save. Click on “Manage Claims” and click run.


Select “K2FORMS” from the drop list (if it is not here, you either missed the SQL part, or didn’t restart the K2 service).

Complete the rest of the fields as below with the correct domain names.


Now navigate to Manage Site Realms and click Run


Enter a site realm for the DMZ form site:


That should be it.

Now load up K2 Workspace by browsing to https://<K2Server>/workspace

Click on Management, then expand Machine Name -> User Managers -> K2 -> Domains


If the K2 server is missing, add it:


For some reason, the DMZ domain does not need to be added here J

Appendix 1 – License

1. You need to have an account in the K2 portal:

2. Authenticate to


3. Click on Support->License Key Request->License Key


Complete the license request form using the correct server names for the environment you are installing in.
Note that Anything other than production can use Non-Production server SKU.


A temporary license will be emailed to you, the proper license should arrive later.

Introducing a Gigabyte BRIX solution 7

Last week I had a disk corruption which proved to be somewhat catastrophic.  My main server had an apparent corruption of the Directory database, and even after I’d run a system restore, still could not bring Active Directory back up.  To cut a long story short, my journey would have ben far shorter if I’d had a backup directory sitting on some cheap hardware.  This brings me to my new solution..


Are you looking for a low cost computing option, but feel constrained by the hobby nature of the smaller options like the Raspberry Pi?  What if you could pay a bit extra and get something with a genuine Intel 64bit processor and support for mobile architecture hardware?  If this sounds like you, you might want to try the Gigabyte BRIX.


I’d read about these little “workstations in a box” last year, they blazed the trail for small footprint machines.  For a reasonably low price, these tiny boxes which almost fit in the palm of your hand (your hand size may vary) pack a powerful punch. 

CPU Intel® Celeron J1900 4 Core Up to 2.4GHz (1.99GHz realised)
Memory Patriot 1333GHz 8GB SO-DIMM
Disk SanDisk SSD Plus 128 GB SSD


Check out the detailed specs (note that the CPU range scales from i-series Intel to Celeron) – my version is the GB-BXBT-1900 [1] which features a dual core Celeron:

  • Features 22nm Intel® Celeron J1900 [2]
  • Ultra compact PC design – 0.69L(56.1x 107.6 x 114.4mm)
  • Supports 2.5” thickness 7.0/9.5mm Hard Drives (1 x 3Gbps)
  • 1x SO-DIMM DDR3L 1.35V Slots (1333 MHz)
  • Preinstall IEEE 802.11 b/g/n Wi-Fi / Bluetooth 4.0 Mini-PCIe card
  • Supports dual displays via a VGA and a HDMI port
  • Gigabit LAN (1 GBps) Ethernet
  • Audio jack (Headphone/MIC)
  • 1x USB 3.0, 2x USB 2.0 ports
  • ssd-plus-product  SoDimm-packaging-web

    The BIOS supports UEFI secure boot for Windows 7 and Windows 8.1, but you can disable secure boot and install via IDE configuration to get other OSes onto it.  Apparently you can run Windows 10 on one, but I have not attempted this just yet.


    When you purchase a new BRIX, it comes without a hard drive and memory, i.e. buy these separately.  Once you’ve unboxed your shiny new BRIX, you’ll need a small screwdriver to undo the base of the box to access the internals.  Installing the HDD and memory is trivial if you have only the slightest modicum of experience with electronics or laptops.

    Once you’ve got your HDD and memory seated and connected, screw the base back on (making sure that the “This Side Up” instruction is pointing the right way!).  All you need to do now is plug the BIRX into a monitor, and perhaps a keyboard and mouse. 

    I’ll cover the BIOS and OS installation in a separate article.  Here’s a nice link to a PowerShell script you can use to dump OS and hardware info.


    Once you are happy with the OS configuration, it’s entirely possible to run the BRIX “headless” (sans monitor, keyboard and mouse).  My intention was always to sit it near the network switch, and courtesy of the VESA mounting plate, it was trivial to wall mount:

    IMG_3165  IMG_3166  IMG_3164

    You can see the BRIX relative to the Router (bottom) and the HP switch (bottom left).  All that’s required is the ethernet cable and power.  It is now my little DMZ environment!