BCD编辑导出失败,当用户没有登录并通过SCCM作为系统帐户在Powershell

问题描述:

我试图将VHD部署到系统时通过SCCM作为系统帐户运行PowerShell脚本的问题。BCD编辑导出失败,当用户没有登录并通过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("$PkgLocation\7za.exe x `"$PkgLocation\$PackageName.7z`" -oD:\ -r -aoa") 
    Log("Running - `'$ScriptBlock`'") 
    $7ZipExtract = 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("$PkgLocation\7za.exe x `"$PkgLocation\$PackageName.7z`" -oD:\ -r -aoa") 
     Log("Running - `'$ScriptBlock`'") 
     $7ZipExtract = 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\00000379.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 

但是这并不能工作,并给出了相同的结果。

我真的很感谢您提供的任何帮助,谢谢您提前。

+0

您存储'调用,Command'的结果。到'$ BCDConfig'中,但是在下一行中,您将'$ cmdReturn'与'操作成功完成'进行比较。 –

+0

和fwiw,我会尝试运行procmon而你的脚本执行时得到真正的错误*(如果有的话)*当执行'bcdedit' –

+0

@LievenKeersmaekers感谢您的评论,我很抱歉,这是我的代表错误将该部分脚本转换回原始版本。感谢您指出它,我已经编辑它应该看起来像。你现在看到的是SCCM运行的内容。我再次道歉。 – Random206

原来,针对此问题的修复是采取BCDEDIT的副本,并把它放在包内,而不是简单地指定“BCDEDIT”。

由于SCCM将目录更改为其复制到本地缓存的包文件夹的根目录,因此我通过脚本更新了对文件'bcdedit.exe'的引用,直至完成“。\ bcdedit.exe”。

我不确定为什么你可以指定bcdedit作为登录到设备上的用户运行,但SCCM作为系统运行时不能使用相同的语法,但此修补程序可以正常工作。

感谢@LievenKeersmaekers的帮助,他帮助我找出系统找不到该文件,当他说$ cmdReturn为空时,这让我想到了为什么会这样,当他测试时它会返回一个值,无论是否存在错误。

编辑: 我也注意到了同样的事情发生了bcdboot.exe,不得不将文件复制到包的源文件和参考\ bcdboot.exe

至于你在评论中的问题不幸的是,我无法使用procmon找到错误。任何其他想法?

不,不是真的。使用psexec作为系统执行该命令的工作原理如下,我发现在SCCM下运行它并没有什么区别。

  • 没有procmon捕获bcdedit的开始和停止?
  • 什么是退出代码?你能发布截图或分享procmon跟踪吗?

procmon trace

+0

感谢您的帮助,只是在脚本中指定'bcdedit'时,出于某种原因以SCCM身份运行时无法找到bcdedit。我必须采用'bcdedit.exe'的副本,并使用'。\ bcdedit.exe'在缓存中的包文件夹内引用它。 – Random206