This repository contains a task driver for HashiCorp Nomad to run web-applications in IIS on Windows machines. Unlike most other Nomad task drivers, this one is written in the C# language using ASP.NET 8. It uses the Microsoft.Web.Administration-API to communicate with IIS. Feel free to use it as-is or as a reference implementation for your own C#-based Nomad-plugins.
Feature | Status | Details |
---|---|---|
Single Web App per Nomad Task | โ | The Task Driver creates an IIS Application Pool and Website for every Nomad Task in the job specification. |
Multiple Applications | โ | Support for multiple sub-applications below the website. |
Virtual Directories | โ | Support for multiple virtual directories below an application. |
HTTP Bindings | โ | |
HTTPS Bindings | โ | GH-3 |
Environment Variables | โ | Details |
Resource Statistics | โ | |
Logging | โ | Experimental UDP logging. See GH-6 for details. |
Signals with nomad alloc signal |
โ | Details |
Exec (Shell Access) | โ | I'am playing around a little bit but don't want to give you hope :/. See GH-15 for status. |
Filesystem Isolation | ๐ถ | Details |
Nomad Networking | โ |
- Windows Server 2016+
- Microsoft IIS 10.0+
Option | Type | Required | Default Value | Description |
---|---|---|---|---|
enabled | bool | no | true | Enables/Disables the Nomad IIS Plugin |
fingerprint_interval | string | no | 30s | Defines the interval how often the plugin should report the driver's fingerprint to Nomad. The smallest possible value is 10s. |
directory_security | bool | no | true | Enables Directory Permission Management for Filesystem Isolation. |
allowed_target_websites | string[] | no | none | A list of IIS websites which are allowed to be used as target_website. An asterisk (*) may be used as a wildcard to allow any website. |
udp_logger_port | number | no | 64001 | The local UDP port where the driver is listening for log-events which will be shipped to the Nomad client. The value 0 will disable this feature. Please read the details here. |
Example
plugin "nomad_iis" {
config {
enabled = true,
fingerprint_interval = "30s",
directory_security = true
allowed_target_websites = [ "Default Web Site" ]
}
}
Option | Type | Required | Default Value | Description |
---|---|---|---|---|
application | block list | yes | none | Defines one more applications. See application schema below for details. |
target_website | string | no | none | Specifies an existing target website. In this case the driver will not create a new website but instead use the existing one where it provisions the virtual applications only. Please read the details here. |
managed_pipeline_mode | string | no | IIS default | Valid options are Integrated or Classic |
managed_runtime_version | string | no | IIS default | Valid options are v4.0, v2.0, None |
start_mode | string | no | IIS default | Valid options are OnDemand or AlwaysRunning |
idle_timeout | string | no | IIS default | The AppPool idle timeout in the form HH:mm:ss or [00w][00d][00h][00m][00s] |
disable_overlapped_recycle | bool | no | IIS default | Defines whether two AppPools are allowed to run while recycling |
periodic_restart | string | no | IIS default | The AppPool periodic restart interval in the form HH:mm:ss or [00w][00d][00h][00m][00s] |
enable_udp_logging | bool | no | false | Enables a UDP log-sink your application can log to. Please read the details here. |
binding | block list | yes | none | Defines one or two port bindings. See binding schema below for details. |
Option | Type | Required | Default Value | Description |
---|---|---|---|---|
path | string | yes | none | Defines the path of the web application, containing the application files |
alias | string | no | / | Defines an optional alias at which the application should be hosted below the website. If not set, the application will be hosted at the website level. |
enable_preload | bool | no | IIS default | Specifies whether the application should be pre-loaded. |
virtual_directory | block list | no | none | Defines optional virtual directories below this application. See virtual_directory schema below for details. |
Option | Type | Required | Default Value | Description |
---|---|---|---|---|
alias | string | yes | none | Defines the alias of the virtual directory |
path | string | yes | none | Defines the path of the virtual directory |
Option | Type | Required | Default Value | Description |
---|---|---|---|---|
type | string | yes | none | Defines the protocol of the port binding. Allowed values are http or https. |
port | string | yes | none | Defines the port label of a network block configuration or a static port like "80". Static ports can only be used when hostname is also set. Otherwise use a nomad network-stanza to specify the port. |
hostname | string | no | IIS default | Only listens to the specified hostname |
require_sni | bool | no | IIS default | Defines whether SNI (Server Name Indication) is required |
ip_address | string | no | IIS default | Specifies the IP-Address of the interface to listen on |
certificate_hash | string | no | none | Specifies the hash of the certificate to use when using type=https |
Example
job "iis-test" {
datacenters = ["dc1"]
type = "service"
group "iis-test" {
count = 1
# You may want to set this to true
# prevent_reschedule_on_lost = true
network {
port "httplabel" {}
}
task "iis-test" {
driver = "iis"
config {
application {
path = "C:\\inetpub\\wwwroot"
}
binding {
type = "http"
port = "httplabel"
}
}
resources {
cpu = 100
memory = 20
}
}
}
}
All System Environment Variables available to the Nomad Client will be applied to the Application Pool.
You can supply additional ones by using the env
Block in the task
stanza.
The Nomad IIS driver supports the following signals:
Signal | Description |
---|---|
SIGHUP or RECYCLE |
Recycles the Application Pool |
SIGINT or SIGKILL |
Stops and removes the Application. Note: When sending this signal manually, the job gets re-scheduled. |
To send a RECYCLE signal, run:
nomad alloc signal -s RECYCLE <allocation> <task>
Details about the command can be found here.
Because there is no chroot
on Windows, filesystem isolation is only handled via permissions.
For every AppPool, IIS creates a dedicated AppPool Service Account which is only allowed to access it's own directories. See commits of GH-5 for details.
Given a job spec with two tasks, the following table depicts the permissions for each AppPool task1 and task2 inside the allocation directory.
Directory | Access Level |
---|---|
/alloc |
No Access |
/alloc/data |
Full Access for task1 and task2 |
/alloc/logs |
Full Access for task1 and task2 |
/alloc/tmp |
Full Access for task1 and task2 |
/task1/local |
Full Access for task1 |
/task1/private |
No Access |
/task1/secrets |
Read Only for task1, No Access for task2, no file listing |
/task1/tmp |
Full Access for task1 |
/task2/local |
Full Access for task2 |
/task2/private |
No Access |
/task2/secrets |
Read Only for task2, No Access for task1, no file listing |
/task2/tmp |
Full Access for task2 |
By specifying a target_website in the task configuration you can re-use an existing website managed outside of nomad. In this case the driver will not create a new website but instead use the existing one where it provisions the virtual applications only.
Note that there're a few restrictions when using a target_website:
- The feature needs to be enabled.
- Re-using an existing website managed by nomad (owned by a different job or task), is not allowed.
- Bindings and other website-related configuration will have no effect.
- You need to make sure you constrain your jobs to nodes having this target_website available, otherwise the job will fail.
- You cannot create a root-application when using a target_website.
Unfortunately, IIS doesn't attach a Console to the w3wp processes and therefore STDOUT and STDERR streams are not available. As a solution, nomad-iis can provide a UDP-endpoint and ship those log messages to the Nomad-Client.
The UDP log-sink exposes two more environment variables:
Name | Description |
---|---|
NOMAD_STDOUT_UDP_LOCAL_PORT |
The local port the appender has to use. Only messages from this port get received and forwarded to nomad. |
NOMAD_STDOUT_UDP_REMOTE_PORT |
The remote port of the log-sink where log events must be sent to. |
Please note, that you need to configure your app's logging provider to log to this UDP endpoint. Here is an example log4net-appender on how to log to the UDP log-sink:
<appender name="UdpAppender" type="log4net.Appender.UdpAppender">
<localPort value="${NOMAD_STDOUT_UDP_LOCAL_PORT}" />
<remoteAddress value="127.0.0.1" />
<remotePort value="${NOMAD_STDOUT_UDP_REMOTE_PORT}" />
<layout type="log4net.Layout.PatternLayout, log4net">
<conversionPattern value="%d{dd.MM.yy HH:mm:ss.fff} %-5p [%-8t] %-35logger - %m%newline" />
</layout>
</appender>
Run the setup command to download the nomad binary.
.\setup.ps1
Build the project by running the following command:
cd src
dotnet build
Of course you can also compile with Visual Studio :)
There is a launch-profile to run nomad in dev-mode which automatically loads the driver plugin. Open Visual Studio, select the Nomad launch profile and press F5.
Note: To debug the driver itself, you need to attach the debugger to the nomad_iis.exe process manually.
Run the Release.pubxml publish profile from Visual Studio. This will create a single binary exe called nomad_iis.exe.
Check the Open Issues here.
You want to support me?