User Profile Names and Sizes into CSV File - powershell

This script gets user profiles on a machine and I am able to provide the output into a text file but I cannot figure out how to export this information to a CSV file.
dir C:\Users | foreach -Begin {} -Process {
$size = (dir $_.FullName -Recurse -Force -EA SilentlyContinue | Measure-Object ‘length’ -Sum -Maximum).Sum
Write-Output ("{0:n2}" -f ($size/1MB) + " MB", $_.FullName) >> "C:\scripts\UserProfiles\UserProfiles.txt"
}

You'll need to create an object with properties and then use Export-Csv
Get-ChildItem C:\Users |
ForEach-Object `
-Begin { Write-Host -Object "Scanning user directories" } `
-Process {
Write-Host "Scanning path '$($_.FullName)'"
$Size = (Get-ChildItem $_.FullName -Recurse -Force -ErrorAction SilentlyContinue | Measure-Object ‘length’ -Sum -Maximum).Sum
[pscustomobject] #{
Name = $_.Name
Path = $_.FullName
Size = '{0:N2} MB' -f ( $Size / 1MB )
}
} |
Export-Csv -Path C:\Scripts\UserProfiles\UserProfiles.csv -NoTypeInformation

Related

Powershell get-childitem against remote server

I would like to get size of user profiles across all servers in OU remotely, I tried this script, but without success
$Exclude = #('admsupp','all users','public','Default','Default user')
$servers = Get-ADComputer -SearchBase "OU=Horizon8,OU=xxx,OU=x,OU=xxx,DC=xx,DC=xx" -Filter *
foreach ($server in $servers)
{
gci -force -path \\$server.dnshostname\c$\users -Exclude $Exclude -ErrorAction SilentlyContinue | ? { $_ -is [io.directoryinfo] } | % {
$len = 0
gci -recurse -force $_.fullname -ErrorAction SilentlyContinue | % { $len += $_.length }
$_.fullname, '{0:N2} GB' -f ($len / 1Gb)
}
}
can someone advise please how to achieve this?
Thank you
The path you construct is wrong.
The way you write \\$server.dnshostname\c$\users will not give you what you think and you should either use a sub expression inside a double-quoted string:
Get-ChildItem -Path "\\$($server.DNSHostName)\c$\users"
or construct it using the -f Format operator like:
Get-ChildItem -Path ('\\{0}\c$\users' -f $server.DNSHostName)
Then, because you don't want to use recursion in the first Get-ChildItem, the -Exclude parameter does not function and instead use the -Directory switch and a Where-Object clause after that to exclude the folders you do not want:
Get-ChildItem -Path "\\$($server.DNSHostName)\c$\users" -Directory | Where-Object { $Exclude -notcontains $_.Name }
As per your comment, if you have server names all starting with a single letter followed by one or more digits and you want to construct a longer path using that, you could do
# construct the path:
# if computername is 'V10testcomputer', this will result in'\\V10testcomputer\c$\Users\testcomputer\AppData'
$path = '\\{0}\c$\users\{1}\AppData' -f $server.DNSHostName, ($server.DNSHostName -replace '^[A-Z]\d+(.*)', '$1')
Get-ChildItem -Path $path -Directory | Where-Object { $Exclude -notcontains $_.Name }
Your problem is the way you construct the Path, see #Theo's answer.
But you can do it far more quickly with Measure-Object instead of another For-Each loop.
You don't have to test the DirectoryInfo type, unless you are still using PowerShell 2, simply use the -Directory parameter.
$Exclude = #('admsupp','all users','public','Default','Default user')
$servers = Get-ADComputer -SearchBase "OU=Horizon8,OU=xxx,OU=x,OU=xxx,DC=xx,DC=xx" `
-Filter *
foreach ($server in $servers)
{
gci -Force -Path "\\$($server.dnshostname)\c$\users" -Exclude $Exclude `
-Directory -ErrorAction SilentlyContinue |
% {
$_.Fullname, '{0:N2} GB' -f ((gci -Recurse -Force $_.Fullname `
-ErrorAction SilentlyContinue | Measure-Object `
-Property Length -Sum).Sum / 1Gb)
}
}
I Suggest you to prefer this
$_.Fullname, (gci -Recurse -Force $_.Fullname -ErrorAction SilentlyContinue |
Measure-Object -Property Length -Sum).Sum
because reusing this info is easier than text data
#theo
Hi,
currently I am running this script
$Exclude = #('admsupp','all users','public','Default','Default user')
$servers = Get-ADComputer -SearchBase "OU=xxx,OU=xxx,OU=xxx,DC=xxx,DC=xxx" -Filter *
$path = '\\{0}\c$\users\{1}\AppData\roaming' -f $server.name, ($server.name -replace '^[A-Z]\d+(.*)', '$1')
foreach ($server in $servers) {
$colItems = Get-ChildItem -Path $path -Exclude $Exclude | Where-Object {$_.PSIsContainer -eq $true} | Sort-Object
foreach ($i in $colItems)
{
$subFolderItems = Get-ChildItem $i.FullName -recurse -force | Where-Object {$_.PSIsContainer -eq $false} | Measure-Object -property Length -sum | Select-Object Sum
$i.FullName + ”;” + “{0:N2}” -f ($subFolderItems.sum / 1GB) + ” GB” >> C:\users\xxx\export.txt
$i.FullName + ”;” + “{0:N2}” -f ($subFolderItems.sum / 1GB) + ” GB”
}
}
excludes are working fine like this, but the script failes immediately after start without any error message. Only thing that I have changed is the path regarding your advice:$path = '\\{0}\c$\users\{1}\AppData\roaming' -f $server.name, ($server.name -replace '^[A-Z]\d+(.*)', '$1')

Folders with more than 40.000 files

I have this script I received to check folders and subfolders on a network drive. I wonder how it could be modified into checking only folders and subfolder and write in the CSV if there is any folder with more then 40.000 files in it and the number of files. The image show a sample output from the script as it is now and I do not need it to show any files as it currently do.
$dir = "D:\test"
$results = #()
gci $dir -Recurse -Depth 1 | % {
$temp = [ordered]#{
NAME = $_
SIZE = "{0:N2} MB" -f ((gci $_.Fullname -Recurse | measure -Property Length -Sum -ErrorAction SilentlyContinue).Sum / 1MB)
FILE_COUNT = (gci -File $_.FullName -Recurse | measure | select -ExpandProperty Count)
FOLDER_COUNT = (gci -Directory $_.FullName -Recurse | measure | select -ExpandProperty Count)
DIRECTORY_PATH = $_.Fullname
}
$results += New-Object PSObject -Property $temp
}
$results | export-csv -Path "C:\temp\output.csv" -NoTypeInformation
Instead of executing so many Get-ChildItem cmdlets, here's an approach that uses robocopy to do the heavy lifting of counting the number of files, folders and total sizes:
# set the rootfolder to search
$dir = 'D:\test'
# switches for robocopy
$roboSwitches = '/L','/E','/NJH','/BYTES','/NC','/NP','/NFL','/XJ','/R:0','/W:0','/MT:16'
# regex to parse the output from robocopy
$regEx = '\s*Total\s*Copied\s*Skipped\s*Mismatch\s*FAILED\s*Extras' +
'\s*Dirs\s*:\s*(?<DirCount>\d+)(?:\s+\d+){3}\s+(?<DirFailed>\d+)\s+\d+' +
'\s*Files\s*:\s*(?<FileCount>\d+)(?:\s+\d+){3}\s+(?<FileFailed>\d+)\s+\d+' +
'\s*Bytes\s*:\s*(?<ByteCount>\d+)(?:\s+\d+){3}\s+(?<BytesFailed>\d+)\s+\d+'
# loop through the directories directly under $dir
$result = Get-ChildItem -Path $dir -Directory | ForEach-Object {
$path = $_.FullName # or if you like $_.Name
$summary = (robocopy.exe $_.FullName NULL $roboSwitches | Select-Object -Last 8) -join [Environment]::NewLine
if ($summary -match $regEx) {
$numFiles = [int64] $Matches['FileCount']
if ($numFiles -gt 40000) {
[PsCustomObject]#{
PATH = $path
SIZE = [int64] $Matches['ByteCount']
FILE_COUNT = [int64] $Matches['FileCount']
FOLDER_COUNT = [int64] $Matches['DirCount']
}
}
}
else {
Write-Warning -Message "Path '$path' output from robocopy was in an unexpected format."
}
}
# output on screen
$result | Format-Table -AutoSize
# output to CSV file
$result | Export-Csv -Path "C:\temp\output.csv" -NoTypeInformation

PowerShell BitsTransfer use file names from variable as source

I am new to PowerShell and I get a lot done using this forum along with other internet searched answers but this one is getting the better of me. I have a script that compares two folders and the part where I'm having problems I cannot get the BitsTransfer source to work. $Differences identifies files from the source folder that are not in the target folder by modified date. I want the BitsTransfer to move the files identified by $Differences but I'm not having any luck getting that behavior. The operation works when I use "Copy-Item -Path $Differences -Destination $Local –Force" instead but i want the progress bar BitsTransfer Uses. The code i am using is as follows
$Local = 'c:\test\local\'
$Remote = 'c:\test\server\'
$Target = Get-ChildItem -Path $Local -File
$Source = Get-ChildItem -Path $Remote -File
Import-Module BitsTransfer
Set-Location $Remote
filter timestamp {"$(Get-Date -Format g): $_"}
if ($Target -eq $null) {
$bitsjob = Start-BitsTransfer -Source $Remote\*.* -Destination $Local
Write-Output "$Local Folder Empty" | timestamp | Out-File -Encoding Ascii -append "D:\Mitek\DCS Stuff\Display PC Scripts\test\log.txt"
Rename-Item -Path "D:\Mitek\DCS Stuff\Display PC Scripts\test\RemoteComplete.bat" -NewName "Remote.bat"
Exit
} Else {
if ($Target -ne $null){
Compare-Object $Source $Target -Property Name -PassThru | Where-Object {$_.SideIndicator -eq "=>"} | % {
if(-not $_.FullName.PSIsContainer) {
Write-Output "Removed From $Local" | timestamp | Out-File -Encoding Ascii -append "D:\Mitek\DCS Stuff\Display PC Scripts\test\log.txt"
Remove-Item -Path $_.FullName -Force -ErrorAction SilentlyContinue}}}}
$Differences = Compare-Object -ReferenceObject $Source -DifferenceObject $Target -Property LastWriteTime -PassThru
$Differences | Group-Object Name | Select -ExpandProperty Group | Sort-Object LastWriteTime | Select-Object -Last 1
if ($Differences -ne $null) {
foreach ($file in $Differences) {
#Copy-Item -Path $Differences -Destination $Local –Force
Start-BitsTransfer -Source $Differences -Destination $Local
Write-Output "Copied to $Local" | timestamp | Out-File -Encoding Ascii -append "D:\Mitek\DCS Stuff\Display PC Scripts\test\log.txt"
Rename-Item -Path "D:\Mitek\DCS Stuff\Display PC Scripts\test\RemoteComplete.bat" -NewName "Remote.bat"}
} Else {
Write-Output "$Local and $Remote are Equal" | timestamp | Out-File -Encoding Ascii -append "D:\Mitek\DCS Stuff\Display PC Scripts\test\log.txt"}`
I got the issue figured out with some looping behavior the code used to resolve the issue is bellow for those who may find this helpful in the future...
$Local = 'C:\test\local\'
$Remote = 'C:\test\server\'
$Target = Get-ChildItem -Path $Local -File
$Source = Get-ChildItem -Path $Remote -File
$One = 1
Set-Location $Remote
filter timestamp {"$(Get-Date -Format g): $_"}
if ($Target -eq $null) {
$TotalA = $Source | Measure | Select-Object -ExpandProperty Count
echo "Total Number of Files to be Copied= $TotalA"
echo "---------------------------------------------------------------"
Write-Output "$Local Folder Empty" | timestamp | Out-File -Encoding Ascii -append "D:\Mitek\DCS Stuff\Display PC Scripts\test\log.txt"
Start-BitsTransfer -Source $Remote\*.* -Destination $Local -Description "Copying $TotalA Files"
Rename-Item -Path "C:\test\RemoteComplete.bat" -NewName "Remote.bat"
exit
} Else {
if ($Target -ne $null){
Compare-Object $Source $Target -Property Name -PassThru | Where-Object {$_.SideIndicator -eq "=>"} | % {
if(-not $_.FullName.PSIsContainer) {
Write-Output "Removed From $Local" | timestamp | Out-File -Encoding Ascii -append "D:\Mitek\DCS Stuff\Display PC Scripts\test\log.txt"
Remove-Item -Path $_.FullName -Force -ErrorAction SilentlyContinue}}}}
$Differences = Compare-Object -ReferenceObject $Source -DifferenceObject $Target -Property LastWriteTime -PassThru
if ($Differences -ne $null) {
$TotalB=$Differences | Group-Object Name | Select -ExpandProperty Group | Sort-Object Name | Measure | Select-Object -ExpandProperty Count
echo "Total Number of Files to be Copied= $TotalB"
Write-Output "Copied to $Local" | timestamp | Out-File -Encoding Ascii -append "D:\Mitek\DCS Stuff\Display PC Scripts\test\log.txt"
do {
$Tar = Get-ChildItem -Path $Local -File
$Src = Get-ChildItem -Path $Remote -File
$Diffs = Compare-Object -ReferenceObject $Src -DifferenceObject $Tar -Property LastWriteTime -PassThru
$ListB=$Diffs | Group-Object Name | Select -ExpandProperty Group | Sort-Object Name | Select-Object -First 1
if ($ListB -ne $null) {
echo "---------------------------------------------------------------"
$Rmn=$Diffs | Group-Object Name | Select -ExpandProperty Group | Sort-Object Name | Measure | Select-Object -ExpandProperty Count
$RemainB = $Rmn - $One
Start-BitsTransfer -Source $ListB -Destination $Local -Description "$RemainB Files Remain Copying - $List" }}
Until ($ListB -eq $null)
Rename-Item -Path "C:\test\RemoteComplete.bat" -NewName "Remote.bat"
} Else {
Write-Output "$Local and $Remote are Equal" | timestamp | Out-File -Encoding Ascii -append "D:\Mitek\DCS Stuff\Display PC Scripts\test\log.txt"}

Copy everything except files on the list

I am trying to copy all files recursively from a:\ to b:\, except those whose metadata is present in a:\list.txt. The list.txt pattern is LastWriteTimeYYYY-MM-DD HH:MM:SS,size,.fileextension, for example:
2001-01-31 23:59:59,12345,.doc
2001-01-31 23:59:59,12345,.txt
2001-01-31 23:59:00,456,.csv
...so any and all files, anywhere in the a:\ dir tree, matching these metadata should not be copied.
I seem to be having trouble with the Where-Object in order to exclude the items on the list.txt, but copy everything else:
$Source = "C:\a"
$Target = "C:\b"
$List = Import-Csv list.txt -Header LastWriteTime,Size,Name
$Hash = #{}
ForEach ($Row in $List){
$Key = ("{0},{1},.{2}" -F $Row.LastWriteTime,$Row.Size,$Row.Name.Split('.')[-1].ToLower())
IF (!($Hash[$Key])) {$Hash.Add($Key,$Row.Name)}
}
$Hash | Format-Table -Auto
Get-Childitem -Path $Source -Recurse -File | Where-Object {$Hash -eq $Hash[$Key]}| ForEach-Object {$Key = ("{0},{1},{2}" -F ($_.LastWriteTime).ToString('yyyy-MM-dd HH:mm:ss'),$_.Length,$_.Extension.ToLower())
#$Key
If ($Hash[$Key]){
$Destination = $_.FullName -Replace "^$([RegEx]::Escape($Source))","$Target"
If (!(Test-Path (Split-Path $Destination))){MD (Split-Path $Destination)|Out-Null}
$_ | Copy-Item -Destination $Destination
}
}
I propose you a simplification of your code :
$Source = "C:\a\"
$Target = "C:\b\"
New-Item -ItemType Directory $Target -Force | Out-Null
$List = Import-Csv list.txt -Header LastWriteTime,Length,Extension
Get-Childitem $Source -Recurse -File | %{
$File=$_
$exist=$List | where {$_.LastWriteTime -eq $File.LastWriteTime.ToString('yyyy-MM-dd HH:mm:ss') -and $_.Length -eq $File.Length -and $_.Extension -eq $File.Extension} | select -first 1
if ($exist -ne $null) {continue}
New-Item -ItemType Directory $File.DirectoryName.Replace($Source, $Target) -Force | Out-Null
Copy-Item $File.FullName $File.FullName.Replace($Source, $Target) -Force
}

Output Rename-Item events to log file

I'm trying to log data surrounding the renaming of files using the following script. The only problem is that the log file contains files that were not renamed due to 'access denied' errors when attempting to rename. I need to figure out how to only create log entries for files that were SUCCESSFULLY renamed or pipe the failed renames to a different log file. I'd also like the total number of files renamed listed at the top of the log file if at all possible(ie 'xxx files were renamed') I appreciate any suggestions for getting this to work using powershell v2.
$drivesArray = Get-PSDrive -PSProvider 'FileSystem' | select -Expand Root
foreach ($drive in $drivesArray) {
Get-ChildItem $drive | Where-Object {
$_.FullName -notlike "${Env:WinDir}*" -and
$_.FullName -notlike "${Env:ProgramFiles}*"
} | ForEach-Object {
Get-ChildItem $_.FullName -Recurse -ErrorAction SilentlyContinue
} | Where-Object {
-not $_.PSIsContainer -and
$_.Extension -notmatch '^\.(xxx|exe|html)$'
} | ForEach-Object {
$newName = $_.FullName + '.xxx';
Rename-Item -Path $_.FullName -NewName ($_.FullName + '.xxx') -ErrorAction SilentlyContinue
Add-Content c:\temp\renameLog.txt -Value $('{0} {1} {2} {3}' -f $(Get-Date),$_.fullname,$_.name,$newName )
}
}
Here is an example, untested of course.
$success = 0
$failed = 0
$drivesArray = Get-PSDrive -PSProvider 'FileSystem' | select -Expand Root
foreach ($drive in $drivesArray) {
Get-ChildItem $drive | Where-Object {
$_.FullName -notlike "${Env:WinDir}*" -and
$_.FullName -notlike "${Env:ProgramFiles}*"
} | ForEach-Object {
Get-ChildItem $_.FullName -Recurse -ErrorAction SilentlyContinue
} | Where-Object {
-not $_.PSIsContainer -and $_.Extension -notmatch '^\.(xxx|exe|html)$' -and $_.Name -notmatch '^renameLog.txt|^renameLog_failed.txt'
} | ForEach-Object {
try {
$newName = $_.FullName + '.xxx';
Rename-Item -Path $_.FullName -NewName ($_.FullName + '.xxx') -ErrorAction Stop
Add-Content c:\temp\renameLog.txt -Value $('{0} {1} {2} {3}' -f $(Get-Date),$_.fullname,$_.name,$newName )
$success ++
} catch [exception] {
Add-Content c:\temp\renameLog_failed.txt -Value $('{0} {1} {2} {3}' -f $(Get-Date),$_.fullname,$_.name,$newName )
$failed ++
$error.Clear()
}
}
}
Add-Content "c:\temp\renameLog.txt" -Value $("Total: " + $success)
Add-Content "c:\temp\renameLog_failed.txt" -Value $("Total: " + $failed)