How-To: Advanced Windows 11 Unattended Installation with PowerShell

AI Preview

Is this content worth your time? Use the quick preview to decide.

Welcome to Techaptic!

In today’s guide, we push the boundaries of flexibility in Windows 11 deployment by moving beyond standard static unattended installations. We explore how to empower answer files with custom PowerShell scripts to make your setup process highly adaptable to different requirements.

We dive deep into configuring the autounattend.xml file for zero-touch deployment and crafting a light-touch PowerShell GUI that dynamically handles Active Directory joining, organizational unit placement, and automated software installation via WinGet. Follow along to establish a high standard of deployment in your own lab or enterprise environments using a single installation media.

🔗 Helpful Links

Video Tutorial

Used Commands


The command line written under the "FirstLogon" section.

for %%i in (C D E F G H I J K L M N O P Q R S T U V W X Y Z) do if exist %%i:\Techaptic_Provisioning.ps1 powershell.exe -ExecutionPolicy Bypass -File %%i:\Techaptic_Provisioning.ps1

The "Techaptic_Provisioning.ps1" script inside the video.

<#
.SYNOPSIS
Techaptic Enterprise Post-Install Provisioning Tool
.DESCRIPTION
Automates post-install tasks: prioritizes Network Deployment Type, dynamically toggles
UI elements based on domain/workgroup selection, and performs single-step AD join.
#>

# --- PART 1: GUI FOR DATA COLLECTION ---
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

$Form = New-Object System.Windows.Forms.Form
$Form.Text = "Techaptic Enterprise Provisioning Manager"
$Form.Size = New-Object System.Drawing.Size(450, 560)
$Form.StartPosition = "CenterScreen"
$Form.BackColor = [System.Drawing.Color]::White

# 1. Network Deployment Type (Moved to TOP)
$GrpNet = New-Object System.Windows.Forms.GroupBox
$GrpNet.Text = "Network Deployment Type"
$GrpNet.Location = New-Object System.Drawing.Point(20, 20)
$GrpNet.Size = New-Object System.Drawing.Size(380, 60)
$Form.Controls.Add($GrpNet)

$RadWG = New-Object System.Windows.Forms.RadioButton
$RadWG.Text = "Workgroup"
$RadWG.Location = New-Object System.Drawing.Point(20, 25)
$RadWG.Size = New-Object System.Drawing.Size(100, 20)
$RadWG.Checked = $true # Workgroup is default
$GrpNet.Controls.Add($RadWG)

$RadDomain = New-Object System.Windows.Forms.RadioButton
$RadDomain.Text = "Active Directory"
$RadDomain.Location = New-Object System.Drawing.Point(150, 25)
$RadDomain.Size = New-Object System.Drawing.Size(160, 20)
$GrpNet.Controls.Add($RadDomain)

# 2. Location Selection
$LblLoc = New-Object System.Windows.Forms.Label
$LblLoc.Text = "Location:"
$LblLoc.Location = New-Object System.Drawing.Point(20, 100)
$LblLoc.Enabled = $false # Greyed out by default
$Form.Controls.Add($LblLoc)

$ComboLoc = New-Object System.Windows.Forms.ComboBox
$ComboLoc.Location = New-Object System.Drawing.Point(150, 97)
$ComboLoc.Size = New-Object System.Drawing.Size(250, 20)
$ComboLoc.Items.AddRange(@("IST - Istanbul", "LON - London", "MON - Montreal"))
$ComboLoc.SelectedIndex = 0
$ComboLoc.Enabled = $false # Greyed out by default
$Form.Controls.Add($ComboLoc)

# 3. Department Selection
$LblDept = New-Object System.Windows.Forms.Label
$LblDept.Text = "Department:"
$LblDept.Location = New-Object System.Drawing.Point(20, 140)
$LblDept.Enabled = $false # Greyed out by default
$Form.Controls.Add($LblDept)

$ComboDept = New-Object System.Windows.Forms.ComboBox
$ComboDept.Location = New-Object System.Drawing.Point(150, 137)
$ComboDept.Size = New-Object System.Drawing.Size(250, 20)
$ComboDept.Items.AddRange(@("ACC - Accounting", "HR - Human Resources", "IT - Info Tech", "MNG - Managers"))
$ComboDept.SelectedIndex = 2
$ComboDept.Enabled = $false # Greyed out by default
$Form.Controls.Add($ComboDept)

# 4. Application Selection (Always enabled)
$GrpApps = New-Object System.Windows.Forms.GroupBox
$GrpApps.Text = "Enterprise Applications (Winget)"
$GrpApps.Location = New-Object System.Drawing.Point(20, 190)
$GrpApps.Size = New-Object System.Drawing.Size(380, 80)
$Form.Controls.Add($GrpApps)

$ChkChrome = New-Object System.Windows.Forms.CheckBox
$ChkChrome.Text = "Google Chrome"
$ChkChrome.Location = New-Object System.Drawing.Point(20, 30)
$ChkChrome.Checked = $true
$GrpApps.Controls.Add($ChkChrome)

$ChkAcrobat = New-Object System.Windows.Forms.CheckBox
$ChkAcrobat.Text = "Adobe Acrobat"
$ChkAcrobat.Location = New-Object System.Drawing.Point(140, 30)
$ChkAcrobat.Checked = $true
$GrpApps.Controls.Add($ChkAcrobat)

$ChkWinRar = New-Object System.Windows.Forms.CheckBox
$ChkWinRar.Text = "WinRAR"
$ChkWinRar.Location = New-Object System.Drawing.Point(270, 30)
$ChkWinRar.Checked = $true
$GrpApps.Controls.Add($ChkWinRar)

# 5. Active Directory Group
$GrpAD = New-Object System.Windows.Forms.GroupBox
$GrpAD.Text = "Active Directory Details (ad.techaptic.com)"
$GrpAD.Location = New-Object System.Drawing.Point(20, 290)
$GrpAD.Size = New-Object System.Drawing.Size(380, 120)
$GrpAD.Enabled = $false # Greyed out by default
$Form.Controls.Add($GrpAD)

$LblUser = New-Object System.Windows.Forms.Label
$LblUser.Text = "Admin Account:"
$LblUser.Location = New-Object System.Drawing.Point(20, 35)
$GrpAD.Controls.Add($LblUser)

$TxtUser = New-Object System.Windows.Forms.TextBox
$TxtUser.Text = "[email protected]"
$TxtUser.Location = New-Object System.Drawing.Point(120, 32)
$TxtUser.Size = New-Object System.Drawing.Size(200, 20)
$GrpAD.Controls.Add($TxtUser)

$LblPass = New-Object System.Windows.Forms.Label
$LblPass.Text = "Password:"
$LblPass.Location = New-Object System.Drawing.Point(20, 75)
$GrpAD.Controls.Add($LblPass)

$TxtPass = New-Object System.Windows.Forms.TextBox
$TxtPass.Location = New-Object System.Drawing.Point(120, 72)
$TxtPass.Size = New-Object System.Drawing.Size(200, 20)
$TxtPass.UseSystemPasswordChar = $true
$GrpAD.Controls.Add($TxtPass)

# Toggle Logic: Enable/Disable fields based on Domain selection
$RadDomain.Add_CheckedChanged({
if ($RadDomain.Checked) {
$GrpAD.Enabled = $true
$ComboLoc.Enabled = $true
$LblLoc.Enabled = $true
$ComboDept.Enabled = $true
$LblDept.Enabled = $true
} else {
$GrpAD.Enabled = $false
$ComboLoc.Enabled = $false
$LblLoc.Enabled = $false
$ComboDept.Enabled = $false
$LblDept.Enabled = $false
}
})

# Start Button
$BtnStart = New-Object System.Windows.Forms.Button
$BtnStart.Text = "Start Provisioning"
$BtnStart.Location = New-Object System.Drawing.Point(120, 430)
$BtnStart.Size = New-Object System.Drawing.Size(200, 40)
$BtnStart.BackColor = [System.Drawing.Color]::SteelBlue
$BtnStart.ForeColor = [System.Drawing.Color]::White

# Transfer Data
$BtnStart.Add_Click({
$script:SelectedLocCity = $ComboLoc.SelectedItem.Split('-')[1].Trim().ToUpper()
$script:SelectedDeptCode = $ComboDept.SelectedItem.Split('-')[0].Trim().ToUpper()

$script:DoChrome = $ChkChrome.Checked
$script:DoAcrobat = $ChkAcrobat.Checked
$script:DoWinRar = $ChkWinRar.Checked

$script:IsDomain = $RadDomain.Checked
$script:ADUser = $TxtUser.Text
$script:ADPass = $TxtPass.Text
$Form.Close()
})
$Form.Controls.Add($BtnStart)

$Form.ShowDialog() | Out-Null

# --- PART 2: SYSTEM CONFIGURATION (CONSOLE LOG) ---

Clear-Host
Write-Host "Techaptic - Enterprise Provisioning Initiated..." -ForegroundColor Cyan
Write-Host "------------------------------------------------------------------" -ForegroundColor Cyan

$NeedsRestart = $false

# 1. WMI Hardware Detection & Smart Naming
Write-Host "Step 1: Processing Hardware Detection & Smart Naming... "
try {
$ChassisObj = Get-CimInstance -ClassName Win32_SystemEnclosure
$ChassisType = $ChassisObj.ChassisTypes[0]

if ($ChassisType -in 8,9,10,11,12,14,18,21) {
$HwType = "LAP"
} else {
$HwType = "PC"
}

$RandomID = Get-Random -Minimum 1000 -Maximum 9999

if ($IsDomain) {
$Prefix = $SelectedDeptCode
} else {
$Prefix = "WG"
}

$NewComputerName = "$Prefix-$HwType-$RandomID"
Write-Host "[SUCCESS] Hardware identified as $HwType. Target Name: $NewComputerName" -ForegroundColor Green
} catch {
Write-Host "[ERROR] Failed to generate name: $($_.Exception.Message)" -ForegroundColor Red
}

Write-Host "------------------------------------------------------------------" -ForegroundColor Cyan

# 2. Winget Application Deployment
Write-Host "Step 2: Processing Winget Application Deployment... "
try {
$WingetCheck = Get-Command winget -ErrorAction SilentlyContinue
if ($WingetCheck) {
$InstalledCount = 0
if ($DoChrome) {
Write-Host "-> Installing Google Chrome..." -ForegroundColor Yellow
winget install --id Google.Chrome -e --accept-package-agreements --accept-source-agreements | Out-Null
$InstalledCount++
}
if ($DoAcrobat) {
Write-Host "-> Installing Adobe Acrobat..." -ForegroundColor Yellow
winget install --id Adobe.Acrobat.Reader.64-bit -e --accept-package-agreements --accept-source-agreements | Out-Null
$InstalledCount++
}
if ($DoWinRar) {
Write-Host "-> Installing WinRAR..." -ForegroundColor Yellow
winget install --id RARLab.WinRAR -e --accept-package-agreements --accept-source-agreements | Out-Null
$InstalledCount++
}
Write-Host "[SUCCESS] $InstalledCount Applications Installed via Winget." -ForegroundColor Green
} else {
Write-Host "[SKIPPED] Winget package manager is not ready or accessible." -ForegroundColor Yellow
}
} catch {
Write-Host "[ERROR] Application deployment failed: $($_.Exception.Message)" -ForegroundColor Red
}

Write-Host "------------------------------------------------------------------" -ForegroundColor Cyan

# 3. Active Directory Domain Join OR Workgroup Rename
Write-Host "Step 3: Processing Network Identity & Rename... "
try {
if ($IsDomain) {
if ([string]::IsNullOrWhiteSpace($ADPass)) { throw "Password cannot be empty." }

$DomainName = "ad.techaptic.com"
$TargetOU = "OU=COMPUTERS,OU=$SelectedLocCity,OU=TECHAPTIC,DC=ad,DC=techaptic,DC=com"

Write-Host "-> Attempting to join $DomainName under $TargetOU as $NewComputerName..." -ForegroundColor Yellow
$SecurePass = ConvertTo-SecureString $ADPass -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential ($ADUser, $SecurePass)

Add-Computer -DomainName $DomainName -OUPath $TargetOU -NewName $NewComputerName -Credential $Credential -Force -ErrorAction Stop

Write-Host "[SUCCESS] Device successfully renamed and joined to Domain." -ForegroundColor Green
$NeedsRestart = $true
} else {
Write-Host "-> Workgroup selected. Renaming PC only..." -ForegroundColor Yellow
Rename-Computer -NewName $NewComputerName -Force -ErrorAction Stop
Write-Host "[SUCCESS] Device renamed to $NewComputerName." -ForegroundColor Green
$NeedsRestart = $true
}
} catch {
Write-Host "[ERROR] Operation Failed: $($_.Exception.Message)" -ForegroundColor Red
Write-Host "NOTE: Check if your test VM is receiving the correct DNS IP from the Domain Controller." -ForegroundColor Yellow
}

Write-Host "------------------------------------------------------------------" -ForegroundColor Cyan

# 4. Auto-Restart Logic
if ($NeedsRestart) {
Write-Host "Provisioning Complete. System requires a restart to apply changes." -ForegroundColor Cyan
for ($i = 10; $i -gt 0; $i--) {
Write-Host "Restarting in $i seconds... Press Ctrl+C to abort." -ForegroundColor Yellow
Start-Sleep -Seconds 1
}
Restart-Computer -Force
} else {
Write-Host "Provisioning Complete. No restart required." -ForegroundColor Green
Start-Sleep -Seconds 10
}

Quiz


QUIZ-EN-How-To-Advanced-Windows-11-Unattended-Installation-with-PowerShell-5
  • Question
  • Question
  • Question
  • Question
  • Question

Full Transcript

Rating

Post Rating (EN)
Scroll to Top