PSBackup.psm1
Class PSBackupBackupJob { [string]$Name [string]$Source [string]$Destination [string]$Parameters [string]$LogPath PSBackupBackupJob ([string]$Name, [string]$Source, [string]$Destination, [string]$Parameters, [string]$LogPath) { $this.Name = $Name $this.Source = $Source $this.Destination = $Destination $this.Parameters = $Parameters $this.LogPath = $LogPath } [string]ToString(){ return ("{0}|{1}|{2}|{3}|{4}" -f $this.Name, $this.Source, $this.Destination, $this.Parameters, $this.LogPath) } } Function Import-PSBackupDataFromFile { [CmdletBinding()] Param( [Parameter(Mandatory=$True)] [string]$Path ) Write-Verbose "[Import-PSBackupDataFromFile] Path: $Path" If (-not (Test-PSBackupFile -Path $Path)) { Throw "[Import-PSBackupDataFromFile] BackupJob File Validation Failed" } Else { Write-Verbose '[Import-PSBackupDataFromFile] BackupJob File Validation Passed' $Params = @{ Path=$Path Verbose=$VerbosePreference } $ImportedData = Import-PowerShellDataFile @Params $Output = @() ForEach ($Name in $ImportedData.keys) { Write-Verbose "[Import-PSBackupDataFromFile] Importing Job: $Name" $Obj = New-Object -TypeName PSBackupBackupJob -ArgumentList $Name, $($ImportedData[$Name].Source), $($ImportedData[$Name].Destination), $($ImportedData[$Name].Parameters), $($ImportedData[$Name].LogPath) $Output += $Obj } Write-Output $Output } } Function Get-PSBackup { <# .EXTERNALHELP PSBackup-help.xml #> [CmdletBinding()] Param( [Parameter(Mandatory=$True,ParameterSetName='BackupJobFile')] [ValidateScript({Test-Path $_ -PathType Leaf})] [ValidateScript({(Get-ChildItem -Path $_).Extension -eq '.psd1'})] [ValidateScript({Test-Path $_})] [string]$Path ) If ($PSCmdlet.ParameterSetName -eq 'BackupJobFile') { Write-Verbose "[Get-PSBackup] From File: $(Resolve-Path -Path $Path)" Write-Verbose "[Get-PSBackup] Calling 'Import-PSBackupDataFromFile'" $Params = @{ Path = $Path Verbose = $VerbosePreference } $Output = Import-PSBackupDataFromFile @Params } Write-Output $Output } Function Invoke-PSBackup { <# .EXTERNALHELP PSBackup-help.xml #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter(Mandatory=$True,Position=0,ParameterSetName='BackupJobFile')] [ValidateScript({Test-Path $_ -PathType Leaf})] [ValidateScript({(Get-ChildItem $_).Extension -eq '.psd1'})] [ValidateScript({Test-Path $_})] [string]$Path ) If ($PSCmdlet.ParameterSetName -eq 'BackupJobFile') { Write-Verbose "[Invoke-PSBackup] From File: $(Resolve-Path -Path $Path)" $SequenceName = $((Get-ChildItem -Path $Path).BaseName) $Params = @{ Path = $Path Verbose = $VerbosePreference } $BackupJobs = Import-PSBackupDataFromFile @Params Write-Verbose "Start: $(Get-Date)" Write-Verbose "[Invoke-PSBackup] Backup Sequence: $SequenceName" ForEach ($BackupJob in $BackupJobs) { Write-Verbose "[Invoke-PSBackup][$SequenceName] Starting Backup Job: $($BackupJob.Name)" $LogFileName = ('Backup_{0}_{1:yyyyMMdd}.log' -f $SequenceName, $(Get-Date)) $LogFile = Join-Path -Path $($BackupJob.LogPath) -ChildPath $LogFileName Write-Verbose "[Invoke-PSBackup][$SequenceName][$($BackupJob.Name)] Source: $($BackupJob.Source)" Write-Verbose "[Invoke-PSBackup][$SequenceName][$($BackupJob.Name)] Destination: $($BackupJob.Destination)" Write-Verbose "[Invoke-PSBackup][$SequenceName][$($BackupJob.Name)] Parameters: $($BackupJob.Parameters)" Write-Verbose "[Invoke-PSBackup][$SequenceName][$($BackupJob.Name)] LogFile: $LogFile" [string]$ArgumentList = ('"{0}" "{1}" /LOG+:"{2}" {3}' -f $($BackupJob.Source), $($BackupJob.Destination), $LogFile, $($BackupJob.Parameters)) If ($PSCmdlet.ShouldProcess("Copying '$($BackupJob.Source)' to '$($BackupJob.Destination)'")) { Try { $Stopwatch = [system.diagnostics.stopwatch]::StartNew() Start-Process -FilePath robocopy.exe -ArgumentList $ArgumentList -NoNewWindow -Wait | Out-Null $Stopwatch.Stop() } Catch { $ErrorMessage = $_.Exception.Message Write-Warning $ErrorMessage } $Output = [ordered]@{ Name = $($BackupJob.Name) Source = $($BackupJob.Source) Destination = $($BackupJob.Destination) TimeElapsed = $($Stopwatch.Elapsed) } Write-Output $(New-Object -TypeName PSObject -Property $Output) } } } } Function Test-PSBackupFile { <# .EXTERNALHELP PSBackup-help.xml #> [CmdletBinding()] [OutputType([System.Boolean])] Param( [Parameter(Mandatory=$True)] [string]$Path ) Function script:ValidatePsd1 { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] Param ( [Parameter(Mandatory = $true)] [Microsoft.PowerShell.DesiredStateConfiguration.ArgumentToConfigurationDataTransformation()] [hashtable] $Data ) return $Data } $TestErrors = 0 $Params = @{ Data = $Path } $PSD1 = ValidatePsd1 @Params If (-not $PSD1) { $TestErrors++ Write-Waring "[Test-PSBackupFile][ValidatePsd1] The BackupJob file has failed the PSD1 syntax check." } Else { Write-Verbose "[Test-PSBackupFile][ValidatePsd1] The BackupJob file syntax is valid." } $ValidBackupJobNames = @('_ANY') ForEach ($Name in $PSD1.keys) { ForEach ($BJName in $ValidBackupJobNames) { If ($BJName -eq '_ANY') { Write-Verbose "[Test-PSBackupFile][BackupJob Names] '$Name' is a valid BackupJob name" } ElseIf ($BJName -eq $Name) { Write-Verbose "[Test-PSBackupFile][BackupJob Names] '$Name' is a valid BackupJob name" } Else { $TestErrors++ Write-Warning "[Test-PSBackupFile][BackupJob Names] '$Name' is not a valid BackupJob name." } } } $ValidBackupJobAttributes = @('Source','Destination','Parameters','LogPath') ForEach ($ValidAttribute in $ValidBackupJobAttributes) { ForEach ($Name in $PSD1.keys) { If ($PSD1[$Name].keys -contains $ValidAttribute) { Write-Verbose "[Test-PSBackupFile][BackupJobAttributes] '$ValidAttribute' is present in BackupJob '$Name'" } Else { $TestErrors++ Write-Warning "[Test-PSBackupFile][BackupJobAttributes] '$ValidAttribute' is not present in BackupJob '$Name'" } } } $RequiredBackupJobAttributeCount = 4 ForEach ($Name in $PSD1.keys) { If ($PSD1[$Name].keys.count -lt $RequiredBackupJobAttributeCount) { $TestErrors++ Write-Warning "[Test-PSBackupFile][BackupJobAttributeCount] BackupJob: '$Name' does not have enough attributes. '$RequiredBackupJobAttributeCount' is required." } ElseIf ($PSD1[$Name].keys.count -gt $RequiredBackupJobAttributeCount) { $TestErrors++ Write-Warning "[Test-PSBackupFile][BackupJobAttributeCount] BackupJob: '$Name' has too many attributes. '$RequiredBackupJobAttributeCount' is required." } ElseIf ($PSD1[$Name].keys.count -eq $RequiredBackupJobAttributeCount) { Write-Verbose "[Test-PSBackupFile][BackupJobAttributeCount] BackupJob: '$Name' has a valid number of attributes with '$RequiredBackupJobAttributeCount'." } } ForEach ($Name in $PSD1.keys) { ForEach ($Attribute in $PSD1[$Name].keys) { If ($PSD1[$Name][$Attribute].keys) { $TestErrors++ Write-Warning "[Test-PSBackupFile][NestingLimit] Cannot exceed Nesting Limits: BackupJob: '$Name' Attribute: '$Attribute' cannot be a hashtable." } Else { Write-Verbose "[Test-PSBackupFile][NestingLimit] BackupJob: '$Name' Attribute: '$Attribute' is correct." } } } If ($TestErrors -ne 0) { Write-Warning "[Test-PSBackupFile] BackJob Syntax Errors Found: '$TestErrors'" Return $False } Else {Return $True} } |