1

VHD をシステムに展開しようとするときに、SCCM を介してシステム アカウントとして powershell スクリプトを実行すると問題が発生します。

スクリプトは bcdedit /export を実行しようとしますが、自分で実行するか、psexec /i /s cmd を使用してから、自分のアカウントでシステムにログオンしているときにそれを介して powershell を実行すると、正常に動作します。

SCCM 経由でスクリプトを実行すると、スクリプトが停止し、ログオンしているかどうかに関係なく、以下に示すログ出力コメントにカスタム エラーがスローされます。ファイル。

スクリプトは次のとおりです。

#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 - Certiport 2013 VHD インストールの開始

10:28:29 - ソース ディレクトリは次のとおりです: C:\Windows\SysWOW64\CCM\Cache\00000379.2.System

10:28:29 - VHD は既に D:\Certiport に存在します - MD5 ハッシュを検証しています

10:36:41 - VHD ハッシュ チェック成功

10:36:44 - エラー: BCD をバックアップできませんでした - 起動順序を壊さないようにすぐに終了します -

10:36:44 - 重大なエラーが検出されました! - スクリプトは終了します

問題は次のスクリプト ブロックにあるようです。

$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

しかし、それもうまくいかず、同じ結果が得られました。

提供されたヘルプに本当に感謝します。事前に感謝します。

4

2 に答える 2

1

コメントでの質問については、残念ながら procmon を使用してエラーを見つけることができませんでした。他のアイデアはありますか?

いいえ、そうではありません。psexec を使用してシステムとしてコマンドを実行すると、あなたが言うように動作しますが、SCCM で実行しても違いはありません。

  • procmon は の開始と停止をキャプチャしましたbcdeditか?
  • 終了コードは何でしたか? スクリーンショットを投稿したり、procmon トレースを共有したりできますか?

procmon トレース

于 2016-08-26T06:00:03.990 に答える
1

この問題の修正は、単純に「bcdedit」を指定するのではなく、bcdedit のコピーを取得してパッケージ内に配置することであることがわかりました。

SCCMがディレクトリをローカルキャッシュにコピーするパッケージフォルダーのルートに変更するため、スクリプト全体でファイル「bcdedit.exe」への参照を「.\bcdedit.exe」に更新しました。

デバイスにログオンしたユーザーとして実行しているときに bcdedit を指定できる理由はわかりませんが、システムとして実行している SCCM では同じ構文を使用できませんが、この修正は機能しました。

$cmdReturn が空であると彼が言ったとき、システムでファイルが見つからないことを理解するのを手伝ってくれた @LievenKeersmaekers に感謝します。エラーかどうかに関係なく、値。

編集:bcdboot.exeでも同じことが起こったことに気付き、ファイルをパッケージソースファイルにコピーして.\bcdboot.exeを参照する必要がありました

于 2016-08-30T05:29:15.267 に答える