list all local admins with properties report

Hello

I am trying to work out a method of getting a report of all local administrators for a group of servers. The servers are on multiple domains and some are not on any domain. I have a common account which has the same username and password on all systems. I think that will work to run this?

I need to get a list of all the local admin accounts on each server in the list. This should be a local account or domain account and then I need to get at least the last login time for each of the admins.

The script below works great for getting a report of local admins. How can I get this to provide the last login date/time for each of the local admins, and ensure it will report the users that are a member of one of the groups that have been added to the local admins groups to?

#>
[CmdletBinding()]
Param(
[Parameter( ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true
)]
[string]
$ComputerName = $env:ComputerName,

[Parameter()]
[string]
$GroupName = “Administrators”,

[Parameter()]
[string]
$OutputFolder = “c:\scripts\reports”
)

Begin {

$OutputFile = Join-Path $OutputFolder “Local_Report.CSV”
Write-Verbose “Script will write the output to $OutputFile folder”
Add-Content -Path $OutPutFile -Value “ComputerName, GroupName, QueryResult, ObjectType, DomainName, Name”
}

Process {
ForEach($Computer in $ComputerName) {
Write-host “Working on $Computer”
If(!(Test-Connection -ComputerName $Computer -Count 1 -Quiet)) {
Write-Verbose “$Computer is offline. Proceeding with next computer”
Add-Content -Path $OutputFile -Value “$Computer,$GroupName,Offline”
Continue
} else {
Write-Verbose “Working on $computer”
try {
$group = [ADSI]“WinNT://$Computer/$GroupName”
$members = @($group.Invoke(“Members”))
Write-Verbose “Successfully queries the members of $computer”
if(!$members) {
Add-Content -Path $OutputFile -Value “$Computer,$GroupName,NoMembers”
Write-Verbose “No members found in the group”
continue
}
}
catch {
Write-Verbose “Failed to query the members of $computer”
Add-Content -Path $OutputFile -Value “$Computer,QueryFailed”
Continue
}
foreach($member in $members) {
try {
$MemberDisplayName = $member.GetType().Invokemember(“Name”,“GetProperty”,$null,$member,$null)
$ObjectType = $member.GetType().Invokemember(“Class”,“GetProperty”,$null,$member,$null)
$MemberPath = $member.GetType().Invokemember(“ADSPath”,“GetProperty”,$null,$member,$null)
$MemberDomain = $null
if($MemberPath -match “^Winnt://(?\S+)/(?\S+)/”) {
if($ObjectType -eq “User”) {
$ObjectType = “LocalUser”
} elseif($ObjectTypee -eq “Group”){
$ObjectType = “LocalGroup”
}
$MemberDomain = $matches[“CompName”]

} elseif($MemberPath -match “^WinNT://(?\S+)/”) {
if($ObjectType -eq “User”) {
$ObjectType = “DomainUser”
} elseif($ObjectType -eq “Group”){
$ObjectType = “DomainGroup”
}
$DomainOfMember = $matches[“domainname”]

} else {
$ObjectType = “”
$DomainOfMember = “Unknown”
}
Add-Content -Path $OutPutFile -Value “$Computer, $GroupName, SUCCESS, $ObjectType, $DomainOfMember, $MemberDisplayName”
} catch {
Write-Verbose “failed to query details of a member. Details $_”
Add-Content -Path $OutputFile -Value “$Computer,QueryFailed”
}

}
}

}

}

Windows doesn’t track local logon times anyplace other than the event log, and for that you have to enable auditing of those events. So you’d need to enable auditing, and then query the local Security event log for whatever event ID that logon is. You’ll then have to filter through the events to capture only the ones you care about, and extract the date/time of the event.

There’s also no easy way to recurse group membership - especially when you start adding domain groups to local groups. You simply have to enumerate the members of the local group, see which ones are groups (rather than users), and then enumerate their memberships.

I was under the same impression about the local logon times but came across this article by Boe Prox. In there, it does have a LastLogin property so it looks like it might be stored in the local computer user account database.

Prior to this article I was not aware this was logged anywhere and had always relied on techniques like looking at the last write time of the profile folder. Any thoughts on the validity of this value? It seems to be accurate based on the values on my PC and a test machine.

$computerName = 'PC-1'
$adsi = [ADSI]"WinNT://$computerName"
$users = $adsi.Children | where {$_.SchemaClassName -eq 'User'}
$users | select Name,LastLogin

Thanks Don and Matt!

I read up on this quite a bit and read that the stamp is stored in the local SAM db.

How can I take that code and add it to the script so it will add that as a column in a csv? I found a few articles on this and one from Jeff Hicks that’s fairly old works but only for 1 named account.

I like the script I posted as it works great, but I’m not sure how to add the group portion into it and get the login stamp. Below is the script I found on a blog Jeff wrote.

Get-content servers.txt | foreach {
[ADSI]"WinNT://$/Administrator" | select `
@{Name=“Account”;Expression={($
.PSBase).Path}},`
@{Name=“PasswordSet”;Expression={(Get-Date).AddSeconds(-($.PasswordAge)[0])}},`
@{Name=“PasswordAge (Days)”;Expression={int }},`
@{Name=“Last Logon”;Expression={$
.LastLogin}},`
@{Name=“Days since last login”;Expression={`
(New-TimeSpan -start ($_.LastLogin[0]) -end (Get-Date)).days}}} `
| export-csv “c:\adminage.csv” -notypeinformation