From b4ef22bd98d35aa5373ec0dd1e87610e82319f94 Mon Sep 17 00:00:00 2001 From: nathanmcnulty Date: Fri, 22 Jan 2016 16:42:23 -0800 Subject: [PATCH 01/21] Initial commit Left off on parsing the sucatalog and dist files to select the right PKG --- Install-Bootcamp.ps1 | 64 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 Install-Bootcamp.ps1 diff --git a/Install-Bootcamp.ps1 b/Install-Bootcamp.ps1 new file mode 100644 index 0000000..dd176c8 --- /dev/null +++ b/Install-Bootcamp.ps1 @@ -0,0 +1,64 @@ +# Rewrite from Perl to PowerShell - Requires PowerShell v3 or greater +[CmdletBinding()] +Param( + [string]$Model = (Get-WmiObject -Class Win32_ComputerSystem | Select -ExpandProperty Model), + [switch]$Install = $false, + [string]$OutputDir = "$env:TEMP", + [switch]$KeepFiles = $false, + [string]$ProductId, + [string]$PlistPath = "brigadier.plist", + [string]$SUCATALOG_URL = 'http://swscan.apple.com/content/catalogs/others/index-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog', + # 7-Zip MSI (15.14) + [string]$SEVENZIP_URL = 'http://7-zip.org/a/7z1514-x64.msi', + # dmg2img zip download from http://vu1tur.eu.org/tools + [string]$DMG2IMG_URL = 'http://vu1tur.eu.org/tools/dmg2img-1.6.5-win32.zip' +) + +# Set values from plist +if (Test-Path $PlistPath) { + [xml]$Plist = Get-Content -Path $PlistPath + 0..($Plist.plist.dict.key.Length - 1) | ForEach-Object { + if ($Plist.plist.dict.key[$_] -eq "CatalogURL") { $SUCATALOG_URL = $plist.plist.dict.string[$_] } + if ($Plist.plist.dict.key[$_] -eq "7zipURL") { $SEVENZIP_URL = $plist.plist.dict.string[$_] } + if ($Plist.plist.dict.key[$_] -eq "Dmg2ImgURL") { $DMG2IMG_URL = $plist.plist.dict.string[$_] } + } +} + +# Check if 7zip is installed. If not, download and install it +$7zPath = "$env:ProgramFiles\7-Zip\7z.exe" +if (Test-Path $7zPath) { + $7zInstalled = $true +} else { + Invoke-WebRequest -Uri $SEVENZIP_URL -OutFile "$env:TEMP\$($SEVENZIP_URL.Split('/')[-1])" -ErrorAction Stop -Proxy $proxyserver -ProxyCredential $proxycreds + Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/i $env:TEMP\$($SEVENZIP_URL.Split('/')[-1]) /qb- /norestart" -Wait +} + +# Download Dmg2Img +function GetDmg2Img { + Invoke-WebRequest -Uri $DMG2IMG_URL -OutFile "$env:TEMP\$($DMG2IMG_URL.Split('/')[-1])" -ErrorAction Stop -Proxy $proxyserver -ProxyCredential $proxycreds + Invoke-Command -ScriptBlock {cmd /c "$7zPath" -o"$env:TEMP" x "$env:TEMP\$($DMG2IMG_URL.Split('/')[-1])" -y} +} + +# Find Bootcamp.msi +$BootCampMSI = Get-ChildItem -Path $BootCampPath -Recurse -Include BootCamp*.msi | Select -ExpandProperty FullName +if ($BootCampMSI.Length -gt 1) { + +} else { } + + +# Read data from sucatalog +[xml]$sucatalog = Invoke-WebRequest -Uri $SUCATALOG_URL -Method Get -ErrorAction Stop -Proxy $proxyserver -ProxyCredential $proxycreds +# Find all Bootcamp ESD's +$sucatalog.plist.dict.dict.dict | Where-Object { $_.String -match "Bootcamp" } | ForEach-Object { + # Get dist file to find supported models, using regex match to find models in dist files + $SupportedModels = [regex]::Matches((Invoke-RestMethod -Uri ($_.dict | Where-Object { $_.Key -match "English" } | Select -ExpandProperty String)).InnerXml,"([a-zA-Z]{4,12}[1-9]{1,2}\,[1-6])") | Select -ExpandProperty Value + if ($SupportedModels -contains $Model) { + + $download = $sucatalog.plist.dict.dict.dict | Where-Object { $_.String -match "Bootcamp" } | ForEach-Object { $_.array.dict | Select -ExpandProperty String | Where-Object { $_ -match ".pkg" }} + } +} + + +# Install Bootcamp +Invoke-Command -ScriptBlock { cmd /c "msiexec.exe /i $BootCampMSI /qb- /norestart /log $env:SystemRoot\BootcampInstall.log" + From 4def8e51dc61f654b6000467fbaf32a87b568e45 Mon Sep 17 00:00:00 2001 From: nathanmcnulty Date: Sun, 24 Jan 2016 22:09:46 -0800 Subject: [PATCH 02/21] Finish basic code This should be functioning. Still need to test and validate on a Mac. Once working, will add in additional params. --- Install-Bootcamp.ps1 | 82 +++++++++++++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 24 deletions(-) diff --git a/Install-Bootcamp.ps1 b/Install-Bootcamp.ps1 index dd176c8..c1b88d9 100644 --- a/Install-Bootcamp.ps1 +++ b/Install-Bootcamp.ps1 @@ -1,4 +1,5 @@ -# Rewrite from Perl to PowerShell - Requires PowerShell v3 or greater +# Rewrite from Python to PowerShell - Requires PowerShell v3 or greater +# To do: Set-Location to temp, add more output, add in functions for params like keep files, product id, etc., compile to EXE [CmdletBinding()] Param( [string]$Model = (Get-WmiObject -Class Win32_ComputerSystem | Select -ExpandProperty Model), @@ -14,7 +15,7 @@ Param( [string]$DMG2IMG_URL = 'http://vu1tur.eu.org/tools/dmg2img-1.6.5-win32.zip' ) -# Set values from plist +# Set values from plist - I'm sure there's a better way to do this... if (Test-Path $PlistPath) { [xml]$Plist = Get-Content -Path $PlistPath 0..($Plist.plist.dict.key.Length - 1) | ForEach-Object { @@ -25,40 +26,73 @@ if (Test-Path $PlistPath) { } # Check if 7zip is installed. If not, download and install it -$7zPath = "$env:ProgramFiles\7-Zip\7z.exe" -if (Test-Path $7zPath) { - $7zInstalled = $true -} else { +$7z = "$env:ProgramFiles\7-Zip\7z.exe" +if (!(Test-Path $7z)) { Invoke-WebRequest -Uri $SEVENZIP_URL -OutFile "$env:TEMP\$($SEVENZIP_URL.Split('/')[-1])" -ErrorAction Stop -Proxy $proxyserver -ProxyCredential $proxycreds - Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/i $env:TEMP\$($SEVENZIP_URL.Split('/')[-1]) /qb- /norestart" -Wait -} + Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/i $env:TEMP\$($SEVENZIP_URL.Split('/')[-1]) /qb- /norestart" -Wait +} else { $7zInstalled = $true } # Download Dmg2Img -function GetDmg2Img { - Invoke-WebRequest -Uri $DMG2IMG_URL -OutFile "$env:TEMP\$($DMG2IMG_URL.Split('/')[-1])" -ErrorAction Stop -Proxy $proxyserver -ProxyCredential $proxycreds - Invoke-Command -ScriptBlock {cmd /c "$7zPath" -o"$env:TEMP" x "$env:TEMP\$($DMG2IMG_URL.Split('/')[-1])" -y} -} - -# Find Bootcamp.msi -$BootCampMSI = Get-ChildItem -Path $BootCampPath -Recurse -Include BootCamp*.msi | Select -ExpandProperty FullName -if ($BootCampMSI.Length -gt 1) { - -} else { } - +Invoke-WebRequest -Uri $DMG2IMG_URL -OutFile "$env:TEMP\$($DMG2IMG_URL.Split('/')[-1])" -ErrorAction Stop -Proxy $proxyserver -ProxyCredential $proxycreds +Invoke-Command -ScriptBlock { cmd /c "$7z" -o"$env:TEMP" -y e "$env:TEMP\$($DMG2IMG_URL.Split('/')[-1])" } # Read data from sucatalog [xml]$sucatalog = Invoke-WebRequest -Uri $SUCATALOG_URL -Method Get -ErrorAction Stop -Proxy $proxyserver -ProxyCredential $proxycreds # Find all Bootcamp ESD's $sucatalog.plist.dict.dict.dict | Where-Object { $_.String -match "Bootcamp" } | ForEach-Object { - # Get dist file to find supported models, using regex match to find models in dist files + # Search dist files to find supported models, using regex match to find models in dist files - stole regex from brigadier's source $SupportedModels = [regex]::Matches((Invoke-RestMethod -Uri ($_.dict | Where-Object { $_.Key -match "English" } | Select -ExpandProperty String)).InnerXml,"([a-zA-Z]{4,12}[1-9]{1,2}\,[1-6])") | Select -ExpandProperty Value if ($SupportedModels -contains $Model) { - - $download = $sucatalog.plist.dict.dict.dict | Where-Object { $_.String -match "Bootcamp" } | ForEach-Object { $_.array.dict | Select -ExpandProperty String | Where-Object { $_ -match ".pkg" }} + $version = [regex]::Match(($_.dict | Where-Object { $_.Key -match "English" } | Select -ExpandProperty String),"(\d{3}-\d{5})") | Select -ExpandProperty Value + Write-Output "Found supported ESD: $Version" + [array]$bootcamplist += $_ } } +if ($bootcamplist.Length -gt 1) { + Write-Output "Found more than 1 supported Bootcamp ESD. Selecting newest based on posted date" + $bootcamplist | ForEach-Object { + if ($_.date -gt $latestdate) { + $latestdate = $_.date + $download = $_.array.dict | Select -ExpandProperty String | Where-Object { $_ -match '.pkg' } + #$download = $_.string.Replace(".smd",".pkg") - URL matches the .smd but may not always? + } + } +} else { $download = $_.array.dict | Select -ExpandProperty String | Where-Object { $_ -match '.pkg' }} +# Download the BootCamp ESD +Invoke-WebRequest -Uri $download -OutFile "$env:TEMP\BootCampESD.pkg" -ErrorAction Stop -Proxy $proxyserver -ProxyCredential $proxycreds +if (Test-Path -Path "$env:TEMP\BootCampESD.pkg") { + +} else { Write-Output "BootCampESD.pkg could not be found" } # Should see the error from the Invoke-WebRequest, but throwing this in there until I change this to try / catch -# Install Bootcamp -Invoke-Command -ScriptBlock { cmd /c "msiexec.exe /i $BootCampMSI /qb- /norestart /log $env:SystemRoot\BootcampInstall.log" +# Extract the WindowsSupport.dmg from the PKG +Invoke-Command -ScriptBlock { + cmd /c $7z -o"$env:TEMP" -y e "$env:TEMP\BootCampESD.pkg" + cmd /c $7z -o"$env:TEMP" -y e "$env:TEMP\Payload~" +} +# Convert the DMG to ISO +Invoke-Command -ScriptBlock { cmd /c "$env:TEMP\dmg2img.exe" -v "$env:TEMP\WindowsSupport.dmg" "$env:TEMP\WindowsSupport.iso" } + +# Extract the ISO so we can run the installer +If (!(Test-Path -Path "$env:TEMP\BootCamp")) { New-Item -Path "$env:TEMP\BootCamp" -ItemType Directory -Force } +Invoke-Command -ScriptBlock { cmd /c $7z -o"$env:TEMP\Bootcamp" -y x "$env:TEMP\WindowsSupport.iso" } + +#Uninstall 7zip and remove installer +if ($7zInstalled -ne $true) { Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/x $env:TEMP\$($SEVENZIP_URL.Split('/')[-1]) /qb- /norestart" -Wait } +Remove-Item -Path "$env:TEMP\$($SEVENZIP_URL.Split('/')[-1])" -Force + +# Find Bootcamp.msi and install correct one +$BootCampMSI = Get-ChildItem -Path $BootCampPath -Recurse -Include BootCamp*.msi | Select -ExpandProperty FullName +if ($BootCampMSI.Length -gt 1) { + # Check OS architecture and install correct version + if ((Get-WmiObject -Class Win32_OperatingSystem | Select -ExpandProperty OSArchitecture) -eq "64-bit") { + $BootCampMSI = $BootCampMSI | Where-Object { $_ -match "64" } + } else { + $BootCampMSI = $BootCampMSI | Where-Object { $_ -notmatch "64" } + } +} +Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/i $BootCampMSI /qb- /norestart /log $env:SystemRoot\BootcampInstall.log" -Wait + +# Clean up +Remove-Item -Path "$env:TEMP\*" -Recurse -Force -ErrorAction SilentlyContinue \ No newline at end of file From 33c77bdb7b3a51cde172e60783a03959935815f7 Mon Sep 17 00:00:00 2001 From: nathanmcnulty Date: Mon, 25 Jan 2016 13:44:27 -0800 Subject: [PATCH 03/21] First revision of working script Changed to BITS transfer for performance, removed dmg2img dependency, fixed a couple of errors, and moved BootCamp extract to root of drive to fix issues with DISM commands that Apple hardcoded to use C:\$WinPEDriver$ as source for WinRE injection --- Install-Bootcamp.ps1 | 63 +++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/Install-Bootcamp.ps1 b/Install-Bootcamp.ps1 index c1b88d9..0f0e0a2 100644 --- a/Install-Bootcamp.ps1 +++ b/Install-Bootcamp.ps1 @@ -1,5 +1,5 @@ # Rewrite from Python to PowerShell - Requires PowerShell v3 or greater -# To do: Set-Location to temp, add more output, add in functions for params like keep files, product id, etc., compile to EXE +# To do: Add more output, add in functions for params like install, product id, etc. [CmdletBinding()] Param( [string]$Model = (Get-WmiObject -Class Win32_ComputerSystem | Select -ExpandProperty Model), @@ -10,11 +10,14 @@ Param( [string]$PlistPath = "brigadier.plist", [string]$SUCATALOG_URL = 'http://swscan.apple.com/content/catalogs/others/index-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog', # 7-Zip MSI (15.14) - [string]$SEVENZIP_URL = 'http://7-zip.org/a/7z1514-x64.msi', - # dmg2img zip download from http://vu1tur.eu.org/tools - [string]$DMG2IMG_URL = 'http://vu1tur.eu.org/tools/dmg2img-1.6.5-win32.zip' + [string]$SEVENZIP_URL = 'http://7-zip.org/a/7z1514-x64.msi' + # Newer 7zip supports extracting DMG. May add support back if older version of 7z is found in the future. + #[string]$DMG2IMG_URL = 'http://vu1tur.eu.org/tools/dmg2img-1.6.5-win32.zip' ) +# Script processes too fast, so we have to add a pause to allow params to set up... +Start-Sleep -Seconds 1 + # Set values from plist - I'm sure there's a better way to do this... if (Test-Path $PlistPath) { [xml]$Plist = Get-Content -Path $PlistPath @@ -28,13 +31,14 @@ if (Test-Path $PlistPath) { # Check if 7zip is installed. If not, download and install it $7z = "$env:ProgramFiles\7-Zip\7z.exe" if (!(Test-Path $7z)) { - Invoke-WebRequest -Uri $SEVENZIP_URL -OutFile "$env:TEMP\$($SEVENZIP_URL.Split('/')[-1])" -ErrorAction Stop -Proxy $proxyserver -ProxyCredential $proxycreds - Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/i $env:TEMP\$($SEVENZIP_URL.Split('/')[-1]) /qb- /norestart" -Wait + Start-BitsTransfer -Source $SEVENZIP_URL -Destination "$OutputDir\$($SEVENZIP_URL.Split('/')[-1])" -ErrorAction Stop -ProxyList $proxyserver -ProxyCredential $proxycreds + #Invoke-WebRequest -Uri $SEVENZIP_URL -OutFile "$OutputDir\$($SEVENZIP_URL.Split('/')[-1])" -ErrorAction Stop -Proxy $proxyserver -ProxyCredential $proxycreds + Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/i $OutputDir\$($SEVENZIP_URL.Split('/')[-1]) /qb- /norestart" -Wait -Verbose } else { $7zInstalled = $true } # Download Dmg2Img -Invoke-WebRequest -Uri $DMG2IMG_URL -OutFile "$env:TEMP\$($DMG2IMG_URL.Split('/')[-1])" -ErrorAction Stop -Proxy $proxyserver -ProxyCredential $proxycreds -Invoke-Command -ScriptBlock { cmd /c "$7z" -o"$env:TEMP" -y e "$env:TEMP\$($DMG2IMG_URL.Split('/')[-1])" } +Start-BitsTransfer -Source $DMG2IMG_URL -Destination "$OutputDir\$($DMG2IMG_URL.Split('/')[-1])" -ErrorAction Stop -ProxyList $proxyserver -ProxyCredential $proxycreds +Invoke-Command -ScriptBlock { cmd /c "$7z" -o"$OutputDir" -y e "$OutputDir\$($DMG2IMG_URL.Split('/')[-1])" } # Read data from sucatalog [xml]$sucatalog = Invoke-WebRequest -Uri $SUCATALOG_URL -Method Get -ErrorAction Stop -Proxy $proxyserver -ProxyCredential $proxycreds @@ -57,33 +61,37 @@ if ($bootcamplist.Length -gt 1) { #$download = $_.string.Replace(".smd",".pkg") - URL matches the .smd but may not always? } } -} else { $download = $_.array.dict | Select -ExpandProperty String | Where-Object { $_ -match '.pkg' }} +} else { $download = $bootcamplist.array.dict | Select -ExpandProperty String | Where-Object { $_ -match '.pkg' }} # Download the BootCamp ESD -Invoke-WebRequest -Uri $download -OutFile "$env:TEMP\BootCampESD.pkg" -ErrorAction Stop -Proxy $proxyserver -ProxyCredential $proxycreds -if (Test-Path -Path "$env:TEMP\BootCampESD.pkg") { - -} else { Write-Output "BootCampESD.pkg could not be found" } # Should see the error from the Invoke-WebRequest, but throwing this in there until I change this to try / catch - -# Extract the WindowsSupport.dmg from the PKG -Invoke-Command -ScriptBlock { - cmd /c $7z -o"$env:TEMP" -y e "$env:TEMP\BootCampESD.pkg" - cmd /c $7z -o"$env:TEMP" -y e "$env:TEMP\Payload~" -} +Start-BitsTransfer -Source $download -Destination "$OutputDir\BootCampESD.pkg" -ErrorAction Stop -ProxyList $proxyserver -ProxyCredential $proxycreds +if (Test-Path -Path "$OutputDir\BootCampESD.pkg") { + # Extract the bootcamp installer + Invoke-Command -ScriptBlock { + cmd /c $7z -o"$OutputDir" -y e "$OutputDir\BootCampESD.pkg" + cmd /c $7z -o"$OutputDir" -y e "$OutputDir\Payload~" + If (!(Test-Path -Path "$OutputDir\BootCamp")) { New-Item -Path "$OutputDir\BootCamp" -ItemType Directory -Force } + cmd /c $7z -o"$env:SystemDrive" -y x "$OutputDir\WindowsSupport.dmg" + } +} else { Write-Output "BootCampESD.pkg could not be found"; exit } # Convert the DMG to ISO -Invoke-Command -ScriptBlock { cmd /c "$env:TEMP\dmg2img.exe" -v "$env:TEMP\WindowsSupport.dmg" "$env:TEMP\WindowsSupport.iso" } +# Invoke-Command -ScriptBlock { cmd /c "$OutputDir\dmg2img.exe" -v "$OutputDir\WindowsSupport.dmg" "$OutputDir\WindowsSupport.iso" } # Extract the ISO so we can run the installer -If (!(Test-Path -Path "$env:TEMP\BootCamp")) { New-Item -Path "$env:TEMP\BootCamp" -ItemType Directory -Force } -Invoke-Command -ScriptBlock { cmd /c $7z -o"$env:TEMP\Bootcamp" -y x "$env:TEMP\WindowsSupport.iso" } +# Invoke-Command -ScriptBlock { cmd /c $7z -o"$OutputDir\Bootcamp" -y x "$OutputDir\WindowsSupport.iso" } -#Uninstall 7zip and remove installer -if ($7zInstalled -ne $true) { Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/x $env:TEMP\$($SEVENZIP_URL.Split('/')[-1]) /qb- /norestart" -Wait } -Remove-Item -Path "$env:TEMP\$($SEVENZIP_URL.Split('/')[-1])" -Force +# Uninstall 7zip and remove installer +if ($7zInstalled -ne $true) { + Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/x $OutputDir\$($SEVENZIP_URL.Split('/')[-1]) /qb- /norestart" -Wait + Remove-Item -Path "$OutputDir\$($SEVENZIP_URL.Split('/')[-1])" -Force +} + +# Must install Realtek Audio driver before installing Bootcamp. This step simply moves it, but you should install it first, reboot, then run this script +Get-ChildItem -Path "$env:SystemDrive\" | Where-Object { $_.Name -like "BootCamp" -or $_.Name -eq "Drivers" } | Select -ExpandProperty FullName | ForEach-Object { Get-ChildItem -Path $_ -Recurse -Include RealtekSetup.exe | Select -ExpandProperty FullName } | Move-Item -Destination $OutputDir # Find Bootcamp.msi and install correct one -$BootCampMSI = Get-ChildItem -Path $BootCampPath -Recurse -Include BootCamp*.msi | Select -ExpandProperty FullName +$BootCampMSI = Get-ChildItem -Path "$env:SystemDrive\" | Where-Object { $_.Name -like "BootCamp" -or $_.Name -eq "Drivers" } | Select -ExpandProperty FullName | ForEach-Object { Get-ChildItem -Path $_ -Recurse -Include BootCamp*.msi | Select -ExpandProperty FullName } if ($BootCampMSI.Length -gt 1) { # Check OS architecture and install correct version if ((Get-WmiObject -Class Win32_OperatingSystem | Select -ExpandProperty OSArchitecture) -eq "64-bit") { @@ -92,7 +100,8 @@ if ($BootCampMSI.Length -gt 1) { $BootCampMSI = $BootCampMSI | Where-Object { $_ -notmatch "64" } } } +# Need to test if Realtek Audio can be installed without reboot and then install Bootcamp or if reboot is required first. Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/i $BootCampMSI /qb- /norestart /log $env:SystemRoot\BootcampInstall.log" -Wait # Clean up -Remove-Item -Path "$env:TEMP\*" -Recurse -Force -ErrorAction SilentlyContinue \ No newline at end of file +if ($KeepFiles -eq $false) { Remove-Item -Path "$OutputDir\*" -Recurse -Force -ErrorAction SilentlyContinue } \ No newline at end of file From acbe9c2a1e684f48f59a7c6bdb947d05fb2ecb5d Mon Sep 17 00:00:00 2001 From: nathanmcnulty Date: Thu, 7 Apr 2016 16:07:37 -0700 Subject: [PATCH 04/21] Initial commit of working script Changed from the previous Install-Bootcamp.ps1 file as the default behavior now follows Tim's script in more areas. By default, this will now download the Bootcamp installer to the desktop rather than installing by default (hence Get- instead of Install-). It now requires the -Install argument to actually perform the installation. --- Get-Bootcamp.ps1 | 86 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 Get-Bootcamp.ps1 diff --git a/Get-Bootcamp.ps1 b/Get-Bootcamp.ps1 new file mode 100644 index 0000000..899c970 --- /dev/null +++ b/Get-Bootcamp.ps1 @@ -0,0 +1,86 @@ +# Rewrite from Python to PowerShell - Requires PowerShell v3 or greater +# 16/4/7 - Removd DMG2IMG components, removed plist as we can take arguments instead, preparing for ability to pass ProductID as an array +# To do: Add logging, include fallback in case BITS is not installed: https://blog.jourdant.me/3-ways-to-download-files-with-powershell/ +[CmdletBinding()] +Param( + [string]$Model = (Get-WmiObject -Class Win32_ComputerSystem).Model, + [switch]$Install, + [string]$OutputDir = "$env:TEMP", + [switch]$KeepFiles, + [array]$ProductId, + [string]$Mst, + [string]$SUCATALOG_URL = 'http://swscan.apple.com/content/catalogs/others/index-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog', + [string]$SEVENZIP_URL = 'http://7-zip.org/a/7z1514-x64.msi' +) + +# Create Output Directory if it does not exist +if (!(Test-Path $OutputDir)) { New-Item -Path $OutputDir -ItemType Directory -Force } + +# Check if 7zip 15.14 is installed. If not, download and install it. +$7z = "$env:ProgramFiles\7-Zip\7z.exe" +if (Test-Path $7z) { $7zInstalled = $true; [decimal]$7zVersion = (Get-ItemProperty $7z).VersionInfo.FileVersion } +if ($7zVersion -lt 15.14) { + Start-BitsTransfer -Source $SEVENZIP_URL -Destination "$OutputDir\$($SEVENZIP_URL.Split('/')[-1])" -ErrorAction Stop + Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/i $OutputDir\$($SEVENZIP_URL.Split('/')[-1]) /qb- /norestart" -Wait -Verbose +} + +# Read data from sucatalog and find all Bootcamp ESD's +[xml]$sucatalog = Invoke-WebRequest -Uri $SUCATALOG_URL -Method Get -ErrorAction Stop +$sucatalog.plist.dict.dict.dict | Where-Object { $_.String -match "Bootcamp" } | ForEach-Object { + # Search dist files to find supported models, using regex match to find models in dist files - stole regex from brigadier's source + $SupportedModels = [regex]::Matches((Invoke-RestMethod -Uri ($_.dict | Where-Object { $_.Key -match "English" }).String).InnerXml,"([a-zA-Z]{4,12}[1-9]{1,2}\,[1-6])").Value + if ($SupportedModels -contains $Model) { + $version = [regex]::Match(($_.dict | Where-Object { $_.Key -match "English" }).String,"(\d{3}-\d{5})").Value + Write-Output "Found supported ESD: $Version" + [array]$bootcamplist += $_ + } +} +if ($bootcamplist.Length -gt 1) { + Write-Output "Found more than 1 supported Bootcamp ESD. Selecting newest based on posted date" + $bootcamplist | ForEach-Object { + if ($_.date -gt $latestdate) { + $latestdate = $_.date + $download = $_.array.dict.string | Where-Object { $_ -match '.pkg' } + } + } +} else { $download = $bootcamplist.array.dict.string | Where-Object { $_ -match '.pkg' }} + +# Download the BootCamp ESD +Start-BitsTransfer -Source $download -Destination "$OutputDir\BootCampESD.pkg" -ErrorAction Stop +if (Test-Path -Path "$OutputDir\BootCampESD.pkg") { + # Extract the bootcamp installer + Invoke-Command -ScriptBlock { + cmd /c $7z -o"$OutputDir" -y e "$OutputDir\BootCampESD.pkg" + cmd /c $7z -o"$OutputDir" -y e "$OutputDir\Payload~" + # If just downloading, put the extracted installers on the desktop + if ($Install) { cmd /c $7z -o"$OutputDir" -y x "$OutputDir\WindowsSupport.dmg" } else { if ($OutputDir -eq "$env:TEMP") { cmd /c $7z -o"$env:USERPROFILE\Desktop\$version" -y x "$OutputDir\WindowsSupport.dmg" }} + } +} else { Write-Output "BootCampESD.pkg could not be found"; exit } + +# Uninstall 7zip if we installed it +if ($7zInstalled -ne $true) { Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/x $OutputDir\$($SEVENZIP_URL.Split('/')[-1]) /qb- /norestart" -Wait } + +# Testing for iMac14,1 issue with Realtek Audio driver hanging the installation +#"Bootcamp","Drivers" | ForEach-Object { if (Test-Path -Path "$OutputDir\$_") { (Get-ChildItem -Path "$OutputDir\$_" -Recurse -Include RealtekSetup.exe -ErrorAction SilentlyContinue).FullName }} | Move-Item -Destination $OutputDir + +# Find Bootcamp.msi and install matching based on OS architecture +[array]$BootCampMSI = "Bootcamp","Drivers" | ForEach-Object { if (Test-Path -Path "$OutputDir\$_") { (Get-ChildItem -Path "$OutputDir\$_" -Recurse -Include BootCamp*.msi).FullName }} +if ($BootCampMSI.Length -gt 1) { + # Check OS architecture and install correct version + if ((Get-WmiObject -Class Win32_OperatingSystem).OSArchitecture -eq "64-bit") { + $BootCampMSI = $BootCampMSI | Where-Object { $_ -match "64" } + } else { + $BootCampMSI = $BootCampMSI | Where-Object { $_ -notmatch "64" } + } +} + +# Install Bootcamp and use MST if specified (I uploaded one that I had to use to fix the latest ESD on an iMac14,1) +if ($Install) { + if ($mst -ne "") { + Copy-Item -Path $Mst -Destination $($BootCampMSI.TrimEnd("\BootCamp.msi")) + Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/i $BootCampMSI TRANSFORMS=$($Mst.Split('\')[-1]) /qb- /norestart /l*v $env:SystemDrive\BootcampInstall.log" -Verbose -Wait + } else { Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/i $BootCampMSI /qb- /norestart /l*v $env:SystemDrive\BootcampInstall.log" -Verbose -Wait } +} else { exit } + +# Clean up +if ($KeepFiles) { exit } else { Remove-Item -Path "$OutputDir\*" -Recurse -Force -ErrorAction SilentlyContinue } \ No newline at end of file From 43592ade612429284a3bd3f95de8794975e02e89 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 9 Jun 2019 19:42:03 +0100 Subject: [PATCH 05/21] Remove Install-Bootcamp.ps1 Redundant file, replaced by Get-BootCamp.ps1; commits kept for history. (see a3291851eef98ee247194b1ddd1126fe92ae012a) --- Install-Bootcamp.ps1 | 107 ------------------------------------------- 1 file changed, 107 deletions(-) delete mode 100644 Install-Bootcamp.ps1 diff --git a/Install-Bootcamp.ps1 b/Install-Bootcamp.ps1 deleted file mode 100644 index 0f0e0a2..0000000 --- a/Install-Bootcamp.ps1 +++ /dev/null @@ -1,107 +0,0 @@ -# Rewrite from Python to PowerShell - Requires PowerShell v3 or greater -# To do: Add more output, add in functions for params like install, product id, etc. -[CmdletBinding()] -Param( - [string]$Model = (Get-WmiObject -Class Win32_ComputerSystem | Select -ExpandProperty Model), - [switch]$Install = $false, - [string]$OutputDir = "$env:TEMP", - [switch]$KeepFiles = $false, - [string]$ProductId, - [string]$PlistPath = "brigadier.plist", - [string]$SUCATALOG_URL = 'http://swscan.apple.com/content/catalogs/others/index-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog', - # 7-Zip MSI (15.14) - [string]$SEVENZIP_URL = 'http://7-zip.org/a/7z1514-x64.msi' - # Newer 7zip supports extracting DMG. May add support back if older version of 7z is found in the future. - #[string]$DMG2IMG_URL = 'http://vu1tur.eu.org/tools/dmg2img-1.6.5-win32.zip' -) - -# Script processes too fast, so we have to add a pause to allow params to set up... -Start-Sleep -Seconds 1 - -# Set values from plist - I'm sure there's a better way to do this... -if (Test-Path $PlistPath) { - [xml]$Plist = Get-Content -Path $PlistPath - 0..($Plist.plist.dict.key.Length - 1) | ForEach-Object { - if ($Plist.plist.dict.key[$_] -eq "CatalogURL") { $SUCATALOG_URL = $plist.plist.dict.string[$_] } - if ($Plist.plist.dict.key[$_] -eq "7zipURL") { $SEVENZIP_URL = $plist.plist.dict.string[$_] } - if ($Plist.plist.dict.key[$_] -eq "Dmg2ImgURL") { $DMG2IMG_URL = $plist.plist.dict.string[$_] } - } -} - -# Check if 7zip is installed. If not, download and install it -$7z = "$env:ProgramFiles\7-Zip\7z.exe" -if (!(Test-Path $7z)) { - Start-BitsTransfer -Source $SEVENZIP_URL -Destination "$OutputDir\$($SEVENZIP_URL.Split('/')[-1])" -ErrorAction Stop -ProxyList $proxyserver -ProxyCredential $proxycreds - #Invoke-WebRequest -Uri $SEVENZIP_URL -OutFile "$OutputDir\$($SEVENZIP_URL.Split('/')[-1])" -ErrorAction Stop -Proxy $proxyserver -ProxyCredential $proxycreds - Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/i $OutputDir\$($SEVENZIP_URL.Split('/')[-1]) /qb- /norestart" -Wait -Verbose -} else { $7zInstalled = $true } - -# Download Dmg2Img -Start-BitsTransfer -Source $DMG2IMG_URL -Destination "$OutputDir\$($DMG2IMG_URL.Split('/')[-1])" -ErrorAction Stop -ProxyList $proxyserver -ProxyCredential $proxycreds -Invoke-Command -ScriptBlock { cmd /c "$7z" -o"$OutputDir" -y e "$OutputDir\$($DMG2IMG_URL.Split('/')[-1])" } - -# Read data from sucatalog -[xml]$sucatalog = Invoke-WebRequest -Uri $SUCATALOG_URL -Method Get -ErrorAction Stop -Proxy $proxyserver -ProxyCredential $proxycreds -# Find all Bootcamp ESD's -$sucatalog.plist.dict.dict.dict | Where-Object { $_.String -match "Bootcamp" } | ForEach-Object { - # Search dist files to find supported models, using regex match to find models in dist files - stole regex from brigadier's source - $SupportedModels = [regex]::Matches((Invoke-RestMethod -Uri ($_.dict | Where-Object { $_.Key -match "English" } | Select -ExpandProperty String)).InnerXml,"([a-zA-Z]{4,12}[1-9]{1,2}\,[1-6])") | Select -ExpandProperty Value - if ($SupportedModels -contains $Model) { - $version = [regex]::Match(($_.dict | Where-Object { $_.Key -match "English" } | Select -ExpandProperty String),"(\d{3}-\d{5})") | Select -ExpandProperty Value - Write-Output "Found supported ESD: $Version" - [array]$bootcamplist += $_ - } -} -if ($bootcamplist.Length -gt 1) { - Write-Output "Found more than 1 supported Bootcamp ESD. Selecting newest based on posted date" - $bootcamplist | ForEach-Object { - if ($_.date -gt $latestdate) { - $latestdate = $_.date - $download = $_.array.dict | Select -ExpandProperty String | Where-Object { $_ -match '.pkg' } - #$download = $_.string.Replace(".smd",".pkg") - URL matches the .smd but may not always? - } - } -} else { $download = $bootcamplist.array.dict | Select -ExpandProperty String | Where-Object { $_ -match '.pkg' }} - -# Download the BootCamp ESD -Start-BitsTransfer -Source $download -Destination "$OutputDir\BootCampESD.pkg" -ErrorAction Stop -ProxyList $proxyserver -ProxyCredential $proxycreds -if (Test-Path -Path "$OutputDir\BootCampESD.pkg") { - # Extract the bootcamp installer - Invoke-Command -ScriptBlock { - cmd /c $7z -o"$OutputDir" -y e "$OutputDir\BootCampESD.pkg" - cmd /c $7z -o"$OutputDir" -y e "$OutputDir\Payload~" - If (!(Test-Path -Path "$OutputDir\BootCamp")) { New-Item -Path "$OutputDir\BootCamp" -ItemType Directory -Force } - cmd /c $7z -o"$env:SystemDrive" -y x "$OutputDir\WindowsSupport.dmg" - } -} else { Write-Output "BootCampESD.pkg could not be found"; exit } - -# Convert the DMG to ISO -# Invoke-Command -ScriptBlock { cmd /c "$OutputDir\dmg2img.exe" -v "$OutputDir\WindowsSupport.dmg" "$OutputDir\WindowsSupport.iso" } - -# Extract the ISO so we can run the installer -# Invoke-Command -ScriptBlock { cmd /c $7z -o"$OutputDir\Bootcamp" -y x "$OutputDir\WindowsSupport.iso" } - -# Uninstall 7zip and remove installer -if ($7zInstalled -ne $true) { - Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/x $OutputDir\$($SEVENZIP_URL.Split('/')[-1]) /qb- /norestart" -Wait - Remove-Item -Path "$OutputDir\$($SEVENZIP_URL.Split('/')[-1])" -Force -} - -# Must install Realtek Audio driver before installing Bootcamp. This step simply moves it, but you should install it first, reboot, then run this script -Get-ChildItem -Path "$env:SystemDrive\" | Where-Object { $_.Name -like "BootCamp" -or $_.Name -eq "Drivers" } | Select -ExpandProperty FullName | ForEach-Object { Get-ChildItem -Path $_ -Recurse -Include RealtekSetup.exe | Select -ExpandProperty FullName } | Move-Item -Destination $OutputDir - -# Find Bootcamp.msi and install correct one -$BootCampMSI = Get-ChildItem -Path "$env:SystemDrive\" | Where-Object { $_.Name -like "BootCamp" -or $_.Name -eq "Drivers" } | Select -ExpandProperty FullName | ForEach-Object { Get-ChildItem -Path $_ -Recurse -Include BootCamp*.msi | Select -ExpandProperty FullName } -if ($BootCampMSI.Length -gt 1) { - # Check OS architecture and install correct version - if ((Get-WmiObject -Class Win32_OperatingSystem | Select -ExpandProperty OSArchitecture) -eq "64-bit") { - $BootCampMSI = $BootCampMSI | Where-Object { $_ -match "64" } - } else { - $BootCampMSI = $BootCampMSI | Where-Object { $_ -notmatch "64" } - } -} -# Need to test if Realtek Audio can be installed without reboot and then install Bootcamp or if reboot is required first. -Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/i $BootCampMSI /qb- /norestart /log $env:SystemRoot\BootcampInstall.log" -Wait - -# Clean up -if ($KeepFiles -eq $false) { Remove-Item -Path "$OutputDir\*" -Recurse -Force -ErrorAction SilentlyContinue } \ No newline at end of file From e0d3a79951057a424f99a7369c91722f8754206a Mon Sep 17 00:00:00 2001 From: nathanmcnulty Date: Sat, 15 Jun 2019 14:21:24 +0100 Subject: [PATCH 06/21] Merge in changes from Get-Bootcamp script in #24 A version of the script is in a comment on #24, merge in differences. https://github.com/timsutton/brigadier/issues/24#issuecomment-295011284 --- Get-Bootcamp.ps1 | 62 ++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/Get-Bootcamp.ps1 b/Get-Bootcamp.ps1 index 899c970..b39da22 100644 --- a/Get-Bootcamp.ps1 +++ b/Get-Bootcamp.ps1 @@ -1,6 +1,3 @@ -# Rewrite from Python to PowerShell - Requires PowerShell v3 or greater -# 16/4/7 - Removd DMG2IMG components, removed plist as we can take arguments instead, preparing for ability to pass ProductID as an array -# To do: Add logging, include fallback in case BITS is not installed: https://blog.jourdant.me/3-ways-to-download-files-with-powershell/ [CmdletBinding()] Param( [string]$Model = (Get-WmiObject -Class Win32_ComputerSystem).Model, @@ -8,19 +5,21 @@ Param( [string]$OutputDir = "$env:TEMP", [switch]$KeepFiles, [array]$ProductId, - [string]$Mst, [string]$SUCATALOG_URL = 'http://swscan.apple.com/content/catalogs/others/index-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog', - [string]$SEVENZIP_URL = 'http://7-zip.org/a/7z1514-x64.msi' + [string]$SEVENZIP_URL = 'http://www.7-zip.org/a/7z1604-x64.msi' ) +# Disable Invoke-WebRequest progress bar to speed up download due to bug +$ProgressPreference = "SilentlyContinue" + # Create Output Directory if it does not exist if (!(Test-Path $OutputDir)) { New-Item -Path $OutputDir -ItemType Directory -Force } -# Check if 7zip 15.14 is installed. If not, download and install it. +# Check if at least 7zip 15.14 is installed. If not, download and install it. $7z = "$env:ProgramFiles\7-Zip\7z.exe" -if (Test-Path $7z) { $7zInstalled = $true; [decimal]$7zVersion = (Get-ItemProperty $7z).VersionInfo.FileVersion } -if ($7zVersion -lt 15.14) { - Start-BitsTransfer -Source $SEVENZIP_URL -Destination "$OutputDir\$($SEVENZIP_URL.Split('/')[-1])" -ErrorAction Stop +if (Test-Path $7z) { $7zInstalled = $true } +if ([decimal](Get-ItemProperty $7z).VersionInfo.FileVersion -lt 15.14) { + Invoke-WebRequest -Uri $SEVENZIP_URL -OutFile "$OutputDir\$($SEVENZIP_URL.Split('/')[-1])" -ErrorAction Stop Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/i $OutputDir\$($SEVENZIP_URL.Split('/')[-1]) /qb- /norestart" -Wait -Verbose } @@ -36,7 +35,7 @@ $sucatalog.plist.dict.dict.dict | Where-Object { $_.String -match "Bootcamp" } | } } if ($bootcamplist.Length -gt 1) { - Write-Output "Found more than 1 supported Bootcamp ESD. Selecting newest based on posted date" + Write-warning "Found more than 1 supported Bootcamp ESD. Selecting newest based on posted date which may not always be correct" $bootcamplist | ForEach-Object { if ($_.date -gt $latestdate) { $latestdate = $_.date @@ -50,37 +49,34 @@ Start-BitsTransfer -Source $download -Destination "$OutputDir\BootCampESD.pkg" - if (Test-Path -Path "$OutputDir\BootCampESD.pkg") { # Extract the bootcamp installer Invoke-Command -ScriptBlock { - cmd /c $7z -o"$OutputDir" -y e "$OutputDir\BootCampESD.pkg" - cmd /c $7z -o"$OutputDir" -y e "$OutputDir\Payload~" + & $7z -o"$OutputDir" -y e "$OutputDir\BootCampESD.pkg" + & $7z -o"$OutputDir" -y e "$OutputDir\Payload~" # If just downloading, put the extracted installers on the desktop - if ($Install) { cmd /c $7z -o"$OutputDir" -y x "$OutputDir\WindowsSupport.dmg" } else { if ($OutputDir -eq "$env:TEMP") { cmd /c $7z -o"$env:USERPROFILE\Desktop\$version" -y x "$OutputDir\WindowsSupport.dmg" }} + if ($Install) { + & $7z -o"$OutputDir" -y x "$OutputDir\WindowsSupport.dmg" + } + else { + if ($OutputDir -eq "$env:TEMP") { & $7z -o"$env:USERPROFILE\Desktop\$version" -y x "$OutputDir\WindowsSupport.dmg" } else { & $7z -o"$OutputDir" -y x "$OutputDir\WindowsSupport.dmg" } + } } -} else { Write-Output "BootCampESD.pkg could not be found"; exit } + } +else { Write-Warning "BootCampESD.pkg could not be found"; exit } # Uninstall 7zip if we installed it if ($7zInstalled -ne $true) { Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/x $OutputDir\$($SEVENZIP_URL.Split('/')[-1]) /qb- /norestart" -Wait } -# Testing for iMac14,1 issue with Realtek Audio driver hanging the installation -#"Bootcamp","Drivers" | ForEach-Object { if (Test-Path -Path "$OutputDir\$_") { (Get-ChildItem -Path "$OutputDir\$_" -Recurse -Include RealtekSetup.exe -ErrorAction SilentlyContinue).FullName }} | Move-Item -Destination $OutputDir - -# Find Bootcamp.msi and install matching based on OS architecture -[array]$BootCampMSI = "Bootcamp","Drivers" | ForEach-Object { if (Test-Path -Path "$OutputDir\$_") { (Get-ChildItem -Path "$OutputDir\$_" -Recurse -Include BootCamp*.msi).FullName }} -if ($BootCampMSI.Length -gt 1) { - # Check OS architecture and install correct version - if ((Get-WmiObject -Class Win32_OperatingSystem).OSArchitecture -eq "64-bit") { - $BootCampMSI = $BootCampMSI | Where-Object { $_ -match "64" } - } else { - $BootCampMSI = $BootCampMSI | Where-Object { $_ -notmatch "64" } - } -} - # Install Bootcamp and use MST if specified (I uploaded one that I had to use to fix the latest ESD on an iMac14,1) if ($Install) { - if ($mst -ne "") { - Copy-Item -Path $Mst -Destination $($BootCampMSI.TrimEnd("\BootCamp.msi")) - Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/i $BootCampMSI TRANSFORMS=$($Mst.Split('\')[-1]) /qb- /norestart /l*v $env:SystemDrive\BootcampInstall.log" -Verbose -Wait - } else { Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/i $BootCampMSI /qb- /norestart /l*v $env:SystemDrive\BootcampInstall.log" -Verbose -Wait } -} else { exit } + # Install Bootcamp + $scaction = New-ScheduledTaskAction -Execute "msiexec.exe" -Argument "/i $OutputDir\Bootcamp\Drivers\Apple\BootCamp.msi /qn /norestart" + $sctrigger = New-ScheduledTaskTrigger -At ((Get-Date).AddSeconds(15)) -Once + $scprincipal = New-ScheduledTaskPrincipal "SYSTEM" -RunLevel Highest + $scsettings = New-ScheduledTaskSettingsSet + $sctask = New-ScheduledTask -Action $scaction -Principal $scprincipal -Trigger $sctrigger -Settings $scsettings + Register-ScheduledTask "Install Bootcamp" -InputObject $sctask -User "SYSTEM" + do { Write-Output "Sleeping 20 seconds"; Start-Sleep -Seconds 20 } while (Get-Process -Name "msiexec" -ErrorAction SilentlyContinue) +} +else { exit } # Clean up if ($KeepFiles) { exit } else { Remove-Item -Path "$OutputDir\*" -Recurse -Force -ErrorAction SilentlyContinue } \ No newline at end of file From d2e5319176a446fe531a996c4109301e677641e3 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 15 Jun 2019 15:53:03 +0100 Subject: [PATCH 07/21] Simplify selecting which ESD to download No need to pick newest manually, just sort the list by date. Also builds a hash table of package's properties rather than just looking for a string ending in '.pkg' --- Get-Bootcamp.ps1 | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Get-Bootcamp.ps1 b/Get-Bootcamp.ps1 index b39da22..091f326 100644 --- a/Get-Bootcamp.ps1 +++ b/Get-Bootcamp.ps1 @@ -36,13 +36,11 @@ $sucatalog.plist.dict.dict.dict | Where-Object { $_.String -match "Bootcamp" } | } if ($bootcamplist.Length -gt 1) { Write-warning "Found more than 1 supported Bootcamp ESD. Selecting newest based on posted date which may not always be correct" - $bootcamplist | ForEach-Object { - if ($_.date -gt $latestdate) { - $latestdate = $_.date - $download = $_.array.dict.string | Where-Object { $_ -match '.pkg' } - } - } -} else { $download = $bootcamplist.array.dict.string | Where-Object { $_ -match '.pkg' }} +} +$esd = $bootcamplist | Sort-Object -Property Date | Select-Object -Last 1 +# Build a hash table of the package's properties from the XML +$package = $esd.array.dict.selectnodes('key') | ForEach-Object {@{$($_.'#text') = $($_.nextsibling.'#text')}} +$download = $package.URL # Download the BootCamp ESD Start-BitsTransfer -Source $download -Destination "$OutputDir\BootCampESD.pkg" -ErrorAction Stop @@ -79,4 +77,4 @@ if ($Install) { else { exit } # Clean up -if ($KeepFiles) { exit } else { Remove-Item -Path "$OutputDir\*" -Recurse -Force -ErrorAction SilentlyContinue } \ No newline at end of file +if ($KeepFiles) { exit } else { Remove-Item -Path "$OutputDir\*" -Recurse -Force -ErrorAction SilentlyContinue } From 4fb8a3e54dc2eea00a9999a779e2c8b74601a71a Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 15 Jun 2019 17:24:54 +0100 Subject: [PATCH 08/21] Make ESD matching a bit easier to follow Also add ESD version to package info --- Get-Bootcamp.ps1 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Get-Bootcamp.ps1 b/Get-Bootcamp.ps1 index 091f326..7ad0883 100644 --- a/Get-Bootcamp.ps1 +++ b/Get-Bootcamp.ps1 @@ -27,9 +27,12 @@ if ([decimal](Get-ItemProperty $7z).VersionInfo.FileVersion -lt 15.14) { [xml]$sucatalog = Invoke-WebRequest -Uri $SUCATALOG_URL -Method Get -ErrorAction Stop $sucatalog.plist.dict.dict.dict | Where-Object { $_.String -match "Bootcamp" } | ForEach-Object { # Search dist files to find supported models, using regex match to find models in dist files - stole regex from brigadier's source - $SupportedModels = [regex]::Matches((Invoke-RestMethod -Uri ($_.dict | Where-Object { $_.Key -match "English" }).String).InnerXml,"([a-zA-Z]{4,12}[1-9]{1,2}\,[1-6])").Value + $modelRegex = "([a-zA-Z]{4,12}[1-9]{1,2}\,[1-6])" + $distURL = ($_.dict | Where-Object { $_.Key -match "English" }).String + $distXML = (Invoke-RestMethod -Uri $distURL).InnerXml + $SupportedModels = [regex]::Matches($distXML,$modelRegex).Value if ($SupportedModels -contains $Model) { - $version = [regex]::Match(($_.dict | Where-Object { $_.Key -match "English" }).String,"(\d{3}-\d{5})").Value + $version = [regex]::Match($distURL,"(\d{3}-\d{5})").Value Write-Output "Found supported ESD: $Version" [array]$bootcamplist += $_ } @@ -40,6 +43,7 @@ if ($bootcamplist.Length -gt 1) { $esd = $bootcamplist | Sort-Object -Property Date | Select-Object -Last 1 # Build a hash table of the package's properties from the XML $package = $esd.array.dict.selectnodes('key') | ForEach-Object {@{$($_.'#text') = $($_.nextsibling.'#text')}} +$package += @{'ESDVersion' = $Version} $download = $package.URL # Download the BootCamp ESD From a55697dadc250ba43423f4f25dc6e6bea58d9591 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 15 Jun 2019 17:31:41 +0100 Subject: [PATCH 09/21] Use System.Version for version comparisons --- Get-Bootcamp.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Get-Bootcamp.ps1 b/Get-Bootcamp.ps1 index 7ad0883..918d47a 100644 --- a/Get-Bootcamp.ps1 +++ b/Get-Bootcamp.ps1 @@ -18,7 +18,7 @@ if (!(Test-Path $OutputDir)) { New-Item -Path $OutputDir -ItemType Directory -Fo # Check if at least 7zip 15.14 is installed. If not, download and install it. $7z = "$env:ProgramFiles\7-Zip\7z.exe" if (Test-Path $7z) { $7zInstalled = $true } -if ([decimal](Get-ItemProperty $7z).VersionInfo.FileVersion -lt 15.14) { +if ([version](Get-ItemProperty $7z).VersionInfo.FileVersion -lt 15.14) { Invoke-WebRequest -Uri $SEVENZIP_URL -OutFile "$OutputDir\$($SEVENZIP_URL.Split('/')[-1])" -ErrorAction Stop Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/i $OutputDir\$($SEVENZIP_URL.Split('/')[-1]) /qb- /norestart" -Wait -Verbose } From 561ec98741b7986a3d0f224faf3e8b6eb241dfc8 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 15 Jun 2019 19:12:48 +0100 Subject: [PATCH 10/21] Add progress messages during usage With $ProgressPreference = "SilentlyContinue" set, the script often looks like it's hung. Inform the user what's going on more often, particularly for slow actions. --- Get-Bootcamp.ps1 | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Get-Bootcamp.ps1 b/Get-Bootcamp.ps1 index 918d47a..9828d04 100644 --- a/Get-Bootcamp.ps1 +++ b/Get-Bootcamp.ps1 @@ -19,11 +19,15 @@ if (!(Test-Path $OutputDir)) { New-Item -Path $OutputDir -ItemType Directory -Fo $7z = "$env:ProgramFiles\7-Zip\7z.exe" if (Test-Path $7z) { $7zInstalled = $true } if ([version](Get-ItemProperty $7z).VersionInfo.FileVersion -lt 15.14) { + Write-Host "7-Zip not installed, will install and remove." Invoke-WebRequest -Uri $SEVENZIP_URL -OutFile "$OutputDir\$($SEVENZIP_URL.Split('/')[-1])" -ErrorAction Stop Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/i $OutputDir\$($SEVENZIP_URL.Split('/')[-1]) /qb- /norestart" -Wait -Verbose } +Write-Host "Using model: $Model" + # Read data from sucatalog and find all Bootcamp ESD's +Write-Host "Downloading software update catalog..." [xml]$sucatalog = Invoke-WebRequest -Uri $SUCATALOG_URL -Method Get -ErrorAction Stop $sucatalog.plist.dict.dict.dict | Where-Object { $_.String -match "Bootcamp" } | ForEach-Object { # Search dist files to find supported models, using regex match to find models in dist files - stole regex from brigadier's source @@ -32,24 +36,27 @@ $sucatalog.plist.dict.dict.dict | Where-Object { $_.String -match "Bootcamp" } | $distXML = (Invoke-RestMethod -Uri $distURL).InnerXml $SupportedModels = [regex]::Matches($distXML,$modelRegex).Value if ($SupportedModels -contains $Model) { - $version = [regex]::Match($distURL,"(\d{3}-\d{5})").Value - Write-Output "Found supported ESD: $Version" + $version = [regex]::Match($distURL,"(\d{3}-\d{4,5})").Value + Write-Output "Found supported ESD: $Version, posted $($_.Date)" [array]$bootcamplist += $_ } } if ($bootcamplist.Length -gt 1) { - Write-warning "Found more than 1 supported Bootcamp ESD. Selecting newest based on posted date which may not always be correct" + Write-Host "Found more than 1 supported Bootcamp ESD. Selecting newest based on posted date which may not always be correct" } $esd = $bootcamplist | Sort-Object -Property Date | Select-Object -Last 1 # Build a hash table of the package's properties from the XML $package = $esd.array.dict.selectnodes('key') | ForEach-Object {@{$($_.'#text') = $($_.nextsibling.'#text')}} $package += @{'ESDVersion' = $Version} -$download = $package.URL +Write-Host "Selected $($package.ESDVersion) as it's the most recently posted." # Download the BootCamp ESD -Start-BitsTransfer -Source $download -Destination "$OutputDir\BootCampESD.pkg" -ErrorAction Stop +Write-Host "Starting download from $($package.URL)" +Start-BitsTransfer -Source $package.URL -Destination "$OutputDir\BootCampESD.pkg" -ErrorAction Stop +Write-Host "Download complete" if (Test-Path -Path "$OutputDir\BootCampESD.pkg") { # Extract the bootcamp installer + Write-Host "Extracting..." Invoke-Command -ScriptBlock { & $7z -o"$OutputDir" -y e "$OutputDir\BootCampESD.pkg" & $7z -o"$OutputDir" -y e "$OutputDir\Payload~" From 344de3b871284f030b4eb735475e256f77e11309 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 16 Jun 2019 14:42:41 +0100 Subject: [PATCH 11/21] Handle not finding any matching ESDs --- Get-Bootcamp.ps1 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Get-Bootcamp.ps1 b/Get-Bootcamp.ps1 index 9828d04..c2040e5 100644 --- a/Get-Bootcamp.ps1 +++ b/Get-Bootcamp.ps1 @@ -41,9 +41,14 @@ $sucatalog.plist.dict.dict.dict | Where-Object { $_.String -match "Bootcamp" } | [array]$bootcamplist += $_ } } + if ($bootcamplist.Length -gt 1) { Write-Host "Found more than 1 supported Bootcamp ESD. Selecting newest based on posted date which may not always be correct" +} elseif ($bootcamplist.length -eq 0) { + Write-Warning "Couldn't find a Boot Camp ESD for the model $Model in the given software update catalog." + exit 1 } + $esd = $bootcamplist | Sort-Object -Property Date | Select-Object -Last 1 # Build a hash table of the package's properties from the XML $package = $esd.array.dict.selectnodes('key') | ForEach-Object {@{$($_.'#text') = $($_.nextsibling.'#text')}} From 51c0e0c3011d0bad1e1341198aeeaeb2964f92e4 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 16 Jun 2019 15:30:53 +0100 Subject: [PATCH 12/21] Change save locations to act more like Python Script will now download and unpack in $env:Temp and output final result to $pwd\BootCamp-$Version like Python Script Python uses a randomly generated directory in temp, but use a predictable one to allow us to check if .pkg is already downloaded and skip if so. --- Get-Bootcamp.ps1 | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/Get-Bootcamp.ps1 b/Get-Bootcamp.ps1 index c2040e5..4a77fce 100644 --- a/Get-Bootcamp.ps1 +++ b/Get-Bootcamp.ps1 @@ -2,7 +2,7 @@ Param( [string]$Model = (Get-WmiObject -Class Win32_ComputerSystem).Model, [switch]$Install, - [string]$OutputDir = "$env:TEMP", + [string]$OutputDir = $PWD, [switch]$KeepFiles, [array]$ProductId, [string]$SUCATALOG_URL = 'http://swscan.apple.com/content/catalogs/others/index-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog', @@ -20,7 +20,7 @@ $7z = "$env:ProgramFiles\7-Zip\7z.exe" if (Test-Path $7z) { $7zInstalled = $true } if ([version](Get-ItemProperty $7z).VersionInfo.FileVersion -lt 15.14) { Write-Host "7-Zip not installed, will install and remove." - Invoke-WebRequest -Uri $SEVENZIP_URL -OutFile "$OutputDir\$($SEVENZIP_URL.Split('/')[-1])" -ErrorAction Stop + Invoke-WebRequest -Uri $SEVENZIP_URL -OutFile "$env:Temp\$($SEVENZIP_URL.Split('/')[-1])" -ErrorAction Stop Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/i $OutputDir\$($SEVENZIP_URL.Split('/')[-1]) /qb- /norestart" -Wait -Verbose } @@ -56,41 +56,49 @@ $package += @{'ESDVersion' = $Version} Write-Host "Selected $($package.ESDVersion) as it's the most recently posted." # Download the BootCamp ESD +$landingDir = Join-Path $OutputDir "BootCamp-$($package.ESDVersion)" +$workingDir = Join-Path $env:Temp "BootCamp-unpack-$($package.ESDVersion)" +$packagePath = Join-Path $workingDir 'BootCampESD.pkg' +$payloadPath = Join-Path $workingDir 'Payload~' +$dmgPath = Join-Path $workingDir 'WindowsSupport.dmg' + +if (-not (Test-Path -PathType Container $landingDir)) {mkdir $landingDir > $null} +if (-not (Test-Path -PathType Container $workingDir)) {mkdir $workingDir > $null} + Write-Host "Starting download from $($package.URL)" -Start-BitsTransfer -Source $package.URL -Destination "$OutputDir\BootCampESD.pkg" -ErrorAction Stop +Start-BitsTransfer -Source $package.URL -Destination "$packagePath" -ErrorAction Stop Write-Host "Download complete" -if (Test-Path -Path "$OutputDir\BootCampESD.pkg") { +if (Test-Path -Path "$packagePath") { # Extract the bootcamp installer Write-Host "Extracting..." Invoke-Command -ScriptBlock { - & $7z -o"$OutputDir" -y e "$OutputDir\BootCampESD.pkg" - & $7z -o"$OutputDir" -y e "$OutputDir\Payload~" - # If just downloading, put the extracted installers on the desktop - if ($Install) { - & $7z -o"$OutputDir" -y x "$OutputDir\WindowsSupport.dmg" - } - else { - if ($OutputDir -eq "$env:TEMP") { & $7z -o"$env:USERPROFILE\Desktop\$version" -y x "$OutputDir\WindowsSupport.dmg" } else { & $7z -o"$OutputDir" -y x "$OutputDir\WindowsSupport.dmg" } - } - } + & $7z -o"$workingDir" -y e "$packagePath" + & $7z -o"$workingDir" -y e "$payloadPath" + & $7z -o"$landingDir" -y x "$dmgPath" + # # If just downloading, put the extracted installers on the desktop + # if ($Install) { + # & $7z -o"$OutputDir" -y x "$dmgPath" + # } + # else { + # if ($OutputDir -eq "$env:TEMP") { & $7z -o"$env:USERPROFILE\Desktop\$version" -y x "$dmgPath" } else { & $7z -o"$OutputDir" -y x "$dmgPath" } + # } } +} else { Write-Warning "BootCampESD.pkg could not be found"; exit } # Uninstall 7zip if we installed it -if ($7zInstalled -ne $true) { Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/x $OutputDir\$($SEVENZIP_URL.Split('/')[-1]) /qb- /norestart" -Wait } +if ($7zInstalled -ne $true) { Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/x $env:Temp\$($SEVENZIP_URL.Split('/')[-1]) /qb- /norestart" -Wait } # Install Bootcamp and use MST if specified (I uploaded one that I had to use to fix the latest ESD on an iMac14,1) if ($Install) { # Install Bootcamp - $scaction = New-ScheduledTaskAction -Execute "msiexec.exe" -Argument "/i $OutputDir\Bootcamp\Drivers\Apple\BootCamp.msi /qn /norestart" + $scaction = New-ScheduledTaskAction -Execute "msiexec.exe" -Argument "/i $landingDir\Bootcamp\Drivers\Apple\BootCamp.msi /qn /norestart" $sctrigger = New-ScheduledTaskTrigger -At ((Get-Date).AddSeconds(15)) -Once $scprincipal = New-ScheduledTaskPrincipal "SYSTEM" -RunLevel Highest $scsettings = New-ScheduledTaskSettingsSet $sctask = New-ScheduledTask -Action $scaction -Principal $scprincipal -Trigger $sctrigger -Settings $scsettings Register-ScheduledTask "Install Bootcamp" -InputObject $sctask -User "SYSTEM" do { Write-Output "Sleeping 20 seconds"; Start-Sleep -Seconds 20 } while (Get-Process -Name "msiexec" -ErrorAction SilentlyContinue) + if (-not $KeepFiles) { Remove-Item -Path "$landingDir" -Recurse -Force -ErrorAction SilentlyContinue } } else { exit } - -# Clean up -if ($KeepFiles) { exit } else { Remove-Item -Path "$OutputDir\*" -Recurse -Force -ErrorAction SilentlyContinue } From 5d0b7dff4e59d7976cf80cdd83c1999fa9281986 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 16 Jun 2019 15:33:49 +0100 Subject: [PATCH 13/21] Stop download if final destination exists Python automatically deletes it, but that seems unwise? Warn instead. --- Get-Bootcamp.ps1 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Get-Bootcamp.ps1 b/Get-Bootcamp.ps1 index 4a77fce..c073c1e 100644 --- a/Get-Bootcamp.ps1 +++ b/Get-Bootcamp.ps1 @@ -62,7 +62,11 @@ $packagePath = Join-Path $workingDir 'BootCampESD.pkg' $payloadPath = Join-Path $workingDir 'Payload~' $dmgPath = Join-Path $workingDir 'WindowsSupport.dmg' -if (-not (Test-Path -PathType Container $landingDir)) {mkdir $landingDir > $null} +if (Test-Path -PathType Container $landingDir) { + # Python just deletes the folder + Write-Warning "Final destination folder $landingDir already exists, please remove it to redownload." + exit 1 +} if (-not (Test-Path -PathType Container $workingDir)) {mkdir $workingDir > $null} Write-Host "Starting download from $($package.URL)" @@ -74,6 +78,7 @@ if (Test-Path -Path "$packagePath") { Invoke-Command -ScriptBlock { & $7z -o"$workingDir" -y e "$packagePath" & $7z -o"$workingDir" -y e "$payloadPath" + if (-not (Test-Path -PathType Container $landingDir)) {mkdir $landingDir > $null} & $7z -o"$landingDir" -y x "$dmgPath" # # If just downloading, put the extracted installers on the desktop # if ($Install) { From 3b240d6fab0d408d2f073e6e3224e208f30fad58 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 16 Jun 2019 15:55:25 +0100 Subject: [PATCH 14/21] Always clean up working directory --- Get-Bootcamp.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Get-Bootcamp.ps1 b/Get-Bootcamp.ps1 index c073c1e..658fd39 100644 --- a/Get-Bootcamp.ps1 +++ b/Get-Bootcamp.ps1 @@ -106,4 +106,6 @@ if ($Install) { do { Write-Output "Sleeping 20 seconds"; Start-Sleep -Seconds 20 } while (Get-Process -Name "msiexec" -ErrorAction SilentlyContinue) if (-not $KeepFiles) { Remove-Item -Path "$landingDir" -Recurse -Force -ErrorAction SilentlyContinue } } -else { exit } + +Write-Host "Cleaning up working directory..." +Remove-Item -Path $workingDir -Recurse From 6d7020e8eade5ec37bdd5c1ef176c65ec348ba30 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 16 Jun 2019 15:56:04 +0100 Subject: [PATCH 15/21] Don't redownload package if already done Match file found against size from catalog, warn if it doesn't match. --- Get-Bootcamp.ps1 | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Get-Bootcamp.ps1 b/Get-Bootcamp.ps1 index 658fd39..a058115 100644 --- a/Get-Bootcamp.ps1 +++ b/Get-Bootcamp.ps1 @@ -55,7 +55,6 @@ $package = $esd.array.dict.selectnodes('key') | ForEach-Object {@{$($_.'#text') $package += @{'ESDVersion' = $Version} Write-Host "Selected $($package.ESDVersion) as it's the most recently posted." -# Download the BootCamp ESD $landingDir = Join-Path $OutputDir "BootCamp-$($package.ESDVersion)" $workingDir = Join-Path $env:Temp "BootCamp-unpack-$($package.ESDVersion)" $packagePath = Join-Path $workingDir 'BootCampESD.pkg' @@ -69,9 +68,20 @@ if (Test-Path -PathType Container $landingDir) { } if (-not (Test-Path -PathType Container $workingDir)) {mkdir $workingDir > $null} -Write-Host "Starting download from $($package.URL)" -Start-BitsTransfer -Source $package.URL -Destination "$packagePath" -ErrorAction Stop -Write-Host "Download complete" +# Download the BootCamp ESD if required +if (-not (Test-Path -PathType Leaf $packagePath)) { + Write-Host "Starting download from $($package.URL)" + Start-BitsTransfer -Source $package.URL -Destination "$packagePath" -ErrorAction Stop + Write-Host "Download complete" +} else { + # Not sure what's used for the digest, but we can match size. + if ((Get-Item $packagePath | Select -ExpandProperty Length) -eq $package.Size) { + Write-Host "$($package.ESDVersion) already exists at $packagePath, not redownloading." + } else { + Write-Warning "A file already exists at $packagePath but does not match $($package.URL), please remove it." + exit 1 + } +} if (Test-Path -Path "$packagePath") { # Extract the bootcamp installer Write-Host "Extracting..." From 9600a252669a79ca0f034fdd60aecaeeacb45449 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 16 Jun 2019 16:14:24 +0100 Subject: [PATCH 16/21] Simplify extraction --- Get-Bootcamp.ps1 | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/Get-Bootcamp.ps1 b/Get-Bootcamp.ps1 index a058115..3fb6b76 100644 --- a/Get-Bootcamp.ps1 +++ b/Get-Bootcamp.ps1 @@ -82,24 +82,13 @@ if (-not (Test-Path -PathType Leaf $packagePath)) { exit 1 } } -if (Test-Path -Path "$packagePath") { - # Extract the bootcamp installer - Write-Host "Extracting..." - Invoke-Command -ScriptBlock { - & $7z -o"$workingDir" -y e "$packagePath" - & $7z -o"$workingDir" -y e "$payloadPath" - if (-not (Test-Path -PathType Container $landingDir)) {mkdir $landingDir > $null} - & $7z -o"$landingDir" -y x "$dmgPath" - # # If just downloading, put the extracted installers on the desktop - # if ($Install) { - # & $7z -o"$OutputDir" -y x "$dmgPath" - # } - # else { - # if ($OutputDir -eq "$env:TEMP") { & $7z -o"$env:USERPROFILE\Desktop\$version" -y x "$dmgPath" } else { & $7z -o"$OutputDir" -y x "$dmgPath" } - # } - } -} -else { Write-Warning "BootCampESD.pkg could not be found"; exit } + +# Extract the bootcamp installer +Write-Host "Extracting..." +& $7z -o"$workingDir" -y e "$packagePath" +& $7z -o"$workingDir" -y e "$payloadPath" +if (-not (Test-Path -PathType Container $landingDir)) {mkdir $landingDir > $null} +& $7z -o"$landingDir" -y x "$dmgPath" # Uninstall 7zip if we installed it if ($7zInstalled -ne $true) { Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/x $env:Temp\$($SEVENZIP_URL.Split('/')[-1]) /qb- /norestart" -Wait } From 455550864cec8ea502e7eab89499acc149b5a30a Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 16 Jun 2019 16:17:37 +0100 Subject: [PATCH 17/21] Fix 7-Zip install code --- Get-Bootcamp.ps1 | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Get-Bootcamp.ps1 b/Get-Bootcamp.ps1 index 3fb6b76..a0e7a78 100644 --- a/Get-Bootcamp.ps1 +++ b/Get-Bootcamp.ps1 @@ -17,11 +17,12 @@ if (!(Test-Path $OutputDir)) { New-Item -Path $OutputDir -ItemType Directory -Fo # Check if at least 7zip 15.14 is installed. If not, download and install it. $7z = "$env:ProgramFiles\7-Zip\7z.exe" +$7zDownload = Join-Path $env:Temp $SEVENZIP_URL.Split('/')[-1] if (Test-Path $7z) { $7zInstalled = $true } if ([version](Get-ItemProperty $7z).VersionInfo.FileVersion -lt 15.14) { Write-Host "7-Zip not installed, will install and remove." - Invoke-WebRequest -Uri $SEVENZIP_URL -OutFile "$env:Temp\$($SEVENZIP_URL.Split('/')[-1])" -ErrorAction Stop - Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/i $OutputDir\$($SEVENZIP_URL.Split('/')[-1]) /qb- /norestart" -Wait -Verbose + Invoke-WebRequest -Uri $SEVENZIP_URL -OutFile $7zDownload -ErrorAction Stop + Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/i $7zDownload /qb- /norestart" -Wait -Verbose } Write-Host "Using model: $Model" @@ -91,7 +92,11 @@ if (-not (Test-Path -PathType Container $landingDir)) {mkdir $landingDir > $null & $7z -o"$landingDir" -y x "$dmgPath" # Uninstall 7zip if we installed it -if ($7zInstalled -ne $true) { Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/x $env:Temp\$($SEVENZIP_URL.Split('/')[-1]) /qb- /norestart" -Wait } +if ($7zInstalled -ne $true) { + Write-Host "Removing 7-Zip..." + Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/x $7zDownload /qb- /norestart" -Wait + Remove-Item $7zDownload +} # Install Bootcamp and use MST if specified (I uploaded one that I had to use to fix the latest ESD on an iMac14,1) if ($Install) { From 0c813c9ab479c1f689e0c43c1dcd6d30a74a4de5 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 16 Jun 2019 16:50:18 +0100 Subject: [PATCH 18/21] Support ProductID filtering --- Get-Bootcamp.ps1 | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Get-Bootcamp.ps1 b/Get-Bootcamp.ps1 index a0e7a78..af4d7e0 100644 --- a/Get-Bootcamp.ps1 +++ b/Get-Bootcamp.ps1 @@ -29,6 +29,7 @@ Write-Host "Using model: $Model" # Read data from sucatalog and find all Bootcamp ESD's Write-Host "Downloading software update catalog..." +$bootcamplist = @() [xml]$sucatalog = Invoke-WebRequest -Uri $SUCATALOG_URL -Method Get -ErrorAction Stop $sucatalog.plist.dict.dict.dict | Where-Object { $_.String -match "Bootcamp" } | ForEach-Object { # Search dist files to find supported models, using regex match to find models in dist files - stole regex from brigadier's source @@ -37,12 +38,17 @@ $sucatalog.plist.dict.dict.dict | Where-Object { $_.String -match "Bootcamp" } | $distXML = (Invoke-RestMethod -Uri $distURL).InnerXml $SupportedModels = [regex]::Matches($distXML,$modelRegex).Value if ($SupportedModels -contains $Model) { - $version = [regex]::Match($distURL,"(\d{3}-\d{4,5})").Value - Write-Output "Found supported ESD: $Version, posted $($_.Date)" - [array]$bootcamplist += $_ + $_ | Add-Member -NotePropertyName Version -NotePropertyValue ([regex]::Match($distURL,"(\d{3}-\d{4,5})").Value) + Write-Output "Found supported ESD: $($_.Version), posted $($_.Date)" + $bootcamplist += $_ } } +if ($ProductId) { + Write-Host "ProductID specified, filtering Boot Camp ESD selection to match." + $bootcamplist = $bootcamplist | Where-Object {$_.Version -in $ProductId} +} + if ($bootcamplist.Length -gt 1) { Write-Host "Found more than 1 supported Bootcamp ESD. Selecting newest based on posted date which may not always be correct" } elseif ($bootcamplist.length -eq 0) { @@ -53,7 +59,7 @@ if ($bootcamplist.Length -gt 1) { $esd = $bootcamplist | Sort-Object -Property Date | Select-Object -Last 1 # Build a hash table of the package's properties from the XML $package = $esd.array.dict.selectnodes('key') | ForEach-Object {@{$($_.'#text') = $($_.nextsibling.'#text')}} -$package += @{'ESDVersion' = $Version} +$package += @{'ESDVersion' = $($esd.Version)} Write-Host "Selected $($package.ESDVersion) as it's the most recently posted." $landingDir = Join-Path $OutputDir "BootCamp-$($package.ESDVersion)" From 856c671207f9b3b9adca82d1992428ee29cdad22 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 16 Jun 2019 16:53:31 +0100 Subject: [PATCH 19/21] Use CIM cmdlet, trim whitespace --- Get-Bootcamp.ps1 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Get-Bootcamp.ps1 b/Get-Bootcamp.ps1 index af4d7e0..8eba166 100644 --- a/Get-Bootcamp.ps1 +++ b/Get-Bootcamp.ps1 @@ -1,6 +1,6 @@ [CmdletBinding()] Param( - [string]$Model = (Get-WmiObject -Class Win32_ComputerSystem).Model, + [string]$Model = (Get-CimInstance -Class Win32_ComputerSystem).Model, [switch]$Install, [string]$OutputDir = $PWD, [switch]$KeepFiles, @@ -37,10 +37,10 @@ $sucatalog.plist.dict.dict.dict | Where-Object { $_.String -match "Bootcamp" } | $distURL = ($_.dict | Where-Object { $_.Key -match "English" }).String $distXML = (Invoke-RestMethod -Uri $distURL).InnerXml $SupportedModels = [regex]::Matches($distXML,$modelRegex).Value - if ($SupportedModels -contains $Model) { + if ($SupportedModels -contains $Model) { $_ | Add-Member -NotePropertyName Version -NotePropertyValue ([regex]::Match($distURL,"(\d{3}-\d{4,5})").Value) Write-Output "Found supported ESD: $($_.Version), posted $($_.Date)" - $bootcamplist += $_ + $bootcamplist += $_ } } @@ -49,7 +49,7 @@ if ($ProductId) { $bootcamplist = $bootcamplist | Where-Object {$_.Version -in $ProductId} } -if ($bootcamplist.Length -gt 1) { +if ($bootcamplist.Length -gt 1) { Write-Host "Found more than 1 supported Bootcamp ESD. Selecting newest based on posted date which may not always be correct" } elseif ($bootcamplist.length -eq 0) { Write-Warning "Couldn't find a Boot Camp ESD for the model $Model in the given software update catalog." @@ -82,7 +82,7 @@ if (-not (Test-Path -PathType Leaf $packagePath)) { Write-Host "Download complete" } else { # Not sure what's used for the digest, but we can match size. - if ((Get-Item $packagePath | Select -ExpandProperty Length) -eq $package.Size) { + if ((Get-Item $packagePath | Select-Object -ExpandProperty Length) -eq $package.Size) { Write-Host "$($package.ESDVersion) already exists at $packagePath, not redownloading." } else { Write-Warning "A file already exists at $packagePath but does not match $($package.URL), please remove it." @@ -105,7 +105,7 @@ if ($7zInstalled -ne $true) { } # Install Bootcamp and use MST if specified (I uploaded one that I had to use to fix the latest ESD on an iMac14,1) -if ($Install) { +if ($Install) { # Install Bootcamp $scaction = New-ScheduledTaskAction -Execute "msiexec.exe" -Argument "/i $landingDir\Bootcamp\Drivers\Apple\BootCamp.msi /qn /norestart" $sctrigger = New-ScheduledTaskTrigger -At ((Get-Date).AddSeconds(15)) -Once From 3749683a805a54c96586b0f4fa37d2f79b4106ae Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 27 Jul 2019 14:31:25 +0100 Subject: [PATCH 20/21] Add Comment Based Help to PowerShell version Also updates the default download URLs for catalog/7zip to current. --- Get-Bootcamp.ps1 | 56 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/Get-Bootcamp.ps1 b/Get-Bootcamp.ps1 index 8eba166..f8cddc9 100644 --- a/Get-Bootcamp.ps1 +++ b/Get-Bootcamp.ps1 @@ -1,12 +1,56 @@ -[CmdletBinding()] +<# +.SYNOPSIS + Fetch and install Boot Camp ESDs with ease. +.DESCRIPTION + Download and unpack Boot Camp drivers and support software from Apple or your software update servers for specified Mac models. + + Can also install drivers and software if used with the "-Install" parameter. +.EXAMPLE + Get-Bootcamp.ps1 + Download and unpack the ESD that applies to current computer's model to the current working directory. + +.EXAMPLE + Get-Bootcamp.ps1 -Model 'MacBookAir5,2' + Download and unpack the ESD for a specific model to the current working directory. + +.EXAMPLE + Get-Bootcamp.ps1 -Install + Download, unpack, and install drivers for the current computer, deleting the drivers after installation. +.NOTES + This is a PowerShell port of timsutton's original Python script https://github.com/timsutton/brigadier/ +.LINK + https://github.com/timsutton/brigadier/ +#> + +[CmdletBinding(DefaultParameterSetName='Download')] Param( + # Model identifier to use, defaulting to the current machine's model. + [Parameter(ParameterSetName='Download')] [string]$Model = (Get-CimInstance -Class Win32_ComputerSystem).Model, + + # After the installer is downloaded, perform the install automatically. + [Parameter(ParameterSetName='Install', + Mandatory=$true + )] [switch]$Install, + + # Directory to extract installer files to. Defaults to the current directory. [string]$OutputDir = $PWD, + + # Keep the files that were downloaded/extracted after installing/ + [Parameter(ParameterSetName='Install')] [switch]$KeepFiles, + + # Specify an exact product ID to download. [array]$ProductId, - [string]$SUCATALOG_URL = 'http://swscan.apple.com/content/catalogs/others/index-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog', - [string]$SEVENZIP_URL = 'http://www.7-zip.org/a/7z1604-x64.msi' + + # URL for software update catalog to use, eg for an intenal Software Update Service or Reposado + [Alias('SUCATALOG_URL')] + [string]$CatalogURL = 'https://swscan.apple.com/content/catalogs/others/index-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog', + + # URL to download 7-Zip from, if not installed + [Alias('SEVENZIP_URL')] + [string]$SevenZipURL = 'https://www.7-zip.org/a/7z1900-x64.exe' ) # Disable Invoke-WebRequest progress bar to speed up download due to bug @@ -17,11 +61,11 @@ if (!(Test-Path $OutputDir)) { New-Item -Path $OutputDir -ItemType Directory -Fo # Check if at least 7zip 15.14 is installed. If not, download and install it. $7z = "$env:ProgramFiles\7-Zip\7z.exe" -$7zDownload = Join-Path $env:Temp $SEVENZIP_URL.Split('/')[-1] +$7zDownload = Join-Path $env:Temp $SevenZipURL.Split('/')[-1] if (Test-Path $7z) { $7zInstalled = $true } if ([version](Get-ItemProperty $7z).VersionInfo.FileVersion -lt 15.14) { Write-Host "7-Zip not installed, will install and remove." - Invoke-WebRequest -Uri $SEVENZIP_URL -OutFile $7zDownload -ErrorAction Stop + Invoke-WebRequest -Uri $SevenZipURL -OutFile $7zDownload -ErrorAction Stop Start-Process -FilePath $env:SystemRoot\System32\msiexec.exe -ArgumentList "/i $7zDownload /qb- /norestart" -Wait -Verbose } @@ -30,7 +74,7 @@ Write-Host "Using model: $Model" # Read data from sucatalog and find all Bootcamp ESD's Write-Host "Downloading software update catalog..." $bootcamplist = @() -[xml]$sucatalog = Invoke-WebRequest -Uri $SUCATALOG_URL -Method Get -ErrorAction Stop +[xml]$sucatalog = Invoke-WebRequest -Uri $CatalogURL -Method Get -ErrorAction Stop $sucatalog.plist.dict.dict.dict | Where-Object { $_.String -match "Bootcamp" } | ForEach-Object { # Search dist files to find supported models, using regex match to find models in dist files - stole regex from brigadier's source $modelRegex = "([a-zA-Z]{4,12}[1-9]{1,2}\,[1-6])" From fbe42d25d8dfa2e64f5a33a4eca4b1fb0c7ad013 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 27 Jul 2019 14:37:27 +0100 Subject: [PATCH 21/21] Rename PowerShell version to brigadier.ps1 Matches python/exe version, plus it doesn't just Get-Bootcamp. Also updates examples. --- Get-Bootcamp.ps1 => brigadier.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename Get-Bootcamp.ps1 => brigadier.ps1 (96%) diff --git a/Get-Bootcamp.ps1 b/brigadier.ps1 similarity index 96% rename from Get-Bootcamp.ps1 rename to brigadier.ps1 index f8cddc9..fbf765c 100644 --- a/Get-Bootcamp.ps1 +++ b/brigadier.ps1 @@ -6,15 +6,15 @@ Can also install drivers and software if used with the "-Install" parameter. .EXAMPLE - Get-Bootcamp.ps1 + brigadier.ps1 Download and unpack the ESD that applies to current computer's model to the current working directory. .EXAMPLE - Get-Bootcamp.ps1 -Model 'MacBookAir5,2' + brigadier.ps1 -Model 'MacBookAir5,2' Download and unpack the ESD for a specific model to the current working directory. .EXAMPLE - Get-Bootcamp.ps1 -Install + brigadier.ps1 -Install Download, unpack, and install drivers for the current computer, deleting the drivers after installation. .NOTES This is a PowerShell port of timsutton's original Python script https://github.com/timsutton/brigadier/