Reliably start WinRM, support Win10x86, fix other

Big features:

- Reliably start WinRM now!!
- Support Windows 10 x86!

Smaller features

- Improve README
- Don't power off the (virtual) monitor to save power
- Add Invoke-ScriptblockAndCatch and use it in postinstall scripts

Fixes and reorgs:

- Move all the packer stuff inside the packer/ directory
- Break out possibly-nonfunctional slipstream stuff to its own module
- Clean up broken bits in buildlab
- Clean up vestigial bits in wintriallab-postinstall
- Fix lots of broken pieces after encountering them one by one
- Fix RestartAction stuff in autounattend-postinstall
jowjDev
Micah R Ledbetter 9 years ago
parent 736a42c514
commit 8a999fdd58

@ -16,14 +16,10 @@ param(
[parameter(mandatory=$true,ParameterSetName="VagrantUp")]
[string] $baseConfigName,
[parameter(mandatory=$true,ParameterSetName="DownloadWSUS")] [switch] $DownloadWSUS,
[parameter(mandatory=$true,ParameterSetName="ApplyWSUS")] [switch] $ApplyWSUS,
[parameter(mandatory=$true,ParameterSetName="BuildPacker")] [switch] $BuildPacker,
[parameter(mandatory=$true,ParameterSetName="AddToVagrant")] [switch] $AddToVagrant,
[parameter(mandatory=$true,ParameterSetName="VagrantUp")] [switch] $VagrantUp,
[parameter(mandatory=$true,ParameterSetName="ApplyWSUS")] [string] $isoPath,
[string] $baseOutDir = "D:\iso\wintriallab",
[string] $tempDirOverride,
[string] $tag,
@ -31,21 +27,9 @@ param(
[switch] $whatIf
)
import-module dism -verbose:$false
# Module useful for Download-URL at least. TODO: this mixes concerns and may not be ideal?
get-module wintriallab-postinstall | remove-module
import-module $PSScriptRoot\scripts\wintriallab-postinstall.psm1 -verbose:$false
$errorActionPreference = "Stop"
Set-StrictMode -Version 2.0
# This seems to be required with strict mode?
$verbose = $true
# This correctly covers -verbose -verbose:$false and -verbose:$true
# if ($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) {
# $verbose = $true
# }
$dateStamp = get-date -UFormat "%Y-%m-%d-%H-%M-%S"
$packerOutDir = "$baseOutDir\PackerOut"
$packerCacheDir = "$baseOutDir\packer_cache"
@ -59,7 +43,6 @@ $wimMountDir = "${labTempDir}\MountInstallWim"
$installMediaTemp = "${labTempDir}\InstallMedia"
$newMediaIsoPath = "${labTempDir}\windows.iso"
$errorActionPreference = "Stop"
$fullConfigName = "wintriallab-${baseConfigName}"
set-alias packer (gcm packer | select -expand path)
@ -69,98 +52,11 @@ set-alias vagrant (gcm vagrant | select -expand path)
$outDir = "${packerOutDir}\${fullConfigName}"
if ($tag) { $outDir += "-${tag}"}
$packerConfigRoot = "${PSScriptRoot}\${baseConfigName}"
$packerConfigRoot = "${PSScriptRoot}\packer\${baseConfigName}"
$packerFile = "${packerConfigRoot}\${baseConfigName}.packerfile.json"
$packedBoxPath = "${outDir}\${baseConfigName}_virtualbox.box"
$vagrantTemplate = "${packerConfigRoot}\vagrantfile-${baseConfigName}.template"
function Download-WSUSOfflineUpdater {
if (test-path $wsusOfflineDir) {
throw "WSUSOffline is already extracted to '$wsusOfflineDir'"
}
$filename = "wsusoffline101.zip"
$url = "http://download.wsusoffline.net/$filename"
$dlPath = "$labTempDir\$filename"
Get-WebUrl -url $url -downloadPath $dlPath
$exDir = resolve-path "$wsusOfflineDir\.." # why the ".." ? because the zipfile puts everything in a 'wsusoffline' folder
sevenzip x "$dlPath" "-o$exDir"
}
function Download-WindowsUpdates {
set-alias DownloadUpdates "$wsusOfflineDir\cmd\DownloadUpdates.cmd"
foreach ($product in @('w63','w63-x64','w100','w100-x64')) {
DownloadUpdates $product glb /includedotnet /verify
}
}
<#
.notes
The install.wim file doesn't (ever? sometimes?) denote architecture in its image names, but boot.wim (always? usually?) does
#>
function Get-BootWimArchitecture {
[cmdletbinding()] param(
[parameter(mandatory=$true)] $wimFile
)
$bootWimInfo = Get-WindowsImage -imagePath $wimFile -verbose:$verbose
$arch = $null
if (-not $bootWimInfo) { throw "Got no information for wimfile at '$wimFile'"}
elseif ($bootWimInfo[0].ImageName -match "x86") { $arch = $ArchitectureId.i386 }
elseif ($bootWimInfo[0].ImageName -match "x64") { $arch = $ArchitectureId.amd64 }
else { throw "Could not determine architecture for '$wimFile'"}
write-verbose "Found an architecture of '$arch' for '$wimFile'"
return $arch
}
function Apply-WindowsUpdatesToIso {
[cmdletbinding()] param (
[parameter(mandatory=$true)] [string] $inputIso,
[parameter(mandatory=$true)] [string] $outputIso,
[parameter(mandatory=$true)] [string] $wsusOfflineDir,
[parameter(mandatory=$true)] [string] $wimMountDir
)
$myWimMounts = @()
mount-diskimage -imagepath $inputIso
$mountedDrive = get-diskimage -imagepath $inputIso | get-volume | select -expand DriveLetter
$installWim = "$labTempDir\install.wim"
if (-not (test-path $installWim)) {
cp "${mountedDrive}:\Sources\install.wim" $labTempDir -verbose:$verbose
}
else {
write-verbose "Using EXISTING install.wim at '$installWim'"
}
Set-ItemProperty -path $installWim -name IsReadOnly -value $false -force
$arch = Get-BootWimArchitecture -wimFile "${mountedDrive}:\sources\boot.wim" -verbose:$verbose
dismount-diskimage -imagepath $inputIso
$wimInfo = Get-WindowsImage -imagePath $installWim
$shortCode = Get-WOShortCode -OSName $wimInfo[0].ImageName -OSArchitecture $arch
#$updatePath = resolve-path "${wsusOfflineDir}\client\$shortCode\glb" | select -expand Path
$updatePath = "D:\iso\wintriallab\temp-slipstream\WSUSCache\w63-i386-glb"
foreach ($wimInfo in (Get-WindowsImage -imagePath $installWim)) {
$wimMountSubdir = mkdir "${wimMountDir}\$($wimInfo.ImageIndex)" -force | select -expand fullname
Mount-WindowsImage -imagePath $installWim -index $wimInfo.ImageIndex -path $wimMountSubdir
write-verbose "Applying '$((ls $updatePath).count)' updates to '$wimInfo'''"
try {
Add-WindowsPackage -PackagePath $updatePath -path $wimMountSubdir
}
catch {
write-verbose "Caught error(s) when installing packages:`n`n$_`n"
}
Dismount-WindowsImage -Path $wimMountSubdir -Save
}
New-WindowsInstallMedia -sourceIsoPath $inputIso -installMediaTemp $installMediaTemp -installWimPath $installWim -outputIsoPath $outputIso
}
function Build-PackerFile {
[cmdletbinding()]
param(
@ -199,10 +95,10 @@ function Build-PackerFile {
popd
}
$outBox = get-item $outDir\*.box
if ($outBox.count -gt 1) {
if ($outBox.PSObject.Properties['count'] -and $outBox.count -gt 1) {
throw "Somehow you came up with more than one box here: '$outBox'"
}
if ($outBox -notmatch [Regex]::Escape($packedBoxPath)) {
if ($outBox.fullname -notmatch [Regex]::Escape($packedBoxPath)) {
throw "Found an output box '$outBox', but it doesn't match the expected packed box path of '$packedBoxPath'"
}
cp "$vagrantTemplate" "$outDir\Vagrantfile"
@ -279,15 +175,6 @@ if ($baseConfigName) {
write-output ""
}
if ($DownloadWSUS) {
if (-not (test-path $wsusOfflineDir)) {
Download-WSUSOfflineUpdater
}
Download-WindowsUpdates
}
if ($ApplyWSUS) {
Apply-WindowsUpdatesToIso -inputIso $isoPath -outputIso $newMediaIsoPath -wsusOfflineDir $wsusOfflineDir -wimMountDir $wimMountDir -verbose:$verbose
}
if ($BuildPacker) {
$bpfParam = @{
packerFile = $packerFile

@ -0,0 +1,154 @@
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<servicing/>
<settings pass="windowsPE">
<component xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Microsoft-Windows-Setup" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<DiskConfiguration>
<Disk wcm:action="add">
<CreatePartitions>
<CreatePartition wcm:action="add">
<Order>1</Order>
<Type>Primary</Type>
<Extend>true</Extend>
</CreatePartition>
</CreatePartitions>
<ModifyPartitions>
<ModifyPartition wcm:action="add">
<Extend>false</Extend>
<Format>NTFS</Format>
<Letter>C</Letter>
<Order>1</Order>
<PartitionID>1</PartitionID>
<Label>SystemDrive</Label>
</ModifyPartition>
</ModifyPartitions>
<DiskID>0</DiskID>
<WillWipeDisk>true</WillWipeDisk>
</Disk>
<WillShowUI>OnError</WillShowUI>
</DiskConfiguration>
<UserData>
<AcceptEula>true</AcceptEula>
<!--
NOTE: If you are re-configuring this for use of a retail key
and using a retail ISO, you need to adjust the <ProductKey> block
below to look like this:
<ProductKey>
<Key>33PXH-7Y6KF-2VJC9-XBBR8-HVTHH</Key>
<WillShowUI>Never</WillShowUI>
</ProductKey>
Notice the addition of the `<Key>` element.
-->
<!-- Product Key from http://technet.microsoft.com/en-us/library/jj612867.aspx -->
<ProductKey>
NPPR9-FWDCX-D2C8J-H872K-2YT43
<WillShowUI>Never</WillShowUI>
</ProductKey>
</UserData>
<ImageInstall>
<OSImage>
<InstallTo>
<DiskID>0</DiskID>
<PartitionID>1</PartitionID>
</InstallTo>
<WillShowUI>OnError</WillShowUI>
<InstallToAvailablePartition>false</InstallToAvailablePartition>
<InstallFrom>
<MetaData wcm:action="add">
<Key>/IMAGE/NAME</Key>
<Value>Windows 10 Enterprise Evaluation</Value>
</MetaData>
</InstallFrom>
</OSImage>
</ImageInstall>
</component>
<component xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<SetupUILanguage>
<UILanguage>en-US</UILanguage>
</SetupUILanguage>
<InputLocale>en-US</InputLocale>
<SystemLocale>en-US</SystemLocale>
<UILanguage>en-US</UILanguage>
<UILanguageFallback>en-US</UILanguageFallback>
<UserLocale>en-US</UserLocale>
</component>
</settings>
<settings pass="offlineServicing">
<component name="Microsoft-Windows-LUA-Settings" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<EnableLUA>false</EnableLUA>
</component>
</settings>
<settings pass="oobeSystem">
<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>
<AdministratorPassword>
<Value>V@grant123</Value>
<PlainText>true</PlainText>
</AdministratorPassword>
<LocalAccounts>
<LocalAccount wcm:action="add">
<Password>
<Value>V@grant123</Value>
<PlainText>true</PlainText>
</Password>
<Description>Vagrant User</Description>
<DisplayName>vagrant</DisplayName>
<Group>administrators</Group>
<Name>vagrant</Name>
</LocalAccount>
</LocalAccounts>
</UserAccounts>
<OOBE>
<HideEULAPage>true</HideEULAPage>
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
<NetworkLocation>Home</NetworkLocation>
<ProtectYourPC>1</ProtectYourPC>
</OOBE>
<AutoLogon>
<Password>
<Value>V@grant123</Value>
<PlainText>true</PlainText>
</Password>
<Username>vagrant</Username>
<Enabled>true</Enabled>
</AutoLogon>
<FirstLogonCommands>
<SynchronousCommand wcm:action="add">
<CommandLine>cmd.exe /c powershell -ExecutionPolicy Unrestricted -Command "Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Force"</CommandLine>
<Description>Set Execution Policy</Description>
<Order>1</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
<SynchronousCommand wcm:action="add">
<!-- This task MUST include enabling winrm for Packer to be able to continue -->
<CommandLine>powershell.exe -File A:\autounattend-postinstall.ps1</CommandLine>
<Description>Run Postinstall Script</Description>
<Order>99</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
</FirstLogonCommands>
<ShowWindowsLive>false</ShowWindowsLive>
</component>
</settings>
<settings pass="specialize">
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<OEMInformation>
<HelpCustomized>false</HelpCustomized>
</OEMInformation>
<!-- Rename computer here. -->
<ComputerName>vagrant-10-x86</ComputerName>
<TimeZone>Central Standard Time</TimeZone>
<RegisteredOwner/>
</component>
<component xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Microsoft-Windows-Security-SPP-UX" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<SkipAutoActivation>true</SkipAutoActivation>
</component>
</settings>
<cpi:offlineImage xmlns:cpi="urn:schemas-microsoft-com:cpi" cpi:source="catalog:d:/sources/install_windows 7 ENTERPRISE.clg"/>
</unattend>

@ -0,0 +1,27 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.require_version ">= 1.6.2"
Vagrant.configure("2") do |config|
config.vm.define "vagrant-windows-10"
config.vm.box = "windows_10"
config.vm.communicator = "winrm"
# Admin user name and password
config.winrm.username = "vagrant"
config.winrm.password = "V@grant123"
config.vm.guest = :windows
config.windows.halt_timeout = 15
config.vm.network :forwarded_port, guest: 3389, host: 3389, id: "rdp", auto_correct: true
config.vm.provider :virtualbox do |v, override|
#v.gui = true
v.customize ["modifyvm", :id, "--memory", 1024]
v.customize ["modifyvm", :id, "--cpus", 1]
v.customize ["setextradata", "global", "GUI/SuppressMessages", "all" ]
end
end

@ -0,0 +1,54 @@
{
"variables": {
"output_directory": "packer-output"
},
"builders": [
{
"type": "virtualbox-iso",
"iso_url": "http://care.dlservice.microsoft.com/dl/download/C/3/9/C399EEA8-135D-4207-92C9-6AAB3259F6EF/10240.16384.150709-1700.TH1_CLIENTENTERPRISEEVAL_OEMRET_X86FRE_EN-US.ISO",
"iso_checksum_type": "sha1",
"iso_checksum": "875b450d67e7176b8b3c72a80c60a0628bf1afac",
"headless": true,
"boot_wait": "2m",
"communicator": "winrm",
"winrm_username": "vagrant",
"winrm_password": "V@grant123",
"winrm_timeout": "72h",
"shutdown_command": "shutdown /s /t 10 /f /d p:4:1 /c \"Packer Shutdown\"",
"guest_os_type": "Windows10",
"disk_size": 61440,
"guest_additions_mode": "attach",
"floppy_files": [
"./Autounattend.xml",
"../../scripts/win-updates.ps1",
"../../scripts/enable-winrm.ps1",
"../../scripts/wintriallab-postinstall.psm1",
"../../scripts/autounattend-postinstall.ps1",
"../../scripts/provisioner-postinstall.ps1"
],
"vboxmanage": [
["setextradata", "global", "GUI/SuppressMessages", "all" ],
["modifyvm", "{{.Name}}", "--memory", "2048" ],
["modifyvm", "{{.Name}}", "--cpus", "2" ],
["modifyvm", "{{.Name}}", "--accelerate2dvideo", "on"],
["modifyvm", "{{.Name}}", "--vram", 128]
]
}
],
"provisioners": [
{
"type": "powershell",
"inline": ["A:\\provisioner-postinstall.ps1 -Verbose"]
}
],
"post-processors": [
{
"type": "vagrant",
"keep_input_artifact": false,
"output": "{{user `output_directory`}}/windows_10_x86_{{.Provider}}.box",
"vagrantfile_template": "vagrantfile-windows_10_x86.template"
}
]
}

@ -29,14 +29,8 @@
</DiskConfiguration>
<UserData>
<AcceptEula>true</AcceptEula>
<!-- do I need this for anything at all?
<FullName>Vagrant Administrator</FullName>
<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
and using a retail ISO, you need to adjust the <ProductKey> block
below to look like this:
@ -137,7 +131,7 @@
<Order>99</Order>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
</FirstLogonCommands>
<ShowWindowsLive>false</ShowWindowsLive>
</component>

@ -13,22 +13,25 @@
"communicator": "winrm",
"winrm_username": "vagrant",
"winrm_password": "V@grant123",
"winrm_timeout": "8h",
"winrm_timeout": "72h",
"shutdown_command": "shutdown /s /t 10 /f /d p:4:1 /c \"Packer Shutdown\"",
"guest_os_type": "Windows81",
"disk_size": 61440,
"guest_additions_mode": "attach",
"floppy_files": [
"./Autounattend.xml",
"../scripts/win-updates.ps1",
"../scripts/enable-winrm.ps1",
"../scripts/wintriallab-postinstall.psm1",
"../scripts/autounattend-postinstall.ps1",
"../scripts/provisioner-postinstall.ps1"
"../../scripts/win-updates.ps1",
"../../scripts/enable-winrm.ps1",
"../../scripts/wintriallab-postinstall.psm1",
"../../scripts/autounattend-postinstall.ps1",
"../../scripts/provisioner-postinstall.ps1"
],
"vboxmanage": [
[ "modifyvm", "{{.Name}}", "--memory", "2048" ],
[ "modifyvm", "{{.Name}}", "--cpus", "2" ]
["setextradata", "global", "GUI/SuppressMessages", "all" ],
["modifyvm", "{{.Name}}", "--memory", "2048" ],
["modifyvm", "{{.Name}}", "--cpus", "2" ],
["modifyvm", "{{.Name}}", "--accelerate2dvideo", "on"],
["modifyvm", "{{.Name}}", "--vram", 128]
]
}
],

@ -1,17 +1,65 @@
windows-trial-lab: scripts for building one or more machines from Windows trial ISOs
## Credits
This started as some customizations for [joefitzgerald/packer-windows](https://github.com/joefitzgerald/packer-windows) that got a liiiiiiiittle out of hand.
These were the *types* of changes I'm trying to make:
- I rewrote their Windows Update script to be much more readable (imo). Now it has clearly defined functions with parameter blocks, you can set the postinstall step when calling it (rather than hardcoding calling `A:\openssh.ps1`) and you only have to set it once, and all functions MAY read global variables set at the top level but DO NOT write to them.
- I want to use WinRM rather than OpenSSH
- As a result of this, I don't copy anything to the host for provisioning, because this is buggy with WinRM
- I rewrote lots of their scripts as functions in my Powershell module
- I log to Windows Event Log
And these are some specific changes that may impact you
- The original project has [a way to install KB2842230](https://github.com/joefitzgerald/packer-windows/blob/master/scripts/hotfix-KB2842230.bat). I haven't run into this problem, but if I did, I'd have to figure this one out too. I'm not sure but it appears that they have an installer script but not a downloader script - it's unclear whether people are actually using this or not.
- The original project has [a script that forces all network locations to be private](https://github.com/joefitzgerald/packer-windows/blob/master/scripts/fixnetwork.ps1), which is necessary to enable PS Remoting. I haven't hit a problem that this solved yet, so I don't include it.
- The Windows 10 Autounattend.xml also sets the NewNetworkWindowOff registry key, per <https://technet.microsoft.com/en-us/library/gg252535%28v=ws.10%29.aspx>, by doing `cmd.exe /c reg add "HKLM\System\CurrentControlSet\Control\Network\NewNetworkWindowOff"`, before running the fixnetwork.ps1 script.
- I don't have VMware, only VirtualBox, so all the VMware support was removed (since I was rewriting it and couldn't test it, this seemed like the right thing to do)
- I use WinRM rather than OpenSSH
- I don't include installers for puppet/salt/etc
- I had to change the vagrant user's password to something more complex so you could remote in to it
## Layout and script purpose
- marionettist/windows-trial-lab/
- buildlab.ps1 # controls the whole flow of everything
- scripts/
- windeploy-marionettist/
- windeploy-marionettist.psm1
- (etc)
- autounattend-postinstall.ps1 # run from Autounattend.xml, contains hardcoded values
- packer-postinstall.ps1 # run by a packer provisioner, contains hardcoded values
- download-windowsupdates.ps1 # run on schedule, should use a config file somewhere
- build-updatedwindowsisos.ps1 # run on schedule, should use a config file somewhere
- build-vagrantboxes.ps1 # run on schedule, should use a config file somewhere
- buildlab.ps1 # ?? maybe? controls the whole flow of everything? I at least need something that talks to Packer
- (any other scripts)
- provisioner-postinstall.ps1 # run by a packer provisioner, contains hardcoded values
- win-updates.ps1 # run from autounattend-postinstall if desired, reboots system repeatedly
- enable-winrm.ps1 # run from autounattend-postinstall
- packer/
- (folders for each version of Windows)
## To do
buildlab.ps1 improvements:
- would like to use the -tag in the name for the vagrant box too, but that requires parameterizing both the packerfile and the vagrantfile template :/ not sure what to do about this
- I have a concept of "packer basename" and "tag" in buildlab. Extend this to also have "architecture" and "flavor" (or something - to capture Server Standard vs Core vs Datacenter etc)
packer/vagrant/postinstall improvements:
- store passwords securely for shit and/or generate them on the fly
- use client certs for WinRM: https://msdn.microsoft.com/en-us/library/aa384295%28v=vs.85%29.aspx
- enable clipboard and drag&drop in my Vagrantfile - though NOT for throwaway VMs that might be insecure!
- would be great if I didn't have duplicated Autounattend.xml files everywhere - can I templatize this?
- disable monitor blanking (useful for seeing where your VM is at from VBox's Manager app without actually opening the window)
other improvements
- I really wish I had a way to slipstream updates into ISOs so the first Windows Update run is just getting recent stuff. There are 150+ updates for Win 8.1 at first boot, and these take a few hours to install. Ughhhh.
upstream improvements
- It's possible that the original project might be interested in some of the stuff I've done, particularly the Windows Update work, and maybe even my postinstall module. Clean up the code and submit it to them and see what they think.
## Whines
- The shell, windows-shell, and powershell provisioners are VERY finicky. I canNOT make them work reliably. The easiest thing I can figure out how to do is to use a Powershell provisioner to call a file with no arguments over WinRM. lmfao

@ -1,29 +1,25 @@
[cmdletbinding()]
param()
[cmdletbinding()] param()
import-module $PSScriptRoot\wintriallab-postinstall.psm1
try {
Invoke-ScriptblockAndCatch -scriptBlock {
Write-EventLogWrapper "Starting the autounattend postinstall script"
Set-IdleDisplayPoweroffTime -seconds 0
Set-PasswordExpiry -accountName "vagrant" -expirePassword $false
Disable-HibernationFile
Enable-MicrosoftUpdate
Set-AllNetworksToPrivate # Required for Windows 10, not required for 81, not sure about other OSes
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 -CalledFromRegistry -RestartAction RunAtLogon -PostUpdateExpression '$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
$winRmCommand = "$PSHome\powershell.exe -File A:\enable-winrm.ps1"
$winUpdateCpmmand = "$PSHOME\powershell.exe -File A:\win-updates.ps1 -CalledFromRegistry -RestartAction RunAtLogon -PostUpdateExpression `"$winRmCommand`""
# To install Windows Updates then enable WinRM after reboot:
Set-RestartRegistryEntry -restartAction RunAtLogon -restartCommand $winUpdateCpmmand
# To just enable WinRM without installing updates after reboot:
#Set-RestartRegistryEntry -restartAction RunAtLogon -restartCommand $winRmCommand
Restart-Computer -force
}
catch {
$message = "======== CAUGHT EXCEPTION ========`r`n$_`r`n"
$message += "======== ERROR STACK ========"
$error |% { $message += "$_`r`n----`r`n" }
$message += "======== ========"
Write-EventLogWrapper $message
exit 666
}

@ -1,18 +1,13 @@
# I've had the best luck doing it this way - NOT doing it in a single batch script
# Sometimes one of these commands will stop further execution in a batch script, but when I
# call cmd.exe over and over like this, that problem goes away.
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'
import-module $PSScriptRoot\wintriallab-postinstall.psm1
try {
Enable-WinRM
}
catch {
$message = "======== CAUGHT EXCEPTION ========`r`n$_`r`n"
$message += "======== ERROR STACK ========`r`n"
$error |% { $message += "$_`r`n----`r`n" }
$message += "======== ========"
Write-EventLogWrapper $message
exit 666
}

@ -2,26 +2,14 @@
Fucking Packer is giving me problems with its shell, windows-shell, and powershell provisioners, so fuck it
Don't require parameters - it won't run with parameters during post install. This is just for clarity & ease of debugging
#>
[cmdletbinding()]
param(
[cmdletbinding()] param(
$packerBuildName = ${env:PACKER_BUILD_NAME},
$packerBuilderType = ${env:PACKER_BUILDER_TYPE},
$tempDir # calculated later on if this is empty
$packerBuilderType = ${env:PACKER_BUILDER_TYPE}
)
$errorActionPreference = "stop"
write-verbose "PostInstall for packer build '$packerBuildName' of type '$packerBuilderType'"
if ($packerBuilderType -notmatch "virtualbox") {
$warning = "@@@WARNING@@@ I have no way to install tools for your selected Packer build type of '$packerBuilderType'"
write-host -foreground red -object $warning
}
$LASTEXITCODE = 0 # just in case
import-module $PSScriptRoot\wintriallab-postinstall.psm1
try {
Invoke-ScriptblockAndCatch -scriptBlock {
Write-EventLogWrapper "PostInstall for packer build '$packerBuildName' of type '$packerBuilderType'"
Install-SevenZip
Disable-AutoAdminLogon
Enable-RDP
@ -41,16 +29,3 @@ try {
Install-CompiledDotNetAssemblies # Takes about 15 minutes for me
Compress-WindowsInstall # Takes maybe another 15 minutes
}
catch {
write-host "======== CAUGHT EXCEPTION ========"
write-host "$_"
write-host "======== CALL STACK ========"
Get-PSCallStack | format-list
write-host "======== ERROR STACK ========"
for ($i=0; $i -lt $error.count; $i+=1) {
write-host "`$error[$i]"
write-host $error[$i]
}
write-host "======== ========"
exit 666
}

@ -0,0 +1,179 @@
import-module dism -verbose:$false
# TODO: Copy-ItemAndExclude
# function Copy-ItemAndExclude {
# [cmdletbinding()] param(
# [parameter(mandatory=$true)] [string] $path,
# [parameter(mandatory=$true)] [string] $destination,
# [parameter(mandatory=$true)] [string[]] $exclude,
# [switch] $force
# )
# $path = resolve-path $path | select -expand path
# $sourceItems = Get-ChildItem -Path $path -Recurse -Exclude $exclude
# 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 {
# if ($_.GetType() -eq [System.IO.FileInfo]) {
# Join-Path $destination $_.FullName.Substring($path.length)
# }
# else {
# Join-Path $destination $_.Parent.FullName.Substring($path.length)
# }
# }
# }
<#
.description
Get the path of the Windows ADK or AIK or whatever the fuck they're calling it from a format string
- {0} is always the WAIK directory
- e.g. "C:\Program Files (x86)\Windows Kits\8.1\"
- e.g. "X:\Program Files\Windows Kits\8.0"
- {1} is always the host architecture (x86 or amd64)
- i THINK this is right, but I don't understand WHY. why do you need an amd64 version of oscdimg.exe?
- however, there are arm executables lying around, and i definitely can't execute those. wtf?
So we expect a string like "{0}\bin\{1}\wsutil.exe"
#>
function Get-AdkPath {
[cmdletbinding()] param(
[parameter(mandatory=$true)] [string] $pathFormatString
)
$adkPath = ""
$possibleAdkPaths = @("${env:ProgramFiles(x86)}\Windows Kits\8.1","${env:ProgramFiles}\Windows Kits\8.1")
$possibleAdkPaths |% { if (test-path $_) { $adkPath = $_ } }
if (-not $adkPath) { throw "Could not find the Windows Automated Installation Kit" }
Write-EventLogWrapper "Found the WAIK at '$adkPath'"
$arch = Get-OSArchitecture
switch ($arch) {
$ArchitectureId.i386 {
$formatted = $pathFormatString -f $adkPath,$waikArch
if (test-path $formatted) { return $formatted }
}
$ArchitectureId.amd64 {
foreach ($waikArch in @("amd64","x64")) {
$formatted = $pathFormatString -f $adkPath,$waikArch
if (test-path $formatted) { return $formatted }
}
}
default {
throw "Could not determine architecture of '$arch'"
}
}
throw "Could not resolve format string '$pathFormatString' to an existing path"
}
function New-WindowsInstallMedia { # TODO fixme not sure I wanna handle temp dirs this way??
[cmdletbinding()] param(
[parameter(mandatory=$true)] [string] $sourceIsoPath,
[parameter(mandatory=$true)] [string] $installMediaTemp, # WILL BE DELETED
[parameter(mandatory=$true)] [string] $installWimPath, # your new install.wim file
[parameter(mandatory=$true)] [string] $outputIsoPath
)
$oscdImgPath = Get-AdkPath "{0}\Assessment and Deployment Kit\Deployment Tools\{1}\Oscdimg\oscdimg.exe"
$installWimPath = resolve-path $installWimPath | select -expand path
$installMediaTemp = mkdir -force $installMediaTemp | select -expand fullname
$outputIsoParentPath = split-path $outputIsoPath -parent
$outputIsoFilename = split-path $outputIsoPath -leaf
$outputIsoParentPath = mkdir -force $outputIsoParentPath | select -expand fullname
if (test-path $installMediaTemp) { rm -recurse -force $installMediaTemp }
mkdir -force $installMediaTemp | out-null
$diskVol = get-diskimage -imagepath $sourceIsoPath | get-volume
if (-not $diskVol) {
mount-diskimage -imagepath $sourceIsoPath
$diskVol = get-diskimage -imagepath $sourceIsoPath | get-volume
}
$driveLetter = $diskVol | select -expand DriveLetter
$existingInstallMediaDir = "${driveLetter}:"
# TODO: the first copy here copies the original install.wim, and the second copies the new one over it
# this is really fucking dumb right? but then, THIS is way fucking dumber:
# http://stackoverflow.com/questions/731752/exclude-list-in-powershell-copy-item-does-not-appear-to-be-working
# PS none of those solutions are generic enough to get included so fuck it
copy-item -recurse -path "$existingInstallMediaDir\*" -destination "$installMediaTemp" -verbose:$verbose
remove-item -force -path "$installMediaTemp\sources\install.wim"
copy-item -path $installWimPath -destination "$installMediaTemp\sources\install.wim" -force -verbose:$verbose
$etfsBoot = resolve-path "$existingInstallMediaDir\boot\etfsboot.com" | select -expand Path
$oscdimgCall = '& "{0}" -m -n -b"{1}" "{2}" "{3}"' -f @($oscdImgPath, $etfsBoot, $installMediaTemp, $outputIsoPath)
Write-EventLogWrapper "Calling OSCDIMG: '$oscdimgCall"
Invoke-ExpressionAndCheck $oscdimgCall -verbose:$verbose
dismount-diskimage -imagepath $sourceIsoPath
}
<#
.notes
The install.wim file doesn't (ever? sometimes?) denote architecture in its image names, but boot.wim (always? usually?) does
#>
function Get-BootWimArchitecture {
[cmdletbinding()] param(
[parameter(mandatory=$true)] $wimFile
)
$bootWimInfo = Get-WindowsImage -imagePath $wimFile -verbose:$verbose
$arch = $null
if (-not $bootWimInfo) { throw "Got no information for wimfile at '$wimFile'"}
elseif ($bootWimInfo[0].ImageName -match "x86") { $arch = $ArchitectureId.i386 }
elseif ($bootWimInfo[0].ImageName -match "x64") { $arch = $ArchitectureId.amd64 }
else { throw "Could not determine architecture for '$wimFile'"}
write-verbose "Found an architecture of '$arch' for '$wimFile'"
return $arch
}
function Apply-WindowsUpdatesToIso {
[cmdletbinding()] param (
[parameter(mandatory=$true)] [string] $inputIso,
[parameter(mandatory=$true)] [string] $outputIso,
[parameter(mandatory=$true)] [string] $wsusOfflineDir,
[parameter(mandatory=$true)] [string] $wimMountDir
)
$myWimMounts = @()
mount-diskimage -imagepath $inputIso
$mountedDrive = get-diskimage -imagepath $inputIso | get-volume | select -expand DriveLetter
$installWim = "$labTempDir\install.wim"
if (-not (test-path $installWim)) {
cp "${mountedDrive}:\Sources\install.wim" $labTempDir -verbose:$verbose
}
else {
write-verbose "Using EXISTING install.wim at '$installWim'"
}
Set-ItemProperty -path $installWim -name IsReadOnly -value $false -force
$arch = Get-BootWimArchitecture -wimFile "${mountedDrive}:\sources\boot.wim" -verbose:$verbose
dismount-diskimage -imagepath $inputIso
$wimInfo = Get-WindowsImage -imagePath $installWim
$shortCode = Get-WOShortCode -OSName $wimInfo[0].ImageName -OSArchitecture $arch
#$updatePath = resolve-path "${wsusOfflineDir}\client\$shortCode\glb" | select -expand Path
$updatePath = "D:\iso\wintriallab\temp-slipstream\WSUSCache\w63-i386-glb"
foreach ($wimInfo in (Get-WindowsImage -imagePath $installWim)) {
$wimMountSubdir = mkdir "${wimMountDir}\$($wimInfo.ImageIndex)" -force | select -expand fullname
Mount-WindowsImage -imagePath $installWim -index $wimInfo.ImageIndex -path $wimMountSubdir
write-verbose "Applying '$((ls $updatePath).count)' updates to '$wimInfo'''"
try {
Add-WindowsPackage -PackagePath $updatePath -path $wimMountSubdir
}
catch {
write-verbose "Caught error(s) when installing packages:`n`n$_`n"
}
Dismount-WindowsImage -Path $wimMountSubdir -Save
}
New-WindowsInstallMedia -sourceIsoPath $inputIso -installMediaTemp $installMediaTemp -installWimPath $installWim -outputIsoPath $outputIso
}

@ -370,7 +370,17 @@ function Run-WindowsUpdate {
}
if ($MyInvocation.InvocationName -ne '.') {
Run-WindowsUpdate
try {
Run-WindowsUpdate
}
catch {
$message = "======== CAUGHT EXCEPTION ========`r`n$_`r`n"
$message += "======== ERROR STACK ========`r`n"
$error |% { $message += "$_`r`n----`r`n" }
$message += "======== ========"
Write-WinUpEventLog $message
exit 666
}
}
# Original version was 234 lines

@ -4,16 +4,6 @@ param(
[String] $ScriptName = $MyInvocation.MyCommand.Name
)
<#
jesus fucking christ
fucking Packer
TODO:
- make every function 100% reliant on itself only.
- get rid of calls to Get-LabTempDir
- decided whether I'm using $URLs or not lol
- get better logging - use an Event Log
#>
### Global Constants that I use elsewhere
$ArchitectureId = @{
@ -21,7 +11,7 @@ $ArchitectureId = @{
i386 = "i386"
}
$WindowsVersionId = @{
w63 = "w63" # TODO: rename to w81 probably
w81 = "w81"
w10 = "w10"
w10ltsb = "w10ltsb"
server2012r2 = "server2012r2"
@ -30,7 +20,7 @@ $OfficeVersionId = @{
o2013 = "o2013"
}
$IsoUrls = @{
$WindowsVersionId.w63 = @{
$WindowsVersionId.w81 = @{
$ArchitectureId.i386 = "http://care.dlservice.microsoft.com/dl/download/B/9/9/B999286E-0A47-406D-8B3D-5B5AD7373A4A/9600.17050.WINBLUE_REFRESH.140317-1640_X86FRE_ENTERPRISE_EVAL_EN-US-IR3_CENA_X86FREE_EN-US_DV9.ISO"
$ArchitectureId.amd64 = "http://care.dlservice.microsoft.com/dl/download/B/9/9/B999286E-0A47-406D-8B3D-5B5AD7373A4A/9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_ENTERPRISE_EVAL_EN-US-IR3_CENA_X64FREE_EN-US_DV9.ISO"
}
@ -55,7 +45,7 @@ $WSUSOfflineRepoBaseUrl = "https://svn.wsusoffline.net/svn/wsusoffline/trunk"
$szUrl = "http://7-zip.org/a/$szFilename"
$URLs = @{
ISOs = @{
$WindowsVersionId.w63 = @{
$WindowsVersionId.w81 = @{
$ArchitectureId.i386 = "http://care.dlservice.microsoft.com/dl/download/B/9/9/B999286E-0A47-406D-8B3D-5B5AD7373A4A/9600.17050.WINBLUE_REFRESH.140317-1640_X86FRE_ENTERPRISE_EVAL_EN-US-IR3_CENA_X86FREE_EN-US_DV9.ISO"
}
}
@ -95,161 +85,35 @@ function Get-WebUrl {
return (get-item $outFile)
}
function Invoke-ExpressionAndCheck {
<#
.synopsis
Invoke an expression; log the expression, any output, and the last exit code
#>
function Invoke-ExpressionAndLog {
[cmdletbinding()] param(
[parameter(mandatory=$true)] [string] $command,
[switch] $invokeWithCmdExe,
[switch] $checkExitCode,
[int] $sleepSeconds
)
$global:LASTEXITCODE = 0
Write-EventLogWrapper "Invoking expression '$command'"
invoke-expression -command $command
Write-EventLogWrapper "Expression '$command' had a last exit code of '$LastExitCode'"
if ($global:LASTEXITCODE -ne 0) {
throw "LASTEXITCODE: ${global:LASTEXITCODE} for command: '${command}'"
if ($invokeWithCmdExe) {
Write-EventLogWrapper "Invoking CMD: '$command'"
$output = cmd /c "$command"
}
if ($sleepSeconds) { start-sleep $sleepSeconds }
}
# TODO: Copy-ItemAndExclude
# function Copy-ItemAndExclude {
# [cmdletbinding()] param(
# [parameter(mandatory=$true)] [string] $path,
# [parameter(mandatory=$true)] [string] $destination,
# [parameter(mandatory=$true)] [string[]] $exclude,
# [switch] $force
# )
# $path = resolve-path $path | select -expand path
# $sourceItems = Get-ChildItem -Path $path -Recurse -Exclude $exclude
# 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 {
# if ($_.GetType() -eq [System.IO.FileInfo]) {
# Join-Path $destination $_.FullName.Substring($path.length)
# }
# else {
# Join-Path $destination $_.Parent.FullName.Substring($path.length)
# }
# }
# }
function Apply-XmlTransform {
[cmdletbinding()] param(
[parameter(mandatory=$true)] [string] $xmlFile,
[parameter(mandatory=$true)] [string] $xsltFile,
[parameter(mandatory=$true)] [string] $outFile
)
$xmlFile = resolve-path $xmlFile | select -expand path
$xsltFile = resolve-path $xsltFile | select -expand path
if (test-path $outFile) { throw "outFile exists at '$outFile'" }
$outParent = split-path -parent $outFile | resolve-path | select -expand Path
$outName = split-path -leaf $outFile
$outFile = "$outParent\$outName"
$xslt = New-Object System.Xml.Xsl.XslCompiledTransform
$xslt.Load($xsltFile)
$xslt.Transform($xmlFile, $outFile)
return (get-item $outFile)
}
<#
.description
Get the path of the Windows ADK or AIK or whatever the fuck they're calling it from a format string
- {0} is always the WAIK directory
- e.g. "C:\Program Files (x86)\Windows Kits\8.1\"
- e.g. "X:\Program Files\Windows Kits\8.0"
- {1} is always the host architecture (x86 or amd64)
- i THINK this is right, but I don't understand WHY. why do you need an amd64 version of oscdimg.exe?
- however, there are arm executables lying around, and i definitely can't execute those. wtf?
So we expect a string like "{0}\bin\{1}\wsutil.exe"
#>
function Get-AdkPath {
[cmdletbinding()] param(
[parameter(mandatory=$true)] [string] $pathFormatString
)
$adkPath = ""
$possibleAdkPaths = @("${env:ProgramFiles(x86)}\Windows Kits\8.1","${env:ProgramFiles}\Windows Kits\8.1")
$possibleAdkPaths |% { if (test-path $_) { $adkPath = $_ } }
if (-not $adkPath) { throw "Could not find the Windows Automated Installation Kit" }
Write-EventLogWrapper "Found the WAIK at '$adkPath'"
$arch = Get-OSArchitecture
switch ($arch) {
$ArchitectureId.i386 {
$formatted = $pathFormatString -f $adkPath,$waikArch
if (test-path $formatted) { return $formatted }
}
$ArchitectureId.amd64 {
foreach ($waikArch in @("amd64","x64")) {
$formatted = $pathFormatString -f $adkPath,$waikArch
if (test-path $formatted) { return $formatted }
}
}
default {
throw "Could not determine architecture of '$arch'"
}
else {
Write-EventLogWrapper "Invoking Powershell expression: '$command'"
$output = invoke-expression -command $command
}
throw "Could not resolve format string '$pathFormatString' to an existing path"
}
<#
.notes
For use with WSUS Offline Updater
#>
function Get-WOShortCode { # TODO fixme I think I don't need this anymore because I'm not using WSUS Offline anymore
param(
[parameter(mandatory=$true)] [string] $OSName,
[parameter(mandatory=$true)] [string] $OSArchitecture
)
# I'm adding to this list slowly, only as I encounter the actual names from install.wim
# on the trial CDs when I actually try to install them
$shortCodeTable = @{
"8.1" = "w63"
Write-EventLogWrapper "Expression '$command' had a last exit code of '$LastExitCode' and output the following to the console:`r`n`r`n$output"
if (if ($checkExitCode -and $global:LASTEXITCODE -ne 0) {
throw "LASTEXITCODE: ${global:LASTEXITCODE} for command: '${command}'"
}
$shortCodeTable.keys |% { if ($OSName -match $_) { $shortCode = $shortCodeTable[$_] } }
if (-not $shortCode) { throw "Could not determine shortcode for an OS named '$OSName'" }
if ($OSArchitecture -match $ArchitectureId.i386) { $shortCode += "" }
elseif ($OSArchitecture -match $ArchitectureId.amd64) { $shortCode += "-x64" }
else { throw "Could not determine shortcode for an OS of architecture '$OSArchitecture'" }
Write-EventLogWrapper "Found shortcode '$shortcode' for OS named '$OSName' of architecture '$OSArchitecture'"
return $shortCode
if ($sleepSeconds) { start-sleep $sleepSeconds }
}
### Publicly exported functions called directly from slipstreaming scripts
<#
.notes
This is intended for use in the postinstall phase, on the target machine
We expect calling scripts to get a lab temp dir with this function, but we do NOT permit functions in the module to call it
TODO: does that even make sense to do??
Only functions that were intended to run in that phase should have the concept of a "LabTempDir".
NOTE: this will return the same directory every time it's called until the module is reimported
#>
function Get-LabTempDir {
if ("${script:WinTrialLabTemp}") {} # noop
elseif ("${env:WinTrialLabTemp}") {
$script:WinTrialLabTemp = $env:WinTrialLabTemp
}
else {
$dateStamp = get-date -UFormat "%Y-%m-%d-%H-%M-%S"
$script:WinTrialLabTemp = "${env:Temp}\WinTrialLab-$dateStamp"
}
$script:WinTrialLabTemp = [System.IO.Path]::GetFullPath($script:WinTrialLabTemp)
Write-EventLogWrapper "Using WinTrialLabTemp directory at '${script:WinTrialLabTemp}'"
if (-not (test-path $script:WinTrialLabTemp)) {
Write-EventLogWrapper "Temporary directory does not exist, creating it..."
mkdir -force $script:WinTrialLabTemp | out-null
}
return $script:WinTrialLabTemp
}
<#
.synopsis
Wrapper that writes to the event log but also to the screen
@ -271,6 +135,31 @@ function Write-EventLogWrapper {
Write-EventLog -LogName $eventLogName -Source $EventLogSource -EventID $eventId -EntryType $entryType -Message $MessagePlus
}
<#
.synopsis
Invoke a scriptblock. If it throws, write the errors out to the event log and exist with an error code
.notes
This is intended to be a handy wrapper for calling functions in this module that takes care of logging an exception for you.
See the autounattend-postinstall.ps1 and provisioner-postinstall.ps1 scripts for examples.
#>
function Invoke-ScriptblockAndCatch {
[cmdletbinding()] param(
[parameter(mandatory=$true)] [ScriptBlock] $scriptBlock,
[int] $failureExitCode = 666
)
try {
$scriptBlock.invoke()
}
catch {
$message = "======== CAUGHT EXCEPTION ========`r`n$_`r`n"
$message += "======== ERROR STACK ========`r`n"
$_ |% { $message += "$_`r`n----`r`n" }
$message += "======== ========"
Write-EventLogWrapper $message
exit 666
}
}
<#
.synopsis
Create and set the registry property which will run this script on reboot
@ -341,7 +230,7 @@ function Install-SevenZip {
Write-EventLogWrapper "Downloaded '$szUrl' to '$szDlPath', now running msiexec..."
$msiCall = '& msiexec /qn /i "{0}"' -f $szDlPath
# Windows suxxx so msiexec sometimes returns right away? or something idk. fuck
Invoke-ExpressionAndCheck -command $msiCall -sleepSeconds 30
Invoke-ExpressionAndLog -checkExitCode -command $msiCall -sleepSeconds 30
}
finally {
rm -force $szDlPath
@ -362,9 +251,9 @@ function Install-VBoxAdditions {
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
Invoke-ExpressionAndCheck -command ('& "{0}" add-trusted-publisher "{1}" --root "{1}"' -f "$baseDir\cert\VBoxCertUtil.exe",$oracleCert)
Invoke-ExpressionAndLog -checkExitCode -command ('& "{0}" add-trusted-publisher "{1}" --root "{1}"' -f "$baseDir\cert\VBoxCertUtil.exe",$oracleCert)
Write-EventLogWrapper "Installing the virtualbox additions"
Invoke-ExpressionAndCheck -command ('& "{0}" /with_wddm /S' -f "$baseDir\VBoxWindowsAdditions.exe") # returns IMMEDIATELY, goddamn fuckers
Invoke-ExpressionAndLog -checkExitCode -command ('& "{0}" /with_wddm /S' -f "$baseDir\VBoxWindowsAdditions.exe") # returns IMMEDIATELY, goddamn fuckers
while (get-process -Name VBoxWindowsAdditions*) { write-host 'Waiting for VBox install to finish...'; sleep 1; }
Write-EventLogWrapper "virtualbox additions have now been installed"
}
@ -375,7 +264,7 @@ function Install-VBoxAdditions {
$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)
Invoke-ExpressionAndLog -checkExitCode -command ('sevenzip x "{0}" -o"{1}"' -f $isoPath, $vbgaPath)
InstallVBoxAdditionsFromDir $vbgaPath
}
finally {
@ -402,7 +291,8 @@ function Disable-AutoAdminLogon {
set-itemproperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name AutoAdminLogon -Value 0
}
function Enable-RDP { # TODO fixme
function Enable-RDP {
Write-EventLogWrapper "Enabling RDP"
netsh advfirewall firewall add rule name="Open Port 3389" dir=in action=allow protocol=TCP localport=3389
reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server" /v fDenyTSConnections /t REG_DWORD /d 0 /f
}
@ -410,15 +300,16 @@ function Enable-RDP { # TODO fixme
function Install-CompiledDotNetAssemblies {
# http://support.microsoft.com/kb/2570538
# http://robrelyea.wordpress.com/2007/07/13/may-be-helpful-ngen-exe-executequeueditems/
# Don't check the return value - sometimes it fails and that's fine
$ngen = "${env:WinDir}\microsoft.net\framework\v4.0.30319\ngen.exe"
Invoke-ExpressionAndCheck -command "& $ngen update /force /queue"
Invoke-ExpressionAndCheck -command "& $ngen executequeueditems"
set-alias ngen32 "${env:WinDir}\microsoft.net\framework\v4.0.30319\ngen.exe"
ngen32 update /force /queue
ngen32 executequeueditems
if ((Get-OSArchitecture) -match $ArchitectureId.amd64) {
$ngen64 = "${env:WinDir}\microsoft.net\framework64\v4.0.30319\ngen.exe"
Invoke-ExpressionAndCheck -command "& $ngen64 update /force /queue"
Invoke-ExpressionAndCheck -command "& $ngen64 executequeueditems"
set-alias ngen64 "${env:WinDir}\microsoft.net\framework64\v4.0.30319\ngen.exe"
ngen64 update /force /queue
ngen64 executequeueditems
}
}
@ -428,23 +319,23 @@ function Compress-WindowsInstall {
$udfZipPath = Get-WebUrl -url $URLs.UltraDefragDownload.$OSArch -outDir $env:temp
$udfExPath = "${env:temp}\ultradefrag-portable-6.1.0.$OSArch"
# 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-ExpressionAndLog -checkExitCode -command ('sevenzip x "{0}" "-o{1}"' -f $udfZipPath,$env:temp)
$sdZipPath = Get-WebUrl -url $URLs.SdeleteDownload -outDir $env:temp
$sdExPath = "${env:temp}\SDelete"
# This archive does NOT contain a folder - extract it to a subfolder (will create if necessary)
Invoke-ExpressionAndCheck -command ('sevenzip x "{0}" "-o{1}"' -f $sdZipPath,$sdExPath)
Invoke-ExpressionAndLog -checkExitCode -command ('sevenzip x "{0}" "-o{1}"' -f $sdZipPath,$sdExPath)
stop-service wuauserv
rm -recurse -force ${env:WinDir}\SoftwareDistribution\Download
start-service wuauserv
Invoke-ExpressionAndCheck -command ('& {0} --optimize --repeat "{1}"' -f "$udfExPath\udefrag.exe","$env:SystemDrive")
Invoke-ExpressionAndLog -checkExitCode -command ('& {0} --optimize --repeat "{1}"' -f "$udfExPath\udefrag.exe","$env:SystemDrive")
$sdKey = "HKCU:\Software\Sysinternals\SDelete"
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)
Invoke-ExpressionAndLog -checkExitCode -command ('& {0} -q -z "{1}"' -f "$sdExPath\SDelete.exe",$env:SystemDrive)
}
finally {
rm -recurse -force $udfZipPath,$udfExPath,$sdZipPath,$sdExPath -ErrorAction Continue
@ -530,63 +421,36 @@ function Disable-HibernationFile {
Set-ItemProperty -path $powerKey -name HibernateEnabled -value 0 # disable hibernation altogether
}
function Enable-WinRM { # TODO fixme, would prefer this to be in Powershell if possible. also it's totally insecure
<#
.synopsis
Forcibly enable WinRM
.notes
TODO: Rewrite in pure Powershell
#>
function Enable-WinRM {
[cmdletbinding()] param()
Write-EventLogWrapper "Enabling WinRM..."
cmd /c winrm quickconfig -q
cmd /c winrm quickconfig -transport:http
cmd /c winrm set winrm/config @{MaxTimeoutms="1800000"}
cmd /c winrm set winrm/config/winrs @{MaxMemoryPerShellMB="1800"}
cmd /c winrm set winrm/config/service @{AllowUnencrypted="true"}
cmd /c winrm set winrm/config/service/auth @{Basic="true"}
cmd /c winrm set winrm/config/client/auth @{Basic="true"}
cmd /c winrm set winrm/config/listener?Address=*+Transport=HTTP @{Port="5985"}
cmd /c netsh advfirewall firewall set rule group="remote administration" new enable=yes
cmd /c netsh firewall add portopening TCP 5985 "Port 5985"
cmd /c net stop winrm
cmd /c sc config winrm start= auto
cmd /c net start winrm
<#
# cmd /c winrm quickconfig -q
# cmd /c winrm quickconfig -transport:http
Enable-PSRemoting force -SkipNetworkProfileCheck
set-item wsman:\localhost\MaxTimeoutms -value 1800000 -force
set-item wsman:\localhost\Shell\MaxMemoryPerShellMB -value 1800 -force
set-item wsman:\localhost\Service\AllowUnencrypted -value $true -force
set-item wsman:\localhost\Service\Auth\Basic -value $true -force
set-item wsman:\localhost\Client\Auth\Basic -value $true -force
# cmd /c winrm set winrm/config/listener?Address=*+Transport=HTTP @{Port="5985"}
$httpListener = $null
foreach ($listener in (ls WSMan:\localhost\Listener)) {
foreach ($key in $listener.keys) {
$subKey = $key.split("=")[0]
$subVal = $key.split("=")[1]
if (($subKey -match "Transport") -and ($subVal = "HTTP")) {
$httpListener = $listener
break
}
}
if ($httpListener) { break }
}
set-item $httpListener -value 5894
Set-Item wsman:\localhost\client\auth\CredSSP -value true -force
Enable-WSManCredSSP -force -role server -force
#set-item wsman:localhost\client\trustedhosts -value * -force
#set-item wsman:localhost\client\trustedhosts -value 1.1.2.242
set-item wsman:\localhost\listener\listener*\port -value 81 -force
stop-service winrm
set-service -StartupType "automatic"
start-service winrm
winrm get winrm/config
winrm enumerate winrm/config/listener
#>
# I've had the best luck doing it this way - NOT doing it in a single batch script
# Sometimes one of these commands will stop further execution in a batch script, but when I
# call cmd.exe over and over like this, that problem goes away.
# Note: order is important. This order makes sure that any time packer can successfully
# connect to WinRm, it won't later turn winrm back off or make it unavailable.
Invoke-ExpressionAndLog -invokeWithCmdExe -command 'net stop winrm'
Invoke-ExpressionAndLog -invokeWithCmdExe -command 'sc.exe config winrm start= auto'
Invoke-ExpressionAndLog -invokeWithCmdExe -command 'winrm quickconfig -q'
Invoke-ExpressionAndLog -invokeWithCmdExe -command 'winrm quickconfig -transport:http'
Invoke-ExpressionAndLog -invokeWithCmdExe -command 'winrm set winrm/config @{MaxTimeoutms="1800000"}'
Invoke-ExpressionAndLog -invokeWithCmdExe -command 'winrm set winrm/config/winrs @{MaxMemoryPerShellMB="2048"}'
Invoke-ExpressionAndLog -invokeWithCmdExe -command 'winrm set winrm/config/service @{AllowUnencrypted="true"}'
Invoke-ExpressionAndLog -invokeWithCmdExe -command 'winrm set winrm/config/client @{AllowUnencrypted="true"}'
Invoke-ExpressionAndLog -invokeWithCmdExe -command 'winrm set winrm/config/service/auth @{Basic="true"}'
Invoke-ExpressionAndLog -invokeWithCmdExe -command 'winrm set winrm/config/client/auth @{Basic="true"}'
Invoke-ExpressionAndLog -invokeWithCmdExe -command 'winrm set winrm/config/service/auth @{CredSSP="true"}'
Invoke-ExpressionAndLog -invokeWithCmdExe -command 'winrm set winrm/config/listener?Address=*+Transport=HTTP @{Port="5985"}'
Invoke-ExpressionAndLog -invokeWithCmdExe -command 'netsh advfirewall firewall set rule group="remote administration" new enable=yes'
Invoke-ExpressionAndLog -invokeWithCmdExe -command 'netsh firewall add portopening TCP 5985 "Port 5985"'
Invoke-ExpressionAndLog -invokeWithCmdExe -command 'net start winrm'
}
function Set-PasswordExpiry { # TODO fixme use pure Powershell
@ -594,78 +458,69 @@ function Set-PasswordExpiry { # TODO fixme use pure Powershell
[parameter(mandatory=$true)] [string] $accountName,
[parameter(mandatory=$true)] [bool] $expirePassword
)
$pe = "TRUE"
if (-not $expirePassword) { $pe = "FALSE" }
cmd.exe /c wmic useraccount where "name='$accountName'" set "PasswordExpires=$pe"
$passwordExpires = if ($expirePassword) {"TRUE"} else {"FALSE"}
$command = @"
wmic useraccount where "name='{0}'" set "PasswordExpires={1}"
"@
$command = $command -f $accountName, $passwordExpiress
Invoke-ExpressionAndLog -invokeWithCmdExe -command $command
}
function New-WindowsInstallMedia { # TODO fixme not sure I wanna handle temp dirs this way??
[cmdletbinding()] param(
[parameter(mandatory=$true)] [string] $sourceIsoPath,
[parameter(mandatory=$true)] [string] $installMediaTemp, # WILL BE DELETED
[parameter(mandatory=$true)] [string] $installWimPath, # your new install.wim file
[parameter(mandatory=$true)] [string] $outputIsoPath
)
$oscdImgPath = Get-AdkPath "{0}\Assessment and Deployment Kit\Deployment Tools\{1}\Oscdimg\oscdimg.exe"
$installWimPath = resolve-path $installWimPath | select -expand path
$installMediaTemp = mkdir -force $installMediaTemp | select -expand fullname
$outputIsoParentPath = split-path $outputIsoPath -parent
$outputIsoFilename = split-path $outputIsoPath -leaf
$outputIsoParentPath = mkdir -force $outputIsoParentPath | select -expand fullname
if (test-path $installMediaTemp) { rm -recurse -force $installMediaTemp }
mkdir -force $installMediaTemp | out-null
<#
.synopsis
Set all attached networks to Private
.description
(On some OSes) you cannot enable Windows PowerShell Remoting on network connections that are set to Public
Spin through all the network locations and if they are set to Public, set them to Private
using the INetwork interface:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa370750(v=vs.85).aspx
For more info, see:
http://blogs.msdn.com/b/powershell/archive/2009/04/03/setting-network-location-to-private.aspx
#>
function Set-AllNetworksToPrivate {
[cmdletbinding()] param()
$diskVol = get-diskimage -imagepath $sourceIsoPath | get-volume
if (-not $diskVol) {
mount-diskimage -imagepath $sourceIsoPath
$diskVol = get-diskimage -imagepath $sourceIsoPath | get-volume
# Network location feature was only introduced in Windows Vista - no need to bother with this
# if the operating system is older than Vista
if([environment]::OSVersion.version.Major -lt 6) { return }
if(1,3,4,5 -contains (Get-WmiObject win32_computersystem).DomainRole) { throw "Cannot change network location on a domain-joined computer" }
# Disable the GUI which will modally pop up (at least on Win10) lol
New-Item "HKLM:\System\CurrentControlSet\Control\Network\NewNetworkWindowOff" -force | out-null
# Get network connections
$networkListManager = [Activator]::CreateInstance([Type]::GetTypeFromCLSID([Guid]"{DCB00C01-570F-4A9B-8D69-199FDBA5723B}"))
foreach ($connection in $networkListManager.GetNetworkConnections()) {
$connName = $connection.GetNetwork().GetName()
$oldCategory = $connection.GetNetwork().GetCategory()
$connection.getNetwork().SetCategory(1)
$newCategory = $connection.GetNetwork().GetCategory()
Write-EventLogWrapper "Changed connection category for '$connName' from '$oldCategory' to '$newCategory'"
}
$driveLetter = $diskVol | select -expand DriveLetter
$existingInstallMediaDir = "${driveLetter}:"
# TODO: the first copy here copies the original install.wim, and the second copies the new one over it
# this is really fucking dumb right? but then, THIS is way fucking dumber:
# http://stackoverflow.com/questions/731752/exclude-list-in-powershell-copy-item-does-not-appear-to-be-working
# PS none of those solutions are generic enough to get included so fuck it
copy-item -recurse -path "$existingInstallMediaDir\*" -destination "$installMediaTemp" -verbose:$verbose
remove-item -force -path "$installMediaTemp\sources\install.wim"
copy-item -path $installWimPath -destination "$installMediaTemp\sources\install.wim" -force -verbose:$verbose
$etfsBoot = resolve-path "$existingInstallMediaDir\boot\etfsboot.com" | select -expand Path
$oscdimgCall = '& "{0}" -m -n -b"{1}" "{2}" "{3}"' -f @($oscdImgPath, $etfsBoot, $installMediaTemp, $outputIsoPath)
Write-EventLogWrapper "Calling OSCDIMG: '$oscdimgCall"
Invoke-ExpressionAndCheck $oscdimgCall -verbose:$verbose
dismount-diskimage -imagepath $sourceIsoPath
}
function Get-WindowsUpdateUrls { # TODO: is this how we wanna do temps tho?
<#
.synopsis
Set the idle time that must elapse before Windows will power off a display
.parameter seconds
The number of seconds before poweroff. A value of 0 means never power off.
.notes
AFAIK, this cannot be done without shelling out to powercfg
#>
function Set-IdleDisplayPoweroffTime {
[cmdletbinding()] param(
[parameter(mandatory=$true)] $windowsVersion,
[parameter(mandatory=$true)] $osArchitecture,
[parameter(mandatory=$true)] $packageXml,
[parameter(mandatory=$true)] $outFile,
[switch] $debugSaveXslt
[parameter(mandatory=$true)] [int] $seconds
)
$xsltPath = [IO.Path]::GetTempFileName()
Write-EventLogWrapper "Downloading XSLT to '$xsltPath'"
if ($osArchitecture -match $ArchitectureId.i386) { $arch = "x86" }
elseif ($osArchitecture -match $ArchitectureId.amd64) { $arch = "x64" }
else { throw "Dunno bout architecture '$osArchitecture'" }
$xsltUrl = "$WSUSOfflineRepoBaseUrl/xslt/ExtractDownloadLinks-$windowsVersion-$arch-glb.xsl"
Get-WebFile -url $xsltUrl -outFile $xsltPath
Apply-XmlTransform -xmlFile $packageXml -xsltFile $xsltPath -outFile $outFile
if (-not $debugSaveXslt) { rm -force $xsltPath }
return $outFile
$currentScheme = (powercfg /getactivescheme).split()[3]
$DisplaySubgroupGUID = "7516b95f-f776-4464-8c53-06167f40cc99"
$TurnOffAfterGUID = "3c0bc021-c8a8-4e07-a973-6b14cbcb2b7e"
set-alias powercfg "${env:SystemRoot}\System32\powercfg.exe"
powercfg /setacvalueindex $currentScheme $DisplaySubgroupGUID $TurnOffAfterGUID 0
}
# Exports: #TODO
$emmParams = @{
Alias = @("sevenzip")

@ -1,35 +0,0 @@
[cmdletbinding()] param(
[parameter(mandatory=$true)] [string] $osArchitecture,
[parameter(mandatory=$true)] [string] $WindowsVersion,
[parameter(mandatory=$true)] [string] $isoPath,
[parameter(mandatory=$true)] [string] $osArchitecture,
[parameter(mandatory=$true)] [string] $osArchitecture,
)
# This seems to be required with strict mode?
$verbose = $false
# This correctly covers -verbose -verbose:$false and -verbose:$true
if ($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent -eq $true) {
$verbose = $true
}
Set-StrictMode -Version 2.0
$ErrorActionPreference = "Stop"
get-module slipstream | remove-module
ipmo $PSScriptRoot\slipstream.psm1
$arch = $ArchitectureId.i386
$winver = $WindowsVersionId.w63
$ssTempDir = 'D:\iso\wintriallab\temp-slipstream'
mkdir -force $ssTempDir | out-null
$packageXmlFile = 'D:\iso\wintriallab\wsusscn2\wsusscn2\cabs-extracted\package.xml'
$wuUrlFile = "$ssTempDir\wuUrls.txt"
$wuDownloadCache = "${ssTempDir}\WSUSCache\${winver}-${arch}-glb"
mkdir -force $wuDownloadCache | out-null
Get-WindowsUpdateUrls -windowsVersion $winver -osArchitecture $arch -packageXml $packageXmlFile -outFile $wuUrlFile -verbose:$verbose
foreach ($url in (gc $wuUrlFile)) {
Get-WebFile -url $url -outDir $wuDownloadCache -verbose:$verbose
}

@ -1,17 +0,0 @@
- what the FUCK is going on in vagrant-ssh.bat
- better windows update mechanism imo
- would like to use the -tag in the name for the vagrant box too, but that requires parameterizing both the packerfile and the vagrantfile template :/ not sure what to do about this
- store passwords securely for shit and/or generate them on the fly
- test `lab2 -action VagrantUp -baseConfigName windows_81_x86 -tag PreLunchTest` tomorrow afternoon - will it have 89 days remaining? or 90? basically, was it activated at boot or nah?
- need to audit **all** of the scripts ../scripts actually
- it seems like the vbox tools aren't getting installed? why not?
- enable clipboard and drag&drop in my Vagrantfile - though NOT for throwaway VMs that might be insecure!
- Seems like I'm not actually defragging it in my compact.bat?
- Fucking vbox guest tools aren't installing, god I hate batch scripts
Dumb shit I figured out about how fucking bad Packer is while I got to do this
- The shell, windows-shell, and powershell provisioners are VERY finicky. I canNOT make them work reliably
- Best thing to do is upload a script with a "file" provisioner and run it with a "windows-shell" provisioner that has one inline command
- ps you can't upload directories for some reason. just individual files. one. by. fucking. one.
Loading…
Cancel
Save