ict-autopilot.ps1


<#PSScriptInfo
 
.VERSION 3.0.2
 
.GUID 1522f19a-667a-4638-8def-7d5590a61094
 
.AUTHOR a.twist@imperial.ac.uk
 
.COMPANYNAME Imperial College London
 
.COPYRIGHT
 
.TAGS
 
.LICENSEURI
 
.PROJECTURI
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
 
#>


<#
 
.DESCRIPTION
Enrols an ICT laptop into Autopilot, creates a CSV file for the hardware hash with various other tools
 
#>
 
#####
## Imperial College London
## -----------------------
## Enrolement script
##
#####
###


# Install NuGet Provider silently if not present
if (-not (Get-PackageProvider -ListAvailable -Name NuGet -ErrorAction SilentlyContinue)) {
    Write-Host "Installing NuGet Provider..."
    Install-PackageProvider -Name NuGet -Force -Scope CurrentUser | Out-Null
}

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

function Create-MainForm {
    $form = New-Object System.Windows.Forms.Form
    $form.Text = "ICT-Autopilot 3.0.2"
    $form.Size = New-Object System.Drawing.Size(500, 600)  # Increased height to accommodate header
    $form.StartPosition = "CenterScreen"
    $form.BackColor = [System.Drawing.Color]::White

    # Add custom icon
    try {
        # Create temp directory if it doesn't exist
        if (!(test-path -path "c:\temp")) {
            new-item -path "c:\" -name "temp" -ItemType "directory"
        }
        invoke-webrequest https://ictautopilot.blob.core.windows.net/assets/win11.ico -outfile c:\temp\win11.ico
        $iconPath = "c:\temp\win11.ico"
        if (Test-Path $iconPath) {
            $form.Icon = New-Object System.Drawing.Icon($iconPath)
        }
    }
    catch {
        Write-Warning "Could not load custom icon: $_"
    }
    

    # Add serial number header
    $headerLabel = New-Object System.Windows.Forms.Label
    $headerLabel.Location = New-Object System.Drawing.Point(20, 20)
    $headerLabel.Size = New-Object System.Drawing.Size(440, 30)
    $headerLabel.Font = New-Object System.Drawing.Font("Segoe UI", 12, [System.Drawing.FontStyle]::Bold)
    $headerLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
    
    # Get and display serial number
    try {
        $serial = Get-WmiObject win32_bios | Select-Object serialnumber -ExpandProperty serialnumber
        $headerLabel.Text = "Serial Number: $serial"
    } catch {
        $headerLabel.Text = "Serial Number: Unable to retrieve"
    }

    # Adjust other controls' positions to accommodate header
    $script:listBox = New-Object System.Windows.Forms.ListBox
    $script:listBox.Location = New-Object System.Drawing.Point(20, 60)  # Moved down
    $script:listBox.Size = New-Object System.Drawing.Size(440, 200)
    $script:listBox.Font = New-Object System.Drawing.Font("Segoe UI", 10)
    
    # ...existing listbox items...

    $executeButton = New-Object System.Windows.Forms.Button
    $executeButton.Location = New-Object System.Drawing.Point(20, 280)  # Moved down
    $executeButton.Size = New-Object System.Drawing.Size(440, 30)
    $executeButton.Text = "Execute Selected Option"
    $executeButton.Add_Click({ 
        if ($script:listBox.SelectedItem) {
            Execute-Selection $script:listBox.SelectedItem
        } else {
            $script:outputBox.Text = "Please select an option from the list."
        }
    })
    # Add options to the listbox
    [void]$script:listBox.Items.Add("1. Enroll your machine into Autopilot")
    [void]$script:listBox.Items.Add("2. Create Autopilot CSV file")
    [void]$script:listBox.Items.Add("3. Enroll Custom Device into Autopilot")
    [void]$script:listBox.Items.Add("4. Enroll Software Hub into Autopilot")
    [void]$script:listBox.Items.Add("5. Run Windows Update")
    [void]$script:listBox.Items.Add("6. Display System Information")
    [void]$script:listBox.Items.Add("7. Check Windows 11 Compatibility")
    [void]$script:listBox.Items.Add("8. Reset Device")
    [void]$script:listBox.Items.Add("9. Remove from Autopilot, Intune and Entra ID")
    [void]$script:listBox.Items.Add("10. Restart PC")

    $script:outputBox = New-Object System.Windows.Forms.RichTextBox
    $script:outputBox.Location = New-Object System.Drawing.Point(20, 330)  # Moved down
    $script:outputBox.Size = New-Object System.Drawing.Size(440, 200)
    $script:outputBox.ReadOnly = $true
    $script:outputBox.BackColor = [System.Drawing.Color]::White
    $script:outputBox.Font = New-Object System.Drawing.Font("Segoe UI", 10)

    $form.Controls.Add($headerLabel)
    $form.Controls.Add($script:listBox)
    $form.Controls.Add($executeButton)
    $form.Controls.Add($script:outputBox)

    return $form
}

# Update the Update-Output function
function Update-Output {
    param($message)
    $script:outputBox.Text = $message
}

function Execute-Selection {
    param($selection)
    
    switch -Wildcard ($selection) {
        "*1.*" { 
            Enroll-Autopilot
        }
        "*2.*" { 
            Enroll-Autopilot-csv
        }
        "*3.*" { 
            Enroll-Autopilot-Custom
        }
        "*4.*" { 
            Enroll-Autopilot-MTR
        }
        "*5.*" { 
            Run-WindowsUpdate
        }
        "*6.*" { 
            Get-SystemInfo
        }
        "*7.*" { 
            Check-Windows11Compatibility
        }
        "*8.*" { 
            Reset-Device
        }
        "*9.*" {
            Remove-DeviceFromCloud
        }
        "*10.*" {
            Restart-PCWithCountdown
        }
        default {
            Update-Output "Please select an option from the list."
        }
    }
}

function Check-Windows11Compatibility {
    try {
        $results = @()
        
        # Create temp directory if it doesn't exist
        if (!(test-path -path "c:\temp")) {
            new-item -path "c:\" -name "temp" -ItemType "directory"
        }

        # Download CPU compatibility lists
        Update-Output "Downloading CPU compatibility lists..."
        invoke-webrequest https://ictautopilot.blob.core.windows.net/win11compat/AMD-CPU.txt -outfile c:\temp\AMD-CPU.txt
        invoke-webrequest https://ictautopilot.blob.core.windows.net/win11compat/Intel-CPU.txt -outfile c:\temp\INTEL-CPU.txt
        invoke-webrequest https://ictautopilot.blob.core.windows.net/win11compat/snap-CPU.txt -outfile c:\temp\Snap-CPU.txt

        # Load CPU lists and get system info
        $listprocIntel = get-content "C:\temp\INTEL-CPU.txt"
        $listprocAMD = get-content "c:\temp\AMD-CPU.txt"
        $listprocSNAP = get-content "c:\temp\SNAP-CPU.txt"
        $info = Get-ComputerInfo
        $procs = (($info).CsProcessors).name
        $cpuCompatible = $false

        # Check CPU compatibility
        foreach ($proc in $procs) {
            if ($proc -like "*Intel*") {
                $cpuCompatible = $listprocIntel | Where-Object { $proc -like "*$_*" } | Select-Object -First 1
            }
            elseif ($proc -like "*AMD*") {
                $cpuCompatible = $listprocAMD | Where-Object { $proc -like "*$_*" } | Select-Object -First 1
            }
            else {
                $cpuCompatible = $listprocSNAP | Where-Object { $proc -like "*$_*" } | Select-Object -First 1
            }
        }

        # Add CPU compatibility result
        $results += "CPU Model: $proc - $(if($cpuCompatible){'Pass'}else{'Fail'})"
        
        # ...existing checks...
        # Check RAM
        $ram = Get-WmiObject -Class Win32_ComputerSystem
        $ramGB = [math]::Round($ram.TotalPhysicalMemory / 1GB, 2)
        $ramCheck = $ramGB -ge 4
        $results += "RAM: $ramGB GB (Minimum: 4 GB) - $(if($ramCheck){'Pass'}else{'Fail'})"
        
        # Check Storage
        $disk = Get-WmiObject -Class Win32_LogicalDisk -Filter "DeviceID='C:'"
        $diskGB = [math]::Round($disk.Size / 1GB, 2)
        $diskCheck = $diskGB -ge 64
        $results += "Storage: $diskGB GB (Minimum: 64 GB) - $(if($diskCheck){'Pass'}else{'Fail'})"
        
        # Check TPM
        try {
            $tpm = Get-WmiObject -Namespace "root\CIMV2\Security\MicrosoftTpm" -Class Win32_Tpm
            $tpmVersion = if ($tpm.SpecVersion -match "2.0") { "2.0" } else { "1.2 or lower" }
            $tpmCheck = $tpm.SpecVersion -match "2.0"
            $results += "TPM Version: $tpmVersion (Required: 2.0) - $(if($tpmCheck){'Pass'}else{'Fail'})"
        } catch {
            $results += "TPM: Not detected - Fail"
            $tpmCheck = $false
        }
        
        # Check Secure Boot
        $secureBootStatus = Confirm-SecureBootUEFI
        $results += "Secure Boot: $(if($secureBootStatus){'Enabled'}else{'Disabled'}) - $(if($secureBootStatus){'Pass'}else{'Fail'})"
        
        # Overall assessment (updated to include CPU compatibility)
        $compatible = $cpuCompatible -and $ramCheck -and $diskCheck -and $tpmCheck -and $secureBootStatus
        
        $output = "Windows 11 Compatibility Check:`n`n"
        $output += "Some Brand new CPUs may incorrectly flag as Not Compatible`n`n"
        $output += $results | ForEach-Object { "$_`n" }
        $output += "`nOverall Result: $(if($compatible){'Compatible with Windows 11'}else{'Not Compatible with Windows 11'})"
        
        Update-Output $output
    }
    catch {
        Update-Output "Error checking Windows 11 compatibility: $_"
    }
}

function Remove-DeviceFromCloud {
    try {
        # Check if running as admin
        if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
            Update-Output "Please run as Administrator to remove device from cloud services"
            return
        }

        # Show confirmation dialog
        $confirmation = [System.Windows.Forms.MessageBox]::Show(
            "Are you sure you want to remove this device from Autopilot, Intune, and Entra ID?`n`nThis action cannot be undone.",
            "Confirm Device Removal",
            [System.Windows.Forms.MessageBoxButtons]::YesNo,
            [System.Windows.Forms.MessageBoxIcon]::Warning
        )

        if ($confirmation -eq [System.Windows.Forms.DialogResult]::No) {
            Update-Output "Device removal cancelled by user."
            return
        }

        Update-Output "Starting device removal process..."

        # Rest of the existing code...
        $serialNumber = Get-WmiObject win32_bios | Select-Object -ExpandProperty SerialNumber
        $deviceName = $env:COMPUTERNAME

        Update-Output "Installing required PowerShell modules..."
        Install-Module WindowsAutopilotIntune -Force -AllowClobber -Scope CurrentUser
        Install-Module Microsoft.Graph.Intune -Force -Scope CurrentUser
        Install-Module Microsoft.Graph.Identity.DirectoryManagement -Force -Scope CurrentUser

        Update-Output "Connecting to Microsoft Graph..."
        Connect-MgGraph -Scopes "Device.ReadWrite.All", "DeviceManagementServiceConfig.ReadWrite.All"

        # Show progress as each service is processed
        Update-Output "Step 1/3: Removing from Autopilot..."
        try {
            $autopilotDevice = Get-AutopilotDevice -serial $serialNumber
            if ($autopilotDevice) {
                Remove-AutopilotDevice -id $autopilotDevice.id
                Update-Output "Successfully removed from Autopilot"
            } else {
                Update-Output "Device not found in Autopilot"
            }
        } catch {
            Update-Output "Error removing from Autopilot: $_"
        }

        Update-Output "`nStep 2/3: Removing from Intune..."
        try {
            $intuneDevice = Get-MgDeviceManagementManagedDevice -Filter "serialNumber eq '$serialNumber'"
            if ($intuneDevice) {
                Remove-MgDeviceManagementManagedDevice -ManagedDeviceId $intuneDevice.Id
                Update-Output "Successfully removed from Intune"
            } else {
                Update-Output "Device not found in Intune"
            }
        } catch {
            Update-Output "Error removing from Intune: $_"
        }

        Update-Output "`nStep 3/3: Removing from Entra ID..."
        try {
            $aadDevice = Get-MgDevice -Filter "displayName eq '$deviceName'"
            if ($aadDevice) {
                Remove-MgDevice -DeviceId $aadDevice.Id
                Update-Output "Successfully removed from Entra ID"
            } else {
                Update-Output "Device not found in Entra ID"
            }
        } catch {
            Update-Output "Error removing from Entra ID: $_"
        }

        # Clean up local device state
        Update-Output "`nCleaning up local device state..."
        try {
            $dsregcmd = Start-Process "dsregcmd.exe" -ArgumentList "/leave" -PassThru -Wait
            if ($dsregcmd.ExitCode -eq 0) {                
                Update-Output "It will take around 5 minutes to show this deletion in intune"
            } else {
                Update-Output "Warning: Local device state cleanup may not be complete"
            }
        } catch {
            Update-Output "Error cleaning local device state: $_"
        }

        # Final confirmation
        [System.Windows.Forms.MessageBox]::Show(
            "Device removal process completed. Please restart your device to complete the process.",
            "Removal Complete",
            [System.Windows.Forms.MessageBoxButtons]::OK,
            [System.Windows.Forms.MessageBoxIcon]::Information
        )

    } catch {
        Update-Output "Error in device removal process: $_"
        [System.Windows.Forms.MessageBox]::Show(
            "An error occurred during the removal process. Please check the output for details.",
            "Error",
            [System.Windows.Forms.MessageBoxButtons]::OK,
            [System.Windows.Forms.MessageBoxIcon]::Error
        )
    }
}


function Restart-PCWithCountdown {
    try {
        # Check for admin privileges
        if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
            Update-Output "Please run as Administrator to restart the PC"
            return
        }

        Update-Output "System will restart in 5 seconds..."
        
        # Start countdown
        for ($i = 5; $i -gt 0; $i--) {
            Update-Output "Restarting in $i seconds..."
            Start-Sleep -Seconds 1
        }

        # Initiate restart
        Restart-Computer -Force
    }
    catch {
        Update-Output "Error initiating restart: $_"
    }
}

function Test-AutopilotRequirements {
    [CmdletBinding()]
    param()

    $requirements = @{
        OSVersion = $false
        OSEdition = $false
        TPM = $false
        AllPassed = $false
    }

    try {
        # Check Windows version
        Write-Host "Checking Windows version..." -ForegroundColor Cyan
        $osInfo = Get-WmiObject -Class Win32_OperatingSystem
        $buildNumber = [int]($osInfo.BuildNumber)
        $osCaption = $osInfo.Caption

        if ($buildNumber -ge 17134) {
            Write-Host "Windows build $buildNumber meets minimum requirement (17134)" -ForegroundColor Green
            $requirements.OSVersion = $true
        } else {
            Write-Host "Windows build $buildNumber does not meet minimum requirement (17134)" -ForegroundColor Red
            Write-Host "Please upgrade Windows to continue" -ForegroundColor Red
        }

        # Check Windows Edition
        Write-Host "`nChecking Windows Edition..." -ForegroundColor Cyan
        if ($osCaption -match "(Enterprise|Professional|Education|Pro)") {
            Write-Host "Windows edition ($osCaption) is supported" -ForegroundColor Green
            $requirements.OSEdition = $true
        } else {
            Write-Host "Windows edition ($osCaption) is not supported" -ForegroundColor Red
            Write-Host "Requires Enterprise, Professional, or Education edition" -ForegroundColor Red
        }

        # Check TPM 2.0
        Write-Host "`nChecking TPM 2.0..." -ForegroundColor Cyan
        try {
            $tpm = Get-Tpm
            if ($tpm.TpmPresent) {
                if ($tpm.TpmReady) {
                    $tpmVersion = $tpm.ManufacturerVersionFull20
                    if (-not ($tpmVersion.Contains("not supported"))) {
                        Write-Host "TPM 2.0 is present and ready" -ForegroundColor Green
                        $requirements.TPM = $true
                    } else {
                        Write-Host "TPM 2.0 is not supported on this device" -ForegroundColor Red
                    }
                } else {
                    Write-Host "TPM is present but not ready" -ForegroundColor Red
                }
            } else {
                Write-Host "No TPM found on this device" -ForegroundColor Red
            }
        } catch {
            Write-Host "Error checking TPM status: $_" -ForegroundColor Red
        }

        # Check if all requirements passed
        $requirements.AllPassed = $requirements.OSVersion -and $requirements.OSEdition -and $requirements.TPM

        # Final summary
        Write-Host "`nRequirements Summary:" -ForegroundColor Cyan
        Write-Host "Windows Version: $($requirements.OSVersion)" -ForegroundColor $(if ($requirements.OSVersion) { "Green" } else { "Red" })
        Write-Host "Windows Edition: $($requirements.OSEdition)" -ForegroundColor $(if ($requirements.OSEdition) { "Green" } else { "Red" })
        Write-Host "TPM 2.0: $($requirements.TPM)" -ForegroundColor $(if ($requirements.TPM) { "Green" } else { "Red" })

        if ($requirements.AllPassed) {
            Write-Host "`nAll requirements met. Device is ready for Autopilot enrollment." -ForegroundColor Green
        } else {
            Write-Host "`nDevice does not meet all requirements for Autopilot enrollment." -ForegroundColor Red
        }

        return $requirements.AllPassed
    }
    catch {
        Write-Host "Error checking requirements: $_" -ForegroundColor Red
        return $false
    }
}

function Reset-Device {
    try {
        # Check if running as admin
        if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
            Update-Output "Please run as Administrator to reset the device"
            return
        }

        Update-Output "Resetting device..."
        $confirmation = [System.Windows.Forms.MessageBox]::Show("Are you sure you want to reset the device?", "Confirm Reset", [System.Windows.Forms.MessageBoxButtons]::YesNo, [System.Windows.Forms.MessageBoxIcon]::Warning)

        if ($confirmation -eq [System.Windows.Forms.DialogResult]::Yes) {
            # Reset the device
            systemreset -factoryreset
        } else {
            Update-Output "Device reset cancelled."
        }
    }
    catch {
        Update-Output "Error resetting device: $_"
    }
}

function Check-Enrol {
    try {
        # Initialize return object
        $enrollmentStatus = @{
            IsEnrolled = $false
            SerialNumber = $null
            Error = $null
        }

        # Get WindowsAutopilotIntune module (and dependencies)
        Update-Output "Checking for WindowsAutopilotIntune module..."
        install-module WindowsAutopilotIntune -Force -AllowClobber -Confirm:$False -ErrorAction Stop
        $module = Import-Module WindowsAutopilotIntune -PassThru -ErrorAction Stop
        
        if (-not $module) {
            Update-Output "Installing WindowsAutopilotIntune module..."
            Install-Module WindowsAutopilotIntune -Force -AllowClobber -Confirm:$False -ErrorAction Stop
            Import-Module WindowsAutopilotIntune -Scope Global -ErrorAction Stop
        }
        
        update-output "Connecting to Microsoft Graph..."
        connect-mggraph

        # Get serial number
        Update-Output "Getting device serial number..."
        $serial = Get-WmiObject win32_bios -ErrorAction Stop | 
                 Select-Object serialnumber -ExpandProperty serialnumber

        if ([string]::IsNullOrEmpty($serial)) {
            throw "Unable to retrieve device serial number"
        }

        $enrollmentStatus.SerialNumber = $serial

        # Check Autopilot enrollment status
        Update-Output "Checking Autopilot enrollment status..."
        $enrolled = Get-AutopilotDevice -serial $serial -ErrorAction Stop

        if ($enrolled) {
            $enrollmentStatus.IsEnrolled = $true
            Update-Output "Device with serial number $serial is already enrolled in Autopilot"
        } else {
            Update-Output "Device with serial number $serial is not enrolled in Autopilot"
        }

        return $enrollmentStatus

    } catch {
        $errorMessage = "Error in Check-Enrol: $($_.Exception.Message)"
        Update-Output $errorMessage
        return @{
            IsEnrolled = $false
            SerialNumber = $null
            Error = $errorMessage
        }
    }
}
function Enroll-Autopilot {
    # Check requirements first
    if (-not (Test-AutopilotRequirements)) {
        Write-Host "Cannot proceed with Autopilot enrollment - requirements not met." -ForegroundColor Red
        return
    }
    
    $enrollmentStatus = Check-Enrol

    if ($enrollmentStatus.Error) {
        Update-Output "Error checking enrollment: $($enrollmentStatus.Error)"
        return
    }

    if ($enrollmentStatus.IsEnrolled) {
        Update-Output "Device with serial number $($enrollmentStatus.SerialNumber) is already enrolled in Autopilot."
        return
    }

    try {
        # Check if running as admin
        if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
            Update-Output "Please run as Administrator to enroll in Autopilot"
            return
        }

        Update-Output "Installing Autopilot PowerShell module"
        Install-Script -Name Get-WindowsAutoPilotInfo -Force
        Update-Output "Enrolling in Autopilot, please wait..."
        Get-WindowsAutoPilotInfo -online -assign
        Update-Output "Device successfully enrolled in Autopilot. Please wait 15 minutes for the device to be registered."
    }
    catch {
        Update-Output "Error enrolling device: $_"
    }
}

function Enroll-Autopilot-Custom {

    # Check requirements first
    if (-not (Test-AutopilotRequirements)) {
        Write-Host "Cannot proceed with Autopilot enrollment - requirements not met." -ForegroundColor Red
        return
    }
    
    $enrollmentStatus = Check-Enrol

    if ($enrollmentStatus.Error) {
        Update-Output "Error checking enrollment: $($enrollmentStatus.Error)"
        return
    }

    if ($enrollmentStatus.IsEnrolled) {
        Update-Output "Device with serial number $($enrollmentStatus.SerialNumber) is already enrolled in Autopilot."
        return
    }

    try {
        # Check if running as admin
        if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
            Update-Output "Please run as Administrator to enroll in Autopilot"
            return
        }

        Update-Output "Installing Autopilot PowerShell module"
        Install-Script -Name Get-WindowsAutoPilotInfo -Force
        Update-Output "Enrolling Custom Device in Autopilot, please wait..."
        Get-WindowsAutoPilotInfo -GroupTag CUSTOM -Online -assign
        Update-Output "Custom Device successfully enrolled in Autopilot. Please wait 15 minutes for the device to be registered."
    }
    catch {
        Update-Output "Error enrolling device: $_"
    }
}

function Enroll-Autopilot-MTR {
    # Check requirements first
    if (-not (Test-AutopilotRequirements)) {
        Write-Host "Cannot proceed with Autopilot enrollment - requirements not met." -ForegroundColor Red
        return
    }
    
    $enrollmentStatus = Check-Enrol

    if ($enrollmentStatus.Error) {
        Update-Output "Error checking enrollment: $($enrollmentStatus.Error)"
        return
    }

    if ($enrollmentStatus.IsEnrolled) {
        Update-Output "Device with serial number $($enrollmentStatus.SerialNumber) is already enrolled in Autopilot."
        return
    }

    try {
        # Check if running as admin
        if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
            Update-Output "Please run as Administrator to enroll in Autopilot"
            return
        }

        Update-Output "Installing Autopilot PowerShell module"
        Install-Script -Name Get-WindowsAutoPilotInfo -Force
        Update-Output "Enrolling Surface Hub in Autopilot, please wait..."
        Get-WindowsAutoPilotInfo -GroupTag MTR -Online -assign
        Update-Output "Surface Hub successfully enrolled in Autopilot. Please wait 15 minutes for the device to be registered."
    }
    catch {
        Update-Output "Error enrolling device: $_"
    }
}



function Enroll-Autopilot-csv {
    try {
        # Check if running as admin
        if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
            Update-Output "Please run as Administrator to enroll in Autopilot"
            return
        }

        if (!(test-path -path "c:\temp"))
    {
        new-item -path "c:\" -name "temp" -ItemType "directory"
        update-output "Creating c:\temp"
    }
        update-output "Installing Autopilot Powershell module"
        Install-Script -Name Get-WindowsAutoPilotInfo -Force
        Update-output "Creating Autopilot csv in c:\temp please wait..."
        Get-WindowsAutoPilotInfo -OutputFile c:\temp\AutopilotHWID.csv
        Update-Output "Hardware hash saved to c:\temp\AutopilotHWID.csv"
    }
    catch {
        Update-Output "Error enrolling device: $_"
    }
}

function Run-WindowsUpdate {
    try {
        if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
            Update-Output "Please run as Administrator to perform Windows Update"
            return
        }

        # Install required module
        Update-Output "Installing required PowerShell modules..."
        Install-Module PSWindowsUpdate -Force
        Update-Output "PSWindowsUpdate module installed successfully."

        # Import module
        Update-Output "Importing PSWindowsUpdate module..."
        Import-Module PSWindowsUpdate
        Update-Output "Module imported successfully."

        # Check for updates
        Update-Output "Checking for Windows Updates..."
        $updates = Get-WindowsUpdate
        
        if ($updates.Count -eq 0) {
            Update-Output "No Windows Updates are available."
            return
        }

        Update-Output "Found $($updates.Count) update(s). Starting installation..."
        
        # Install updates with progress
        Get-WindowsUpdate -Install -AcceptAll -AutoReboot | ForEach-Object {
            Update-Output "Installing update: $($_.Title)`nStatus: $($_.Status)"
        }
        
        Update-Output "Windows Update completed successfully. The system may need to reboot to apply some updates."
    }
    catch {
        Update-Output "Error running Windows Update: $_"
    }
}

function Get-SystemInfo {
    try {
        $info = Get-ComputerInfo
        $OS = ($info).windowsproductname
        $cpu = Get-WmiObject -Class Win32_Processor
        $ram = [math]::Round((Get-WmiObject -Class Win32_ComputerSystem).TotalPhysicalMemory / 1GB, 2)
        $disk = Get-WmiObject -Class Win32_LogicalDisk -Filter "DeviceID='C:'"
        $freeSpace = [math]::Round($disk.FreeSpace / 1GB, 2)
        $model = ($info).csmodel
        $bios = ($info).BiosSMBIOSBIOSVersion
        $locale = ($info).OsLanguage
        $serial = ($info).BiosSeralNumber

        $info = @"
CPU: $($cpu.Name)
RAM: $ram GB
Free Disk Space: $freeSpace GB
OS: $OS
Model: $model
BIOS Version: $bios
Serial Number: $serial
locale: $locale
 
 
"@

        Update-Output $info
    }
    catch {
        Update-Output "Error getting system information: $_"
    }
}

$form = Create-MainForm
if ($form -is [System.Windows.Forms.Form]) {
    [void]$form.ShowDialog()
} else {
    Write-Error "Failed to create the main form. Ensure the Create-MainForm function returns a valid System.Windows.Forms.Form object."
}
# SIG # Begin signature block
# MIIRCAYJKoZIhvcNAQcCoIIQ+TCCEPUCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUlY3uopc/rO65Ecd0kOHf7S5J
# tr6ggg47MIIG7zCCBNegAwIBAgITIQAAAAQ+jdT+OF0JbAAAAAAABDANBgkqhkiG
# 9w0BAQsFADCBzTExMC8GCSqGSIb3DQEJARYiaXQtc2VjdXJpdHktb2ZmaWNlckBp
# bXBlcmlhbC5hYy51azELMAkGA1UEBhMCVUsxDzANBgNVBAgTBkxvbmRvbjEPMA0G
# A1UEBxMGTG9uZG9uMSAwHgYDVQQKExdJbXBlcmlhbCBDb2xsZWdlIExvbmRvbjFH
# MEUGA1UEAxM+SW1wZXJpYWwgQ29sbGVnZSBMb25kb24gUm9vdCBTZWN1cmUgQ2Vy
# dGlmaWNhdGUgQXV0aG9yaXR5IDIwMjAwHhcNMjAwMjA0MTQ1ODE5WhcNNDEwMjA0
# MTUwODE5WjB+MRIwEAYKCZImiZPyLGQBGRYCdWsxEjAQBgoJkiaJk/IsZAEZFgJh
# YzESMBAGCgmSJomT8ixkARkWAmljMUAwPgYDVQQDEzdJbXBlcmlhbCBDb2xsZWdl
# IFN1Ym9yZGluYXRlIENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDIwMIICIjANBgkq
# hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwWXS9zPICV4jIdQq8gEWILjc77DGVFAX
# iQw5jnbpEbp0MRybyO7xhkgvPWjApnFBQVb2MLl38kXDGfFOkk6BxM8rdzbGk7dN
# 1+g5P1HMAxBEv7g3xwOMRhruXwmcFYhXO9aK5vm+LfwgB+7cI3Rald0xH88c/j2O
# 5oyXwipm8QKTq+uiQgc1hJicGTwpee+MgIKg4ErOGd7u6WworI4Q5ssTRLc4QsTN
# 3FsH/YoNcIH8YBOf6zsyyFVAp81wwtnpXbmb12t81xL8ilJCzId621nGmSaIieTG
# t5WO3lHb5hDAR6d7j5FGvilkG5NxLqxDcTcAZwzi47J4G17XLonzG2dis5gdbxuC
# csJtI15D4yIuelM0QL9tBv1rK9L3a9rNFw68PdKGLFdy45LxNKEPobW99x8akQF9
# BqkuiCQHPwy0J2IT8mGDFrwJOkQafuZH05oD9Ve0DSEmfkrb2ov3RJCtfMeoPXX8
# qqwciLbtqBPG7RdUkadvpa/57arpaaqpxmKbKWmQkkIVb70CIjoMsifZo8xOenvJ
# lW0PR1DklgBPYFyMVgkjkFBlfByI+RKl2LGHdhGkjYor5Vm+0BpuvmafhzmKq4hl
# t3x9w6ej3PG47hfRs2/x2rle98BXdEErbOYJQDiwC9zLWakbtXRbczJdpNRfcWOQ
# VRXfK/ohEAECAwEAAaOCARQwggEQMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQW
# BBSEUdMHUDoNmOjy1Qr0hY7LRkokGzAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMA
# QTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBSyoWQR
# aDlAlbayM02OogP+NFWLajCBggYDVR0fBHsweTB3oHWgc4ZxaHR0cDovL2ljY2E0
# LmljLmFjLnVrL0NlcnRFbnJvbGwvSW1wZXJpYWwlMjBDb2xsZWdlJTIwTG9uZG9u
# JTIwUm9vdCUyMFNlY3VyZSUyMENlcnRpZmljYXRlJTIwQXV0aG9yaXR5JTIwMjAy
# MC5jcmwwDQYJKoZIhvcNAQELBQADggIBAJynGpiQjCg0dKkERvQ1cjFGykU5AyFL
# su+UShC4yXsGEu07OfkmY2d+75A8p6hXzcZW/KgpWQ39eaeKITi5q5ChdsXHHszW
# /JZPFaJk1cV/DwPSKKa2KJDG1TtwYUWrVFZMR+r3ns+If6f5IPJMIEU8MmBncAo0
# KaIIljwS8uRSFBW9XmaGCrxFeRs+n2a+sjf+NUXtriCgJz+8lNGzyP9Ge1/1IU8b
# ZJuiV1/DgHSwqMHfVDxYPYY5RDHVnWRlSPN6R+d40Vd+ZxWq1jMxUCuRJPjjCmL3
# j/SuoAtJit5r+6yisR4jobV8wtgeFq7zLGYPz96/3BdPN/HEmmSLyi0AjiRY23jY
# cDAlM+cAgNgoMyoHt77PVrkYMM8NI5GSu0PcRT+pkCEyzyGdaVWUZhzN5TmPyvrj
# mwQsKuOY3B1Eid5xv+NBZGnJmJQfhiHzdu7J1h+xoLy6ylf0dkoBl1lV9JUSv8bY
# bTJ3Ds+FjwH6B/6ns7Qwq89d6WnnQJDwfWDPvOC+7s8jOGaYReijxkNE/8GUK69b
# Iw6oyCHjfMiyPMhnIMRZJP6jUPzP5Ka2xFOEWDWivfAolRtJjQbqGLQiITDLzY23
# bKIhdKL4FdGNd8a5z6HIe/r6Zhn4E4RkrJ8JP3dzbcxwLWVxxLk+GD2G0PP+vFJ8
# r24tTZBnA3U8MIIHRDCCBSygAwIBAgITbQAC4ullHJjXjzOpdwAAAALi6TANBgkq
# hkiG9w0BAQsFADB+MRIwEAYKCZImiZPyLGQBGRYCdWsxEjAQBgoJkiaJk/IsZAEZ
# FgJhYzESMBAGCgmSJomT8ixkARkWAmljMUAwPgYDVQQDEzdJbXBlcmlhbCBDb2xs
# ZWdlIFN1Ym9yZGluYXRlIENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDIwMB4XDTIx
# MDgxNzE1Mjc0M1oXDTI2MDgxNjE1Mjc0M1owfDEPMA0GA1UEBxMGTG9uZG9uMSAw
# HgYDVQQKExdJbXBlcmlhbCBDb2xsZWdlIExvbmRvbjEaMBgGA1UECxMRSUNUIEVV
# QyBDb2RlIFNpZ24xKzApBgkqhkiG9w0BCQEWHGphc29uLmJlbm5ldHRAaW1wZXJp
# YWwuYWMudWswggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIxO4kTEjy
# 59THp/+s5SzWc/GmI5U+EIiomLUl29lrQb5uKwxg771VbtAxDC7Cp/QMiVPWcZWg
# fuiU7ku2awzlYzi9VOMQO1vodpcBX3qHux1L3c2580l0N57qJnxMjHpcsbvnGcoM
# It9Etufkv1kLmTWiQOdFG1SRFKpF1hqvqjYHghThPQwz4ZdWn9m7+qWYJSrJ5SDp
# 4PredIgfJwcmbn14/wmihuXxAdxWd3gdgGPRiKdH2WeCTlVT9IBTu9gP5FBLhXUl
# Gg7lUkqVnJyxzlP6PhjvuVvOsSf+YfPTU/+cPfHBUAhMPw6KWcVCfZ46uvqSgBft
# zXvn6s6N8lpNAgMBAAGjggK7MIICtzA8BgkrBgEEAYI3FQcELzAtBiUrBgEEAYI3
# FQiGwpoMgYTsC82RAYTl+DaHmsUBRYHk5RmF75EkAgFkAgEIMBMGA1UdJQQMMAoG
# CCsGAQUFBwMDMAsGA1UdDwQEAwIFoDAbBgkrBgEEAYI3FQoEDjAMMAoGCCsGAQUF
# BwMDMB0GA1UdDgQWBBQlMfwwz4lk5V87PMknQfO9ttwfHDAfBgNVHSMEGDAWgBSE
# UdMHUDoNmOjy1Qr0hY7LRkokGzCB/wYDVR0fBIH3MIH0MIHxoIHuoIHrhoHobGRh
# cDovLy9DTj1JbXBlcmlhbCUyMENvbGxlZ2UlMjBTdWJvcmRpbmF0ZSUyMENlcnRp
# ZmljYXRlJTIwQXV0aG9yaXR5JTIwLTAwNzQwLENOPWljY2E0LENOPUNEUCxDTj1Q
# dWJsaWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0
# aW9uLERDPWljLERDPWFjLERDPXVrP2NlcnRpZmljYXRlUmV2b2NhdGlvbkxpc3Q/
# YmFzZT9vYmplY3RDbGFzcz1jUkxEaXN0cmlidXRpb25Qb2ludDCB9QYIKwYBBQUH
# AQEEgegwgeUwgeIGCCsGAQUFBzAChoHVbGRhcDovLy9DTj1JbXBlcmlhbCUyMENv
# bGxlZ2UlMjBTdWJvcmRpbmF0ZSUyMENlcnRpZmljYXRlJTIwQXV0aG9yaXR5JTIw
# LTAwNzQwLENOPUFJQSxDTj1QdWJsaWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2
# aWNlcyxDTj1Db25maWd1cmF0aW9uLERDPWljLERDPWFjLERDPXVrP2NBQ2VydGlm
# aWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0aWZpY2F0aW9uQXV0aG9yaXR5MA0G
# CSqGSIb3DQEBCwUAA4ICAQCPxZGIOolo569qE7sw2Kuf+wjYWN6XaedFn8ATJeJj
# IAZvUHhn8fDJrrdEfAsAEWUKVmh9wCJ8O93KmpoqDLw8gdhyz/I8eXssZkssonG5
# 1n5APZ9TlA2O2CIIGWNKtMFSjm+KUKmDTbGyq9xlwY6S3u376mKYoZJNoXHveyVP
# tRdvvAthN+XgFjbWaE5Y/xykOGI+CcMy+veL5/xEOStWRN6lFCuU1Mr3FiPy018o
# a9RUNouRvLV46pSBrDvuj0glOUFqjuqClZ/2qY0pCFZHtguV3VProVAJQIo9TjS4
# RcnQQ8oxMFXTU1bT4gWDw4pR6bZS3ozmGt3GFRDRZoI0Jly+ID1CCxEKFYOw1g1z
# jM+s/FdKfMDEbLZUVzzEPxCG45TDVI3PTPS/EZ8ySwyGq6qbwsK3HXPGCR1YA6zt
# K8Ua7qweTAC4jGIQSWv4RnZkZc/th0rHQEEa8MTKDIi+HrNzfAnXhqwJdgfimAc6
# Bl7BwGKtNRVMBxjoFrX3ruXXRi6FrTDPZ/f/0HXIu6T1KlKlWoFZj0++gROD1zFR
# UE9jUW6/Ug3hbGOiDDHf6uNCTrl7eK9BAhlkJQCO1/iJ/JwQz9+pkXD/rkODVssU
# c/XQxjRwjcnwWqsbU2hPGbfZuwAseQhgVlx//63swaKYxq6gDGuCySVBwenbvJ8Z
# VDGCAjcwggIzAgEBMIGVMH4xEjAQBgoJkiaJk/IsZAEZFgJ1azESMBAGCgmSJomT
# 8ixkARkWAmFjMRIwEAYKCZImiZPyLGQBGRYCaWMxQDA+BgNVBAMTN0ltcGVyaWFs
# IENvbGxlZ2UgU3Vib3JkaW5hdGUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMjAC
# E20AAuLpZRyY148zqXcAAAAC4ukwCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcCAQwx
# CjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGC
# NwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFJNhBGw+kb6397bW
# 1ZM7K+7nGMhcMA0GCSqGSIb3DQEBAQUABIIBACmS3Z7ALbyxjYMRq+HYkp1EdrNJ
# /NIWYm7pSDH+1grGPi+dHCLUiJHx1xtTbZAXyo2Xg37i2pvphrJUK+PbKtqguhNQ
# NFLqrpn6TMIniAZ4yNL1/tPBBVQggDD8R9Ko0PWYPaoUKK+XXxbxQc/aj9tiP2pG
# glzYzSSeCfuBZ1/xKSp0bcqv8Helx/L0+B9qd/T7b2+tSFeYsM6cPsbjW3KYfmFy
# s1MAHJkYyu1ZkiAd7+WQ5VBoPCxaZa24ELgoX3YWhl3s1hhpcKbuiPp952ZsR4G0
# CV05osty05lfO3NLxpL1gZs2nCUe+aBcq2BEvCF8C/T3FtNbOX0AaYi1F7A=
# SIG # End signature block