我一直在努力使用我的脚本来备份我的SQL数据库并将各种信息返回到html文件以进行报告。我试图在备份运行时捕获一些特定的细节,然后再从备份文件本身检索其余信息。当我在单个服务器上以各种形式运行脚本时,它会运行,当我尝试在服务器列表上运行它时,它不起作用。
Set-Location \\DB\Powershell\SQLBackups
. .\SQLBackupFunctions.ps1
$JobFunction=
{
function backup_server_dbs
{
#backup function takes server name as a paramenter
param([String]$ServerName)
#Load SMO features for powershell
Push-Location
Import-Module "SQLPS" -DisableNameChecking
Pop-Location
$nl = [Environment]::NewLine
#create connection to SQL server, ensure timeout set to 25 minutes (default is 10 minutes which is too short for backups)
$SQLSvr = New-Object -TypeName Microsoft.SQLServer.Management.Smo.Server($ServerName)
$SQLSvr.ConnectionContext.StatementTimeout = 0
#create database object type to be called in backup script
$Db = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Database
$BackupFolder = "\\DB\SQL\BACKUPS\$($ServerName)\"
$output = @{}
#for each database on the SQL server instance (excluding system databases)
foreach ($Db in $SQLSvr.Databases | Where-Object {@("tempdb", "model", "msdb", "ReportServer", "ReportServerTempDB") -notcontains $_.Name })
{
$BackupSetDescription = "Full Backup of "+$Db.Name
$BackupFileSuffix = ".dat"
#set backup file name
$BackupName = $BackupFolder+$Db.Name+$BackupFileSuffix
$errorcatch = 0
$btmp = @{}
#useful error checking
#try to backup the sql database
try
{
Backup-SqlDatabase -ServerInstance $ServerName -Database $Db.Name -BackupFile $BackupName -BackupSetDescription $BackupSetDescription -Initialize
}#end try
#catch any errors, add them to the output variable and set the error variable to 1
Catch [System.Exception]
{
$btmp["Info"] = ($error[0].ToString())
$btmp["Status"] = "Failed"
$btmp["DatabaseName"] = $db.name
$btmp["Serveranem"] = $ServerName
$output.Add("$($servername)$($db.name)", $btmp)
$errorcatch = 1
}#end catch
#check error variable, if it is not 1, backup is successful and add success message to output variable, if not return output variable containing error information
Finally
{
if ($errorcatch -ne 1)
{
$btmp["Info"] = "No information"
$btmp["Status"] = "Success"
$btmp["DatabaseName"] = $db.name
$btmp["Serveranem"] = $ServerName
$output.Add("$($servername)$($db.name)", $btmp)
}#end if statement
$errorcatch = 0
}#end finally statement
}#end for each
return $output
}#end function backup_server_dbs
}#end job function
#set up parameters for running jobs
$ScriptStartTime = Get-Date
$BackupFileSuffix = ""
$servers = @("server1",
"server2",
"server3",
"server4",
"server5")
$jobs = @()
foreach($servername in $servers)
{
#set location of backup folder
$BackupFolder = "\\DB\SQL\BACKUPS\$($servername)\"
#test the location of the backup folder, if it does not exist then create the location
if ((Test-Path $BackupFolder) -eq $False)
{
New-Item $BackupFolder -type Directory -Force
}#end if statement
}
#for each server in servers array run job function script block, add outputs to $jobs array
foreach ($servername in $servers)
{
$jobs += Start-job -ScriptBlock {backup_server_dbs $args[0]} -InitializationScript $JobFunction -ArgumentList($servername) -Name $servername
}#end for each statement
$jobs | Wait-Job
$nl = [Environment]::NewLine
$backupfiles = @()
$backups = @()
$finalreport = $null
$fulloutput = @{}
$index = 0
#for each job in jobs array simultaneously display the information and add it to the joutput variable (Tee-object), then remove the job from the array
foreach ($servername in $servers)
{
Receive-Job -Name $servername | Tee-Object -Variable joutput
$fulloutput.Add("$($servername)", $joutput)
#retrieve information from output and backup files themselves for report
$backupsummary = $null
$htmltableheader = "<p>
<h2 align=""left"">$($servername)</h2>
<table>
<tr>
<th>Database</th>
<th>Result</th>
<th>Time Completed</th>
<th>Information</th>
<th>Size</th>
</tr>"
$backupsummary += $htmltableheader
$BackupFolder = "\\DB\SQL\BACKUPS\$($servername)\"
$BackupFiles += Get-SQLBackupFiles -FilePath $BackupFolder -SQLServer $($servername)
foreach($File in $BackupFiles)
{
$Backups = Get-BackupContents -FilePath $file -SQLServer $($servername)
$htmltablerow = "<tr>"
$key = "$($servername)$($backups.databasename)"
if($fulloutput.Item($servername).Item($key).Item("Status") -eq "Failed")
{
$htmltablerow = $htmltablerow + "<td class=""fail"">$($backups.DatabaseName)</td>"
$htmltablerow = $htmltablerow + "<td class=""fail"">Failed</td>"
$htmltablerow = $htmltablerow + "<td class=""fail"">$($backups.BackupFinishDate)</td>"
$htmltablerow = $htmltablerow + "<td class=""fail"">$($fulloutput.Item($key).Item("Info")) </td>"
$htmltablerow = $htmltablerow + "<td class=""fail"">$([math]::Round($backups.BackupSize / 1MB))</td>"
}
else
{
$htmltablerow = $htmltablerow + "<td class=""pass"">$($backups.DatabaseName)</td>"
$htmltablerow = $htmltablerow + "<td class=""pass"">Success</td>"
$htmltablerow = $htmltablerow + "<td class=""pass"">$($backups.BackupFinishDate)</td>"
$htmltablerow = $htmltablerow + "<td class=""pass"">$($fulloutput.Item($key).Item("Info")) </td>"
$htmltablerow = $htmltablerow + "<td class=""pass"">$([math]::Round($backups.BackupSize / 1MB))</td>"
}
$htmltablerow = $htmltablerow + "</tr>"
$backupsummary += $htmltablerow
}
$backupsummary += "</table>
</p>"
$finalreport += $backupsummary
Remove-Job -Name $servername
}
抛出此错误
Method invocation failed because [System.Object[]] doesn't contain a method named 'Item'.
At \\DB\Powershell\SQLBackups\SQLBackupsFull.ps1:176 char:46
+ if($fulloutput.Item($servername).Item <<<< ($key).Item("Status") -eq "Failed")
+ CategoryInfo : InvalidOperation: (Item:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
如果我稍微修改脚本并将重新发送作业信息的部分更改为
#for each job in jobs array simultaneously display the information and add it to the joutput variable (Tee-object), then remove the job from the array
foreach ($servername in $servers)
{
Receive-Job -Name $servername | Tee-Object -Variable joutput
#retrieve information from output and backup files themselves for report
$backupsummary = $null
$htmltableheader = "<p>
<h2 align=""left"">$($servername)</h2>
<table>
<tr>
<th>Database</th>
<th>Result</th>
<th>Time Completed</th>
<th>Information</th>
<th>Size</th>
</tr>"
$backupsummary += $htmltableheader
$BackupFolder = "\\ad.mitsubishi-trust.co.uk\MUTBROOT\DB\SQL\BACKUPS\$($servername)\"
$BackupFiles += Get-SQLBackupFiles -FilePath $BackupFolder -SQLServer $($servername)
foreach($File in $BackupFiles)
{
$Backups = Get-BackupContents -FilePath $file -SQLServer $($servername)
$htmltablerow = "<tr>"
$key = "$($servername)$($backups.databasename)"
if($joutput.Item($key).Item("Status") -eq "Failed")
{
$htmltablerow = $htmltablerow + "<td class=""fail"">$($backups.DatabaseName)</td>"
$htmltablerow = $htmltablerow + "<td class=""fail"">Failed</td>"
$htmltablerow = $htmltablerow + "<td class=""fail"">$($backups.BackupFinishDate)</td>"
$htmltablerow = $htmltablerow + "<td class=""fail"">$($joutput.Item($key).Item("Info")) </td>"
$htmltablerow = $htmltablerow + "<td class=""fail"">$([math]::Round($backups.BackupSize / 1MB))</td>"
}
else
{
$htmltablerow = $htmltablerow + "<td class=""pass"">$($backups.DatabaseName)</td>"
$htmltablerow = $htmltablerow + "<td class=""pass"">Success</td>"
$htmltablerow = $htmltablerow + "<td class=""pass"">$($backups.BackupFinishDate)</td>"
$htmltablerow = $htmltablerow + "<td class=""pass"">$($joutput.Item($key).Item("Info")) </td>"
$htmltablerow = $htmltablerow + "<td class=""pass"">$([math]::Round($backups.BackupSize / 1MB))</td>"
}
$htmltablerow = $htmltablerow + "</tr>"
$backupsummary += $htmltablerow
}
$backupsummary += "</table>
</p>"
$finalreport += $backupsummary
Remove-Job -Name $servername
}
然后我得到了以下错误,并且我为使其工作所做的所有尝试通常都会导致此错误:
You cannot call a method on a null-valued expression.
At \\DB\Powershell\SQLBackups\SQLBackupsFull.ps1:170 char:36
+ if($joutput.Item($key).Item <<<< ("Status") -eq "Failed")
+ CategoryInfo : InvalidOperation: (Item:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
我已经对这个问题喋喋不休了好几天了,当$ servers数组只包含一个名字而不包含多个名字时,我无法弄清楚它为什么会起作用。我也尝试修改包含Receive-Job cmdlet的foreach来调用$ jobs中的每个$ job,然后使用$ job.name(这甚至可能吗??)而不是$ servername,但这也不起作用并且抛出上述空值错误。
编辑 *****************
感谢评论中的帮助,解决方案是使用
的组合$joutput = Receive-job....
代替Tee-Object并使用
if($joutput.$($key).Status -eq "Failed")
而不是调用.Item方法