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

    Get-CachedFile -FileName "TakeHomeCreds.xml" -FileURL "https://nettools.psd202.org/AutoPilotFast/TakeHomeCreds.xml" -BaseDir $CacheDir | Out-Null

    Write-Host "Ideal autopilot quick environment initialized!" -Foreground Green
    Write-Progress -Activity "Initializing ideal autopilot quick environment" -Completed -Id 500
}


<#
 .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 disks 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
        }
    }

}


Export-ModuleMember -Function New-AutopilotQuickDrive, Initialize-IdealAutopilotQuickEnvironment