Report 28: Configuration & Infrastructure Comparison (V8 vs V17)¶
Generated: 2026-02-27 | Scope: Web.config, appsettings, Startup/Program.cs, CI/CD
1. Overview¶
| Aspect | V8 | V17 | Change Impact |
|---|---|---|---|
| Configuration format | Web.config (XML) |
appsettings.json (JSON) |
Complete rewrite |
| Config transforms | 353 Web.*.config files |
6 appsettings.*.json files |
345+ transforms need client-specific JSON |
| Startup | Startup.cs (OWIN) |
Program.cs (minimal API) |
Complete rewrite |
| DI framework | Current.Factory (Lightinject) |
builder.Services (MS DI) |
All registrations rewritten |
| Build pipeline | MSBuild + NuGet + VSBuild | dotnet build + Docker |
Windows -> Linux containers |
| Deploy pipeline | Azure WebApps (IIS) | Docker + SSH deploy | IIS -> Kestrel/Docker |
| CI/CD scripts | 15 PowerShell scripts | Dockerfile + 1 YAML pipeline | Simplified |
2. Configuration: Web.config vs appsettings.json¶
2.1 V8 Web.config Structure¶
The v8 Web.config is a monolithic XML file containing:
- ASP.NET configuration sections (compilation, httpRuntime, authentication)
- Umbraco-specific config references (umbracoSettings, ClientDependency, ImageProcessor)
- AppSettings key-value pairs (Umbraco core, ModelsBuilder, notifications, captcha)
- Connection strings
- IIS system.webServer configuration (modules, handlers, URL rewrite rules)
<!-- V8 Web.config excerpt -->
<appSettings>
<add key="Umbraco.Core.ConfigurationStatus" value="8.18.15" />
<add key="Umbraco.Core.Path" value="~/office" />
<add key="Umbraco.ModelsBuilder.ModelsMode" value="LiveAppData" />
<add key="notificationsCountry" value="Ireland" />
<add key="notificationsCuName" value="ProgressCreditUnion" />
<add key="FriendlyCaptchaSecret" value="A1VK6UTS..." />
<add key="OpenModulesUrl" value="https://api-g1.progress.ie/open-modules/test2" />
</appSettings>
<connectionStrings>
<add name="umbracoDbDSN" connectionString="Server=cb-sql.double.pt,7071;..." />
</connectionStrings>
2.2 V17 appsettings.json Structure¶
The v17 configuration uses the ASP.NET Core hierarchical JSON pattern:
{
"Serilog": {
"MinimumLevel": { "Default": "Debug" }
},
"ConnectionStrings": {
"umbracoDbDSN": "Server=sql.double.pt,1433;Database=dbl_progress_cms_staging;..."
},
"Umbraco": {
"CMS": {
"ModelsBuilder": { "ModelsMode": "Nothing" },
"Global": { "UseHttps": true },
"Security": { "AllowConcurrentLogins": true }
}
},
"OpenModulesUrl": "https://api-g1.progress.ie/open-modules/test2",
"FriendlyCaptcha": {
"Secret": "A1VK6UTS...",
"SiteKey": "FCMHJ1UO5UEIAM2H",
"ApiUrl": "https://global.frcapi.com/api/v2/captcha/siteverify"
},
"ProgressSettings": {
"GoogleMapsApiKey": "AIzaSyBK6...",
"Notifications": {
"OnlineBanking": "true",
"Country": "Ireland",
"CuName": "Progress"
}
}
}
2.3 Side-by-Side Comparison¶
| Setting | V8 (Web.config) | V17 (appsettings.json) |
|---|---|---|
| DB connection | <connectionStrings><add name="umbracoDbDSN" .../> |
"ConnectionStrings": { "umbracoDbDSN": "..." } |
| Umbraco path | <add key="Umbraco.Core.Path" value="~/office"/> |
Not needed (v17 uses /umbraco/) |
| ModelsBuilder | <add key="Umbraco.ModelsBuilder.ModelsMode" value="LiveAppData"/> |
"Umbraco:CMS:ModelsBuilder:ModelsMode": "Nothing" |
| HTTPS | <add key="Umbraco.Core.UseHttps" value="false"/> |
"Umbraco:CMS:Global:UseHttps": true |
| Notifications | Flat keys: notificationsCountry, notificationsCuName |
Nested: ProgressSettings:Notifications:Country |
| Captcha | Flat keys: FriendlyCaptchaSecret, FriendlyCaptchaSiteKey |
Nested: FriendlyCaptcha:Secret, FriendlyCaptcha:SiteKey |
| uSync | <add key="uSync.ImportAtStartup" value="false"/> |
"uSync:Sets:Default:Enabled": false |
| Logging | log4net in config/log4net.config |
Serilog in appsettings.json |
2.4 V17 appsettings Files (13 total)¶
| File | Purpose |
|---|---|
appsettings.json |
Base configuration (shared defaults) |
appsettings.Development.json |
Local development overrides |
appsettings.progress.json |
Progress CU production config |
appsettings.clontarffc.json |
Clontarf FC client-specific config |
appsettings.cuisosolutions.json |
CU ISO Solutions client config |
appsettings.Migration.json |
Migration tool runtime config |
appsettings.TestMigration.json |
Test migration config |
appsettings.CuisoMigration.json |
CU ISO migration-specific config |
appsettings-schema.json |
JSON schema for validation |
appsettings-schema.Umbraco.Cms.json |
Umbraco CMS schema |
appsettings-schema.Umbraco.Forms.json |
Umbraco Forms schema |
appsettings-schema.Umbraco.Licenses.json |
Licensing schema |
appsettings-schema.usync.json |
uSync schema |
3. Startup.cs vs Program.cs¶
3.1 V8 Startup.cs (OWIN)¶
The v8 startup is minimal because most DI registration happened via Umbraco's composer system and static Current.Factory:
// V8: Startup.cs (8 lines of actual logic)
[assembly: OwinStartup(typeof(Progress.Web.Startup))]
namespace Progress.Web
{
public partial class Startup : UmbracoDefaultOwinStartup
{
public override void Configuration(IAppBuilder app)
{
OwinConfiguration.ConfigureAuth(app);
ServicePointManager.ServerCertificateValidationCallback += (s, c, ch, e) => true;
base.Configuration(app);
}
}
}
3.2 V17 Program.cs (Minimal API)¶
The v17 startup is the complete application composition root:
// V17: Program.cs (97 lines)
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
// Pre-startup services
DatabaseCleanupService.CleanupDatabase(builder.Configuration); // conditional
CacheCleanupService.CleanupCache(builder.Configuration); // conditional
// DI registrations
builder.Services.AddProgressBaselineCore(); // ISiteSettingsService
builder.Services.AddScoped<IDictionaryService, DictionaryService>();
builder.Services.AddScoped<IArticleService, ArticleService>();
builder.Services.AddScoped<ICalculatorService, CalculatorService>();
builder.Services.AddScoped<ILoanCalculatorEstimator, LoanCalculatorEstimator>();
builder.Services.AddScoped<IHeadlessContentService, HeadlessContentService>();
builder.Services.AddScoped<IGlobalNotificationsService, GlobalNotificationsService>();
builder.Services.AddHttpClient();
builder.Services.AddControllers();
builder.Services.AddSingleton<IBackOfficeTwoFactorOptions, TwoFactorUserGroupOptions>();
// Umbraco pipeline
builder.CreateUmbracoBuilder().AddBackOffice().AddWebsite().AddComposers().Build();
WebApplication app = builder.Build();
// Middleware
app.UseForwardedHeaders(...);
app.BootUmbracoAsync();
// URL rewriting (replaces IIS URL rewrite rules)
app.UseRewriter(new RewriteOptions()
.AddRewrite(@"^news/category/(.*)/?$", "news-events?newsCategory=$1", true)
.AddRewrite(@"^sitemap\.xml$", "sitemap", true));
// Umbraco middleware + endpoints
app.UseUmbraco()
.WithMiddleware(u => { u.UseBackOffice(); u.UseWebsite(); })
.WithEndpoints(u => { u.UseBackOfficeEndpoints(); u.UseWebsiteEndpoints();
u.EndpointRouteBuilder.MapControllers(); });
await app.RunAsync();
3.3 Key DI Registration Differences¶
| Service | V8 Registration | V17 Registration |
|---|---|---|
| Site settings | Current.Factory.GetInstance<>() (static) |
builder.Services.AddScoped<ISiteSettingsService, SiteSettingsService>() |
| Article service | Current.Factory.GetInstance<IArticleService>() |
builder.Services.AddScoped<IArticleService, ArticleService>() |
| Calculator | Inline in SurfaceController | builder.Services.AddScoped<ILoanCalculatorEstimator, LoanCalculatorEstimator>() |
| Dictionary | XPath queries in views | builder.Services.AddScoped<IDictionaryService, DictionaryService>() |
| HTTP clients | System.Net.WebClient |
builder.Services.AddHttpClient() |
| 2FA | N/A (no 2FA in v8) | builder.Services.AddSingleton<IBackOfficeTwoFactorOptions, TwoFactorUserGroupOptions>() |
| Auth | OWIN cookie auth | Umbraco built-in + TwoFactorAuthComposer |
| Logging | log4net | Serilog (configured in appsettings.json) |
| URL rewrite | IIS web.config <rewrite> rules |
app.UseRewriter() middleware |
4. CI/CD Comparison¶
4.1 V8 Build Pipeline¶
Platform: Azure DevOps, Windows agent (vmImage: windows-latest)
# V8: azure-build-pipelines-webApps.yml
pool:
vmImage: 'windows-latest'
steps:
- task: gitversion/setup@3 # Semantic versioning
- task: NuGetToolInstaller@1 # NuGet CLI
- task: Cache@2 # NuGet package cache
- task: NuGetCommand@2 # NuGet restore
- task: VSBuild@1 # MSBuild via Visual Studio
inputs:
msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package ...'
Key characteristics:
- Full .NET Framework build using VSBuild (not dotnet build)
- WebDeploy packaging for Azure App Service deployment
- NuGet restore with caching
- GitVersion for semantic versioning
- Produces a .zip deployment package
4.2 V8 Deployment Scripts (15 PowerShell scripts)¶
| Script | Purpose |
|---|---|
preUmbracoDeployment.ps1 |
Pre-deploy checks and preparation |
postUmbracoDeployment.ps1 |
Post-deploy validation |
RenameUmbracoFolderOffice.ps1 |
Rename /umbraco to /office (security) |
RemoveUnusedConfigTransformFiles.ps1 |
Clean up unused config transforms |
RemoveuSyncFolder.ps1 |
Clean uSync files post-deploy |
Copy51DegreesFile.ps1 |
Copy device detection database |
IncrementClientDependency.ps1 |
Bust client-side caches |
UmbracoErrorPage.ps1 |
Set up custom error pages |
CleanUp.ps1 |
General deployment cleanup |
ExecRemoteScript.ps1 |
Execute scripts on remote server |
addEnvironmentTags.ps1 |
Tag deployment with environment info |
exportPipelineVariables.ps1 |
Export build variables |
importPipelineVariables.ps1 |
Import build variables |
setRunName.ps1 |
Set Azure DevOps run name |
updateCreditUnionsList.ps1 |
Update client list for multi-tenant deploy |
4.3 V17 Build & Deploy Pipeline¶
Platform: Azure DevOps, Linux agent (vmImage: ubuntu-latest) + Docker
# V17: azure-deploy-uat.yml
trigger:
- main
stages:
- stage: Build
jobs:
- job: Build
pool:
vmImage: ubuntu-latest
steps:
- task: Docker@2 # Build Docker image
inputs:
command: "build"
Dockerfile: "**/Dockerfile"
- task: Docker@2 # Push to registry
inputs:
command: push
repository: $(imageUmbraco)
containerRegistry: $(containerRegistry)
- task: SSH@0 # Deploy via SSH
inputs:
inline: |
cd /docker/clients/progress
docker-compose pull
docker-compose up -d
4.4 V17 Dockerfile¶
The Dockerfile is a multi-stage build:
# Stage 1: Build
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
# Install Node.js 18 for custom property editor TypeScript build
RUN apt-get install -y nodejs
# Restore, copy, publish
RUN dotnet publish src/www/www.csproj -c ${CONFIGURATION} -o /app/publish
# Stage 2: Runtime
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS final
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "www.dll"]
4.5 Pipeline Comparison Summary¶
| Aspect | V8 | V17 |
|---|---|---|
| Agent OS | Windows | Linux (Ubuntu) |
| Build tool | MSBuild (VSBuild task) | dotnet publish |
| Package format | WebDeploy .zip |
Docker image |
| Registry | Azure App Service | Private Docker registry (registry.double.pt) |
| Deploy target | Azure App Service (IIS) | Docker Compose on Linux VM |
| Deploy method | WebDeploy / Azure CLI | SSH + docker-compose up -d |
| Scripts needed | 15 PowerShell scripts | 0 (all in Dockerfile) |
| Node.js build | Not needed | Required (Vite/TypeScript for custom editors) |
| Config transforms | 353 Web.config transforms | Environment variables / per-client appsettings |
5. Per-Client Configuration Gap¶
5.1 The Problem¶
V8 has 353 Web.config transform files (Web.{ClientName}.{Environment}.config), each containing client-specific overrides for:
- Database connection strings
- Notification settings (CU name, country, county)
- uSync import/export flags
- Captcha keys
- API endpoints
- Feature flags
Example (Web.1stclasscu.Prod.config):
<appSettings xdt:Transform="Replace">
<add key="notificationsCountry" value="United Kingdom" />
<add key="notificationsCounty" value="Glasgow" />
<add key="notificationsCuName" value="1stClassCreditUnion" />
<add key="uSync.ImportAtStartup" value="true" />
<!-- ... 60+ more keys ... -->
</appSettings>
<connectionStrings>
<add name="umbracoDbDSN" connectionString="Server=...;Database=1stclasscu;..." />
</connectionStrings>
5.2 V17 Current State¶
Only 4 client-specific appsettings exist:
| File | Client |
|---|---|
appsettings.progress.json |
Progress CU (IE) |
appsettings.clontarffc.json |
Clontarf FC (IE) |
appsettings.cuisosolutions.json |
CU ISO Solutions |
appsettings.CuisoMigration.json |
CU ISO (migration variant) |
5.3 Migration Path¶
Each of the 353 transforms needs to become a per-client appsettings.{client}.json file. The pattern is:
{
"ConnectionStrings": {
"umbracoDbDSN": "Server=...;Database={client_db};..."
},
"ProgressSettings": {
"Notifications": {
"Country": "United Kingdom",
"County": "Glasgow",
"CuName": "1stClassCreditUnion"
}
}
}
The ASPNETCORE_ENVIRONMENT variable selects which appsettings file is loaded. In Docker deployments, this is set per-container in docker-compose.yml.
Gap: ~170 client configs not yet created
This is a mechanical task that can be automated: parse each Web.{client}.{env}.config XML file, extract the key-value pairs, and generate the equivalent appsettings.{client}.json. A script for this should be part of the Migration Orchestrator plan.
6. Summary¶
| Area | Migration Status | Notes |
|---|---|---|
Base config (Web.config -> appsettings.json) |
DONE | Core settings migrated |
Startup (Startup.cs -> Program.cs) |
DONE | All DI registrations, middleware, URL rewrite |
| Build pipeline (MSBuild -> Docker) | DONE | Multi-stage Dockerfile with Node.js |
| Deploy pipeline (WebDeploy -> Docker Compose) | DONE | SSH-based docker-compose deploy |
| Per-client configs (353 transforms) | 4/~170 DONE | Major gap -- needs automation |
| PowerShell scripts (15 scripts) | OBSOLETE | No longer needed with Docker |
| IIS URL rewrite rules | DONE | Converted to app.UseRewriter() |
| Logging (log4net -> Serilog) | DONE | Configured in appsettings.json |
Key Architectural Wins in V17¶
- Containerized deployment: No more IIS dependency; runs on any Docker host
- Simplified build: 1 Dockerfile replaces MSBuild + NuGet + 15 PowerShell scripts
- Structured configuration: Hierarchical JSON with schema validation replaces flat XML keys
- Native DI: Microsoft.Extensions.DependencyInjection replaces static
Current.Factorycalls - Cross-platform: Linux-based build agent and runtime (was Windows-only)
Remaining Actions¶
| Priority | Action | Effort |
|---|---|---|
| P1 | Generate ~170 client-specific appsettings.{client}.json from v8 transforms |
4h (scripted) |
| P2 | Create per-client docker-compose.yml templates |
2h |
| P3 | Add health check endpoint for container orchestration | 1h |
| P3 | Add staging/production pipeline variants | 2h |