2 # PowerShell script for backing up MySQL / MariaDB databases on Windows
4 # Author: Patrick Canterino <patrick@patrick-canterino.de>
5 # WWW: https://www.patrick-canterino.de/
6 # https://github.com/pcanterino/psmysqlbackup
7 # License: 2-Clause BSD License
12 $configMysqlHost = 'localhost'
14 $configMysqlPort = 3306
15 # MySQL user using to connect to MySQL
16 $configMysqlUser = 'backup'
17 # Password for MySQL user
18 $configMysqlPassword = 'backup'
20 # Path to MySQL CLI program
21 $configMysqlCli = 'mysql.exe'
22 # Path to mysqldump CLI program
23 $configMysqldumpCli = 'mysqldump.exe'
25 # Directory where to store the backups
26 $configBackupDir = 'backup'
27 # Number of backups to keep, set to 0 to keep all backups
28 $configBackupRotate = 7
30 # Directory where to store the logfiles
32 # Number of logfiles to keep, set to 0 to keep all logfiles
33 # You should set this to at least the same as $configBackupRotate
36 # Databases to backup, leave empty to backup all databases
38 # If $configDbBackup is empty, don't backup the databases defined here
39 $configDbExclude = @('test')
40 # If $configDbBackup is empty, don't backup the databases matching these patterns
41 $configDbExcludePattern = @()
47 Write-Log writes a message to a specified log file with the current time stamp.
49 The Write-Log function is designed to add logging capability to other scripts.
50 In addition to writing output and/or verbose you can write to a log file for
53 Created by: Jason Wasser @wasserja
54 Modified: 11/24/2015 09:30:19 AM
57 * Code simplification and clarification - thanks to @juneb_get_help
58 * Added documentation.
59 * Renamed LogPath parameter to Path to keep it standard - thanks to @JeffHicks
60 * Revised the Force switch to work as it should - thanks to @JeffHicks
63 * Add error handling if trying to create a log file in a inaccessible location.
64 * Add ability to write $Message to $Verbose or $Error pipelines to eliminate
67 Message is the content that you wish to add to the log file.
69 The path to the log file to which you would like to write. By default the function will
70 create the path and file if it does not exist.
72 Specify the criticality of the log information being written to the log (i.e. Error, Warning, Informational)
74 Use NoClobber if you do not wish to overwrite an existing file.
76 Write-Log -Message 'Log message'
77 Writes the message to c:\Logs\PowerShellLog.log.
79 Write-Log -Message 'Restarting Server.' -Path c:\Logs\Scriptoutput.log
80 Writes the content to the specified log file and creates the path and file specified.
82 Write-Log -Message 'Folder does not exist.' -Path c:\Logs\Script.log -Level Error
83 Writes the message to the specified log file as an error message, and writes the message to the error pipeline.
85 https://gallery.technet.microsoft.com/scriptcenter/Write-Log-PowerShell-999c32d0
92 [Parameter(Mandatory=$true,
93 ValueFromPipelineByPropertyName=$true)]
94 [ValidateNotNullOrEmpty()]
98 [Parameter(Mandatory=$false)]
100 [string]$Path='C:\Logs\PowerShellLog.log',
102 [Parameter(Mandatory=$false)]
103 [ValidateSet("Error","Warn","Info")]
104 [string]$Level="Info",
106 [Parameter(Mandatory=$false)]
112 # Set VerbosePreference to Continue so that verbose messages are displayed.
113 $VerbosePreference = 'Continue'
118 # If the file already exists and NoClobber was specified, do not write to the log.
119 if ((Test-Path $Path) -AND $NoClobber) {
120 Write-Error "Log file $Path already exists, and you specified NoClobber. Either delete the file or specify a different name."
124 # If attempting to write to a log file in a folder/path that doesn't exist create the file including the path.
125 elseif (!(Test-Path $Path)) {
126 Write-Verbose "Creating $Path."
127 $NewLogFile = New-Item $Path -Force -ItemType File
131 # Nothing to see here yet.
134 # Format Date for our Log File
135 $FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
137 # Write message to error, warning, or verbose pipeline and specify $LevelText
141 $LevelText = 'ERROR:'
144 Write-Warning $Message
145 $LevelText = 'WARNING:'
148 Write-Verbose $Message
153 # Write log entry to $Path
154 "$FormattedDate $LevelText $Message" | Out-File -FilePath $Path -Append
161 function Get-Databases() {
162 $databaseString = (& $configMysqlCli --host=$configMysqlHost --port=$configMysqlPort --user=$configMysqlUser --password=$configMysqlPassword --batch --skip-column-names -e "SHOW DATABASES;")
164 if($LastExitCode -ne 0) {
165 throw "MySQL CLI exited with Exit code $LastExitCode"
168 $databases = $databaseString.split([Environment]::NewLine)
173 function Create-Backup([String]$database, [String]$target) {
174 & $configMysqldumpCli --host=$configMysqlHost --port=$configMysqlPort --user=$configMysqlUser --password=$configMysqlPassword --single-transaction --result-file=$target $database
176 if($LastExitCode -ne 0) {
177 throw "mysqldump exited with Exit code $LastExitCode"
180 function Invoke-FileRotation {
184 [Parameter(Mandatory=$false)]
186 [Parameter(Mandatory=$false)]
190 if($MaxFiles -le 0) {
194 $keepFilesCount = $MaxFiles
196 Get-ChildItem $Dir -File | Where-Object {($null -eq $Pattern -or $_.Name -match $Pattern)} | Sort-Object -Descending |
198 if($keepFilesCount -ge 0) {
202 if($keepFilesCount -eq -1) {
203 if($null -ne $LogFile) {
204 Write-Log "Deleting file $($_.FullName)" -Path $LogFile
207 Remove-Item -Force $_.FullName
212 $defaultDbExclude = @('information_schema', 'performance_schema')
214 $patternBackupFile = '^backup-.+-\d{8,}-\d{6}\.sql$'
215 $patternLogFile = '^log-\d{8,}-\d{6}\.log$'
217 $currDaytime = Get-Date -format 'yyyyMMdd-HHmmss'
219 $logFile = "$configLogDir\log-$currDaytime.log"
221 $startTime = Get-Date -format 'yyyy-MM-dd HH:mm:ss'
222 Write-Log "Started at $startTime" -Path $logFile
224 # Get a list of all databases
226 $databases = Get-Databases | Where-Object {!($_ -in $defaultDbExclude)}
229 Write-Log 'Failed to get list of databases' -Path $logFile -Level Error
230 Write-Log $_ -Path $logFile -Level Error
231 Write-Log 'Exiting' -Path $logFile -Level Error
236 # Create a list of databases to backup
238 $databasesToBackup = @()
240 if($configDbBackup -and $configDbBackup.count -gt 0) {
241 foreach($cDb in $configDbBackup) {
242 if($cDb -in $databases) {
243 $databasesToBackup += $cDb
246 Write-Log "Not backing up database $cDb, because it does not exist" -Path $logFile -Level Warn
252 foreach($rDb in $databases) {
253 if($rDb -in $configDbExclude) {
257 foreach($cPattern in $configDbExcludePattern) {
258 if($rDb -match $cPattern) {
259 continue excludeOuter;
263 $databasesToBackup += $rDb
267 # Iterate over the list of databases and back them up and rotate the backups
268 foreach($d in $databasesToBackup) {
269 $databaseBackupDir = Join-Path -Path $configBackupDir -ChildPath $d
271 if(!(Test-Path $databaseBackupDir)) {
273 New-Item -ItemType directory -Path "$databaseBackupDir" -ErrorAction Stop | Out-Null
276 Write-Log "Failed to create directory $databaseBackupDir" -Path $logFile -Level Error
277 Write-Log $_ -Path $logFile -Level Error
278 Write-Log 'Exiting' -Path $logFile -Level Error
284 $databaseBackupFile = Join-Path -Path $databaseBackupDir -ChildPath "backup-$d-$currDaytime.sql"
286 Write-Log "Backing up $d to $databaseBackupFile..." -Path $logFile
289 Create-Backup $d $databaseBackupFile
290 Invoke-FileRotation -Dir $databaseBackupDir -MaxFiles $configBackupRotate -Pattern $patternBackupFile -LogFile $logFile
293 Write-Log "Could not backup database $d to $databaseBackupFile" -Path $logFile -Level Error
294 Write-Log $_ -Path $logFile -Level Error
298 Invoke-FileRotation -Dir $configLogDir -MaxFiles $configLogRotate -Pattern $patternLogFile -LogFile $logFile
300 $endTime = Get-Date -format 'yyyy-MM-dd HH:mm:ss'
301 Write-Log "Ended at $endTime" -Path $logFile