From 9c2927f2b7eedfb384170fdbdae406c3bd0cd6b6 Mon Sep 17 00:00:00 2001 From: Sam Erde <20478745+SamErde@users.noreply.github.com> Date: Wed, 29 Apr 2026 05:51:43 -0400 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=90=9B=20fix(scripts):=20fix=20all=20?= =?UTF-8?q?critical=20code=20bugs=20found=20during=20audit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - F04: add missing comma in Purge-InstalledModules module name array - F06: add missing -Value to Set-Variable in Get-ADUserTransitiveGroupMembership - F08: add missing -Value to Set-Variable in Export-AllADUserTransitiveGroupMemberships - F10: fix broken AD -Filter (single-quoted string) in Export-AllGroupMemberships - F12: fix ValidateSet mismatch ('Office' -> 'physicalDeliveryOfficeName') in Get-ADAttributeUniqueValues - F13/F15: convert empty hard-coded \ to mandatory parameter; fix date format (hh-m-ss -> HH-mm-ss); add TODO comments on unreplaceable placeholders in Archive-ObsoleteGroups - F21: fix Select-Object -ExpandProperty with multiple properties in Get-FSMORoleDetails (only accepts one); cache Forest/Domain objects and access properties directly - F23: fix \$._Index typo -> \ in Update-DnsServerList - F26: replace Enter-PSSession/Exit-PSSession (interactive-only) with Invoke-Command -Session in Push-DNSClientServerAddresses; add Remove-PSSession in finally block Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../AD Groups/Archive-ObsoleteGroups.ps1 | 26 ++++++++++++++----- .../Domain Services/Get-FSMORoleDetails.ps1 | 12 +++++++-- ...rt-AllADUserTransitiveGroupMemberships.ps1 | 2 +- .../Export-AllGroupMemberships.ps1 | 2 +- .../Get-ADAttributeUniqueValues.ps1 | 2 +- .../Get-ADUserTransitiveGroupMembership.ps1 | 2 +- DDI/Update-DnsServerList.ps1 | 2 +- General/Purge-InstalledModules.ps1 | 2 +- Windows/Push-DNSClientServerAddresses.ps1 | 20 ++++++++------ 9 files changed, 48 insertions(+), 22 deletions(-) diff --git a/Active Directory/AD Groups/Archive-ObsoleteGroups.ps1 b/Active Directory/AD Groups/Archive-ObsoleteGroups.ps1 index d93bd0f..cca8ec0 100644 --- a/Active Directory/AD Groups/Archive-ObsoleteGroups.ps1 +++ b/Active Directory/AD Groups/Archive-ObsoleteGroups.ps1 @@ -21,11 +21,23 @@ #Import the Active Directory module so we can work with AD groups. Import-Module ActiveDirectory -#Set the Active Directory server name that will be used. Using a serverless domain name here may also work. -$Domain = '' +# TODO: This script requires customization before running. The $Domain, archive paths, +# TODO: group identity/OU values in the loop, and Move-ADObject target path must all be set +# TODO: for your environment. See inline TODO comments below. +param ( + # The Active Directory domain controller or domain name to run against. + [Parameter(Mandatory)] + [string] + $Domain, + + # Path to the file listing obsolete group names (one per line). + [Parameter()] + [string] + $GroupListPath = 'C:\Scripts\ObsoleteGroups\ObsoleteGroups.csv' +) #Read in the CSV or text file of group names. -$File = Get-Content -Path C:\Scripts\ObsoleteGroups\ObsoleteGroups.csv +$File = Get-Content -Path $GroupListPath #Loop through each line of the text file and run the following commands for each line: Foreach ($Group in $File) { @@ -37,11 +49,13 @@ Foreach ($Group in $File) { This section will require special customization until we further develop the script to pull the full group DN. In the interest of time today, I have hard coded some of the information. * * * * * * * * * * - /#> + #> + # TODO: Replace the -group, -ou, and -domain placeholder values with real values for your environment. .\Remove-AllGroupMembers.ps1 -group "CN=$Group" -ou 'OU=' -domain 'DC=' + # TODO: Replace -Identity and -TargetPath with the correct DN values for your environment. Move-ADObject -Server $Domain -Identity 'CN=ps,DC=' -TargetPath '' } #Copy and rename the CSV file with a timestamp to keep as a record of run history. -$timeStamp = Get-Date -Format 'yyyy-MM-dd hh-m-ss' -Copy-Item -Path C:\Scripts\ObsoleteGroups\ObsoleteGroups.csv -Destination "C:\Scripts\ObsoleteGroups\Run History\ObsoleteGroups $timeStamp.csv" +$timeStamp = Get-Date -Format 'yyyy-MM-dd HH-mm-ss' +Copy-Item -Path $GroupListPath -Destination "C:\Scripts\ObsoleteGroups\Run History\ObsoleteGroups $timeStamp.csv" diff --git a/Active Directory/Domain Services/Get-FSMORoleDetails.ps1 b/Active Directory/Domain Services/Get-FSMORoleDetails.ps1 index aab2564..b013486 100644 --- a/Active Directory/Domain Services/Get-FSMORoleDetails.ps1 +++ b/Active Directory/Domain Services/Get-FSMORoleDetails.ps1 @@ -1,8 +1,16 @@ #WIP # Get the hostname and AD site location of domain controllers that hold the AD FSMO roles. -$FSMORoles = Get-ADForest | Select-Object -ExpandProperty SchemaMaster, DomainNamingMaster -$FSMORoles += Get-ADDomain | Select-Object -ExpandProperty PDCEmulator, RIDMaster, InfrastructureMaster +# Note: -ExpandProperty only accepts one property at a time; collect each role separately. +$Forest = Get-ADForest +$Domain = Get-ADDomain +$FSMORoles = @( + $Forest.SchemaMaster + $Forest.DomainNamingMaster + $Domain.PDCEmulator + $Domain.RIDMaster + $Domain.InfrastructureMaster +) # Get the details of each FSMO role holder foreach ($role in $FSMORoles) { diff --git a/Active Directory/Export-AllADUserTransitiveGroupMemberships.ps1 b/Active Directory/Export-AllADUserTransitiveGroupMemberships.ps1 index f7ff926..c44d68c 100644 --- a/Active Directory/Export-AllADUserTransitiveGroupMemberships.ps1 +++ b/Active Directory/Export-AllADUserTransitiveGroupMemberships.ps1 @@ -148,7 +148,7 @@ begin { } $CurrentProgressPreference = Get-Variable -Name ProgressPreference -ValueOnly - Set-Variable -Name ProgressPreference 'SilentlyContinue' -Scope Global -Force -ErrorAction SilentlyContinue + Set-Variable -Name ProgressPreference -Value 'SilentlyContinue' -Scope Global -Force -ErrorAction SilentlyContinue # Check if the global catalog server is available on the specified port. if (-not (Test-NetConnection -ComputerName $Server -Port $Port -InformationLevel Quiet -ErrorAction SilentlyContinue)) { if (-not (Test-NetConnection -ComputerName $Server -Port $AltPort -InformationLevel Quiet -ErrorAction SilentlyContinue)) { diff --git a/Active Directory/Export-AllGroupMemberships.ps1 b/Active Directory/Export-AllGroupMemberships.ps1 index 7c92c95..a764137 100644 --- a/Active Directory/Export-AllGroupMemberships.ps1 +++ b/Active Directory/Export-AllGroupMemberships.ps1 @@ -30,7 +30,7 @@ function Export-AllUserGroupMemberships { process { # Get all users in the domain and their group memberships. Write-Verbose -Message 'Getting all enabled users in the domain.' - $Users = Get-ADUser -Filter 'Enabled -eq $true' -Properties EmployeeId, memberOf | + $Users = Get-ADUser -Filter { Enabled -eq $true } -Properties EmployeeId, memberOf | Select-Object Name, DisplayName, samAccountName, userPrincipalName, EmployeeId, memberOf Write-Verbose -Message " - Found $($Users.Count) users in the domain." diff --git a/Active Directory/Get-ADAttributeUniqueValues.ps1 b/Active Directory/Get-ADAttributeUniqueValues.ps1 index 09fb797..733d9df 100644 --- a/Active Directory/Get-ADAttributeUniqueValues.ps1 +++ b/Active Directory/Get-ADAttributeUniqueValues.ps1 @@ -47,7 +47,7 @@ function Get-ADAttributeUniqueValues { [ValidateNotNullOrEmpty()] [ValidateSet('company', 'country', 'department', 'homeDrive', 'l', 'physicalDeliveryOfficeName', 'postalCode', 'state', 'streetAddress', 'title')] [string[]] - $AttributesToCheck = @('Company', 'Department', 'Office', 'Title'), + $AttributesToCheck = @('company', 'department', 'physicalDeliveryOfficeName', 'title'), # The directory to save the exported JSON file in. (Optional, defaults to C:\Temp.) [Parameter()] diff --git a/Active Directory/Get-ADUserTransitiveGroupMembership.ps1 b/Active Directory/Get-ADUserTransitiveGroupMembership.ps1 index 5dfcb56..c0ae3c9 100644 --- a/Active Directory/Get-ADUserTransitiveGroupMembership.ps1 +++ b/Active Directory/Get-ADUserTransitiveGroupMembership.ps1 @@ -57,7 +57,7 @@ function Get-ADUserTransitiveGroupMembership { } $CurrentProgressPreference = Get-Variable -Name ProgressPreference -ValueOnly - Set-Variable -Name ProgressPreference 'SilentlyContinue' -Force -Scope Global -ErrorAction SilentlyContinue + Set-Variable -Name ProgressPreference -Value 'SilentlyContinue' -Force -Scope Global -ErrorAction SilentlyContinue # Check if the global catalog server is available on the specified port. if (-not (Test-NetConnection -ComputerName $Server -Port $Port -InformationLevel Quiet -ErrorAction SilentlyContinue)) { if (-not (Test-NetConnection -ComputerName $Server -Port $AltPort -InformationLevel Quiet -ErrorAction SilentlyContinue)) { diff --git a/DDI/Update-DnsServerList.ps1 b/DDI/Update-DnsServerList.ps1 index cc61f79..ea2f436 100644 --- a/DDI/Update-DnsServerList.ps1 +++ b/DDI/Update-DnsServerList.ps1 @@ -20,7 +20,7 @@ function Update-DnsServerList { foreach ($netadapter in $NetworkAdapters) { [ipaddress[]]$ClientDnsServerSearchOrder = $netadapter.DnsServerSearchOrder if (Compare-Object -ReferenceObject $ClientDnsServerSearchOrder -DifferenceObject $OldDnsServers -IncludeEqual -ExcludeDifferent) { - $NetAdapterConfig = Get-CimInstance -Class Win32_NetworkAdapterConfiguration -Filter "Index = $._Index" + $NetAdapterConfig = Get-CimInstance -Class Win32_NetworkAdapterConfiguration -Filter "Index = $($netadapter.Index)" $NetAdapterConfig.SetDnsServerSearchOrder($($NewDnsServers.IPAddressToString -join ',')) IpConfig /FlushDns diff --git a/General/Purge-InstalledModules.ps1 b/General/Purge-InstalledModules.ps1 index 6ab4b1a..ef2eace 100644 --- a/General/Purge-InstalledModules.ps1 +++ b/General/Purge-InstalledModules.ps1 @@ -18,7 +18,7 @@ $Modules = @( 'Az.MySql', 'Az.Network', 'Az.Nginx', - 'Az.RedisCache' + 'Az.RedisCache', 'Az.Sql', 'Az.SqlVirtualMachine', 'Az.StackHCI', diff --git a/Windows/Push-DNSClientServerAddresses.ps1 b/Windows/Push-DNSClientServerAddresses.ps1 index 20e691a..5cb069f 100644 --- a/Windows/Push-DNSClientServerAddresses.ps1 +++ b/Windows/Push-DNSClientServerAddresses.ps1 @@ -5,10 +5,10 @@ foreach ($server in $servers) # Connect to the server. $serverName = $server.Name Write-Output "Connecting to $serverName" + $s = $null try { - # Create and connect to the PSSession. - $s = New-PSSession -ComputerName $serverName - Enter-PSSession $s -ErrorAction SilentlyContinue + # Create the PSSession. Enter-PSSession is interactive-only and cannot redirect script commands remotely. + $s = New-PSSession -ComputerName $serverName -ErrorAction Stop } catch { # Log the failure and continue the for loop on the next item. @@ -16,13 +16,17 @@ foreach ($server in $servers) Continue } - # Connected to session. Now updated the DNS client server address on any interfaces that currently use a domain controller IP. + # Connected to session. Now update the DNS client server address on any interfaces that currently use a domain controller IP. try { - Get-NetIPInterface | Get-DnsClientServerAddress | Where-Object {$_.ServerAddresses -like '10.10.10.*'} | ` - Set-DnsClientServerAddress -ServerAddresses ("","","") -Verbose + Invoke-Command -Session $s -ScriptBlock { + Get-NetIPInterface | Get-DnsClientServerAddress | Where-Object { $_.ServerAddresses -like '10.10.10.*' } | + Set-DnsClientServerAddress -ServerAddresses ('', '', '') -Verbose + } } catch { - Write-Output "Failed to change the DNS client server address on $servername" + Write-Output "Failed to change the DNS client server address on $serverName" + } + finally { + if ($s) { Remove-PSSession -Session $s } } - Exit-PSSession } # End foreach server loop. From 28005d5b81e8f43e5331d103efd75e25bd6fe0b1 Mon Sep 17 00:00:00 2001 From: Sam Erde <20478745+SamErde@users.noreply.github.com> Date: Wed, 29 Apr 2026 06:07:31 -0400 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=90=9B=20fix(function):=20resolve=20h?= =?UTF-8?q?igh-severity=20bugs=20in=20AD,=20DNS,=20Exchange,=20and=20gener?= =?UTF-8?q?al=20scripts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit F07: Move Sort-Object emit from end to process block in Get-ADUserTransitiveGroupMembership to prevent pipeline clobbering (only last input was returned to end block) F09: Fix undefined \\\ -> \\.Count\ in Export-AllADUserTransitiveGroupMemberships F16: Fix operator precedence bug in Get-InactiveADUser (add parens to -not comparison) F17: Use \\\ parameter value as export path when provided in Get-InactiveADUser F19: Replace \continue\ with \ eturn\ in catch outside loop in Get-LockedOutLocation F22: Move pipeline identity resolution from begin to process block in Get-ADObjectFromPipeline F24: Make \\\ mandatory in Update-ModuleVersion to prevent null member access F27: Replace empty placeholder strings with mandatory params in Push-DNSClientServerAddresses F29: Remove hardcoded DOMAINNAME.org placeholder from Exchange ConnectionUri Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../AD Users/Get-InactiveADUser.ps1 | 4 ++-- .../AD Users/Get-LockedOutLocation.ps1 | 2 +- ...rt-AllADUserTransitiveGroupMemberships.ps1 | 2 +- Active Directory/Get-ADObjectFromPipeline.ps1 | 6 +++--- .../Get-ADUserTransitiveGroupMembership.ps1 | 3 ++- Exchange/Parse-TransportLogs.ps1 | 2 +- General/Update-ModuleVersion.ps1 | 1 + Windows/Push-DNSClientServerAddresses.ps1 | 19 +++++++++++++++++-- 8 files changed, 28 insertions(+), 11 deletions(-) diff --git a/Active Directory/AD Users/Get-InactiveADUser.ps1 b/Active Directory/AD Users/Get-InactiveADUser.ps1 index f4e47be..5452be2 100644 --- a/Active Directory/AD Users/Get-InactiveADUser.ps1 +++ b/Active Directory/AD Users/Get-InactiveADUser.ps1 @@ -96,7 +96,7 @@ if ($CheckAllDCs) { # Skip the check across all DCs if there is already a LastLogonDate within the past 14 days and if the most recent logon is more recent than the inactive date threshold. - if ( $MostRecentLogon -lt (Get-Date).AddDays(-14) -and (-not $MostRecentLogon -lt $InactiveDate) ) { + if ( $MostRecentLogon -lt (Get-Date).AddDays(-14) -and (-not ($MostRecentLogon -lt $InactiveDate)) ) { # Check LastLogon (non-replicated) on every domain controller. foreach ($DC in $DomainControllers) { try { @@ -143,7 +143,7 @@ # Optional: Export to CSV if ($PSBoundParameters.ContainsKey('ExportCSV')) { - $ExportPath = ".\InactiveUsers_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv" + $ExportPath = if ($ExportCSV) { $ExportCSV } else { ".\InactiveUsers_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv" } $Results | Export-Csv -Path $ExportPath -NoTypeInformation Write-Host "Results exported to: $ExportPath" -ForegroundColor Green } diff --git a/Active Directory/AD Users/Get-LockedOutLocation.ps1 b/Active Directory/AD Users/Get-LockedOutLocation.ps1 index dfa44da..1ace6fb 100644 --- a/Active Directory/AD Users/Get-LockedOutLocation.ps1 +++ b/Active Directory/AD Users/Get-LockedOutLocation.ps1 @@ -74,7 +74,7 @@ $LockedOutEvents = Get-WinEvent -ComputerName $PDCEmulator.HostName -FilterHashtable @{LogName = 'Security'; Id = 4740 } -ErrorAction Stop | Sort-Object -Property TimeCreated -Descending } catch { Write-Warning $_ - continue + return }#end catch foreach ($item in $LockedOutEvents) { diff --git a/Active Directory/Export-AllADUserTransitiveGroupMemberships.ps1 b/Active Directory/Export-AllADUserTransitiveGroupMemberships.ps1 index c44d68c..8e3c4da 100644 --- a/Active Directory/Export-AllADUserTransitiveGroupMemberships.ps1 +++ b/Active Directory/Export-AllADUserTransitiveGroupMemberships.ps1 @@ -52,7 +52,7 @@ begin { Get-ADUserTransitiveGroupMembership -UserDN $_.DistinguishedName } } - Write-Verbose -Message " - Found $($UserCount) users in the domain." + Write-Verbose -Message " - Found $($Users.Count) users in the domain." # Export the data to a JSON file. $JsonData = $Users | ConvertTo-Json diff --git a/Active Directory/Get-ADObjectFromPipeline.ps1 b/Active Directory/Get-ADObjectFromPipeline.ps1 index 59e33eb..b727709 100644 --- a/Active Directory/Get-ADObjectFromPipeline.ps1 +++ b/Active Directory/Get-ADObjectFromPipeline.ps1 @@ -16,7 +16,10 @@ function Get-ADObjectFromPipeline { begin { Import-Module ActiveDirectory $GlobalCatalog = Get-ADDomainController -Discover -Service GlobalCatalog + } + process { + # Resolve identity type in process block where pipeline input ($Identity) is available. if ($Identity -is [Microsoft.ActiveDirectory.Management.ADUser]) { # We have an ADUser object # Might want to normalize the type to an ADObject IF we can get sidHistory from an ADObject @@ -30,9 +33,6 @@ function Get-ADObjectFromPipeline { $Identity = Get-ADObject -Filter "Name -eq `"$Identity`"" } $IdentityType = $Identity.ObjectClass - } - - process { switch ($IdentityType) { 'user' { # Not Complete diff --git a/Active Directory/Get-ADUserTransitiveGroupMembership.ps1 b/Active Directory/Get-ADUserTransitiveGroupMembership.ps1 index c0ae3c9..15f594f 100644 --- a/Active Directory/Get-ADUserTransitiveGroupMembership.ps1 +++ b/Active Directory/Get-ADUserTransitiveGroupMembership.ps1 @@ -80,10 +80,11 @@ function Get-ADUserTransitiveGroupMembership { $TransitiveMemberOfGroupDNs = foreach ($result in ($results.properties)) { $result['distinguishedname'] } + # Emit deduplicated results per user in process block so pipeline results are not overwritten. + $TransitiveMemberOfGroupDNs | Sort-Object -Unique } end { - $TransitiveMemberOfGroupDNs | Sort-Object -Unique Remove-Variable Filter, TransitiveMemberOfGroupDNs, Results, Searcher, Server, Port, UserDN -ErrorAction SilentlyContinue } } # end function Get-ADUserTransitiveGroupMembership diff --git a/Exchange/Parse-TransportLogs.ps1 b/Exchange/Parse-TransportLogs.ps1 index 715f242..5780d02 100644 --- a/Exchange/Parse-TransportLogs.ps1 +++ b/Exchange/Parse-TransportLogs.ps1 @@ -16,7 +16,7 @@ Set-ExecutionPolicy RemoteSigned $ExchangeCredential = Get-Credential -Message "Please enter credentials to connect to your Exchange Server. `nThis will be used to pull message subject lines from the tracking logs." $ExchangeServer = Read-Host 'Please specify an Exchange Server name.' -$ExchangeSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://$ExchangeServer.DOMAINNAME.org/PowerShell/ -Authentication Kerberos -Credential $ExchangeCredential +$ExchangeSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "http://$ExchangeServer/PowerShell/" -Authentication Kerberos -Credential $ExchangeCredential Import-PSSession $ExchangeSession -DisableNameChecking $SMTPLogPath = Read-Host "`nWhat is the path of the folder containing your SMTP transport logs?" diff --git a/General/Update-ModuleVersion.ps1 b/General/Update-ModuleVersion.ps1 index 7aead54..4818ef6 100644 --- a/General/Update-ModuleVersion.ps1 +++ b/General/Update-ModuleVersion.ps1 @@ -4,6 +4,7 @@ param ( # Specify the version to update from (or read from a module manifest). + [Parameter(Mandatory)] [version] $InputVersion, # Basic version switches. diff --git a/Windows/Push-DNSClientServerAddresses.ps1 b/Windows/Push-DNSClientServerAddresses.ps1 index 5cb069f..f109396 100644 --- a/Windows/Push-DNSClientServerAddresses.ps1 +++ b/Windows/Push-DNSClientServerAddresses.ps1 @@ -1,5 +1,20 @@ +[CmdletBinding()] +param ( + # The OU or container in Active Directory to search for servers. + [Parameter(Mandatory)] + [string] $SearchBase, + + # The Active Directory domain controller to query. + [Parameter(Mandatory)] + [string] $ADServer, + + # The DNS server addresses to assign to network adapters on the target servers. + [Parameter(Mandatory)] + [string[]] $DNSServerAddresses +) + Import-Module ActiveDirectory -$servers = Get-ADComputer -SearchBase "" -Server "" -SearchScope Subtree -Filter * +$servers = Get-ADComputer -SearchBase $SearchBase -Server $ADServer -SearchScope Subtree -Filter * foreach ($server in $servers) { # Connect to the server. @@ -20,7 +35,7 @@ foreach ($server in $servers) try { Invoke-Command -Session $s -ScriptBlock { Get-NetIPInterface | Get-DnsClientServerAddress | Where-Object { $_.ServerAddresses -like '10.10.10.*' } | - Set-DnsClientServerAddress -ServerAddresses ('', '', '') -Verbose + Set-DnsClientServerAddress -ServerAddresses $using:DNSServerAddresses -Verbose } } catch { From 23f3ffe1215b841fb4bf11d21cb483a9426e6228 Mon Sep 17 00:00:00 2001 From: Sam Erde <20478745+SamErde@users.noreply.github.com> Date: Wed, 29 Apr 2026 06:22:59 -0400 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=90=9B=20fix(function):=20resolve=20m?= =?UTF-8?q?edium-severity=20bugs=20across=20scripts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit F02: Fix RegistryAccessRule from FullControl to ReadKey in Activate and Get License (comment/output said 'read permissions' but code was granting FullControl) F05: Fix Write-Error \\Cannot validate argument on parameter 'MaximumHistoryCount'. The 0 argument is less than the minimum allowed range of 1. Supply an argument that is greater than or equal to 1 and then try the command again.[0]\ -> \\\ in Get Hostnames from CSV IP Addresses (\\Cannot validate argument on parameter 'MaximumHistoryCount'. The 0 argument is less than the minimum allowed range of 1. Supply an argument that is greater than or equal to 1 and then try the command again.[0]\ may not reflect the current exception in a catch block) F15: Confirmed already fixed in critical pass (Get-Date format HH-mm-ss correct) F18: Replace \reak 1\ with \ eturn\ in two catch blocks in Get-InactiveUsers (break outside a loop throws LoopFlowException in PowerShell) F20: Add BadPasswordTime to Get-ADUser -Properties list in Get-LockedOutLocation (property was used in output object but never requested from AD) F25: Fix undefined \\\ -> \\\\ in Update-ModuleVersion F28: Remove Set-ExecutionPolicy RemoteSigned from Parse-TransportLogs (modifies machine security policy as a script side effect) F30: Add bounds check before split('<')[1] in Parse-TransportLogs (throws index-out-of-range if no '<' delimiter present in log data) F31: Replace \\Continue\ global mutation with try/catch for DNS lookup in Parse-TransportLogs (scoped error handling instead of global state change) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Active Directory/AD Users/Get-InactiveUsers.ps1 | 4 ++-- .../AD Users/Get-LockedOutLocation.ps1 | 2 +- DDI/Get Hostnames from CSV IP Addresses.ps1 | 2 +- Exchange/Parse-TransportLogs.ps1 | 16 +++++++++++----- General/Update-ModuleVersion.ps1 | 2 +- Windows/Activate and Get License.ps1 | 4 ++-- 6 files changed, 18 insertions(+), 12 deletions(-) diff --git a/Active Directory/AD Users/Get-InactiveUsers.ps1 b/Active Directory/AD Users/Get-InactiveUsers.ps1 index a4b88cb..334592c 100644 --- a/Active Directory/AD Users/Get-InactiveUsers.ps1 +++ b/Active Directory/AD Users/Get-InactiveUsers.ps1 @@ -73,7 +73,7 @@ Write-Verbose 'Active Directory module imported successfully' } catch { Write-Error "Failed to import Active Directory module: $($_.Exception.Message)" - break 1 + return } # Calculate the cutoff date and its file time representation for the AD filter. @@ -99,7 +99,7 @@ Write-Host "Found $($InactiveUsersResult.Count) inactive user account(s)." -ForegroundColor Green } catch { Write-Error "Failed to get user accounts: $($_.Exception.Message)" - break 1 + return } $InactiveUsers = @() diff --git a/Active Directory/AD Users/Get-LockedOutLocation.ps1 b/Active Directory/AD Users/Get-LockedOutLocation.ps1 index 1ace6fb..ccf30ea 100644 --- a/Active Directory/AD Users/Get-LockedOutLocation.ps1 +++ b/Active Directory/AD Users/Get-LockedOutLocation.ps1 @@ -48,7 +48,7 @@ $DCCounter++ Write-Progress -Activity 'Contacting DCs for lockout info' -Status "Querying $($DC.Hostname)" -PercentComplete (($DCCounter / $DomainControllers.Count) * 100) try { - $UserInfo = Get-ADUser -Identity $Identity -Server $DC.Hostname -Properties AccountLockoutTime, LastBadPasswordAttempt, BadPwdCount, LockedOut -ErrorAction Stop + $UserInfo = Get-ADUser -Identity $Identity -Server $DC.Hostname -Properties AccountLockoutTime, BadPasswordTime, LastBadPasswordAttempt, BadPwdCount, LockedOut -ErrorAction Stop } catch { Write-Warning $_ continue diff --git a/DDI/Get Hostnames from CSV IP Addresses.ps1 b/DDI/Get Hostnames from CSV IP Addresses.ps1 index 3d96d38..4f23c9a 100644 --- a/DDI/Get Hostnames from CSV IP Addresses.ps1 +++ b/DDI/Get Hostnames from CSV IP Addresses.ps1 @@ -10,7 +10,7 @@ $IPAddressList | foreach-object { $_.Hostname = ([System.Net.Dns]::GetHostEntry($ip)).HostName } catch { - Write-Error $error[0] #.Exception.Message.Split(':')[1] + Write-Error $_ #.Exception.Message.Split(':')[1] } } # Write the data back to the CSV with the hostnames added. diff --git a/Exchange/Parse-TransportLogs.ps1 b/Exchange/Parse-TransportLogs.ps1 index 5780d02..5c5b3a8 100644 --- a/Exchange/Parse-TransportLogs.ps1 +++ b/Exchange/Parse-TransportLogs.ps1 @@ -13,7 +13,6 @@ http://blog.chrislehr.com/2015/07/parse-transportlogs-which-ips-on-my.html #> -Set-ExecutionPolicy RemoteSigned $ExchangeCredential = Get-Credential -Message "Please enter credentials to connect to your Exchange Server. `nThis will be used to pull message subject lines from the tracking logs." $ExchangeServer = Read-Host 'Please specify an Exchange Server name.' $ExchangeSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "http://$ExchangeServer/PowerShell/" -Authentication Kerberos -Credential $ExchangeCredential @@ -47,13 +46,20 @@ foreach ($item in $TestSet) { $testProgress++; $testPercent = [math]::Round(($testProgress / $testCount), 2) * 100 Write-Progress -Activity "Parsing message $testProgress of $testCount." -Status "$testPercent% complete" -PercentComplete $testPercent - $data = ($item.data.split('<')[1]).Split('>')[0] + $splitData = $item.data.Split('<') + if ($splitData.Count -lt 2) { + Write-Warning "Skipping item with no message-id delimiter: $($item.data)" + continue + } + $data = $splitData[1].Split('>')[0] $subject = (Get-MessageTrackingLog -MessageId $data).MessageSubject | Select-Object -Unique $item.MessageID = $data $item.Subject = $subject - $ErrorActionPreference = 'SilentlyContinue' #To avoid ambiguous error output if/when a hostname is not found. - $item.Hostname = ([System.Net.DNS]::GetHostbyAddress($item.IPAddress)).Hostname - $ErrorActionPreference = 'Continue' + try { + $item.Hostname = ([System.Net.DNS]::GetHostbyAddress($item.IPAddress)).Hostname + } catch { + # Hostname not found for this IP; leave blank. + } } Remove-PSSession $ExchangeSession.Id diff --git a/General/Update-ModuleVersion.ps1 b/General/Update-ModuleVersion.ps1 index 4818ef6..4d80996 100644 --- a/General/Update-ModuleVersion.ps1 +++ b/General/Update-ModuleVersion.ps1 @@ -87,7 +87,7 @@ } if ("$NewVersion$PrereleaseTag" -notmatch $PatternValidation) { - Write-Error -Message "The prerelease version '$PrereleaseVersion' is not a valid semantic version." -ErrorAction Continue + Write-Error -Message "The prerelease version '$NewVersion$PrereleaseTag' is not a valid semantic version." -ErrorAction Continue } else { $matches | Write-Debug -Debug foreach ($match in $matches.GetEnumerator()) { diff --git a/Windows/Activate and Get License.ps1 b/Windows/Activate and Get License.ps1 index f19c6a8..9187ff1 100644 --- a/Windows/Activate and Get License.ps1 +++ b/Windows/Activate and Get License.ps1 @@ -29,10 +29,10 @@ if (-not (Test-Path -Path $registryPath)) { # Add read permissions for SID (S-1-1-0, Everyone) to the registry key with inheritance $acl = Get-Acl -Path $registryPath -$ruleSID = New-Object System.Security.AccessControl.RegistryAccessRule($sid, 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow') +$ruleSID = New-Object System.Security.AccessControl.RegistryAccessRule($sid, 'ReadKey', 'ContainerInherit,ObjectInherit', 'None', 'Allow') $acl.AddAccessRule($ruleSID) Set-Acl -Path $registryPath -AclObject $acl -Write-Output "Added 'Interactive' group and SID ($sid) with read permissions (with inheritance) to the registry key." +Write-Output "Added 'Interactive' group and SID ($sid) with read (ReadKey) permissions (with inheritance) to the registry key." #Remove the # below to make sure it will kick off the scheduled task on already enrolled devices Start-Process "$env:SystemRoot\system32\ClipRenew.exe" From e69154f9625e2f017a291d37a775425dadcbf9e2 Mon Sep 17 00:00:00 2001 From: Sam Erde <20478745+SamErde@users.noreply.github.com> Date: Wed, 29 Apr 2026 06:30:52 -0400 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=A7=B9=20chore(function):=20remove=20?= =?UTF-8?q?dead=20code=20re-fetch=20of=20ProductKey=20at=20EOF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit F03: The final line re-fetched \ via Get-CimInstance but the value was never used. The variable is already assigned earlier in the script where it is consumed by Invoke-Expression slmgr.vbs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Windows/Activate and Get License.ps1 | 2 -- 1 file changed, 2 deletions(-) diff --git a/Windows/Activate and Get License.ps1 b/Windows/Activate and Get License.ps1 index 9187ff1..90a9c17 100644 --- a/Windows/Activate and Get License.ps1 +++ b/Windows/Activate and Get License.ps1 @@ -36,5 +36,3 @@ Write-Output "Added 'Interactive' group and SID ($sid) with read (ReadKey) permi #Remove the # below to make sure it will kick off the scheduled task on already enrolled devices Start-Process "$env:SystemRoot\system32\ClipRenew.exe" - -$ProductKey = (Get-CimInstance -ClassName SoftwareLicensingService).OA3xOriginalProductKey