Skip to content

Instantly share code, notes, and snippets.

@9000cats
Created October 31, 2024 00:32
Show Gist options
  • Save 9000cats/72c7fdb213a4f99ecb1d304204de4966 to your computer and use it in GitHub Desktop.
Save 9000cats/72c7fdb213a4f99ecb1d304204de4966 to your computer and use it in GitHub Desktop.
PowerShell script for running Oracle RMAN backups, with a full backup on Sunday and incrementals Monday-Saturday.
<#
.SYNOPSIS
Performs automated RMAN (Recovery Manager) backups of Oracle databases with comprehensive logging and monitoring.
.DESCRIPTION
This script automates Oracle RMAN backup operations with enhanced logging capabilities. It performs
either full (level 0) or incremental (level 1) backups based on the day of the week, with full
backups scheduled for Sundays. Features include:
- Detailed logging with separate files for general and detailed logs
- Process monitoring and resource usage tracking
- Automatic cleanup of obsolete backups
- Archive log management
- Comprehensive error handling and validation
- System resource monitoring during backup
.PARAMETER LogPath
Path where log files will be stored. Default: "C:\Scripts\Log"
.PARAMETER RmanScriptPath
Path where temporary RMAN script files will be created. Default: "C:\Scripts"
.PARAMETER OracleSid
Oracle System ID (SID) for the target database. Default: "database"
.PARAMETER OraclePassword
Password for the Oracle SYS user. Default: "password" (Should be replaced with secure password management)
.NOTES
Version: 1.0
Date: 10/30/2024
Authors: Warren Held, ChatGPT o1-preview, Claude 3.5 Opus
Requires: PowerShell 5.1 or later
Oracle RMAN
.EXAMPLE
.\Run-RMANBackup.ps1 -OracleSid "PROD" -OraclePassword "SecurePass123"
.EXAMPLE
.\Run-RMANBackup.ps1 -LogPath "D:\Backup\Logs" -RmanScriptPath "D:\Backup\Scripts" -OracleSid "DEV"
#>
param (
[string]$LogPath = "C:\Scripts\Log",
[string]$RmanScriptPath = "C:\Scripts",
[string]$OracleSid = "database",
[string]$OraclePassword = "password" # Better to use secure password management
)
# Create log directory if it doesn't exist
if (!(Test-Path $LogPath)) {
New-Item -ItemType Directory -Path $LogPath | Out-Null
}
# Initialize log file with timestamp and script version
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$logFile = Join-Path $LogPath "rman_backup_$timestamp.log"
$detailedLogFile = Join-Path $LogPath "rman_backup_detailed_$timestamp.log"
function Write-Log {
param(
[Parameter(Mandatory=$true)]
[string]$Message,
[ValidateSet('INFO', 'WARNING', 'ERROR', 'DEBUG')]
[string]$Level = 'INFO'
)
$logMessage = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss'): [$Level] $Message"
Write-Output $logMessage
Add-Content -Path $logFile -Value $logMessage
Add-Content -Path $detailedLogFile -Value $logMessage
}
function Write-DetailedLog {
param([string]$Message)
$logMessage = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss'): $Message"
Add-Content -Path $detailedLogFile -Value $logMessage
}
function Test-RmanSuccess {
param([string[]]$Output)
Write-Log "Analyzing RMAN output for success/failure indicators" -Level "DEBUG"
# Check for common RMAN error patterns
$errorPatterns = @(
'RMAN-\d{5}:', # Any RMAN error
'ORA-\d{5}:', # Any Oracle error
'ERROR:',
'FATAL:',
'failed',
'incorrect username/password',
'connection failed',
'backup piece .* not found',
'CATALOG must be connected',
'backup failure',
'media recovery failed'
)
foreach ($line in $Output) {
foreach ($pattern in $errorPatterns) {
if ($line -match $pattern) {
Write-Log "Found error pattern '$pattern' in: $line" -Level "ERROR"
return $false
}
}
}
# Check for successful completion message
$successPatterns = @(
'RMAN> exit',
'completed successfully',
'Finished backup at'
)
$foundSuccess = $false
foreach ($line in $Output) {
foreach ($pattern in $successPatterns) {
if ($line -match $pattern) {
Write-Log "Found success pattern '$pattern' in: $line" -Level "DEBUG"
$foundSuccess = $true
}
}
}
return $foundSuccess
}
try {
# Log script start with environment details
Write-Log "=== RMAN Backup Script Started ===" -Level "INFO"
Write-Log "Script Version: 1.1 (Enhanced Logging)" -Level "INFO"
Write-Log "PowerShell Version: $($PSVersionTable.PSVersion)" -Level "INFO"
Write-Log "Script Parameters:" -Level "DEBUG"
Write-Log " LogPath: $LogPath" -Level "DEBUG"
Write-Log " RmanScriptPath: $RmanScriptPath" -Level "DEBUG"
Write-Log " OracleSid: $OracleSid" -Level "DEBUG"
# Check environment
Write-Log "Checking Oracle environment..." -Level "INFO"
$env:PATH -split ';' | ForEach-Object { Write-DetailedLog "PATH: $_" }
# Verify Oracle RMAN availability
if (-not (Get-Command rman -ErrorAction SilentlyContinue)) {
throw "RMAN command not found. Please ensure Oracle environment is properly configured."
}
Write-Log "RMAN command found in path" -Level "INFO"
# Log system resources
$systemInfo = Get-WmiObject Win32_OperatingSystem
Write-DetailedLog "Available Physical Memory: $([math]::Round($systemInfo.FreePhysicalMemory / 1MB, 2)) GB"
Write-DetailedLog "Available Virtual Memory: $([math]::Round($systemInfo.FreeVirtualMemory / 1MB, 2)) GB"
# Determine backup type
$dayOfWeek = (Get-Date).DayOfWeek
$isFullBackup = $dayOfWeek -eq "Sunday"
$backupType = if ($isFullBackup) { "FULL" } else { "INCR" }
Write-Log "Backup Type: $backupType (Day of week: $dayOfWeek)" -Level "INFO"
# Create backup tag
$shortDate = Get-Date -Format "yyMMdd_HHmm"
$backupTag = "${backupType}_${shortDate}"
Write-Log "Backup Tag: $backupTag" -Level "INFO"
# Create RMAN command file
$rmanCmdFile = Join-Path $RmanScriptPath "rman_backup_$timestamp.txt"
$rmanCommands = @"
connect target sys/$OraclePassword@$OracleSid
set echo on;
show all;
list backup summary;
CROSSCHECK ARCHIVELOG ALL;
report obsolete;
RUN {
BACKUP AS BACKUPSET
INCREMENTAL LEVEL $(if ($isFullBackup) { "0" } else { "1" })
DATABASE
TAG '$backupTag'
PLUS ARCHIVELOG
DELETE INPUT;
DELETE OBSOLETE;
}
list backup summary;
exit;
"@
$rmanCommands | Out-File -FilePath $rmanCmdFile -Encoding ASCII
Write-Log "Created RMAN command file at: $rmanCmdFile" -Level "INFO"
Write-DetailedLog "RMAN Commands:"
Write-DetailedLog $rmanCommands
# Execute RMAN
Write-Log "Starting RMAN backup execution..." -Level "INFO"
$startTime = Get-Date
Write-Log "Start Time: $startTime" -Level "INFO"
# Run RMAN with timeout
$rmanProcess = Start-Process rman -ArgumentList "cmdfile=`"$rmanCmdFile`"" -NoNewWindow -PassThru -RedirectStandardOutput "$LogPath\rman_output_$timestamp.txt" -RedirectStandardError "$LogPath\rman_error_$timestamp.txt"
# Monitor process and log memory usage
while (!$rmanProcess.HasExited) {
Start-Sleep -Seconds 30
$processInfo = Get-Process -Id $rmanProcess.Id
Write-DetailedLog "RMAN Process Memory Usage: $([math]::Round($processInfo.WorkingSet / 1MB, 2)) MB"
Write-DetailedLog "RMAN Process CPU Time: $($processInfo.CPU)"
}
# Calculate execution time
$endTime = Get-Date
$executionTime = $endTime - $startTime
Write-Log "End Time: $endTime" -Level "INFO"
Write-Log "Total Execution Time: $($executionTime.ToString())" -Level "INFO"
# Process RMAN output
Write-Log "Reading RMAN output files..." -Level "DEBUG"
$rmanOutput = Get-Content "$LogPath\rman_output_$timestamp.txt"
$rmanError = Get-Content "$LogPath\rman_error_$timestamp.txt"
# Log all RMAN output to detailed log
Write-DetailedLog "=== RMAN STDOUT ==="
$rmanOutput | ForEach-Object { Write-DetailedLog $_ }
Write-DetailedLog "=== RMAN STDERR ==="
$rmanError | ForEach-Object { Write-DetailedLog $_ }
# Log only important RMAN messages to main log
$rmanOutput | Where-Object {
$_ -match "RMAN-|ORA-|starting|finished|ERROR|WARNING|completing|completed successfully|failed"
} | ForEach-Object {
Write-Log "RMAN: $_" -Level $(
if ($_ -match "ERROR|RMAN-|ORA-") { "ERROR" }
elseif ($_ -match "WARNING") { "WARNING" }
else { "INFO" }
)
}
# Check for successful completion
if (-not (Test-RmanSuccess -Output $rmanOutput)) {
throw "RMAN backup failed. Check log files for details."
}
Write-Log "RMAN backup completed successfully" -Level "INFO"
# RMAN command file cleanup
Write-Log "Cleaning up temporary files..." -Level "DEBUG"
Remove-Item $rmanCmdFile -Force
Write-Log "Removed RMAN command file" -Level "DEBUG"
# Archive logs if they're getting large
if ((Get-Item $detailedLogFile).Length -gt 10MB) {
$archiveDir = Join-Path $LogPath "Archive"
if (!(Test-Path $archiveDir)) { New-Item -ItemType Directory -Path $archiveDir }
Compress-Archive -Path $detailedLogFile -DestinationPath "$archiveDir\rman_backup_detailed_$timestamp.zip"
Remove-Item $detailedLogFile
Write-Log "Archived detailed log file due to size" -Level "INFO"
}
exit 0
}
catch {
$errorMessage = $_.Exception.Message
$errorDetails = $_.Exception.ToString()
Write-Log "ERROR: $errorMessage" -Level "ERROR"
Write-DetailedLog "=== FULL ERROR DETAILS ==="
Write-DetailedLog $errorDetails
Write-Log "Backup process failed. Check detailed log for full error information." -Level "ERROR"
exit 1
}
finally {
Write-Log "=== RMAN Backup Script Finished ===" -Level "INFO"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment