Skip to content

Commit 7364bf4

Browse files
robgrameCopilot
andcommitted
feat(hybridagent): add TargetOUs to static config + deploy script
AdScannerService reads RuntimeConfig.TargetOUs (multi-OU surface) but the field was missing from ScanConfiguration, so SeedFromStatic never projected it. In static-only mode (HybridAgent) the producer could only scan the single LdapSearchBase, losing parity with the on-prem Worker's portal-edited TargetOUs list. - ScanConfiguration.TargetOUs added (mirrors RuntimeConfig.TargetOUs) - SeedFromStatic projects it through - Divergence-warning helper compares it (no-op in static mode but useful when same Core lib is hosted with a DB) - HybridAgent appsettings.json placeholder + doc comment - Deploy-HybridAgent.ps1 -TargetOUs param + import-list entry Tests: 86 Core + 15 Functions + 8 HybridAgent pass; 86 on-prem Core unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent a26439d commit 7364bf4

4 files changed

Lines changed: 19 additions & 1 deletion

File tree

Deploy-HybridAgent.ps1

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ param(
122122
[string]$ImportFromWorkerAppSettings,
123123
[string]$LdapServer,
124124
[string]$LdapSearchBase,
125+
# Optional newline- or comma-separated list of OU DNs to scan. Maps to ScanSettings:TargetOUs.
126+
# When empty, the producer scans LdapSearchBase as a single root.
127+
[string]$TargetOUs,
125128
[int]$LdapPort,
126129
[Nullable[bool]]$LdapUseSsl,
127130
[string]$CronSchedule,
@@ -342,7 +345,7 @@ try {
342345
# ScanSettings: pull the AD-relevant fields only — Graph/Entra fields are intentionally
343346
# skipped because Entra scanning lives on the Azure Functions side of the deployment.
344347
$adFields = @(
345-
'CronSchedule','LdapServer','LdapSearchBase','LdapPort','LdapUseSsl',
348+
'CronSchedule','LdapServer','LdapSearchBase','TargetOUs','LdapPort','LdapUseSsl',
346349
'IncludeRecoveryKeyValue','MaxRetryAttempts','BaseRetryDelaySeconds',
347350
'CsvOutputPath','KeyValueExportPath','RetirementGraceDays'
348351
)
@@ -382,6 +385,7 @@ try {
382385
if ($PSBoundParameters.ContainsKey('CronSchedule')) { $scanSettings['CronSchedule'] = $CronSchedule }
383386
if ($PSBoundParameters.ContainsKey('LdapServer')) { $scanSettings['LdapServer'] = $LdapServer }
384387
if ($PSBoundParameters.ContainsKey('LdapSearchBase')) { $scanSettings['LdapSearchBase'] = $LdapSearchBase }
388+
if ($PSBoundParameters.ContainsKey('TargetOUs')) { $scanSettings['TargetOUs'] = $TargetOUs }
385389
if ($PSBoundParameters.ContainsKey('LdapPort')) { $scanSettings['LdapPort'] = $LdapPort }
386390
if ($PSBoundParameters.ContainsKey('LdapUseSsl') -and $null -ne $LdapUseSsl) {
387391
$scanSettings['LdapUseSsl'] = [bool]$LdapUseSsl

src/BitLockerKeyMonitor.Core/Models/ScanConfiguration.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ public class ScanConfiguration
1616
/// <summary>LDAP search base DN</summary>
1717
public string LdapSearchBase { get; set; } = string.Empty;
1818

19+
/// <summary>
20+
/// Comma/newline-separated list of OUs to scan. When empty, the scanner falls back to the
21+
/// single <see cref="LdapSearchBase"/>. Mirrors <c>RuntimeConfig.TargetOUs</c> so the
22+
/// HybridAgent (which reads config straight from appsettings.json in static-only mode)
23+
/// can target the same multi-OU surface as the on-prem Worker's portal-edited DB row.
24+
/// Example: <c>"OU=Workstations,DC=contoso,DC=com\nOU=Laptops,DC=contoso,DC=com"</c>.
25+
/// </summary>
26+
public string TargetOUs { get; set; } = string.Empty;
27+
1928
/// <summary>LDAP port (389 or 636 for SSL)</summary>
2029
public int LdapPort { get; set; } = 636;
2130

src/BitLockerKeyMonitor.Core/Services/ConfigurationProvider.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,8 @@ private void WarnIfRuntimeConfigDivergesFromAppsettings(RuntimeConfig dbConfig)
274274
divergences.Add($"LdapUseSsl: DB={dbConfig.LdapUseSsl} appsettings={_staticConfig.LdapUseSsl}");
275275
if (!string.Equals(dbConfig.LdapSearchBase ?? string.Empty, _staticConfig.LdapSearchBase ?? string.Empty, StringComparison.OrdinalIgnoreCase))
276276
divergences.Add($"LdapSearchBase: DB=\"{dbConfig.LdapSearchBase}\" appsettings=\"{_staticConfig.LdapSearchBase}\"");
277+
if (!string.Equals(dbConfig.TargetOUs ?? string.Empty, _staticConfig.TargetOUs ?? string.Empty, StringComparison.OrdinalIgnoreCase))
278+
divergences.Add($"TargetOUs: DB=\"{dbConfig.TargetOUs}\" appsettings=\"{_staticConfig.TargetOUs}\"");
277279

278280
if (!string.Equals(dbConfig.CsvOutputPath ?? string.Empty, _staticConfig.CsvOutputPath ?? string.Empty, StringComparison.OrdinalIgnoreCase))
279281
divergences.Add($"CsvOutputPath: DB=\"{dbConfig.CsvOutputPath}\" appsettings=\"{_staticConfig.CsvOutputPath}\"");
@@ -326,6 +328,7 @@ private void EnsureNotStaticOnly(string operation)
326328
LdapPort = _staticConfig.LdapPort,
327329
LdapUseSsl = _staticConfig.LdapUseSsl,
328330
LdapSearchBase = _staticConfig.LdapSearchBase,
331+
TargetOUs = _staticConfig.TargetOUs,
329332
CsvOutputPath = _staticConfig.CsvOutputPath,
330333
IncludeRecoveryKeyValue = _staticConfig.IncludeRecoveryKeyValue,
331334
KeyValueExportPath = _staticConfig.KeyValueExportPath,

src/BitLockerKeyMonitor.HybridAgent/appsettings.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
"CronSchedule": "0 0 2 * * ?",
3838
"LdapServer": "",
3939
"LdapSearchBase": "",
40+
"//TargetOUs": "Optional multi-OU list (newline-separated DNs). When empty, the scanner falls back to LdapSearchBase as a single root. Mirrors the on-prem Worker portal 'Target OUs' field.",
41+
"TargetOUs": "",
4042
"LdapPort": 636,
4143
"LdapUseSsl": true,
4244
"IncludeRecoveryKeyValue": true,

0 commit comments

Comments
 (0)