Add XML configuration transforms (XDT) to IIS deploy (Phase 7)
Summary
Mirrors Octopus's Octopus.Features.ConfigurationTransforms feature (ConfigurationTransformsBehaviour.cs:84-101). When enabled, the deploy script applies XDT (XML Document Transform) overlays to every *.config file under WebRoot:
-
Auto-discovery:
<base>.Release.configover<base>.config -
Auto-discovery:
<base>.{EnvironmentName}.configover<base>.config -
Explicit: CSV of
transform.config => target.configentries
Pairs with Phase 6 (.NET Configuration Variables). PS1 hook order matches Octopus pipeline: ConfigurationTransforms → ConfigurationVariables → IIS configure.
What ships
Production (+200 LOC across 3 files + 1 workflow update)
| Change | Notes |
|---|---|
3 new constants in IISDeployProperties
|
ConfigurationTransformsEnabled + EnvironmentName + AdditionalTransforms
|
Builder routes AdditionalTransforms through base64 |
Multi-line CSV needs newline preservation; reused the existing base64 emission path |
Update-IISConfigurationTransforms PS1 function |
Probes for Microsoft.Web.XmlTransform.dll (GAC + NuGet global cache); auto-discovers Release + env overlays; processes explicit CSV transforms; skip-itself logic for <base>.Release
|
Workflow installs Microsoft.Web.Xdt 3.0.0
|
NuGet global cache → PS1 probe resolves it |
Test code (+435 LOC, 10 new tests across 3 tiers)
| Tier | New | What |
|---|---|---|
|
|
1 | XDT function presence + type references + auto-transform name list + ordering BEFORE ConfigurationVariables (Octopus pipeline) |
|
|
3 | 3 new properties in preamble; AdditionalTransforms via base64 (CSV with newlines preserved); empty defaults |
|
|
1 Theory × 2 | Variable substitution: #{EnvName} resolves to literal env name before serialisation |
|
|
4 | Auto Release transform; env-specific transform (Staging); explicit AdditionalTransforms with non-standard naming; feature-disabled gate |
Test infrastructure
IsMicrosoftWebXmlTransformAvailable() probe — tests skip cleanly when the DLL isn't installed (dev hosts without VS Build Tools); CI installs the package so tests run there.
Octopus alignment notes
The agent-side function mirrors ConfigurationTransformsBehaviour.cs:80-117 semantically: auto-discovery iterates Release + environment-name overlays; explicit transforms via CSV => separator; uses the IDENTICAL Microsoft.Web.XmlTransform.dll Octopus uses, so transform semantics (SetAttributes, Insert, Replace, Locator=Match/Condition/XPath) are byte-for-byte the same.
Squid Phase 7 covers Release + EnvironmentName. Octopus also auto-discovers <base>.{TenantName}.config — deferred because Squid doesn't have a tenant concept yet.
Cumulative state
- Unit: 63 → 67 (+4)
- Pipeline E2E: 12 → 14 (+2)
- Real-host: 42 → 46 (+4)
Test plan
-
dotnet test --filter FullyQualifiedName~IISDeployon macOS — 67 unit tests green -
dotnet test --filter Category=IISDeployE2Eon macOS — 46 real-host tests skip cleanly -
dotnet build0 errors -
CI run on windows-latest: workflow installs Microsoft.Web.Xdt → 4 new XDT tests against real config files
Out of scope
| Phase | Scope |
|---|---|
| 8 |
SubstituteInFiles — #{X} token replacement INSIDE files |
| 9 | JSON/YAML structured configuration variables |
| 10 | Package extraction |
| 11 | Custom installation directory + purge |
| 12 | IIS certificate auto-import + private-key ACL |
Breaking-change risk
None. Pure additive: 3 new properties, new builder routing (existing tests pass unchanged), new PS1 function + gate (no-op when disabled), new workflow step (idempotent NuGet install).