wintriallab: WORKING BUILDS

- It can auto trigger WinRM enabling
- Use stronger password for vagrant user so WinRM will allow it
- Reboot after installing VBox Additions before running WSUS
- Way more verbose Packer
- Slightly better error handling in postinstall scripts
- Use the Event Log for logging in the postinstall module
- Fix lots of bugs that cropped up without me realizing
- Install VBox Additions from either ISO or attached disk
- Install from ISO by default (smoother in Packer)
- Accept EULA for Windows Updates where relevant
jowjDev
Micah R Ledbetter 9 years ago
parent 09b2071a4b
commit 4192667eec

@ -49,6 +49,7 @@ $verbose = $true
$dateStamp = get-date -UFormat "%Y-%m-%d-%H-%M-%S" $dateStamp = get-date -UFormat "%Y-%m-%d-%H-%M-%S"
$packerOutDir = "$baseOutDir\PackerOut" $packerOutDir = "$baseOutDir\PackerOut"
$packerCacheDir = "$baseOutDir\packer_cache" $packerCacheDir = "$baseOutDir\packer_cache"
$packerLogFile = "$baseOutDir\packer.log"
$wsusOfflineDir = "$baseOutDir\wsusoffline" $wsusOfflineDir = "$baseOutDir\wsusoffline"
$labTempDir = "$baseOutDir\temp-$dateStamp" $labTempDir = "$baseOutDir\temp-$dateStamp"
@ -185,7 +186,11 @@ function Build-PackerFile {
pushd (get-item $packerFile | select -expand fullname | split-path -parent) pushd (get-item $packerFile | select -expand fullname | split-path -parent)
try { try {
write-host "Building packer file '$($packerFile.fullname)' to directory '$outDir'..." write-host "Building packer file '$($packerFile.fullname)' to directory '$outDir'..."
$packerCall = ''
if (-not $whatif) { if (-not $whatif) {
$env:PACKER_DEBUG = 1
$env:PACKER_LOG = 1
$env:PACKER_PATH = $packerLogFile
packer build -var "output_directory=$outDir" "$($packerFile.fullname)" packer build -var "output_directory=$outDir" "$($packerFile.fullname)"
if ($LASTEXITCODE -ne 0) { throw "External command failed with code $LASTEXITCODE" } if ($LASTEXITCODE -ne 0) { throw "External command failed with code $LASTEXITCODE" }
} }

@ -0,0 +1,15 @@
cmd.exe /c 'winrm quickconfig -q'
cmd.exe /c 'winrm quickconfig -transport:http'
cmd.exe /c 'winrm set winrm/config @{MaxTimeoutms="1800000"}'
cmd.exe /c 'winrm set winrm/config/winrs @{MaxMemoryPerShellMB="2048"}'
cmd.exe /c 'winrm set winrm/config/service @{AllowUnencrypted="true"}'
cmd.exe /c 'winrm set winrm/config/client @{AllowUnencrypted="true"}'
cmd.exe /c 'winrm set winrm/config/service/auth @{Basic="true"}'
cmd.exe /c 'winrm set winrm/config/client/auth @{Basic="true"}'
cmd.exe /c 'winrm set winrm/config/service/auth @{CredSSP="true"}'
cmd.exe /c 'winrm set winrm/config/listener?Address=*+Transport=HTTP @{Port="5985"} '
cmd.exe /c 'netsh advfirewall firewall set rule group="remote administration" new enable=yes'
cmd.exe /c 'netsh firewall add portopening TCP 5985 "Port 5985"'
cmd.exe /c 'net stop winrm'
cmd.exe /c 'sc.exe config winrm start= auto'
cmd.exe /c 'net start winrm'

@ -4,21 +4,26 @@ param()
import-module $PSScriptRoot\wintriallab-postinstall.psm1 import-module $PSScriptRoot\wintriallab-postinstall.psm1
try { try {
Write-EventLogWrapper "Starting the autounattend postinstall script"
Set-PasswordExpiry -accountName "vagrant" -expirePassword $false Set-PasswordExpiry -accountName "vagrant" -expirePassword $false
Disable-HibernationFile Disable-HibernationFile
Enable-MicrosoftUpdate Enable-MicrosoftUpdate
A:\win-updates.ps1 -RestartAction RunAtLogon -PostUpdateExpression A:\enable-winrm.bat Install-VBoxAdditions -fromDisc # Need to reboot for some of these drivers to take
# To reboot, then run Windows updates, then enable WinRM:
$restartCommand = "$PSHOME\powershell.exe -File A:\win-updates.ps1 -RestartAction RunAtLogon -PostUpdateExpression -CalledFromRegistry '$PSHOME\powershell.exe -File A:\enable-winrm.ps1'"
# To reboot, then run winrm immediately without Windows Update
#$restartCommand = "$PSHOME\powershell.exe -File A:\enable-winrm.ps1"
Set-RestartRegistryEntry -restartAction RunAtLogon -restartCommand $restartCommand
Restart-Computer -force
} }
catch { catch {
write-host "======== CAUGHT EXCEPTION ========" $message = "======== CAUGHT EXCEPTION ========`r`n$_`r`n"
write-host "$_" $message += "======== ERROR STACK ========"
write-host "======== CALL STACK ========" $error |% { $message += "$_`r`n----`r`n" }
Get-PSCallStack | format-list $message += "======== ========"
write-host "======== ERROR STACK ========" Write-EventLogWrapper $message
for ($i=0; $i -lt $error.count; $i+=1) {
write-host "`$error[$i]"
write-host $error[$i]
}
write-host "======== ========"
exit 666 exit 666
} }

@ -23,8 +23,6 @@ $LASTEXITCODE = 0 # just in case
import-module $PSScriptRoot\wintriallab-postinstall.psm1 import-module $PSScriptRoot\wintriallab-postinstall.psm1
try { try {
Install-SevenZip Install-SevenZip
#Install-VBoxAdditions -isoPath "C:\Users\vagrant\VBoxGuestAdditions.iso"
Install-VBoxAdditions -fromDisc
Disable-AutoAdminLogon Disable-AutoAdminLogon
Enable-RDP Enable-RDP
Install-Chocolatey Install-Chocolatey

@ -1,3 +1,9 @@
param(
[String] $ScriptProductName = "PostInstall-Marionettist",
[String] $ScriptPath = $MyInvocation.MyCommand.Path,
[String] $ScriptName = $MyInvocation.MyCommand.Name
)
<# <#
jesus fucking christ jesus fucking christ
fucking Packer fucking Packer
@ -5,9 +11,9 @@ TODO:
- make every function 100% reliant on itself only. - make every function 100% reliant on itself only.
- get rid of calls to Get-LabTempDir - get rid of calls to Get-LabTempDir
- decided whether I'm using $URLs or not lol - decided whether I'm using $URLs or not lol
- get better logging - use an Event Log
#> #>
### Global Constants that I use elsewhere ### Global Constants that I use elsewhere
$ArchitectureId = @{ $ArchitectureId = @{
@ -65,7 +71,13 @@ $URLs = @{
} }
SdeleteDownload = "http://download.sysinternals.com/files/SDelete.zip" SdeleteDownload = "http://download.sysinternals.com/files/SDelete.zip"
} }
$script:ScriptPath = $MyInvocation.MyCommand.Path
$script:RestartRegistryKeys = @{
RunBeforeLogon = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunServicesOnce"
RunAtLogon = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce"
}
$script:RestartRegistryProperty = "$ScriptProductName"
### Private support functions I use behind the scenes ### Private support functions I use behind the scenes
function Get-WebUrl { function Get-WebUrl {
@ -89,9 +101,9 @@ function Invoke-ExpressionAndCheck {
[int] $sleepSeconds [int] $sleepSeconds
) )
$global:LASTEXITCODE = 0 $global:LASTEXITCODE = 0
write-verbose "Invoking expression '$command'" Write-EventLogWrapper "Invoking expression '$command'"
invoke-expression -command $command invoke-expression -command $command
write-verbose "Expression '$command' had a last exit code of '$LastExitCode'" Write-EventLogWrapper "Expression '$command' had a last exit code of '$LastExitCode'"
if ($global:LASTEXITCODE -ne 0) { if ($global:LASTEXITCODE -ne 0) {
throw "LASTEXITCODE: ${global:LASTEXITCODE} for command: '${command}'" throw "LASTEXITCODE: ${global:LASTEXITCODE} for command: '${command}'"
} }
@ -108,7 +120,7 @@ function Invoke-ExpressionAndCheck {
# ) # )
# $path = resolve-path $path | select -expand path # $path = resolve-path $path | select -expand path
# $sourceItems = Get-ChildItem -Path $path -Recurse -Exclude $exclude # $sourceItems = Get-ChildItem -Path $path -Recurse -Exclude $exclude
# write-verbose "Found $($sourceItems.count) items to copy from '$path'" # Write-EventLogWrapper "Found $($sourceItems.count) items to copy from '$path'"
# #$sourceItems | copy-item -force:$force -destination {Join-Path $destination $_.FullName.Substring($path.length)} # #$sourceItems | copy-item -force:$force -destination {Join-Path $destination $_.FullName.Substring($path.length)}
# $sourceItems | copy-item -force:$force -destination { # $sourceItems | copy-item -force:$force -destination {
# if ($_.GetType() -eq [System.IO.FileInfo]) { # if ($_.GetType() -eq [System.IO.FileInfo]) {
@ -161,7 +173,7 @@ function Get-AdkPath {
$possibleAdkPaths = @("${env:ProgramFiles(x86)}\Windows Kits\8.1","${env:ProgramFiles}\Windows Kits\8.1") $possibleAdkPaths = @("${env:ProgramFiles(x86)}\Windows Kits\8.1","${env:ProgramFiles}\Windows Kits\8.1")
$possibleAdkPaths |% { if (test-path $_) { $adkPath = $_ } } $possibleAdkPaths |% { if (test-path $_) { $adkPath = $_ } }
if (-not $adkPath) { throw "Could not find the Windows Automated Installation Kit" } if (-not $adkPath) { throw "Could not find the Windows Automated Installation Kit" }
write-verbose "Found the WAIK at '$adkPath'" Write-EventLogWrapper "Found the WAIK at '$adkPath'"
$arch = Get-OSArchitecture $arch = Get-OSArchitecture
switch ($arch) { switch ($arch) {
@ -205,7 +217,7 @@ function Get-WOShortCode { # TODO fixme I think I don't need this anymore becaus
elseif ($OSArchitecture -match $ArchitectureId.amd64) { $shortCode += "-x64" } elseif ($OSArchitecture -match $ArchitectureId.amd64) { $shortCode += "-x64" }
else { throw "Could not determine shortcode for an OS of architecture '$OSArchitecture'" } else { throw "Could not determine shortcode for an OS of architecture '$OSArchitecture'" }
write-verbose "Found shortcode '$shortcode' for OS named '$OSName' of architecture '$OSArchitecture'" Write-EventLogWrapper "Found shortcode '$shortcode' for OS named '$OSName' of architecture '$OSArchitecture'"
return $shortCode return $shortCode
} }
@ -230,14 +242,70 @@ function Get-LabTempDir {
$script:WinTrialLabTemp = "${env:Temp}\WinTrialLab-$dateStamp" $script:WinTrialLabTemp = "${env:Temp}\WinTrialLab-$dateStamp"
} }
$script:WinTrialLabTemp = [System.IO.Path]::GetFullPath($script:WinTrialLabTemp) $script:WinTrialLabTemp = [System.IO.Path]::GetFullPath($script:WinTrialLabTemp)
write-verbose "Using WinTrialLabTemp directory at '${script:WinTrialLabTemp}'" Write-EventLogWrapper "Using WinTrialLabTemp directory at '${script:WinTrialLabTemp}'"
if (-not (test-path $script:WinTrialLabTemp)) { if (-not (test-path $script:WinTrialLabTemp)) {
write-verbose "Temporary directory does not exist, creating it..." Write-EventLogWrapper "Temporary directory does not exist, creating it..."
mkdir -force $script:WinTrialLabTemp | out-null mkdir -force $script:WinTrialLabTemp | out-null
} }
return $script:WinTrialLabTemp return $script:WinTrialLabTemp
} }
<#
.synopsis
Wrapper that writes to the event log but also to the screen
#>
function Write-EventLogWrapper {
[cmdletbinding()] param(
[parameter(mandatory=$true)] [String] $message,
[int] $eventId = 0,
[ValidateSet("Error",'Warning','Information','SuccessAudit','FailureAudit')] $entryType = "Information",
[String] $EventLogName = $ScriptProductName,
[String] $EventLogSource = $ScriptName
)
if (-not (get-eventlog -logname * |? { $_.Log -eq $eventLogName })) {
New-EventLog -Source $EventLogSource -LogName $eventLogName
}
$messagePlus = "$message`r`n`r`nScript: $($script:ScriptPath)`r`nUser: ${env:USERDOMAIN}\${env:USERNAME}"
Write-Host -foreground magenta "====Writing to $EvengLogName event log===="
write-host -foreground darkgray "$messagePlus`r`n"
Write-EventLog -LogName $eventLogName -Source $EventLogSource -EventID $eventId -EntryType $entryType -Message $MessagePlus
}
<#
.synopsis
Create and set the registry property which will run this script on reboot
#>
function Set-RestartRegistryEntry {
param(
[parameter(mandatory=$true)] [ValidateSet('RunBeforeLogon','RunAtLogon','NoRestart')] [string] $RestartAction,
[string] $restartCommand
)
if ($RestartAction -match "NoRestart") {
Write-EventLogWrapper "Called Set-RestartRegistryEntry with -RestartAction NoRestart, will not write registry key"
return
}
$message = "Setting the Restart Registry Key at: {0}\{1}`r`n{2}" -f $script:RestartRegistryKeys.$RestartAction, $script:RestartRegistryProperty, $restartCommand
Write-EventLogWrapper -message $message
New-Item $script:RestartRegistryKeys.$RestartAction -force | out-null
Set-ItemProperty -Path $script:RestartRegistryKeys.$RestartAction -Name $script:RestartRegistryProperty -Value $restartCommand
}
function Get-RestartRegistryEntries {
[cmdletbinding()] param()
foreach ($key in $script:RestartRegistryKeys.Keys) {
try { Get-ItemProperty -Path $script:RestartRegistryKeys[$key] -name $script:RestartRegistryProperty} catch {}
}
}
function Remove-RestartRegistryEntries {
[cmdletbinding()] param()
foreach ($key in $script:RestartRegistryKeys.Keys) {
try { Remove-ItemProperty -Path $script:RestartRegistryKeys[$key] -name $script:RestartRegistryProperty} catch {}
}
}
<# <#
.description .description
Return the OS Architecture of the current system, as determined by WMI Return the OS Architecture of the current system, as determined by WMI
@ -270,8 +338,8 @@ function Install-SevenZip {
$OSArch = Get-OSArchitecture $OSArch = Get-OSArchitecture
$szDlPath = Get-WebUrl -url $URLs.SevenZipDownload.$OSArch -outDir $env:temp $szDlPath = Get-WebUrl -url $URLs.SevenZipDownload.$OSArch -outDir $env:temp
try { try {
write-verbose "Downloaded '$szUrl' to '$szDlPath', now running msiexec..." Write-EventLogWrapper "Downloaded '$szUrl' to '$szDlPath', now running msiexec..."
$msiCall = 'msiexec /qn /i "{0}"' -f $szDlPath $msiCall = '& msiexec /qn /i "{0}"' -f $szDlPath
# Windows suxxx so msiexec sometimes returns right away? or something idk. fuck # Windows suxxx so msiexec sometimes returns right away? or something idk. fuck
Invoke-ExpressionAndCheck -command $msiCall -sleepSeconds 30 Invoke-ExpressionAndCheck -command $msiCall -sleepSeconds 30
} }
@ -282,34 +350,55 @@ function Install-SevenZip {
set-alias sevenzip "${env:ProgramFiles}\7-Zip\7z.exe" set-alias sevenzip "${env:ProgramFiles}\7-Zip\7z.exe"
function Install-VBoxAdditions { function Install-VBoxAdditions {
[cmdletbinding()] [cmdletbinding(DefaultParameterSetName="InstallFromDisc")] param(
param( [parameter(ParameterSetName="InstallFromIsoPath",mandatory=$true)] [string] $isoPath,
[parameter(mandatory=$true)] [string] $isoPath [parameter(ParameterSetName="InstallFromDisc",mandatory=$true)] [switch] $fromDisc
) )
$isoPath = resolve-path $isoPath | select -expand Path
$vbgaPath = "${env:Temp}\InstallVbox"
try {
mkdir -force $vbgaPath
write-verbose "Extracting iso at '$isoPath' to directory at '$vbgaPath'..."
Invoke-ExpressionAndCheck -command ('sevenzip x "{0}" -o"{1}"' -f $isoPath, $vbgaPath)
write-verbose "Installing the Oracle certificate..." function InstallVBoxAdditionsFromDir {
$oracleCert = resolve-path "$vbgaPath\cert\oracle-vbox.cer" | select -expand path param([Parameter(Mandatory=$true)][String]$baseDir)
$baseDir = resolve-path $baseDir | select -expand Path
Write-EventLogWrapper "Installing VBox Additions from '$baseDir'"
Write-EventLogWrapper "Installing the Oracle certificate..."
$oracleCert = resolve-path "$baseDir\cert\oracle-vbox.cer" | select -expand path
# NOTE: Checking for exit code, but this command will fail with an error if the cert is already installed # NOTE: Checking for exit code, but this command will fail with an error if the cert is already installed
Invoke-ExpressionAndCheck -command ('"{0}" add-trusted-publisher "{1}" --root "{1}"' -f "$vbgaPath\cert\VBoxCertUtil.exe",$oracleCert) Invoke-ExpressionAndCheck -command ('& "{0}" add-trusted-publisher "{1}" --root "{1}"' -f "$baseDir\cert\VBoxCertUtil.exe",$oracleCert)
Write-EventLogWrapper "Installing the virtualbox additions"
write-verbose "Installing the virtualbox additions" Invoke-ExpressionAndCheck -command ('& "{0}" /with_wddm /S' -f "$baseDir\VBoxWindowsAdditions.exe") # returns IMMEDIATELY, goddamn fuckers
Invoke-ExpressionAndCheck -command ('"{0}" /with_wddm /S' -f "$vbgaPath\VBoxWindowsAdditions.exe") # returns IMMEDIATELY, goddamn fuckers while (get-process -Name VBoxWindowsAdditions*) { write-host 'Waiting for VBox install to finish...'; sleep 1; }
while (get-process -Name VBoxWindowsAdditions*) { write-verbose 'Waiting for VBox install to finish...'; sleep 1; } Write-EventLogWrapper "virtualbox additions have now been installed"
} }
finally {
rm -recurse -force $vbgaPath switch ($PSCmdlet.ParameterSetName) {
"InstallFromIsoPath" {
$isoPath = resolve-path $isoPath | select -expand Path
$vbgaPath = mkdir -force "${env:Temp}\InstallVbox" | select -expand fullname
try {
Write-EventLogWrapper "Extracting iso at '$isoPath' to directory at '$vbgaPath'..."
Invoke-ExpressionAndCheck -command ('sevenzip x "{0}" -o"{1}"' -f $isoPath, $vbgaPath)
InstallVBoxAdditionsFromDir $vbgaPath
}
finally {
rm -recurse -force $vbgaPath
}
}
"InstallFromDisc" {
$vboxDiskDrive = get-psdrive -PSProvider Filesystem |? { test-path "$($_.Root)\VBoxWindowsAdditions.exe" }
if ($vboxDiskDrive) {
Write-EventLogWrapper "Found VBox Windows Additions disc at $vboxDiskDrive"
InstallVBoxAdditionsFromDir $vboxDiskDrive.Root
}
else {
$message = "Could not find VBox Windows Additions disc"
Write-EventLogWrapper $message
throw $message
}
}
} }
} }
function Disable-AutoAdminLogon { function Disable-AutoAdminLogon {
write-verbose "Function: $($MyInvocation.MyCommand)..." Write-EventLogWrapper "Disabling auto admin logon"
set-itemproperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name AutoAdminLogon -Value 0 set-itemproperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name AutoAdminLogon -Value 0
} }
@ -323,13 +412,13 @@ function Install-CompiledDotNetAssemblies {
# http://robrelyea.wordpress.com/2007/07/13/may-be-helpful-ngen-exe-executequeueditems/ # http://robrelyea.wordpress.com/2007/07/13/may-be-helpful-ngen-exe-executequeueditems/
$ngen = "${env:WinDir}\microsoft.net\framework\v4.0.30319\ngen.exe" $ngen = "${env:WinDir}\microsoft.net\framework\v4.0.30319\ngen.exe"
Invoke-ExpressionAndCheck -command "$ngen update /force /queue" Invoke-ExpressionAndCheck -command "& $ngen update /force /queue"
Invoke-ExpressionAndCheck -command "$ngen executequeueditems" Invoke-ExpressionAndCheck -command "& $ngen executequeueditems"
if ((Get-OSArchitecture) -match $ArchitectureId.amd64) { if ((Get-OSArchitecture) -match $ArchitectureId.amd64) {
$ngen64 = "${env:WinDir}\microsoft.net\framework64\v4.0.30319\ngen.exe" $ngen64 = "${env:WinDir}\microsoft.net\framework64\v4.0.30319\ngen.exe"
Invoke-ExpressionAndCheck -command "$ngen64 update /force /queue" Invoke-ExpressionAndCheck -command "& $ngen64 update /force /queue"
Invoke-ExpressionAndCheck -command "$ngen64 executequeueditems" Invoke-ExpressionAndCheck -command "& $ngen64 executequeueditems"
} }
} }
@ -337,7 +426,7 @@ function Compress-WindowsInstall {
$OSArch = Get-OSArchitecture $OSArch = Get-OSArchitecture
try { try {
$udfZipPath = Get-WebUrl -url $URLs.UltraDefragDownload.$OSArch -outDir $env:temp $udfZipPath = Get-WebUrl -url $URLs.UltraDefragDownload.$OSArch -outDir $env:temp
$udfExPath = "${env:temp}\ultradefrag-portable-6.1.0.$udfArch" $udfExPath = "${env:temp}\ultradefrag-portable-6.1.0.$OSArch"
# This archive contains a folder - extract it directly to the temp dir # This archive contains a folder - extract it directly to the temp dir
Invoke-ExpressionAndCheck -command ('sevenzip x "{0}" "-o{1}"' -f $udfZipPath,$env:temp) Invoke-ExpressionAndCheck -command ('sevenzip x "{0}" "-o{1}"' -f $udfZipPath,$env:temp)
@ -350,10 +439,12 @@ function Compress-WindowsInstall {
rm -recurse -force ${env:WinDir}\SoftwareDistribution\Download rm -recurse -force ${env:WinDir}\SoftwareDistribution\Download
start-service wuauserv start-service wuauserv
Invoke-ExpressionAndCheck -command ('{0} --optimize --repeat "{1}"' -f "$udfExPath\udefrag.exe","$env:SystemDrive") Invoke-ExpressionAndCheck -command ('& {0} --optimize --repeat "{1}"' -f "$udfExPath\udefrag.exe","$env:SystemDrive")
Set-ItemProperty -path HKCU\Software\Sysinternals\SDelete -name EulaAccepted -value 1 $sdKey = "HKCU:\Software\Sysinternals\SDelete"
Invoke-ExpressionAndCheck -command ('{0} -q -z "{1}"' -f $sdExPath,$env:SystemDrive) if (-not (test-path $sdKey)) { New-Item $sdKey -Force }
Set-ItemProperty -path $sdKey -name EulaAccepted -value 1
Invoke-ExpressionAndCheck -command ('& {0} -q -z "{1}"' -f "$sdExPath\SDelete.exe",$env:SystemDrive)
} }
finally { finally {
rm -recurse -force $udfZipPath,$udfExPath,$sdZipPath,$sdExPath -ErrorAction Continue rm -recurse -force $udfZipPath,$udfExPath,$sdZipPath,$sdExPath -ErrorAction Continue
@ -375,24 +466,24 @@ function Disable-WindowsUpdates {
function Enable-MicrosoftUpdate { function Enable-MicrosoftUpdate {
[cmdletbinding()] param() [cmdletbinding()] param()
write-verbose "Enabling Microsoft Update..." Write-EventLogWrapper "Enabling Microsoft Update..."
stop-service wuauserv stop-service wuauserv
$auKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update" $auKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update"
Set-ItemProperty -path $auKey -name EnableFeaturedSoftware -value 1 Set-ItemProperty -path $auKey -name EnableFeaturedSoftware -value 1
Set-ItemProperty -path $auKey -name IncludeRecommendedUpdates -value 1 Set-ItemProperty -path $auKey -name IncludeRecommendedUpdates -value 1
$ServiceManager = New-Object -ComObject "Microsoft.Update.ServiceManager" $ServiceManager = New-Object -ComObject "Microsoft.Update.ServiceManager"
$ServiceManager.AddService2("7971f918-a847-4430-9279-4a52d1efe18d",7,"") $ServiceManager.AddService2("7971f918-a847-4430-9279-4a52d1efe18d",7,"") | out-null
start-service wuauserv start-service wuauserv
} }
function Install-Chocolatey { function Install-Chocolatey {
[cmdleetbinding()] param() [cmdletbinding()] param()
$chocoExePath = "${env:ProgramData}\Chocolatey\bin" $chocoExePath = "${env:ProgramData}\Chocolatey\bin"
if ($($env:Path).ToLower().Contains($($chocoExePath).ToLower())) { if ($($env:Path).ToLower().Contains($($chocoExePath).ToLower())) {
write-verbose "Chocolatey already in path, exiting..." Write-EventLogWrapper "Attempting to install Chocolatey but it's already in path, exiting..."
return return
} }
@ -404,7 +495,9 @@ function Install-Chocolatey {
$userPath = [Environment]::GetEnvironmentVariable('Path', [System.EnvironmentVariableTarget]::User) $userPath = [Environment]::GetEnvironmentVariable('Path', [System.EnvironmentVariableTarget]::User)
if ($userPath) { $env:Path += ";$userPath" } if ($userPath) { $env:Path += ";$userPath" }
iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1')) # TODO: capture and log output
$chocoOutput = iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))
Write-EventLogWrapper "Chocolatey install process completed:`r`n`r`n$chocoOutput"
} }
function Set-UserOptions { function Set-UserOptions {
@ -431,7 +524,7 @@ function Set-UserOptions {
function Disable-HibernationFile { function Disable-HibernationFile {
[cmdletbinding()] param() [cmdletbinding()] param()
write-verbose "Removing Hibernation file..." Write-EventLogWrapper "Removing Hibernation file..."
$powerKey = 'HKLM:\SYSTEM\CurrentControlSet\Control\Power' $powerKey = 'HKLM:\SYSTEM\CurrentControlSet\Control\Power'
Set-ItemProperty -path $powerKey -name HibernateFileSizePercent -value 0 # hiberfil is zero bytes Set-ItemProperty -path $powerKey -name HibernateFileSizePercent -value 0 # hiberfil is zero bytes
Set-ItemProperty -path $powerKey -name HibernateEnabled -value 0 # disable hibernation altogether Set-ItemProperty -path $powerKey -name HibernateEnabled -value 0 # disable hibernation altogether
@ -439,7 +532,7 @@ function Disable-HibernationFile {
function Enable-WinRM { # TODO fixme, would prefer this to be in Powershell if possible. also it's totally insecure function Enable-WinRM { # TODO fixme, would prefer this to be in Powershell if possible. also it's totally insecure
[cmdletbinding()] param() [cmdletbinding()] param()
write-verbose "Enabling WinRM..." Write-EventLogWrapper "Enabling WinRM..."
cmd /c winrm quickconfig -q cmd /c winrm quickconfig -q
cmd /c winrm quickconfig -transport:http cmd /c winrm quickconfig -transport:http
@ -542,7 +635,7 @@ function New-WindowsInstallMedia { # TODO fixme not sure I wanna handle temp dir
$etfsBoot = resolve-path "$existingInstallMediaDir\boot\etfsboot.com" | select -expand Path $etfsBoot = resolve-path "$existingInstallMediaDir\boot\etfsboot.com" | select -expand Path
$oscdimgCall = '& "{0}" -m -n -b"{1}" "{2}" "{3}"' -f @($oscdImgPath, $etfsBoot, $installMediaTemp, $outputIsoPath) $oscdimgCall = '& "{0}" -m -n -b"{1}" "{2}" "{3}"' -f @($oscdImgPath, $etfsBoot, $installMediaTemp, $outputIsoPath)
write-verbose "Calling OSCDIMG: '$oscdimgCall" Write-EventLogWrapper "Calling OSCDIMG: '$oscdimgCall"
Invoke-ExpressionAndCheck $oscdimgCall -verbose:$verbose Invoke-ExpressionAndCheck $oscdimgCall -verbose:$verbose
dismount-diskimage -imagepath $sourceIsoPath dismount-diskimage -imagepath $sourceIsoPath
@ -557,7 +650,7 @@ function Get-WindowsUpdateUrls { # TODO: is this how we wanna do temps tho?
[switch] $debugSaveXslt [switch] $debugSaveXslt
) )
$xsltPath = [IO.Path]::GetTempFileName() $xsltPath = [IO.Path]::GetTempFileName()
write-verbose "Downloading XSLT to '$xsltPath'" Write-EventLogWrapper "Downloading XSLT to '$xsltPath'"
if ($osArchitecture -match $ArchitectureId.i386) { $arch = "x86" } if ($osArchitecture -match $ArchitectureId.i386) { $arch = "x86" }
elseif ($osArchitecture -match $ArchitectureId.amd64) { $arch = "x64" } elseif ($osArchitecture -match $ArchitectureId.amd64) { $arch = "x64" }

@ -16,7 +16,7 @@ This script is intended to be 100% standalone because it needs to be able to tel
#> #>
param( param(
[int] $MaxCycles = 5, [int] $MaxCycles = 5,
[string] $ScriptProductName = "MarionettistWindowsUpdate", [string] $ScriptProductName = "WinUp-Marionettist",
[string] $PostUpdateExpression, [string] $PostUpdateExpression,
[string] [ValidateSet('RunBeforeLogon','RunAtLogon','NoRestart')] $RestartAction = "NoRestart", [string] [ValidateSet('RunBeforeLogon','RunAtLogon','NoRestart')] $RestartAction = "NoRestart",
[switch] $CalledFromRegistry [switch] $CalledFromRegistry
@ -190,7 +190,7 @@ function Install-WindowsUpdates {
[int] $MaxDownloadAttempts = 12 [int] $MaxDownloadAttempts = 12
) )
if ((-not $UpdateList) -or ($UpdateList.Count -lt 1)) { if (-not $UpdateList) {
Write-WinUpEventLog -message "No Updates To Download..." Write-WinUpEventLog -message "No Updates To Download..."
return $false return $false
} }
@ -202,6 +202,17 @@ function Install-WindowsUpdates {
$UpdateComObject = New-Object -ComObject 'Microsoft.Update.UpdateColl' $UpdateComObject = New-Object -ComObject 'Microsoft.Update.UpdateColl'
$UpdateList |% { $UpdateComObject.Add($_) } $UpdateList |% { $UpdateComObject.Add($_) }
$AcceptedEulas = @()
foreach ($update in $UpdateComObject) {
if ($update.PSObject.Properties['EulaAccepted']) {
$AcceptedEulas += ($update)
$update.AcceptEula()
}
}
$message = "There were $($AcceptedEulas.count) updates with a EULA which was automatically accepted`r`n"
$AcceptedEulas |% { $message += "`r`n - $($_.Title)"}
Write-WinUpEventLog $message
Write-WinUpEventLog -message 'Downloading Updates...' Write-WinUpEventLog -message 'Downloading Updates...'
$ok = $false $ok = $false
$attempts = 0 $attempts = 0
@ -344,7 +355,7 @@ function Run-WindowsUpdate {
Write-WinUpEventLog -message "Starting to check for updates. $maxCycles cycles remain." Write-WinUpEventLog -message "Starting to check for updates. $maxCycles cycles remain."
$CheckedUpdates = Check-WindowsUpdates -FilterInteractiveUpdates -UpdateSession $UpdateSession $CheckedUpdates = Check-WindowsUpdates -FilterInteractiveUpdates -UpdateSession $UpdateSession
if ((-not $CheckedUpdates) -or ($CheckedUpdates.Count -lt 1)) { if (-not $CheckedUpdates) {
Write-WinUpEventLog "No applicable updates were detected. Done!" Write-WinUpEventLog "No applicable updates were detected. Done!"
break break
} }
@ -355,7 +366,6 @@ function Run-WindowsUpdate {
Restart-ComputerAndUpdater -CyclesRemaining $maxCycles -RestartAction $script:RestartAction Restart-ComputerAndUpdater -CyclesRemaining $maxCycles -RestartAction $script:RestartAction
} }
} }
Run-PostUpdate Run-PostUpdate
} }

@ -19,7 +19,7 @@
<Letter>C</Letter> <Letter>C</Letter>
<Order>1</Order> <Order>1</Order>
<PartitionID>1</PartitionID> <PartitionID>1</PartitionID>
<Label>Windows 81</Label> <Label>SystemDrive</Label>
</ModifyPartition> </ModifyPartition>
</ModifyPartitions> </ModifyPartitions>
<DiskID>0</DiskID> <DiskID>0</DiskID>
@ -29,8 +29,11 @@
</DiskConfiguration> </DiskConfiguration>
<UserData> <UserData>
<AcceptEula>true</AcceptEula> <AcceptEula>true</AcceptEula>
<!-- do I need this for anything at all?
<FullName>Vagrant Administrator</FullName> <FullName>Vagrant Administrator</FullName>
<Organization>Vagrant Inc.</Organization> <Organization>Vagrant Inc.</Organization>
-->
<!-- <!--
NOTE: If you are re-configuring this for use of a retail key NOTE: If you are re-configuring this for use of a retail key
@ -46,7 +49,8 @@
--> -->
<!-- Product Key from http://technet.microsoft.com/en-us/library/jj612867.aspx --> <!-- Product Key from http://technet.microsoft.com/en-us/library/jj612867.aspx -->
<ProductKey>MHF9N-XY6XB-WVXMC-BTDCT-MKKG7 <ProductKey>
MHF9N-XY6XB-WVXMC-BTDCT-MKKG7
<WillShowUI>Never</WillShowUI> <WillShowUI>Never</WillShowUI>
</ProductKey> </ProductKey>
</UserData> </UserData>
@ -87,13 +91,13 @@
<component xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Microsoft-Windows-Shell-Setup" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> <component xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Microsoft-Windows-Shell-Setup" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<UserAccounts> <UserAccounts>
<AdministratorPassword> <AdministratorPassword>
<Value>vagrant</Value> <Value>V@grant123</Value>
<PlainText>true</PlainText> <PlainText>true</PlainText>
</AdministratorPassword> </AdministratorPassword>
<LocalAccounts> <LocalAccounts>
<LocalAccount wcm:action="add"> <LocalAccount wcm:action="add">
<Password> <Password>
<Value>vagrant</Value> <Value>V@grant123</Value>
<PlainText>true</PlainText> <PlainText>true</PlainText>
</Password> </Password>
<Description>Vagrant User</Description> <Description>Vagrant User</Description>
@ -111,7 +115,7 @@
</OOBE> </OOBE>
<AutoLogon> <AutoLogon>
<Password> <Password>
<Value>vagrant</Value> <Value>V@grant123</Value>
<PlainText>true</PlainText> <PlainText>true</PlainText>
</Password> </Password>
<Username>vagrant</Username> <Username>vagrant</Username>
@ -120,17 +124,17 @@
<FirstLogonCommands> <FirstLogonCommands>
<SynchronousCommand wcm:action="add"> <SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c powershell -Command "Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Force"</CommandLine> <CommandLine>cmd.exe /c powershell -ExecutionPolicy Unrestricted -Command "Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Force"</CommandLine>
<Description>Set Execution Policy</Description> <Description>Set Execution Policy</Description>
<Order>1</Order> <Order>1</Order>
<RequiresUserInput>true</RequiresUserInput> <RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand> </SynchronousCommand>
<SynchronousCommand wcm:action="add"> <SynchronousCommand wcm:action="add">
<!-- This task must include enabling winrm --> <!-- This task MUST include enabling winrm for Packer to be able to continue -->
<CommandLine>powershell.exe -File A:\autounattend-postinstall.ps1</CommandLine> <CommandLine>powershell.exe -File A:\autounattend-postinstall.ps1</CommandLine>
<Description>Run Postinstall Script</Description> <Description>Run Postinstall Script</Description>
<Order>49</Order> <Order>99</Order>
<RequiresUserInput>true</RequiresUserInput> <RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand> </SynchronousCommand>

@ -10,7 +10,7 @@ Vagrant.configure("2") do |config|
# Admin user name and password # Admin user name and password
config.winrm.username = "vagrant" config.winrm.username = "vagrant"
config.winrm.password = "vagrant" config.winrm.password = "V@grant123"
config.vm.guest = :windows config.vm.guest = :windows
config.windows.halt_timeout = 15 config.windows.halt_timeout = 15

@ -12,7 +12,7 @@
"boot_wait": "2m", "boot_wait": "2m",
"communicator": "winrm", "communicator": "winrm",
"winrm_username": "vagrant", "winrm_username": "vagrant",
"winrm_password": "vagrant", "winrm_password": "V@grant123",
"winrm_timeout": "8h", "winrm_timeout": "8h",
"shutdown_command": "shutdown /s /t 10 /f /d p:4:1 /c \"Packer Shutdown\"", "shutdown_command": "shutdown /s /t 10 /f /d p:4:1 /c \"Packer Shutdown\"",
"guest_os_type": "Windows81", "guest_os_type": "Windows81",
@ -21,7 +21,7 @@
"floppy_files": [ "floppy_files": [
"./Autounattend.xml", "./Autounattend.xml",
"../scripts/win-updates.ps1", "../scripts/win-updates.ps1",
"../scripts/enable-winrm.bat", "../scripts/enable-winrm.ps1",
"../scripts/postinstall/wintriallab-postinstall.psm1", "../scripts/postinstall/wintriallab-postinstall.psm1",
"../scripts/postinstall/autounattend-postinstall.ps1", "../scripts/postinstall/autounattend-postinstall.ps1",
"../scripts/postinstall/provisioner-postinstall.ps1" "../scripts/postinstall/provisioner-postinstall.ps1"

Loading…
Cancel
Save