Complicated Transposed Table With Powershell or Excel - excel

I'm trying to manipulate the layout of a file-share report. Basically what the layout looks like now is this:
Path,Username/Group
path1,user1
path2,user1
path3,user1
path1,user2
path3,group1
path2,group2
It's showing folder paths and what users have access to them.
I'd like to change this to the following layout:
user1,user2,group1,group2
path1,path1,path3,path2
path2
path3
Whether it be importing the data into excel and manipulating it in excel or using powershell script to manipulate the data, I'm not quite sure what to do to get it the way I want.
I've tried importing this text file into excel and trying to transpose but I can't figure out how to show a list of file paths for each user. I've messed around in Access with it as well, but I'm not experienced enough in access to get it to display properly. I tried a few things in powershell but it amounted to a bunch of text documents named after the users with a list of file paths in each document. Not quite as neat as I'd like it unfortunately.

PowerShell could do it. Assuming the data is what you show in the question it looks like a CSV file. You could do:
$DataIn = Import-CSV $file
$HTOut = #{}
$File | Group 'Username/Group' | ForEach{$HTOut.add($_.Name,$_.Group.Path)}
New-Object PSObject -Prop $HTOut | Export-CSV $file
I thought about it, and this doesn't do exactly what I had said, it would make one object with a property for each user/group, and that property's value would be all of the paths for that person/group. What you really want is X objects that iterate through all of those paths. For that the first 3 lines stay the same, except that we capture the number of paths for the user/group with the most paths. Then we make that many objects iterating through paths for each user.
$DataIn = Import-CSV $file
$HTOut = #{}
$MaxPaths = $DataIn | Group 'Username/Group' | ForEach{$HTOut.add($_.Name,$_.Group.Path);$_} |% Count |Sort -Descend |Select -first 1
$Results = For($i=0;$i -le $MaxPaths;$i++){
$Record = New-Object PSObject
$HTOut.Keys|ForEach{Add-Member -InputObject $Record -NotePropertyName $_ -NotePropertyValue $(([array]$HTOut["$_"])[$i])}
$Record
}

Related

Remove random items from `Get-ChildItem` output

I'd like to ask a question about removing lines from a variable that has strings obtained from get-childitem.
Right now, I'm extracing lines indicating files with this :
$FolderToOpen= Get-ChildItem -Path $SrcDir -Filter *.$ExtLst
That gets the files from a specified folder and specified extensions
Then, I get 4 random lines from that variable
$FourFiles = $FolderToOpen | Get-Random -Count 4
But then, here's my problem, I'd like to delete those 4 lines from the first variable. However I can't get to do that, doing
$FolderToOpen -replace "$FourFiles",""
for exemple doesn't do anything.
Could someone help me please?
Thank you in advance :)
The simplest way of doing that is to take advantage of Compare-Object which compares arrays element-wise:
Compare-Object -ReferenceObject $FolderToOpen -DifferenceObject $FourFiles -PassThru
The Get-ChildItem cmdlet returns an array of objects by default and as such is of a fixed size (see Everything you wanted to know about arrays for further information on this)
If we cast the results of the Get-ChildItem call to a list first, we'll then be able to add and remove items (without the need to create new arrays to hold the updated data)
Updated code below should give you what you're looking for:
# Call Get-ChildItem but store the results as a list of objects
# rather than a standard array of objects
[Collections.Generic.List[Object]]$FolderToOpen= Get-ChildItem -Path $SrcDir -Filter *.$ExtLst
# Get your random selection of 4 files
$FourFiles = $FolderToOpen | Get-Random -Count 4
# Remove them from the list
$FolderToOpen.RemoveAll({ param($folder) $folder -in $FourFiles })
The RemoveAll function above will return the total number of entries removed from the list (in this example, 4).
The problem with your approach is that PowerShell prefers operating on objects instead of strings. The output of Get-ChildItem is a stream of objects, what can be proven by piping it to Get-Member:
PS> dir | Get-Member
TypeName: System.IO.DirectoryInfo
...
TypeName: System.IO.FileInfo
...
Since you are interested in particularly filtering strings, you can cast all the objects to strings beforehand and then perform filtering:
$FourFiles = $FolderToOpen | Get-Random -Count 4 | % {$_.ToString()}
$FolderToOpen | Where {$_.ToString() -inotin $FourFiles}
Note that $FolderToOpen is a stream of strings, instead of one big string like you would have in Bash. Therefore I used Where to filter the entries.
A solution that uses only Get-Random. Basically use Get-Random to select the files to include, instead of exclude. Then you don't need to remove anything. The output of Get-Random is already what you want.
$FolderToOpen = #(Get-ChildItem -Path $SrcDir -Filter *.$ExtLst)
$includeCount = $FolderToOpen.Count - 4
$FolderToOpen = if( $includeCount -gt 0 ) {
$FolderToOpen | Get-Random -Count $includeCount
}
To make sure we always get an array out of Get-ChildItem, use the array sub-expression operator #(). Otherwise you'd get a single object, if Get-ChildItem outputs only a single file.
The if statement is required to make sure we don't pass an invalid -Count argument to Get-Random, when Get-ChildItems returns four or less files.
Note that this outputs the files in random order, but you could easily sort them by path again:
$FolderToOpen | Get-Random -Count $includeCount | Sort-Object FullName

Powershell script to pull remote pc serial number then match against an xlsx file then match against another and output only needed info

hey im trying to build a powershell script that will pull a serial number from a remote pc and then match that against an xlsx file which would then match a column against another xlsx file i have gotten to the point where i can pull the remote sn and have everything put in to a csv output but i am having issues matching the data then filtering based on the match and then outputting only what i need im new to scripting so im pretty sure its more my lack of experiance than anything else this is my code so far
$computers = Get-Content c:\script\computerlist.txt
Get-wmiobject Win32_Bios -ComputerName $computers | Select-Object __SERVER, SerialNumber| Format-Table |out-file C:\script\computerinfo.txt
$computerinfo = Import-Excel C:\script\compDB.xlsx
$userinfo = Import-Excel C:\script\userDB.xlsx
$Computerinfo[2].SERIAL -eq
$Computerinfo[2].DATE_ADDED
$Computerinfo[2].OS
$Computerinfo[2].MODEL
$Computerinfo[2].USER
$userinfo[2].NAME_FIRST
$userinfo[2].NAME_LAST
$userinfo[2].NT_USERID
''
'Computer Info'
'----------'
$computerinfo ,$userinfo | Format-Table - | Out-File c:\script\computerinfo.csv
First you need to save the wmi information to a variable:
$WMIinfo = Get-wmiobject Win32_Bios -ComputerName $computers | Select-Object __SERVER, SerialNumber
Then you would need to loop through the spreadsheet and compare to data in Computer spreadsheet. If it matches loop through user spreadsheet for match:
foreach ($CompEntry in $Computerinfo) {
if ($WMIinfo.serial -eq $CompEntry.serial) {
foreach ($UserEntry in $userinfo) {
if ($UserEntry.NT_USERID -eq $CompEntry.USER) {
#output information you want here
}
}
}
I'll try to help you get there.
I created an Excel file called Serial.xslx. Here's what it looks like
SerialNumber DeployedTo
212 Ham
4M24N32 Stephen
I then import this as $list.
$list = import-excel C:\temp\serial.xlsx
Next,to get the Win32_Bios info, so I can grab the SerialNumber property.
$bios = get-WmiObject Win32_Bios
Finally, I'll filter through the $list (which contains the Excel file), and find a row which has a SerialNumber that matches this computers serial number. If I find a matching one, then I grab the .DeployedTo value for that record.
$user = $list | Where SerialNumber -eq $bios.SerialNumber | Select -ExpandProperty DeployedTo
All that remains is to demonstrate that it works.
"the computer with serial $($bios.SerialNumber) is deployed to $user"
>the computer with serial 4M24N32 is deployed to Stephen
Now, you've got two separate excel files, so I would either manually join them into one, or repeat this same basic approach.

Removing text in a string between two characters using Powershell

I have a powershell script that runs and collects information and puts it in a .csv file. A sample of the information looks like what is listed below, with each line starting with a unique server name followed by a random unique identifier in contained a pair of ( ).
"GDR01W01SQ004 (e785754f-eeb1)","1","4","63","NY-TER-PLN-P-5N"
"GDR01L02D001 (4b889a4d-d281)","4","12","129","CO-FDP-STE-NP-5N"
I have a second powershell script that runs and takes this .csv file and its information and formats it into a report with a header and proper spacing.
Can someone please assist me with removing the text in between the ( ) as well as the ( )?
I would like the entries for each line to look like the following:
"GDR01W01SQ004","1","4","63","NY-TER-PLN-P-5N"
Thank you very much in advance!
Here is the script I have been using.
####################PowerCLI Check####################
# Verify whether the PowerCLI module is loaded, if not load it.
if ( (Get-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue) -eq $null )
{
Add-PsSnapin VMware.VimAutomation.Core -ErrorAction Stop
}
################### Run Time Section ####################
#This script should be run from a server that has DNS records for all entries in vcenters.txt
$file = get-Content c:\reports\vcenter\vcenters.txt
foreach ( $server in $file) {
Connect-VIserver -Server $server
Get-VM | select Name, NumCpu, MemoryGB, ProvisionedSpaceGB, Notes | Out-Null
}
# Command for Custom Annotations.
Get-VM | select Name, NumCpu, MemoryGB, ProvisionedSpaceGB, Notes -expandproperty customfields | Export-Csv -path “c:\reports\vcenter\vcenall.csv” -NoTypeInformation
# Takes vcenall.csv and sorts only the Name and Notes columns and selects all but the custom fields. Capacity Reporting script caprep.ps1 runs against this csv.
Import-csv c:\reports\vcenter\vcenall.csv | Sort-Object Notes, Name | Select-Object Name, NumCpu, MemoryGB, ProvisionedSpaceGB, Notes |Export-csv capacity.csv -NoTypeInformation
#Used to remove domain from server name
(Get-Content capacity.csv) | ForEach-Object { $_ -replace ".domain.local", "" } | Set-Content capacity.csv
# Takes vcenall.csv and sorts only the Notes column and selects only the Name and Notes columns. Nimsoft comparison script nimcomp.ps1 runs against this csv.
Import-csv c:\reports\vcenter\vcenall.csv | Sort-Object Notes | Select-Object Name, Notes | Export-csv nimsoft.csv -NoTypeInformation
# Takes vcenall.csv and sorts only the Name columns and exports all fields. Backup/Restore comparison script bure.ps1 runs against this csv.
Import-csv c:\reports\vcenter\vcenall.csv | Sort-Object Name | Export-csv bure.csv -NoTypeInformation
I think you need to add more information but just using what you have let try this one approach
Import-Csv C:\temp\test.csv -Header server,1,2,3,4 | ForEach-Object{
$_.server = (($_.server).split("(")[0]).Trim()
$_
}
We import the csv data and assign a header. If you already have one then this parameter can be omitted.
Then we examine each row of data as an object. Change the server data by splitting it up by its spaces. If this data is for server names then it is safe to assume that that everything before the first space is the server name. This approach is dependent on the space being there. We could also use the same logic with the ( but this would be easier if the space was a guarantee.
So we update the server and then send the data back down the pipe with $_.
Sample Output
server 1 2 3 4
------ - - - -
GDR01W01SQ004 1 4 63 NY-TER-PLN-P-5N
GDR01L02D001 4 12 129 CO-FDP-STE-NP-5N
Edit based on comments
Since it is a server display name I changed the logic to split based on the "(". Also using the Split() method instead of the -split operator.

Outputting PowerShell data to a string

This is really PowerShell 101, I realise, but I'm stuck.
I'm trying to iterate through a folder tree, getting each subfolder name and a count of files. No problems there.
The new requirement is to get the ACLs on each subfolder as well. All of this data needs to be output as a CSV file, with a line consisting of each folder name, the file count, and the ACLs in a single string in one field of the CSV (I was going to delimit them with semicolons).
I am open to exporting to XML if the data can be viewed in Excel.
The part where I'm stuck is getting the ACL information into a single string for the CSV.
Get-ACL on each directory shows the data as follows (I'm doing a Select to just get the IdentityReference and FileSystemRights, which is all we're interested in):
IdentityReference FileSystemRights
----------------- ----------------
BUILTIN\Users ReadAndExecute, Synchronize
BUILTIN\Users AppendData
BUILTIN\Users CreateFiles
I would like the output file formatted with one line per subdirectory, similar to
#filecount,folder,perms
51,C:\temp,BUILTIN\Users:ReadAndExecute,Synchronize;BUILTIN\Users:AppendData...
I however can't get any kind of join working to have it presented in this way. I don't care about what combination of delimiters are used (again, must be readable in Excel).
The script, such as it is, is as follows. The output file has its line of data appended with each directory it traverses. I'm sure this isn't very efficient, but I don't want the process consuming all the server memory either. The bits I can't figure out are prepended with ###.
(Get-ChildItem C:\temp -recurse | Where-Object {$_.PSIsContainer -eq $True}) | foreach {
$a = ($_.GetFiles().Count)
$f = $_.FullName
$p = (get-acl $_.FullName).Access | select-object identityreference,filesystemrights
### do something with $p?
Out-File -FilePath c:\outfile.csv -Append -InputObject $a`,$f`,###$p?
}
Since you want all ACEs of a folder mangled into a single line you need something like this:
Get-ChildItem 'C:\temp' -Recurse | ? { $_.PSIsContainer } | % {
# build a list of "trustee:permissions" pairs
$perms = (Get-Acl $_.FullName).Access | % {
"{0}:{1}" -f $_.IdentityReference, $_.FileSystemRights
}
New-Object -Type PSObject -Property #{
'Filecount' = $_.GetFiles().Count
'Folder' = $_.FullName
'Permissions' = $perms -join ';' # join the list to a single string
}
} | Export-Csv 'c:\outfile.csv' -NoType
Repeated appending inside a loop usually guarantees poor performance, so it should be avoided whenever possible. The outer loop creates a list of custom objects, which can then be exported via Export-Csv in a single go.

save as "proper" csv / delete quotes from CSV except for where comma exists

I am downloading a CSV from a SharePoint site. It comes with a .csv file extension.
When I inspect the file's contents by opening it in Notepad, I see data that looks like this sample row:
"TITLE",OFFICE CODE,="","CUSTOMER'S NAME",ACCOUNT
I want the data look like this:
TITLE,OFFICE CODE,,"CUSTOMER'S NAME",ACCOUNT
One way to solve this problem is manually. When I open the file in Excel and save it (without altering anything), it prompts me with the following: fileOrig.csv may contain features that are not compatible with CSV (Comma delimited). Do you want to keep the workbook in this format? When I save it, and then inspect it in Notepad, the data is formatted according to how I want it do look.
Is there a quick way to resave the original CSV with PowerShell?
If there is no quick way to resave the file with PowerShell, I would like to use PowerShell to parse it.
These are the parsing rules I want to introduce:
Remove encapsulating doublequote from cells that do not contain a , char
Remove the = char
I tried writing a test script that just looks at the column that potentially contains , chars. It is supposed to find the cells that do not contain a , char, and remove the doublequotes that encapsulate the text. It does not work, because I think it tosses the doublequote upon Import-Csv
$source = 'I:\dir\fileOrig.csv'
$dest = 'I:\dir\fileStaging.csv'
$dest2 = 'I:\dir\fileFinal.csv'
get-content $source |
select -Skip 1 |
set-content "$file-temp"
move "$file-temp" $dest -Force
$testcsv = Import-Csv $dest
foreach($test in $testcsv)
{
#Write-Host $test."CUSTOMER NAME"
if($test."CUSTOMER NAME" -NotLike "*,*") {
$test."CUSTOMER NAME" -replace '"', ''
}
}
$testcsv | Export-Csv -path $dest2 -Force
Can someone please help me either with implementing the logic above, or if you know of a better way to save the file as a proper CSV, can you please let me know?
Since Excel can handle the problem, why not use a vbs script to automate it? Use notepad to create "Fix.vbs" with the following lines:
Set objExcel = CreateObject("Excel.Application")
Set objWorkbook = objExcel.Workbooks.Open("C:\test\test.csv")
objworkbook.Application.DisplayAlerts = False
objworkbook.Save
objexcel.quit
run it from a command prompt and it should do the trick.
I see that there's already an approved answer, I'm just offering an alternative.
If you want to keep it in PowerShell you could do this:
$File = 'I:\dir\fileOrig.csv'
$dest = 'I:\dir\fileStaging.csv'
$Output = 'I:\dir\fileFinal.csv'
$CSV = Import-Csv $file
$Members = $test|gm -MemberType Properties|select -ExpandProperty name
$test|%{$row=$_;$Members|%{if(!($row.$_ -match "\w+")){$row.$_=$null}};$_=$row}|export-csv $dest -NoTypeInformation -Force
gc $file|%{($_.split(",") -replace "^`"(.*)`"$","`$1") -join ","}|Out-File $Output
That imports the CSV, makes sure that there are words (letters, numbers, and/or underscores... don't ask my why underscores are considered words, RegEx demands that it be so!) in each property for each entry, exports the CSV, then runs through the file again as just text splitting at commas and if it shows up enclosed in double quotes it strips those, re-joins the line, and then outputs it to a file. The only thing that I don't think shows up like your "preferred output" in the OP is that instead of "CUSTOMER'S NAME" you get CUSTOMER'S NAME.

Resources