Dump connected drives serials and users - powershell

I need to list users of a connected drive and it's serial # in an output file. I'll be connecting between 12-24 drives in arrays at a time. I would like to be able to put the assigned drive letters into a variable. And then have the entire script loop for each connected drive. dumping serial + linking it to the users of that drive in a CSV output file
How can I put the assigned drive letters into an array?
$(get-physicaldisk; get-childitem -path (array variable):\Users) | add-content C:\path\to\my\output.csv
almost gets the output I need when I try this on a single drive. But I'd really like to clean it up and only display the important info (PSChildName) excluding all default, public admin accounts to reduce duplicate un-needed info.
I wanted this to work
$(get-physicaldisk | select-object FriendlyName, SerialNumber)-$(get-childitem -path L:\Users| select-object PSChildName)
but it did not
I need it to grab the serial for each drive - and output the users associated with that drive … i'm struggling with making the output look the way I want.
For each - drive in array - output ((serial #) + (users on the drive)) amending my .csv
After much plugging and chugging i'm now here, thanks to everyone's help
function Get-UsersOnDrive([string[]]$DriveLetters){
if (!$DriveLetters){
$DriveLetters = Get-WmiObject Win32_Logicaldisk | %{$_.Name -replace ":", ""}
}
foreach($DriveLetter in $DriveLetters)
{
$SerialNumber = get-partition -DriveLetter $DriveLetter -ErrorAction Ignore | get-disk | select -ExpandProperty SerialNumber
$path = $DriveLetter + ":\Users"
$Users = get-childitem -path $path | select-object PSChildName
$Users | %{
$OutPut = new-object PsCustomObject
$OutPut | Add-Member -MemberType NoteProperty -Name SerialNumber -Value $SerialNumber -PassThru |
Add-Member -MemberType NoteProperty -Name Username -Value $_
return $OutPut
}
}
}
Get-UsersOnDrive -DriveLetters #("C") | Export-Csv -Path C:\sample\Test.csv -NoTypeInformation

Ok so here is what i came up with and its rough
Get-WmiObject Win32_Logicaldisk | %{
$DriveLetter = $_.Name -replace ":", ""
$SerialNumber = get-partition -DriveLetter $DriveLetter | get-disk | select -ExpandProperty SerialNumber
$Users = Get-WmiObject Win32_UserProfile | select -ExpandProperty LocalPath | ?{$_ -like "$DriveLetter*"} | %{
$_ -replace '.*\\'
}
$Users | %{
$OutPut = new-object PsCustomObject
$OutPut | Add-Member -MemberType NoteProperty -Name SerialNumber -Value $SerialNumber -PassThru |
Add-Member -MemberType NoteProperty -Name Username -Value $_
return $OutPut
}
} | Export-Csv -Path C:\sample\Test.csv -NoTypeInformation
A. Get WMI LogicalDisk (gets you the drive letters)
B. Pass the $DriveLetter into a get-partition and get the SerialNumber property value.
C. Get Users Profile path, then find the ones on the current drive and replace everything except for the last slash, which is the username
D. Foreach user on drive we create a Custom Object and add the properties SerialNumber and Username, then return output and export to CSV
Here is a function that you can call to get users on drive as well
function Get-UsersOnDrive([string[]]$DriveLetters){
if (!$DriveLetters){
$DriveLetters = Get-WmiObject Win32_Logicaldisk | %{$_.Name -replace ":", ""}
}
foreach($DriveLetter in $DriveLetters){
$SerialNumber = get-partition -DriveLetter $DriveLetter -ErrorAction Ignore | get-disk | select -ExpandProperty SerialNumber
$Users = Get-WmiObject Win32_UserProfile | select -ExpandProperty LocalPath | ?{$_ -like "$DriveLetter*"} | %{
$_ -replace '.*\\'
}
$Users | %{
$OutPut = new-object PsCustomObject
$OutPut | Add-Member -MemberType NoteProperty -Name SerialNumber -Value $SerialNumber -PassThru |
Add-Member -MemberType NoteProperty -Name Username -Value $_
return $OutPut
}
}
}
Get-UsersOnDrive -DriveLetters #("C","V","F") | Export-Csv -Path C:\sample\Test.csv -NoTypeInformation
If you remove -DriveLetters parameter and the drives then it will check all drives

The following code gets the disk serial number. I am not sure why that is needed. Will this give you a start?
function Get-DiskSerialNumber {
param(
[Parameter(Mandatory = $true,Position=0)]
[string] $DriveLetter
)
Get-CimInstance -ClassName Win32_DiskDrive |
Get-CimAssociatedInstance -Association Win32_DiskDriveToDiskPartition |
Get-CimAssociatedInstance -Association Win32_LogicalDiskToPartition |
Where-Object DeviceId -eq $DriveLetter |
Get-CimAssociatedInstance -Association Win32_LogicalDiskToPartition |
Get-CimAssociatedInstance -Association Win32_DiskDriveToDiskPartition |
Select-Object -Property SerialNumber
}
& openfiles /query /fo csv |
Select-Object -Skip 5 |
ConvertFrom-Csv -Header #('ID','USER','TYPE','PATH') |
Select-Object -Property USER,#{name='DRIVE';expression={$_.PATH.Substring(0,2)}} |
Sort-Object -Property DRIVE,USER -Unique |
Select-Object -Property *,
#{name='SERIALNUMBER';expression={(Get-DiskSerialNumber -Drive $_.DRIVE).SerialNumber}}

Related

Powershell - Export-CSV outside loop only last line is printed/exported

Is it possible to adjust this code to export all lines outside foreach loop:
This works fine (inside loop):
$vms = Get-VM | Where { $_.State –eq ‘Running’ } | Select-Object -ExpandProperty Name
foreach($vm in $vms) {
# Get network interface details
$out = Get-VMNetworkAdapter -vmname $vm | select VMName, MacAddress, IPAddresses
$vm_name = $out.VMName | Get-Unique
$ip = ($out.IPAddresses | ForEach-Object {
$_ | ? {$_ -notmatch ':'}
}) -join " "
# If more than 1 MAC , put it in same row separated by space (00:15:5D:58:12:5E 00:15:5D:58:12:5F )
$mac = ($out.MacAddress | ForEach-Object {
$_.Insert(2,":").Insert(5,":").Insert(8,":").Insert(11,":").Insert(14,":")
}) -join ' '
$results = #()
$comp = Get-WmiObject Win32_ComputerSystem | Select-Object -ExpandProperty name
$obj = New-Object -TypeName psobject
$obj | Add-Member -MemberType NoteProperty -Name "VM NAME" -Value $vm_name
$obj | Add-Member -MemberType NoteProperty -Name "IP ADDRESS" -Value $ip
$obj | Add-Member -MemberType NoteProperty -Name "MAC ADDRESS" -Value $mac
$obj | Add-Member -MemberType NoteProperty -Name "HYPER-V HOST" -Value $comp
$results += $obj
Write-Output $results
$results| Export-Csv -Path "c:\1.csv" -NoTypeInformation -append
}
However, when i move $results| Export-Csv -Path "c:\1.csv" -NoTypeInformation -append outside loop,
only one (last) line is saved to CSV
Inside loop, $results variable contains all lines, when i move this variable outside loop write-host $results only one (last) line is printed
For what it's worth, your code can be condensed quite a bit. Many of your steps are not necessary:
$results = Get-VM | Where State –eq Running | Get-VMNetworkAdapter | ForEach-Object {
[pscustomobject]#{
'VM NAME' = $_.VMName
'IP ADDRESS' = ($_.IPAddresses -notmatch ':') -join ' '
'MAC ADDRESS' = ($_.MacAddress -replace '(..)(..)(..)(..)(..)','$1:$2:$3:$4:$5:') -join ' '
'HYPER-V HOST' = $env:COMPUTERNAME
}
}
$results | Export-Csv -Path "c:\1.csv" -NoTypeInformation
Notes:
You can pipe the VMs that Get-VM returns directly into Get-VMNetworkAdapter
If you filter on a single property you don't need a script block for Where-Object. Where State -eq Running is a bit easier to write and read than Where { $_.State -eq 'Running' }.
$_.IPAddresses -notmatch ':' Operators like -notmatch work on arrays. 'a','b','0','c' -notmatch '\d' will return 'a','b','c'.
The same goes for -replace. 'a0','b1','c2' -replace '\d','' will return return 'a','b','c'. No foreach loops necessary at all.
$env:COMPUTERNAME should be faster than using WMI to get the computer name
Any object you create in a script block (like the ForEach-Object {...} script block) that you do not assign to a variable will be in the script block's output. This is why $results = ... | ForEach-Object {...} works. There is no need to explicitly create arrays with #() and add values to them.
Casting a hash table to [pscustomobject] is much easier than using Add-Member.
Figured it out:
moved $results variable outside loop (make it "global")
$vms = Get-VM | Where { $_.State –eq ‘Running’ } | Select-Object -ExpandProperty Name
$results = #()
foreach($vm in $vms) {
# Get network interface details
$out = Get-VMNetworkAdapter -vmname $vm | select VMName, MacAddress, IPAddresses
# Remove duplicate VM names
$vm_name = $out.VMName | Get-Unique
# In case more than 1 IP, put it in same row separated by space (192.168.1.1, 192.168.1.2)
$ip = ($out.IPAddresses | ForEach-Object {
$_ | ? {$_ -notmatch ':'}
}) -join " "
# If more than 1 MAC , put it in same row separated by space (00:15:5D:58:12:5E 00:15:5D:58:12:5F )
$mac = ($out.MacAddress | ForEach-Object {
$_.Insert(2,":").Insert(5,":").Insert(8,":").Insert(11,":").Insert(14,":")
}) -join ' '
$comp = Get-WmiObject Win32_ComputerSystem | Select-Object -ExpandProperty name
$obj = New-Object -TypeName psobject
$obj | Add-Member -MemberType NoteProperty -Name "VM NAME" -Value $vm_name
$obj | Add-Member -MemberType NoteProperty -Name "IP ADDRESS" -Value $ip
$obj | Add-Member -MemberType NoteProperty -Name "MAC ADDRESS" -Value $mac
$obj | Add-Member -MemberType NoteProperty -Name "HYPER-V HOST" -Value $comp
$results += $obj
}
$results| Export-Csv -Path "c:\1.csv" -NoTypeInformation

How can I format this output better?

I would like to pipe the output to a .csv, but when I do, I cannot add the host name, so I have settled on shooting it to a .txt, however, I don't have much latitude to manipulate the results.
The original one-liner was:
$([ADSI]"WinNT://$env:COMPUTERNAME").Children | where {$_.SchemaClassName -eq 'user'} | select #{l='name';e={$_.name}},#{l='LastLogin';e={$_.lastlogin}} | export-csv C:\csv.csv
I have modified it to run against a list, however, the original code does not denote the host name... I would love to know how to do this. Here is the modified code:
$computers = Get-Content C:\LocalLogin.txt
ForEach ($Computer in $Computers)
{
$COMPUTER | Out-File C:\StaleLocalLogins.txt -Append
$([ADSI]"WinNT://$COMPUTER").Children |
where {$_.SchemaClassName -eq 'user'} |
select #{l='name';e={$_.name}},#{l='LastLogin';e={$_.lastlogin}} |
Out-File C:\StaleLocalLogins.txt -Append
}
So basically you can add the hostname from $env:COMPUTERNAME to a later part of the script. Below is a 1 liner but spaced for ease of reading
$([ADSI]"WinNT://$env:COMPUTERNAME").Children |
where {$_.SchemaClassName -eq 'user'} |
select #{l='name';e={$_.name}},#{l='LastLogin';e={$_.lastlogin}} |
%{
$_ |
Add-Member -MemberType NoteProperty -Name "HostName" -Value "$env:COMPUTERNAME"
$_ | Select-Object "HostName", "Name", "LastLogin"
} | Export-Csv "C:\Test\test.csv"
This part adds a new property to the PSCustomObject that was created. It stores the hostname. Then it reorders the customobject in the order HostName, Name, LastLogin
%{
$_ | Add-Member -MemberType NoteProperty -Name "HostName" -Value "$env:COMPUTERNAME"
$_ | Select-Object "HostName", "Name", "LastLogin"
}
here it is as a one-liner
$([ADSI]"WinNT://$env:COMPUTERNAME").Children | where {$_.SchemaClassName -eq 'user'} | select #{l='name';e={$_.name}},#{l='LastLogin';e={$_.lastlogin}} | foreach-object {$_ | Add-Member -MemberType NoteProperty -Name "HostName" -Value "$env:COMPUTERNAME"; $_ | Select-Object "HostName", "Name", "LastLogin"} | Export-Csv "C:\scripts\test.csv" -NoTypeInformation

powershell command to list all users, their home directories + sizes

I need a script or command that would list all users on a computer plus their home directories and sizes of their home directories. I can do it only for users are looged in. I also managed to get a list of all users :
$adsi = [ADSI]"WinNT://$env:COMPUTERNAME"
$adsi.Children | where {$_.SchemaClassName -eq 'user'} | Select-Object #{n='UserName'
e={$_.Name}}
but I don't know how to get home dir, plus size from that list.
thanks in advance
Here is a powershell loop that outputs objects with the properties you are looking for (Name, LocalPath, FolderSize), formatted as a table. This combines a few techniques to get the values you are looking for.
Get-WmiObject win32_userprofile | % {
try {
$out = new-object psobject
$out | Add-Member noteproperty Name (New-Object System.Security.Principal.SecurityIdentifier($_.SID)).Translate([System.Security.Principal.NTAccount]).Value
$out | Add-Member noteproperty LocalPath $_.LocalPath
$out | Add-Member noteproperty FolderSize ("{0:N2}" -f ((Get-ChildItem -Recurse $_.LocalPath | Measure-Object -property length -sum -ErrorAction SilentlyContinue).sum / 1MB) + " MB")
$out
} catch {}
} | Format-Table

Include if/else logic

Hello I am trying to if/else and write two separate files, if PST exists then do the following. Export-Csv -NoTypeInformation C:\$UserName-$ComputerName-OpenPSTs-$Date.csv
Else Export-Csv -NoTypeInformation C:\$UserName-$ComputerName-NOPSTs-$Date.csv
Could anyone please suggest.
$Date = Get-Date -format d-M-yyyy
$UserName = $env:USERNAME
$ComputerName = $env:COMPUTERNAME
$Outlook = New-Object -comObject Outlook.Application
$object = $Outlook.Session.Stores | Where {$_.FilePath -like "*.PST"} | Select `
#{Expression={$_.DisplayName}; Label="PST Name in Outlook"},`
#{Expression={$_.FilePath}; Label="PST Location/FileName"},`
#{Expression={$_.IsOpen}; Label="PST Open in Outlook"},`
#{Expression={(Get-Item $_.FilePath).Length / 1KB}; Label="PST File Size (KB)"}
$object | Add-Member -MemberType NoteProperty -Name 'ComputerName' -Value $ComputerName
$object | Add-Member -MemberType NoteProperty -Name 'UserName' -Value $UserName
$object | Export-Csv -NoTypeInformation C:\$UserName-$ComputerName-OpenPSTs-$Date.csv
Start-Sleep 5
Get-Process | Where {$_.Name -like "Outlook*"} | Stop-Process
You could replace the Where-Object filter with a ForEach-Object loop and a nested conditional:
$Outlook.Session.Stores | % {
if ($_.FilePath -like '*.pst') {
$_ | select ... | Export-Csv 'OpenPST.csv' -NoType -Append
} else {
$_ | select ... | Export-Csv 'NoPST.csv' -NoType -Append
}
}
That might not perform too well, though, because it repeatedly appends to the output files. It might be better to just run 2 pipelines with complementary filters:
$stores = $Outlook.Session.Stores
$stores | ? { $_.FilePath -like '*.pst' } | select ... |
Export-Csv 'OpenPST.csv' -NoType
$stores | ? { $_.FilePath -notlike '*.pst' } | select ... |
Export-Csv 'NoPST.csv' -NoType

Changing the Export-CSV output output format

I am working on a PowerShell script that will output a list od system admins to a CSV file using the Export-Csv command. The portion of the script that gets the data is:
Foreach ($Computer in $Computers){
$Online = Test-Connection -ComputerName $Computer -Quiet
if ($Online -eq "True"){
$GroupName = Get-WmiObject win32_group -ComputerName $Computer | ? {$_.SID -eq 'S-1-5-32-544'} | Select-Object name -ExpandProperty name
$LocalGroup =[ADSI]"WinNT://$Computer/$GroupName"
$GroupMembers = #($LocalGroup.psbase.Invoke("Members"))
$Members = $GroupMembers | foreach {$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)}
foreach ($Member in $Members){
$obj = New-Object System.Object
$obj | Add-Member -MemberType NoteProperty -Name "Computer" -Value $Computer
$obj | Add-Member -MemberType NoteProperty -Name "AdminGroupMembers" -Value $Member
$obj
}
}
}
}
Get-Admins | Export-Csv -NoTypeInformation c:\scripts\adm.csv -Encoding UTF8
The current output is formatted looks like this:
"Computer1", "Admin1"
"Computer1", "Admin2"
"Computer1", "Admin3"
"Computer1", "Admin4"
"Computer2", "Admin1"
"Computer2", "Admin2"
"Computer3", "Admin1"
I am trying to get the output to look like this:
"Computer1", "Admin1" , "Admin2" , "Admin3" , "Admin4"
"Computer2", "Admin1" , "Admin2"
"Computer3", "Admin1" , "Admin2" , "Admin3"
Any Ideas?
Your output format is not CSV, so Export-Csv is not a suitable tool for you. Try this instead:
Get-Admins | group { $_.Computer } | % {
'{0},{1}' -f #($_.Group.Computer)[0], ($_.Group.AdminGroupMembers -join ',')
} | Out-File 'output.csv'
For PowerShell v2 you'll need to manually expand the group properties:
Get-Admins | group { $_.Computer } | % {
$computer = #($_.Group | select -Expand Computer)[0]
$admins = ($_.Group | select -Expand AdminGroupMembers) -join ','
'{0},{1}' -f $computer, $admins
} | Out-File 'output.csv'