Whether or not it fixes the problem, try the code below. It's basically your code minus the four scriptblocks (begin, process, finally, and end). Because your script isn't accepting pipelined input there's no need for a PROCESS block, the BEGIN block code can just be part of script body (it will still execute just once before the code in what was the PROCESS block). The code in the scriptblock FINALLY will run once the main "foreach" finishes. Removing those scriptblocks makes error handling clearer; there's no longer nested try/catch blocks.
FWIW, there's an undefined variable on line 14 ($logfile). It won't disrupt anything unless you add "Set-PSDebug -Strict" in the script (which you should do while you're developing the code). I eliminated a pipeline (or two) in your code, and I replaced the variable interpolation inside double-quoted strings with PowerShell format operators ("-f") mostly because it's cleaner and also eliminates any quoting errors. I also added some error checking in the code that processed the config file.
NOTE: I have no way to check the execution of the code -- I have no SQL server.
[CmdletBinding()]
param ()
# BEGIN
Import-Module SQLServer # if Import-Module fails shouldn't the script terminate????
$ErrorActionPreference = "Stop"
[boolean]$ErrorOccurred = $false
[boolean]$ExitWhenFinished = $true
[string]$LogFileName = "SSAS_Backup_log_{0}.txt" -f (GET-DATE -Format 'yyyyMMdd_HHmmss')
[string]$FileNameTemplate = "<ServerName>_<DBName>_{0}.abf" -f (get-date -Format 'yyyyMMdd_HHmmss')
[string]$ConfigFile = $MyInvocation.MyCommand.Source.Replace($MyInvocation.MyCommand.Name, 'SSAS_Config.json')
if ($LogPath){ # $LogPath is undefined! If "Set-PSDebug -Strict" was used the script would throw an error (undefined variable)
[String]$LogFile = "{0}\{1}" -f $LogPath, $LogFileName
}
else {
[String]$LogFile = $MyInvocation.MyCommand.Source.Replace($MyInvocation.MyCommand.Name, $LogFileName)
}
function Write-Log {
[CmdletBinding()]
param(
[Parameter()]
[ValidateNotNullOrEmpty()]
[string]$Message,
[Parameter()]
[ValidateNotNullOrEmpty()]
[ValidateSet('INFO','WARN','EROR')]
[string]$Severity = 'INFO',
[Parameter()]
[ValidateNotNullOrEmpty()]
[string]$Directory
)
[pscustomobject]@{
Time = "[$(Get-Date -f "MM/dd/yyyy HH:mm:ss")]"
Severity = "[$Severity]"
Message = $Message
} | Export-Csv -Path "$Directory" -Append -NoTypeInformation -Delimiter "`t"
} # end Write-Log function
Write-Log -Message "Let the games begin." -Severity INFO -Directory $LogFile
#PROCESS
try {
## Read the config file to capture the required server information.
$SSASConfigInfo = (Get-Content -path $ConfigFile | Out-String | ConvertFrom-Json)
if ($SSASConfigInfo.Count -lt 1){
Throw "Empty config file"
}
Write-Log -Message "Config File: $ConfigFile" -Severity INFO -Directory $LogFile
Write-Log -Message "Records Read : $($SSASConfigInfo.Count)" -Severity INFO -Directory $LogFile
Write-Log -Message "Exit when finished : $ExitWhenFinished)" -Severity INFO -Directory $LogFile
}
catch{
Write-Log -Message "Failed to process configuration file [$ConfigFile]" -Severity "EROR" -Directory $LogFile
$ErrorOccurred = $true
Exit 1
}
## Loop through each server
foreach ($ServerInfo in $SSASConfigInfo)
{
[string]$Server = $ServerInfo.SSAS_Server
[string]$BackupFolder = $ServerInfo.BackupFolder
[int]$RetentionInHours = $ServerInfo.Retention
Write-Log -Message "Server: $Server" -Severity INFO -Directory $LogFile
Write-Log -Message "BackupPath: $BackupFolder" -Severity INFO -Directory $LogFile
Write-Log -Message "Retention: $RetentionInHours hours." -Severity INFO -Directory $LogFile
Write-Log -Message "Error Occurred : $ErrorOccurred" -Severity INFO -Directory $LogFile
Write-Log "Starting backups of [$Server]." -Severity INFO -Directory $LogFile
## Let's get the db/cube information from the SSAS Server
try {
$query = "<Discover xmlns='urn:schemas-microsoft-com:xml-analysis'><RequestType>DBSCHEMA_CATALOGS</RequestType><Restrictions /><Properties /></Discover>"
[xml]$AllDBInfo = Invoke-ASCmd -Server $Server -Query $query -ErrorAction Stop
$DBList = $AllDBInfo.DiscoverResponse.return.root.row.Catalog_Name
Write-Log "DB count retrieved = $($DBList.Count)" -Severity INFO -Directory $LogFile
}
catch {
$ErrorOccurred = $true
Write-Log "$($_.Exception.Message)" -Severity EROR -Directory $LogFile
continue # foreach ($ServerInfo in $SSASConfigInfo)
}
foreach ($db in $DBList){
## Let's set our vairables
Write-Log "`n" -Severity INFO -Directory $LogFile
Write-Log "Starting backup of [$db]." -Severity INFO -Directory $LogFile
$BackupFileName = $FileNameTemplate.Replace('<ServerName>', $Server)
$BackupFileName = $BackupFileName.Replace('<DBName>', $DB)
$BackupPath = Join-Path -Path $BackupFolder -ChildPath $Server
$BackupPath = Join-Path -Path $BackupPath -ChildPath $db
$BackupPath = Join-Path -Path $BackupPath -ChildPath 'SSASBackup'
$FullBackupName = join-path -path $BackupPath -childpath $BackupFileName
Write-Log "Backup Path: $BackupPath" -Severity INFO -Directory $LogFile
Write-Log "Backup Name: $BackupFileName" -Severity INFO -Directory $LogFile
Write-Log "Full Path : $FullBackupName" -Severity INFO -Directory $LogFile
if (test-path -path $BackupPath){
Write-Log "BackupPath validated successfully."-Severity INFO -Directory $LogFile
}
else{
Write-Log "Created the backup path: '$BackupPath'"-Severity INFO -Directory $LogFile
New-Item -Path $BackupPath -ItemType Directory | out-null
}
<############################################## Backup SSAS Cube/DB #########################################>
try {
write-log "Backup Command: Backup-ASDatabase -Server $Server -BackupFile $FullBackupName -Name $db -AllowOverwrite -ApplyCompression -Verbose" -Severity INFO -Directory $LogFile
Write-Log "Backup Start" -Severity INFO -Directory $LogFile
measure-command {
Backup-ASDatabase -Server $Server -BackupFile $FullBackupName -Name $db -AllowOverwrite -ApplyCompression -ErrorAction Stop
} -OutVariable duration | out-null
Write-Log "Backup Command Completed. " -Severity INFO -Directory $LogFile
Write-Log "Backup Duration: $($duration.Hours) hour(s) $($duration.Minutes) minute(s) $($duration.Seconds) seconds" -Severity INFO -Directory $LogFile
write-log "Results: Success." -Severity INFO -Directory $LogFile
}
catch {
## write the error to the log file and then continue to the next cube. Note this will also bypass the delete of the older backup, which is intended.
Write-Log -Message "$($Error.exception.message)" -Severity EROR -Directory $LogFile
Write-Log "Backup Command Completed" -Severity INFO -Directory $LogFile
Write-Log "Backup Duration: $($duration.Hours) hour(s) $($duration.Minutes) minute(s) $($duration.Seconds) seconds" -Severity INFO -Directory $LogFile
Write-Log -Message "Result: Fail" -Severity INFO -Directory $LogFile
$ErrorOccurred = $true
continue # foreach ($db in $DBList)
}
## Let's remove older backups
if ($RetentionInHours -gt 0){
$Retentioninhours *= -1
}
$RemoveFilesOlderThan = (get-date).AddHours($Retentioninhours)
## Get file list
$filesToRemove = (get-childitem -Path $BackupPath |
Where-Object {$_.extension -ieq '.abf' -and $_.LastWriteTime -le $RemoveFilesOlderThan}).FullName
try {
Remove-Item -Path $filesToRemove -ErrorAction Stop
}
catch {
## Write the error but contiue. Shouldn't stop taking new backups if we can't remove the old ones.
Write-Log -Message "$($Error.exception.message)" -Severity EROR -Directory $LogFile
$ErrorOccurred = $true
continue # foreach ($db in $DBList)
}
Write-Log "Server: [$server] completed.`n" -Severity INFO -Directory $LogFile
} # end foreach ($db in $DBList)
} # end foreach ($ServerInfo in $SSASConfigInfo)
# FINALLY
if ($ExitWhenFinished -and $ErrorOccurred){
Write-Error -Message "Error has occurred. Log at $LogFile"
#throw "Error has occurred. Log at $LogFile"
Exit 1
}
elseif ($ExitWhenFinished){
Write-Host 'Process completed Successfully.'
Exit 0
}
else { # $ExitWhenFinished is not $true
Write-Host 'The process completed, but unable to determine if successful or not. This is basically a catch all statement. Review logs and code logic to figure out how we got here.'
}
#END
Write-Verbose 'Got to the END segment of the code.'
# at this point the script will exit without setting exit code
# even though $ExitWhenFinished implies the script should continue