Initial work on an isoupdater config for win81x86

jowjDev
Micah R Ledbetter 8 years ago
parent 889b323fc1
commit 02d3de7c9d

@ -0,0 +1,149 @@
<?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>
MHF9N-XY6XB-WVXMC-BTDCT-MKKG7
<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 8.1 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="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 -SkipWindowsUpdates</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-81-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,31 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.require_version ">= 1.6.2"
Vagrant.configure("2") do |config|
config.vm.define "vagrant-windows-81-x86"
config.vm.box = "windows_81_x86_noupdates"
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.network :forwarded_port, guest: 22, host: 2222, id: "ssh", auto_correct: true
config.vm.provider :virtualbox do |v, override|
#v.gui = true
v.customize ["modifyvm", :id, "--memory", 2048]
v.customize ["modifyvm", :id, "--cpus", 2]
v.customize ["setextradata", "global", "GUI/SuppressMessages", "all" ]
v.customize ["modifyvm", :id, "--accelerate2dvideo", "on"]
v.customize ["modifyvm", :id, "--vram", 128]
v.customize ["modifyvm", :id, "--clipboard", "bidirectional"]
v.customize ["modifyvm", :id, "--draganddrop", "bidirectional"]
end
end

@ -0,0 +1,54 @@
{
"variables": {
"output_directory": "packer-output"
},
"builders": [
{
"type": "virtualbox-iso",
"iso_url": "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",
"iso_checksum_type": "sha1",
"iso_checksum": "4ddd0881779e89d197cb12c684adf47fd5d9e540",
"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": "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"
],
"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_81_x86_noupdates_{{.Provider}}.box",
"vagrantfile_template": "vagrantfile-windows_81_x86_noupdates.template"
}
]
}

@ -0,0 +1,168 @@
<#
This script is part of my ISO updater workflow.
It expects to be run on a machine that has applied Windows updates, but not deleted its cache directory at `${env:WinDir}\SoftwareDistribution\Download`.
It downloads the Windows trial ISO that corresponds to the machine that runs it and applies all Windows Updates to that ISO that are downloaded to its cache directory. Note that only MSI updates can be applied this way. The vast majority of updates are MSI updates, but other types of updates such as EXE updates cannot be applied to an ISO.
#>
[cmdletbinding()] param(
$WorkingDirectory
)
import-module $PSScriptRoot\wintriallab-postinstall.psm1
$errorActionPreference = "Stop"
<#
.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-verbose "Found the WAIK at '$adkPath'"
$arch = Get-OSArchitecture
switch ($arch) {
$ArchitectureId.i386 {
$formatted = $pathFormatString -f $adkPath,$arch
if (test-path $formatted) { return $formatted }
}
$ArchitectureId.amd64 {
foreach ($goddammit in @("amd64","x64")) {
$formatted = $pathFormatString -f $adkPath,$goddammit
if (test-path $formatted) { return $formatted }
}
}
default {
throw "Could not determine path for format string '$pathFormatString' for host architecture of '$arch'"
}
}
throw "Could not resolve format string '$pathFormatString' to an existing path"
}
<#
.synopsis
Create a new Windows ISO from a working Windows ISO + a new install.wim file
.parameter SourceIsoPath
Path of a working ISO
.parameter InstallMediaTemp
A temporary directory. NOTE: THE CONTENTS OF THIS DIRECTORY WILL BE ERASED
.parameter InstallWimPath
A new install.wim to use
.parameter OutputIsoPath
Path of the resulting ISO
#>
function New-WindowsInstallMedia {
[cmdletbinding()] param(
[parameter(mandatory=$true)] [string] $sourceIsoPath,
[parameter(mandatory=$true)] [string] $installMediaTemp,
[parameter(mandatory=$true)] [string] $installWimPath,
[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-verbose "Calling OSCDIMG: '$oscdimgCall"
Invoke-ExpressionAndCheck $oscdimgCall -verbose:$verbose
dismount-diskimage -imagepath $sourceIsoPath
}
<#
.synopsis
Download the Windows trial ISO that corresponds to the host OS, copy its install.wim, apply Windows Updates to the copy from the local WSUS cache, and create a new ISO containing the updated install.wim
.notes
TODO: could use some cleanup
TODO: improve documentation of parameters
#>
function Apply-WindowsUpdatesToTrialIso {
[cmdletbinding(DefaultParameterSetName="CorrespondingVersion")]
param(
$WorkingDirectory = (New-TemporaryDirectory | Select -Expand FullName),
[Parameter(Mandatory=$True, ParameterSetName="CorrespondingVersion")] [switch] $CorrespondingVersion,
[Parameter(Mandatory=$True, ParameterSetName="SpecifiedVersion")] $TrialIsoInfo,
[Parameter(Mandatory=$True, ParameterSetName="SpecifiedVersion")] $Architecture,
[Parameter(Mandatory=$True, ParameterSetName="SpecifiedVersion")] $WindowsUpdateCacheDir
)
if ($PsCmdlet.ParameterSetName -match "$CorrespondingVersion") {
$TrialIsoInfo = Get-WindowsTrialISO
$Architecture = Get-OSArchitecture
$WindowsUpdateCacheDir = "${env:WinDir}\SoftwareDistribution\Download"
}
$WorkingDirectory = mkdir -Force $WorkingDirectory | Select -Expand FullName
$hostWinVer = [Environment]::OSVersion.Version
$filenameVersionStamp = "$($hostWinVer.Major)-$($hostWinVer.Minor)-$Architecture"
$dateStamp = Get-Date -Format get-date -format yyyy-MM-dd
$pristineIsoPath = "$WorkingDirectory\Windows-$filenameVersionStamp-Pristine.iso"
$updatedIsoPath = "$WorkingDirectory\Windows-$filenameVersionStamp-Updated-$dateStamp.iso"
$installWimFilePath = "$WorkingDirectory\install.wim"
$installWimMountPath = "$WorkingDirectory\mnt"
$nwimTemp = "$WorkingDirectory\NewWindowsInstallMediaTemp"
Get-WebUrl -url $TrialIsoInfo.URL -outFile $pristineIsoPath
$mountedIso = Mount-DiskImage -ImagePath $pristineIsoPath -PassThru
cp "$($mountedIso.DevicePath)\sources\install.wim" $installWimFilePath
Set-ItemProperty -Path $installWimFilePath -Name IsReadOnly -Value $false -Force
Dismount-DiskImage -ImagePath $pristineIsoPath
foreach ($wimInfo in (Get-WindowsImage -ImagePath $installWimFilePath)) {
$wimMountSubdir = mkdir "${installWimMountPath}\$(wimInfo.ImageIndex)" -force | Select -Expand FullName
Mount-WindowsImage -ImagePath $installWimFilePath -Index $wimInfo.ImageIndex -Path $wimMountSubdir
try {
Add-WindowsPackage -PackagePath $WindowsUpdateCacheDir -Path $wimMountSubdir
}
catch {
write-verbose "Caught error(s) when installing packages:`n`n$_`n"
}
Dismount-WindowsImage -Path $wimMountSubdir -Save
}
New-WindowsInstallMedia -SourceIsoPath $pristineIsoPath -InstallMediaTemp $nwimTemp -InstallWimPath $installWimFilePath -OutputIsoPath $updatedIsoPath
}
throw "Need to install oscdimg.exe and the DISM Powershell module somehow...?"
Apply-WindowsUpdatesToTrialIso -CorrespondingVersion

@ -16,17 +16,38 @@ $WindowsVersionId = @{
w10ltsb = "w10ltsb"
server2012r2 = "server2012r2"
}
$URLs = @{
SevenZipDownload = @{
$ArchitectureId.i386 = "http://7-zip.org/a/7z920.msi"
$ArchitectureId.i386 = "http://7-zip.org/a/7z920.msi"
$ArchitectureId.amd64 = "http://7-zip.org/a/7z920-x64.msi"
}
UltraDefragDownload = @{
$ArchitectureId.i386 = "http://downloads.sourceforge.net/project/ultradefrag/stable-release/6.1.0/ultradefrag-portable-6.1.0.bin.i386.zip"
$ArchitectureId.i386 = "http://downloads.sourceforge.net/project/ultradefrag/stable-release/6.1.0/ultradefrag-portable-6.1.0.bin.i386.zip"
$ArchitectureId.amd64 = "http://downloads.sourceforge.net/project/ultradefrag/stable-release/6.1.0/ultradefrag-portable-6.1.0.bin.amd64.zip"
}
SdeleteDownload = "http://download.sysinternals.com/files/SDelete.zip"
WindowsIsoDownload = @{
$WindowsVersionId.w81 = @{
$ArchitectureId.i386 = @{
URL = "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"
SHA1 = "4ddd0881779e89d197cb12c684adf47fd5d9e540"
}
$ArchitectureId.amd64 = @{
URL = "http://download.microsoft.com/download/B/9/9/B999286E-0A47-406D-8B3D-5B5AD7373A4A/9600.16384.WINBLUE_RTM.130821-1623_X64FRE_ENTERPRISE_EVAL_EN-US-IRM_CENA_X64FREE_EN-US_DV5.ISO"
SHA1 = "5e4ecb86fd8619641f1d58f96e8561ec"
}
}
$WindowsVersionId.w10 = @{
$ArchitectureId.i386 = @{
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"
SHA1 = "875b450d67e7176b8b3c72a80c60a0628bf1afac"
}
$ArchitectureId.amd64 = @{
URL = "http://care.dlservice.microsoft.com/dl/download/C/3/9/C399EEA8-135D-4207-92C9-6AAB3259F6EF/10240.16384.150709-1700.TH1_CLIENTENTERPRISEEVAL_OEMRET_X64FRE_EN-US.ISO"
SHA1 = "56ab095075be28a90bc0b510835280975c6bb2ce"
}
}
}
}
$script:ScriptPath = $MyInvocation.MyCommand.Path
@ -94,6 +115,38 @@ function Invoke-ExpressionEx {
### Publicly exported functions called directly from slipstreaming scripts
<#
.synopsis
Create a temporary directory
#>
function New-TemporaryDirectory {
$dirPath = [System.IO.Path]::GetTempFileName() # creates a file automatically
rm $dirPath
mkdir $dirPath # mkdir returns a DirectoryInfo object; not capturing it here returns it to the caller
}
<#
.synopsis
Return an object containing metadata for the trial ISO for a particular version of Windows
.notes
TODO: this sucks but I can't think of anything better to do
#>
function Get-WindowsTrialISO {
[cmdletbinding()] param(
$WindowsVersion = [Environment]::OSVersion.Version.
$WindowsArchitecture = Get-OSArchitecture
)
if ($WindowsVersion.Major -eq 6 -and $WindowsVersion.Minor -eq 3) {
return $URLs.WindowsIsoDownload.w81.$WindowsArchitecture
}
elseif ($WindowsVersion.Major -eq 10 -and $WindowsVersion.Minor -eq 0) {
return $URLs.WindowsIsoDownload.w10.$WindowsArchitecture
}
else {
throw "No URL known for Windows version '$WindowsVersion' and architecture '$WindowsArchitecture'"
}
}
<#
.synopsis
Wrapper that writes to the event log but also to the screen

@ -0,0 +1,44 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
require "date"
Vagrant.require_version ">= 1.6.2"
# Resolve paths before passing them to config.vm.provision
# I do this because, when run from my buildlab.ps1 script, I set ENV:VAGRANT_CWD to this directory,
# but pushd away into a working directory on a bigger disk for actually keeping the VM
thisVagrantfileDir = File.dirname(__FILE__)
commonScriptsDir = "#{thisVagrantfileDir}/../../scripts"
Vagrant.configure("2") do |config|
config.vm.define "isoupdater-windows_81_x86"
config.vm.box = "wintriallab-windows_81_x86_noupdates"
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" ]
v.customize ["modifyvm", :id, "--accelerate2dvideo", "on"]
v.customize ["modifyvm", :id, "--vram", 128]
v.customize ["modifyvm", :id, "--clipboard", "bidirectional"]
v.customize ["modifyvm", :id, "--draganddrop", "bidirectional"]
end
config.vm.provision "file", source: "#{commonScriptsDir}/wintriallab-postinstall.psm1", destination: "marionettist/wintriallab-postinstall.psm1"
config.vm.provision "file", source: "#{commonScriptsDir}/isoupdater-postinstall.ps1", destination: "marionettist/isoupdater-postinstall.ps1"
config.vm.provision "file", source: "#{commonScriptsDir}/win-updates.ps1", destination: "marionettist/win-updates.ps1"
config.vm.provision "shell", inline: "C:/marionettist/win-updates.ps1 -PostUpdateExpression C:/marionettist/isoupdater-postinstall.ps1"
end
Loading…
Cancel
Save