I'm trying to find and log any local PST files on multiple machines, accross all local drives on each machine. I have the code below so far, but I can't get it to run in the context of the root of the current drive in the foreach loop, it just runs in the context of where the script was run from.
If (-not (Test-Path -Path "\\BETH-FS01\F$\PSTBackups")){
Exit 1
} # check if PC is connected to domain, some laptops aren't always on the VPN
Else{
#defines the path where to store the log CSV
$LogPath = "\\BETH-FS01\F$\PSTBackups"
$Log = #() #object array to store "PST objects"
# defining string variable to combine with another string variable later on
$Ext = ".pst"
# creates array of local drives on PC
$Drives = Get-PSDrive -PSProvider 'FileSystem'
foreach ($Drive in $Drives) {
# searches drive for PST files, creates an array of those files,
# then passes each through to create PST objects
$Log = ForEach ($PST in ($PSTS = Get-ChildItem -LiteralPath $Drive.Name -Include *.pst -Recurse -Force -erroraction silentlycontinue)){
New-Object PSObject -Property #{
ComputerName = $env:COMPUTERNAME
Path = $PST.DirectoryName
FileName = $PST.BaseName+$Ext
Size = "{0:N2} MB" -f ($PST.Length / 1mb)
Date = $PST.LastWriteTime.ToString("yyyy-MM-dd HH:mm")
} #creates PST object
}
}
}
$Name = $env:COMPUTERNAME #define string to use in log path below
$Log | Export-Csv $LogPath\$Name.csv -NoTypeInformation #exports the Log object array to a CSV
To clarify, I'm trying to find out how to reference the fact that if the foreach loop is currently doing the C: drive, it would use the "C:" path as the -path for Get-ChildItem, i.e.:
$PSTS = Get-ChildItem -path "*somehow reference C: drive path*" -Include *.pst -Recurse -Force -erroraction silentlycontinue
Sorry if the code is sloppy, I'm not the best at keeping clean code...
In this line...
$Log = ForEach ($PST in ($PSTS = Get-ChildItem -LiteralPath $Drive.Name -Include *.pst -Recurse -Force -erroraction silentlycontinue)){
... you are just passing the drive letter for -LiteralPath, which is not a path.
You need to pass the root path, e. g. "C:\", not just "C" or "C:". The latter only means the current directory on drive C.
This should do the trick:
$Log = ForEach ($PST in ($PSTS = Get-ChildItem -LiteralPath $Drive.Root -Include *.pst -Recurse -Force -erroraction silentlycontinue)){
I wrote a script to find the particular file on windows servers based on Disks and exporting that list to .txt file for each server and drive.
I want to run the script for multiple servers and export all the list details in one csv or excel file with server name and the file location path as output.
#Clearing the Host file
Clear-Host
#Get all the list of Servers
$Machines = get-content "C:\Scripts\Servers.txt"
#Get all the list of Disks to search
$Disks = get-content "C:\Scripts\Disks.txt"
#Lopping through specified servers
foreach ($Machine in $Machines)
{
#Lopping through each Disks
foreach ($Disk in $Disks)
{
if (Test-Path \\$Machine\$Disk$)
{
Write-Host Checking $Machine Disk $Disk -BackgroundColor DarkRed
Get-ChildItem -Path \\$Machine\$Disk$\ -Filter log4j.jar -Recurse -Name -Force | Out-File "C:\Scripts\Output\$Machine $Disk.txt"
}
}
}
Thanks in Advance
Don't send output from the script until you have the information you need.
Using a custom object will allow you to store the values you want
#Clear Host
Clear-Host
#Get all the list of Servers
$Machines = get-content "C:\Scripts\Servers.txt"
#Get all the list of Disks to search
$Disks = #('D','G')
# Looping through specified servers
foreach ($Machine in $Machines) {
#Looping through each Disks
$FileResults = $Disks | ForEach-Object {
If (Test-Path \\$Machine\$_$) {
Write-Host Checking $Machine Disk $_ -BackgroundColor DarkRed
$DiskResults = Get-ChildItem -Path \\$Machine\$_$\ -Filter log4j.jar -Name -Recurse -Force
#
# Send the results of each disk to custom object, to be stored in $FileResults
#
[pscustomobject]#{Disk=$_;Files=$DiskResults}
}
}
If (-not $FileResults) {
$FileResults = 'None found'
}
#
# Send the results of each completed server
#
[pscustomobject]#{ComputerName=$Machine;Found=$FileResults}
#
}
I have a script that needs to copy a list of files to ceraitn directories and locations on target servers.
I was able to understand that I need to creat an csv file as follows:
I need to understand from you how to make the files from source location to their adjacent file in target location. Any ideas?
My code looks like this:
# customize log file
$date = (Get-Date -Format d).ToString() | foreach {$_ -replace "/", "_"}
$time = (Get-Date)
$scriptDir = "D:\Scripts\ServerBuildToolkitT1\SingleFileUpfate\"
$logDir = "D:\Scripts\ServerBuildToolkitT1\Logs\SingleFileUpfate\"
$logFileName = "SingleFileUpfate $date.log"
$sources = #()
$destinsyions = #()
function CSV {
Import-Csv D:\Scripts_PS\SD.csv | ForEach-Object {
$sources += $_."Source Location"
$destinations += $_."Destination Location"
}
}
# this file contains the list of destination server that you want copy
# file/folder to
$computers = Get-Content "D:\Scripts_PS\ServerList.txt"
function main
{
foreach ($computer in $computers) {
foreach ($destination in $destinations) {
Write-Output "$([DateTime]::Now) Copying files update to $computer now" |
Out-File -FilePath "$logDir\$logFileName" -Append
Copy-Item $sources -Destination "\\$computer\$destination" -Force -Recurse -Verbose:$false
}
}
}
csv
main
Write-Output "$([DateTime]::Now) the operation SingleFileUpdate completed successefully" |
Out-File -FilePath "$logDir\$logFileName" -Append
i have updtaed the Script (as seen above) and now i am getting the following ERROR
"WARNING: One or more headers were not specified. Default names starting with "H" have been used in place of any missing headers."
I am trying to search a log file for updates that were not installed, then using the returned array install the updates. Problem is my files are named:
Windows6.1-KB3102429-v2-x64.msu
My parsed array has a item of KB3102429 how can I use a wild card - call the array item - then another wildcard .msu
my code is listed below:
# Read KBLIST.txt and create array of KB Updates that were not installed
$Failed = Get-Content -Path C:/Updates/KBLIST.txt | Where-Object {$_ -like '*NOT*'}
# create a list of all items in Updates folder
$dir = (Get-Item -Path "C:\Updates" -Verbose).FullName
# Parse the $Failed array down to just the KB#######
for($i = $Failed.GetLowerBound(0); $i -le $Failed.GetUpperBound(0); $i++)
{
$Failed[$i][1..9] -join ""
# Search the $dir list for files that contain KB####### and end in .msu then quiet install
Foreach($item in (ls $dir *$Failed[$i]*.msu -Name))
{
echo $item
$item = "C:\Updates\" + $item
wusa $item /quiet /norestart | Out-Null
}
}
It works down to the Foreach($item in (ls $dir *$Failed[$i]*.msu -Name)).
If I just use * instead of the wildcard,string,wildcard it returns a list of all the .msu files for the basic syntax it correct.
It was hard to follow your work since you used aliases, but I think this should be able to accomplish what you're looking for.
$UpdateFolder = 'C:\Updates'
$FailedUpdates = Get-Content -Path C:/Updates/KBLIST.txt | Where-Object {$_ -like '*NOT*'}
foreach ( $Update in $FailedUpdates )
{
Write-Host -Object "Update $Update failed"
$UpdatePath = Get-Item -Path "$UpdateFolder\*$Update*.msu" | Select-Object -ExpandProperty FullName
Write-Host -Object "`tReinstalling from path: $UpdatePath"
wusa $UpdatePath /quiet /norestart | Out-Null
}
Ok, I have a script I am writing in powershell that will delete old files in the recycle bin. I want it to delete all files from the recycle bin that were deleted more than 2 days ago. I have done lots of research on this and have not found a suitable answer.
This is what I have so far(found the script online, i don't know much powershell):
$Path = 'C' + ':\$Recycle.Bin'
Get-ChildItem $Path -Force -Recurse -ErrorAction SilentlyContinue |
#Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-3) } |
Remove-Item -Recurse -exclude *.ini -ErrorAction SilentlyContinue
It is working great with one exception, it checks the file parameter "LastWriteTime". That is awesome if the user deletes the file they same day they modify it. Otherwise it fails.
How can I modify this code so that it will check when the file was deleted, not when it was written.
-On a side note, if I run this script from an administrator account on Microsoft Server 2008 will it work for all users recycle bins or just mine?
Answer:
the code that worked for me is:
$Shell = New-Object -ComObject Shell.Application
$Global:Recycler = $Shell.NameSpace(0xa)
foreach($item in $Recycler.Items())
{
$DeletedDate = $Recycler.GetDetailsOf($item,2) -replace "\u200f|\u200e",""
$dtDeletedDate = get-date $DeletedDate
If($dtDeletedDate -lt (Get-Date).AddDays(-3))
{
Remove-Item -Path $item.Path -Confirm:$false -Force -Recurse
}#EndIF
}#EndForeach item
It works awesome for me, however 2 questions remain...How do I do this with multiple drives? and Will this apply to all users or just me?
WMF 5 includes the new "Clear-RecycleBin" cmdlet.
PS > Clear-RecycleBin -DriveLetter C:\
These two lines will empty all the files recycle bin:
$Recycler = (New-Object -ComObject Shell.Application).NameSpace(0xa)
$Recycler.items() | foreach { rm $_.path -force -recurse }
This article has answers to all your questions
http://baldwin-ps.blogspot.be/2013/07/empty-recycle-bin-with-retention-time.html
Code for posterity:
# -----------------------------------------------------------------------
#
# Author : Baldwin D.
# Description : Empty Recycle Bin with Retention (Logoff Script)
#
# -----------------------------------------------------------------------
$Global:Collection = #()
$Shell = New-Object -ComObject Shell.Application
$Global:Recycler = $Shell.NameSpace(0xa)
$csvfile = "\\YourNetworkShare\RecycleBin.txt"
$LogFailed = "\\YourNetworkShare\RecycleBinFailed.txt"
function Get-recyclebin
{
[CmdletBinding()]
Param
(
$RetentionTime = "7",
[Switch]$DeleteItems
)
$User = $env:USERNAME
$Computer = $env:COMPUTERNAME
$DateRun = Get-Date
foreach($item in $Recycler.Items())
{
$DeletedDate = $Recycler.GetDetailsOf($item,2) -replace "\u200f|\u200e","" #Invisible Unicode Characters
$DeletedDate_datetime = get-date $DeletedDate
[Int]$DeletedDays = (New-TimeSpan -Start $DeletedDate_datetime -End $(Get-Date)).Days
If($DeletedDays -ge $RetentionTime)
{
$Size = $Recycler.GetDetailsOf($item,3)
$SizeArray = $Size -split " "
$Decimal = $SizeArray[0] -replace ",","."
If ($SizeArray[1] -contains "bytes") { $Size = [int]$Decimal /1024 }
If ($SizeArray[1] -contains "KB") { $Size = [int]$Decimal }
If ($SizeArray[1] -contains "MB") { $Size = [int]$Decimal * 1024 }
If ($SizeArray[1] -contains "GB") { $Size = [int]$Decimal *1024 *1024 }
$Object = New-Object Psobject -Property #{
Computer = $computer
User = $User
DateRun = $DateRun
Name = $item.Name
Type = $item.Type
SizeKb = $Size
Path = $item.path
"Deleted Date" = $DeletedDate_datetime
"Deleted Days" = $DeletedDays }
$Object
If ($DeleteItems)
{
Remove-Item -Path $item.Path -Confirm:$false -Force -Recurse
if ($?)
{
$Global:Collection += #($object)
}
else
{
Add-Content -Path $LogFailed -Value $error[0]
}
}#EndIf $DeleteItems
}#EndIf($DeletedDays -ge $RetentionTime)
}#EndForeach item
}#EndFunction
Get-recyclebin -RetentionTime 7 #-DeleteItems #Remove the comment if you wish to actually delete the content
if (#($collection).count -gt "0")
{
$Collection = $Collection | Select-Object "Computer","User","DateRun","Name","Type","Path","SizeKb","Deleted Days","Deleted Date"
$CsvData = $Collection | ConvertTo-Csv -NoTypeInformation
$Null, $Data = $CsvData
Add-Content -Path $csvfile -Value $Data
}
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($shell)
#ScriptEnd
Had to do a bit of research on this myself, the recycle bin contains two files for every file deleted on every drive in win 10 (in win 7 files are as is so this script is too much and needs to be cut down, especially for powershell 2.0, win 8 untested), an info file created at time of deletion $I (perfect for ascertaining the date of deletion) and the original file $R, i found the com object method would ignore more files than i liked but on the up side had info i was interested in about the original file deleted, so after a bit of exploring i found a simple get-content of the info files included the original file location, after cleaning it up with a bit of regex and came up with this:
# Refresh Desktop Ability
$definition = #'
[System.Runtime.InteropServices.DllImport("Shell32.dll")]
private static extern int SHChangeNotify(int eventId, int flags, IntPtr item1, IntPtr item2);
public static void Refresh() {
SHChangeNotify(0x8000000, 0x1000, IntPtr.Zero, IntPtr.Zero);
}
'#
Add-Type -MemberDefinition $definition -Namespace WinAPI -Name Explorer
# Set Safe within deleted days and get physical drive letters
$ignoreDeletedWithinDays = 2
$drives = (gwmi -Class Win32_LogicalDisk | ? {$_.drivetype -eq 3}).deviceid
# Process discovered drives
$drives | % {$drive = $_
gci -Path ($drive+'\$Recycle.Bin\*\$I*') -Recurse -Force | ? {($_.LastWriteTime -lt [datetime]::Now.AddDays(-$ignoreDeletedWithinDays)) -and ($_.name -like "`$*.*")} | % {
# Just a few calcs
$infoFile = $_
$originalFile = gi ($drive+"\`$Recycle.Bin\*\`$R$($infoFile.Name.Substring(2))") -Force
$originalLocation = [regex]::match([string](gc $infoFile.FullName -Force -Encoding Unicode),($drive+'[^<>:"/|?*]+\.[\w\-_\+]+')).Value
$deletedDate = $infoFile.LastWriteTime
$sid = $infoFile.FullName.split('\') | ? {$_ -like "S-1-5*"}
$user = try{(gpv "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\ProfileList\$($sid)" -Name ProfileImagePath).replace("$(gpv 'HKLM:\Software\Microsoft\Windows NT\CurrentVersion\ProfileList' -Name ProfilesDirectory)\",'')}catch{$Sid}
#' Various info
$originalLocation
$deletedDate
$user
$sid
$infoFile.Fullname
((gi $infoFile -force).length / 1mb).ToString('0.00MB')
$originalFile.fullname
((gi $originalFile -force).length / 1mb).ToString('0.00MB')
""
# Blow it all Away
#ri $InfoFile -Recurse -Force -Confirm:$false -WhatIf
#ri $OriginalFile -Recurse -Force -Confirm:$false- WhatIf
# remove comment before two lines above and the '-WhatIf' statement to delete files
}
}
# Refresh desktop icons
[WinAPI.Explorer]::Refresh()
This works well also as a script with the task scheduler.
Clear-RecycleBin -Force