Powershell - Get-DistributionGroup exporting to csv issue - powershell

The below command will output the correct information to the screen, however when exporting to a csv file it looks nothing like what is shown on the screen. I have been able to export with other cmdlets, Get-DistributionGroup seems to corrupt the data when using with export.csv.
Outputting to screen works great!
Get-DistributionGroup | Where {$_.emailaddresses -like "*#adomain.com*"} | FT Name,Alias,EmailAddresses
Exporting to csv doesnt work correctly
Get-DistributionGroup | Where {$_.emailaddresses -like "*#adomain.com*"} | FT Name,Alias,EmailAddresses | Export-csv C:/thisis.csv

Instead of Format-Table (alias: ft) you should use Select-Object. Export-Csv expects objects as input, not formating instructions.
Format-Table, by definition, will convert objects to something that looks well in your output, but it's one way trip: you loose original objects as a part of the process.
Select-Object on the other hand can create objects with subset of properties, so that cmdlets like Export-Csv can still use data from original source.
EDIT
Thing I missed originally: you try to export a property that is a collection (EmailAddresses). That won't work unless you 'flatten' the collection first:
Get-DistributionGroup | Where {$_.emailaddresses -like "*#adomain.com*"} | select Name,Alias, #{
Name = 'EmailAddresses'
Expression = { $_.EmailAddresses -join '|' }
}

Thank you the answer has been resolved.
BartekB's answer
Get-DistributionGroup | Where {$.emailaddresses -like "*#adomain.com*"} | select Name,Alias, #{
Name = 'EmailAddresses'
Expression = { $.EmailAddresses -join '|' }
}

Related

Powershell print sub-level objects in FT

I have the following generic structure:
Powershell command returns an object containing named fields and values. Some of those values are objects with another struct of names and values below them.
I want to simply print the results in a FL, but I want the sub-object values included.
The following code provides me the output I'm looking for, but I feel like I'm not doing this in a powershell-efficient way and I should be able to pipe this into a one-liner
foreach ($user in (Get-MsolUser | ?{$_.StrongAuthenticationmethods -ne $null})){
Write-host "$($user.UserPrincipalName) :: $($user.DisplayName)"
foreach($method in $user.StrongAuthenticationMethods){
write-host "`t$($method.MethodType)"
}}
I was hoping the above could be shortened to resemble the below non-functional code... is something like this possible to dump the property values when there could be a number of results between 0-X (max 4 in my case)?
Get-msolUser|?{$_.StrongAuthenticationmethods -ne $null} | select UserPrincipalName,Displayname,isLicensed,(StrongAuthenticationmethods | fl)
Use a calculated property:
Get-MsolUser |
Where-Object { $null -ne $_.StrongAuthenticationmethods } |
Select-Object UserPrincipalName, Displayname, isLicensed, #{
Name='StrongAuthenticationmethods'
Expression={ $_.StrongAuthenticationmethods.MethodType -join "`n" }
} |
Format-List
The above uses Format-List (fl), but if you prefer a tabular representation via Format-Table (ft) instead, replace | Format-List with | Format-Table -Wrap.

PowerShell Export-CSV - Missing Columns [duplicate]

This question already has an answer here:
Not all properties displayed
(1 answer)
Closed 1 year ago.
This is a follow-up question from PowerShell | EVTX | Compare Message with Array (Like)
I changed the tactic slightly, now I am collecting all the services installed,
$7045 = Get-WinEvent -FilterHashtable #{ Path="1system.evtx"; Id = 7045 } | select
#{N=’Timestamp’; E={$_.TimeCreated.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')}},
Id,
#{N=’Machine Name’; E={$_.MachineName}},
#{N=’Service Name’; E={$_.Properties[0].Value}},#{N=’Image Path’;E=$_.Properties[1].Value}},
#{N=’RunAsUser’; E={$_.Properties[4].Value}},#{N=’Installed By’; E={$_.UserId}}
Now I match each object for any suspicious traits and if found, I add a column 'Suspicious' with the value 'Yes'. This is because I want to leave the decision upto the analyst and pretty sure the bad guys might use something we've not seen before.
foreach ($Evt in $7045)
{
if ($Evt.'Image Path' -match $sus)
{
$Evt | Add-Member -MemberType NoteProperty -Name 'Suspicious' -Value 'Yes'
}
}
Now, I'm unable to get PowerShell to display all columns unless I specifically Select them
$7045 | Format-Table
Same goes for CSV Export. The first two don't include the Suspicious Column but the third one does but that's because I'm explicitly asking it to.
$7045 | select * | Export-Csv -Path test.csv -NoTypeInformation
$7045 | Export-Csv -Path test.csv -NoTypeInformation
$7045 | Select-Object Timestamp, Id, 'Machine Name', 'Service Name', 'Image Path', 'RunAsUser', 'Installed By', Suspicious | Export-Csv -Path test.csv -NoTypeInformation
I read the Export-CSV documentation on MS. Searched StackOverFlow for some tips, I think it has something to do with PS checking the first Row and then compares if the property exists for the second row and so on.
Thank you
The issue you're experiencing is partially because of how objects are displayed to the console, the first object's Properties determines the displayed Properties (Columns) to the console.
The bigger problem though, is that Export-Csv will not export those properties that do not match with first object's properties unless they're explicitly added to the remaining objects or the objects are reconstructed, for this one easy way is to use Select-Object as you have pointed out in the question.
Given the following example:
$test = #(
[pscustomobject]#{
A = 'ValA'
}
[pscustomobject]#{
A = 'ValA'
B = 'ValB'
}
[pscustomobject]#{
C = 'ValC'
D = 'ValD'
E = 'ValE'
}
)
Format-Table will not display the properties B to E:
$test | Format-Table
A
-
ValA
ValA
Format-List can display the objects properly, this is because each property with it's corresponding value has it's own console line in the display:
PS /> $test | Format-List
A : ValA
A : ValA
B : ValB
C : ValC
D : ValD
E : ValE
Export-Csv and ConvertTo-Csv will also miss properties B to E:
$test | ConvertTo-Csv
"A"
"ValA"
"ValA"
You have different options as a workaround for this, you could either add the Suspicious property to all objects and for those events that are not suspicious you could add $null as Value.
Another workaround is to use Select-Object explicitly calling the Suspicious property (this works because you know the property is there and you know it's Name).
If you did not know how many properties your objects had, a dynamic way to solve this would be to discover their properties using the PSObject intrinsic member.
using namespace System.Collections.Generic
function ConvertTo-NormalizedObject {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline, Mandatory)]
[object[]] $InputObject
)
begin {
$list = [List[object]]::new()
$props = [HashSet[string]]::new([StringComparer]::InvariantCultureIgnoreCase)
}
process {
foreach($object in $InputObject) {
$list.Add($object)
foreach($property in $object.PSObject.Properties) {
$null = $props.Add($property.Name)
}
}
}
end {
$list | Select-Object ([object[]] $props)
}
}
Usage:
# From Pipeline
$test | ConvertTo-NormalizedObject | Format-Table
# From Positional / Named parameter binding
ConvertTo-NormalizedObject $test | Format-Table
Lastly, a pretty easy way of doing it thanks to Select-Object -Unique:
$prop = $test.ForEach{ $_.PSObject.Properties.Name } | Select-Object -Unique
$test | Select-Object $prop
Using $test for this example, the result would become:
A B C D E
- - - - -
ValA
ValA ValB
ValC ValD ValE
Continuing from my previous answer, you can add a column Suspicious straight away if you take out the Where-Object filter and simply add another calculated property to the Select-Object cmdlet:
# create a regex for the suspicious executables:
$sus = '(powershell|cmd|psexesvc)\.exe'
# alternatively you can join the array items like this:
# $sus = ('powershell.exe','cmd.exe','psexesvc.exe' | ForEach-Object {[regex]::Escape($_)}) -join '|'
$7045 = Get-WinEvent -FilterHashtable #{ LogName = 'System';Id = 7045 } |
Select-Object Id,
#{N='Timestamp';E={$_.TimeCreated.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')}},
#{N='Machine Name';E={$_.MachineName}},
#{N='Service Name'; E={$_.Properties[0].Value}},
#{N='Image Path'; E={$_.Properties[1].Value}},
#{N='RunAsUser'; E={$_.Properties[4].Value}},
#{N='Installed By'; E={$_.UserId}},
#{N='Suspicious'; E={
if ($_.Properties[1].Value -match $sus) { 'Yes' } else {'No'}
}}
$7045 | Export-Csv -Path 'X:\Services.csv' -UseCulture -NoTypeInformation
Because you have many columns, this will not fit the console width anymore if you do $7045 | Format-Table, but the CSV file will hold all columns you wanted.
I added switch -UseCulture to the Export-Csv cmdlet, which makes sure you can simply double-click the csv file so it opens correctly in your Excel.
As sidenote: Please do not use those curly so-called 'smart-quotes' in code as they may lead to unforeseen errors. Straighten these ’ thingies and use normal double or single quotes (" and ')

Formatting variable with 2 outputs

Code:
$adgroups = Get-ADPrincipalGroupMembership $tag$ | select -ExpandProperty Name | Sort | Select-String "iSite"
Output:
DFSR Managed iSite Enterprise 4.4.542.2 WSA_Rad_A
DFSR Managed iSite Radiology 4.4.516.27 WSA_Rad_A
Basically one command generates two items (output using $variable | out-file C:\file.txt -Append) and when I go to open these in excel they format as one line like this:
DFSR Managed iSite Enterprise 4.4.542.2 WSA_Rad_A DFSR Managed iSite Radiology 4.4.516.27 WSA_Rad_A
Is there a way to break it up // add a new line after each item but still keep them both inside one variable?
Get-ADPrincipalGroupMembership $tag$ | select -ExpandProperty Name | Sort | Select-String "iSite" | ConvertTo-Csv -NoTypeInformation | Out-File C~\Desktop\Sites.csv
I would break up your request a bit.
First, you're using Select -Expand, which is going to discard all of the properties and only return the values for each object's Name. This is a problem because when you export it as a CSV, you're not going to have a heading. I think the lack of header is ultimately leading to the issue you're facing here.
Try this instead:
$adgroups = Get-ADPrincipalGroupMembership $tag$ | Where Name -like "*iSite*" |
select Name | Export-Csv c:\pathto\YourCsv.Csv
Finally, I don't think Select-String is doing you any favors. You could instead use the -like operator.

How do I add a prefix to Format-List output?

I need to generate an import file in the format of sip:username#domain.com for the Lync User Management tool.
I'm using this PowerShell commmand:
Get-DistributionGroupmember FirmOperationS#company.com | Select-Object primarysmtpaddress
I'm having trouble prepending the characters "sip:" to the column output, and saving it to a CSV using Export-Csv.
Try a custom/calculated property:
Get-DistributionGroupmember FirmOperationS#company.com |
Select-Object #{n="PrimarySMTPAddress";e={ "sip:$($_.primarysmtpaddress)" }}
If you only need that property, you could also replace Select-Object with Foreach-Object, like
Get-DistributionGroupmember FirmOperationS#company.com |
ForEach-Object { "sip:$($_.primarysmtpaddress)" }

Powershell System.Array to CSV file

I am having some difficulty getting an Export-Csv to work. I am creating an array like this...
[pscustomobject] #{
Servername = $_.Servername
Name = $_.Servername
Blk = ""
Blk2 = ""
Method = "RDP"
Port = "3389"
}
The issue I have is when I try to export that to a CSV I get garbage that looks like this...
"9e210fe47d09416682b841769c78b8a3",,,,,
I have read a ton of articles addressing this issue, but I just don't understand how to get the data right.
For testing, I built a CSV file w/ the servernames, and read it in, and the following works in PS4:
$serverList = import-csv "datafile.csv"
$AllObjects = #()
$serverList | ForEach-Object {
$AllObjects += [pscustomobject]#{
Servername = $_.Servername
Name = $_.Servername
Blk = ""
Blk2 = ""
Method = "RDP"
Port = "3389"
}
}
$AllObjects | Export-Csv -Path "outfile.csv" -NoTypeInformation
This happens when you try to pipe out from any of the Format-* commands.
The Format-List, Format-Table and Format-Wide cmdlets are special in PowerShell, in that they're meant to consume the pipeline, and transform it for display in the console. So, you can't pipe from FL, FT or FW into Export-csv. As Don Jones says "Format On the Right".
Don't believe me? Observe, as I run Get-Process, send it through Format-Table and then convert to Csv.
gps | ft | ConvertTo-Csv
#TYPE Microsoft.PowerShell.Commands.Internal.Format.FormatStartData
"ClassId2e4f51ef21dd47e99d3c952918aff9cd","pageHeaderEntry","pageFooterEntry","autosizeInfo","shapeInfo","groupingEntry"
"033ecb2bc07a4d43b5ef94ed5a35d280",,,,"Microsoft.PowerShell.Commands.Internal.Format.TableHeaderInfo",
"9e210fe47d09416682b841769c78b8a3",,,,,
"27c87ef9bbda4f709f6b4002fa4af63c",,,,,
"27c87ef9bbda4f709f6b4002fa4af63c",,,,,
"27c87ef9bbda4f709f6b4002fa4af63c",,,,,
It's even the same string! Why do we get this string? I really wish I knew, but I think it has something to do with the way the Format-* commands convert objects into text instructions for display in the console.
If you REALLY love the way your Format-Table looks, send it to Out-File, which is the only way to redirect the output of a Format-* command.
Another message is to use Tee-Object to dump a copy of your pipe to a variable, and then send that out to Export.
Get-Process | Tee-Object -Variable ExportMe | Format-Table
$exportMe | export-Csv .\Export.csv
I call this the 'have your cake and eat it too approach'.
Use Select-Object to prevent the bad CSV export.
Example:
Get-Services | Select-Object * | export-csv -Path C:\log.csv