# Custom Image

## Overview

This section shows how to create your own custom WorkflowGen image in order to customize WorkflowGen's files and DLLs.

## Prerequisites

* Windows 10 Pro with Docker Desktop for Windows installed and Windows Containers enabled\
  \
  **OR**<br>
* Windows Server 2019 with Docker Enterprise installed

## Description

WorkflowGen's image provides a useful variant named `advantys/workflowgen:7.18.3-win-ltsc2019-onbuild` that can be used to easily customize any file contained in `C:\inetpub\wwwroot` or `C:\Program Files\Advantys\WorkflowGen`. All you need to do is create your own image with a Dockerfile that inherits from the **onbuild** variant and put the customized files in the Docker build context.

## Simple example

Here's a simple example that replaces the `web.config` file, customizes the banner, and adds a custom library.

{% hint style="info" %}
The `.\inetpub\wwwroot` and `.\Program Files\Advantys\WorkflowGen` paths must exist in order for the build to succeed. They don't have to have files in them, so they can be empty.
{% endhint %}

### Step 1: Add your modified files at the root of the context

Here's the file tree of the context directory from which you'll build your custom WorkflowGen image:

```
 context-dir\
      inetpub\
          wwwroot\
              web.config
              wfgen\
                  web.config
                  bin\
                      MyCustomLib.dll
                  ws\
                      bin\
                          MyCustomLib.dll
                  App_Themes\
                      Default\
                          portal\
                              banner\
                                  banner.htm
      Program Files\
          Advantys\
              WorkflowGen\
                  Services\
                      Bin\
                          MyCustomLib.dll
```

### Step 2: Add a Dockerfile to the context directory

File tree:

```
 context-dir\
      Dockerfile
      inetpub\
          wwwroot\
              web.config
              wfgen\
                  web.config
                  bin\
                      MyCustomLib.dll
                  ws\
                      bin\
                          MyCustomLib.dll
                  App_Themes\
                      Default\
                          portal\
                              banner\
                                  banner.htm
      Program Files\
          Advantys\
              WorkflowGen\
                  Services\
                      Bin\
                          MyCustomLib.dll
```

Here's the content of the Dockerfile:

```
 #escape=`
 FROM advantys/workflowgen:7.18.3-win-ltsc2019-onbuild

 # You can add any instructions in order to install other services or tools, etc.
 # You also can add other files at any other location in the container.
```

### Step 3: Build your custom WorkflowGen image

```
 Set-Location C:\Path\To\context-dir
 docker image build -t mycorporation/workflowgen:7.18.3-win-ltsc2019 .

 # Optionally, you can push your custom image to your container registry
 docker image push mycorporation/workflowgen:7.18.3-win-ltsc2019
```

**Build with Docker Compose**

With Docker Compose, you can specify the build parameters in a declarative format in order to integrate the Docker build process with the run. For example, you could have the following Compose file named `docker-compose.yml` in your context directory:

```yaml
 version: '3.7'
 services:
   workflowgen:
     build:
       context: .
       dockerfile: .\Dockerfile
     image: mycorporation/workflowgen:7.18.3-win-ltsc2019
     restart: always
     env_file:
       - '.\workflowgen.env'
     ports:
       - '8080:80'
     volumes:
       - 'wfgdata:C:\wfgen\data'
       - 'licenses:C:\wfgen\licenses:RO'
     depends_on:
       - database
   database:
     image: advantys/workflowgen-sql:7.18.3-express-win-ltsc2019
     env_file:
       - '.\database.env'
     volumes:
       - 'sqldata:C:\wfgen\sql'
 
 volumes:
   wfgdata:
   licenses:
     external: true
   sqldata:
 
```

(This file is from the [Getting started](https://docs.workflowgen.com/docker/getting-started) section.)

{% hint style="info" %}
The important section is the `build` object in the `workflowgen` service object.
{% endhint %}

Then, to build and tag automatically, you could run the following command:

```
 docker-compose build workflowgen
```

Docker Compose will then build and tag your image with the tag present in the `image` property. If you want to immediately update your Compose deployment on your local machine or immediately execute this deployment after the build, execute the following command, which will build your container in addition to running the services after the build:

```
 docker-compose up --build
```

To push the image using Docker Compose, execute the following command:

```
 docker-compose push workflowgen
```

You now have a customized WorkflowGen image. For more information on updating your container when a new WorkflowGen version is available, see the [Update Management](https://docs.workflowgen.com/docker/update-management) section.

## Example with legacy custom WorkflowGen web application/procedure

Let's say in addition to DLLs and custom banners you have an ASP.NET 2.0 web application to add to the `wfapps` folder and configure in IIS. You would perform the same steps as before, but also add a custom Docker CMD script that will configure the application in IIS.

Multiple PowerShell scripts come with the WorkflowGen image and have different purposes:

* `docker-entrypoint.ps1`: This is the main script that gets executed when you run a container. It handles the parsing of environment variables, configuration of authentication methods, etc.
* `monitor-services.ps1`: This script handles the monitoring of processes (IIS and Windows services), as well as gathering the logs of the container and redirecting them to the standard output.
* `healthcheck.ps1`: This handles the periodic check that indicates if WorkflowGen is working properly or not. This is defined in the WorkflowGen Dockerfile and is handled by the Docker engine.
* `*.psm1`: Various developed PowerShell modules available in the image.
* `ServiceMonitor.exe`: Binary executable provided by Microsoft. This is the main executable used by the monitoring script to check the state of a service. (You can find more information about ServiceMonitor on its GitHub page at [microsoft/IIS.ServiceMonitor](https://github.com/Microsoft/IIS.ServiceMonitor)).
* `set-state.ps1`: Sets the state of the container, such as bringing the website offline or online.

By creating your own WorkflowGen image in Docker, you can replace any script with your own that does something else completely, and it's a best practice to do so if the stock scripts don't do what you want. In the case of this example, the stock scripts don't take care of setting the added web application as in IIS; you need to develop your own script for this. Here's the one that you'll use in this example:

```
<#
.SYNOPSIS
    Add the custom web application to IIS.
.NOTES
    File name: custom-web-app-install.ps1
#>
#requires -Version 5.1
Import-Module IISAdministration
Import-Module C:\Const.psm1 -Variable Constants
Import-Module C:\Utils.psm1 -Fonction "Get-EnvVar"

$wfgenStartService = (Get-EnvVar "WFGEN_START_SERVICE" -DefaultValue "all").ToLower()
$isWebServices = $wfgenStartService -eq $Constants.SERVICE_ALL -or
    $wfgenStartService -eq $Constants.SERVICE_WEB_APPS

if ($isWebServices -and (Get-WebApplication -Site "wfgenroot" -Name "wfgen/wfapps/<YOUR_WEB_SERVICE_NAME>").Count -le 0) {
    Write-Host "CONFIG: Adding mywebapp to IIS ... " -NoNewLine
    ConvertTo-WebApplication -PSPath "IIS:\Sites\wfgenroot\wfgen\WfApps\<YOUR_WEB_SERVICE_NAME>" | Out-Null
    Write-Host "done" -ForegroundColor Green
}

if ($args.Count -gt 0) {
    Invoke-Expression ($args -join " ")
} else {
    . C:\monitor-services.ps1
}
```

{% hint style="info" %}
Replace `<YOUR_WEB_SERVICE_NAME>` with the name of your web service that you added in the `wfapps` folder.
{% endhint %}

In this script, note the following:

1. You're checking for the start service type and checking if you've already added the web service as a web application in IIS.<br>

   **Rationale**\
   \
   This is because the entrypoint script is re-run between restarts of the container. Therefore, this script will also be re-run after a restart. When restarting a container, the IIS configuration stays and the `ConvertTo-WebApplication` command will cause the start procedure to fail when trying to add an already-added web application. Consequently, you have to check that you haven't already added the application.<br>

   Also, you're checking if the container is running in web applications mode. When it isn't, you don't need to add your web service in IIS because there will be only Windows services that will run.<br>
2. You're checking for arguments and executing them if there are some. If not, you start to monitor the container's services.<br>

   **Rationale**<br>

   This is a general good practice in a Docker container. For example, if you're debugging the container and only want to prompt a PowerShell command line after the start sequence, you would pass `powershell` as an argument to the run command like so:

   ```
    docker container run -it `
        # ...
        mycorporation/workflowgen:7.18.3-win-ltsc2019 C:\custom-web-app-install.ps1 powershell
   ```

   The `powershell` argument will be executed by the `Invoke-Expression` command and a new PowerShell command prompt will be displayed. If no arguments are passed, the default behavior is to start monitoring the services. Since the image already has a script for that, you only need to execute it.

Your Dockerfile will then be as follows:

```
#escape=`
FROM advantys/workflowgen:7.18.3-win-ltsc2019-onbuild

SHELL ["powershell", "-Command"] 

COPY .\custom-web-app-install.ps1 C:\
CMD C:\custom-web-app-install.ps1
```

After that, you'll perform the same build and push steps as the previous example.
