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')
I´m trying to get a
a) list of all empty folders and subfolders if the folder is named "Archiv"
b) I´d like to delete all those empty folders. My current approch doesn´t check the subfolders.
It would be also great if the results would be exportet in a .csv =)
$TopDir = 'C:\Users\User\Test'
$DirToFind = 'Archiv'>$EmptyDirList = #(
Get-ChildItem -LiteralPath $TopDir -Directory -Recurse |
Where-Object {
#[System.IO.Directory]::GetFileSystemEntries($_.FullName).Count -eq 0
$_.GetFileSystemInfos().Count -eq 0 -and
$_.Name -match $DirToFind
}
).FullName
$EmptyDirList
Any ideas how to adjust the code? Thanks in advance
You need to reverse the order in which Get-ChildItem lists the items so you can remove using the deepest nested empty folder first.
$LogFile = 'C:\Users\User\RemovedEmptyFolders.log'
$TopDir = 'C:\Users\User\Test'
# first get a list of all folders below the $TopDir directory that are named 'Archiv' (FullNames only)
$archiveDirs = (Get-ChildItem -LiteralPath $TopDir -Filter 'Archiv' -Recurse -Directory -Force).FullName |
# sort on the FullName.Length property in Descending order to get 'deepest-nesting-first'
Sort-Object -Property Length -Descending
# next, remove all empty subfolders in each of the $archiveDirs
$removed = foreach ($dir in $archiveDirs) {
(Get-ChildItem -LiteralPath $dir -Directory -Force) |
# sort on the FullName.Length property in Descending order to get 'deepest-nesting-first'
Sort-Object #{Expression = {$_.FullName.Length}} -Descending |
ForEach-Object {
# if this folder is empty, remove it and output its FullName for the log
if (#($_.GetFileSystemInfos()).Count -eq 0) {
$_.FullName
Remove-Item -LiteralPath $_.FullName -Force
}
}
# next remove the 'Archiv' folder that is now possibly empty too
if (#(Get-ChildItem -LiteralPath $dir -Force).Count -eq 0) {
# output this folders fullname and delete
$dir
Remove-Item -LiteralPath $dir -Force
}
}
$removed | Set-Content -Path $LogFile -PassThru # write your log file. -PassThru also writes the output on screen
Not sure a CSV is needed, I think a simple text file will suffice as it's just a list.
Anyway, here's (although not the most elegant) a solution which will also delete "nested empty directories". Meaning if a directory only contains empty directorIS, it will also get deleted
$TopDir = "C:\Test" #Top level directory to scan
$EmptyDirListReport = "C:\EmptyDirList.txt" #Text file location to store a file with the list of deleted directorues
if (Test-Path -Path $EmptyDirListReport -PathType Leaf)
{
Remove-Item -Path $EmptyDirListReport -Force
}
$EmptyDirList = ""
Do
{
$EmptyDirList = Get-ChildItem -Path $TopDir -Recurse | Where-Object -FilterScript { $_.PSIsContainer } | Where-Object -FilterScript { ((Get-ChildItem -Path $_.FullName).Count -eq 0) } | Select-Object -ExpandProperty FullName
if ($EmptyDirList)
{
$EmptyDirList | Out-File -FilePath $EmptyDirListReport -Append
$EmptyDirList | Remove-Item -Force
}
} while ($EmptyDirList)
This should do the trick, should works with nested too.
$result=(Get-ChildItem -Filter "Archiv" -Recurse -Directory $topdir | Sort-Object #{Expression = {$_.FullName.Length}} -Descending | ForEach-Object {
if ((Get-ChildItem -Attributes d,h,a $_.fullname).count -eq 0){
$_
rmdir $_.FullName
}
})
$result | select Fullname |ConvertTo-Csv |Out-File $Logfile
You can do this with a one-liner:
> Get-ChildItem -Recurse dir -filter Archiv |
Where-Object {($_ | Get-ChildItem).count -eq 0} |
Remove-Item
Although, for some reason, if you have nested Archiv files like Archiv/Archiv, you need to run the line several times.
$Servers = Get-content 'D:\utils\Backup\SBX servers.txt'
foreach($Path in $Servers){
$Path = "E:\Backup"
$result = Get-ChildItem E:\Backup\*_DB.zip -Recurse -Force -File |Format-Wide| Where-Object { $_.LastWriteTime -gt (get-date).AddDays(-1)}
$result
}
you were not far from the solution, but there are multiple problem with your syntax. First you should not "filter" (where-object), after "formating" (Format-wide).
foreach($Path in $Servers){
$Path = "E:\Backup"
$result = Get-ChildItem E:\Backup*DB.zip -Recurse -Force -File | Where-Object { $_.LastWriteTime -gt (get-date).AddDays(-1)}
$result | Format-Wide
}
you were missing the "underscore" in your filter too: "$." -> "$_."
I am trying to copy machines names to a text file if the programs are found in the user installations
machines are copying over to text file however it is getting machines that are not installed as well
$computers = Get-Content C:\temp\MY_TEST.txt | ForEach-Object {$_.trim()}
foreach($computer in $computers) {
Invoke-command -computername $computer {
$UserHives = Get-ChildItem Registry::HKEY_USERS\ | Where-Object {$_.Name -match '^HKEY_USERS\\S-1-5-[\d\-]+$'}
foreach($Hive in $UserHives)
{
# Construct path from base key
$Path = Join-Path $Hive.PSPath "Software\Microsoft\Windows\CurrentVersion\Uninstall\*"
# Attempt to retrieve Item property
$one = Get-ItemProperty -Path $Path -ErrorAction SilentlyContinue | Where-Object {$_.displayname -eq 'ABC'}
$two = Get-ItemProperty -Path $Path -ErrorAction SilentlyContinue | Where-Object {$_.displayname -eq 'DEF'}
$three = Get-ItemProperty -Path $Path -ErrorAction SilentlyContinue | Where-Object {$_.displayname -eq '123'}
if($one -or $two -or $three){
$computer | Out-File C:\temp\MY.txt -Append
}
} #end foreach
}
#only copies here outside of loop
#$computer | Out-File C:\temp\MY.txt -Append
} #end main foreach loop
I expect only machines found in my registry hive to be copied to text file
My Current Code Is
Param(
[string]$filePath = "C:\",
[string]$logFileFind = "error.log",
[string]$logFileReplace ="ThisHasBeenReplaced.log"
)
($configFile = Get-ChildItem -Recurse -Force $filePath -ErrorAction SilentlyContinue | Where-Object { ($_.PSIsContainer -eq $false) -and ( $_.Name -like "*.config") }
It Works fine and gives me list of files i was wondering how i could go through these files and find and replace certain words for when I'm moving though environments and the path wont be the same. I'm very limited in my powershell knowledge and i tried adding this to the end of the script.
ForEach-Object{(Get-Content $configFile) -replace $logFileFind , $logFileReplace | Set-Content $configFile})
This didn't work and i was wondering if there was anyone out there who knew what i could do to make it work.
Thanks in Advance!
You always access $configFile in your foreach loop (which is probably an System.Array), not the actual element. Try this:
$configFile | foreach { (get-content $_.FullName -Raw) -replace $logFileFind , $logFileReplace | Set-Content $_.FullName }
Here is a full example:
Get-ChildItem -Recurse -force $filePath -ea 0 |
where { ($_.PSIsContainer -eq $false) -and ( $_.Name -like "*.config") } |
foreach {
(gc $_.FullName -raw) -replace $logFileFind , $logFileReplace | sc $_.FullName
}