I have an interesting situation going on where an agent was installed and it communicates back to the server. This agent is now EOL and support with the vendor was not renewed. Many of our agents are no longer communicating back to the server because its on stuck calling back to our DMZ network where the server was decommissioned. The agent has a tamper protection with password and the old admin doesn't know the password.
What I found we can do is on these servers, install the agent again and then the appliance that is on our inside network change the password to whatever, for my case I did blank.
To make things worse the agent is installed in either Program Files, Program Files (x86) and in some cases it's installed twice on the same server.
That's the background of the script I'm trying to develop. My idea was to download the agent from the appliance, detect the architecture and run the install then run an uninstall and loop it for each Program Files and/or Program Files(x86). I have it working OK, but it's not removing the application from the x86 directory. Also, I was hoping to grab some formatting tips.
if ((Get-WmiObject Win32_OperatingSystem).OSArchitecture -eq "64-bit") {
# Download Application
$source = "http://heartrate0001/x64/HeartRate.exe"
$filename = [System.IO.Path]::GetFileName($source)
$destination = "$ENV:USERPROFILE\Desktop\$filename"
$webclient = New-Object System.Net.WebClient
$webclient.DownloadFile($source,$destination)
$patha = "C:\Program Files\ICU\HeartRate.exe"
$pathb ="C:\Program Files (x86)\ICU\HeartRate.exe"
$Folder1Path = 'C:\Program Files\ICU\HeartRate.exe'
$Folder2Path = 'C:\Program Files (x86)\ICU\HeartRate.exe'
}
# Install Application
$process = Start-Process $destination -PassThru -Wait
$process.ExitCode
Write-Host $process.Exitcode
# Uninstall
if ((Test-Path -Path $Folder1Path) -eq "true") {
{
$Folder1Path = "C:\Program Files\ICU\HeartRate.exe"
$arg1 = "-uninstall"
& $Folder1Path $Arg1
}
}
if ((Test-Path -Path $Folder2Path) -eq "true") {
{
$Folder2Path = "C:\Program Files\ICU\HeartRate.exe"
$arg1 = "-uninstall"
& $Folder2Path $Arg1
}
} elseif ((Get-WmiObject Win32_OperatingSystem).OSArchitecture -eq "32-bit") {
# Download Application
$source = "http://heartrate0001/HeartRate.exe"
$destination = "$ENV:USERPROFILE\Desktop\$filename"
$webclient = New-Object System.Net.WebClient
$webclient.DownloadFile($source,$destination)
# Install Application
$process = Start-Process $destination -PassThru -Wait
$process.ExitCode
Write-Host $process.Exitcode
# Uninstall
$app = "C:\Program Files\ICU\HeartRate.exe"
$arg1 = "-uninstall"
& $app $Arg1
}
You have a script block inside your if statement. However, you never invoke that script block.
Change:
if ((Test-Path -Path $Folder2Path) -eq "true") {
{
$Folder2Path = "C:\Program Files\ICU\HeartRate.exe"
$arg1 = "-uninstall"
& $Folder2Path $Arg1
}
}
to:
if ((Test-Path -Path $Folder2Path) -eq "true") {
$Folder2Path = "C:\Program Files\ICU\HeartRate.exe"
$arg1 = "-uninstall"
& $Folder2Path $Arg1
}
This answer is purely for a formatting example as requested by OP. Most of your work was redundant:
If ((Get-WmiObject Win32_OperatingSystem).OSArchitecture -eq '64-bit')
{
$Source = 'http://heartrate0001/x64/HeartRate.exe'
$Destination = "$env:UserProfile\Desktop\$([IO.Path]::GetFileName($Source))"
# PSv3+
Invoke-WebRequest -Uri $Source -OutFile $Destination
$x64 = "$env:ProgramFiles\ICU\HeartRate.exe"
$x86 = "${env:ProgramFiles(x86)}\ICU\HeartRate.exe"
(Start-Process $Destination -PassThru -Wait).ExitCode
If (Test-Path $x64) { & $x64 -uninstall }
If (Test-Path $x86) { & $x86 -uninstall }
}
Else
{
$Source = 'http://heartrate0001/HeartRate.exe'
$Destination = "$env:UserProfile\Desktop\$([IO.Path]::GetFileName($Source))"
Invoke-WebRequest -Uri $Source -OutFile $Destination
$x86 = "$env:ProgramFiles\ICU\HeartRate.exe"
(Start-Process $Destination -PassThru -Wait).ExitCode
If (Test-Path $x86) { & $x86 -uninstall }
}
Related
I'm new to Powershell. I have 80 servers that I need to connect to and run a Pshell script on remotely to find files recursively in one share by last access date and move them to another \server\share for archiving purposes. I also need the file creation, last accessed etc. timestamps to be preserved.
I would welcome any help please
thank you
You need to test this thoroughly before actually using it on all 80 servers!
What you could do if you want to use PowerShell on this is to use Invoke-Command on the servers adding admin credentials so the script can both access the files to move as well as the destination Archive folder.
I would suggest using ROBOCOPY to do the heavy lifting:
$servers = 'Server1', 'Server2', 'Server3' # etcetera
$cred = Get-Credential -Message "Please supply admin credentials for archiving"
$scriptBlock = {
$SourcePath = 'D:\StuffToArchive' # this is the LOCAL path on the server
$TargetPath = '\\NewServer\ArchiveShare' # this is the REMOTE path to where the files should be moved
$LogFile = 'D:\ArchivedFiles.txt' # write a textfile with all fie fullnames that are archived
$DaysAgo = 130
# from a cmd box, type 'robocopy /?' to see all possible switches you might want to use
# /MINAGE:days specifies the LastWriteTime
# /MINLAD:days specifies the LastAccessDate
robocopy $SourcePath $TargetPath /MOVE /MINLAD:$DaysAgo /COPYALL /E /FP /NP /XJ /XA:H /R:5 /W:5 /LOG+:$logFile
}
Invoke-Command -ComputerName $servers -ScriptBlock $scriptBlock -Credential $cred
If you want to do all using just PowerShell, try something like this:
$servers = 'Server1', 'Server2', 'Server3' # etcetera
$cred = Get-Credential -Message "Please supply admin credentials for archiving"
$scriptBlock = {
$SourcePath = 'D:\StuffToArchive' # this is the LOCAL path on the server
$TargetPath = '\\NewServer\ArchiveShare' # this is the REMOTE path to where the files should be moved
$LogFile = 'D:\ArchivedFiles.txt' # write a textfile with all fie fullnames that are archived
$refDate = (Get-Date).AddDays(-130).Date # the reference date set to midnight
# set the ErrorActionPreference to Stop, so exceptions are caught in the catch block
$OldErrorAction = $ErrorActionPreference
$ErrorActionPreference = 'Stop'
# loop through the servers LOCAL path to find old files and move them to the remote archive
Get-ChildItem -Path $SourcePath -File -Recurse |
Where-Object { $_.LastAccessTime -le $refDate } |
ForEach-Object {
try {
$target = Join-Path -Path $TargetPath -ChildPath $_.DirectoryName.Substring($SourcePath.Length)
# create the folder in the archive if not already exists
$null = New-Item -Path $target -ItemType Directory -Force
$_ | Move-Item -Destination $target -Force
Add-Content -Path $LogFile -Value "File '$($_.FullName)' moved to '$target'"
}
catch {
Add-Content -Path $LogFile -Value $_.Exception.Message
}
}
$ErrorActionPreference = $OldErrorAction
}
Invoke-Command -ComputerName $servers -ScriptBlock $scriptBlock -Credential $cred
I have a script that will install VLC from the server. What happens when I run the script is every computer name in my text file will start the install process where you will then hit "next next next" (basically).
I was curious of how I can write these scripts for any software to just install and not prompt for all of those "next" and just install on its own. Is this possible to do?
# Run As Administrator
Function Get-FileName{
[CmdletBinding()]
Param(
[String]$Filter = "|*.*",
[String]$InitialDirectory = "C:\")
[void][System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.initialDirectory = $InitialDirectory
$OpenFileDialog.filter = $Filter
[void]$OpenFileDialog.ShowDialog()
$OpenFileDialog.filename
}
$file = Get-FileName -InitialDirectory $env:USERPROFILE\Desktop -Filter "Text files (*.txt)|*.txt|All files (*.*)|*.*"
ForEach ($item in (Get-Content $file)) {
$sitem = $item.Split("|")
$computer = $sitem[0].Trim()
$user = $sitem[1].Trim()
$filepath = Test-Path -Path "\\$computer\c$\Program Files (x86)\VideoLAN\VLC\"
If ($filepath -eq $false) {
Get-Service remoteregistry -ComputerName $computer | Start-Service
Copy-Item -Path "\\server\Unsupported Software\VLC MediaPlayer" -Destination "\\$computer\c$\windows\temp\" -Container -Recurse -Force
$InstallString = '"C:\windows\temp\VLC MediaPlayer\vlc-2.2.1-win32.exe"'
([WMICLASS]"\\$computer\ROOT\CIMV2:Win32_Process").Create($InstallString)
"$computer" + "-" + "$(Get-Date)" | Out-File -FilePath "\\server\Unsupported Software\VLC MediaPlayer\RemoteInstallfile.txt" -Append
} Else {
"$computer" + "_Already_Had_Software_" + "$(Get-Date)" | Out-File -FilePath "\\server\Unsupported Software\VLC MediaPlayer\RemoteInstallfile.txt" -Append
}
}
According to the documentation, you can just use the command line switch to install VLC like:
vlc-2.0.1-win32.exe /L=1033 /S
Where /S probably is a switch for silent installation.
As part of my continuous integration build I am creating an SQL script. This SQL script has to be checked back in to TFS after it is generated. I'm using the TFS Powertools in Powershell.
The code I used on my machine was:
Add-TfsPendingChange -Add -Item $filename | New-TfsChangeSet
This worked fine on my dev box because the folder I was in is mapped to a TFS workspace. When I move it to my build server it no longer works because TeamCity doens't map it's checkouts to a workspace it just pulls the files down.
How do I check files into a specific folder in TFS without being in a mapped workspace? Is that even possible?
I worked on something to do this for our continuous delivery project using GO. I got it working using a combination of PowerShell and the .NET assemblies provider with Team Explorer. I could not get it working purely in PowerShell (although there may be a way!)
The following script will check-in whatever is contained in the material path which is supplied as a parameter into the specified server path (also a parameter). You can also specify credentials to use and a url for the TFS server.
This code requires either Visual Studio or the TFS Team Explorer client to be installed. You need to provide the directory location of the assemblies to the script in the AssemblyPath parameter. If these assemblies are not found then the script will error and show which ones are missing.
NOTE: The code has not been checked for a while so there may be typos etc. If you have any problems let me know and I will try to help.
[CmdletBinding(PositionalBinding=$false)]
Param(
[Parameter(Mandatory)] [string] $ServerUrl,
[Parameter(Mandatory)] [string] $ServerPath,
[Parameter(Mandatory=$False)] [string] $Domain = "",
[Parameter(Mandatory=$False)] [string] $Username = "",
[Parameter(Mandatory=$False)] [System.Security.SecureString] $Password,
[Parameter(Mandatory)] [string] [ValidateScript({($_ -eq $null) -or (Test-Path -Path $_ -PathType Container)})] $MaterialPath,
[Parameter(Mandatory)] [string] [ValidateScript({ Test-Path -Path $_ -PathType Container})] $AssemblyPath
)
<#
.SYNOPSIS
Responsible for checking in files into Source Control
.DESCRIPTION
#>
$clientDllName = "Microsoft.TeamFoundation.Client.dll"
$commonDllName = "Microsoft.TeamFoundation.Common.dll"
$versionControlClientDllName = "Microsoft.TeamFoundation.VersionControl.Client.dll"
$versionControlClientCommonDllName = "Microsoft.TeamFoundation.VersionControl.Common.dll"
#Create global variables to hold the value of Debug and Verbose action preferences which can then be used for all module function calls and passed into the remote session.
$verboseParameter = $PSCmdlet.MyInvocation.BoundParameters["Verbose"]
if ($verboseParameter -ne $null)
{
$Global:Verbose = [bool]$verboseParameter.IsPresent
}
else
{
$Global:Verbose = $false
}
$debugParameter = $PSCmdlet.MyInvocation.BoundParameters["Debug"]
if ($debugParameter -ne $null)
{
$Global:Debug = [bool]$debugParameter.IsPresent
}
else
{
$Global:Debug = $false
}
$scriptName = $(Split-Path -Leaf $PSCommandPath)
#Ensure any errors cause failure
$ErrorActionPreference = "Stop"
Write-Host "Running script ""$scriptName"" as user ""$env:USERDOMAIN\$env:USERNAME"""
#Check assembly path is a valid directory
If (Test-Path -Path $AssemblyPath -PathType Container)
{
Write-Host "Loading required assemblies from assembly path ""$AssemblyPath"""
$clientDllPath = Join-Path -Path $AssemblyPath -ChildPath $clientDllName
$commonDllPath = Join-Path -Path $AssemblyPath -ChildPath $commonDllName
$versionControlClientDllPath = Join-Path -Path $AssemblyPath -ChildPath $versionControlClientDllName
$versionControlClientCommonDllPath = Join-Path -Path $AssemblyPath -ChildPath $versionControlClientCommonDllName
If (!Test-Path -Path $clientDllPath -PathType Leaf)
{
Throw "Required assembly ""$clientDllName"" not found at path ""$clientDllPath"""
}
If (!Test-Path -Path $commonDllPath -PathType Leaf)
{
Throw "Required assembly ""$commonDllName"" not found at path ""$commonDllPath"""
}
If (!Test-Path -Path $versionControlClientDllPath -PathType Leaf)
{
Throw "Required assembly ""$versionControlClientDllName"" not found at path ""$versionControlClientDllPath"""
}
If (!Test-Path -Path $versionControlClientCommonDllPath -PathType Leaf)
{
Throw "Required assembly ""$versionControlClientCommonDllName"" not found at path ""$versionControlClientCommonDllPath"""
}
#Load the Assemblies
[Reflection.Assembly]::LoadFrom($clientDllPath) | Out-Null
[Reflection.Assembly]::LoadFrom($commonDllPath)| Out-Null
[Reflection.Assembly]::LoadFrom($versionControlClientDllPath) | Out-Null
[Reflection.Assembly]::LoadFrom($versionControlClientCommonDllPath) | Out-Null
#If the credentials have been specified then create a credential object otherwise we will use the default ones
If ($Username -and $Password)
{
$creds = New-Object System.Net.NetworkCredential($Username,$Password,$Domain)
Write-Host "Created credential object for user ""$($creds.UserName)"" in domain ""$($creds.Domain)"""
$tfsProjectCollection = New-Object Microsoft.TeamFoundation.Client.TFSTeamProjectCollection($ServerUrl, $creds)
}
else
{
Write-Host "Using default credentials for user ""$Env:Username"""
$tfsProjectCollection = New-Object Microsoft.TeamFoundation.Client.TFSTeamProjectCollection($ServerUrl)
}
$versionControlType = [Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer]
$versionControlServer = $tfsProjectCollection.GetService($versionControlType)
Write-Host "Version control server authenticated user: $($versionControlServer.AuthenticatedUser)"
#Create a local path in the temp directory to hold the workspace
$LocalPath = Join-Path -Path $env:TEMP -ChildPath $([System.Guid]::NewGuid().ToString())
$null = New-Item -Path $LocalPath -ItemType Directory
#Create a "workspace" and map a local folder to a TFS location
$workspaceName = "PowerShell Workspace_{0}" -f [System.Guid]::NewGuid().ToString()
$workspace = $versionControlServer.CreateWorkspace($workspaceName, $versionControlServer.AuthenticatedUser)
$workingfolder = New-Object Microsoft.TeamFoundation.VersionControl.Client.WorkingFolder($ServerPath,$LocalPath)
$result = $workspace.CreateMapping($workingFolder)
$result = $workspace.Get() #Get the latest version into the workspace
Write-Host "Copying files from materials path ""$MaterialPath"" to temporary workspace path ""$LocalPath"""
robocopy $MaterialPath $LocalPath /s | Out-Null
$checkInComments = "Files automatically checked in by PowerShell script ""$scriptName"""
#Submit file as a Pending Change and submit the change
$result = $workspace.PendAdd($LocalPath,$true)
$pendingChanges = $workspace.GetPendingChanges()
Write-Host "Getting pending changes"
#Only try to check in if there are changes
If ($pendingChanges -ne $null)
{
If ($pendingChanges.Count -gt 0)
{
$changeSetId = $workspace.CheckIn($pendingChanges,$checkInComments)
Write-Host "Successfully checked in ""$($pendingChanges.Count)"" changes using changeset id ""$changeSetId"""
}
else
{
Write-Host "No changes to check-in"
}
}
else
{
Write-Host "No changes to check-in"
}
Write-Host "Deleting workspace and temporary folders"
$result = $workspace.Delete()
$null = Remove-Item -Path $LocalPath -Recurse -Force
}
else
{
Write-Error "The path to required assemblies ""$AssemblyPath"" cannot be found"
}
Hi I have installed the PowerShellPack and I am using the FileSystem watcher module,but the problem when I safe the file as a script and execute it.
The problem is that if you execute the script it runs and the monitors the folder for changes but once the script stops (gets to the end of execution) the folder is no longer monitored.
I have tried to place everything in a do while loop but that does not seem to work.
PowerShellPack Install
Import-Module -Name FileSystem
$TempCopyFolder = "c:\test"
$PatchStorage = "c:\testpatch"
Start-FileSystemWatcher -File $TempCopyFolder -Do {
$SearchPath = $File
$PatchesPath = $PatchStorage
$NewFolderFullPath = "$($eventArgs.FullPath)"
$NewFolderName = "$($eventArgs.Name)"
$PathToCheck = "$PatchesPath\$NewFolderName"
#Check if it is a filde or folder
switch ($ObjectType)
{{((Test-Path $NewFolderFullPath -PathType Container) -eq $true)}{$ObjectType = 1;break}
{((Test-Path $NewFolderFullPath -PathType Leaf) -eq $true)}{$ObjectType = 2;break}}
# Its a folder so lets check if we have a folder in the $PatchesPath already
IF($ObjectType -eq 1){
IF(!(Test-Path -LiteralPath $PathToCheck -EA 0))
{
sleep -Seconds 3
#Make a new directory where we store the patches
New-item -Path $PatchesPath -Name $NewFolderName -ItemType directory
#Make a folde in the folder for TC1
$TcFolder=$NewFolderName + '_1'
$NewPatchesPath = "$PatchesPath\$NewFolderName"
New-item -path $NewPatchesPath -Name $TcFolder -ItemType directory
$CopySrc = $NewFolderFullPath
$CopyDes = "$NewPatchesPath\$TcFolder"
}
# There is a folder there so lets get the next number
Else{
$HighNumber = Get-ChildItem -Path $PathToCheck | select -Last 1
#Core_SpanishLoginAttemptsConfiguration_Patch_03
$NewNumber = [int](Select-String -InputObject $HighNumber.Name -Pattern "(\d\d|\d)" | % { $_.Matches } | % { $_.Value } )+1
$TcFolder= $NewFolderName + '_' + $NewNumber
$NewPatchesPath = "$PatchesPath\$NewFolderName"
$CopySrc = $NewFolderFullPath
$CopyDes = "$NewPatchesPath\$TcFolder"
}
#Lets copy the files to their new home now that we know where every thing goes
$robocopy = "robocopy.exe"
$arguments = '''' + $CopySrc + '''' +' '+ ''''+ $CopyDes + '''' + '/E'
Invoke-Expression -Command "$robocopy $arguments"
Do {sleep -Seconds 1;$p = Get-Process "robo*" -ErrorAction SilentlyContinue}
While($p -ne $null)
#Now lets check every thing copyed
$RefObj = Get-ChildItem -LiteralPath $NewFolderFullPath -Recurse
$DifObj = Get-ChildItem -LiteralPath $CopyDes -Recurse
IF(Compare-Object -ReferenceObject $RefObj -DifferenceObject $DifObj)
{write-host "Fail"}
Else{# Now lets delete the source
Remove-Item -LiteralPath $CopySrc -Force -Recurse
}
}}
You don't need add-on modules or WMI for this. Just set of the FileSystemWatcher yourself and register an event. Sure, it's a bit more code, but at least you know what's going on. :)
$watcher = new-object System.IO.FileSystemWatcher
$watcher.Path = 'c:\logs'
$watcher.Filter = '*.log' # whatever you need
$watcher.IncludeSubDirectories = $true # if needed
$watcher.EnableRaisingEvents = $true
Register-ObjectEvent $watcher -EventName Changed -SourceIdentifier 'Watcher' -Action { param($sender, $eventArgs)
<process event here>
}
When done:
Unregister-Event -SourceIdentifier 'Watcher'
This is something you probably need: Monitoring file creation using WMI and PowerEvents module
PowerEvents Module isn't mandatory if you know how to create Permanent Events in WMI. For more information on that, check my eBook on WQL via PowerShell: http://www.ravichaganti.com/blog/?page_id=2134
When using the rm command to delete files in Powershell, they are permanently deleted.
Instead of this, I would like to have the deleted item go to the recycle bin, like what happens when files are deleted through the UI.
How can you do this in PowerShell?
2017 answer: use the Recycle module
Install-Module -Name Recycle
Then run:
Remove-ItemSafely file
I like to make an alias called trash for this.
If you don't want to always see the confirmation prompt, use the following:
Add-Type -AssemblyName Microsoft.VisualBasic
[Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile('d:\foo.txt','OnlyErrorDialogs','SendToRecycleBin')
(solution courtesy of Shay Levy)
It works in PowerShell pretty much the same way as Chris Ballance's solution in JScript:
$shell = new-object -comobject "Shell.Application"
$folder = $shell.Namespace("<path to file>")
$item = $folder.ParseName("<name of file>")
$item.InvokeVerb("delete")
Here is a shorter version that reduces a bit of work
$path = "<path to file>"
$shell = new-object -comobject "Shell.Application"
$item = $shell.Namespace(0).ParseName("$path")
$item.InvokeVerb("delete")
Here's an improved function that supports directories as well as files as input:
Add-Type -AssemblyName Microsoft.VisualBasic
function Remove-Item-ToRecycleBin($Path) {
$item = Get-Item -Path $Path -ErrorAction SilentlyContinue
if ($item -eq $null)
{
Write-Error("'{0}' not found" -f $Path)
}
else
{
$fullpath=$item.FullName
Write-Verbose ("Moving '{0}' to the Recycle Bin" -f $fullpath)
if (Test-Path -Path $fullpath -PathType Container)
{
[Microsoft.VisualBasic.FileIO.FileSystem]::DeleteDirectory($fullpath,'OnlyErrorDialogs','SendToRecycleBin')
}
else
{
[Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile($fullpath,'OnlyErrorDialogs','SendToRecycleBin')
}
}
}
Remove file to RecycleBin:
Add-Type -AssemblyName Microsoft.VisualBasic
[Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile('e:\test\test.txt','OnlyErrorDialogs','SendToRecycleBin')
Remove folder to RecycleBin:
Add-Type -AssemblyName Microsoft.VisualBasic
[Microsoft.VisualBasic.FileIO.FileSystem]::Deletedirectory('e:\test\testfolder','OnlyErrorDialogs','SendToRecycleBin')
Here's slight mod to sba923s' great answer.
I've changed a few things like the parameter passing and added a -WhatIf to test the deletion for the file or directory.
function Remove-ItemToRecycleBin {
Param
(
[Parameter(Mandatory = $true, HelpMessage = 'Directory path of file path for deletion.')]
[String]$LiteralPath,
[Parameter(Mandatory = $false, HelpMessage = 'Switch for allowing the user to test the deletion first.')]
[Switch]$WhatIf
)
Add-Type -AssemblyName Microsoft.VisualBasic
$item = Get-Item -LiteralPath $LiteralPath -ErrorAction SilentlyContinue
if ($item -eq $null) {
Write-Error("'{0}' not found" -f $LiteralPath)
}
else {
$fullpath = $item.FullName
if (Test-Path -LiteralPath $fullpath -PathType Container) {
if (!$WhatIf) {
Write-Verbose ("Moving '{0}' folder to the Recycle Bin" -f $fullpath)
[Microsoft.VisualBasic.FileIO.FileSystem]::DeleteDirectory($fullpath,'OnlyErrorDialogs','SendToRecycleBin')
}
else {
Write-Host "Testing deletion of folder: $fullpath"
}
}
else {
if (!$WhatIf) {
Write-Verbose ("Moving '{0}' file to the Recycle Bin" -f $fullpath)
[Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile($fullpath,'OnlyErrorDialogs','SendToRecycleBin')
}
else {
Write-Host "Testing deletion of file: $fullpath"
}
}
}
}
$tempFile = [Environment]::GetFolderPath("Desktop") + "\deletion test.txt"
"stuff" | Out-File -FilePath $tempFile
$fileToDelete = $tempFile
Start-Sleep -Seconds 2 # Just here for you to see the file getting created before deletion.
# Tests the deletion of the folder or directory.
Remove-ItemToRecycleBin -WhatIf -LiteralPath $fileToDelete
# PS> Testing deletion of file: C:\Users\username\Desktop\deletion test.txt
# Actually deletes the file or directory.
# Remove-ItemToRecycleBin -LiteralPath $fileToDelete
Here is a complete solution that can be added to your user profile to make 'rm' send files to the Recycle Bin. In my limited testing, it handles relative paths better than the previous solutions.
Add-Type -AssemblyName Microsoft.VisualBasic
function Remove-Item-toRecycle($item) {
Get-Item -Path $item | %{ $fullpath = $_.FullName}
[Microsoft.VisualBasic.FileIO.FileSystem]::DeleteFile($fullpath,'OnlyErrorDialogs','SendToRecycleBin')
}
Set-Alias rm Remove-Item-toRecycle -Option AllScope