MS SQL Skrypt Power Shell backup

Wraz ze sztuczną inteligencją stworzyłem jakiś czas skrypt PowerShell mający na celu wykonywanie kopii baz danych MS SQL, kompresji za pomocą 7z a następnie przenoszenia tych kopii na FTP i do katalogu który jest synchronizowany z chmurą.

Na FTP przenoszone są pliki z datą wykonania kopii i usuwane starsze pliki, zaś do katalogu chmury kopie są numerowane dniem tygodnia w celu ograniczenia ilości kopii gdyż usunięcie pliku z katalogu nie usuwa z chmury więc muszą być nadpisywane, przynajmniej w moim przypadku właśnie tak się działo.

Nie będę opisywał kolejnych linii kodu i tłumaczył jak to rozwiązanie działa, tak więc ten skrypt jest dla ambitnych którym się chce i są ambitni w jego rozszyfrowaniu. Być może znajdziecie elementy które zrobili byście inaczej lub łatwiej więc z racji iż ten twór jest zrobiony na spółkę ze sztuczną inteligencją możecie śmiało modyfikować i wykorzystywać do własnych celów.

# Ścieżka, gdzie zostaną zapisane kopie baz danych
$backupPath = "E:\BACKUP\TMP"

# Parametry połączenia do serwera SQL
$serverName = ".\MSSQLEXPRESS"  # Zastąp odpowiednimi danymi
$username = "sa"  # Zastąp odpowiednimi danymi
$password = "MSSQLP@$w00rd"  # Zastąp odpowiednimi danymi
# Ustawienie timeout na 600 sekund (10 minut)
$timeout = 600

# Wyślij pliki ZIP na serwer FTP
$ftpServer = "192.168.1.8"
$ftpUsername = "itbackup"
$ftpPassword = "FTPP@$w00rd"
$ftpDestinationDirectory = "/BACKUP/"
#MOBILBOX/"

# Ścieżka docelowa dla skopiowanych plików ZIP
$destinationPath = "E:\BACKUP\CLOUD\CATBAZY"

# Liczba dni, po których starsze pliki zostaną usunięte
$daysToKeep = 7
$daysToKeepTMP = 3

# Tablica z nazwami baz danych do wykonania kopii
$databasesToBackup = @("baza1")

$LogFile = "E:/BACKUP/LOG/" + (Get-Date -Format "yyyyMMdd") + ".MOBILEBOX.log"

# Funkcja do wykonywania kopii baz danych z kompresją do ZIP za pomocą 7-Zip
function Backup-SelectedDatabasesWithCompression {
    param (
        [string]$serverName,
        [string]$username,
        [string]$password,
        [string]$backupPath,
        [int]$timeout,
        [string[]]$databasesToBackup
    )

    # Utwórz połączenie do serwera SQL
    $connectionString = "Server=$serverName;Database=master;User Id=$username;Password=$password;"
    $connection = New-Object System.Data.SqlClient.SqlConnection
    $connection.ConnectionString = $connectionString
    $connection.Open()

    $sevenZipExe = "C:\Program Files\7-Zip\7z.exe"  # Ścieżka do pliku wykonywalnego 7-Zip

    $zipFileNames = @()  # Tablica do przechowywania nazw plików ZIP

    foreach ($databaseName in $databasesToBackup) {
        
    "[START]		"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " " + $databaseName >> $LogFile
        # Utwórz ścieżkę do kopii danej bazy danych z datą i nazwą bazy
        $timestamp = Get-Date -Format "yyyyMMddHHmmss"
        $databaseBackupPath = Join-Path $backupPath "$timestamp-$databaseName.bak"

        # Utwórz kopię danej bazy danych	
        $backupCommand = $connection.CreateCommand()
        $backupCommand.CommandText = "BACKUP DATABASE $databaseName TO DISK = '$databaseBackupPath'"
        $backupCommand.CommandTimeout = $timeout  # Ustaw timeout
        try {
            $backupCommand.ExecuteNonQuery()
            Write-Host "[BAK] OK	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	Kopia bazy danych wykonana poprawnie."
            "[BAK] OK	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	Kopia bazy danych wykonana poprawnie." >> $LogFile
        } catch {
            Write-Host "ERROR Kopia bazy danych $databaseName została wykonana i zapisana w $databaseBackupPath"
            "[BAK] ERROR	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	Kopia bazy danych nie została wykonana." >> $LogFile
        }

        # Kompresuj kopię do formatu ZIP za pomocą 7-Zip (po udanej kopii)
        $zipFileName = "$timestamp-$databaseName.zip"
        $zipFileNames += $zipFileName

        Compress-DatabaseBackupToZIPWith7Zip $databaseBackupPath $zipFileName
        #Write-Host "Kopia bazy danych $databaseBackupPath została skopresowana do $zipFileName"

        # Usuń plik BAK po udanej kompresji
        
        $backupCommand.CommandTimeout = $timeout  # Ustaw timeout
        try {
            Remove-Item $databaseBackupPath -Force
            Write-Host "[BAK] OK	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	Usunięto okuj BAK po kompresji."
            "[BAK] OK	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	Usunięto okuj BAK po kompresji." >> $LogFile
        } catch {
            Write-Host "[BAK] INFO	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	Nie usunięto okuj BAK po kompresji."
            "[BAK] INFO	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	Nie usunięto okuj BAK po kompresji." >> $LogFile
        }

        Send-ZipFilesToFTP -zipFileName $zipFileName -ftpServer $ftpServer -ftpUsername $ftpUsername -ftpPassword $ftpPassword -ftpDestinationDirectory	$ftpDestinationDirectory -backupPath $backupPath -desiredString $databaseName -databaseName $databaseName -daysToKeep $daysToKeep
        	
	    Remove-OldFilesWithPattern -path $backupPath -daysToKeep $daysToKeepTMP -databaseName $databaseName
    }

    $connection.Close()
	"[END]   	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + " " + $databaseName >> $LogFile
}

# Funkcja do kompresji pliku do formatu ZIP za pomocą 7-Zip
function Compress-DatabaseBackupToZIPWith7Zip {
    param (
        [string]$sourcePath,
        [string]$zipFileName
    )

    $sevenZipExe = "C:\Program Files\7-Zip\7z.exe"  # Ścieżka do pliku wykonywalnego 7-Zip
    Write-Host "$zipFileName $sourcePath"

    # Kompresuj plik do formatu ZIP za pomocą 7-Zip
    try {
        & $sevenZipExe a -tzip $backupPath\$zipFileName $sourcePath
        Write-Host "[ZIP] OK	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	Kompresja wykonana poprawnie."
        "[ZIP] OK	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	Kompresja wykonana poprawnie." >> $LogFile
    } catch {
        Write-Host "[ZIP] ERROR	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	Kopresja nie została wykonana."
        "[ZIP] ERROR	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	Kopresja nie została wykonana." >> $LogFile
    }
}

function Delete-FTPFileList($ftpServer, $ftpUsername, $ftpPassword, $ftpDirectory,$databaseName) {
    $request = [System.Net.FtpWebRequest]::Create("ftp://$ftpServer$ftpDirectory")
    $request.Credentials = New-Object System.Net.NetworkCredential($ftpUsername, $ftpPassword)
    $request.Method = [System.Net.WebRequestMethods+Ftp]::ListDirectory

    $response = $request.GetResponse()
    $reader = New-Object System.IO.StreamReader($response.GetResponseStream())
    $directoryListing = $reader.ReadToEnd()

    $reader.Close()
    $response.Close()

    #$directoryListing = $directoryListing -split "`r`n"
    #$filteredFiles = $directoryListing -split "`r`n" | Where-Object { $_ -like "*$databaseName*" }
    
    #-------------------
       # Inicjalizuj pustą tablicę na dane plików
    $fileDataArray = @()

    # Iteruj przez listę plików
    foreach ($fileInfo in $directoryListing -split "`n") {
    Write-Host $fileInfo
        # Podziel informacje o pliku
        #$fileInfoArray = $fileInfo -split("\s+")

        # Uzyskaj nazwę pliku
        $fileName = $fileInfo
        if ($fileName -and $fileName -like "*$databaseName*") {

            # Spróbuj wyciągnąć 12 cyfr z nazwy pliku i potraktować jako datę i czas w formacie yyyyMMddHHmm
            $fileNumber = $fileName -replace '[^\d]', ''  # Usuń wszystko, co nie jest cyfrą
            $fileNumber = $fileNumber.Substring(0, [Math]::Min(12, $fileNumber.Length))  # Weź 12 pierwszych cyfr

            try {
                $parsedDateTime = [datetime]::ParseExact($fileNumber, "yyyyMMddHHmm", $null)

                # Dodaj nazwę pliku i datę do tablicy
                $fileDataArray += [PSCustomObject]@{
                    FileName = $fileName
                    FileDateTime = $parsedDateTime
                }
            }
            catch {
                Write-Host ("Nie udało się sparsować 12 cyfr jako daty i czasu: {0}" -f $_.Exception.Message)
            }
        }
    }

    # Sortuj tablicę według daty
    $fileDataArray = $fileDataArray | Sort-Object FileDateTime

    # Pobierz 14 najmłodszych plików
    $youngestFiles = $fileDataArray | Select-Object -First 14

    # Pobierz pliki starsze niż 14 najmłodszych
    $olderFiles = $fileDataArray | Where-Object { $_.FileDateTime -lt $youngestFiles[-1].FileDateTime }

    # Wyświetl pierwsze pliki pomijając 14 ostatnich, gdy jest ich więcej niż 14
    $filesToDisplay = $fileDataArray | Select-Object -First ([Math]::Max(0, ($fileDataArray.Count - 14)))

    # Zwróć tablicę zawierającą wyłącznie nazwy plików
    $fileNamesArray = $filesToDisplay | ForEach-Object { $_.FileName }

    # Usuń pliki na serwerze FTP na podstawie listy nazw plików
    foreach ($fileNameToDelete in $fileNamesArray) {
      Write-Host "ftp://$ftpServer$ftpDirectory$fileNameToDelete"


        $ftpDeletePath = "ftp://$ftpServer$ftpDirectory$fileNameToDelete"
        
        $ftpDeleteRequest = [System.Net.FtpWebRequest]::Create($ftpDeletePath)
        $ftpDeleteRequest.Credentials = New-Object System.Net.NetworkCredential($ftpUsername, $ftpPassword)
        $ftpDeleteRequest.Method = [System.Net.WebRequestMethods+Ftp]::DeleteFile

        try {
            $ftpDeleteResponse = $ftpDeleteRequest.GetResponse()
            Write-Host "[FTP] OK	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss")  +"	Plik $fileNameToDelete usunięty z FTP."
            "[FTP] OK	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	Plik $fileNameToDelete usunięty z FTP." >> $LogFile
        }
        catch {
            Write-Host "[FTP] ERROR	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss")  +"	Plik $fileNameToDelete usunięty z FTP."
            "[FTP] ERROR	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	Plik $fileNameToDelete usunięty z FTP." >> $LogFile
        }


    }
    #---------------------

    return $directoryListing
}



# Funkcja do wysyłania plików na serwer FTP
function Send-ZipFilesToFTP {
    param (
        [string[]]$zipFileNames,
        [string]$ftpServer,
        [string]$ftpUsername,
        [string]$ftpPassword,
        [string]$ftpDestinationDirectory,
        [string]$backupPath,
        [string]$desiredString,
        [string]$databaseName,
        [string]$daysToKeep
    )

    $ftpSession = New-Object System.Net.WebClient

    # Ustaw parametry autoryzacji
    $ftpSession.Credentials = New-Object System.Net.NetworkCredential($ftpUsername, $ftpPassword)

    # Pobierz listę plików na serwerze FTP
    $ftpFiles = Delete-FTPFileList $ftpServer $ftpUsername $ftpPassword $ftpDestinationDirectory $databaseName


    foreach ($zipFileName in $zipFileNames) {
        $sourcePath = Join-Path $backupPath $zipFileName
        if (Test-Path $sourcePath) {
            # Zapisz zawartość pliku do strumienia FTP
            $remoteFilePath = "ftp://$ftpServer$ftpDestinationDirectory$zipFileName"
            try {
                $ftpSession.UploadFile($remoteFilePath, $sourcePath)
                Write-Host "[FTP] OK	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	ZIP $zipFileName wysłany na FTP."
                "[FTP] OK	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	ZIP $zipFileName wysłany na FTP." >> $LogFile
            } catch {
                Write-Host "[FTP] ERROR	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	ZIP $zipFileName nie wysłany na FTP."
                "[FTP] ERROR	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	ZIP $zipFileName nie wysłany na FTP." >> $LogFile
            }


            # Skopiuj plik do innego katalogu, zmieniając nazwę na numer dnia tygodnia
            $dayOfWeek = (Get-Date).DayOfWeek.value__
            $newFileName = "$dayOfWeek-$($zipFileName.Substring(15))"
            $newPath = Join-Path $destinationPath $newFileName
            try {
                Copy-Item -Path $backupPath\$zipFileName -Destination $newPath
                Write-Host "[CLO] OK	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	Plik $newFileName skopiowany do katalogu CLOUD."
                "[CLO] OK	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	Plik $newFileName skopiowany do katalogu CLOUD." >> $LogFile
            } catch {
                Write-Host "[CLO] ERROR	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	Plik $newFileName nie został skopiowany do katalogu CLOUD."
                "[CLO] ERROR	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	Plik $newFileName nie został skopiowany do katalogu CLOUD." >> $LogFile
            }
        } else {
            Write-Host "[ZIP] ERROR	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	Plik $zipFileName nie istnieje pod ścieżką: $sourcePath"
            "[ZIP] ERROR	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	Plik $zipFileName nie istnieje pod ścieżką: $sourcePath" >> $LogFile
        }
    }


}

# Funkcja do usuwania plików starszych niż określona liczba dni i zawierających określony ciąg znaków w nazwie
function Remove-OldFilesWithPattern($path, $daysToKeepTMP, $databaseName) {
    Write-Host "[TMP] OK	Sprawdzenie i usuwanie starszych plików z TMP"
    "[TMP] OK	Sprawdzenie i usuwanie starszych plików z TMP" >> $LogFile

    # Budowanie maski plików na podstawie nazwy bazy danych
    $fileMask = "*$databaseName.*"

    # Pobieranie plików według maski, posortowanych po dacie modyfikacji (od najnowszych do najstarszych)
    $files = Get-ChildItem -Path $path -Filter $fileMask | Sort-Object LastWriteTime -Descending

    # Zachowanie określonej liczby najnowszych plików
    $filesToKeep = $files | Select-Object -First $daysToKeepTMP

    # Wyświetlenie plików, które zostaną zachowane
    Write-Host "[TMP] Pliki, które zostaną zachowane:"
    "[TMP] Pliki, które zostaną zachowane:" >> $LogFile
    foreach ($file in $filesToKeep) {
        Write-Host "[TMP]    - $($file.FullName) (Data modyfikacji: $($file.LastWriteTime))"
        "[TMP]	- $($file.FullName) (Data modyfikacji: $($file.LastWriteTime))" >> $LogFile
    }

    # Wyszukanie plików do usunięcia
    $filesToDelete = $files | Where-Object { $_ -notin $filesToKeep }

    # Usuwanie starych plików
    foreach ($file in $filesToDelete) {
        Remove-Item -Path $file.FullName -Force
        Write-Host "[TMP] OK	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	Usunięto plik $file.FullName z katalogu TMP"
        "[TMP] OK	"+ (Get-Date -Format "yyyy-MM-dd HH:mm:ss") + "	Usunięto plik $file.FullName z katalogu TMP" >> $LogFile
    }

    Write-Host "[TMP] OK	Operacja usuwania starszych plików z TMP zakończona"
    "[TMP] OK	Operacja usuwania starszych plików z TMP zakończona" >> $LogFile
}

# Wykonaj kopie wybranych baz danych z kompresją do ZIP
Backup-SelectedDatabasesWithCompression -serverName $serverName -username $username -password $password -backupPath $backupPath -timeout $timeout -databasesToBackup $databasesToBackup

Opublikowano w BACKUP, INFORMATYKA, MS SQL, PowerShell, SKRYPT, SQL i oznaczono , , , .