Proper way to export to CSV? - powershell

I had a quick questions regarding exporting to CSV.
How is it decided on what to export to a CSV? What do i mean by that? The below example is just reading off a list and querying the Systemversion off AD. Then, i am associating it with the proper version we go off by. Unfortunately, it does export it properly or sometimes at all. Im not sure if this is making any sense so pardon the ignorance on my part.
$CStrings = Get-Content "C:\users\ME\Desktop\Comps.txt"
$comps = Foreach($Computer in $CStrings){
$VS = Get-ADComputer -Identity:"CN=$Computer,OU=MY Computers,OU=LAB " -Properties:operatingSystemVersion | Select-Object -ExpandProperty operatingsystemversion
if($VS -like "21543"){
"$Computer", "Version 45.32"}
else {
if($VS -like "21544"){
"$Computer", "Version 45.33"}
else{
if($VS -like "21545"){
"$Computer", "Version 45.34"}
else{
"$Computer", "$VS"
}
}
}
}
Foreach($comp in $comps){
Write-Output $comp | Out-File C:\users\ME\Desktop\comps.csv
}
The result in the CSV comes out to just one version output, but nothing else. I tried "$computer - Versions 45.32" which joins it, and exports each computer but, its all in one column.
Before i get hounded at, i did check other forums including ms_docs lol would rather have someone dumb it down for me if possible.
Summary: Looking to keep the Computer Name, and Version in a separate Column.
Happy New Years BTW!(:

One of the easiest ways is to create a list of custom objects and then simply export that list to csv. Here is one way to do that with your current requirements.
$CStrings = Get-Content "C:\users\ME\Desktop\Comps.txt"
$comps = Foreach($Computer in $CStrings)
{
$VS = Get-ADComputer -Identity:"CN=$Computer,OU=MY Computers,OU=LAB " -Properties operatingSystemVersion | Select-Object -ExpandProperty operatingsystemversion
$VS = switch -regex ($VS)
{
'21543' {"Version 45.32"}
'21544' {"Version 45.33"}
'21545' {"Version 45.34"}
default {$_}
}
[PSCustomObject]#{
ComputerName = $Computer
OSVersion = $VS
}
}
$comps | Export-Csv -Path C:\users\ME\Desktop\comps.csv -NoTypeInformation

Related

PowerShell Team Owners and Team names from a List of GroupIDs

Good afternoon all,
Hoping someone can help with a broad problem I'm having with efficiency in the follow PowerShell query.
In brief, the idea behind the following code is:
**1)**From a predefined list of MS Teams GroupID's (260+ in total), all held in 'List.txt' file
**2)**Run through each of the MS Teams GroupIDs and extract the Team Name and the Team Owners.
**3)**Export the data to a .csv in the format:
Team Name:
Team Owners:
STH-TeamName
Owner1#nhs.net, Owner2#nhs.net
STH-AnotherTeam
Owner3#nhs.net, Owner4#nhs.net
The code, whilst cobbled together from a number of sources does appear to be working in essence. However, "Write-Host" steps 2 and 5 appear to be returning the DisplayName very slowly indeed. I am a complete beginner with PowerShell queries so the syntax and general efficiencies are not in my favour at the moment.
Import-Module Microsoftteams
Connect-MicrosoftTeams
Write-Host 1 Opening Lists File...
$TeamList = Get-content C:\users\USERNAME\TeamOwnerReport\List.txt
Write-Host 2 Stepping Through Team GroupIDs...
$TeamIDs = Foreach ($Item in $TeamList)
{get-team | where-object {$_.GroupID -eq $Item}
}
Write-Host 3 Stepping AllTeams are TEAMIDs...
$AllTeams = $TeamIDs.GroupID
Write-Host 4 Doing Array bit...
$TeamReport = #()
Foreach ($Team in $AllTeams)
{
Write-Host 5 Doing teamName bit...
$TeamName = (Get-Team | Where-object {$_.GroupID -eq $Team}).DisplayName
Write-Host 6 Doing teamOwner bit...
$TeamOwner = (Get-TeamUser -GroupId $Team | Where-object {$_.Role -eq 'Owner'}).User
Write-Host 7 Doing teamReport bit...
$TeamReport = $TeamReport + [PSCustomObject]#{TeamName = $TeamName;TeamOwners = $TeamOwner -join ', ';}
}
$Path = 'C:\users\USERNAME\TeamOwnerReport'
$TeamReport | Export-csv ("$Path\TeamsReport_" + (get-date -format "dd-MM-yy") +".csv") -NoTypeInformation
Any support here would be greatly appreciated - still very much learning the ropes of PowerShell and of Stack Overflow.
Looking at the error Get-Team : A parameter cannot be found that matches parameter name 'GroupId'., you may have an older version of the MicrosoftTeamsPowerShell module, but then you could try this instead:
Import-Module Microsoftteams
Connect-MicrosoftTeams
# read the array of team group id's, remove empty or whitespace only lines
$TeamList = Get-Content -Path 'C:\users\USERNAME\TeamOwnerReport\List.txt' | Where-Object { $_ -match '\S' }
$TeamReport = Get-Team | Where-object {$TeamList -contains $_.GroupId} | ForEach-Object {
$owners= Get-TeamUser -GroupId $_.GroupId -Role Owner
[PsCustomObject]#{
TeamName = $_.DisplayName
TeamOwners = ($owners.User -join ', ')
}
}
# save the report as CSV file
$outputPath = 'C:\users\USERNAME\TeamOwnerReport_{0:dd-MM-yyyy}.csv' -f (Get-Date)
$TeamReport | Export-Csv -Path $outputPath -NoTypeInformation
You're calling Get-Team which, without parameters, will return all teams. This means you're getting the full Team list every iteration. You could improve that by changing the query to just a parameter.
Write-Host 2 Stepping Through Team GroupIDs...
$TeamIDs = Foreach ($Item in $TeamList)
{get-team -GroupId $Item
}
The same thing could be improved on step 5
Foreach ($Team in $AllTeams)
{
Write-Host 5 Doing teamName bit...
$TeamName = (Get-Team -GroupId $Team).DisplayName
...
But these changes are only to keep your steps the same. This whole script could definitely be improved because you should only need to call Get-Team one time per team.

Working with a list of AD 'displayNames' in Powershell. How to indicate which users were not found?

I have written enough PS code to go through a list of displayNames (e.g "John Smith", "Taylor Hanson" - all stored on seperate lines of a txt file) to spit back enough data into another text file that can be used for mailmerge etc. Convincing thousands of employees to simply update Windows is like breaking stones! It has to be automatted to some degree...
Here is the code... the functions that let the user open a specific text file and later save are out of view...
$displayname = #()
$names = get-content $FileIN
foreach ($name in $names) {
$displaynamedetails = Get-ADUser -filter { DisplayName -eq $name } | Select Name, GivenName, Surname, UserPrincipalName
$displayname += $displaynamedetails
}
$displayname | Export-Csv -NoTypeInformation -path $fileOUT -Encoding UTF8
From time to time, a name might be spelled incorrectly in the list, or the employee may have left the organisation.
Is there any way that a statement such as 'Not Found' can be written to the specific line of the text file if an error is ever made (so that an easy side-by-side comparison of the two files can be made?
For most of the other solutions I've tried to find, the answers are based around the samAccoutName or merging the first and last names together. Here, i am specifically interested in displaynames.
Thanks
You can give this a try, since -Filter or -LDAPFilter don't throw any exception whenever an object couldn't be found (unless you're feeding a null value) you can add an if condition to check if the variable where the AD User object is going to be stored is not null and if it is you can add this "not found" user into a different array.
$domain = (Get-ADRootDSE).DefaultNamingContext
$names = Get-Content $FileIN
$refNotFound = [System.Collections.Generic.List[string]]::new()
$displaynamedetails = foreach($name in $names)
{
if($aduser = Get-ADUser -LDAPFilter "(DisplayName=$name)")
{
$aduser
continue
}
$refNotFound.Add(
"Cannot find an object with DisplayName: '$name' under: $domain"
)
}
$displaynamedetails | Select-Object Name, GivenName, Surname, UserPrincipalName |
Export-Csv -NoTypeInformation -path $fileOUT -Encoding UTF8
$refNotFound # => Here are the users that couldn't be found
Side note, consider stop using $displayname = #() and += for well known reasons.
As for AD Cmdlets, using scriptblock based filtering (-Filter {...}) is not supported and even though it can work, it can also bring you problems in the future.

Powershell : import-csv with if condition

I have a csv as below :
Server OS HotFixID
Comp1 Win12 KB452
Comp1 Win12 KB453
svrname3 Win8 KB134
I have written below script for checking OS and if its matched then it should check if the server is having the same HotfixID or not.
$file = Import-Csv .\Desktop\hotfix.csv
if($Win12 = $file | where {$_.os -eq "Win12"} | select Source, HotFixID)
{
Get-HotFix -ComputerName ($Win12.Source) -Id ($Win12.HotFixID)
}
else
{
$Win8 = $file | where {$_.os -eq "Win8"} | select Source, HotFixID
Get-HotFix -ComputerName ($Win8.Source) -Id ($Win8.HotFixID)
}
problem is with output.. I have 2 Win12 server in csv, but I am getting 4 output 2 as duplicate.. I am able to understand that here one nested loop is running but unable to rectify it. Please!! let me know how can we fix this issue.
Assumed I understood your task correct you should iterate over all elements in your CSV file and check each item indivually.
$CSVList = Import-Csv .\Desktop\hotfix.csv
foreach ($CSVItem in $CSVList) {
if ($CSVItem.os -eq 'Win12' -or $CSVItem.os -eq 'Win8') {
if (Test-Connection -ComputerName $CSVItem.Source) {
Get-HotFix -ComputerName $CSVItem.Source -Id $CSVItem.HotFixID
}
}
}
You have multiple entries with Win12 so $Win12 is an array of PSCustomObject. When you use ($Win12.Source) it will output an array of all sources. (Comp1,Comp1). Get-Hotfix accepts this array and tests $Win12.HotFixID (which is an array, too) with each of the sources. Thats the reason you get each Item the Number of times they apear in the CSV. To avoid this, process each line in the CSV on their own. If you want to keep your structure, your code would look like this:
if($Win12 = $file | where {$_.os -eq "Win12"} | select Source, HotFixID)
{
$Win12 | foreach {
Get-HotFix -ComputerName ($_.Source) -Id ($_.HotFixID)
}
}
else
{
$Win8 = $file | where {$_.os -eq "Win8"} | select Source, HotFixID
$Win8 | foreach {
Get-HotFix -ComputerName ($_.Source) -Id ($_.HotFixID)
}
}
However the Code of Olaf is cleaner and does the same.

Export Powershell output to CSV

I have the following code intended to take a list of user names and output a CSV report of username - GroupMembership. At the command line the output looks great as i get "name" on the left and recursive "group memberships" on the right (see pic http://i.stack.imgur.com/zLxUR.jpg for command line output, sorry can't post imbedded Pics yet)
I would like to have the output written to a CSV file with the same format, namely Username in one column and GroupMemberships in the second column.. Original code from: http://thesurlyadmin.com/2013/03/21/get-a-users-group-memberships/ with a few small changes.
Param (
[Parameter(Mandatory=$true,ValueFromPipeLine=$true)]
[Alias("ID","Users")]
[string[]]$User
)
Begin {
Try { Import-Module ActiveDirectory -ErrorAction Stop }
Catch { Write-Host "Unable to load Active Directory module, is RSAT installed?"; Break }
}
Process {
ForEach ($U in $User)
{ $UN = Get-ADUser $U -Properties MemberOf
$Groups = ForEach ($Group in ($UN.MemberOf))
{ (Get-ADGroup $Group).Name
}
$Groups = $Groups | Sort
ForEach ($Group in $Groups)
{ New-Object PSObject -Property #{
Name = $UN.Name
Group = $Group
}
}
}
}
I tried using this "$PSObject | Export-CSV C:\Scripts\GroupMembershipList.csv" but it only writes the first line to the CSV and nothing after that.
Nate,
In Powershell v3.0, the Export-CSV cmdlet introduced the -Append parameter.
Reference: http://technet.microsoft.com/en-us/library/hh849932.aspx
Not knowing the version of Powershell you are using, this may require an update on your side to make use of the new functionality.
In my own cases, I generally see the opposite issue if I forget to -Append to my CSV; I will only end up with the LAST entry as opposed to just the first.
I won't claim this to be your fix, but might be worth a shot...
Example: $PSObject | Export-CSV C:\Scripts\GroupMembershipList.csv -Append

Convert GUID string to octetBytes using PowerShell

I have a powershell script which outputs all Exchange 2003 mailboxes by size.
$computers = "vexch01","vexch02"
foreach ($computer in $computers) {
Get-Wmiobject -namespace root\MicrosoftExchangeV2 -class Exchange_Mailbox -computer $computer | sort-object -desc Size | select-object MailboxDisplayName,StoreName,#{Name="Size/Mb";Expression={[math]::round(($_.Size / 1024),2)}}, MailboxGUID | Export-Csv -notype -Path $computer.csv
}
Currently this outputs the MailboxGUID as a string type GUID (e.g. {21EC2020-3AEA-1069-A2DD-08002B30309D}). I want to look up users in AD by this, but AD stores them in octetBytes format.
I have found some powershell functions which will do the conversion but only when the curly braces are removed. The Guid.ToString method should supply this, but I can't get it to work in the above.
However, if I could figure out how to do that, the Guid.ToByteArray method might get me even closer.
Has anyone cracked this?
Update: the answers so far helped me write a function that converts the mailboxguid into the correct format for searching via LDAP. However, I now cannot get this working in the script. This is my updated script:
function ConvertGuidToLdapSearchString(
[parameter(mandatory=$true, position=0)]$Guid
)
{
$guid_object = [System.Guid]$Guid
($guid_object.ToByteArray() | foreach { '\' + $_.ToString('x2') }) -join ''
}
# Gets data through WMI from specified Exchange mailbox servers
$servers = "vexch01","vexch02"
foreach ($server in $servers) {
Get-Wmiobject -namespace root\MicrosoftExchangeV2 -class Exchange_Mailbox -computer $computer | sort-object -desc Size | select-object MailboxDisplayName,StoreName,#{Name="Size/Mb";Expression={[math]::round(($_.Size / 1024),2)}}, #{Name="LDAP Guid";Expression={ConvertGuidToLdapSearchString(MailboxGUID)}} | Export-Csv -notype -Path $server.csv
}
I'm not sure why using the function in the select-object with #{Name="LDAP Guid";Expression={ConvertGuidToLdapSearchString(MailboxGUID)}} doesn't work.
Is there another way of using this function in select-object that will give the string?
In conjunction with Andy Schneider's answer, you may find this function useful:
function Convert-GuidToLdapSearchString(
[parameter(mandatory=$true, position=0)][guid]$Guid
)
{
($Guid.ToByteArray() | foreach { '\' + $_.ToString('x2') }) -join ''
}
(I thought I had a more clever way to do this by adding a ScriptProperty to System.Guid, but I seem to have learned that you can't effectively add members to structs.)
I'm not sure I understand what you are trying to accomplish based on your comment, but I think you may have just left out a $_. Here is a somewhat contrived example that creates an object with a property that is a GUID, then uses select and Convert-GuidToLdapSearchString to convert the format. I hope it helps.
$o = New-Object PSObject -Property #{ GUID = $([Guid]::NewGuid()) }
$o
$o | select #{ Name='SearchString'; Expression={ Convert-GuidToLdapSearchString $_.GUID } }
This is not at all how I had imagined the function being used. I expected you would use it to create an LDAP search clause such as:
$searchString = Convert-GuidToLdapSearchString '{9e76c48b-e764-4f0c-8857-77659108a41e}'
$searcher = [adsisearcher]"(msExchMailboxGuid=$searchString)"
$searcher.FindAll()
Are you casting the string to a GUID ?
$guid = [System.Guid]"{21EC2020-3AEA-1069-A2DD-08002B30309D}"
$guid.ToString()
$guid.ToByteArray()