当用户未登录且 运行 通过 SCCM 作为 Powershell 中的系统帐户时,BCD 编辑导出失败
BCD Edit Export fails when user not logged on and run via SCCM as System account in Powershell
我遇到一个问题,在尝试将 VHD 部署到系统时,运行通过 SCCM 将 powershell 脚本设置为系统帐户。
该脚本尝试执行 bcdedit /export - 但是当我 运行 它作为我自己或使用 psexec /i /s cmd 然后 运行 在我登录时通过它的 powershell用我自己的帐户连接到系统,它工作正常。
当 运行通过 SCCM 运行脚本时,它会停止脚本并抛出下面在日志输出注释中看到的自定义错误,无论我是否登录,这让我相信系统没有无法将 bcd 导出到文件。
脚本如下:
#Set Package Details
$PackageName = "Certiport-Office2013_01.30_86a"
# Set Logging Location
$Global:LogFile = "C:\Logs$PackageName.log"
$Global:Returnval = 0
##Set Diskpart Commands
$DiskpartAttachVDisk = "SELECT VDISK FILE=D:\Certiport$PackageName.vhd
ATTACH VDISK"
$DiskpartChangeLetter ="SELECT VDISK FILE=D:\Certiport$PackageName.vhd
SELECT PARTITION 1
ASSIGN LETTER=V
EXIT"
$DiskpartDetachVDisk = "SELECT VDISK FILE=D:\Certiport$PackageName.vhd
DETACH VDISK
EXIT"
# Set PKGLocation
$Global:hostinvocation = (Get-Variable MyInvocation).Value
$Global:PkgLocation = if($hostinvocation.MyCommand.path -ne $null) { Split-Path $hostinvocation.MyCommand.path }
else { (Get-Location).Path }
######################
## LOGGING FUNCTION ##
##Writes to the log file in C:\Logs
$OSName = (Get-WmiObject -class Win32_OperatingSystem).Caption.trim()
Function Log {
Param ([string]$LogEntry)
$TimeStamp = Get-Date -Format "HH:mm:ss"
Write-Host $TimeStamp - $LogEntry
Add-Content "$LogFile" -value "$TimeStamp - $LogEntry"
}
##Creates the C:\Logs\PackageName.log or renames it to .lo_ if it already exists
##The .lo_ format is still readable by some live log file readers such as CMTrace and Trace32 and doesn't require being renamed before reading.
$Date = Get-Date
If (test-path $LogFile) {
Copy-Item -Path $LogFile ($LogFile.TrimEnd("log") + "lo_") -Force
New-Item -Path "C:\Logs$PackageName.log" -Force -ItemType File -Value "---Log File---`r`nInstalling = $PackageName`r`nDate = $Date`r`nOperating System = $OSName`r`nOS Architecture = $env:PROCESSOR_ARCHITECTURE`r`n`r`n`r`n---Start Logging---`r`n`r`n"
} else {
New-Item -Path "C:\Logs$PackageName.log" -Force -ItemType File -Value "---Log File---`r`nInstalling = $PackageName`r`nDate = $Date`r`nOperating System = $OSName`r`nOS Architecture = $env:PROCESSOR_ARCHITECTURE`r`n`r`n`r`n---Start Logging---`r`n`r`n"
}
######################
Function Check-CriticalError(){
If($Global:CriticalError -eq $true){
Log("Critical Error detected! - Script will now Exit")
$returnval = 1
Exit($returnval)
}
}
$Global:CriticalError = $False
Log("######################")
Log("Starting Certiport 2013 VHD Installation")
Log("Source Directory is: $PKGLocation")
# Check that D Drive Exists
If(-Not (Test-Path D:\)){
Log("ERROR: D Drive does not exist - Exiting")
$Global:CriticalError = $true
}
Check-CriticalError
# Check Disk Space requirement (40 GB) for D Drive
If(((Get-WmiObject Win32_Volume -Namespace "root\CIMV2" -Filter {DriveLetter = "D:"}).FreeSpace) -lt "21474836480"){
Log("ERROR: Insufficient Disk Space - 40GB Required - $("{0:N2}" -f (((Get-WmiObject Win32_Volume -Namespace "root\CIMV2" -Filter {DriveLetter = "D:"}).FreeSpace)/1024/1024)) Mb Free - Exiting")
$Global:CriticalError = $true
}
Check-CriticalError
# Check the Certiport Install Directory exists and create it if not.
If(-Not (Test-Path D:\CertiPort)){
New-Item -ItemType Directory D:\Certiport | Out-Null
}
# Extract the VHD to the correct directory and perform an MD5 Check OR Verify and validate the state of the currently existing VHD.
$Global:VHDFile = "D:\Certiport$PackageName.vhd"
$Global:MasterHash = Get-Content $PkgLocation\MD5.txt
If(-Not (Test-Path D:\Certiport$PackageName.vhd)){
Log("VHD Does not exist in D:\Certiport - Extracting from Compressed File")
$ScriptBlock = [Scriptblock]::Create("$PkgLocationza.exe x `"$PkgLocation$PackageName.7z`" -oD:\ -r -aoa")
Log("Running - `'$ScriptBlock`'")
ZipExtract = Invoke-Command -ScriptBlock $ScriptBlock
Log("Verifying MD5 Hash")
$hash = [Security.Cryptography.HashAlgorithm]::Create( "MD5" )
$stream = ([IO.StreamReader]"$VHDFile").BaseStream
$HashCode = -join ($hash.ComputeHash($stream) | ForEach { "{0:x2}" -f $_ })
$stream.Close()
If($MasterHash -ne $HashCode){
Log("ERROR: Hash Check Failed - `"$MasterHash`" is not `"$HashCode`"")
Log("ERROR: Source appears corrupted")
$Global:CriticalError = $true
}Else{
Log("Hash Check Successful")
}
}Else{
Log("VHD already exists in D:\Certiport - Verifying MD5 Hash")
$hash = [Security.Cryptography.HashAlgorithm]::Create( "MD5" )
$stream = ([IO.StreamReader]"$VHDFile").BaseStream
$HashCode = -join ($hash.ComputeHash($stream) | ForEach { "{0:x2}" -f $_ })
$stream.Close()
If($MasterHash -ne $HashCode){
Log("WARNING: Hash Check Failed - `"$MasterHash`" is not `"$HashCode`"")
Log("Extracting file from source...")
$ScriptBlock = [Scriptblock]::Create("$PkgLocationza.exe x `"$PkgLocation$PackageName.7z`" -oD:\ -r -aoa")
Log("Running - `'$ScriptBlock`'")
ZipExtract = Invoke-Command -ScriptBlock $ScriptBlock
Log("Verifying MD5 Hash")
$hash = [Security.Cryptography.HashAlgorithm]::Create( "MD5" )
$stream = ([IO.StreamReader]"$VHDFile").BaseStream
$HashCode = -join ($hash.ComputeHash($stream) | ForEach { "{0:x2}" -f $_ })
$stream.Close()
If($MasterHash -ne $HashCode){
Log("ERROR: Hash Check Failed - `"$MasterHash`" is not `"$HashCode`"")
Log("ERROR: Source appears corrupted")
$Global:CriticalError = $true
}Else{
Log("VHD Hash Check Successful")
}
}Else{
Log("VHD Hash Check Successful")
}
}
Check-CriticalError
# Check BCD For any Previous Entry and remove it
$ScriptBlock = [Scriptblock]::Create("bcdedit /v")
$BCDConfig = Invoke-Command -ScriptBlock $ScriptBlock
$BCDItem = (0..($BCDConfig.Count - 1) | Where { $BCDConfig[$_] -like "description*Certiport 2013 Certification*" }) - 3
If($BCDConfig[$BCDItem] -ne $BCDConfig[-3]){
$BCDIdentifier = (((($BCDConfig[$BCDItem]).Split("{"))[1]).Split("}"))[0]
Log("Previous entry found - $BCDIdentifier")
$ScriptBlock = [Scriptblock]::Create("bcdedit /delete ``{$BCDIdentifier``}")
$cmdReturn = Invoke-Command -ScriptBlock $ScriptBlock
If($cmdReturn -eq "The operation completed successfully."){
Log("Successfully Removed previous Certiport Entry")
}Else{
Log("ERROR: Could not remove previous Entry - $cmdReturn")
$Global:CriticalError = $true
}
}
Check-CriticalError
# Update Boot Files for UEFI devices and Windows 7
$ScriptBlock = [Scriptblock]::Create("bcdedit /export C:\CertiportVHD-2013-BCD-Backup")
$cmdReturn = Invoke-Command -ScriptBlock $ScriptBlock
If($cmdReturn -eq "The operation completed successfully."){
Set-Content $Env:Temp\CertiportVHD.txt $DiskpartAttachVDisk
$ScriptBlock = [Scriptblock]::Create("diskpart /s $($Env:Temp)\CertiportVHD.txt")
$cmdReturn = Invoke-Command -ScriptBlock $ScriptBlock
If($cmdReturn -like "*DiskPart successfully attached the virtual disk file.*"){
Sleep 10
Set-Content $Env:Temp\CertiportVHD.txt $DiskpartChangeLetter
$ScriptBlock = [Scriptblock]::Create("diskpart /s $($Env:Temp)\CertiportVHD.txt")
$cmdReturn = Invoke-Command -ScriptBlock $ScriptBlock
If($cmdReturn -like "*DiskPart successfully assigned the drive letter or mount point.*"){
Log("VHD Successfully Mounted")
$ScriptBlock = [Scriptblock]::Create("bcdboot.exe V:\Windows")
$cmdReturn = Invoke-Command -ScriptBlock $ScriptBlock
If($cmdReturn -eq "Boot files successfully created."){
Log("Boot files successfully created")
Set-Content $Env:Temp\CertiportVHD.txt $DiskpartDetachVDisk
$ScriptBlock = [Scriptblock]::Create("diskpart /s $($Env:Temp)\CertiportVHD.txt")
$cmdReturn = Invoke-Command -ScriptBlock $ScriptBlock
If($cmdReturn -like "DiskPart successfully detached the virtual disk file."){
Log("Successfully Detached the VHD")
sleep 10
$ScriptBlock = [Scriptblock]::Create("bcdedit /import C:\CertiportVHD-2013-BCD-Backup")
$cmdReturn = Invoke-Command -ScriptBlock $ScriptBlock
If($cmdReturn -eq "The operation completed successfully."){
Log("Successfully Imported the BCD Backup")
}Else{
Log("ERROR: Could not restore the BCD Backup - $cmdReturn")
}
}Else{
Log("ERROR: Could not detach the VHD - $cmdReturn")
}
}Else{
Log("ERROR: Could not create the boot files - $cmdReturn")
}
}Else{
Log("ERROR: Could not assign the VHD to `"V:`" drive - $cmdReturn")
$Global:CriticalError = $true
Check-CriticalError
}
}Else{
Log("ERROR: Could not mount the VHD - $cmdReturn")
$Global:CriticalError = $true
Check-CriticalError
}
}Else{
Log("ERROR: Could not back up BCD - Quitting immediately to avoid destroying boot order - $cmdReturn")
$Global:CriticalError = $true
Check-CriticalError
}
# Configure new BCD Entry For Certiport 2013
Log("Creating BCD Entry")
$ScriptBlock = [Scriptblock]::Create("bcdedit.exe /copy ``{default``} /d `"Certiport 2013 Certification v1.3`"")
$cmdReturn = Invoke-Command -ScriptBlock $ScriptBlock
If($cmdReturn -like "The entry was successfully copied to*"){
$CertiportGuid = ((($cmdReturn.Split(" "))[6]) -replace ".$") -replace '[{}]',''
Log("Created new BCD entry - $CertiportGuid")
$ScriptBlock = [Scriptblock]::Create("bcdedit.exe /set ``{$CertiportGuid``} osdevice vhd=[d:]\Certiport$PackageName.vhd")
$cmdReturn = Invoke-Command -ScriptBlock $ScriptBlock
If($cmdReturn -like "The operation completed successfully."){
Log("Successfully changed BCD osdevice")
$ScriptBlock = [Scriptblock]::Create("bcdedit.exe /set ``{$CertiportGuid``} device vhd=[d:]\Certiport$PackageName.vhd")
$cmdReturn = Invoke-Command -ScriptBlock $ScriptBlock
If($cmdReturn -like "The operation completed successfully."){
Log("Successfully changed BCD device")
$ScriptBlock = [Scriptblock]::Create("bcdedit.exe /timeout 5")
$cmdReturn = Invoke-Command -ScriptBlock $ScriptBlock
If($cmdReturn -like "The operation completed successfully."){
Log("Successfully changed boot timeout")
}Else{
Log("ERROR: Could not change boot timeout - $cmdReturn")
}
}Else{
Log("ERROR: Could not change BCD device - $cmdReturn")
$Global:CriticalError = $true
}
}Else{
Log("ERROR: Could not change BCD osdevice - $cmdReturn")
$Global:CriticalError = $true
}
}Else{
Log("ERROR: Could not copy BCD Entry for Certiport - $cmdReturn")
$Global:CriticalError = $true
}
Check-CriticalError
Log("Certiport Exam VHD Deployed")
exit $returnval
日志输出:
10:28:29 - ######################
10:28:29 - Starting Certiport 2013 VHD Installation
10:28:29 - Source Directory is: C:\Windows\SysWOW64\CCM\Cache[=14=]000379.2.System
10:28:29 - VHD already exists in D:\Certiport - Verifying MD5 Hash
10:36:41 - VHD Hash Check Successful
10:36:44 - ERROR: Could not back up BCD - Quitting immediately to
avoid destroying boot order -
10:36:44 - Critical Error detected! - Script will now Exit
在我看来问题出在这个脚本块中:
$ScriptBlock = [Scriptblock]::Create("bcdedit /export C:\CertiportVHD-2013-BCD-Backup")
$cmdReturn = Invoke-Command -ScriptBlock $ScriptBlock
我运行在多种设备上编写代码,结果相同。
Windows 10 x64、Windows 8.1 x64 和 Windows 7 x86/x64.
运行 powershell v4
我尝试将我认为是上述问题的部分更改为:
$App = "bcdedit"
$Arguments = "/export C:\CertiportVHD-2013-BCD-Backup"
$cmdReturn = Start-Process -FilePath $App -ArgumentList $Arguments -Wait -PassThru
然而这也不起作用,并给出了相同的结果。
非常感谢您提供的任何帮助,提前致谢。
关于您在评论中提出的问题 不幸的是,我无法通过使用 procmon 找到错误。还有其他想法吗?
不,不是真的。你说的使用 psexec 作为系统执行命令是有效的,我认为它在 SCCM 下没有区别 运行。
- procmon 是否捕获了
bcdedit
的开始和停止?
- 退出代码是什么?您可以 post 截图或分享 procmon 跟踪吗?
事实证明,此问题的解决方法是获取 bcdedit 的副本并将其放入包中,而不是简单地指定 "bcdedit".
我在整个脚本中将对文件 'bcdedit.exe' 的引用更新为“.\bcdedit.exe”,因为 SCCM 将目录更改为它复制到本地缓存的包文件夹的根目录。
我不确定为什么当 运行 作为用户登录到设备时可以指定 bcdedit,但是当 运行 作为系统登录时 SCCM 不能使用相同的语法,但是这修复成功。
感谢@LievenKeersmaekers 帮助我弄清楚系统找不到文件,当时他说 $cmdReturn 是空的,这让我开始思考为什么会这样,当他测试了它并返回了一个值,无论它是否出错。
编辑:
我还注意到 bcdboot.exe 发生了同样的事情,不得不将文件复制到包源文件并引用 .\bcdboot.exe
我遇到一个问题,在尝试将 VHD 部署到系统时,运行通过 SCCM 将 powershell 脚本设置为系统帐户。
该脚本尝试执行 bcdedit /export - 但是当我 运行 它作为我自己或使用 psexec /i /s cmd 然后 运行 在我登录时通过它的 powershell用我自己的帐户连接到系统,它工作正常。
当 运行通过 SCCM 运行脚本时,它会停止脚本并抛出下面在日志输出注释中看到的自定义错误,无论我是否登录,这让我相信系统没有无法将 bcd 导出到文件。
脚本如下:
#Set Package Details
$PackageName = "Certiport-Office2013_01.30_86a"
# Set Logging Location
$Global:LogFile = "C:\Logs$PackageName.log"
$Global:Returnval = 0
##Set Diskpart Commands
$DiskpartAttachVDisk = "SELECT VDISK FILE=D:\Certiport$PackageName.vhd
ATTACH VDISK"
$DiskpartChangeLetter ="SELECT VDISK FILE=D:\Certiport$PackageName.vhd
SELECT PARTITION 1
ASSIGN LETTER=V
EXIT"
$DiskpartDetachVDisk = "SELECT VDISK FILE=D:\Certiport$PackageName.vhd
DETACH VDISK
EXIT"
# Set PKGLocation
$Global:hostinvocation = (Get-Variable MyInvocation).Value
$Global:PkgLocation = if($hostinvocation.MyCommand.path -ne $null) { Split-Path $hostinvocation.MyCommand.path }
else { (Get-Location).Path }
######################
## LOGGING FUNCTION ##
##Writes to the log file in C:\Logs
$OSName = (Get-WmiObject -class Win32_OperatingSystem).Caption.trim()
Function Log {
Param ([string]$LogEntry)
$TimeStamp = Get-Date -Format "HH:mm:ss"
Write-Host $TimeStamp - $LogEntry
Add-Content "$LogFile" -value "$TimeStamp - $LogEntry"
}
##Creates the C:\Logs\PackageName.log or renames it to .lo_ if it already exists
##The .lo_ format is still readable by some live log file readers such as CMTrace and Trace32 and doesn't require being renamed before reading.
$Date = Get-Date
If (test-path $LogFile) {
Copy-Item -Path $LogFile ($LogFile.TrimEnd("log") + "lo_") -Force
New-Item -Path "C:\Logs$PackageName.log" -Force -ItemType File -Value "---Log File---`r`nInstalling = $PackageName`r`nDate = $Date`r`nOperating System = $OSName`r`nOS Architecture = $env:PROCESSOR_ARCHITECTURE`r`n`r`n`r`n---Start Logging---`r`n`r`n"
} else {
New-Item -Path "C:\Logs$PackageName.log" -Force -ItemType File -Value "---Log File---`r`nInstalling = $PackageName`r`nDate = $Date`r`nOperating System = $OSName`r`nOS Architecture = $env:PROCESSOR_ARCHITECTURE`r`n`r`n`r`n---Start Logging---`r`n`r`n"
}
######################
Function Check-CriticalError(){
If($Global:CriticalError -eq $true){
Log("Critical Error detected! - Script will now Exit")
$returnval = 1
Exit($returnval)
}
}
$Global:CriticalError = $False
Log("######################")
Log("Starting Certiport 2013 VHD Installation")
Log("Source Directory is: $PKGLocation")
# Check that D Drive Exists
If(-Not (Test-Path D:\)){
Log("ERROR: D Drive does not exist - Exiting")
$Global:CriticalError = $true
}
Check-CriticalError
# Check Disk Space requirement (40 GB) for D Drive
If(((Get-WmiObject Win32_Volume -Namespace "root\CIMV2" -Filter {DriveLetter = "D:"}).FreeSpace) -lt "21474836480"){
Log("ERROR: Insufficient Disk Space - 40GB Required - $("{0:N2}" -f (((Get-WmiObject Win32_Volume -Namespace "root\CIMV2" -Filter {DriveLetter = "D:"}).FreeSpace)/1024/1024)) Mb Free - Exiting")
$Global:CriticalError = $true
}
Check-CriticalError
# Check the Certiport Install Directory exists and create it if not.
If(-Not (Test-Path D:\CertiPort)){
New-Item -ItemType Directory D:\Certiport | Out-Null
}
# Extract the VHD to the correct directory and perform an MD5 Check OR Verify and validate the state of the currently existing VHD.
$Global:VHDFile = "D:\Certiport$PackageName.vhd"
$Global:MasterHash = Get-Content $PkgLocation\MD5.txt
If(-Not (Test-Path D:\Certiport$PackageName.vhd)){
Log("VHD Does not exist in D:\Certiport - Extracting from Compressed File")
$ScriptBlock = [Scriptblock]::Create("$PkgLocationza.exe x `"$PkgLocation$PackageName.7z`" -oD:\ -r -aoa")
Log("Running - `'$ScriptBlock`'")
ZipExtract = Invoke-Command -ScriptBlock $ScriptBlock
Log("Verifying MD5 Hash")
$hash = [Security.Cryptography.HashAlgorithm]::Create( "MD5" )
$stream = ([IO.StreamReader]"$VHDFile").BaseStream
$HashCode = -join ($hash.ComputeHash($stream) | ForEach { "{0:x2}" -f $_ })
$stream.Close()
If($MasterHash -ne $HashCode){
Log("ERROR: Hash Check Failed - `"$MasterHash`" is not `"$HashCode`"")
Log("ERROR: Source appears corrupted")
$Global:CriticalError = $true
}Else{
Log("Hash Check Successful")
}
}Else{
Log("VHD already exists in D:\Certiport - Verifying MD5 Hash")
$hash = [Security.Cryptography.HashAlgorithm]::Create( "MD5" )
$stream = ([IO.StreamReader]"$VHDFile").BaseStream
$HashCode = -join ($hash.ComputeHash($stream) | ForEach { "{0:x2}" -f $_ })
$stream.Close()
If($MasterHash -ne $HashCode){
Log("WARNING: Hash Check Failed - `"$MasterHash`" is not `"$HashCode`"")
Log("Extracting file from source...")
$ScriptBlock = [Scriptblock]::Create("$PkgLocationza.exe x `"$PkgLocation$PackageName.7z`" -oD:\ -r -aoa")
Log("Running - `'$ScriptBlock`'")
ZipExtract = Invoke-Command -ScriptBlock $ScriptBlock
Log("Verifying MD5 Hash")
$hash = [Security.Cryptography.HashAlgorithm]::Create( "MD5" )
$stream = ([IO.StreamReader]"$VHDFile").BaseStream
$HashCode = -join ($hash.ComputeHash($stream) | ForEach { "{0:x2}" -f $_ })
$stream.Close()
If($MasterHash -ne $HashCode){
Log("ERROR: Hash Check Failed - `"$MasterHash`" is not `"$HashCode`"")
Log("ERROR: Source appears corrupted")
$Global:CriticalError = $true
}Else{
Log("VHD Hash Check Successful")
}
}Else{
Log("VHD Hash Check Successful")
}
}
Check-CriticalError
# Check BCD For any Previous Entry and remove it
$ScriptBlock = [Scriptblock]::Create("bcdedit /v")
$BCDConfig = Invoke-Command -ScriptBlock $ScriptBlock
$BCDItem = (0..($BCDConfig.Count - 1) | Where { $BCDConfig[$_] -like "description*Certiport 2013 Certification*" }) - 3
If($BCDConfig[$BCDItem] -ne $BCDConfig[-3]){
$BCDIdentifier = (((($BCDConfig[$BCDItem]).Split("{"))[1]).Split("}"))[0]
Log("Previous entry found - $BCDIdentifier")
$ScriptBlock = [Scriptblock]::Create("bcdedit /delete ``{$BCDIdentifier``}")
$cmdReturn = Invoke-Command -ScriptBlock $ScriptBlock
If($cmdReturn -eq "The operation completed successfully."){
Log("Successfully Removed previous Certiport Entry")
}Else{
Log("ERROR: Could not remove previous Entry - $cmdReturn")
$Global:CriticalError = $true
}
}
Check-CriticalError
# Update Boot Files for UEFI devices and Windows 7
$ScriptBlock = [Scriptblock]::Create("bcdedit /export C:\CertiportVHD-2013-BCD-Backup")
$cmdReturn = Invoke-Command -ScriptBlock $ScriptBlock
If($cmdReturn -eq "The operation completed successfully."){
Set-Content $Env:Temp\CertiportVHD.txt $DiskpartAttachVDisk
$ScriptBlock = [Scriptblock]::Create("diskpart /s $($Env:Temp)\CertiportVHD.txt")
$cmdReturn = Invoke-Command -ScriptBlock $ScriptBlock
If($cmdReturn -like "*DiskPart successfully attached the virtual disk file.*"){
Sleep 10
Set-Content $Env:Temp\CertiportVHD.txt $DiskpartChangeLetter
$ScriptBlock = [Scriptblock]::Create("diskpart /s $($Env:Temp)\CertiportVHD.txt")
$cmdReturn = Invoke-Command -ScriptBlock $ScriptBlock
If($cmdReturn -like "*DiskPart successfully assigned the drive letter or mount point.*"){
Log("VHD Successfully Mounted")
$ScriptBlock = [Scriptblock]::Create("bcdboot.exe V:\Windows")
$cmdReturn = Invoke-Command -ScriptBlock $ScriptBlock
If($cmdReturn -eq "Boot files successfully created."){
Log("Boot files successfully created")
Set-Content $Env:Temp\CertiportVHD.txt $DiskpartDetachVDisk
$ScriptBlock = [Scriptblock]::Create("diskpart /s $($Env:Temp)\CertiportVHD.txt")
$cmdReturn = Invoke-Command -ScriptBlock $ScriptBlock
If($cmdReturn -like "DiskPart successfully detached the virtual disk file."){
Log("Successfully Detached the VHD")
sleep 10
$ScriptBlock = [Scriptblock]::Create("bcdedit /import C:\CertiportVHD-2013-BCD-Backup")
$cmdReturn = Invoke-Command -ScriptBlock $ScriptBlock
If($cmdReturn -eq "The operation completed successfully."){
Log("Successfully Imported the BCD Backup")
}Else{
Log("ERROR: Could not restore the BCD Backup - $cmdReturn")
}
}Else{
Log("ERROR: Could not detach the VHD - $cmdReturn")
}
}Else{
Log("ERROR: Could not create the boot files - $cmdReturn")
}
}Else{
Log("ERROR: Could not assign the VHD to `"V:`" drive - $cmdReturn")
$Global:CriticalError = $true
Check-CriticalError
}
}Else{
Log("ERROR: Could not mount the VHD - $cmdReturn")
$Global:CriticalError = $true
Check-CriticalError
}
}Else{
Log("ERROR: Could not back up BCD - Quitting immediately to avoid destroying boot order - $cmdReturn")
$Global:CriticalError = $true
Check-CriticalError
}
# Configure new BCD Entry For Certiport 2013
Log("Creating BCD Entry")
$ScriptBlock = [Scriptblock]::Create("bcdedit.exe /copy ``{default``} /d `"Certiport 2013 Certification v1.3`"")
$cmdReturn = Invoke-Command -ScriptBlock $ScriptBlock
If($cmdReturn -like "The entry was successfully copied to*"){
$CertiportGuid = ((($cmdReturn.Split(" "))[6]) -replace ".$") -replace '[{}]',''
Log("Created new BCD entry - $CertiportGuid")
$ScriptBlock = [Scriptblock]::Create("bcdedit.exe /set ``{$CertiportGuid``} osdevice vhd=[d:]\Certiport$PackageName.vhd")
$cmdReturn = Invoke-Command -ScriptBlock $ScriptBlock
If($cmdReturn -like "The operation completed successfully."){
Log("Successfully changed BCD osdevice")
$ScriptBlock = [Scriptblock]::Create("bcdedit.exe /set ``{$CertiportGuid``} device vhd=[d:]\Certiport$PackageName.vhd")
$cmdReturn = Invoke-Command -ScriptBlock $ScriptBlock
If($cmdReturn -like "The operation completed successfully."){
Log("Successfully changed BCD device")
$ScriptBlock = [Scriptblock]::Create("bcdedit.exe /timeout 5")
$cmdReturn = Invoke-Command -ScriptBlock $ScriptBlock
If($cmdReturn -like "The operation completed successfully."){
Log("Successfully changed boot timeout")
}Else{
Log("ERROR: Could not change boot timeout - $cmdReturn")
}
}Else{
Log("ERROR: Could not change BCD device - $cmdReturn")
$Global:CriticalError = $true
}
}Else{
Log("ERROR: Could not change BCD osdevice - $cmdReturn")
$Global:CriticalError = $true
}
}Else{
Log("ERROR: Could not copy BCD Entry for Certiport - $cmdReturn")
$Global:CriticalError = $true
}
Check-CriticalError
Log("Certiport Exam VHD Deployed")
exit $returnval
日志输出:
10:28:29 - ######################
10:28:29 - Starting Certiport 2013 VHD Installation
10:28:29 - Source Directory is: C:\Windows\SysWOW64\CCM\Cache[=14=]000379.2.System
10:28:29 - VHD already exists in D:\Certiport - Verifying MD5 Hash
10:36:41 - VHD Hash Check Successful
10:36:44 - ERROR: Could not back up BCD - Quitting immediately to avoid destroying boot order -
10:36:44 - Critical Error detected! - Script will now Exit
在我看来问题出在这个脚本块中:
$ScriptBlock = [Scriptblock]::Create("bcdedit /export C:\CertiportVHD-2013-BCD-Backup")
$cmdReturn = Invoke-Command -ScriptBlock $ScriptBlock
我运行在多种设备上编写代码,结果相同。 Windows 10 x64、Windows 8.1 x64 和 Windows 7 x86/x64.
运行 powershell v4
我尝试将我认为是上述问题的部分更改为:
$App = "bcdedit"
$Arguments = "/export C:\CertiportVHD-2013-BCD-Backup"
$cmdReturn = Start-Process -FilePath $App -ArgumentList $Arguments -Wait -PassThru
然而这也不起作用,并给出了相同的结果。
非常感谢您提供的任何帮助,提前致谢。
关于您在评论中提出的问题 不幸的是,我无法通过使用 procmon 找到错误。还有其他想法吗?
不,不是真的。你说的使用 psexec 作为系统执行命令是有效的,我认为它在 SCCM 下没有区别 运行。
- procmon 是否捕获了
bcdedit
的开始和停止? - 退出代码是什么?您可以 post 截图或分享 procmon 跟踪吗?
事实证明,此问题的解决方法是获取 bcdedit 的副本并将其放入包中,而不是简单地指定 "bcdedit".
我在整个脚本中将对文件 'bcdedit.exe' 的引用更新为“.\bcdedit.exe”,因为 SCCM 将目录更改为它复制到本地缓存的包文件夹的根目录。
我不确定为什么当 运行 作为用户登录到设备时可以指定 bcdedit,但是当 运行 作为系统登录时 SCCM 不能使用相同的语法,但是这修复成功。
感谢@LievenKeersmaekers 帮助我弄清楚系统找不到文件,当时他说 $cmdReturn 是空的,这让我开始思考为什么会这样,当他测试了它并返回了一个值,无论它是否出错。
编辑: 我还注意到 bcdboot.exe 发生了同样的事情,不得不将文件复制到包源文件并引用 .\bcdboot.exe