[{"data":1,"prerenderedAt":2888},["ShallowReactive",2],{"post:\u002F2025\u002F10\u002F01\u002Fpowershell-active-directory-bulk-reset-passwords-with-a-forced-change\u002F":3},{"post":4,"newer":2849,"older":2859,"related":2868,"series":2886},{"id":5,"title":6,"body":7,"canonical":2831,"categories":2832,"date":2835,"description":2836,"extension":2837,"featured":2838,"hero":2839,"image":2839,"meta":2840,"navigation":706,"path":2841,"readingTime":466,"seo":2842,"series":2839,"seriesOrder":2839,"sites":2843,"source":2839,"stem":2844,"tags":2845,"updated":2839,"url":2847,"__hash__":2848},"blog\u002Fblog\u002F2025\u002F10\u002F01\u002Fpowershell-active-directory-bulk-reset-passwords-with-a-forced-change.md","PowerShell: Active Directory – Bulk Password Reset with Forced Change",{"type":8,"value":9,"toc":2823},"minimark",[10,19,43,48,95,99,195,205,209,215,225,228,271,274,298,304,307,313,330,429,433,2689,2693,2779,2783,2819],[11,12,13,14,18],"p",{},"A credential exposure, a breached vendor list, or a phishing campaign that landed will eventually put a list of usernames in your hands with the instruction \"reset all of these.\" Doing it by hand in ADUC doesn't scale past a handful of accounts, and it's easy to fat-finger a password into a policy violation or forget to tick \"User must change password at next logon.\" This script takes a CSV of ",[15,16,17],"code",{},"sAMAccountName"," values, generates a cryptographically random password for each one that is at least as long as the password policy that actually applies to that user, resets it, forces a change at next logon, unlocks the account if it was locked, and writes a report you can hand to whoever notifies the affected users.",[11,20,21,22,26,27,30,31,34,35,38,39,42],{},"It also deals with the one case that trips up most bulk-reset scripts: an account with ",[23,24,25],"strong",{},"Password never expires"," set. Microsoft's ",[15,28,29],{},"Set-ADUser"," documentation states that ",[15,32,33],{},"PasswordNeverExpires"," and ",[15,36,37],{},"ChangePasswordAtLogon"," can't both be true on the same account, so the script either clears the never-expires flag (when you ask it to with ",[15,40,41],{},"-ClearPasswordNeverExpires",") or resets the password, skips the forced change, and flags the account in the report so nobody assumes it was handled.",[44,45,47],"h2",{"id":46},"requirements","Requirements",[49,50,51,59,81,88],"ul",{},[52,53,54,55,58],"li",{},"Windows PowerShell 5.1 or PowerShell 7 on Windows, with the ActiveDirectory module (RSAT). The module talks to Active Directory Web Services on a writable DC; ",[15,56,57],{},"Set-ADAccountPassword"," doesn't work against a read-only DC or the global catalog port.",[52,60,61,62,65,66,69,70,73,74,77,78,80],{},"The ",[23,63,64],{},"Reset password"," right on the target accounts, plus write access to ",[15,67,68],{},"pwdLastSet"," (for the forced change), ",[15,71,72],{},"lockoutTime"," (for the unlock) and ",[15,75,76],{},"userAccountControl"," (only if you use ",[15,79,41],{},"). Delegated rights on the relevant OUs are enough; Domain Admin is not required.",[52,82,83,84,87],{},"Read access to the password settings container if fine-grained password policies are in use, so ",[15,85,86],{},"Get-ADUserResultantPasswordPolicy"," can return the policy for each user.",[52,89,90,91,94],{},"A CSV with a ",[15,92,93],{},"SamAccountName"," column.",[44,96,98],{"id":97},"parameters","Parameters",[100,101,102,121],"table",{},[103,104,105],"thead",{},[106,107,108,112,115,118],"tr",{},[109,110,111],"th",{},"Name",[109,113,114],{},"Type",[109,116,117],{},"Required",[109,119,120],{},"Description",[122,123,124,143,157,177],"tbody",{},[106,125,126,132,135,138],{},[127,128,129],"td",{},[15,130,131],{},"CsvPath",[127,133,134],{},"String",[127,136,137],{},"Yes",[127,139,140,141,94],{},"Path to the CSV of accounts to reset. Needs a ",[15,142,93],{},[106,144,145,150,152,154],{},[127,146,147],{},[15,148,149],{},"ReportPath",[127,151,134],{},[127,153,137],{},[127,155,156],{},"Path to write the report CSV (username, temporary password, what was done).",[106,158,159,164,167,170],{},[127,160,161],{},[15,162,163],{},"PasswordLength",[127,165,166],{},"Int",[127,168,169],{},"No",[127,171,172,173,176],{},"Minimum length of the generated password. Defaults to ",[15,174,175],{},"16",". Raised automatically if a user's effective policy requires more.",[106,178,179,184,187,189],{},[127,180,181],{},[15,182,183],{},"ClearPasswordNeverExpires",[127,185,186],{},"Switch",[127,188,169],{},[127,190,191,192,194],{},"Clears ",[23,193,25],{}," on accounts that have it so the forced change can be set. Without it those accounts are reset and flagged, but not forced to change.",[11,196,197,198,34,201,204],{},"The script supports ",[15,199,200],{},"-WhatIf",[15,202,203],{},"-Confirm",".",[44,206,208],{"id":207},"usage","Usage",[11,210,211,212,214],{},"Build the input file. One ",[15,213,93],{}," per row:",[216,217,223],"pre",{"className":218,"code":220,"language":221,"meta":222},[219],"language-text","SamAccountName\njsmith\nmgarcia\nkpatel\nsvc-reporting\n","text","",[15,224,220],{"__ignoreMap":222},[11,226,227],{},"Dry run first, to confirm every account resolves and see which ones have never-expires set:",[216,229,233],{"className":230,"code":231,"language":232,"meta":222,"style":222},"language-powershell shiki shiki-themes github-dark",".\\Reset-ADAccountPasswordsBulk.ps1 -CsvPath \"C:\\Security\\accounts-to-reset.csv\" -ReportPath \"C:\\Security\\reset-report.csv\" -WhatIf\n","powershell",[15,234,235],{"__ignoreMap":222},[236,237,240,243,247,251,254,258,260,263,266,268],"span",{"class":238,"line":239},"line",1,[236,241,204],{"class":242},"s95oV",[236,244,246],{"class":245},"sDLfK","\\Reset-ADAccountPasswordsBulk.ps1",[236,248,250],{"class":249},"snl16"," -",[236,252,253],{"class":242},"CsvPath ",[236,255,257],{"class":256},"sU2Wk","\"C:\\Security\\accounts-to-reset.csv\"",[236,259,250],{"class":249},[236,261,262],{"class":242},"ReportPath ",[236,264,265],{"class":256},"\"C:\\Security\\reset-report.csv\"",[236,267,250],{"class":249},[236,269,270],{"class":242},"WhatIf\n",[11,272,273],{},"Run it for real:",[216,275,277],{"className":230,"code":276,"language":232,"meta":222,"style":222},".\\Reset-ADAccountPasswordsBulk.ps1 -CsvPath \"C:\\Security\\accounts-to-reset.csv\" -ReportPath \"C:\\Security\\reset-report_2025-10-01.csv\"\n",[15,278,279],{"__ignoreMap":222},[236,280,281,283,285,287,289,291,293,295],{"class":238,"line":239},[236,282,204],{"class":242},[236,284,246],{"class":245},[236,286,250],{"class":249},[236,288,253],{"class":242},[236,290,257],{"class":256},[236,292,250],{"class":249},[236,294,262],{"class":242},[236,296,297],{"class":256},"\"C:\\Security\\reset-report_2025-10-01.csv\"\n",[216,299,302],{"className":300,"code":301,"language":221,"meta":222},[219],"Reset jsmith: ChangePasswordAtLogon set.\nWARNING: mgarcia not found in Active Directory - skipped.\nReset kpatel: ChangePasswordAtLogon set, account unlocked.\nWARNING: svc-reporting has PasswordNeverExpires set; password reset but ChangePasswordAtLogon NOT set. Re-run with -ClearPasswordNeverExpires or handle it by hand.\n3 of 4 account(s) reset, 1 failed or skipped. Report written to C:\\Security\\reset-report_2025-10-01.csv\n",[15,303,301],{"__ignoreMap":222},[11,305,306],{},"The report (passwords replaced with placeholders here):",[216,308,311],{"className":309,"code":310,"language":221,"meta":222},[219],"\"SamAccountName\",\"TemporaryPassword\",\"Result\",\"ResetDate\"\n\"jsmith\",\"\u003Cgenerated>\",\"Reset; must change at next logon\",\"2025-10-01 09:12:44\"\n\"mgarcia\",\"\",\"Not found\",\"2025-10-01 09:12:44\"\n\"kpatel\",\"\u003Cgenerated>\",\"Reset; must change at next logon; unlocked\",\"2025-10-01 09:12:45\"\n\"svc-reporting\",\"\u003Cgenerated>\",\"Reset; NOT forced (PasswordNeverExpires)\",\"2025-10-01 09:12:45\"\n",[15,312,310],{"__ignoreMap":222},[11,314,315,316,318,319,34,322,325,326,329],{},"Check that the forced change actually landed (a ",[15,317,68],{}," of 0 is what \"must change at next logon\" is stored as, which the module surfaces as an empty ",[15,320,321],{},"PasswordLastSet",[15,323,324],{},"PasswordExpired"," = ",[15,327,328],{},"True","):",[216,331,333],{"className":230,"code":332,"language":232,"meta":222,"style":222},"Import-Csv -Path \"C:\\Security\\accounts-to-reset.csv\" | ForEach-Object {\n    Get-ADUser -Identity $_.SamAccountName -Properties PasswordLastSet, PasswordExpired, PasswordNeverExpires, LockedOut\n} | Select-Object -Property SamAccountName, PasswordLastSet, PasswordExpired, PasswordNeverExpires, LockedOut\n",[15,334,335,356,395],{"__ignoreMap":222},[236,336,337,340,342,345,347,350,353],{"class":238,"line":239},[236,338,339],{"class":245},"Import-Csv",[236,341,250],{"class":249},[236,343,344],{"class":242},"Path ",[236,346,257],{"class":256},[236,348,349],{"class":249}," |",[236,351,352],{"class":245}," ForEach-Object",[236,354,355],{"class":242}," {\n",[236,357,359,362,364,367,370,373,376,379,382,385,387,390,392],{"class":238,"line":358},2,[236,360,361],{"class":245},"    Get-ADUser",[236,363,250],{"class":249},[236,365,366],{"class":242},"Identity ",[236,368,369],{"class":245},"$_",[236,371,372],{"class":242},".SamAccountName ",[236,374,375],{"class":249},"-",[236,377,378],{"class":242},"Properties PasswordLastSet",[236,380,381],{"class":249},",",[236,383,384],{"class":242}," PasswordExpired",[236,386,381],{"class":249},[236,388,389],{"class":242}," PasswordNeverExpires",[236,391,381],{"class":249},[236,393,394],{"class":242}," LockedOut\n",[236,396,398,401,404,407,409,412,414,417,419,421,423,425,427],{"class":238,"line":397},3,[236,399,400],{"class":242},"} ",[236,402,403],{"class":249},"|",[236,405,406],{"class":245}," Select-Object",[236,408,250],{"class":249},[236,410,411],{"class":242},"Property SamAccountName",[236,413,381],{"class":249},[236,415,416],{"class":242}," PasswordLastSet",[236,418,381],{"class":249},[236,420,384],{"class":242},[236,422,381],{"class":249},[236,424,389],{"class":242},[236,426,381],{"class":249},[236,428,394],{"class":242},[44,430,432],{"id":431},"script","Script",[216,434,436],{"className":230,"code":435,"language":232,"meta":222,"style":222},"\u003C#\n.SYNOPSIS\n    Resets passwords for a list of Active Directory accounts to random values and forces a change at next logon.\n.DESCRIPTION\n    Reads a CSV of SamAccountName values. For each account it looks up the effective password policy\n    (a fine-grained PSO if one applies, otherwise the default domain policy), generates a random password\n    from a cryptographic RNG that is at least that long and contains all four character classes, applies\n    it with Set-ADAccountPassword -Reset, sets ChangePasswordAtLogon, and unlocks the account if it was\n    locked. Accounts with PasswordNeverExpires are reset and flagged, or have the flag cleared first when\n    -ClearPasswordNeverExpires is used. Writes a report CSV with each username, its temporary password\n    and the outcome, so it can be handed off securely and deleted after use.\n.PARAMETER CsvPath\n    Path to the CSV of accounts to reset. Needs a SamAccountName column.\n.PARAMETER ReportPath\n    Path to write the report CSV (username, temporary password, what was done).\n.PARAMETER PasswordLength\n    Minimum length of the generated password. Defaults to 16. Raised automatically if a user's\n    effective policy requires more.\n.PARAMETER ClearPasswordNeverExpires\n    Clears Password never expires on accounts that have it so the forced change can be set.\n.EXAMPLE\n    .\\Reset-ADAccountPasswordsBulk.ps1 -CsvPath \"C:\\Security\\accounts-to-reset.csv\" -ReportPath \"C:\\Security\\reset-report_2025-10-01.csv\"\n.NOTES\n    Author  : Thomas Lasswell (https:\u002F\u002Fwww.techcolumnist.com)\n    Version : 1.0 (2025-10-01)\n    Requires: ActiveDirectory module, Reset Password rights on the target accounts\n#>\n[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]\nparam(\n    [Parameter(Mandatory = $true)]\n    [string]$CsvPath,\n\n    [Parameter(Mandatory = $true)]\n    [string]$ReportPath,\n\n    [Parameter(Mandatory = $false)]\n    [ValidateRange(12, 128)]\n    [int]$PasswordLength = 16,\n\n    [Parameter(Mandatory = $false)]\n    [switch]$ClearPasswordNeverExpires\n)\n\nImport-Module ActiveDirectory -ErrorAction Stop\n\n$rng = [System.Security.Cryptography.RandomNumberGenerator]::Create()\n\nfunction Get-RandomIndex {\n    param([int]$MaxExclusive)\n\n    # Rejection sampling avoids the modulo bias of a plain \"random % length\".\n    $limit = [uint32]::MaxValue - ([uint32]::MaxValue % [uint32]$MaxExclusive)\n    $bytes = New-Object -TypeName byte[] -ArgumentList 4\n    do {\n        $rng.GetBytes($bytes)\n        $value = [System.BitConverter]::ToUInt32($bytes, 0)\n    } while ($value -ge $limit)\n    return [int]($value % [uint32]$MaxExclusive)\n}\n\nfunction New-RandomComplexPassword {\n    param([int]$Length)\n\n    # Look-alike characters (I, l, O, o, 0, 1) are left out so the password can be read over the phone.\n    $sets = @(\n        'ABCDEFGHJKLMNPQRSTUVWXYZ',\n        'abcdefghijkmnpqrstuvwxyz',\n        '23456789',\n        '!@#$%^&*-_=+'\n    )\n    $allChars = -join $sets\n\n    # One character from each class, then fill the rest from the combined set.\n    $chars = New-Object -TypeName System.Collections.Generic.List[char]\n    foreach ($set in $sets) {\n        $chars.Add($set[(Get-RandomIndex -MaxExclusive $set.Length)])\n    }\n    while ($chars.Count -lt $Length) {\n        $chars.Add($allChars[(Get-RandomIndex -MaxExclusive $allChars.Length)])\n    }\n\n    # Fisher-Yates shuffle so the guaranteed characters are not always at the front.\n    for ($i = $chars.Count - 1; $i -gt 0; $i--) {\n        $j = Get-RandomIndex -MaxExclusive ($i + 1)\n        $swap = $chars[$i]\n        $chars[$i] = $chars[$j]\n        $chars[$j] = $swap\n    }\n    return -join $chars\n}\n\n$defaultPolicy = Get-ADDefaultDomainPasswordPolicy\n$accounts = @(Import-Csv -Path $CsvPath | Where-Object { -not [string]::IsNullOrWhiteSpace($_.SamAccountName) })\n$report = New-Object -TypeName System.Collections.Generic.List[object]\n$successCount = 0\n\nforeach ($account in $accounts) {\n    $samAccountName = $account.SamAccountName.Trim()\n    $timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'\n\n    # Double any single quote so a name such as o'brien can't break out of the filter string.\n    $filterName = $samAccountName -replace \"'\", \"''\"\n    $adUser = Get-ADUser -Filter \"SamAccountName -eq '$filterName'\" -Properties LockedOut, PasswordNeverExpires, Enabled\n\n    if ($null -eq $adUser) {\n        Write-Warning \"$samAccountName not found in Active Directory - skipped.\"\n        $report.Add([PSCustomObject]@{ SamAccountName = $samAccountName; TemporaryPassword = ''; Result = 'Not found'; ResetDate = $timestamp })\n        continue\n    }\n\n    # Decided before ShouldProcess so -WhatIf and -Confirm show which accounts won't be forced to change.\n    $forceChange = $ClearPasswordNeverExpires -or -not $adUser.PasswordNeverExpires\n    $operation = if (-not $forceChange) {\n        'Reset password only; PasswordNeverExpires is set, so change at next logon is NOT forced'\n    } elseif ($adUser.PasswordNeverExpires) {\n        'Clear PasswordNeverExpires, reset password and force change at next logon'\n    } else {\n        'Reset password and force change at next logon'\n    }\n\n    if (-not $PSCmdlet.ShouldProcess($samAccountName, $operation)) {\n        continue\n    }\n\n    $passwordSet = $false\n    try {\n        # Effective minimum length: a fine-grained PSO wins over the domain policy when one applies.\n        $policy = Get-ADUserResultantPasswordPolicy -Identity $adUser\n        if (-not $policy) {\n            $policy = $defaultPolicy\n        }\n        $length = [math]::Max($PasswordLength, [int]$policy.MinPasswordLength)\n\n        $newPassword = New-RandomComplexPassword -Length $length\n        $securePassword = ConvertTo-SecureString -String $newPassword -AsPlainText -Force\n\n        Set-ADAccountPassword -Identity $adUser -NewPassword $securePassword -Reset -ErrorAction Stop\n        $passwordSet = $true\n        $result = 'Reset'\n\n        if ($adUser.PasswordNeverExpires -and $ClearPasswordNeverExpires) {\n            Set-ADUser -Identity $adUser -PasswordNeverExpires $false -ErrorAction Stop\n            $result += '; PasswordNeverExpires cleared'\n        }\n\n        if (-not $forceChange) {\n            Write-Warning \"$samAccountName has PasswordNeverExpires set; password reset but ChangePasswordAtLogon NOT set. Re-run with -ClearPasswordNeverExpires or handle it by hand.\"\n            $result += '; NOT forced (PasswordNeverExpires)'\n        } else {\n            Set-ADUser -Identity $adUser -ChangePasswordAtLogon $true -ErrorAction Stop\n            $result += '; must change at next logon'\n        }\n\n        $message = \"Reset ${samAccountName}: ChangePasswordAtLogon set\"\n        if ($adUser.LockedOut) {\n            Unlock-ADAccount -Identity $adUser -ErrorAction Stop\n            $result += '; unlocked'\n            $message += ', account unlocked'\n        }\n        if (-not $adUser.Enabled) {\n            $result += '; account is disabled'\n        }\n\n        if ($result -notmatch 'NOT forced') {\n            Write-Host \"$message.\"\n        }\n\n        $report.Add([PSCustomObject]@{ SamAccountName = $samAccountName; TemporaryPassword = $newPassword; Result = $result; ResetDate = $timestamp })\n        $successCount++\n    } catch {\n        if ($passwordSet) {\n            # The password did change, so keep it in the report; only the follow-up step failed.\n            Write-Warning \"Password for $samAccountName was reset, but a follow-up step failed: $($_.Exception.Message)\"\n            $report.Add([PSCustomObject]@{ SamAccountName = $samAccountName; TemporaryPassword = $newPassword; Result = \"Reset, then failed: $($_.Exception.Message)\"; ResetDate = $timestamp })\n        } else {\n            Write-Warning \"Failed on ${samAccountName}: $($_.Exception.Message)\"\n            $report.Add([PSCustomObject]@{ SamAccountName = $samAccountName; TemporaryPassword = ''; Result = \"Failed: $($_.Exception.Message)\"; ResetDate = $timestamp })\n        }\n    }\n}\n\nif ($report.Count -gt 0) {\n    $report | Export-Csv -Path $ReportPath -NoTypeInformation -Encoding UTF8\n}\n\n$rng.Dispose()\n\nif ($WhatIfPreference) {\n    Write-Host \"WhatIf run: no passwords were changed.\"\n    return\n}\n\n$failed = $accounts.Count - $successCount\nWrite-Host \"$successCount of $($accounts.Count) account(s) reset, $failed failed or skipped. Report written to $ReportPath\"\n",[15,437,438,444,451,456,464,470,476,482,488,494,500,506,517,523,533,539,549,555,561,571,577,585,591,599,605,611,617,623,658,667,687,701,708,725,737,742,760,780,799,804,821,832,838,843,857,862,879,884,896,910,915,921,955,979,987,993,1016,1034,1055,1061,1066,1076,1088,1093,1099,1112,1120,1128,1136,1142,1148,1162,1167,1173,1194,1209,1223,1229,1244,1257,1262,1267,1273,1309,1331,1342,1353,1364,1369,1379,1384,1389,1400,1442,1461,1472,1477,1491,1502,1521,1526,1532,1554,1593,1598,1616,1631,1675,1681,1686,1691,1697,1717,1735,1741,1752,1758,1768,1774,1779,1784,1804,1809,1814,1819,1830,1838,1844,1860,1873,1884,1890,1915,1920,1935,1961,1966,1991,2002,2013,2018,2032,2054,2066,2071,2076,2087,2100,2110,2120,2141,2151,2156,2161,2178,2186,2200,2210,2221,2226,2238,2248,2253,2258,2274,2288,2293,2298,2329,2338,2348,2356,2362,2391,2437,2446,2471,2517,2522,2527,2532,2537,2552,2578,2583,2588,2594,2599,2611,2620,2626,2631,2636,2652],{"__ignoreMap":222},[236,439,440],{"class":238,"line":239},[236,441,443],{"class":442},"sAwPA","\u003C#\n",[236,445,446,448],{"class":238,"line":358},[236,447,204],{"class":245},[236,449,450],{"class":249},"SYNOPSIS\n",[236,452,453],{"class":238,"line":397},[236,454,455],{"class":442},"    Resets passwords for a list of Active Directory accounts to random values and forces a change at next logon.\n",[236,457,459,461],{"class":238,"line":458},4,[236,460,204],{"class":245},[236,462,463],{"class":249},"DESCRIPTION\n",[236,465,467],{"class":238,"line":466},5,[236,468,469],{"class":442},"    Reads a CSV of SamAccountName values. For each account it looks up the effective password policy\n",[236,471,473],{"class":238,"line":472},6,[236,474,475],{"class":442},"    (a fine-grained PSO if one applies, otherwise the default domain policy), generates a random password\n",[236,477,479],{"class":238,"line":478},7,[236,480,481],{"class":442},"    from a cryptographic RNG that is at least that long and contains all four character classes, applies\n",[236,483,485],{"class":238,"line":484},8,[236,486,487],{"class":442},"    it with Set-ADAccountPassword -Reset, sets ChangePasswordAtLogon, and unlocks the account if it was\n",[236,489,491],{"class":238,"line":490},9,[236,492,493],{"class":442},"    locked. Accounts with PasswordNeverExpires are reset and flagged, or have the flag cleared first when\n",[236,495,497],{"class":238,"line":496},10,[236,498,499],{"class":442},"    -ClearPasswordNeverExpires is used. Writes a report CSV with each username, its temporary password\n",[236,501,503],{"class":238,"line":502},11,[236,504,505],{"class":442},"    and the outcome, so it can be handed off securely and deleted after use.\n",[236,507,509,511,514],{"class":238,"line":508},12,[236,510,204],{"class":245},[236,512,513],{"class":249},"PARAMETER",[236,515,516],{"class":249}," CsvPath\n",[236,518,520],{"class":238,"line":519},13,[236,521,522],{"class":442},"    Path to the CSV of accounts to reset. Needs a SamAccountName column.\n",[236,524,526,528,530],{"class":238,"line":525},14,[236,527,204],{"class":245},[236,529,513],{"class":249},[236,531,532],{"class":249}," ReportPath\n",[236,534,536],{"class":238,"line":535},15,[236,537,538],{"class":442},"    Path to write the report CSV (username, temporary password, what was done).\n",[236,540,542,544,546],{"class":238,"line":541},16,[236,543,204],{"class":245},[236,545,513],{"class":249},[236,547,548],{"class":249}," PasswordLength\n",[236,550,552],{"class":238,"line":551},17,[236,553,554],{"class":442},"    Minimum length of the generated password. Defaults to 16. Raised automatically if a user's\n",[236,556,558],{"class":238,"line":557},18,[236,559,560],{"class":442},"    effective policy requires more.\n",[236,562,564,566,568],{"class":238,"line":563},19,[236,565,204],{"class":245},[236,567,513],{"class":249},[236,569,570],{"class":249}," ClearPasswordNeverExpires\n",[236,572,574],{"class":238,"line":573},20,[236,575,576],{"class":442},"    Clears Password never expires on accounts that have it so the forced change can be set.\n",[236,578,580,582],{"class":238,"line":579},21,[236,581,204],{"class":245},[236,583,584],{"class":249},"EXAMPLE\n",[236,586,588],{"class":238,"line":587},22,[236,589,590],{"class":442},"    .\\Reset-ADAccountPasswordsBulk.ps1 -CsvPath \"C:\\Security\\accounts-to-reset.csv\" -ReportPath \"C:\\Security\\reset-report_2025-10-01.csv\"\n",[236,592,594,596],{"class":238,"line":593},23,[236,595,204],{"class":245},[236,597,598],{"class":249},"NOTES\n",[236,600,602],{"class":238,"line":601},24,[236,603,604],{"class":442},"    Author  : Thomas Lasswell (https:\u002F\u002Fwww.techcolumnist.com)\n",[236,606,608],{"class":238,"line":607},25,[236,609,610],{"class":442},"    Version : 1.0 (2025-10-01)\n",[236,612,614],{"class":238,"line":613},26,[236,615,616],{"class":442},"    Requires: ActiveDirectory module, Reset Password rights on the target accounts\n",[236,618,620],{"class":238,"line":619},27,[236,621,622],{"class":442},"#>\n",[236,624,626,629,632,635,639,642,645,647,650,652,655],{"class":238,"line":625},28,[236,627,628],{"class":242},"[",[236,630,631],{"class":245},"CmdletBinding",[236,633,634],{"class":242},"(",[236,636,638],{"class":637},"s9osk","SupportsShouldProcess",[236,640,641],{"class":249}," =",[236,643,644],{"class":245}," $true",[236,646,381],{"class":249},[236,648,649],{"class":637}," ConfirmImpact",[236,651,641],{"class":249},[236,653,654],{"class":256}," 'High'",[236,656,657],{"class":242},")]\n",[236,659,661,664],{"class":238,"line":660},29,[236,662,663],{"class":249},"param",[236,665,666],{"class":242},"(\n",[236,668,670,673,676,678,681,683,685],{"class":238,"line":669},30,[236,671,672],{"class":242},"    [",[236,674,675],{"class":245},"Parameter",[236,677,634],{"class":242},[236,679,680],{"class":637},"Mandatory",[236,682,641],{"class":249},[236,684,644],{"class":245},[236,686,657],{"class":242},[236,688,690,692,695,698],{"class":238,"line":689},31,[236,691,672],{"class":242},[236,693,694],{"class":249},"string",[236,696,697],{"class":242},"]$CsvPath",[236,699,700],{"class":249},",\n",[236,702,704],{"class":238,"line":703},32,[236,705,707],{"emptyLinePlaceholder":706},true,"\n",[236,709,711,713,715,717,719,721,723],{"class":238,"line":710},33,[236,712,672],{"class":242},[236,714,675],{"class":245},[236,716,634],{"class":242},[236,718,680],{"class":637},[236,720,641],{"class":249},[236,722,644],{"class":245},[236,724,657],{"class":242},[236,726,728,730,732,735],{"class":238,"line":727},34,[236,729,672],{"class":242},[236,731,694],{"class":249},[236,733,734],{"class":242},"]$ReportPath",[236,736,700],{"class":249},[236,738,740],{"class":238,"line":739},35,[236,741,707],{"emptyLinePlaceholder":706},[236,743,745,747,749,751,753,755,758],{"class":238,"line":744},36,[236,746,672],{"class":242},[236,748,675],{"class":245},[236,750,634],{"class":242},[236,752,680],{"class":637},[236,754,641],{"class":249},[236,756,757],{"class":245}," $false",[236,759,657],{"class":242},[236,761,763,765,768,770,773,775,778],{"class":238,"line":762},37,[236,764,672],{"class":242},[236,766,767],{"class":245},"ValidateRange",[236,769,634],{"class":242},[236,771,772],{"class":245},"12",[236,774,381],{"class":249},[236,776,777],{"class":245}," 128",[236,779,657],{"class":242},[236,781,783,785,788,791,794,797],{"class":238,"line":782},38,[236,784,672],{"class":242},[236,786,787],{"class":249},"int",[236,789,790],{"class":242},"]$PasswordLength ",[236,792,793],{"class":249},"=",[236,795,796],{"class":245}," 16",[236,798,700],{"class":249},[236,800,802],{"class":238,"line":801},39,[236,803,707],{"emptyLinePlaceholder":706},[236,805,807,809,811,813,815,817,819],{"class":238,"line":806},40,[236,808,672],{"class":242},[236,810,675],{"class":245},[236,812,634],{"class":242},[236,814,680],{"class":637},[236,816,641],{"class":249},[236,818,757],{"class":245},[236,820,657],{"class":242},[236,822,824,826,829],{"class":238,"line":823},41,[236,825,672],{"class":242},[236,827,828],{"class":249},"switch",[236,830,831],{"class":242},"]$ClearPasswordNeverExpires\n",[236,833,835],{"class":238,"line":834},42,[236,836,837],{"class":242},")\n",[236,839,841],{"class":238,"line":840},43,[236,842,707],{"emptyLinePlaceholder":706},[236,844,846,849,852,854],{"class":238,"line":845},44,[236,847,848],{"class":245},"Import-Module",[236,850,851],{"class":242}," ActiveDirectory ",[236,853,375],{"class":249},[236,855,856],{"class":242},"ErrorAction Stop\n",[236,858,860],{"class":238,"line":859},45,[236,861,707],{"emptyLinePlaceholder":706},[236,863,865,868,870,873,876],{"class":238,"line":864},46,[236,866,867],{"class":242},"$rng ",[236,869,793],{"class":249},[236,871,872],{"class":242}," [",[236,874,875],{"class":249},"System.Security.Cryptography.RandomNumberGenerator",[236,877,878],{"class":242},"]::Create()\n",[236,880,882],{"class":238,"line":881},47,[236,883,707],{"emptyLinePlaceholder":706},[236,885,887,890,894],{"class":238,"line":886},48,[236,888,889],{"class":249},"function",[236,891,893],{"class":892},"svObZ"," Get-RandomIndex",[236,895,355],{"class":242},[236,897,899,902,905,907],{"class":238,"line":898},49,[236,900,901],{"class":249},"    param",[236,903,904],{"class":242},"([",[236,906,787],{"class":249},[236,908,909],{"class":242},"]$MaxExclusive)\n",[236,911,913],{"class":238,"line":912},50,[236,914,707],{"emptyLinePlaceholder":706},[236,916,918],{"class":238,"line":917},51,[236,919,920],{"class":442},"    # Rejection sampling avoids the modulo bias of a plain \"random % length\".\n",[236,922,924,927,929,931,934,937,939,942,944,946,949,951,953],{"class":238,"line":923},52,[236,925,926],{"class":242},"    $limit ",[236,928,793],{"class":249},[236,930,872],{"class":242},[236,932,933],{"class":249},"uint32",[236,935,936],{"class":242},"]::MaxValue ",[236,938,375],{"class":249},[236,940,941],{"class":242}," ([",[236,943,933],{"class":249},[236,945,936],{"class":242},[236,947,948],{"class":249},"%",[236,950,872],{"class":242},[236,952,933],{"class":249},[236,954,909],{"class":242},[236,956,958,961,963,966,968,971,973,976],{"class":238,"line":957},53,[236,959,960],{"class":242},"    $bytes ",[236,962,793],{"class":249},[236,964,965],{"class":245}," New-Object",[236,967,250],{"class":249},[236,969,970],{"class":242},"TypeName byte[] ",[236,972,375],{"class":249},[236,974,975],{"class":242},"ArgumentList ",[236,977,978],{"class":245},"4\n",[236,980,982,985],{"class":238,"line":981},54,[236,983,984],{"class":249},"    do",[236,986,355],{"class":242},[236,988,990],{"class":238,"line":989},55,[236,991,992],{"class":242},"        $rng.GetBytes($bytes)\n",[236,994,996,999,1001,1003,1006,1009,1011,1014],{"class":238,"line":995},56,[236,997,998],{"class":242},"        $value ",[236,1000,793],{"class":249},[236,1002,872],{"class":242},[236,1004,1005],{"class":249},"System.BitConverter",[236,1007,1008],{"class":242},"]::ToUInt32($bytes",[236,1010,381],{"class":249},[236,1012,1013],{"class":245}," 0",[236,1015,837],{"class":242},[236,1017,1019,1022,1025,1028,1031],{"class":238,"line":1018},57,[236,1020,1021],{"class":242},"    } ",[236,1023,1024],{"class":249},"while",[236,1026,1027],{"class":242}," ($value ",[236,1029,1030],{"class":249},"-ge",[236,1032,1033],{"class":242}," $limit)\n",[236,1035,1037,1040,1042,1044,1047,1049,1051,1053],{"class":238,"line":1036},58,[236,1038,1039],{"class":249},"    return",[236,1041,872],{"class":242},[236,1043,787],{"class":249},[236,1045,1046],{"class":242},"]($value ",[236,1048,948],{"class":249},[236,1050,872],{"class":242},[236,1052,933],{"class":249},[236,1054,909],{"class":242},[236,1056,1058],{"class":238,"line":1057},59,[236,1059,1060],{"class":242},"}\n",[236,1062,1064],{"class":238,"line":1063},60,[236,1065,707],{"emptyLinePlaceholder":706},[236,1067,1069,1071,1074],{"class":238,"line":1068},61,[236,1070,889],{"class":249},[236,1072,1073],{"class":892}," New-RandomComplexPassword",[236,1075,355],{"class":242},[236,1077,1079,1081,1083,1085],{"class":238,"line":1078},62,[236,1080,901],{"class":249},[236,1082,904],{"class":242},[236,1084,787],{"class":249},[236,1086,1087],{"class":242},"]$Length)\n",[236,1089,1091],{"class":238,"line":1090},63,[236,1092,707],{"emptyLinePlaceholder":706},[236,1094,1096],{"class":238,"line":1095},64,[236,1097,1098],{"class":442},"    # Look-alike characters (I, l, O, o, 0, 1) are left out so the password can be read over the phone.\n",[236,1100,1102,1105,1107,1110],{"class":238,"line":1101},65,[236,1103,1104],{"class":242},"    $sets ",[236,1106,793],{"class":249},[236,1108,1109],{"class":249}," @",[236,1111,666],{"class":242},[236,1113,1115,1118],{"class":238,"line":1114},66,[236,1116,1117],{"class":256},"        'ABCDEFGHJKLMNPQRSTUVWXYZ'",[236,1119,700],{"class":249},[236,1121,1123,1126],{"class":238,"line":1122},67,[236,1124,1125],{"class":256},"        'abcdefghijkmnpqrstuvwxyz'",[236,1127,700],{"class":249},[236,1129,1131,1134],{"class":238,"line":1130},68,[236,1132,1133],{"class":256},"        '23456789'",[236,1135,700],{"class":249},[236,1137,1139],{"class":238,"line":1138},69,[236,1140,1141],{"class":256},"        '!@#$%^&*-_=+'\n",[236,1143,1145],{"class":238,"line":1144},70,[236,1146,1147],{"class":242},"    )\n",[236,1149,1151,1154,1156,1159],{"class":238,"line":1150},71,[236,1152,1153],{"class":242},"    $allChars ",[236,1155,793],{"class":249},[236,1157,1158],{"class":249}," -join",[236,1160,1161],{"class":242}," $sets\n",[236,1163,1165],{"class":238,"line":1164},72,[236,1166,707],{"emptyLinePlaceholder":706},[236,1168,1170],{"class":238,"line":1169},73,[236,1171,1172],{"class":442},"    # One character from each class, then fill the rest from the combined set.\n",[236,1174,1176,1179,1181,1183,1185,1188,1191],{"class":238,"line":1175},74,[236,1177,1178],{"class":242},"    $chars ",[236,1180,793],{"class":249},[236,1182,965],{"class":245},[236,1184,250],{"class":249},[236,1186,1187],{"class":242},"TypeName System.Collections.Generic.List[",[236,1189,1190],{"class":249},"char",[236,1192,1193],{"class":242},"]\n",[236,1195,1197,1200,1203,1206],{"class":238,"line":1196},75,[236,1198,1199],{"class":249},"    foreach",[236,1201,1202],{"class":242}," ($set ",[236,1204,1205],{"class":249},"in",[236,1207,1208],{"class":242}," $sets) {\n",[236,1210,1212,1215,1218,1220],{"class":238,"line":1211},76,[236,1213,1214],{"class":242},"        $chars.Add($set[(",[236,1216,1217],{"class":245},"Get-RandomIndex",[236,1219,250],{"class":249},[236,1221,1222],{"class":242},"MaxExclusive $set.Length)])\n",[236,1224,1226],{"class":238,"line":1225},77,[236,1227,1228],{"class":242},"    }\n",[236,1230,1232,1235,1238,1241],{"class":238,"line":1231},78,[236,1233,1234],{"class":249},"    while",[236,1236,1237],{"class":242}," ($chars.Count ",[236,1239,1240],{"class":249},"-lt",[236,1242,1243],{"class":242}," $Length) {\n",[236,1245,1247,1250,1252,1254],{"class":238,"line":1246},79,[236,1248,1249],{"class":242},"        $chars.Add($allChars[(",[236,1251,1217],{"class":245},[236,1253,250],{"class":249},[236,1255,1256],{"class":242},"MaxExclusive $allChars.Length)])\n",[236,1258,1260],{"class":238,"line":1259},80,[236,1261,1228],{"class":242},[236,1263,1265],{"class":238,"line":1264},81,[236,1266,707],{"emptyLinePlaceholder":706},[236,1268,1270],{"class":238,"line":1269},82,[236,1271,1272],{"class":442},"    # Fisher-Yates shuffle so the guaranteed characters are not always at the front.\n",[236,1274,1276,1279,1282,1284,1287,1289,1292,1295,1298,1300,1303,1306],{"class":238,"line":1275},83,[236,1277,1278],{"class":249},"    for",[236,1280,1281],{"class":242}," ($i ",[236,1283,793],{"class":249},[236,1285,1286],{"class":242}," $chars.Count ",[236,1288,375],{"class":249},[236,1290,1291],{"class":245}," 1",[236,1293,1294],{"class":242},"; $i ",[236,1296,1297],{"class":249},"-gt",[236,1299,1013],{"class":245},[236,1301,1302],{"class":242},"; $i",[236,1304,1305],{"class":249},"--",[236,1307,1308],{"class":242},") {\n",[236,1310,1312,1315,1317,1319,1321,1324,1327,1329],{"class":238,"line":1311},84,[236,1313,1314],{"class":242},"        $j ",[236,1316,793],{"class":249},[236,1318,893],{"class":245},[236,1320,250],{"class":249},[236,1322,1323],{"class":242},"MaxExclusive ($i ",[236,1325,1326],{"class":249},"+",[236,1328,1291],{"class":245},[236,1330,837],{"class":242},[236,1332,1334,1337,1339],{"class":238,"line":1333},85,[236,1335,1336],{"class":242},"        $swap ",[236,1338,793],{"class":249},[236,1340,1341],{"class":242}," $chars[$i]\n",[236,1343,1345,1348,1350],{"class":238,"line":1344},86,[236,1346,1347],{"class":242},"        $chars[$i] ",[236,1349,793],{"class":249},[236,1351,1352],{"class":242}," $chars[$j]\n",[236,1354,1356,1359,1361],{"class":238,"line":1355},87,[236,1357,1358],{"class":242},"        $chars[$j] ",[236,1360,793],{"class":249},[236,1362,1363],{"class":242}," $swap\n",[236,1365,1367],{"class":238,"line":1366},88,[236,1368,1228],{"class":242},[236,1370,1372,1374,1376],{"class":238,"line":1371},89,[236,1373,1039],{"class":249},[236,1375,1158],{"class":249},[236,1377,1378],{"class":242}," $chars\n",[236,1380,1382],{"class":238,"line":1381},90,[236,1383,1060],{"class":242},[236,1385,1387],{"class":238,"line":1386},91,[236,1388,707],{"emptyLinePlaceholder":706},[236,1390,1392,1395,1397],{"class":238,"line":1391},92,[236,1393,1394],{"class":242},"$defaultPolicy ",[236,1396,793],{"class":249},[236,1398,1399],{"class":245}," Get-ADDefaultDomainPasswordPolicy\n",[236,1401,1403,1406,1408,1410,1412,1414,1416,1419,1421,1424,1427,1430,1432,1434,1437,1439],{"class":238,"line":1402},93,[236,1404,1405],{"class":242},"$accounts ",[236,1407,793],{"class":249},[236,1409,1109],{"class":249},[236,1411,634],{"class":242},[236,1413,339],{"class":245},[236,1415,250],{"class":249},[236,1417,1418],{"class":242},"Path $CsvPath ",[236,1420,403],{"class":249},[236,1422,1423],{"class":245}," Where-Object",[236,1425,1426],{"class":242}," { ",[236,1428,1429],{"class":249},"-not",[236,1431,872],{"class":242},[236,1433,694],{"class":249},[236,1435,1436],{"class":242},"]::IsNullOrWhiteSpace(",[236,1438,369],{"class":245},[236,1440,1441],{"class":242},".SamAccountName) })\n",[236,1443,1445,1448,1450,1452,1454,1456,1459],{"class":238,"line":1444},94,[236,1446,1447],{"class":242},"$report ",[236,1449,793],{"class":249},[236,1451,965],{"class":245},[236,1453,250],{"class":249},[236,1455,1187],{"class":242},[236,1457,1458],{"class":249},"object",[236,1460,1193],{"class":242},[236,1462,1464,1467,1469],{"class":238,"line":1463},95,[236,1465,1466],{"class":242},"$successCount ",[236,1468,793],{"class":249},[236,1470,1471],{"class":245}," 0\n",[236,1473,1475],{"class":238,"line":1474},96,[236,1476,707],{"emptyLinePlaceholder":706},[236,1478,1480,1483,1486,1488],{"class":238,"line":1479},97,[236,1481,1482],{"class":249},"foreach",[236,1484,1485],{"class":242}," ($account ",[236,1487,1205],{"class":249},[236,1489,1490],{"class":242}," $accounts) {\n",[236,1492,1494,1497,1499],{"class":238,"line":1493},98,[236,1495,1496],{"class":242},"    $samAccountName ",[236,1498,793],{"class":249},[236,1500,1501],{"class":242}," $account.SamAccountName.Trim()\n",[236,1503,1505,1508,1510,1513,1515,1518],{"class":238,"line":1504},99,[236,1506,1507],{"class":242},"    $timestamp ",[236,1509,793],{"class":249},[236,1511,1512],{"class":245}," Get-Date",[236,1514,250],{"class":249},[236,1516,1517],{"class":242},"Format ",[236,1519,1520],{"class":256},"'yyyy-MM-dd HH:mm:ss'\n",[236,1522,1524],{"class":238,"line":1523},100,[236,1525,707],{"emptyLinePlaceholder":706},[236,1527,1529],{"class":238,"line":1528},101,[236,1530,1531],{"class":442},"    # Double any single quote so a name such as o'brien can't break out of the filter string.\n",[236,1533,1535,1538,1540,1543,1546,1549,1551],{"class":238,"line":1534},102,[236,1536,1537],{"class":242},"    $filterName ",[236,1539,793],{"class":249},[236,1541,1542],{"class":242}," $samAccountName ",[236,1544,1545],{"class":249},"-replace",[236,1547,1548],{"class":256}," \"'\"",[236,1550,381],{"class":249},[236,1552,1553],{"class":256}," \"''\"\n",[236,1555,1557,1560,1562,1565,1567,1570,1573,1576,1579,1581,1584,1586,1588,1590],{"class":238,"line":1556},103,[236,1558,1559],{"class":242},"    $adUser ",[236,1561,793],{"class":249},[236,1563,1564],{"class":245}," Get-ADUser",[236,1566,250],{"class":249},[236,1568,1569],{"class":242},"Filter ",[236,1571,1572],{"class":256},"\"SamAccountName -eq '",[236,1574,1575],{"class":242},"$filterName",[236,1577,1578],{"class":256},"'\"",[236,1580,250],{"class":249},[236,1582,1583],{"class":242},"Properties LockedOut",[236,1585,381],{"class":249},[236,1587,389],{"class":242},[236,1589,381],{"class":249},[236,1591,1592],{"class":242}," Enabled\n",[236,1594,1596],{"class":238,"line":1595},104,[236,1597,707],{"emptyLinePlaceholder":706},[236,1599,1601,1604,1607,1610,1613],{"class":238,"line":1600},105,[236,1602,1603],{"class":249},"    if",[236,1605,1606],{"class":242}," (",[236,1608,1609],{"class":245},"$null",[236,1611,1612],{"class":249}," -eq",[236,1614,1615],{"class":242}," $adUser) {\n",[236,1617,1619,1622,1625,1628],{"class":238,"line":1618},106,[236,1620,1621],{"class":245},"        Write-Warning",[236,1623,1624],{"class":256}," \"",[236,1626,1627],{"class":242},"$samAccountName",[236,1629,1630],{"class":256}," not found in Active Directory - skipped.\"\n",[236,1632,1634,1637,1640,1643,1646,1649,1651,1654,1656,1659,1662,1664,1667,1670,1672],{"class":238,"line":1633},107,[236,1635,1636],{"class":242},"        $report.Add([",[236,1638,1639],{"class":249},"PSCustomObject",[236,1641,1642],{"class":242},"]",[236,1644,1645],{"class":249},"@",[236,1647,1648],{"class":242},"{ SamAccountName ",[236,1650,793],{"class":249},[236,1652,1653],{"class":242}," $samAccountName; TemporaryPassword ",[236,1655,793],{"class":249},[236,1657,1658],{"class":256}," ''",[236,1660,1661],{"class":242},"; Result ",[236,1663,793],{"class":249},[236,1665,1666],{"class":256}," 'Not found'",[236,1668,1669],{"class":242},"; ResetDate ",[236,1671,793],{"class":249},[236,1673,1674],{"class":242}," $timestamp })\n",[236,1676,1678],{"class":238,"line":1677},108,[236,1679,1680],{"class":249},"        continue\n",[236,1682,1684],{"class":238,"line":1683},109,[236,1685,1228],{"class":242},[236,1687,1689],{"class":238,"line":1688},110,[236,1690,707],{"emptyLinePlaceholder":706},[236,1692,1694],{"class":238,"line":1693},111,[236,1695,1696],{"class":442},"    # Decided before ShouldProcess so -WhatIf and -Confirm show which accounts won't be forced to change.\n",[236,1698,1700,1703,1705,1708,1711,1714],{"class":238,"line":1699},112,[236,1701,1702],{"class":242},"    $forceChange ",[236,1704,793],{"class":249},[236,1706,1707],{"class":242}," $ClearPasswordNeverExpires ",[236,1709,1710],{"class":249},"-or",[236,1712,1713],{"class":249}," -not",[236,1715,1716],{"class":242}," $adUser.PasswordNeverExpires\n",[236,1718,1720,1723,1725,1728,1730,1732],{"class":238,"line":1719},113,[236,1721,1722],{"class":242},"    $operation ",[236,1724,793],{"class":249},[236,1726,1727],{"class":249}," if",[236,1729,1606],{"class":242},[236,1731,1429],{"class":249},[236,1733,1734],{"class":242}," $forceChange) {\n",[236,1736,1738],{"class":238,"line":1737},114,[236,1739,1740],{"class":256},"        'Reset password only; PasswordNeverExpires is set, so change at next logon is NOT forced'\n",[236,1742,1744,1746,1749],{"class":238,"line":1743},115,[236,1745,1021],{"class":242},[236,1747,1748],{"class":249},"elseif",[236,1750,1751],{"class":242}," ($adUser.PasswordNeverExpires) {\n",[236,1753,1755],{"class":238,"line":1754},116,[236,1756,1757],{"class":256},"        'Clear PasswordNeverExpires, reset password and force change at next logon'\n",[236,1759,1761,1763,1766],{"class":238,"line":1760},117,[236,1762,1021],{"class":242},[236,1764,1765],{"class":249},"else",[236,1767,355],{"class":242},[236,1769,1771],{"class":238,"line":1770},118,[236,1772,1773],{"class":256},"        'Reset password and force change at next logon'\n",[236,1775,1777],{"class":238,"line":1776},119,[236,1778,1228],{"class":242},[236,1780,1782],{"class":238,"line":1781},120,[236,1783,707],{"emptyLinePlaceholder":706},[236,1785,1787,1789,1791,1793,1796,1799,1801],{"class":238,"line":1786},121,[236,1788,1603],{"class":249},[236,1790,1606],{"class":242},[236,1792,1429],{"class":249},[236,1794,1795],{"class":245}," $PSCmdlet",[236,1797,1798],{"class":242},".ShouldProcess($samAccountName",[236,1800,381],{"class":249},[236,1802,1803],{"class":242}," $operation)) {\n",[236,1805,1807],{"class":238,"line":1806},122,[236,1808,1680],{"class":249},[236,1810,1812],{"class":238,"line":1811},123,[236,1813,1228],{"class":242},[236,1815,1817],{"class":238,"line":1816},124,[236,1818,707],{"emptyLinePlaceholder":706},[236,1820,1822,1825,1827],{"class":238,"line":1821},125,[236,1823,1824],{"class":242},"    $passwordSet ",[236,1826,793],{"class":249},[236,1828,1829],{"class":245}," $false\n",[236,1831,1833,1836],{"class":238,"line":1832},126,[236,1834,1835],{"class":249},"    try",[236,1837,355],{"class":242},[236,1839,1841],{"class":238,"line":1840},127,[236,1842,1843],{"class":442},"        # Effective minimum length: a fine-grained PSO wins over the domain policy when one applies.\n",[236,1845,1847,1850,1852,1855,1857],{"class":238,"line":1846},128,[236,1848,1849],{"class":242},"        $policy ",[236,1851,793],{"class":249},[236,1853,1854],{"class":245}," Get-ADUserResultantPasswordPolicy",[236,1856,250],{"class":249},[236,1858,1859],{"class":242},"Identity $adUser\n",[236,1861,1863,1866,1868,1870],{"class":238,"line":1862},129,[236,1864,1865],{"class":249},"        if",[236,1867,1606],{"class":242},[236,1869,1429],{"class":249},[236,1871,1872],{"class":242}," $policy) {\n",[236,1874,1876,1879,1881],{"class":238,"line":1875},130,[236,1877,1878],{"class":242},"            $policy ",[236,1880,793],{"class":249},[236,1882,1883],{"class":242}," $defaultPolicy\n",[236,1885,1887],{"class":238,"line":1886},131,[236,1888,1889],{"class":242},"        }\n",[236,1891,1893,1896,1898,1900,1903,1906,1908,1910,1912],{"class":238,"line":1892},132,[236,1894,1895],{"class":242},"        $length ",[236,1897,793],{"class":249},[236,1899,872],{"class":242},[236,1901,1902],{"class":249},"math",[236,1904,1905],{"class":242},"]::Max($PasswordLength",[236,1907,381],{"class":249},[236,1909,872],{"class":242},[236,1911,787],{"class":249},[236,1913,1914],{"class":242},"]$policy.MinPasswordLength)\n",[236,1916,1918],{"class":238,"line":1917},133,[236,1919,707],{"emptyLinePlaceholder":706},[236,1921,1923,1926,1928,1930,1932],{"class":238,"line":1922},134,[236,1924,1925],{"class":242},"        $newPassword ",[236,1927,793],{"class":249},[236,1929,1073],{"class":245},[236,1931,250],{"class":249},[236,1933,1934],{"class":242},"Length $length\n",[236,1936,1938,1941,1943,1946,1948,1951,1953,1956,1958],{"class":238,"line":1937},135,[236,1939,1940],{"class":242},"        $securePassword ",[236,1942,793],{"class":249},[236,1944,1945],{"class":245}," ConvertTo-SecureString",[236,1947,250],{"class":249},[236,1949,1950],{"class":242},"String $newPassword ",[236,1952,375],{"class":249},[236,1954,1955],{"class":242},"AsPlainText ",[236,1957,375],{"class":249},[236,1959,1960],{"class":242},"Force\n",[236,1962,1964],{"class":238,"line":1963},136,[236,1965,707],{"emptyLinePlaceholder":706},[236,1967,1969,1972,1974,1977,1979,1982,1984,1987,1989],{"class":238,"line":1968},137,[236,1970,1971],{"class":245},"        Set-ADAccountPassword",[236,1973,250],{"class":249},[236,1975,1976],{"class":242},"Identity $adUser ",[236,1978,375],{"class":249},[236,1980,1981],{"class":242},"NewPassword $securePassword ",[236,1983,375],{"class":249},[236,1985,1986],{"class":242},"Reset ",[236,1988,375],{"class":249},[236,1990,856],{"class":242},[236,1992,1994,1997,1999],{"class":238,"line":1993},138,[236,1995,1996],{"class":242},"        $passwordSet ",[236,1998,793],{"class":249},[236,2000,2001],{"class":245}," $true\n",[236,2003,2005,2008,2010],{"class":238,"line":2004},139,[236,2006,2007],{"class":242},"        $result ",[236,2009,793],{"class":249},[236,2011,2012],{"class":256}," 'Reset'\n",[236,2014,2016],{"class":238,"line":2015},140,[236,2017,707],{"emptyLinePlaceholder":706},[236,2019,2021,2023,2026,2029],{"class":238,"line":2020},141,[236,2022,1865],{"class":249},[236,2024,2025],{"class":242}," ($adUser.PasswordNeverExpires ",[236,2027,2028],{"class":249},"-and",[236,2030,2031],{"class":242}," $ClearPasswordNeverExpires) {\n",[236,2033,2035,2038,2040,2042,2044,2047,2050,2052],{"class":238,"line":2034},142,[236,2036,2037],{"class":245},"            Set-ADUser",[236,2039,250],{"class":249},[236,2041,1976],{"class":242},[236,2043,375],{"class":249},[236,2045,2046],{"class":242},"PasswordNeverExpires ",[236,2048,2049],{"class":245},"$false",[236,2051,250],{"class":249},[236,2053,856],{"class":242},[236,2055,2057,2060,2063],{"class":238,"line":2056},143,[236,2058,2059],{"class":242},"            $result ",[236,2061,2062],{"class":249},"+=",[236,2064,2065],{"class":256}," '; PasswordNeverExpires cleared'\n",[236,2067,2069],{"class":238,"line":2068},144,[236,2070,1889],{"class":242},[236,2072,2074],{"class":238,"line":2073},145,[236,2075,707],{"emptyLinePlaceholder":706},[236,2077,2079,2081,2083,2085],{"class":238,"line":2078},146,[236,2080,1865],{"class":249},[236,2082,1606],{"class":242},[236,2084,1429],{"class":249},[236,2086,1734],{"class":242},[236,2088,2090,2093,2095,2097],{"class":238,"line":2089},147,[236,2091,2092],{"class":245},"            Write-Warning",[236,2094,1624],{"class":256},[236,2096,1627],{"class":242},[236,2098,2099],{"class":256}," has PasswordNeverExpires set; password reset but ChangePasswordAtLogon NOT set. Re-run with -ClearPasswordNeverExpires or handle it by hand.\"\n",[236,2101,2103,2105,2107],{"class":238,"line":2102},148,[236,2104,2059],{"class":242},[236,2106,2062],{"class":249},[236,2108,2109],{"class":256}," '; NOT forced (PasswordNeverExpires)'\n",[236,2111,2113,2116,2118],{"class":238,"line":2112},149,[236,2114,2115],{"class":242},"        } ",[236,2117,1765],{"class":249},[236,2119,355],{"class":242},[236,2121,2123,2125,2127,2129,2131,2134,2137,2139],{"class":238,"line":2122},150,[236,2124,2037],{"class":245},[236,2126,250],{"class":249},[236,2128,1976],{"class":242},[236,2130,375],{"class":249},[236,2132,2133],{"class":242},"ChangePasswordAtLogon ",[236,2135,2136],{"class":245},"$true",[236,2138,250],{"class":249},[236,2140,856],{"class":242},[236,2142,2144,2146,2148],{"class":238,"line":2143},151,[236,2145,2059],{"class":242},[236,2147,2062],{"class":249},[236,2149,2150],{"class":256}," '; must change at next logon'\n",[236,2152,2154],{"class":238,"line":2153},152,[236,2155,1889],{"class":242},[236,2157,2159],{"class":238,"line":2158},153,[236,2160,707],{"emptyLinePlaceholder":706},[236,2162,2164,2167,2169,2172,2175],{"class":238,"line":2163},154,[236,2165,2166],{"class":242},"        $message ",[236,2168,793],{"class":249},[236,2170,2171],{"class":256}," \"Reset ",[236,2173,2174],{"class":242},"${samAccountName}",[236,2176,2177],{"class":256},": ChangePasswordAtLogon set\"\n",[236,2179,2181,2183],{"class":238,"line":2180},155,[236,2182,1865],{"class":249},[236,2184,2185],{"class":242}," ($adUser.LockedOut) {\n",[236,2187,2189,2192,2194,2196,2198],{"class":238,"line":2188},156,[236,2190,2191],{"class":245},"            Unlock-ADAccount",[236,2193,250],{"class":249},[236,2195,1976],{"class":242},[236,2197,375],{"class":249},[236,2199,856],{"class":242},[236,2201,2203,2205,2207],{"class":238,"line":2202},157,[236,2204,2059],{"class":242},[236,2206,2062],{"class":249},[236,2208,2209],{"class":256}," '; unlocked'\n",[236,2211,2213,2216,2218],{"class":238,"line":2212},158,[236,2214,2215],{"class":242},"            $message ",[236,2217,2062],{"class":249},[236,2219,2220],{"class":256}," ', account unlocked'\n",[236,2222,2224],{"class":238,"line":2223},159,[236,2225,1889],{"class":242},[236,2227,2229,2231,2233,2235],{"class":238,"line":2228},160,[236,2230,1865],{"class":249},[236,2232,1606],{"class":242},[236,2234,1429],{"class":249},[236,2236,2237],{"class":242}," $adUser.Enabled) {\n",[236,2239,2241,2243,2245],{"class":238,"line":2240},161,[236,2242,2059],{"class":242},[236,2244,2062],{"class":249},[236,2246,2247],{"class":256}," '; account is disabled'\n",[236,2249,2251],{"class":238,"line":2250},162,[236,2252,1889],{"class":242},[236,2254,2256],{"class":238,"line":2255},163,[236,2257,707],{"emptyLinePlaceholder":706},[236,2259,2261,2263,2266,2269,2272],{"class":238,"line":2260},164,[236,2262,1865],{"class":249},[236,2264,2265],{"class":242}," ($result ",[236,2267,2268],{"class":249},"-notmatch",[236,2270,2271],{"class":256}," 'NOT forced'",[236,2273,1308],{"class":242},[236,2275,2277,2280,2282,2285],{"class":238,"line":2276},165,[236,2278,2279],{"class":245},"            Write-Host",[236,2281,1624],{"class":256},[236,2283,2284],{"class":242},"$message",[236,2286,2287],{"class":256},".\"\n",[236,2289,2291],{"class":238,"line":2290},166,[236,2292,1889],{"class":242},[236,2294,2296],{"class":238,"line":2295},167,[236,2297,707],{"emptyLinePlaceholder":706},[236,2299,2301,2303,2305,2307,2309,2311,2313,2315,2317,2320,2322,2325,2327],{"class":238,"line":2300},168,[236,2302,1636],{"class":242},[236,2304,1639],{"class":249},[236,2306,1642],{"class":242},[236,2308,1645],{"class":249},[236,2310,1648],{"class":242},[236,2312,793],{"class":249},[236,2314,1653],{"class":242},[236,2316,793],{"class":249},[236,2318,2319],{"class":242}," $newPassword; Result ",[236,2321,793],{"class":249},[236,2323,2324],{"class":242}," $result; ResetDate ",[236,2326,793],{"class":249},[236,2328,1674],{"class":242},[236,2330,2332,2335],{"class":238,"line":2331},169,[236,2333,2334],{"class":242},"        $successCount",[236,2336,2337],{"class":249},"++\n",[236,2339,2341,2343,2346],{"class":238,"line":2340},170,[236,2342,1021],{"class":242},[236,2344,2345],{"class":249},"catch",[236,2347,355],{"class":242},[236,2349,2351,2353],{"class":238,"line":2350},171,[236,2352,1865],{"class":249},[236,2354,2355],{"class":242}," ($passwordSet) {\n",[236,2357,2359],{"class":238,"line":2358},172,[236,2360,2361],{"class":442},"            # The password did change, so keep it in the report; only the follow-up step failed.\n",[236,2363,2365,2367,2370,2372,2375,2378,2380,2382,2385,2388],{"class":238,"line":2364},173,[236,2366,2092],{"class":245},[236,2368,2369],{"class":256}," \"Password for ",[236,2371,1627],{"class":242},[236,2373,2374],{"class":256}," was reset, but a follow-up step failed: ",[236,2376,2377],{"class":249},"$",[236,2379,634],{"class":256},[236,2381,369],{"class":245},[236,2383,2384],{"class":242},".Exception.Message",[236,2386,2387],{"class":256},")",[236,2389,2390],{"class":256},"\"\n",[236,2392,2394,2397,2399,2401,2403,2405,2407,2409,2411,2413,2415,2418,2420,2422,2424,2426,2428,2431,2433,2435],{"class":238,"line":2393},174,[236,2395,2396],{"class":242},"            $report.Add([",[236,2398,1639],{"class":249},[236,2400,1642],{"class":242},[236,2402,1645],{"class":249},[236,2404,1648],{"class":242},[236,2406,793],{"class":249},[236,2408,1653],{"class":242},[236,2410,793],{"class":249},[236,2412,2319],{"class":242},[236,2414,793],{"class":249},[236,2416,2417],{"class":256}," \"Reset, then failed: ",[236,2419,2377],{"class":249},[236,2421,634],{"class":256},[236,2423,369],{"class":245},[236,2425,2384],{"class":242},[236,2427,2387],{"class":256},[236,2429,2430],{"class":256},"\"",[236,2432,1669],{"class":242},[236,2434,793],{"class":249},[236,2436,1674],{"class":242},[236,2438,2440,2442,2444],{"class":238,"line":2439},175,[236,2441,2115],{"class":242},[236,2443,1765],{"class":249},[236,2445,355],{"class":242},[236,2447,2449,2451,2454,2456,2459,2461,2463,2465,2467,2469],{"class":238,"line":2448},176,[236,2450,2092],{"class":245},[236,2452,2453],{"class":256}," \"Failed on ",[236,2455,2174],{"class":242},[236,2457,2458],{"class":256},": ",[236,2460,2377],{"class":249},[236,2462,634],{"class":256},[236,2464,369],{"class":245},[236,2466,2384],{"class":242},[236,2468,2387],{"class":256},[236,2470,2390],{"class":256},[236,2472,2474,2476,2478,2480,2482,2484,2486,2488,2490,2492,2494,2496,2499,2501,2503,2505,2507,2509,2511,2513,2515],{"class":238,"line":2473},177,[236,2475,2396],{"class":242},[236,2477,1639],{"class":249},[236,2479,1642],{"class":242},[236,2481,1645],{"class":249},[236,2483,1648],{"class":242},[236,2485,793],{"class":249},[236,2487,1653],{"class":242},[236,2489,793],{"class":249},[236,2491,1658],{"class":256},[236,2493,1661],{"class":242},[236,2495,793],{"class":249},[236,2497,2498],{"class":256}," \"Failed: ",[236,2500,2377],{"class":249},[236,2502,634],{"class":256},[236,2504,369],{"class":245},[236,2506,2384],{"class":242},[236,2508,2387],{"class":256},[236,2510,2430],{"class":256},[236,2512,1669],{"class":242},[236,2514,793],{"class":249},[236,2516,1674],{"class":242},[236,2518,2520],{"class":238,"line":2519},178,[236,2521,1889],{"class":242},[236,2523,2525],{"class":238,"line":2524},179,[236,2526,1228],{"class":242},[236,2528,2530],{"class":238,"line":2529},180,[236,2531,1060],{"class":242},[236,2533,2535],{"class":238,"line":2534},181,[236,2536,707],{"emptyLinePlaceholder":706},[236,2538,2540,2543,2546,2548,2550],{"class":238,"line":2539},182,[236,2541,2542],{"class":249},"if",[236,2544,2545],{"class":242}," ($report.Count ",[236,2547,1297],{"class":249},[236,2549,1013],{"class":245},[236,2551,1308],{"class":242},[236,2553,2555,2558,2560,2563,2565,2568,2570,2573,2575],{"class":238,"line":2554},183,[236,2556,2557],{"class":242},"    $report ",[236,2559,403],{"class":249},[236,2561,2562],{"class":245}," Export-Csv",[236,2564,250],{"class":249},[236,2566,2567],{"class":242},"Path $ReportPath ",[236,2569,375],{"class":249},[236,2571,2572],{"class":242},"NoTypeInformation ",[236,2574,375],{"class":249},[236,2576,2577],{"class":242},"Encoding UTF8\n",[236,2579,2581],{"class":238,"line":2580},184,[236,2582,1060],{"class":242},[236,2584,2586],{"class":238,"line":2585},185,[236,2587,707],{"emptyLinePlaceholder":706},[236,2589,2591],{"class":238,"line":2590},186,[236,2592,2593],{"class":242},"$rng.Dispose()\n",[236,2595,2597],{"class":238,"line":2596},187,[236,2598,707],{"emptyLinePlaceholder":706},[236,2600,2602,2604,2606,2609],{"class":238,"line":2601},188,[236,2603,2542],{"class":249},[236,2605,1606],{"class":242},[236,2607,2608],{"class":245},"$WhatIfPreference",[236,2610,1308],{"class":242},[236,2612,2614,2617],{"class":238,"line":2613},189,[236,2615,2616],{"class":245},"    Write-Host",[236,2618,2619],{"class":256}," \"WhatIf run: no passwords were changed.\"\n",[236,2621,2623],{"class":238,"line":2622},190,[236,2624,2625],{"class":249},"    return\n",[236,2627,2629],{"class":238,"line":2628},191,[236,2630,1060],{"class":242},[236,2632,2634],{"class":238,"line":2633},192,[236,2635,707],{"emptyLinePlaceholder":706},[236,2637,2639,2642,2644,2647,2649],{"class":238,"line":2638},193,[236,2640,2641],{"class":242},"$failed ",[236,2643,793],{"class":249},[236,2645,2646],{"class":242}," $accounts.Count ",[236,2648,375],{"class":249},[236,2650,2651],{"class":242}," $successCount\n",[236,2653,2655,2658,2660,2663,2666,2668,2670,2673,2675,2678,2681,2684,2687],{"class":238,"line":2654},194,[236,2656,2657],{"class":245},"Write-Host",[236,2659,1624],{"class":256},[236,2661,2662],{"class":242},"$successCount",[236,2664,2665],{"class":256}," of ",[236,2667,2377],{"class":249},[236,2669,634],{"class":256},[236,2671,2672],{"class":242},"$accounts.Count",[236,2674,2387],{"class":256},[236,2676,2677],{"class":256}," account(s) reset, ",[236,2679,2680],{"class":242},"$failed",[236,2682,2683],{"class":256}," failed or skipped. Report written to ",[236,2685,2686],{"class":242},"$ReportPath",[236,2688,2390],{"class":256},[44,2690,2692],{"id":2691},"notes","Notes",[49,2694,2695,2705,2725,2741,2750,2761,2764],{},[52,2696,2697,2700,2701,2704],{},[23,2698,2699],{},"The report contains plaintext passwords."," Write it to a folder only the operator can read, move it over an already-encrypted channel (a password manager's secure share, not email or chat), and delete it once every user has signed in and changed their password. If you don't need to hand passwords out, because users will go through self-service reset or the help desk will set a new one on the phone, remove the ",[15,2702,2703],{},"TemporaryPassword"," column from the report.",[52,2706,2707,2710,2711,2714,2715,2718,2719,2721,2722,204],{},[15,2708,2709],{},"Set-ADAccountPassword -Reset"," requires ",[15,2712,2713],{},"-NewPassword"," and does not need the old one. The new password still has to satisfy the effective policy's length and complexity rules, or the cmdlet throws and the account lands in the report as failed. That's why the generator reads ",[15,2716,2717],{},"MinPasswordLength"," from ",[15,2720,86],{}," (which returns nothing when no fine-grained policy applies) and falls back to ",[15,2723,2724],{},"Get-ADDefaultDomainPasswordPolicy",[52,2726,2727,2730,2731,2734,2735,2737,2738,2740],{},[15,2728,2729],{},"ChangePasswordAtLogon $true"," writes ",[15,2732,2733],{},"pwdLastSet = 0",". It's also what makes ",[15,2736,324],{}," read ",[15,2739,328],{}," until the user changes it, which is why the verification snippet above is a reliable check.",[52,2742,2743,2746,2747,2749],{},[23,2744,2745],{},"Don't put service accounts in the list without a plan."," A service account can't answer a \"change your password at next logon\" prompt, so forcing the change on one will break whatever runs under it until someone sets a new password in AD and on every service, scheduled task or application pool that uses it. For those, reset to a known value and update the consumers in the same change window, or better, move them to group managed service accounts: ",[15,2748,57],{}," can't set a gMSA password at all, because the domain rotates it on its own schedule.",[52,2751,2752,2753,2756,2757,2760],{},"A password reset does not end sessions that already exist. Kerberos tickets that were issued before the reset stay valid until they expire, and cloud sessions are separate: if the accounts sync to Entra ID and the incident calls for it, also revoke refresh tokens with ",[15,2754,2755],{},"Revoke-MgUserSignInSession -UserId \u003Cuser-id>"," (Microsoft Graph PowerShell, ",[15,2758,2759],{},"User.RevokeSessions.All",").",[52,2762,2763],{},"The script reports disabled accounts but still resets them. That's deliberate for a breach response, where a disabled account with a known password is still a risk if someone re-enables it.",[52,2765,2766,2769,2770,2773,2774,2776,2777,204],{},[15,2767,2768],{},"ConfirmImpact = 'High'"," means PowerShell asks for confirmation on each account unless you pass ",[15,2771,2772],{},"-Confirm:$false",". For a long list, run ",[15,2775,200],{}," first, review, then run with ",[15,2778,2772],{},[44,2780,2782],{"id":2781},"source","Source",[49,2784,2785,2793,2800,2806,2812],{},[52,2786,2787],{},[2788,2789,57],"a",{"href":2790,"rel":2791},"https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fpowershell\u002Fmodule\u002Factivedirectory\u002Fset-adaccountpassword",[2792],"nofollow",[52,2794,2795],{},[2788,2796,2799],{"href":2797,"rel":2798},"https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fpowershell\u002Fmodule\u002Factivedirectory\u002Fset-aduser",[2792],"Set-ADUser (ChangePasswordAtLogon, PasswordNeverExpires)",[52,2801,2802],{},[2788,2803,86],{"href":2804,"rel":2805},"https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fpowershell\u002Fmodule\u002Factivedirectory\u002Fget-aduserresultantpasswordpolicy",[2792],[52,2807,2808],{},[2788,2809,2724],{"href":2810,"rel":2811},"https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fpowershell\u002Fmodule\u002Factivedirectory\u002Fget-addefaultdomainpasswordpolicy",[2792],[52,2813,2814],{},[2788,2815,2818],{"href":2816,"rel":2817},"https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fpowershell\u002Fmodule\u002Fmicrosoft.graph.users.actions\u002Frevoke-mgusersigninsession",[2792],"Revoke-MgUserSignInSession",[2820,2821,2822],"style",{},"html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}",{"title":222,"searchDepth":358,"depth":358,"links":2824},[2825,2826,2827,2828,2829,2830],{"id":46,"depth":358,"text":47},{"id":97,"depth":358,"text":98},{"id":207,"depth":358,"text":208},{"id":431,"depth":358,"text":432},{"id":2691,"depth":358,"text":2692},{"id":2781,"depth":358,"text":2782},"techcolumnist",[2833,2834],"ps","scripts","2025-10-01T14:00:00Z","Reset a list of AD accounts to random, policy-length passwords, force a change at next logon, handle never-expires and lockouts, and write a report.","md",false,null,{},"\u002Fblog\u002F2025\u002F10\u002F01\u002Fpowershell-active-directory-bulk-reset-passwords-with-a-forced-change",{"title":6,"description":2836},[2831],"blog\u002F2025\u002F10\u002F01\u002Fpowershell-active-directory-bulk-reset-passwords-with-a-forced-change",[232,2846],"active-directory","\u002F2025\u002F10\u002F01\u002Fpowershell-active-directory-bulk-reset-passwords-with-a-forced-change\u002F","su6EoLp3yocZwDK470EEil6ObP5IMTZstuPKy_XOf6Q",{"title":2850,"description":2851,"date":2852,"url":2853,"categories":2854,"tags":2855,"image":2839,"readingTime":466,"canonical":2831,"sites":2858,"series":2839,"seriesOrder":2839},"PowerShell: Windows – Discover Local Admins Across Every Workstation","A script that reads the local Administrators group on every workstation by SID, survives orphaned-SID errors, and flags members outside an approved allowlist.","2025-10-08T14:00:00Z","\u002F2025\u002F10\u002F08\u002Fpowershell-windows-discover-local-admins-across-every-workstation\u002F",[2833,2834],[232,2856,2857,2846],"windows","discovery",[2831],{"title":2860,"description":2861,"date":2862,"url":2863,"categories":2864,"tags":2865,"image":2839,"readingTime":458,"canonical":2831,"sites":2867,"series":2839,"seriesOrder":2839},"PowerShell: Azure – Tag Every Untagged Resource in a Subscription","A PowerShell sweep that finds untagged Azure resources and adds only the missing resource group tags, plus the built-in Azure Policy that does it going forward.","2025-09-24T14:00:00Z","\u002F2025\u002F09\u002F24\u002Fpowershell-azure-tag-every-untagged-resource-in-a-subscription\u002F",[2833,2834],[232,2866,2857],"azure",[2831],[2869,2849,2878],{"title":2870,"description":2871,"date":2872,"url":2873,"categories":2874,"tags":2875,"image":2839,"readingTime":466,"canonical":2831,"sites":2877,"series":2839,"seriesOrder":2839},"PowerShell: Active Directory – Report Group Membership Sprawl","Rank AD security groups by nesting depth, transitive member count, ownership and staleness for an access review, plus a per-user Kerberos token size estimate.","2025-12-03T14:00:00Z","\u002F2025\u002F12\u002F03\u002Fpowershell-active-directory-report-group-membership-sprawl\u002F",[2833,2834],[232,2846,2876],"reporting",[2831],{"title":2879,"description":2880,"date":2881,"url":2882,"categories":2883,"tags":2884,"image":2839,"readingTime":397,"canonical":2831,"sites":2885,"series":2839,"seriesOrder":2839},"PowerShell: Windows – Inventory Installed Software Across a Domain","A script that reads the Uninstall registry keys on many Windows computers in parallel over PowerShell remoting and writes one CSV, without touching Win32_Product.","2025-08-27T14:00:00Z","\u002F2025\u002F08\u002F27\u002Fpowershell-windows-inventory-installed-software-across-a-domain\u002F",[2833,2834],[232,2856,2857,2846],[2831],{"doc":2839,"posts":2887},[],1790052513189]