Powershell - Bytes sent/received - powershell

I need to create a script that samples the network traffic every 30 seconds and stores the bytes sent/received. This data is then later used to draw graphs. I wrote one that works perfectly on Windows 2012 but i realised some of the cmdlets were not available in previous versions like 2008 so i am seeking alternatives.
For windows 2012 i used get-netadapterstatistics to get the received/sent bytes but this won't work on pre 2012 so i thought i could use netstat -e but the problem is both are giving me completely different results and i was hoping someone can tell me why? The script below was written to see the different between data.
function getNic{
$nic = Get-NetRoute | ? DestinationPrefix -eq '0.0.0.0/0' | Get-NetIPInterface | Where ConnectionState -eq "Connected" | Select -ExpandProperty InterfaceAlias
return $nic
}
function getBR{
$b = ((netstat -e | Select-String "Bytes") -split '\s+')[2]
$a = (Get-NetAdapterStatistics |Where InterfaceAlias -eq $nic_name |Select -ExpandProperty SentBytes)
$a - $script:startbr
$b - $script:startbr2
$script:startbr = $a
$script:Startbr2 = $b
}
$nic_name = getNic
$startbr = (Get-NetAdapterStatistics |Where InterfaceAlias -eq $nic_name |Select -ExpandProperty SentBytes)
$startbr2 = ((netstat -e | Select-String "Bytes") -split '\s+')[2]
for(1..1000){
getBR
Start-Sleep 5
}
The results are as below
0
0
4577
18308
6695
26780
9055
36220
Ideally i am only interested in capturing traffic on the external interface.

While i can´t offer you an explanation for the difference between your methods i could offer you an alternative that should work on pre 2012 as well as on 2012 upwards:
$ifIndex = Get-WmiObject -Class win32_ip4routetable | where {$_.destination -eq "0.0.0.0"} | select -ExpandProperty InterfaceIndex
$ifIndex = "InterfaceIndex=" + $ifIndex
$nic_name = Get-WmiObject -Class win32_networkadapterconfiguration -Filter $ifIndex | select -ExpandProperty Description
$nic = [System.Net.NetworkInformation.Networkinterface]::GetAllNetworkInterfaces() | where {($_.description -eq $nic_name) -and ($_.operationalstatus -eq "up")}
$stats = $nic.GetIPv4Statistics()
$bytesSent = $stats.BytesSent
$bytesReceived = $stats.BytesReceived
This gives results consistent with the Get-NetAdapterStatistics Cmdlet on my system
After thinking about it maybe netstat shows statistics for multiple network adapters (maybe including loopback) combined since there is no differentiation by nic? Just guessing but this might explain the increased bytecount. Sadly there´s no details to be found in the docs

Related

Get processes that use the network

Are there any good methods that allow to get processes using the network? I did not find such parameters in Get-Process or Get-Counter. Therefore, at the moment, I extracted the id from NetStat and looked in Get-Process:
$Id = (NetStat -Ano).Where({ $_ -match "LISTENING|ESTABLISHED" }).ForEach({ $_.Split(' ')[-1] }) | Select -Unique
(Get-Process -Id $Id).Path
But surely there is a special simple method for this. Thanks
Edited Using #Lieven Keersmaekers’s hint (this), I did this way:
$Id = (Get-NetTCPConnection -AppliedSetting Internet).OwningProcess | Select -Unique
(Get-Process -Id $Id).Path

PowerShell property expression increases execution time by 4-5 times

Scroll down for TL;DR
I need to get the following properties for every process as quickly as possible, ideally 5 seconds, maximum 10 seconds: ID, Name, Description, Path, Company, Username, Session ID, StartTime, Memory, CPU (percentage, not time)
To get this data, I put together the following snippet which (I think) is functionally perfect:
$ProcessCPU = Get-WmiObject Win32_PerfFormattedData_PerfProc_Process | Select-Object IDProcess, PercentProcessorTime
$Processes = Get-Process -IncludeUserName |
Select-Object `
#{Name='Id';Expression={[int]$_.Id}},
#{Name='Name';Expression={[string]$_.Name}},
#{Name='Description';Expression={[string]$_.Description}},
#{Name='Path';Expression={[string]$_.Path}},
#{Name='Company';Expression={[string]$_.Company}},
#{Name='Username';Expression={[string]$_.UserName}},
#{Name='SessionId';Expression={[string]$_.SessionId}},
#{Name='StartTime';Expression={[string](($_.StartTime).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))}},
#{Name='MemoryMB';Expression={[int]([math]::Round($_.WorkingSet/1MB,2))}},
#{Name='CPUPercent';Expression={
[int]($ProcessCPU | ?{'IDProcess' -eq $_.Id}).PercentProcessorTime
}}
The issue is that its taking 18-22 seconds to execute, caused by this line (which adds about 16 seconds):
#{Name='CPUPercent';Expression={
[int]($ProcessCPU | ?{'IDProcess' -eq $_.Id}).PercentProcessorTime
}}
PS C:\Windows\system32> Measure-Command -Expression {
$ProcessCPU = Get-WmiObject Win32_PerfFormattedData_PerfProc_Process | Select-Object IDProcess, PercentProcessorTime
$Processes = Get-Process -IncludeUserName |
Select-Object `
#{Name='Id';Expression={[int]$_.Id}},
#{Name='Name';Expression={[string]$_.Name}},
#{Name='Description';Expression={[string]$_.Description}},
#{Name='Path';Expression={[string]$_.Path}},
#{Name='Company';Expression={[string]$_.Company}},
#{Name='Username';Expression={[string]$_.UserName}},
#{Name='SessionId';Expression={[string]$_.SessionId}},
#{Name='StartTime';Expression={[string](($_.StartTime).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))}},
#{Name='MemoryMB';Expression={[int]([math]::Round($_.WorkingSet/1MB,2))}},
#{Name='CPUPercent';Expression={
[int]($ProcessCPU | ?{'IDProcess' -eq $_.Id}).PercentProcessorTime
}}
}
TotalSeconds : 19.061206
When I remove the slow property expression noted above and keep the WMI query, execution takes about 4.5 seconds:
Measure-Command -Expression {
$ProcessCPU = Get-WmiObject Win32_PerfFormattedData_PerfProc_Process | Select-Object IDProcess, PercentProcessorTime
$Processes = Get-Process -IncludeUserName |
Select-Object `
#{Name='Id';Expression={[int]$_.Id}},
#{Name='Name';Expression={[string]$_.Name}},
#{Name='Description';Expression={[string]$_.Description}},
#{Name='Path';Expression={[string]$_.Path}},
#{Name='Company';Expression={[string]$_.Company}},
#{Name='Username';Expression={[string]$_.UserName}},
#{Name='SessionId';Expression={[string]$_.SessionId}},
#{Name='StartTime';Expression={[string](($_.StartTime).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))}},
#{Name='MemoryMB';Expression={[int]([math]::Round($_.WorkingSet/1MB,2))}}
}
TotalSeconds : 4.5202906
I thought that by getting all of the required data in a single query and referring back to the $ProcessCPU array would be fast - but I appreciate I'm iterating through each of the 250 arrays stored in $Processes.
TL;DR:
Is there a more performant method of joining two objects on a common property rather than using iteration as I have above? I.E. $ProcessCPU.IDProcess on $Processes.Id?
I tried the following block to test $Output = $ProcessCPU + $Processes | Group-Object -Property Id, it executed in just 3 seconds, but the output wasn't acceptable:
PS C:\Windows\system32> Measure-Command -Expression {
$ProcessCPU = Get-WmiObject Win32_PerfFormattedData_PerfProc_Process | Select-Object #{Name='Id';Expression={[int]$_.IDProcess}}, PercentProcessorTime
$Processes = Get-Process -IncludeUserName |
Select-Object `
#{Name='Id';Expression={[int]$_.Id}},
#{Name='Name';Expression={[string]$_.Name}},
#{Name='Description';Expression={[string]$_.Description}},
#{Name='Path';Expression={[string]$_.Path}},
#{Name='Company';Expression={[string]$_.Company}},
#{Name='Username';Expression={[string]$_.UserName}},
#{Name='SessionId';Expression={[string]$_.SessionId}},
#{Name='StartTime';Expression={[string](($_.StartTime).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))}},
#{Name='MemoryMB';Expression={[int]([math]::Round($_.WorkingSet/1MB,2))}}
$Output = $ProcessCPU + $Processes | Group-Object -Property Id
}
TotalSeconds : 2.9656969
Use CIM to build up a hashtable that maps process IDs (PIDs) to their CPU percentages first.
Then make the calculated property passed to Select-Object consult that hashtable for efficient lookups:
Get-CimInstance Win32_PerfFormattedData_PerfProc_Process |
ForEach-Object -Begin { $htCpuPctg=#{} } `
-Process { $htCpuPctg[$_.IdProcess] = $_.PercentProcessorTime } #`
Get-Process -IncludeUserName |
Select-Object Id,
Name,
Description,
Path,
Company,
UserName,
SessionId,
#{Name='StartTime';Expression={[string](($_.StartTime).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))}},
#{Name='MemoryMB';Expression={[int]([math]::Round($_.WorkingSet/1MB,2))}},
#{Name='CPUPercent';Expression={ $htCpuPctg[[uint32] $_.Id] }}
Note:
Get-CimInstance rather than Get-WimObject is used, because the CIM cmdlets superseded the WMI cmdlets in PowerShell v3 (released in September 2012). Therefore, the WMI cmdlets should be avoided, not least because PowerShell Core, where all future effort will go, doesn't even have them anymore. For more information, see this answer.
There is usually no need to use calculated properties such as #{Name='Id';Expression={[int]$_.Id}} to simply extract a property as-is - just use the property's name - Id - as a Select-Object -Property argument (but you've since clarified that you're using calculated properties because you want explicit control over the property's data type for sending data to an IoT Gateway via JSON).
Note that CIM reports PIDs as [uint32]-typed values, whereas Get-Process uses [int] values - hence the need to cast to [uint32] in the hashtable lookup.

Property has a value but cannot select it

I have a function that checks the registry for an uninstall key called Get-InstalledApps
Function Get-InstalledApps {
param (
[Parameter(ValueFromPipeline=$true)]
[string[]]$ComputerName = $env:COMPUTERNAME,
[string]$NameRegex = ''
)
foreach ($comp in $ComputerName) {
$keys = '','\Wow6432Node'
foreach ($key in $keys) {
try {
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $comp)
$apps = $reg.OpenSubKey("SOFTWARE$key\Microsoft\Windows\CurrentVersion\Uninstall").GetSubKeyNames()
} catch {
continue
}
foreach ($app in $apps) {
$program = $reg.OpenSubKey("SOFTWARE$key\Microsoft\Windows\CurrentVersion\Uninstall\$app")
$name = $program.GetValue('DisplayName')
if ($name -and $name -match $NameRegex) {
[pscustomobject]#{
ComputerName = $comp
DisplayName = $name
DisplayVersion = $program.GetValue('DisplayVersion')
Publisher = $program.GetValue('Publisher')
InstallDate = $program.GetValue('InstallDate')
UninstallString = $program.GetValue('UninstallString')
Bits = $(if ($key -eq '\Wow6432Node') {'64'} else {'32'})
Path = $program.name
}
}
}
}
}
}
and then I grab the DisplayName/Version for what I need. My current problem is that it only seems to work on certain machines. Example:
Get-InstalledApps | Where-Object {$_.Displayname -like "*Citrix Receiver*"}
Name Value
---- -----
InstallDate
ComputerName Computer
DisplayName Citrix Receiver 4.7
Bits 64
UninstallString C:\ProgramData\Citrix\Citrix Receiver 4.7\TrolleyExpress.exe /uninstall /cleanup
Path HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\CitrixOnlinePluginPackWeb
Publisher Citrix Systems, Inc.
DisplayVersion 14.7.0.13011
So this is great, I get what I want. Now I normally just pipe in | Select-Object Displayname -ExpandProperty Displayname and it would return "Citrix Receiver 4.7" just like I want. My problem is that on certain machines I'm getting this:
Get-InstalledApps | Where-Object {$_.Displayname -like "*Citrix Receiver*"} | Select-Object DisplayName
DisplayName
-----------
And that's it. Why is there no value listed? If I try to expandproperty I get an error because it says nothing is there, but clearly there is something there or the Where-Object would not have found it in my search. Again, in a lot cases this code works just fine and I get the value I want but on a lot of machines I'm getting what you see above.
Edited in from comments:
I run this on a user's machine and I get the results I posted. If I run it on my machine I'll get the value "Citrix Receiver 4.7" every time. Also, on my machine I don't get the Name and Value columns. Only about 1/4 of the machines I ran this code on actually gave me the value I expected. Windows 7 vs Windows 10 thing?
It looks to me like your function returns a [hashtable], but you're using it like it's an object with properties.
That happens to work fine with Where-Object because the .Member syntax works for accessing [hashtable] values, but it's not going to work with Select-Object because it's operating on actual properties.
So what can you do?
If you want to keep it as a [hashtable], and insist on doing it in a pipeline, you can use ForEach-Object:
Get-InstalledApps |
Where-Object {$_.Displayname -like "*Citrix Receiver*"} |
ForEach-Object -Process { $_.DisplayName }
or
Get-InstalledApps |
Where-Object {$_.Displayname -like "*Citrix Receiver*"} |
ForEach-Object -MemberName Item -ArgumentList DisplayName
Another thing you can do is change your function to return an object.
This is really easy to do with a [hashtable]; so say your function is about to return $hash, instead return:
New-Object -TypeName PSObject -Property $hash
Now you can use the normal suite of cmdlets and have them work as expected.
Edit: after seeing your code, it looks like you are converting your hashtable to an object already, but your output says otherwise. It wouldn't display as Name and Value columns if that were the case, so I still think something is wrong and the output is a [hashtable].
Edit 2: with info from comments about the platform differences, this seems to be happening because the object conversion is being done with the [pscustomobject] type accelerator which was added in PowerShell v3. Since the problematic machine is running Windows 7, it may be running v2 (which is what Win 7 shipped with).
Recommendations:
Get rid of Windows 7.
If you can't do that, upgrade PowerShell (Windows Management Framework) on that machine.
Either way, use New-Object as posted above.

Powershell on 2012 PrintServer searching for printer port with certain subnets

I am interning at a company and we need to redefine our client's printer IP schema. I'm fairly green with powershell and I am using Powershell ISE and when I use:
Get-PrinterPort
I get the full list as expected. However, if I am looking for any printer ports within the subnet set of 192.168.228.*, I have used the following:
$IPAddress = Get-WmiObject -Class Win32_TCPIPPrinterPort | Where-Object ({$_.Name.Split('.').index[-1]} -eq '228')
$IPAddress = Get-WmiObject -Class Win32_TCPIPPrinterPort | Where-Object ({$_.Name.Split('.')} -match '192.168.228*')
$IPAddress = Get-WmiObject -Class Win32_TCPIPPrinterPort | Where-Object ({$_.Name.Split('.').index[-1]} -match '228')
Nothing will display. I have tried many variations of the -match, -icontains, -contains, -like and several others that will search for the partial IP address
$IPAddress = Get-WmiObject -Class Win32_TCPIPPrinterPort | Where-Object ({$_.Name.Split('.')} -match '*228')
I have also modified the script to:
Get-WmiObject -Class Win32_TCPIPPrinterPort -OutVariable allIPs
foreach ($splitIP in $allIPs -split '.' ){
$splitIP = $allIPs -split '.'
sort $splitIP[2] -Unique -OutVariable uniqueSubnetIP
}
$uniqueSubnetIP
This is the output I get from the above code
Ultimately, I want to filter through each of the subnet IPs and locate the ones where the last octet are outside the schema and automate a process to put them in range like this:
< 192.268.***.50 || > 192.168.***.60
Any help you can offer is really appreciated; I've spent days trying to figure this out.
First, I wouldn't consider $printerport.name to be reliable for detecting subnets; use $printerport.hostaddress instead.
Get-WMIObject -Class Win32_TCPIPPrinterPort | Where-Object {($_.HostAddress -split '\.')[2] -eq 228} appears to return the printerport objects in the subnet you want - for a different subnet, just change the 228 to whatever the appropriate value is.
The -split operator takes a regular expression, so the . must be escaped, \.. The operation will result in an array of four items, representing the octets of the IP address of the port. The third octet is in #()[2]; you can test other octets similarly by changing the [2] to [0], [1], or [3].

Verifying system partition alignment via scripting

I'm trying to verify that the file system partitions within each of the servers I'm working on are aligned correctly. I've got the following script that when I've tried running will either claim that all virtual servers are aligned or not aligned based on which if statement I use (one is commented out):
$myArr = #()
$vms = get-vm | where {$_.PowerState -eq "PoweredOn" -and $_.Guest.OSFullName -match "Microsoft Windows*" } | sort name
foreach($vm in $vms){
$wmi = get-wmiobject -class "win32_DiskPartition" -namespace "root\CIMV2" -ComputerName $vm
foreach ($partition in $wmi){
$Details = "" | Select-Object VMName, Partition, Status
#if (($partition.startingoffset % 65536) -isnot [decimal]){
if ($partition.startingoffSet -eq "65536"){
$Details.VMName = $partition.SystemName
$Details.Partition = $partition.Name
$Details.Status = "Partition aligned"
}
else{
$Details.VMName = $partition.SystemName
$Details.Partition = $partition.Name
$Details.Status = "Partition not aligned"
}
$myArr += $Details
}
}
$myArr | Export-CSV -NoTypeInformation "C:\users\a411882\Documents\Scripts\PartitionAlignment.csv"
Would anyone know what is wrong with my code? I'm still learning about partitions so I'm not sure how I need to check the starting off-set number to verify alignment.
You're passing a virtual machine object instead of a string to get-wmiObject -ComputerName. When I do that, get-wmiObject throws an RPC error. You might try -computerName $vm.guest.Hostname instead of -computerName $vm.
In the commented line, your use of % should return a remainder, which will always be a whole number or zero. Maybe you were expecting a quotient instead, and wanted to evaluate if it's an integer?
PS C:\temp> (1 / 2) -isnot [int]
True
PS C:\temp> (2 / 1) -isnot [int]
False
Recent Windows OS align their partitions automatically, so there's that. Here's a good post about alignment generally on VMware, including a link to a more detailed discussion of guest partitions.