├── .gitattributes ├── .gitignore ├── Pivot-Entity.ps1 └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | -------------------------------------------------------------------------------- /Pivot-Entity.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 2.0 2 | [cmdletbinding()] 3 | Param ( 4 | [parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Position=0)] 5 | [int]$Throttle = 15, 6 | [parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Position=1)] 7 | [string]$location, 8 | [parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Position=2)] 9 | [string]$user, 10 | [parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Position=3)] 11 | [string]$password, 12 | [parameter(Mandatory=$false,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Position=4)] 13 | [string]$GroupName = "Administrators" 14 | ) 15 | Begin { 16 | #Function that will be used to process runspace jobs 17 | Function Get-RunspaceData { 18 | [cmdletbinding()] 19 | param( 20 | [switch]$Wait 21 | ) 22 | Do { 23 | $more = $false 24 | Foreach($runspace in $runspaces) { 25 | If ($runspace.Runspace.isCompleted) { 26 | $runspace.powershell.EndInvoke($runspace.Runspace) 27 | $runspace.powershell.dispose() 28 | $runspace.Runspace = $null 29 | $runspace.powershell = $null 30 | $Script:i++ 31 | } ElseIf ($runspace.Runspace -ne $null) { 32 | $more = $true 33 | } 34 | } 35 | If ($more -AND $PSBoundParameters['Wait']) { 36 | Start-Sleep -Milliseconds 100 37 | } 38 | #Clean out unused runspace jobs 39 | $temphash = $runspaces.clone() 40 | $temphash | Where { 41 | $_.runspace -eq $Null 42 | } | ForEach { 43 | Write-Verbose ("Removing {0}" -f $_.computer) 44 | $Runspaces.remove($_) 45 | } 46 | } while ($more -AND $PSBoundParameters['Wait']) 47 | } 48 | 49 | 50 | $Script:report = @() 51 | 52 | Write-Verbose ("Building hash table for NT parameters") 53 | $NThash = @{ 54 | ErrorAction = "Stop" 55 | } 56 | 57 | #Define hash table for Get-RunspaceData function 58 | $runspacehash = @{} 59 | 60 | #Define Scriptblock for runspaces 61 | $scriptblock = { 62 | Param ( 63 | $Computer, 64 | $user, 65 | $password, 66 | $NTQuery, 67 | $NTQueryResult, 68 | $GroupName 69 | ) 70 | If (Test-Connection -ComputerName $Computer -Count 2 -Quiet) { 71 | try { 72 | cmdkey /add:$Computer /user:$user /pass:$password 73 | 74 | echo cmdkey /add:$Computer /user:$user /pass:$password 75 | 76 | $NTQuery = [ADSI]("WinNT://$Computer/Administrators") 77 | 78 | [String]$NTQueryResult = $NTQuery.PsBase.Invoke("Members") | foreach { $_.GetType().InvokeMember("ADSpath", 'GetProperty', $null, $_, $null)} 79 | 80 | $NTQueryResult.Substring(8) 81 | } Catch {Write-Warning ("{0}" -f $_.Exception.Message)} 82 | 83 | cmdkey /delete:$Computer 84 | 85 | } else{"Host $Computer Seems Down or Unreachable, Skipping"} 86 | } 87 | 88 | Write-Verbose ("Creating runspace pool and session states") 89 | $sessionstate = [system.management.automation.runspaces.initialsessionstate]::CreateDefault() 90 | $runspacepool = [runspacefactory]::CreateRunspacePool(1, $Throttle, $sessionstate, $Host) 91 | $runspacepool.Open() 92 | 93 | Write-Verbose ("Creating empty collection to hold runspace jobs") 94 | $Script:runspaces = New-Object System.Collections.ArrayList 95 | } 96 | Process { 97 | $computername = Get-Content -Path $location 98 | $totalcount = $computername.count 99 | Write-Verbose ("Querying Administrators on " + $totalcount + " systems") 100 | ForEach ($Computer in $computername) { 101 | 102 | $Computer = [System.Net.Dns]::GetHostAddresses($Computer) | Where-Object IPAddressToString -NotLike "*:*" | select -ExpandProperty IPAddressToString 103 | $powershell = [powershell]::Create().AddScript($ScriptBlock).AddArgument($Computer).AddArgument($user).AddArgument($password) 104 | 105 | $powershell.RunspacePool = $runspacepool 106 | 107 | $temp = "" | Select-Object PowerShell,Runspace,Computer 108 | $Temp.Computer = $Computer 109 | $temp.PowerShell = $powershell 110 | 111 | $temp.Runspace = $powershell.BeginInvoke() 112 | Write-Verbose ("Adding {0} collection" -f $temp.Computer) 113 | $runspaces.Add($temp) | Out-Null 114 | 115 | Write-Verbose ("Checking status of runspace jobs") 116 | Get-RunspaceData @runspacehash 117 | 118 | } 119 | } 120 | End { 121 | Write-Verbose ("Finish processing the remaining runspace jobs: {0}" -f (@(($runspaces | Where {$_.Runspace -ne $Null}).Count))) 122 | $runspacehash.Wait = $true 123 | Get-RunspaceData @runspacehash 124 | 125 | Write-Verbose ("Closing the runspace pool") 126 | $runspacepool.close() 127 | } 128 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Enterprise environment, using 1 process with 15 threads, queried 600 systems in ~15 minutes. 2 | 3 | #Known Bugs: 4 | Errors out on systems with more than one DNS entry (dual-homed etc.). Workaround: providing the IP directly instead of hostname. 5 | 6 | #Updates 7 | Coming soon: Alternate mode for when storing network credentials is disabled also enabling the script to run as the current logged in user (no user/pass input) for instances where a system is compromised, but not the user password. As a workaround, the script can be changed to make the paramaters not mandatory. 8 | 9 | 1/12/16 Update: Previous update introduced an error that severely limited the speed due to a process being outside of the scriptblock. Please be sure to download the updated version to get the proper speed. 10 | 11 | Update: Variable was in wrong position in the original version - fixed 12 | 13 | Pivot-Entity queries local admins very quickly, but utilizes a DNS lookup because the WinNT Provider doesn't like FQDN. 14 | Obviously you'll get some errors for network paths not found or no response. 15 | 16 | #Usage: 17 | 18 | Just run the script, it will prompt for all the arguments 19 | 20 | Both versions of power admin take these arguments 21 | 22 | "Thread:" - The number of threads to run concurrently 23 | 24 | "Location:" - The location of a file with all hostnames or IP addresses seperated by line breaks 25 | 26 | "User:" - Pretty obvious, supports DOMAIN\user format 27 | 28 | "Pass:" - Also obvious 29 | 30 | To Do: Support groups besides just administrators, bug fixes 31 | --------------------------------------------------------------------------------