Skip to content

Windows (IIS)

Deploy RAMP to a Windows Server with IIS using Windows Integrated Authentication (Kerberos/NTLM). This is ideal for corporate Windows environments with Active Directory where users authenticate with their domain credentials.

The automated deployment script (Deploy-RAMPToHyperV.ps1) handles:

  • Building RAMP (backend + frontend)
  • Connecting to a remote VM via PowerShell Remoting
  • Installing and configuring IIS
  • Deploying application files
  • Configuring Windows Integrated Authentication
  • Setting up the database (SQLite or SQL Server)
  • Validating deployment with health checks
  • PowerShell 5.1+ or PowerShell 7+
  • RAMP source code with all dependencies
  • .NET 10 SDK for building the backend
  • Node.js 18+ for building the frontend
  • Network access to the target VM
  • Windows Server 2016+ (2019 or 2022 recommended)
  • Active Directory Domain Controller or domain-joined machine
  • IIS (installed by the script if missing)
  • ASP.NET Core 10 Runtime (Hosting Bundle) — download from dotnet.microsoft.com
  • PowerShell Remoting enabled:
Terminal window
Enable-PSRemoting -Force

Ensure these ports are open on the target VM:

PortProtocolPurpose
5985HTTPPowerShell Remoting (WinRM)
5986HTTPSPowerShell Remoting over SSL (optional)
80HTTPRAMP Web Application
443HTTPSRAMP Web Application (if using SSL)

  1. Navigate to the RAMP source directory:

    Terminal window
    cd C:\Source\RAMP
  2. Run the deployment script with default settings:

    Terminal window
    .\Deploy-RAMPToHyperV.ps1
  3. Access RAMP at http://<vm-ip-address>. You will be prompted for Windows credentials.

Default settings:

SettingDefault value
Site NameRAMP
App PoolRAMP_AppPool
Port80
DatabaseSQLite
Deployment PathC:\inetpub\RAMP

Terminal window
# Deploy to default VM
.\Deploy-RAMPToHyperV.ps1

After deployment, IIS serves from the following structure:

C:\inetpub\RAMP\
├── wwwroot\ # Frontend build output
│ ├── index.html
│ └── assets\
├── appsettings.json
├── appsettings.Production.json # Generated by script
├── RAMP.API.dll
├── RAMP.API.exe
├── web.config # Generated by script
├── logs\
│ └── stdout_*.log # IIS stdout logs
└── data\
└── ramp.db # SQLite database

The deployment script generates this file. Key sections:

{
"ConnectionStrings": {
"DefaultConnection": "Data Source=C:\\inetpub\\RAMP\\data\\ramp.db"
},
"Authentication": {
"WindowsAuthentication": {
"Enabled": true,
"AutomaticChallenge": true
},
"Jwt": {
"Issuer": "http://your-server",
"Audience": "http://your-server",
"SecretKey": "CHANGE_THIS_IN_PRODUCTION_MIN_32_CHARS"
}
},
"CORS": {
"AllowedOrigins": ["http://your-server"]
}
}

For full configuration reference, see Application Settings.

The deployment script generates web.config with the ASP.NET Core Module and Windows Authentication:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*"
modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="dotnet"
arguments=".\RAMP.API.dll"
stdoutLogEnabled="true"
stdoutLogFile=".\logs\stdout"
hostingModel="inprocess" />
<security>
<authentication>
<windowsAuthentication enabled="true">
<providers>
<add value="Negotiate" />
<add value="NTLM" />
</providers>
</windowsAuthentication>
<anonymousAuthentication enabled="false" />
</authentication>
</security>
</system.webServer>
</location>
</configuration>
SettingValue
Application PoolRAMP_AppPool
.NET CLR VersionNo Managed Code (uses .NET 10 Runtime)
Managed Pipeline ModeIntegrated
Enable 32-Bit ApplicationsFalse
IdentityApplicationPoolIdentity
Windows AuthenticationEnabled (Negotiate, NTLM)
Anonymous AuthenticationDisabled

How Windows Integrated Authentication works

Section titled “How Windows Integrated Authentication works”
  1. User navigates to the application at http://your-server.

  2. IIS challenges the browser with HTTP 401 and WWW-Authenticate: Negotiate header.

  3. Browser sends Windows credentials using Kerberos (preferred) or NTLM fallback.

  4. IIS validates credentials against Active Directory and sets HttpContext.User.Identity to the WindowsIdentity.

  5. User logs in to RAMP — the frontend calls POST /_api/auth/windows-login. The backend extracts the WindowsIdentity and maps DOMAIN\username to a RAMP user.

  6. RAMP issues JWT tokens — an access token (8-hour expiration) and a refresh token.

  7. Frontend stores tokens in localStorage and sends Authorization: Bearer <token> on subsequent requests.

The username is extracted from the DOMAIN\username format:

Windows IdentityExtracted UsernameRAMP User
CORP\john.doejohn.doejohn.doe
DOMAIN\jsmithjsmithjsmith
  1. Log in as administrator
  2. Navigate to Users page
  3. Create a user with username matching the Windows username (e.g., john.doe)
  4. Assign appropriate roles

Error: Failed to connect to <ip-address>

Solution — run these commands as Administrator on the VM:

Terminal window
Enable-PSRemoting -Force
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "<ip-address>" -Force
Restart-Service WinRM

Error: .NET 10 Runtime required

  1. Download the ASP.NET Core 10 Hosting Bundle from dotnet.microsoft.com.
  2. Install it on the VM.
  3. Restart IIS: iisreset

Cause: web.config is invalid or the ASP.NET Core Module is not installed.

Solution: Install the ASP.NET Core Hosting Bundle and restart IIS with iisreset.

HTTP 401 — Unauthorized (credentials loop)

Section titled “HTTP 401 — Unauthorized (credentials loop)”

Cause: Windows Authentication not configured correctly.

Verify IIS authentication settings:

Terminal window
# Should return True
Get-WebConfigurationProperty `
-Filter "/system.webServer/security/authentication/windowsAuthentication" `
-Name "enabled" -PSPath "IIS:\Sites\RAMP"
# Should include Negotiate and NTLM
Get-WebConfiguration `
-Filter "/system.webServer/security/authentication/windowsAuthentication/providers" `
-PSPath "IIS:\Sites\RAMP"

Symptom: Windows login succeeds but RAMP returns 401 with “User not found”.

Solution: Create the user in RAMP with a username matching the Windows username (see Creating users above).

Cause: IIS application pool identity cannot write to the database directory.

Terminal window
icacls "C:\inetpub\RAMP\data" /grant IIS_IUSRS:(OI)(CI)F
Terminal window
Get-Content "C:\inetpub\RAMP\logs\stdout_*.log" -Tail 50

  1. Use HTTPS — install an SSL certificate in IIS, bind to port 443, and force HTTPS redirect.

  2. Change the JWT secret — update Jwt:SecretKey in appsettings.Production.json with a strong, randomly generated key (minimum 32 characters).

  3. Restrict CORS origins — update CORS:AllowedOrigins to only include your production domains. Remove http://localhost:5173.

  4. Use SQL Server for production — SQLite is suitable for demos only. Configure a SQL Server connection string and run database migrations.

  5. Configure Kerberos SPNs — for Kerberos authentication, register Service Principal Names:

    Terminal window
    setspn -S HTTP/ramp.example.com DOMAIN\ServiceAccount
    setspn -S HTTP/ramp DOMAIN\ServiceAccount
  6. Use a dedicated service account — create a dedicated AD service account for the IIS App Pool with minimal required permissions.

Terminal window
# Set Idle Timeout to 0 (always running)
Set-ItemProperty "IIS:\AppPools\RAMP_AppPool" `
-Name processModel.idleTimeout -Value ([TimeSpan]::FromMinutes(0))
# Disable periodic recycling
Set-ItemProperty "IIS:\AppPools\RAMP_AppPool" `
-Name recycling.periodicRestart.time -Value ([TimeSpan]::FromMinutes(0))

Also enable IIS response compression (both static and dynamic) for better performance.

EndpointPurpose
/_healthComprehensive health check (database, Hangfire)
/_health/readyReadiness probe for load balancers
/_health/liveLiveness probe
Terminal window
# Copy database file
Copy-Item "C:\inetpub\RAMP\data\ramp.db" `
"C:\Backups\ramp_$(Get-Date -Format 'yyyyMMdd_HHmmss').db"

For high availability with IIS:

  • Deploy to multiple IIS servers behind a load balancer (IIS ARR, Azure Load Balancer, F5, etc.)
  • Use SQL Server with AlwaysOn Availability Groups for shared database
  • Configure a distributed cache (Redis) for shared session state and refresh token storage