Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 34 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ All notable changes to this module are recorded here. Format follows
secret for later runs.

What it creates, reports and removes: five custom user attributes, STRING and JSON, one of them
carrying the seed tag; four populations, one deliberately empty; 330 users, nineteen
hand-designed and 311 generated, including Han, Cyrillic, Greek, Arabic, Devanagari and a
carrying the seed tag; four populations, one deliberately empty; 319 users, nineteen
hand-designed and 300 generated, including Han, Cyrillic, Greek, Arabic, Devanagari and a
decomposed name; eleven groups, nested three deep, two dynamic by filter and one scoped to a
population, with 464 static memberships; two resources with four scopes; and six applications
population, with 447 static memberships; two resources with four scopes; and six applications
across OIDC web, single-page with PKCE, native and SAML, one of them disabled, with five
restricted to seeded groups and three granted scopes.

Expand Down Expand Up @@ -216,8 +216,39 @@ All notable changes to this module are recorded here. Format follows
memberships missing. `-WhatIf` named the users step alone; the repair re-ran it, and the
second verification passed with all 319 users present. Teardown then found nothing left.

- **Every shared person exists once in every provider.** The Active Directory data logs the
eleven people written in other writing systems in under logins of its own - `josen` for
`jnino`, `zoem` for `zmueller` - and the four generators mapped those rows across as bulk
people beside the core rows that already carried them, so Entra, Authentik, FreeIPA and PingOne
each held the same eleven people twice under two logins. The generators now recognise a shared
person by name, skip the AD row, and remember it under the shared key so a bulk person managed
by one of them still points at them. Each of the four bulk tiers is 300 people rather than 311:
318 Entra users, 319 Authentik and PingOne users, 346 FreeIPA users. The Active Directory data
keeps its own logins, which is why `Compare-TestEnvironment` matches it by name.

- **`-Tier` means the same thing on every provider.** Entra, Authentik, FreeIPA and PingOne
already took `-Tier Core` for the designed people and `-Tier Bulk` for the generated volume,
and Active Directory and Okta took nothing, so a script written for one provider failed on the
other two. `ADUsers.csv` now carries a `Tier` column, Core for the eleven people every provider
holds and Bulk for the three hundred generated, and `New-ADTestUser` and `New-ADEnvironment`
take `-Tier` to seed one tier or both; a manager left out by the tier is not set rather than
reported missing. `OktaUsers.csv` carries the column too, every row Core, because eight
designed people leave no room for volume, and `New-OktaUser` and `New-OktaEnvironment` accept
`-Tier` so the same call runs everywhere. The seed-data tests pin both files.

### Changed

- **`New-ADEnvironment` runs its steps from a table.** The eight steps were eight copies of the
same forty lines, and the order they run in - the part of the orchestrator that matters - had
to be inferred from four hundred lines of try/catch. They are now declared as data in
dependency order, the way the Entra and Okta orchestrators declare theirs, and one loop
announces, gates, runs and records each of them. Nothing observable changed: the same
`ShouldProcess` targets, the same step results under `Operations`, the password policy and DNS
steps still judged on the errors they collect, the deny-logon policy still right after the
service accounts and only when they succeeded, and the vault or the password file still
written for the service accounts. One message did: a step not attempted because the domain
controller stopped answering now says so, where it used to say "skipped as requested".

- **The Entra report takes the shared parameters.** `-Format` and `-Path` are kept as aliases of
`-OutputFormat` and `-OutputPath`, so an existing call binds. `-Format Object` is gone: `-PassThru`
returns the object, for every provider. A file format requires `-OutputPath` rather than defaulting
Expand Down
624 changes: 312 additions & 312 deletions Providers/AD/Data/ADUsers.csv

Large diffs are not rendered by default.

82 changes: 82 additions & 0 deletions Providers/AD/Private/Select-ADTestSeedUser.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
function Select-ADTestSeedUser {
<#
.SYNOPSIS
Chooses the user rows a seed creates and the manager assignments among them

.DESCRIPTION
Reads ADUsers.csv, keeps the rows in the requested tier or every row, orders them so a
manager is created before the people who report to them, and lists the rows whose
manager is among the rows being created.

Held apart from New-ADTestUser because that command creates users on background jobs,
which a unit test cannot stand in for, and the two decisions worth testing - which rows
a tier keeps, and that a manager left out by the tier is not looked for - are made here
without touching a directory.

A manager is assigned only when the manager is among the rows being created. With one
tier, a manager in the other tier is not there to point at, and looking for them in the
directory would report an error for a person the caller chose to leave out.

.PARAMETER Path
The users CSV.

.PARAMETER Tier
Core, Bulk, or both. Nothing keeps every row.

.EXAMPLE
PS> Select-ADTestSeedUser -Path (Join-Path (Get-ADTestDataPath) 'ADUsers.csv') -Tier Core

DESCRIPTION: The eleven people every provider holds, and the manager pairs among them
OUTPUT: An object with Users and ManagerAssignments
USE CASE: Called by New-ADTestUser before any job starts

.OUTPUTS
PSCustomObject with Users, the rows to create in creation order, and ManagerAssignments,
the subset of those rows whose manager is also being created.

.NOTES
Author: Jeffrey Stuhr
Blog: https://www.techbyjeff.net
LinkedIn: https://www.linkedin.com/in/jeffrey-stuhr-034214aa/
#>
[CmdletBinding()]
[OutputType([PSCustomObject])]
param(
[Parameter(Mandatory)]
[string]$Path,

[Parameter()]
[AllowNull()]
[AllowEmptyCollection()]
[string[]]$Tier
)

$rows = @(Import-Csv -LiteralPath $Path -Encoding UTF8)
if ($Tier) {
$rows = @($rows | Where-Object { $Tier -contains $_.Tier })
}

# Managers before their reports, so a report's manager exists when the report is created.
# Two passes over the file rather than a sort, because Sort-Object is not stable on Windows
# PowerShell and -Stable does not exist there: this way two runs create the same rows in
# the same order on both editions.
$ordered = @($rows | Where-Object { [string]::IsNullOrWhiteSpace($_.Manager) }) +
@($rows | Where-Object { -not [string]::IsNullOrWhiteSpace($_.Manager) })

$createdNames = @{}
foreach ($row in $ordered) { $createdNames[$row.Name] = $true }

$assignments = @($ordered | Where-Object {
-not [string]::IsNullOrWhiteSpace($_.Manager) -and $_.Manager -ne 'CN='
} | Where-Object {
$managerName = $_.Manager -replace '^CN=', ''
if ($createdNames.ContainsKey($managerName)) { return $true }
Write-Verbose "Not setting the manager of $($_.Name): $managerName is not among the users being created"
$false
})

[PSCustomObject]@{
Users = $ordered
ManagerAssignments = $assignments
}
}
Loading