Powershell, combine output of a foreach loop with multiple commands - powershell

I need to get a list of properties for multiple server, but I'm stuck with the output of second command in my loop:
$(foreach ( $Net in $Nets ) {
Get-NetAdapterBinding -ComponentID ms_msclient,ms_server,ms_tcpip6 -ErrorAction SilentlyContinue | select Name,DisplayName,Enabled
Get-NetAdapterAdvancedProperty $Net -DisplayName "Speed & Duplex" | select DisplayValue
}) | Format-List
The output of first cmd is correct:
Name : LAN_Clients
DisplayName : Internet Protocol Version 6 (TCP/IPV6)
Enabled : False
Name : LAN_Clients
DisplayName : File and Print Sharing
Enabled : False
Name : LAN_Clients
DisplayName : Client for Microsoft Networks
Enabled : False
The second cmd seems ignored...
If I run cmd manually the output is correct:
Get-NetAdapterAdvancedProperty "LAN_Clients" -DisplayName "Speed & Duplex" | select DisplayValue
DisplayValue
------------
Auto Negotiation
What am I doing wrong?

You need to combine both outputs into a single object.
Try
$(foreach ( $Net in $Nets ) {
$speed = (Get-NetAdapterAdvancedProperty $Net -DisplayName "Speed & Duplex").DisplayValue
Get-NetAdapterBinding -ComponentID ms_msclient,ms_server,ms_tcpip6 -ErrorAction SilentlyContinue |
Select-Object Name,DisplayName,Enabled,
#{Name = 'Speed & Duplex'; Expression = {$speed}}
}) | Format-List

You don't need to call Get-NetAdapter, as Get-NetAdapterBinding already returns all the bindings for all the adapters.
In the foreach loop, you're not using the -Name parameter with Get-NetAdapterBinding so it's returning all the bindings for all your adapters every iteration of the loop.
You can use an expression block in Select-Object to get the additional duplex property you're after, like this:
Get-NetAdapterBinding -ComponentID ms_msclient,ms_server,ms_tcpip6 -ErrorAction SilentlyContinue |
Select-Object Name,DisplayName,Enabled, #{Name = 'Speed & Duplex'; Expression={Get-NetAdapterAdvancedProperty -InterfaceAlias $_.InterfaceAlias -DisplayName 'Speed & Duplex' | select -ExpandProperty DisplayValue }} |
Format-List

For troubleshooting purposes, try this:
$Nets = Get-NetAdapterBinding -ComponentID ms_msclient,ms_server,ms_tcpip6 -ErrorAction SilentlyContinue
foreach ($Net in $Nets.name ) {
Get-NetAdapterAdvancedProperty $Net -DisplayName "Speed & Duplex" | select DisplayValue
}

Related

Apply filter to Test-NetConnection result pipeline

I'm trying to create simple "ping" job using PowerShell. And I want to do it in "pipeline" way. Though it looks like Where-Object receives strings, not objects of TestNetConnectionResult class. Could you please explain how to filter out the results of Test-NetConnection where ping was successful?
Get-Content .\adresses.txt | Test-NetConnection | Where-Object { $_.PingSucceeded } | Write-Output
The filter is working correctly but you need a final ForEach-Object for processing results in the pipeline.
Get-Content .\adresses.txt `
| Test-NetConnection `
| Where-Object { $_.PingSucceeded } `
| ForEach-Object { Write-Host "SUCCEDED: $($_.ComputerName)" }
Note: if you also want to silent warnings from Test-NetConnection please check this question.
function megaPing {
[CmdletBinding()]
Param(
[Parameter(ValueFromPipeline, Mandatory, Position = 0)]
[string]$serverName
)
process {
, [PSCustomObject]#{
serverName = $serverName
PingSucceeded = (Test-NetConnection $serverName).PingSucceeded
}
}
}
"127.0.0.1", "127.0.0.2" | megaPing
You need use pipe lines
Here's another approach:
Get-Content -Path '.\adresses.txt' | Test-NetConnection |
Select-Object #{Name = 'Address'; Expression = {$_.ComputerName}},
#{Name = 'PingSucceeded'; Expression = {$_.PingSucceeded}}
If you don't want the Progress bar, do this:
$oldProgress = $ProgressPreference
$ProgressPreference = 'SilentlyContinue'
Get-Content -Path '.\adresses.txt' | Test-NetConnection |
Select-Object #{Name = 'Address'; Expression = {$_.ComputerName}},
#{Name = 'PingSucceeded'; Expression = {$_.PingSucceeded}}
$ProgressPreference = $oldProgress
Result will look something like
Address PingSucceeded
--------- -------------
127.0.0.1 True
10.10.2.153 False
Ignore warnings? Warnings wouldn't put objects in the pipeline anyway.
echo yahoo.com microsoft.com | set-content addresses.txt
Get-Content addresses.txt | Test-NetConnection -WarningAction SilentlyContinue |
Where PingSucceeded
ComputerName : yahoo.com
RemoteAddress : 74.6.231.20
InterfaceAlias : Wi-Fi
SourceAddress : 192.168.1.114
PingSucceeded : True
PingReplyDetails (RTT) : 61 ms

Unable to pipe names of pinged computers that return false to test-netconnection

I am trying to create a basic script that pulls a list of computer names from a text file, then pings them, and returns true or false. I then want to output the ones that returned false to a text file so I can know which ones are not responding.
the closest I have got to what I want to is below:
$workstations = Get-Content "workstation_list.txt"
$workstations | Test-NetConnection -InformationLevel Quiet -WarningAction SilentlyContinue
However whenever I try to pipe the results anywhere all I get is the true or false.
How can I pass the original names that were in the $workstations array to show for all the ones that return false?
I have tried:
$workstations = Get-Content "workstation_list.txt"
$workstations |
Test-NetConnection -InformationLevel Detailed -WarningAction SilentlyContinue |
Select-Object computername, pingsucceeded |
if(pingsucceeded -eq False){write-output} else{continue}
with the following error:
pingsucceeded : The term 'pingsucceeded' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included,
verify that the path is correct and try again.
At line:11 char:144
+ ... Select-Object computername, pingsucceeded | if(pingsucceeded -eq Fal ...
+ ~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (pingsucceeded:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException*
However I can't figure out how to only return the original name of the computer that is returning false when I ping it.
I then want to output it to a text file, however if I can't get it to pass the correct information to the screen It doesn't go to a file either.
Am I close or do I need to approach this a completely different way?
Thanks!
PS.this is one of my first times posting a question on stack overflow, if I need to provide information in a different way to make it easier for you to answer please provide constructive feedback so I can do better in the future.
I'd recommend using a PSCustomObject to store your results like this:
$workstations = Get-Content "workstation_list.txt"
$Result =
foreach ($ComputerName in $workstations) {
[PSCustomObject]#{
ComputerName = $ComputerName
Online = (Test-Connection -ComputerName $ComputerName -Count 1 -Quiet)
}
}
$Result
This way you can use the variable $Result for further steps if needed. Output the successful ones for example
$Result | Where-Object -Property 'Online' -EQ -Value $true
Or filter the unsuccessful ones and output them to another file for example:
$Result |
Where-Object -Property 'Online' -EQ -Value $false |
Select-Object -ExpandProperty ComputerName |
Out-File -FilePath 'offline_workstation_list.txt'
There's some basic powershell that you need to learn. You can't pipe to an if statement for one thing, but you can to foreach-object:
$workstations = Get-Content "workstation_list.txt"
$workstations |
Test-NetConnection -InformationLevel Detailed -WarningAction SilentlyContinue |
Select-Object computername, pingsucceeded |
foreach-object { if($_.pingsucceeded -eq $False){write-output $_} else{continue} }
ComputerName PingSucceeded
------------ -------------
microsoft.com False
Trying something with the call operator and $input.
echo hi | & { if ($input -eq 'hi') { 'yes' } }
yes

powershell system.object array to String

I'm using the below code to pull the IP Address of the User (supplied at beginning of code) that had a failed logon attempt. The code works, but when I use GetType() the variable $ipp, it comes back as a Name is Object[] Base Type is System.Array I need this variable to be a string (I'm looking for the IP in a text file and removing it if it's found).
$ipp = Get-WinEvent -FilterHashtable #{LogName='Security';Id=4625;StartTime=$hours} |
Where-Object {$_.Properties[5].Value -like "*$userName*"} |
Select-Object -First 1 {$_.Properties[19].value}
I'm using PowerShell 5
Thanks
Kevin
Pre-warning: This will probably be your best bet for a full array of each bit of information from the bad attempt/lockout event. You can assign each object to a variable easily, check out the last line for some of the property names I use when checking locked accounts.
I have been doing a lot of this sort of stuff at work lately and came across a nice Microsoft demo. I unashamedly stole their script, works like a charm.
I hoped you were connecting to a domain controller but it looks like that event ID is for a bad attempt on the clients machine, not the DC.
Changed it a bit so you can slot it in after you have defined your $userName and left the Microsoft comments. Just check out line 87/88 as you may need to complete that if you encounter errors with your DC.
# Set up the lockout report
$report = #()
# Pick the DCs to crawl
$DCs = Get-ADDomainController -Filter * |
Select-Object HostName, IPv4Address, Site, OperatingSystem, OperationMasterRoles |
Out-Gridview -Title "Select the DCs to query" -PassThru |
Select-Object -ExpandProperty HostName
# Find the lockout stats for that user on all selected DCs
ForEach ($DC in $DCs) {
$report += Get-ADUser $userName -Server $DC -ErrorAction Continue `
-Properties cn, LockedOut, pwdLastSet, badPwdCount, badPasswordTime, lastLogon, lastLogoff, lastLogonTimeStamp, whenCreated, whenChanged | `
Select-Object *, #{name='DC';expression={$DC}}
}
$DCs = $report |
Select-Object `
DC, `
cn, `
LockedOut, `
pwdLastSet, `
#{name='pwdLastSetConverted';expression={[datetime]::fromFileTime($_.pwdlastset)}}, `
badPwdCount,
badPasswordTime, `
#{name='badPasswordTimeConverted';expression={[datetime]::fromFileTime($_.badPasswordTime)}}, `
lastLogon, `
#{name='lastLogonConverted';expression={[datetime]::fromFileTime($_.lastLogon)}}, `
lastLogoff, `
#{name='lastLogoffConverted';expression={[datetime]::fromFileTime($_.lastLogoff)}}, `
lastLogonTimeStamp, `
#{name='lastLogonTimestampConverted';expression={[datetime]::fromFileTime($_.lastLogonTimestamp)}}, `
whenCreated, `
whenChanged |
Out-GridView -Title "Select the DC to query event logs for lockouts" -PassThru
# Filter generated using Event Viewer GUI Custom View
# Logon/Lockout events in the last 24 hours
[xml]$xmlFilter = #"
<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">*[System[(EventID=529 or EventID=644 or EventID=675 or EventID=676 or EventID=681 or EventID=4740 or EventID=4771) and TimeCreated[timediff(#SystemTime) <= 86400000]]]</Select>
</Query>
</QueryList>
"#
$Events = #()
ForEach ($DC in $DCs) {
"Getting events from $($DC.DC)"
# Must enable the firewall rule for remote EventLog management - CHECK WITH YOUR IT TEAM, THIS MAY BREACH YOUR COMPANY SECURITY POLICY
# Invoke-Command -ComputerName $DC.DC -ScriptBlock {Get-NetFirewallRule -Name *eventlog* | Where-Object {$_.Enabled -eq 'False'} | Enable-NetFirewallRule -Verbose}
### Filter for the userID in the event message properties
$Events += Get-WinEvent -ComputerName $DC.DC -FilterXML $xmlFilter
}
ForEach ($Event in $Events) {
# Convert the event to XML
$eventXML = [xml]$Event.ToXml()
# Iterate through each one of the XML message properties
For ($i=0; $i -lt $eventXML.Event.EventData.Data.Count; $i++) {
# Append these as object properties
Add-Member -InputObject $Event -MemberType NoteProperty -Force `
-Name $eventXML.Event.EventData.Data[$i].name `
-Value $eventXML.Event.EventData.Data[$i].'#text'
}
}
# View the lockout details
$Events | Where-Object {$_.TargetUserName -eq $userName} | Select-Object TargetUserName, IPAddress, MachineName, TimeCreated | Out-GridView

Getting Installed Programs via Registry

So I've been over at
https://blogs.technet.microsoft.com/heyscriptingguy/2013/11/15/use-powershell-to-find-installed-software/
trying to use this to get a list of installed programs on a remote machine. I already started the WinRM remotely via PS, and am using the command
Invoke-Command -cn MC-PKS-MCARDH-L -ScriptBlock {
Get-ItemProperty HKLM:\Software\Wow6432Node\* |
select PSPath, PSParentPath, PSChildName
}
The primary use of this is to get the Adobe versions of programs on the client's PC, but for some reason this doesn't return many of the folders. It just returns HP, ESRI, Malwarebytes, and a few others:
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\Software\Wow6432Node\ESRI
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\Software\Wow6432Node
PSChildName : ESRI
PSComputerName : mc-pks-mcardh-l
RunspaceId : 76050648-eec5-4e90-960d-872264a894d4
PSShowComputerName : True
Any reason this is? I tried using the one from the page I linked:
HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*
but Adobe Reader doesn't even show up on that list.
I'm an AD Admin on the domain so why is it not showing all the folders? I'm looking via regedit now on the test machin and theres a folder called Adobe.
I'd recommend using, or at least trying, WMI:
Get-WmiObject -Class Win32_Product | Select-Object -Property Name, Vendor, Version | Format-Table;
You can also specify -ComputerName to query a remote machine.
To list all properties for dev. purposes, try:
Get-WmiObject -Class Win32_Product | Format-List -Property *;
Good luck!
--- ALTERNATIVELY, please try:
[String] $strKey = '';
[String] $strSubKey = '';
[PSCustomObject] $objData = $null;
#( 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall', 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall' ) | Foreach-Object {
$strKey = $_;
Get-ChildItem -Path $strKey | Select-Object -ExpandProperty PSChildName | Foreach-Object {
$strSubKey = $_;
$objData = Get-ItemProperty -LiteralPath (Join-Path -Path $strKey -ChildPath $strSubKey) | Select-Object -Property DisplayName, DisplayVersion;
if ( $objData.DisplayName -ne $null ) {
Write-Output -InputObject $objData;
} #if
} #Foreach-Object
} #Foreach-Object

List process for current user

As an administrator I can get a users processes by running this
Get-Process -IncludeUserName | Where UserName -match test
But as a non-administrator I can't use -IncludeUserName becuase "The 'IncludeUserName' parameter requires elevated user rights".
So if I'm logged on as the user test, how do I list only his processes and not everything that's running?
This is faster, one line, doesn't require admin.
Get-Process | ? {$_.SI -eq (Get-Process -PID $PID).SessionId}
You can do that through WMI. Here is an excerpt of an article you can find here.
$View = #(
#{l='Handles';e={$_.HandleCount}},
#{l='NPM(K)';e={ (Get-Process -Id $_.ProcessId).NonpagedSystemMemorySize/1KB -as [int]}},
#{l='PM(K)';e={ $_.PrivatePageCount/1KB -as [int]}},
#{l='WS(K)';e={ $_.WorkingSetSize/1KB -as [int]}},
#{l='VM(M)';e={ $_.VirtualSize/1mB -as [int]}},
#{l='CPU(s)';e={ (Get-Process -Id $_.ProcessId).CPU -as [int]}},
#{l='Id';e={ $_.ProcessId}},
'UserName'
#{l='ProcessName';e={ $_.ProcessName}}
)
Get-WmiObject Win32_Process | % { $_ |
Add-Member -MemberType ScriptProperty -Name UserName -Value {
'{0}\{1}' -f $this.GetOwner().Domain,$this.GetOwner().User
} -Force -PassThru
} | ? UserName -match $env:USERNAME | ft $View -AutoSize
Get-Process alone will not give you this information, you'll need WMI:
$owners = #{}
gwmi win32_process |% {$owners[$_.handle] = $_.getowner().user}
$ps = get-process | select processname,Id,#{l="Owner";e={$owners[$_.id.tostring()]}}
foreach($p in $ps) {
if($p.Owner -eq $env:USERNAME) {
$p
}
}
Thanks for your code. Based on this, I created a modified version to allow users to kill (a subset of) their own processes:
#Script to allow users to kill (a subset of) their own processes
#Based on : https://stackoverflow.com/questions/35195221/list-process-for-current-user
#Previously we used Task Nanny created by Michel Stevelmans which is a lot faster, but it did not show a process that was causing issues for our users.
$UserProcesses = #()
$Owners = #{}
Get-WmiObject win32_process | Foreach{$owners[$_.handle] = $_.getowner().user}
$Processes = Get-Process | select processname,Description,Id,#{l="Owner";e={$owners[$_.id.tostring()]}}
Foreach($Process in $Processes)
{
IF($process.Owner -eq $env:USERNAME)
{
$UserProcesses += $Process
}
}
$UserProcessesToExclude = #(
'concentr', #Citrix Connection Center
'conhost', #Console Window Host
'dwm', #Desktop Windows Manager
'explorer', #Explorer
'Receiver', #Citrix Receiver Application
'rundll32', #Windows host process (Rundll32)
'ssonsvr', #Citrix Receiver
'taskhost' #Host Process for Windows Tasks
'wfcrun32' #Citrix Connection Manager
'wfshell' #Citrix wfshell shell
)
$UserProcesses | Where{$_.ProcessName -notin $UserProcessesToExclude} | Out-GridView -Title 'Task killer - Select the process(es) you want to kill. Hold CTRL to select multiple processes.' -PassThru | Foreach{Stop-Process -id $_.Id}