Public/Set-SemiPrivilegedKeyPairCheck.ps1
Function Set-SemiPrivilegedKeyPairCheck { <# .SYNOPSIS Manage Semi-Privileged Users by disabling or deleting based on conditions. .DESCRIPTION This function processes a list of semi-privileged users in Active Directory, checks exclusion lists, and either disables or deletes users based on the associated non-privileged user's status. The Semi-Privileged user account is associated to a Non-Privileged account by storing the Non-Privileged account SID into the employeeNumber attribute of the Semi-Privileged user. .PARAMETER AdminUsersDN The distinguished name (DN) of the OU that contains the semi-privileged users. .PARAMETER ExcludeList A list of users to exclude from processing. Defaults to an internal list if not provided. .OUTPUTS List of users that were disabled or deleted. [PSCustomObject] with the following properties: - DisabledUsers: [System.Collections.Generic.List[String]] List of disabled user SamAccountNames - DeletedUsers: [System.Collections.Generic.List[String]] List of deleted user SamAccountNames .EXAMPLE Set-SemiPrivilegedKeyPairCheck -AdminUsersDN "OU=Users,OU=Admin,DC=EguibarIT,DC=local" .EXAMPLE Set-SemiPrivilegedKeyPairCheck -AdminUsersDN "OU=Users,OU=Admin,DC=EguibarIT,DC=local" -ExcludeList "davader", "hasolo" .EXAMPLE "OU=Users,OU=Admin,DC=EguibarIT,DC=local" | Set-SemiPrivilegedKeyPairCheck #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] [OutputType([PSCustomObject])] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ValueFromRemainingArguments = $true, HelpMessage = 'Distinguished Name of the container where the Admin Accounts are located.', Position = 0)] [ValidateScript({ Test-IsValidDN -ObjectDN $_ }, ErrorMessage = 'DistinguishedName provided is not valid! Please Check.')] [Alias('DN', 'DistinguishedName', 'LDAPPath')] [string] $AdminUsersDN, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ValueFromRemainingArguments = $true, HelpMessage = 'User list to be excluded from this process.', Position = 1)] [System.Collections.Generic.List[String]] $ExcludeList ) Begin { $txt = ($Variables.HeaderHousekeeping -f (Get-Date).ToShortDateString(), $MyInvocation.Mycommand, (Get-FunctionDisplay -HashTable $PsBoundParameters -Verbose:$False) ) Write-Verbose -Message $txt Import-MyModule ActiveDirectory ############################## # Variables Definition # parameters variable for splatting CMDlets [hashtable]$Splat = [hashtable]::New([StringComparer]::OrdinalIgnoreCase) # Initialize lists to track actions $DisableList = [System.Collections.Generic.List[String]]::New() $DeleteList = [System.Collections.Generic.List[String]]::New() # Progress bar initialization $ProgressId = 0 $Activity = 'Processing Semi-Privileged Users' [int]$i = 0 # If parameter is parsed, initialize variable to be used by default objects If (-Not $PSBoundParameters.ContainsKey('ExcludeList')) { $ExcludeList = [System.Collections.Generic.List[String]]::New() } #end If $wellKnownUserSids = @{ 'S-1-5-21-*-500' = 'Administrator' 'S-1-5-21-*-501' = 'Guest' 'S-1-5-21-*-502' = 'krbtgt' } foreach ($sid in $wellKnownUserSids.Keys) { # For these SIDs, we always need to use the wildcard approach $users = Get-ADUser -Filter * | Where-Object -FilterScript { $_.SID -like $sid } foreach ($item in $users) { If ($item.SamAccountName -notin $ExcludeList) { $ExcludeList.Add($item.SamAccountName) | Out-Null } } # end foreach } #end Foreach } #end Begin Process { # Set up Active Directory context try { $Splat = @{ Filter = '*' SearchBase = $AdminUsersDN Property = 'SamAccountName', 'employeeNumber', 'Enabled' } $AllPrivUsers = Get-ADUser @Splat } catch { Write-Error -Message ('Error retrieving users from the provided DN: {0}' -f $_) return } if ($AllPrivUsers.Count -eq 0) { Write-Verbose -Message 'No Privileged/Semi-Privileged users found in the given Distinguished Name container.' return } else { Write-Verbose -Message ('INFO - Found {0} Privileged/Semi-Privileged users.' -f $AllPrivUsers.Count) } # Iterate through each semi-privileged user foreach ($semiPrivilegedUser in $AllPrivUsers) { $i++ $PercentComplete = [math]::Round(($i / $AllPrivUsers.Count) * 100) # Update progress bar $Splat = @{ Id = $ProgressId Activity = $Activity Status = ('Processing {0} (% Complete: {1}%)' -f $semiPrivilegedUser.SamAccountName, $PercentComplete) PercentComplete = $PercentComplete } Write-Progress @Splat # Skip users in the exclusion list if ($semiPrivilegedUser.SamAccountName -in $ExcludeList) { Write-Verbose -Message ('Skipping excluded user: {0}' -f $semiPrivilegedUser.SamAccountName) continue } # Flags for account actions [bool]$DisableSemiPrivilegedUser = $false [bool]$DeleteSemiPrivilegedUser = $false # Check if employeeNumber is null (this implies deletion) if (-not $semiPrivilegedUser.employeeNumber) { Write-Warning -Message (' User {0} has no linked non-privileged user (employeeNumber is null). Marking for deletion.' -f $semiPrivilegedUser.SamAccountName ) $DeleteSemiPrivilegedUser = $true } else { try { # Retrieve the non-privileged user by SID $Splat = @{ Identity = $semiPrivilegedUser.employeeNumber ErrorAction = 'SilentlyContinue' } $nonPrivilegedUser = Get-ADUser @Splat # Check if non-privileged user is disabled if (-not $nonPrivilegedUser.Enabled) { Write-Warning -Message (' Non-Privileged user {0} is disabled. Marking semi-privileged user for disable.' -f $nonPrivilegedUser.SamAccountName ) $DisableSemiPrivilegedUser = $true } #end If Enabled } catch { Write-Warning -Message (' Non-Privileged user not found for {0}. Marking for deletion.' -f $semiPrivilegedUser.SamAccountName ) $DeleteSemiPrivilegedUser = $true } #end Try-Catch Write-Verbose -Message ('Non-privileged user found: {0}' -f $nonPrivilegedUser.SamAccountName) } #end If-Else # Disable Semi-Privileged Account if ($DisableSemiPrivilegedUser -and $PSCmdlet.ShouldProcess($semiPrivilegedUser.SamAccountName, 'Disable Semi-Privileged Account')) { try { Set-ADUser -Identity $semiPrivilegedUser.SamAccountName -Enabled $false $DisableList.Add($semiPrivilegedUser.SamAccountName) } catch { Write-Error ('Failed to disable user {0}: {1}' -f $semiPrivilegedUser.SamAccountName, $_) } #end Try-Catch } #end If # Delete Semi-Privileged Account if ($DeleteSemiPrivilegedUser -and $PSCmdlet.ShouldProcess($semiPrivilegedUser.SamAccountName, 'Delete Semi-Privileged Account')) { try { Remove-ADUser -Identity $semiPrivilegedUser.SamAccountName $DeleteList.Add($semiPrivilegedUser.SamAccountName) } catch { Write-Error -Message ('Failed to delete user {0}: {1}' -f $semiPrivilegedUser.SamAccountName, $_) } #end Try-Catch } #end If } #end Foreach } #end Process End { Write-Progress -Id $ProgressId -Activity $Activity -Completed # Output summary of actions Write-Verbose -Message (' Summary of actions: Total users disabled...: {0} Total users deleted....: {1}' -f $DisableList.Count, $DeleteList.Count ) # List disabled users if ($DisableList.Count -gt 0) { Write-Verbose 'Disabled users:' $DisableList | ForEach-Object { $_ } } # List deleted users if ($DeleteList.Count -gt 0) { Write-Verbose 'Deleted users:' $DeleteList | ForEach-Object { $_ } } $txt = ($Variables.FooterHousekeeping -f $MyInvocation.InvocationName, 'checking semi-privileged and/or Privileged user Key-Pair.' ) Write-Verbose -Message $txt # Return a custom object with the results [PSCustomObject]@{ DisabledUsers = $DisableList DeletedUsers = $DeleteList } } #end End } #end Function Set-SemiPrivilegedKeyPairCheck |