sentinel/Modules/NWABackup.ps1

280 lines
10 KiB
PowerShell
Raw Permalink Normal View History

2025-01-03 02:23:16 +00:00
# --------------------------------------------
# Northwest Analytics (NWA) Backup Script
# --------------------------------------------
# This script backs up .DAT and .NWH files from the shared
# directory to a user-specific backup folder, preserving
# the company directory structure and compressing the
# backup into a zip file. It also maintains a global
# manifest in JSON format.
# --------------------------------------------
function ConvertTo-Hashtable {
param (
[Parameter(Mandatory = $true)]
[object]$InputObject
)
if ($InputObject -is [System.Management.Automation.PSCustomObject]) {
$hash = @{}
foreach ($prop in $InputObject.PSObject.Properties) {
$hash[$prop.Name] = ConvertTo-Hashtable -InputObject $prop.Value
}
return $hash
}
elseif ($InputObject -is [System.Array]) {
return $InputObject | ForEach-Object { ConvertTo-Hashtable -InputObject $_ }
}
else {
return $InputObject
}
}
# ----------- Step 0: Import Logging Module -----------
$modulesPath = Join-Path -Path (Split-Path -Parent $MyInvocation.MyCommand.Path) -ChildPath ""
Import-Module (Join-Path -Path $modulesPath -ChildPath "Logging\Logging.psm1") -Force
# ----------- Step 1: Define Variables -----------
$sharedDirectory = "X:\dev\BOYD\%NWA\Data Collection" # Using environment variable
$username = $env:USERNAME
$backupRoot = "C:\Users\$username\Desktop\NWABackup"
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$daysBack = 4
$thresholdDate = (Get-Date).AddDays(-$daysBack)
$backupDirectory = Join-Path -Path $backupRoot -ChildPath $timestamp
$zipFilePath = Join-Path -Path $backupRoot -ChildPath "$timestamp.zip"
$manifestFilePath = Join-Path -Path $backupRoot -ChildPath "manifest.json"
# Define log file path
$logDirectory = Join-Path -Path $backupRoot -ChildPath "Logs"
$logFile = Join-Path -Path $logDirectory -ChildPath "$timestamp.log"
# ----------- Step 2: Initialize Logging -----------
# Create log directory if it doesn't exist
if (!(Test-Path -Path $logDirectory)) {
try {
New-Item -ItemType Directory -Path $logDirectory -Force | Out-Null
# Initialize Logging after creating the log directory
Initialize-Logging -Path $logFile
Write-Log -Message "Log directory created at ${logDirectory}" -Type "INFO"
}
catch {
Write-Error "Failed to create log directory at ${logDirectory}: $_"
exit 1
}
}
else {
# Initialize Logging if log directory already exists
Initialize-Logging -Path $logFile
}
# Log the start of the backup process
Write-Log -Message "=== NWABackup Process Started at $timestamp ===" -Type "INFO"
# ----------- Step 3: Create Backup Directory -----------
if (!(Test-Path -Path $backupDirectory)) {
try {
New-Item -ItemType Directory -Path $backupDirectory -Force | Out-Null
Write-Log -Message "Backup directory created at ${backupDirectory}" -Type "INFO"
}
catch {
Write-Log -Message "Failed to create backup directory at ${backupDirectory}. Error: $_" -Type "ERROR"
exit 1
}
}
else {
Write-Log -Message "Backup directory already exists at ${backupDirectory}" -Type "INFO"
}
# ----------- Step 4: Copy .DAT and .NWH Files -----------
# Define the file extensions to include (case-insensitive)
$fileExtensions = @('.DAT', '.NWH')
Write-Log -Message "Starting file retrieval and copy process." -Type "INFO"
# Get all files recursively and filter by extension and modification date in a single pass
try {
$files = Get-ChildItem -Path $sharedDirectory -Recurse -File | Where-Object {
($fileExtensions -contains $_.Extension.ToUpper()) -and ($_.LastWriteTime -ge $thresholdDate)
}
Write-Log -Message "Found $($files.Count) files with specified extensions modified in the last $daysBack days." -Type "INFO"
}
catch {
Write-Log -Message "Error retrieving files: $_" -Type "ERROR"
exit 1
}
# Initialize a hashtable to build the manifest
$manifest = @{}
foreach ($file in $files) {
# Extract company name from the folder structure
$relativePath = $file.FullName.Substring($sharedDirectory.Length).TrimStart("\")
$pathParts = $relativePath.Split("\")
if ($pathParts.Length -lt 2) {
Write-Log -Message "Invalid file path structure: $relativePath" -Type "WARNING"
continue
}
$companyName = $pathParts[0]
$fileName = $pathParts[-1]
# Extract part name and file type from the file name
$partName, $fileType = $fileName -split '\.', 2
if (-not $fileType) {
Write-Log -Message "Invalid file format: $fileName" -Type "WARNING"
continue
}
# Initialize company entry if not exists
if (-not $manifest.ContainsKey($companyName)) {
$manifest[$companyName] = @{}
}
# Initialize part entry if not exists
if (-not $manifest[$companyName].ContainsKey($partName)) {
$manifest[$companyName][$partName] = @{} # This must be a hash table, not an array
}
# Initialize file type entry if not exists
if (-not $manifest[$companyName][$partName].ContainsKey($fileType)) {
$manifest[$companyName][$partName][$fileType] = @()
}
# Add the current backup zip file to the file type's backup array
if (-not $manifest[$companyName][$partName][$fileType].Contains($zipFilePath)) {
$manifest[$companyName][$partName][$fileType] += $zipFilePath
}
# Define the destination path
$destinationPath = Join-Path -Path $backupDirectory -ChildPath $relativePath
# Get the destination directory
$destinationDir = Split-Path -Path $destinationPath -Parent
# Create the destination directory if it doesn't exist
if (!(Test-Path -Path $destinationDir)) {
try {
New-Item -ItemType Directory -Path $destinationDir -Force | Out-Null
Write-Log -Message "Created directory: ${destinationDir}" -Type "INFO"
}
catch {
Write-Log -Message "Failed to create directory ${destinationDir}: $_" -Type "ERROR"
continue
}
}
# Copy the file to the backup directory
try {
Copy-Item -Path $file.FullName -Destination $destinationPath -Force -ErrorAction Stop
Write-Log -Message "Copied: $($file.FullName) to ${destinationPath}" -Type "INFO"
}
catch {
Write-Log -Message "Failed to copy $($file.FullName) to ${destinationPath}: $_" -Type "ERROR"
}
}
# ----------- Step 5: Update and Save the Manifest JSON -----------
if (Test-Path -Path $manifestFilePath) {
try {
$existingManifest = Get-Content -Path $manifestFilePath | ConvertFrom-Json
Write-Log -Message "Loaded existing manifest file." -Type "INFO"
}
catch {
Write-Log -Message "Failed to load existing manifest file. Creating a new one. Error: $_" -Type "ERROR"
$existingManifest = @{}
}
}
else {
$existingManifest = @{}
}
# Convert PSCustomObject to Hashtable
$existingManifest = ConvertTo-Hashtable -InputObject $existingManifest
# Merge the current manifest into the existing manifest
foreach ($company in $manifest.Keys) {
if (-not $existingManifest.ContainsKey($company)) {
$existingManifest[$company] = @{}
Write-Log -Message "Added new company '$company' to manifest." -Type "INFO"
}
foreach ($partName in $manifest[$company].Keys) {
if (-not $existingManifest[$company].ContainsKey($partName)) {
$existingManifest[$company][$partName] = @{}
Write-Log -Message "Added new part '$partName' under company '$company' to manifest." -Type "INFO"
}
foreach ($fileType in $manifest[$company][$partName].Keys) {
if (-not $existingManifest[$company][$partName].ContainsKey($fileType)) {
$existingManifest[$company][$partName][$fileType] = @()
Write-Log -Message "Added new file type '$fileType' under part '$partName' for company '$company' to manifest." -Type "INFO"
}
foreach ($zip in $manifest[$company][$partName][$fileType]) {
# Ensure the value is treated as an array
if ($existingManifest[$company][$partName][$fileType] -isnot [System.Collections.ArrayList]) {
$existingManifest[$company][$partName][$fileType] = @($existingManifest[$company][$partName][$fileType])
}
# Add the zip path to the array if it doesn't already exist
if (-not $existingManifest[$company][$partName][$fileType].Contains($zip)) {
$existingManifest[$company][$partName][$fileType] += $zip
Write-Log -Message "Added zip '$zip' to '$company\\$partName\\$fileType' in manifest." -Type "INFO"
}
}
}
}
}
# Convert the hashtable to JSON with formatting
$jsonContent = $existingManifest | ConvertTo-Json -Depth 10 -Compress:$false
# Save the updated manifest
try {
Set-Content -Path $manifestFilePath -Value $jsonContent -Encoding UTF8
Write-Log -Message "Manifest file updated at $manifestFilePath" -Type "INFO"
}
catch {
Write-Log -Message "Failed to update manifest file at $manifestFilePath. Error: $_" -Type "ERROR"
}
# ----------- Step 6: Compress the Backup Directory -----------
try {
# Compress the *contents* of the backup directory, not the directory itself
Compress-Archive -Path "$backupDirectory\*" -DestinationPath $zipFilePath -Force
Write-Log -Message "Successfully compressed backup to $zipFilePath" -Type "INFO"
}
catch {
Write-Log -Message "Failed to compress backup directory: $_" -Type "ERROR"
# Optionally, decide whether to exit or continue
}
# ----------- Step 7: Cleanup (Optional) -----------
# Optionally, remove the uncompressed backup directory after compression
try {
Remove-Item -Path $backupDirectory -Recurse -Force
Write-Log -Message "Removed uncompressed backup directory: ${backupDirectory}" -Type "INFO"
}
catch {
Write-Log -Message "Failed to remove uncompressed backup directory ${backupDirectory}: $_" -Type "ERROR"
}
# ----------- Step 8: Completion Message -----------
Write-Log -Message "Backup process completed." -Type "INFO"
# Optionally, display a message to the user
Write-Host "Backup completed successfully. Log file located at $logFile"