Automated Windows 11 provisioning — from bare metal to ready-to-use in one boot. No clicking through OOBE, no OEM bloat, no Microsoft account required.
This project provisions a fresh Windows 11 machine with apps, security settings, VPN, and user accounts — all scripted and repeatable.
| Option A — Full Reinstall | Option B — Existing Install | |
|---|---|---|
| When to use | New machine or wipe OEM image | Machine already has Windows 11 |
| OOBE bypass | Fully automated | Manual (see below) |
| Hands-on time | ~5 min prep, walk away | ~10 min |
| OEM bloat | Gone | Still there |
| What you need | USB drive + Windows 11 ISO | USB or network share |
win11AutomadeSetup/
├── Build-USB.ps1 # Generates autounattend.xml from config.ps1
├── Build-USB.cmd # Double-click wrapper to run Build-USB.ps1
├── autounattend.template.xml # Template — DO NOT edit directly or put on USB
├── autounattend.xml # Generated output — this goes on the USB
└── Setup/
├── Setup.ps1 # Main provisioning script
├── Launch-Setup.cmd # Auto-launcher — fires after first login
├── packages.json # App list for winget
├── config.example.ps1 # Config template — copy to config.ps1
└── config.ps1 # Your local secrets (gitignored, never committed)
Build-USB.ps1
│ reads config.ps1
│ prompts for any missing values
│ writes autounattend.xml
▼
USB Drive
│ autounattend.xml at root
│ Setup/ folder at root
▼
Boot target machine from USB
│
├─ windowsPE pass → sets locale, partitions disk, installs Windows
│
├─ specialize pass → sets computer name + timezone
│ registers RunOnce key → points to Launch-Setup.cmd
│
├─ oobeSystem pass → skips all OOBE screens
│ creates ITAdmin local account
│ auto-logs in as ITAdmin (once)
│
└─ First login → RunOnce fires Launch-Setup.cmd
waits 5 min for system to initialize
sets PowerShell execution policy to RemoteSigned
runs Setup.ps1 from USB automatically
Setup.ps1 installs apps, configures VPN,
creates user account, hardens security,
triggers Windows Update, increments
computer name on USB for next machine
Copy Setup/config.example.ps1 to Setup/config.ps1 and fill in your values:
# Key fields to set:
$Config_Organization = "Your Company"
$Config_ITAdminUsername = "itadmin"
$Config_ITAdminPassword = "" # prompted if blank
$Config_Timezone = "Eastern Standard Time"
$Config_WindowsEdition = "Windows 11 Pro"
$Config_ComputerName = "PC-HOSTNAME" # max 15 chars, letters/numbers/hyphens
$Config_TailscaleAuthKey = "" # optional VPN
$Config_WifiSSID = "" # optional WiFi profile
$Config_NewUsername = "" # end user account
$Config_NewFullName = ""
$Config_NewPassword = ""
$Config_NewUserIsAdmin = $falseconfig.ps1 is gitignored — it will never be committed.
Computer name rules: max 15 characters, letters, numbers, and hyphens only. No spaces.
On Windows, double-click Build-USB.cmd or run in PowerShell:
.\Build-USB.ps1On macOS/Linux (for preparing USB from another machine):
brew install powershell
pwsh ./Build-USB.ps1The script will:
- Load any values already in
config.ps1 - Prompt for anything missing (passwords are masked)
- Write all values back to
config.ps1 - Generate
autounattend.xmlfrom the template with your values substituted in
- Download the Windows 11 ISO
- Download Rufus and write the ISO to a USB drive (8 GB+):
- Partition scheme: GPT
- Target system: UEFI (non-CSM)
- Click START — when prompted, choose "Write in ISO image mode"
After Rufus finishes, copy these files to the root of the USB drive:
USB root/
├── autounattend.xml ← generated by Build-USB.ps1
└── Setup/ ← the entire Setup/ folder
├── Setup.ps1
├── Launch-Setup.cmd
├── packages.json
├── config.example.ps1
└── config.ps1 ← your filled-in config (stays on USB only)
- Plug the USB into the target machine
- Power on and boot from USB (F12 on Dell for boot menu)
- Windows installs fully automatically — no interaction needed
- The machine reboots, logs in as ITAdmin, and after ~5 minutes Setup.ps1 launches automatically in a PowerShell window
- Follow the prompts for any values not pre-filled in
config.ps1:- Tailscale auth key (if not in config)
- End user account details (if not in config)
- IT admin account to hide from login screen
- Wait for Setup.ps1 to complete and display its summary
- Delete
autounattend.xmlfrom the USB — it contains a plain-text password - Rotate the ITAdmin password
- Remove the USB drive
Use this when the machine already has Windows 11.
Windows 11 Pro:
Setup screen → "Sign in with Microsoft" → click "Sign-in options" → "Domain join instead" → create a local account
Windows 11 Home:
Press
Shift + F10to open a command prompt → typeoobe\bypassnro→ Enter → PC restarts → choose "I don't have internet" → "Continue with limited setup" → create a local account
- Copy the
Setup/folder to the machine or keep it on a USB drive - Open PowerShell as Administrator
- Run:
Set-ExecutionPolicy RemoteSigned -Scope LocalMachine -Force
powershell.exe -ExecutionPolicy Bypass -File "D:\Setup\Setup.ps1"Replace D: with the actual drive letter where the Setup folder is located.
| Phase | Action |
|---|---|
| 1 — Config | Loads config.ps1 if present; prompts for any missing values |
| 2 — Logging | Creates a timestamped log at %USERPROFILE%\win_setup_*.log |
| 3 — Pre-flight | Verifies Windows 11 build, admin rights, winget availability |
| 3.4 — WiFi | Configures WPA2 WiFi profile if SSID provided |
| 4 — Packages | Updates winget sources |
| 5 — VPN | Installs and authenticates Tailscale (if auth key provided) |
| 6 — Apps | Installs all applications from packages.json via winget import |
| 7 — Users | Hides IT admin account from login screen; creates end-user account |
| 8 — Security | Enables auto-updates, firewall, screen lock, Remote Desktop; disables SMBv1 |
| 9 — Dell Command Update | Installs Dell Command Update explicitly via winget (resolves .NET timing issue with winget import) |
| 10 — Windows Update | Triggers Windows Update via UsoClient StartInteractiveScan (native update engine — updates download in the background) |
| 11 — USB name increment | Reads autounattend.xml on the USB and increments the trailing number in <ComputerName> so the next machine gets a unique name automatically |
| 12 — Summary | Displays log file path and summary of all actions taken |
| Variable | Used by | Description |
|---|---|---|
Config_Organization |
autounattend.xml | Company name shown during install |
Config_WindowsEdition |
autounattend.xml | Must match ISO image name exactly |
Config_Timezone |
autounattend.xml | Windows timezone ID |
Config_ITAdminUsername |
autounattend.xml | Local admin account created during OOBE |
Config_ITAdminDisplayName |
autounattend.xml | Display name for IT admin account |
Config_ITAdminPassword |
autounattend.xml | Temporary password — rotate after provisioning |
Config_ComputerName |
autounattend.xml | Machine hostname, set during specialize pass |
Config_PackageFile |
Setup.ps1 | Winget import file (default: packages.json) |
Config_WifiSSID |
Setup.ps1 | WiFi network to connect to (optional) |
Config_WifiPassword |
Setup.ps1 | WiFi password (optional) |
Config_TailscaleAuthKey |
Setup.ps1 | Tailscale pre-auth key (optional) |
Config_NewUsername |
Setup.ps1 | End user account username |
Config_NewFullName |
Setup.ps1 | End user full name |
Config_NewPassword |
Setup.ps1 | End user password (optional, prompted if blank) |
Config_NewUserIsAdmin |
Setup.ps1 | $true to add end user to Administrators |
| Category | Apps |
|---|---|
| Browser | Google Chrome, Mozilla Firefox |
| Comms | Slack, Zoom, Microsoft Teams |
| Productivity | Hubstaff |
| Remote Access | AnyDesk |
| Utilities | 7-Zip, Notepad++, VLC |
| Security | Bitdefender, Tailscale |
| Hardware | Dell Command Update |
Edit packages.json freely — add or remove entries using winget search <appname> to find the correct Package ID.
- Windows Update set to auto-download and install
- Firewall enabled on all profiles (Domain, Public, Private)
- Password required on wake / screen lock
- Remote Desktop enabled (with firewall rule)
- SMBv1 disabled
- IT admin account hidden from login screen
- Log file permissions restricted to owner only (equivalent to chmod 600)
| Model | Status | Notes |
|---|---|---|
| Dell OptiPlex 3070 MFF | Supported | No driver injection needed. Windows Update covers Intel I219 NIC, UHD 630, Realtek audio. Dell Command Update handles BIOS and firmware. |
| Dell OptiPlex 5070 MFF | Supported | Same as above |
autounattend.xmlcontains the ITAdmin password in plain text. It is only used on the USB during provisioning — delete it from the USB when done.config.ps1is gitignored and must never be committed to version control.- Use a temporary ITAdmin password and rotate it immediately after provisioning completes.
- The
autounattend.template.xmlin the repo contains only placeholders — no credentials.
Pull requests are welcome. If you test on additional hardware or add support for new Dell models, open a PR with your results.
- Fork the repo
- Create a branch:
git checkout -b feature/your-feature - Commit and push
- Open a Pull Request
MIT License — free to use, modify, and distribute.
Made with