Grouping / adding similar objects - powershell

I am querying SCCM database to check the history of users who logged into a computer.
What I have so far works fine however there is a lot of data and I would like to group them all together. For example here is the output for one computer:
These are all logons of the same user (let's say JohnDoe123).
What I want is, instead of having like 30 entries for John, 20 entries for Jane, etc. I want a total of each user. For example:
Computer1 - JohnDoe123 - FullName - Latest time for LastConsoleUse - 500 (total number of console logons) - Latest TimeStamp - 50 000 (total minutes)
You see what I mean?
Obviously I can't sum up the LastConsoleUse and TimeStamp values, so ideally I would like to have just the latest value.
I'm trying ... | Group-Object -property SystemConsoleUser but it's returning an hashtable which isn't the format I'm after:
I'm pretty confused when it comes to manipulating objects within an object like this, so thanks for your help!
#Hist Users SCCM
$ConnexionsUsagers = $null
$poste = "X"
$SiteCode = "X"
$SiteServer = "X"
$ResourceID_Name = (Get-WmiObject -namespace root\sms\site_$SiteCode -computer $SiteServer -query "select ResourceId,Name from SMS_R_SYSTEM where SMS_R_SYSTEM.Name LIKE '%$poste%'")
$ConnexionsUsagers += foreach ($item in $ResourceID_Name) {
$query = #"
SELECT *
FROM SMS_GH_System_SYSTEM_CONSOLE_USER
WHERE SMS_GH_System_SYSTEM_CONSOLE_USER.ResourceID
LIKE '%$($item.ResourceId)%'
"#
Get-WmiObject -Namespace root\sms\site_$SiteCode -Computer $SiteServer -Query $query |
Where-Object {$_.SystemConsoleUser -ne $null} |
Select-Object #{name='Poste';expression={$item.Name}}, SystemConsoleUser,
#{name='Nom complet';expression={Get-ADUser -Properties userprincipalname ($_.SystemConsoleUser -replace "X\\") | Select-Object -ExpandProperty userPrincipalName}},
#{name='LastConsoleUse';expression={[DateTime]::ParseExact(($_.LastConsoleUse).Split('.')[0], "yyyyMMddHHmmss", [System.Globalization.CultureInfo]::InvariantCulture)}},
NumberofConsoleLogons,
#{name='TimeStamp';expression={[DateTime]::ParseExact(($_.TimeStamp).Split('.')[0], "yyyyMMddHHmmss", [System.Globalization.CultureInfo]::InvariantCulture)}},
TotalUserConsoleMinutes
}
$ConnexionsUsagers |
Sort-Object #{expression="Poste"; Ascending=$true}, #{expression="TimeStamp"; Descending=$true} |
Out-GridView

You can achieve the desired result using Group-Object and Measure-Object.
$ConnexionsUsagers | Group-Object -Property SystemConsoleUser |
Select-Object #{n='SystemConsoleUser';e={$_.Name}},
#{n='Poste';e={($_.group | Group-Object -Property Poste -NoElement).Name -join ';'}},
#{n='Nom Complet';e={($_.group | Group-Object -Property "Nom Complet" -NoElement).Name}},
#{n='NumberOfConsoleLogons';e={($_.group.NumberOfConsoleLogons | Measure-Object -Sum).Sum}},
#{n='TotalUserConsoleMinutes';e={($_.group.TotalUserConsoleMinutes | Measure-Object -Sum).Sum}},
#{n='LastConsoleUse';e={$_.group.LastConsoleUse | Sort-Object -Descending | Select-Object -First 1}}
Group-Object -Property PropertyName groups the array of objects by PropertyName. Every unique value of PropertyName will be grouped into a single line or item in the resulting collection. PropertyName values will now be listed under the Name property with all other properties and their values being listed as an array of objects under the Group property. You can then access the Group property to access the remaining ungrouped properties and values. The -NoElement switch leaves off the Group property. It is simply a way to minimize the returned data when it is not needed.
-join operator is used just in case there are multiple computers used by the user. -join ';' joins an array of values into a single string delimited by the semi-colon.

Related

Exchange/Powershell - Add data to new column rather than joining

I am trying to export list of users in mail enabled security groups to csv but want to have each member in a separate column rather than joining the existing column.
$Csvfile = "C:\SPOgroupmembers.csv"
$Groups = Get-DistributionGroup -Filter "Alias -like '*.spo'" -ResultSize Unlimited
$Groups | ForEach-Object {
$GroupDN = $_.DistinguishedName
$DisplayName = $_.DisplayName
$PrimarySmtpAddress = $_.PrimarySmtpAddress
$Members = Get-DistributionGroupMember $GroupDN -ResultSize Unlimited
[PSCustomObject]#{
DisplayName = $DisplayName
PrimarySmtpAddress = $PrimarySmtpAddress
Members = ($Members.Name -join ',')
}
} | Sort-Object DisplayName | Export-CSV -Path $Csvfile -NoTypeInformation -Encoding UTF8 #-Delimiter ";"
This is how it currently outputs:
DisplayName
PrimarySmtpAddress
Member
Test.SPO
Test.SPO#test.com
User1,User2,User3
This is what I am trying to achieve:
DisplayName
PrimarySmtpAddress
Test.SPO
Test.SPO#test.com
User1
User2
I may be missing something simple but any help would be appreciated
If you want to export the data where each member has it's own row, which in my opinion, would be the proper way to do it, you can have an inner loop to create a new pscustomobject per member of the Group:
Get-DistributionGroup -Filter "Alias -like '*.spo'" -ResultSize Unlimited | ForEach-Object {
foreach($member in Get-DistributionGroupMember $_.DistinguishedName -ResultSize Unlimited) {
[PSCustomObject]#{
DisplayName = $_.DisplayName
PrimarySmtpAddress = $_.PrimarySmtpAddress
Member = $member
}
}
} | Sort-Object DisplayName | Export-CSV -Path ....
The simplest way to construct a [pscustomobject] dynamically is to construct an ordered hashtable first - which is easy to extend iteratively - and cast it to [pscustomobject] when done.
However, in the context of creating CSV output, you need to commit to a fixed number of properties (columns) ahead of time - if feasible[1]; e.g.:
$maxMembers = 10 # <- adjust this number to the max. count of members you expect
$Groups | ForEach-Object { ...
# ...
# Initialize an ordered hashtable with the static entries...
$oht = [ordered] #{
DisplayName = $DisplayName
PrimarySmtpAddress = $PrimarySmtpAddress
}
# ... then iteratively add the Member1, Member2, ... entries
foreach ($i in 1..$maxMembers) {
$oht["Member$i"] = $Members[$i-1]
}
# Convert to a [pscustomobject] and output
[pscustomobject] $oht
} | Sort-Object DisplayName | Export-CSV -Path $Csvfile -NoTypeInformation -Encoding UTF8
If no max. member count can / should be assumed, consider denormalizing the data by using a single member column combined with creating a separate row for each member, as shown in Santiago's helpful answer, which is unquestionably the better approach for subsequent programmatic processing of the data vs. the multi-column approach you're seeking, which may be simpler to grasp for the human observer.
[1] That is, you need to know how many members a group can have at most. You could even try to determine that count programmatically, ahead of time, but either way the resulting number may be too large to be practical.

How to get Memory (Private working set) per users in powershell

I'm trying to get the memory usage of process per user (like task manager), that info comes to Memory (Private working set) if we convert that values into MB we should get the memory usage, like in the users view in task manager...
Maybe i missing something, if someone know about this pls tell me.
And this is my script
Get-WmiObject Win32_Process | Sort-Object -Property privatememorysize -Descending |
Select processname, #{Name="Mem Usage(MB)";Expression={[math]::round($_.privatememorysize/ 1mb,2)}},#{Name="UserID";Expression={$_.getowner().Domain+"\"+$_.getowner().user}} | fl *
Try using WorkingSetSize instead of PrivateMemorySize.
Get-WmiObject Win32_Process | Sort-Object -Property WorkingSetSize -Descending |
Select processname, #{Name="Mem Usage(MB)";Expression={[math]::round($_.WorkingSetSize / 1mb,2)}},#{Name="UserID";Expression={$_.getowner().Domain+"\"+$_.getowner().user}} | FL
The issue is that the Win32_Process class doesn't have a property named 'privatememorysize'. Replacing that with 'privatepagecount' instead makes this work as expected.
Get-WmiObject Win32_Process | Sort-Object -Property privatepagecount -Descending |
Select processname, #{Name="Mem Usage(MB)";Expression={[math]::round($_.privatepagecount/ 1mb,2)}},#{Name="UserID";Expression={$_.getowner().Domain+"\"+$_.getowner().user}}
I see, that is not the same, so we have the issue here where the WMI object doesn't give private working set, and other methods that do include that don't have the user. So what we can do is use Get-Process to get each process and the private working set, and use Get-WMIObject to get the user associated with each object, and then match them up. Probably best to make a hashtable from one to reference, then use that to add the property to the other object. So, let's do that!
#Get WMI Process objects
$WMIProcs = Get-WmiObject Win32_Process
#Get Get-Process object
$GPProcs = Get-Process
#Convert Get-Process objects to a hashtable for easy lookup
$GPHT = #{}
$GPProcs | ForEach-Object {$GPHT.Add($_.ID.ToString(),$_)}
#Add PrivateWorkingSet and UserID to WMI objects
$WMIProcs|ForEach-Object{
$_ | Add-Member "Mem Usage(MB)" $([math]::round($GPHT[$_.ProcessId.ToString()].PrivateMemorySize64/1mb,2))
$_ | Add-Member "UserID" $($_.getowner().Domain+"\"+$_.getowner().user)
}
#Output to screen
$WMIProcs | Format-Table ProcessName, "Mem Usage(MB)", UserID

Export Get-ADComputer to CSV [duplicate]

This question already has answers here:
How to get an object's property's value by property name?
(6 answers)
Closed 5 years ago.
I am working on a script which takes Hostnames from a CSV, Files them against Get-ADComputer and then saves certain Objects in certain columns of the original CSV.
While the solution seems like a basic Task (code below), my problem is, that the Output of Get-ADComputer always (you can see I played around with Out-String but also tried other formatting options) contains a lot of NewLine characters or other formatting issues which make the CSV confusing.
Clear-Host
Import-Module ActiveDirectory
$import = Import-Csv 'XXX' -Delimiter ';'
Foreach ($row in $import){
$hostname = $row.HOSTNAME
if($hostname.length -gt 3){
$computer = Get-ADComputer -Filter {Name -like $hostname} -Properties LastLogonDate, LastLogonTimeStamp
$row.AD_LastLogon.ToString() = $computer | Select-Object LastLogonDate | Select-Object -first 1 | FT -HideTableHeaders | Out-String
$row.AD_LLTimestamp = $computer | Select LastLogonTimestamp |Select-Object -first 1 | FT -HideTableHeaders | Out-String
}
}
$import | Export-Csv 'XXX' -Delimiter ';' -NoType
My question now is, if anyone could help with a method to get the bare string result of for example Get-ADComputer's LastLogonDate, without any formatting or headers included.
Thanks in advance!
Use the -ExpandProperty parameter of Select-Object to extract just the parameter you want. You can only specify one parameter to exapand at a time.
$row.AD_LastLogon = $computer | Select-Object -ExpandProperty LastLogonDate -First 1 | Out-String
$row.AD_LLTimestamp = $computer | Select-Object -ExpandProperty LastLogonTimestamp -First 1
I don't believe the -First 1 should be necessary. Get-ADComputer shouldn't be finding multiple computers with the same name.
Also, you shouldn't need to retrieve both LastLogonDate and LastLogonTimestamp. The former is the same value as the latter, just converted to a DateTime from the irritating NT Time Epoch that LastLogonTimestamp uses. Have you got a system that requires both?
Finally, just a note but this:
$row.AD_LastLogon.ToString() = $computer | [...]
It doesn't make sense. You can't assign a value to a method. I would be surprised if that didn't error or otherwise do nothing at all.

PowerShell: How can I list the index of an item using format-table?

I wish to list the index of an item in a result table.
In this case I am using get-aduser with the filter option.
I am then using format-table with -Property to display only the properties I want.
I previously used a loop to display the items along with a counter to emulate the index but this was messy and I wanted it in a table.
Code:
$search_param = read-host "Enter search string: "
$search_param = "*" + $search_param + "*" # Construct search param.
$search_result = Get-ADUser -Filter {Surname -like $search_param -or GivenName -like $search_param -or SamAccountName -like $search_param}
$search_result | format-table -Property GivenName,Surname,SamAccountName
How can I get format-table to display the item index/position without using some kind of loop? ie, is there some kind of 'hidden' index property that I can simply provide to format-table?
The format-table CmdLet -Property param can be a new calculated property see Format-table help.
Here is an example with a computed index on Get-Process objects.
$index=0
Get-Process | Format-Table -Property #{name="index";expression={$global:index;$global:index+=1}},name,handles
The ForEach-Object cmdlet can be used to create a calculated "index" property for the Format-Table cmdlet
For Example:
$search_result | ForEach-Object {$index=0} {$_; $index++} | Format-Table -Property #{ Label="index";Expression={$index}; Width=5 },GivenName,Surname,SamAccountName
Additional References:
Adding Array Index Numbers to PowerShell Custom Objects
ForEach-Object
Format-Table
In my opinion you can't, you need to extend the objects with an Index property and include it in your Format-Table command.
$procs = Get-Process
#option 1
$procs | Select #{N='Index'; E={$procs.IndexOf($_)}}, name, handles
#option 2
$procs | Format-Table -Property #{name="index";expression={$procs.IndexOf($_)}},name,handles

Simple script to ping PCs, output table - what's wrong?

I was trying to write a simple script to ping a list of computers, and output the result in a table. I ended up doing this, which works fine:
Get-ADComputer -searchbase "OU=Materials,OU=MMC Computers,OU=REI,DC=REIDOMAIN,DC=LOCAL" -filter * | select -expand name | % {
$output = New-Object PSObject
$output | Add-Member NoteProperty "Computer name"($_)
$output | Add-Member NoteProperty "Ping result"("$($(Test-Connection $_ -count 1 -quiet).ToString())")
write $output }
However, I'd like to understand why my first two attempts didn't work, so that I can avoid making the same mistakes later. Here they are:
Script 1: See edit below
Get-ADComputer -searchbase "OU=Materials,OU=MMC Computers,OU=REI,DC=REIDOMAIN,DC=LOCAL" -filter * |
select -expand name | % { $computer = $_; $obj = "" |
select #{Name="Computer";Expression="$computer"},`
#{Name="Pingable";Expression="$($(Test-Connection $computer -count 1 -quiet).ToString())"}
$obj }
Output 1:
Computer Pingable
-----— -----—
Note: Under the table headers, this script actually prints one blank line for each computer I'm pinging.
Script 2:
$table = #{Expression={$_};Label="Computer"},#{Expression={"$($(Test-Connection $_ -count 1 -quiet).ToString())"};Label="Pingable"}
Get-ADComputer -searchbase "OU=Materials,OU=MMC Computers,OU=REI,DC=REIDOMAIN,DC=LOCAL" -filter * | select -expand name | format-table $table
Output 2:
mickeymouse
goofy
minnie
pluto
frank
This one doesn't even output a table...it just prints one computer name per line.
I'd appreciate if someone could explain what's going wrong in these two attempts. Thanks!
Edit: Got Script 1 to work.
Get-ADComputer -searchbase "OU=Materials,OU=MMC Computers,OU=REI,DC=REIDOMAIN,DC=LOCAL" -filter * |
select #{Name="Computer";Expression={$_.Name}},#{Name="Pingable";Expression={"$($(Test-Connection $_.Name -count 1 -quiet).ToString())"}};
Still curious about Script 2
In your second example, you are using the -ExpandProperty of Select-Object which effectively strips the data from the object and outputs an array of strings which format-table then outputs as a list.
See this example:
#First create the array of objects
$rawData = #( #{"Name"="First Obj"; "OtherParam"=1;}, #{"Name"="Second Obj"; "OtherParam"=2;})
$objects = $rawData | %{new-object -type psobject -prop $_}
#Just output the objects
$objects | format-table
Output:
Name OtherParam
---- ----------
First Obj 1
Second Obj 2
Now select the name property, this gives an array of objects with just a single property "name"
$objects | select name | format-table
Output:
Name
----
First Obj
Second Obj
Expand the name property, this gives an array of strings that format table just lists with no heading:
$objects | select -expand name | format-table
Output:
First Obj
Second Obj