Selection of only one user from list - powershell

I have this script that I need to use to retrieve the data of a particular user "ADTuser" from a list of servers the script works well, but the output file with my user add also other users' detail that is not needed for my final output how can I filter it to only the user that I need.
get-content C:\servers.txt | foreach-object {
$Comp = $_
if (test-connection -computername $Comp -count 1 -quiet) {
([ADSI]"WinNT://$comp").Children | ?{$_.SchemaClassName -eq 'user' } | %{
$groups = $_.Groups() | %{$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)}
$_ | Select #{n='Computername';e={$comp}},
#{n='UserName';e={$_.Name}},
#{n='Memberof';e={$groups -join ';'}},
#{n='status'; e={if($groups -like "*Administrators*"){$true} else{$false}}}
}
} Else {Write-Warning "Server '$Comp' is Unreachable hence Could not fetch data"}
} | Out-File -FilePath C:\users.txt

This should be an easier way of doing what you're looking for, Get-CimInstance and Get-CimAssociatedInstance have been around since PowerShell 3:
Get-Content C:\servers.txt | ForEach-Object {
$computer = $_
try {
$query = Get-CimInstance Win32_UserAccount -Filter "Name='ADTuser'" -ComputerName $_ -ErrorAction Stop
foreach($object in $query) {
$membership = Get-CimAssociatedInstance -InputObject $object -ResultClassName Win32_Group -ComputerName $_
[pscustomobject]#{
Computername = $_
UserName = $object.Name
Memberof = $membership.Name -join ';'
Status = $membership.Name -contains 'Administrators'
}
}
}
catch {
Write-Warning "Server '$computer' is Unreachable hence Could not fetch data"
}
} | Export-Csv C:\users.csv -NoTypeInformation
If that doesn't work for you, your code would require a simple modification on your first filtering statement:
Where-Object { $_.SchemaClassName -eq 'user' -and $_.Name.Value -eq 'ADTuser' }
It's important to note that Test-Connection -ComputerName $_ -Count 1 -Quiet is not a relevant test for this script, this command is testing for ICMP response and adsi over WinNT requires RPC connectivity as well SMB.
Putting it all together with minor improvements the script would look like this:
Get-Content C:\servers.txt | ForEach-Object {
if (-not (Test-Connection -ComputerName $_ -Count 1 -Quiet)) {
Write-Warning "Server '$_' is Unreachable hence Could not fetch data"
return
}
$computer = $_
([adsi]"WinNT://$_").Children.ForEach{
if($_.SchemaClassName -ne 'user' -and $_.Name.Value -ne 'ADTuser') {
return
}
$groups = $_.Groups().ForEach([adsi]).Name
[pscustomobject]#{
Computername = $computer
UserName = $_.Name.Value
Memberof = $groups -join ';'
Status = $groups -contains 'Administrators'
}
}
} | Export-Csv C:\users.csv -NoTypeInformation

Related

Optimize slow powershell script

I wrote this script and it works but its painfully slow, can you please point out why ? and provide some ideas on how to optimize its functionality. in can make simple Powershell scrips however I have a very had time looking up methodology on google not knowing what to look for
my script
$i=1;
foreach ($PC in $ComputerName) {
$per = ($i/$ComputerName.Length)*100
try {
# Get-ADComputer $pcs -properties name,enabled | select-object name,enabled
$status = Get-ADComputer -Identity $PC -Properties Enabled | select-object -ExpandProperty Enabled
if(Test-Connection -ComputerName $PC -Quiet -Count 1){
$quserOut = quser.exe /SERVER:$PC 2>&1
if ($quserOut -match "No user exists"){
"$PC>On Line>$status>No users loggedIn"; continue
}else{
$users = $quserOut -replace '\s{2,}', ',' |
ConvertFrom-CSV -Header 'username', 'sessionname', 'id', 'state', 'idleTime', 'logonTime' |
Add-Member -MemberType NoteProperty -Name ComputerName -Value $PC -PassThru
$users = $users[1..$users.count]
for ($i = 0; $i -lt $users.count; $i++){
if ($users[$i].sessionname -match '^\d+$'){
$users[$i].logonTime = $users[$i].idleTime
$users[$i].idleTime = $users[$i].STATE
$users[$i].STATE = $users[$i].ID
$users[$i].ID = $users[$i].SESSIONNAME
$users[$i].SESSIONNAME = $null
}
}
$users = $users | Sort-Object -Property idleTime
# $status = Get-ADComputer -Identity $PC -Properties Enabled | select-object -ExpandProperty Enabled
$Usr = $users | Where-Object { $_.state -eq 'Active' } | select-object -ExpandProperty username
"$PC>On Line>$status>$Usr"
}
} else {
"$PC>Not Online>$status>NoUserDataRetrieve"
}
}
catch {
"$PC>Not in AD>$status>NoUserDataRetrieve"
}
Write-Progress -Activity "Procesando Usuarios:" -Status "Usuario EN Proceso: $i -- $PC" -PercentComplete $per
Start-Sleep -Milliseconds 100
$i++
}
this displays the following data
basically script test if PC exist in Active Directory, it does a ping test , and gets back current logged in user
Machine Name > Ping Test pass? > AD status > current logged in user
PC1>Online>True>BazVic
PC2>NotOnLine>True>No Available Data
PC3>OnLine>True>ReyesDa
PC2>NotOnLine>FALSE>No Available Data

Modifying PowerShell to export Windows Update to CSV file for specific OU only not working

I need to get the list of the server last Windows Update patch from multiple different OU and then export it as in CSV file with the below column and its sample result I gather manually running Get-HotFix locally on each server:
ServerName, Last Time Update Installed, KB Number, KB Update Name, InstalledBy
PRODSQL01-VM, 31/12/2018 02:46:55, KB4462930, Cumulative Update, NT AUTHORITY\SYSTEM
PRODSQL02-VM, 18/12/2018 12:00:00 AM, KB4471324, Security Update, DOMAIN\SVC_SCCM
PRODDC01-VM, 16/1/2019 02:16:31, KB4343669, Cumulative Update, DOMAIN\SVC_SCCM
PRODDC02-VM, 13/1/2018 03:00:00 AM, KB4457146, Security Update, DOMAIN\Admin-Staff1
This is the modified script for multiple OU processing, but somehow the result is still a blank CSV file:
$CsvFile = 'C:\Result.csv'
$key = 'SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\Results\Install'
$keytype = [Microsoft.Win32.RegistryHive]::LocalMachine
$OUList = #(
"OU=TEST Servers,OU=MyDomain Testing,DC=MyDomain,DC=com"
"OU=PROD Servers,OU=Servers,OU=MyDomain Sydney,DC=MyDomain,DC=com"
"OU=PROD Servers,OU=Servers,OU=New Company,DC=MyDomain,DC=com"
)
$OUList | ForEach-Object {
$OU = $_
$Computers = Get-ADComputer -Filter {Enabled -eq $True -and OperatingSystem -like "*Server*"} -SearchBase $OU |
Select-Object -ExpandProperty DNSHostName |
ForEach-Object {
If (Test-Connection $_ -Count 1 -Quiet) {
$_
}
Else {
Write-Host "Cannot reach $($_)" -ForegroundColor Red
}
}
ForEach ($computer in $Computers) {
Try {
$remoteBase = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($keytype, $computer)
$regKey = $remoteBase.OpenSubKey($key)
$keyValue = $regkey.GetValue('LastSuccessTime')
Write-Host ''
Write-Host "$($computer): last time updates were installed was $($keyValue)"
}
Catch {
$ | Write-Error
}
Finally {
If ($regKey) {$regKey.Close()}
}
}
} | Export-Csv -Path $Csvfile -NoTypeInformation
As others have mentioned, you're doing write-host rather than adding anything to your CSV file.
Note that I haven't tested any of the below code - it's just a bit of rearranging.
$CsvFile = 'C:\Result.csv'
$Results = #() #object to hold the output
$key = 'SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\Results\Install'
$keytype = [Microsoft.Win32.RegistryHive]::LocalMachine
$OUList = #(
"OU=TEST Servers,OU=MyDomain Testing,DC=MyDomain,DC=com"
"OU=PROD Servers,OU=Servers,OU=MyDomain Sydney,DC=MyDomain,DC=com"
"OU=PROD Servers,OU=Servers,OU=New Company,DC=MyDomain,DC=com"
)
ForEach ($OU in $OUList) {
Get-ADComputer -Filter {Enabled -eq $True -and OperatingSystem -like "*Server*"} -SearchBase $OU |
Select-Object -ExpandProperty DNSHostName |
ForEach-Object {
If (Test-Connection $_ -Count 1 -Quiet) {
Try {
$remoteBase = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($keytype, $_)
$regKey = $remoteBase.OpenSubKey($key)
$keyValue = $regkey.GetValue('LastSuccessTime')
# Add result to Results array. Not Result
$results += [pscustomobject]#{
Computer = $_
LastSuccessTime = $KeyValue
}
}
Catch {
$ | Write-Error
}
Finally {
If ($regKey) {$regKey.Close()}
}
}
Else {
Write-Host "Cannot reach $($_)" -ForegroundColor Red
}
}
}
#export result object to CSV
$Results | Export-Csv -Path $Csvfile -NoTypeInformation

Parse String with PowerShell

I'm getting a report for users. The 'membership of' is a string with all the groups per user between double quotes.
I need to place \ between each group. Now I have:
"GROUP1""GROUP2""GROUP3"
And I need:
"GROUP1"\"GROUP2"\"GROUP3"
Right now my code is:
foreach ($server in $servers) {
$Comp = $server
if (test-connection -computername $Comp -count 1 -quiet) {
([ADSI]"WinNT://$comp").Children | ?{$_.SchemaClassName -eq 'user'} | %{
$groups = $_.Groups() | %{$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)}
$_ | Select #{n='Server';e={$comp}},
#{n='DistingishedName';e={$_.AdsPath}},
#{n='Nombre';e={$_.FullName}},
#{n='Departamento';e={$_.Department}},
#{n='Email';e={$_.EmailAddress}},
#{n='Active';e={if ($_.PasswordAge -like 0) {$false} else {$true}}},
#{n='PasswordExpired';e={if ($_.PasswordExpired) {$true} else {$false}}},
#{n='LastLogin';e={$_.LastLogin}},
#{n='Description';e={$_.Description}},
#{n='Groups';e={$Groups -join '"'}}
} | Export-Csv $ReportFile -Delimiter ';' -Force -Encoding UTF8 -NoTypeInformation -Append
} else {
Write-Warning "Server '$Comp' is Unreachable hence Could not fetch data" 3>> $LogFile
}
}
try -join '"\"' instead of -join '"'

Table not showing output in new line

I'm sure there is a simple solution, but I'm stuck. The output in the members column is like this
{domain\Domain Admins, domain\joerod...
How can I show the
$member
value on each line?
Function Get-AdminGroups{
foreach($i in (Get-Content C:\Users\joerod\Desktop\remove_users.txt)){
#test if machine is on the network
if (-not (Test-Connection -computername $i -count 1 -Quiet -ErrorAction SilentlyContinue)) {
Write-Warning "$i is Unavalible"
"`r"
}
else {
(invoke-command {
$members = net localgroup administrators |
? {$_ -AND $_ -notmatch "command completed successfully"} |
select -skip 4
New-Object PSObject -Property #{
Computername = $env:COMPUTERNAME
Users=$members
}
} -computer $i -HideComputerName |
Select * -ExcludeProperty RunspaceID )
}
}
}
Get-AdminGroups |ft
Iterate through $members and make an object for each one. This creates an empty array, loops through the computers in your text file, and in that loop it pulls a list of the local administrators, and for each one it creates a custom object just like you are doing, and it adds it to that array.
$Results = #()
foreach($i in (GC C:\Users\joerod\Desktop\remove_users.txt)){
#test if machine is on the network
if (!(Test-Connection -computername $i -count 1 -Quiet -ErrorAction SilentlyContinue)) {
Write-Warning "$i is Unavalible`r"
Continue
}
invoke-command {
$members = net localgroup administrators |?{$_ -AND $_ -notmatch "command completed successfully"} | select -skip 4
ForEach($member in $members){
$Results += New-Object PSObject -Property #{
Computername = $env:COMPUTERNAME
Users=$member
}
}
} -computer $i -HideComputerName # | Select * -ExcludeProperty RunspaceID
}
$Results | FT

Append CSV output coding issue

Hi i created the below script to audit local admin groups on remote hosts. It works fine, but since it only outputs the data after it has went through all the hosts, i am afraid the array will run out of buffer space before it has a chance to export to csv, so i have been trying to have it create and append the output from each host as it goes through the list except i cannot get the headers to display on the first line and append additonal lines below it. Below is the output i get when i try to append. The italicized words should be the headers and the other info should be listed in the next row. what am i doing wrong?
#{Server=pc1; Members=Administrator;DistinguishedName=DC=Domain,DC=com
This is how it should look. It looks this way if i dont append and i let it create the csv after it has finished going through the list of hosts
Server Members DistinguishedName
host1 Administrator;Admin2 DC=DOMAIN,DC=COM
$servers= get-content "C:\Scripts\AD Audits\Local Admin\workstations.txt"
$output = "c:\temp\local admin audit $CurrentDate.csv"
$results = #()
$disconnected = "Did not respond"
foreach($server in $servers)
{
$connected = Test-Connection $server -count 1 -quiet
if ($connected) {
"$server responded" | Out-File -append "c:\temp\LocalAdmin goodhosts $CurrentDate.txt"}
else {
"$server did not respond" | Out-File -append "c:\temp\LocalAdmin badhosts $CurrentDate.txt"}
$group =[ADSI]"WinNT://$server/Administrators"
$members = $group.Members() | foreach {$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null) }
$results += New-Object PsObject -Property #{
DistinguishedName = (get-adcomputer ($server) -properties * | select distinguishedname).distinguishedname
Server = $server
Members = $members -join ";"
}
$results | Export-Csv $Output -NoTypeInformation
}`
if($connected -eq $True) {
New-Object PSObject -Property #{
DistinguishedName = (Get-ADComputer $_).DistinguishedName
Server = $_
Members = $members -join ";"
}} else {write-host ""}
My suggestion is to use the pipeline rather than a foreach statement, so each object is written to the file as soon as it's processed.
$servers | ForEach-Object{
$connected = Test-Connection $_ -Count 1 -Quiet -ErrorAction SilentlyContinue
$state = if($connected) {"$_ responded"} else {"$_ did not respond"}
$state | Out-File -Append "c:\temp\LocalAdmin goodhosts $CurrentDate.txt"
$group =[ADSI]"WinNT://$_/Administrators,group"
$members = $group.Members() | ForEach-Object {$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null) }
if($connected)
{
New-Object PSObject -Property #{
DistinguishedName = (Get-ADComputer $_).DistinguishedName
Server = $_
Members = $members -join ";"
}
}
} | Export-Csv $Output -NoTypeInformation