Public/OSDCloud/Save-OSDCloud.usb.ps1
<#
.SYNOPSIS Saves OSDCloud to an NTFS Partition on a USB Drive .DESCRIPTION Saves OSDCloud to an NTFS Partition on a USB Drive .PARAMETER OSEdition Edition of the Windows installation .PARAMETER OSCulture Culture of the Windows installation .LINK https://osdcloud.osdeploy.com/ .NOTES 21.3.13 Initial Release #> function Save-OSDCloud.usb { [CmdletBinding()] param ( [ValidateSet('Windows 10','Windows 11')] [string]$OSVersion = 'Windows 10', [ValidateSet('21H2','21H1','20H2','2004','1909','1903','1809')] [Alias('Build')] [string]$OSBuild, [ValidateSet('Home','Home N','Home Single Language','Education','Education N','Enterprise','Enterprise N','Pro','Pro N')] [Alias('Edition')] [string]$OSEdition, [ValidateSet ( 'ar-sa','bg-bg','cs-cz','da-dk','de-de','el-gr', 'en-gb','en-us','es-es','es-mx','et-ee','fi-fi', 'fr-ca','fr-fr','he-il','hr-hr','hu-hu','it-it', 'ja-jp','ko-kr','lt-lt','lv-lv','nb-no','nl-nl', 'pl-pl','pt-br','pt-pt','ro-ro','ru-ru','sk-sk', 'sl-si','sr-latn-rs','sv-se','th-th','tr-tr', 'uk-ua','zh-cn','zh-tw' )] [Alias('Culture','OSCulture')] [string]$OSLanguage, [string]$Manufacturer = (Get-MyComputerManufacturer -Brief), [string]$Product = (Get-MyComputerProduct) ) #================================================= # Start the Clock #================================================= $Global:OSDCloudStartTime = Get-Date #================================================= # Global Variables #================================================= $Global:OSDCloudOSVersion = $OSVersion $Global:OSDCloudOSEdition = $OSEdition $Global:OSDCloudOSCulture = $OSCulture #================================================= # Block #================================================= Block-PowerShellVersionLt5 Block-NoCurl #================================================= # Header #================================================= Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "$((Get-Date).ToString('yyyy-MM-dd-HHmmss')) $($MyInvocation.MyCommand.Name)" -NoNewline Write-Host -ForegroundColor Cyan " | Manufacturer: $Manufacturer | Product: $Product" Write-Host -ForegroundColor Cyan "OSDCloud content can be saved to an 8GB+ NTFS USB Volume" Write-Host -ForegroundColor White "Windows 10 will require about 4GB, and DriverPacks up to 2GB" #================================================= # Get-Volume.usb #================================================= $GetUSBVolume = Get-Volume.usb | Where-Object {$_.FileSystem -eq 'NTFS'} | Where-Object {$_.SizeGB -ge 8} | Sort-Object DriveLetter -Descending if (-NOT ($GetUSBVolume)) { Write-Warning "Unfortunately, I don't see any USB Volumes that will work" Write-Warning "OSDCloud Failed!" Write-Host -ForegroundColor DarkGray "================================================" Break } Write-Warning "USB Free Space is not verified before downloading yet, so this is on you!" Write-Host -ForegroundColor DarkGray "================================================" if ($GetUSBVolume) { #$GetUSBVolume | Select-Object -Property DriveLetter, FileSystemLabel, SizeGB, SizeRemainingMB, DriveType | Format-Table $SelectUSBVolume = Select-Volume.usb -MinimumSizeGB 8 -FileSystem 'NTFS' $Global:OSDCloudOfflineFullName = "$($SelectUSBVolume.DriveLetter):\OSDCloud" Write-Host -ForegroundColor White "OSDCloud content will be saved to $OSDCloudOfflineFullName" } else { Write-Warning "Save-OSDCloud.usb Requirements:" Write-Warning "8 GB Minimum" Write-Warning "NTFS File System" Break } #================================================= # Autopilot Profiles #================================================= Write-Host -ForegroundColor DarkGray "================================================" Write-Host -ForegroundColor Cyan "Autopilot Profiles" if (-NOT (Test-Path "$OSDCloudOfflineFullName\Config\AutopilotJSON")) { New-Item -Path "$OSDCloudOfflineFullName\Config\AutopilotJSON" -ItemType Directory -Force | Out-Null Write-Host "Autopilot Profiles can be saved to $OSDCloudOfflineFullName\Config\AutopilotJSON" } $FindOSDCloudFile = @() [array]$FindOSDCloudFile = Find-OSDCloudFile -Name $Global:OSDCloudAutopilotJsonName -Path '\OSDCloud\Autopilot\Profiles\' | Sort-Object FullName [array]$FindOSDCloudFile += Find-OSDCloudFile -Name $Global:OSDCloudAutopilotJsonName -Path '\OSDCloud\Config\AutopilotJSON\' | Sort-Object FullName $FindOSDCloudFile = $FindOSDCloudFile | Where-Object {$_.FullName -notlike "C*"} if ($FindOSDCloudFile) { foreach ($Item in $FindOSDCloudFile) { Write-Host -ForegroundColor White "$($Item.FullName)" } } else { Write-Warning "No Autopilot Profiles were found in any <PSDrive>:\OSDCloud\Config\AutopilotJSON" Write-Warning "Autopilot Profiles must be located in a $OSDCloudOfflineFullName\Config\AutopilotJSON direcory" } #================================================= # OSBuild #================================================= Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "$OSVersion OSBuild " -NoNewline if ($OSVersion -eq 'Windows 11') { $OSBuild = '21H2' } if ($OSBuild) { Write-Host -ForegroundColor Green $OSBuild } else { Write-Host -ForegroundColor Cyan "Menu" $OSBuildNames = @('21H2','21H1','20H2','2004','1909','1903','1809') $i = $null $OSBuildMenu = foreach ($Item in $OSBuildNames) { $i++ $ObjectProperties = @{ Selection = $i Name = $Item } New-Object -TypeName PSObject -Property $ObjectProperties } $OSBuildMenu | Select-Object -Property Selection, Name | Format-Table | Out-Host do { $SelectReadHost = Read-Host -Prompt "Enter a Selection for the $OSVersion OSBuild" } until (((($SelectReadHost -ge 0) -and ($SelectReadHost -in $OSBuildMenu.Selection)))) $OSBuild = $OSBuildMenu | Where-Object {$_.Selection -eq $SelectReadHost} | Select-Object -ExpandProperty Name Write-Host -ForegroundColor Cyan "OSBuild: " -NoNewline Write-Host -ForegroundColor Green "$OSBuild" } #================================================= # OSEdition #================================================= Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "$OSVersion OSEdition " -NoNewline if ($PSBoundParameters.ContainsKey('OSEdition')) { Write-Host -ForegroundColor Green $OSEdition } else { Write-Host -ForegroundColor Cyan "Menu" $OSEditionNames = @('Home','Home N','Home Single Language','Education','Education N','Enterprise','Enterprise N','Pro','Pro N') $i = $null $OSEditionMenu = foreach ($Item in $OSEditionNames) { $i++ $ObjectProperties = @{ Selection = $i Name = $Item } New-Object -TypeName PSObject -Property $ObjectProperties } $OSEditionMenu | Select-Object -Property Selection, Name | Format-Table | Out-Host do { $SelectReadHost = Read-Host -Prompt "Enter a Selection for the $OSVersion OSEdition" } until (((($SelectReadHost -ge 0) -and ($SelectReadHost -in $OSEditionMenu.Selection)))) $OSEdition = $OSEditionMenu | Where-Object {$_.Selection -eq $SelectReadHost} | Select-Object -ExpandProperty Name Write-Host -ForegroundColor Cyan "OSEdition: " -NoNewline Write-Host -ForegroundColor Green "$OSEdition" } #================================================= # OSEditionId and OSLicense #================================================= if ($OSEdition -eq 'Home') { $OSEditionId = 'Core' $OSLicense = 'Retail' $ImageIndex = 4 } if ($OSEdition -eq 'Home N') { $OSEditionId = 'CoreN' $OSLicense = 'Retail' $ImageIndex = 5 } if ($OSEdition -eq 'Home Single Language') { $OSEditionId = 'CoreSingleLanguage' $OSLicense = 'Retail' $ImageIndex = 6 } if ($OSEdition -eq 'Enterprise') { $OSEditionId = 'Enterprise' $OSLicense = 'Volume' $ImageIndex = 6 } if ($OSEdition -eq 'Enterprise N') { $OSEditionId = 'EnterpriseN' $OSLicense = 'Volume' $ImageIndex = 7 } #================================================= # OSLicense #================================================= Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "$OSVersion OSLicense " -NoNewline if ($OSLicense) { Write-Host -ForegroundColor Green $OSLicense } else { Write-Host -ForegroundColor Cyan "Menu" $OSLicenseNames = @('Retail Windows Consumer Editions','Volume Windows Business Editions') $i = $null $OSLicenseMenu = foreach ($Item in $OSLicenseNames) { $i++ $ObjectProperties = @{ Selection = $i Name = $Item } New-Object -TypeName PSObject -Property $ObjectProperties } $OSLicenseMenu | Select-Object -Property Selection, Name | Format-Table | Out-Host do { $SelectReadHost = Read-Host -Prompt "Enter a Selection for the $OSVersion License" } until (((($SelectReadHost -ge 0) -and ($SelectReadHost -in $OSLicenseMenu.Selection)))) $OSLicenseMenu = $OSLicenseMenu | Where-Object {$_.Selection -eq $SelectReadHost} | Select-Object -ExpandProperty Name if ($OSLicenseMenu -match 'Retail') { $OSLicense = 'Retail' } else { $OSLicense = 'Volume' } Write-Host -ForegroundColor Cyan "OSLicense: " -NoNewline Write-Host -ForegroundColor Green "$OSLicense" } if ($OSEdition -eq 'Education') { $OSEditionId = 'Education' if ($OSLicense -eq 'Retail') {$ImageIndex = 7} if ($OSLicense -eq 'Volume') {$ImageIndex = 4} } if ($OSEdition -eq 'Education N') { $OSEditionId = 'EducationN' if ($OSLicense -eq 'Retail') {$ImageIndex = 8} if ($OSLicense -eq 'Volume') {$ImageIndex = 5} } if ($OSEdition -eq 'Pro') { $OSEditionId = 'Professional' if ($OSLicense -eq 'Retail') {$ImageIndex = 9} if ($OSLicense -eq 'Volume') {$ImageIndex = 8} } if ($OSEdition -eq 'Pro N') { $OSEditionId = 'ProfessionalN' if ($OSLicense -eq 'Retail') {$ImageIndex = 10} if ($OSLicense -eq 'Volume') {$ImageIndex = 9} } Write-Host -ForegroundColor Cyan "OSEditionId: " -NoNewline Write-Host -ForegroundColor Green "$OSEditionId" Write-Host -ForegroundColor Cyan "ImageIndex: " -NoNewline Write-Host -ForegroundColor Green "$ImageIndex" #================================================= # OSLanguage #================================================= Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "$OSVersion OSLanguage " -NoNewline if ($PSBoundParameters.ContainsKey('OSLanguage')) { Write-Host -ForegroundColor Green $OSLanguage } else { Write-Host -ForegroundColor Cyan "Menu" $OSLanguageNames = @('ar-sa','bg-bg','cs-cz','da-dk','de-de','el-gr','en-gb','en-us','es-es','es-mx','et-ee','fi-fi','fr-ca','fr-fr','he-il','hr-hr','hu-hu','it-it','ja-jp','ko-kr','lt-lt','lv-lv','nb-no','nl-nl','pl-pl','pt-br','pt-pt','ro-ro','ru-ru','sk-sk','sl-si','sr-latn-rs','sv-se','th-th','tr-tr','uk-ua','zh-cn','zh-tw') $i = $null $OSLanguageMenu = foreach ($Item in $OSLanguageNames) { $i++ $ObjectProperties = @{ Selection = $i Name = $Item } New-Object -TypeName PSObject -Property $ObjectProperties } $OSLanguageMenu | Select-Object -Property Selection, Name | Format-Table | Out-Host do { $SelectReadHost = Read-Host -Prompt "Enter a Selection for the $OSVersion OSLanguage" } until (((($SelectReadHost -ge 0) -and ($SelectReadHost -in $OSLanguageMenu.Selection)))) $OSLanguage = $OSLanguageMenu | Where-Object {$_.Selection -eq $SelectReadHost} | Select-Object -ExpandProperty Name Write-Host -ForegroundColor Cyan "OSLanguage: " -NoNewline Write-Host -ForegroundColor Green "$OSLanguage" } #================================================= # Get-FeatureUpdate #================================================= Write-Host -ForegroundColor DarkGray "=========================================================================" Write-Host -ForegroundColor Cyan "Get-FeatureUpdate" Write-Host -ForegroundColor DarkGray "$OSVersion x64 | OSLicense: $OSLicense | OSBuild: $OSBuild | OSLanguage: $OSLanguage" if ($OSVersion -eq 'Windows 11') { $GetFeatureUpdate = Get-FeatureUpdate -OSVersion 'Windows 11' -OSLicense $OSLicense -OSBuild $OSBuild -OSLanguage $OSLanguage } else { $GetFeatureUpdate = Get-FeatureUpdate -OSLicense $OSLicense -OSBuild $OSBuild -OSLanguage $OSLanguage } if ($GetFeatureUpdate) { $GetFeatureUpdate = $GetFeatureUpdate | Select-Object -Property CreationDate,KBNumber,Title,UpdateOS,UpdateBuild,UpdateArch,FileName, @{Name='SizeMB';Expression={[int]($_.Size /1024/1024)}},FileUri,Hash,AdditionalHash } else { Write-Warning "Unable to locate a $OSVersion Feature Update" Write-Warning "OSDCloud cannot continue" Break } #================================================= # Offline OS #================================================= $OSDCloudOfflineOS = Find-OSDCloudOfflineFile -Name $GetFeatureUpdate.FileName | Select-Object -First 1 if ($OSDCloudOfflineOS) { $OSDCloudOfflineOSFullName = $OSDCloudOfflineOS.FullName Write-Host -ForegroundColor Cyan "Offline: $OSDCloudOfflineOSFullName" } elseif (Test-WebConnection -Uri $GetFeatureUpdate.FileUri) { $SaveFeatureUpdate = Save-FeatureUpdate -OSLicense $OSLicense -OSBuild $OSBuild -OSLanguage $OSLanguage -DownloadPath "$OSDCloudOfflineFullName\OS" | Out-Null } else { Write-Warning "Could not verify an Internet connection for the Windows Feature Update" Write-Warning "OSDCloud cannot continue" Break } #================================================= # Save-MyDriverPack #================================================= Write-Host -ForegroundColor DarkGray "================================================" Write-Host -ForegroundColor Cyan "Save-MyDriverPack" Save-MyDriverPack -DownloadPath "$OSDCloudOfflineFullName\DriverPacks\$Manufacturer" -Manufacturer $Manufacturer -Product $Product #================================================= # PSGallery Modules #================================================= Write-Host -ForegroundColor DarkGray "================================================" Write-Host -ForegroundColor Cyan "PowerShell Modules and Scripts" #Offline Write-Host "PowerShell Offline Modules and Scripts are located at $OSDCloudOfflineFullName\PowerShell\Offline" Write-Host "This is for Modules and Scripts that OSDCloud needs to add to the Offline OS" Write-Host "" if (-NOT (Test-Path "$OSDCloudOfflineFullName\PowerShell\Offline\Modules")) { New-Item -Path "$OSDCloudOfflineFullName\PowerShell\Offline\Modules" -ItemType Directory -Force | Out-Null } if (-NOT (Test-Path "$OSDCloudOfflineFullName\PowerShell\Offline\Scripts")) { New-Item -Path "$OSDCloudOfflineFullName\PowerShell\Offline\Scripts" -ItemType Directory -Force | Out-Null } #Required Write-Host "PowerShell Required Modules and Scripts are located at $OSDCloudOfflineFullName\PowerShell\Required" Write-Host "This is for Modules and Scripts that you want to add to the Offline OS" Write-Host "" if (-NOT (Test-Path "$OSDCloudOfflineFullName\PowerShell\Required\Modules")) { New-Item -Path "$OSDCloudOfflineFullName\PowerShell\Required\Modules" -ItemType Directory -Force | Out-Null } if (-NOT (Test-Path "$OSDCloudOfflineFullName\PowerShell\Required\Scripts")) { New-Item -Path "$OSDCloudOfflineFullName\PowerShell\Required\Scripts" -ItemType Directory -Force | Out-Null } if (Test-WebConnection -Uri "https://www.powershellgallery.com") { Write-Host -ForegroundColor DarkGray "Save-Module OSD to $OSDCloudOfflineFullName\PowerShell\Offline\Modules" Save-Module -Name OSD -Path "$OSDCloudOfflineFullName\PowerShell\Offline\Modules" Copy-PSModuleToFolder -Name OSD -Destination "$OSDCloudOfflineFullName\PowerShell\Offline\Modules" Write-Host -ForegroundColor DarkGray "Save-Module WindowsAutopilotIntune to $OSDCloudOfflineFullName\PowerShell\Offline\Modules" Save-Module -Name WindowsAutopilotIntune -Path "$OSDCloudOfflineFullName\PowerShell\Offline\Modules" Write-Host -ForegroundColor DarkGray "Save-Module AzureAD to $OSDCloudOfflineFullName\PowerShell\Offline\Modules" Write-Host -ForegroundColor DarkGray "Save-Module Microsoft.Graph.Intune to $OSDCloudOfflineFullName\PowerShell\Offline\Modules" Write-Host -ForegroundColor DarkGray "Save-Script Get-WindowsAutopilotInfo to $OSDCloudOfflineFullName\PowerShell\Offline\Scripts" Save-Script -Name Get-WindowsAutopilotInfo -Path "$OSDCloudOfflineFullName\PowerShell\Offline\Scripts" } else { Write-Warning "Could not validate an Internet connection to the PowerShell Gallery" Write-Warning "OSDCloud cannot continue" Break } #================================================= # Save-OSDCloud.usb Complete #================================================= $Global:OSDCloudEndTime = Get-Date $Global:OSDCloudTimeSpan = New-TimeSpan -Start $Global:OSDCloudStartTime -End $Global:OSDCloudEndTime Write-Host -ForegroundColor DarkGray "================================================" Write-Host -ForegroundColor Yellow "$((Get-Date).ToString('yyyy-MM-dd-HHmmss')) $($MyInvocation.MyCommand.Name) " -NoNewline Write-Host -ForegroundColor Cyan "Completed in $($Global:OSDCloudTimeSpan.ToString("mm' minutes 'ss' seconds'"))!" explorer $OSDCloudOfflineFullName #================================================= } |