Add .NET Configuration Variables rewriter to IIS deploy (Phase 6)
Summary
Mirrors Octopus's Octopus.Features.ConfigurationVariables feature (Calamari.Common/Features/Behaviours/ConfigurationVariablesBehaviour.cs:15-81) — the agent walks every *.config file under WebRoot and replaces matching appSettings, applicationSettings, and connectionStrings entries with values from the deployment's variable set.
This is the operator-facing ".NET Configuration Variables" checkbox on the Octopus IIS step UI. After Phase 6, 5 of 7 UI sections from the Octopus IIS step are end-to-end covered by Squid:
| UI section | Status |
|---|---|
| Web Site |
|
| Application Pool |
|
| Bindings |
|
| IIS Authentication |
|
| .NET Configuration Variables |
|
| Package selection |
|
| Physical path "Package installation directory" mode |
|
What ships
Production (+175 LOC across 4 files)
| File | Change |
|---|---|
IISDeployProperties.cs |
New ConfigurationVariablesEnabled constant — matches operator-facing UI checkbox |
IISDeployScriptBuilder.cs |
New Build(action, variables) overload — ships deployment variable set as $SquidVariables hashtable. Always emitted (even when empty) so future features (SubstituteInFiles, StructuredConfigurationVariables) reuse the same channel |
IISDeployActionHandler.cs |
DescribeIntentAsync now passes ctx.Variables through to the builder |
DeployToIISWebSite.ps1 |
New Update-IISConfigurationVariables function with 3 XPath probes (appSettings/add@key, connectionStrings/add@name, applicationSettings//setting@name). Runs AFTER PreDeploy + BEFORE IIS configure dispatch |
Test code (+466 LOC, 11 new tests across 3 tiers)
| Tier | New | What |
|---|---|---|
| Drift detector | 1 | Pins Update-IISConfigurationVariables function + all 3 XPath probes + enablement-gate placement (after PreDeploy, before IIS configure) |
| Unit | 5 |
$SquidVariables hashtable emission (empty + populated); positional ordering; escape rules for variable values; empty-name defensive skip |
| Pipeline E2E | 1 Theory × 2 + 1 Fact | Variables flow through pipeline → captured request (incl. sensitive); $SquidVariables always ships (even when feature off) so future phases reuse |
| Real-host | 4 | Real web.config stage → deploy → assert XML attribute replaced: appSettings value, connectionString value (sibling providerName survives), feature-disabled gate, no-matching-variable orphan pass-through |
Octopus alignment notes
The agent-side rewriter mirrors ConfigurationVariablesBehaviour.cs:15-81 semantically. Same 3 XPath probes, same key/name match against variable set, same replacement scope. Squid embeds the rewriter inside the IIS deploy script (no DeployPackageCommand orchestrator yet); Octopus runs it as a separate convention. Operator-facing contract is byte-identical — operators carrying Octopus variable specs see same behaviour.
Sensitive variables flow through plaintext — same as Octopus. Script body is encrypted in Halibut TLS transit; rendered config file on disk contains plaintext. Log masking (Squid's Serilog masker) handles the sensitive-in-logs case separately.
Cumulative coverage state
- Unit: 56 → 63 (+7)
- Pipeline E2E: 9 → 12 (+3)
- Real-host: 36 → 40 (+4)
Test plan
-
dotnet test --filter FullyQualifiedName~IISDeployon macOS — 63 unit tests green -
dotnet test --filter Category=IISDeployE2Eon macOS — 40 real-host tests skip cleanly -
dotnet build0 errors -
CI run on windows-latest: 4 new real-host tests against real web.config files
Out of scope (remaining phases)
| Phase | Scope |
|---|---|
| 7 | XML config transforms (XDT) — web.Release.config
|
| 8 | Variable substitution INTO files (SubstituteInFiles) — #{X} tokens |
| 9 | JSON/YAML structured configuration variables |
| 10 | Package extraction (NuGet/Zip) — unblocks Physical path "Package installation directory" UI mode |
| 11 | Custom installation directory + purge |
| 12 | IIS certificate auto-import + private-key ACL |
Breaking-change risk
None. Pure additive: new property, new builder overload (1-arg preserved), new PS1 function + gate (no-op when disabled). All existing tests unchanged.