Использование Credential разбивает блок скрипта

Обзор: это сценарий PowerShell, созданный для автоматизации резервного копирования базы данных SQL, которое должно выполняться параллельно, насколько это возможно.

Это прекрасно работает.

$ServerInstanceSource = ".\INSTANCE01"
$ServerInstanceDestination = ".\INSTANCE02"
$UserName = 'user'
$PassWord = 'pass'

$GetDBs = Invoke-Sqlcmd -ServerInstance $ServerInstanceSource -Username $UserName -Password $Password -Database tempdb -Query "SELECT name FROM sys.databases WHERE database_id > 4 AND recovery_model_desc != 'SIMPLE'"
$Date =  (Get-Date -Format 'yyyyMMddhhmmss')
$BackupSourceDirectory = 'F:\DB-BACKUP\SQLBACKUP\Manual Logshipping Restoration\'
$BackupDestinationDirectory = 'D:\BACKUPS\'

Get-Job | Remove-Job -Force

Write-Output "Creating FULL backup. . ."

ForEach($db in $GetDBs.name){
    $FileName = "FULL-$db-$Date.bak"
    $FullBackupPath = Join-Path -Path $BackupSourceDirectory -ChildPath $FileName
    $BackupFullScript = "BACKUP DATABASE $db TO DISK = N'$FullBackupPath' WITH NOFORMAT, NOINIT,  NAME = N'$db-FULL Database Backup', SKIP, NOREWIND, NOUNLOAD, COMPRESSION,  STATS = 10"
    $SQLBackupFull = "Invoke-Sqlcmd -ServerInstance $ServerInstanceSource -Username $UserName -Password $Password -Database tempdb -Query `"$BackupFullScript`" -QueryTimeout 21600;" # 5 hours
    [scriptblock]$cmdblock1 = [ScriptBlock]::Create($SQLBackupFull)
    Start-Job $cmdblock1
}

Get-Job | Wait-Job
Get-Job | Receive-Job

Однако, когда я преобразовал код для принятия учетных данных вместо жестко запрограммированных, я получаю ошибки:

$ServerInstanceSource = ".\INSTANCE01"
$ServerInstanceDestination = ".\INSTANCE02"
$Cred = Get-Credential

$GetDBs = Invoke-Sqlcmd -ServerInstance $ServerInstanceSource -Credential $Cred -Database tempdb -Query "SELECT name FROM sys.databases WHERE database_id > 4 AND recovery_model_desc != 'SIMPLE' -- Dynamic; Input specific databases only, if necessary."
$Date =  (Get-Date -Format 'yyyyMMddhhmmss')
$BackupSourceDirectory = 'C:\Users\edgar.bayron\Music\Edgar\Trash Can\DB-BACKUP\SQLBACKUP\LAX-DBMON\' # "F:\DB-BACKUP\SQLBACKUP\Manual Logshipping Restoration\"
$BackupDestinationDirectory = 'C:\Users\edgar.bayron\Music\Edgar\Trash Can\DB-BACKUP\SQLBACKUP\DAL-INDBS01\' # "D:\BACKUPS\"

Get-Job | Remove-Job -Force

Write-Output "Creating FULL backup. . ."

ForEach($db in $GetDBs.name){
    $FileName = "FULL-$db-$Date.bak"
    $FullBackupPath = Join-Path -Path $BackupSourceDirectory -ChildPath $FileName
    $BackupFullScript = "BACKUP DATABASE $db TO DISK = N'$FullBackupPath' WITH NOFORMAT, NOINIT,  NAME = N'$db-FULL Database Backup', SKIP, NOREWIND, NOUNLOAD, COMPRESSION,  STATS = 10"
    $SQLBackupFull = "Invoke-Sqlcmd -ServerInstance $ServerInstanceSource -Credential $Cred -Database tempdb -Query `"$BackupFullScript`" -QueryTimeout 21600;" # 5 hours
    [scriptblock]$cmdblock1 = [ScriptBlock]::Create($SQLBackupFull)
    Start-Job $cmdblock1
}

Get-Job | Wait-Job
Get-Job | Receive-Job

Ошибка:

Wait-Job : The Wait-Job cmdlet cannot finish working, because one or more jobs are blocked waiting for user interaction.  Process interactive job 
output by using the Receive-Job cmdlet, and then try again.

Я пытаюсь применить то, что было рекомендовано здесь, но я не могу полностью применить его, поскольку в примере у него только один параметр, а у меня их несколько. Если я пытаюсь применить его, он ищет другие переменные.

Я пытался использовать {PARAM($ServerInstanceSource, $cred, $BackupFullScript), но он прерывает SQL-запрос внутри $BackupFullScript.

Может ли кто-нибудь помочь мне в этом?


person Edgar Allan Bayron    schedule 09.07.2021    source источник
comment
Да, ну, Get-Credential запрашивает учетные данные, не так ли? Вы смотрели Примеры Get-Credential, чтобы увидеть, как создавать PSCredential объекты без запроса?   -  person AlwaysLearning    schedule 09.07.2021
comment
я не вижу веских причин, по которым я бы использовал этот метод. В целях безопасности я перехожу на использование Get-Credential, чтобы удалить жестко закодированные имя пользователя и пароль в сценарии.   -  person Edgar Allan Bayron    schedule 09.07.2021


Ответы (1)


Вы пытаетесь использовать объект учетных данных в виде строки. Что происходит, так это то, что $Cred преобразуется в строку с использованием метода .ToString(), который будет выглядеть как этот System.Management.Automation.PSCredential, который, очевидно, не будет работать при передаче в качестве аргумента параметру -Credential в вашем блоке скриптов.

Вы можете либо вернуться к исходной реализации использования имени пользователя и пароля, где вы можете передать имя пользователя и пароль, взятые из объекта $Cred

$SQLBackupFull = "Invoke-Sqlcmd -ServerInstance $ServerInstanceSource -Username $($Cred.UserName) -Password $($Cred.GetNetworkCredential().Password) -Database tempdb -Query `"$BackupFullScript`" -QueryTimeout 21600;" # 5 hours

или вы можете создать блок сценария с блоком param(), где вы можете передать объект $Cred в качестве аргумента

EDIT: блок сценария $cmd перемещен перед foreach, поэтому он создается только один раз.

$cmd = {
    param(
        $ServerInstanceSource,
        $Cred,
        $BackupFullScript
    )
    Invoke-Sqlcmd -ServerInstance $ServerInstanceSource -Credential $Cred -Database tempdb -Query $BackupFullScript -QueryTimeout 21600;
}

ForEach ($db in $GetDBs.name) {
    $FileName = "FULL-$db-$Date.bak"
    $FullBackupPath = Join-Path -Path $BackupSourceDirectory -ChildPath $FileName
    $BackupFullScript = "BACKUP DATABASE $db TO DISK = N'$FullBackupPath' WITH NOFORMAT, NOINIT,  NAME = N'$db-FULL Database Backup', SKIP, NOREWIND, NOUNLOAD, COMPRESSION,  STATS = 10"

    Start-Job $cmd -ArgumentList $ServerInstanceSource, $Cred, $BackupFullScript
}
person Daniel    schedule 09.07.2021
comment
вау, 1-й способ сработал. Я не знал, что смогу это сделать ($Cred.UserName). Будет ли второй метод иметь преимущество перед первым? Я бы тоже хотел попробовать такой. Но он выглядит немного сложнее, чем 1-й. В любом случае большое спасибо! - person Edgar Allan Bayron; 09.07.2021
comment
Рад, что смог помочь. Я думаю, что вы в порядке с первым методом. Я предпочитаю блок сценария с блоком параметров, где я могу передавать полные объекты в качестве аргументов и не иметь жестко закодированного кода внутри блока сценария, но все, что работает. Второй метод может быть немного более безопасным, потому что вы не передаете пароль в открытом виде, но я думаю, что риск здесь незначителен. Кто читает это, поправьте меня, если я ошибаюсь. - person Daniel; 09.07.2021