Как узнать, у каких учетных записей домена есть права локального администратора?

Передо мной стоит задача - найти в домене учетные записи пользователей, у которых есть права локального администратора. я стал изучать этот вопрос, и вариант "Ходи к каждому сотруднику и смотри в настройках системы строчку Администратор меня не устраивает, и вычитал я про такую штуку как команда Get-ADUser,но вот составить скрипт не вышло по той причине что не нашел параметр, который может это показать. Может кто подскажет в этом вопросе, или может есть другой, более простой способ решения данной проблемы?


Ответы (1 шт):

Автор решения: Dyakov Alexander

Можно попробовать что-то вроде, Get-ADComputer -filter "Enabled -eq $true" |Foreach-Object {Invoke-Command -ComputerName $_.DNSHostName -ScriptBlock {Get-LocalGroupMember Administrators}}

Но тут всё сильно зависит от зоопарка русских/английских версий, версий ОС, версий Powershell, включенного WinRM, межсетевых экранов, в общем много всего.

Если мы предположим, что WinRM у нас всех компьютерах доступен,и в AD содержится актуальная информация, то тогда можем использовать более универсальный скрипт:

function Get-Time{
    Return Get-date -Format HH:mm:ss
}
Function Get-LocalAdminsGroupMember{
    Param(
        [string]$ComputerName = $env:COMPUTERNAME
    )
    $obj = [ADSI]"WinNT://$ComputerName"
    $GroupSid = 'S-1-5-32-544'
    $AdminGroupSid = New-Object System.Security.Principal.SecurityIdentifier($GroupSid)
    $AdminGroupName = $AdminGroupSid.Translate([System.Security.Principal.NTAccount]).Value -replace '.+\\'
    if ($obj){
        $admingroup = $obj.Children |  Where-Object {$_.name -eq $AdminGroupName} # where {$_.name -match "^Administrators$|^Администраторы$"}
        if ($admingroup){
            $AdminGroupName = $admingroup | Select-Object -ExpandProperty Name
            $members = $admingroup.Invoke('Members') 
            if ($Members){
                Foreach ($Member in $Members){
                    $Account = Get-GroupMember -Member $Member -ComputerName $ComputerName
                    if ($Account){
                        $account | Add-Member -Type NoteProperty -Name GroupName -Value $AdminGroupName
                        $Accounts += @($Account)
                    }
                }
            }
        }
    }
    Return $Accounts
}
Function Get-GroupMember{
    Param (
        $Member,
        $ComputerName
        )
        $AdsPath=$member.GetType().Invokemember("ADSPath","GetProperty",$null,$member,$null)
        $domain=($AdsPath -split '/')[-2]
        $SamAccountName=($AdsPath -split '/')[-1]
        if($domain -eq $Computername){
            $accounttype='Local'
        }else{
            $accounttype='Domain'
        }
        $objectsid = $member.GetType().Invokemember("objectsid","GetProperty",$null,$member,$null)
        $sid = [System.Security.Principal.SecurityIdentifier]::new($objectsid,0).value
        $Account = [pscustomobject]@{
            Name = $SamAccountName
            SID = $sid
            Class=$member.GetType().Invokemember("Class","GetProperty",$null,$member,$null)
            AccountType=$accounttype
            ComputerName = $computername
            
        }
        if ($Account){
            Return $Account 
        }

}
$StartTime = Get-Date
$COmputers = Get-ADComputer -Filter {Enabled -eq $true}
$OUS = $Computers | Group-Object {$_.DistinguishedName.Substring($_.distinguishedName.IndexOf('OU='))} | Select-Object -ExpandProperty Name
$ComputersCount = $Computers.count
$j=0
Foreach ($OU in $OUS){
    Write-host "`n$(Get-Time) Начинаем работать с организационным подразделением $OU" -ForegroundColor Cyan
    $Computers = Get-ADComputer -Filter {Enabled -eq $true} -SearchBase $OU
    if ($Computers){
        switch -Regex ($Computers.GetType().basetype.Name){
            "Array" {$CompCounts = $Computers.count}
            "ADComputer|ADAccount"{$CompCounts = 1}
        } 
    }
    $i = 1
    Foreach ($Computer in $Computers.Name){
        Write-host "$(Get-Time) $i/$CompCounts $Computer" -ForegroundColor Green
        $object = Get-LocalAdminsGroup -ComputerName $Computer
        if ($object){
            $Objects+=@($object)
        } else {
            $FailedObjects += @($Computer)
        }
        $i++
    }

    $j = $j+$CompCounts
    $endtime=Get-Date
    $totaltime=$endtime-$starttime
    $TimePassString = ("{0:hh\:mm\:ss}" -f $totaltime) -replace "(^\d{2}):(\d{2}):(\d{2}$)",'$1 часов $2 минут $3 секунд'
    Write-Host "$j из $ComputersCount обработали, прошло $TimePassString" -ForegroundColor Cyan
}
$objects | Export-Csv -Path c:\tmp\LocalAdmins.csv -Encoding utf8 -Delimiter ";" -NoTypeInformation
$FailedObjects | Out-File C:\tmp\FailedComputer.txt -Encoding UTF8 -Force

Самым же надёжным способом на мой взгляд будет использовать GPO, в таск шедуллер добавить выполнение следующего скрипта:

Function Get-LocalAdminsGroup{
    Param(
        [string]$ComputerName = $env:ComputerName
    )
    $obj = [ADSI]"WinNT://$ComputerName"
    $GroupSid = "S-1-5-32-544"
    $AdminGroupSid = New-Object System.Security.Principal.SecurityIdentifier($GroupSid)
    $AdminGroupName = $AdminGroupSid.Translate([System.Security.Principal.NTAccount]).Value -replace ".+\\"
    if ($obj){
        $admingroup = $obj.Children |  Where-Object {$_.name -eq $AdminGroupName}
        if ($admingroup){
            $AdminGroupName = $admingroup | Select-Object -ExpandProperty Name
            $members = $admingroup.Invoke("Members") 
            if ($Members){
                Foreach ($Member in $Members){
                    $Account = Get-GroupMember -Member $Member -ComputerName $ComputerName
                    if ($Account){
                        $account | Add-Member -Type NoteProperty -Name GroupName -Value $AdminGroupName
                        $Accounts += @($Account)
                    }
                }
            }
        }
    }
    Return $Accounts
}
Function Get-GroupMember{
    Param (
        $Member,
        $ComputerName
        )
        $AdsPath=$member.GetType().Invokemember("ADSPath","GetProperty",$null,$member,$null)
        $Domain=($AdsPath -split "/")[-2]
        $SamAccountName=($AdsPath -split "/")[-1]
        $AccountType = if($Domain -eq $ComputerName){"Local"}else{"Domain"}
        
        $ObjectSid = $member.GetType().Invokemember("objectsid","GetProperty",$null,$member,$null)
        $Sid = New-Object System.Security.Principal.SecurityIdentifier($ObjectSid,0)
        $Class = $member.GetType().Invokemember("Class","GetProperty",$null,$member,$null)

        $Account = [pscustomobject]@{
            Name = $SamAccountName
            SID = $Sid
            Class = $Class
            AccountType=$AccountType
            ComputerName = $ComputerName
            
        }
        if ($Class -eq "User"){
            [bool]$enabled = if (($member.GetType().Invokemember("Userflags","GetProperty",$null,$member,$null) -band 0x2) -ne 0x2){$true}else{$false}
            $Account |Add-Member -MemberType NoteProperty -Name Enabled -Value $Enabled -Force
        }
        if ($Account){
            Return $Account 
        }

}

$CSVPath = "<Тут указать пусть к сетевой папке, в которую бы могли записывать данные все компьютеры>\localadmins\$env:COMPUTERNAME.csv"
$LocalAdmins = Get-LocalAdminsGroup

if (Test-Path $CSVPath){

    $CSVAdmins = Import-csv $CSVPath  -Delimiter ";" -Encoding UTF8
    $CurrentLocalAdmins = $LocalAdmins | Select-Object Name,Enabled,SID,Class,AccountType |ConvertTo-Csv -NoTypeInformation |ConvertFrom-Csv
    $Comparison = Compare-Object $CurrentLocalAdmins $CSVAdmins

    if ($Comparison.Sideindicator -contains '<='){
        $DeltaFile = "<Тут указать пусть к сетевой папке, в которую бы могли записывать данные все компьютеры>\localadmins\Delta\Delta_$((Get-Date).ToShortDateString().Replace('.','')).csv"
        $NewLocalAdmins = $Comparison |Where-Object SideIndicator -eq '<=' |Select-Object -ExpandProperty InputObject
        $NewLocalAdmins | Add-Member -MemberType NoteProperty -Name ComputerName -Value $env:COMPUTERNAME -Force
        $NewLocalAdmins | Export-Csv -Path $Deltafile -NoTypeInformation -Encoding UTF8 -Delimiter ";" -Append
    }
}
 
$LocalAdmins | Select-Object Name,Enabled,SID,Class,AccountType | `
Export-Csv $CSVPath -NoTypeInformation -Delimiter ";" -Encoding UTF8 -Force
→ Ссылка