I'm having a problem when outputting my foreach loop to a csv file.
My Groups are set like this:
$Groups = "Group1", "Group2", "Group3"
My code is:
$results = ForEach ($Group in $Groups) {
$memberof = get-adgroup $Group | select -expandproperty distinguishedname
Write-Output $Group
Get-ADObject -Filter 'memberof -eq $memberof -and (ObjectClass -eq "user" -or ObjectClass -eq "contact")' -properties * | select name, Objectclass, mail
Write-Output ""
Write-Output ""
}
$results | Export-csv Contacts.csv -NoTypeInformation
The problem seems to be coming from the Write-Output lines but I have no clue why. When I run my code without writing to a csv file, I get the expected result, something like:
NameOfGroup1
name Objectclass mail
---- ----------- ----
User1 user User1#mail.com
User2 user User2#mail.com
#Spaces caused by write-output ""
NameOfGroup2
User1 user User1#mail.com
Contact1 contact Contact1#externalmail.com
Then again when I run my code to write to csv file and have the write-output $Group commented out I get a similar result.
But if I run my full code from the top of this page including the write-output $Group, it comes out like this:
I've figured out what these results represent but I haven't got clue why they do print out like this.
Eseentially the numbers refer to the length of the group name, so the first 17 would be a 17 character group name, and then the number of lines below is equal to the number of contacts and users that are inside that group. The 2 zeros at the end of each group are the length of the write-output "" lines.
What is causing this behavior?
The following code will closely output what you are attempting.
$results = ForEach ($Group in $Groups) {
$memberof = get-adgroup $Group | select -expandproperty distinguishedname
Get-ADUser -Filter "memberof -eq '$memberof' -and (ObjectClass -eq 'user' -or ObjectClass -eq 'contact')" -properties name,ObjectClass,Mail | Select-Object #{n='Group';e={$Group}},name, Objectclass, mail
[pscustomobject]"" | Select-Object Group,Name,ObjectClass,Mail
[pscustomobject]"" | Select-Object Group,Name,ObjectClass,Mail
}
$results | Export-csv Contacts.csv -NoTypeInformation
Explanation:
Export-Csv converts an object or array of objects with properties into a CSV file. You can see the same result in the console with ConvertTo-Csv. Properties are converted into columns and property values are placed under their associated columns. When you output a string as in Write-Output $Group, it has a property of Length. To fix this, you need to add $Group as a calculated property in your Select-Object. If you want to do blank lines in your CSV, then you should output another object with all of the property values as ''.
When you mix objects in your PowerShell outputs, you can see unexpected results. Your Get-ADObject outputs a custom object. Your Write-Output lines output a string. Those two object types do not share properties. So you only see the properties for the first object in your array, which is a string. If you put all of the Write-Output statements at the end of your loop, you will see more properties in your CSV. See below for an example that just by reversing the order of processed objects, you get a different result.
$str = "string"
$obj = [pscustomobject]#{property1 = "value1"; property2 = "value2"}
$str,$obj | convertto-csv -notype
"Length"
"6"
$obj,$str | convertto-csv -notype
"property1","property2"
"value1","value2"
,
Notice the properties available to the custom object $obj and the string $str.
$obj | get-member -Type Properties
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
property1 NoteProperty string property1=value1
property2 NoteProperty string property2=value2
$str | get-member -Type Properties
TypeName: System.String
Name MemberType Definition
---- ---------- ----------
Length Property int Length {get;}
Related
I was hoping someone can help me out. I am trying to get the date a license was assigned to a user and export it to a new csv. The import csv contains the UserPrincipalName. I was able to narrow down to only show which license I want but having the UPN show next to the license/date would complete this script. Thanks in advance
$getusers = Import-csv -Path 'C:\test\userlist.csv'
foreach ($user in $getusers) {
(Get-AzureADUser -searchstring $User.UserPrincipalName).assignedplans | where {$_.Service -eq 'MicrosoftOffice'} | Select-Object Service,AssignedTimeStamp |
Export-CSV -Path "C:\test\userlist-export.csv" -notypeinformation
}
I would do it this way, first querying the user and storing it in a variable and then filter the AssignedPlans where Service = MicrosoftOffice. To construct the objects you can use [pscustomobject]. Worth noting, the call to Export-Csv should be the last statement in your pipeline (it shouldn't be inside the loop), otherwise you would be replacing the Csv with a new value on each loop iteration instead of appending data.
Import-Csv -Path 'C:\test\userlist.csv' | ForEach-Object {
$azUser = Get-AzureADUser -ObjectId $_.UserPrincipalName
foreach($plan in $azUser.AssignedPlans) {
if($plan.Service -eq 'MicrosoftOffice') {
[pscustomobject]#{
UserPrincipalName = $azUser.UserPrincipalName
Service = $plan.Service
AssignedTimeStamp = $plan.AssignedTimeStamp
}
}
}
} | Export-Csv "C:\test\userlist-export.csv" -NoTypeInformation
I have this below script
Get-AzureADAuditDirectoryLogs | more
It will provide the output as below as expected;
enter image description here
But when we try to export this to a .csv I am not getting the output properly
Get-AzureADAuditDirectoryLogs | more | Export-Csv C:\temp\securitylogs.csv -NoType
If you see TargetResources or Additionaldetails columns its capturing something else which is not in the actual output. Can someone please tell us what we are missing here in Export-csv command?
enter image description here
Those properties cannot be accessed directly. I have given a reference code to display those properties.
$users = #()
$logs = Get-AzureADAuditDirectoryLogs
foreach ($log in $logs) {
$obj = [PSCustomObject]#{
ActivityDateTime = $log.ActivityDateTime
UserPrincipalName = $log.TargetResources.UserPrincipalName
Category = $log.Category
}
$users += $obj
}
$users | Export-Csv C:\Output\SomeFilename.csv -Force -NoTypeInformation
And also, you have to filter the logs using unique properties or matching some condition(like date), otherwise your output file will have multiple varieties of data.
sample:
Get-AzureADAuditDirectoryLogs -All $true -Filter "activityDateTime le 2021-11-29 and Category eq 'UserManagement' and OperationType eq 'Update' and ActivityDisplayName eq 'Update user'"
Hi both properties TargetResources and Additionaldetails have multiple values, so you need to be specific when extracting multiple values for the property. I'm not sure what the delimiter for those fields are.. try the following.
Get-AzureADAuditDirectoryLogs | more | Select activityDateTime, LoggedByService, `
OperationType, InitiatedBy,`
#{name="TargetResources";expression={$_.TargetResources -join ";"}},`
#{name="Additionaldetails";expression={$_.Additionaldetails -join ";"}} |`
Export-csv -NoTypeInformation C:\temp\securitylogs.csv -NoType
I previously had asked a question regarding adding together files and folders with a common name and having them summed up with a total size (Sum of file folder size based on file/folder name). This was successfully answered with the PS script below:
$root = 'C:\DBFolder'
Get-ChildItem "$root\*.mdf" | Select-Object -Expand BaseName |
ForEach-Object {
New-Object -Type PSObject -Property #{
Database = $_
Size = (Get-ChildItem "$root\$_*\*" -Recurse |
Measure-Object Length -Sum |
Select-Object -Expand Sum ) / 1GB
}
}
This now leaves me with a list that is ordered by the 'Database' Property by default. I have attempted to use a Sort-Object suffix to use the 'Size' property with no joy. I have also attempted to use Export-Csv with confounding results.
Ideally, if I could pass the results of this script to Excel/CSV so I can rinse/repeat across multiple SQL Servers and collate the data and sort within Excel, I would be laughing all the way to the small dark corner of the office where I can sleep.
Just for clarity, the output is looking along the lines of this:
Database Size
-------- ----
DBName1 2.5876876
DBName2 4.7657657
DBName3 3.5676578
Ok, it was one pipe character that I had missed when using the Export-csv function. This resolved my problem.
$root = 'C:\DB\Databases'
Get-ChildItem "$root\*.mdf" | Select-Object -Expand BaseName |
ForEach-Object {
New-Object -Type PSObject -Property #{
Database = $_
Size = (Get-ChildItem "$root\$_*\*" -Recurse |
Measure-Object Length -Sum |
Select-Object -Expand Sum ) / 1GB
}
} | Export-Csv 'C:\Test\test.csv'
I have a two-dimensional array of property names and values, which I need to add to a PowerShell object.
I have NO issues creating and displaying an object like this using New-Object and Add-Member:
$obj = New-Object PSObject
$obj | Add-Member NoteProperty IName($fiel.IName)
$obj | Add-Member NoteProperty SName($fiel.SName)
$obj | Add-Member NoteProperty Taggy($fiel.Taggy)
$obj | Add-Member NoteProperty Title($fiel.Title)
Write-Output $obj
But when I try something like this:
for ($k=0; $k -lt $fieldsArray.Count; $k++)
{
$itemobj | Add-Member –MemberType NoteProperty –Name $fieldsArray[$k].InternalName –Value $itemki[$j][$fieldsArray[$k].InternalName]
#Write-Host $k
#Write-Host $fieldsArray[$k].InternalName.ToString()
#Write-Host $itemki[$j][$fieldsArray[$k].InternalName]
}
Write-Output $itemobj
The Write-Output $itemobj will return only one property member that should be added without any neat column names.
The commented out parts were added for testing purposes and return correct values for all items.
I also tried
$itemobj | Add-Member NoteProperty $fieldsArray[$k].InternalName.ToString()($fieldsArray[$k].InternalName)
without any improvement.
Why are the other property members not added?
I have the data I need. If I write:
for ($k=0; $k -lt $fieldsArray.Count; $k++)
{
Write-Host $k
Write-Host $fieldsArray[$k].InternalName.ToString()
Write-Host $itemki[$j][$fieldsArray[$k].InternalName]
}
I get:
0 ID 1
1 ContentTypeId 0x0108007345CD807822EA4E85691E5C642F3A27
2 ContentType
3 Title Task0
4 Modified 11/24/2014 12:29:30 PM
And these are exactly the values that I expect and want. The problem is adding them as properties to an object. I think I cannot have a variable as a NotePropertyName, but it's a wild guess based on the results I am getting.
Some of the values in $itemki[$j][$fieldsArray[$k].InternalName] are empty - could it be it?
Forget all the arrays. They were just for the context:
Write-Host $fieldsArray[$k].InternalName.ToString() # Writes out the correct value
Write-Host $itemki[$j][$fieldsArray[$k].InternalName] # writes out the correct value
$itemobj | Add-Member NoteProperty $fieldsArray[$k].InternalName.ToString()($fieldsArray[$k].InternalName) # The values/property are not added
The question is: WHY NOT? Are there any restrictions in Add-Member on passing values as variables? Empty values?
I am still not sure completely, but if you are just focusing on the Add-Member then consider the following.
$fieldsarray = "ID", "ContentTypeID", "ContentType", "Title", "Modified"
$itemki = "1", "0x0108007345CD807822EA4E85691E5C642F3A27", "", "Task0", "11/24/2014 12:29:30 PM"
$itemobj = New-Object pscustomobject
for($k=0;$k -lt $fieldsArray.Count ; $k++)
{
$itemobj | Add-Member NoteProperty $fieldsarray[$k] $itemki[$k]
}
$itemobj
Notice the empty string entry in the array $itemki. This would generate the output.
ID : 1
ContentTypeID : 0x0108007345CD807822EA4E85691E5C642F3A27
ContentType :
Title : Task0
Modified : 11/24/2014 12:29:30 PM
Change the "" to an empty element: "1","0x0108007345CD807822EA4E85691E5C642F3A27",,"Task0","11/24/2014 12:29:30 PM", and you get this output:
ID : 1
ContentTypeID : 0x0108007345CD807822EA4E85691E5C642F3A27
ContentType : {Task0}
Title : 11/24/2014 12:29:30 PM
Modified :
Which is wrong. Then if you change the empty element to $null your output looks much like the first:
ID : 1
ContentTypeID : 0x0108007345CD807822EA4E85691E5C642F3A27
ContentType :
Title : Task0
Modified : 11/24/2014 12:29:30 PM
Concerning your output
You say you only get the last element when you do $itemobj outside the loop. What does $itemobj look like after each pass? for(;;){} loops don't send data to the output stream in the same way that a ForEach-Object{} would which is worth mentioning.
You could use a hash table instead of two arrays. It's very easy to create an object from a hash table. Here's an example:
$hash = #{
ID = '1'
ContentTypeID = '0x0108007345CD807822EA4E85691E5C642F3A27'
ContentType = ''
Title = 'Task0'
Modified = '11/24/2014 12:29:30 PM'
}
$Object = New-Object PSObject -Property $hash
Don't do a For loop, do a ForEach-Object loop. Something like:
$Object = New-Object PSObject
$Array | ForEach{
Add-Member -InputObject $Object -NotePropertyName $_[0] -NotePropertyValue $_[1]
}
That should do what you're looking for I think.
I am making a script to query active directory via powershell and pull all computers that contain a username in the description field, then filter that list with only computers last logged in the past 14 days.
This is what I have so far:
$queryAD = Get-ADComputer -SearchBase 'OU=West Division,DC=cable,DC=comcast,DC=com' -Properties Name, Description -Filter {(Name -like "WA*") -and (Description -like $wildCard)} | Select-Object Name, Description
$lastLogon = $queryAD | Select-Object -ExpandProperty Description | %{$_.replace(("$NTname" + ";"),"").split(";")[0]} | %{get-date $_ -format d}
I'd like to add the list generated from $lastLogon to $queryAD, right now $queryAD is returning two columns with headers Name and Description. I need a third header added called Last Logon Date and contain the list in $lastLogon. Please advise.
You could assign the values to an array of objects to make your output cleaner (if this method is providing you the data you want) like so:
$queryAD = Get-ADComputer -SearchBase 'OU=West Division,DC=cable,DC=comcast,DC=com' -Properties Name, Description -Filter {(Name -like "WA*") -and (Description -like $wildCard)} | Select-Object Name, Description
$computer_list = #()
foreach($computer in $queryAD) {
$computer_info = New-Object PSObject -Property #{
Name = $computer.Name
Description = $computer.Description
LastLogonDate = $computer | Select-Object -ExpandProperty Description | %{$_.replace(("$NTname" + ";"),"").split(";")[0]} | %{get-date $_ -format d}
}
$computer_list += $computer_info
}
in which case $computer_list will contain all of the info you're gathering in tidy objects.
...but this method seems overcomplicated. Look into this blog entry by Matt Vogt for a better way to query for old machines in AD.