My below script will genrate Error log when we have some error in the ZIP file. Zip file should cosnsist of three file if any one is missing the run.bat throw an Error.log file.
I intentionaly made a missing file in ZIP and the script should generat an error.log, but the script is throwing that the Error.log file is blank.
When I tried with break point the script is workking properly and getting mail as there is an error, if I disbale the break point and run it will not send mail. What is wrong in the script. Thank you for your support.
$errorlog = "D:\EmailConnector-Disc Optimus\logs\error.log"
$emailconnecter_log = "D:\EmailConnector-Disc Optimus\logs\connector.log"
#1 print "Running email bat file...... \n";
Write-Host "Running email bat file...... \n"
$prog="cmd.exe"
$params=#('/C','"D:\EmailConnector-Disc Optimus\run.bat"','connector.log')
start-process $prog $params -WorkingDirectory "D:\EmailConnector-Disc Optimus" -RedirectStandardOutput $emailconnecter_log
#$rc = & D:\EmailConnector-Disc Optimus\run.bat > $emailconnecter_log
if (-not $?)
{
Write-Host $rc
$status = "running Daily email bat file failed on $hostname"
Del_Daily
Del_Dat
Send_Mail
exit
}
else
{
Write-Host $rc
Write-Host "Email send sucess fully....."
}
###check for email error log
If (Test-Path $errorlog)
{
$content = Get-Content $errorlog
Write-Host "File is not blank"
Write-Host "File is not blank"
foreach ($line in $content)
{
Write-Host $line
IF ($line | Select-String -Quiet -Pattern '^\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\sERROR')
{
Write-Host $line "Done"
$status = "Daily Email Connector Has ERRORS in the log."
$body = "Check $hostname\logs or $archive_location "
Write-Host $status `n $body
# Send_Mail
$rc = Copy-Item $errorlog $archive_location
$rc = Copy-Item $emailconnecter_log $archive_location
Del_Daily
Del_Dat
exit
}
else{Write-Host "No Error message"}
}
}
else{Write-Host "File is blank"}
Related
I'm brand new to PowerShell and am working on modifying a script to combine 4 functions into one. I am having a little trouble understanding how all the pieces of the individual functions fit together. For example, it has a $msg variable that doesn't seem to be declared anywhere else in the script. So essentially i'm looking for any advice on how to make these fit.
##LogSuccess function
##Display provided message as a SUCCESS in green, with SUCCESS: prefix
##If logging mode is set to true, also write SUCCESS message to $logfileSS
Function global:LogSuccess($msg){
Write-Host "SUCCESS: " $msg -ForegroundColor "Green"
$timestamp = Get-Date
$msg = $timestamp.ToString() + ": " + $msg
if ($global:loggingmode){
Write-Output "SUCCESS: " $msg | Out-File -filepath $global:logfile -Append
}
}
##LogError function
##Display provided message as an error in red, with ERROR: prefix
##If logging mode is set to true, also write ERROR message to $logfile
Function global:LogError($msg){
Write-Host "ERROR: " $msg -ForegroundColor "Red"
$timestamp = Get-Date
$msg = $timestamp.ToString() + ": " + $msg
if ($global:loggingmode){
Write-Output "ERROR: " $msg | Out-File -filepath $global:logfile -Append
}
}
##LogWarning function
##Display provided message as a WARNING in yellow, with WARNING: prefix
##If logging mode is set to true, also write WARNING message to $logfile
Function global:LogWarning($msg){
Write-Host "WARNING: " $msg -ForegroundColor "Yellow"
$timestamp = Get-Date
$msg = $timestamp.ToString() + ": " + $msg
if ($global:loggingmode){
Write-Output "WARNING: " $msg | Out-File -filepath $global:logfile -Append
}
}
##Logging function
##Display provided message as a general information message in cyan
##If logging mode is set to true, also write information message to $logfile
Function global:Logging($msg){
Write-Host $msg -ForegroundColor "Cyan"
$timestamp = Get-Date
$msg = $timestamp.ToString() + ": " + $msg
if ($global:loggingmode){
Write-Output $msg | Out-File -filepath $global:logfile -Append
}
}
from my point of view those functions are not designed as intended, e.g.:
Function global:Logging($msg){
Write-Host $msg -ForegroundColor "Cyan"
$timestamp = Get-Date
$msg = $timestamp.ToString() + ": " + $msg
if ($global:loggingmode){
Write-Output $msg | Out-File -filepath $global:logfile -Append
}
}
PowersShell functions accept named input parameters and are outputting objects in general. In simple words this is the concept. Currently those functions do not return objects they do update/use variables with the scope global. This is a dangerous approach and not needed.
About scopes: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_scopes?view=powershell-7.2
About functions: https://learn.microsoft.com/en-us/powershell/scripting/learn/ps101/09-functions?view=powershell-7.2
Back to the one example you should do something like this:
Function write-LogFile {
<#
.Description
Enter your description here
.Parameter Message
Enter your description of the parameter here
.Parameter LogToTextFile
Enter the desscription of the parameter here
.Parameter Path
Enter the desscription of the parameter here
.Example
Enter example calls here
#>
param (
[parameter(Mandatory=$true,Position=1)]
[ValidateNotNullOrEmpty()]
[string]$Message,
[parameter(Mandatory=$true,Position=2)]
[switch]$LogToTextFile,
[parameter(Mandatory=$true,Position=3)]
[ValidateNotNullOrEmpty()]
[string]$Path
)
Begin {
}
Process {
try {
Write-Host $msg -ForegroundColor "Cyan"
$timestamp = Get-Date
$msg = $timestamp.ToString() + ": " + $msg
if ($LogToTextFile){
Write-Output $msg | Out-File -filepath $path -Append
}
Else {
$msg
}
}
Catch {
write-error $_
}
}
End{
}
}
So I think before you start to merge those function you need first to understand the concept how to write a function. The provided links should help you to find the right path...
I need to check that I have all files before running the rest of my powershell script.
What is the best way to go through the below code and after it has found each file required, continue with the rest of my script. Or if any of the files are not found, I send an email alert along with exiting the script.
# collect log one
if (Test-Path $logone) {
$one = Import-Csv -Path $logone
Write-Host "Log one found"
}
else {
#Send email "Log one not found!"
Write-Host "Log one not found!"
}
# collect log two
if (Test-Path -Path $logtwo) {
$two = Import-Csv -Path $logtwo
Write-Host "Log two found"
}
else {
#Send email "Log two not found!"
Write-Host "Log two not found!"
}
# collect log three data
if (Test-Path -Path $logthree) {
$three = Import-Csv -Path $logthree
Write-Host "Log three found"
}
else {
#Send email "Log three not found!"
Write-Host "Log three not found!"
}
Would I just add the following code below what I have:
if (Test-Path $logone) -and (Test-Path $logtwo) -and (Test-Path $logthree) {
# continue with the rest of my code
}
else {
Write-Host "Script exited with error"
Exit
}
Is there a cleaner way of doing this?
Based on I.T Delinquent's answer here an example of how to achieve this:
$logOne = "C:\Temp\log1"
$logTwo = "C:\Temp\log2"
$logThree = "C:\Temp\log3"
$mandatoryLogs = $logOne,$logTwo,$logThree
$errors = #()
$count = 0
$logs = #{}
foreach($log in $mandatoryLogs){
$count++
if(Test-Path $log){
$logs["log$count"] = Import-Csv -Path $log
Write-Host "Log $count found!"
}else{
$errors += "$log is missing"
}
}
if($errors){
$body = #"
Dear Admin,
The following errors occured: $($errors | Out-String)
Regards
"#
try{
Send-MailMessage -Body $body # -To -From etc etc
}catch{
throw $_
}
throw "Quiting because of errors, mail has been sent"
}else{
Write-Host "Continuing script" -ForegroundColor Green
}
Next you can access each of the logs in the following way:
$logs['log1']
Or:
Write-Host "$($logs['log3'].randomPropertyWhichExistsInTheCSV)"
Hope this helps!
I have this PowerShell script that I'm working on. CSV file is imported to get source and destination paths. The goal is to move files from a SFTP/FTP server into a destination and send an email report.
Task scheduler will run this code every hour. And if there's a new file, as email will be sent out.
It's almost done, but two things are missing:
Check if the file already exists and Body email seems empty: Getting the following error: Cannot validate argument on parameter 'Body'. The argument is null or empty. Provide an argument that is not
null or empty, and then try the command again.
I would like some assistance on how to check if the file exists and how to get this email if a new file was dropped and copied to the destination list
$SMTPBody = ""
$SMTPMessage = #{
"SMTPServer" = ""
"From" = ""
"To" = ""
"Subject" = "New File"
}
try {
# Load WinSCP .NET assembly
Add-Type -Path "C:\Program Files (x86)\WinSCP\WinSCPnet.dll"
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions -Property #{
Protocol = [WinSCP.Protocol]::sftp
HostName = ""
UserName = ""
Password = ""
PortNumber = "22"
FTPMode = ""
GiveUpSecurityAndAcceptAnySshHostKey = $true
}
$session = New-Object WinSCP.Session
try
{
# Connect
$session.Open($sessionOptions)
# Download files
$transferOptions = New-Object WinSCP.TransferOptions
$transferOptions.TransferMode = [WinSCP.TransferMode]::Binary
Import-Csv -Path "D:\FILESOURCE.csv" -ErrorAction Stop | foreach {
$synchronizationResult = $session.SynchronizeDirectories(
[WinSCP.SynchronizationMode]::Local, $_.Destination, $_.Source, $False)
$synchronizationResult.Check()
foreach ($download in $synchronizationResult.Downloads ) {
Write-Host "File $($download.FileName) downloaded" -ForegroundColor Green
$SMTPBody +=
"`n Files: $($download.FileName -join ', ') `n" +
"Current Location: $($_.Destination)`n"
Send-MailMessage #SMTPMessage -Body $SMTPBody
}
$transferResult =
$session.GetFiles($_.Source, $_.Destination, $False, $transferOptions)
#Find the latest downloaded file
$latestTransfer =
$transferResult.Transfers |
Sort-Object -Property #{ Expression = { (Get-Item $_.Destination).LastWriteTime }
} -Descending |Select-Object -First 1
}
if ($latestTransfer -eq $Null) {
Write-Host "No files found."
$SMTPBody += "There are no new files at the moment"
}
else
{
$lastTimestamp = (Get-Item $latestTransfer.Destination).LastWriteTime
Write-Host (
"Downloaded $($transferResult.Transfers.Count) files, " +
"latest being $($latestTransfer.FileName) with timestamp $lastTimestamp.")
$SMTPBody += "file : $($latestTransfer)"
}
Write-Host "Waiting..."
Start-Sleep -Seconds 5
}
finally
{
Send-MailMessage #SMTPMessage -Body $SMTPBody
# Disconnect, clean up
$session.Dispose()
}
}
catch
{
Write-Host "Error: $($_.Exception.Message)"
}
I believe your code has more problems than you think.
Your combination of SynchronizeDirectories and GetFiles is suspicious. You first download only the new files by SynchronizeDirectories and then you download all files by GetFiles. I do not think you want that.
On any error the .Check call will throw and you will not collect the error into your report.
You keep sending partial reports by Send-MailMessage in the foreach loop
This is my take on your problem, hoping I've understood correctly what you want to implement:
$SMTPBody = ""
Import-Csv -Path "FILESOURCE.csv" -ErrorAction Stop | foreach {
Write-Host "$($_.Source) => $($_.Destination)"
$SMTPBody += "$($_.Source) => $($_.Destination)`n"
$synchronizationResult =
$session.SynchronizeDirectories(
[WinSCP.SynchronizationMode]::Local, $_.Destination, $_.Source, $False)
$downloaded = #()
$failed = #()
$latestName = $Null
$latest = $Null
foreach ($download in $synchronizationResult.Downloads)
{
if ($download.Error -eq $Null)
{
Write-Host "File $($download.FileName) downloaded" -ForegroundColor Green
$downloaded += $download.FileName
$ts = (Get-Item $download.Destination).LastWriteTime
if ($ts -gt $latest)
{
$latestName = $download.FileName;
$latest = $ts
}
}
else
{
Write-Host "File $($download.FileName) download failed" -ForegroundColor Red
$failed += $download.FileName
}
}
if ($downloaded.Count -eq 0)
{
$SMTPBody += "No new files were downloaded`n"
}
else
{
$SMTPBody +=
"Downloaded $($downloaded.Count) files:`n" +
($downloaded -join ", ") + "`n" +
"latest being $($latestName) with timestamp $latest.`n"
}
if ($failed.Count -gt 0)
{
$SMTPBody +=
"Failed to download $($failed.Count) files:`n" +
($failed -join ", ") + "`n"
}
$SMTPBody += "`n"
}
It will give you a report like:
/source1 => C:\dest1`
Downloaded 3 files:
/source1/aaa.txt, /source1/bbb.txt, /source1/ccc.txt
latest being /source1/ccc.txt with timestamp 01/29/2020 07:49:07.
/source2 => C:\dest2
Downloaded 1 files:
/source2/aaa.txt
latest being /source2/aaa.txt with timestamp 01/29/2020 07:22:37.
Failed to download 1 files:
/source2/bbb.txt
To check and make sure the csv file exists before you process the entire thing, you can use the Test-Path,
...
if (!(Test-Path D:\FileSource.csv)) {
Write-Output "No File Found"
$SMTPBody += "There are no new files at the moment"
return; # Dont run. file not found. Exit out.
}
Import-Csv -Path "D:\FILESOURCE.csv" -ErrorAction Stop | foreach {
...
and for the Body error you are getting, it is coming from the finally loop because there are cases where $SMTPBody would be null. This will no longer be an issue because $SMTPBody will have some text when file is not found at the beginning.
Even though you are using return in the if statement to check if the file exists, finally will always get executed. Since we updated $smtpbody, your Send-MailMessage will no longer error out.
Update
If you want to check if the file you are downloading already exists, you can use the if statement like this,
foreach ($download in $synchronizationResult.Downloads ) {
if (!(Test-Path Join-Path D: $download.FileName) {
$SMTPBody += "File $($download.Filename) already exists, skipping."
continue # will go to the next download...
}
Write-Host "File $($download.FileName) downloaded" -ForegroundColor Green
...
If you do get the error regarding body, thats mostly because your script came across an exception and was sent straight over to finally statement. Finally statement sends the email with empty body because it was never set (due to exception). I would recommend using the debugger (step through) and see which step causes the exception and look into adding steps to make sure script doesnt fail.
I'm working on a powershell script that would allow me to see if my DHCP servers are running well and what's their current state (how much address left, how much of them are used...).
So far, so good. I'm getting all the info i need but, the first test here is to check if the server is responding pretty much.
For this, i'm using try-catch and i'd like to output into a .txt all of the servers that are not responding.
Problem : i'm only outputting the last server that did not respond, i'm not getting the previous one that did not respond aswell.
Tried | out-file
| set-content and | add-content
There's nothing that i've found searching seems to work.
$DHCPSRV=""
$myError=0
$myArray=#( Import-Csv .\CSV\DHCP_list.csv)
foreach ($element in $myArray) {
try {
Write-Output ""
$DHCPSRV=$element.FQDN
$Message = "Server DHCP: " + $DHCPSRV
Write-Output $Message
Write-Output ""
$srv=get-dhcpserverv4statistics -ComputerName $DHCPSRV
$Message ="Server start time : " + $srv.ServerStartTime
Write-Output $Message
$Message ="Number of address : " + $srv.TotalAddresses
Write-Output $Message
$Message ="Address used : " + $srv.AddressesInUse
Write-Output $Message
$Message ="% remaining : " + $srv.PercentageAvailable + " %"
Write-Output $Message
Get-DhcpServerSetting -ComputerName $DHCPSRV
Write-Output ""
}
catch{
Write-host "Server not responding " $DHCPSRV -BackgroundColor red -ForegroundColor White
$myerror=$error+1
$test = $DHCPSRV
}
}
if ($myError -eq 0){
Write-Output ""
Write-host "All DHCP are working good" -BackgroundColor green -ForegroundColor black
}
$test | Set-Content '.\Output\dhcp_failed.txt'
$test | Add-Content '.\Output\dhcp_failed.txt'
Write-Output ""
Write-Output ""
Write-Output "------------------------------------------------"
pause
I'd like to output all of the server that failed the try-catch test in my txt!
solved by bluuf - Thank you !
Just had to add the -append in my catch
catch{
Write-host "Server not responding " $DHCPSRV -BackgroundColor red -ForegroundColor White
$myerror=$error+1
$DHCPSRV | out-file '.\path\file.txt' -append
}
also added clear-content '.\path\file.txt' at the beginning of the script so my file get cleared every time i launch it!
I am trying to write a script to find and replace a string in a file. How do I catch an exception if the script fails to replace the string for some reason and log it in an external file? Here is what I have so far.
Write-Host "Checking Execution Policy"
$currentExecutionPolicy = Get-ExecutionPolicy
if( $currentExecutionPolicy -eq "RemoteSigned")
{
Write-Host "Execution policy check passed"
}
else
{
"Setting Execution policy to RemoteSigned as per https://msdn.microsoft.com/powershell/reference/5.1/Microsoft.PowerShell.Core/about/about_Execution_Policies"
Set-ExecutionPolicy Remotesigned
}
Write-Host "Starting Script"
#Recurse through all the file shares and find the file.
$rootPath='\\do.main.name\shared\Information Technology\IT\u.name'
$hotspotFile = Get-ChildItem -Path $rootPath -Recurse -Include "hotspot.mac"
Write-Host "Found file" $hotspotFile
$logstring = "Found file" + $hotspotFile
WriteLog $logstring
try
{
(Get-Content $hotspotFile).Replace("olddomain.com","do.main.name") | Set-Content $hotspotFile
}
catch
{
Write-Host "Failed to replace string -" $file
$logstring = "Failed to replace string -" + $file
WriteLog $logstring
}
#Logging
$Logfile = "F:\u.name\Documents\Logs\SCR_To_Find_And_Replace_Old_Domain_String.log"
Function WriteLog
{
Param ([string]$logstring)
Add-content $Logfile -value $logstring
}
Per the comments, I think a failed replace does not generate an exception so you can't use try catch.
Instead you could use an if test, e.g.:
If ($hotspotfile -notcontains "do.main.name") { }
Or test the file for the presence of the old string. You'd probably be wise to also put in an earlier if statement testing if the string was present in the file in the first place and skipping the replace and check if it wasn't.