AutopilotQuick.psm1
Import-Module WebFileCacher workflow New-MultipleDrives { param ( #Path to an OSDCloud ISO #This file will be mounted and the contents will be copied to the OSDCloud USB [Parameter(ParameterSetName='WinpePath',Mandatory)] [string]$WinpePath, [Parameter(Mandatory)] [int[]] $DriveNumbers, [Parameter(Mandatory)] [string]$PathToAutopilotQuickIdealEnvironment, [Parameter(Mandatory)] [string]$ScriptPath, [Switch]$NoPreload, [System.Management.Automation.SwitchParameter]$Force ) foreach -parallel($drive in $DriveNumbers){ Invoke-Expression -Command ".'$ScriptPath\New-OSDCloudUSBModified.ps1' -fromPath $WinpePath -DriveNumber $drive -Force" $driveLetter = ((Get-Partition -DiskNumber $drive) | Where-Object {$_.Size -eq ((Get-Partition -DiskNumber $drive).Size | measure -Maximum).Maximum}).DriveLetter if(-not ($NoPreload)){ Invoke-Expression -Command ".'$ScriptPath\CopyAutopilotQuickIdealEnvironmentToDrive.ps1' -DriveLetter $driveLetter -PathToAutopilotQuickIdealEnvironment $PathToAutopilotQuickIdealEnvironment" } } } function Get-WimManData(){ $wimManData = Invoke-WebRequest "https://nettools.psd202.org/AutoPilotFast/WimManConfig.json" | ConvertFrom-Json [System.Collections.ArrayList]$data= @() foreach ($item in ($wimManData | Get-Member -type NoteProperty)){ $data.Add($wimManData."$($item.Name)") | Out-Null } $ActualDownloads = $data | Sort-Object -Property URL, Name -Unique return $ActualDownloads } function Sync-WimManDataToDir([string]$dir){ Get-CachedFile -Name "WimManConfig.json" -URL "https://nettools.psd202.org/AutoPilotFast/WimManConfig.json" -Dir $dir | Out-Null } function Select-ImagesToPreload(){ $wimManData = Invoke-WebRequest "https://nettools.psd202.org/AutoPilotFast/WimManConfig.json" | ConvertFrom-Json [System.Collections.ArrayList]$data= @() foreach ($item in ($wimManData | Get-Member -type NoteProperty)){ if($item.Name -ne "default"){ $thing = [PSCustomObject]@{ Model = $item.Name FileName = $wimManData.$($item.Name).Name URL = $wimManData.$($item.Name).URL } $data.Add($thing) | Out-Null } } $data = @($data | Out-GridView -OutputMode Multiple -Title "Select laptop models to preload. CTRL+CLICK to select multiple." -OutVariable URL) $ActualDownloads = $data | Sort-Object -Property URL, FileName -Unique return $ActualDownloads.FileName } function Sync-WimImagesToDir([string]$dir, [string[]]$ImagesToPreload){ $data = Get-WimManData $i = 0; foreach ($wimFile in $data){ $i++; Write-Host -Foreground Magenta -NoNewline "LOADING $($wimFile.Name) $i of $($data.Count)..." if($ImagesToPreload -contains $wimFile.Name){ Get-CachedFile -Name $wimFile.Name -FileURL $wimFile.URL -BaseDir $dir | Out-Null } else { $dataPath = Join-Path $dir $wimFile.Name $dataCachePath = Join-Path $dir "$($wimFile.Name)-CacheData.json" Remove-Item $dataPath, $dataCachePath -Force -ErrorAction Ignore } Write-Host -ForegroundColor Green "DONE" } } <# .Synopsis Initializes an ideal autopilot quick environment .Description Downloads all windows images, and autopilot quick executable that autopilotquick needs to run .Parameter Dir The directory where autopilot quick will be initialized .Example # Initialize/Prepare autopilot quick resources in C:\AutopilotQuick (default behaviour) Initialize-IdealAutopilotQuickEnvironment .Example # Initialize/Prepare autopilot quick resources in C:\AutopilotQuickPreload Initialize-IdealAutopilotQuickEnvironment -Dir "C:\AutopilotQuickPreload" #> function Initialize-IdealAutopilotQuickEnvironment([string]$dir="C:\AutopilotQuick", [string[]]$ImagesToPreload=@("Laptops.wim")){ Write-Host "Initializing ideal autopilot quick environment..." -Foreground Yellow Write-Progress -Activity "Initializing ideal autopilot quick environment" -PercentComplete 0 -Status "Loading AutopilotQuick.zip" -Id 500 $IdealEnvironmentDir = Join-Path $dir "AutopilotQuick" $repo = "PCSD202/AutopilotQuick" $file = "AutopilotQuick.zip" $cachedAutopilotZip = Get-CachedFile -FileName $file -FileURL "https://github.com/$repo/releases/latest/download/$file" -BaseDir $dir Write-Progress -Activity "Initializing ideal autopilot quick environment" -PercentComplete 15 -Status "Extracting AutopilotQuick.exe" -Id 500 Expand-Archive ($cachedAutopilotZip.FullName) -DestinationPath "$IdealEnvironmentDir" -Force $CacheDir = Join-Path $IdealEnvironmentDir "Cache" #================================================= # Start downloading things that cacher would need #================================================= Write-Progress -Activity "Initializing ideal autopilot quick environment" -PercentComplete 25 -Status "Preloading image config" -Id 500 Sync-WimManDataToDir -dir $CacheDir Write-Progress -Activity "Initializing ideal autopilot quick environment" -PercentComplete 50 -Status "Preloading images" -Id 500 Sync-WimImagesToDir -dir $CacheDir -ImagesToPreload $ImagesToPreload Write-Progress -Activity "Initializing ideal autopilot quick environment" -PercentComplete 75 -Status "Preloading logging config" -Id 500 Get-CachedFile -FileName "AzureLogSettings.json" -FileURL "https://nettools.psd202.org/AutoPilotFast/AzureLogSettings.json" -BaseDir $CacheDir | Out-Null Write-Host "Ideal autopilot quick environment initialized!" -Foreground Green Write-Progress -Activity "Initializing ideal autopilot quick environment" -Completed -Id 500 } function Block-StandardUser { $CallingFunction = (Get-PSCallStack)[1].InvocationInfo.Line $Message = "[$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))] $CallingFunction requires Admin Rights" $IsAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator') if($IsAdmin -eq $false){ Write-Warning $Message Break; } } function Block-NoInternet { [CmdletBinding()] param ( [System.Management.Automation.SwitchParameter]$Warn, [System.Management.Automation.SwitchParameter]$Pause ) $CallingFunction = (Get-PSCallStack)[1].InvocationInfo.Line $Message = "[$((Get-Date).ToString('yyyy-MM-dd-HHmmss'))] $CallingFunction requires Internet access" if (-NOT (Test-WebConnection -Uri 'www.google.com')) { Write-Warning $Message if ($PSBoundParameters.ContainsKey('Pause')) { [void]('Press Enter to Continue') } if (-NOT ($PSBoundParameters.ContainsKey('Warn'))) { Break } } } <# .Synopsis Creates a new drive that has AutopilotQuick on it .Description Prepares one, or many, drives to be used with AutopilotQuick .Parameter NoPreload Add this to not copy images/autopilot quick executable to drive for faster image .Parameter DataDir Specifies where to store the data for AutopilotQuick .Example # Create a new AutopilotQuick drive New-AutopilotQuickDrive .Example # Create a new AutopilotQuick drive without copying windows images New-AutopilotQuickDrive -NoPreload #> function New-AutopilotQuickDrive([Switch]$NoPreload, [string]$DataDir="C:\AutopilotQuick"){ Block-StandardUser Block-NoInternet if($NoPreload){ Write-Warning "-NoPreload specified, drive setup will be completed on first boot of new drive. Recommend plugging into ethernet for first boot." } [System.IO.Directory]::CreateDirectory($DataDir) | Out-Null $MinimumSizeGB = 25 if($NoPreload){ $MinimumSizeGB = 7 } $MaximumSizeGB = 2000 $disksToSelect = @(Get-Disk.usb | Where-Object {($_.Size -gt ($MinimumSizeGB * 1GB)) -and ($_.Size -lt ($MaximumSizeGB * 1GB))} | Select-Object FriendlyName, DiskNumber, @{Name='SizeGB';Expression={[int]($_.Size / 1000000000)}}) if($disksToSelect.Count -eq 0){ Write-Host "No drives found, exiting" break; } if($disksToSelect.Count -gt 1){ Write-Host "Waiting for drive selection..." -ForegroundColor Cyan $disks = @($disksToSelect | Out-GridView -OutputMode Multiple -Title 'Select disks to image. CTRL+CLICK to select multiple.' | Select-Object -ExpandProperty DiskNumber) } else { $disks = @($disksToSelect[0] | Select-Object -ExpandProperty DiskNumber) } if($disks.Count -eq 0){ Write-Host "No disks selected, exiting" break; } $GetDisk = Get-Disk.usb | Sort-Object Number | Where-Object {$_.DiskNumber -in $disks} $GetDisk | Select-Object -Property Number, BusType, MediaType,` FriendlyName, PartitionStyle, NumberOfPartitions,` @{Name='SizeGB';Expression={[int]($_.Size / 1000000000)}} | Format-Table $title = 'All data on these disks will be cleared and lost' $question = 'Are you sure you want to proceed?' $choices = '&Yes', '&No' $decision = $Host.UI.PromptForChoice($title, $question, $choices, 1) if($decision -ne 0){ break; } if(($GetDisk.PartitionStyle -contains "GPT")){ NEW-ITEM -Name convert.txt -ItemType file -force | OUT-NULL foreach ($disk in ($GetDisk | Where-Object {$_.PartitionStyle} -NE "MBR")){ ADD-CONTENT -Path convert.txt "SELECT DISK $($disk.DiskNumber)" ADD-CONTENT -Path convert.txt "clean" ADD-CONTENT -Path convert.txt "convert MBR" ADD-CONTENT -Path convert.txt "create partition primary" } DISKPART /S convert.txt Remove-Item convert.txt } if(-not ($NoPreload)){ $ImagesToPreload = Select-ImagesToPreload; Initialize-IdealAutopilotQuickEnvironment -dir $DataDir -ImagesToPreload $ImagesToPreload } try { Write-Warning "DO NOT UNPLUG/PLUG IN DEVICES WHILE THIS IS WORKING" Write-Host -Foreground Magenta -NoNewline "LOADING OS Image..." $IdealEnvironmentDir = Join-Path $DataDir "AutopilotQuick" $CacheDir = Join-Path $IdealEnvironmentDir "Cache" $cachedOSDCloudisoFile = Get-CachedFile -FileName "OSDImage.iso" -FileURL "https://nettools.psd202.org/AutoPilotFast/OSDCloud_NoPrompt.iso" -BaseDir $CacheDir Write-Host -ForegroundColor Green "DONE" $fromIsoFile = $cachedOSDCloudisoFile.FullName $fromIsoFileGetItem = Get-Item -Path $fromIsoFile -ErrorAction Ignore $fromIsoFileFullName = $fromIsoFileGetItem.FullName if ($fromIsoFileGetItem -and $fromIsoFileGetItem.Extension -eq '.iso') { #Do nothing } else { Write-Warning "$((Get-Date).ToString('yyyy-MM-dd-HHmmss')) Unable to get the properties of $fromIsoFile" Write-Warning "$((Get-Date).ToString('yyyy-MM-dd-HHmmss')) Something went very very wrong in this process" return; } #================================================= # Mount fromIsoFile #================================================= $Volumes = (Get-Volume).Where({$_.DriveLetter}).DriveLetter #Write-Host -ForegroundColor DarkGray "$((Get-Date).ToString('yyyy-MM-dd-HHmmss')) Mounting OSDCloudISO at $fromIsoFileFullName" $MountDiskImage = Mount-DiskImage -ImagePath $fromIsoFileFullName Start-Sleep -Seconds 1 $MountDiskImageDriveLetter = (Compare-Object -ReferenceObject $Volumes -DifferenceObject (Get-Volume).Where({$_.DriveLetter}).DriveLetter).InputObject if ($MountDiskImageDriveLetter) { $WinpeSourcePath = "$($MountDiskImageDriveLetter):\" } else { Write-Warning "$((Get-Date).ToString('yyyy-MM-dd-HHmmss')) Unable to mount $MountDiskImage" Write-Warning "$((Get-Date).ToString('yyyy-MM-dd-HHmmss')) Something went very very wrong in this process" return; } New-MultipleDrives -ScriptPath $PSScriptRoot -DriveNumbers $disks -Force -WinpePath $WinpeSourcePath -PathToAutopilotQuickIdealEnvironment (Join-Path $DataDir "AutopilotQuick") -NoPreload:($NoPreload.IsPresent) Write-Host -ForegroundColor Magenta "Images that are not preloaded can still be used inside AutopilotQuick. They will be downloaded automatically the first time that you need them." Write-Host -ForegroundColor Green "Drive creation complete." if($NoPreload){ Write-Warning "-NoPreload specified, drive setup will be completed on first boot of new drive. Recommend plugging into ethernet for first boot." } } finally { #================================================= # Dismount OSDCloudISO #================================================= if ($MountDiskImage) { Write-Verbose "$((Get-Date).ToString('yyyy-MM-dd-HHmmss')) Dismounting $($MountDiskImage.ImagePath)" $null = Dismount-DiskImage -ImagePath $MountDiskImage.ImagePath } } } function Get-AutopilotQuickDrivePath { $autopilotQuickPath = Get-PSDrive -PSProvider FileSystem | Where-Object {$_.Name -ne 'C'} | ForEach-Object { Get-Item "$($_.Name):\AutopilotQuick" -Force -ErrorAction Ignore } if($null -eq $autopilotQuickPath){ Write-Warning "No AutopilotQuick drive found" } return $autopilotQuickPath; } function Get-DeviceIdentifier { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [string]$FilePath ) if (!(Test-Path $FilePath)) { Write-Error "The file $FilePath does not exist." return $null } try { $content = Get-Content -Path $FilePath -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop } catch { Write-Warning "The file $FilePath could not be parsed as JSON. Please check if the file is valid JSON." return $null } if ($null -eq $content.ID) { Write-Warning "The DeviceIdentifier.json file does not contain an 'ID' property." return $null } return $content.ID } function Get-AutopilotQuickDriveID { [CmdletBinding()] param ( [Parameter(Mandatory=$false)] [switch]$ReturnID ) # This should find an autopilotQuick drive, prompt if multiple, and then give the Drive ID if it exists $aqPath = Get-AutopilotQuickDrivePath if($null -eq $aqPath){ if($ReturnID){ return $null } else { return } } $DeviceIDFile = Join-Path $aqPath "DeviceIdentifier.json" if(!(Test-Path $DeviceIDFile)) { Write-Warning "AutopilotQuick DriveIdentifier.json missing" if($ReturnID){ return $null } else { return } } # Read from the DeviceIdentifier.json now that we know it exists $ID = Get-DeviceIdentifier -FilePath $DeviceIDFile Write-Host -ForegroundColor Cyan "AutopilotQuick drive ID: " -NoNewline Write-Host $ID -ForegroundColor Magenta if($ReturnID){ return $ID } } Export-ModuleMember -Function New-AutopilotQuickDrive, Initialize-IdealAutopilotQuickEnvironment, Get-AutopilotQuickDriveID |