Public/Set-AllUserAdminCount.ps1

function Set-AllUserAdminCount {
    <#
        .SYNOPSIS
            Clears the 'adminCount' attribute and enables inherited security on all user objects with 'adminCount = 1' in a specified scope.

        .DESCRIPTION
            This function finds all user objects with 'adminCount = 1' in a specified Organizational Unit (OU) or in the entire domain
            and clears the 'adminCount' attribute while also enabling inherited security on those user objects.

        .PARAMETER SubTree
            Processes user objects with 'adminCount = 1' in a specified OU and its child OUs. If not specified, the entire domain is processed.

        .PARAMETER SearchRootDN
            The Distinguished Name of the OU where the search should begin. This parameter is required if using the SubTree parameter.

        .EXAMPLE
            Set-AllUserAdminCount

            Description
            -----------
            Clears the 'adminCount' attribute and resets inheritance on all user objects with 'adminCount = 1' in the domain.

        .EXAMPLE
            Set-AllUserAdminCount -SubTree -SearchRootDN "OU=Admin Accounts,OU=Admin,DC=EguibarIT,DC=local"

            Description
            -----------
            Clears the 'adminCount' attribute and resets inheritance on all user objects with 'adminCount = 1' in the specified OU.

        .OUTPUTS
            Integer
            Outputs the number of user objects modified.

        .NOTES
            Version: 1.1
            Author: Vicente Rodriguez Eguibar
            Date: 08/Feb/2024
            Contact: vicente@eguibar.com
            Company: Eguibar Information Technology S.L.
            http://www.eguibarit.com
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    [OutputType([int])]

    Param (
        [Parameter(
            Position = 0,
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ParameterSetName = 'SubTree',
            HelpMessage = 'Process user objects within a specific OU and its child OUs.'
        )]
        [switch]$SubTree,

        [Parameter(
            Position = 1,
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            ParameterSetName = 'SubTree',
            HelpMessage = 'The Distinguished Name of the OU where the search starts.'
        )]
        [string]
        $SearchRootDN,

        [Parameter(
            Position = 2,
            Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'List of user SamAccountNames to exclude from processing (Administrator and krbtgt are already included).'
        )]
        [System.Collections.Generic.List[string]]
        $ExcludedUsers
    )

    Begin {
        $txt = ($Variables.HeaderHousekeeping -f
            (Get-Date).ToShortDateString(),
            $MyInvocation.Mycommand,
            (Get-FunctionDisplay -Hashtable $PsBoundParameters -Verbose:$False)
        )
        Write-Verbose -Message $txt

        ##############################
        # Variables Definition

        $ObjectsChanged = 0
        # Initialize a generic list to store ADUser objects
        $UsersToProcess = [System.Collections.Generic.List[Microsoft.ActiveDirectory.Management.ADUser]]::New()

        # parameters variable for splatting CMDlets
        [hashtable]$Splat = [hashtable]::New([StringComparer]::OrdinalIgnoreCase)

        # If parameter is parsed, initialize variable to be used by default objects
        If (-Not $PSBoundParameters.ContainsKey('ExcludedUsers')) {
            $ExcludedUsers = [System.Collections.Generic.List[string]]::New()
        } #end If

        $wellKnownUserSids = @{
            'S-1-5-21-*-500' = 'Administrator'
            '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 ($user in $users) {
                if ($user -and ($user.SamAccountName -notin $ExcludedUsers)) {
                    $ExcludedUsers.Add($user.SamAccountName)
                } #end If
            } #end Foreach
        } #end Foreach
    }

    Process {
        try {
            # Query AD for user objects with adminCount = 1
            $Splat.Clear()
            $Splat['Filter'] = 'adminCount -eq 1'
            $Splat['Properties'] = @('SamAccountName', 'DistinguishedName')

            if ($SubTree) {
                $Splat['SearchBase'] = $SearchRootDN
                $Splat['SearchScope'] = 'Subtree'
            }

            $users = Get-ADUser @Splat

            $totalUsers = $users.Count

            Write-Verbose -Message ('Total users found: {0}' -f $totalUsers)

            foreach ($user in $Users) {

                $Splat.Clear()
                $Splat['Activity'] = 'Processing Users'
                $Splat['Status'] = 'Processing {0} ({1} of {2})' -f $user.SamAccountName, ($users.IndexOf($user) + 1), $totalUsers
                $Splat['PercentComplete'] = (($users.IndexOf($user) + 1) / $totalUsers) * 100
                Write-Progress @Splat

                # Skip certain built-in accounts
                if ($User.SamAccountName -notin $ExcludedUsers) {

                    if ($PSCmdlet.ShouldProcess($User.DistinguishedName, 'Clear adminCount and reset inheritance')) {

                        $Result = Clear-AdminCount -SamAccountName $User.SamAccountName -Verbose:$VerbosePreference

                        if ($Result -like '*Updated*') {
                            $ObjectsChanged++
                            Write-Verbose -Message ('Updated user: {0}' -f $user.DistinguishedName)
                        } #end If
                    } #end If
                } else {
                    Write-Verbose -Message ('
                        [Skipped]
                            Excluded user: {0}
                            '
 -f $user.SamAccountName
                    )
                }#end If
            } #end Foreach
        } catch {
            Write-Error -Message ('An error occurred: {0}' -f $_)
        } #end Try-Catch
    } #end Process

    End {
        $txt = ($Variables.FooterHousekeeping -f $MyInvocation.InvocationName,
            'processing AdminCount on user objects.'
        )
        Write-Verbose -Message $txt

        return $ObjectsChanged
    } #end End
}