I have been handed over a Powershell script that searches a folder for new items, if there are any items, it sends an email with the items attached and moves them to another directory. However, my problem is that the files in the first directory will always be named the same so i would need to rename the files as i move them to the back-up directory with date and time to prevent the older files from being overwritten. How would i go about the code to do so?
I should probably add that i have no experience with powershell, hence the probably stupid question.
$Folders = #( 'C:\Users\PAT\Desktop\attachement testfolder'
)
$DestFolder = 'C:\Users\PAT\Desktop\Backup folder'
$EmailList = #( 'recieving-email#hotmail.com'
)
$From = 'pat#email.com'
$Subject = 'New files'
$SMTPServer = '111.111.111.111'
# Get new files
$NewFiles = $Folders | Get-ChildItem -File -Recurse
# If there are an new files...
If ( $NewFiles )
{
# Add the file count and file names to the body of the email
$Body = '$($NewFiles.Count) new files attached'
$NewFiles | ForEach { $Body += $_.Name + "" }
# Send the email with attachments
Send-MailMessage -From $From -To $EmailList -Subject $Subject -Body $Body -
BodyAsHTML -Attachments $NewFiles.FullName -SmtpServer $SMTPServer
# Move the emailed files to the destination folder
$NewFiles.FullName | Move-Item -Destination $DestFolder -Force
}
Ended up figuring it out
I changed the Move-Item with this code
# Rename files with current date and time and moves them to backup directory
$NewFiles | ForEach-Object {
Move-Item -Path $_.FullName -Destination "C:\Users\PAT\Desktop\Backup
mappe\$($_.BaseName,(Get-Date).ToString("MMddyyyy hhmmss"),$_.Extension)"}
Also, as an alternative:
$NewFiles | Rename-Item -NewName { "$(Get-Date -Format "yyyyMMddHHmmss")-$($_.Name)" } -PassThru | Move-Item -Destination $DestFolder
Related
I have a question that I hope one of you can help me with.
The following script does its job, it sends the email of the folders that are older than 30 days.
I want to add 2 more features...
If the OwnerEmail.txt is not there or empty.. it will not send the email.
If there are not folders older than 30 days, it will not send the email.
I am just having problems using the CONTINUE feature to skip the ForEach
$ParentFolders = Get-ChildItem -Path "C:\Test\WRA" -Directory |
Select-Object -ExpandProperty FullName
foreach ($SubFolder in $ParentFolders) {
$Contact = Get-Content -Path ($SubFolder + "\OwnerEmail.txt")
$emailTime = (Get-Date).ToString("MM/dd/yyyy")
$WRAHTMLReport = Get-ChildItem -Path $SubFolder -Directory |
Where-Object {($_.LastWriteTime -lt (Get-Date).AddDays(-30))} |
Select-Object Name, CreationTime |
ConvertTo-Html -Head $Header -PreContent "
<h2>Current Nuix Case(s)</h2>
<body>
Report was sent on $emailTime
<br></br>
When you have a moment can you please confirm if any of these cases can be removed from Nuix:
<body>
<br></br> " |
$WRAHTMLReport = $WRAHTMLReport -join ""
Start-Sleep -s 2
Send-MailMessage -SmtpServer mail.company.com -to $Contact -from cubam1#company.com -Subject "Nuix Cases" -Body $WRAHTMLReport -BodyAsHtml
}
So, it could look something like this, note that I'm only showing the relevant parts of the code.
I think in this case, is safe to put the $emailTime outside the loop since you're only gathering the date in MM/dd/yyyy format and, I also added the $folderLimitDate outside the loop.
This variable contains the HTML body
$mailBody = ConvertTo-Html -InputObject $WRAHTMLReport... so remember to change the -Body $mailBody on Send-MailMessage
$emailTime = (Get-Date).ToString("MM/dd/yyyy")
$folderLimitDate = (Get-Date).AddDays(-30)
foreach ($SubFolder in $ParentFolders)
{
$path = Join-Path $SubFolder -ChildPath 'OwnerEmail.txt'
# If OwnerEmail.txt doesn't exist on this path,
# go to next loop iteration
if(-not(Test-Path $path)){ continue }
$Contact = Get-Content $path
$WRAHTMLReport = Get-ChildItem -Path $SubFolder -Directory |
Where-Object LastWriteTime -LT $folderLimitDate |
Select-Object Name, CreationTime
# If this variable is null, go to next loop iteration
if(-not $WRAHTMLReport){ continue }
[string]$mailBody = ConvertTo-Html -InputObject $WRAHTMLReport...
...
...
...
I am adding an email function to send an email if the file uploads success or failure, but when I upload the file, it sends a message as I write in the body for success but I am not sure if it captures the error function as well in the email.
I upload the file as per the naming convention and it moved successfully.
When I tried to upload the wrong file through this, it does not capture the catch statement in the log file.
I want to capture the backup copy name in logs as well.
Got an email on the successful event but didn't get an email on the catch statement.
Please see the below code.
$Source = 'c:\uploadtool\' # Source Location
$RetailSource = 'Retail P&C Sales Intelligence\*'
$GroupSource = 'Group P&C Sales Intelligence\*'
$RetailDest = 'D:\ToolUpload\Retail-EIP' # 1st Destination Location
$GroupDest = 'D:\ToolUpload\Group-EIP' # 2nd Destination location
$ArchiveData = 'D:\Backup\backup_{0:yyyyMMddHHmm}' -f (Get-Date)
$LogFolder = 'D:\logs'
# because of your wish to get a Table-style log, use CSV format you can open in Excel
$LogFile = 'D:\logs\uploadlog_{0:yyyyMMdd}.csv'-f (Get-Date)
$Sourcetest = Test-Path -Path '$RetailSource','$GroupSource' -PathType Leaf
$SmtpServer ='essexlake1.mail.protection.outlook.com' # SMTP Server name
$SmtpPort='25'
$Subject='File Upload Status'
$To="####domain.com"
$From="#####domain.com"
# make sure the output LogFolder exist
# by adding the -Force switch there is no need to use Test-Path first, because if
# the folder already exists, the cmdlet will return the DirectoryInfo of that,
# otherwise it will create a new folder. Since we dont want output, we use $null = ..
$null = New-Item -Path $LogFolder -ItemType Directory -Force
# loop through the files in the source folder and collect the outputted objects
$result = Get-ChildItem -Path $Sourcetest -Include '*Group-EIP*', '*Retail-EIP*' -File -Force -Recurse |
ForEach-Object {
Write-Host "Processing file '$($_.FullName)'"
# create an object with (for now) 3 empty properties
$out = $_ | Select-Object #{Name = 'Date'; Expression = {(Get-Date)}},
#{Name = 'Source'; Expression = {$_.FullName}},
#{Name = 'FileSize'; Expression = {$_.Length}},
Destination, # depends on the file name
#{Name = 'Archive'; Expression = {$ArchiveData}}, # initialize to Not Applicable
Result
# depending on its name, get the correct destination folder
$destFolder = if($_.Name -match "Retail-EIP") { $RetailDest } else { $GroupDest }
# create the backup destination folder if it didn't already exist
# the first file in column 'Source' is now responsible for creating the backup folder
$null = New-Item -Path $destFolder -ItemType Directory -Force
# get the full path and filename for the destination
$existingFile = Join-Path -Path $destFolder -ChildPath $_.Name
# add the destination folder to the output object
$out.Destination = $destFolder
try {
# if a file with that name already exists in the destination, move it to the Archive folder
if (Test-Path -Path $existingFile -PathType Leaf) {
# create the Archive folder if it didn't already exist
$null = New-Item -Path $ArchiveData -ItemType Directory -Force
Move-Item -Path $existingFile -Destination $ArchiveData -ErrorAction Stop
# add the archived file to the output object
$out.Archive = $existingFile
Write-Host "File '$existingFile' has been backed-up to '$ArchiveData'"
}
# next move the file from the source folder to its destination (either $RetailDest or $GroupDest)
$_ | Move-Item -Destination $destFolder -ErrorAction Stop
$out.Result = 'OK'
Write-Host "File '$($_.FullName)' has been moved to '$destFolder'"
$Body=" The File '$($_.FullName)' has been moved to '$destFolder"
Send-MailMessage -SmtpServer $SmtpServer -Port $Port -From $From -To $To -Subject $Subject -Body $Body
}
catch {
# ouch.. something went horribly wrong on a Move-Item action
Write-Warning "An error occurred: $_.Exception.Message"
$out.Result = "Error: $($_.Exception.Message)" | Add-Content -Path $LogFile -Force
}
# output the object so it gets collected in variable $result
$out
}
# now you can save the results as structured CSV file to open in Excel
$result | Export-Csv -Path $LogFile -UseCulture -NoTypeInformation -Append
# and display on screen using Out-GridView as the data will probably be too wide for Format-Table
$result | Out-GridView -Title 'Backup results
'
It's not going to send an email on-error because Send-MailMessage is the last statement in the Try{...} block. If anything errors before that the statement Send-MailMessage won't run.
If it were me, and from this starting point I'd populate $Body respective to the outcome (success/fail) and send the email afterward. Not only would this send with correct information regardless but avoids having a second Send-MailMessage command in the catch block. Obviously I can't rig up a test of such a big code segment, but it might look something like:
$Source = 'c:\uploadtool\' # Source Location
$RetailSource = 'Retail P&C Sales Intelligence\*'
$GroupSource = 'Group P&C Sales Intelligence\*'
$RetailDest = 'D:\ToolUpload\Retail-EIP' # 1st Destination Location
$GroupDest = 'D:\ToolUpload\Group-EIP' # 2nd Destination location
$ArchiveData = 'D:\Backup\backup_{0:yyyyMMddHHmm}' -f (Get-Date)
$LogFolder = 'D:\logs'
# because of your wish to get a Table-style log, use CSV format you can open in Excel
$LogFile = 'D:\logs\uploadlog_{0:yyyyMMdd}.csv'-f (Get-Date)
$Sourcetest = Test-Path -Path '$RetailSource','$GroupSource' -PathType Leaf
#Email Params:
$EmailParams = #{
SmtpServer ='essexlake1.mail.protection.outlook.com' # SMTP Server name
Port ='25'
Subject ='File Upload Status'
To ="####domain.com"
From ="#####domain.com"
}
# make sure the output LogFolder exist
# by adding the -Force switch there is no need to use Test-Path first, because if
# the folder already exists, the cmdlet will return the DirectoryInfo of that,
# otherwise it will create a new folder. Since we dont want output, we use $null = ..
$null = New-Item -Path $LogFolder -ItemType Directory -Force
# loop through the files in the source folder and collect the outputted objects
$result =
Get-ChildItem -Path $Sourcetest -Include '*Group-EIP*', '*Retail-EIP*' -File -Force -Recurse |
ForEach-Object {
Write-Host "Processing file '$($_.FullName)'"
# create an object with (for now) 3 empty properties
$out = $_ |
Select-Object #{Name = 'Date'; Expression = {(Get-Date)}},
#{Name = 'Source'; Expression = {$_.FullName}},
#{Name = 'FileSize'; Expression = {$_.Length}},
Destination, # depends on the file name
#{Name = 'Archive'; Expression = {$ArchiveData}}, # initialize to Not Applicable
Result
# depending on its name, get the correct destination folder
$destFolder = if($_.Name -match "Retail-EIP") { $RetailDest } else { $GroupDest }
# create the backup destination folder if it didn't already exist
# the first file in column 'Source' is now responsible for creating the backup folder
$null = New-Item -Path $destFolder -ItemType Directory -Force
# get the full path and filename for the destination
$existingFile = Join-Path -Path $destFolder -ChildPath $_.Name
# add the destination folder to the output object
$out.Destination = $destFolder
try
{ # if a file with that name already exists in the destination, move it to the Archive folder
if (Test-Path -Path $existingFile -PathType Leaf) {
# create the Archive folder if it didn't already exist
$null = New-Item -Path $ArchiveData -ItemType Directory -Force
Move-Item -Path $existingFile -Destination $ArchiveData -ErrorAction Stop
# add the archived file to the output object
$out.Archive = $existingFile
Write-Host "File '$existingFile' has been backed-up to '$ArchiveData'"
}
# next move the file from the source folder to its destination (either $RetailDest or $GroupDest)
$_ | Move-Item -Destination $destFolder -ErrorAction Stop
$out.Result = 'OK'
Write-Host "File '$($_.FullName)' has been moved to '$destFolder'"
$Body = " The File '$($_.FullName)' has been moved to '$destFolder"
}
catch
{ # ouch.. something went horribly wrong on a Move-Item action
$Body = " Error occured trying to move the file '$($_.FullName)' to '$destFolder. `n$($_.Exception.Message)"
Write-Warning "An error occurred: $_.Exception.Message"
$out.Result = "Error: $($_.Exception.Message)" | Add-Content -Path $LogFile -Force
}
Send-MailMessage #EmailParams -Body $Body
# output the object so it gets collected in variable $result
$out
}
# now you can save the results as structured CSV file to open in Excel
$result | Export-Csv -Path $LogFile -UseCulture -NoTypeInformation -Append
# and display on screen using Out-GridView as the data will probably be too wide for Format-Table
$result | Out-GridView -Title 'Backup results'
Forgive the reformat, that was for my own readability. I also added splatting to the email command. You were setting all those parameter vars early in the script any way...
Note: for all other parts of this question I think the straight forward answer is you have to add the instructions to tell the program what you want it to do. If you want to log on a specific condition, figure out where that's going to happen and add an Add-Content command etc...
Trying to accomplish the following:
1)Move files from multiple sources into multiple destinations.
Therefore i listed sources under a SOURCE column, and destinations under a DESTINATION column (WORKING)
2)Send an email with a list of files that were moved and the destination (Not working)
Param (
[string]$SMTPServer = "XXX.XX.xXX",
[string]$From = "from#email.com",
[string]$To = "to#email.com",
[string]$Subject = "New File"
)
Import-Csv -Path "C:\moveallfiles.csv" -ErrorAction Stop | foreach {
if (-not (Test-Path -Path $_.Destination))
{
# Create directory if needed
New-Item -ItemType directory -Path $_.Destination -WhatIf
}
# Copy file
$MoveFileprocess = Move-Item -Path $_.Source -Destination $_.Destination -force -PassThru
}
$SMTPMessage = #{
To = $To
From = $From
Subject = "$Subject"
Smtpserver = $SMTPServer
}
$SMTPBody = "`nThe following files have recently been added `n
$MoveFileprocess `n"
Send-MailMessage #SMTPMessage -Body $SMTPBody
Right now its listing all the files in one single line, hard to read
I was wondering if theres a way to send the body email as
File 1 moved to Destination 1
File 2 moved to Destination 2
and so on...
You were pretty close. Try this:
Param (
[string]$SMTPServer = "XXX.XX.xXX",
[string]$From = "from#email.com",
[string]$To = "to#email.com",
[string]$Subject = "New File"
)
$SMTPBody = "`nThe following files have recently been added `n"
Import-Csv -Path "C:\moveallfiles.csv" -ErrorAction Stop | foreach {
if (-not (Test-Path -Path $_.Destination)){
# Create directory if needed
New-Item -ItemType directory -Path $_.Destination
}
If((Get-Item $_.Source).LastWriteTime -gt (Get-Item $_.Destination).LastWriteTime){
Move-Item -Path $_.Source -Destination $_.Destination -force
$SMTPBody += "$($_.Source) moved to $($_.Destination)`n"
}Else{
Write-Warning "Skipped overwriting newer $($_.Destination)"
}
}
$SMTPMessage = #{
To = $To
From = $From
Subject = "$Subject"
Smtpserver = $SMTPServer
}
Send-MailMessage #SMTPMessage -Body $SMTPBody
Note that Move-Item -PassThru returns the affected object, not a string. I took out the -WhatIf parameter for the New-Item command, indented the foreach{} loop and made indenting and brackets consistent.
I am really trying to speed this script up. I have a directory with about 17k files in it:
$date= Get-Date -Format yyyyMMdd
$dir= "C:\test\$date"
$path=Get-ChildItem -Path $dir -Recurse
$pattern = "<RESULT>FAILED</RESULT>"
$submitted = (select-string -path $path -pattern $pattern | measure-object).Count
select-string -path $path -pattern $pattern | select Path,Filename,Line | Export-Csv -Path "D:\Failed.csv"
if($submitted -eq 0) {
Remove-Item "D:\Failed.csv" -recurse
}
else
{
Send-MailMessage -From "noreply#email.com" -To users#email.com -Subject "Failed Report" -Body "Attached are the failed files. This is for the last 3 hours. There may be files that were already reported." -Attachments "D:\Failed.csv" -SmtpServer 0.0.0.0
Remove-Item "D:\Failed.csv" -recurse
}
If Select-String is taking a long time in the script above, try only doing it once. Also, I see that you check the count and if nothing is going on, you delete that CSV you made. So howabout you count it first, and then only make it if you need to.
...
$submitted = select-string -path $path -pattern $pattern
...
if(($submitted | Measure-Object).Count -gt 0){
...make the csv using the $submitted variable as the source...
...send the csv...
...delete the csv...
}
I've whipped up a script that deletes old video directories, and it will create a log of what has been deleted in a .txt file.
Running the script on my Windows 7 PC, the .txt file in Notepad puts each output on a new line.
Running the script on my Windows 2012 Server, the .txt file in Notepad puts the output on the same line. If I open the log file with Wordpad, the outputs are on new lines.
Here's what the powershell script looks like:
$limit = (Get-Date).AddDays(0)
$logFile = "C:\BodyCam\bodycamlog.txt"
$output = "The following directories contain incorrect video dates" + "`n"
#Throw a timestamp on the log file
Add-Content -Path $logFile "=======$(Get-Date)======="
#Get all of the paths for each camera
$paths = Get-ChildItem -Path "D:\Videos\" |Select-Object -ExpandProperty FullName
#for each path we found
foreach ($pa in $paths) {
# Delete directories older than the $limit.
$dir = Get-ChildItem -Path $pa -Recurse -Force | Where-Object { $_.PSIsContainer -and $_.CreationTime -lt $limit }
$dir | Remove-Item -Recurse -Force
$dir | Select -Expand FullName | Out-File $logFile -append
if ($dir -match "1970"){
$output += $pa + "`n"
}
}
Send-MailMessage -to "Ray <***>" -from "BodyCamScript <***>" -subject "Bad Bodycam Date Found" -body $output -SmtpServer ***
What's the deal here?
The default encoding for Add-Content is ASCII, but the default for Out-File is Unicode. Use the encoding parameter on both to force the same encoding and it should work as you expect.