Как узнать, у каких учетных записей домена есть права локального администратора?
Передо мной стоит задача - найти в домене учетные записи пользователей, у которых есть права локального администратора. я стал изучать этот вопрос, и вариант "Ходи к каждому сотруднику и смотри в настройках системы строчку Администратор меня не устраивает, и вычитал я про такую штуку как команда Get-ADUser,но вот составить скрипт не вышло по той причине что не нашел параметр, который может это показать. Может кто подскажет в этом вопросе, или может есть другой, более простой способ решения данной проблемы?
Ответы (1 шт):
Можно попробовать что-то вроде, 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