280 lines
10 KiB
PowerShell
280 lines
10 KiB
PowerShell
# --------------------------------------------
|
|
# 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"
|