Windows Autopilot per USB: ISO mit WLAN-Profil und Autopilot-Konfiguration
Wer regelmäßig neue Windows-Geräte einrichtet, kennt den Ablauf: Windows installieren, das Gerät mit dem WLAN verbinden, das Autopilot-Profil laden und anschließend die Einrichtung über Intune starten oder noch ganz manuell. Autopilot nimmt einem bereits viel Arbeit ab. Bis die eigentliche Bereitstellung beginnt, sind aber trotzdem einige manuelle Schritte nötig.
Bei uns kam noch eine weitere Anforderung dazu. Wir wollten neue oder vollständig zurückgesetzte Geräte vorbereiten können, ohne auf jedem Gerät zuerst das WLAN-Passwort einzutippen oder den Hardware-Hash manuell zu importieren.
Dafür habe ich das Script create-windows-iso-with-apjson.ps1 von Andrew Taylor angepasst. Das Script erstellt aus einem normalen Windows-ISO ein eigenes Installationsmedium und integriert zwei zusätzliche Bausteine:
- die
AutopilotConfigurationFile.jsonmit unserem Autopilot-Profil - ein WLAN-Profil, das während der Windows-Installation automatisch importiert wird
Das fertige ISO wird anschließend mit Rufus auf einen USB-Stick geschrieben. Das Gerät startet vom Stick, installiert Windows, verbindet sich mit dem hinterlegten WLAN und wechselt direkt in die für unseren Tenant vorbereitete Autopilot-OOBE.
Was dabei genau automatisiert wird
Das Script erledigt beim Erstellen des ISO folgende Schritte:
- Es verbindet sich über Microsoft Graph mit dem Entra-Tenant.
- Es liest die vorhandenen Autopilot-Deployment-Profile aus Intune aus.
- Das gewünschte Profil wird ausgewählt und als
AutopilotConfigurationFile.jsonerzeugt. - Ein vorhandenes Windows-ISO wird eingebunden und die gewünschte Windows-Edition ausgewählt.
- Die
install.wimwird mit DISM gemountet. - Das Autopilot-Profil, das WLAN-Profil und die
unattend.xmlwerden in das Image kopiert. - Die angepasste
install.wimwird gespeichert und daraus ein neues ISO erzeugt.
Microsoft beschreibt dieses Verfahren als Windows Autopilot für vorhandene Geräte. Das Gerät muss für den ersten Durchlauf nicht vorher mit seinem Hardware-Hash im Autopilot-Dienst registriert sein. Stattdessen liest die OOBE das lokale Profil aus der AutopilotConfigurationFile.json. Unterstützt werden dabei benutzergesteuerte Profile für Microsoft Entra Join und Microsoft Entra Hybrid Join.
Voraussetzungen
Für den Aufbau werden folgende Komponenten benötigt:
- ein Microsoft Intune Tenant mit einem benutzergesteuerten Autopilot-Profil
- eine Entra-App-Registrierung für den Zugriff auf Microsoft Graph
- ein aktuelles Windows 10 oder Windows 11 ISO
- Windows PowerShell mit Administratorrechten
- das Modul
Microsoft.Graph.Authentication - Rufus oder ein vergleichbares Tool zum Erstellen des USB-Sticks
- ein WLAN, das während der Installation den Zugriff auf die benötigten Microsoft-Dienste erlaubt
Das Script kann das Windows-ISO über Fido herunterladen. In unserem Fall war es zuverlässiger, das ISO vorher direkt bei Microsoft herunterzuladen und über den Parameter -isopath zu übergeben. Damit entfällt eine zusätzliche Fehlerquelle.
Entra-App für den Graph-Zugriff
Für einen automatisierten Build verwende ich eine eigene App-Registrierung in Entra. Sie authentifiziert sich über Client-ID, Tenant-ID und Client Secret. Dadurch ist beim Erstellen des ISO keine interaktive Anmeldung erforderlich.
Das Script liest Autopilot-Profile und Informationen zur Organisation aus Microsoft Graph. Die benötigten Application Permissions:
| Berechtigung | Typ | Admin Consent |
|---|---|---|
| DeviceManagementConfiguration.ReadWrite.All | Application | Yes |
| DeviceManagementManagedDevices.ReadWrite.All | Application | Yes |
| DeviceManagementServiceConfig.ReadWrite.All | Application | Yes |
Der Aufruf sieht in unserer angepassten Version so aus:
.\New-AutopilotISOWithWiFi.ps1 `
-tenant "deinetenant.onmicrosoft.com" `
-clientid "00000000-0000-0000-0000-000000000000" `
-clientsecret "CLIENT_SECRET" `
-isopath "C:\ISO\Windows11.iso"Nach dem Start zeigt das Script die verfügbaren Autopilot-Profile und Windows-Editionen in einer GridView an. Nach der Auswahl wird das Image erstellt. Das Ergebnis liegt anschließend als windowswithautopilot.iso im temporären Arbeitsverzeichnis.
WLAN-Profil in das Windows-Image integrieren
Die eigentliche Anpassung besteht aus einer wificonf.xml und einer unattend.xml. SSID und Kennwort werden am Anfang des Scripts festgelegt:
$InjectWiFi = $true
$wifiSSID = "WIFI_SSID"
$wifiPassphrase = "WIFI_PASSWORD"Das Script erzeugt daraus ein Windows-WLAN-Profil. Für unser verstecktes Netz ist zusätzlich nonBroadcast gesetzt:
<?xml version="1.0"?>
<WLANProfile xmlns="http://www.microsoft.com/networking/WLAN/profile/v1">
<name>WIFI_SSID</name>
<SSIDConfig>
<SSID>
<name>WIFI_SSID</name>
</SSID>
<nonBroadcast>true</nonBroadcast>
</SSIDConfig>
<connectionType>ESS</connectionType>
<connectionMode>auto</connectionMode>
<MSM>
<security>
<authEncryption>
<authentication>WPA2PSK</authentication>
<encryption>AES</encryption>
<useOneX>false</useOneX>
</authEncryption>
<sharedKey>
<keyType>passPhrase</keyType>
<protected>false</protected>
<keyMaterial>WIFI_PASSWORD</keyMaterial>
</sharedKey>
</security>
</MSM>
</WLANProfile>Die Datei landet im Image unter:
C:\Windows\Setup\Scripts\wificonf.xmlDas Fertige Powershell Script
<#PSScriptInfo
.VERSION 3.0.6-wifi
.GUID 26fabcfd-1773-409e-a952-a8f94fbe660b
.AUTHOR AndrewTaylor (angepasst von Felix Ruby WiFi Injection ergaenzt)
.DESCRIPTION Creates a Windows 10/11 ISO using the latest download and auto-injects Autopilot JSON und WiFi Profil
.COMPANYNAME
.COPYRIGHT GPL
.TAGS intune endpoint MEM environment winget win32
.LICENSEURI https://github.com/andrew-s-taylor/public/blob/main/LICENSE
.PROJECTURI https://github.com/andrew-s-taylor/public
.ICONURI
.EXTERNALMODULEDEPENDENCIES
.REQUIREDSCRIPTS
.EXTERNALSCRIPTDEPENDENCIES
.RELEASENOTES
#>
<#
.SYNOPSIS
Creates a Windows 10/11 ISO using the latest download and auto-injects Autopilot JSON
.DESCRIPTION
.Downloads latest windows ISO
.Grabs Autopilot Profile
.Injects profile
.Creates new ISO
.INPUTS
Profile and Windows OS (from Gridview)
.OUTPUTS
In-Line Outputs
.NOTES
Version: 3.0.6
Author: Andrew Taylor
Twitter: @AndrewTaylor_2
WWW: andrewstaylor.com
Creation Date: 27/06/2023
Last Modified: 27/02/2024
Purpose/Change: Initial script development
Change: Amended to grab latest supported versions
Change: Now uses Fido (https://github.com/pbatard/Fido) to grab ISO URL
Change: Added Organization.Read.All to scopes
Change: Added support for multiple languages
Change: Languages fix
Change: Added support to select version
Change: JSON update
Change: Region fix
Change: Added ISO path parameter
Change: Replaced invoke-expression to work with PS7
.EXAMPLE
N/A
#>
[cmdletbinding()]
param
(
[string]$tenant #Tenant ID (optional) for when automating and you want to use across tenants instead of hard-coded
,
[string]$clientid #ClientID is the type of Azure AD App Reg ID
,
[string]$clientsecret #ClientSecret is the type of Azure AD App Reg Secret
,
[string]$isopath
)
###############################################################################################################
###### WiFi Injection Einstellungen ######
###############################################################################################################
#Auf $false setzen um WiFi Injection zu ueberspringen
$InjectWiFi = $true
#Unser WLAN ist ein verstecktes Netzwerk, daher nonBroadcast = true
$wifiSSID = "WIFI_SSID"
$wifiPassphrase = "WIFI_PASSWORD"
###############################################################################################################
###### Install Modules ######
###############################################################################################################
Write-Host "Installing Intune modules if required (current user scope)"
#Install MS Graph if not available
if (Get-Module -ListAvailable -Name Microsoft.Graph.Authentication) {
Write-Host "Microsoft Graph Already Installed"
}
else {
Install-Module -Name Microsoft.Graph.Authentication -Scope CurrentUser -Repository PSGallery -Force
}
import-module microsoft.graph.authentication
###############################################################################################################
###### Create Dir ######
###############################################################################################################
#Create path for files
$DirectoryToCreate = "c:\temp"
if (-not (Test-Path -LiteralPath $DirectoryToCreate)) {
try {
New-Item -Path $DirectoryToCreate -ItemType Directory -ErrorAction Stop | Out-Null #-Force
}
catch {
Write-Error -Message "Unable to create directory '$DirectoryToCreate'. Error was: $_" -ErrorAction Stop
}
"Successfully created directory '$DirectoryToCreate'."
}
else {
"Directory already existed"
}
$random = Get-Random -Maximum 1000
$random = $random.ToString()
$date =get-date -format yyMMddmmss
$date = $date.ToString()
$path2 = $random + "-" + $date
$path = "c:\temp\" + $path2
New-Item -ItemType Directory -Path $path
###############################################################################################################
###### Add Functions ######
###############################################################################################################
function GrabProfiles() {
# Defining Variables
$graphApiVersion = "beta"
$Resource = "deviceManagement/windowsAutopilotDeploymentProfiles"
$uri = "https://graph.microsoft.com/$graphApiVersion/$Resource"
$response = Invoke-MGGraphRequest -Uri $uri -Method Get -OutputType PSObject
$profiles = $response.value
$profilesNextLink = $response."@odata.nextLink"
while ($null -ne $profilesNextLink) {
$profilesResponse = (Invoke-MGGraphRequest -Uri $profilesNextLink -Method Get -outputType PSObject)
$profilesNextLink = $profilesResponse."@odata.nextLink"
$profiles += $profilesResponse.value
}
$selectedprofile = $profiles | out-gridview -passthru -title "Select a profile"
return $selectedprofile.id
}
function grabandoutput() {
[cmdletbinding()]
param
(
[string]$id
)
# Defining Variables
$graphApiVersion = "beta"
$Resource = "deviceManagement/windowsAutopilotDeploymentProfiles"
$uri = "https://graph.microsoft.com/$graphApiVersion/$Resource/$id"
$approfile = Invoke-MGGraphRequest -Uri $uri -Method Get -OutputType PSObject
# Set the org-related info
$script:TenantOrg = (Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/v1.0/organization" -OutputType PSObject).value
foreach ($domain in $script:TenantOrg.VerifiedDomains) {
if ($domain.isDefault) {
$script:TenantDomain = $domain.name
}
}
$oobeSettings = $approfile.outOfBoxExperienceSettings
# Build up properties
$json = @{}
$json.Add("Comment_File", "Profile $($approfile.displayName)")
$json.Add("Version", 2049)
$json.Add("ZtdCorrelationId", $approfile.id)
if ($approfile."@odata.type" -eq "#microsoft.graph.activeDirectoryWindowsAutopilotDeploymentProfile") {
$json.Add("CloudAssignedDomainJoinMethod", 1)
}
else {
$json.Add("CloudAssignedDomainJoinMethod", 0)
}
if ($approfile.deviceNameTemplate) {
$json.Add("CloudAssignedDeviceName", $approfile.deviceNameTemplate)
}
# Figure out config value
$oobeConfig = 8 + 256
if ($oobeSettings.userType -eq 'standard') {
$oobeConfig += 2
}
if ($oobeSettings.hidePrivacySettings -eq $true) {
$oobeConfig += 4
}
if ($oobeSettings.hideEULA -eq $true) {
$oobeConfig += 16
}
if ($oobeSettings.skipKeyboardSelectionPage -eq $true) {
$oobeConfig += 1024
if ($_.language) {
$json.Add("CloudAssignedLanguage", $_.language)
# Use the same value for region so that screen is skipped too
$json.Add("CloudAssignedRegion", $_.language)
}
}
if ($oobeSettings.deviceUsageType -eq 'shared') {
$oobeConfig += 32 + 64
}
$json.Add("CloudAssignedOobeConfig", $oobeConfig)
# Set the forced enrollment setting
if ($oobeSettings.hideEscapeLink -eq $true) {
$json.Add("CloudAssignedForcedEnrollment", 1)
}
else {
$json.Add("CloudAssignedForcedEnrollment", 0)
}
$json.Add("CloudAssignedTenantId", $script:TenantOrg.id)
$json.Add("CloudAssignedTenantDomain", $script:TenantDomain)
$embedded = @{}
$embedded.Add("CloudAssignedTenantDomain", $script:TenantDomain)
$embedded.Add("CloudAssignedTenantUpn", "")
if ($oobeSettings.hideEscapeLink -eq $true) {
$embedded.Add("ForcedEnrollment", 1)
}
else {
$embedded.Add("ForcedEnrollment", 0)
}
$ztc = @{}
$ztc.Add("ZeroTouchConfig", $embedded)
$json.Add("CloudAssignedAadServerData", (ConvertTo-JSON $ztc -Compress))
# Skip connectivity check
if ($approfile.hybridAzureADJoinSkipConnectivityCheck -eq $true) {
$json.Add("HybridJoinSkipDCConnectivityCheck", 1)
}
# Hard-code properties not represented in Intune
$json.Add("CloudAssignedAutopilotUpdateDisabled", 1)
$json.Add("CloudAssignedAutopilotUpdateTimeout", 1800000)
# Return the JSON
ConvertTo-JSON $json
}
Function Connect-ToGraph {
<#
.SYNOPSIS
Authenticates to the Graph API via the Microsoft.Graph.Authentication module.
.DESCRIPTION
The Connect-ToGraph cmdlet is a wrapper cmdlet that helps authenticate to the Intune Graph API using the Microsoft.Graph.Authentication module. It leverages an Azure AD app ID and app secret for authentication or user-based auth.
.PARAMETER Tenant
Specifies the tenant (e.g. contoso.onmicrosoft.com) to which to authenticate.
.PARAMETER AppId
Specifies the Azure AD app ID (GUID) for the application that will be used to authenticate.
.PARAMETER AppSecret
Specifies the Azure AD app secret corresponding to the app ID that will be used to authenticate.
.PARAMETER Scopes
Specifies the user scopes for interactive authentication.
.EXAMPLE
Connect-ToGraph -TenantId $tenantID -AppId $app -AppSecret $secret
-#>
[cmdletbinding()]
param
(
[Parameter(Mandatory = $false)] [string]$Tenant,
[Parameter(Mandatory = $false)] [string]$AppId,
[Parameter(Mandatory = $false)] [string]$AppSecret,
[Parameter(Mandatory = $false)] [string]$scopes
)
Process {
Import-Module Microsoft.Graph.Authentication
$version = (get-module microsoft.graph.authentication | Select-Object -expandproperty Version).major
if ($AppId -ne "") {
$body = @{
grant_type = "client_credentials";
client_id = $AppId;
client_secret = $AppSecret;
scope = "https://graph.microsoft.com/.default";
}
$response = Invoke-RestMethod -Method Post -Uri https://login.microsoftonline.com/$Tenant/oauth2/v2.0/token -Body $body
$accessToken = $response.access_token
$accessToken
if ($version -eq 2) {
write-host "Version 2 module detected"
$accesstokenfinal = ConvertTo-SecureString -String $accessToken -AsPlainText -Force
}
else {
write-host "Version 1 Module Detected"
Select-MgProfile -Name Beta
$accesstokenfinal = $accessToken
}
$graph = Connect-MgGraph -AccessToken $accesstokenfinal
Write-Host "Connected to Intune tenant $TenantId using app-based authentication (Azure AD authentication not supported)"
}
else {
if ($version -eq 2) {
write-host "Version 2 module detected"
}
else {
write-host "Version 1 Module Detected"
Select-MgProfile -Name Beta
}
$graph = Connect-MgGraph -scopes $scopes
Write-Host "Connected to Intune tenant $($graph.TenantId)"
}
}
}
###############################################################################################################
###### Graph Connection ######
###############################################################################################################
Write-Verbose "Connecting to Microsoft Graph"
if ($clientid -and $clientsecret -and $tenant) {
Connect-ToGraph -Tenant $tenant -AppId $clientid -AppSecret $clientsecret
write-output "Graph Connection Established"
}
else {
##Connect to Graph
Connect-ToGraph -scopes "Group.ReadWrite.All, Device.ReadWrite.All, DeviceManagementManagedDevices.ReadWrite.All, DeviceManagementServiceConfig.ReadWrite.All, GroupMember.ReadWrite.All, Domain.ReadWrite.All, Organization.Read.All"
}
Write-Verbose "Graph connection established"
###############################################################################################################
###### Execution ######
###############################################################################################################
##Grab all profiles and output to gridview
$selectedprofile = GrabProfiles
##Grab JSON for selected profile
$profilejson = grabandoutput -id $selectedprofile
##Set filename and filepath
$isocontents = "$path\iso\"
$wimname = "$isocontents\sources\install.wim"
$wimnametemp = "$path\installtemp.wim"
##check if ISO path has been passed
$isocheck = $PSBoundParameters.ContainsKey('isopath')
if ($isocheck -eq $true) {
$isofilename = $isopath
}
else {
$isofilename = "$path\microsoftwindows.iso"
write-host "Selecting OS"
write-host "Finding latest supported versions"
$allversions = @()
##Popup a gridview to select which OS to download and configure
###WINDOWS 11
$url = "https://learn.microsoft.com/en-us/windows/release-health/windows11-release-information"
$content = (Invoke-WebRequest -Uri $url -UseBasicParsing).content
[regex]$regex = "(?s)<tr class=.*?</tr>"
$tables = $regex.matches($content).groups.value
$tables = $tables.replace("<td>","")
$tables = $tables.replace("</td>","")
$tables = $tables.replace('<td align="left">',"")
$tables = $tables.replace('<tr class="highlight">',"")
$tables = $tables.replace("</tr>","")
##Add each found version for array
$availableversions = @()
foreach ($table in $tables) {
[array]$toArray = $table.Split("`n") | Where-Object {$_.Trim("")}
$availableversions += ($toArray[0]).Trim()
}
##We want n-1 so grab the first two objects
$Win11versions = $availableversions | select-object -first 2
####WINDOWS 10
##Scrape the release information to find latest supported versions
$url = "https://learn.microsoft.com/en-us/windows/release-health/release-information"
$content = (Invoke-WebRequest -Uri $url -UseBasicParsing).content
[regex]$regex = "(?s)<tr class=.*?</tr>"
$tables = $regex.matches($content).groups.value
$tables = $tables.replace("<td>","")
$tables = $tables.replace("</td>","")
$tables = $tables.replace('<td align="left">',"")
$tables = $tables.replace('<tr class="highlight">',"")
$tables = $tables.replace("</tr>","")
##Add each found version for array
$availableversions = @()
foreach ($table in $tables) {
[array]$toArray = $table.Split("`n") | Where-Object {$_.Trim("")}
$availableversions += ($toArray[0]).Trim()
}
##We want n-1 so grab the first two objects
$win10versions = $availableversions | select-object -first 2
##Create a custom object to store versions
foreach ($win11 in $Win11versions) {
$os = "11"
$osversion = $win11
$objectdetails = [pscustomobject]@{
Major = $os
Minor = $osversion
Name = "Windows $os $osversion"
}
$allversions += $objectdetails
}
foreach ($win10 in $Win10versions) {
$os = "10"
$osversion = $win10
$objectdetails = [pscustomobject]@{
Major = $os
Minor = $osversion
Name = "Windows $os $osversion"
}
$allversions += $objectdetails
}
$options = @()
foreach ($foundversion in $allversions) {
$options += $foundversion.name
}
$object = foreach($option in $options){new-object psobject -Property @{'Pick your Option' = $option}}
$osinput = $object | Out-GridView -Title "Windows Selection" -PassThru
$selectedname = $osinput.'Pick your Option'
$selectedos = $allversions | Where-Object Name -eq "$selectedname"
write-host "$selectedname Chosen"
##Prompt for language
write-output "Select a language"
$url = "https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/available-language-packs-for-windows?view=windows-11"
$content = (Invoke-WebRequest -Uri $url -UseBasicParsing).content
# Use regex to extract the first table from the HTML content
$tableRegex = '<table.*?>(.*?)</table>'
$tableMatches = [regex]::Matches($content, $tableRegex, [System.Text.RegularExpressions.RegexOptions]::Singleline)
$firstTable = $tableMatches[0].Value
$rowRegex = '<tr.*?>\s*<td.*?>.*?</td>\s*<td.*?>(.*?)</td>'
$rowMatches = [regex]::Matches($firstTable, $rowRegex, [System.Text.RegularExpressions.RegexOptions]::Singleline)
$rowgroups = $rowMatches.Groups
$languages = @()
foreach ($row in $rowgroups) {
$secondColumnContent = [regex]::Match($row.Value, '<td.*?>(.*?)</td>\s*<td.*?>(.*?)</td>').Groups[2].Value
if ($secondColumnContent) {
if ($secondColumnContent -notlike "*<p>*") {
$languages += $secondColumnContent
}
}
}
$selectedlanguage = $languages | Out-GridView -Title "Select a Language" -PassThru
write-host "$selectedlanguage Chosen"
##Convert to text
switch ($selectedlanguage) {
"ar-SA" { $Locale = "Arabic" }
"pt-BR" { $Locale = "Brazilian Portuguese" }
"bg-BG" { $Locale = "Bulgarian" }
"zh-CN" { $Locale = "Chinese (Simplified)" }
"zh-TW" { $Locale = "Chinese (Traditional)" }
"hr-HR" { $Locale = "Croatian" }
"cs-CZ" { $Locale = "Czech" }
"da-DK" { $Locale = "Danish" }
"nl-NL" { $Locale = "Dutch" }
"en-US" { $Locale = "English" }
"en-GB" { $Locale = "English International" }
"et-EE" { $Locale = "Estonian" }
"fi-FI" { $Locale = "Finnish" }
"fr-FR" { $Locale = "French" }
"fr-CA" { $Locale = "French Canadian" }
"de-DE" { $Locale = "German" }
"el-GR" { $Locale = "Greek" }
"he-IL" { $Locale = "Hebrew" }
"hu-HU" { $Locale = "Hungarian" }
"it-IT" { $Locale = "Italian" }
"ja-JP" { $Locale = "Japanese" }
"ko-KR" { $Locale = "Korean" }
"lv-LV" { $Locale = "Latvian" }
"lt-LT" { $Locale = "Lithuanian" }
"nb-NO" { $Locale = "Norwegian" }
"pl-PL" { $Locale = "Polish" }
"pt-PT" { $Locale = "Portuguese" }
"ro-RO" { $Locale = "Romanian" }
"ru-RU" { $Locale = "Russian" }
"sr-Latn-RS" { $Locale = "Serbian Latin" }
"sk-SK" { $Locale = "Slovak" }
"sl-SI" { $Locale = "Slovenian" }
"es-ES" { $Locale = "Spanish" }
"es-MX" { $Locale = "Spanish (Mexico)" }
"sv-SE" { $Locale = "Swedish" }
"th-TH" { $Locale = "Thai" }
"tr-TR" { $Locale = "Turkish" }
"uk-UA" { $Locale = "Ukrainian" }
default { $Locale = $selectedlanguage }
}
write-host "Locale $locale Selected"
##Download Fido
write-host "Downloading Fido"
$fidourl = "https://raw.githubusercontent.com/pbatard/Fido/master/Fido.ps1"
$fidopath = $path + "\fido.ps1"
Invoke-WebRequest -Uri $fidourl -OutFile $fidopath -UseBasicParsing
write-host "Fido Downloaded"
##Run Fido
# Set the parameters
$Win = $selectedos.Major
$Rel = $selectedos.Minor
$Ed = "Pro"
$GetUrl = $true
write-host "Grabbing ISO URL"
# Build the command string
$Command = "$fidopath -Lang '$Locale' -Win $Win -Rel $Rel -Ed $Ed -GetUrl"
write-host "Running $command"
# Run the command and store the output in a variable
$process = Start-Process -FilePath PowerShell.exe -ArgumentList "-Command & {$Command}" -NoNewWindow -PassThru -Wait -RedirectStandardOutput "$path\output.txt"
$windowsuri = Get-Content "$path\output.txt"
# Display the output
Write-Output $windowsuri
write-host "Downloading OS ISO"
##Download the OS
$download = Start-BitsTransfer -Source $windowsuri -Destination $isofilename -Asynchronous
while ($download.JobState -ne "Transferred") {
[int] $dlProgress = ($download.BytesTransferred / $download.BytesTotal) * 100;
Write-Progress -Activity "Downloading File..." -Status "$dlProgress% Complete:" -PercentComplete $dlProgress;
}
Complete-BitsTransfer $download.JobId;
write-host "Download Complete"
}
$isofilenamewithap = "$path\windowswithautopilot.iso"
##Mount the ISO
write-host "Mounting Windows ISO"
$mountiso = Mount-DiskImage $isofilename -PassThru
##Find the Drive Letter used
write-host "Detecting Drive Letter"
$ISODrive = (Get-DiskImage -ImagePath $isofilename | Get-Volume).DriveLetter
write-host "Drive Letter is $ISODrive"
##Copy the ISO files to manipulate
write-host "Copying ISO Contents"
$copyisofules = Copy-Item -Path $isodrive":" -Destination $isocontents -Recurse
write-host "Copying Complete"
##Select Image
write-host "Select Windows Version"
$wimpath = $isodrive+":\sources\install.wim"
$WinImages = Get-windowsimage -ImagePath $wimpath
$Report = @()
Foreach ($WinImage in $WinImages)
{
$curImage=Get-WindowsImage -ImagePath $wimpath -Index $WinImage.ImageIndex
$objImage = [PSCustomObject]@{
ImageIndex = $curImage.ImageIndex
ImageName = $curImage.ImageName
Version = $curImage.Version
Languages=$curImage.Languages
Architecture =$curImage.Architecture
}
$Report += $objImage
}
$imageselection = $Report | Out-GridView -PassThru
$imageindex = $imageselection.ImageIndex
##Copy the WIM to mount
write-host "Copying temporary WIM for manipulation"
$copywim = copy-item $wimname $wimnametemp
write-host "Copying Complete"
##Set further paths
$Image = $wimnametemp
$MountPoint = "$path\mount"
$InstallImage = $ISODrive+":"
$TargetISOFile = $isofilenamewithap
$ImageIndex = $imageindex
##WIM is read-only by default, we don't want that
write-host "Setting Temp WIM as read/write"
Set-ItemProperty -Path $image -Name IsReadOnly -Value $false
write-host "Set to read/write"
##Create the mount folder
write-host "Creating mount folder"
new-item "$path\mount" -ItemType Directory
Write-Host "Mount folder created"
##Mount the WIM
write-host "Mounting WIM"
Mount-WindowsImage -ImagePath $Image -Path $MountPoint -Index $ImageIndex
write-host "WIM Mounted"
##Inject the Autopilot JSON
write-host "Injecting Autopilot JSON"
$profilejson | Set-Content -Encoding Ascii "$MountPoint\Windows\Provisioning\Autopilot\AutopilotConfigurationFile.json"
write-host "JSON Injected"
##Inject WiFi Profil (wificonf.xml + unattend.xml, specialize pass)
if ($InjectWiFi -eq $true) {
write-host "Injecting WiFi Profil fuer $wifiSSID"
$wificonfContent = @"
<?xml version="1.0"?>
<WLANProfile xmlns="http://www.microsoft.com/networking/WLAN/profile/v1">
<name>$wifiSSID</name>
<SSIDConfig>
<SSID>
<name>$wifiSSID</name>
</SSID>
<nonBroadcast>true</nonBroadcast>
</SSIDConfig>
<connectionType>ESS</connectionType>
<connectionMode>auto</connectionMode>
<MSM>
<security>
<authEncryption>
<authentication>WPA2PSK</authentication>
<encryption>AES</encryption>
<useOneX>false</useOneX>
</authEncryption>
<sharedKey>
<keyType>passPhrase</keyType>
<protected>false</protected>
<keyMaterial>$wifiPassphrase</keyMaterial>
</sharedKey>
</security>
</MSM>
</WLANProfile>
"@
$scriptsPath = "$MountPoint\Windows\Setup\Scripts"
if (-not (Test-Path $scriptsPath)) {
New-Item -Path $scriptsPath -ItemType Directory -Force | Out-Null
}
$wificonfContent | Set-Content -Encoding UTF8 "$scriptsPath\wificonf.xml"
write-host "wificonf.xml Injected"
$unattendContent = @"
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<settings pass="specialize">
<component name="Microsoft-Windows-Deployment" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
<RunSynchronous>
<RunSynchronousCommand wcm:action="add">
<Order>1</Order>
<Path>cmd /c netsh wlan add profile filename="C:\Windows\Setup\Scripts\wificonf.xml"</Path>
<Description>Add WiFi Profile</Description>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>2</Order>
<Path>cmd /c netsh wlan connect name="$wifiSSID" ssid="$wifiSSID"</Path>
<Description>Connect to WiFi</Description>
</RunSynchronousCommand>
</RunSynchronous>
</component>
</settings>
</unattend>
"@
$pantherPath = "$MountPoint\Windows\Panther"
if (-not (Test-Path $pantherPath)) {
New-Item -Path $pantherPath -ItemType Directory -Force | Out-Null
}
$unattendContent | Set-Content -Encoding UTF8 "$pantherPath\unattend.xml"
write-host "unattend.xml Injected"
}
else {
write-host "WiFi Injection uebersprungen (InjectWiFi = false)"
}
##Dismount with the JSON und WiFi Profil injected
write-host "Dismounting WIM and Applying Changes"
Dismount-WindowsImage -Path $MountPoint -Save
write-host "WIM Dismounted"
##Again, install.wim is read-only
write-host "Setting install.wim as read/write"
Set-ItemProperty -Path $wimname -Name IsReadOnly -Value $false
write-host "Set to read/write"
##Remove the old install.wim
write-host "Removing install.wim from Sources directory"
remove-item $wimname
write-host "Removed"
#Export install.wim to replace old one
write-host "Exporting new install.wim to sources directory"
Export-WindowsIMage -SourceImagePath $Image -DestinationImagePath $wimname -SourceIndex $ImageIndex
write-host "Exported"
##Create a directory for oscdimg files
write-host "Creating oscdimg directory"
new-item -Path "$path\oscdimg" -ItemType Directory
write-host "Created"
#Set Paths for download
$url = "https://github.com/andrew-s-taylor/oscdimg/archive/main.zip"
$output = "$path\oscdimg.zip"
#Download Files
write-host "Downloading OSCDIMG Files"
Invoke-WebRequest -Uri $url -OutFile $output -Method Get
write-host "Download Complete"
#Unzip them
write-host "Unzipping Files"
Expand-Archive $output -DestinationPath "$path\oscdimg" -Force
Write-Host "Unzipped"
#Remove Zip file downloaded
write-host "Removing Zip File"
remove-item $output -Force
write-host "Removed"
# Create an ISO file from the installimage and new wim file
write-host "Creating ISO"
& "$path\oscdimg\oscdimg-main\oscdimg.exe" -b"$InstallImage\efi\microsoft\boot\efisys.bin" -pEF -u1 -udfver102 $isocontents $TargetISOFile
write-host "ISO $TargetISOFile created"
##Clean-up
write-host "Cleaning Environment"
##Dismount the ISO
write-host "Ejecting ISO"
$dismount = Dismount-DiskImage $isofilename
write-host "ISO Ejected"
##Remove the temporary wim
write-host "Removing temporary WIM"
remove-item $wimnametemp
write-host "Removed"
if ($isocheck -eq $true) {
##Leave ISO
}
else {
##Remove the original ISO
write-host "Removing original ISO"
remove-item $isofilename
write-host "Removed"
}
##Remove the extracted ISO contents
write-host "Removing extracted ISO contents"
remove-item $isocontents -Recurse -Force
write-host "Removed"
##Remove Mount folder
write-host "Removing mount folder"
remove-item "$path\mount" -recurse -Force
write-host "Removed"
##Remove oscdimg folder
write-host "Removing oscdimg folder"
remove-item "$path\oscdimg" -recurse -force
write-host "Removed"
##We're left with the new ISO and the autopilot JSON file
write-host "ISO Creation Complete"
# SIG # Begin signature block
# MIIoGQYJKoZIhvcNAQcCoIIoCjCCKAYCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCFwVK/c5oI/oNe
# L9zQwfI3wSAZhUWWHphtgHrfy94Vy6CCIRwwggWNMIIEdaADAgECAhAOmxiO+dAt
# 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV
# BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa
# Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy
# dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD
# ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
# ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E
# MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy
# unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF
# xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1
# 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB
# MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR
# WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6
# nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB
# YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S
# UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x
# q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB
# NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP
# TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC
# AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp
# Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv
# bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0
# aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB
# LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc
# Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov
# Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy
# oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW
# juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF
# mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z
# twGpn1eqXijiuZQwggauMIIElqADAgECAhAHNje3JFR82Ees/ShmKl5bMA0GCSqG
# SIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx
# GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRy
# dXN0ZWQgUm9vdCBHNDAeFw0yMjAzMjMwMDAwMDBaFw0zNzAzMjIyMzU5NTlaMGMx
# CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMy
# RGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcg
# Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDGhjUGSbPBPXJJUVXH
# JQPE8pE3qZdRodbSg9GeTKJtoLDMg/la9hGhRBVCX6SI82j6ffOciQt/nR+eDzMf
# UBMLJnOWbfhXqAJ9/UO0hNoR8XOxs+4rgISKIhjf69o9xBd/qxkrPkLcZ47qUT3w
# 1lbU5ygt69OxtXXnHwZljZQp09nsad/ZkIdGAHvbREGJ3HxqV3rwN3mfXazL6IRk
# tFLydkf3YYMZ3V+0VAshaG43IbtArF+y3kp9zvU5EmfvDqVjbOSmxR3NNg1c1eYb
# qMFkdECnwHLFuk4fsbVYTXn+149zk6wsOeKlSNbwsDETqVcplicu9Yemj052FVUm
# cJgmf6AaRyBD40NjgHt1biclkJg6OBGz9vae5jtb7IHeIhTZgirHkr+g3uM+onP6
# 5x9abJTyUpURK1h0QCirc0PO30qhHGs4xSnzyqqWc0Jon7ZGs506o9UD4L/wojzK
# QtwYSH8UNM/STKvvmz3+DrhkKvp1KCRB7UK/BZxmSVJQ9FHzNklNiyDSLFc1eSuo
# 80VgvCONWPfcYd6T/jnA+bIwpUzX6ZhKWD7TA4j+s4/TXkt2ElGTyYwMO1uKIqjB
# Jgj5FBASA31fI7tk42PgpuE+9sJ0sj8eCXbsq11GdeJgo1gJASgADoRU7s7pXche
# MBK9Rp6103a50g5rmQzSM7TNsQIDAQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB
# /wIBADAdBgNVHQ4EFgQUuhbZbU2FL3MpdpovdYxqII+eyG8wHwYDVR0jBBgwFoAU
# 7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoG
# CCsGAQUFBwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29j
# c3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDig
# NqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9v
# dEc0LmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZI
# hvcNAQELBQADggIBAH1ZjsCTtm+YqUQiAX5m1tghQuGwGC4QTRPPMFPOvxj7x1Bd
# 4ksp+3CKDaopafxpwc8dB+k+YMjYC+VcW9dth/qEICU0MWfNthKWb8RQTGIdDAiC
# qBa9qVbPFXONASIlzpVpP0d3+3J0FNf/q0+KLHqrhc1DX+1gtqpPkWaeLJ7giqzl
# /Yy8ZCaHbJK9nXzQcAp876i8dU+6WvepELJd6f8oVInw1YpxdmXazPByoyP6wCeC
# RK6ZJxurJB4mwbfeKuv2nrF5mYGjVoarCkXJ38SNoOeY+/umnXKvxMfBwWpx2cYT
# gAnEtp/Nh4cku0+jSbl3ZpHxcpzpSwJSpzd+k1OsOx0ISQ+UzTl63f8lY5knLD0/
# a6fxZsNBzU+2QJshIUDQtxMkzdwdeDrknq3lNHGS1yZr5Dhzq6YBT70/O3itTK37
# xJV77QpfMzmHQXh6OOmc4d0j/R0o08f56PGYX/sr2H7yRp11LB4nLCbbbxV7HhmL
# NriT1ObyF5lZynDwN7+YAN8gFk8n+2BnFqFmut1VwDophrCYoCvtlUG3OtUVmDG0
# YgkPCr2B2RP+v6TR81fZvAT6gt4y3wSJ8ADNXcL50CN/AAvkdgIm2fBldkKmKYcJ
# RyvmfxqkhQ/8mJb2VVQrH4D6wPIOK+XW+6kvRBVK5xMOHds3OBqhK/bt1nz8MIIG
# sDCCBJigAwIBAgIQCK1AsmDSnEyfXs2pvZOu2TANBgkqhkiG9w0BAQwFADBiMQsw
# CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
# ZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQw
# HhcNMjEwNDI5MDAwMDAwWhcNMzYwNDI4MjM1OTU5WjBpMQswCQYDVQQGEwJVUzEX
# MBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0
# ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExMIICIjAN
# BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1bQvQtAorXi3XdU5WRuxiEL1M4zr
# PYGXcMW7xIUmMJ+kjmjYXPXrNCQH4UtP03hD9BfXHtr50tVnGlJPDqFX/IiZwZHM
# gQM+TXAkZLON4gh9NH1MgFcSa0OamfLFOx/y78tHWhOmTLMBICXzENOLsvsI8Irg
# nQnAZaf6mIBJNYc9URnokCF4RS6hnyzhGMIazMXuk0lwQjKP+8bqHPNlaJGiTUyC
# EUhSaN4QvRRXXegYE2XFf7JPhSxIpFaENdb5LpyqABXRN/4aBpTCfMjqGzLmysL0
# p6MDDnSlrzm2q2AS4+jWufcx4dyt5Big2MEjR0ezoQ9uo6ttmAaDG7dqZy3SvUQa
# khCBj7A7CdfHmzJawv9qYFSLScGT7eG0XOBv6yb5jNWy+TgQ5urOkfW+0/tvk2E0
# XLyTRSiDNipmKF+wc86LJiUGsoPUXPYVGUztYuBeM/Lo6OwKp7ADK5GyNnm+960I
# HnWmZcy740hQ83eRGv7bUKJGyGFYmPV8AhY8gyitOYbs1LcNU9D4R+Z1MI3sMJN2
# FKZbS110YU0/EpF23r9Yy3IQKUHw1cVtJnZoEUETWJrcJisB9IlNWdt4z4FKPkBH
# X8mBUHOFECMhWWCKZFTBzCEa6DgZfGYczXg4RTCZT/9jT0y7qg0IU0F8WD1Hs/q2
# 7IwyCQLMbDwMVhECAwEAAaOCAVkwggFVMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYD
# VR0OBBYEFGg34Ou2O/hfEYb7/mF7CIhl9E5CMB8GA1UdIwQYMBaAFOzX44LScV1k
# TN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcD
# AzB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2lj
# ZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29t
# L0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0
# cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmww
# HAYDVR0gBBUwEzAHBgVngQwBAzAIBgZngQwBBAEwDQYJKoZIhvcNAQEMBQADggIB
# ADojRD2NCHbuj7w6mdNW4AIapfhINPMstuZ0ZveUcrEAyq9sMCcTEp6QRJ9L/Z6j
# fCbVN7w6XUhtldU/SfQnuxaBRVD9nL22heB2fjdxyyL3WqqQz/WTauPrINHVUHmI
# moqKwba9oUgYftzYgBoRGRjNYZmBVvbJ43bnxOQbX0P4PpT/djk9ntSZz0rdKOtf
# JqGVWEjVGv7XJz/9kNF2ht0csGBc8w2o7uCJob054ThO2m67Np375SFTWsPK6Wrx
# oj7bQ7gzyE84FJKZ9d3OVG3ZXQIUH0AzfAPilbLCIXVzUstG2MQ0HKKlS43Nb3Y3
# LIU/Gs4m6Ri+kAewQ3+ViCCCcPDMyu/9KTVcH4k4Vfc3iosJocsL6TEa/y4ZXDlx
# 4b6cpwoG1iZnt5LmTl/eeqxJzy6kdJKt2zyknIYf48FWGysj/4+16oh7cGvmoLr9
# Oj9FpsToFpFSi0HASIRLlk2rREDjjfAVKM7t8RhWByovEMQMCGQ8M4+uKIw8y4+I
# Cw2/O/TOHnuO77Xry7fwdxPm5yg/rBKupS8ibEH5glwVZsxsDsrFhsP2JjMMB0ug
# 0wcCampAMEhLNKhRILutG4UI4lkNbcoFUCvqShyepf2gpx8GdOfy1lKQ/a+FSCH5
# Vzu0nAPthkX0tGFuv2jiJmCG6sivqf6UHedjGzqGVnhOMIIGwjCCBKqgAwIBAgIQ
# BUSv85SdCDmmv9s/X+VhFjANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEX
# MBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0
# ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTIzMDcxNDAw
# MDAwMFoXDTM0MTAxMzIzNTk1OVowSDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRp
# Z2lDZXJ0LCBJbmMuMSAwHgYDVQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMzCC
# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKNTRYcdg45brD5UsyPgz5/X
# 5dLnXaEOCdwvSKOXejsqnGfcYhVYwamTEafNqrJq3RApih5iY2nTWJw1cb86l+uU
# UI8cIOrHmjsvlmbjaedp/lvD1isgHMGXlLSlUIHyz8sHpjBoyoNC2vx/CSSUpIIa
# 2mq62DvKXd4ZGIX7ReoNYWyd/nFexAaaPPDFLnkPG2ZS48jWPl/aQ9OE9dDH9kgt
# XkV1lnX+3RChG4PBuOZSlbVH13gpOWvgeFmX40QrStWVzu8IF+qCZE3/I+PKhu60
# pCFkcOvV5aDaY7Mu6QXuqvYk9R28mxyyt1/f8O52fTGZZUdVnUokL6wrl76f5P17
# cz4y7lI0+9S769SgLDSb495uZBkHNwGRDxy1Uc2qTGaDiGhiu7xBG3gZbeTZD+BY
# QfvYsSzhUa+0rRUGFOpiCBPTaR58ZE2dD9/O0V6MqqtQFcmzyrzXxDtoRKOlO0L9
# c33u3Qr/eTQQfqZcClhMAD6FaXXHg2TWdc2PEnZWpST618RrIbroHzSYLzrqawGw
# 9/sqhux7UjipmAmhcbJsca8+uG+W1eEQE/5hRwqM/vC2x9XH3mwk8L9CgsqgcT2c
# kpMEtGlwJw1Pt7U20clfCKRwo+wK8REuZODLIivK8SgTIUlRfgZm0zu++uuRONhR
# B8qUt+JQofM604qDy0B7AgMBAAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYD
# VR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgG
# BmeBDAEEAjALBglghkgBhv1sBwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxq
# II+eyG8wHQYDVR0OBBYEFKW27xPn783QZKHVVqllMaPe1eNJMFoGA1UdHwRTMFEw
# T6BNoEuGSWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
# NFJTQTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGD
# MIGAMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYB
# BQUHMAKGTGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0
# ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQEL
# BQADggIBAIEa1t6gqbWYF7xwjU+KPGic2CX/yyzkzepdIpLsjCICqbjPgKjZ5+PF
# 7SaCinEvGN1Ott5s1+FgnCvt7T1IjrhrunxdvcJhN2hJd6PrkKoS1yeF844ektrC
# QDifXcigLiV4JZ0qBXqEKZi2V3mP2yZWK7Dzp703DNiYdk9WuVLCtp04qYHnbUFc
# jGnRuSvExnvPnPp44pMadqJpddNQ5EQSviANnqlE0PjlSXcIWiHFtM+YlRpUurm8
# wWkZus8W8oM3NG6wQSbd3lqXTzON1I13fXVFoaVYJmoDRd7ZULVQjK9WvUzF4UbF
# KNOt50MAcN7MmJ4ZiQPq1JE3701S88lgIcRWR+3aEUuMMsOI5ljitts++V+wQtaP
# 4xeR0arAVeOGv6wnLEHQmjNKqDbUuXKWfpd5OEhfysLcPTLfddY2Z1qJ+Panx+VP
# NTwAvb6cKmx5AdzaROY63jg7B145WPR8czFVoIARyxQMfq68/qTreWWqaNYiyjvr
# moI1VygWy2nyMpqy0tg6uLFGhmu6F/3Ed2wVbK6rr3M66ElGt9V/zLY4wNjsHPW2
# obhDLN9OTH0eaHDAdwrUAuBcYLso/zjlUlrWrBciI0707NMX+1Br/wd3H3GXREHJ
# uEbTbDJ8WC9nR2XlG3O2mflrLAZG70Ee8PBf4NvZrZCARK+AEEGKMIIHWzCCBUOg
# AwIBAgIQCLGfzbPa87AxVVgIAS8A6TANBgkqhkiG9w0BAQsFADBpMQswCQYDVQQG
# EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0
# IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0Ex
# MB4XDTIzMTExNTAwMDAwMFoXDTI2MTExNzIzNTk1OVowYzELMAkGA1UEBhMCR0Ix
# FDASBgNVBAcTC1doaXRsZXkgQmF5MR4wHAYDVQQKExVBTkRSRVdTVEFZTE9SLkNP
# TSBMVEQxHjAcBgNVBAMTFUFORFJFV1NUQVlMT1IuQ09NIExURDCCAiIwDQYJKoZI
# hvcNAQEBBQADggIPADCCAgoCggIBAMOkYkLpzNH4Y1gUXF799uF0CrwW/Lme676+
# C9aZOJYzpq3/DIa81oWv9b4b0WwLpJVu0fOkAmxI6ocu4uf613jDMW0GfV4dRodu
# tryfuDuit4rndvJA6DIs0YG5xNlKTkY8AIvBP3IwEzUD1f57J5GiAprHGeoc4Utt
# zEuGA3ySqlsGEg0gCehWJznUkh3yM8XbksC0LuBmnY/dZJ/8ktCwCd38gfZEO9UD
# DSkie4VTY3T7VFbTiaH0bw+AvfcQVy2CSwkwfnkfYagSFkKar+MYwu7gqVXxrh3V
# /Gjval6PdM0A7EcTqmzrCRtvkWIR6bpz+3AIH6Fr6yTuG3XiLIL6sK/iF/9d4U2P
# iH1vJ/xfdhGj0rQ3/NBRsUBC3l1w41L5q9UX1Oh1lT1OuJ6hV/uank6JY3jpm+Of
# Z7YCTF2Hkz5y6h9T7sY0LTi68Vmtxa/EgEtG6JVNVsqP7WwEkQRxu/30qtjyoX8n
# zSuF7TmsRgmZ1SB+ISclejuqTNdhcycDhi3/IISgVJNRS/F6Z+VQGf3fh6ObdQLV
# woT0JnJjbD8PzJ12OoKgViTQhndaZbkfpiVifJ1uzWJrTW5wErH+qvutHVt4/sEZ
# AVS4PNfOcJXR0s0/L5JHkjtM4aGl62fAHjHj9JsClusj47cT6jROIqQI4ejz1slO
# oclOetCNAgMBAAGjggIDMIIB/zAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiI
# ZfROQjAdBgNVHQ4EFgQU0HdOFfPxa9Yeb5O5J9UEiJkrK98wPgYDVR0gBDcwNTAz
# BgZngQwBBAEwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20v
# Q1BTMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzCBtQYDVR0f
# BIGtMIGqMFOgUaBPhk1odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
# cnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDBToFGg
# T4ZNaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29k
# ZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcmwwgZQGCCsGAQUFBwEBBIGH
# MIGEMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wXAYIKwYB
# BQUHMAKGUGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0
# ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3J0MAkGA1UdEwQC
# MAAwDQYJKoZIhvcNAQELBQADggIBAEkRh2PwMiyravr66Zww6Pjl24KzDcGYMSxU
# KOEU4bykcOKgvS6V2zeZIs0D/oqct3hBKTGESSQWSA/Jkr1EMC04qJHO/Twr/sBD
# CDBMtJ9XAtO75J+oqDccM+g8Po+jjhqYJzKvbisVUvdsPqFll55vSzRvHGAA6hjy
# DyakGLROcNaSFZGdgOK2AMhQ8EULrE8Riri3D1ROuqGmUWKqcO9aqPHBf5wUwia8
# g980sTXquO5g4TWkZqSvwt1BHMmu69MR6loRAK17HvFcSicK6Pm0zid1KS2z4ntG
# B4Cfcg88aFLog3ciP2tfMi2xTnqN1K+YmU894Pl1lCp1xFvT6prm10Bs6BViKXfD
# fVFxXTB0mHoDNqGi/B8+rxf2z7u5foXPCzBYT+Q3cxtopvZtk29MpTY88GHDVJsF
# MBjX7zM6aCNKsTKC2jb92F+jlkc8clCQQnl3U4jqwbj4ur1JBP5QxQprWhwde0+M
# ifDVp0vHZsVZ0pnYMCKSG5bUr3wOU7EP321DwvvEsTjCy/XDgvy8ipU6w3GjcQQF
# mgp/BX/0JCHX+04QJ0JkR9TTFZR1B+zh3CcK1ZEtTtvuZfjQ3viXwlwtNLy43vbe
# 1J5WNTs0HjJXsfdbhY5kE5RhyfaxFBr21KYx+b+evYyolIS0wR6New6FqLgcc4Ge
# 94yaYVTqMYIGUzCCBk8CAQEwfTBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGln
# aUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBT
# aWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExAhAIsZ/Ns9rzsDFVWAgBLwDp
# MA0GCWCGSAFlAwQCAQUAoIGEMBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJ
# KoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQB
# gjcCARUwLwYJKoZIhvcNAQkEMSIEINaRtsdfA3gHmAe7XGlKARlmY/tqtc2gvai3
# XSoTYa5FMA0GCSqGSIb3DQEBAQUABIICAMOZ3v6+vijgg98VJKV0l6s9R9SqquCw
# ll/DnMGgAcOVBx25ttjiE6hjB518AKj8fqX5rUCMD6r0pjSLaSotnE1fN7Y+ceTO
# t9pvCmFlKJe9071LWzeP1MMsawRbKDvarS6gjFmMDrgN+2GL7xPInyZv6qLWsdDw
# 71vsrW4MXRVNsAAl1rS9nJoJUnwOxIkgC1wPLfCjKrYxIsr3P4NEcrsbJZZkl5x5
# hhV8t6knllAN2wxwrJ5NY+RDV8L8MwL+Q7U1ZR1FMHc/ca4DM0u96L1q2h14Wtbt
# +rgdV00nSUzbV5tJjWlHTZwoT/brkM6EIas7Y1Tm04f2HqMwt7Nr75ZcDmTzvV4y
# rXqC0cuQSiM/i9MjWq6Z8ndek98GzZGOK/KbWpRWtlJkG+pWho5qVZ8GUi3LKfPJ
# DUB8OiZdPf9HZWa2msHutxYiQyU4oA9X2rASJSbsVeE+AzoPtpmTtlYtJSd3Bjyp
# 63f+F11lSAVzQTG6pCRLtk9v+BVg/bql9Un6izgILm9dU+bvy/Q7DMKHcZIBr4wy
# FjSv48I8QMz3+zSREVZ4C1uZ8mIGFYFMw2egz7LDcyE7jcsRc0emvTlsRZxyZ1Kx
# dJN1PIv71pQjmSYWOPnwXZFl7UKg84JicImrRoVybqnfR8pycusVfBj2GJmOLlKo
# 0YV79AsGVD8QoYIDIDCCAxwGCSqGSIb3DQEJBjGCAw0wggMJAgEBMHcwYzELMAkG
# A1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdp
# Q2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQQIQ
# BUSv85SdCDmmv9s/X+VhFjANBglghkgBZQMEAgEFAKBpMBgGCSqGSIb3DQEJAzEL
# BgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTI0MDIyNzEwMzY1M1owLwYJKoZI
# hvcNAQkEMSIEIGUizJ4gl4QxtdcCOY1b6TP9Q1KBSlbE0DOY71Yt/kTPMA0GCSqG
# SIb3DQEBAQUABIICADa4YLhuJTuiitnkTNoWgHFMan9UTThL1jiPjCkO6vS+NmoP
# qEQYEWQjzjmrsgq/aGTUw/JHCAAToKMweIvaA3WsaCpSkzWElD+TPDgxn2JUek5d
# GyMKdjrMZ6Za8ybB3ujA1wPrnTvayuXVGSq8r4OENqhBrxWlnaNZ7Pm6O1Dq4JBU
# Y4yfPBUoFwe0CX6o8UAZNyIISciC/cI8tgPj/93iVpUX9QKmov1wcbp4o+dUVTq6
# nPFXB4pQhKTnpx0pWW60XwrykVv8bKLkYwZ8QvmNK/ccHj4vfZr54rPWAQxqrd1b
# 0+Nwtn0jaBWRR+JjHraW8xFecpiWt6PrPzEuCBhHPaeWUoSaR2u8bo2MAMJ9vZlN
# Qkw/9RtxbgxM6eW/BXss9VUTJY/XcC676x+iM/XYvh4wZbgg9AUM4zBVFxQen9p+
# MHliwph0pQN0anJtTmuosSpPRcSVvEzsZhaNggz4ZZhlR1KDVKmWEO27105a8AEX
# P4qguiQEzVnxX22FYDSHdKnU9fO2LPzFORvJhAODDp/VRk752xOKkonf6xFsLnYa
# rlmJJfOmAfZxTZQaLVHOF6KtQveVRze0oELqh8pUct+POM+NjaOYJz5BNEKTSoiJ
# KNGlgI6acVLdNsVW+XUK9/0K+CUiIVq3kDyfNMijNfN25MVR4wkZU6g933/C
# SIG # End signature block
New-AutopilotISOWithWiFi.ps1