
Problem definition
Recently at a customer I was asked if it was possible, in preparation for their new modern workplace, that the end user could reset his device himself. Basically, the same as the famous "wipe" button in Intune.
I had indicated that this can be done by means of the following ways:
End user goes to the Settings app ➡️ Update & Security ➡️ Recovery ➡️ Get Started and follow the wizard
End user chooses CTRL + Windowskey + R on the sign-in screen
In the Run field, type
systemreset.exeand follow the wizardOh.. and the last option is to open the Company Portal ➡️ Devices ➡️ This Device ➡️ Actions ➡️ Reset
A requirement of the customer was that the end users may not be a local admin, so option 2 is dropped. They also wanted to protect the end user from the unnecessary questions that arise when choosing options 1 and 3. So they also fall off. Option 4 is to many clicks for the end-user. So there is nothing left. But we still want to offer a service with which the user can reset his device.
🤔
So, how does Intune work?
I don't know if I can judge this as a command, but after some research I ended up on this forum post: https://techcommunity.microsoft.com/t5/windows-deployment/factory-reset-windows-10-without-user-intervention/m-p/1349038/highlight/true#M559. There is a piece of script there that Intune also uses in theory. So if we can cram that piece nicely into a Win32 app with a nice warning?
💡💡💡💡💡
The win32app
The win32app is really nothing more than a powershell script. For the sake of simplicity, I will divide it in two.
Part 1: Warning window
As soon as the end user presses install, the script starts immediately without any kind of warning. So perhaps so neat to at least provide the end user with a warning and some tips before continuing.
For this I use the System.Windows.Forms language to create a popup containing the text, two buttons, and their formatting. I will not go into more detail here, except that you can add a title bar icon yourself by means of a base64 code. Websites enough that can do this for you.
# Show a messagebox where the enduser can accept or decline the reset
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$brand = @{
Yellow = [System.Drawing.Color]::FromArgb(255, 237, 0)
Peach = [System.Drawing.Color]::FromArgb(252, 215, 184)
Ink = [System.Drawing.Color]::FromArgb(35, 35, 35)
Paper = [System.Drawing.Color]::FromArgb(255, 255, 255)
SoftYellow = [System.Drawing.Color]::FromArgb(255, 253, 226)
WarningText = [System.Drawing.Color]::FromArgb(89, 65, 0)
Border = [System.Drawing.Color]::FromArgb(190, 190, 190)
}
# Create the form.
$form = New-Object system.Windows.Forms.Form
$form.ClientSize = New-Object System.Drawing.Size(600, 540)
$form.MinimumSize = New-Object System.Drawing.Size(600, 540)
$form.MaximumSize = New-Object System.Drawing.Size(600, 540)
$form.Text = "Reset My Device"
$form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
$form.MaximizeBox = $false
$form.MinimizeBox = $false
$form.Topmost = $true
$form.StartPosition = "CenterScreen"
$form.BackColor = $brand.Paper
$form.ForeColor = $brand.Ink
$form.Font = New-Object System.Drawing.Font("Segoe UI", 10)
$form.Padding = New-Object System.Windows.Forms.Padding(0)
$form.AutoScaleMode = [System.Windows.Forms.AutoScaleMode]::None
# This base64 string holds the bytes that make up the The Pesky Ghosts icon for a 32x32 pixel image
$iconBase64 = '<scrambled>'
$iconBytes = [Convert]::FromBase64String($iconBase64)
# initialize a Memory stream holding the bytes
$stream = [System.IO.MemoryStream]::new($iconBytes, 0, $iconBytes.Length)
$iconBitmap = [System.Drawing.Bitmap]::new($stream)
$Form.Icon = [System.Drawing.Icon]::FromHandle($iconBitmap.GetHIcon())
$brandIconBitmap = $iconBitmap
try {
$brandIconBytes = (New-Object System.Net.WebClient).DownloadData('https://thepeskyghosts.it/images/png/favicon.png')
$brandIconStream = [System.IO.MemoryStream]::new($brandIconBytes)
$brandIconBitmap = [System.Drawing.Bitmap]::new($brandIconStream)
$Form.Icon = [System.Drawing.Icon]::FromHandle($brandIconBitmap.GetHIcon())
}
catch {
# Keep the embedded fallback icon when the device is offline.
}
$headerPanel = New-Object System.Windows.Forms.Panel
$headerPanel.Dock = [System.Windows.Forms.DockStyle]::Top
$headerPanel.Height = 88
$headerPanel.BackColor = $brand.Ink
$headerIcon = New-Object System.Windows.Forms.PictureBox
$headerIcon.Location = New-Object System.Drawing.Point(24, 23)
$headerIcon.Size = New-Object System.Drawing.Size(42, 42)
$headerIcon.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::StretchImage
$headerIcon.Image = $brandIconBitmap
$headerPanel.Controls.Add($headerIcon)
$headerTitle = New-Object System.Windows.Forms.Label
$headerTitle.Location = New-Object System.Drawing.Point(82, 11)
$headerTitle.Size = New-Object System.Drawing.Size(480, 32)
$headerTitle.AutoSize = $false
$headerTitle.TextAlign = [System.Drawing.ContentAlignment]::MiddleLeft
$headerTitle.Text = "Reset My Device"
$headerTitle.ForeColor = $brand.Yellow
$headerTitle.Font = New-Object System.Drawing.Font("Segoe UI Semibold", 19)
$headerPanel.Controls.Add($headerTitle)
$headerSubtitle = New-Object System.Windows.Forms.Label
$headerSubtitle.Location = New-Object System.Drawing.Point(84, 48)
$headerSubtitle.Size = New-Object System.Drawing.Size(480, 22)
$headerSubtitle.AutoSize = $false
$headerSubtitle.TextAlign = [System.Drawing.ContentAlignment]::MiddleLeft
$headerSubtitle.Text = "Review the information before continuing"
$headerSubtitle.ForeColor = $brand.Peach
$headerSubtitle.Font = New-Object System.Drawing.Font("Segoe UI", 9)
$headerPanel.Controls.Add($headerSubtitle)
$form.Controls.Add($headerPanel)
$contentPanel = New-Object System.Windows.Forms.TableLayoutPanel
$contentPanel.Location = New-Object System.Drawing.Point(0, 88)
$contentPanel.Size = New-Object System.Drawing.Size(600, 312)
$contentPanel.Padding = New-Object System.Windows.Forms.Padding(28, 20, 28, 10)
$contentPanel.ColumnCount = 1
$contentPanel.RowCount = 4
[void]$contentPanel.ColumnStyles.Add((New-Object System.Windows.Forms.ColumnStyle([System.Windows.Forms.SizeType]::Percent, 100)))
[void]$contentPanel.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, 42)))
[void]$contentPanel.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, 166)))
[void]$contentPanel.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, 50)))
[void]$contentPanel.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Percent, 100)))
$introLabel = New-Object System.Windows.Forms.Label
$introLabel.Dock = [System.Windows.Forms.DockStyle]::Fill
$introLabel.AutoSize = $false
$introLabel.Text = "Starting this app will reinstall your device."
$introLabel.Font = New-Object System.Drawing.Font("Segoe UI Semibold", 11)
$introLabel.ForeColor = $brand.Ink
$contentPanel.Controls.Add($introLabel, 0, 0)
$warningBox = New-Object System.Windows.Forms.Panel
$warningBox.Dock = [System.Windows.Forms.DockStyle]::Fill
$warningBox.BackColor = $brand.SoftYellow
$warningLabel = New-Object System.Windows.Forms.Label
$warningLabel.Dock = [System.Windows.Forms.DockStyle]::Fill
$warningLabel.AutoSize = $false
$warningLabel.Padding = New-Object System.Windows.Forms.Padding(16, 14, 16, 14)
$warningLabel.Text = "The hard drive will be totally erased.`r`n`r`nContinue only after important files are synchronized to OneDrive or SharePoint.`r`n`r`nKeep the laptop connected to power for the entire reset."
$warningLabel.TextAlign = [System.Drawing.ContentAlignment]::TopLeft
$warningLabel.Font = New-Object System.Drawing.Font("Segoe UI", 10)
$warningLabel.ForeColor = $brand.WarningText
$warningBox.Controls.Add($warningLabel)
$contentPanel.Controls.Add($warningBox, 0, 1)
$acknowledge = New-Object System.Windows.Forms.CheckBox
$acknowledge.Dock = [System.Windows.Forms.DockStyle]::Fill
$acknowledge.Padding = New-Object System.Windows.Forms.Padding(0, 12, 0, 0)
$acknowledge.Text = "I understand that this action erases the device."
$acknowledge.AutoSize = $false
$acknowledge.Font = New-Object System.Drawing.Font("Segoe UI Semibold", 10)
$acknowledge.ForeColor = $brand.Ink
$contentPanel.Controls.Add($acknowledge, 0, 2)
$form.Controls.Add($contentPanel)The magic
The end user has received a warning window with two buttons. A CONTINUE and a CANCEL button. But what happens when the end user presses the CONTINUE button?
Well...
Do you have a moment?
⏱️⏱️
Time is up.
As soon as you press the CONTINUE button, the following script will take effect. This script is based on the local MDM WMI Bridge Provider.
$buttonPanel = New-Object System.Windows.Forms.Panel
$buttonPanel.Location = New-Object System.Drawing.Point(0, 400)
$buttonPanel.Size = New-Object System.Drawing.Size(600, 68)
$buttonPanel.Height = 68
$buttonPanel.BackColor = $brand.Paper
$form.Controls.Add($buttonPanel)
$okButton = New-Object System.Windows.Forms.Button
$okButton.Location = New-Object System.Drawing.Point(312, 17)
$okButton.Size = New-Object System.Drawing.Size(220, 34)
$okButton.Text = 'CONTINUE WITH RESET'
$okButton.DialogResult = [System.Windows.Forms.DialogResult]::Yes
$okButton.Enabled = $false
$okButton.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
$okButton.FlatAppearance.BorderSize = 0
$okButton.BackColor = $brand.Yellow
$okButton.ForeColor = $brand.Ink
$okButton.Font = New-Object System.Drawing.Font("Segoe UI Semibold", 9)
$form.AcceptButton = $okButton
$buttonPanel.Controls.Add($okButton)
$cancelButton = New-Object System.Windows.Forms.Button
$cancelButton.Location = New-Object System.Drawing.Point(28, 17)
$cancelButton.Size = New-Object System.Drawing.Size(120, 34)
$cancelButton.Text = 'CANCEL'
$cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::No
$cancelButton.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
$cancelButton.FlatAppearance.BorderColor = $brand.Border
$cancelButton.FlatAppearance.BorderSize = 1
$cancelButton.BackColor = $brand.Paper
$cancelButton.ForeColor = $brand.Ink
$cancelButton.Font = New-Object System.Drawing.Font("Segoe UI Semibold", 9)
$form.CancelButton = $cancelButton
$buttonPanel.Controls.Add($cancelButton)
$acknowledge.Add_CheckedChanged({ $okButton.Enabled = $acknowledge.Checked })
$form.Add_Shown({ $cancelButton.Focus() })
$Result = $form.ShowDialog()
switch ($Result) {
'Yes' {
# Dispose the form and its controls. Skip, if you want to redisplay the form later.
$form.Close()
$stream.Dispose()
$form.Dispose()
# And this is where the magic happens
$namespaceName = "root\cimv2\mdm\dmmap"
$className = "MDM_RemoteWipe"
$methodName = "doWipeProtectedMethod" #change this to doWipeMethod if you run this app on Surface devices
$session = New-CimSession
$params = New-Object Microsoft.Management.Infrastructure.CimMethodParametersCollection
$param = [Microsoft.Management.Infrastructure.CimMethodParameter]::Create("param", "", "String", "In")
$params.Add($param)
$instance = Get-CimInstance -Namespace $namespaceName -ClassName $className -Filter "ParentID='./Vendor/MSFT' and InstanceID='RemoteWipe'"
$session.InvokeMethod($namespaceName, $instance, $methodName, $params)
}
If you press the CANCEL button, the warning window is closed, and nothing happens to that particular device.
'No' {
# Dispose the form and its controls. Skip, if you want to redisplay the form later.
$form.Close()
$stream.Dispose()
$form.Dispose()
exit
}
}Create and deploy
I do think we all know how to make a win32app these days, so I'll skip that, but I'll tell you what you need to do in Intune to deploy the app.
Go to the Intune Portal
Go to Apps ➡️ Windows and click on + Add
In the dropdown list, select Windows app (Win32)
Click on Select
Click on Select app package file
Browse to the intunewin file, that you created or downloaded from my GitHub
Click on OK
Fill in a Name, Description and the Publisher. If you want you can fill in the rest of the fields as well
Click on Next
At the Install command field, type this:
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\Reset-MDMWipeDevice.ps1At the Uninstall command field, type this:
cmd.exe /c del %ProgramData%\ThePeskyGhosts\ResetMDMDevice\Reset-MDMWipeDevice.ps1.tagLeave the rest as default

Click on Next
Choose at the Operating System architecture for 64-bit
Choose at the Minimum operating system a value that suits your environment

Click on Next
From the Rules format dropdown, select Manually configure detection rules
Click on + Add
At the Rule type dropdown, select File
In the Path field, type
%ProgramData%\ThePeskyGhosts\ResetMDMDevice\In the File or folder field, type
Reset-MDMWipeDevice.ps1.tagFrom the Detection method dropdown, select File or folder exist
Click on OK

Click on Next
Click on Next
Click on Next
On the Assignments tab, choose an Entra ID group with your users and select them as avaialble.
Click on Next
Review your settings and click on Create
App in action
In the video below you can clearly see what the end user will see as soon as he / she installs the app.
Complete script
The script can be found on my [GitHub repository] (https://github.com/BurgerhoutJ/scripts/tree/main/reset-my-device) as well including the intunewin file and a fancy app icon.
Thanks for reading this post. See you next time!


