Get-MsolDevice RegisteredOwners export to CSV fails - powershell

I've written a simple script that is attempting to use the input from a source CSV file containing ObjectID's in a column and then exporting the following information; DisplayName, DeviceTrustType, RegisteredOwners, ApproximateLastLoginTimeStamp using the Get-MsolDevice command.
Here's the code I've got so far:
Connect-MsolService
Connect-AzureAD
Get-AzureADDevice -All $True | Select ObjectID | Export-Csv "C:\temp\Project\IntuneDevices\AllAADObjectIDs.csv" -noType
$allobjects = Import-Csv "C:\temp\Project\IntuneDevices\AllAADObjectIDs.csv"
ForEach ($line in $allobjects)
{
Get-MsolDevice -ObjectID $line.ObjectId | Select DisplayName,DeviceTrustType,RegisteredOwners,ApproximateLastLogonTimestamp| Export-Csv -Path "C:\temp\Project\IntuneDevices\Final.csv" -Append -NoTypeInformation
}
The problem is that when I open Final.csv the entry for RegisteredOwners shows up as System.Collections.Generic.List`1[System.String]
When I simply run the command from the terminal without exporting to a CSV so the oputput is displayed on the screen it looks fine, I see the RegisteredOwner listed in curly brackets like {username#domain.org}.
I've read that is because the output for RegisteredOwners is a list item and not simple text like the rest of the output, but I'm having a heck of time converting it inline.
I've tried things like inserting
Select -Property DisplayName,#{ n='RegisteredOwners'; e={ $_.RegisteredOwners -join ';' } }
But that errors during the run no matter the variations I've tried with it.
Any help with this would be greatly appreciated as I've searched and tried multiple solutions but none are working for me.
TIA

Thanks for pushing me in the right direction Bender, turns out I must have had some sort of syntax error I didn't understand. Here is the final code and it's working as intended.
## Script to collect all AzureAD registered devices, their trust type, and their owner.
## Connect to AzureAD and MSOL
Connect-MsolService
Connect-AzureAD
## Get all devices filtering their ObjectID
Get-AzureADDevice -All $True | Select ObjectID | Export-Csv "C:\temp\Project\IntuneDevices\AllAADObjectIDs.csv" -noType
## Create the variable from the expoted Csv
$allobjects = Import-Csv "C:\temp\Project\IntuneDevices\AllAADObjectIDs.csv"
## Create a new file using Column 1 of allobjects.csv entries to suss the information required for each device
ForEach ($line in $allobjects)
{
Get-MsolDevice -ObjectID $line.ObjectId | Select DisplayName,DeviceTrustType,#{ n='RegisteredOwners'; e={ $_.RegisteredOwners -join ';' } },ApproximateLastLogonTimestamp | Export-Csv -Path "C:\temp\Project\IntuneDevices\Final.csv" -Append -NoTypeInformation
}

Related

Getting data from AD via Powershell

Can someone help?
I'm trying to get data from AD (via PS) in Excel (CSV), but my script put all objects in 1 columns, not in deafferents.
class GoodFree {
static [object[]] Make ([string]$GroupName){
return (Get-ADGroupMember $GroupName |
ForEach-Object {
[PSCustomObject]#{
lic = '1123'
name = $_.Name
gName = $GroupName
}
})
}
[GoodFree]::Make('LV-DPA') | Out-File C:\Users\Downloads\Users.csv
As Scepticalist mentions in their comment, there's a specific Export-Csv cmdlet for CSV-files.
After the pipe use Export-Csv -Path C:\Users\Downloads\Users.csv
Powershell automatically adds type information to CSV-files so you may want to add the -NoTypeInformation switch to the above command to skip that.
If you wish to manage the file encoding you can use the -Encoding switch to for instance get the file in UTF8.
Powershell uses the Windows-culture to determine the standard delimiter for CSV-files, but you can change that with the -Delimiter switch. Add ';' to use semicolons as delimiters for the file.

[PowerShell]Get-Content and Add Column Entry?

I am trying to input a list of users into PowerShell and get a specific security group attached to the user's account. At this current time, I have two pieces - an Excel sheet with multiple pieces of data, and a .txt with just the user's usernames. The script I have currently just inputs the user's usernames from the .txt and gets the security group from their account that matches a specific prefix, however I noticed doing it this way doesn't give any specific order. Even though the users are in a specific order (copied and pasted exactly from the excel document), the actual output doesn't come back well.
So, here's what I'd Like to do now, I just don't know how. I would like to get the content from the Excel document, take all of the usernames and do Get-ADPrincipalGroupMembership like I am now, and then write the security group Back to the line that matches the username. For example, if I looked up the SG for msnow, it would get the SG for msnow and then write the SG back to the row that has msnow, and continues through the list. Instead of just doing an Out-GridView, it would actually write this to the Excel document.
Any help on making this work?
Here is the code I have right now.
Import-Module ActiveDirectory
$Names = Get-Content C:\Temp\Users.txt
$Records = #()
Foreach ($ADUsers in $Names) {
Try {
$SG = Get-ADPrincipalGroupMembership -Identity $ADUsers | Select Name | Where {$_.Name -Like "SG - *"}
$SGName = $SG.Name
}
Catch [ADIdentityNotFoundException] {
$SGName = "User not found"
}
$Records += New-Object PSObject -Property #{"UserName" = $ADUsers;"Security Group" = $SGName}
}
Write-Host "Generating CSV File..."
$Records | Out-GridView
Thank you!
If you save the Excel as CSV, so it will look something like
"UserName","Security Group","InsideInfo"
"bloggsj","","tall guy"
"ftastic","","nothing worth mentioning"
things shouldn't be that hard to do.
$out = 'D:\Test\Updated_usersandgroups.csv'
$csv = Import-Csv -Path 'D:\Test\usersandgroups.csv'
Write-Host "Updating CSV File..."
foreach ($user in $csv) {
try {
$SG = Get-ADPrincipalGroupMembership -Identity $user.UserName -ErrorAction Stop
# if more groups are returned, combine them into a delimited string
# I'm using ', ' here, but you can change that to something else of course
$SGName = ($SG | Where-Object {$_.Name -Like "SG - *"}).Name -join ', '
}
catch [ADIdentityNotFoundException] {
$SGName = "User $($user.UserName) not found"
}
catch {
# something else went wrong?
$SGName = $_.Exception.Message
}
# update the 'Security Group' value
$user.'Security Group' = $SGName
}
Write-Host "Generating updated CSV File..."
$csv | Export-Csv -Path $out -UseCulture -NoTypeInformation
# show output on screen
$csv | Format-Table -AutoSize # or -Wrap if there is a lot of data
# show as GridView (sorts by column)
$csv | Out-GridView
Output in console would then look like
UserName Security Group InsideInfo
-------- -------------- ----------
bloggsj SG - Group1, SG - Group1 tall guy
ftastic SG - Group1 nothing worth mentioning
Note: I don't know what delimiter your Excel uses when saving to CSV file. On my Dutch machine, it uses the semi-colon ;, so if in your case this is not a comma, add the delimiter character as parameter to the Import-Csv cmdlet: -Delimiter ';'
Excel uses whatever is set in your locale as ListSeparator for the delimiter character. In PowerShell you can see what that is by doing (Get-Culture).TextInfo.ListSeparator. On output, the -UseCulture switch will make sure it uses that delimiter so Excel will understand

Copy altered CSV Data to new CSV

The whole point of this issue is going to be: How to copy data from one CSV to another without knowing/listing the headers of the original CSV.
The cmdlet I'm building is meant to convert a report from CSV to a spreadsheet eventually. And if I write the column headers to the code, each time somebody changes the report, the code will break and it would have to be updated.
The steps I would take right now:
# Import the Source CSV. Gonna pull data from this later.
$SourceCSV = Import-Csv -Path $reportSourceCSV -Delimiter ";"
# Remove NULL characters, white spaces and change comma separator to semicolon.
(Get-Content -Path $reportSourceCSV | Where-Object {-not [string]::IsNullOrWhiteSpace($PSItem)}).Replace('","',";") | Out-File -FilePath $TMP1
# Import the modified new temp CSV.
$Input = Import-Csv -Path $TMP1 -Delimiter ";"
# Take existing CSV file headers and append some new ones. Rename a long column name.
((($GetHeaders = foreach ($Header in $SourceCSV[0].PSObject.Properties.Name) {
"`"$Header`""
}) + '"column4"','"column5"','"column6"') -join ";").Replace("VerylongOldColumnName","ShortName") | Out-File -FilePath $TMP2
foreach ($Item in $Input) {
"`"$($Item.column1)`";`"$($Item.'column2')`";`"$($Item.column3)`"" | Out-File -FilePath $TMP2 -Append
}
$exportToXLSX = Import-Csv -Path $TMP2 -Delimiter ";" | Export-Excel -Path $Target -WorkSheetname "reportname" -TableName "tablename" -TableStyle Medium2 -FreezeTopRow -AutoSize -PassThru
$exportToXLSX.Save()
$exportToXLSX.Dispose()
Remove-Item -Path $TMP1, $TMP2
This works! But I don't want to create infinite amount of different reports and just as many different logic blocks to process all these reports.
So this is as far as I was able to get trying a more dynamic way of processing the report CSVs:
(Get-Content -Path $reportSourceCSV | Where-Object {-not [string]::IsNullOrWhiteSpace($PSItem)}).Replace('","',";") | Out-File -FilePath $TMP1
$import = Import-Csv -Path $TMP1 -Delimiter ";"
$headers = ($import[0].PSObject.Properties.Name).Replace("VerylongOldColumnName","ShortName")
$headers | Out-File -FilePath "C:\TEMP\test.csv"
foreach ($item in $import) {
for ($h = 0; $h -le ($headers).Count; $h++) {
$($item.$($headers[$h]))
}
}
Now, this works... kind of. If I run the script like this, it shows me the output I want, but I was NOT able to export this to CSV.
I added Export-Csv to this line: $($item.$($headers[$h])) so this particular line would look like this:
$($item.$($headers[$h])) | Export-Csv -Path $Output -Delimiter ";" -Append -NoTypeInformation
And this is the error I get:
Export-Csv : Cannot append CSV content to the following file: C:\TEMP\test.csv.
The appended object does not have a property that corresponds to the following
column: column1. To continue with mismatched properties, add the -Force parameter,
and then retry the command.
At line:11 char:36
+ ... ers[$h])) | Export-Csv -Path $Output -Delimiter ";" -Append -NoTypeIn ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (column1:String) [Export-Csv], InvalidOperationException
+ FullyQualifiedErrorId : CannotAppendCsvWithMismatchedPropertyNames,Microsoft.PowerShell.Commands.ExportCsvCommand
If I add -Force parameter, the output will be the headers and a bunch of empty lines.
As little as I understand, is that the output is for some reason a string? To my knowledge everything should be an object in PS, unless converted to string (Write-Host cmdlet being an exception). And I don't really know how to force the output back to being objects.
Edit: Added sample source CSV
"Plugin","Plugin Name","Family","Severity","IP Address","Protocol","Port","Exploit?","Repository","DNS Name","NetBIOS Name","Plugin Text","Synopsis","Description","Solution","See Also","Vulnerability Priority Rating","CVSS V3 Base Score","CVSS V3 Temporal Score","CVSS V3 Vector","CPE","CVE","Cross References","First Discovered","Last Observed","Vuln Publication Date","Patch Publication Date","Exploit Ease","Exploit Frameworks"
"65057","Insecure Windows Service Permissions","Windows","High","127.0.0.1","TCP","445","No","Individual Scan","computer.domain.tld","NetBIOS Name","Plugin Output:
Path : c:\program files (x86)\application\folder\service.exe
Used by services : application
File write allowed for groups : Users, Authenticated Users
Full control of directory allowed for groups : Users, Authenticated Users","At least one improperly configured Windows service may have a privilege escalation vulnerability.","At least one Windows service executable with insecure permissions was detected on the remote host. Services configured to use an executable with weak permissions are vulnerable to privilege escalation attacks.
An unprivileged user could modify or overwrite the executable with arbitrary code, which would be executed the next time the service is started. Depending on the user that the service runs as, this could result in privilege escalation.
This plugin checks if any of the following groups have permissions to modify executable files that are started by Windows services :
- Everyone
- Users
- Domain Users
- Authenticated Users","Ensure the groups listed above do not have permissions to modify or write service executables. Additionally, ensure these groups do not have Full Control permission to any directories that contain service executables.","http://www.nessus.org/u?e4e766b2","","8.4","","AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","cpe:/o:microsoft:windows","","","Jul 11, 2029 06:48:20 CEST","Jul 11, 2029 06:48:20 CEST","N/A","N/A","",""
Edit: I think I found another way how to accomplish this and looking at it, it looks I tried to overdo it quite a bit.
# Doing cleanup, changing delimiters, renaming that one known column. All in one line.
$importCSV = 'C:\TEMP\sourceReport.csv'
(Get-Content -Path $importCSV | Where-Object {-not [string]::IsNullOrWhiteSpace($PSItem)}).Replace('","','";"').Replace"VerylongOldColumnName","ShortName") | Out-File -FilePath C:\TEMP\tmp1.csv
# Adding additional columns and exporting it all to result CSV.
Import-Csv -Path C:\TEMP\tmp1.csv -Delimiter ";" | Select-Object *, "Column1", "Column2" | Export-Csv -Path C:\TEMP\result.csv -NoTypeInformation -Delimiter ";"
You should not simply replace , with ; because the fields actually contain commas as in ..Additionally, ensure these groups .. By replacing just like that, the field will get separated from the rest of its content and you'll end up with a mis-aligned csv.
The below approach will do what you want, leaving the structure of the csv file intact:
$importCSV = 'C:\TEMP\sourceReport.csv'
$exportCSV = 'C:\TEMP\result.csv'
$columnsToAdd = "Column1", "Column2"
# read the file as string array, not including empty lines
$content = Get-Content -Path $importCSV | Where-Object { $_ -match '\S' }
# replace the column header in the top line only
$content[0] = $content[0].Replace("VerylongOldColumnName", "ShortName")
# join the string array with newlines and convert that to an object with ConvertFrom-Csv
# add the columns to the object and export it using the semi-colon as delimiter
($content -join [Environment]::NewLine) | ConvertFrom-Csv |
Select-Object *, $columnsToAdd |
Export-Csv -Path $exportCSV -NoTypeInformation -Delimiter ";"

Replace One CSV Field With Another Through PowerShell

In PowerShell I'm importing a CSV SAMTemp2 which will have a field called SO. Sometimes SO will be populated with "NW" and in these cases I just want to pull the field called ProdProj from the same line and replace the data in SO with the data in ProdProj then export it the data in that condition.
$RepNW = Import-Csv $SAMTemp2
foreach($d in $data){
If($d.SO -eq "NW"){($d.SO).Replace($d.ProdProj)}}
$RepNW | Export-Csv $SAMTemp -NoTypeInformation
I don't get an error, but this doesn't seem to do anything, either. Can anyone assist me, please?
Update
Per Matt below, I tried:
$RepNW = Import-Csv $SAMTemp2
foreach($d in $RepNW){
If($d.SO -eq "NW"){$d.SO = ($d.SO).Replace($d.ProdProj)}}
$RepNW | Export-Csv $SAMTemp -NoTypeInformation
But I'm not seeing any change. Any assistance is appreciated.
As LotPings pointed out in this line foreach($d in $data){, you haven't defined $data and it seems that you mean it to be foreach($d in $RepNW){
Secondly, rather than using Replace() you can just set one property to be equal to the other.
Last, this probably easiest to do all in the pipeline with ForEach-Object
Import-Csv $SAMTemp2 | ForEach-Object {
If($_.SO -eq "NW"){
$_.SO = $_.ProdProj
}
$_
} | Export-Csv $SAMTemp -NoTypeInformation

run powershell command using csv as input

I have a csv that looks like
Name, email, address
Name, email, address
Name, email, address
I am wanting to run
New-Mailbox -Name "*Name*" -WindowsLiveID *email* -ImportLiveId
(where *x* is replaced by the value from the csv).
on each line in the csv file.
How can I do this?
$csv = Import-Csv c:\path\to\your.csv
foreach ($line in $csv) {
New-Mailbox -Name $line.Name -WindowsLiveID $line.Email -ImportLiveId
}
First line of csv has to be something like Name,Email,Address
If you cannot have the header in the CSV, you can also have:
$csv = Import-Csv c:\path\to\your.csv -Header #("Name","Email","Address")
-Header doesn't modify the csv file in any way.
import-csv .\file.csv -header ("first","second","third") | foreach{New-Mailbox -Name $_.first -WindowsLiveID $_.second -ImportLiveId}
This is some of the most useful information I have seen yet - it just made my job so much easier!!!
Combining Netapp commands:
get volumes from a controller, get snapshot schedule for said volumes, and export to a csv:
get-navol | Get-NaSnapshotSchedule | Export-Csv -path d:\something.csv
Import the csv reading in current values and assigning each column a label.
For each object, create a new schedule by RE-USING 4 of the 5 available columns/data fields
import-csv d:\something.csv -header ("label1","label2","label3","label4","label5") | foreach {Set-naSnapshotschedule $.label1 -Weeks $.label2 -Days $.label3 -Hours $.label4 -Whichhours "1,2,3,4,5"}
EXCELLENT STUFF!!!
Please note that the "Labels" should have an underscore - for whatever reason it isn't reflecting in the page so Dollar($)Underscore(_)Dot(.)Label