I have some simple Exchange Powershell I have written. I would like to list the UPN, Displayname, Item Count, and Item Size into a single CSV. However I have only been able to successfully push the data to two arrays and then manually combine them. Here is my code.
$MailBoxs = Get-Mailbox * | Select UserPrincipalName -ExpandProperty UserPrincipalName | Sort-Object UserPrincipalName
$Mailboxs2 = $MailBoxs.Where({ $_ -ne $null })
ForEach($MailBox2 in $MailBoxs2) { Get-MailboxStatistics $Mailbox2 | Sort-Object TotalItemSize –Descending | Select #{label=”User”;expression={$_.DisplayName}},#{label=”Total Size (MB)”;expression={$_.TotalItemSize.Value.ToMB()}},#{label=”Items”;expression={$_.ItemCount}} | Export-CSV "C:\T2\MailBoxSize.csv" -Append -NoTypeInformation }
ForEach($MailBox2 in $MailBoxs2) { $Mailbox2 | Export-CSV "C:\T2\MailBoxSize2.csv" -Append -NoTypeInformation }
Basically the second CSV gives me two fields for some reason the SMTP address and some random Length field, It also gives me a leading whitespace. If anyone has any ideas on how to clean this up I would love to hear them. Thanks for your time.
The multiple select statements were unnecessary. Here's a bit simplified way:
$mailboxes = #(Get-Mailbox *).
Where({$_.UserPrincipalName}) |
Sort-Object -Property UserPrincipalName
foreach ($box in $mailboxes) {
Get-MailboxStatistics $box.UserPrincipalName |
Sort-Object -Property TotalItemSize -Descending |
Select-Object -Property #(
#{L='UPN';E={$box.UserPrincipalName}}
#{L='User';E={$_.DisplayName}}
#{L='Total Size (MB)';E={$_.TotalItemSize.Value.ToMB()}}
#{L='Items';E={$_.ItemCount}}
) |
Export-Csv -Path 'C:\T2\MailBoxSize.csv' -NoTypeInformation -Append
}
Related
Hi Powershell newbie alert,
I am trying to compare the computer name of my $getADComp function (CN) with the output of $WSUSArr.
I want to know which PC's are in the WSUS but not and the AD and vice versa. I want the PC names to go in 2 diffrent result arrays so i can use these again later
Eventually i want something like this
$separator = "."
$GetWSUSComp = Get-wsuscomputer -UpdateServer $wsus
$GetADComp = Get-ADComputer -Filter * -Property CN, CanonicalName, Description | Select-Object CN, CanonicalName, Description
$WSUSArr = #()
for ($i = 0; $i -lt $GetWSUSComp.Count; $i++){
$WSUSArr += $GetWSUSComp[$i].FullDomainName.split($separator)[0].ToUpper()
}
Compare-Object -ReferenceObject $WSUSArr -DifferenceObject $GetADComp
If there is a more efficient way to do this feel free to make use of another method (hash table, etc.)
I have another array which is called $WSUSArr which contains the names of all the computer that are connected to the WSUS server i will have to compare those two lists with eachother.
Why not go for an array of objects ?
$GetADComp = Get-ADComputer -Filter * -Property CN, DistinguishedName, Description |
Select-Object CN, DistinguishedName, Description
This way you avoid gathering all properties with -Property * where you only want three.
Every property of items in the array can be accessed by using
$GetADComp[$index].CN
$GetADComp[$index].DistinguishedName
$GetADComp[$index].Description
and compared with an array of CN's like
$GetADComp | Where-Object { $_.CN -eq $WSUSArr[$index] }
In order to compare the computer CN's with the objects returned from the Get-ADComputer cmdlet:
1.
Get the computers that are both in AD and in the WSUS array.
If you want this to be a simple string array of just the CN's, do this:
$ADcomputersInWsus = $GetADComp | Where-Object { $WSUSArr -contains $_.CN } | Select-Object -ExpandProperty CN
Without the Select-Object, this will get you an array of objects with three
properties: CN, DistinguishedName, Description.
$ADcomputersInWsus = $GetADComp | Where-Object { $WSUSArr -contains $_.CN }
# An object array like this is perfect for saving as CSV:
# $ADcomputersInWsus | Export-Csv -Path 'X:\ADcomputersInWsus.csv' -NoTypeInformation
2.
Get a list of computers that are in WSUS, but not in AD:
# ($GetADComp).CN returns an string array with just the CN's, just like the $WSUSArr
$WsusComputersNotInAD = $WSUSArr | Where-Object { ($GetADComp).CN -notcontains $_ }
3.
Get a list of AD computers that are not in WSUS:
$ADcomputersNotInWsus = $GetADComp | Where-Object { $WSUSArr -notcontains $_.CN } | Select-Object -ExpandProperty CN
Note, the $WsusComputersNotInAD is derived from the $WSUSArr string array and is therefore also an array of strings, not objects.
To save that to file, either use:
$WsusComputersNotInAD | Out-File -FilePath "C:\XXX\XXX\WSUSCompNotInAD.txt" -Force
Or convert to an object array and use Export-Csv like the other results:
$WsusComputersNotInAD | ForEach-Object { [PsCustomObject]#{'ComputerName' = $_}} |
Export-Csv -Path "C:\XXX\XXX\WSUSCompNotInAD.csv" -NoTypeInformation
I guess the question is in the title.
I have a CSV that looks something like
user,path,original_path
I'm trying to find duplicates on the original path, then output both the user and original_path line.
This is what I have so far.
$2 = Import-Csv 'Total 20_01_16.csv' | Group-Object -Property Original_path |
Where-Object { $_.count -ge 2 } | fl Group | out-string -width 500
This gives me the duplicates in Original_Path. I can see all the required information but I'll be danged if I know how to get to it or format it into something useful.
I did a bit of Googleing and found this script:
$ROWS = Import-CSV -Path 'Total 20_01_16.csv'
$NAMES = #{}
$OUTPUT = foreach ( $ROW in $ROWS ) {
IF ( $NAMES.ContainsKey( $ROW.Original_path ) -and $NAMES[$ROW.original_path] -lt 2 )
{ $ROW }
$NAMES[$ROW.original_path] += 1 }
Write-Output $OUTPUT
I'm reluctant to use this because, well first I have no idea what it's doing. So little of the makes any sense to me, I don't like using scripts I can't get my head around.
Also, and this is the more important part, it's only giving me a single duplicate, it's not giving me both sets. I'm after both offending lines, so I can find both users with the same file.
If anyone could be so kind as to lend a hand I'd appreciate it.
Thanks
It depends on the output format you need, but to build on what you already have we can use this to show the records in the console:
Import-Csv 'Total 20_01_16.csv' |
Group-Object -Property Original_path |
Where-Object { $_.count -ge 2 } |
Foreach-Object { $_.Group } |
Format-Table User, Path, Original_path -AutoSize
Alternatively, use this to save them in a new csv-file:
Import-Csv 'Total 20_01_16.csv' |
Group-Object -Property Original_path |
Where-Object { $_.count -ge 2 } |
Foreach-Object { $_.Group } |
Select User, Path, Original_path |
Export-csv -Path output.csv -NoTypeInformation
I'm trying to search through one column in each row of the table. I would then like to add another value to the row based on the number being search.
This code produces the table:
$LUNSSummary = ($NY_LUNS) -split '\s+(?=LOGICAL UNIT NUMBER)' | foreach {
$Stringdata = $_.replace(':','=')
New-Object PSObject -Property $(ConvertFrom-StringData $Stringdata)
}
$LUNSSummary |
select 'Name','LOGICAL UNIT NUMBER','State','LUN Capacity(Megabytes)','LU Storage Groups' |
Format-Table -AutoSize
Then I have this code which can search using the "Logical Unit Number" and produce the desired output. In this example the -contains is 1029 from the above screenshot.
$data = $LUNS_in_Pools | Out-String
$pools = $data -replace ': +','=' -split "`r`n`r`n" |
% { New-Object -Type PSCustomObject -Property (ConvertFrom-StringData $_) } |
select -Property *,#{n='LUNs';e={$_.LUNs -split ', '}} -Exclude LUNs
$pools | ? { $_.LUNs -contains 1029 } | select -Expand 'Pool Name'
Which produces in this case "Pool 2". The result can be Pool 1-99.
I want to combine these two codes to search every "Logical Unit Number" and add the result to the end of the table in a 5th section/column "Pools".
EDIT
As requested, raw data:
$NY_LUNS before $LUNSSummary gets it: http://pastebin.com/5wrd51Lf
$LUNS_in_Pools raw data: http://pastebin.com/Zg9q6jhe
Desired Output: (Pool is obtained from "Logical Unit Number")
EDIT 2
This is now the closest to correct so far, it prints the same pool result every time.
$LUNSSummary =
($NY_LUNS) -split '\s+(?=LOGICAL UNIT NUMBER)' |
foreach { $Stringdata =
$_.replace(':','=')
New-Object PSObject -Property $(ConvertFrom-StringData $Stringdata)
}
$data = $LUNS_in_Pools | Out-String
$pools = $data -replace ': +','=' -split "`r`n`r`n" |
% { New-Object -Type PSCustomObject -Property (ConvertFrom-StringData $_) } |
select -Property *,#{n='LUNs';e={$_.LUNs -split ', '}} -Exclude LUNs
$poolProperty = #{Label="Pool";Expression={$pools | ? { $_.LUNs -contains [int]$_.'LOGICAL UNIT NUMBER'} | select -Expand 'Pool Name'}}
$LUNSSummary | select 'Name','LOGICAL UNIT NUMBER','State','LUN Capacity(Megabytes)','LU Storage Groups',$poolProperty
if I check the output of $pools | ? { $_.LUNs -contains [int]$_.'LOGICAL UNIT NUMBER'} | select -Expand 'Pool Name'
I only see one result. I'm thinking maybe it has to be looped some how?
From the guess of it you just need one more calculated property on the end there for 'Pool'. You already have, and tested, the logic. Just need to implement it.
$poolProperty = #{Label="Pool";Expression={
$lunID = $_.'LOGICAL UNIT NUMBER';
$pools | Where-Object{$_.LUNs -contains $lunID} |
Select-Object -Expand 'Pool Name'}
}
$LUNSSummary | select 'Name','LOGICAL UNIT NUMBER','State','LUN Capacity(Megabytes)','LU Storage Groups',$poolProperty
We take the LOGICAL UNIT NUMBER of the current item in the pipeline and save it so that we can start another to extract the match from the $pools object. As long as you luns are exclusive this would always return one Pool Name.
The above should work but I changed how $pools was created so it matched the logic of $LUNSSummary. I used here-strings for the raw data from your paste bin.
$LUNSSummary = ($NY_LUNS) -split '\s+(?=LOGICAL UNIT NUMBER)' |
foreach { $Stringdata =
$_.replace(':','=')
New-Object PSObject -Property $(ConvertFrom-StringData $Stringdata)
}
$pools = ($LUNS_in_Pools | Out-String) -split '\s+(?=Pool Name)' | ForEach-Object{
New-Object -Type PSCustomObject -Property (ConvertFrom-StringData ($_ -replace ":","=")) |
Select -Property *,#{n='LUNs';e={$_.LUNs -split ',\s*'}} -Exclude LUNs
}
$poolProperty = #{Label="Pool";Expression={
$lunID = $_.'LOGICAL UNIT NUMBER';
$pools | Where-Object{$_.LUNs -contains $lunID} |
Select-Object -Expand 'Pool Name'}
}
$LUNSSummary | select 'Name','LOGICAL UNIT NUMBER','State','LUN Capacity(Megabytes)','LU Storage Groups',$poolProperty
Looks like $LUNS_in_Pools was a newline delimited string. Piping to Out-String cleaned it up to remove the newlines and allow the regex/ConvertFrom-StringData to work.
I extracted two lists of computers from two different tools, Array1 and Array2.
Now I need to extract the ones which are in Array1, but not in Array2.
I managed to get all the matching ones by doing this:
$matchingComp = #()
foreach ($SCCMcomputer in $SCCMcomputers) {
foreach ($eWPTcomputer in $eWPTcomputers) {
if ($SCCMcomputer.Computername -eq $eWPTComputer.Computername) {
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value $SCCMcomputer.Computername
$matchingComp +=$obj
}
}
}
$matchingComp | Export-Csv $inEWPT -Delimiter "," -NoTypeInformation
But I still need the ones that are in $SCCMcomputer but NOT in $eWPTcomputers...
I've found some solutions on SO with other languages (e.g. Perl) but not for PowerShell.
UPDATE
I still don't get the correct output, in Excel with this formula:
the output looks like:
means some are here, some not. The output in powershell is like this
means 0KB is emtpy.
$SCCMcomputers | Export-Csv $sccmexport -Delimiter "," -NoTypeInformation
$eWPTcomputers | Export-Csv $ewptexport -Delimiter "," -NoTypeInformation
Compare-Object -ReferenceObject $SCCMcomputers -DifferenceObject $eWPTcomputers | ?{$_.sideIndicator -eq "=>"} |select inputobject | Export-Csv $inEWPT -NoTypeInformation
Compare-Object -ReferenceObject $SCCMcomputers -DifferenceObject $eWPTcomputers | ?{$_.sideIndicator -eq "=="} |select inputobject | Export-Csv $inBoth -NoTypeInformation
Compare-Object -ReferenceObject $SCCMcomputers -DifferenceObject $eWPTcomputers | ?{$_.sideIndicator -eq "<="} |select inputobject | Export-Csv $inSCCM -NoTypeInformation
And both Column Name (or what it's called) from SCCMcomptuers/eWPTcomputers is "Computername"
Any idea what I could be doing wrong? Both computer arrays are generated from SQL and in hashtables (I think it's called): #{Computername=......}#{Computername...., something like this.
Update 2
foreach ($t in $sccmComputers) {
$Test1 += $t.computername
}
$Test2 = #()
foreach ($t in $ewptComputers) {
$Test2 += $t.computername
}
By removing the Header of the Hashtable and just having arrays full of strings works fantasctic..... even -Property computername did not work... :S
use compare-object cmdlet
Compare-Object -ReferenceObject $sccm -DifferenceObject $wpt | ?{$_.sideIndicator -eq "<="} |select inputobject
example :
$sccm=#(1,2,3)
$wpt=#(2,4)
Compare-Object -ReferenceObject $sccm -DifferenceObject $wpt -IncludeEqual
will output :
InputObject SideIndicator
2 ==
4 =>
1 <=
3 <=
that means value "2" is on both objects, "1" and "3" only on "the left side" (ie the reference object), while "4" is only on the difference object
Use compare-object as follows
Compare-Object -ReferenceObject $sccm -DifferenceObject $wpt -passthru
This should give you just the objects in $sccm but not in $wpt.
CORRECTION:
The above code WILL work for the case where DifferenceObject is guaranteed to be a subset of ReferenceObject. It will FAIL, though, if there are additional objects in DifferenceObject that are not also present in ReferenceObject. The above code returns any objects which are present in EITHER ReferenceObject OR DifferenceObject but NOT in both.
To properly return ONLY objects in ReferenceObject that are not also present in DifferenceObject, the following code is required:
Compare-Object -ReferenceObject $sccm -DifferenceObject $wpt |
Where-Object { $_.SideIndicator -eq '<=' } |
ForEach-Object { Write-Output $_.InputObject }
The where-object clause ensures only objects that are present in ReferenceObject are passed down the pipeline.
The foreach-object clause forces the output back to a simple array (ref: Converting Custom Object arrays to String arrays in Powershell - thanks Keith)
You can use -contains and -notcontains
$A1 ="asd","zxc","qwe",'a'
$A2 = "asd","zxc","bqwe",'b'
$A1,$A2 |
%{
$_|
%{
If ($A1 -contains $_ -and $A2 -notcontains $_) {$_}
}
}
The Compare-Object method is indeed the best. What hasn't been addressed yet is clean output to your Excel file.
Compare-Object $sccm $ewpt | ?{ $_.SideIndicator -eq '<=' | Export-Csv sccm-only.csv -NoTypeInformation
will produce two columns. One with "InputObject" and your computer names, and another column with "SideIndicator" and a bunch of rows with "<=".
The easy fix is to select only the column you want:
Compare-Object $sccm $ewpt | ?{ $_.SideIndicator -eq '<=' | Select-Object InputObject | Export-Csv sccm-only.csv -NoTypeInformation
This will give you a single column labeled "InputObject" and your computer names.
If you want to change the column label, use the method from another thread, Windows Powershell Rename Column Heading CSV file:
Compare-Object $sccm $ewpt | ?{ $_.SideIndicator -eq '<=' | Select-Object #{ expression={$_.InputObject}; label='ComputerName' } | Export-Csv sccm-only.csv -NoTypeInformation
Also, simply change the SideIndicator comparison to get those computers in both systems, or only in eWPT:
# Only in eWPT
Compare-Object $sccm $ewpt | ?{ $_.SideIndicator -eq '=>' | Select-Object #{ expression={$_.InputObject}; label='ComputerName' } | Export-Csv sccm-only.csv -NoTypeInformation
# In both SCCM and eWPT
Compare-Object $sccm $ewpt | ?{ $_.SideIndicator -eq '==' | Select-Object #{ expression={$_.InputObject}; label='ComputerName' } | Export-Csv sccm-only.csv -NoTypeInformation
$sccm=#(1,2,2,3)
$wpt=#(2,4)
Compare-Object -ReferenceObject $sccm -DifferenceObject $wpt |
Where-Object { $_.SideIndicator -eq '<=' } |
ForEach-Object { Write-Output $_.InputObject }
This will return 1,2,3, this method is not correct
I have a CSV like below:
location,id
loc1,1234
loc1,1235
loc1,1236
Running $a = Import-CSV C:\File.csv | Group-Object "location" I get the following output:
Count Name Group
----- ---- -----
3 loc1 {#{location=loc1; id=1234}, #{location=loc1; id=1235), #{location=loc1, id=1236}}
I would like to add all ID's to a single group (Using Add-QADGroupMember) but I can't figure out how to get a group of ID's for $loc1. It seems to be be grouping them correctly but I can't seem to parse the output into a single group. E.g $loc1 = 1234,1235,1236 that I can loop through.
Any suggestions would be appreciated!
Group-Object doesn't handle hashtables well, since the keys aren't real properties.
Assuming:
$csv = Import-CSV C:\File.csv
You should be able to do, for example:
$ids = $csv | %{ $_.id }
to get an array of the ID values. You'd probably want to pipe through Get-Unique for location.
If you wanted to get the location for a single ID quickly:
$location = $csv | ?{ $_.id -eq 42 } | %{ $_.location }
If you wanted to get an array of all IDs for a single location quickly (I think this is what you want):
$loc1 = $csv | ?{ $_.location -eq 'loc1' }
For reference, if you wanted to get a hashtable mapping each location to an array of IDs:
$groups = $csv | %{ $_.location } | &{
begin
{
$hash = #{}
}
process
{
$location = $_.location
$hash[$location] = $csv | ?{ $_.location -eq $location }
}
end
{
$hash
}
}
A bit tricky, but this will do it:
Import-Csv C:\File.csv | Group-Object "location" | %{Set-Variable ($_.Name) ($_.Group | Select-Object -ExpandProperty id)}
After running that, $loc1, $loc2, etc. will be arrays of all the ids for each location.
And yet another option:
(Import-Csv c:\foo.csv | Group Location -AsHashTable).Loc1 | Foreach {$_.id}
And if you're on V3, you can do this:
(Import-Csv c:\foo.csv | Group Location -AsHashTable).Loc1.Id