Private/Add-BackupToManifest.ps1
function Add-BackupToManifest { <# .SYNOPSIS Adds backup information to a daily backup manifest file. .DESCRIPTION Creates or updates a daily backup manifest (backup-manifest.json) containing metadata for all backups created on a specific date. This approach maintains full restoration capabilities for all backed up files and directories. .PARAMETER SourcePath The original path that was backed up. .PARAMETER BackupPath The path to the created backup archive (without .zip extension). .PARAMETER PathType The type of the source path ('File' or 'Directory'). .PARAMETER DatePath The date-organized backup directory path (e.g., /backups/2025-09-15). .PARAMETER NoHash Skip hash calculation to improve performance in simple backup scenarios. .PARAMETER HashAlgorithm The hash algorithm to use for calculating hashes. Available options: SHA1, SHA256, SHA384, SHA512, MD5. Defaults to SHA256. .OUTPUTS None. Creates or updates backup-manifest.json in the date directory. .NOTES This function creates a single backup-manifest.json file per backup date, containing metadata for all backups created on that date. .EXAMPLE PS > Add-BackupToManifest -SourcePath 'C:\Documents\report.pdf' -BackupPath 'C:\Backups\2025-09-15\Documents__report.pdf' -PathType 'File' -DatePath 'C:\Backups\2025-09-15' Adds backup entry to C:\Backups\2025-09-15\backup-manifest.json #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $SourcePath, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $BackupPath, [Parameter(Mandatory = $true)] [ValidateSet('File', 'Directory')] [string] $PathType, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $DatePath, [Parameter(Mandatory = $false)] [switch] $NoHash, [Parameter(Mandatory = $false)] [ValidateSet('SHA1', 'SHA256', 'SHA384', 'SHA512', 'MD5')] [string] $HashAlgorithm = 'SHA256' ) try { # Validate and resolve paths if (-not (Test-Path -Path $DatePath -PathType Container)) { Write-Warning "Add-BackupToManifest> Date path does not exist: $DatePath" return } # Validate source path exists if (-not (Test-Path -Path $SourcePath)) { Write-Warning "Add-BackupToManifest> Source path does not exist: $SourcePath" return } $backupManifestFilePath = Join-MultiplePaths -Segments @($DatePath, 'backup-manifest.json') $generatedArchiveFileName = [System.IO.Path]::GetFileName($BackupPath) + '.zip' # Get module version dynamically $currentModuleInfo = Get-Module -Name DailyBackup $detectedModuleVersion = if ($currentModuleInfo) { $currentModuleInfo.Version.ToString() } else { '1.0.0' } # Create backup entry $newBackupEntryObject = @{ ArchiveName = $generatedArchiveFileName SourcePath = $SourcePath PathType = $PathType BackupCreated = Get-Date -Format 'yyyy-MM-ddTHH:mm:ss.fffZ' } # Calculate hashes unless disabled if (-not $NoHash) { Write-Verbose "Add-BackupToManifest> Calculating source hash for: $SourcePath" $calculatedSourceHash = Get-PathHash -Path $SourcePath -Algorithm $HashAlgorithm if ($calculatedSourceHash) { $newBackupEntryObject.SourceHash = $calculatedSourceHash $newBackupEntryObject.HashAlgorithm = $HashAlgorithm Write-Verbose "Add-BackupToManifest> Source hash: $calculatedSourceHash" # Calculate archive hash after it's created $fullArchiveFilePath = "$BackupPath.zip" if (Test-Path $fullArchiveFilePath) { Write-Verbose "Add-BackupToManifest> Calculating archive hash for: $fullArchiveFilePath" $calculatedArchiveHashObject = Get-FileHash -Path $fullArchiveFilePath -Algorithm $HashAlgorithm $newBackupEntryObject.ArchiveHash = $calculatedArchiveHashObject.Hash Write-Verbose "Add-BackupToManifest> Archive hash: $($calculatedArchiveHashObject.Hash)" } else { Write-Warning "Add-BackupToManifest> Archive not found for hash calculation: $fullArchiveFilePath" } } else { Write-Warning "Add-BackupToManifest> Failed to calculate source hash for: $SourcePath" } } # Add file/directory specific metadata if (Test-Path -Path $SourcePath) { $sourceFileOrDirectoryItem = Get-Item -Path $SourcePath $newBackupEntryObject.OriginalName = $sourceFileOrDirectoryItem.Name $newBackupEntryObject.LastWriteTime = $sourceFileOrDirectoryItem.LastWriteTime.ToString('yyyy-MM-ddTHH:mm:ss.fffZ') $newBackupEntryObject.Attributes = $sourceFileOrDirectoryItem.Attributes.ToString() if ($PathType -eq 'File') { $newBackupEntryObject.Size = $sourceFileOrDirectoryItem.Length $newBackupEntryObject.Extension = $sourceFileOrDirectoryItem.Extension } } # Load existing manifest or create new one $loadedManifestObject = $null if (Test-Path $backupManifestFilePath) { try { $manifestFileContent = Get-Content $backupManifestFilePath -Raw -ErrorAction Stop if ($manifestFileContent -and $manifestFileContent.Trim()) { $loadedManifestObject = $manifestFileContent | ConvertFrom-Json -ErrorAction Stop # Validate manifest structure if (-not $loadedManifestObject.PSObject.Properties['Backups']) { Write-Verbose 'Add-BackupToManifest> Adding missing Backups array to existing manifest' $loadedManifestObject | Add-Member -NotePropertyName 'Backups' -NotePropertyValue @() -Force } elseif ($null -eq $loadedManifestObject.Backups) { $loadedManifestObject.Backups = @() } } else { Write-Verbose 'Add-BackupToManifest> Manifest file is empty, creating new manifest' $loadedManifestObject = $null } } catch { Write-Warning "Add-BackupToManifest> Failed to read existing manifest, creating new one: $_" $loadedManifestObject = $null } } # Create new manifest structure if needed or validate existing one if (-not $loadedManifestObject) { $extractedBackupDate = Split-Path $DatePath -Leaf $loadedManifestObject = @{ BackupDate = $extractedBackupDate BackupVersion = '1.0' ModuleVersion = $detectedModuleVersion Backups = @() } } elseif (-not $loadedManifestObject.PSObject.Properties['Backups'] -or $null -eq $loadedManifestObject.Backups) { # Ensure Backups array exists $loadedManifestObject | Add-Member -NotePropertyName 'Backups' -NotePropertyValue @() -Force } # Add new backup entry $loadedManifestObject.Backups += $newBackupEntryObject # Save updated manifest $loadedManifestObject | ConvertTo-Json -Depth 4 | Out-File -FilePath $backupManifestFilePath -Encoding UTF8 Write-Verbose "Add-BackupToManifest> Added backup to manifest: $backupManifestFilePath" } catch { Write-Warning "Add-BackupToManifest> Failed to update manifest for $SourcePath : $_" } } |