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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ All notable changes to IDBridge are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versions use
a calendar scheme `YY.M.D.build` (see [CONTRIBUTING.md](CONTRIBUTING.md#versioning--releases)).

## [26.9.1.3] - 2026-09-01

### Changed
- **`Reset-IDBridgeADPassword` CSVs now lead with `FirstName` and `LastName` columns**
(from AD GivenName/Surname), ahead of SamAccountName/Passphrase/OrgUnit — both the
reset run's handout CSV and the export-only CSV.

## [26.9.1.2] - 2026-09-01

### Changed
Expand Down
4 changes: 2 additions & 2 deletions docs/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ ChangePasswordAtLogon, passphrase-CSV export — off by default). "Load Users" l
accounts for review; "Reset Passwords" confirms with the exact count, fetches every passphrase
up front (`Get-ADUsersToResetPassword` — any Keysmith failure aborts before a single write),
then applies `Set-ADAccountPassword -Reset` per account. "Export Passphrases" instead writes
the loaded users' regenerated passphrases as a username/passphrase/OU CSV to `Exports`
the loaded users' regenerated passphrases as a name/username/passphrase/OU CSV to `Exports`
**without touching AD** (matches current passwords only when nonce/mode/word count/rev match
what they were last set with — pin the rev for older accounts). Writes to AD when confirmed
**regardless of `Debug.readOnly`** — interactive, gated by its own confirmation dialog.
Expand Down Expand Up @@ -523,7 +523,7 @@ deterministic passphrase per SamAccountName (one batched call per 500 users, the
per-request max; phrases pair with users by position) and builds a `Set-ADAccountPassword -Reset`
splat each. An API failure or returned-count mismatch throws — no partial reset list. The
plaintext phrase rides on each item for the caller's optional export and is never logged.
**Returns:** `@{ SamAccountName; DistinguishedName; Passphrase; Splat }[]`.
**Returns:** `@{ SamAccountName; GivenName; Surname; DistinguishedName; Passphrase; Splat }[]`.

### `Get-ADUserGroupsToUpdate` 🔒 🧮
**Params:** `-UserList`, `-CurrentADGroups`. Diffs `GroupsProposed` vs `ADCurrentGroups`
Expand Down
2 changes: 1 addition & 1 deletion src/IDBridge/IDBridge.psd1
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@{
RootModule = 'IDBridge.psm1'
ModuleVersion = '26.9.1.2'
ModuleVersion = '26.9.1.3'
GUID = 'a0a0c664-888e-44e2-9c12-fc8647a520c0'
Author = 'Sam Cattanach'
CompanyName = 'SC Networks LLC'
Expand Down
6 changes: 4 additions & 2 deletions src/IDBridge/Private/AD/Get-ADUsersToResetPassword.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ The caller is only exporting the passphrases, not resetting — the per-user pro
"Export passphrase" instead of "Reset password". Output is identical either way.

.OUTPUTS
[object[]] of @{ SamAccountName; DistinguishedName; Passphrase; Splat } where Splat is the
Set-ADAccountPassword parameter hashtable.
[object[]] of @{ SamAccountName; GivenName; Surname; DistinguishedName; Passphrase; Splat }
where Splat is the Set-ADAccountPassword parameter hashtable.

.EXAMPLE
$resets = Get-ADUsersToResetPassword -UserList $users -PassphraseAPI $api
Expand Down Expand Up @@ -86,6 +86,8 @@ function Get-ADUsersToResetPassword {

$itemList += [PSCustomObject]@{
SamAccountName = $item.SamAccountName
GivenName = $item.GivenName
Surname = $item.Surname
DistinguishedName = $item.DistinguishedName
Passphrase = $phrases[$i]
Splat = @{
Expand Down
6 changes: 3 additions & 3 deletions src/IDBridge/Public/Core/Reset-IDBridgeADPassword.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ account, optionally also setting ChangePasswordAtLogon.

Because Keysmith passphrases are deterministic, the phrases never need to be stored — anyone
with the nonce can regenerate them at keysmith.scnlabs.net. Optionally the run can export a
username/passphrase/OU CSV to the Exports folder for handout (off by default). "Export
name/username/passphrase/OU CSV to the Exports folder for handout (off by default). "Export
Passphrases" instead writes that same CSV for the loaded users WITHOUT touching AD —
regenerating what the current passwords already are, e.g. for login slips. The exported
phrases match the accounts' current passwords only when the nonce, mode, word count, and
Expand Down Expand Up @@ -520,7 +520,7 @@ function Reset-IDBridgeADPassword {
if ($chkExport.Checked -and $succeededItems.Count -gt 0) {
try {
$state.ExportPath = Join-Path $IDConfig.Paths.ExportsRoot ("ADPasswordReset_" + (Get-Date -Format "yyyy-MM-dd-HH.mm.ss") + ".csv")
$succeededItems | Select-Object SamAccountName, Passphrase, @{n = 'OrgUnit'; e = { [regex]::Match(($_.DistinguishedName -replace '^CN=(?:\\.|[^,\\])*,', ''), '^OU=((?:\\.|[^,\\])*)').Groups[1].Value -replace '\\(.)', '$1' }} | Export-Csv -Path $state.ExportPath -NoTypeInformation
$succeededItems | Select-Object @{n = 'FirstName'; e = { $_.GivenName }}, @{n = 'LastName'; e = { $_.Surname }}, SamAccountName, Passphrase, @{n = 'OrgUnit'; e = { [regex]::Match(($_.DistinguishedName -replace '^CN=(?:\\.|[^,\\])*,', ''), '^OU=((?:\\.|[^,\\])*)').Groups[1].Value -replace '\\(.)', '$1' }} | Export-Csv -Path $state.ExportPath -NoTypeInformation
Write-Log -Message "Reset: Exported $($succeededItems.Count) passphrase(s) to $($state.ExportPath). Delete the file after handout."
}
catch {
Expand Down Expand Up @@ -564,7 +564,7 @@ function Reset-IDBridgeADPassword {

try {
$state.ExportPath = Join-Path $IDConfig.Paths.ExportsRoot ("ADPasswordExport_" + (Get-Date -Format "yyyy-MM-dd-HH.mm.ss") + ".csv")
$resets | Select-Object SamAccountName, Passphrase, @{n = 'OrgUnit'; e = { [regex]::Match(($_.DistinguishedName -replace '^CN=(?:\\.|[^,\\])*,', ''), '^OU=((?:\\.|[^,\\])*)').Groups[1].Value -replace '\\(.)', '$1' }} | Export-Csv -Path $state.ExportPath -NoTypeInformation
$resets | Select-Object @{n = 'FirstName'; e = { $_.GivenName }}, @{n = 'LastName'; e = { $_.Surname }}, SamAccountName, Passphrase, @{n = 'OrgUnit'; e = { [regex]::Match(($_.DistinguishedName -replace '^CN=(?:\\.|[^,\\])*,', ''), '^OU=((?:\\.|[^,\\])*)').Groups[1].Value -replace '\\(.)', '$1' }} | Export-Csv -Path $state.ExportPath -NoTypeInformation
}
catch {
Write-Log -Message ("Reset: Passphrase export failed: $($_.Exception.Message)") -Level Error
Expand Down
2 changes: 2 additions & 0 deletions tests/Private/AD/Get-ADUsersToResetPassword.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ Describe 'Get-ADUsersToResetPassword' {

$result.Count | Should -Be 2
$result[0].SamAccountName | Should -Be 'alice'
$result[0].GivenName | Should -Be 'Test'
$result[0].Surname | Should -Be 'User'
$result[0].Passphrase | Should -Be 'phrase-alice'
$result[1].Passphrase | Should -Be 'phrase-bob'

Expand Down
Loading